dev(lp): port to windows (msys2)

Signed-off-by: Lev Nachmanson <levnach@microsoft.com>
This commit is contained in:
Lev Nachmanson 2016-01-12 03:11:23 -08:00 committed by Leonardo de Moura
parent 99dcad0dda
commit 28bf891b7f
85 changed files with 12230 additions and 10019 deletions

View file

@ -337,6 +337,8 @@ add_subdirectory(util/sexpr)
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:sexpr>)
add_subdirectory(util/interval)
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:interval>)
add_subdirectory(util/lp)
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:lp>)
add_subdirectory(kernel)
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:kernel>)
add_subdirectory(kernel/inductive)

View file

@ -1,4 +1,4 @@
add_executable(lp_tst lp.cpp $<TARGET_OBJECTS:util> $<TARGET_OBJECTS:numerics>)
add_executable(lp_tst lp.cpp $<TARGET_OBJECTS:util> $<TARGET_OBJECTS:numerics> $<TARGET_OBJECTS:lp>)
target_link_libraries(lp_tst ${EXTRA_LIBS})
add_test(lp_tst ${CMAKE_CURRENT_BINARY_DIR}/lp_tst)
add_executable(double_compare double_compare.cpp $<TARGET_OBJECTS:util> $<TARGET_OBJECTS:numerics>)

View file

@ -17,7 +17,6 @@ Author: Lev Nachmanson
#include <stdlib.h>
#include <utility>
#include "util/pair.h"
#include "util/lp/lp.h"
#include "util/lp/lp_primal_simplex.h"
#include "tests/util/lp/mps_reader.h"
#include "tests/util/lp/smt_reader.h"
@ -69,8 +68,8 @@ void test_matrix(sparse_matrix<T, X> & a) {
a.set(row, col, T(0));
unsigned i = rand_r(& seed) % m;
unsigned j = rand_r(& seed) % m;
unsigned i = my_random() % m;
unsigned j = my_random() % m;
auto t = T(1);
@ -248,7 +247,7 @@ void fill_long_row_exp(sparse_matrix<double, double> &m, int i) {
int n = m.dimension();
for (int j = 0; j < n; j ++) {
m(i, j) = rand_r(& seed) % 20;
m(i, j) = my_random() % 20;
}
}
@ -256,7 +255,7 @@ void fill_long_row_exp(static_matrix<double, double> &m, int i) {
int n = m.column_count();
for (int j = 0; j < n; j ++) {
m(i, j) = rand_r(& seed) % 20;
m(i, j) = my_random() % 20;
}
}
@ -528,6 +527,8 @@ void test_lp_primal_core_solver() {
test_lp_0();
test_lp_1();
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
void test_swap_rows_with_permutation(sparse_matrix<T, X>& m){
@ -538,8 +539,8 @@ void test_swap_rows_with_permutation(sparse_matrix<T, X>& m){
print_matrix(m);
lean_assert(original == q * m);
for (int i = 0; i < 100; i++) {
unsigned row1 = lrand48() % dim;
unsigned row2 = lrand48() % dim;
unsigned row1 = my_random() % dim;
unsigned row2 = my_random() % dim;
if (row1 == row2) continue;
cout << "swap " << row1 << " " << row2 << std::endl;
m.swap_rows(row1, row2);
@ -553,36 +554,6 @@ void test_swap_rows_with_permutation(sparse_matrix<T, X>& m){
template <typename T, typename X>
void fill_matrix(sparse_matrix<T, X>& m); // forward definition
#ifdef LEAN_DEBUG
void matrix_repro_test() {
unsigned dim = 10;
sparse_matrix<double, double> m(dim);
fill_matrix(m);
for (int i = 0; i < 100; i++) {
unsigned row1 = lrand48() % dim;
unsigned row2 = lrand48() % dim;
if (row1 == row2) continue;
m.swap_rows(row1, row2);
}
for (int i = 0; i < 100; i++) {
unsigned col1 = lrand48() % dim;
unsigned col2 = lrand48() % dim;
if (col1 == col2) continue;
m.swap_columns(col1, col2);
}
dense_matrix<double, double> d(m);
for (unsigned i = 0; i < d.row_count(); i++)
for (unsigned j = 0; j < d.column_count(); j++)
d.set_elem(i, j, m.get_not_adjusted(i, j));
auto l = d * m.m_column_permutation;
l = m.m_row_permutation * l;
cout << "matrix_repro_test" << std::endl;
print_matrix(l);
lean_assert(l == m);
}
template <typename T, typename X>
void test_swap_cols_with_permutation(sparse_matrix<T, X>& m){
cout << "testing swaps" << std::endl;
@ -592,8 +563,8 @@ void test_swap_cols_with_permutation(sparse_matrix<T, X>& m){
print_matrix(m);
lean_assert(original == q * m);
for (int i = 0; i < 100; i++) {
unsigned row1 = lrand48() % dim;
unsigned row2 = lrand48() % dim;
unsigned row1 = my_random() % dim;
unsigned row2 = my_random() % dim;
if (row1 == row2) continue;
cout << "swap " << row1 << " " << row2 << std::endl;
m.swap_rows(row1, row2);
@ -915,8 +886,7 @@ void test_swap_operations() {
test_swap_rows();
test_swap_columns();
}
#endif
#ifdef LEAN_DEBUG
void test_dense_matrix() {
dense_matrix<double, double> d(3, 2);
d.set_elem(0, 0, 1);
@ -945,104 +915,9 @@ void test_dense_matrix() {
p2.set_elem(1, 0, 1);
auto c2 = d * p2;
}
void test_conjugate_eta_matrix() {
permutation_matrix<double, double> p(5);
p[0] = 3; p[1] = 2; p[2] = 1; p[3] = 4;
p[4] = 0;
cout << "p="; p.print();
#ifdef LEAN_DEBUG
eta_matrix<double, double> l(2, 5);
#else
eta_matrix<double, double> l(2);
#endif
for (unsigned i = 3; i <= 4; i++)
l.push_back(i, i+2); // i+2 for the value
l.set_diagonal_element(10);
cout << "l" << std::endl;
print_matrix(l);
dense_matrix<double, double> lcopy(l);
l.conjugate_by_permutation(p);
permutation_matrix<double, double> pr = p.get_inverse();
auto conj = lcopy * pr;
cout << "U*pr" << std::endl;
print_matrix(conj);
conj = p * conj;
cout << "conj " << std::endl;
print_matrix(conj);
cout << "l" << std::endl;
print_matrix(l);
lean_assert(conj == l);
}
void test_conjugate0() {
permutation_matrix<double, double> p(5);
p[0] = 3; p[1] = 2; p[2] = 1; p[3] = 4;
p[4] = 0;
one_off_diagonal_matrix<double, double> one_off(1, 4, 2, 3);
one_off.set_number_of_rows(5);
one_off.set_number_of_columns(5);
one_off_diagonal_matrix<double, double> one_off_copy(1, 4, 2, 3);
one_off_copy.set_number_of_rows(5);
one_off_copy.set_number_of_columns(5);
one_off.conjugate_by_permutation(p);
permutation_matrix<double, double> pr = p.get_inverse();
auto conj = one_off_copy * (pr);
conj = p * conj;
lean_assert(conj == one_off);
}
void test_conjugate_perm(){
permutation_matrix<double, double> p(5);
p[0] = 1; p[1] = 2; p[2] = 3; p[3] = 4;
p[4] = 0;
one_off_diagonal_matrix<double, double> one_off(1, 3, 2, 3);
one_off.set_number_of_rows(5);
one_off.set_number_of_columns(5);
one_off_diagonal_matrix<double, double> one_off_copy(1, 3, 2, 3);
one_off_copy.set_number_of_rows(5);
one_off_copy.set_number_of_columns(5);
one_off.conjugate_by_permutation(p);
permutation_matrix<double, double> pr = p.get_inverse();
auto conj = one_off_copy * pr;
conj = p * conj;
lean_assert(conj == one_off);
test_conjugate0();
}
void test_conjugate() {
permutation_matrix<double, double> p(5);
p[0] = 1; p[1] = 2; p[2] = 3; p[3] = 4;
p[4] = 0;
one_off_diagonal_matrix<double, double> one_off(1, 3, 2, 3);
one_off.set_number_of_rows(5);
one_off.set_number_of_columns(5);
one_off_diagonal_matrix<double, double> one_off_copy(1, 3, 2, 3);
one_off_copy.set_number_of_rows(5);
one_off_copy.set_number_of_columns(5);
one_off.conjugate_by_permutation(p);
permutation_matrix<double, double> pr = p.get_inverse();
auto conj = one_off_copy * pr;
conj = p * conj;
lean_assert(conj == one_off);
test_conjugate0();
}
#endif
std::vector<permutation_matrix<double, double>> vector_of_permutaions() {
std::vector<permutation_matrix<double, double>> ret;
@ -1327,9 +1202,9 @@ void test_upair_queue() {
binary_heap_upair_queue<int> q(2);
unordered_map<upair, int> m;
for (int k = 0; k < 100; k++) {
int i = lrand48()%n;
int j = lrand48()%n;
q.enqueue(i, j, lrand48()%n);
int i = my_random()%n;
int j = my_random()%n;
q.enqueue(i, j, my_random()%n);
}
q.remove(5, 5);
@ -1439,15 +1314,15 @@ void solve_mps_with_known_solution(std::string file_name, std::unordered_map<str
}
int get_random_rows() {
return 5 + rand_r(& seed) % 2;
return 5 + my_random() % 2;
}
int get_random_columns() {
return 5 + rand_r(& seed) % 3;
return 5 + my_random() % 3;
}
int get_random_int() {
return -1 + rand_r(& seed) % 2; // (1.0 + RAND_MAX);
return -1 + my_random() % 2; // (1.0 + RAND_MAX);
}
void add_random_row(lp_primal_simplex<double, double> * solver, int cols, int row) {
@ -1634,7 +1509,11 @@ void test_out_dir(string out_dir) {
DIR *out_dir_p = opendir(out_dir.c_str());
if (out_dir_p == nullptr) {
cout << "creating directory " << out_dir << std::endl;
#ifdef LEAN_WINDOWS
int res = mkdir(out_dir.c_str());
#else
int res = mkdir(out_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
#endif
if (res) {
cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl;
}
@ -1898,26 +1777,6 @@ void test_replace_column() {
}
}
void test_eta_matrix() {
sparse_matrix<double, double> m(8);
fill_matrix(m);
lp_settings settings;
for (unsigned j = 0; j < m.dimension(); j++){
eta_matrix<double, double> * eta;
m.fill_eta_matrix(j, &eta);
m.prepare_for_factorization();
for (auto it = eta->get_sparse_vector_iterator(); !it.done(); it.move()) {
m.pivot_row_to_row(j, it.value(), it.index(), settings);
double de = eta->get_diagonal_element();
m.divide_row_by_constant(j, de, settings);
}
delete eta;
}
lean_assert(m.is_upper_triangular_and_maximums_are_set_correctly_in_rows(settings));
}
void setup_args_parser(argument_parser & parser) {
parser.add_option_with_after_string_with_help("--density", "the percentage of non-zeroes in the matrix below which it is not dense");
@ -2154,7 +2013,8 @@ void compare_costs(string glpk_out_file_name,
void compare_with_glpk(string glpk_out_file_name, string lp_out_file_name, unsigned & successes, unsigned & failures, string lp_file_name) {
void compare_with_glpk(string glpk_out_file_name, string lp_out_file_name, unsigned & successes, unsigned & failures, string /*lp_file_name*/) {
#ifdef CHECK_GLPK_SOLUTION
std::unordered_map<string, double> * solution_table = get_solution_from_glpsol_output(glpk_out_file_name);
if (solution_is_feasible(lp_file_name, *solution_table)) {
cout << "glpk solution is feasible" << std::endl;
@ -2162,6 +2022,7 @@ void compare_with_glpk(string glpk_out_file_name, string lp_out_file_name, unsig
cout << "glpk solution is infeasible" << std::endl;
}
delete solution_table;
#endif
if (compare_statuses(glpk_out_file_name, lp_out_file_name, successes, failures)) {
compare_costs(glpk_out_file_name, lp_out_file_name, successes, failures);
}
@ -2213,6 +2074,14 @@ void process_test_file(string test_dir, string test_file_name, argument_parser &
}
}
}
int my_readdir(DIR *dirp, struct dirent *entry, struct dirent **result) {
#ifdef LEAN_WINDOWS
*result = readdir(dirp);
return *result != nullptr? 0 : 1;
#else
return readdir_r(dirp, entry, result);
#endif
}
std::vector<std::pair<std::string, int>> get_file_list_of_dir(std::string test_file_dir) {
DIR *dir;
@ -2224,16 +2093,19 @@ std::vector<std::pair<std::string, int>> get_file_list_of_dir(std::string test_f
struct dirent entry;
struct dirent* result;
int return_code;
for (return_code = readdir_r(dir, &entry, &result);
result != NULL && return_code == 0;
return_code = readdir_r(dir, &entry, &result)) {
DIR *tmp_dp = opendir(entry.d_name);
for (return_code = my_readdir(dir, &entry, &result);
#ifndef LEAN_WINDOWS
result != nullptr &&
#endif
return_code == 0;
return_code = my_readdir(dir, &entry, &result)) {
DIR *tmp_dp = opendir(result->d_name);
struct stat file_record;
if (tmp_dp == nullptr) {
std::string s = test_file_dir+ "/" + entry.d_name;
std::string s = test_file_dir+ "/" + result->d_name;
int stat_ret = stat(s.c_str(), & file_record);
if (stat_ret!= -1) {
ret.push_back(make_pair(entry.d_name, file_record.st_size));
ret.push_back(make_pair(result->d_name, file_record.st_size));
} else {
perror("stat");
exit(1);
@ -2577,7 +2449,6 @@ int main(int argn, char * const * argv) {
int ret;
argument_parser args_parser(argn, argv);
setup_args_parser(args_parser);
if (!args_parser.parse()) {
std::cout << args_parser.m_error_message << std::endl;
std::cout << args_parser.usage_string();
@ -2675,9 +2546,7 @@ int main(int argn, char * const * argv) {
test_replace_column();
#ifdef LEAN_DEBUG
test_perm_apply_reverse_from_right();
test_conjugate_perm();
sparse_matrix_with_permutaions_test();
test_conjugate();
test_dense_matrix();
test_swap_operations();
test_permutations();

View file

@ -1 +1,26 @@
add_library(lp OBJECT lp.cpp)
add_library(lp OBJECT
binary_heap_priority_queue_instances.cpp
binary_heap_upair_queue_instances.cpp
core_solver_pretty_printer_instances.cpp
indexed_vector_instances.cpp
lar_core_solver_instances.cpp
lar_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
permutation_matrix_instances.cpp
row_eta_matrix_instances.cpp
sparse_matrix_instances.cpp
square_dense_submatrix_instances.cpp
static_matrix_instances.cpp
scaler_instances.cpp
dense_matrix_instances.cpp
eta_matrix_instances.cpp
matrix_instances.cpp
lar_constraints.cpp
)

View file

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

View file

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

View file

@ -0,0 +1,21 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/numeric_pair.h"
#include "util/lp/binary_heap_priority_queue.cpp"
namespace lean {
template binary_heap_priority_queue<int>::binary_heap_priority_queue(unsigned int);
template unsigned binary_heap_priority_queue<int>::dequeue();
template void binary_heap_priority_queue<int>::enqueue(unsigned int, int const&);
template void binary_heap_priority_queue<int>::remove(unsigned int);
template unsigned binary_heap_priority_queue<numeric_pair<mpq> >::dequeue();
template void binary_heap_priority_queue<numeric_pair<mpq> >::enqueue(unsigned int, numeric_pair<mpq> const&);
template void binary_heap_priority_queue<numeric_pair<mpq> >::resize(unsigned int);
template binary_heap_priority_queue<unsigned int>::binary_heap_priority_queue(unsigned int);
template unsigned binary_heap_priority_queue<unsigned int>::dequeue();
template void binary_heap_priority_queue<unsigned int>::enqueue(unsigned int, unsigned int const&);
template void binary_heap_priority_queue<unsigned int>::remove(unsigned int);
}

View file

@ -0,0 +1,250 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE
Author: Lev Nachmanson
*/
#include "util/lp/binary_heap_upair_queue.h"
namespace lean {
template <typename T> binary_heap_upair_queue<T>::binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) {
lean_assert(size);
for (unsigned i = 0; i < size; i++)
m_available_spots.push_back(i);
}
template <typename T> unsigned
binary_heap_upair_queue<T>::dequeue_available_spot() {
lean_assert(m_available_spots.empty() == false);
unsigned ret = m_available_spots.back();
m_available_spots.pop_back();
return ret;
}
template <typename T> void binary_heap_upair_queue<T>::remove(unsigned i, unsigned j) {
upair p(i, j);
auto it = m_pairs_to_index.find(p);
if (it == m_pairs_to_index.end())
return; // nothing to do
m_q.remove(it->second);
m_available_spots.push_back(it->second);
m_pairs_to_index.erase(it);
}
template <typename T> bool binary_heap_upair_queue<T>::ij_index_is_new(unsigned ij_index) const {
for (auto it : m_pairs_to_index) {
if (it.second == ij_index)
return false;
}
return true;
}
template <typename T> void binary_heap_upair_queue<T>::enqueue(unsigned i, unsigned j, const T & priority) {
upair p(i, j);
auto it = m_pairs_to_index.find(p);
unsigned ij_index;
if (it == m_pairs_to_index.end()) {
// it is a new pair, let us find a spot for it
if (m_available_spots.empty()) {
// we ran out of empty spots
unsigned size_was = m_pairs.size();
unsigned new_size = size_was << 1;
for (unsigned i = size_was; i < new_size; i++)
m_available_spots.push_back(i);
m_pairs.resize(new_size);
}
ij_index = dequeue_available_spot();
// lean_assert(ij_index<m_pairs.size() && ij_index_is_new(ij_index));
m_pairs[ij_index] = p;
m_pairs_to_index[p] = ij_index;
} else {
ij_index = it->second;
}
m_q.enqueue(ij_index, priority);
}
template <typename T> void binary_heap_upair_queue<T>::dequeue(unsigned & i, unsigned &j) {
lean_assert(!m_q.is_empty());
unsigned ij_index = m_q.dequeue();
upair & p = m_pairs[ij_index];
i = p.first;
j = p.second;
m_available_spots.push_back(ij_index);
m_pairs_to_index.erase(p);
}
template <typename T> T binary_heap_upair_queue<T>::get_priority(unsigned i, unsigned j) const {
auto it = m_pairs_to_index.find(std::make_pair(i, j));
if (it == m_pairs_to_index.end())
return T(0xFFFFFF); // big number
return m_q.get_priority(it->second);
}
#ifdef LEAN_DEBUG
template <typename T> bool binary_heap_upair_queue<T>::pair_to_index_is_a_bijection() const {
std::set<int> tmp;
for (auto p : m_pairs_to_index) {
unsigned j = p.second;
auto it = tmp.find(j);
if (it != tmp.end()) {
std::cout << "for pair (" << p.first.first << ", " << p.first.second << "), the index " << j
<< " is already inside " << std::endl;
lean_assert(false);
} else {
tmp.insert(j);
}
}
return true;
}
template <typename T> bool binary_heap_upair_queue<T>::available_spots_are_correct() const {
std::set<int> tmp;
for (auto p : m_available_spots){
tmp.insert(p);
}
if (tmp.size() != m_available_spots.size())
return false;
for (auto it : m_pairs_to_index)
if (tmp.find(it.second) != tmp.end())
return false;
return true;
}
#endif
}
/*
#include <unordered_set>
#include <queue>
#include <vector>
#include <set>
#include <utility>
typedef std::pair<unsigned, unsigned> upair;
namespace lean {
template <typename T>
class binary_heap_upair_queue {
binary_heap_priority_queue<T> m_q;
std::unordered_map<upair, unsigned> m_pairs_to_index;
std::vector<upair> m_pairs; // inverse to index
std::vector<unsigned> m_available_spots;
public:
binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) {
lean_assert(size);
for (unsigned i = 0; i < size; i++)
m_available_spots.push_back(i);
}
unsigned dequeue_available_spot() {
lean_assert(m_available_spots.empty() == false);
unsigned ret = m_available_spots.back();
m_available_spots.pop_back();
return ret;
}
void remove(unsigned i, unsigned j) {
upair p(i, j);
auto it = m_pairs_to_index.find(p);
if (it == m_pairs_to_index.end())
return; // nothing to do
m_q.remove(it->second);
m_available_spots.push_back(it->second);
m_pairs_to_index.erase(it);
}
bool ij_index_is_new(unsigned ij_index) const {
for (auto it : m_pairs_to_index) {
if (it.second == ij_index)
return false;
}
return true;
}
void enqueue(unsigned i, unsigned j, const T & priority) {
upair p(i, j);
auto it = m_pairs_to_index.find(p);
unsigned ij_index;
if (it == m_pairs_to_index.end()) {
// it is a new pair, let us find a spot for it
if (m_available_spots.empty()) {
// we ran out of empty spots
unsigned size_was = m_pairs.size();
unsigned new_size = size_was << 1;
for (unsigned i = size_was; i < new_size; i++)
m_available_spots.push_back(i);
m_pairs.resize(new_size);
}
ij_index = dequeue_available_spot();
// lean_assert(ij_index<m_pairs.size() && ij_index_is_new(ij_index));
m_pairs[ij_index] = p;
m_pairs_to_index[p] = ij_index;
} else {
ij_index = it->second;
}
m_q.enqueue(ij_index, priority);
}
void dequeue(unsigned & i, unsigned &j) {
lean_assert(!m_q.is_empty());
unsigned ij_index = m_q.dequeue();
upair & p = m_pairs[ij_index];
i = p.first;
j = p.second;
m_available_spots.push_back(ij_index);
m_pairs_to_index.erase(p);
}
bool is_empty() const {
return m_q.is_empty();
}
unsigned size() const {
return m_q.size();
}
bool contains(unsigned i, unsigned j) const {
return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end();
}
T get_priority(unsigned i, unsigned j) const {
auto it = m_pairs_to_index.find(std::make_pair(i, j));
if (it == m_pairs_to_index.end())
return T(0xFFFFFF); // big number
return m_q.get_priority(it->second);
}
bool pair_to_index_is_a_bijection() const {
std::set<int> tmp;
for (auto p : m_pairs_to_index) {
unsigned j = p.second;
auto it = tmp.find(j);
if (it != tmp.end()) {
std::cout << "for pair (" << p.first.first << ", " << p.first.second << "), the index " << j
<< " is already inside " << std::endl;
lean_assert(false);
} else {
tmp.insert(j);
}
}
return true;
}
bool available_spots_are_correct() const {
std::set<int> tmp;
for (auto p : m_available_spots){
tmp.insert(p);
}
if (tmp.size() != m_available_spots.size())
return false;
for (auto it : m_pairs_to_index)
if (tmp.find(it.second) != tmp.end())
return false;
return true;
}
bool is_correct() const {
return m_q.is_consistent() && pair_to_index_is_a_bijection() && available_spots_are_correct();
}
};
}
*/

View file

@ -6,12 +6,15 @@
*/
#pragma once
#include "util/lp/binary_heap_priority_queue.h"
#include <unordered_set>
#include <unordered_map>
#include <queue>
#include <vector>
#include <set>
#include <utility>
#include "util/lp/binary_heap_priority_queue.h"
#include "util/lp/hash_helper.h"
typedef std::pair<unsigned, unsigned> upair;
namespace lean {
@ -22,122 +25,27 @@ class binary_heap_upair_queue {
std::vector<upair> m_pairs; // inverse to index
std::vector<unsigned> m_available_spots;
public:
binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) {
lean_assert(size);
for (unsigned i = 0; i < size; i++)
m_available_spots.push_back(i);
}
unsigned dequeue_available_spot() {
lean_assert(m_available_spots.empty() == false);
unsigned ret = m_available_spots.back();
m_available_spots.pop_back();
return ret;
}
void remove(unsigned i, unsigned j) {
upair p(i, j);
auto it = m_pairs_to_index.find(p);
if (it == m_pairs_to_index.end())
return; // nothing to do
m_q.remove(it->second);
m_available_spots.push_back(it->second);
m_pairs_to_index.erase(it);
}
bool ij_index_is_new(unsigned ij_index) const {
for (auto it : m_pairs_to_index) {
if (it.second == ij_index)
return false;
}
return true;
}
void enqueue(unsigned i, unsigned j, const T & priority) {
upair p(i, j);
auto it = m_pairs_to_index.find(p);
unsigned ij_index;
if (it == m_pairs_to_index.end()) {
// it is a new pair, let us find a spot for it
if (m_available_spots.empty()) {
// we ran out of empty spots
unsigned size_was = m_pairs.size();
unsigned new_size = size_was << 1;
for (unsigned i = size_was; i < new_size; i++)
m_available_spots.push_back(i);
m_pairs.resize(new_size);
}
ij_index = dequeue_available_spot();
// lean_assert(ij_index<m_pairs.size() && ij_index_is_new(ij_index));
m_pairs[ij_index] = p;
m_pairs_to_index[p] = ij_index;
} else {
ij_index = it->second;
}
m_q.enqueue(ij_index, priority);
}
void dequeue(unsigned & i, unsigned &j) {
lean_assert(!m_q.is_empty());
unsigned ij_index = m_q.dequeue();
upair & p = m_pairs[ij_index];
i = p.first;
j = p.second;
m_available_spots.push_back(ij_index);
m_pairs_to_index.erase(p);
}
bool is_empty() const {
return m_q.is_empty();
}
unsigned size() const {
return m_q.size();
}
bool contains(unsigned i, unsigned j) const {
return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end();
}
T get_priority(unsigned i, unsigned j) const {
auto it = m_pairs_to_index.find(std::make_pair(i, j));
if (it == m_pairs_to_index.end())
return T(0xFFFFFF); // big number
return m_q.get_priority(it->second);
}
bool pair_to_index_is_a_bijection() const {
std::set<int> tmp;
for (auto p : m_pairs_to_index) {
unsigned j = p.second;
auto it = tmp.find(j);
if (it != tmp.end()) {
std::cout << "for pair (" << p.first.first << ", " << p.first.second << "), the index " << j
<< " is already inside " << std::endl;
lean_assert(false);
} else {
tmp.insert(j);
}
}
return true;
}
bool available_spots_are_correct() const {
std::set<int> tmp;
for (auto p : m_available_spots){
tmp.insert(p);
}
if (tmp.size() != m_available_spots.size())
return false;
for (auto it : m_pairs_to_index)
if (tmp.find(it.second) != tmp.end())
return false;
return true;
binary_heap_upair_queue(unsigned size);
unsigned dequeue_available_spot();
bool is_empty() const { return m_q.is_empty(); }
unsigned size() const {return m_q.size(); }
bool contains(unsigned i, unsigned j) const { return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end();
}
void remove(unsigned i, unsigned j);
bool ij_index_is_new(unsigned ij_index) const;
void enqueue(unsigned i, unsigned j, const T & priority);
void dequeue(unsigned & i, unsigned &j);
T get_priority(unsigned i, unsigned j) const;
#ifdef LEAN_DEBUG
bool pair_to_index_is_a_bijection() const;
bool available_spots_are_correct() const;
bool is_correct() const {
return m_q.is_consistent() && pair_to_index_is_a_bijection() && available_spots_are_correct();
}
#endif
};
}

View file

@ -0,0 +1,19 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE
Author: Lev Nachmanson
*/
#include "util/lp/binary_heap_upair_queue.cpp"
namespace lean {
template binary_heap_upair_queue<int>::binary_heap_upair_queue(unsigned int);
template binary_heap_upair_queue<unsigned int>::binary_heap_upair_queue(unsigned int);
template unsigned binary_heap_upair_queue<int>::dequeue_available_spot();
template unsigned binary_heap_upair_queue<unsigned int>::dequeue_available_spot();
template void binary_heap_upair_queue<int>::enqueue(unsigned int, unsigned int, int const&);
template void binary_heap_upair_queue<int>::remove(unsigned int, unsigned int);
template void binary_heap_upair_queue<unsigned int>::remove(unsigned int, unsigned int);
template void binary_heap_upair_queue<int>::dequeue(unsigned int&, unsigned int&);
template void binary_heap_upair_queue<unsigned int>::enqueue(unsigned int, unsigned int, unsigned int const&);
template void binary_heap_upair_queue<unsigned int>::dequeue(unsigned int&, unsigned int&);
}

View file

@ -9,7 +9,10 @@
#include <vector>
#include <string>
#include <algorithm>
#include <utility>
#include "util/numerics/mpq.h"
#include "util/lp/column_info.h"
#include "util/lp/hash_helper.h"
namespace lean {
typedef unsigned var_index;
typedef unsigned constraint_index;
@ -18,7 +21,7 @@ enum lconstraint_kind {
};
class lar_normalized_constraint; // forward definition
bool compare(const pair<mpq, var_index> & a, const pair<mpq, var_index> & b) {
inline bool compare(const std::pair<mpq, var_index> & a, const std::pair<mpq, var_index> & b) {
return a.second < b.second;
}
@ -26,12 +29,12 @@ class canonic_left_side {
public:
int m_row_index = -1;
int m_column_index = -1; // this is the column of the left side variable in the matrix
std::vector<pair<mpq, var_index>> m_coeffs;
std::vector<std::pair<mpq, var_index>> m_coeffs;
column_info<mpq> m_column_info;
lar_normalized_constraint * m_low_bound_witness = nullptr;
lar_normalized_constraint * m_upper_bound_witness = nullptr;
canonic_left_side(buffer<pair<mpq, var_index>> buffer) {
canonic_left_side(buffer<std::pair<mpq, var_index>> buffer) {
for (auto it : buffer) {
if (numeric_traits<mpq>::is_zero(it.first)) continue;
m_coeffs.push_back(it);
@ -65,7 +68,7 @@ public:
std::size_t hash_of_ls() const {
std::size_t ret = 0;
std::hash<pair<mpq, var_index>> hash_fun;
std::hash<std::pair<mpq, var_index>> hash_fun;
for (auto v : m_coeffs) {
ret |= (hash_fun(v) << 2);
}

View file

@ -6,12 +6,11 @@
*/
#pragma once
#include "util/lp/lp_primal_core_solver.h"
#include "util/lp/lp_solver.h"
#include <vector>
#include <unordered_map>
#include <string>
#include <algorithm>
#include "util/lp/lp_settings.h"
namespace lean {
template <typename T>
class column_info {
@ -27,16 +26,8 @@ class column_info {
bool m_is_fixed = false;
public:
column_type get_column_type() {
if (m_is_fixed) {
return column_type::fixed;
}
if (m_low_bound_is_set) {
return m_upper_bound_is_set? boxed: low_bound;
}
// we are flipping the bounds!
return m_upper_bound_is_set? low_bound: free_column;
column_type get_column_type() const {
return m_is_fixed? fixed : (m_low_bound_is_set? (m_upper_bound_is_set? boxed : low_bound) : (m_upper_bound_is_set? upper_bound: free_column));
}
column_type get_column_type_no_flipping() {

View file

@ -0,0 +1,339 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lp_core_solver_base.h"
#include "util/lp/core_solver_pretty_printer.h"
namespace lean {
template <typename T, typename X>
core_solver_pretty_printer<T, X>::core_solver_pretty_printer(lp_core_solver_base<T, X > & core_solver): m_core_solver(core_solver),
m_column_widths(core_solver.m_A.column_count(), 0),
m_A(core_solver.m_A.row_count(), vector<string>(core_solver.m_A.column_count(), "")),
m_signs(core_solver.m_A.row_count(), vector<string>(core_solver.m_A.column_count(), " ")),
m_costs(ncols(), ""),
m_cost_signs(ncols(), " "),
m_rs(ncols(), zero_of_type<X>()) {
m_w_buff = new T[m_core_solver.m_m];
m_ed_buff = new T[m_core_solver.m_m];
m_core_solver.save_state(m_w_buff, m_ed_buff);
init_m_A_and_signs();
init_costs();
init_column_widths();
init_rs_width();
m_cost_title = "costs";
m_basis_heading_title = "heading";
m_x_title = "x*";
m_title_width = std::max(std::max(m_cost_title.size(), std::max(m_basis_heading_title.size(), m_x_title.size())), m_approx_norm_title.size());
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: init_costs() {
vector<T> local_y(m_core_solver.m_m);
m_core_solver.solve_yB(local_y);
for (unsigned i = 0; i < ncols(); i++) {
if (m_core_solver.m_basis_heading[i] < 0) {
T t = m_core_solver.m_costs[i] - m_core_solver.m_A.dot_product_with_column(local_y, i);
set_coeff(m_costs, m_cost_signs, i, t, m_core_solver.column_name(i));
}
}
}
template <typename T, typename X> core_solver_pretty_printer<T, X>:: ~core_solver_pretty_printer() {
m_core_solver.restore_state(m_w_buff, m_ed_buff);
delete [] m_w_buff;
delete [] m_ed_buff;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: init_rs_width() {
m_rs_width = T_to_string(m_core_solver.get_cost()).size();
for (unsigned i = 0; i < nrows(); i++) {
auto wt = T_to_string(m_rs[i]).size();
if (wt > m_rs_width) {
m_rs_width = wt;
}
}
}
template <typename T, typename X> T core_solver_pretty_printer<T, X>:: current_column_norm() {
T ret = zero_of_type<T>();
for (T & ed : m_core_solver.m_ed)
ret += ed * ed;
return ret;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: init_m_A_and_signs() {
for (unsigned column = 0; column < ncols(); column++) {
m_core_solver.solve_Bd(column); // puts the result into m_core_solver.m_ed
string name = m_core_solver.column_name(column);
for (unsigned row = 0; row < nrows(); row ++) {
set_coeff(
m_A[row],
m_signs[row],
column,
m_core_solver.m_ed[row],
name);
m_rs[row] += m_core_solver.m_ed[row] * m_core_solver.m_x[column];
}
m_exact_column_norms.push_back(current_column_norm() + 1);
}
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: init_column_widths() {
for (unsigned i = 0; i < ncols(); i++) {
m_column_widths[i] = get_column_width(i);
}
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: adjust_width_with_low_bound(unsigned column, unsigned & w) {
if (!m_core_solver.low_bounds_are_set()) return;
w = std::max(w, (unsigned)T_to_string(m_core_solver.low_bound_value(column)).size());
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: adjust_width_with_upper_bound(unsigned column, unsigned & w) {
w = std::max(w, (unsigned)T_to_string(m_core_solver.upper_bound_value(column)).size());
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: adjust_width_with_bounds(unsigned column, unsigned & w) {
switch (m_core_solver.get_column_type(column)) {
case fixed:
case boxed:
adjust_width_with_low_bound(column, w);
adjust_width_with_upper_bound(column, w);
break;
case low_bound:
adjust_width_with_low_bound(column, w);
break;
case upper_bound:
adjust_width_with_upper_bound(column, w);
break;
case free_column:
break;
default:
lean_assert(false);
break;
}
}
template <typename T, typename X> unsigned core_solver_pretty_printer<T, X>:: get_column_width(unsigned column) {
unsigned w = std::max(m_costs[column].size(), T_to_string(m_core_solver.m_x[column]).size());
adjust_width_with_bounds(column, w);
adjust_width_with_basis_heading(column, w);
for (unsigned i = 0; i < nrows(); i++) {
unsigned cellw = m_A[i][column].size();
if (cellw > w) {
w = cellw;
}
}
w = std::max(w, (unsigned)T_to_string(m_exact_column_norms[column]).size());
w = std::max(w, (unsigned)T_to_string(m_core_solver.m_column_norms[column]).size());
return w;
}
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::regular_cell_string(unsigned row, unsigned column, std::string name) {
T t = fabs(m_core_solver.m_ed[row]);
if ( t == 1) return name;
return T_to_string(t) + name;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: set_coeff(vector<string>& row, vector<string> & row_signs, unsigned col, const T & t, string name) {
if (numeric_traits<T>::is_zero(t)) {
return;
}
if (col > 0) {
if (t > 0) {
row_signs[col] = "+";
row[col] = t != 1? T_to_string(t) + name : name;
} else {
row_signs[col] = "-";
row[col] = t != -1? T_to_string(-t) + name: name;
}
} else { // col == 0
if (t == -1) {
row[col] = "-" + name;
} else if (t == 1) {
row[col] = name;
} else {
row[col] = T_to_string(t) + name;
}
}
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_x() {
if (ncols() == 0) {
return;
}
int blanks = m_title_width + 1 - m_x_title.size();
std::cout << m_x_title;
print_blanks(blanks);
auto bh = m_core_solver.m_x;
for (unsigned i = 0; i < ncols(); i++) {
string s = T_to_string(bh[i]);
int blanks = m_column_widths[i] - s.size();
print_blanks(blanks);
std::cout << s << " "; // the column interval
}
std::cout << std::endl;
}
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_low_bound_string(unsigned j) {
switch (m_core_solver.get_column_type(j)){
case boxed:
case low_bound:
case fixed:
if (m_core_solver.low_bounds_are_set())
return T_to_string(m_core_solver.low_bound_value(j));
else
return std::string("0");
break;
default:
return std::string();
}
}
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_upp_bound_string(unsigned j) {
switch (m_core_solver.get_column_type(j)){
case boxed:
case upper_bound:
case fixed:
return T_to_string(m_core_solver.upper_bound_value(j));
break;
default:
return std::string();
}
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_lows() {
if (ncols() == 0) {
return;
}
int blanks = m_title_width + 1 - m_low_bounds_title.size();
std::cout << m_low_bounds_title;
print_blanks(blanks);
for (unsigned i = 0; i < ncols(); i++) {
string s = get_low_bound_string(i);
int blanks = m_column_widths[i] - s.size();
print_blanks(blanks);
std::cout << s << " "; // the column interval
}
std::cout << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_upps() {
if (ncols() == 0) {
return;
}
int blanks = m_title_width + 1 - m_upp_bounds_title.size();
std::cout << m_upp_bounds_title;
print_blanks(blanks);
for (unsigned i = 0; i < ncols(); i++) {
string s = get_upp_bound_string(i);
int blanks = m_column_widths[i] - s.size();
print_blanks(blanks);
std::cout << s << " "; // the column interval
}
std::cout << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_exact_norms() {
int blanks = m_title_width + 1 - m_exact_norm_title.size();
std::cout << m_exact_norm_title;
print_blanks(blanks);
for (unsigned i = 0; i < ncols(); i++) {
string s = get_exact_column_norm_string(i);
int blanks = m_column_widths[i] - s.size();
print_blanks(blanks);
std::cout << s << " ";
}
std::cout << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_approx_norms() {
int blanks = m_title_width + 1 - m_approx_norm_title.size();
std::cout << m_approx_norm_title;
print_blanks(blanks);
for (unsigned i = 0; i < ncols(); i++) {
string s = T_to_string(m_core_solver.m_column_norms[i]);
int blanks = m_column_widths[i] - s.size();
print_blanks(blanks);
std::cout << s << " ";
}
std::cout << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print() {
for (unsigned i = 0; i < nrows(); i++) {
print_row(i);
}
print_bottom_line();
print_cost();
print_x();
print_basis_heading();
print_lows();
print_upps();
print_exact_norms();
print_approx_norms();
std::cout << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_basis_heading() {
int blanks = m_title_width + 1 - m_basis_heading_title.size();
std::cout << m_basis_heading_title;
print_blanks(blanks);
if (ncols() == 0) {
return;
}
auto bh = m_core_solver.m_basis_heading;
for (unsigned i = 0; i < ncols(); i++) {
string s = T_to_string(bh[i]);
int blanks = m_column_widths[i] - s.size();
print_blanks(blanks);
std::cout << s << " "; // the column interval
}
std::cout << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_cost() {
int blanks = m_title_width + 1 - m_cost_title.size();
std::cout << m_cost_title;
print_blanks(blanks);
print_given_rows(m_costs, m_cost_signs, m_core_solver.get_cost());
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_given_rows(vector<string> & row, vector<string> & signs, X rst) {
for (unsigned col = 0; col < row.size(); col++) {
unsigned width = m_column_widths[col];
string s = row[col];
int number_of_blanks = width - s.size();
lean_assert(number_of_blanks >= 0);
print_blanks(number_of_blanks);
std::cout << s << ' ';
if (col < row.size() - 1) {
std::cout << signs[col + 1] << ' ';
}
}
std::cout << '=';
string rs = T_to_string(rst);
int nb = m_rs_width - rs.size();
lean_assert(nb >= 0);
print_blanks(nb + 1);
std::cout << rs << std::endl;
}
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_row(unsigned i){
print_blanks(m_title_width + 1);
auto row = m_A[i];
auto sign_row = m_signs[i];
auto rs = m_rs[i];
print_given_rows(row, sign_row, rs);
}
}

View file

@ -9,6 +9,7 @@
#include <string>
#include <algorithm>
#include <vector>
#include "util/lp/lp_settings.h"
namespace lean {
template <typename T, typename X> class lp_core_solver_base; // forward definition
@ -46,346 +47,70 @@ class core_solver_pretty_printer {
vector<T> m_exact_column_norms;
public:
core_solver_pretty_printer(lp_core_solver_base<T, X > & core_solver): m_core_solver(core_solver),
m_column_widths(core_solver.m_A.column_count(), 0),
m_A(core_solver.m_A.row_count(), vector<string>(core_solver.m_A.column_count(), "")),
m_signs(core_solver.m_A.row_count(), vector<string>(core_solver.m_A.column_count(), " ")),
m_costs(ncols(), ""),
m_cost_signs(ncols(), " "),
m_rs(ncols(), zero_of_type<X>()) {
m_w_buff = new T[m_core_solver.m_m];
m_ed_buff = new T[m_core_solver.m_m];
m_core_solver.save_state(m_w_buff, m_ed_buff);
init_m_A_and_signs();
init_costs();
init_column_widths();
init_rs_width();
m_cost_title = "costs";
m_basis_heading_title = "heading";
m_x_title = "x*";
m_title_width = std::max(std::max(m_cost_title.size(), std::max(m_basis_heading_title.size(), m_x_title.size())), m_approx_norm_title.size());
}
core_solver_pretty_printer(lp_core_solver_base<T, X > & core_solver);
void init_costs() {
vector<T> local_y(m_core_solver.m_m);
m_core_solver.solve_yB(local_y);
for (unsigned i = 0; i < ncols(); i++) {
if (m_core_solver.m_basis_heading[i] < 0) {
T t = m_core_solver.m_costs[i] - m_core_solver.m_A.dot_product_with_column(local_y, i);
set_coeff(m_costs, m_cost_signs, i, t, m_core_solver.column_name(i));
}
}
}
void init_costs();
~core_solver_pretty_printer() {
m_core_solver.restore_state(m_w_buff, m_ed_buff);
delete [] m_w_buff;
delete [] m_ed_buff;
}
void init_rs_width() {
m_rs_width = T_to_string(m_core_solver.get_cost()).size();
for (unsigned i = 0; i < nrows(); i++) {
auto wt = T_to_string(m_rs[i]).size();
if (wt > m_rs_width) {
m_rs_width = wt;
}
}
}
~core_solver_pretty_printer();
void init_rs_width();
T current_column_norm() {
T ret = zero_of_type<T>();
for (T & ed : m_core_solver.m_ed)
ret += ed * ed;
return ret;
}
T current_column_norm();
void init_m_A_and_signs() {
for (unsigned column = 0; column < ncols(); column++) {
m_core_solver.solve_Bd(column); // puts the result into m_core_solver.m_ed
string name = m_core_solver.column_name(column);
for (unsigned row = 0; row < nrows(); row ++) {
set_coeff(
m_A[row],
m_signs[row],
column,
m_core_solver.m_ed[row],
name);
m_rs[row] += m_core_solver.m_ed[row] * m_core_solver.m_x[column];
}
m_exact_column_norms.push_back(current_column_norm() + 1);
}
}
void init_m_A_and_signs();
void init_column_widths() {
for (unsigned i = 0; i < ncols(); i++) {
m_column_widths[i] = get_column_width(i);
}
}
void init_column_widths();
void 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());
}
void 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());
}
void adjust_width_with_low_bound(unsigned column, unsigned & w);
void adjust_width_with_upper_bound(unsigned column, unsigned & w);
void adjust_width_with_bounds(unsigned column, unsigned & w) {
switch (m_core_solver.get_column_type(column)) {
case fixed:
case boxed:
adjust_width_with_low_bound(column, w);
adjust_width_with_upper_bound(column, w);
break;
case low_bound:
adjust_width_with_low_bound(column, w);
break;
case upper_bound:
adjust_width_with_upper_bound(column, w);
break;
case free_column:
break;
default:
lean_assert(false);
break;
}
}
void adjust_width_with_bounds(unsigned column, unsigned & w);
void adjust_width_with_basis_heading(unsigned column, unsigned & w) {
w = std::max(w, (unsigned)T_to_string(m_core_solver.m_basis_heading[column]).size());
}
unsigned get_column_width(unsigned column) {
unsigned w = std::max(m_costs[column].size(), T_to_string(m_core_solver.m_x[column]).size());
adjust_width_with_bounds(column, w);
adjust_width_with_basis_heading(column, w);
for (unsigned i = 0; i < nrows(); i++) {
unsigned cellw = m_A[i][column].size();
if (cellw > w) {
w = cellw;
}
}
w = std::max(w, (unsigned)T_to_string(m_exact_column_norms[column]).size());
w = std::max(w, (unsigned)T_to_string(m_core_solver.m_column_norms[column]).size());
return w;
}
unsigned get_column_width(unsigned column);
unsigned regular_cell_width(unsigned row, unsigned column, std::string name) {
return regular_cell_string(row, column, name).size();
}
std::string regular_cell_string(unsigned row, unsigned column, std::string name) {
T t = fabs(m_core_solver.m_ed[row]);
if ( t == 1) return name;
return T_to_string(t) + name;
}
std::string regular_cell_string(unsigned row, unsigned column, std::string name);
void set_coeff(vector<string>& row, vector<string> & row_signs, unsigned col, const T & t, string name) {
if (numeric_traits<T>::is_zero(t)) {
return;
}
if (col > 0) {
if (t > 0) {
row_signs[col] = "+";
row[col] = t != 1? T_to_string(t) + name : name;
} else {
row_signs[col] = "-";
row[col] = t != -1? T_to_string(-t) + name: name;
}
} else { // col == 0
if (t == -1) {
row[col] = "-" + name;
} else if (t == 1) {
row[col] = name;
} else {
row[col] = T_to_string(t) + name;
}
}
}
void set_coeff(vector<string>& row, vector<string> & row_signs, unsigned col, const T & t, string name);
void print_x() {
if (ncols() == 0) {
return;
}
void print_x();
int blanks = m_title_width + 1 - m_x_title.size();
std::cout << m_x_title;
print_blanks(blanks);
std::string get_low_bound_string(unsigned j);
auto bh = m_core_solver.m_x;
for (unsigned i = 0; i < ncols(); i++) {
string s = T_to_string(bh[i]);
int blanks = m_column_widths[i] - s.size();
print_blanks(blanks);
std::cout << s << " "; // the column interval
}
std::cout << std::endl;
}
std::string get_low_bound_string(unsigned j) {
switch (m_core_solver.get_column_type(j)){
case boxed:
case low_bound:
case fixed:
if (m_core_solver.low_bounds_are_set())
return T_to_string(m_core_solver.low_bound_value(j));
else
return std::string("0");
break;
default:
return std::string();
}
}
std::string get_upp_bound_string(unsigned j) {
switch (m_core_solver.get_column_type(j)){
case boxed:
case upper_bound:
case fixed:
return T_to_string(m_core_solver.upper_bound_value(j));
break;
default:
return std::string();
}
}
std::string get_upp_bound_string(unsigned j);
void print_lows() {
if (ncols() == 0) {
return;
}
int blanks = m_title_width + 1 - m_low_bounds_title.size();
std::cout << m_low_bounds_title;
print_blanks(blanks);
void print_lows();
for (unsigned i = 0; i < ncols(); i++) {
string s = get_low_bound_string(i);
int blanks = m_column_widths[i] - s.size();
print_blanks(blanks);
std::cout << s << " "; // the column interval
}
std::cout << std::endl;
}
void print_upps() {
if (ncols() == 0) {
return;
}
int blanks = m_title_width + 1 - m_upp_bounds_title.size();
std::cout << m_upp_bounds_title;
print_blanks(blanks);
for (unsigned i = 0; i < ncols(); i++) {
string s = get_upp_bound_string(i);
int blanks = m_column_widths[i] - s.size();
print_blanks(blanks);
std::cout << s << " "; // the column interval
}
std::cout << std::endl;
}
void print_upps();
string get_exact_column_norm_string(unsigned col) {
return T_to_string(m_exact_column_norms[col]);
}
void print_exact_norms() {
int blanks = m_title_width + 1 - m_exact_norm_title.size();
std::cout << m_exact_norm_title;
print_blanks(blanks);
for (unsigned i = 0; i < ncols(); i++) {
string s = get_exact_column_norm_string(i);
int blanks = m_column_widths[i] - s.size();
print_blanks(blanks);
std::cout << s << " ";
}
std::cout << std::endl;
}
void print_exact_norms();
void print_approx_norms() {
int blanks = m_title_width + 1 - m_approx_norm_title.size();
std::cout << m_approx_norm_title;
print_blanks(blanks);
for (unsigned i = 0; i < ncols(); i++) {
string s = T_to_string(m_core_solver.m_column_norms[i]);
int blanks = m_column_widths[i] - s.size();
print_blanks(blanks);
std::cout << s << " ";
}
std::cout << std::endl;
}
void print_approx_norms();
void print() {
for (unsigned i = 0; i < nrows(); i++) {
print_row(i);
}
print_bottom_line();
print_cost();
print_x();
print_basis_heading();
print_lows();
print_upps();
print_exact_norms();
print_approx_norms();
std::cout << std::endl;
}
void print();
void print_basis_heading() {
int blanks = m_title_width + 1 - m_basis_heading_title.size();
std::cout << m_basis_heading_title;
print_blanks(blanks);
if (ncols() == 0) {
return;
}
auto bh = m_core_solver.m_basis_heading;
for (unsigned i = 0; i < ncols(); i++) {
string s = T_to_string(bh[i]);
int blanks = m_column_widths[i] - s.size();
print_blanks(blanks);
std::cout << s << " "; // the column interval
}
std::cout << std::endl;
}
void print_basis_heading();
void print_bottom_line() {
std::cout << "----------------------" << std::endl;
}
void print_cost() {
int blanks = m_title_width + 1 - m_cost_title.size();
std::cout << m_cost_title;
print_blanks(blanks);
print_given_rows(m_costs, m_cost_signs, m_core_solver.get_cost());
}
void print_cost();
void print_given_rows(vector<string> & row, vector<string> & signs, X rst) {
for (unsigned col = 0; col < row.size(); col++) {
unsigned width = m_column_widths[col];
string s = row[col];
int number_of_blanks = width - s.size();
lean_assert(number_of_blanks >= 0);
print_blanks(number_of_blanks);
std::cout << s << ' ';
if (col < row.size() - 1) {
std::cout << signs[col + 1] << ' ';
}
}
std::cout << '=';
void print_given_rows(vector<string> & row, vector<string> & signs, X rst);
string rs = T_to_string(rst);
int nb = m_rs_width - rs.size();
lean_assert(nb >= 0);
print_blanks(nb + 1);
std::cout << rs << std::endl;
}
void print_row(unsigned i){
print_blanks(m_title_width + 1);
auto row = m_A[i];
auto sign_row = m_signs[i];
auto rs = m_rs[i];
print_given_rows(row, sign_row, rs);
}
void print_row(unsigned i);
};
}

View file

@ -0,0 +1,17 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/numeric_pair.h"
#include "util/lp/core_solver_pretty_printer.cpp"
template lean::core_solver_pretty_printer<double, double>::core_solver_pretty_printer(lean::lp_core_solver_base<double, double> &);
template void lean::core_solver_pretty_printer<double, double>::print();
template lean::core_solver_pretty_printer<double, double>::~core_solver_pretty_printer();
template lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::core_solver_pretty_printer(lean::lp_core_solver_base<lean::mpq, lean::mpq> &);
template void lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::print();
template lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::~core_solver_pretty_printer();
template lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::core_solver_pretty_printer(lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> > &);
template lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::~core_solver_pretty_printer();
template void lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::print();

View file

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

View file

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

View file

@ -0,0 +1,14 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#ifdef LEAN_DEBUG
#include "util/lp/dense_matrix.cpp"
template lean::dense_matrix<double, double> lean::operator*<double, double>(lean::matrix<double, double>&, lean::matrix<double, double>&);
template void lean::dense_matrix<double, double>::apply_from_left(std::vector<double, std::allocator<double> >&);
template lean::dense_matrix<double, double>::dense_matrix(lean::matrix<double, double> const&);
template lean::dense_matrix<double, double>::dense_matrix(unsigned int, unsigned int);
template lean::dense_matrix<double, double>& lean::dense_matrix<double, double>::operator=(lean::dense_matrix<double, double> const&);
#endif

View file

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

View file

@ -7,6 +7,8 @@
#pragma once
#include <vector>
#include "util/lp/tail_matrix.h"
#include "util/lp/permutation_matrix.h"
namespace lean {
// This is the sum of a unit matrix and a one-column matrix
@ -17,9 +19,9 @@ class eta_matrix
unsigned m_length;
#endif
unsigned m_column_index;
public:
sparse_vector<T> m_column_vector;
T m_diagonal_element;
public:
#ifdef LEAN_DEBUG
eta_matrix(unsigned column_index, unsigned length):
#else
@ -49,42 +51,10 @@ public:
return m_diagonal_element;
}
void apply_from_left(std::vector<X> & w, lp_settings & ) {
auto w_at_column_index = w[m_column_index];
w[m_column_index] /= m_diagonal_element;
for (auto it = sparse_vector_iterator<T>(m_column_vector); !it.done(); it.move()) {
w[it.index()] += w_at_column_index * it.value();
}
}
void apply_from_left(std::vector<X> & w, lp_settings & );
template <typename L>
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
const L w_at_column_index = w[m_column_index];
if (is_zero(w_at_column_index)) return;
if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_column_index] /= m_diagonal_element)) {
w[m_column_index] = zero_of_type<L>();
w.erase_from_index(m_column_index);
}
for (auto it = sparse_vector_iterator<T>(m_column_vector); !it.done(); it.move()) {
unsigned i = it.index();
if (is_zero(w[i])) {
L v = w[i] = w_at_column_index * it.value();
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
w[i] = zero_of_type<L>();
continue;
}
w.m_index.push_back(i);
} else {
L v = w[i] += w_at_column_index * it.value(); // todo indexed_vector
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
w[i] = zero_of_type<L>();
w.erase_from_index(i);
}
}
}
}
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings);
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
apply_from_left_local(w, settings);
@ -96,60 +66,18 @@ public:
m_column_vector.push_back(row_index, val);
}
void apply_from_right(std::vector<T> & w) {
#ifdef LEAN_DEBUG
// dense_matrix<T, X> deb(*this);
// auto clone_w = clone_vector<T>(w, get_number_of_rows());
// deb.apply_from_right(clone_w);
#endif
T t = w[m_column_index] / m_diagonal_element;
for (auto it = sparse_vector_iterator<T>(m_column_vector); !it.done(); it.move()) {
t += w[it.index()] * it.value();
}
w[m_column_index] = t;
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w, get_number_of_rows()));
// delete clone_w;
#endif
}
void apply_from_right(std::vector<T> & w);
T get_elem(unsigned i, unsigned j) const {
if (j == m_column_index){
if (i == j) {
return 1 / m_diagonal_element;
}
return m_column_vector[i];
}
return i == j ? numeric_traits<T>::one() : numeric_traits<T>::zero();
}
T get_elem(unsigned i, unsigned j) const;
#ifdef LEAN_DEBUG
unsigned row_count() const { return m_length; }
unsigned column_count() const { return m_length; }
void set_number_of_rows(unsigned /*m*/) { }
void set_number_of_columns(unsigned /*n*/) { }
#endif
sparse_vector_iterator<T> get_sparse_vector_iterator() {
return sparse_vector_iterator<T>(m_column_vector);
}
void divide_by_diagonal_element() {
m_column_vector.divide(m_diagonal_element);
}
void conjugate_by_permutation(permutation_matrix<T, X> & p) {
// this = p * this * p(-1)
#ifdef LEAN_DEBUG
// auto rev = p.get_reverse();
// auto deb = ((*this) * rev);
// deb = p * deb;
#endif
m_column_index = p.get_rev(m_column_index);
for (auto & pair : m_column_vector.m_data) {
pair.first = p.get_rev(pair.first);
}
#ifdef LEAN_DEBUG
// lean_assert(deb == *this);
#endif
}
void conjugate_by_permutation(permutation_matrix<T, X> & p);
};
}

View file

@ -0,0 +1,25 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/numeric_pair.h"
#include "util/lp/eta_matrix.cpp"
#ifdef LEAN_DEBUG
template double lean::eta_matrix<double, double>::get_elem(unsigned int, unsigned int) const;
template lean::mpq lean::eta_matrix<lean::mpq, lean::mpq>::get_elem(unsigned int, unsigned int) const;
template lean::mpq lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::get_elem(unsigned int, unsigned int) const;
#endif
template void lean::eta_matrix<double, double>::apply_from_left(std::vector<double, std::allocator<double> >&, lean::lp_settings&);
template void lean::eta_matrix<double, double>::apply_from_right(std::vector<double, std::allocator<double> >&);
template void lean::eta_matrix<double, double>::conjugate_by_permutation(lean::permutation_matrix<double, double>&);
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_left(std::vector<lean::mpq, std::allocator<lean::mpq> >&, lean::lp_settings&);
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_right(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template void lean::eta_matrix<lean::mpq, lean::mpq>::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::mpq>&);
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left(std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, lean::lp_settings&);
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_right(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
template void lean::eta_matrix<double, double>::apply_from_left_local<double>(lean::indexed_vector<double>&, lean::lp_settings&);
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_left_local<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&);
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left_local<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&);

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

@ -0,0 +1,35 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE
Author: Lev Nachmanson
*/
#pragma once
#include <utility>
#include <functional>
#include "util/numerics/mpq.h"
namespace std {
template<>
struct hash<lean::mpq> {
inline size_t operator()(const lean::mpq & v) const {
return v.hash();
}
};
}
template <class T>
inline void hash_combine(std::size_t & seed, const T & v) {
std::hash<T> hasher;
seed ^= hasher(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;
}
};
}

View file

@ -46,6 +46,7 @@ public:
m_value = val;
}
};
#ifdef LEAN_DEBUG
template <typename X>
bool check_vector_for_small_values(indexed_vector<X> & w, lp_settings & settings) {
for (unsigned i : w.m_index) {
@ -55,4 +56,5 @@ bool check_vector_for_small_values(indexed_vector<X> & w, lp_settings & settings
}
return true;
}
#endif
}

View file

@ -0,0 +1,94 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/indexed_vector.h"
namespace lean {
template <typename T>
void print_vector(const std::vector<T> & t) {
for (unsigned i = 0; i < t.size(); i++)
std::cout << t[i] << " ";
std::cout << std::endl;
}
template <typename T>
void print_vector(const buffer<T> & t) {
for (unsigned i = 0; i < t.size(); i++)
std::cout << t[i] << " ";
std::cout << std::endl;
}
template <typename T>
void print_sparse_vector(const std::vector<T> & t) {
for (unsigned i = 0; i < t.size(); i++) {
if (is_zero(t[i]))continue;
std::cout << "[" << i << "] = " << t[i] << ", ";
}
std::cout << std::endl;
}
void print_vector(const std::vector<mpq> & t) {
for (unsigned i = 0; i < t.size(); i++)
std::cout << t[i].get_double() << std::setprecision(3) << " ";
std::cout << std::endl;
}
template <typename T>
void indexed_vector<T>::resize(unsigned data_size) {
m_index.clear();
m_data.resize(data_size, numeric_traits<T>::zero());
}
template <typename T>
void indexed_vector<T>::set_value(T value, unsigned index) {
m_data[index] = value;
m_index.push_back(index);
}
template <typename T>
void indexed_vector<T>::clear() {
for (unsigned i : m_index)
m_data[i] = numeric_traits<T>::zero();
m_index.clear();
}
template <typename T>
void indexed_vector<T>::clear_all() {
unsigned i = m_data.size();
while (i--) m_data[i] = numeric_traits<T>::zero();
m_index.clear();
}
template <typename T>
void indexed_vector<T>::erase_from_index(unsigned j) {
auto it = std::find(m_index.begin(), m_index.end(), j);
if (it != m_index.end()) m_index.erase(it);
}
#ifdef LEAN_DEBUG
template <typename T>
bool indexed_vector<T>::is_OK() const {
int size = 0;
for (unsigned i = 0; i < m_data.size(); i++) {
if (!is_zero(m_data[i])) {
if (std::find(m_index.begin(), m_index.end(), i) == m_index.end())
return false;
size++;
}
}
return size == m_index.size();
}
template <typename T>
void indexed_vector<T>::print() {
std::cout << "m_index " << std::endl;
for (unsigned i = 0; i < m_index.size(); i++) {
std::cout << m_index[i] << " ";
}
std::cout << std::endl;
print_vector(m_data);
}
#endif
}

View file

@ -20,42 +20,12 @@
#include "util/lp/sparse_vector.h"
#include <iomanip>
namespace lean {
template <typename T>
void print_vector(const T * t, unsigned l) {
for (unsigned i = 0; i < l; i++)
std::cout << t[i] << " ";
std::cout << std::endl;
}
template <typename T>
void print_vector(const std::vector<T> & t) {
for (unsigned i = 0; i < t.size(); i++)
std::cout << t[i] << " ";
std::cout << std::endl;
}
template <typename T>
void print_vector(const buffer<T> & t) {
for (unsigned i = 0; i < t.size(); i++)
std::cout << t[i] << " ";
std::cout << std::endl;
}
template <typename T>
void print_sparse_vector(const std::vector<T> & t) {
for (unsigned i = 0; i < t.size(); i++) {
if (is_zero(t[i]))continue;
std::cout << "[" << i << "] = " << t[i] << ", ";
}
std::cout << std::endl;
}
void print_vector(const std::vector<mpq> & t) {
for (unsigned i = 0; i < t.size(); i++)
std::cout << t[i].get_double() << std::setprecision(3) << " ";
std::cout << std::endl;
}
template <typename T> void print_vector(const std::vector<T> & t);
template <typename T> void print_vector(const buffer<T> & t);
template <typename T> void print_sparse_vector(const std::vector<T> & t);
void print_vector(const std::vector<mpq> & t);
template <typename T>
class indexed_vector {
public:
@ -66,11 +36,7 @@ public:
m_data.resize(data_size, numeric_traits<T>::zero());
}
void resize(unsigned data_size) {
m_index.clear();
m_data.resize(data_size, numeric_traits<T>::zero());
}
void resize(unsigned data_size);
unsigned data_size() const {
return m_data.size();
}
@ -79,25 +45,9 @@ public:
return m_index.size();
}
void set_value(T value, unsigned index) {
m_data[index] = value;
m_index.push_back(index);
}
void clear() {
for (unsigned i : m_index) {
m_data[i] = numeric_traits<T>::zero();
}
m_index.clear();
}
void clear_all() {
unsigned i = m_data.size();
while (i--) m_data[i] = numeric_traits<T>::zero();
m_index.clear();
}
void set_value(T value, unsigned index);
void clear();
void clear_all();
const T& operator[] (unsigned i) const {
return m_data[i];
}
@ -107,31 +57,10 @@ public:
}
void erase_from_index(unsigned j) {
auto it = std::find(m_index.begin(), m_index.end(), j);
if (it != m_index.end()) m_index.erase(it);
}
void erase_from_index(unsigned j);
#ifdef LEAN_DEBUG
bool is_OK() const {
int size = 0;
for (unsigned i = 0; i < m_data.size(); i++) {
if (!is_zero(m_data[i])) {
if (std::find(m_index.begin(), m_index.end(), i) == m_index.end())
return false;
size++;
}
}
return size == m_index.size();
}
void print() {
std::cout << "m_index " << std::endl;
for (unsigned i = 0; i < m_index.size(); i++) {
std::cout << m_index[i] << " ";
}
std::cout << std::endl;
print_vector(m_data);
}
bool is_OK() const;
void print();
#endif
};
}

View file

@ -0,0 +1,24 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/indexed_vector.cpp"
namespace lean {
template void indexed_vector<double>::clear();
template void indexed_vector<double>::clear_all();
template void indexed_vector<double>::erase_from_index(unsigned int);
template void indexed_vector<double>::set_value(double, unsigned int);
template void indexed_vector<float>::set_value(float, unsigned int);
template void indexed_vector<mpq>::clear();
template void indexed_vector<mpq>::clear_all();
template void indexed_vector<mpq>::erase_from_index(unsigned int);
template void indexed_vector<mpq>::resize(unsigned int);
template void indexed_vector<mpq>::set_value(mpq, unsigned int);
#ifdef LEAN_DEBUG
template bool indexed_vector<double>::is_OK() const;
template bool lean::indexed_vector<mpq>::is_OK() const;
#endif
}

View file

@ -0,0 +1,47 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lar_constraints.h"
namespace lean {
lar_constraint::lar_constraint(const buffer<std::pair<mpq, var_index>> & left_side, lconstraint_kind kind, mpq right_side, constraint_index index) : lar_base_constraint(kind, right_side, index) {
for (auto & it : left_side) {
auto r = m_left_side.find(it.second);
if (r == m_left_side.end()) {
m_left_side[it.second] = it.first;
} else {
r->second += it.first;
}
}
}
lar_constraint::lar_constraint(const lar_base_constraint & c): lar_base_constraint(c.m_kind, c.m_right_side, c.m_index) {
for (auto t : c.get_left_side_coefficients())
m_left_side.insert(std::make_pair(t.second, t.first));
}
buffer<std::pair<mpq, var_index>> lar_constraint::get_left_side_coefficients() const {
buffer<std::pair<mpq, var_index>> ret;
for (auto it : m_left_side) {
ret.push_back(std::make_pair(it.second, it.first));
}
return ret;
}
buffer<std::pair<mpq, var_index>> lar_normalized_constraint::get_left_side_coefficients() const {
buffer<std::pair<mpq, var_index>> ret;
for (auto t : m_canonic_left_side->m_coeffs) ret.push_back(t);
return ret;
}
}

View file

@ -16,11 +16,11 @@
#include <algorithm>
#include "util/lp/canonic_left_side.h"
namespace lean {
lconstraint_kind flip_kind(lconstraint_kind t) {
inline lconstraint_kind flip_kind(lconstraint_kind t) {
return static_cast<lconstraint_kind>( - static_cast<int>(t));
}
std::string lconstraint_kind_string(lconstraint_kind t) {
inline std::string lconstraint_kind_string(lconstraint_kind t) {
switch (t) {
case LE: return std::string("<=");
case LT: return std::string("<");
@ -35,7 +35,7 @@ class lar_base_constraint {
public:
lconstraint_kind m_kind;
mpq m_right_side;
virtual buffer<pair<mpq, var_index>> get_left_side_coefficients() const = 0;
virtual buffer<std::pair<mpq, var_index>> get_left_side_coefficients() const = 0;
constraint_index m_index; // the index of constraint
lar_base_constraint() {}
lar_base_constraint(lconstraint_kind kind, mpq right_side, constraint_index index) :m_kind(kind), m_right_side(right_side), m_index(index) {}
@ -48,33 +48,15 @@ class lar_constraint : public lar_base_constraint {
public:
std::unordered_map<var_index, mpq> m_left_side;
lar_constraint() {}
lar_constraint(const buffer<pair<mpq, var_index>> & left_side, lconstraint_kind kind, mpq right_side, constraint_index index) : lar_base_constraint(kind, right_side, index) {
for (auto & it : left_side) {
auto r = m_left_side.find(it.second);
if (r == m_left_side.end()) {
m_left_side[it.second] = it.first;
} else {
r->second += it.first;
}
}
}
lar_constraint(const buffer<std::pair<mpq, var_index>> & left_side, lconstraint_kind kind, mpq right_side, constraint_index index);
lar_constraint(const lar_base_constraint & c): lar_base_constraint(c.m_kind, c.m_right_side, c.m_index) {
for (auto t : c.get_left_side_coefficients())
m_left_side.insert(std::make_pair(t.second, t.first));
}
lar_constraint(const lar_base_constraint & c);
unsigned size() const {
return m_left_side.size();
}
buffer<pair<mpq, var_index>> get_left_side_coefficients() const {
buffer<pair<mpq, var_index>> ret;
for (auto it : m_left_side) {
ret.push_back(std::make_pair(it.second, it.first));
}
return ret;
}
buffer<std::pair<mpq, var_index>> get_left_side_coefficients() const;
};
class lar_normalized_constraint : public lar_base_constraint {
@ -91,12 +73,8 @@ public:
lar_normalized_constraint() {}
virtual buffer<pair<mpq, var_index>> get_left_side_coefficients() const {
buffer<pair<mpq, var_index>> ret;
for (auto t : m_canonic_left_side->m_coeffs) ret.push_back(t);
return ret;
}
virtual unsigned size() const {
buffer<std::pair<mpq, var_index>> get_left_side_coefficients() const;
unsigned size() const {
return m_canonic_left_side->size();
}
};

View file

@ -0,0 +1,818 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lar_core_solver.h"
namespace lean {
template <typename T, typename X>
lar_core_solver<T, X>::lar_core_solver(std::vector<X> & x, std::vector<column_type> & column_types,
std::vector<X> & low_bounds, std::vector<X> & upper_bounds,
std::vector<unsigned> & basis,
static_matrix<T, X> & A,
lp_settings & settings,
std::unordered_map<unsigned, std::string> & column_names,
std::vector<X> & right_side,
std::vector<T> & costs) : // right_side and costs are redundant
lp_core_solver_base<T, X>(A,
right_side,
basis,
x,
costs,
settings,
column_names,
column_types,
low_bounds,
upper_bounds) {
}
template <typename T, typename X> void lar_core_solver<T, X>::init_costs() {
lean_assert(this->m_x.size() >= this->m_n);
lean_assert(this->m_column_type.size() >= this->m_n);
X inf = m_infeasibility;
m_infeasibility = zero_of_type<X>();
for (unsigned j = this->m_n; j--;)
init_cost_for_column(j);
if (!(this->m_total_iterations ==0 || inf >= m_infeasibility)) {
std::cout << "inf was " << T_to_string(inf) << " and now " << T_to_string(m_infeasibility) << std::endl;
}
lean_assert(this->m_total_iterations ==0 || inf >= m_infeasibility);
if (inf == m_infeasibility)
this->m_iters_with_no_cost_growing++;
}
template <typename T, typename X> void lar_core_solver<T, X>::init_cost_for_column(unsigned j) {
// If j is a breakpoint column, then we set the cost zero.
// When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function
const X & x = this->m_x[j];
// set zero cost for each non-basis column
if (this->m_basis_heading[j] < 0) {
this->m_costs[j] = numeric_traits<T>::zero();
return;
}
// j is a basis column
switch (this->m_column_type[j]) {
case fixed:
case boxed:
if (x > this->m_upper_bound_values[j]) {
this->m_costs[j] = 1;
m_infeasibility += x - this->m_upper_bound_values[j];
} else if (x < this->m_low_bound_values[j]) {
m_infeasibility += this->m_low_bound_values[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_bound_values[j]) {
this->m_costs[j] = -1;
m_infeasibility += this->m_low_bound_values[j] - x;
} else {
this->m_costs[j] = numeric_traits<T>::zero();
}
break;
case upper_bound:
if (x > this->m_upper_bound_values[j]) {
this->m_costs[j] = 1;
m_infeasibility += x - this->m_upper_bound_values[j];
} else {
this->m_costs[j] = numeric_traits<T>::zero();
}
break;
case free_column:
this->m_costs[j] = numeric_traits<T>::zero();
break;
default:
lean_assert(false);
break;
}
}
template <typename T, typename X> void lar_core_solver<T, X>::init_local() {
this->m_start_time = get_millisecond_count();
this->m_m = this->m_A.row_count();
this->m_n = this->m_A.column_count();
this->m_pivot_row_of_B_1.resize(this->m_m);
this->m_pivot_row.resize(this->m_n);
this->m_b.resize(this->m_m);
this->m_y.resize(this->m_m);
this->m_w.resize(this->m_m);
this->m_d.resize(this->m_n);
this->m_ed.resize(this->m_m);
this->m_costs.resize(this->m_n);
this->m_breakpoint_indices_queue.resize(this->m_n);
this->m_copy_of_xB.resize(this->m_n);
}
// returns m_sign_of_alpha_r
template <typename T, typename X> int lar_core_solver<T, X>::column_is_out_of_bounds(unsigned j) {
switch (this->m_column_type[j]) {
case fixed:
case boxed:
if (this->x_below_low_bound(j)) {
return -1;
}
if (this->x_above_upper_bound(j)) {
return 1;
}
return 0;
case low_bound:
if (this->x_below_low_bound(j)) {
return -1;
}
return 0;
case upper_bound:
if (this->x_above_upper_bound(j)) {
return 1;
}
return 0;
default:
return 0;
break;
}
}
template <typename T, typename X> bool lar_core_solver<T, X>::can_enter_basis_mpq(unsigned j) {
switch (this->m_column_type[j]) {
case low_bound:
lean_assert(this->x_is_at_low_bound(j));
return this->m_d[j] < numeric_traits<T>::zero();
case upper_bound:
lean_assert(this->x_is_at_upper_bound(j));
return this->m_d[j] > numeric_traits<T>::zero();
case fixed:
return false;
case boxed:
{
bool low_bound = this->x_is_at_low_bound(j);
lean_assert(low_bound || this->x_is_at_upper_bound(j));
return (low_bound && this->m_d[j] < numeric_traits<T>::zero()) || ((!low_bound) && this->m_d[j] > numeric_traits<T>::zero());
}
case free_column:
return !numeric_traits<T>::is_zero(this->m_d[j]);
default:
return false;
}
return false;
}
template <typename T, typename X> void lar_core_solver<T, X>::calculate_pivot_row(unsigned i) {
this->calculate_pivot_row_of_B_1(i);
this->calculate_pivot_row_when_pivot_row_of_B1_is_ready();
}
template <typename T, typename X> X lar_core_solver<T, X>::get_deb_inf_column(unsigned j) {
const X & x = this->m_x[j];
switch (this->m_column_type[j]) {
case low_bound:
if (x < this->m_low_bound_values[j])
return this->m_low_bound_values[j] - x;
return zero_of_type<X>();
case upper_bound:
if (x > this->m_upper_bound_values[j])
return x - this->m_upper_bound_values[j];
return zero_of_type<X>();
case fixed:
case boxed:
{
if (x < this->m_low_bound_values[j])
return this->m_low_bound_values[j] - x;
if (x > this->m_upper_bound_values[j])
return x - this->m_upper_bound_values[j];
return zero_of_type<X>();
}
case free_column:
{
return zero_of_type<X>();
}
default:
lean_assert(false);
return zero_of_type<X>();
}
}
template <typename T, typename X> X lar_core_solver<T, X>::get_deb_inf() {
X ret = zero_of_type<X>();
for (unsigned j = 0; j < this->m_n; j++) {
X d = get_deb_inf_column(j);
// if (! numeric_traits<T>::is_zero(d)) {
// std::cout << "column " << j << ", " << this->column_name(j) << " inf is " << d.get_double() << std::endl;
// }
ret += d;
}
return ret;
}
template <typename T, typename X> bool lar_core_solver<T, X>::debug_profit_delta(unsigned j, const T & delta) {
this->update_x(j, delta);
bool ret = m_infeasibility > get_deb_inf();
if (ret) {
std::cout << "found profit for " << this->column_name(j) << " and delta = " << delta.get_double() << std::endl;
std::cout << "improvement = " << (m_infeasibility - get_deb_inf()).get_double() << std::endl;
}
return ret;
}
template <typename T, typename X> bool lar_core_solver<T, X>::debug_profit(unsigned j) {
if (this->m_column_type[j] == fixed) return false;
T delta = numeric_traits<T>::one() / 10000000;
delta /= 10000000;
return debug_profit_delta(j, -delta) || debug_profit_delta(j, delta);
}
template <typename T, typename X> int lar_core_solver<T, X>::choose_column_entering_basis() {
unsigned offset = my_random() % this->m_non_basic_columns.size();
unsigned initial_offset_in_non_basis = offset;
do {
unsigned j = this->m_non_basic_columns[offset];
if (can_enter_basis_mpq(j))
return j;
offset++;
if (offset == this->m_non_basic_columns.size()) offset = 0;
} while (offset != initial_offset_in_non_basis);
return -1;
}
template <typename T, typename X> void lar_core_solver<T, X>::one_iteration() {
this->m_total_iterations++;
lean_assert(this->m_non_basic_columns.size() + this->m_basis.size() == this->m_basis_heading.size());
if (is_zero(m_infeasibility)) {
this->m_status = OPTIMAL;
return;
}
int entering = choose_column_entering_basis();
if (entering == -1) {
std::cout << "cannot choose entering" << std::endl;
decide_on_status_when_cannot_enter();
} else {
advance_on_entering(entering);
}
}
template <typename T, typename X> void lar_core_solver<T, X>::decide_on_status_when_cannot_enter() {
if (!is_zero(m_infeasibility))
this->m_status = INFEASIBLE;
else
this->m_status = FEASIBLE;
std::cout << "status is " << lp_status_to_string(this->m_status) << std::endl;
}
// j is the basic column, x is the value at x[j]
// d is the coefficient before m_entering in the row with j as the basis column
template <typename T, typename X> void lar_core_solver<T, X>::try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value) {
X diff = x - break_value;
if (is_zero(diff)) {
switch (break_type) {
case low_break:
if (!same_sign_with_entering_delta(d))
return; // no breakpoint
break;
case upper_break:
if (same_sign_with_entering_delta(d))
return; // no breakpoint
break;
default: break;
}
add_breakpoint(j, zero_of_type<X>(), break_type);
return;
}
auto delta_j = diff / d;
if (same_sign_with_entering_delta(delta_j))
add_breakpoint(j, delta_j, break_type);
}
template <typename T, typename X> void lar_core_solver<T, X>::add_breakpoint(unsigned j, X delta, breakpoint_type type) {
m_breakpoints.push_back(breakpoint<X>(j, delta, type));
m_breakpoint_indices_queue.enqueue(m_breakpoint_indices_queue.size(), abs(delta));
}
template <typename T, typename X> void lar_core_solver<T, X>::try_add_breakpoint_in_row(unsigned i) {
lean_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_type[j]) {
case fixed:
try_add_breakpoint(j, x, d, fixed_break, this->m_low_bound_values[j]);
break;
case boxed:
try_add_breakpoint(j, x, d, low_break, this->m_low_bound_values[j]);
try_add_breakpoint(j, x, d, upper_break, this->m_upper_bound_values[j]);
break;
case low_bound:
try_add_breakpoint(j, x, d, low_break, this->m_low_bound_values[j]);
break;
case upper_bound:
try_add_breakpoint(j, x, d, upper_break, this->m_upper_bound_values[j]);
break;
case free_column:
break;
default:
lean_assert(false);
break;
}
}
template <typename T, typename X> std::string lar_core_solver<T, X>:: break_type_to_string(breakpoint_type type) {
switch (type){
case low_break: return "low_break";
case upper_break: return "upper_break";
case fixed_break: return "fixed_break";
default:
lean_assert(false);
break;
}
return "type is not found";
}
template <typename T, typename X> void lar_core_solver<T, X>::print_breakpoint(const breakpoint<X> * b) {
std::cout << "(" << this->column_name(b->m_j) << "," << break_type_to_string(b->m_type) << "," << T_to_string(b->m_delta) << ")" << std::endl;
print_bound_info_and_x(b->m_j);
}
template <typename T, typename X> void lar_core_solver<T, X>::print_bound_info_and_x(unsigned j) {
std::cout << "type of " << this->column_name(j) << " is " << column_type_to_string(this->m_column_type[j]) << std::endl;
std::cout << "x[" << this->column_name(j) << "] = " << this->m_x[j] << std::endl;
switch (this->m_column_type[j]) {
case fixed:
case boxed:
std::cout << "[" << this->m_low_bound_values[j] << "," << this->m_upper_bound_values[j] << "]" << std::endl;
break;
case low_bound:
std::cout << "[" << this->m_low_bound_values[j] << ", inf" << std::endl;
break;
case upper_bound:
std::cout << "inf ," << this->m_upper_bound_values[j] << "]" << std::endl;
break;
case free_column:
std::cout << "inf, inf" << std::endl;
break;
default:
lean_assert(false);
break;
}
}
template <typename T, typename X> void lar_core_solver<T, X>::clear_breakpoints() {
m_breakpoints.clear();
m_breakpoint_indices_queue.clear();
}
template <typename T, typename X> void lar_core_solver<T, X>::fill_breakpoints_array(unsigned entering) {
clear_breakpoints();
for (unsigned i = this->m_m; i--;)
try_add_breakpoint_in_row(i);
if (this->m_column_type[entering] == boxed) {
if (m_sign_of_entering_delta < 0)
add_breakpoint(entering, - this->bound_span(entering), low_break);
else
add_breakpoint(entering, this->bound_span(entering), upper_break);
}
}
template <typename T, typename X> void lar_core_solver<T, X>::advance_on_entering(unsigned entering) {
this->solve_Bd(entering); // prepares the entering column to be like the one of the tableau
m_sign_of_entering_delta = this->m_d[entering] < zero_of_type<T>() ? 1 : -1;
fill_breakpoints_array(entering);
advance_on_sorted_breakpoints(entering);
}
template <typename T, typename X> void lar_core_solver<T, X>::print_cost() {
std::cout << "reduced costs " << std::endl;
for (unsigned j = 0; j < this->m_n; j++) {
if (numeric_traits<T>::is_zero(this->m_d[j])) continue;
std::cout << T_to_string(this->m_d[j]) << this->column_name(j) << " ";
}
std::cout << std::endl;
}
template <typename T, typename X> void lar_core_solver<T, X>::update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta) {
if (entering != leaving)
this->update_basis_and_x(entering, leaving, delta);
else
this->update_x(entering, delta);
}
template <typename T, typename X> void lar_core_solver<T, X>::advance_on_sorted_breakpoints(unsigned entering) {
T slope_at_entering = this->m_d[entering];
breakpoint<X> * last_bp = nullptr;
while (m_breakpoint_indices_queue.is_empty() == false) {
unsigned bi = m_breakpoint_indices_queue.dequeue();
breakpoint<X> *b = &m_breakpoints[bi];
change_slope_on_breakpoint(entering, b, slope_at_entering);
last_bp = b;
if (slope_at_entering * m_sign_of_entering_delta > 0) { // the slope started to increase infeasibility
break;
} else {
if (numeric_traits<T>::is_zero(slope_at_entering) && my_random() % 2 == 0) {
// it is not cost benefitial to advance the delta more, so just break to increas the randomness
break;
}
}
}
update_basis_and_x_with_comparison(entering, last_bp->m_j, last_bp->m_delta);
}
template <typename T, typename X> void lar_core_solver<T, X>::change_slope_on_breakpoint(unsigned entering, breakpoint<X> * b, T & slope_at_entering) {
if (b->m_j == entering) {
lean_assert(b->m_type != fixed_break && (!is_zero(b->m_delta)));
slope_at_entering += m_sign_of_entering_delta;
return;
}
lean_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;
T delta = m_sign_of_entering_delta * abs(d);
switch (b->m_type) {
case fixed_break:
if (is_zero(b->m_delta)) {
slope_at_entering += delta;
} else {
slope_at_entering += 2 * delta;
}
break;
case low_break:
case upper_break:
slope_at_entering += delta;
break;
default:
lean_unreachable();
}
}
template <typename T, typename X> bool lar_core_solver<T, X>::row_is_infeasible(unsigned row, int & inf_sign) {
unsigned j = this->m_basis[row];
inf_sign = get_infeasibility_sign(j);
return inf_sign != 0;
}
template <typename T, typename X> bool lar_core_solver<T, X>::row_is_evidence(unsigned row, int & inf_sign) {
if (!row_is_infeasible(row, inf_sign)) return false;
calculate_pivot_row(row);
int entering = choose_entering_column_for_row_inf_strategy(inf_sign);
if (entering == -1) {
return true;
}
return false;
}
template <typename T, typename X> bool lar_core_solver<T, X>::find_evidence_row() {
for (unsigned i = this->m_m; --i;) {
int inf_sign;
if (row_is_evidence(i, inf_sign)) {
fill_evidence(i, inf_sign);
return true;
}
}
return false;
}
template <typename T, typename X> bool lar_core_solver<T, X>::done() {
if (this->m_status == OPTIMAL) return true;
if (this->m_status == INFEASIBLE) {
if (this->m_settings.row_feasibility == false) {
if (find_evidence_row()) {
std::cout << "found evidence" << std::endl;
} else {
std::cout << "did not find evidence" << std::endl;
std::cout << "started feasibility_loop at iteration " << this->m_total_iterations << std::endl;
unsigned iters = this->m_total_iterations;
this->m_status = FEASIBLE;
row_feasibility_loop();
std::cout << "made another " << this->m_total_iterations - iters << ", percentage is " << 100.0 * (this->m_total_iterations - iters) / this->m_total_iterations << std::endl;
}
}
return true;
}
if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) {
std::cout << "m_iters_with_no_cost_growing = " << this->m_iters_with_no_cost_growing << std::endl;
this->m_status = ITERATIONS_EXHAUSTED; return true;
}
if (this->m_total_iterations >= this->m_settings.max_total_number_of_iterations) {
std::cout << "max_total_number_of_iterations " << this->m_total_iterations << " is reached " << std::endl;
this->m_status = ITERATIONS_EXHAUSTED; return true;
}
return false;
}
template <typename T, typename X> void lar_core_solver<T, X>::move_as_many_as_possible_fixed_columns_to_non_basis() {
unsigned i = 0; // points to basis
auto& bs = this->m_basis;
unsigned j = 0; // points to m_column_types
auto & ct = this->m_column_type;
std::vector<int> heading(this->m_n, -1);
for (int i = 0; i < bs.size(); i ++) heading[bs[j]] = i;
lean_assert(this->m_basis.size() == this->m_m);
while (i < this->m_m && j < ct.size()) {
unsigned basis_j = bs[i];
if (ct[basis_j] != fixed) {i++; continue;}
do {
if (heading[j] == -numeric_traits<T>::one() && ct[j] != fixed)
break;
j++;
} while (j < ct.size());
if (j == ct.size()) break;
bs[i++] = j++;
}
}
template <typename T, typename X> bool lar_core_solver<T, X>::non_basis_columns_are_set_correctly() {
for (unsigned j : this->m_non_basic_columns) {
if (!this->x_is_at_bound(j))
return false;
}
return true;
}
template <typename T, typename X> void lar_core_solver<T, X>::prefix() {
init_local();
this->init();
this->init_basis_heading();
}
template <typename T, typename X> void lar_core_solver<T, X>::feasibility_loop() {
while (true) {
init_costs();
this->init_reduced_costs_for_one_iteration();
if (this->print_statistics_with_cost_and_check_that_the_time_is_over(this->m_total_iterations, m_infeasibility)){
this->m_status = lp_status::TIME_EXHAUSTED;
return; // this->m_total_iterations;
}
one_iteration();
if (done()) {
break;
}
}
}
template <typename T, typename X> unsigned lar_core_solver<T, X>::get_number_of_inf_rows() const {
unsigned r = 0;
for (unsigned k = this->m_m; --k;) {
unsigned j = this->m_basis[k];
if (get_infeasibility_sign(j)) r++;
}
return r;
}
template <typename T, typename X> void lar_core_solver<T, X>::row_feasibility_loop() {
while (true) {
if (this->print_statistics_with_iterations_and_check_that_the_time_is_over(this->m_total_iterations++)){
this->m_status = lp_status::TIME_EXHAUSTED;
return; // this->m_total_iterations;
}
int inf_sign;
int i = find_infeasible_row(inf_sign);
if (i == -1) {
this->m_status = OPTIMAL;
break;
}
advance_on_infeasible_row(i, inf_sign);
if (done())
break;
}
}
template <typename T, typename X> int lar_core_solver<T, X>::find_infeasible_row(int & inf_sign) {
unsigned offset = my_random() % this->m_m;
unsigned initial_offset_in_basis = offset;
do {
unsigned j = this->m_basis[offset];
inf_sign = get_infeasibility_sign(j);
if (inf_sign)
return offset;
if (++offset == this->m_m) offset = 0;
} while (offset != initial_offset_in_basis);
return -1;
}
template <typename T, typename X> int lar_core_solver<T, X>::get_infeasibility_sign(unsigned j) const {
const auto & x = this->m_x[j];
switch (this->m_column_type[j]) {
case fixed:
case boxed:
if (x < this->m_low_bound_values[j]) return 1;
if (x > this->m_upper_bound_values[j]) return -1;
return 0;
case low_bound:
return x < this->m_low_bound_values[j] ? 1 : 0;
case upper_bound:
return x > this->m_upper_bound_values[j]? -1 :0;
default:
return 0;
}
}
template <typename T, typename X> bool lar_core_solver<T, X>::improves_pivot_row_inf(unsigned j, int inf_sign) {
lean_assert(this->m_basis_heading[j] < 0);
// we have x[basis[i]] = sum (mj*x[j]), where mj = -m_pivot_row[j]
switch (this->m_column_type[j]) {
case fixed:
return false;
case boxed:
{
lean_assert(this->x_is_at_bound(j));
int j_sign = - get_sign(this->m_pivot_row[j]) * inf_sign;
if (this->x_is_at_low_bound(j))
return j_sign > 0;
return j_sign < 0;
}
case low_bound:
{
lean_assert(this->x_is_at_low_bound(j));
int j_sign = - get_sign(this->m_pivot_row[j]) * inf_sign;
return j_sign > 0;
}
case upper_bound:
{
lean_assert(this->x_is_at_upper_bound(j));
int j_sign = - get_sign(this->m_pivot_row[j]) * inf_sign;
return j_sign < 0;
}
break;
case free_column: {
return numeric_traits<T>::is_zero(this->m_pivot_row[j]) == false;
}
default:
lean_unreachable();
}
}
template <typename T, typename X> int lar_core_solver<T, X>::choose_entering_column_for_row_inf_strategy(int inf_sign) {
unsigned offset = my_random() % this->m_non_basic_columns.size();
unsigned initial_offset_in_non_basis = offset;
do {
unsigned j = this->m_non_basic_columns[offset];
if (improves_pivot_row_inf(j, inf_sign))
return j;
if (++offset == this->m_non_basic_columns.size()) offset = 0;
} while (offset != initial_offset_in_non_basis);
return -1;
}
template <typename T, typename X> void lar_core_solver<T, X>::fill_evidence(unsigned row, int inf_sign) {
m_infeasible_row_sign = inf_sign;
m_infeasible_row.push_back(std::make_pair(numeric_traits<T>::one(), this->m_basis[row]));
for (auto j : this->m_non_basic_columns) {
T aj = this->m_pivot_row[j];
if (!numeric_traits<T>::is_zero(aj)) {
m_infeasible_row.push_back(std::make_pair(aj, j));
}
}
}
template <typename T, typename X> void lar_core_solver<T, X>::update_delta_of_entering_and_leaving_candidates(X del, X & delta,
std::vector<unsigned> & leaving_candidates,
unsigned bj) {
if (del < delta) {
leaving_candidates.clear();
leaving_candidates.push_back(bj);
delta = del;
} else if (del == delta) {
leaving_candidates.push_back(bj);
}
}
template <typename T, typename X> void lar_core_solver<T, X>::update_delta_of_entering(int delta_sign, unsigned row, X & delta,
std::vector<unsigned> & leaving_candidates) {
unsigned bj = this->m_basis[row]; // bj - the basis column for the row
const T & ed = this->m_ed[row]; // this is the coefficent before x[entering] in the sum representing the basis column of this row taken with minus
if (numeric_traits<T>::is_zero(ed)) return;
const X & x = this->m_x[bj]; // the value of the basis column
// adjusted sign
int adj_sign = ed < zero_of_type<T>() ? delta_sign : - delta_sign;
switch (this->m_column_type[bj]) {
case fixed:
case boxed:
if (adj_sign > 0 && x <= this->m_upper_bound_values[bj])
update_delta_of_entering_and_leaving_candidates((this->m_upper_bound_values[bj] - x) / abs(ed), delta, leaving_candidates, bj);
else if (adj_sign < 0 && x >= this->m_low_bound_values[bj])
update_delta_of_entering_and_leaving_candidates((x - this->m_low_bound_values[bj]) / abs(ed), delta, leaving_candidates, bj);
break;
case low_bound:
if (adj_sign < 0 && x >= this->m_low_bound_values[bj])
update_delta_of_entering_and_leaving_candidates((x - this->m_low_bound_values[bj]) / abs(ed), delta, leaving_candidates, bj);
break;
case upper_bound:
if (adj_sign > 0 && x <= this->m_upper_bound_values[bj])
update_delta_of_entering_and_leaving_candidates((this->m_upper_bound_values[bj] - x) / abs(ed), delta, leaving_candidates, bj);
break;
default:
break;
}
}
template <typename T, typename X> X lar_core_solver<T, X>::find_initial_delta_and_its_sign(unsigned row, unsigned entering,
int inf_sign, int & entering_delta_sign,
std::vector<unsigned> & leaving_candidates) {
lean_assert(inf_sign != 0);
unsigned bj = this->m_basis[row]; // this is the infeasible basis column
const X & x = this->m_x[bj];
entering_delta_sign = - get_sign(this->m_pivot_row[entering]) * inf_sign;
lean_assert(entering_delta_sign != 0);
X delta = (inf_sign > 0? (this->m_low_bound_values[bj] - x) : (x - this->m_upper_bound_values[bj])) / abs(this->m_pivot_row[entering]);
if (this->m_column_type[entering] == boxed) {
X span = this->bound_span(entering);
if (span < delta) {
delta = span;
leaving_candidates.push_back(entering);
} else {
leaving_candidates.push_back(bj);
}
} else {
leaving_candidates.push_back(bj);
}
return delta;
}
template <typename T, typename X> void lar_core_solver<T, X>::advance_on_infeasible_row_and_entering(unsigned inf_row, unsigned entering, int inf_sign) {
this->solve_Bd(entering); // puts the tableau column of entering into this->m_ed
int entering_delta_sign;
std::vector<unsigned> leaving_candidates;
X delta = find_initial_delta_and_its_sign(inf_row, entering, inf_sign, entering_delta_sign, leaving_candidates);
lean_assert(leaving_candidates.size());
lean_assert(delta > zero_of_type<X>());
unsigned row = my_random() % this->m_m;
unsigned initial_row = row;
do {
if (row != inf_row)
update_delta_of_entering(entering_delta_sign, row, delta, leaving_candidates);
if (++row == this->m_m) row = 0;
} while (row != initial_row);
unsigned leaving = find_leaving_for_inf_row_strategy(leaving_candidates);
update_basis_and_x_with_comparison(entering, leaving, delta * entering_delta_sign);
}
template <typename T, typename X> void lar_core_solver<T, X>::advance_on_infeasible_row(unsigned i, int inf_sign) {
calculate_pivot_row(i);
int entering = choose_entering_column_for_row_inf_strategy(inf_sign);
if (entering == -1) {
fill_evidence(i, inf_sign);
this->m_status = INFEASIBLE;
return;
}
advance_on_infeasible_row_and_entering(i, entering, inf_sign);
}
template <typename T, typename X> void lar_core_solver<T, X>::solve() {
prefix();
if (is_empty()) {
this->m_status = OPTIMAL;
return;
}
this->snap_xN_to_bounds(); // we start with non-basic variables at their bounds
lean_assert(non_basis_columns_are_set_correctly());
if (this->m_settings.row_feasibility) {
std::cout << "optimizing by rows " << std::endl;
row_feasibility_loop();
} else {
std::cout << "optimizing total infeasibility" << std::endl;
feasibility_loop();
}
}
template <typename T, typename X> void lar_core_solver<T, X>::print_column_info(unsigned j) {
std::cout << "type = " << column_type_to_string(this->m_column_type[j]) << std::endl;
switch (this->m_column_type[j]) {
case fixed:
case boxed:
std::cout << "(" << this->m_low_bound_values[j] << ", " << this->m_upper_bound_values[j] << ")" << std::endl;
break;
case low_bound:
std::cout << this->m_low_bound_values[j] << std::endl;
break;
case upper_bound:
std::cout << this->m_upper_bound_values[j] << std::endl;
break;
default:
lean_unreachable();
}
}
}

View file

@ -34,263 +34,44 @@ public:
lp_settings & settings,
std::unordered_map<unsigned, std::string> & column_names,
std::vector<X> & right_side,
std::vector<T> & costs) : // right_side and costs are redundant
lp_core_solver_base<T, X>(A,
right_side,
basis,
x,
costs,
settings,
column_names,
column_types,
low_bounds,
upper_bounds) {
}
std::vector<T> & costs);
int get_infeasible_row_sign() const {
return m_infeasible_row_sign;
}
int get_infeasible_row_sign() const { return m_infeasible_row_sign; }
const std::vector<pair<mpq, unsigned>> & get_infeasibility_info(int & inf_sign) const {
inf_sign = m_infeasible_row_sign;
return m_infeasible_row;
}
void init_costs() {
lean_assert(this->m_x.size() >= this->m_n);
lean_assert(this->m_column_type.size() >= this->m_n);
X inf = m_infeasibility;
m_infeasibility = zero_of_type<X>();
for (unsigned j = this->m_n; j--;)
init_cost_for_column(j);
if (!(this->m_total_iterations ==0 || inf >= m_infeasibility)) {
std::cout << "inf was " << T_to_string(inf) << " and now " << T_to_string(m_infeasibility) << std::endl;
}
lean_assert(this->m_total_iterations ==0 || inf >= m_infeasibility);
if (inf == m_infeasibility)
this->m_iters_with_no_cost_growing++;
}
void init_costs();
void init_cost_for_column(unsigned j) {
// If j is a breakpoint column, then we set the cost zero.
// When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function
const X & x = this->m_x[j];
// set zero cost for each non-basis column
if (this->m_basis_heading[j] < 0) {
this->m_costs[j] = numeric_traits<T>::zero();
return;
}
// j is a basis column
switch (this->m_column_type[j]) {
case fixed:
case boxed:
if (x > this->m_upper_bound_values[j]) {
this->m_costs[j] = 1;
m_infeasibility += x - this->m_upper_bound_values[j];
} else if (x < this->m_low_bound_values[j]) {
m_infeasibility += this->m_low_bound_values[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_bound_values[j]) {
this->m_costs[j] = -1;
m_infeasibility += this->m_low_bound_values[j] - x;
} else {
this->m_costs[j] = numeric_traits<T>::zero();
}
break;
case upper_bound:
if (x > this->m_upper_bound_values[j]) {
this->m_costs[j] = 1;
m_infeasibility += x - this->m_upper_bound_values[j];
} else {
this->m_costs[j] = numeric_traits<T>::zero();
}
break;
case free_column:
this->m_costs[j] = numeric_traits<T>::zero();
break;
default:
lean_assert(false);
break;
}
}
void init_local() {
this->m_start_time = get_millisecond_count();
this->m_m = this->m_A.row_count();
this->m_n = this->m_A.column_count();
this->m_pivot_row_of_B_1.resize(this->m_m);
this->m_pivot_row.resize(this->m_n);
this->m_b.resize(this->m_m);
this->m_y.resize(this->m_m);
this->m_w.resize(this->m_m);
this->m_d.resize(this->m_n);
this->m_ed.resize(this->m_m);
this->m_costs.resize(this->m_n);
this->m_breakpoint_indices_queue.resize(this->m_n);
this->m_copy_of_xB.resize(this->m_n);
}
void init_cost_for_column(unsigned j);
void init_local();
// returns m_sign_of_alpha_r
int column_is_out_of_bounds(unsigned j) {
switch (this->m_column_type[j]) {
case fixed:
case boxed:
if (this->x_below_low_bound(j)) {
return -1;
}
if (this->x_above_upper_bound(j)) {
return 1;
}
return 0;
case low_bound:
if (this->x_below_low_bound(j)) {
return -1;
}
return 0;
case upper_bound:
if (this->x_above_upper_bound(j)) {
return 1;
}
return 0;
default:
return 0;
break;
}
}
int column_is_out_of_bounds(unsigned j);
bool can_enter_basis_mpq(unsigned j) {
switch (this->m_column_type[j]) {
case low_bound:
lean_assert(this->x_is_at_low_bound(j));
return this->m_d[j] < numeric_traits<T>::zero();
case upper_bound:
lean_assert(this->x_is_at_upper_bound(j));
return this->m_d[j] > numeric_traits<T>::zero();
case fixed:
return false;
case boxed:
{
bool low_bound = this->x_is_at_low_bound(j);
lean_assert(low_bound || this->x_is_at_upper_bound(j));
return (low_bound && this->m_d[j] < numeric_traits<T>::zero()) || ((!low_bound) && this->m_d[j] > numeric_traits<T>::zero());
}
case free_column:
return !numeric_traits<T>::is_zero(this->m_d[j]);
default:
return false;
}
return false;
}
bool can_enter_basis_mpq(unsigned j);
void calculate_pivot_row(unsigned i) {
this->calculate_pivot_row_of_B_1(i);
this->calculate_pivot_row_when_pivot_row_of_B1_is_ready();
}
void calculate_pivot_row(unsigned i);
X get_deb_inf_column(unsigned j) {
const X & x = this->m_x[j];
switch (this->m_column_type[j]) {
case low_bound:
if (x < this->m_low_bound_values[j])
return this->m_low_bound_values[j] - x;
return zero_of_type<X>();
case upper_bound:
if (x > this->m_upper_bound_values[j])
return x - this->m_upper_bound_values[j];
return zero_of_type<X>();
case fixed:
case boxed:
{
if (x < this->m_low_bound_values[j])
return this->m_low_bound_values[j] - x;
if (x > this->m_upper_bound_values[j])
return x - this->m_upper_bound_values[j];
return zero_of_type<X>();
}
case free_column:
{
return zero_of_type<X>();
}
default:
lean_assert(false);
return zero_of_type<X>();
}
}
X get_deb_inf_column(unsigned j);
X get_deb_inf() {
X ret = zero_of_type<X>();
for (unsigned j = 0; j < this->m_n; j++) {
X d = get_deb_inf_column(j);
// if (! numeric_traits<T>::is_zero(d)) {
// std::cout << "column " << j << ", " << this->column_name(j) << " inf is " << d.get_double() << std::endl;
// }
ret += d;
}
return ret;
}
X get_deb_inf();
bool debug_profit_delta(unsigned j, const T & delta) {
this->update_x(j, delta);
bool ret = m_infeasibility > get_deb_inf();
if (ret) {
std::cout << "found profit for " << this->column_name(j) << " and delta = " << delta.get_double() << std::endl;
std::cout << "improvement = " << (m_infeasibility - get_deb_inf()).get_double() << std::endl;
}
return ret;
}
bool debug_profit_delta(unsigned j, const T & delta);
bool debug_profit(unsigned j) {
if (this->m_column_type[j] == fixed) return false;
T delta = numeric_traits<T>::one() / 10000000;
delta /= 10000000;
return debug_profit_delta(j, -delta) || debug_profit_delta(j, delta);
}
bool debug_profit(unsigned j);
int choose_column_entering_basis() {
unsigned offset = lrand48() % this->m_non_basic_columns.size();
unsigned initial_offset_in_non_basis = offset;
do {
unsigned j = this->m_non_basic_columns[offset];
if (can_enter_basis_mpq(j))
return j;
offset++;
if (offset == this->m_non_basic_columns.size()) offset = 0;
} while (offset != initial_offset_in_non_basis);
return -1;
}
int choose_column_entering_basis();
void one_iteration() {
this->m_total_iterations++;
lean_assert(this->m_non_basic_columns.size() + this->m_basis.size() == this->m_basis_heading.size());
if (is_zero(m_infeasibility)) {
this->m_status = OPTIMAL;
return;
}
int entering = choose_column_entering_basis();
if (entering == -1) {
std::cout << "cannot choose entering" << std::endl;
decide_on_status_when_cannot_enter();
} else {
advance_on_entering(entering);
}
}
void one_iteration();
void decide_on_status_when_cannot_enter() {
if (!is_zero(m_infeasibility))
this->m_status = INFEASIBLE;
else
this->m_status = FEASIBLE;
std::cout << "status is " << lp_status_to_string(this->m_status) << std::endl;
}
void decide_on_status_when_cannot_enter();
template <typename L>
bool same_sign_with_entering_delta(const L & a) {
return (a > zero_of_type<L>() && m_sign_of_entering_delta > 0) || (a < zero_of_type<L>() && m_sign_of_entering_delta < 0);
@ -298,366 +79,60 @@ public:
// j is the basic column, x is the value at x[j]
// d is the coefficient before m_entering in the row with j as the basis column
void try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value) {
X diff = x - break_value;
if (is_zero(diff)) {
switch (break_type) {
case low_break:
if (!same_sign_with_entering_delta(d))
return; // no breakpoint
break;
case upper_break:
if (same_sign_with_entering_delta(d))
return; // no breakpoint
break;
default: break;
}
add_breakpoint(j, zero_of_type<X>(), break_type);
return;
}
auto delta_j = diff / d;
if (same_sign_with_entering_delta(delta_j))
add_breakpoint(j, delta_j, break_type);
}
void try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value);
void add_breakpoint(unsigned j, X delta, breakpoint_type type);
void try_add_breakpoint_in_row(unsigned i);
std::string break_type_to_string(breakpoint_type type);
void print_breakpoint(const breakpoint<X> * b);
void print_bound_info_and_x(unsigned j);
void clear_breakpoints();
void fill_breakpoints_array(unsigned entering);
void advance_on_entering(unsigned entering);
void print_cost();
void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta);
void advance_on_sorted_breakpoints(unsigned entering);
void change_slope_on_breakpoint(unsigned entering, breakpoint<X> * b, T & slope_at_entering);
bool row_is_infeasible(unsigned row, int & inf_sign);
bool row_is_evidence(unsigned row, int & inf_sign);
bool find_evidence_row();
void add_breakpoint(unsigned j, X delta, breakpoint_type type) {
m_breakpoints.push_back(breakpoint<X>(j, delta, type));
m_breakpoint_indices_queue.enqueue(m_breakpoint_indices_queue.size(), abs(delta));
}
bool done();
void try_add_breakpoint_in_row(unsigned i) {
lean_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_type[j]) {
case fixed:
try_add_breakpoint(j, x, d, fixed_break, this->m_low_bound_values[j]);
break;
case boxed:
try_add_breakpoint(j, x, d, low_break, this->m_low_bound_values[j]);
try_add_breakpoint(j, x, d, upper_break, this->m_upper_bound_values[j]);
break;
case low_bound:
try_add_breakpoint(j, x, d, low_break, this->m_low_bound_values[j]);
break;
case upper_bound:
try_add_breakpoint(j, x, d, upper_break, this->m_upper_bound_values[j]);
break;
case free_column:
break;
default:
lean_assert(false);
break;
}
}
void move_as_many_as_possible_fixed_columns_to_non_basis();
std::string break_type_to_string(breakpoint_type type) {
switch (type){
case low_break: return "low_break";
case upper_break: return "upper_break";
case fixed_break: return "fixed_break";
default:
lean_assert(false);
break;
}
return "type is not found";
}
bool non_basis_columns_are_set_correctly();
void print_breakpoint(const breakpoint<X> * b) {
std::cout << "(" << this->column_name(b->m_j) << "," << break_type_to_string(b->m_type) << "," << T_to_string(b->m_delta) << ")" << std::endl;
print_bound_info_and_x(b->m_j);
}
void prefix();
void print_bound_info_and_x(unsigned j) {
std::cout << "type of " << this->column_name(j) << " is " << column_type_to_string(this->m_column_type[j]) << std::endl;
std::cout << "x[" << this->column_name(j) << "] = " << this->m_x[j] << std::endl;
switch (this->m_column_type[j]) {
case fixed:
case boxed:
std::cout << "[" << this->m_low_bound_values[j] << "," << this->m_upper_bound_values[j] << "]" << std::endl;
break;
case low_bound:
std::cout << "[" << this->m_low_bound_values[j] << ", inf" << std::endl;
break;
case upper_bound:
std::cout << "inf ," << this->m_upper_bound_values[j] << "]" << std::endl;
break;
case free_column:
std::cout << "inf, inf" << std::endl;
break;
default:
lean_assert(false);
break;
}
}
bool is_tiny() const { return this->m_m < 10 && this->m_n < 20; }
bool is_empty() const { return this->m_m == 0 || this->m_n == 0; }
void clear_breakpoints() {
m_breakpoints.clear();
m_breakpoint_indices_queue.clear();
}
void feasibility_loop();
void fill_breakpoints_array(unsigned entering) {
clear_breakpoints();
for (unsigned i = this->m_m; i--;)
try_add_breakpoint_in_row(i);
if (this->m_column_type[entering] == boxed) {
if (m_sign_of_entering_delta < 0)
add_breakpoint(entering, - this->bound_span(entering), low_break);
else
add_breakpoint(entering, this->bound_span(entering), upper_break);
}
}
void advance_on_entering(unsigned entering) {
this->solve_Bd(entering); // prepares the entering column to be like the one of the tableau
m_sign_of_entering_delta = this->m_d[entering] < zero_of_type<T>() ? 1 : -1;
fill_breakpoints_array(entering);
advance_on_sorted_breakpoints(entering);
}
void print_cost() {
std::cout << "reduced costs " << std::endl;
for (unsigned j = 0; j < this->m_n; j++) {
if (numeric_traits<T>::is_zero(this->m_d[j])) continue;
std::cout << T_to_string(this->m_d[j]) << this->column_name(j) << " ";
}
std::cout << std::endl;
}
void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta) {
if (entering != leaving)
this->update_basis_and_x(entering, leaving, delta);
else
this->update_x(entering, delta);
}
void advance_on_sorted_breakpoints(unsigned entering) {
T slope_at_entering = this->m_d[entering];
breakpoint<X> * last_bp = nullptr;
while (m_breakpoint_indices_queue.is_empty() == false) {
unsigned bi = m_breakpoint_indices_queue.dequeue();
breakpoint<X> *b = &m_breakpoints[bi];
change_slope_on_breakpoint(entering, b, slope_at_entering);
last_bp = b;
if (slope_at_entering * m_sign_of_entering_delta > 0) { // the slope started to increase infeasibility
break;
} else {
if (numeric_traits<T>::is_zero(slope_at_entering) && lrand48() % 2 == 0) {
// it is not cost benefitial to advance the delta more, so just break to increas the randomness
break;
}
}
}
update_basis_and_x_with_comparison(entering, last_bp->m_j, last_bp->m_delta);
}
void change_slope_on_breakpoint(unsigned entering, breakpoint<X> * b, T & slope_at_entering) {
if (b->m_j == entering) {
lean_assert(b->m_type != fixed_break && (!is_zero(b->m_delta)));
slope_at_entering += m_sign_of_entering_delta;
return;
}
lean_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;
T delta = m_sign_of_entering_delta * abs(d);
switch (b->m_type) {
case fixed_break:
if (is_zero(b->m_delta)) {
slope_at_entering += delta;
} else {
slope_at_entering += 2 * delta;
}
break;
case low_break:
case upper_break:
slope_at_entering += delta;
break;
default:
lean_unreachable();
}
}
bool row_is_infeasible(unsigned row, int & inf_sign) {
unsigned j = this->m_basis[row];
inf_sign = get_infeasibility_sign(j);
return inf_sign != 0;
}
bool row_is_evidence(unsigned row, int & inf_sign) {
if (!row_is_infeasible(row, inf_sign)) return false;
calculate_pivot_row(row);
int entering = choose_entering_column_for_row_inf_strategy(inf_sign);
if (entering == -1) {
return true;
}
return false;
}
bool find_evidence_row() {
for (unsigned i = this->m_m; --i;) {
int inf_sign;
if (row_is_evidence(i, inf_sign)) {
fill_evidence(i, inf_sign);
return true;
}
}
return false;
}
unsigned get_number_of_inf_rows() const;
bool done() {
if (this->m_status == OPTIMAL) return true;
if (this->m_status == INFEASIBLE) {
if (this->m_settings.row_feasibility == false) {
if (find_evidence_row()) {
std::cout << "found evidence" << std::endl;
} else {
std::cout << "did not find evidence" << std::endl;
std::cout << "started feasibility_loop at iteration " << this->m_total_iterations << std::endl;
unsigned iters = this->m_total_iterations;
this->m_status = FEASIBLE;
row_feasibility_loop();
std::cout << "made another " << this->m_total_iterations - iters << ", percentage is " << 100.0 * (this->m_total_iterations - iters) / this->m_total_iterations << std::endl;
}
}
return true;
}
void row_feasibility_loop();
if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) {
std::cout << "m_iters_with_no_cost_growing = " << this->m_iters_with_no_cost_growing << std::endl;
this->m_status = ITERATIONS_EXHAUSTED; return true;
}
if (this->m_total_iterations >= this->m_settings.max_total_number_of_iterations) {
std::cout << "max_total_number_of_iterations " << this->m_total_iterations << " is reached " << std::endl;
this->m_status = ITERATIONS_EXHAUSTED; return true;
}
return false;
}
int find_infeasible_row(int & inf_sign);
void move_as_many_as_possible_fixed_columns_to_non_basis() {
unsigned i = 0; // points to basis
auto& bs = this->m_basis;
unsigned j = 0; // points to m_column_types
auto & ct = this->m_column_type;
std::vector<int> heading(this->m_n, -1);
for (int i = 0; i < bs.size(); i ++) heading[bs[j]] = i;
lean_assert(this->m_basis.size() == this->m_m);
while (i < this->m_m && j < ct.size()) {
unsigned basis_j = bs[i];
if (ct[basis_j] != fixed) {i++; continue;}
do {
if (heading[j] == -numeric_traits<T>::one() && ct[j] != fixed)
break;
j++;
} while (j < ct.size());
if (j == ct.size()) break;
bs[i++] = j++;
}
}
bool non_basis_columns_are_set_correctly() {
for (unsigned j : this->m_non_basic_columns) {
if (!this->x_is_at_bound(j))
return false;
}
return true;
}
void prefix() {
init_local();
this->init();
this->init_basis_heading();
}
bool is_tiny() const {
return this->m_m < 10 && this->m_n < 20;
}
bool is_empty() const {
return this->m_m == 0 || this->m_n == 0;
}
void feasibility_loop() {
while (true) {
init_costs();
this->init_reduced_costs_for_one_iteration();
if (this->print_statistics_with_cost_and_check_that_the_time_is_over(this->m_total_iterations, m_infeasibility)){
this->m_status = lp_status::TIME_EXHAUSTED;
return; // this->m_total_iterations;
}
one_iteration();
if (done()) {
break;
}
}
}
unsigned get_number_of_inf_rows() const {
unsigned r = 0;
for (unsigned k = this->m_m; --k;) {
unsigned j = this->m_basis[k];
if (get_infeasibility_sign(j)) r++;
}
return r;
}
void row_feasibility_loop() {
while (true) {
if (this->print_statistics_with_iterations_and_check_that_the_time_is_over(this->m_total_iterations++)){
this->m_status = lp_status::TIME_EXHAUSTED;
return; // this->m_total_iterations;
}
int inf_sign;
int i = find_infeasible_row(inf_sign);
if (i == -1) {
this->m_status = OPTIMAL;
break;
}
advance_on_infeasible_row(i, inf_sign);
if (done())
break;
}
}
int find_infeasible_row(int & inf_sign) {
unsigned offset = lrand48() % this->m_m;
unsigned initial_offset_in_basis = offset;
do {
unsigned j = this->m_basis[offset];
inf_sign = get_infeasibility_sign(j);
if (inf_sign)
return offset;
if (++offset == this->m_m) offset = 0;
} while (offset != initial_offset_in_basis);
return -1;
}
int get_infeasibility_sign(unsigned j) const {
const auto & x = this->m_x[j];
switch (this->m_column_type[j]) {
case fixed:
case boxed:
if (x < this->m_low_bound_values[j]) return 1;
if (x > this->m_upper_bound_values[j]) return -1;
return 0;
case low_bound:
return x < this->m_low_bound_values[j] ? 1 : 0;
case upper_bound:
return x > this->m_upper_bound_values[j]? -1 :0;
default:
return 0;
}
}
int get_infeasibility_sign(unsigned j) const;
template <typename L>
@ -665,202 +140,37 @@ public:
return v > zero_of_type<L>() ? 1 : (v < zero_of_type<L>() ? -1 : 0);
}
bool improves_pivot_row_inf(unsigned j, int inf_sign) {
lean_assert(this->m_basis_heading[j] < 0);
// we have x[basis[i]] = sum (mj*x[j]), where mj = -m_pivot_row[j]
bool improves_pivot_row_inf(unsigned j, int inf_sign);
switch (this->m_column_type[j]) {
case fixed:
return false;
case boxed:
{
lean_assert(this->x_is_at_bound(j));
int j_sign = - get_sign(this->m_pivot_row[j]) * inf_sign;
if (this->x_is_at_low_bound(j))
return j_sign > 0;
return j_sign < 0;
}
case low_bound:
{
lean_assert(this->x_is_at_low_bound(j));
int j_sign = - get_sign(this->m_pivot_row[j]) * inf_sign;
return j_sign > 0;
}
case upper_bound:
{
lean_assert(this->x_is_at_upper_bound(j));
int j_sign = - get_sign(this->m_pivot_row[j]) * inf_sign;
return j_sign < 0;
}
break;
case free_column: {
return numeric_traits<T>::is_zero(this->m_pivot_row[j]) == false;
}
default:
lean_unreachable();
}
}
int choose_entering_column_for_row_inf_strategy(int inf_sign);
int choose_entering_column_for_row_inf_strategy(int inf_sign) {
unsigned offset = lrand48() % this->m_non_basic_columns.size();
unsigned initial_offset_in_non_basis = offset;
do {
unsigned j = this->m_non_basic_columns[offset];
if (improves_pivot_row_inf(j, inf_sign))
return j;
if (++offset == this->m_non_basic_columns.size()) offset = 0;
} while (offset != initial_offset_in_non_basis);
return -1;
}
void fill_evidence(unsigned row, int inf_sign) {
m_infeasible_row_sign = inf_sign;
m_infeasible_row.push_back(std::make_pair(numeric_traits<T>::one(), this->m_basis[row]));
for (auto j : this->m_non_basic_columns) {
T aj = this->m_pivot_row[j];
if (!numeric_traits<T>::is_zero(aj)) {
m_infeasible_row.push_back(std::make_pair(aj, j));
}
}
}
void fill_evidence(unsigned row, int inf_sign);
void update_delta_of_entering_and_leaving_candidates(X del, X & delta,
std::vector<unsigned> & leaving_candidates,
unsigned bj) {
if (del < delta) {
leaving_candidates.clear();
leaving_candidates.push_back(bj);
delta = del;
} else if (del == delta) {
leaving_candidates.push_back(bj);
}
}
unsigned bj);
void update_delta_of_entering(int delta_sign, unsigned row, X & delta,
std::vector<unsigned> & leaving_candidates) {
unsigned bj = this->m_basis[row]; // bj - the basis column for the row
const T & ed = this->m_ed[row]; // this is the coefficent before x[entering] in the sum representing the basis column of this row taken with minus
if (numeric_traits<T>::is_zero(ed)) return;
const X & x = this->m_x[bj]; // the value of the basis column
// adjusted sign
int adj_sign = ed < zero_of_type<T>() ? delta_sign : - delta_sign;
switch (this->m_column_type[bj]) {
case fixed:
case boxed:
if (adj_sign > 0 && x <= this->m_upper_bound_values[bj])
update_delta_of_entering_and_leaving_candidates((this->m_upper_bound_values[bj] - x) / abs(ed), delta, leaving_candidates, bj);
else if (adj_sign < 0 && x >= this->m_low_bound_values[bj])
update_delta_of_entering_and_leaving_candidates((x - this->m_low_bound_values[bj]) / abs(ed), delta, leaving_candidates, bj);
break;
case low_bound:
if (adj_sign < 0 && x >= this->m_low_bound_values[bj])
update_delta_of_entering_and_leaving_candidates((x - this->m_low_bound_values[bj]) / abs(ed), delta, leaving_candidates, bj);
break;
case upper_bound:
if (adj_sign > 0 && x <= this->m_upper_bound_values[bj])
update_delta_of_entering_and_leaving_candidates((this->m_upper_bound_values[bj] - x) / abs(ed), delta, leaving_candidates, bj);
break;
default:
break;
}
}
std::vector<unsigned> & leaving_candidates);
unsigned find_leaving_for_inf_row_strategy(std::vector<unsigned> & leaving_candidates) {
lean_assert(leaving_candidates.size());
return leaving_candidates[lrand48() % leaving_candidates.size()]; // more randomness
return leaving_candidates[my_random() % leaving_candidates.size()]; // more randomness
}
X find_initial_delta_and_its_sign(unsigned row, unsigned entering,
int inf_sign, int & entering_delta_sign,
std::vector<unsigned> & leaving_candidates) {
lean_assert(inf_sign != 0);
unsigned bj = this->m_basis[row]; // this is the infeasible basis column
const X & x = this->m_x[bj];
entering_delta_sign = - get_sign(this->m_pivot_row[entering]) * inf_sign;
lean_assert(entering_delta_sign != 0);
X delta = (inf_sign > 0? (this->m_low_bound_values[bj] - x) : (x - this->m_upper_bound_values[bj])) / abs(this->m_pivot_row[entering]);
if (this->m_column_type[entering] == boxed) {
X span = this->bound_span(entering);
if (span < delta) {
delta = span;
leaving_candidates.push_back(entering);
} else {
leaving_candidates.push_back(bj);
}
} else {
leaving_candidates.push_back(bj);
}
std::vector<unsigned> & leaving_candidates);
return delta;
}
void advance_on_infeasible_row_and_entering(unsigned inf_row, unsigned entering, int inf_sign);
void advance_on_infeasible_row_and_entering(unsigned inf_row, unsigned entering, int inf_sign) {
this->solve_Bd(entering); // puts the tableau column of entering into this->m_ed
int entering_delta_sign;
std::vector<unsigned> leaving_candidates;
X delta = find_initial_delta_and_its_sign(inf_row, entering, inf_sign, entering_delta_sign, leaving_candidates);
lean_assert(leaving_candidates.size());
lean_assert(delta > zero_of_type<X>());
unsigned row = lrand48() % this->m_m;
unsigned initial_row = row;
do {
if (row != inf_row)
update_delta_of_entering(entering_delta_sign, row, delta, leaving_candidates);
if (++row == this->m_m) row = 0;
} while (row != initial_row);
unsigned leaving = find_leaving_for_inf_row_strategy(leaving_candidates);
update_basis_and_x_with_comparison(entering, leaving, delta * entering_delta_sign);
}
void advance_on_infeasible_row(unsigned i, int inf_sign);
void advance_on_infeasible_row(unsigned i, int inf_sign) {
calculate_pivot_row(i);
int entering = choose_entering_column_for_row_inf_strategy(inf_sign);
if (entering == -1) {
fill_evidence(i, inf_sign);
this->m_status = INFEASIBLE;
return;
}
advance_on_infeasible_row_and_entering(i, entering, inf_sign);
}
void solve() {
prefix();
if (is_empty()) {
this->m_status = OPTIMAL;
return;
}
this->snap_xN_to_bounds(); // we start with non-basic variables at their bounds
lean_assert(non_basis_columns_are_set_correctly());
if (this->m_settings.row_feasibility) {
std::cout << "optimizing by rows " << std::endl;
row_feasibility_loop();
} else {
std::cout << "optimizing total infeasibility" << std::endl;
feasibility_loop();
}
}
void solve();
bool low_bounds_are_set() const { return true; }
void print_column_info(unsigned j) {
std::cout << "type = " << column_type_to_string(this->m_column_type[j]) << std::endl;
switch (this->m_column_type[j]) {
case fixed:
case boxed:
std::cout << "(" << this->m_low_bound_values[j] << ", " << this->m_upper_bound_values[j] << ")" << std::endl;
break;
case low_bound:
std::cout << this->m_low_bound_values[j] << std::endl;
break;
case upper_bound:
std::cout << this->m_upper_bound_values[j] << std::endl;
break;
default:
lean_unreachable();
}
}
void print_column_info(unsigned j);
};
}

View file

@ -0,0 +1,13 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lar_core_solver.cpp"
template lean::lar_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::lar_core_solver(std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<lean::column_type, std::allocator<lean::column_type> >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<unsigned int, std::allocator<unsigned int> >&, lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, lean::lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template void lean::lar_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::solve();
template void lean::lar_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::prefix();
template void lean::lar_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::print_column_info(unsigned int);
template lean::numeric_pair<lean::mpq> lean::lar_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::get_deb_inf();
template bool lean::lar_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::is_empty() const;

791
src/util/lp/lar_solver.cpp Normal file
View file

@ -0,0 +1,791 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lar_solver.h"
namespace lean {
double conversion_helper <double>::get_low_bound(const column_info<mpq> & ci) {
if (!ci.low_bound_is_strict())
return ci.get_low_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;
}
double conversion_helper <double>::get_upper_bound(const column_info<mpq> & ci) {
if (!ci.upper_bound_is_strict())
return ci.get_upper_bound().get_double();
double eps = 0.00001;
if (!ci.low_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);
return ci.get_upper_bound().get_double() - eps;
}
canonic_left_side * lar_solver::create_or_fetch_existing_left_side(const buffer<pair<mpq, var_index>>& left_side_par) {
auto left_side = new canonic_left_side(left_side_par);
lean_assert(left_side->size() > 0);
auto it = m_canonic_left_sides.find(left_side);
if (it == m_canonic_left_sides.end()) {
m_canonic_left_sides.insert(left_side);
} else {
delete left_side;
left_side = *it;
}
return left_side;
}
bool lar_solver::var_is_fixed(unsigned j) {
auto it = m_map_from_var_index_to_left_side.find(j);
lean_assert(it != m_map_from_var_index_to_left_side.end());
return it->second->m_column_info.is_fixed();
}
mpq lar_solver::find_ratio(canonic_left_side * ls, const lar_constraint & constraint) {
lean_assert(ls->m_coeffs.size() > 0);
auto first_pair = ls->m_coeffs[0];
lean_assert(first_pair.first == numeric_traits<mpq>::one());
var_index i = first_pair.second;
auto it = constraint.m_left_side.find(i);
lean_assert(it != constraint.m_left_side.end());
return it->second;
}
void lar_solver::add_canonic_left_side_for_var(var_index i, std::string var_name) {
buffer<pair<mpq, var_index>> b;
b.push_back(std::make_pair(numeric_traits<mpq>::one(), i));
auto can_ls = new canonic_left_side(b);
can_ls->set_name(var_name);
lean_assert(m_canonic_left_sides.find(can_ls) == m_canonic_left_sides.end());
m_canonic_left_sides.insert(can_ls);
m_map_from_var_index_to_left_side[i] = can_ls;
}
void lar_solver::map_left_side_to_column_of_A(canonic_left_side* left_side, unsigned & j) {
lean_assert(m_column_indices_to_canonic_left_sides.find(j) == m_column_indices_to_canonic_left_sides.end());
left_side->m_column_index = j; // assigning this index does not change the hash of canonic_left_side
if (left_side->size() > 1) { // if size is one we will not create a row for this left side
left_side->m_row_index = m_basis.size();
m_basis.push_back(j); // j will be a basis column, so we put it into the basis as well
}
m_column_indices_to_canonic_left_sides[j++] = left_side; // pointing from the column to the left side
}
void lar_solver::map_left_sides_to_columns_of_A() {
unsigned j = 0;
for (auto it : m_canonic_left_sides)
map_left_side_to_column_of_A(it, j);
}
bool valid_index(unsigned j) { return static_cast<int>(j) >= 0;}
// this adds a row to A
template <typename U, typename V>
void lar_solver::add_row_to_A(static_matrix<U, V> & A, unsigned i, canonic_left_side * ls) {
for (auto & t : ls->m_coeffs) {
var_index vi = t.second;
canonic_left_side *var_left_side = m_map_from_var_index_to_left_side[vi];
unsigned column = var_left_side->m_column_index;
lean_assert(valid_index(column));
A.set(i, column, convert_struct<U, mpq>::convert(t.first));
}
A.set(i, ls->m_column_index, - one_of_type<U>());
}
template <typename U, typename V>
void lar_solver::create_matrix_A(static_matrix<U, V> & A) {
unsigned n = m_column_indices_to_canonic_left_sides.size();
unsigned m = m_basis.size();
A.init_empty_matrix(m, n);
unsigned i = 0;
for (auto & t : m_column_indices_to_canonic_left_sides)
if (valid_index(t.second->m_row_index))
add_row_to_A(A, i++, t.second);
#ifdef LEAN_DEBUG
// print_matrix(m_A);
#endif
}
// void lar_solver::fill_column_info_names() {
// for (unsigned j = 0; j < m_A.column_count(); j++) {
// column_info<mpq> t;
// m_column_infos.push_back(t);
// if (j < m_map_from_var_index_to_name_left_side_pair.size()) {
// m_column_infos.back().set_name(m_map_from_var_index_to_name_left_side_pair[j]);
// } else {
// string pref("_s_");
// m_column_infos.back().set_name(pref + T_to_string(j));
// }
// m_column_names
// }
// }
void lar_solver::set_upper_bound_for_column_info(lar_normalized_constraint * norm_constr) {
const mpq & v = norm_constr->m_right_side;
canonic_left_side * ls = norm_constr->m_canonic_left_side;
column_info<mpq> & ci = ls->m_column_info;
lean_assert(norm_constr->m_kind == LE || norm_constr->m_kind == LT || norm_constr->m_kind == EQ);
bool strict = norm_constr->m_kind == LT;
if (!ci.upper_bound_is_set()) {
ls->m_upper_bound_witness = norm_constr;
ci.set_upper_bound(v);
ci.set_upper_bound_strict(strict);
} else if (ci.get_upper_bound() > v) {
ci.set_upper_bound(v);
ls->m_upper_bound_witness = norm_constr;
ci.set_upper_bound_strict(strict);
}
if (ci.is_infeasible()) {
m_status= INFEASIBLE;
m_infeasible_canonic_left_side = ls;
return;
}
try_to_set_fixed(ci);
}
bool lar_solver::try_to_set_fixed(column_info<mpq> & ci) {
if (ci.upper_bound_is_set() && ci.low_bound_is_set() && ci.get_upper_bound() == ci.get_low_bound() && !ci.is_fixed()) {
ci.set_fixed_value(ci.get_upper_bound());
return true;
}
return false;
}
void lar_solver::set_low_bound_for_column_info(lar_normalized_constraint * norm_constr) {
const mpq & v = norm_constr->m_right_side;
canonic_left_side * ls = norm_constr->m_canonic_left_side;
column_info<mpq> & ci = ls->m_column_info;
lean_assert(norm_constr->m_kind == GE || norm_constr->m_kind == GT || norm_constr->m_kind == EQ);
bool strict = norm_constr->m_kind == GT;
if (!ci.low_bound_is_set()) {
ci.set_low_bound(v);
ls->m_low_bound_witness = norm_constr;
ci.set_low_bound_strict(strict);
} else if (ci.get_low_bound() < v) {
ci.set_low_bound(v);
ls->m_low_bound_witness = norm_constr;
ci.set_low_bound_strict(strict);
}
if (ci.is_infeasible()) {
m_status= INFEASIBLE;
m_infeasible_canonic_left_side = ls;
return;
}
try_to_set_fixed(ci);
}
void lar_solver::update_column_info_of_normalized_constraint(lar_normalized_constraint & norm_constr) {
lean_assert(norm_constr.size() > 0);
switch (norm_constr.m_kind) {
case LE:
case LT:
set_upper_bound_for_column_info(&norm_constr);
break;
case GE:
case GT:
set_low_bound_for_column_info(&norm_constr);
break;
case EQ:
{
set_upper_bound_for_column_info(&norm_constr);
set_low_bound_for_column_info(&norm_constr);
}
break;
default:
lean_unreachable();
}
}
column_type lar_solver::get_column_type(column_info<mpq> & ci) {
auto ret = ci.get_column_type_no_flipping();
if (ret == boxed) { // changing boxed to fixed because of the no span
if (ci.get_low_bound() == ci.get_upper_bound())
ret = fixed;
}
return ret;
}
void lar_solver::fill_column_names() {
m_column_names.clear();
for (auto t : m_canonic_left_sides) {
auto & ci = t->m_column_info;
unsigned j = t->m_column_index;
lean_assert(valid_index(j));
std::string name = ci.get_name();
if (name.size() == 0)
name = std::string("_s") + T_to_string(j);
m_column_names[j] = name;
}
}
void lar_solver::fill_column_types() {
m_column_types.clear();
m_column_types.resize(m_canonic_left_sides.size(), free_column);
for (auto t : m_canonic_left_sides) {
auto & ci = t->m_column_info;
unsigned j = t->m_column_index;
lean_assert(valid_index(j));
m_column_types[j] = get_column_type(ci);
}
}
template <typename V>
void lar_solver::fill_bounds_for_core_solver(std::vector<V> & lb, std::vector<V> & ub) {
unsigned n = m_canonic_left_sides.size(); // this is the number of columns
lb.resize(n);
ub.resize(n);
for (auto t : m_canonic_left_sides) {
auto & ci = t->m_column_info;
unsigned j = t->m_column_index;
lean_assert(valid_index(j));
if (ci.low_bound_is_set())
lb[j] = conversion_helper<V>::get_low_bound(ci);
if (ci.upper_bound_is_set())
ub[j] = conversion_helper<V>::get_upper_bound(ci);
}
}
template <typename V>
void lar_solver::resize_x_and_init_with_zeros(std::vector<V> & x, unsigned n) {
x.clear();
x.resize(n, zero_of_type<V>()); // init with zeroes
}
template <typename V>
void lar_solver::resize_x_and_init_with_signature(std::vector<V> & x, std::vector<V> & low_bound,
std::vector<V> & upper_bound, const lar_solution_signature & signature) {
x.clear();
x.resize(low_bound.size());
for (auto & t : signature.non_basic_column_value_positions) {
x[t.first] = get_column_val(low_bound, upper_bound, t.second, t.first);
}
}
template <typename V> V lar_solver::get_column_val(std::vector<V> & low_bound, std::vector<V> & upper_bound, non_basic_column_value_position pos_type, unsigned j) {
switch (pos_type) {
case at_low_bound: return low_bound[j];
case at_fixed:
case at_upper_bound: return upper_bound[j];
case free_of_bounds: return zero_of_type<V>();
default:
lean_unreachable();
}
}
lar_solver::~lar_solver() {
std::vector<canonic_left_side*> to_delete;
for (auto it : m_canonic_left_sides)
to_delete.push_back(it);
for (auto t : to_delete)
delete t;
}
var_index lar_solver::add_var(std::string s) {
auto got = m_var_names_to_var_index.find(s);
if (got != m_var_names_to_var_index.end()) return got->second;
var_index i = m_available_var_index++;
m_var_names_to_var_index[s] = i;
add_canonic_left_side_for_var(i, s);
return i;
}
constraint_index lar_solver::add_constraint(const buffer<pair<mpq, var_index>>& left_side, lconstraint_kind kind_par, mpq right_side_par) {
if (left_side.size() == 0) {
std::cout << "cannot add a constraint without left side" << std::endl;
return (constraint_index)(-1);
}
constraint_index i = m_available_constr_index++;
lar_constraint original_constr(left_side, kind_par, right_side_par, i);
canonic_left_side * ls = create_or_fetch_existing_left_side(left_side);
mpq ratio = find_ratio(ls, original_constr);
auto kind = ratio.is_neg()? flip_kind(kind_par): kind_par;
mpq right_side = right_side_par / ratio;
lar_normalized_constraint normalized_constraint(ls, ratio, kind, right_side, original_constr);
m_normalized_constraints[i] = normalized_constraint;
return i;
}
bool lar_solver::all_constraints_hold() {
std::unordered_map<var_index, mpq> var_map;
get_model(var_map);
for ( auto & it : m_normalized_constraints )
if (!constraint_holds(it.second.m_origin_constraint, var_map)) {
print_constraint(&it.second.m_origin_constraint);
std::cout << std::endl;
return false;
}
return true;
}
bool lar_solver::constraint_holds(const lar_constraint & constr, std::unordered_map<var_index, mpq> & var_map) {
mpq left_side_val = get_left_side_val(constr, var_map);
switch (constr.m_kind) {
case LE: return left_side_val <= constr.m_right_side;
case LT: return left_side_val <= constr.m_right_side;
case GE: return left_side_val >= constr.m_right_side;
case GT: return left_side_val >= constr.m_right_side;
case EQ: return left_side_val == constr.m_right_side;
default:
lean_unreachable();
}
}
void lar_solver::solve_with_core_solver() {
m_mpq_core_solver.solve();
m_status = m_mpq_core_solver.m_status;
lean_assert(m_status != OPTIMAL || all_constraints_hold());
#ifdef LEAN_DEBUG
lean_assert(!settings().row_feasibility || m_status != INFEASIBLE || the_evidence_is_correct());
#endif
}
bool lar_solver::the_relations_are_of_same_type(const buffer<pair<mpq, unsigned>> & evidence, lconstraint_kind & the_kind_of_sum) {
unsigned n_of_G = 0, n_of_L = 0;
bool strict = false;
for (auto & it : evidence) {
mpq coeff = it.first;
constraint_index con_ind = it.second;
std::cout << coeff.get_double() << std::endl;
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
print_constraint(&constr); std::cout << std::endl;
lconstraint_kind kind = coeff.is_pos()? constr.m_kind: flip_kind(constr.m_kind);
if (kind == GT || kind == LT)
strict = true;
if (kind == GE || kind == GT) n_of_G++;
else if (kind == LE || kind == LT) n_of_L++;
}
the_kind_of_sum = n_of_G? GE : (n_of_L? LE : EQ);
if (strict)
the_kind_of_sum = static_cast<lconstraint_kind>((static_cast<int>(the_kind_of_sum)/2));
return n_of_G == 0 || n_of_L == 0;
}
void lar_solver::register_in_map(std::unordered_map<var_index, mpq> & coeffs, lar_constraint & cn, const mpq & a) {
for (auto & it : cn.m_left_side) {
unsigned j = it.first;
auto p = coeffs.find(j);
if (p == coeffs.end()) coeffs[j] = it.second * a;
else p->second += it.second * a;
}
}
bool lar_solver::the_left_sides_sum_to_zero(const buffer<pair<mpq, unsigned>> & evidence) {
std::unordered_map<var_index, mpq> coeff_map;
for (auto & it : evidence) {
mpq coeff = it.first;
constraint_index con_ind = it.second;
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
register_in_map(coeff_map, constr, coeff);
}
for (auto & it : coeff_map) {
if (!numeric_traits<mpq>::is_zero(it.second)) return false;
}
return true;
}
bool lar_solver::the_righ_sides_do_not_sum_to_zero(const buffer<pair<mpq, unsigned>> & evidence) {
mpq ret = numeric_traits<mpq>::zero();
for (auto & it : evidence) {
mpq coeff = it.first;
constraint_index con_ind = it.second;
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
ret += constr.m_right_side * coeff;
}
return !numeric_traits<mpq>::is_zero(ret);
}
#ifdef LEAN_DEBUG
bool lar_solver::the_evidence_is_correct() {
buffer<pair<mpq, unsigned>> evidence;
get_infeasibility_evidence(evidence);
lconstraint_kind kind;
lean_assert(the_relations_are_of_same_type(evidence, kind));
lean_assert(the_left_sides_sum_to_zero(evidence));
mpq rs = sum_of_right_sides_of_evidence(evidence);
switch (kind) {
case LE: lean_assert(rs < zero_of_type<mpq>());
break;
case LT: lean_assert(rs <= zero_of_type<mpq>());
break;
case GE: lean_assert(rs > zero_of_type<mpq>());
break;
case GT: lean_assert(rs >= zero_of_type<mpq>());
break;
case EQ: lean_assert(rs != zero_of_type<mpq>());
break;
default:
lean_assert(false);
return false;
}
return true;
}
#endif
void lar_solver::update_column_info_of_normalized_constraints() {
for (auto & it : m_normalized_constraints)
update_column_info_of_normalized_constraint(it.second);
}
template <typename V>
void lar_solver::init_right_sides_with_zeros(std::vector<V> & rs) {
rs.clear();
rs.resize(m_basis.size(), zero_of_type<V>());
}
mpq lar_solver::sum_of_right_sides_of_evidence(const buffer<pair<mpq, unsigned>> & evidence) {
mpq ret = numeric_traits<mpq>::zero();
for (auto & it : evidence) {
mpq coeff = it.first;
constraint_index con_ind = it.second;
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
ret += constr.m_right_side * coeff;
}
return ret;
}
void lar_solver::prepare_independently_of_numeric_type() {
update_column_info_of_normalized_constraints();
map_left_sides_to_columns_of_A();
fill_column_names();
fill_column_types();
}
template <typename U, typename V>
void lar_solver::prepare_core_solver_fields(static_matrix<U, V> & A, std::vector<V> & x,
std::vector<V> & right_side_vector,
std::vector<V> & low_bound,
std::vector<V> & upper_bound) {
create_matrix_A(A);
fill_bounds_for_core_solver(low_bound, upper_bound);
if (m_status == INFEASIBLE) {
lean_assert(false); // not implemented
}
init_right_sides_with_zeros(right_side_vector);
resize_x_and_init_with_zeros(x, A.column_count());
lean_assert(m_basis.size() == A.row_count());
}
template <typename U, typename V>
void lar_solver::prepare_core_solver_fields_with_signature(static_matrix<U, V> & A, std::vector<V> & x,
std::vector<V> & right_side_vector,
std::vector<V> & low_bound,
std::vector<V> & upper_bound, const lar_solution_signature & signature) {
create_matrix_A(A);
fill_bounds_for_core_solver(low_bound, upper_bound);
if (m_status == INFEASIBLE) {
lean_assert(false); // not implemented
}
init_right_sides_with_zeros(right_side_vector);
resize_x_and_init_with_signature(x, low_bound, upper_bound, signature);
}
void lar_solver::find_solution_signature_with_doubles(lar_solution_signature & signature) {
static_matrix<double, double> A;
std::vector<double> x, right_side_vector, low_bounds, upper_bounds;
prepare_core_solver_fields<double, double>(A, x, right_side_vector, low_bounds, upper_bounds);
std::vector<double> column_scale_vector;
scaler<double, double > scaler(right_side_vector, A, m_settings.scaling_minimum, m_settings.scaling_maximum, column_scale_vector, this->m_settings);
if (!scaler.scale()) {
// the scale did not succeed, unscaling
A.clear();
create_matrix_A(A);
for (auto & s : column_scale_vector)
s = one_of_type<double>();
}
std::vector<double> costs(A.column_count());
auto core_solver = lp_primal_core_solver<double, double>(A,
right_side_vector,
x,
m_basis,
costs,
m_column_types,
low_bounds,
upper_bounds,
m_settings,
m_column_names);
core_solver.find_feasible_solution();
extract_signature_from_lp_core_solver(core_solver, signature);
}
template <typename U, typename V>
void lar_solver::extract_signature_from_lp_core_solver(lp_primal_core_solver<U, V> & core_solver, lar_solution_signature & signature) {
for (auto j : core_solver.m_non_basic_columns)
signature.non_basic_column_value_positions[j] = core_solver.get_non_basic_column_value_position(j);
}
void lar_solver::solve_on_signature(const lar_solution_signature & signature) {
prepare_core_solver_fields_with_signature(m_A, m_x, m_right_side_vector, m_low_bounds, m_upper_bounds, signature);
solve_with_core_solver();
}
void lar_solver::solve() {
prepare_independently_of_numeric_type();
if (m_settings.use_double_solver_for_lar) {
lar_solution_signature solution_signature;
find_solution_signature_with_doubles(solution_signature);
// here the basis that is kept in m_basis is the same that was used in the double solver
solve_on_signature(solution_signature);
return;
}
prepare_core_solver_fields(m_A, m_x, m_right_side_vector, m_low_bounds, m_upper_bounds);
solve_with_core_solver();
}
lp_status lar_solver::check() {
// for the time being just call solve()
solve();
return m_status;
}
void lar_solver::get_infeasibility_evidence(buffer<pair<mpq, constraint_index>> & evidence){
if (!m_mpq_core_solver.get_infeasible_row_sign()) {
std::cout << "don't have the infeasibility evidence" << std::endl;
return;
}
// the infeasibility sign
int inf_sign;
auto inf_row = m_mpq_core_solver.get_infeasibility_info(inf_sign);
lean_assert(inf_sign != 0);
get_infeasibility_evidence_for_inf_sign(evidence, inf_row, inf_sign);
}
void lar_solver::get_infeasibility_evidence_for_inf_sign(buffer<pair<mpq, constraint_index>> & evidence,
const std::vector<pair<mpq, unsigned>> & inf_row,
int inf_sign) {
for (auto & it : inf_row) {
mpq coeff = it.first;
unsigned j = it.second;
canonic_left_side * ls = m_column_indices_to_canonic_left_sides[j];
int adj_sign = coeff.is_pos() ? inf_sign : -inf_sign;
lar_normalized_constraint * bound_constr = adj_sign < 0? ls->m_upper_bound_witness : ls->m_low_bound_witness;
lean_assert(bound_constr != nullptr);
evidence.push_back(std::make_pair(coeff / bound_constr->m_ratio_to_original, bound_constr->m_index));
}
}
mpq lar_solver::find_delta_for_strict_bounds() {
mpq delta = numeric_traits<mpq>::one();
for (auto t : m_canonic_left_sides) {
auto & ci = t->m_column_info;
unsigned j = t->m_column_index;
lean_assert (valid_index(j));
if (ci.low_bound_is_set())
restrict_delta_on_low_bound_column(delta, j);
if (ci.upper_bound_is_set())
restrict_delta_on_upper_bound(delta, j);
}
return delta;
}
void lar_solver::restrict_delta_on_low_bound_column(mpq& delta, unsigned j) {
numeric_pair<mpq> & x = m_x[j];
numeric_pair<mpq> & l = m_low_bounds[j];
mpq & xx = x.x;
mpq & xy = x.y;
mpq & lx = l.x;
if (xx == lx) {
lean_assert(xy >= numeric_traits<mpq>::zero());
} else {
lean_assert(xx >= lx); // we need lx <= xx + delta*xy, or delta*xy >= lx - xx, or - delta*xy <= xx - ls.
// The right part is not negative. The delta is positive. If xy >= 0 we have the ineqality
// otherwise we need to have delta not greater than - (xx - lx)/xy. We use the 2 coefficient to handle the strict case
if (xy >= zero_of_type<mpq>()) return;
delta = std::min(delta, (lx - xx)/ (2 * xy)); // we need to have delta * xy < xx - lx for the strict case
}
}
void lar_solver::restrict_delta_on_upper_bound(mpq& delta, unsigned j) {
numeric_pair<mpq> & x = m_x[j];
numeric_pair<mpq> & u = m_upper_bounds[j];
mpq & xx = x.x;
mpq & xy = x.y;
mpq & ux = u.x;
if (xx == ux) {
lean_assert(xy <= numeric_traits<mpq>::zero());
} else {
lean_assert(xx < ux);
if (xy <= zero_of_type<mpq>()) return;
delta = std::min(delta, (ux - xx)/ (2 * xy)); // we need to have delta * xy < ux - xx, for the strict case
}
}
void lar_solver::get_model(std::unordered_map<var_index, mpq> & variable_values){
lean_assert(m_status == OPTIMAL);
mpq delta = find_delta_for_strict_bounds();
for (auto & it : m_map_from_var_index_to_left_side) {
numeric_pair<mpq> & rp = m_x[it.second->m_column_index];
// std::cout << it.second->m_column_info.get_name() << " = " << rp << std::endl;
variable_values[it.first] = rp.x + delta * rp.y;
}
}
std::string lar_solver::get_variable_name(var_index vi) {
if (m_map_from_var_index_to_left_side.size() <= vi) {
std::string s = "variable " + T_to_string(vi) + " is not found";
return s;
}
return m_map_from_var_index_to_left_side[vi]->m_column_info.get_name();
}
// ********** print region start
void lar_solver::print_constraint(constraint_index ci) {
if (m_normalized_constraints.size() <= ci) {
std::string s = "constraint " + T_to_string(ci) + " is not found";
std::cout << s << std::endl;
return;
}
print_constraint(&m_normalized_constraints[ci]);
}
void lar_solver::print_canonic_left_side(const canonic_left_side & c) {
bool first = true;
for (auto it : c.m_coeffs) {
auto val = it.first;
if (first) {
first = false;
} else {
if (val.is_pos()) {
std::cout << " + ";
} else {
std::cout << " - ";
val = -val;
}
}
if (val != numeric_traits<mpq>::one())
std::cout << T_to_string(val);
std::cout << m_map_from_var_index_to_left_side[it.second]->m_column_info.get_name();
}
}
void lar_solver::print_left_side_of_constraint(const lar_base_constraint * c) {
bool first = true;
for (auto it : c->get_left_side_coefficients()) {
auto val = it.first;
if (numeric_traits<mpq>::is_zero(val)) continue;
if (first) {
first = false;
} else {
if (val.is_pos()) {
std::cout << " + ";
} else {
std::cout << " - ";
val = -val;
}
}
if (val != numeric_traits<mpq>::one())
std::cout << val;
std::cout << m_map_from_var_index_to_left_side[it.second]->m_column_info.get_name();
}
}
numeric_pair<mpq> lar_solver::get_infeasibility_from_core_solver(std::unordered_map<std::string, mpq> & solution) {
prepare_independently_of_numeric_type();
prepare_core_solver_fields(m_A, m_x, m_right_side_vector, m_low_bounds, m_upper_bounds);
m_mpq_core_solver.prefix(); // just to fill the core solver
for (auto ls : m_canonic_left_sides) {
lean_assert(valid_index(ls->m_column_index));
m_x[ls->m_column_index] = numeric_pair<mpq>(get_canonic_left_side_val(ls, solution), 0);
}
return m_mpq_core_solver.get_deb_inf();
}
void lar_solver::print_info_on_column(unsigned j) {
for (auto ls : m_canonic_left_sides) {
if (static_cast<unsigned>(ls->m_column_index) == j) {
auto & ci = ls->m_column_info;
if (ci.low_bound_is_set()) {
std::cout << "l = " << ci.get_low_bound();
}
if (ci.upper_bound_is_set()) {
std::cout << "u = " << ci.get_upper_bound();
}
std::cout << std::endl;
m_mpq_core_solver.print_column_info(j);
}
}
}
mpq lar_solver::get_infeasibility_of_solution(std::unordered_map<std::string, mpq> & solution) {
std::cout << "solution" << std::endl;
for (auto it : solution) {
std::cout << it.first << " = " << it.second.get_double() << std::endl;
}
mpq ret = numeric_traits<mpq>::zero();
for (auto it : m_normalized_constraints) {
ret += get_infeasibility_of_constraint(it.second, solution);
}
std::cout << "ret = " << ret.get_double() << std::endl;
auto core_inf = get_infeasibility_from_core_solver(solution);
std::cout << "core inf = " << T_to_string(core_inf) << std::endl;
lean_assert(numeric_pair<mpq>(ret, 0) == core_inf);
return ret;
}
mpq lar_solver::get_infeasibility_of_constraint(const lar_normalized_constraint & norm_constr, std::unordered_map<std::string, mpq> & solution) {
auto kind = norm_constr.m_kind;
mpq left_side_val = get_canonic_left_side_val(norm_constr.m_canonic_left_side, solution);
switch (kind) {
case LT:
case LE: return std::max(left_side_val - norm_constr.m_right_side, numeric_traits<mpq>::zero());
case GT:
case GE: return std::max(- (left_side_val - norm_constr.m_right_side), numeric_traits<mpq>::zero());
case EQ:
return abs(left_side_val - norm_constr.m_right_side);
default:
lean_unreachable();
}
}
mpq lar_solver::get_canonic_left_side_val(canonic_left_side * ls, std::unordered_map<std::string, mpq> & solution) {
mpq ret = numeric_traits<mpq>::zero();
for (auto it : ls->m_coeffs) {
var_index j = it.second;
auto vi = m_map_from_var_index_to_left_side.find(j);
lean_assert(vi != m_map_from_var_index_to_left_side.end());
canonic_left_side * var_ls = vi->second;
std::string s = var_ls->m_column_info.get_name();
auto t = solution.find(s);
lean_assert(t != solution.end());
ret += it.first * (t->second);
}
return ret;
}
mpq lar_solver::get_left_side_val(const lar_constraint & cns, const std::unordered_map<var_index, mpq> & var_map) {
mpq ret = numeric_traits<mpq>::zero();
for (auto it : cns.m_left_side) {
var_index j = it.first;
auto vi = var_map.find(j);
lean_assert(vi != var_map.end());
ret += it.second * vi->second;
}
return ret;
}
void lar_solver::print_constraint(const lar_base_constraint * c) {
print_left_side_of_constraint(c);
std::cout <<" " << lconstraint_kind_string(c->m_kind) << " " << c->m_right_side;
}
}

View file

@ -20,15 +20,7 @@
#include "util/lp/numeric_pair.h"
#include "util/lp/lar_solution_signature.h"
#include "util/lp/scaler.h"
namespace std {
template<>
struct hash<lean::mpq> {
inline size_t operator()(const lean::mpq & v) const {
return v.hash();
}
};
}
#include "util/lp/lp_primal_core_solver.h"
namespace lean {
template <typename V>
@ -44,24 +36,8 @@ struct conversion_helper {
template<>
struct conversion_helper <double> {
static double get_low_bound(const column_info<mpq> & ci) {
if (!ci.low_bound_is_strict())
return ci.get_low_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;
}
static double get_upper_bound(const column_info<mpq> & ci) {
if (!ci.upper_bound_is_strict())
return ci.get_upper_bound().get_double();
double eps = 0.00001;
if (!ci.low_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);
return ci.get_upper_bound().get_double() - eps;
}
static double get_low_bound(const column_info<mpq> & ci);
static double get_upper_bound(const column_info<mpq> & ci);
};
class lar_solver {
@ -85,93 +61,29 @@ class lar_solver {
std::vector<numeric_pair<mpq>> m_right_side_vector; // this vector will be all zeroes, it might change when the optimization with fixed variables will used
std::vector<mpq> m_costs;
canonic_left_side * m_infeasible_canonic_left_side = nullptr; // such can be found at the initialization step
canonic_left_side * create_or_fetch_existing_left_side(const buffer<pair<mpq, var_index>>& left_side_par) {
auto left_side = new canonic_left_side(left_side_par);
lean_assert(left_side->size() > 0);
auto it = m_canonic_left_sides.find(left_side);
if (it == m_canonic_left_sides.end()) {
m_canonic_left_sides.insert(left_side);
} else {
delete left_side;
left_side = *it;
}
return left_side;
}
canonic_left_side * create_or_fetch_existing_left_side(const buffer<pair<mpq, var_index>>& left_side_par);
bool var_is_fixed(unsigned j) {
auto it = m_map_from_var_index_to_left_side.find(j);
lean_assert(it != m_map_from_var_index_to_left_side.end());
return it->second->m_column_info.is_fixed();
}
bool var_is_fixed(unsigned j);
mpq find_ratio(canonic_left_side * ls, const lar_constraint & constraint) {
lean_assert(ls->m_coeffs.size() > 0);
auto first_pair = ls->m_coeffs[0];
lean_assert(first_pair.first == numeric_traits<mpq>::one());
var_index i = first_pair.second;
auto it = constraint.m_left_side.find(i);
lean_assert(it != constraint.m_left_side.end());
return it->second;
}
mpq find_ratio(canonic_left_side * ls, const lar_constraint & constraint);
void add_canonic_left_side_for_var(var_index i, std::string var_name) {
buffer<pair<mpq, var_index>> b;
b.push_back(std::make_pair(numeric_traits<mpq>::one(), i));
auto can_ls = new canonic_left_side(b);
can_ls->set_name(var_name);
void add_canonic_left_side_for_var(var_index i, std::string var_name);
lean_assert(m_canonic_left_sides.find(can_ls) == m_canonic_left_sides.end());
m_canonic_left_sides.insert(can_ls);
m_map_from_var_index_to_left_side[i] = can_ls;
}
void map_left_side_to_column_of_A(canonic_left_side* left_side, unsigned & j) {
lean_assert(m_column_indices_to_canonic_left_sides.find(j) == m_column_indices_to_canonic_left_sides.end());
left_side->m_column_index = j; // assigning this index does not change the hash of canonic_left_side
if (left_side->size() > 1) { // if size is one we will not create a row for this left side
left_side->m_row_index = m_basis.size();
m_basis.push_back(j); // j will be a basis column, so we put it into the basis as well
}
m_column_indices_to_canonic_left_sides[j++] = left_side; // pointing from the column to the left side
}
void map_left_side_to_column_of_A(canonic_left_side* left_side, unsigned & j);
void map_left_sides_to_columns_of_A() {
unsigned j = 0;
for (auto it : m_canonic_left_sides)
map_left_side_to_column_of_A(it, j);
}
void map_left_sides_to_columns_of_A();
bool valid_index(unsigned j) { return static_cast<int>(j) >= 0;}
// this adds a row to A
template <typename U, typename V>
void add_row_to_A(static_matrix<U, V> & A, unsigned i, canonic_left_side * ls) {
for (auto & t : ls->m_coeffs) {
var_index vi = t.second;
canonic_left_side *var_left_side = m_map_from_var_index_to_left_side[vi];
unsigned column = var_left_side->m_column_index;
lean_assert(valid_index(column));
A.set(i, column, convert_struct<U, mpq>::convert(t.first));
}
A.set(i, ls->m_column_index, - one_of_type<U>());
}
void add_row_to_A(static_matrix<U, V> & A, unsigned i, canonic_left_side * ls);
template <typename U, typename V>
void create_matrix_A(static_matrix<U, V> & A) {
unsigned n = m_column_indices_to_canonic_left_sides.size();
unsigned m = m_basis.size();
A.init_empty_matrix(m, n);
unsigned i = 0;
for (auto & t : m_column_indices_to_canonic_left_sides)
if (valid_index(t.second->m_row_index))
add_row_to_A(A, i++, t.second);
#ifdef LEAN_DEBUG
// print_matrix(m_A);
#endif
}
void create_matrix_A(static_matrix<U, V> & A);
// void fill_column_info_names() {
// for (unsigned j = 0; j < m_A.column_count(); j++) {
@ -186,175 +98,39 @@ class lar_solver {
// m_column_names
// }
// }
void set_upper_bound_for_column_info(lar_normalized_constraint * norm_constr) {
const mpq & v = norm_constr->m_right_side;
canonic_left_side * ls = norm_constr->m_canonic_left_side;
column_info<mpq> & ci = ls->m_column_info;
lean_assert(norm_constr->m_kind == LE || norm_constr->m_kind == LT || norm_constr->m_kind == EQ);
bool strict = norm_constr->m_kind == LT;
if (!ci.upper_bound_is_set()) {
ls->m_upper_bound_witness = norm_constr;
ci.set_upper_bound(v);
ci.set_upper_bound_strict(strict);
} else if (ci.get_upper_bound() > v) {
ci.set_upper_bound(v);
ls->m_upper_bound_witness = norm_constr;
ci.set_upper_bound_strict(strict);
}
if (ci.is_infeasible()) {
m_status= INFEASIBLE;
m_infeasible_canonic_left_side = ls;
return;
}
try_to_set_fixed(ci);
}
void set_upper_bound_for_column_info(lar_normalized_constraint * norm_constr);
bool try_to_set_fixed(column_info<mpq> & ci) {
if (ci.upper_bound_is_set() && ci.low_bound_is_set() && ci.get_upper_bound() == ci.get_low_bound() && !ci.is_fixed()) {
ci.set_fixed_value(ci.get_upper_bound());
return true;
}
return false;
}
bool try_to_set_fixed(column_info<mpq> & ci);
void set_low_bound_for_column_info(lar_normalized_constraint * norm_constr) {
const mpq & v = norm_constr->m_right_side;
canonic_left_side * ls = norm_constr->m_canonic_left_side;
column_info<mpq> & ci = ls->m_column_info;
lean_assert(norm_constr->m_kind == GE || norm_constr->m_kind == GT || norm_constr->m_kind == EQ);
bool strict = norm_constr->m_kind == GT;
if (!ci.low_bound_is_set()) {
ci.set_low_bound(v);
ls->m_low_bound_witness = norm_constr;
ci.set_low_bound_strict(strict);
} else if (ci.get_low_bound() < v) {
ci.set_low_bound(v);
ls->m_low_bound_witness = norm_constr;
ci.set_low_bound_strict(strict);
}
void set_low_bound_for_column_info(lar_normalized_constraint * norm_constr);
if (ci.is_infeasible()) {
m_status= INFEASIBLE;
m_infeasible_canonic_left_side = ls;
return;
}
void update_column_info_of_normalized_constraint(lar_normalized_constraint & norm_constr);
try_to_set_fixed(ci);
}
column_type get_column_type(column_info<mpq> & ci);
void update_column_info_of_normalized_constraint(lar_normalized_constraint & norm_constr) {
lean_assert(norm_constr.size() > 0);
switch (norm_constr.m_kind) {
case LE:
case LT:
set_upper_bound_for_column_info(&norm_constr);
break;
case GE:
case GT:
set_low_bound_for_column_info(&norm_constr);
break;
void fill_column_names();
case EQ:
{
set_upper_bound_for_column_info(&norm_constr);
set_low_bound_for_column_info(&norm_constr);
}
break;
default:
lean_unreachable();
}
}
column_type get_column_type(column_info<mpq> & ci) {
auto ret = ci.get_column_type_no_flipping();
if (ret == boxed) { // changing boxed to fixed because of the no span
if (ci.get_low_bound() == ci.get_upper_bound())
ret = fixed;
}
return ret;
}
void fill_column_names() {
m_column_names.clear();
for (auto t : m_canonic_left_sides) {
auto & ci = t->m_column_info;
unsigned j = t->m_column_index;
lean_assert(valid_index(j));
std::string name = ci.get_name();
if (name.size() == 0)
name = std::string("_s") + T_to_string(j);
m_column_names[j] = name;
}
}
void fill_column_types() {
m_column_types.clear();
m_column_types.resize(m_canonic_left_sides.size(), free_column);
for (auto t : m_canonic_left_sides) {
auto & ci = t->m_column_info;
unsigned j = t->m_column_index;
lean_assert(valid_index(j));
m_column_types[j] = get_column_type(ci);
}
}
void fill_column_types();
template <typename V>
void fill_bounds_for_core_solver(std::vector<V> & lb, std::vector<V> & ub) {
unsigned n = m_canonic_left_sides.size(); // this is the number of columns
lb.resize(n);
ub.resize(n);
for (auto t : m_canonic_left_sides) {
auto & ci = t->m_column_info;
unsigned j = t->m_column_index;
lean_assert(valid_index(j));
if (ci.low_bound_is_set())
lb[j] = conversion_helper<V>::get_low_bound(ci);
if (ci.upper_bound_is_set())
ub[j] = conversion_helper<V>::get_upper_bound(ci);
}
}
void fill_bounds_for_core_solver(std::vector<V> & lb, std::vector<V> & ub);
template <typename V>
void resize_x_and_init_with_zeros(std::vector<V> & x, unsigned n) {
x.clear();
x.resize(n, zero_of_type<V>()); // init with zeroes
}
void resize_x_and_init_with_zeros(std::vector<V> & x, unsigned n);
template <typename V>
void resize_x_and_init_with_signature(std::vector<V> & x, std::vector<V> & low_bound,
std::vector<V> & upper_bound, const lar_solution_signature & signature) {
x.clear();
x.resize(low_bound.size());
for (auto & t : signature.non_basic_column_value_positions) {
x[t.first] = get_column_val(low_bound, upper_bound, t.second, t.first);
}
}
std::vector<V> & upper_bound, const lar_solution_signature & signature);
template <typename V> V get_column_val(std::vector<V> & low_bound, std::vector<V> & upper_bound, non_basic_column_value_position pos_type, unsigned j) {
switch (pos_type) {
case at_low_bound: return low_bound[j];
case at_fixed:
case at_upper_bound: return upper_bound[j];
case free_of_bounds: return zero_of_type<V>();
default:
lean_unreachable();
}
}
template <typename V> V get_column_val(std::vector<V> & low_bound, std::vector<V> & upper_bound, non_basic_column_value_position pos_type, unsigned j);
public:
~lar_solver() {
std::vector<canonic_left_side*> to_delete;
for (auto it : m_canonic_left_sides)
to_delete.push_back(it);
for (auto t : to_delete)
delete t;
}
~lar_solver();
lp_settings & settings() { return m_settings;}
void clear() {
lean_assert(false); // not implemented
void clear() {lean_assert(false); // not implemented
}
lar_solver() : m_mpq_core_solver(m_x,
@ -370,501 +146,96 @@ public:
}
var_index add_var(std::string s) {
auto got = m_var_names_to_var_index.find(s);
if (got != m_var_names_to_var_index.end()) return got->second;
var_index add_var(std::string s);
var_index i = m_available_var_index++;
m_var_names_to_var_index[s] = i;
add_canonic_left_side_for_var(i, s);
return i;
}
constraint_index add_constraint(const buffer<pair<mpq, var_index>>& left_side, lconstraint_kind kind_par, mpq right_side_par) {
if (left_side.size() == 0) {
std::cout << "cannot add a constraint without left side" << std::endl;
return (constraint_index)(-1);
}
constraint_index i = m_available_constr_index++;
lar_constraint original_constr(left_side, kind_par, right_side_par, i);
canonic_left_side * ls = create_or_fetch_existing_left_side(left_side);
mpq ratio = find_ratio(ls, original_constr);
auto kind = ratio.is_neg()? flip_kind(kind_par): kind_par;
mpq right_side = right_side_par / ratio;
lar_normalized_constraint normalized_constraint(ls, ratio, kind, right_side, original_constr);
m_normalized_constraints[i] = normalized_constraint;
return i;
}
constraint_index add_constraint(const buffer<pair<mpq, var_index>>& left_side, lconstraint_kind kind_par, mpq right_side_par);
bool is_infeasible(const column_info<mpq> & ci) {
return ci.low_bound_is_set() && ci.upper_bound_is_set() && ci.get_low_bound() > ci.get_upper_bound();
}
bool all_constraints_hold() {
std::unordered_map<var_index, mpq> var_map;
get_model(var_map);
for ( auto & it : m_normalized_constraints )
if (!constraint_holds(it.second.m_origin_constraint, var_map)) {
print_constraint(&it.second.m_origin_constraint);
std::cout << std::endl;
return false;
}
return true;
}
bool all_constraints_hold();
bool constraint_holds(const lar_constraint & constr, std::unordered_map<var_index, mpq> & var_map) {
mpq left_side_val = get_left_side_val(constr, var_map);
switch (constr.m_kind) {
case LE: return left_side_val <= constr.m_right_side;
case LT: return left_side_val <= constr.m_right_side;
case GE: return left_side_val >= constr.m_right_side;
case GT: return left_side_val >= constr.m_right_side;
case EQ: return left_side_val == constr.m_right_side;
default:
lean_unreachable();
}
}
bool constraint_holds(const lar_constraint & constr, std::unordered_map<var_index, mpq> & var_map);
lp_status get_status() const { return m_status;}
void solve_with_core_solver() {
m_mpq_core_solver.solve();
m_status = m_mpq_core_solver.m_status;
lean_assert(m_status != OPTIMAL || all_constraints_hold());
lean_assert(!settings().row_feasibility || m_status != INFEASIBLE || the_evidence_is_correct());
}
void solve_with_core_solver();
bool the_relations_are_of_same_type(const buffer<pair<mpq, unsigned>> & evidence, lconstraint_kind & the_kind_of_sum) {
unsigned n_of_G = 0, n_of_L = 0;
bool strict = false;
for (auto & it : evidence) {
mpq coeff = it.first;
constraint_index con_ind = it.second;
std::cout << coeff.get_double() << std::endl;
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
print_constraint(&constr); std::cout << std::endl;
bool the_relations_are_of_same_type(const buffer<pair<mpq, unsigned>> & evidence, lconstraint_kind & the_kind_of_sum);
lconstraint_kind kind = coeff.is_pos()? constr.m_kind: flip_kind(constr.m_kind);
if (kind == GT || kind == LT)
strict = true;
if (kind == GE || kind == GT) n_of_G++;
else if (kind == LE || kind == LT) n_of_L++;
}
the_kind_of_sum = n_of_G? GE : (n_of_L? LE : EQ);
if (strict)
the_kind_of_sum = static_cast<lconstraint_kind>((static_cast<int>(the_kind_of_sum)/2));
void register_in_map(std::unordered_map<var_index, mpq> & coeffs, lar_constraint & cn, const mpq & a);
bool the_left_sides_sum_to_zero(const buffer<pair<mpq, unsigned>> & evidence);
return n_of_G == 0 || n_of_L == 0;
}
bool the_righ_sides_do_not_sum_to_zero(const buffer<pair<mpq, unsigned>> & evidence);
void register_in_map(std::unordered_map<var_index, mpq> & coeffs, lar_constraint & cn, const mpq & a) {
for (auto & it : cn.m_left_side) {
unsigned j = it.first;
auto p = coeffs.find(j);
if (p == coeffs.end()) coeffs[j] = it.second * a;
else p->second += it.second * a;
}
}
bool the_left_sides_sum_to_zero(const buffer<pair<mpq, unsigned>> & evidence) {
std::unordered_map<var_index, mpq> coeff_map;
for (auto & it : evidence) {
mpq coeff = it.first;
constraint_index con_ind = it.second;
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
register_in_map(coeff_map, constr, coeff);
}
for (auto & it : coeff_map) {
if (!numeric_traits<mpq>::is_zero(it.second)) return false;
}
return true;
}
bool the_righ_sides_do_not_sum_to_zero(const buffer<pair<mpq, unsigned>> & evidence) {
mpq ret = numeric_traits<mpq>::zero();
for (auto & it : evidence) {
mpq coeff = it.first;
constraint_index con_ind = it.second;
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
ret += constr.m_right_side * coeff;
}
return !numeric_traits<mpq>::is_zero(ret);
}
bool the_evidence_is_correct() {
buffer<pair<mpq, unsigned>> evidence;
get_infeasibility_evidence(evidence);
lconstraint_kind kind;
lean_assert(the_relations_are_of_same_type(evidence, kind));
lean_assert(the_left_sides_sum_to_zero(evidence));
mpq rs = sum_of_right_sides_of_evidence(evidence);
switch (kind) {
case LE: lean_assert(rs < zero_of_type<mpq>());
break;
case LT: lean_assert(rs <= zero_of_type<mpq>());
break;
case GE: lean_assert(rs > zero_of_type<mpq>());
break;
case GT: lean_assert(rs >= zero_of_type<mpq>());
break;
case EQ: lean_assert(rs != zero_of_type<mpq>());
break;
default:
lean_assert(false);
return false;
}
return true;
}
void update_column_info_of_normalized_constraints() {
for (auto & it : m_normalized_constraints)
update_column_info_of_normalized_constraint(it.second);
}
bool the_evidence_is_correct();
void update_column_info_of_normalized_constraints();
template <typename V>
void init_right_sides_with_zeros(std::vector<V> & rs) {
rs.clear();
rs.resize(m_basis.size(), zero_of_type<V>());
}
void init_right_sides_with_zeros(std::vector<V> & rs);
mpq sum_of_right_sides_of_evidence(const buffer<pair<mpq, unsigned>> & evidence) {
mpq ret = numeric_traits<mpq>::zero();
for (auto & it : evidence) {
mpq coeff = it.first;
constraint_index con_ind = it.second;
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
ret += constr.m_right_side * coeff;
}
return ret;
}
void prepare_independently_of_numeric_type() {
update_column_info_of_normalized_constraints();
map_left_sides_to_columns_of_A();
fill_column_names();
fill_column_types();
}
mpq sum_of_right_sides_of_evidence(const buffer<pair<mpq, unsigned>> & evidence);
void prepare_independently_of_numeric_type();
template <typename U, typename V>
void prepare_core_solver_fields(static_matrix<U, V> & A, std::vector<V> & x,
std::vector<V> & right_side_vector,
std::vector<V> & low_bound,
std::vector<V> & upper_bound) {
create_matrix_A(A);
fill_bounds_for_core_solver(low_bound, upper_bound);
if (m_status == INFEASIBLE) {
lean_assert(false); // not implemented
}
init_right_sides_with_zeros(right_side_vector);
resize_x_and_init_with_zeros(x, A.column_count());
lean_assert(m_basis.size() == A.row_count());
}
std::vector<V> & upper_bound);
template <typename U, typename V>
void prepare_core_solver_fields_with_signature(static_matrix<U, V> & A, std::vector<V> & x,
std::vector<V> & right_side_vector,
std::vector<V> & low_bound,
std::vector<V> & upper_bound, const lar_solution_signature & signature) {
create_matrix_A(A);
fill_bounds_for_core_solver(low_bound, upper_bound);
if (m_status == INFEASIBLE) {
lean_assert(false); // not implemented
}
init_right_sides_with_zeros(right_side_vector);
resize_x_and_init_with_signature(x, low_bound, upper_bound, signature);
}
std::vector<V> & upper_bound, const lar_solution_signature & signature);
void find_solution_signature_with_doubles(lar_solution_signature & signature) {
static_matrix<double, double> A;
std::vector<double> x, right_side_vector, low_bounds, upper_bounds;
prepare_core_solver_fields<double, double>(A, x, right_side_vector, low_bounds, upper_bounds);
std::vector<double> column_scale_vector;
scaler<double, double > scaler(right_side_vector, A, m_settings.scaling_minimum, m_settings.scaling_maximum, column_scale_vector, this->m_settings);
if (!scaler.scale()) {
// the scale did not succeed, unscaling
A.clear();
create_matrix_A(A);
for (auto & s : column_scale_vector)
s = one_of_type<double>();
}
std::vector<double> costs(A.column_count());
auto core_solver = lp_primal_core_solver<double, double>(A,
right_side_vector,
x,
m_basis,
costs,
m_column_types,
low_bounds,
upper_bounds,
m_settings,
m_column_names);
core_solver.find_feasible_solution();
extract_signature_from_lp_core_solver(core_solver, signature);
}
void find_solution_signature_with_doubles(lar_solution_signature & signature);
template <typename U, typename V>
void extract_signature_from_lp_core_solver(lp_primal_core_solver<U, V> & core_solver, lar_solution_signature & signature) {
for (auto j : core_solver.m_non_basic_columns)
signature.non_basic_column_value_positions[j] = core_solver.get_non_basic_column_value_position(j);
}
void extract_signature_from_lp_core_solver(lp_primal_core_solver<U, V> & core_solver, lar_solution_signature & signature);
void solve_on_signature(const lar_solution_signature & signature) {
prepare_core_solver_fields_with_signature(m_A, m_x, m_right_side_vector, m_low_bounds, m_upper_bounds, signature);
solve_with_core_solver();
}
void solve_on_signature(const lar_solution_signature & signature);
void solve() {
prepare_independently_of_numeric_type();
if (m_settings.use_double_solver_for_lar) {
lar_solution_signature solution_signature;
find_solution_signature_with_doubles(solution_signature);
// here the basis that is kept in m_basis is the same that was used in the double solver
solve_on_signature(solution_signature);
return;
}
prepare_core_solver_fields(m_A, m_x, m_right_side_vector, m_low_bounds, m_upper_bounds);
solve_with_core_solver();
}
void solve();
lp_status check() {
// for the time being just call solve()
solve();
return m_status;
}
void get_infeasibility_evidence(buffer<pair<mpq, constraint_index>> & evidence){
if (!m_mpq_core_solver.get_infeasible_row_sign()) {
std::cout << "don't have the infeasibility evidence" << std::endl;
return;
}
// the infeasibility sign
int inf_sign;
auto inf_row = m_mpq_core_solver.get_infeasibility_info(inf_sign);
lean_assert(inf_sign != 0);
get_infeasibility_evidence_for_inf_sign(evidence, inf_row, inf_sign);
}
lp_status check();
void get_infeasibility_evidence(buffer<pair<mpq, constraint_index>> & evidence);
void get_infeasibility_evidence_for_inf_sign(buffer<pair<mpq, constraint_index>> & evidence,
const std::vector<pair<mpq, unsigned>> & inf_row,
int inf_sign) {
for (auto & it : inf_row) {
mpq coeff = it.first;
unsigned j = it.second;
canonic_left_side * ls = m_column_indices_to_canonic_left_sides[j];
int adj_sign = coeff.is_pos() ? inf_sign : -inf_sign;
lar_normalized_constraint * bound_constr = adj_sign < 0? ls->m_upper_bound_witness : ls->m_low_bound_witness;
lean_assert(bound_constr != nullptr);
evidence.push_back(std::make_pair(coeff / bound_constr->m_ratio_to_original, bound_constr->m_index));
}
}
int inf_sign);
mpq find_delta_for_strict_bounds() {
mpq delta = numeric_traits<mpq>::one();
for (auto t : m_canonic_left_sides) {
auto & ci = t->m_column_info;
unsigned j = t->m_column_index;
lean_assert (valid_index(j));
if (ci.low_bound_is_set())
restrict_delta_on_low_bound_column(delta, j);
if (ci.upper_bound_is_set())
restrict_delta_on_upper_bound(delta, j);
}
return delta;
}
mpq find_delta_for_strict_bounds();
void restrict_delta_on_low_bound_column(mpq& delta, unsigned j) {
numeric_pair<mpq> & x = m_x[j];
numeric_pair<mpq> & l = m_low_bounds[j];
mpq & xx = x.x;
mpq & xy = x.y;
mpq & lx = l.x;
if (xx == lx) {
lean_assert(xy >= numeric_traits<mpq>::zero());
} else {
lean_assert(xx >= lx); // we need lx <= xx + delta*xy, or delta*xy >= lx - xx, or - delta*xy <= xx - ls.
// The right part is not negative. The delta is positive. If xy >= 0 we have the ineqality
// otherwise we need to have delta not greater than - (xx - lx)/xy. We use the 2 coefficient to handle the strict case
if (xy >= zero_of_type<mpq>()) return;
delta = std::min(delta, (lx - xx)/ (2 * xy)); // we need to have delta * xy < xx - lx for the strict case
}
}
void restrict_delta_on_upper_bound(mpq& delta, unsigned j) {
numeric_pair<mpq> & x = m_x[j];
numeric_pair<mpq> & u = m_upper_bounds[j];
mpq & xx = x.x;
mpq & xy = x.y;
mpq & ux = u.x;
if (xx == ux) {
lean_assert(xy <= numeric_traits<mpq>::zero());
} else {
lean_assert(xx < ux);
if (xy <= zero_of_type<mpq>()) return;
delta = std::min(delta, (ux - xx)/ (2 * xy)); // we need to have delta * xy < ux - xx, for the strict case
}
}
void restrict_delta_on_low_bound_column(mpq& delta, unsigned j);
void restrict_delta_on_upper_bound(mpq& delta, unsigned j);
void get_model(std::unordered_map<var_index, mpq> & variable_values){
lean_assert(m_status == OPTIMAL);
mpq delta = find_delta_for_strict_bounds();
for (auto & it : m_map_from_var_index_to_left_side) {
numeric_pair<mpq> & rp = m_x[it.second->m_column_index];
// std::cout << it.second->m_column_info.get_name() << " = " << rp << std::endl;
variable_values[it.first] = rp.x + delta * rp.y;
}
}
void get_model(std::unordered_map<var_index, mpq> & variable_values);
std::string get_variable_name(var_index vi) {
if (m_map_from_var_index_to_left_side.size() <= vi) {
std::string s = "variable " + T_to_string(vi) + " is not found";
return s;
}
return m_map_from_var_index_to_left_side[vi]->m_column_info.get_name();
}
std::string get_variable_name(var_index vi);
// ********** print region start
void print_constraint(constraint_index ci) {
if (m_normalized_constraints.size() <= ci) {
std::string s = "constraint " + T_to_string(ci) + " is not found";
std::cout << s << std::endl;
return;
}
void print_constraint(constraint_index ci);
print_constraint(&m_normalized_constraints[ci]);
}
void print_canonic_left_side(const canonic_left_side & c);
void print_canonic_left_side(const canonic_left_side & c) {
bool first = true;
for (auto it : c.m_coeffs) {
auto val = it.first;
if (first) {
first = false;
} else {
if (val.is_pos()) {
std::cout << " + ";
} else {
std::cout << " - ";
val = -val;
}
}
if (val != numeric_traits<mpq>::one())
std::cout << T_to_string(val);
std::cout << m_map_from_var_index_to_left_side[it.second]->m_column_info.get_name();
}
}
void print_left_side_of_constraint(const lar_base_constraint * c);
void print_left_side_of_constraint(const lar_base_constraint * c) {
bool first = true;
for (auto it : c->get_left_side_coefficients()) {
auto val = it.first;
if (numeric_traits<mpq>::is_zero(val)) continue;
if (first) {
first = false;
} else {
if (val.is_pos()) {
std::cout << " + ";
} else {
std::cout << " - ";
val = -val;
}
}
numeric_pair<mpq> get_infeasibility_from_core_solver(std::unordered_map<std::string, mpq> & solution);
if (val != numeric_traits<mpq>::one())
std::cout << val;
std::cout << m_map_from_var_index_to_left_side[it.second]->m_column_info.get_name();
}
}
void print_info_on_column(unsigned j);
numeric_pair<mpq> get_infeasibility_from_core_solver(std::unordered_map<std::string, mpq> & solution) {
prepare_independently_of_numeric_type();
prepare_core_solver_fields(m_A, m_x, m_right_side_vector, m_low_bounds, m_upper_bounds);
m_mpq_core_solver.prefix(); // just to fill the core solver
mpq get_infeasibility_of_solution(std::unordered_map<std::string, mpq> & solution);
for (auto ls : m_canonic_left_sides) {
lean_assert(valid_index(ls->m_column_index));
m_x[ls->m_column_index] = numeric_pair<mpq>(get_canonic_left_side_val(ls, solution), 0);
}
return m_mpq_core_solver.get_deb_inf();
}
mpq get_infeasibility_of_constraint(const lar_normalized_constraint & norm_constr, std::unordered_map<std::string, mpq> & solution);
void print_info_on_column(unsigned j) {
for (auto ls : m_canonic_left_sides) {
if (static_cast<unsigned>(ls->m_column_index) == j) {
auto & ci = ls->m_column_info;
if (ci.low_bound_is_set()) {
std::cout << "l = " << ci.get_low_bound();
}
if (ci.upper_bound_is_set()) {
std::cout << "u = " << ci.get_upper_bound();
}
std::cout << std::endl;
m_mpq_core_solver.print_column_info(j);
}
}
}
mpq get_canonic_left_side_val(canonic_left_side * ls, std::unordered_map<std::string, mpq> & solution);
mpq get_infeasibility_of_solution(std::unordered_map<std::string, mpq> & solution) {
std::cout << "solution" << std::endl;
for (auto it : solution) {
std::cout << it.first << " = " << it.second.get_double() << std::endl;
}
mpq ret = numeric_traits<mpq>::zero();
for (auto it : m_normalized_constraints) {
ret += get_infeasibility_of_constraint(it.second, solution);
}
std::cout << "ret = " << ret.get_double() << std::endl;
auto core_inf = get_infeasibility_from_core_solver(solution);
std::cout << "core inf = " << T_to_string(core_inf) << std::endl;
lean_assert(numeric_pair<mpq>(ret, 0) == core_inf);
return ret;
}
mpq get_left_side_val(const lar_constraint & cns, const std::unordered_map<var_index, mpq> & var_map);
mpq get_infeasibility_of_constraint(const lar_normalized_constraint & norm_constr, std::unordered_map<std::string, mpq> & solution) {
auto kind = norm_constr.m_kind;
mpq left_side_val = get_canonic_left_side_val(norm_constr.m_canonic_left_side, solution);
switch (kind) {
case LT:
case LE: return std::max(left_side_val - norm_constr.m_right_side, numeric_traits<mpq>::zero());
case GT:
case GE: return std::max(- (left_side_val - norm_constr.m_right_side), numeric_traits<mpq>::zero());
case EQ:
return abs(left_side_val - norm_constr.m_right_side);
default:
lean_unreachable();
}
}
mpq get_canonic_left_side_val(canonic_left_side * ls, std::unordered_map<std::string, mpq> & solution) {
mpq ret = numeric_traits<mpq>::zero();
for (auto it : ls->m_coeffs) {
var_index j = it.second;
auto vi = m_map_from_var_index_to_left_side.find(j);
lean_assert(vi != m_map_from_var_index_to_left_side.end());
canonic_left_side * var_ls = vi->second;
std::string s = var_ls->m_column_info.get_name();
auto t = solution.find(s);
lean_assert(t != solution.end());
ret += it.first * (t->second);
}
return ret;
}
mpq get_left_side_val(const lar_constraint & cns, const std::unordered_map<var_index, mpq> & var_map) {
mpq ret = numeric_traits<mpq>::zero();
for (auto it : cns.m_left_side) {
var_index j = it.first;
auto vi = var_map.find(j);
lean_assert(vi != var_map.end());
ret += it.second * vi->second;
}
return ret;
}
void print_constraint(const lar_base_constraint * c) {
print_left_side_of_constraint(c);
std::cout <<" " << lconstraint_kind_string(c->m_kind) << " " << c->m_right_side;
}
void print_constraint(const lar_base_constraint * c);
unsigned get_total_iterations() const { return m_mpq_core_solver.m_total_iterations; }
};
}

View file

@ -0,0 +1,8 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#
#include "util/lp/lar_solver.cpp"

View file

@ -1,13 +0,0 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#pragma once
#include <vector>
#include "util/debug.h"
#include "util/numerics/numeric_traits.h"
#include "util/numerics/xnumeral.h"
#include "util/lp/lp_primal_core_solver.h"

View file

@ -0,0 +1,695 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lp_core_solver_base.h"
namespace lean {
void init_basic_part_of_basis_heading(std::vector<unsigned> & basis, unsigned m, std::vector<int> & basis_heading) {
for (unsigned i = 0; i < m; i++) {
unsigned column = basis[i];
basis_heading[column] = i;
}
}
void init_non_basic_part_of_basis_heading(std::vector<int> & basis_heading, std::vector<unsigned> & non_basic_columns, unsigned n) {
for (int j = n; j--;){
if (basis_heading[j] < 0) {
non_basic_columns.push_back(j);
// the index of column j in m_non_basic_columns is (- basis_heading[j] - 1)
basis_heading[j] = - non_basic_columns.size();
}
}
}
void init_basis_heading_and_non_basic_columns_vector(std::vector<unsigned> & basis,
unsigned m,
std::vector<int> & basis_heading,
unsigned n,
std::vector<unsigned> & non_basic_columns) {
init_basic_part_of_basis_heading(basis, m, basis_heading);
init_non_basic_part_of_basis_heading(basis_heading, non_basic_columns, n);
}
template <typename T, typename X> lp_core_solver_base<T, X>::
lp_core_solver_base(static_matrix<T, X> & A,
std::vector<X> & b, // the right side vector
std::vector<unsigned> & basis,
std::vector<X> & x,
std::vector<T> & costs,
lp_settings & settings,
const std::unordered_map<unsigned, std::string> & column_names,
std::vector<column_type> & column_types,
std::vector<X> & low_bound_values,
std::vector<X> & upper_bound_values):
m_m(A.row_count()),
m_n(A.column_count()),
m_pivot_row_of_B_1(m_m),
m_pivot_row(m_n, zero_of_type<T>()),
m_A(A),
m_b(b),
m_basis(basis),
m_x(x),
m_costs(costs),
m_settings(settings),
m_y(m_m),
m_status(FEASIBLE),
m_factorization(nullptr),
m_column_names(column_names),
m_w(m_m),
m_d(m_n),
m_ed(m_m),
m_column_type(column_types),
m_low_bound_values(low_bound_values),
m_upper_bound_values(upper_bound_values),
m_column_norms(m_n, T(1)),
m_copy_of_xB(m_m) {
if (m_m) {
lean_assert(m_A.col_val_equal_to_row_val());
init();
init_basis_heading();
}
}
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
m_basis_heading.clear();
m_basis_heading.resize(m_n, -1);
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
init() {
lean_assert(m_costs.size() == m_n);
lean_assert(m_basis.size() == m_m);
lean_assert(m_b.size() == m_m);
allocate_basis_heading();
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
m_refactor_counter = 0;
unsigned seed = 1;
my_random_init(&seed);
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
fill_cb(T * y){
for (unsigned i = 0; i < m_m; i++) {
y[i] = m_costs[m_basis[i]];
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
fill_cb(std::vector<T> & y){
for (unsigned i = 0; i < m_m; i++)
y[i] = m_costs[m_basis[i]];
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
solve_yB(std::vector<T> & y) {
fill_cb(y); // now y = cB, that is the projection of costs to basis
m_factorization->solve_yB(y);
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
update_index_of_ed() {
m_index_of_ed.clear();
unsigned i = m_ed.size();
while (i--) {
if (!is_zero(m_ed[i]))
m_index_of_ed.push_back(i);
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
solve_Bd(unsigned entering) {
m_factorization->solve_Bd(entering, m_ed, m_w);
update_index_of_ed();
#ifdef LEAN_DEBUG
// auto B = get_B(m_factorization);
// vector<T> a(m_m);
// m_A.copy_column_to_vector(entering, a);
// vector<T> cd(m_ed);
// B.apply_from_left(cd, m_settings);
// lean_assert(vectors_are_equal(cd , a));
#endif
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
pretty_print() {
core_solver_pretty_printer<T, X> pp(*this);
pp.print();
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
save_state(T * w_buffer, T * d_buffer) {
copy_m_w(w_buffer);
copy_m_ed(d_buffer);
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
restore_state(T * w_buffer, T * d_buffer) {
restore_m_w(w_buffer);
restore_m_ed(d_buffer);
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
copy_m_w(T * buffer) {
unsigned i = m_m;
while (i --) {
buffer[i] = m_w[i];
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
restore_m_w(T * buffer) {
m_w.m_index.clear();
unsigned i = m_m;
while (i--) {
if (!is_zero(m_w[i] = buffer[i]))
m_w.m_index.push_back(i);
}
}
// needed for debugging
template <typename T, typename X> void lp_core_solver_base<T, X>::
copy_m_ed(T * buffer) {
unsigned i = m_m;
while (i --) {
buffer[i] = m_ed[i];
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
restore_m_ed(T * buffer) {
unsigned i = m_m;
while (i --) {
m_ed[i] = buffer[i];
}
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
A_mult_x_is_off() {
if (precise<T>()) {
return false;
}
T feps = convert_struct<T, double>::convert(m_settings.refactor_tolerance);
X one = convert_struct<X, double>::convert(1.0);
for (unsigned i = 0; i < m_m; i++) {
X delta = abs(m_b[i] - m_A.dot_product_with_row(i, m_x));
X eps = feps * (one + T(0.1) * abs(m_b[i]));
if (delta >eps) {
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 = " << m_total_iterations << ")" << std::endl;
return true;
}
}
return false;
}
// 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) {
unsigned i = m_m;
while (i--) {
m_pivot_row_of_B_1[i] = numeric_traits<T>::zero();
}
m_pivot_row_of_B_1[pivot_row] = numeric_traits<T>::one();
m_factorization->solve_yB(m_pivot_row_of_B_1);
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
zero_pivot_row() {
for (unsigned j : m_pivot_row_index)
m_pivot_row[j] = numeric_traits<T>::zero();
m_pivot_row_index.clear();
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
calculate_pivot_row_when_pivot_row_of_B1_is_ready() {
zero_pivot_row();
int i = m_m;
while (i--) {
T pi_1 = m_pivot_row_of_B_1[i];
if (numeric_traits<T>::is_zero(pi_1)) {
continue;
}
for (auto & c : m_A.m_rows[i]) {
unsigned j = c.m_j;
if (m_factorization->m_basis_heading[j] < 0) {
m_pivot_row[j] += c.get_val() * pi_1;
}
}
}
unsigned j = m_pivot_row.size();
while (j--) {
if (!is_zero(m_pivot_row[j]))
m_pivot_row_index.push_back(j);
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
update_x(unsigned entering, X delta) {
if (is_zero(delta)) {
return;
}
m_x[entering] += delta;
for (unsigned i : m_index_of_ed) {
m_copy_of_xB[i] = m_x[m_basis[i]];
m_x[m_basis[i]] -= delta * m_ed[i];
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
print_statistics(X cost) {
std::cout << "cost = " << T_to_string(cost) <<
", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl;
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
print_statistics_with_iterations_and_check_that_the_time_is_over(unsigned total_iterations) {
if (total_iterations % m_settings.report_frequency == 0) {
std::cout << "iterations = " << total_iterations << ", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl;
if (time_is_over()) {
return true;
}
}
return false;
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string str, unsigned total_iterations) {
if (total_iterations % m_settings.report_frequency == 0) {
std::cout << str << " iterations = " << total_iterations << " cost = " << T_to_string(get_cost()) <<", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl;
if (time_is_over()) {
return true;
}
}
return false;
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
print_statistics_with_cost_and_check_that_the_time_is_over(unsigned total_iterations, X cost) {
if (total_iterations % m_settings.report_frequency == 0) {
std::cout << "iterations = " << total_iterations << ", ";
print_statistics(cost);
if (time_is_over()) {
return true;
}
}
return false;
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
print_statistics_and_check_that_the_time_is_over(unsigned total_iterations) {
if (total_iterations % (numeric_traits<T>::precise()? static_cast<unsigned>(m_settings.report_frequency/10) : m_settings.report_frequency) == 0) {
std::cout << "iterations = " << total_iterations << ", ";
if (time_is_over()) {
return true;
}
}
return false;
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
set_non_basic_x_to_correct_bounds() {
for (unsigned j : non_basis()) {
switch (m_column_type[j]) {
case boxed:
m_x[j] = m_d[j] < 0? m_upper_bound_values[j]: m_low_bound_values[j];
break;
case low_bound:
m_x[j] = m_low_bound_values[j];
lean_assert(column_is_dual_feasible(j));
break;
case upper_bound:
m_x[j] = m_upper_bound_values[j];
lean_assert(column_is_dual_feasible(j));
break;
default:
break;
}
}
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
column_is_dual_feasible(unsigned j) {
switch (m_column_type[j]) {
case fixed:
case boxed:
return (x_is_at_low_bound(j) && d_is_not_negative(j)) ||
(x_is_at_upper_bound(j) && d_is_not_positive(j));
case low_bound:
return x_is_at_low_bound(j) && d_is_not_negative(j);
case upper_bound:
std::cout << "upper_bound type should be switched to low_bound" << std::endl;
lean_assert(false); // impossible case
case free_column:
return numeric_traits<X>::is_zero(m_d[j]);
default:
std::cout << "column = " << j << std::endl;
std::cout << "unexpected column type = " << column_type_to_string(m_column_type[j]) << std::endl;
lean_unreachable();
}
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
d_is_not_negative(unsigned j) {
if (numeric_traits<T>::precise()) {
return m_d[j] >= numeric_traits<T>::zero();
}
return m_d[j] > -T(0.00001);
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
d_is_not_positive(unsigned j) {
if (numeric_traits<T>::precise()) {
return m_d[j] <= numeric_traits<T>::zero();
}
return m_d[j] < T(0.00001);
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
time_is_over() {
int span_in_mills = get_millisecond_span(m_start_time);
if (span_in_mills / 1000.0 > m_settings.time_limit) {
std::cout << "time is over" << std::endl;
return true;
}
return false;
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
rs_minus_Anx(std::vector<X> & rs) {
unsigned row = m_m;
while (row--) {
auto &rsv = rs[row] = m_b[row];
for (auto & it : m_A.m_rows[row]) {
unsigned j = it.m_j;
if (m_basis_heading[j] < 0) {
rsv -= m_x[j] * it.get_val();
}
}
}
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
find_x_by_solving() {
solve_Ax_eq_b();
bool ret= !A_mult_x_is_off();
if (ret)
std::cout << "find_x_by_solving succeeded" << std::endl;
else
std::cout << "find_x_by_solving did not succeed" << std::endl;
return ret;
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
update_basis_and_x(int entering, int leaving, X const & tt) {
if (!is_zero(tt)) {
update_x(entering, tt);
if (A_mult_x_is_off() && !find_x_by_solving()) {
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
m_refactor_counter = 0;
if (!find_x_by_solving()) {
restore_x(entering, tt);
lean_assert(!A_mult_x_is_off());
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
m_refactor_counter = 0;
m_iters_with_no_cost_growing++;
if (m_factorization->get_status() != LU_status::OK) {
throw exception(sstream() << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << m_total_iterations);
}
return false;
}
}
}
bool refactor = m_refactor_counter++ >= 200;
if (!refactor) {
const T & pivot = this->m_pivot_row[entering]; // m_ed[m_factorization->basis_heading(leaving)] is the same but the one that we are using is more precise
m_factorization->replace_column(leaving, pivot, m_w);
if (m_factorization->get_status() == LU_status::OK) {
m_factorization->change_basis(entering, leaving);
return true;
}
}
// need to refactor
m_refactor_counter = 0;
m_factorization->change_basis(entering, leaving);
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
if (m_factorization->get_status() != LU_status::OK || A_mult_x_is_off()) {
std::cout << "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << m_total_iterations << std::endl;
restore_x_and_refactor(entering, leaving, tt);
lean_assert(!A_mult_x_is_off());
m_iters_with_no_cost_growing++;
std::cout << "rolled back after failing of init_factorization()" << std::endl;
m_status = UNSTABLE;
return false;
}
return true;
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
init_basis_heading() {
init_basis_heading_and_non_basic_columns_vector(m_basis, m_m, m_basis_heading, m_n, m_non_basic_columns);
lean_assert(basis_heading_is_correct());
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
basis_has_no_doubles() {
std::set<unsigned> bm;
for (unsigned i = 0; i < m_m; i++) {
bm.insert(m_basis[i]);
}
return bm.size() == m_m;
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
non_basis_has_no_doubles() {
std::set<int> bm;
for (auto j : m_non_basic_columns) {
bm.insert(j);
}
return bm.size() == m_non_basic_columns.size();
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
basis_is_correctly_represented_in_heading() {
for (unsigned i = 0; i < m_m; i++) {
if (m_basis_heading[m_basis[i]] != i)
return false;
}
return true;
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
non_basis_is_correctly_represented_in_heading() {
for (int i = 0; i < m_non_basic_columns.size(); i++) {
if (m_basis_heading[m_non_basic_columns[i]] != - i - 1)
return false;
}
for (int j = 0; j < m_A.column_count(); j++) {
if (m_basis_heading[j] >= 0) {
lean_assert(m_basis_heading[j] < m_A.row_count() && m_basis[m_basis_heading[j]] == j);
}
}
return true;
}
template <typename T, typename X> bool lp_core_solver_base<T, X>::
basis_heading_is_correct() {
return basis_has_no_doubles() && non_basis_has_no_doubles() && basis_is_correctly_represented_in_heading() && non_basis_is_correctly_represented_in_heading();
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
restore_x_and_refactor(int entering, int leaving, X const & t) {
m_factorization->restore_basis_change(entering, leaving);
restore_x(entering, t);
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
if (m_factorization->get_status() == LU_status::Degenerated) {
std::cout << "cannot refactor" << std::endl;
m_status = lp_status::FLOATING_POINT_ERROR;
}
// solve_Ax_eq_b();
if (A_mult_x_is_off()) {
std::cout << "cannot restore solution" << std::endl;
m_status = lp_status::FLOATING_POINT_ERROR;
return;
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
restore_x(unsigned entering, X const & t) {
if (is_zero(t)) return;
std::cout << "calling restore for entering " << entering << std::endl;
m_x[entering] -= t;
for (unsigned i : m_index_of_ed) {
m_x[m_basis[i]] = m_copy_of_xB[i];
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
fill_reduced_costs_from_m_y_by_rows() {
unsigned j = m_n;
while (j--) {
if (m_factorization->m_basis_heading[j] < 0)
m_d[j] = m_costs[j];
else
m_d[j] = numeric_traits<T>::zero();
}
unsigned i = m_m;
while (i--) {
const T & y = m_y[i];
if (is_zero(y)) continue;
for (auto & it : m_A.m_rows[i]) {
j = it.m_j;
if (m_factorization->m_basis_heading[j] < 0)
m_d[j] -= y * it.get_val();
}
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
copy_rs_to_xB(std::vector<X> & rs) {
unsigned j = m_m;
while (j--) {
m_x[m_basis[j]] = rs[j];
}
}
template <typename T, typename X> std::string lp_core_solver_base<T, X>::
column_name(unsigned column) const {
auto it = m_column_names.find(column);
if (it == m_column_names.end()) {
std::string name = T_to_string(column);
return std::string(std::string("u") + name);
}
return it->second;
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
copy_right_side(std::vector<X> & rs) {
unsigned i = m_m;
while (i --) {
rs[i] = m_b[i];
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
add_delta_to_xB(std::vector<X> & del) {
unsigned i = m_m;
while (i--) {
this->m_x[this->m_basis[i]] -= del[i];
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
find_error_in_BxB(std::vector<X>& rs){
unsigned row = m_m;
while (row--) {
auto &rsv = rs[row];
for (auto & it : m_A.m_rows[row]) {
unsigned j = it.m_j;
if (m_basis_heading[j] >= 0) {
rsv -= m_x[j] * it.get_val();
}
}
}
}
// recalculates the projection of x to B, such that Ax = b, whereab is the right side
template <typename T, typename X> void lp_core_solver_base<T, X>::
solve_Ax_eq_b() {
std::vector<X> rs(m_m);
rs_minus_Anx(rs);
std::vector<X> rrs = rs; // another copy of rs
m_factorization->solve_By(rs);
copy_rs_to_xB(rs);
find_error_in_BxB(rrs);
m_factorization->solve_By(rrs);
add_delta_to_xB(rrs);
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
snap_non_basic_x_to_bound() {
for (unsigned j : non_basis()) {
switch (m_column_type[j]) {
case fixed:
case boxed:
if (x_is_at_bound(j))
break; // we should preserve x if possible
m_x[j] = m_low_bound_values[j];
break;
case low_bound:
if (x_is_at_low_bound(j))
break;
m_x[j] = m_low_bound_values[j];
break;
case upper_bound:
if (x_is_at_upper_bound(j))
break;
m_x[j] = m_upper_bound_values[j];
break;
default:
break;
}
}
}
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()) {
lean_assert(j < m_x.size());
switch (m_column_type[j]) {
case fixed:
case boxed:
case low_bound:
m_x[j] = m_low_bound_values[j];
break;
case upper_bound:
m_x[j] = m_upper_bound_values[j];
break;
default:
m_x[j] = zero_of_type<X>();
break;
}
}
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
snap_xN_to_bounds() {
snap_non_basic_x_to_bound();
solve_Ax_eq_b();
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
snap_xN_to_bounds_and_free_columns_to_zeroes() {
snap_non_basic_x_to_bound_and_free_to_zeroes();
solve_Ax_eq_b();
}
template <typename T, typename X> void lp_core_solver_base<T, X>::
init_reduced_costs_for_one_iteration() {
solve_yB(m_y);
fill_reduced_costs_from_m_y_by_rows();
}
template <typename T, typename X> non_basic_column_value_position lp_core_solver_base<T, X>::
get_non_basic_column_value_position(unsigned j) {
switch (m_column_type[j]) {
case fixed:
return at_fixed;
case free_column:
return free_of_bounds;
case boxed:
return x_is_at_low_bound(j)? at_low_bound : at_upper_bound;
case low_bound:
return at_low_bound;
case upper_bound:
return at_upper_bound;
default:
lean_unreachable();
}
}
}

View file

@ -10,34 +10,20 @@
#include <string>
#include "util/sstream.h"
#include "util/exception.h"
#include "util/lp/lp.h"
#include "util/lp/core_solver_pretty_printer.h"
#include "util/lp/numeric_pair.h"
#include "util/lp/static_matrix.h"
#include "util/lp/lu.h"
#include "util/lp/permutation_matrix.h"
namespace lean {
void init_basic_part_of_basis_heading(std::vector<unsigned> & basis, unsigned m, std::vector<int> & basis_heading) {
for (unsigned i = 0; i < m; i++) {
unsigned column = basis[i];
basis_heading[column] = i;
}
}
void init_basic_part_of_basis_heading(std::vector<unsigned> & basis, unsigned m, std::vector<int> & basis_heading);
void init_non_basic_part_of_basis_heading(std::vector<int> & basis_heading, std::vector<unsigned> & non_basic_columns, unsigned n) {
for (int j = n; j--;){
if (basis_heading[j] < 0) {
non_basic_columns.push_back(j);
// the index of column j in m_non_basic_columns is (- basis_heading[j] - 1)
basis_heading[j] = - non_basic_columns.size();
}
}
}
void init_non_basic_part_of_basis_heading(std::vector<int> & basis_heading, std::vector<unsigned> & non_basic_columns, unsigned n);
void init_basis_heading_and_non_basic_columns_vector(std::vector<unsigned> & basis,
unsigned m,
std::vector<int> & basis_heading,
unsigned n,
std::vector<unsigned> & non_basic_columns) {
init_basic_part_of_basis_heading(basis, m, basis_heading);
init_non_basic_part_of_basis_heading(basis_heading, non_basic_columns, n);
}
std::vector<unsigned> & non_basic_columns);
template <typename T, typename X> // X represents the type of the x variable and the bounds
class lp_core_solver_base {
@ -82,49 +68,10 @@ public:
const std::unordered_map<unsigned, std::string> & column_names,
std::vector<column_type> & column_types,
std::vector<X> & low_bound_values,
std::vector<X> & upper_bound_values):
m_m(A.row_count()),
m_n(A.column_count()),
m_pivot_row_of_B_1(m_m),
m_pivot_row(m_n, zero_of_type<T>()),
m_A(A),
m_b(b),
m_basis(basis),
m_x(x),
m_costs(costs),
m_settings(settings),
m_y(m_m),
m_status(FEASIBLE),
m_factorization(nullptr),
m_column_names(column_names),
m_w(m_m),
m_d(m_n),
m_ed(m_m),
m_column_type(column_types),
m_low_bound_values(low_bound_values),
m_upper_bound_values(upper_bound_values),
m_column_norms(m_n, T(1)),
m_copy_of_xB(m_m) {
if (m_m) {
lean_assert(m_A.col_val_equal_to_row_val());
init();
init_basis_heading();
}
}
void allocate_basis_heading() { // the rest of initilization will be handled by the factorization class
m_basis_heading.clear();
m_basis_heading.resize(m_n, -1);
}
void init() {
lean_assert(m_costs.size() == m_n);
lean_assert(m_basis.size() == m_m);
lean_assert(m_b.size() == m_m);
allocate_basis_heading();
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
m_refactor_counter = 0;
srand48(1);
}
std::vector<X> & upper_bound_values);
void allocate_basis_heading();
void init();
virtual ~lp_core_solver_base() {
if (m_factorization != nullptr)
@ -142,236 +89,62 @@ public:
return m_status;
}
void fill_cb(T * y){
for (unsigned i = 0; i < m_m; i++) {
y[i] = m_costs[m_basis[i]];
}
}
void fill_cb(T * y);
void fill_cb(std::vector<T> & y){
for (unsigned i = 0; i < m_m; i++)
y[i] = m_costs[m_basis[i]];
}
void fill_cb(std::vector<T> & y);
void solve_yB(std::vector<T> & y) {
fill_cb(y); // now y = cB, that is the projection of costs to basis
m_factorization->solve_yB(y);
}
void solve_yB(std::vector<T> & y);
void update_index_of_ed() {
m_index_of_ed.clear();
unsigned i = m_ed.size();
while (i--) {
if (!is_zero(m_ed[i]))
m_index_of_ed.push_back(i);
}
}
void update_index_of_ed();
void solve_Bd(unsigned entering) {
m_factorization->solve_Bd(entering, m_ed, m_w);
update_index_of_ed();
#ifdef LEAN_DEBUG
// auto B = get_B(m_factorization);
// vector<T> a(m_m);
// m_A.copy_column_to_vector(entering, a);
// vector<T> cd(m_ed);
// B.apply_from_left(cd, m_settings);
// lean_assert(vectors_are_equal(cd , a));
#endif
}
void solve_Bd(unsigned entering);
void pretty_print() {
core_solver_pretty_printer<T, X> pp(*this);
pp.print();
}
void pretty_print();
void save_state(T * w_buffer, T * d_buffer) {
copy_m_w(w_buffer);
copy_m_ed(d_buffer);
}
void save_state(T * w_buffer, T * d_buffer);
void restore_state(T * w_buffer, T * d_buffer) {
restore_m_w(w_buffer);
restore_m_ed(d_buffer);
}
void restore_state(T * w_buffer, T * d_buffer);
X get_cost() {
return dot_product(m_costs, m_x, m_n);
}
void copy_m_w(T * buffer) {
unsigned i = m_m;
while (i --) {
buffer[i] = m_w[i];
}
}
void copy_m_w(T * buffer);
void restore_m_w(T * buffer) {
m_w.m_index.clear();
unsigned i = m_m;
while (i--) {
if (!is_zero(m_w[i] = buffer[i]))
m_w.m_index.push_back(i);
}
}
void restore_m_w(T * buffer);
// needed for debugging
void copy_m_ed(T * buffer) {
unsigned i = m_m;
while (i --) {
buffer[i] = m_ed[i];
}
}
void copy_m_ed(T * buffer);
void restore_m_ed(T * buffer) {
unsigned i = m_m;
while (i --) {
m_ed[i] = buffer[i];
}
}
void restore_m_ed(T * buffer);
bool A_mult_x_is_off() {
if (precise<T>()) {
return false;
}
T feps = convert_struct<T, double>::convert(m_settings.refactor_tolerance);
X one = convert_struct<X, double>::convert(1.0);
for (unsigned i = 0; i < m_m; i++) {
X delta = abs(m_b[i] - m_A.dot_product_with_row(i, m_x));
X eps = feps * (one + T(0.1) * abs(m_b[i]));
if (delta >eps) {
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 = " << m_total_iterations << ")" << std::endl;
return true;
}
}
return false;
}
bool A_mult_x_is_off();
// from page 182 of Istvan Maros's book
void calculate_pivot_row_of_B_1(unsigned pivot_row) {
unsigned i = m_m;
while (i--) {
m_pivot_row_of_B_1[i] = numeric_traits<T>::zero();
}
m_pivot_row_of_B_1[pivot_row] = numeric_traits<T>::one();
m_factorization->solve_yB(m_pivot_row_of_B_1);
}
void calculate_pivot_row_of_B_1(unsigned pivot_row);
void zero_pivot_row() {
for (unsigned j : m_pivot_row_index)
m_pivot_row[j] = numeric_traits<T>::zero();
m_pivot_row_index.clear();
}
void zero_pivot_row();
void calculate_pivot_row_when_pivot_row_of_B1_is_ready() {
zero_pivot_row();
int i = m_m;
while (i--) {
T pi_1 = m_pivot_row_of_B_1[i];
if (numeric_traits<T>::is_zero(pi_1)) {
continue;
}
for (auto & c : m_A.m_rows[i]) {
unsigned j = c.m_j;
if (m_factorization->m_basis_heading[j] < 0) {
m_pivot_row[j] += c.get_val() * pi_1;
}
}
}
void calculate_pivot_row_when_pivot_row_of_B1_is_ready();
unsigned j = m_pivot_row.size();
while (j--) {
if (!is_zero(m_pivot_row[j]))
m_pivot_row_index.push_back(j);
}
}
void update_x(unsigned entering, X delta) {
if (is_zero(delta)) {
return;
}
m_x[entering] += delta;
for (unsigned i : m_index_of_ed) {
m_copy_of_xB[i] = m_x[m_basis[i]];
m_x[m_basis[i]] -= delta * m_ed[i];
}
}
void update_x(unsigned entering, X delta);
T get_var_value(unsigned j) const {
return m_x[j];
}
void print_statistics(X cost) {
std::cout << "cost = " << T_to_string(cost) <<
", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl;
}
void print_statistics(X cost);
bool print_statistics_with_iterations_and_check_that_the_time_is_over(unsigned total_iterations) {
if (total_iterations % m_settings.report_frequency == 0) {
std::cout << "iterations = " << total_iterations << ", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl;
if (time_is_over()) {
return true;
}
}
return false;
}
bool print_statistics_with_iterations_and_check_that_the_time_is_over(unsigned total_iterations);
bool print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string str, unsigned total_iterations) {
if (total_iterations % m_settings.report_frequency == 0) {
std::cout << str << " iterations = " << total_iterations << " cost = " << T_to_string(get_cost()) <<", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl;
if (time_is_over()) {
return true;
}
}
return false;
}
bool print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string str, unsigned total_iterations);
bool print_statistics_with_cost_and_check_that_the_time_is_over(unsigned total_iterations, X cost) {
if (total_iterations % m_settings.report_frequency == 0) {
std::cout << "iterations = " << total_iterations << ", ";
print_statistics(cost);
if (time_is_over()) {
return true;
}
}
return false;
}
bool print_statistics_with_cost_and_check_that_the_time_is_over(unsigned total_iterations, X cost);
bool print_statistics_and_check_that_the_time_is_over(unsigned total_iterations) {
if (total_iterations % (numeric_traits<T>::precise()? static_cast<unsigned>(m_settings.report_frequency/10) : m_settings.report_frequency) == 0) {
std::cout << "iterations = " << total_iterations << ", ";
if (time_is_over()) {
return true;
}
}
return false;
}
bool print_statistics_and_check_that_the_time_is_over(unsigned total_iterations);
void set_non_basic_x_to_correct_bounds() {
for (unsigned j : non_basis()) {
switch (m_column_type[j]) {
case boxed:
m_x[j] = m_d[j] < 0? m_upper_bound_values[j]: m_low_bound_values[j];
break;
case low_bound:
m_x[j] = m_low_bound_values[j];
lean_assert(column_is_dual_feasible(j));
break;
case upper_bound:
m_x[j] = m_upper_bound_values[j];
lean_assert(column_is_dual_feasible(j));
break;
default:
break;
}
}
}
void set_non_basic_x_to_correct_bounds();
bool at_bound(const X &x, const X & bound) const {
return !below_bound(x, bound) && !above_bound(x, bound);
}
@ -393,8 +166,6 @@ public:
bool x_above_upper_bound(unsigned p) {
return above_bound(m_x[p], m_upper_bound_values[p]);
}
bool x_is_at_low_bound(unsigned j) const {
return at_bound(m_x[j], m_low_bound_values[j]);
}
@ -406,216 +177,39 @@ public:
return x_is_at_low_bound(j) || x_is_at_upper_bound(j);
}
bool column_is_dual_feasible(unsigned j) {
switch (m_column_type[j]) {
case fixed:
case boxed:
return (x_is_at_low_bound(j) && d_is_not_negative(j)) ||
(x_is_at_upper_bound(j) && d_is_not_positive(j));
case low_bound:
return x_is_at_low_bound(j) && d_is_not_negative(j);
case upper_bound:
std::cout << "upper_bound type should be switched to low_bound" << std::endl;
lean_assert(false); // impossible case
case free_column:
return numeric_traits<X>::is_zero(m_d[j]);
default:
std::cout << "column = " << j << std::endl;
std::cout << "unexpected column type = " << column_type_to_string(m_column_type[j]) << std::endl;
lean_unreachable();
}
}
bool d_is_not_negative(unsigned j) {
if (numeric_traits<T>::precise()) {
return m_d[j] >= numeric_traits<T>::zero();
}
return m_d[j] > -T(0.00001);
}
bool column_is_dual_feasible(unsigned j);
bool d_is_not_negative(unsigned j);
bool d_is_not_positive(unsigned j) {
if (numeric_traits<T>::precise()) {
return m_d[j] <= numeric_traits<T>::zero();
}
return m_d[j] < T(0.00001);
}
bool d_is_not_positive(unsigned j);
bool time_is_over() {
int span_in_mills = get_millisecond_span(m_start_time);
if (span_in_mills / 1000.0 > m_settings.time_limit) {
std::cout << "time is over" << std::endl;
return true;
}
return false;
}
bool time_is_over();
void rs_minus_Anx(std::vector<X> & rs) {
unsigned row = m_m;
while (row--) {
auto &rsv = rs[row] = m_b[row];
for (auto & it : m_A.m_rows[row]) {
unsigned j = it.m_j;
if (m_basis_heading[j] < 0) {
rsv -= m_x[j] * it.get_val();
}
}
}
}
void rs_minus_Anx(std::vector<X> & rs);
bool find_x_by_solving() {
solve_Ax_eq_b();
bool ret= !A_mult_x_is_off();
if (ret)
std::cout << "find_x_by_solving succeeded" << std::endl;
else
std::cout << "find_x_by_solving did not succeed" << std::endl;
return ret;
}
bool find_x_by_solving();
bool update_basis_and_x(int entering, int leaving, X const & tt) {
if (!is_zero(tt)) {
update_x(entering, tt);
if (A_mult_x_is_off() && !find_x_by_solving()) {
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
m_refactor_counter = 0;
if (!find_x_by_solving()) {
restore_x(entering, tt);
lean_assert(!A_mult_x_is_off());
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
m_refactor_counter = 0;
m_iters_with_no_cost_growing++;
if (m_factorization->get_status() != LU_status::OK) {
throw exception(sstream() << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << m_total_iterations);
}
return false;
}
}
}
bool refactor = m_refactor_counter++ >= 200;
if (!refactor) {
const T & pivot = this->m_pivot_row[entering]; // m_ed[m_factorization->basis_heading(leaving)] is the same but the one that we are using is more precise
m_factorization->replace_column(leaving, pivot, m_w);
if (m_factorization->get_status() == LU_status::OK) {
m_factorization->change_basis(entering, leaving);
return true;
}
}
// need to refactor
m_refactor_counter = 0;
m_factorization->change_basis(entering, leaving);
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
if (m_factorization->get_status() != LU_status::OK || A_mult_x_is_off()) {
std::cout << "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << m_total_iterations << std::endl;
restore_x_and_refactor(entering, leaving, tt);
lean_assert(!A_mult_x_is_off());
m_iters_with_no_cost_growing++;
std::cout << "rolled back after failing of init_factorization()" << std::endl;
m_status = UNSTABLE;
return false;
}
return true;
}
bool update_basis_and_x(int entering, int leaving, X const & tt);
void init_basis_heading() {
init_basis_heading_and_non_basic_columns_vector(m_basis, m_m, m_basis_heading, m_n, m_non_basic_columns);
lean_assert(basis_heading_is_correct());
}
void init_basis_heading();
bool basis_has_no_doubles() {
std::set<unsigned> bm;
for (unsigned i = 0; i < m_m; i++) {
bm.insert(m_basis[i]);
}
return bm.size() == m_m;
}
bool basis_has_no_doubles();
bool non_basis_has_no_doubles() {
std::set<int> bm;
for (auto j : m_non_basic_columns) {
bm.insert(j);
}
return bm.size() == m_non_basic_columns.size();
}
bool non_basis_has_no_doubles();
bool basis_is_correctly_represented_in_heading() {
for (unsigned i = 0; i < m_m; i++) {
if (m_basis_heading[m_basis[i]] != i)
return false;
}
return true;
}
bool non_basis_is_correctly_represented_in_heading() {
for (int i = 0; i < m_non_basic_columns.size(); i++) {
if (m_basis_heading[m_non_basic_columns[i]] != - i - 1)
return false;
}
for (int j = 0; j < m_A.column_count(); j++) {
if (m_basis_heading[j] >= 0) {
lean_assert(m_basis_heading[j] < m_A.row_count() && m_basis[m_basis_heading[j]] == j);
}
}
return true;
}
bool basis_is_correctly_represented_in_heading();
bool non_basis_is_correctly_represented_in_heading();
bool basis_heading_is_correct() {
return basis_has_no_doubles() && non_basis_has_no_doubles() && basis_is_correctly_represented_in_heading() && non_basis_is_correctly_represented_in_heading();
}
bool basis_heading_is_correct();
void restore_x_and_refactor(int entering, int leaving, X const & t) {
m_factorization->restore_basis_change(entering, leaving);
restore_x(entering, t);
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
if (m_factorization->get_status() == LU_status::Degenerated) {
std::cout << "cannot refactor" << std::endl;
m_status = lp_status::FLOATING_POINT_ERROR;
}
// solve_Ax_eq_b();
if (A_mult_x_is_off()) {
std::cout << "cannot restore solution" << std::endl;
m_status = lp_status::FLOATING_POINT_ERROR;
return;
}
}
void restore_x_and_refactor(int entering, int leaving, X const & t);
void restore_x(unsigned entering, X const & t) {
if (is_zero(t)) return;
std::cout << "calling restore for entering " << entering << std::endl;
m_x[entering] -= t;
for (unsigned i : m_index_of_ed) {
m_x[m_basis[i]] = m_copy_of_xB[i];
}
}
void restore_x(unsigned entering, X const & t);
void fill_reduced_costs_from_m_y_by_rows() {
unsigned j = m_n;
while (j--) {
if (m_factorization->m_basis_heading[j] < 0)
m_d[j] = m_costs[j];
else
m_d[j] = numeric_traits<T>::zero();
}
void fill_reduced_costs_from_m_y_by_rows();
unsigned i = m_m;
while (i--) {
const T & y = m_y[i];
if (is_zero(y)) continue;
for (auto & it : m_A.m_rows[i]) {
j = it.m_j;
if (m_factorization->m_basis_heading[j] < 0)
m_d[j] -= y * it.get_val();
}
}
}
void copy_rs_to_xB(std::vector<X> & rs) {
unsigned j = m_m;
while (j--) {
m_x[m_basis[j]] = rs[j];
}
}
void copy_rs_to_xB(std::vector<X> & rs);
virtual bool low_bounds_are_set() const { return false; }
X low_bound_value(unsigned j) const { return m_low_bound_values[j]; }
X upper_bound_value(unsigned j) const { return m_upper_bound_values[j]; }
@ -626,130 +220,29 @@ public:
return m_settings.abs_val_is_smaller_than_pivot_tolerance(m_pivot_row[j]);
}
X bound_span(unsigned j) {
X bound_span(unsigned j) const {
return m_upper_bound_values[j] - m_low_bound_values[j];
}
std::string column_name(unsigned column) const {
auto it = m_column_names.find(column);
if (it == m_column_names.end()) {
std::string name = T_to_string(column);
return std::string(std::string("u") + name);
}
return it->second;
}
std::string column_name(unsigned column) const;
void copy_right_side(std::vector<X> & rs) {
unsigned i = m_m;
while (i --) {
rs[i] = m_b[i];
}
}
void copy_right_side(std::vector<X> & rs);
void add_delta_to_xB(std::vector<X> & del) {
unsigned i = m_m;
while (i--) {
this->m_x[this->m_basis[i]] -= del[i];
}
}
void add_delta_to_xB(std::vector<X> & del);
void find_error_in_BxB(std::vector<X>& rs){
unsigned row = m_m;
while (row--) {
auto &rsv = rs[row];
for (auto & it : m_A.m_rows[row]) {
unsigned j = it.m_j;
if (m_basis_heading[j] >= 0) {
rsv -= m_x[j] * it.get_val();
}
}
}
}
void find_error_in_BxB(std::vector<X>& rs);
// recalculates the projection of x to B, such that Ax = b, whereab is the right side
void solve_Ax_eq_b() {
std::vector<X> rs(m_m);
rs_minus_Anx(rs);
std::vector<X> rrs = rs; // another copy of rs
m_factorization->solve_By(rs);
copy_rs_to_xB(rs);
find_error_in_BxB(rrs);
m_factorization->solve_By(rrs);
add_delta_to_xB(rrs);
}
void solve_Ax_eq_b();
void snap_non_basic_x_to_bound() {
for (unsigned j : non_basis()) {
switch (m_column_type[j]) {
case fixed:
case boxed:
if (x_is_at_bound(j))
break; // we should preserve x if possible
m_x[j] = m_low_bound_values[j];
break;
case low_bound:
if (x_is_at_low_bound(j))
break;
m_x[j] = m_low_bound_values[j];
break;
case upper_bound:
if (x_is_at_upper_bound(j))
break;
m_x[j] = m_upper_bound_values[j];
break;
default:
break;
}
}
}
void snap_non_basic_x_to_bound_and_free_to_zeroes() {
for (unsigned j : non_basis()) {
lean_assert(j < m_x.size());
switch (m_column_type[j]) {
case fixed:
case boxed:
case low_bound:
m_x[j] = m_low_bound_values[j];
break;
case upper_bound:
m_x[j] = m_upper_bound_values[j];
break;
default:
m_x[j] = zero_of_type<X>();
break;
}
}
}
void snap_xN_to_bounds() {
snap_non_basic_x_to_bound();
solve_Ax_eq_b();
}
void snap_non_basic_x_to_bound();
void snap_non_basic_x_to_bound_and_free_to_zeroes();
void snap_xN_to_bounds();
void snap_xN_to_bounds_and_free_columns_to_zeroes() {
snap_non_basic_x_to_bound_and_free_to_zeroes();
solve_Ax_eq_b();
}
void snap_xN_to_bounds_and_free_columns_to_zeroes();
void init_reduced_costs_for_one_iteration() {
solve_yB(m_y);
fill_reduced_costs_from_m_y_by_rows();
}
void init_reduced_costs_for_one_iteration();
non_basic_column_value_position get_non_basic_column_value_position(unsigned j) {
switch (m_column_type[j]) {
case fixed:
return at_fixed;
case free_column:
return free_of_bounds;
case boxed:
return x_is_at_low_bound(j)? at_low_bound : at_upper_bound;
case low_bound:
return at_low_bound;
case upper_bound:
return at_upper_bound;
default:
lean_unreachable();
}
}
non_basic_column_value_position get_non_basic_column_value_position(unsigned j);
};
}

View file

@ -0,0 +1,69 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lp_core_solver_base.cpp"
template bool lean::lp_core_solver_base<double, double>::A_mult_x_is_off();
template bool lean::lp_core_solver_base<double, double>::basis_heading_is_correct();
template void lean::lp_core_solver_base<double, double>::calculate_pivot_row_of_B_1(unsigned int);
template void lean::lp_core_solver_base<double, double>::calculate_pivot_row_when_pivot_row_of_B1_is_ready();
template bool lean::lp_core_solver_base<double, double>::column_is_dual_feasible(unsigned int);
template void lean::lp_core_solver_base<double, double>::fill_reduced_costs_from_m_y_by_rows();
template bool lean::lp_core_solver_base<double, double>::find_x_by_solving();
template lean::non_basic_column_value_position lean::lp_core_solver_base<double, double>::get_non_basic_column_value_position(unsigned int);
template void lean::lp_core_solver_base<double, double>::init_reduced_costs_for_one_iteration();
template lean::lp_core_solver_base<double, double>::lp_core_solver_base(lean::static_matrix<double, double>&, std::vector<double, std::allocator<double> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&, lean::lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&, std::vector<lean::column_type, std::allocator<lean::column_type> >&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&);
template bool lean::lp_core_solver_base<double, double>::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string, unsigned int);
template void lean::lp_core_solver_base<double, double>::restore_x(unsigned int, double const&);
template void lean::lp_core_solver_base<double, double>::set_non_basic_x_to_correct_bounds();
template void lean::lp_core_solver_base<double, double>::snap_xN_to_bounds_and_free_columns_to_zeroes();
template void lean::lp_core_solver_base<double, double>::solve_Ax_eq_b();
template void lean::lp_core_solver_base<double, double>::solve_Bd(unsigned int);
template void lean::lp_core_solver_base<double, double>::solve_yB(std::vector<double, std::allocator<double> >&);
template bool lean::lp_core_solver_base<double, double>::update_basis_and_x(int, int, double const&);
template void lean::lp_core_solver_base<double, double>::update_x(unsigned int, double);
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::A_mult_x_is_off();
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::basis_heading_is_correct();
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::calculate_pivot_row_of_B_1(unsigned int);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::calculate_pivot_row_when_pivot_row_of_B1_is_ready();
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_is_dual_feasible(unsigned int);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::fill_reduced_costs_from_m_y_by_rows();
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::find_x_by_solving();
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::init_reduced_costs_for_one_iteration();
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string, unsigned int);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::restore_x(unsigned int, lean::mpq const&);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::set_non_basic_x_to_correct_bounds();
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_Ax_eq_b();
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_Bd(unsigned int);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_yB(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::update_basis_and_x(int, int, lean::mpq const&);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::update_x(unsigned int, lean::mpq);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calculate_pivot_row_of_B_1(unsigned int);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calculate_pivot_row_when_pivot_row_of_B1_is_ready();
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init();
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_basis_heading();
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_reduced_costs_for_one_iteration();
template lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::lp_core_solver_base(lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, lean::lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&, std::vector<lean::column_type, std::allocator<lean::column_type> >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&);
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_cost_and_check_that_the_time_is_over(unsigned int, lean::numeric_pair<lean::mpq>);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::snap_xN_to_bounds();
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_Bd(unsigned int);
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::update_basis_and_x(int, int, lean::numeric_pair<lean::mpq> const&);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::update_x(unsigned int, lean::numeric_pair<lean::mpq>);
template lean::lp_core_solver_base<lean::mpq, lean::mpq>::lp_core_solver_base(lean::static_matrix<lean::mpq, lean::mpq>&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, lean::lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&, std::vector<lean::column_type, std::allocator<lean::column_type> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_iterations_and_check_that_the_time_is_over(unsigned int);
template std::string lean::lp_core_solver_base<double, double>::column_name(unsigned int) const;
template void lean::lp_core_solver_base<double, double>::pretty_print();
template void lean::lp_core_solver_base<double, double>::restore_state(double*, double*);
template void lean::lp_core_solver_base<double, double>::save_state(double*, double*);
template std::string lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_name(unsigned int) const;
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::pretty_print();
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::restore_state(lean::mpq*, lean::mpq*);
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::save_state(lean::mpq*, lean::mpq*);
template std::string lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::column_name(unsigned int) const;
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::pretty_print();
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::restore_state(lean::mpq*, lean::mpq*);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::save_state(lean::mpq*, lean::mpq*);
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB(std::vector<lean::mpq, std::allocator<lean::mpq> >&);

View file

@ -0,0 +1,732 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lp_dual_core_solver.h"
namespace lean {
template <typename T, typename X>
lp_dual_core_solver<T, X>::lp_dual_core_solver(static_matrix<T, X> & A,
std::vector<bool> & can_enter_basis,
std::vector<X> & b, // the right side std::vector
std::vector<X> & x, // the number of elements in x needs to be at least as large as the number of columns in A
std::vector<unsigned> & basis,
std::vector<T> & costs,
std::vector<column_type> & column_type_array,
std::vector<X> & low_bound_values,
std::vector<X> & upper_bound_values,
lp_settings & settings,
std::unordered_map<unsigned, std::string> const & column_names):
lp_core_solver_base<T, X>(A,
b,
basis,
x,
costs,
settings,
column_names,
column_type_array,
low_bound_values,
upper_bound_values),
m_can_enter_basis(can_enter_basis),
m_a_wave(this->m_m),
m_betas(this->m_m) {
m_harris_tolerance = numeric_traits<T>::precise()? numeric_traits<T>::zero() : T(this->m_settings.harris_feasibility_tolerance);
this->solve_yB(this->m_y);
init_basic_part_of_basis_heading(this->m_basis, this->m_m, this->m_basis_heading);
fill_non_basis_with_only_able_to_enter_columns();
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_a_wave_by_zeros() {
unsigned j = this->m_m;
while (j--) {
m_a_wave[j] = numeric_traits<T>::zero();
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_non_basis_with_only_able_to_enter_columns() {
auto & nb = this->m_factorization->m_non_basic_columns;
nb.clear();
unsigned j = this->m_n;
while (j--) {
if (this->m_basis_heading[j] >= 0 || !m_can_enter_basis[j]) continue;
nb.push_back(j);
this->m_basis_heading[j] = - nb.size();
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::print_nb() {
std::cout << "this is nb " << std::endl;
for (auto l : this->m_factorization->m_non_basic_columns) {
std::cout << l << " ";
}
std::cout << std::endl;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::restore_non_basis() {
auto & nb = this->m_factorization->m_non_basic_columns;
nb.clear();
unsigned j = this->m_n;
while (j--) {
if (this->m_basis_heading[j] >= 0 ) continue;
if (m_can_enter_basis[j]) {
lean_assert(std::find(nb.begin(), nb.end(), j) == nb.end());
nb.push_back(j);
this->m_basis_heading[j] = - nb.size();
}
}
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: update_basis(int entering, int leaving) {
// the second argument is the element of the entering column from the pivot row - its value should be equal to the low diagonal element of the bump after all pivoting is done
if (!(this->m_refactor_counter++ >= 200)) {
this->m_factorization->replace_column(leaving, this->m_ed[this->m_factorization->basis_heading(leaving)], this->m_w);
if (this->m_factorization->get_status() != LU_status::OK) {
std::cout << "failed on replace_column( " << leaving << ", " << this->m_ed[this->m_factorization->basis_heading(leaving)] << ") total_iterations = " << this->m_total_iterations << std::endl;
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings, this->m_non_basic_columns);
this->m_iters_with_no_cost_growing++;
this->m_status = UNSTABLE;
return false;
}
this->m_factorization->change_basis(entering, leaving);
} else { // need to refactor
this->m_factorization->change_basis(entering, leaving);
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings, this->m_non_basic_columns);
if (this->m_factorization->get_status() != LU_status::OK) {
std::cout << "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << this->m_total_iterations << std::endl;
this->m_iters_with_no_cost_growing++;
return false;
}
}
return true;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::recalculate_xB_and_d() {
this->solve_Ax_eq_b();
recalculate_d();
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::recalculate_d() {
this->solve_yB(this->m_y);
this->fill_reduced_costs_from_m_y_by_rows();
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_betas() {
// todo : look at page 194 of Progress in the dual simplex algorithm for solving large scale LP problems : techniques for a fast and stable implementation
// the current implementation is not good enough: todo
unsigned i = this->m_m;
while (i--) {
m_betas[i] = 1;
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::adjust_xb_for_changed_xn_and_init_betas() {
this->solve_Ax_eq_b();
init_betas();
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::start_with_initial_basis_and_make_it_dual_feasible() {
this->set_non_basic_x_to_correct_bounds(); // It is not an efficient version, see 3.29,
// however this version does not require that m_x is the solution of Ax = 0 beforehand
adjust_xb_for_changed_xn_and_init_betas();
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: done() {
if (this->m_status == OPTIMAL) {
return true;
}
if (this->m_total_iterations > this->m_settings.max_total_number_of_iterations) { // debug !!!!
this->m_status = ITERATIONS_EXHAUSTED;
return true;
}
return false; // todo, need to be more cases
}
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_low_bound(unsigned p) {
lean_assert(this->m_basis_heading[p] >= 0 && this->m_basis_heading[p] < this->m_m);
T del = this->m_x[p] - this->m_low_bound_values[p];
del *= del;
return del / this->m_betas[this->m_basis_heading[p]];
}
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_upper_bound(unsigned p) {
lean_assert(this->m_basis_heading[p] >= 0 && this->m_basis_heading[p] < this->m_m);
T del = this->m_x[p] - this->m_upper_bound_values[p];
del *= del;
return del / this->m_betas[this->m_basis_heading[p]];
}
// template <typename T, typename X> void lp_dual_core_solver<T, X>::print_x_and_low_bound(unsigned p) {
// std::cout << "x l[" << p << "] = " << this->m_x[p] << " " << this->m_low_bound_values[p] << std::endl;
// }
// template <typename T, typename X> void lp_dual_core_solver<T, X>::print_x_and_upper_bound(unsigned p) {
// std::cout << "x u[" << p << "] = " << this->m_x[p] << " " << this->m_upper_bound_values[p] << std::endl;
// }
// returns the
template <typename T, typename X> T lp_dual_core_solver<T, X>::pricing_for_row(unsigned i) {
unsigned p = this->m_basis[i];
switch (this->m_column_type[p]) {
case fixed:
case boxed:
if (this->x_below_low_bound(p)) {
T del = get_edge_steepness_for_low_bound(p);
// std::cout << "case boxed low_bound in pricing" << std::endl;
// print_x_and_low_bound(p);
return del;
}
if (this->x_above_upper_bound(p)) {
T del = get_edge_steepness_for_upper_bound(p);
// std::cout << "case boxed at upper_bound in pricing" << std::endl;
// print_x_and_upper_bound(p);
return del;
}
return numeric_traits<T>::zero();
case low_bound:
if (this->x_below_low_bound(p)) {
T del = get_edge_steepness_for_low_bound(p);
// std::cout << "case low_bound in pricing" << std::endl;
// print_x_and_low_bound(p);
return del;
}
return numeric_traits<T>::zero();
break;
case upper_bound:
if (this->x_above_upper_bound(p)) {
T del = get_edge_steepness_for_upper_bound(p);
// std::cout << "case upper_bound in pricing" << std::endl;
// print_x_and_upper_bound(p);
return del;
}
return numeric_traits<T>::zero();
break;
case free_column:
lean_assert(numeric_traits<T>::is_zero(this->m_d[p]));
return numeric_traits<T>::zero();
default:
lean_unreachable();
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows) {
m_r = -1;
T steepest_edge_max = numeric_traits<T>::zero();
unsigned initial_offset_in_rows = offset_in_rows;
unsigned i = offset_in_rows;
unsigned rows_left = number_of_rows_to_try;
do {
loop_start:
T se = pricing_for_row(i);
if (se > steepest_edge_max) {
steepest_edge_max = se;
m_r = i;
}
if (++i == this->m_m) {
i = 0;
}
if (rows_left > 0) {
rows_left--;
}
} while (rows_left || (steepest_edge_max < T(1e-5) && i != initial_offset_in_rows));
if (m_r == -1) {
if (this->m_status != UNSTABLE) {
this->m_status = OPTIMAL;
}
} else {
m_p = this->m_basis[m_r];
m_delta = get_delta();
if (advance_on_known_p()){
return;
}
if (this->m_status == FLOATING_POINT_ERROR) {
return;
}
this->m_status = UNSTABLE;
if (i == initial_offset_in_rows) {
return; // made a full loop
}
m_p = m_r = -1;
steepest_edge_max = numeric_traits<T>::zero();
rows_left = number_of_rows_to_try;
goto loop_start;
// if (this->m_total_iterations % 80 == 0) {
// std::cout << "m_delta = " << m_delta << std::endl;
// }
}
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: advance_on_known_p() {
if (done()) {
return true;
}
this->calculate_pivot_row_of_B_1(m_r);
this->calculate_pivot_row_when_pivot_row_of_B1_is_ready();
if (!ratio_test()) {
return true;
}
calculate_beta_r_precisely();
FTran();
DSE_FTran();
return basis_change_and_update();
}
template <typename T, typename X> int lp_dual_core_solver<T, X>:: define_sign_of_alpha_r() {
switch (this->m_column_type[m_p]) {
case boxed:
case fixed:
if (this->x_below_low_bound(m_p)) {
return -1;
}
if (this->x_above_upper_bound(m_p)) {
return 1;
}
lean_unreachable();
case low_bound:
if (this->x_below_low_bound(m_p)) {
return -1;
}
lean_unreachable();
case upper_bound:
if (this->x_above_upper_bound(m_p)) {
return 1;
}
lean_unreachable();
default:
lean_unreachable();
}
}
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_type[j]) {
case low_bound:
lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_low_bound_values[j]));
return m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
case upper_bound:
lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bound_values[j]));
return m_sign_of_alpha_r * this->m_pivot_row[j] < 0;
case boxed:
{
bool low_bound = this->x_is_at_low_bound(j);
bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
return low_bound == grawing;
}
case fixed: // is always dual feasible so we ingore it
return false;
case free_column:
return true;
default:
return false;
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_breakpoint_set() {
m_breakpoint_set.clear();
for (unsigned j : non_basis()) {
if (can_be_breakpoint(j)) {
m_breakpoint_set.insert(j);
}
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::FTran() {
this->solve_Bd(m_q);
}
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_delta() {
switch (this->m_column_type[m_p]) {
case boxed:
if (this->x_below_low_bound(m_p)) {
return this->m_x[m_p] - this->m_low_bound_values[m_p];
}
if (this->x_above_upper_bound(m_p)) {
return this->m_x[m_p] - this->m_upper_bound_values[m_p];;
}
lean_unreachable();
case low_bound:
if (this->x_below_low_bound(m_p)) {
return this->m_x[m_p] - this->m_low_bound_values[m_p];
}
lean_unreachable();
case upper_bound:
if (this->x_above_upper_bound(m_p)) {
return get_edge_steepness_for_upper_bound(m_p);
}
lean_unreachable();
case fixed:
return this->m_x[m_p] - this->m_upper_bound_values[m_p];;
default:
lean_unreachable();
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::restore_d() {
std::cout << "restore_d" << std::endl;
this->m_d[m_p] = numeric_traits<T>::zero();
for (auto j : non_basis()) {
this->m_d[j] += m_theta_D * this->m_pivot_row[j];
}
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: d_is_correct() {
this->solve_yB(this->m_y);
for (auto j : non_basis()) {
T d = this->m_costs[j] - this->m_A.dot_product_with_column(this->m_y, j);
if (numeric_traits<T>::get_double(abs(d - this->m_d[j])) >= 0.001) {
std::cout << "m_total_iterations = " << this->m_total_iterations << std::endl;
std::cout << "d[" << j << "] = " << this->m_d[j] << " but should be " << d << std::endl;
return false;
}
}
return true;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::xb_minus_delta_p_pivot_column() {
unsigned i = this->m_m;
while (i--) {
this->m_x[this->m_basis[i]] -= m_theta_P * this->m_ed[i];
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::update_betas() { // page 194 of Progress ... todo - once in a while betas have to be reinitialized
T one_over_arq = numeric_traits<T>::one() / this->m_pivot_row[m_q];
T beta_r = this->m_betas[m_r] = std::max(T(0.0001), (m_betas[m_r] * one_over_arq) * one_over_arq);
T k = -2 * one_over_arq;
unsigned i = this->m_m;
while (i--) {
if (i == m_r) continue;
T a = this->m_ed[i];
m_betas[i] += a * (a * beta_r + k * this->m_pivot_row_of_B_1[i]);
if (m_betas[i] < T(0.0001))
m_betas[i] = T(0.0001);
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::apply_flips() {
for (unsigned j : m_flipped_boxed) {
lean_assert(this->x_is_at_bound(j));
if (this->x_is_at_low_bound(j)) {
this->m_x[j] = this->m_upper_bound_values[j];
} else {
this->m_x[j] = this->m_low_bound_values[j];
}
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_column_to_bounds(unsigned j) {
switch (this->m_column_type[j]) {
case fixed:
this->m_x[j] = this->m_low_bound_values[j];
break;
case boxed:
if (this->x_is_at_low_bound(j)) {
this->m_x[j] = this->m_low_bound_values[j];
} else {
this->m_x[j] = this->m_upper_bound_values[j];
}
break;
case low_bound:
this->m_x[j] = this->m_low_bound_values[j];
break;
case upper_bound:
this->m_x[j] = this->m_upper_bound_values[j];
break;
case free_column:
break;
default:
lean_unreachable();
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_to_bounds() {
for (auto j : this->non_basis()) {
snap_xN_column_to_bounds(j);
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_beta_precisely(unsigned i) {
std::vector<T> vec(this->m_m, numeric_traits<T>::zero());
vec[i] = numeric_traits<T>::one();
this->m_factorization->solve_yB(vec);
T beta = numeric_traits<T>::zero();
for (T & v : vec) {
beta += v * v;
}
this->m_betas[i] =beta;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_betas_precisely() {
std::cout << "init beta precisely..." << std::endl;
unsigned i = this->m_m;
while (i--) {
init_beta_precisely(i);
}
std::cout << "done" << std::endl;
}
// step 7 of the algorithm from Progress
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: basis_change_and_update() {
update_betas();
update_d_and_xB();
m_theta_P = m_delta / this->m_ed[m_r];
xb_minus_delta_p_pivot_column();
apply_flips();
this->m_x[m_q] += m_theta_P;
if (!update_basis(m_q, m_p) || this->A_mult_x_is_off() || !problem_is_dual_feasible()) {
revert_to_previous_basis();
this->m_iters_with_no_cost_growing++;
return false;
}
lean_assert(d_is_correct());
return true;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::revert_to_previous_basis() {
std::cout << "recovering basis p = " << m_p << " q = " << m_q << std::endl;
this->m_factorization->change_basis(m_p, m_q);
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings, this->m_non_basic_columns);
if (this->m_factorization->get_status() != LU_status::OK) {
this->m_status = FLOATING_POINT_ERROR;
return;
}
this->m_x[m_q] -= m_theta_P;
snap_xN_to_bounds();
recalculate_xB_and_d();
if (this->A_mult_x_is_off()) {
this->m_status = FLOATING_POINT_ERROR;
return;
}
init_betas_precisely();
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: problem_is_dual_feasible() {
for (unsigned j : 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_bound_values[j] << "," << this->m_upper_bound_values[j] << std::endl;
std::cout << "m_total_iterations = " << this->m_total_iterations << std::endl;
return false;
}
}
return true;
}
template <typename T, typename X> unsigned lp_dual_core_solver<T, X>:: get_number_of_rows_to_try_for_leaving() {
unsigned s = this->m_m;
if (this->m_m > 300) {
s = (unsigned)(s / this->m_settings.percent_of_entering_to_check * 100);
}
return my_random() % s + 1;
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: delta_keeps_the_sign(int initial_delta_sign, const T & delta) {
if (numeric_traits<T>::precise())
return ((delta > numeric_traits<T>::zero()) && (initial_delta_sign == 1)) ||
((delta < numeric_traits<T>::zero()) && (initial_delta_sign == -1));
double del = numeric_traits<T>::get_double(delta);
return ( (del > this->m_settings.zero_tolerance) && (initial_delta_sign == 1)) ||
((del < - this->m_settings.zero_tolerance) && (initial_delta_sign == -1));
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::set_status_to_tentative_dual_unbounded_or_dual_unbounded() {
std::cout << "cost = " << this->get_cost() << std::endl;
if (this->m_status == TENTATIVE_DUAL_UNBOUNDED) {
std::cout << "setting status to DUAL_UNBOUNDED" << std::endl;
this->m_status = DUAL_UNBOUNDED;
} else {
std::cout << "setting to TENTATIVE_DUAL_UNBOUNDED" << std::endl;
this->m_status = TENTATIVE_DUAL_UNBOUNDED;
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::add_tight_breakpoints_and_q_to_flipped_set() {
m_flipped_boxed.insert(m_q);
for (auto j : m_tight_set) {
m_flipped_boxed.insert(j);
}
}
template <typename T, typename X> T lp_dual_core_solver<T, X>::delta_lost_on_flips_of_tight_breakpoints() {
T ret = abs(this->bound_span(m_q) * this->m_pivot_row[m_q]);
for (auto j : m_tight_set) {
ret += abs(this->bound_span(j) * this->m_pivot_row[j]);
}
return ret;
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: tight_breakpoinst_are_all_boxed() {
if (this->m_column_type[m_q] != boxed) return false;
for (auto j : m_tight_set) {
if (this->m_column_type[j] != boxed) return false;
}
return true;
}
template <typename T, typename X> T lp_dual_core_solver<T, X>::calculate_harris_delta_on_breakpoint_set() {
bool first_time = true;
T ret = zero_of_type<T>();
lean_assert(m_breakpoint_set.size() > 0);
for (auto j : m_breakpoint_set) {
T t;
if (this->x_is_at_low_bound(j)) {
t = abs((std::max(this->m_d[j], numeric_traits<T>::zero()) + m_harris_tolerance) / this->m_pivot_row[j]);
} else {
t = abs((std::min(this->m_d[j], numeric_traits<T>::zero()) - m_harris_tolerance) / this->m_pivot_row[j]);
}
if (first_time) {
ret = t;
first_time = false;
} else if (t < ret) {
ret = t;
}
}
return ret;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_tight_set_on_harris_delta(const T & harris_delta ){
m_tight_set.clear();
for (auto j : m_breakpoint_set) {
if (this->x_is_at_low_bound(j)) {
if (abs(std::max(this->m_d[j], numeric_traits<T>::zero()) / this->m_pivot_row[j]) <= harris_delta){
m_tight_set.insert(j);
}
} else {
if (abs(std::min(this->m_d[j], numeric_traits<T>::zero() ) / this->m_pivot_row[j]) <= harris_delta){
m_tight_set.insert(j);
}
}
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_on_tight_set() {
m_q = -1;
T max_pivot;
for (auto j : m_tight_set) {
T r = abs(this->m_pivot_row[j]);
if (m_q != -1) {
if (r > max_pivot) {
max_pivot = r;
m_q = j;
}
} else {
max_pivot = r;
m_q = j;
}
}
m_tight_set.erase(m_q);
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_and_tight_set() {
T harris_del = calculate_harris_delta_on_breakpoint_set();
fill_tight_set_on_harris_delta(harris_del);
find_q_on_tight_set();
lean_assert(m_q != -1);
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::erase_tight_breakpoints_and_q_from_breakpoint_set() {
m_breakpoint_set.erase(m_q);
for (auto j : m_tight_set) {
m_breakpoint_set.erase(j);
}
}
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: ratio_test() {
m_sign_of_alpha_r = define_sign_of_alpha_r();
fill_breakpoint_set();
m_flipped_boxed.clear();
int initial_delta_sign = m_delta >= numeric_traits<T>::zero()? 1: -1;
do {
if (m_breakpoint_set.size() == 0) {
set_status_to_tentative_dual_unbounded_or_dual_unbounded();
return false;
}
this->m_status = FEASIBLE;
find_q_and_tight_set();
if (!tight_breakpoinst_are_all_boxed()) break;
T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign;
if (!delta_keeps_the_sign(initial_delta_sign, del)) break;
if (m_tight_set.size() + 1 == m_breakpoint_set.size()) {
break; // deciding not to flip since we might get stuck without finding m_q, the column entering the basis
}
// we can flip m_q together with the tight set and look for another breakpoint candidate for m_q and another tight set
add_tight_breakpoints_and_q_to_flipped_set();
m_delta = del;
erase_tight_breakpoints_and_q_from_breakpoint_set();
} while (true);
m_theta_D = this->m_d[m_q] / this->m_pivot_row[m_q];
return true;
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::process_flipped() {
init_a_wave_by_zeros();
for (auto j : m_flipped_boxed) {
update_a_wave(signed_span_of_boxed(j), j);
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::update_d_and_xB() {
for (auto j : non_basis()) {
this->m_d[j] -= m_theta_D * this->m_pivot_row[j];
}
this->m_d[m_p] = - m_theta_D;
if (m_flipped_boxed.size() > 0) {
process_flipped();
update_xb_after_bound_flips();
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::calculate_beta_r_precisely() {
T t = numeric_traits<T>::zero();
unsigned i = this->m_m;
while (i--) {
T b = this->m_pivot_row_of_B_1[i];
t += b * b;
}
m_betas[m_r] = t;
}
// see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms"
template <typename T, typename X> void lp_dual_core_solver<T, X>::update_xb_after_bound_flips() {
this->m_factorization->solve_By(m_a_wave);
unsigned i = this->m_m;
while (i--) {
this->m_x[this->m_basis[i]] -= m_a_wave[i];
}
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::one_iteration() {
unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving();
unsigned offset_in_rows = my_random() % this->m_m;
if (this->m_status == TENTATIVE_DUAL_UNBOUNDED) {
number_of_rows_to_try = this->m_m;
} else {
this->m_status = FEASIBLE;
}
pricing_loop(number_of_rows_to_try, offset_in_rows);
lean_assert(problem_is_dual_feasible());
}
template <typename T, typename X> void lp_dual_core_solver<T, X>::solve() { // see the page 35
lean_assert(d_is_correct());
lean_assert(problem_is_dual_feasible());
this->m_start_time = get_millisecond_count();
lean_assert(this->basis_heading_is_correct());
this->m_total_iterations = 0;
this->m_iters_with_no_cost_growing = 0;
do {
if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string(), this->m_total_iterations)){
this->m_status = lp_status::TIME_EXHAUSTED;
return;
}
one_iteration();
this->m_total_iterations++;
} while (this->m_status != FLOATING_POINT_ERROR && this->m_status != DUAL_UNBOUNDED && this->m_status != OPTIMAL &&
this->m_iters_with_no_cost_growing <= this->m_settings.max_number_of_iterations_with_no_improvements
&& this->m_total_iterations <= this->m_settings.max_total_number_of_iterations);
}
}

View file

@ -43,323 +43,49 @@ public:
std::vector<X> & low_bound_values,
std::vector<X> & upper_bound_values,
lp_settings & settings,
std::unordered_map<unsigned, std::string> const & column_names):
lp_core_solver_base<T, X>(A,
b,
basis,
x,
costs,
settings,
column_names,
column_type_array,
low_bound_values,
upper_bound_values),
m_can_enter_basis(can_enter_basis),
m_a_wave(this->m_m),
m_betas(this->m_m) {
m_harris_tolerance = numeric_traits<T>::precise()? numeric_traits<T>::zero() : T(this->m_settings.harris_feasibility_tolerance);
this->solve_yB(this->m_y);
init_basic_part_of_basis_heading(this->m_basis, this->m_m, this->m_basis_heading);
fill_non_basis_with_only_able_to_enter_columns();
}
std::unordered_map<unsigned, std::string> const & column_names);
void init_a_wave_by_zeros() {
unsigned j = this->m_m;
while (j--) {
m_a_wave[j] = numeric_traits<T>::zero();
}
}
void init_a_wave_by_zeros();
void fill_non_basis_with_only_able_to_enter_columns() {
auto & nb = this->m_factorization->m_non_basic_columns;
nb.clear();
unsigned j = this->m_n;
while (j--) {
if (this->m_basis_heading[j] >= 0 || !m_can_enter_basis[j]) continue;
nb.push_back(j);
this->m_basis_heading[j] = - nb.size();
}
}
void fill_non_basis_with_only_able_to_enter_columns();
void print_nb() {
std::cout << "this is nb " << std::endl;
for (auto l : this->m_factorization->m_non_basic_columns) {
std::cout << l << " ";
}
std::cout << std::endl;
}
void print_nb();
void restore_non_basis() {
auto & nb = this->m_factorization->m_non_basic_columns;
nb.clear();
unsigned j = this->m_n;
while (j--) {
if (this->m_basis_heading[j] >= 0 ) continue;
if (m_can_enter_basis[j]) {
lean_assert(std::find(nb.begin(), nb.end(), j) == nb.end());
nb.push_back(j);
this->m_basis_heading[j] = - nb.size();
}
}
}
void restore_non_basis();
bool update_basis(int entering, int leaving) {
// the second argument is the element of the entering column from the pivot row - its value should be equal to the low diagonal element of the bump after all pivoting is done
if (!(this->m_refactor_counter++ >= 200)) {
this->m_factorization->replace_column(leaving, this->m_ed[this->m_factorization->basis_heading(leaving)], this->m_w);
if (this->m_factorization->get_status() != LU_status::OK) {
std::cout << "failed on replace_column( " << leaving << ", " << this->m_ed[this->m_factorization->basis_heading(leaving)] << ") total_iterations = " << this->m_total_iterations << std::endl;
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings, this->m_non_basic_columns);
this->m_iters_with_no_cost_growing++;
this->m_status = UNSTABLE;
return false;
}
this->m_factorization->change_basis(entering, leaving);
} else { // need to refactor
this->m_factorization->change_basis(entering, leaving);
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings, this->m_non_basic_columns);
if (this->m_factorization->get_status() != LU_status::OK) {
std::cout << "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << this->m_total_iterations << std::endl;
this->m_iters_with_no_cost_growing++;
return false;
}
}
return true;
}
bool update_basis(int entering, int leaving);
void recalculate_xB_and_d() {
this->solve_Ax_eq_b();
recalculate_d();
}
void recalculate_xB_and_d();
void recalculate_d() {
this->solve_yB(this->m_y);
this->fill_reduced_costs_from_m_y_by_rows();
}
void recalculate_d();
std::vector<unsigned> & non_basis() { return this->m_factorization->m_non_basic_columns; }
void init_betas() {
// todo : look at page 194 of Progress in the dual simplex algorithm for solving large scale LP problems : techniques for a fast and stable implementation
// the current implementation is not good enough: todo
unsigned i = this->m_m;
while (i--) {
m_betas[i] = 1;
}
}
void init_betas();
void adjust_xb_for_changed_xn_and_init_betas() {
this->solve_Ax_eq_b();
init_betas();
}
void adjust_xb_for_changed_xn_and_init_betas();
void start_with_initial_basis_and_make_it_dual_feasible() {
this->set_non_basic_x_to_correct_bounds(); // It is not an efficient version, see 3.29,
// however this version does not require that m_x is the solution of Ax = 0 beforehand
adjust_xb_for_changed_xn_and_init_betas();
}
void start_with_initial_basis_and_make_it_dual_feasible();
bool done() {
if (this->m_status == OPTIMAL) {
return true;
}
if (this->m_total_iterations > this->m_settings.max_total_number_of_iterations) { // debug !!!!
this->m_status = ITERATIONS_EXHAUSTED;
return true;
}
return false; // todo, need to be more cases
}
bool done();
T get_edge_steepness_for_low_bound(unsigned p) {
lean_assert(this->m_basis_heading[p] >= 0 && this->m_basis_heading[p] < this->m_m);
T del = this->m_x[p] - this->m_low_bound_values[p];
del *= del;
return del / this->m_betas[this->m_basis_heading[p]];
}
T get_edge_steepness_for_low_bound(unsigned p);
T get_edge_steepness_for_upper_bound(unsigned p) {
lean_assert(this->m_basis_heading[p] >= 0 && this->m_basis_heading[p] < this->m_m);
T del = this->m_x[p] - this->m_upper_bound_values[p];
del *= del;
return del / this->m_betas[this->m_basis_heading[p]];
}
T get_edge_steepness_for_upper_bound(unsigned p);
// void print_x_and_low_bound(unsigned p) {
// std::cout << "x l[" << p << "] = " << this->m_x[p] << " " << this->m_low_bound_values[p] << std::endl;
// }
// void print_x_and_upper_bound(unsigned p) {
// std::cout << "x u[" << p << "] = " << this->m_x[p] << " " << this->m_upper_bound_values[p] << std::endl;
// }
T pricing_for_row(unsigned i);
// returns the
T pricing_for_row(unsigned i) {
unsigned p = this->m_basis[i];
switch (this->m_column_type[p]) {
case fixed:
case boxed:
if (this->x_below_low_bound(p)) {
T del = get_edge_steepness_for_low_bound(p);
// std::cout << "case boxed low_bound in pricing" << std::endl;
// print_x_and_low_bound(p);
return del;
}
if (this->x_above_upper_bound(p)) {
T del = get_edge_steepness_for_upper_bound(p);
// std::cout << "case boxed at upper_bound in pricing" << std::endl;
// print_x_and_upper_bound(p);
return del;
}
return numeric_traits<T>::zero();
case low_bound:
if (this->x_below_low_bound(p)) {
T del = get_edge_steepness_for_low_bound(p);
// std::cout << "case low_bound in pricing" << std::endl;
// print_x_and_low_bound(p);
return del;
}
return numeric_traits<T>::zero();
break;
case upper_bound:
if (this->x_above_upper_bound(p)) {
T del = get_edge_steepness_for_upper_bound(p);
// std::cout << "case upper_bound in pricing" << std::endl;
// print_x_and_upper_bound(p);
return del;
}
return numeric_traits<T>::zero();
break;
case free_column:
lean_assert(numeric_traits<T>::is_zero(this->m_d[p]));
return numeric_traits<T>::zero();
default:
lean_unreachable();
}
}
void pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows);
void pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows) {
m_r = -1;
T steepest_edge_max = numeric_traits<T>::zero();
unsigned initial_offset_in_rows = offset_in_rows;
unsigned i = offset_in_rows;
unsigned rows_left = number_of_rows_to_try;
do {
loop_start:
T se = pricing_for_row(i);
if (se > steepest_edge_max) {
steepest_edge_max = se;
m_r = i;
}
if (++i == this->m_m) {
i = 0;
}
if (rows_left > 0) {
rows_left--;
}
} while (rows_left || (steepest_edge_max < T(1e-5) && i != initial_offset_in_rows));
if (m_r == -1) {
if (this->m_status != UNSTABLE) {
this->m_status = OPTIMAL;
}
} else {
m_p = this->m_basis[m_r];
m_delta = get_delta();
if (advance_on_known_p()){
return;
}
if (this->m_status == FLOATING_POINT_ERROR) {
return;
}
this->m_status = UNSTABLE;
if (i == initial_offset_in_rows) {
return; // made a full loop
}
m_p = m_r = -1;
steepest_edge_max = numeric_traits<T>::zero();
rows_left = number_of_rows_to_try;
goto loop_start;
// if (this->m_total_iterations % 80 == 0) {
// std::cout << "m_delta = " << m_delta << std::endl;
// }
}
}
bool advance_on_known_p();
bool advance_on_known_p() {
if (done()) {
return true;
}
this->calculate_pivot_row_of_B_1(m_r);
this->calculate_pivot_row_when_pivot_row_of_B1_is_ready();
if (!ratio_test()) {
return true;
}
calculate_beta_r_precisely();
FTran();
DSE_FTran();
return basis_change_and_update();
}
int define_sign_of_alpha_r();
int define_sign_of_alpha_r() {
switch (this->m_column_type[m_p]) {
case boxed:
case fixed:
if (this->x_below_low_bound(m_p)) {
return -1;
}
if (this->x_above_upper_bound(m_p)) {
return 1;
}
lean_unreachable();
case low_bound:
if (this->x_below_low_bound(m_p)) {
return -1;
}
lean_unreachable();
case upper_bound:
if (this->x_above_upper_bound(m_p)) {
return 1;
}
lean_unreachable();
default:
lean_unreachable();
}
}
bool can_be_breakpoint(unsigned j);
bool can_be_breakpoint(unsigned j) {
if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false;
switch (this->m_column_type[j]) {
case low_bound:
lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_low_bound_values[j]));
return m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
case upper_bound:
lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bound_values[j]));
return m_sign_of_alpha_r * this->m_pivot_row[j] < 0;
case boxed:
{
bool low_bound = this->x_is_at_low_bound(j);
bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
return low_bound == grawing;
}
case fixed: // is always dual feasible so we ingore it
return false;
case free_column:
return true;
default:
return false;
}
}
void fill_breakpoint_set();
void fill_breakpoint_set() {
m_breakpoint_set.clear();
for (unsigned j : non_basis()) {
if (can_be_breakpoint(j)) {
m_breakpoint_set.insert(j);
}
}
}
void FTran() {
this->solve_Bd(m_q);
}
void FTran();
// this calculation is needed for the steepest edge update,
// it hijackes m_pivot_row_of_B_1 for this purpose since we will need it anymore to the end of the cycle
@ -367,410 +93,77 @@ public:
this->m_factorization->solve_By(this->m_pivot_row_of_B_1);
}
T get_delta() {
switch (this->m_column_type[m_p]) {
case boxed:
if (this->x_below_low_bound(m_p)) {
return this->m_x[m_p] - this->m_low_bound_values[m_p];
}
if (this->x_above_upper_bound(m_p)) {
return this->m_x[m_p] - this->m_upper_bound_values[m_p];;
}
lean_unreachable();
case low_bound:
if (this->x_below_low_bound(m_p)) {
return this->m_x[m_p] - this->m_low_bound_values[m_p];
}
lean_unreachable();
case upper_bound:
if (this->x_above_upper_bound(m_p)) {
return get_edge_steepness_for_upper_bound(m_p);
}
lean_unreachable();
case fixed:
return this->m_x[m_p] - this->m_upper_bound_values[m_p];;
default:
lean_unreachable();
}
}
T get_delta();
void restore_d() {
std::cout << "restore_d" << std::endl;
this->m_d[m_p] = numeric_traits<T>::zero();
for (auto j : non_basis()) {
this->m_d[j] += m_theta_D * this->m_pivot_row[j];
}
}
void restore_d();
bool d_is_correct() {
this->solve_yB(this->m_y);
for (auto j : non_basis()) {
T d = this->m_costs[j] - this->m_A.dot_product_with_column(this->m_y, j);
if (numeric_traits<T>::get_double(abs(d - this->m_d[j])) >= 0.001) {
std::cout << "m_total_iterations = " << this->m_total_iterations << std::endl;
std::cout << "d[" << j << "] = " << this->m_d[j] << " but should be " << d << std::endl;
return false;
}
}
return true;
}
bool d_is_correct();
void xb_minus_delta_p_pivot_column() {
unsigned i = this->m_m;
while (i--) {
this->m_x[this->m_basis[i]] -= m_theta_P * this->m_ed[i];
}
}
void xb_minus_delta_p_pivot_column();
void update_betas() { // page 194 of Progress ... todo - once in a while betas have to be reinitialized
T one_over_arq = numeric_traits<T>::one() / this->m_pivot_row[m_q];
T beta_r = this->m_betas[m_r] = std::max(T(0.0001), (m_betas[m_r] * one_over_arq) * one_over_arq);
T k = -2 * one_over_arq;
unsigned i = this->m_m;
while (i--) {
if (i == m_r) continue;
T a = this->m_ed[i];
m_betas[i] += a * (a * beta_r + k * this->m_pivot_row_of_B_1[i]);
if (m_betas[i] < T(0.0001))
m_betas[i] = T(0.0001);
}
}
void update_betas();
void apply_flips() {
for (unsigned j : m_flipped_boxed) {
lean_assert(this->x_is_at_bound(j));
if (this->x_is_at_low_bound(j)) {
this->m_x[j] = this->m_upper_bound_values[j];
} else {
this->m_x[j] = this->m_low_bound_values[j];
}
}
}
void apply_flips();
void snap_xN_column_to_bounds(unsigned j) {
switch (this->m_column_type[j]) {
case fixed:
this->m_x[j] = this->m_low_bound_values[j];
break;
case boxed:
if (this->x_is_at_low_bound(j)) {
this->m_x[j] = this->m_low_bound_values[j];
} else {
this->m_x[j] = this->m_upper_bound_values[j];
}
break;
case low_bound:
this->m_x[j] = this->m_low_bound_values[j];
break;
case upper_bound:
this->m_x[j] = this->m_upper_bound_values[j];
break;
case free_column:
break;
default:
lean_unreachable();
}
}
void snap_xN_column_to_bounds(unsigned j);
void snap_xN_to_bounds() {
for (auto j : this->non_basis()) {
snap_xN_column_to_bounds(j);
}
}
void snap_xN_to_bounds();
void init_beta_precisely(unsigned i) {
std::vector<T> vec(this->m_m, numeric_traits<T>::zero());
vec[i] = numeric_traits<T>::one();
this->m_factorization->solve_yB(vec);
T beta = numeric_traits<T>::zero();
for (T & v : vec) {
beta += v * v;
}
this->m_betas[i] =beta;
}
void init_beta_precisely(unsigned i);
void init_betas_precisely() {
std::cout << "init beta precisely..." << std::endl;
unsigned i = this->m_m;
while (i--) {
init_beta_precisely(i);
}
std::cout << "done" << std::endl;
}
void init_betas_precisely();
// step 7 of the algorithm from Progress
bool basis_change_and_update() {
update_betas();
update_d_and_xB();
m_theta_P = m_delta / this->m_ed[m_r];
xb_minus_delta_p_pivot_column();
apply_flips();
this->m_x[m_q] += m_theta_P;
if (!update_basis(m_q, m_p) || this->A_mult_x_is_off() || !problem_is_dual_feasible()) {
revert_to_previous_basis();
this->m_iters_with_no_cost_growing++;
return false;
}
lean_assert(d_is_correct());
return true;
}
bool basis_change_and_update();
void revert_to_previous_basis() {
std::cout << "recovering basis p = " << m_p << " q = " << m_q << std::endl;
this->m_factorization->change_basis(m_p, m_q);
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings, this->m_non_basic_columns);
if (this->m_factorization->get_status() != LU_status::OK) {
this->m_status = FLOATING_POINT_ERROR;
return;
}
this->m_x[m_q] -= m_theta_P;
snap_xN_to_bounds();
recalculate_xB_and_d();
if (this->A_mult_x_is_off()) {
this->m_status = FLOATING_POINT_ERROR;
return;
}
init_betas_precisely();
}
void revert_to_previous_basis();
bool problem_is_dual_feasible();
bool problem_is_dual_feasible() {
for (unsigned j : 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_bound_values[j] << "," << this->m_upper_bound_values[j] << std::endl;
std::cout << "m_total_iterations = " << this->m_total_iterations << std::endl;
return false;
}
}
return true;
}
unsigned get_number_of_rows_to_try_for_leaving() {
unsigned s = this->m_m;
if (this->m_m > 300) {
s = (unsigned)(s / this->m_settings.percent_of_entering_to_check * 100);
}
return lrand48() % s + 1;
}
unsigned get_number_of_rows_to_try_for_leaving();
void update_a_wave(const T & del, unsigned j) {
this->m_A.add_column_to_vector(del, j, & m_a_wave[0]);
}
bool delta_keeps_the_sign(int initial_delta_sign, const T & delta) {
if (numeric_traits<T>::precise())
return ((delta > numeric_traits<T>::zero()) && (initial_delta_sign == 1)) ||
((delta < numeric_traits<T>::zero()) && (initial_delta_sign == -1));
bool delta_keeps_the_sign(int initial_delta_sign, const T & delta);
double del = numeric_traits<T>::get_double(delta);
return ( (del > this->m_settings.zero_tolerance) && (initial_delta_sign == 1)) ||
((del < - this->m_settings.zero_tolerance) && (initial_delta_sign == -1));
}
void set_status_to_tentative_dual_unbounded_or_dual_unbounded() {
std::cout << "cost = " << this->get_cost() << std::endl;
if (this->m_status == TENTATIVE_DUAL_UNBOUNDED) {
std::cout << "setting status to DUAL_UNBOUNDED" << std::endl;
this->m_status = DUAL_UNBOUNDED;
} else {
std::cout << "setting to TENTATIVE_DUAL_UNBOUNDED" << std::endl;
this->m_status = TENTATIVE_DUAL_UNBOUNDED;
}
}
void set_status_to_tentative_dual_unbounded_or_dual_unbounded();
// it is positive if going from low bound to upper bound and negative if going from upper bound to low bound
T signed_span_of_boxed(unsigned j) {
return this->x_is_at_low_bound(j)? this->bound_span(j): - this->bound_span(j);
}
void add_tight_breakpoints_and_q_to_flipped_set() {
m_flipped_boxed.insert(m_q);
for (auto j : m_tight_set) {
m_flipped_boxed.insert(j);
}
}
void add_tight_breakpoints_and_q_to_flipped_set();
T delta_lost_on_flips_of_tight_breakpoints() {
T ret = abs(this->bound_span(m_q) * this->m_pivot_row[m_q]);
for (auto j : m_tight_set) {
ret += abs(this->bound_span(j) * this->m_pivot_row[j]);
}
return ret;
}
T delta_lost_on_flips_of_tight_breakpoints();
bool tight_breakpoinst_are_all_boxed() {
if (this->m_column_type[m_q] != boxed) return false;
for (auto j : m_tight_set) {
if (this->m_column_type[j] != boxed) return false;
}
return true;
}
bool tight_breakpoinst_are_all_boxed();
T calculate_harris_delta_on_breakpoint_set() {
bool first_time = true;
T ret = zero_of_type<T>();
lean_assert(m_breakpoint_set.size() > 0);
for (auto j : m_breakpoint_set) {
T t;
if (this->x_is_at_low_bound(j)) {
t = abs((std::max(this->m_d[j], numeric_traits<T>::zero()) + m_harris_tolerance) / this->m_pivot_row[j]);
} else {
t = abs((std::min(this->m_d[j], numeric_traits<T>::zero()) - m_harris_tolerance) / this->m_pivot_row[j]);
}
if (first_time) {
ret = t;
first_time = false;
} else if (t < ret) {
ret = t;
}
}
return ret;
}
T calculate_harris_delta_on_breakpoint_set();
void fill_tight_set_on_harris_delta(const T & harris_delta ){
m_tight_set.clear();
for (auto j : m_breakpoint_set) {
if (this->x_is_at_low_bound(j)) {
if (abs(std::max(this->m_d[j], numeric_traits<T>::zero()) / this->m_pivot_row[j]) <= harris_delta){
m_tight_set.insert(j);
}
} else {
if (abs(std::min(this->m_d[j], numeric_traits<T>::zero() ) / this->m_pivot_row[j]) <= harris_delta){
m_tight_set.insert(j);
}
}
}
}
void fill_tight_set_on_harris_delta(const T & harris_delta );
void find_q_on_tight_set() {
m_q = -1;
T max_pivot;
for (auto j : m_tight_set) {
T r = abs(this->m_pivot_row[j]);
if (m_q != -1) {
if (r > max_pivot) {
max_pivot = r;
m_q = j;
}
} else {
max_pivot = r;
m_q = j;
}
}
m_tight_set.erase(m_q);
}
void find_q_on_tight_set();
void find_q_and_tight_set() {
T harris_del = calculate_harris_delta_on_breakpoint_set();
fill_tight_set_on_harris_delta(harris_del);
find_q_on_tight_set();
lean_assert(m_q != -1);
}
void find_q_and_tight_set();
void erase_tight_breakpoints_and_q_from_breakpoint_set() {
m_breakpoint_set.erase(m_q);
for (auto j : m_tight_set) {
m_breakpoint_set.erase(j);
}
}
void erase_tight_breakpoints_and_q_from_breakpoint_set();
bool ratio_test() {
m_sign_of_alpha_r = define_sign_of_alpha_r();
fill_breakpoint_set();
m_flipped_boxed.clear();
int initial_delta_sign = m_delta >= numeric_traits<T>::zero()? 1: -1;
do {
if (m_breakpoint_set.size() == 0) {
set_status_to_tentative_dual_unbounded_or_dual_unbounded();
return false;
}
this->m_status = FEASIBLE;
find_q_and_tight_set();
if (!tight_breakpoinst_are_all_boxed()) break;
T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign;
if (!delta_keeps_the_sign(initial_delta_sign, del)) break;
if (m_tight_set.size() + 1 == m_breakpoint_set.size()) {
break; // deciding not to flip since we might get stuck without finding m_q, the column entering the basis
}
// we can flip m_q together with the tight set and look for another breakpoint candidate for m_q and another tight set
add_tight_breakpoints_and_q_to_flipped_set();
m_delta = del;
erase_tight_breakpoints_and_q_from_breakpoint_set();
} while (true);
m_theta_D = this->m_d[m_q] / this->m_pivot_row[m_q];
return true;
}
bool ratio_test();
void process_flipped() {
init_a_wave_by_zeros();
for (auto j : m_flipped_boxed) {
update_a_wave(signed_span_of_boxed(j), j);
}
}
void update_d_and_xB() {
for (auto j : non_basis()) {
this->m_d[j] -= m_theta_D * this->m_pivot_row[j];
}
this->m_d[m_p] = - m_theta_D;
if (m_flipped_boxed.size() > 0) {
process_flipped();
update_xb_after_bound_flips();
}
}
void process_flipped();
void update_d_and_xB();
void calculate_beta_r_precisely() {
T t = numeric_traits<T>::zero();
unsigned i = this->m_m;
while (i--) {
T b = this->m_pivot_row_of_B_1[i];
t += b * b;
}
m_betas[m_r] = t;
}
void calculate_beta_r_precisely();
// see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms"
void update_xb_after_bound_flips() {
this->m_factorization->solve_By(m_a_wave);
unsigned i = this->m_m;
while (i--) {
this->m_x[this->m_basis[i]] -= m_a_wave[i];
}
}
void update_xb_after_bound_flips();
void one_iteration() {
unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving();
unsigned offset_in_rows = lrand48() % this->m_m;
if (this->m_status == TENTATIVE_DUAL_UNBOUNDED) {
number_of_rows_to_try = this->m_m;
} else {
this->m_status = FEASIBLE;
}
pricing_loop(number_of_rows_to_try, offset_in_rows);
lean_assert(problem_is_dual_feasible());
}
void one_iteration();
void solve() { // see the page 35
lean_assert(d_is_correct());
lean_assert(problem_is_dual_feasible());
this->m_start_time = get_millisecond_count();
lean_assert(this->basis_heading_is_correct());
this->m_total_iterations = 0;
this->m_iters_with_no_cost_growing = 0;
do {
if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string(), this->m_total_iterations)){
this->m_status = lp_status::TIME_EXHAUSTED;
return;
}
one_iteration();
this->m_total_iterations++;
} while (this->m_status != FLOATING_POINT_ERROR && this->m_status != DUAL_UNBOUNDED && this->m_status != OPTIMAL &&
this->m_iters_with_no_cost_growing <= this->m_settings.max_number_of_iterations_with_no_improvements
&& this->m_total_iterations <= this->m_settings.max_total_number_of_iterations);
}
void solve();
bool low_bounds_are_set() const { return true; }
};

View file

@ -0,0 +1,15 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lp_dual_core_solver.cpp"
template lean::lp_dual_core_solver<lean::mpq, lean::mpq>::lp_dual_core_solver(lean::static_matrix<lean::mpq, lean::mpq>&, std::vector<bool, std::allocator<bool> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<lean::column_type, std::allocator<lean::column_type> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, lean::lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&);
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::start_with_initial_basis_and_make_it_dual_feasible();
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::solve();
template lean::lp_dual_core_solver<double, double>::lp_dual_core_solver(lean::static_matrix<double, double>&, std::vector<bool, std::allocator<bool> >&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<double, std::allocator<double> >&, std::vector<lean::column_type, std::allocator<lean::column_type> >&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&, lean::lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&);
template void lean::lp_dual_core_solver<double, double>::start_with_initial_basis_and_make_it_dual_feasible();
template void lean::lp_dual_core_solver<double, double>::solve();
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::restore_non_basis();
template void lean::lp_dual_core_solver<double, double>::restore_non_basis();

View file

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

View file

@ -27,254 +27,33 @@ public:
}
}
void decide_on_status_after_stage1() {
switch (m_core_solver->get_status()) {
case OPTIMAL:
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
this->m_status = FEASIBLE;
std::cout << "status is FEASIBLE" << std::endl;
} else {
std::cout << "status is UNBOUNDED" << std::endl;
this->m_status = UNBOUNDED;
}
break;
case DUAL_UNBOUNDED:
lean_unreachable();
case ITERATIONS_EXHAUSTED:
std::cout << "status is ITERATIONS_EXHAUSTED" << std::endl;
this->m_status = ITERATIONS_EXHAUSTED;
break;
case TIME_EXHAUSTED:
std::cout << "status is TIME_EXHAUSTED" << std::endl;
this->m_status = TIME_EXHAUSTED;
break;
case FLOATING_POINT_ERROR:
std::cout << "status is FLOATING_POINT_ERROR" << std::endl;
this->m_status = FLOATING_POINT_ERROR;
break;
default:
lean_unreachable();
}
}
void decide_on_status_after_stage1();
void fix_logical_for_stage2(unsigned j) {
lean_assert(j >= this->number_of_core_structurals());
switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) {
case low_bound:
m_low_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = low_bound;
m_can_enter_basis[j] = true;
break;
case fixed:
this->m_upper_bounds[j] = m_low_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = fixed;
m_can_enter_basis[j] = false;
break;
default:
lean_unreachable();
}
}
void fix_logical_for_stage2(unsigned j);
void fix_structural_for_stage2(unsigned j) {
column_info<T> * ci = this->m_columns[this->m_core_solver_columns_to_external_columns[j]];
switch (ci->get_column_type()) {
case low_bound:
m_low_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = low_bound;
m_can_enter_basis[j] = true;
break;
case fixed:
case upper_bound:
lean_unreachable();
case boxed:
this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j];
m_low_bounds[j] = numeric_traits<T>::zero();
m_column_types_of_core_solver[j] = boxed;
m_can_enter_basis[j] = true;
break;
case free_column:
m_can_enter_basis[j] = true;
m_column_types_of_core_solver[j] = free_column;
break;
default:
lean_unreachable();
}
T cost_was = this->m_costs[j];
this->set_scaled_cost(j);
bool in_basis = m_core_solver->m_factorization->m_basis_heading[j] >= 0;
if (in_basis && cost_was != this->m_costs[j]) {
std::cout << "cost change in basis" << std::endl;
}
}
void fix_structural_for_stage2(unsigned j);
void unmark_boxed_and_fixed_columns_and_fix_structural_costs() {
unsigned j = this->m_A->column_count();
while (j-- > this->number_of_core_structurals()) {
fix_logical_for_stage2(j);
}
j = this->number_of_core_structurals();
while (j--) {
fix_structural_for_stage2(j);
}
}
void unmark_boxed_and_fixed_columns_and_fix_structural_costs();
void restore_right_sides() {
unsigned i = this->m_A->row_count();
while (i--) {
this->m_b[i] = m_b_copy[i];
}
}
void restore_right_sides();
void solve_for_stage2() {
m_core_solver->restore_non_basis();
m_core_solver->solve_yB(m_core_solver->m_y);
m_core_solver->fill_reduced_costs_from_m_y_by_rows();
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible();
m_core_solver->set_status(FEASIBLE);
m_core_solver->solve();
switch (m_core_solver->get_status()) {
case OPTIMAL:
this->m_status = OPTIMAL;
break;
case DUAL_UNBOUNDED:
this->m_status = INFEASIBLE;
break;
case TIME_EXHAUSTED:
this->m_status = TIME_EXHAUSTED;
break;
case FLOATING_POINT_ERROR:
this->m_status = FLOATING_POINT_ERROR;
break;
default:
lean_unreachable();
}
this->m_second_stage_iterations = m_core_solver->m_total_iterations;
}
void solve_for_stage2();
void fill_x_with_zeros() {
unsigned j = this->m_A->column_count();
while (j--) {
this->m_x[j] = numeric_traits<T>::zero();
}
}
void fill_x_with_zeros();
void stage1() {
lean_assert(m_core_solver == nullptr);
this->m_x.resize(this->m_A->column_count(), numeric_traits<T>::zero());
this->print_statistics_on_A();
m_core_solver = new lp_dual_core_solver<T, X>(
*this->m_A,
m_can_enter_basis,
this->m_b, // the right side vector
this->m_x,
this->m_basis,
this->m_costs,
this->m_column_types_of_core_solver,
this->m_low_bounds,
this->m_upper_bounds,
this->m_settings,
this->m_name_map);
m_core_solver->fill_reduced_costs_from_m_y_by_rows();
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible();
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
std::cout << "skipping stage 1" << std::endl;
m_core_solver->set_status(OPTIMAL);
m_core_solver->m_total_iterations = 0;
} else {
std::cout << "stage 1" << std::endl;
m_core_solver->solve();
}
decide_on_status_after_stage1();
this->m_first_stage_iterations = m_core_solver->m_total_iterations;
}
void stage1();
void stage2() {
std::cout << "starting stage2" << std::endl;
unmark_boxed_and_fixed_columns_and_fix_structural_costs();
restore_right_sides();
solve_for_stage2();
}
void stage2();
void fill_first_stage_solver_fields() {
unsigned slack_var = this->number_of_core_structurals();
unsigned artificial = this->number_of_core_structurals() + this->m_slacks;
void fill_first_stage_solver_fields();
for (unsigned row = 0; row < this->row_count(); row++) {
fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial);
}
fill_costs_and_bounds_and_column_types_for_the_first_stage_solver();
}
column_type get_column_type(unsigned j);
column_type get_column_type(unsigned j) {
lean_assert(j < this->m_A->column_count());
if (j >= this->number_of_core_structurals()) {
return m_column_types_of_logicals[j - this->number_of_core_structurals()];
}
return this->m_columns[this->m_core_solver_columns_to_external_columns[j]]->get_column_type();
}
void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j);
void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) {
// see 4.7 in the dissertation of Achim Koberstein
lean_assert(this->m_core_solver_columns_to_external_columns.find(j) !=
this->m_core_solver_columns_to_external_columns.end());
void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j);
T free_bound = T(1e4); // see 4.8
unsigned jj = this->m_core_solver_columns_to_external_columns[j];
lean_assert(this->m_columns.find(jj) != this->m_columns.end());
column_info<T> * ci = this->m_columns[jj];
switch (ci->get_column_type()) {
case upper_bound:
throw exception(sstream() << "unexpected bound type " << j << " "
<< column_type_to_string(get_column_type(j)));
case low_bound: {
m_can_enter_basis[j] = true;
this->set_scaled_cost(j);
this->m_low_bounds[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] =numeric_traits<T>::one();
break;
}
case free_column: {
m_can_enter_basis[j] = true;
this->set_scaled_cost(j);
this->m_upper_bounds[j] = free_bound;
this->m_low_bounds[j] = -free_bound;
break;
}
case boxed:
m_can_enter_basis[j] = false;
this->m_costs[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] = this->m_low_bounds[j] = numeric_traits<T>::zero(); // is it needed?
break;
default:
lean_unreachable();
}
m_column_types_of_core_solver[j] = boxed;
}
void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) {
this->m_costs[j] = 0;
lean_assert(get_column_type(j) != upper_bound);
if ((m_can_enter_basis[j] = (get_column_type(j) == low_bound))) {
m_column_types_of_core_solver[j] = boxed;
this->m_low_bounds[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] = numeric_traits<T>::one();
} else {
m_column_types_of_core_solver[j] = fixed;
this->m_low_bounds[j] = numeric_traits<T>::zero();
this->m_upper_bounds[j] = numeric_traits<T>::zero();
}
}
void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() {
unsigned j = this->m_A->column_count();
while (j-- > this->number_of_core_structurals()) { // go over logicals here
fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j);
}
j = this->number_of_core_structurals();
while (j--) {
fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j);
}
}
void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver();
void set_type_for_logical(unsigned j, column_type col_type) {
this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type;
@ -282,115 +61,20 @@ public:
void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row,
unsigned & slack_var,
unsigned & artificial) {
lean_assert(row < this->row_count());
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]];
// we need to bring the program to the form Ax = b
T rs = this->m_b[row];
switch (constraint.m_relation) {
case Equal: // no slack variable here
set_type_for_logical(artificial, fixed);
this->m_basis[row] = artificial;
this->m_costs[artificial] = numeric_traits<T>::zero();
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
artificial++;
break;
unsigned & artificial);
case Greater_or_equal:
set_type_for_logical(slack_var, low_bound);
(*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
if (rs > 0) {
// adding one artificial
set_type_for_logical(artificial, fixed);
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
this->m_basis[row] = artificial;
this->m_costs[artificial] = numeric_traits<T>::zero();
artificial++;
} else {
// we can put a slack_var into the basis, and avoid adding an artificial variable
this->m_basis[row] = slack_var;
this->m_costs[slack_var] = numeric_traits<T>::zero();
}
slack_var++;
break;
case Less_or_equal:
// introduce a non-negative slack variable
set_type_for_logical(slack_var, low_bound);
(*this->m_A)(row, slack_var) = numeric_traits<T>::one();
if (rs < 0) {
// adding one artificial
set_type_for_logical(artificial, fixed);
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
this->m_basis[row] = artificial;
this->m_costs[artificial] = numeric_traits<T>::zero();
artificial++;
} else {
// we can put slack_var into the basis, and avoid adding an artificial variable
this->m_basis[row] = slack_var;
this->m_costs[slack_var] = numeric_traits<T>::zero();
}
slack_var++;
break;
}
}
void augment_matrix_A_and_fill_x_and_allocate_some_fields() {
this->count_slacks_and_artificials();
this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials);
unsigned n = this->m_A->column_count();
this->m_column_types_of_core_solver.resize(n);
m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials);
this->m_costs.resize(n);
this->m_upper_bounds.resize(n);
this->m_low_bounds.resize(n);
m_can_enter_basis.resize(n);
this->m_basis.resize(this->m_A->row_count());
}
void augment_matrix_A_and_fill_x_and_allocate_some_fields();
void copy_m_b_aside_and_set_it_to_zeros() {
for (int i = 0; i < this->m_b.size(); i++) {
m_b_copy.push_back(this->m_b[i]);
this->m_b[i] = numeric_traits<T>::zero(); // preparing for the first stage
}
}
void copy_m_b_aside_and_set_it_to_zeros();
void find_maximal_solution(){
if (this->problem_is_empty()) {
this->m_status = lp_status::EMPTY;
return;
}
this->flip_costs(); // do it for now, todo ( remove the flipping)
this->cleanup();
if (this->m_status == INFEASIBLE) {
return;
}
this->fill_matrix_A_and_init_right_side();
this->fill_m_b();
this->scale();
augment_matrix_A_and_fill_x_and_allocate_some_fields();
fill_first_stage_solver_fields();
this->fill_column_names_for_core_solver();
copy_m_b_aside_and_set_it_to_zeros();
stage1();
if (this->m_status == FEASIBLE) {
stage2();
}
}
void find_maximal_solution();
virtual T get_column_value(unsigned column) const {
return this->get_column_value_with_core_solver(column, m_core_solver);
}
T get_current_cost() const {
T ret = numeric_traits<T>::zero();
for (auto it : this->m_columns) {
ret += this->get_column_cost_value(it.first, it.second);
}
return -ret; // we flip costs for now
}
T get_current_cost() const;
};
}

View file

@ -0,0 +1,11 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lp_dual_simplex.cpp"
template lean::mpq lean::lp_dual_simplex<lean::mpq, lean::mpq>::get_current_cost() const;
template void lean::lp_dual_simplex<lean::mpq, lean::mpq>::find_maximal_solution();
template double lean::lp_dual_simplex<double, double>::get_current_cost() const;
template void lean::lp_dual_simplex<double, double>::find_maximal_solution();

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,20 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lar_solver.h"
#include "util/lp/lp_primal_core_solver.cpp"
namespace lean {
// template void lar_solver::find_solution_signature_with_doubles(lar_solution_signature&);
template void lp_primal_core_solver<double, double>::find_feasible_solution();
template lp_primal_core_solver<double, double>::lp_primal_core_solver(static_matrix<double, double>&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<double, std::allocator<double> >&, std::vector<column_type, std::allocator<column_type> >&, std::vector<double, std::allocator<double> >&, lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&);
template lp_primal_core_solver<double, double>::lp_primal_core_solver(static_matrix<double, double>&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<double, std::allocator<double> >&, std::vector<column_type, std::allocator<column_type> >&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&, lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&);
template unsigned lp_primal_core_solver<double, double>::solve();
template lp_primal_core_solver<mpq, mpq>::lp_primal_core_solver(static_matrix<mpq, mpq>&, std::vector<mpq, std::allocator<mpq> >&, std::vector<mpq, std::allocator<mpq> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<mpq, std::allocator<mpq> >&, std::vector<column_type, std::allocator<column_type> >&, std::vector<mpq, std::allocator<mpq> >&, std::vector<mpq, std::allocator<mpq> >&, lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&);
template unsigned lp_primal_core_solver<mpq, mpq>::solve();
//template void lp_primal_simplex<double, double>::solve_with_total_inf();
//template void lp_primal_simplex<mpq, mpq>::solve_with_total_inf();
}

View file

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

View file

@ -23,64 +23,21 @@ class lp_primal_simplex: public lp_solver<T, X> {
private:
unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); }
void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) {
unsigned slack_var = original_number_of_columns;
unsigned artificial = original_number_of_columns + this->m_slacks;
void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns);
for (unsigned row = 0; row < this->row_count(); row++) {
fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial);
}
}
void init_buffer(unsigned k, std::vector<T> & r);
void init_buffer(unsigned k, std::vector<T> & r) {
for (unsigned i = 0; i < k; i++) {
r[i] = 0;
}
r[k] = 1;
for (unsigned i = this->row_count() -1; i > k; i--) {
r[i] = 0;
}
}
void refactor();
void refactor() {
m_core_solver->init_lu();
if (m_core_solver->factorization()->get_status() != LU_status::OK) {
throw exception("cannot refactor");
}
}
void set_scaled_costs();
void set_scaled_costs() {
unsigned j = this->number_of_core_structurals();
while (j-- > 0) {
this->set_scaled_cost(j);
}
}
void stage_two() {
std::cout << "starting stage 2" << std::endl;
lean_assert(!m_core_solver->A_mult_x_is_off());
int j = this->m_A->column_count() - 1;
unsigned core_solver_cols = this->number_of_core_structurals();
while (j >= core_solver_cols) {
this->m_costs[j--] = numeric_traits<T>::zero();
}
set_scaled_costs();
m_core_solver->set_status(lp_status::FEASIBLE);
this->m_second_stage_iterations = m_core_solver->solve();
this->m_status = m_core_solver->get_status();
// std::cout << "status is " << lp_status_to_string(this->m_status) << std::endl;
}
void stage_two();
public:
lp_primal_simplex() {}
column_info<T> * get_or_create_column_info(unsigned column) {
auto it = this->m_columns.find(column);
return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info<T>) : it->second;
}
column_info<T> * get_or_create_column_info(unsigned column);
void set_status(lp_status status) {
this->m_status = status;
@ -90,327 +47,48 @@ public:
return this->m_status;
}
void fill_acceptable_values_for_x() {
for (auto t : this->m_core_solver_columns_to_external_columns) {
this->m_x[t.first] = numeric_traits<T>::zero();
lean_assert(this->m_x[t.first] >= 0);
}
}
void fill_acceptable_values_for_x();
void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) {
bound_is_set[i] = true;
bounds[i] = numeric_traits<T>::zero();
}
void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i);
void fill_costs_and_x_for_first_stage_solver_for_row(
int row,
unsigned & slack_var,
unsigned & artificial) {
lean_assert(row >= 0 && row < this->row_count());
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]];
// we need to bring the program to the form Ax = b
T rs = this->m_b[row];
T artificial_cost = - numeric_traits<T>::one();
switch (constraint.m_relation) {
case Equal: // no slack variable here
this->m_column_types[artificial] = low_bound;
this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero
this->m_basis[row] = artificial;
if (rs >= 0) {
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
this->m_x[artificial] = rs;
} else {
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
this->m_x[artificial] = - rs;
}
artificial++;
break;
case Greater_or_equal:
this->m_column_types[slack_var] = low_bound;
(*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
if (rs > 0) {
lean_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
// adding one artificial
this->m_column_types[artificial] = low_bound;
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
this->m_costs[artificial] = artificial_cost;
this->m_basis[row] = artificial;
this->m_x[artificial] = rs;
artificial++;
} else {
// we can put a slack_var into the basis, and avoid adding an artificial variable
this->m_basis[row] = slack_var;
this->m_x[slack_var] = - rs;
}
slack_var++;
break;
case Less_or_equal:
// introduce a non-negative slack variable
this->m_column_types[slack_var] = low_bound;
(*this->m_A)(row, slack_var) = numeric_traits<T>::one();
if (rs < 0) {
// adding one artificial
lean_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
this->m_column_types[artificial] = low_bound;
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
this->m_costs[artificial] = artificial_cost;
this->m_x[artificial] = - rs;
this->m_basis[row] = artificial++;
} else {
// we can put slack_var into the basis, and avoid adding an artificial variable
this->m_basis[row] = slack_var;
this->m_x[slack_var] = rs;
}
slack_var++;
break;
}
}
unsigned & artificial);
std::string name_of_core_solver_column(unsigned j) { // j here is the core solver index
unsigned external_j = this->m_core_solver_columns_to_external_columns[j];
auto t = this->m_columns.find(external_j);
if (t == this->m_columns.end()) {
return std::string("name_not_found");
}
return t->m_name;
}
std::string name_of_core_solver_column(unsigned j);
void set_core_solver_bounds() {
unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials;
this->m_column_types.resize(total_vars);
this->m_upper_bounds.resize(total_vars);
for (auto cit : this->m_columns) {
column_info<T> * ci = cit.second;
auto p = this->m_external_columns_to_core_solver_columns.find(cit.first);
if (p == this->m_external_columns_to_core_solver_columns.end()) continue;
unsigned j = p->second;
switch (this->m_column_types[j] = ci->get_column_type()){
case fixed:
this->m_upper_bounds[j] = numeric_traits<T>::zero();
break;
case boxed:
this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j];
break;
default: break; // do nothing
}
}
}
void set_core_solver_bounds();
void update_time_limit_from_starting_time(int start_time) {
this->m_settings.time_limit -= (get_millisecond_span(start_time) / 1000.);
}
void find_maximal_solution() {
int preprocessing_start_time = get_millisecond_count();
if (this->problem_is_empty()) {
this->m_status = lp_status::EMPTY;
return;
}
void find_maximal_solution();
this->cleanup();
this->fill_matrix_A_and_init_right_side();
if (this->m_status == lp_status::INFEASIBLE) {
return;
}
this->m_x.resize(this->m_A->column_count());
this->fill_m_b();
this->scale();
fill_acceptable_values_for_x();
this->count_slacks_and_artificials();
set_core_solver_bounds();
update_time_limit_from_starting_time(preprocessing_start_time);
solve_with_total_inf();
}
void fill_A_x_and_basis_for_stage_one_total_inf();
void fill_A_x_and_basis_for_stage_one_total_inf() {
for (unsigned row = 0; row < this->row_count(); row++)
fill_A_x_and_basis_for_stage_one_total_inf_for_row(row);
}
void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row);
void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) {
lean_assert(row < this->row_count());
auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row);
lean_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end());
unsigned ext_row = ext_row_it->second;
auto constr_it = this->m_constraints.find(ext_row);
lean_assert(constr_it != this->m_constraints.end());
auto & constraint = constr_it->second;
unsigned j = this->m_A->column_count(); // j is a slack variable
this->m_A->add_column();
// we need to bring the program to the form Ax = b
this->m_basis[row] = j;
switch (constraint.m_relation) {
case Equal:
this->m_x[j] = this->m_b[row];
(*this->m_A)(row, j) = numeric_traits<T>::one();
this->m_column_types[j] = fixed;
this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>();
break;
case Greater_or_equal:
this->m_x[j] = - this->m_b[row];
(*this->m_A)(row, j) = - numeric_traits<T>::one();
this->m_column_types[j] = low_bound;
this->m_upper_bounds[j] = zero_of_type<X>();
break;
case Less_or_equal:
this->m_x[j] = this->m_b[row];
(*this->m_A)(row, j) = numeric_traits<T>::one();
this->m_column_types[j] = low_bound;
this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>();
break;
}
}
void solve_with_total_inf() {
std::cout << "starting solve_with_total_inf()" << std::endl;
int total_vars = this->m_A->column_count() + this->row_count();
m_low_bounds.clear();
m_low_bounds.resize(total_vars, zero_of_type<X>()); // low bounds are shifted ot zero
this->m_x.resize(total_vars, numeric_traits<T>::zero());
this->m_basis.resize(this->row_count());
this->m_costs.clear();
this->m_costs.resize(total_vars, zero_of_type<T>());
fill_A_x_and_basis_for_stage_one_total_inf();
this->print_statistics_on_A();
this->fill_column_names_for_core_solver();
int j = this->m_A->column_count() - 1;
unsigned core_solver_cols = this->number_of_core_structurals();
while (j >= core_solver_cols)
this->m_costs[j--] = numeric_traits<T>::zero();
set_scaled_costs();
m_core_solver = new lp_primal_core_solver<T, X>(*this->m_A,
this->m_b,
this->m_x,
this->m_basis,
this->m_costs,
this->m_column_types,
m_low_bounds,
this->m_upper_bounds,
this->m_settings, this->m_name_map);
m_core_solver->solve();
this->m_status = m_core_solver->m_status;
this->m_total_iterations = m_core_solver->m_total_iterations;
}
void solve_with_total_inf();
~lp_primal_simplex() {
if (m_core_solver != nullptr) {
delete m_core_solver;
}
}
~lp_primal_simplex();
bool bounds_hold(std::unordered_map<std::string, T> const & solution) {
for (auto it : this->m_columns) {
auto sol_it = solution.find(it.second->get_name());
if (sol_it == solution.end()) {
throw exception(sstream() << "cannot find column " << it.first << " in solutio");
}
bool bounds_hold(std::unordered_map<std::string, T> const & solution);
if (!it.second->bounds_hold(sol_it->second)) {
std::cout << "bounds do not hold for " << it.second->get_name() << std::endl;
it.second->bounds_hold(sol_it->second);
return false;
}
}
return true;
}
T get_row_value(unsigned i, std::unordered_map<std::string, T> const & solution, bool print);
T get_row_value(unsigned i, std::unordered_map<std::string, T> const & solution, bool print) {
auto it = this->m_A_values.find(i);
if (it == this->m_A_values.end()) {
throw exception(sstream() << "cannot find row " << i);
}
T ret = numeric_traits<T>::zero();
for (auto & pair : it->second) {
auto cit = this->m_columns.find(pair.first);
if (cit == this->m_columns.end()){
std::cout << "cannot find column " << pair.first << std::endl;
}
bool row_constraint_holds(unsigned i, std::unordered_map<std::string, T> const & solution, bool print);
column_info<T> * ci = cit->second;
auto sol_it = solution.find(ci->get_name());
if (sol_it == solution.end()) {
std::cout << "cannot find in the solution column " << ci->get_name() << std::endl;
}
T column_val = sol_it->second;
if (print) {
std::cout << pair.second << "(" << ci->get_name() << "=" << column_val << ") ";
}
ret += pair.second * column_val;
}
if (print) {
std::cout << " = " << ret << std::endl;
}
return ret;
}
bool row_constraint_holds(unsigned i, std::unordered_map<std::string, T> const & solution, bool print) {
T row_val = get_row_value(i, solution, print);
auto & constraint = this->m_constraints[i];
T rs = constraint.m_rs;
switch (constraint.m_relation) {
case Equal:
if (fabs(numeric_traits<T>::get_double(row_val - rs)) > 0.00001) {
if (print) {
std::cout << "should be = " << rs << std::endl;
}
return false;
}
return true;
case Greater_or_equal:
if (numeric_traits<T>::get_double(row_val - rs) < -0.00001) {
if (print) {
std::cout << "should be >= " << rs << std::endl;
}
return false;
}
return true;;
case Less_or_equal:
if (numeric_traits<T>::get_double(row_val - rs) > 0.00001) {
if (print) {
std::cout << "should be <= " << rs << std::endl;
}
return false;
}
return true;;
}
lean_unreachable();
}
bool row_constraints_hold(std::unordered_map<std::string, T> const & solution) {
for (auto it : this->m_A_values) {
if (!row_constraint_holds(it.first, solution, false)) {
row_constraint_holds(it.first, solution, true);
return false;
}
}
return true;
}
bool row_constraints_hold(std::unordered_map<std::string, T> const & solution);
T * get_array_from_map(std::unordered_map<std::string, T> const & solution) {
T * t = new T[solution.size()];
for (auto it : solution) {
auto g = this->m_names_to_columns.find(it.first);
if (g == this->m_names_to_columns.end()) {
throw exception(sstream() << "cannot find name " << it.first);
}
t[g->second] = it.second;
}
return t;
}
T * get_array_from_map(std::unordered_map<std::string, T> const & solution);
bool solution_is_feasible(std::unordered_map<std::string, T> const & solution) {
return bounds_hold(solution) && row_constraints_hold(solution);
@ -420,12 +98,6 @@ public:
return this->get_column_value_with_core_solver(column, m_core_solver);
}
T get_current_cost() const {
T ret = numeric_traits<T>::zero();
for (auto it : this->m_columns) {
ret += this->get_column_cost_value(it.first, it.second);
}
return ret;
}
T get_current_cost() const;
};
}

View file

@ -0,0 +1,15 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lp_primal_simplex.cpp"
template bool lean::lp_primal_simplex<double, double>::bounds_hold(std::unordered_map<std::string, double, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, double> > > const&);
template bool lean::lp_primal_simplex<double, double>::row_constraints_hold(std::unordered_map<std::string, double, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, double> > > const&);
template double lean::lp_primal_simplex<double, double>::get_current_cost() const;
template lean::lp_primal_simplex<double, double>::~lp_primal_simplex();
template lean::lp_primal_simplex<lean::mpq, lean::mpq>::~lp_primal_simplex();
template lean::mpq lean::lp_primal_simplex<lean::mpq, lean::mpq>::get_current_cost() const;
template void lean::lp_primal_simplex<double, double>::find_maximal_solution();
template void lean::lp_primal_simplex<lean::mpq, lean::mpq>::find_maximal_solution();

149
src/util/lp/lp_settings.cpp Normal file
View file

@ -0,0 +1,149 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/numerics/double.h"
#include "util/lp/lp_settings.h"
namespace lean {
std::string column_type_to_string(column_type t) {
switch (t) {
case fixed:
return std::string("fixed");
case boxed:
return std::string("boxed");
case low_bound:
return std::string("low_bound");
case upper_bound:
return std::string("upper_bound");
case free_column:
return std::string("free_column");
default:
lean_unreachable();
}
}
const char* lp_status_to_string(lp_status status) {
switch (status) {
case UNKNOWN: return "UNKNOWN";
case INFEASIBLE: return "INFEASIBLE";
case UNBOUNDED: return "UNBOUNDED";
case TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED";
case DUAL_UNBOUNDED: return "DUAL_UNBOUNDED";
case OPTIMAL: return "OPTIMAL";
case FEASIBLE: return "FEASIBLE";
case FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR";
case TIME_EXHAUSTED: return "TIME_EXHAUSTED";
case ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED";
case EMPTY: return "EMPTY";
case UNSTABLE: return "UNSTABLE";
default:
lean_unreachable();
}
}
lp_status lp_status_from_string(std::string status) {
if (status == "UNKNOWN") return lp_status::UNKNOWN;
if (status == "INFEASIBLE") return lp_status::INFEASIBLE;
if (status == "UNBOUNDED") return lp_status::UNBOUNDED;
if (status == "OPTIMAL") return lp_status::OPTIMAL;
if (status == "FEASIBLE") return lp_status::FEASIBLE;
if (status == "FLOATING_POINT_ERROR") return lp_status::FLOATING_POINT_ERROR;
if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED;
if (status == "ITERATIONS_EXHAUSTED") return lp_status::ITERATIONS_EXHAUSTED;
if (status == "EMPTY") return lp_status::EMPTY;
lean_unreachable();
}
int get_millisecond_count() {
timeb tb;
ftime(&tb);
return tb.millitm + (tb.time & 0xfffff) * 1000;
}
int get_millisecond_span(int start_time) {
int span = get_millisecond_count() - start_time;
if (span < 0)
span += 0x100000 * 1000;
return span;
}
void my_random_init(unsigned * seed) {
#ifdef LEAN_WINDOWS
srand(*seed);
#else
rand_r(seed);
#endif
}
unsigned my_random() {
#ifdef LEAN_WINDOWS
return rand();
#else
return lrand48();
#endif
}
template <typename T>
bool vectors_are_equal(T * a, std::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 (fabs(numeric_traits<T>::get_double(a[i] - b[i])) > 0.000001) {
std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
return false;
}
}
}
return true;
}
template <typename T>
bool vectors_are_equal(const std::vector<T> & a, const buffer<T> &b) {
unsigned n = a.size();
if (n != b.size()) return false;
if (numeric_traits<T>::precise()) {
for (unsigned i = 0; i < n; i ++){
if (!numeric_traits<T>::is_zero(a[i] - b[i])) {
std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
return false;
}
}
} else {
for (unsigned i = 0; i < n; i ++){
if (fabs(numeric_traits<T>::get_double(a[i] - b[i])) > 0.000001) {
std::cout << "a[" << i <<"] = " << a[i] << ", but " << "b[" << i <<"] = " << b[i] << std::endl;
return false;
}
}
}
return true;
}
template <typename T>
bool vectors_are_equal(const std::vector<T> & a, const std::vector<T> &b) {
unsigned n = a.size();
if (n != b.size()) return false;
if (numeric_traits<T>::precise()) {
for (unsigned i = 0; i < n; i ++){
if (!numeric_traits<T>::is_zero(a[i] - b[i])) {
std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
return false;
}
}
} else {
for (unsigned i = 0; i < n; i ++){
if (fabs(numeric_traits<T>::get_double(a[i] - b[i])) > 0.000001) {
std::cout << "a[" << i <<"] = " << a[i] << ", but " << "b[" << i <<"] = " << b[i] << std::endl;
return false;
}
}
}
return true;
}
}

View file

@ -10,7 +10,10 @@
#include <algorithm>
#include <limits>
#include <sys/timeb.h>
#include "util/debug.h"
#include "util/numerics/numeric_traits.h"
#include "util/sstream.h"
#include "util/numerics/mpq.h"
namespace lean {
@ -22,22 +25,7 @@ enum column_type {
free_column
};
inline std::string column_type_to_string(column_type t) {
switch (t) {
case fixed:
return std::string("fixed");
case boxed:
return std::string("boxed");
case low_bound:
return std::string("low_bound");
case upper_bound:
return std::string("upper_bound");
case free_column:
return std::string("free_column");
default:
lean_unreachable();
}
}
std::string column_type_to_string(column_type t);
enum lp_status {
UNKNOWN,
@ -55,37 +43,9 @@ enum lp_status {
UNSTABLE
};
inline const char* lp_status_to_string(lp_status status) {
switch (status) {
case UNKNOWN: return "UNKNOWN";
case INFEASIBLE: return "INFEASIBLE";
case UNBOUNDED: return "UNBOUNDED";
case TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED";
case DUAL_UNBOUNDED: return "DUAL_UNBOUNDED";
case OPTIMAL: return "OPTIMAL";
case FEASIBLE: return "FEASIBLE";
case FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR";
case TIME_EXHAUSTED: return "TIME_EXHAUSTED";
case ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED";
case EMPTY: return "EMPTY";
case UNSTABLE: return "UNSTABLE";
default:
lean_unreachable();
}
}
const char* lp_status_to_string(lp_status status);
inline lp_status lp_status_from_string(std::string status) {
if (status == "UNKNOWN") return lp_status::UNKNOWN;
if (status == "INFEASIBLE") return lp_status::INFEASIBLE;
if (status == "UNBOUNDED") return lp_status::UNBOUNDED;
if (status == "OPTIMAL") return lp_status::OPTIMAL;
if (status == "FEASIBLE") return lp_status::FEASIBLE;
if (status == "FLOATING_POINT_ERROR") return lp_status::FLOATING_POINT_ERROR;
if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED;
if (status == "ITERATIONS_EXHAUSTED") return lp_status::ITERATIONS_EXHAUSTED;
if (status == "EMPTY") return lp_status::EMPTY;
lean_unreachable();
}
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 };
@ -198,17 +158,55 @@ struct lp_settings {
bool dense_deb;
static unsigned ddd; // used for debugging
#endif
};
int get_millisecond_count() {
timeb tb;
ftime(&tb);
return tb.millitm + (tb.time & 0xfffff) * 1000;
}; // end of lp_settings class
int get_millisecond_count();
int get_millisecond_span(int start_time);
void my_random_init(unsigned * seed);
unsigned my_random();
template <typename T>
std::string T_to_string(const T & t) {
std::ostringstream strs;
strs << t;
return strs.str();
}
int get_millisecond_span(int start_time) {
int span = get_millisecond_count() - start_time;
if (span < 0)
span += 0x100000 * 1000;
return span;
inline std::string T_to_string(const mpq & t) {
std::ostringstream strs;
strs << t.get_double();
return strs.str();
}
template <typename T>
bool val_is_smaller_than_eps(T const & t, double const & eps) {
if (!numeric_traits<T>::precise()) {
return numeric_traits<T>::get_double(t) < eps;
}
return t <= numeric_traits<T>::zero();
}
template <typename T>
bool vectors_are_equal(T * a, std::vector<T> &b, unsigned n);
template <typename T>
bool vectors_are_equal(const std::vector<T> & a, const buffer<T> &b);
template <typename T>
bool vectors_are_equal(const std::vector<T> & a, const std::vector<T> &b);
template <typename T>
T abs (T const & v) { return v >= zero_of_type<T>() ? v : -v; }
template <typename X>
X max_abs_in_vector(std::vector<X>& t){
X r(zero_of_type<X>());
for (auto & v : t)
r = std::max(abs(v) , r);
return r;
}
inline void print_blanks(int n) {
while (n--) {std::cout << ' '; }
}
}

View file

@ -0,0 +1,8 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lp_settings.cpp"
template bool lean::vectors_are_equal<double>(std::vector<double, std::allocator<double> > const&, std::vector<double, std::allocator<double> > const&);

547
src/util/lp/lp_solver.cpp Normal file
View file

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

View file

@ -34,18 +34,10 @@ struct lp_constraint {
template <typename T, typename X>
class lp_solver {
column_info<T> * get_or_create_column_info(unsigned column) {
auto it = m_columns.find(column);
return (it == m_columns.end())? ( m_columns[column] = new column_info<T>) : it->second;
}
column_info<T> * get_or_create_column_info(unsigned column);
protected:
T get_column_cost_value(unsigned j, column_info<T> * ci) const {
if (ci->is_fixed()) {
return ci->get_cost() * ci->get_fixed_value();
}
return ci->get_cost() * get_column_value(j);
}
T get_column_cost_value(unsigned j, column_info<T> * ci) const;
public:
unsigned m_total_iterations;
static_matrix<T, X>* m_A = nullptr; // this is the matrix of constraints
@ -77,11 +69,7 @@ public:
unsigned row_count() const { return this->m_A->row_count(); }
void add_constraint(lp_relation relation, T right_side, unsigned row_index) {
lean_assert(m_constraints.find(row_index) == m_constraints.end());
lp_constraint<T, X> cs(right_side, relation);
m_constraints[row_index] = cs;
}
void add_constraint(lp_relation relation, T right_side, unsigned row_index);
void set_cost_for_column(unsigned column, T column_cost) {
get_or_create_column_info(column)->set_cost(column_cost);
@ -93,36 +81,14 @@ public:
// returns the current cost
virtual T get_current_cost() const = 0;
// do not have to call it
void give_symbolic_name_to_column(std::string name, unsigned column) {
auto it = m_columns.find(column);
column_info<T> *ci;
if (it == m_columns.end()){
m_columns[column] = ci = new column_info<T>;
} else {
ci = it->second;
}
ci->set_name(name);
m_names_to_columns[name] = column;
}
void give_symbolic_name_to_column(std::string name, unsigned column);
virtual T get_column_value(unsigned column) const = 0;
T get_column_value_by_name(std::string name) const {
auto it = m_names_to_columns.find(name);
if (it == m_names_to_columns.end()) {
throw exception(sstream() << "get_column_value_by_name " << name);
}
return get_column_value(it -> second);
}
T get_column_value_by_name(std::string name) const;
// returns -1 if not found
virtual int get_column_index_by_name(std::string name) const {
auto t = m_names_to_columns.find(name);
if (t == m_names_to_columns.end()) {
return -1;
}
return t->second;
}
virtual int get_column_index_by_name(std::string name) const;
void set_low_bound(unsigned i, T bound) {
column_info<T> *ci = get_or_create_column_info(i);
@ -155,21 +121,9 @@ public:
return m_status;
}
virtual ~lp_solver(){
if (m_A != nullptr) {
delete m_A;
}
for (auto t : m_columns) {
delete t.second;
}
}
virtual ~lp_solver();
void flip_costs() {
for (auto t : m_columns) {
column_info<T> *ci = t.second;
ci->set_cost(-ci->get_cost());
}
}
void flip_costs();
virtual void find_maximal_solution() = 0;
void set_time_limit(unsigned time_limit_in_seconds) {
@ -184,71 +138,22 @@ public:
return m_settings.max_total_number_of_iterations;
}
protected:
bool problem_is_empty() {
for (auto & c : m_A_values)
if (c.second.size())
return false;
return true;
}
bool problem_is_empty();
void scale() {
if (numeric_traits<T>::precise() || m_settings.use_scaling == false) {
m_column_scale.clear();
m_column_scale.resize(m_A->column_count(), one_of_type<T>());
return;
}
T smin = T(m_settings.scaling_minimum);
T smax = T(m_settings.scaling_maximum);
scaler<T, X> scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings);
if (!scaler.scale()) {
unscale();
}
}
void scale();
void print_rows_scale_stats() {
std::cout << "rows max" << std::endl;
for (unsigned i = 0; i < m_A->row_count(); i++) {
print_row_scale_stats(i);
}
std::cout << std::endl;
}
void print_rows_scale_stats();
void print_columns_scale_stats() {
std::cout << "columns max" << std::endl;
for (unsigned i = 0; i < m_A->column_count(); i++) {
print_column_scale_stats(i);
}
std::cout << std::endl;
}
void print_columns_scale_stats();
void print_row_scale_stats(unsigned i) {
std::cout << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " ";
std::cout << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")";
}
void print_row_scale_stats(unsigned i);
void print_column_scale_stats(unsigned j) {
std::cout << "(" << m_A->get_min_abs_in_row(j) << " ";
std::cout << m_A->get_max_abs_in_column(j) << ")";
}
void print_column_scale_stats(unsigned j);
void print_scale_stats() {
print_rows_scale_stats();
print_columns_scale_stats();
}
void print_scale_stats();
void get_max_abs_in_row(std::unordered_map<unsigned, T> & row_map) {
T ret = numeric_traits<T>::zero();
for (auto jp : row_map) {
T ac = numeric_traits<T>::abs(jp->second);
if (ac > ret) {
ret = ac;
}
}
return ret;
}
void get_max_abs_in_row(std::unordered_map<unsigned, T> & row_map);
void pin_vars_down_on_row(std::unordered_map<unsigned, T> & row) {
pin_vars_on_row_with_sign(row, - numeric_traits<T>::one());
@ -258,164 +163,19 @@ protected:
pin_vars_on_row_with_sign(row, numeric_traits<T>::one());
}
void pin_vars_on_row_with_sign(std::unordered_map<unsigned, T> & row, T sign ) {
std::unordered_map<unsigned, T> pinned;
for (auto t : row) {
unsigned j = t.first;
column_info<T> * ci = m_columns[j];
T a = t.second;
if (a * sign > numeric_traits<T>::zero()) {
lean_assert(ci->upper_bound_is_set());
ci->set_fixed_value(ci->get_upper_bound());
} else {
lean_assert(ci->low_bound_is_set());
ci->set_fixed_value(ci->get_low_bound());
}
}
}
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) {
low_bound = numeric_traits<T>::zero();
for (auto & t : row) {
T a = t.second;
column_info<T> * ci = m_columns[t.first];
if (a > numeric_traits<T>::zero()) {
if (ci->low_bound_is_set()) {
low_bound += ci->get_low_bound() * a;
} else {
return false;
}
} else {
if (ci->upper_bound_is_set()) {
low_bound += ci->get_upper_bound() * a;
} else {
return false;
}
}
}
return true;
}
bool get_minimal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound);
bool get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound) {
low_bound = numeric_traits<T>::zero();
for (auto & t : row) {
T a = t.second;
column_info<T> * ci = m_columns[t.first];
if (a < numeric_traits<T>::zero()) {
if (ci->low_bound_is_set()) {
low_bound += ci->get_low_bound() * a;
} else {
return false;
}
} else {
if (ci->upper_bound_is_set()) {
low_bound += ci->get_upper_bound() * a;
} else {
return false;
}
}
}
return true;
}
bool get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound);
bool row_is_zero(std::unordered_map<unsigned, T> & row) {
for (auto & t : row) {
if (!is_zero(t.second))
return false;
}
return true;
}
bool row_is_zero(std::unordered_map<unsigned, T> & row);
bool row_e_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
T rs = m_constraints[row_index].m_rs;
if (row_is_zero(row)) {
if (!is_zero(rs))
m_status = INFEASIBLE;
return true;
}
bool row_e_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index);
T low_bound;
bool lb = get_minimal_row_value(row, low_bound);
if (lb) {
T diff = low_bound - rs;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
// low_bound > rs + m_settings.refactor_epsilon
m_status = INFEASIBLE;
return true;
}
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
pin_vars_down_on_row(row);
return true;
}
}
int row_ge_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index);
T upper_bound;
bool ub = get_maximal_row_value(row, upper_bound);
if (ub) {
T diff = rs - upper_bound;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) {
// upper_bound < rs - m_settings.refactor_tolerance
m_status = INFEASIBLE;
return true;
}
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
pin_vars_up_on_row(row);
return true;
}
}
return false;
}
int row_ge_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
T rs = m_constraints[row_index].m_rs;
if (row_is_zero(row)) {
if (rs > zero_of_type<X>())
m_status = INFEASIBLE;
return true;
}
T upper_bound;
if (get_maximal_row_value(row, upper_bound)) {
T diff = rs - upper_bound;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) {
// upper_bound < rs - m_settings.refactor_tolerance
m_status = INFEASIBLE;
return true;
}
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
pin_vars_up_on_row(row);
return true;
}
}
return false;
}
bool row_le_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
T low_bound;
T rs = m_constraints[row_index].m_rs;
if (row_is_zero(row)) {
if (rs < zero_of_type<X>())
m_status = INFEASIBLE;
return true;
}
if (get_minimal_row_value(row, low_bound)) {
T diff = low_bound - rs;
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
// low_bound > rs + m_settings.refactor_tolerance
m_status = lp_status::INFEASIBLE;
return true;
}
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
pin_vars_down_on_row(row);
return true;
}
}
return false;
}
bool row_le_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index);
// analyse possible max and min values that are derived from var boundaries
// Let us say that the we have a "ge" constraint, and the min value is equal to the rs.
@ -423,116 +183,21 @@ protected:
// the low boundary of the var and for a negative - the upper.
// this routing also pins the variables to the boundaries
bool row_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index ) {
auto & constraint = m_constraints[row_index];
switch (constraint.m_relation) {
case lp_relation::Equal:
return row_e_is_obsolete(row, row_index);
bool row_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index );
case lp_relation::Greater_or_equal:
return row_ge_is_obsolete(row, row_index);
void remove_fixed_or_zero_columns();
case lp_relation::Less_or_equal:
return row_le_is_obsolete(row, row_index);
}
lean_unreachable();
}
void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map<unsigned, T> & row);
void remove_fixed_or_zero_columns() {
for (auto & i_row : m_A_values) {
remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second);
}
}
unsigned try_to_remove_some_rows();
void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map<unsigned, T> & row) {
auto & constraint = m_constraints[i];
std::vector<unsigned> removed;
for (auto & col : row) {
unsigned j = col.first;
lean_assert(m_columns.find(j) != m_columns.end());
column_info<T> * ci = m_columns[j];
if (ci->is_fixed()) {
removed.push_back(j);
T aj = col.second;
constraint.m_rs -= aj * ci->get_fixed_value();
} else {
if (numeric_traits<T>::is_zero(col.second)){
removed.push_back(j);
}
}
}
void cleanup();
for (auto j : removed) {
row.erase(j);
}
}
void map_external_rows_to_core_solver_rows();
unsigned try_to_remove_some_rows() {
std::vector<unsigned> rows_to_delete;
for (auto & t : m_A_values) {
if (row_is_obsolete(t.second, t.first)) {
rows_to_delete.push_back(t.first);
}
void map_external_columns_to_core_solver_columns();
if (m_status == lp_status::INFEASIBLE) {
return 0;
}
}
if (rows_to_delete.size() > 0) {
for (unsigned k : rows_to_delete) {
m_A_values.erase(k);
}
}
remove_fixed_or_zero_columns();
return rows_to_delete.size();
}
void cleanup() {
int n = 0; // number of deleted rows
int d;
while ((d = try_to_remove_some_rows()))
n += d;
if (n == 1)
std::cout << "deleted one row" << std::endl;
else if (n)
std::cout << "deleted " << n << " rows" << std::endl;
}
void map_external_rows_to_core_solver_rows() {
unsigned size = 0;
for (auto & row : m_A_values) {
m_external_rows_to_core_solver_rows[row.first] = size;
m_core_solver_rows_to_external_rows[size] = row.first;
size++;
}
}
void map_external_columns_to_core_solver_columns() {
unsigned size = 0;
for (auto & row : m_A_values) {
for (auto & col : row.second) {
if (col.second == numeric_traits<T>::zero() || m_columns[col.first]->is_fixed()) {
throw exception("found fixed column");
}
unsigned j = col.first;
auto j_place = m_external_columns_to_core_solver_columns.find(j);
if (j_place == m_external_columns_to_core_solver_columns.end()) { // j is a newcomer
m_external_columns_to_core_solver_columns[j] = size;
m_core_solver_columns_to_external_columns[size++] = j;
}
}
}
}
void fill_column_names_for_core_solver() {
for (auto it : this->m_columns) {
auto p = this->m_external_columns_to_core_solver_columns.find(it.first);
if (p != this->m_external_columns_to_core_solver_columns.end()) {
this->m_name_map[p->second] = it.second->get_name();
}
}
}
void fill_column_names_for_core_solver();
unsigned number_of_core_structurals() { return m_external_columns_to_core_solver_columns.size(); }
@ -541,141 +206,24 @@ protected:
for (unsigned i = 0; i < m_column_scale.size(); i++) m_column_scale[i] = numeric_traits<T>::one();
}
void unscale() {
delete m_A;
m_A = nullptr;
fill_A_from_A_values();
restore_column_scales_to_one();
fill_m_b();
}
void unscale();
void fill_A_from_A_values() {
m_A = new static_matrix<T, X>(m_A_values.size(), number_of_core_structurals());
for (auto & t : m_A_values) {
lean_assert(m_external_rows_to_core_solver_rows.find(t.first) != m_external_rows_to_core_solver_rows.end());
unsigned row = m_external_rows_to_core_solver_rows[t.first];
for (auto k : t.second) {
lean_assert(m_external_columns_to_core_solver_columns.find(k.first) != m_external_columns_to_core_solver_columns.end());
unsigned col = m_external_columns_to_core_solver_columns[k.first];
bool col_is_flipped = m_columns[k.first]->is_flipped();
void fill_A_from_A_values();
if (!col_is_flipped) {
(*m_A)(row, col) = k.second;
} else {
(*m_A)(row, col) = - k.second;
}
}
}
}
void fill_matrix_A_and_init_right_side();
void fill_matrix_A_and_init_right_side() {
map_external_rows_to_core_solver_rows();
map_external_columns_to_core_solver_columns();
lean_assert(m_A == nullptr);
fill_A_from_A_values();
m_b.resize(m_A->row_count());
}
void count_slacks_and_artificials();
void count_slacks_and_artificials() {
for (int i = row_count() - 1; i >= 0; i--) {
count_slacks_and_artificials_for_row(i);
}
}
void count_slacks_and_artificials_for_row(unsigned i);
void count_slacks_and_artificials_for_row(unsigned i) {
lean_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end());
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]];
T rs;
switch (constraint.m_relation) {
case Equal:
m_artificials++;
break;
case Greater_or_equal:
m_slacks++;
rs = this->m_b[i];
if (rs > 0) {
m_artificials++;
}
break;
case Less_or_equal:
m_slacks++;
rs = this->m_b[i];
if (rs < 0) {
m_artificials++;
}
break;
}
}
T low_bound_shift_for_row(unsigned i);
T low_bound_shift_for_row(unsigned i) {
T ret = numeric_traits<T>::zero();
void fill_m_b();
auto row = this->m_A_values.find(i);
if (row == this->m_A_values.end()) {
throw exception("cannot find row");
}
for (auto col : row->second) {
ret += col.second * this->m_columns[col.first]->get_shift();
}
return ret;
}
void fill_m_b() {
for (int i = this->row_count() - 1; i >= 0; i--) {
lean_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end());
unsigned external_i = this->m_core_solver_rows_to_external_rows[i];
auto & constraint = this->m_constraints[external_i];
this->m_b[i] = constraint.m_rs - low_bound_shift_for_row(external_i);
}
}
T get_column_value_with_core_solver(unsigned column, lp_core_solver_base<T, X> * core_solver) const {
auto cit = this->m_columns.find(column);
if (cit == this->m_columns.end()) {
return numeric_traits<T>::zero();
}
column_info<T> * ci = cit->second;
if (ci->is_fixed()) {
return ci->get_fixed_value();
}
auto t = this->m_external_columns_to_core_solver_columns.find(column);
if (t != this->m_external_columns_to_core_solver_columns.end()){
unsigned cj = t->second;
T v = core_solver->get_var_value(cj) * this->m_column_scale[cj];
if (ci->is_free()) {
return v;
}
if (!ci->is_flipped()) {
return v + ci->get_low_bound();
}
// the flipped case when there is only upper bound
return -v + ci->get_upper_bound(); //
}
return numeric_traits<T>::zero(); // returns zero for out of boundary columns
}
void set_scaled_cost(unsigned j) {
// grab original costs but modify it with the column scales
lean_assert(j < this->m_column_scale.size());
column_info<T> * ci = this->m_columns[this->m_core_solver_columns_to_external_columns[j]];
T cost = ci->get_cost();
if (ci->is_flipped()){
cost *= -1;
}
lean_assert(ci->is_fixed() == false);
this->m_costs[j] = cost * this->m_column_scale[j];
}
void print_statistics_on_A() {
T get_column_value_with_core_solver(unsigned column, lp_core_solver_base<T, X> * core_solver) const;
void set_scaled_cost(unsigned j);
void print_statistics_on_A() {
std::cout << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << std::endl;
// for (unsigned i = 0; i < this->m_A->row_count(); i++) {
// if (this->m_A->number_of_non_zeroes_in_row(i) <= 2 ) {
// std::cout << "m_p[" << i << "] = " << this->m_A->number_of_non_zeroes_in_row(i) << std::endl;
// }
// }
}
public:
lp_settings & settings() { return m_settings;}

View file

@ -0,0 +1,43 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lp_solver.cpp"
template void lean::lp_solver<double, double>::add_constraint(lean::lp_relation, double, unsigned int);
template void lean::lp_solver<double, double>::cleanup();
template void lean::lp_solver<double, double>::count_slacks_and_artificials();
template void lean::lp_solver<double, double>::fill_column_names_for_core_solver();
template void lean::lp_solver<double, double>::fill_m_b();
template void lean::lp_solver<double, double>::fill_matrix_A_and_init_right_side();
template void lean::lp_solver<double, double>::flip_costs();
template double lean::lp_solver<double, double>::get_column_cost_value(unsigned int, lean::column_info<double>*) const;
template int lean::lp_solver<double, double>::get_column_index_by_name(std::string) const;
template double lean::lp_solver<double, double>::get_column_value_with_core_solver(unsigned int, lean::lp_core_solver_base<double, double>*) const;
template lean::column_info<double>* lean::lp_solver<double, double>::get_or_create_column_info(unsigned int);
template void lean::lp_solver<double, double>::give_symbolic_name_to_column(std::string, unsigned int);
template void lean::lp_solver<double, double>::print_statistics_on_A();
template bool lean::lp_solver<double, double>::problem_is_empty();
template void lean::lp_solver<double, double>::scale();
template void lean::lp_solver<double, double>::set_scaled_cost(unsigned int);
template lean::lp_solver<double, double>::~lp_solver();
template void lean::lp_solver<lean::mpq, lean::mpq>::add_constraint(lean::lp_relation, lean::mpq, unsigned int);
template void lean::lp_solver<lean::mpq, lean::mpq>::cleanup();
template void lean::lp_solver<lean::mpq, lean::mpq>::count_slacks_and_artificials();
template void lean::lp_solver<lean::mpq, lean::mpq>::fill_column_names_for_core_solver();
template void lean::lp_solver<lean::mpq, lean::mpq>::fill_m_b();
template void lean::lp_solver<lean::mpq, lean::mpq>::fill_matrix_A_and_init_right_side();
template void lean::lp_solver<lean::mpq, lean::mpq>::flip_costs();
template lean::mpq lean::lp_solver<lean::mpq, lean::mpq>::get_column_cost_value(unsigned int, lean::column_info<lean::mpq>*) const;
template lean::mpq lean::lp_solver<lean::mpq, lean::mpq>::get_column_value_by_name(std::string) const;
template lean::mpq lean::lp_solver<lean::mpq, lean::mpq>::get_column_value_with_core_solver(unsigned int, lean::lp_core_solver_base<lean::mpq, lean::mpq>*) const;
template lean::column_info<lean::mpq>* lean::lp_solver<lean::mpq, lean::mpq>::get_or_create_column_info(unsigned int);
template void lean::lp_solver<lean::mpq, lean::mpq>::give_symbolic_name_to_column(std::string, unsigned int);
template void lean::lp_solver<lean::mpq, lean::mpq>::print_statistics_on_A();
template bool lean::lp_solver<lean::mpq, lean::mpq>::problem_is_empty();
template void lean::lp_solver<lean::mpq, lean::mpq>::scale();
template void lean::lp_solver<lean::mpq, lean::mpq>::set_scaled_cost(unsigned int);
template lean::lp_solver<lean::mpq, lean::mpq>::~lp_solver();
template double lean::lp_solver<double, double>::get_column_value_by_name(std::string) const;

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

@ -0,0 +1,759 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lu.h"
namespace lean {
#ifdef LEAN_DEBUG
template <typename T, typename X> // print the nr x nc submatrix at the top left corner
void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc) {
std::vector<std::vector<std::string>> A;
std::vector<unsigned> widths;
for (unsigned i = 0; i < m.row_count() && i < mr ; i++) {
A.push_back(std::vector<std::string>());
for (unsigned j = 0; j < m.column_count() && j < nc; j++) {
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
}
}
for (unsigned j = 0; j < m.column_count() && j < nc; j++) {
widths.push_back(get_width_of_column(j, A));
}
print_matrix_with_widths(A, widths);
}
template<typename T, typename X>
void print_matrix(static_matrix<T, X> &m) {
std::vector<std::vector<std::string>> A;
std::vector<unsigned> widths;
std::set<pair<unsigned, unsigned>> domain = m.get_domain();
for (unsigned i = 0; i < m.row_count(); i++) {
A.push_back(std::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);
}
template <typename T, typename X>
void print_matrix(sparse_matrix<T, X>& m) {
std::vector<std::vector<std::string>> A;
std::vector<unsigned> widths;
for (unsigned i = 0; i < m.row_count(); i++) {
A.push_back(std::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);
}
#endif
template <typename T, typename X>
X dot_product(const std::vector<T> & a, const std::vector<X> & b, unsigned l) {
auto r = zero_of_type<X>();
for (unsigned i = 0; i < l; i++) {
r += a[i] * b[i];
}
return r;
}
template <typename T, typename X>
one_elem_on_diag<T,X>::one_elem_on_diag(const one_elem_on_diag & o) {
m_i = o.m_i;
m_val = o.m_val;
#ifdef LEAN_DEBUG
m_m = m_n = o.m_m;
m_one_over_val = numeric_traits<T>::one() / o.m_val;
#endif
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
T one_elem_on_diag<T, X>::get_elem(unsigned i, unsigned j) const {
if (i == j){
if (j == m_i) {
return m_one_over_val;
}
return numeric_traits<T>::one();
}
return numeric_traits<T>::zero();
}
#endif
template <typename T, typename X>
void one_elem_on_diag<T, X>::apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
w[m_i] /= m_val;
if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_i])) {
w.erase_from_index(m_i);
w[m_i] = numeric_traits<T>::zero();
}
}
// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c
// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5
template <typename T, typename X>
lu<T, X>::lu(static_matrix<T, X> const & A,
std::vector<unsigned>& basis,
std::vector<int> & basis_heading,
lp_settings & settings,
std::vector<unsigned> & non_basic_columns):
m_dim(A.row_count()),
m_A(A),
m_basis(basis),
m_Q(m_dim),
m_R(m_dim),
m_U(A, basis), // create the square matrix that eventually will be factorized
m_settings(settings),
m_basis_heading(basis_heading),
m_non_basic_columns(non_basic_columns),
m_row_eta_work_vector(A.row_count()){
#ifdef LEAN_DEBUG
debug_test_of_basis(A, basis);
#endif
create_initial_factorization();
if (get_status() != LU_status::OK) {
if (get_status() == LU_status::Degenerated) {
std::cout << "lu status is Degenerated" << std::endl;
} else {
std::cout << "lu status is " <<static_cast<int>(get_status()) << std::endl;
}
return;
}
#ifdef LEAN_DEBUG
// lean_assert(check_correctness());
#endif
}
template <typename T, typename X>
void lu<T, X>::debug_test_of_basis(static_matrix<T, X> const & A, std::vector<unsigned> & basis) {
std::set<unsigned> set;
for (unsigned i = 0; i < A.row_count(); i++) {
lean_assert(basis[i]< A.column_count());
set.insert(basis[i]);
}
lean_assert(set.size() == A.row_count());
}
template <typename T, typename X>
void lu<T, X>::solve_By(std::vector<X> & y) {
init_vector_y(y);
solve_By_when_y_is_ready(y);
}
template <typename T, typename X>
void lu<T, X>::solve_Bd_when_w_is_ready(std::vector<T> & d, indexed_vector<T>& w ) { // w - the vector featuring in 24.3
for (int i = m_dim - 1; i >= 0; i--) { // index ? todo
d[i] = w[i];
}
solve_By_when_y_is_ready(d);
}
template <typename T, typename X>
template <typename L>
void lu<T, X>::solve_By_when_y_is_ready(std::vector<L> & y) {
m_U.double_solve_U_y(y);
m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal
if (precise<X>()) return;
unsigned i = m_dim;
while (i--) {
if (is_zero(y[i])) continue;
if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){
y[i] = zero_of_type<L>();
}
}
}
template <typename T, typename X>
void lu<T, X>::print_basis(std::ofstream & f) {
f << "basis_start" << std::endl;
for (unsigned j : m_basis)
f << j << std::endl;
f << "basis_end" << std::endl;
}
template <typename T, typename X>
void lu<T, X>::print_matrix_compact(std::ofstream & f) {
f << "matrix_start" << std::endl;
f << "nrows " << m_A.row_count() << std::endl;
f << "ncolumns " << m_A.column_count() << std::endl;
for (unsigned i = 0; i < m_A.row_count(); i++) {
auto & row = m_A.m_rows[i];
f << "row " << i << std::endl;
for (auto & t : row.m_cells) {
f << "column " << t.m_j << " value " << t.m_value << std::endl;
}
f << "row_end" << std::endl;
}
f << "matrix_end" << std::endl;
}
template <typename T, typename X>
void lu<T, X>::print(indexed_vector<T> & w) {
std::string dump_file_name("/tmp/lu");
remove(dump_file_name.c_str());
std::ofstream f(dump_file_name);
if (!f.is_open()) {
std::cout << "cannot open file " << dump_file_name << std::endl;
return;
}
std::cout << "writing lu dump to " << dump_file_name << std::endl;
print_matrix_compact(f);
print_basis(f);
print_indexed_vector(w, f);
f.close();
}
template <typename T, typename X>
void lu<T, X>::solve_Bd(unsigned a_column, std::vector<T> & d, indexed_vector<T> & w) {
init_vector_w(a_column, w);
solve_Bd_when_w_is_ready(d, w);
}
template <typename T, typename X>
void lu<T, X>:: solve_yB_internal(std::vector<T>& y) {
// first solve yU = cb*R(-1)
m_R.apply_reverse_from_right(y); // got y = cb*R(-1)
m_U.solve_y_U(y); // got y*U=cb*R(-1)
m_Q.apply_reverse_from_right(y); //
for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) {
#ifdef LEAN_DEBUG
(*e)->set_number_of_columns(m_dim);
#endif
(*e)->apply_from_right(y);
}
}
template <typename T, typename X>
void lu<T, X>::add_delta_to_solution(std::vector<T>& yc, std::vector<T>& y){
unsigned i = y.size();
while (i--)
y[i]+=yc[i];
}
template <typename T, typename X>
void lu<T, X>::find_error_of_yB(std::vector<T>& yc, const std::vector<T>& y) {
unsigned i = m_dim;
while (i--) {
yc[i] -= m_A.dot_product_with_column(y, m_basis[i]);
}
}
// solves y*B = y
// y is the input
template <typename T, typename X>
void lu<T, X>::solve_yB(std::vector<T> & y) {
std::vector<T> yc(y); // copy y aside
solve_yB_internal(y);
find_error_of_yB(yc, y);
solve_yB_internal(yc);
add_delta_to_solution(yc, y);
}
template <typename T, typename X>
void lu<T, X>::apply_Q_R_to_U(permutation_matrix<T, X> & r_wave) {
m_U.multiply_from_right(r_wave);
m_U.multiply_from_left_with_reverse(r_wave);
}
template <typename T, typename X>
void lu<T, X>::change_basis(unsigned entering, unsigned leaving) {
lean_assert(entering < m_A.column_count() && leaving < m_A.column_count());
int place_in_basis = m_basis_heading[leaving];
int place_in_non_basis = - m_basis_heading[entering] - 1;
lean_assert(0 <= place_in_basis && place_in_basis < m_A.column_count());
m_basis_heading[entering] = place_in_basis;
m_basis_heading[leaving] = -place_in_non_basis - 1;
m_basis[place_in_basis] = entering;
m_non_basic_columns[place_in_non_basis] = leaving;
}
template <typename T, typename X>
void lu<T, X>::restore_basis_change(unsigned entering, unsigned leaving) {
if (m_basis_heading[entering] < 0) {
return; // the basis has not been changed
}
change_basis(leaving, entering);
}
// Solving yB = cb to find the entering variable,
// where cb is the cost vector projected to B.
// The result is stored in cb.
// solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering
// variable
template <typename T, typename X>
lu<T, X>::~lu(){
for (auto t : m_tail) {
delete t;
}
}
template <typename T, typename X>
void lu<T, X>::init_vector_y(std::vector<X> & y) {
apply_lp_lists_to_y(y);
m_Q.apply_reverse_from_left(y);
}
template <typename T, typename X>
void lu<T, X>::perform_transformations_on_w(indexed_vector<T>& w) {
apply_lp_lists_to_w(w);
m_Q.apply_reverse_from_left(w);
lean_assert(numeric_traits<T>::precise() || check_vector_for_small_values(w, m_settings));
}
// see Chvatal 24.3
template <typename T, typename X>
void lu<T, X>::init_vector_w(unsigned entering, indexed_vector<T> & w) {
w.clear();
m_A.copy_column_to_vector(entering, w); // w = a, the column
perform_transformations_on_w(w);
}
template <typename T, typename X>
void lu<T, X>::apply_lp_lists_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);
lean_assert(check_vector_for_small_values(w, m_settings));
}
}
template <typename T, typename X>
void lu<T, X>::apply_lp_lists_to_y(std::vector<X>& y) {
for (unsigned i = 0; i < m_tail.size(); i++) {
m_tail[i]->apply_from_left(y, m_settings);
}
}
template <typename T, typename X>
void lu<T, X>::swap_rows(int j, int k) {
if (j != k) {
m_Q.transpose_from_left(j, k);
m_U.swap_rows(j, k);
}
}
template <typename T, typename X>
void lu<T, X>::swap_columns(int j, int pivot_column) {
if (j == pivot_column)
return;
m_R.transpose_from_right(j, pivot_column);
m_U.swap_columns(j, pivot_column);
}
template <typename T, typename X>
bool lu<T, X>::pivot_the_row(int row) {
eta_matrix<T, X> * eta_matrix = get_eta_matrix_for_pivot(row);
if (eta_matrix == nullptr) {
m_U.shorten_active_matrix(row, nullptr);
return true;
}
if (!m_U.pivot_with_eta(row, eta_matrix, m_settings))
return false;
eta_matrix->conjugate_by_permutation(m_Q);
push_matrix_to_tail(eta_matrix);
return true;
}
// we're processing the column j now
template <typename T, typename X>
eta_matrix<T, X> * lu<T, X>::get_eta_matrix_for_pivot(unsigned j) {
eta_matrix<T, X> *ret;
m_U.fill_eta_matrix(j, &ret);
return ret;
}
// we're processing the column j now
template <typename T, typename X>
eta_matrix<T, X> * lu<T, X>::get_eta_matrix_for_pivot(unsigned j, sparse_matrix<T, X>& copy_of_U) {
eta_matrix<T, X> *ret;
copy_of_U.fill_eta_matrix(j, &ret);
return ret;
}
template <typename T, typename X>
void lu<T, X>::print_basis() {
std::cout << "basis ";
for (unsigned i = 0; i < m_dim; i++) {
std::cout << m_basis[i] << " ";
}
std::cout << std::endl;
}
template <typename T, typename X>
void lu<T, X>::print_basis_heading() {
print_basis();
for (unsigned i = 0; i < m_A.column_count(); i++) {
std::cout << m_basis_heading[i] << ",";
}
std::cout << std::endl;
}
// see page 407 of Chvatal
template <typename T, typename X>
unsigned lu<T, X>::transform_U_to_V_by_replacing_column(unsigned leaving, indexed_vector<T> & w) {
int leaving_column = m_basis_heading[leaving];
// std::cout << "leaving_column = " << leaving_column << std::endl;
unsigned column_to_replace = m_R.apply_reverse(leaving_column);
// std::cout << "leaving_column modified = " << column_to_replace << std::endl;
m_U.replace_column(column_to_replace, w, m_settings);
return column_to_replace;
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
void lu<T, X>::check_vector_w(unsigned entering) {
T * w = new T[m_dim];
m_A.copy_column_to_vector(entering, w);
check_apply_lp_lists_to_w(w);
delete [] w;
}
template <typename T, typename X>
void lu<T, X>::check_apply_matrix_to_vector(matrix<T, X> *lp, T *w) {
if (lp != nullptr) {
lp -> set_number_of_rows(m_dim);
lp -> set_number_of_columns(m_dim);
apply_to_vector(*lp, w);
}
}
template <typename T, typename X>
void lu<T, X>::check_apply_lp_lists_to_w(T * w) {
for (unsigned i = 0; i < m_tail.size(); i++) {
check_apply_matrix_to_vector(m_tail[i], w);
}
permutation_matrix<T, X> qr = m_Q.get_reverse();
apply_to_vector(qr, w);
for (int i = m_dim - 1; i >= 0; i--) {
lean_assert(abs(w[i] - w[i]) < 0.0000001);
}
}
// provide some access operators for testing
#endif
template <typename T, typename X>
void lu<T, X>::process_column(int j) {
unsigned pi, pj;
m_U.get_pivot_for_column(pi, pj, T(m_settings.c_partial_pivoting), j);
if (pi == -1) {
std::cout << "cannot find the pivot for column " << j << std::endl;
m_failure = true;
return;
}
swap_columns(j, pj);
swap_rows(j, pi);
if (!pivot_the_row(j)) {
std::cout << "pivot_the_row(" << j << ") failed" << std::endl;
m_failure = true;
}
}
template <typename T, typename X>
bool lu<T, X>::is_correct() {
#ifdef LEAN_DEBUG
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 LEAN_DEBUG
template <typename T, typename X>
dense_matrix<T, X> lu<T, X>::tail_product() {
lean_assert(tail_size() > 0);
dense_matrix<T, X> left_side = permutation_matrix<T, X>(m_dim);
for (unsigned i = 0; i < tail_size(); i++) {
matrix<T, X>* lp = get_lp_matrix(i);
lp->set_number_of_rows(m_dim);
lp->set_number_of_columns(m_dim);
left_side = ((*lp) * left_side);
}
return left_side;
}
template <typename T, typename X>
dense_matrix<T, X> lu<T, X>::get_left_side() {
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 T, typename X>
dense_matrix<T, X> lu<T, X>::get_right_side() {
auto ret = U() * R();
ret = Q() * ret;
return ret;
}
#endif
// needed for debugging purposes
template <typename T, typename X>
void lu<T, X>::copy_w(T *buffer, indexed_vector<T> & w) {
unsigned i = m_dim;
while (i--) {
buffer[i] = w[i];
}
}
// needed for debugging purposes
template <typename T, typename X>
void lu<T, X>::restore_w(T *buffer, indexed_vector<T> & w) {
unsigned i = m_dim;
while (i--) {
w[i] = buffer[i];
}
}
template <typename T, typename X>
bool lu<T, X>::all_columns_and_rows_are_active() {
unsigned i = m_dim;
while (i--) {
lean_assert(m_U.col_is_active(i));
lean_assert(m_U.row_is_active(i));
}
return true;
}
template <typename T, typename X>
bool lu<T, X>::too_dense(unsigned j) const {
unsigned r = m_dim - j;
if (r < 5)
return false;
return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j);
}
template <typename T, typename X>
void lu<T, X>::pivot_in_dense_mode(unsigned i) {
int j = m_dense_LU->find_pivot_column_in_row(i);
if (j == -1) {
m_failure = true;
return;
}
if (i != j) {
swap_columns(i, j);
m_dense_LU->swap_columns(i, j);
}
m_dense_LU->pivot(i, m_settings);
}
template <typename T, typename X>
void lu<T, X>::create_initial_factorization(){
m_U.prepare_for_factorization();
unsigned j;
for (j = 0; j < m_dim; j++) {
process_column(j);
if (m_failure || too_dense(j + 1)) {
break;
}
}
if (m_failure) {
set_status(LU_status::Degenerated);
return;
}
if (j == m_dim) {
lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
return;
}
j++;
// std::cout << "switching to dense factoring for " << j << endl;
m_dense_LU = new square_dense_submatrix<T, X>(&m_U, j);
for (; j < m_dim; j++) {
pivot_in_dense_mode(j);
if (m_failure) {
set_status(LU_status::Degenerated);
return;
}
}
m_dense_LU->update_parent_matrix(m_settings);
lean_assert(m_dense_LU->is_L_matrix());
m_dense_LU->conjugate_by_permutation(m_Q);
push_matrix_to_tail(m_dense_LU);
// lean_assert(is_correct());
// lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
}
template <typename T, typename X>
void lu<T, X>::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix<T, X> & r_wave) {
lean_assert(bump_start <= bump_end);
if (bump_start == bump_end) {
return;
}
r_wave[bump_start] = bump_end; // sending the offensive column to the end of the bump
for ( unsigned i = bump_start + 1 ; i <= bump_end; i++ ) {
r_wave[i] = i - 1;
}
m_U.multiply_from_right(r_wave);
m_U.multiply_from_left_with_reverse(r_wave);
}
template <typename T, typename X>
void lu<T, X>::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) {
std::vector<indexed_value<T>> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump));
for (auto & iv : last_row_vec) {
if (is_zero(iv.m_value)) continue;
lean_assert(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value));
unsigned adjusted_col = m_U.adjust_column_inverse(iv.m_index);
if (adjusted_col < lowest_row_of_the_bump) {
m_row_eta_work_vector.set_value(-iv.m_value, adjusted_col);
} else {
m_row_eta_work_vector.set_value(iv.m_value, adjusted_col); // preparing to calculate the real value in the matrix
}
}
}
template <typename T, typename X>
void lu<T, X>::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) {
// we have the system right side at m_row_eta_work_vector now
// solve the system column wise
for (unsigned j = replaced_column; j < lowest_row_of_the_bump; j++) {
T v = m_row_eta_work_vector[j];
if (numeric_traits<T>::is_zero(v)) continue; // this column does not contribute to the solution
unsigned aj = m_U.adjust_row(j);
std::vector<indexed_value<T>> & row = m_U.get_row_values(aj);
for (auto & iv : row) {
unsigned col = m_U.adjust_column_inverse(iv.m_index);
lean_assert(col >= j || numeric_traits<T>::is_zero(iv.m_value));
if (col == j) continue;
if (numeric_traits<T>::is_zero(iv.m_value)) {
continue;
}
// the -v is for solving the system ( to zero the last row), and +v is for pivoting
T delta = col < lowest_row_of_the_bump? -v * iv.m_value: v * iv.m_value;
lean_assert(numeric_traits<T>::is_zero(delta) == false);
if (numeric_traits<T>::is_zero(m_row_eta_work_vector[col])) {
if (!m_settings.abs_val_is_smaller_than_drop_tolerance(delta)){
m_row_eta_work_vector.set_value(delta, col);
}
} else {
T t = (m_row_eta_work_vector[col] += delta);
if (m_settings.abs_val_is_smaller_than_drop_tolerance(t)){
m_row_eta_work_vector[col] = numeric_traits<T>::zero();
auto it = std::find(m_row_eta_work_vector.m_index.begin(), m_row_eta_work_vector.m_index.end(), col);
if (it != m_row_eta_work_vector.m_index.end())
m_row_eta_work_vector.m_index.erase(it);
}
}
}
}
lean_assert(m_row_eta_work_vector.is_OK());
}
// see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last
// row at the same time
template <typename T, typename X>
row_eta_matrix<T, X> *lu<T, X>::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) {
if (replaced_column == lowest_row_of_the_bump) return nullptr;
scan_last_row_to_work_vector(lowest_row_of_the_bump);
pivot_and_solve_the_system(replaced_column, lowest_row_of_the_bump);
T denom = std::max(T(1), abs(pivot_elem_for_checking));
if (
#ifdef LEAN_DEBUG
!is_zero(pivot_elem_for_checking) &&
#endif
!m_settings.abs_val_is_smaller_than_pivot_tolerance((m_row_eta_work_vector[lowest_row_of_the_bump] - pivot_elem_for_checking) / denom)) {
// std::cout << "m_row_eta_work_vector[" << lowest_row_of_the_bump << "] = " << T_to_string(m_row_eta_work_vector[lowest_row_of_the_bump]) << ", but pivot = " << T_to_string(pivot_elem_for_checking) << endl;
set_status(LU_status::Degenerated);
// std::cout << "diagonal element is off" << endl;
return nullptr;
}
#ifdef LEAN_DEBUG
auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump, m_dim);
#else
auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump);
#endif
for (auto j : m_row_eta_work_vector.m_index) {
if (j < lowest_row_of_the_bump) {
auto & v = m_row_eta_work_vector[j];
if (!is_zero(v)) {
if (!m_settings.abs_val_is_smaller_than_drop_tolerance(v)){
ret->push_back(j, v);
}
v = numeric_traits<T>::zero();
}
}
} // now the lowest_row_of_the_bump contains the rest of the row to the right of the bump with correct values
return ret;
}
// This method does not update the basis: is_correct() should not be called since it works with the basis.
template <typename T, typename X>
void lu<T, X>::replace_column(unsigned leaving, T pivot_elem, indexed_vector<T> & w){
lean_assert(m_basis_heading[leaving] >= 0);
unsigned replaced_column = transform_U_to_V_by_replacing_column(leaving, w);
unsigned lowest_row_of_the_bump = m_U.lowest_row_in_column(replaced_column);
permutation_matrix<T, X> r_wave(m_dim);
calculate_r_wave_and_update_U(replaced_column, lowest_row_of_the_bump, r_wave);
auto row_eta = get_row_eta_matrix_and_set_row_vector(replaced_column, lowest_row_of_the_bump, pivot_elem);
if (get_status() == LU_status::Degenerated) {
m_row_eta_work_vector.clear_all();
return;
}
m_Q.multiply_by_permutation_from_right(r_wave);
m_R.multiply_by_permutation_reverse_from_left(r_wave);
if (row_eta != nullptr) {
row_eta->conjugate_by_permutation(m_Q);
push_matrix_to_tail(row_eta);
}
calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump);
lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
lean_assert(w.is_OK() && m_row_eta_work_vector.is_OK());
}
template <typename T, typename X>
void lu<T, X>::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){
T diagonal_elem;
if (replaced_column < lowest_row_of_the_bump) {
diagonal_elem = m_row_eta_work_vector[lowest_row_of_the_bump];
// lean_assert(m_row_eta_work_vector.is_OK());
m_U.set_row_from_work_vector_and_clean_work_vector_not_adjusted(m_U.adjust_row(lowest_row_of_the_bump), m_row_eta_work_vector, m_settings);
} else {
diagonal_elem = m_U(lowest_row_of_the_bump, lowest_row_of_the_bump); // todo - get it more efficiently
}
if (m_settings.abs_val_is_smaller_than_pivot_tolerance(diagonal_elem)) {
set_status(LU_status::Degenerated);
return;
}
calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem);
// lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
}
template <typename T, typename X>
void lu<T, X>::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) {
auto l = new one_elem_on_diag<T, X>(lowest_row_of_the_bump, diagonal_element);
#ifdef LEAN_DEBUG
l->set_number_of_columns(m_dim);
#endif
push_matrix_to_tail(l);
m_U.divide_row_by_constant(lowest_row_of_the_bump, diagonal_element, m_settings);
l->conjugate_by_permutation(m_Q);
}
template <typename T, typename X>
void init_factorization(lu<T, X>* & factorization, static_matrix<T, X> & m_A, std::vector<unsigned> & m_basis, std::vector<int> & m_basis_heading, lp_settings &m_settings, std::vector<unsigned> & non_basic_columns) {
if (factorization != nullptr) {
delete factorization;
}
factorization = new lu<T, X>(m_A, m_basis, m_basis_heading, m_settings, non_basic_columns);
if (factorization->get_status() != LU_status::OK) {
std::cout << "failing in init_factorization" << std::endl;
return;
}
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
dense_matrix<T, X> get_B(lu<T, X>& f) {
dense_matrix<T, X> B(f.dimension(), f.dimension());
for (unsigned i = 0; i < f.dimension(); i++)
for (unsigned j = 0; j < f.dimension(); j++)
B.set_elem(i, j, f.B_(i, j));
return B;
}
#endif
}

View file

@ -21,77 +21,21 @@
#include <fstream>
#include "util/lp/row_eta_matrix.h"
#include "util/lp/square_dense_submatrix.h"
#include "util/lp/dense_matrix.h"
namespace lean {
template <typename T>
std::string T_to_string(const T & t); // forward definition
#ifdef LEAN_DEBUG
template <typename T, typename X> // print the nr x nc submatrix at the top left corner
void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc) {
std::vector<std::vector<std::string>> A;
std::vector<unsigned> widths;
for (unsigned i = 0; i < m.row_count() && i < mr ; i++) {
A.push_back(std::vector<std::string>());
for (unsigned j = 0; j < m.column_count() && j < nc; j++) {
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
}
}
for (unsigned j = 0; j < m.column_count() && j < nc; j++) {
widths.push_back(get_width_of_column(j, A));
}
print_matrix_with_widths(A, widths);
}
void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc);
template<typename T, typename X>
void print_matrix(static_matrix<T, X> &m) {
std::vector<std::vector<std::string>> A;
std::vector<unsigned> widths;
std::set<pair<unsigned, unsigned>> domain = m.get_domain();
for (unsigned i = 0; i < m.row_count(); i++) {
A.push_back(std::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);
}
void print_matrix(static_matrix<T, X> &m);
template <typename T, typename X>
void print_matrix(sparse_matrix<T, X>& m) {
std::vector<std::vector<std::string>> A;
std::vector<unsigned> widths;
for (unsigned i = 0; i < m.row_count(); i++) {
A.push_back(std::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);
}
void print_matrix(sparse_matrix<T, X>& m);
#endif
enum class LU_status { OK, Degenerated};
template <typename T, typename X>
X dot_product(const std::vector<T> & a, const std::vector<X> & b, unsigned l) {
auto r = zero_of_type<X>();
for (unsigned i = 0; i < l; i++) {
r += a[i] * b[i];
}
return r;
}
X dot_product(const std::vector<T> & a, const std::vector<X> & b, unsigned l);
template <typename T, typename X>
class one_elem_on_diag: public tail_matrix<T, X> {
@ -104,15 +48,7 @@ public:
#endif
}
one_elem_on_diag(const one_elem_on_diag & o) {
m_i = o.m_i;
m_val = o.m_val;
#ifdef LEAN_DEBUG
m_m = m_n = o.m_m;
m_one_over_val = numeric_traits<T>::one() / o.m_val;
#endif
}
one_elem_on_diag(const one_elem_on_diag & o);
#ifdef LEAN_DEBUG
unsigned m_m;
@ -121,16 +57,7 @@ public:
virtual void set_number_of_columns(unsigned n) { m_m = n; m_n = n; }
T m_one_over_val;
T get_elem (unsigned i, unsigned j) const {
if (i == j){
if (j == m_i) {
return m_one_over_val;
}
return numeric_traits<T>::one();
}
return numeric_traits<T>::zero();
}
T get_elem (unsigned i, unsigned j) const;
unsigned row_count() const { return m_m; } // not defined }
unsigned column_count() const { return m_m; } // not defined }
@ -143,13 +70,7 @@ public:
w[m_i] /= m_val;
}
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
w[m_i] /= m_val;
if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_i])) { // todo : is it needed?
w.erase_from_index(m_i);
w[m_i] = numeric_traits<T>::zero();
}
}
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings);
void conjugate_by_permutation(permutation_matrix<T, X> & p) {
// this = p * this * p(-1)
@ -166,10 +87,10 @@ public:
}
}; // end of one_elem_on_diag
// 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
enum class LU_status { OK, Degenerated};
// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c
// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5
template <typename T, typename X>
class lu {
LU_status m_status = LU_status::OK;
@ -198,335 +119,85 @@ public:
std::vector<unsigned>& basis,
std::vector<int> & basis_heading,
lp_settings & settings,
std::vector<unsigned> & non_basic_columns):
m_dim(A.row_count()),
m_A(A),
m_basis(basis),
m_Q(m_dim),
m_R(m_dim),
m_U(A, basis), // create the square matrix that eventually will be factorized
m_settings(settings),
m_basis_heading(basis_heading),
m_non_basic_columns(non_basic_columns),
m_row_eta_work_vector(A.row_count()){
#ifdef LEAN_DEBUG
debug_test_of_basis(A, basis);
#endif
create_initial_factorization();
if (get_status() != LU_status::OK) {
if (get_status() == LU_status::Degenerated) {
std::cout << "lu status is Degenerated" << std::endl;
} else {
std::cout << "lu status is " <<static_cast<int>(get_status()) << std::endl;
}
return;
}
#ifdef LEAN_DEBUG
// lean_assert(check_correctness());
#endif
}
void debug_test_of_basis(static_matrix<T, X> const & A, std::vector<unsigned> & basis) {
std::set<unsigned> set;
for (unsigned i = 0; i < A.row_count(); i++) {
lean_assert(basis[i]< A.column_count());
set.insert(basis[i]);
}
lean_assert(set.size() == A.row_count());
}
std::vector<unsigned> & non_basic_columns);
void debug_test_of_basis(static_matrix<T, X> const & A, std::vector<unsigned> & basis);
unsigned non_basic_column_index_in_non_basic_columns(unsigned j) {
return - m_basis_heading[j] - 1;
}
unsigned non_basic_column_index_in_non_basic_columns(unsigned j) { return - m_basis_heading[j] - 1; }
void solve_By(std::vector<X> & y) {
init_vector_y(y);
solve_By_when_y_is_ready(y);
}
void solve_By(std::vector<X> & y);
void solve_Bd_when_w_is_ready(std::vector<T> & d, indexed_vector<T>& w ) { // w - the vector featuring in 24.3
for (int i = m_dim - 1; i >= 0; i--) { // index ? todo
d[i] = w[i];
}
solve_By_when_y_is_ready(d);
}
void solve_Bd_when_w_is_ready(std::vector<T> & d, indexed_vector<T>& w );
template <typename L>
void solve_By_when_y_is_ready(std::vector<L> & y) {
m_U.double_solve_U_y(y);
m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal
if (precise<X>()) return;
unsigned i = m_dim;
while (i--) {
if (is_zero(y[i])) continue;
if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){
y[i] = zero_of_type<L>();
}
}
}
void solve_By_when_y_is_ready(std::vector<L> & y);
void print_indexed_vector(indexed_vector<T> & w, std::ofstream & f);
void print_basis(std::ofstream & f);
void print_matrix_compact(std::ofstream & f);
void print(indexed_vector<T> & w);
void solve_Bd(unsigned a_column, std::vector<T> & d, indexed_vector<T> & w);
void print_indexed_vector(indexed_vector<T> & w, std::ofstream & f) {
f << "vector_start" << std::endl;
for (unsigned j : w.m_index) {
f << j << " " << w[j] << std::endl;
}
f << "vector_end" << std::endl;
}
void print_basis(std::ofstream & f) {
f << "basis_start" << std::endl;
for (unsigned j : m_basis)
f << j << std::endl;
f << "basis_end" << std::endl;
}
void print_matrix_compact(std::ofstream & f) {
f << "matrix_start" << std::endl;
f << "nrows " << m_A.row_count() << std::endl;
f << "ncolumns " << m_A.column_count() << std::endl;
for (unsigned i = 0; i < m_A.row_count(); i++) {
auto & row = m_A.m_rows[i];
f << "row " << i << std::endl;
for (auto & t : row.m_cells) {
f << "column " << t.m_j << " value " << t.m_value << std::endl;
}
f << "row_end" << std::endl;
}
f << "matrix_end" << std::endl;
}
bool column_can_be_taken_to_basis(unsigned i) { return m_basis_heading[i] < 0; }
void print(indexed_vector<T> & w) {
std::string dump_file_name("/tmp/lu");
remove(dump_file_name.c_str());
std::ofstream f(dump_file_name);
if (!f.is_open()) {
std::cout << "cannot open file " << dump_file_name << std::endl;
return;
}
std::cout << "writing lu dump to " << dump_file_name << std::endl;
print_matrix_compact(f);
print_basis(f);
print_indexed_vector(w, f);
f.close();
}
void solve_yB_internal(std::vector<T>& y);
void solve_Bd(unsigned a_column, std::vector<T> & d, indexed_vector<T> & w) {
init_vector_w(a_column, w);
solve_Bd_when_w_is_ready(d, w);
}
void add_delta_to_solution(std::vector<T>& yc, std::vector<T>& y);
bool column_can_be_taken_to_basis(unsigned i) {
return m_basis_heading[i] < 0;
}
void find_error_of_yB(std::vector<T>& yc, const std::vector<T>& y);
void solve_yB_internal(std::vector<T>& y) {
// first solve yU = cb*R(-1)
m_R.apply_reverse_from_right(y); // got y = cb*R(-1)
m_U.solve_y_U(y); // got y*U=cb*R(-1)
m_Q.apply_reverse_from_right(y); //
for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) {
#ifdef LEAN_DEBUG
(*e)->set_number_of_columns(m_dim);
#endif
(*e)->apply_from_right(y);
}
}
void solve_yB(std::vector<T> & y);
void add_delta_to_solution(std::vector<T>& yc, std::vector<T>& y){
unsigned i = y.size();
while (i--)
y[i]+=yc[i];
}
void apply_Q_R_to_U(permutation_matrix<T, X> & r_wave);
void find_error_of_yB(std::vector<T>& yc, const std::vector<T>& y) {
unsigned i = m_dim;
while (i--) {
yc[i] -= m_A.dot_product_with_column(y, m_basis[i]);
}
}
void change_basis(unsigned entering, unsigned leaving);
// solves y*B = y
// y is the input
void solve_yB(std::vector<T> & y) {
std::vector<T> yc(y); // copy y aside
solve_yB_internal(y);
find_error_of_yB(yc, y);
solve_yB_internal(yc);
add_delta_to_solution(yc, y);
}
void 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);
}
void change_basis(unsigned entering, unsigned leaving) {
lean_assert(entering < m_A.column_count() && leaving < m_A.column_count());
int place_in_basis = m_basis_heading[leaving];
int place_in_non_basis = - m_basis_heading[entering] - 1;
lean_assert(0 <= place_in_basis && place_in_basis < m_A.column_count());
m_basis_heading[entering] = place_in_basis;
m_basis_heading[leaving] = -place_in_non_basis - 1;
m_basis[place_in_basis] = entering;
m_non_basic_columns[place_in_non_basis] = leaving;
}
void restore_basis_change(unsigned entering, unsigned leaving) {
if (m_basis_heading[entering] < 0) {
return; // the basis has not been changed
}
change_basis(leaving, entering);
}
void restore_basis_change(unsigned entering, unsigned leaving);
LU_status get_status() { return m_status; }
void set_status(LU_status status) { m_status = status; }
// Solving yB = cb to find the entering variable,
// where cb is the cost vector projected to B.
// The result is stored in cb.
~lu();
// solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering
// variable
T B(unsigned i, unsigned j) { return m_A(i, m_basis[j]); }
~lu(){
for (auto t : m_tail) {
delete t;
}
}
void init_vector_y(std::vector<X> & y);
T B(unsigned i, unsigned j) {
return m_A(i, m_basis[j]);
}
void perform_transformations_on_w(indexed_vector<T>& w);
void init_vector_y(std::vector<X> & y) {
apply_lp_lists_to_y(y);
m_Q.apply_reverse_from_left(y);
}
void init_vector_w(unsigned entering, indexed_vector<T> & w);
void apply_lp_lists_to_w(indexed_vector<T> & w);
void apply_lp_lists_to_y(std::vector<X>& y);
void swap_rows(int j, int k);
void perform_transformations_on_w(indexed_vector<T>& w) {
apply_lp_lists_to_w(w);
m_Q.apply_reverse_from_left(w);
lean_assert(numeric_traits<T>::precise() || check_vector_for_small_values(w, m_settings));
}
// see Chvatal 24.3
void init_vector_w(unsigned entering, indexed_vector<T> & w) {
w.clear();
m_A.copy_column_to_vector(entering, w); // w = a, the column
perform_transformations_on_w(w);
}
void apply_lp_lists_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);
lean_assert(check_vector_for_small_values(w, m_settings));
}
}
void apply_lp_lists_to_y(std::vector<X>& y) {
for (unsigned i = 0; i < m_tail.size(); i++) {
m_tail[i]->apply_from_left(y, m_settings);
}
}
void swap_rows(int j, int k) {
if (j != k) {
m_Q.transpose_from_left(j, k);
m_U.swap_rows(j, k);
}
}
void 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);
}
void swap_columns(int j, int pivot_column);
void push_matrix_to_tail(tail_matrix<T, X>* tm) {
m_tail.push_back(tm);
}
bool pivot_the_row(int row) {
eta_matrix<T, X> * eta_matrix = get_eta_matrix_for_pivot(row);
if (eta_matrix == nullptr) {
m_U.shorten_active_matrix(row, nullptr);
return true;
}
if (!m_U.pivot_with_eta(row, eta_matrix, m_settings))
return false;
eta_matrix->conjugate_by_permutation(m_Q);
push_matrix_to_tail(eta_matrix);
return true;
}
// we're processing the column j now
eta_matrix<T, X> * get_eta_matrix_for_pivot(unsigned j) {
eta_matrix<T, X> *ret;
m_U.fill_eta_matrix(j, &ret);
return ret;
}
// 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> *ret;
copy_of_U.fill_eta_matrix(j, &ret);
return ret;
}
bool pivot_the_row(int row);
void print_basis() {
std::cout << "basis ";
for (unsigned i = 0; i < m_dim; i++) {
std::cout << m_basis[i] << " ";
}
std::cout << std::endl;
}
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);
void print_basis_heading() {
print_basis();
for (unsigned i = 0; i < m_A.column_count(); i++) {
std::cout << m_basis_heading[i] << ",";
}
std::cout << std::endl;
}
void print_basis();
void print_basis_heading();
// see page 407 of Chvatal
unsigned transform_U_to_V_by_replacing_column(unsigned leaving, indexed_vector<T> & w) {
int leaving_column = m_basis_heading[leaving];
// std::cout << "leaving_column = " << leaving_column << std::endl;
unsigned column_to_replace = m_R.apply_reverse(leaving_column);
// std::cout << "leaving_column modified = " << column_to_replace << std::endl;
m_U.replace_column(column_to_replace, w, m_settings);
return column_to_replace;
}
unsigned transform_U_to_V_by_replacing_column(unsigned leaving, indexed_vector<T> & w);
#ifdef LEAN_DEBUG
void 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;
}
void check_vector_w(unsigned entering);
void check_apply_matrix_to_vector(matrix<T, X> *lp, T *w) {
if (lp != nullptr) {
lp -> set_number_of_rows(m_dim);
lp -> set_number_of_columns(m_dim);
apply_to_vector(*lp, w);
}
}
void check_apply_matrix_to_vector(matrix<T, X> *lp, T *w);
void check_apply_lp_lists_to_w(T * w) {
for (unsigned i = 0; i < m_tail.size(); i++) {
check_apply_matrix_to_vector(m_tail[i], w);
}
permutation_matrix<T, X> qr = m_Q.get_reverse();
apply_to_vector(qr, w);
for (int i = m_dim - 1; i >= 0; i--) {
lean_assert(abs(w[i] - w[i]) < 0.0000001);
}
}
void check_apply_lp_lists_to_w(T * w);
// provide some access operators for testing
permutation_matrix<T, X> & Q() { return m_Q; }
@ -555,34 +226,9 @@ public:
}
void process_column(int j) {
unsigned pi, pj;
m_U.get_pivot_for_column(pi, pj, T(m_settings.c_partial_pivoting), j);
if (pi == -1) {
std::cout << "cannot find the pivot for column " << j << std::endl;
m_failure = true;
return;
}
swap_columns(j, pj);
swap_rows(j, pi);
if (!pivot_the_row(j)) {
std::cout << "pivot_the_row(" << j << ") failed" << std::endl;
m_failure = true;
}
}
void process_column(int j);
bool is_correct() {
#ifdef LEAN_DEBUG
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
}
bool is_correct();
int basis_heading(unsigned j) {
lean_assert(j < m_A.column_count());
@ -590,297 +236,53 @@ public:
}
#ifdef LEAN_DEBUG
dense_matrix<T, X> tail_product() {
lean_assert(tail_size() > 0);
dense_matrix<T, X> left_side = permutation_matrix<T, X>(m_dim);
for (unsigned i = 0; i < tail_size(); i++) {
matrix<T, X>* lp = get_lp_matrix(i);
lp->set_number_of_rows(m_dim);
lp->set_number_of_columns(m_dim);
left_side = ((*lp) * left_side);
}
return left_side;
}
dense_matrix<T, X> 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;
}
dense_matrix<T, X> tail_product();
dense_matrix<T, X> get_left_side();
dense_matrix<T, X> get_right_side() {
auto ret = U() * R();
ret = Q() * ret;
return ret;
}
dense_matrix<T, X> get_right_side();
#endif
// needed for debugging purposes
void copy_w(T *buffer, indexed_vector<T> & w) {
unsigned i = m_dim;
while (i--) {
buffer[i] = w[i];
}
}
void copy_w(T *buffer, indexed_vector<T> & w);
// needed for debugging purposes
void restore_w(T *buffer, indexed_vector<T> & w) {
unsigned i = m_dim;
while (i--) {
w[i] = buffer[i];
}
}
bool all_columns_and_rows_are_active() {
unsigned i = m_dim;
while (i--) {
lean_assert(m_U.col_is_active(i));
lean_assert(m_U.row_is_active(i));
}
return true;
}
void restore_w(T *buffer, indexed_vector<T> & w);
bool all_columns_and_rows_are_active();
bool too_dense(unsigned j) const {
unsigned r = m_dim - j;
if (r < 5)
return false;
return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j);
}
bool too_dense(unsigned j) const;
void pivot_in_dense_mode(unsigned i) {
int j = m_dense_LU->find_pivot_column_in_row(i);
if (j == -1) {
m_failure = true;
return;
}
if (i != j) {
swap_columns(i, j);
m_dense_LU->swap_columns(i, j);
}
m_dense_LU->pivot(i, m_settings);
}
void pivot_in_dense_mode(unsigned i);
void create_initial_factorization(){
m_U.prepare_for_factorization();
unsigned j;
for (j = 0; j < m_dim; j++) {
process_column(j);
if (m_failure || too_dense(j + 1)) {
break;
}
}
if (m_failure) {
set_status(LU_status::Degenerated);
return;
}
if (j == m_dim) {
lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
return;
}
j++;
// std::cout << "switching to dense factoring for " << j << endl;
m_dense_LU = new square_dense_submatrix<T, X>(&m_U, j);
for (; j < m_dim; j++) {
pivot_in_dense_mode(j);
if (m_failure) {
set_status(LU_status::Degenerated);
return;
}
}
m_dense_LU->update_parent_matrix(m_settings);
lean_assert(m_dense_LU->is_L_matrix());
m_dense_LU->conjugate_by_permutation(m_Q);
push_matrix_to_tail(m_dense_LU);
// lean_assert(is_correct());
// lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
}
void create_initial_factorization();
void calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix<T, X> & r_wave) {
lean_assert(bump_start <= bump_end);
if (bump_start == bump_end) {
return;
}
void calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix<T, X> & r_wave);
r_wave[bump_start] = bump_end; // sending the offensive column to the end of the bump
void scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump);
for ( unsigned i = bump_start + 1 ; i <= bump_end; i++ ) {
r_wave[i] = i - 1;
}
bool diagonal_element_is_off(T diag_element) { return false; }
m_U.multiply_from_right(r_wave);
m_U.multiply_from_left_with_reverse(r_wave);
}
void scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) {
std::vector<indexed_value<T>> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump));
for (auto & iv : last_row_vec) {
if (is_zero(iv.m_value)) continue;
lean_assert(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value));
unsigned adjusted_col = m_U.adjust_column_inverse(iv.m_index);
if (adjusted_col < lowest_row_of_the_bump) {
m_row_eta_work_vector.set_value(-iv.m_value, adjusted_col);
} else {
m_row_eta_work_vector.set_value(iv.m_value, adjusted_col); // preparing to calculate the real value in the matrix
}
}
}
bool diagonal_element_is_off(T diag_element) {
return false;
}
void pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) {
// we have the system right side at m_row_eta_work_vector now
// solve the system column wise
for (unsigned j = replaced_column; j < lowest_row_of_the_bump; j++) {
T v = m_row_eta_work_vector[j];
if (numeric_traits<T>::is_zero(v)) continue; // this column does not contribute to the solution
unsigned aj = m_U.adjust_row(j);
std::vector<indexed_value<T>> & row = m_U.get_row_values(aj);
for (auto & iv : row) {
unsigned col = m_U.adjust_column_inverse(iv.m_index);
lean_assert(col >= j || numeric_traits<T>::is_zero(iv.m_value));
if (col == j) continue;
if (numeric_traits<T>::is_zero(iv.m_value)) {
continue;
}
// the -v is for solving the system ( to zero the last row), and +v is for pivoting
T delta = col < lowest_row_of_the_bump? -v * iv.m_value: v * iv.m_value;
lean_assert(numeric_traits<T>::is_zero(delta) == false);
if (numeric_traits<T>::is_zero(m_row_eta_work_vector[col])) {
if (!m_settings.abs_val_is_smaller_than_drop_tolerance(delta)){
m_row_eta_work_vector.set_value(delta, col);
}
} else {
T t = (m_row_eta_work_vector[col] += delta);
if (m_settings.abs_val_is_smaller_than_drop_tolerance(t)){
m_row_eta_work_vector[col] = numeric_traits<T>::zero();
auto it = std::find(m_row_eta_work_vector.m_index.begin(), m_row_eta_work_vector.m_index.end(), col);
if (it != m_row_eta_work_vector.m_index.end())
m_row_eta_work_vector.m_index.erase(it);
}
}
}
}
lean_assert(m_row_eta_work_vector.is_OK());
}
void pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump);
// see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last
// row at the same time
row_eta_matrix<T, X> *get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) {
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);
T denom = std::max(T(1), abs(pivot_elem_for_checking));
if (
#ifdef LEAN_DEBUG
!is_zero(pivot_elem_for_checking) &&
#endif
!m_settings.abs_val_is_smaller_than_pivot_tolerance((m_row_eta_work_vector[lowest_row_of_the_bump] - pivot_elem_for_checking) / denom)) {
// std::cout << "m_row_eta_work_vector[" << lowest_row_of_the_bump << "] = " << T_to_string(m_row_eta_work_vector[lowest_row_of_the_bump]) << ", but pivot = " << T_to_string(pivot_elem_for_checking) << endl;
set_status(LU_status::Degenerated);
// std::cout << "diagonal element is off" << endl;
return nullptr;
}
#ifdef LEAN_DEBUG
auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump, m_dim);
#else
auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump);
#endif
for (auto j : m_row_eta_work_vector.m_index) {
if (j < lowest_row_of_the_bump) {
auto & v = m_row_eta_work_vector[j];
if (!is_zero(v)) {
if (!m_settings.abs_val_is_smaller_than_drop_tolerance(v)){
ret->push_back(j, v);
}
v = numeric_traits<T>::zero();
}
}
} // now the lowest_row_of_the_bump contains the rest of the row to the right of the bump with correct values
return ret;
}
row_eta_matrix<T, X> *get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking);
// This method does not update the basis: is_correct() should not be called since it works with the basis.
void replace_column(unsigned leaving, T pivot_elem, indexed_vector<T> & w){
lean_assert(m_basis_heading[leaving] >= 0);
unsigned replaced_column = transform_U_to_V_by_replacing_column(leaving, w);
unsigned lowest_row_of_the_bump = m_U.lowest_row_in_column(replaced_column);
permutation_matrix<T, X> r_wave(m_dim);
calculate_r_wave_and_update_U(replaced_column, lowest_row_of_the_bump, r_wave);
auto row_eta = get_row_eta_matrix_and_set_row_vector(replaced_column, lowest_row_of_the_bump, pivot_elem);
if (get_status() == LU_status::Degenerated) {
m_row_eta_work_vector.clear_all();
return;
}
m_Q.multiply_by_permutation_from_right(r_wave);
m_R.multiply_by_permutation_reverse_from_left(r_wave);
if (row_eta != nullptr) {
row_eta->conjugate_by_permutation(m_Q);
push_matrix_to_tail(row_eta);
}
calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump);
lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
lean_assert(w.is_OK() && m_row_eta_work_vector.is_OK());
}
void replace_column(unsigned leaving, T pivot_elem, indexed_vector<T> & w);
void calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){
T diagonal_elem;
if (replaced_column < lowest_row_of_the_bump) {
diagonal_elem = m_row_eta_work_vector[lowest_row_of_the_bump];
// lean_assert(m_row_eta_work_vector.is_OK());
m_U.set_row_from_work_vector_and_clean_work_vector_not_adjusted(m_U.adjust_row(lowest_row_of_the_bump), m_row_eta_work_vector, m_settings);
} else {
diagonal_elem = m_U(lowest_row_of_the_bump, lowest_row_of_the_bump); // todo - get it more efficiently
}
if (m_settings.abs_val_is_smaller_than_pivot_tolerance(diagonal_elem)) {
set_status(LU_status::Degenerated);
return;
}
void calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump);
calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem);
// lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
}
void calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) {
auto l = new one_elem_on_diag<T, X>(lowest_row_of_the_bump, diagonal_element);
#ifdef LEAN_DEBUG
l->set_number_of_columns(m_dim);
#endif
push_matrix_to_tail(l);
m_U.divide_row_by_constant(lowest_row_of_the_bump, diagonal_element, m_settings);
l->conjugate_by_permutation(m_Q);
}
void calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element);
void prepare_entering(unsigned entering, indexed_vector<T> & w) {
init_vector_w(entering, w);
}
}; // end of lu
template <typename T, typename X>
void init_factorization(lu<T, X>* & factorization, static_matrix<T, X> & m_A, std::vector<unsigned> & m_basis, std::vector<int> & m_basis_heading, lp_settings &m_settings, std::vector<unsigned> & non_basic_columns) {
if (factorization != nullptr) {
delete factorization;
}
factorization = new lu<T, X>(m_A, m_basis, m_basis_heading, m_settings, non_basic_columns);
if (factorization->get_status() != LU_status::OK) {
std::cout << "failing in init_factorization" << std::endl;
return;
}
}
void init_factorization(lu<T, X>* & factorization, static_matrix<T, X> & m_A, std::vector<unsigned> & m_basis, std::vector<int> & m_basis_heading, lp_settings &m_settings, std::vector<unsigned> & non_basic_columns);
#ifdef LEAN_DEBUG
template <typename T, typename X>
dense_matrix<T, X> get_B(lu<T, X>& f) {
dense_matrix<T, X> B(f.dimension(), f.dimension());
for (unsigned i = 0; i < f.dimension(); i++)
for (unsigned j = 0; j < f.dimension(); j++)
B.set_elem(i, j, f.B_(i, j));
return B;
}
dense_matrix<T, X> get_B(lu<T, X>& f);
#endif
}

View file

@ -0,0 +1,48 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lu.cpp"
template double lean::dot_product<double, double>(std::vector<double, std::allocator<double> > const&, std::vector<double, std::allocator<double> > const&, unsigned int);
template void lean::lu<double, double>::change_basis(unsigned int, unsigned int);
template lean::lu<double, double>::lu(lean::static_matrix<double, double> const&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<int, std::allocator<int> >&, lean::lp_settings&, std::vector<unsigned int, std::allocator<unsigned int> >&);
template void lean::lu<double, double>::push_matrix_to_tail(lean::tail_matrix<double, double>*);
template void lean::lu<double, double>::replace_column(unsigned int, double, lean::indexed_vector<double>&);
template void lean::lu<double, double>::restore_basis_change(unsigned int, unsigned int);
template void lean::lu<double, double>::solve_Bd(unsigned int, std::vector<double, std::allocator<double> >&, lean::indexed_vector<double>&);
template lean::lu<double, double>::~lu();
template void lean::lu<lean::mpq, lean::mpq>::change_basis(unsigned int, unsigned int);
template void lean::lu<lean::mpq, lean::mpq>::push_matrix_to_tail(lean::tail_matrix<lean::mpq, lean::mpq>*);
template void lean::lu<lean::mpq, lean::mpq>::restore_basis_change(unsigned int, unsigned int);
template void lean::lu<lean::mpq, lean::mpq>::solve_Bd(unsigned int, std::vector<lean::mpq, std::allocator<lean::mpq> >&, lean::indexed_vector<lean::mpq>&);
template lean::lu<lean::mpq, lean::mpq>::~lu();
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::change_basis(unsigned int, unsigned int);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::push_matrix_to_tail(lean::tail_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >*);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::restore_basis_change(unsigned int, unsigned int);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_Bd(unsigned int, std::vector<lean::mpq, std::allocator<lean::mpq> >&, lean::indexed_vector<lean::mpq>&);
template lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::~lu();
template lean::mpq lean::dot_product<lean::mpq, lean::mpq>(std::vector<lean::mpq, std::allocator<lean::mpq> > const&, std::vector<lean::mpq, std::allocator<lean::mpq> > const&, unsigned int);
template void lean::init_factorization<double, double>(lean::lu<double, double>*&, lean::static_matrix<double, double>&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<int, std::allocator<int> >&, lean::lp_settings&, std::vector<unsigned int, std::allocator<unsigned int> >&);
template void lean::init_factorization<lean::mpq, lean::mpq>(lean::lu<lean::mpq, lean::mpq>*&, lean::static_matrix<lean::mpq, lean::mpq>&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<int, std::allocator<int> >&, lean::lp_settings&, std::vector<unsigned int, std::allocator<unsigned int> >&);
template void lean::init_factorization<lean::mpq, lean::numeric_pair<lean::mpq> >(lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >*&, lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<int, std::allocator<int> >&, lean::lp_settings&, std::vector<unsigned int, std::allocator<unsigned int> >&);
#ifdef LEAN_DEBUG
template void lean::print_matrix<double, double>(lean::sparse_matrix<double, double>&);
template void lean::print_matrix<float, float>(lean::sparse_matrix<float, float>&);
template void lean::print_matrix<double, double>(lean::static_matrix<double, double>&);
template bool lean::lu<double, double>::is_correct();
template lean::dense_matrix<double, double> lean::get_B<double, double>(lean::lu<double, double>&);
#endif
template void lean::lu<lean::mpq, lean::mpq>::solve_yB(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template void lean::lu<double, double>::solve_yB(std::vector<double, std::allocator<double> >&);
template void lean::lu<lean::mpq, lean::mpq>::solve_By(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template void lean::lu<double, double>::solve_By(std::vector<double, std::allocator<double> >&);
template void lean::lu<lean::mpq, lean::mpq>::replace_column(unsigned int, lean::mpq, lean::indexed_vector<lean::mpq>&);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::replace_column(unsigned int, lean::mpq, lean::indexed_vector<lean::mpq>&);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_By(std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&);
template void lean::lu<double, double>::init_vector_w(unsigned int, lean::indexed_vector<double>&);
template lean::numeric_pair<lean::mpq> lean::dot_product<lean::mpq, lean::numeric_pair<lean::mpq> >(std::vector<lean::mpq, std::allocator<lean::mpq> > const&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > > const&, unsigned int);
template bool lean::lu<double, double>::pivot_the_row(int);

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

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

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

@ -0,0 +1,48 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#ifdef LEAN_DEBUG
#pragma once
#include "util/numerics/numeric_traits.h"
#include "util/numerics/double.h"
#include <vector>
#include <string>
#include "util/lp/lp_settings.h"
namespace lean {
// used for debugging purposes only
template <typename T, typename X>
class matrix {
public:
virtual T get_elem (unsigned i, unsigned j) const = 0;
virtual unsigned row_count() const = 0;
virtual unsigned column_count() const = 0;
virtual void set_number_of_rows(unsigned m) = 0;
virtual void set_number_of_columns(unsigned n) = 0;
virtual ~matrix() {}
bool is_equal(const matrix<T, X>& other);
bool operator == (matrix<T, X> const & other) {
return is_equal(other);
}
T operator()(unsigned i, unsigned j) const { return get_elem(i, j); }
};
template <typename T, typename X>
void apply_to_vector(matrix<T, X> & m, T * w);
unsigned get_width_of_column(unsigned j, std::vector<std::vector<std::string>> & A);
void print_matrix_with_widths(std::vector<std::vector<std::string>> & A, std::vector<unsigned> & ws);
void print_string_matrix(std::vector<std::vector<std::string>> & A);
template <typename T, typename X>
void print_matrix(matrix<T, X> const & m);
}
#endif

View file

@ -1,76 +0,0 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#pragma once
#include <vector>
#include "util/numerics/numeric_traits.h"
#include "util/numerics/xnumeral.h"
#include "util/numerics/mpq.h"
#include "util/numerics/mpz.h"
#include "util/numerics/mpbq.h"
#include "util/numerics/double.h"
#include "util/numerics/float.h"
#include "util/numerics/mpfp.h"
#include <unordered_map>
#include <utility>
template <class T>
inline void hash_combine(std::size_t & seed, const T & v) {
std::hash<T> hasher;
seed ^= hasher(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;
}
};
}
namespace lean {
template <typename T>
class matrix_domain {
std::vector<std::unordered_map<unsigned, void *>> m_domain;
public:
matrix_domain(unsigned rows) {
while (rows--) {
std::unordered_map<unsigned, void *> t;
m_domain.push_back(t);
}
}
void * find(unsigned i, unsigned j) const {
auto & v = m_domain[i];
auto t = v.find(j);
if (t == v.end()) {
return nullptr;
}
return t->second;
}
void erase(unsigned i, unsigned j) {
lean_assert(find(i, j) != nullptr);
m_domain[i].erase(j);
}
void insert(unsigned i, unsigned j, void * cell) {
lean_assert(m_domain[i].find(j) == m_domain[i].end());
m_domain[i][j] = cell;
}
unsigned size() const {
unsigned ret = 0;
for (auto & t : m_domain) {
ret += t.size();
}
return ret;
}
};
}

View file

@ -0,0 +1,11 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#ifdef LEAN_DEBUG
#include "util/lp/matrix.cpp"
template void lean::print_matrix<double, double>(lean::matrix<double, double> const&);
template bool lean::matrix<double, double>::is_equal(lean::matrix<double, double> const&);
#endif

View file

@ -9,6 +9,7 @@
#include <algorithm>
#include "util/numerics/mpq.h"
#include "util/numerics/double.h"
#include "util/lp/lp_settings.h"
namespace lean {
template <typename T>
@ -17,7 +18,7 @@ std::string T_to_string(const T & t); // forward definition
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 abs(get_double(x)) < y; }
static bool is_epsilon_small(const X & x, const double & y) { return std::abs(get_double(x)) < y; }
static bool below_bound_numeric(const X &, const X &, const Y &) { lean_unreachable(); }
static bool above_bound_numeric(const X &, const X &, const Y &) { lean_unreachable(); }
};

View file

@ -0,0 +1,387 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/permutation_matrix.h"
namespace lean {
template <typename T, typename X> permutation_matrix<T, X>::permutation_matrix(unsigned length): m_length(length), m_permutation(length), m_rev(length) {
unsigned i = m_length;
while (i--)
m_permutation[i] = m_rev[i] = i;
}
template <typename T, typename X> permutation_matrix<T, X>::permutation_matrix(unsigned length, std::vector<unsigned> const & values): m_length(length), m_permutation(length), m_rev(length) {
for (unsigned i = 0; i < length; i++) {
set_val(i, values[i]);
}
}
// create a unit permutation of the given length
template <typename T, typename X> void permutation_matrix<T, X>::init(unsigned length) {
m_length = length;
m_permutation.resize(length);
m_rev.resize(length);
unsigned i = length;
while (i--)
m_permutation[i] = m_rev[i] = i;
}
#ifdef LEAN_DEBUG
template <typename T, typename X> void permutation_matrix<T, X>::print() const {
std::cout << "[";
for (unsigned i = 0; i < m_length; i++) {
std::cout << m_permutation[i];
if (i < m_length - 1) {
std::cout << ",";
} else {
std::cout << "]";
}
}
std::cout << std::endl;
}
#endif
template <typename T, typename X> template <typename L>
void permutation_matrix<T, X>:: apply_from_left_perm(std::vector<L> & w) {
#ifdef LEAN_DEBUG
// dense_matrix<L, X> deb(*this);
// L * deb_w = clone_vector<L>(w, row_count());
// deb.apply_from_left(deb_w);
#endif
L * t = new L[m_length];
for (unsigned i = 0; i < m_length; i++) {
t[i] = w[m_permutation[i]];
}
for (unsigned i = 0; i < m_length; i++) {
w[i] = t[i];
}
delete [] t;
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<L>(deb_w, w, row_count()));
// delete [] deb_w;
#endif
}
template <typename T, typename X> template <typename L>
void permutation_matrix<T, X>:: apply_from_left_perm(indexed_vector<L> & w, lp_settings &) {
#ifdef LEAN_DEBUG
// dense_matrix<T, L> deb(*this);
// T * deb_w = clone_vector<T>(w.m_data, row_count());
// deb.apply_from_right(deb_w);
#endif
std::vector<L> t(w.m_index.size());
std::vector<unsigned> tmp_index(w.m_index.size());
copy_aside(t, tmp_index, w);
clear_data(w);
// set the new values
for (unsigned i = t.size(); i > 0;) {
i--;
unsigned j = m_rev[tmp_index[i]];
w[j] = t[i];
w.m_index[i] = j;
}
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(deb_w, w.m_data, row_count()));
// delete [] deb_w;
#endif
}
template <typename T, typename X> void permutation_matrix<T, X>:: apply_from_right(std::vector<T> & w) {
#ifdef LEAN_DEBUG
// dense_matrix<T, X> deb(*this);
// T * deb_w = clone_vector<T>(w, row_count());
// deb.apply_from_right(deb_w);
#endif
T * t = new T[m_length];
for (unsigned i = 0; i < m_length; i++) {
t[i] = w[m_rev[i]];
}
for (unsigned i = 0; i < m_length; i++) {
w[i] = t[i];
}
delete [] t;
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(deb_w, w, row_count()));
// delete [] deb_w;
#endif
}
template <typename T, typename X> template <typename L>
void permutation_matrix<T, X>:: copy_aside(std::vector<L> & t, std::vector<unsigned> & tmp_index, indexed_vector<L> & w) {
for (unsigned i = t.size(); i > 0;) {
i--;
unsigned j = w.m_index[i];
t[i] = w[j]; // copy aside all non-zeroes
tmp_index[i] = j; // and the indices too
}
}
template <typename T, typename X> template <typename L>
void permutation_matrix<T, X>:: clear_data(indexed_vector<L> & w) {
// clear old non-zeroes
for (unsigned i = w.m_index.size(); i > 0;) {
i--;
unsigned j = w.m_index[i];
w[j] = zero_of_type<L>();
}
}
template <typename T, typename X> template <typename L>
void permutation_matrix<T, X>::apply_reverse_from_left(indexed_vector<L> & w) {
// the result will be w = p(-1) * w
#ifdef LEAN_DEBUG
// dense_matrix<L, X> deb(get_reverse());
// L * deb_w = clone_vector<L>(w.m_data, row_count());
// deb.apply_from_left(deb_w);
#endif
std::vector<L> t(w.m_index.size());
std::vector<unsigned> tmp_index(w.m_index.size());
copy_aside(t, tmp_index, w);
clear_data(w);
// set the new values
for (unsigned i = t.size(); i > 0;) {
i--;
unsigned j = m_permutation[tmp_index[i]];
w[j] = t[i];
w.m_index[i] = j;
}
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<L>(deb_w, w.m_data, row_count()));
// delete [] deb_w;
#endif
}
template <typename T, typename X> template <typename L>
void permutation_matrix<T, X>::apply_reverse_from_left(std::vector<L> & w) {
// the result will be w = p(-1) * w
#ifdef LEAN_DEBUG
// dense_matrix<T, X> deb(get_reverse());
// T * deb_w = clone_vector<T>(w, row_count());
// deb.apply_from_left(deb_w);
#endif
std::vector<L> t(m_length);
for (unsigned i = 0; i < m_length; i++) {
t[m_permutation[i]] = w[i];
}
for (unsigned i = 0; i < m_length; i++) {
w[i] = t[i];
}
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(deb_w, w, row_count()));
// delete [] deb_w;
#endif
}
template <typename T, typename X> template <typename L>
void permutation_matrix<T, X>:: apply_reverse_from_right(std::vector<L> & w) {
// the result will be w = w * p(-1)
#ifdef LEAN_DEBUG
// dense_matrix<T, X> deb(get_reverse());
// T * deb_w = clone_vector<T>(w, row_count());
// deb.apply_from_right(deb_w);
#endif
L * t = new T[m_length];
for (unsigned i = 0; i < m_length; i++) {
t[i] = w[m_permutation[i]];
}
for (unsigned i = 0; i < m_length; i++) {
w[i] = t[i];
}
delete [] t;
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(deb_w, w, row_count()));
// delete deb_w;
#endif
}
template <typename T, typename X> void permutation_matrix<T, X>:: transpose_from_left(unsigned i, unsigned j) {
// the result will be this = (i,j)*this
lean_assert(i < m_length && j < m_length && i != j);
auto pi = m_rev[i];
auto pj = m_rev[j];
set_val(pi, j);
set_val(pj, i);
}
template <typename T, typename X> void permutation_matrix<T, X>:: transpose_from_right(unsigned i, unsigned j) {
// the result will be this = this * (i,j)
lean_assert(i < m_length && j < m_length && i != j);
auto pi = m_permutation[i];
auto pj = m_permutation[j];
set_val(i, pj);
set_val(j, pi);
}
template <typename T, typename X> unsigned * permutation_matrix<T, X>::clone_m_permutation() {
auto r = new unsigned[m_length];
for (int i = m_length - 1; i >= 0; i--) {
r[i] = m_permutation[i];
}
return r;
}
template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_permutation_from_left(permutation_matrix<T, X> & p) {
auto clone = clone_m_permutation();
lean_assert(p.m_length == m_length);
for (unsigned i = 0; i < m_length; i++) {
set_val(i, clone[p[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
}
delete clone;
}
// 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) {
auto clone = clone_m_permutation();
lean_assert(p.m_length == m_length);
for (unsigned i = 0; i < m_length; i++) {
set_val(i, p[clone[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
}
delete clone;
}
template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_reverse_from_right(permutation_matrix<T, X> & q){ // todo : condensed permutations ?
auto clone = clone_m_permutation();
// the result is this = this*q(-1)
for (unsigned i = 0; i < m_length; i++) {
set_val(i, q.m_rev[clone[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
}
delete clone;
}
template <typename T, typename X> void permutation_matrix<T, X>:: multiply_by_permutation_reverse_from_left(permutation_matrix<T, X> & r){ // todo : condensed permutations?
// the result is this = r(-1)*this
auto clone = clone_m_permutation();
// the result is this = this*q(-1)
for (unsigned i = 0; i < m_length; i++) {
set_val(i, clone[r.m_rev[i]]);
}
delete clone;
}
template <typename T, typename X> void permutation_matrix<T, X>::shrink_by_one_identity() {
lean_assert(is_identity());
m_length--;
delete [] m_permutation;
delete [] m_rev;
m_permutation = new unsigned[m_length];
m_rev = new unsigned[m_length];
for (unsigned i = 0; i < m_length; i++) {
m_permutation[i] = m_rev[i] = i;
}
}
template <typename T, typename X> bool permutation_matrix<T, X>::is_identity() const {
for (unsigned i = 0; i < m_length; i++) {
if (m_permutation[i] != i) {
return false;
}
}
return true;
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
permutation_generator<T, X>::permutation_generator(unsigned n): m_n(n), m_current(n) {
lean_assert(n > 0);
if (n > 1) {
m_lower = new permutation_generator(n - 1);
} else {
m_lower = nullptr;
}
m_last = 0;
}
template <typename T, typename X>
permutation_generator<T, X>::permutation_generator(const permutation_generator & o): m_n(o.m_n), m_done(o.m_done), m_current(o.m_current), m_last(o.m_last) {
if (m_lower != nullptr) {
m_lower = new permutation_generator(o.m_lower);
} else {
m_lower = nullptr;
}
}
template <typename T, typename X> bool
permutation_generator<T, X>::move_next() {
if (m_done) {
return false;
}
if (m_lower == nullptr) {
if (m_last == 0) {
m_last++;
return true;
} else {
m_done = true;
return false;
}
} else {
if (m_last < m_n && m_last > 0) {
m_current[m_last - 1] = m_current[m_last];
m_current[m_last] = m_n - 1;
m_last++;
return true;
} else {
if (m_lower -> move_next()) {
auto lower_curr = m_lower -> current();
for ( unsigned i = 1; i < m_n; i++ ){
m_current[i] = (*lower_curr)[i - 1];
}
m_current[0] = m_n - 1;
m_last = 1;
return true;
} else {
m_done = true;
return false;
}
}
}
}
template <typename T, typename X>
inline unsigned number_of_inversions(permutation_matrix<T, X> & p) {
unsigned ret = 0;
unsigned n = p.size();
for (unsigned i = 0; i < n; i++) {
for (unsigned j = i + 1; j < n; j++) {
if (p[i] > p[j]) {
ret++;
}
}
}
return ret;
}
template <typename T, typename X>
T det_val_on_perm(permutation_matrix<T, X>* u, const matrix<T, X>& m) {
unsigned n = m.row_count();
T ret = numeric_traits<T>::one();
for (unsigned i = 0; i < n; i++) {
unsigned j = (*u)[i];
ret *= m(i, j);
}
return ret * sign(*u);
}
template <typename T, typename X>
T determinant(const matrix<T, X>& m) {
lean_assert(m.column_count() == m.row_count());
unsigned n = m.row_count();
permutation_generator<T, X> allp(n);
T ret = numeric_traits<T>::zero();
while (allp.move_next()){
ret += det_val_on_perm(allp.current(), m);
}
return ret;
}
#endif
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,64 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/permutation_matrix.cpp"
#include "util/lp/numeric_pair.h"
template void lean::permutation_matrix<double, double>::apply_from_right(std::vector<double, std::allocator<double> >&);
template void lean::permutation_matrix<double, double>::init(unsigned int);
template bool lean::permutation_matrix<double, double>::is_identity() const;
template void lean::permutation_matrix<double, double>::multiply_by_permutation_from_left(lean::permutation_matrix<double, double>&);
template void lean::permutation_matrix<double, double>::multiply_by_permutation_reverse_from_left(lean::permutation_matrix<double, double>&);
template void lean::permutation_matrix<double, double>::multiply_by_reverse_from_right(lean::permutation_matrix<double, double>&);
template lean::permutation_matrix<double, double>::permutation_matrix(unsigned int, std::vector<unsigned int, std::allocator<unsigned int> > const&);
template void lean::permutation_matrix<double, double>::transpose_from_left(unsigned int, unsigned int);
template void lean::permutation_matrix<float, float>::apply_from_right(std::vector<float, std::allocator<float> >&);
template lean::permutation_matrix<float, float>::permutation_matrix(unsigned int);
template void lean::permutation_matrix<float, float>::transpose_from_left(unsigned int, unsigned int);
template void lean::permutation_matrix<float, float>::transpose_from_right(unsigned int, unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_from_right(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template bool lean::permutation_matrix<lean::mpq, lean::mpq>::is_identity() const;
template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_permutation_from_left(lean::permutation_matrix<lean::mpq, lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_permutation_from_right(lean::permutation_matrix<lean::mpq, lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_permutation_reverse_from_left(lean::permutation_matrix<lean::mpq, lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_reverse_from_right(lean::permutation_matrix<lean::mpq, lean::mpq>&);
template lean::permutation_matrix<lean::mpq, lean::mpq>::permutation_matrix(unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::transpose_from_left(unsigned int, unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::transpose_from_right(unsigned int, unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_right(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template bool lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::is_identity() const;
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_permutation_from_left(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_permutation_from_right(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_permutation_reverse_from_left(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_reverse_from_right(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
template lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::permutation_matrix(unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::transpose_from_left(unsigned int, unsigned int);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::transpose_from_right(unsigned int, unsigned int);
template void lean::permutation_matrix<double, double>::apply_from_left_perm<double>(lean::indexed_vector<double>&, lean::lp_settings&);
template void lean::permutation_matrix<double, double>::apply_from_left_perm<double>(std::vector<double, std::allocator<double> >&);
template void lean::permutation_matrix<double, double>::apply_reverse_from_left<double>(lean::indexed_vector<double>&);
template void lean::permutation_matrix<double, double>::apply_reverse_from_left<double>(std::vector<double, std::allocator<double> >&);
template void lean::permutation_matrix<double, double>::apply_reverse_from_right<double>(std::vector<double, std::allocator<double> >&);
template void lean::permutation_matrix<double, double>::transpose_from_right(unsigned int, unsigned int);
template void lean::permutation_matrix<float, float>::apply_from_left_perm<float>(lean::indexed_vector<float>&, lean::lp_settings&);
template void lean::permutation_matrix<float, float>::apply_from_left_perm<float>(std::vector<float, std::allocator<float> >&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_from_left_perm<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_from_left_perm<lean::mpq>(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_left<lean::mpq>(lean::indexed_vector<lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_left<lean::mpq>(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_right<lean::mpq>(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left_perm<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left_perm<lean::numeric_pair<lean::mpq> >(std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_left<lean::mpq>(lean::indexed_vector<lean::mpq>&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_left<lean::mpq>(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_left<lean::numeric_pair<lean::mpq> >(std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&);
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_right<lean::mpq>(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
template void lean::permutation_matrix<double, double>::multiply_by_permutation_from_right(lean::permutation_matrix<double, double>&);
#ifdef LEAN_DEBUG
template bool lean::permutation_generator<double, double>::move_next();
template lean::permutation_generator<double, double>::permutation_generator(unsigned int);
#endif
template lean::permutation_matrix<double, double>::permutation_matrix(unsigned int);

View file

@ -0,0 +1,154 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/row_eta_matrix.h"
namespace lean {
template <typename T, typename X>
void row_eta_matrix<T, X>::apply_from_left(std::vector<X> & w, lp_settings &) {
// #ifdef LEAN_DEBUG
// dense_matrix<T> deb(*this);
// auto clone_w = clone_vector<T>(w, m_dimension);
// deb.apply_from_left(clone_w, settings);
// #endif
auto w_at_row = w[m_row];
for (auto & it :m_row_vector.m_data) {
w_at_row += w[it.first] * it.second;
}
w[m_row] = w_at_row;
// #ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
// delete [] clone_w;
// #endif
}
template <typename T, typename X>
void row_eta_matrix<T, X>::apply_from_left_local_to_T(indexed_vector<T> & w, lp_settings & settings) {
auto w_at_row = w[m_row];
bool was_zero_at_m_row = is_zero(w_at_row);
for (auto & it : m_row_vector.m_data) {
w_at_row += w[it.first] * it.second;
}
if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){
if (was_zero_at_m_row) {
w.m_index.push_back(m_row);
}
w[m_row] = w_at_row;
} else if (!was_zero_at_m_row){
w[m_row] = zero_of_type<T>();
auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row);
w.m_index.erase(it);
}
lean_assert(check_vector_for_small_values(w, settings));
}
template <typename T, typename X>
void row_eta_matrix<T, X>::apply_from_left_local_to_X(indexed_vector<X> & w, lp_settings & settings) {
auto w_at_row = w[m_row];
bool was_zero_at_m_row = is_zero(w_at_row);
for (auto & it : m_row_vector.m_data) {
w_at_row += w[it.first] * it.second;
}
if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){
if (was_zero_at_m_row) {
w.m_index.push_back(m_row);
}
w[m_row] = w_at_row;
} else if (!was_zero_at_m_row){
w[m_row] = zero_of_type<X>();
auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row);
w.m_index.erase(it);
}
lean_assert(check_vector_for_small_values(w, settings));
}
template <typename T, typename X>
void row_eta_matrix<T, X>::apply_from_right(std::vector<T> & w) {
T w_row = w[m_row];
if (numeric_traits<T>::is_zero(w_row)) return;
#ifdef LEAN_DEBUG
// dense_matrix<T> deb(*this);
// auto clone_w = clone_vector<T>(w, m_dimension);
// deb.apply_from_right(clone_w);
#endif
for (auto & it : m_row_vector.m_data) {
w[it.first] += w_row * it.second;
}
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
// delete clone_w;
#endif
}
template <typename T, typename X>
void row_eta_matrix<T, X>::apply_from_right(indexed_vector<T> & w) {
T w_row = w[m_row];
if (numeric_traits<T>::is_zero(w_row)) return;
#ifdef LEAN_DEBUG
// dense_matrix<T> deb(*this);
// auto clone_w = clone_vector<T>(w.m_data, m_dimension);
// deb.apply_from_right(clone_w);
#endif
for (auto & it : m_row_vector.m_data) {
T old_val = w[it.first];
T v = w[it.index()] += w_row * it.second;
if (numeric_traits<T>::is_zero(old_val)) {
w.m_index.push_back(it.index());
} else if (numeric_traits<T>::is_zero(v)) { // it is a very rare case
auto w_it = std::find(w.m_index.begin(), w.m_index.end(), it.index());
lean_assert(w_it != w.m_index.end());
w.m_index.erase(w_it);
}
}
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w.m_data, m_dimension));
// for (unsigned i = 0; i < m_dimension; i++) {
// if (!numeric_traits<T>::is_zero(w.m_data[i])) {
// lean_assert(std::find(w.m_index.begin(), w.m_index.end(), i) != w.m_index.end());
// }
// }
// delete clone_w;
#endif
}
template <typename T, typename X>
void row_eta_matrix<T, X>::conjugate_by_permutation(permutation_matrix<T, X> & p) {
// this = p * this * p(-1)
#ifdef LEAN_DEBUG
// auto rev = p.get_reverse();
// auto deb = ((*this) * rev);
// deb = p * deb;
#endif
m_row = p.apply_reverse(m_row);
// copy aside the column indices
std::vector<unsigned> columns;
for (auto & it : m_row_vector.m_data)
columns.push_back(it.first);
for (unsigned i = columns.size(); i-- > 0;)
m_row_vector.m_data[i].first = p.get_rev(columns[i]);
#ifdef LEAN_DEBUG
// lean_assert(deb == *this);
#endif
}
#ifdef LEAN_DEBUG
template <typename T, typename X>
T row_eta_matrix<T, X>::get_elem(unsigned row, unsigned col) const {
if (row == m_row){
if (col == row) {
return numeric_traits<T>::one();
}
return m_row_vector[col];
}
return col == row ? numeric_traits<T>::one() : numeric_traits<T>::zero();
}
#endif
}

View file

@ -19,7 +19,7 @@
#include <string>
#include "util/lp/sparse_vector.h"
#include "util/lp/indexed_vector.h"
#include "util/lp/permutation_matrix.h"
namespace lean {
// This is the sum of a unit matrix and a lower triangular matrix
// with non-zero elements only in one column
@ -54,145 +54,25 @@ public:
return m_row_vector.m_data[m_row];
}
void apply_from_left(std::vector<X> & w, lp_settings &
#ifdef LEAN_DEBUG
// settings
#endif
) {
// #ifdef LEAN_DEBUG
// dense_matrix<T> deb(*this);
// auto clone_w = clone_vector<T>(w, m_dimension);
// deb.apply_from_left(clone_w, settings);
// #endif
auto w_at_row = w[m_row];
for (auto it = sparse_vector_iterator<T>(m_row_vector); !it.done(); it.move()) {
w_at_row += w[it.index()] * it.value();
}
w[m_row] = w_at_row;
// #ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
// delete [] clone_w;
// #endif
}
void apply_from_left(std::vector<X> & w, lp_settings &);
template <typename L>
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
#ifdef LEAN_DEBUG
// dense_matrix<T> deb(*this);
// auto clone_w = clone_vector<T>(w.m_data, m_dimension);
// deb.apply_from_left(clone_w);
#endif
auto w_at_row = w[m_row];
bool was_zero_at_m_row = is_zero(w_at_row);
for (auto it = sparse_vector_iterator<T>(m_row_vector); !it.done(); it.move()) {
w_at_row += w[it.index()] * it.value();
}
if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){
if (was_zero_at_m_row) {
w.m_index.push_back(m_row);
}
w[m_row] = w_at_row;
} else if (!was_zero_at_m_row){
w[m_row] = zero_of_type<L>();
auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row);
w.m_index.erase(it);
}
lean_assert(check_vector_for_small_values(w, settings));
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w.m_data, m_dimension));
// delete clone_w;
#endif
}
void apply_from_left_local_to_T(indexed_vector<T> & w, lp_settings & settings);
void apply_from_left_local_to_X(indexed_vector<X> & w, lp_settings & settings);
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
apply_from_left_local(w, settings);
apply_from_left_local_to_T(w, settings);
}
void push_back(unsigned row_index, T val ) {
m_row_vector.push_back(row_index, val);
}
void apply_from_right(std::vector<T> & w) {
T w_row = w[m_row];
if (numeric_traits<T>::is_zero(w_row)) return;
#ifdef LEAN_DEBUG
// dense_matrix<T> deb(*this);
// auto clone_w = clone_vector<T>(w, m_dimension);
// deb.apply_from_right(clone_w);
#endif
for (auto it = sparse_vector_iterator<T>(m_row_vector); !it.done(); it.move()) {
w[it.index()] += w_row * it.value();
}
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
// delete clone_w;
#endif
}
void apply_from_right(std::vector<T> & w);
void apply_from_right(indexed_vector<T> & w);
void apply_from_right(indexed_vector<T> & w) {
T w_row = w[m_row];
if (numeric_traits<T>::is_zero(w_row)) return;
#ifdef LEAN_DEBUG
// dense_matrix<T> deb(*this);
// auto clone_w = clone_vector<T>(w.m_data, m_dimension);
// deb.apply_from_right(clone_w);
#endif
for (auto it = sparse_vector_iterator<T>(m_row_vector); !it.done(); it.move()) {
T old_val = w[it.index()];
T v = w[it.index()] += w_row * it.value();
if (numeric_traits<T>::is_zero(old_val)) {
w.m_index.push_back(it.index());
} else if (numeric_traits<T>::is_zero(v)) { // it is a very rare case
auto w_it = std::find(w.m_index.begin(), w.m_index.end(), it.index());
lean_assert(w_it != w.m_index.end());
w.m_index.erase(w_it);
}
}
#ifdef LEAN_DEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w.m_data, m_dimension));
// for (unsigned i = 0; i < m_dimension; i++) {
// if (!numeric_traits<T>::is_zero(w.m_data[i])) {
// lean_assert(std::find(w.m_index.begin(), w.m_index.end(), i) != w.m_index.end());
// }
// }
// delete clone_w;
#endif
}
void conjugate_by_permutation(permutation_matrix<T, X> & p) {
// this = p * this * p(-1)
#ifdef LEAN_DEBUG
// auto rev = p.get_reverse();
// auto deb = ((*this) * rev);
// deb = p * deb;
#endif
m_row = p.apply_reverse(m_row);
// copy aside the column indices
std::vector<unsigned> columns;
for (auto it = sparse_vector_iterator<T>(m_row_vector); !it.done(); it.move()) {
columns.push_back(it.index());
}
for (unsigned i = columns.size(); i-- > 0;) {
m_row_vector.m_data[i].first = p.get_rev(columns[i]);
}
#ifdef LEAN_DEBUG
// lean_assert(deb == *this);
#endif
}
T get_elem(unsigned row, unsigned col) const {
if (row == m_row){
if (col == row) {
return numeric_traits<T>::one();
}
return m_row_vector[col];
}
return col == row ? numeric_traits<T>::one() : numeric_traits<T>::zero();
}
void conjugate_by_permutation(permutation_matrix<T, X> & p);
#ifdef LEAN_DEBUG
T get_elem(unsigned row, unsigned col) const;
unsigned row_count() const { return m_dimension; }
unsigned column_count() const { return m_dimension; }
void set_number_of_rows(unsigned /*m*/) { }

View file

@ -0,0 +1,31 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/row_eta_matrix.cpp"
#include "util/lp/lu.h"
namespace lean {
template void row_eta_matrix<double, double>::conjugate_by_permutation(permutation_matrix<double, double>&);
template void row_eta_matrix<mpq, numeric_pair<mpq> >::conjugate_by_permutation(permutation_matrix<mpq, numeric_pair<mpq> >&);
template void row_eta_matrix<mpq, mpq>::conjugate_by_permutation(permutation_matrix<mpq, mpq>&);
#ifdef LEAN_DEBUG
template mpq row_eta_matrix<mpq, mpq>::get_elem(unsigned int, unsigned int) const;
template mpq row_eta_matrix<mpq, numeric_pair<mpq> >::get_elem(unsigned int, unsigned int) const;
template double row_eta_matrix<double, double>::get_elem(unsigned int, unsigned int) const;
#endif
template void row_eta_matrix<mpq, mpq>::apply_from_left(std::vector<mpq, std::allocator<mpq> >&, lp_settings&);
template void row_eta_matrix<mpq, mpq>::apply_from_right(std::vector<mpq, std::allocator<mpq> >&);
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_left(std::vector<numeric_pair<mpq>, std::allocator<numeric_pair<mpq> > >&, lp_settings&);
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_right(std::vector<mpq, std::allocator<mpq> >&);
template void row_eta_matrix<double, double>::apply_from_left(std::vector<double, std::allocator<double> >&, lp_settings&);
template void row_eta_matrix<double, double>::apply_from_right(std::vector<double, std::allocator<double> >&);
template void row_eta_matrix<mpq, mpq>::apply_from_left_to_T(indexed_vector<mpq>&, lp_settings&);
template void row_eta_matrix<mpq, mpq>::apply_from_left_local_to_T(indexed_vector<mpq>&, lp_settings&);
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_left_to_T(indexed_vector<mpq>&, lp_settings&);
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_left_local_to_T(indexed_vector<mpq>&, lp_settings&);
template void row_eta_matrix<double, double>::apply_from_left_to_T(indexed_vector<double>&, lp_settings&);
template void row_eta_matrix<double, double>::apply_from_left_local_to_T(indexed_vector<double>&, lp_settings&);
}

236
src/util/lp/scaler.cpp Normal file
View file

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

View file

@ -12,6 +12,7 @@
#include <stdio.h> /* printf, fopen */
#include <stdlib.h> /* exit, EXIT_FAILURE */
#include "util/numerics/double.h"
#include "util/lp/static_matrix.h"
namespace lean {
// for scaling an LP
template <typename T, typename X>
@ -36,233 +37,45 @@ public:
m_column_scale.resize(m_A.column_count(), numeric_traits<T>::one());
}
T right_side_balance() {
T ret = zero_of_type<T>();
unsigned i = m_A.row_count();
while (i--) {
T rs = abs(convert_struct<T, X>::convert(m_b[i]));
if (!is_zero<T>(rs)) {
numeric_traits<T>::log(rs);
ret += rs * rs;
}
}
return ret;
}
T right_side_balance();
T get_balance() {
return m_A.get_balance();
}
T get_balance() { return m_A.get_balance(); }
T A_min() const {
T min = zero_of_type<T>();
for (unsigned i = 0; i < m_A.row_count(); i++) {
T t = m_A.get_min_abs_in_row(i);
min = i == 0 ? t : std::min(t, min);
}
return min;
}
T A_min() const;
T A_max() const {
T max = zero_of_type<T>();
for (unsigned i = 0; i < m_A.row_count(); i++) {
T t = m_A.get_max_abs_in_row(i);
max = i == 0? t : std::max(t, max);
}
return max;
}
T A_max() const;
T get_A_ratio() const {
T min = A_min();
T max = A_max();
T ratio = max / min;
return ratio;
}
T get_A_ratio() const;
T get_max_ratio_on_rows() const {
T ret = T(1);
unsigned i = m_A.row_count();
while (i--) {
T t = m_A.get_max_abs_in_row(i)/m_A.get_min_abs_in_row(i);
if (t > ret)
ret = t;
}
return ret;
}
T get_max_ratio_on_rows() const;
T get_max_ratio_on_columns() const {
T ret = T(1);
unsigned i = m_A.column_count();
while (i--) {
T t = m_A.get_max_abs_in_column(i)/m_A.get_min_abs_in_column(i);
if (t > ret)
ret = t;
}
return ret;
}
T get_max_ratio_on_columns() const;
void scale_rows_with_geometric_mean() {
unsigned i = m_A.row_count();
while (i--) {
T max = m_A.get_max_abs_in_row(i);
T min = m_A.get_min_abs_in_row(i);
lean_assert(max > zero_of_type<T>() && min > zero_of_type<T>());
T gm = T(sqrt(numeric_traits<T>::get_double(max*min)));
m_A.divide_row_by_constant(i, gm);
m_b[i] /= gm;
}
}
void scale_rows_with_geometric_mean();
void scale_columns_with_geometric_mean() {
unsigned i = m_A.column_count();
while (i--) {
T max = m_A.get_max_abs_in_column(i);
T min = m_A.get_min_abs_in_column(i);
T gm = T(1)/T(sqrt(numeric_traits<T>::get_double(max*min)));
m_A.scale_column(i, gm);
m_column_scale[i]*=gm;
}
}
void scale_columns_with_geometric_mean();
void scale_once_for_ratio() {
T max_ratio_on_rows = get_max_ratio_on_rows();
T max_ratio_on_columns = get_max_ratio_on_columns();
bool scale_rows_first = max_ratio_on_rows > max_ratio_on_columns;
// if max_ratio_on_columns is the largerst then the rows are in worser shape then columns
if (scale_rows_first) {
scale_rows_with_geometric_mean();
scale_columns_with_geometric_mean();
} else {
scale_columns_with_geometric_mean();
scale_rows_with_geometric_mean();
}
}
void scale_once_for_ratio();
bool scale_with_ratio() {
T ratio = get_A_ratio();
// The ratio is greater than or equal to one. We would like to diminish it and bring it as close to 1 as possible
unsigned reps = 20;
do {
scale_once_for_ratio();
T new_r = get_A_ratio();
if (new_r >= T(0.9) * ratio)
break;
} while (reps--);
bool scale_with_ratio();
bring_rows_and_columns_maximums_to_one();
return true;
}
void bring_row_maximums_to_one();
void bring_row_maximums_to_one() {
unsigned i = m_A.row_count();
while (i--) {
T t = m_A.get_max_abs_in_row(i);
m_A.divide_row_by_constant(i, t);
m_b[i] /= t;
}
}
void bring_column_maximums_to_one();
void bring_column_maximums_to_one() {
unsigned i = m_A.column_count();
while (i--) {
T t = T(1) / m_A.get_max_abs_in_column(i);
m_A.scale_column(i, t);
m_column_scale[i] *= t;
}
}
void bring_rows_and_columns_maximums_to_one();
void bring_rows_and_columns_maximums_to_one() {
if (get_max_ratio_on_rows() > get_max_ratio_on_columns()) {
bring_row_maximums_to_one();
bring_column_maximums_to_one();
} else {
bring_column_maximums_to_one();
bring_row_maximums_to_one();
}
}
bool scale_with_log_balance() {
T balance = get_balance();
T balance_before_scaling = balance;
// todo : analyze the scale order : rows-columns, or columns-rows. Iterate if needed
for (int i = 0; i < 10; i++) {
scale_rows();
scale_columns();
T nb = get_balance();
if (nb < T(0.9) * balance) {
balance = nb;
} else {
balance = nb;
break;
}
}
return balance <= balance_before_scaling;
}
bool scale_with_log_balance();
// Returns true if and only if the scaling was successful.
// It is the caller responsibility to restore the matrix
bool scale() {
if (numeric_traits<T>::precise()) return true;
if (m_settings.scale_with_ratio)
return scale_with_ratio();
return scale_with_log_balance();
}
bool scale();
void scale_rows() {
for (unsigned i = 0; i < m_A.row_count(); i++)
scale_row(i);
}
void scale_rows();
void scale_row(unsigned i) {
T row_max = std::max(m_A.get_max_abs_in_row(i), abs(convert_struct<T, X>::convert(m_b[i])));
T alpha = numeric_traits<T>::one();
if (numeric_traits<T>::is_zero(row_max)) {
return;
}
if (numeric_traits<T>::get_double(row_max) < m_scaling_minimum) {
do {
alpha *= 2;
row_max *= 2;
} while (numeric_traits<T>::get_double(row_max) < m_scaling_minimum);
m_A.scale_row(i, alpha);
m_b[i] *= alpha;
} else if (numeric_traits<T>::get_double(row_max) > m_scaling_maximum) {
do {
alpha /= 2;
row_max /= 2;
} while (numeric_traits<T>::get_double(row_max) > m_scaling_maximum);
m_A.scale_row(i, alpha);
m_b[i] *= alpha;
}
}
void scale_row(unsigned i);
void scale_column(unsigned i){
T column_max = m_A.get_max_abs_in_column(i);
T alpha = numeric_traits<T>::one();
void scale_column(unsigned i);
if (numeric_traits<T>::is_zero(column_max)){
return; // the column has zeros only
}
if (numeric_traits<T>::get_double(column_max) < m_scaling_minimum) {
do {
alpha *= 2;
column_max *= 2;
} while (numeric_traits<T>::get_double(column_max) < m_scaling_minimum);
} else if (numeric_traits<T>::get_double(column_max) > m_scaling_maximum) {
do {
alpha /= 2;
column_max /= 2;
} while (numeric_traits<T>::get_double(column_max) > m_scaling_maximum);
} else {
return;
}
m_A.scale_column(i, alpha);
m_column_scale[i] = alpha;
}
void scale_columns() {
for (unsigned i = 0; i < m_A.column_count(); i++) {
scale_column(i);
}
}
void scale_columns();
};
}

View file

@ -0,0 +1,9 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/scaler.cpp"
template bool lean::scaler<double, double>::scale();
template bool lean::scaler<lean::mpq, lean::mpq>::scale();

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,99 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/lu.h"
#include "util/lp/sparse_matrix.cpp"
namespace lean {
template double sparse_matrix<double, double>::dot_product_with_row<double>(unsigned int, std::vector<double> const&) const;
template void sparse_matrix<double, double>::add_new_element(unsigned int, unsigned int, double);
template void sparse_matrix<double, double>::divide_row_by_constant(unsigned int, double&, lp_settings&);
template void sparse_matrix<double, double>::fill_eta_matrix(unsigned int, eta_matrix<double, double>**);
template const double & sparse_matrix<double, double>::get(unsigned int, unsigned int) const;
template unsigned sparse_matrix<double, double>::get_number_of_nonzeroes() const;
template unsigned sparse_matrix<double, double>::get_number_of_nonzeroes_below_row(unsigned int) const;
template bool sparse_matrix<double, double>::get_pivot_for_column(unsigned int&, unsigned int&, double const&, unsigned int);
template unsigned sparse_matrix<double, double>::lowest_row_in_column(unsigned int);
template bool sparse_matrix<double, double>::pivot_row_to_row(unsigned int, double, unsigned int, lp_settings&);
template bool sparse_matrix<double, double>::pivot_with_eta(unsigned int, eta_matrix<double, double>*, lp_settings&);
template void sparse_matrix<double, double>::prepare_for_factorization();
template void sparse_matrix<double, double>::remove_element(std::vector<indexed_value<double> >&, indexed_value<double>&);
template void sparse_matrix<double, double>::replace_column(unsigned int, indexed_vector<double>&, lp_settings&);
template void sparse_matrix<double, double>::set(unsigned int, unsigned int, double);
template void sparse_matrix<double, double>::set_max_in_row(std::vector<indexed_value<double> >&);
template bool sparse_matrix<double, double>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector<double>&, lp_settings&);
template bool sparse_matrix<double, double>::shorten_active_matrix(unsigned int, eta_matrix<double, double>*);
template void sparse_matrix<double, double>::solve_y_U(std::vector<double>&) const;
template sparse_matrix<double, double>::sparse_matrix(static_matrix<double, double> const&, std::vector<unsigned int>&);
template sparse_matrix<double, double>::sparse_matrix(unsigned int);
template float const & sparse_matrix<float, float>::get(unsigned int, unsigned int) const;
template bool sparse_matrix<float, float>::pivot_row_to_row(unsigned int, float, unsigned int, lp_settings&);
template void sparse_matrix<float, float>::replace_column(unsigned int, indexed_vector<float>&, lp_settings&);
template void sparse_matrix<float, float>::set(unsigned int, unsigned int, float);
template sparse_matrix<float, float>::sparse_matrix(unsigned int);
template void sparse_matrix<mpq, mpq>::add_new_element(unsigned int, unsigned int, mpq);
template void sparse_matrix<mpq, mpq>::divide_row_by_constant(unsigned int, mpq&, lp_settings&);
template void sparse_matrix<mpq, mpq>::fill_eta_matrix(unsigned int, eta_matrix<mpq, mpq>**);
template mpq const & sparse_matrix<mpq, mpq>::get(unsigned int, unsigned int) const;
template unsigned sparse_matrix<mpq, mpq>::get_number_of_nonzeroes() const;
template unsigned sparse_matrix<mpq, mpq>::get_number_of_nonzeroes_below_row(unsigned int) const;
template bool sparse_matrix<mpq, mpq>::get_pivot_for_column(unsigned int&, unsigned int&, mpq const&, unsigned int);
template unsigned sparse_matrix<mpq, mpq>::lowest_row_in_column(unsigned int);
template bool sparse_matrix<mpq, mpq>::pivot_with_eta(unsigned int, eta_matrix<mpq, mpq>*, lp_settings&);
template void sparse_matrix<mpq, mpq>::prepare_for_factorization();
template void sparse_matrix<mpq, mpq>::remove_element(std::vector<indexed_value<mpq>> &, indexed_value<mpq>&);
template void sparse_matrix<mpq, mpq>::replace_column(unsigned int, indexed_vector<mpq>&, lp_settings&);
template void sparse_matrix<mpq, mpq>::set_max_in_row(std::vector<indexed_value<mpq>>&);
template bool sparse_matrix<mpq, mpq>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector<mpq>&, lp_settings&);
template bool sparse_matrix<mpq, mpq>::shorten_active_matrix(unsigned int, eta_matrix<mpq, mpq>*);
template void sparse_matrix<mpq, mpq>::solve_y_U(std::vector<mpq>&) const;
template sparse_matrix<mpq, mpq>::sparse_matrix(static_matrix<mpq, mpq> const&, std::vector<unsigned int>&);
template void sparse_matrix<mpq, numeric_pair<mpq>>::add_new_element(unsigned int, unsigned int, mpq);
template void sparse_matrix<mpq, numeric_pair<mpq>>::divide_row_by_constant(unsigned int, mpq&, lp_settings&);
template void sparse_matrix<mpq, numeric_pair<mpq>>::fill_eta_matrix(unsigned int, eta_matrix<mpq, numeric_pair<mpq> >**);
template const mpq & sparse_matrix<mpq, numeric_pair<mpq>>::get(unsigned int, unsigned int) const;
template unsigned sparse_matrix<mpq, numeric_pair<mpq>>::get_number_of_nonzeroes() const;
template unsigned sparse_matrix<mpq, numeric_pair<mpq>>::get_number_of_nonzeroes_below_row(unsigned int) const;
template bool sparse_matrix<mpq, numeric_pair<mpq>>::get_pivot_for_column(unsigned int&, unsigned int&, mpq const&, unsigned int);
template unsigned sparse_matrix<mpq, numeric_pair<mpq>>::lowest_row_in_column(unsigned int);
template bool sparse_matrix<mpq, numeric_pair<mpq>>::pivot_with_eta(unsigned int, eta_matrix<mpq, numeric_pair<mpq> >*, lp_settings&);
template void sparse_matrix<mpq, numeric_pair<mpq>>::prepare_for_factorization();
template void sparse_matrix<mpq, numeric_pair<mpq>>::remove_element(std::vector<indexed_value<mpq>>&, indexed_value<mpq>&);
template void sparse_matrix<mpq, numeric_pair<mpq>>::replace_column(unsigned int, indexed_vector<mpq>&, lp_settings&);
template void sparse_matrix<mpq, numeric_pair<mpq>>::set_max_in_row(std::vector<indexed_value<mpq>>&);
template bool sparse_matrix<mpq, numeric_pair<mpq>>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector<mpq>&, lp_settings&);
template bool sparse_matrix<mpq, numeric_pair<mpq>>::shorten_active_matrix(unsigned int, eta_matrix<mpq, numeric_pair<mpq> >*);
template void sparse_matrix<mpq, numeric_pair<mpq>>::solve_y_U(std::vector<mpq>&) const;
template sparse_matrix<mpq, numeric_pair<mpq>>::sparse_matrix(static_matrix<mpq, numeric_pair<mpq> > const&, std::vector<unsigned int>&);
template void sparse_matrix<double, double>::double_solve_U_y<double>(std::vector<double>&);
template void sparse_matrix<mpq, mpq>::double_solve_U_y<mpq>(std::vector<mpq>&);
template void sparse_matrix<mpq, numeric_pair<mpq>>::double_solve_U_y<mpq>(std::vector<mpq>&);
template void sparse_matrix<mpq, numeric_pair<mpq> >::double_solve_U_y<numeric_pair<mpq> >(std::vector<numeric_pair<mpq>>&);
/////////////////////
/*
template void lu<double, double>::create_initial_factorization();
template void lu<mpq, mpq>::create_initial_factorization();
template void lu<mpq, mpq>::replace_column(unsigned int, mpq, indexed_vector<mpq>&);
template void lu<mpq, numeric_pair<mpq> >::create_initial_factorization();
template void lu<mpq, numeric_pair<mpq> >::replace_column(unsigned int, mpq, indexed_vector<mpq>&);
template void lu<double, double>::init_vector_w(unsigned int, indexed_vector<double>&);
template void lu<mpq, mpq>::find_error_of_yB(std::vector<mpq, std::allocator<mpq> >&, std::vector<mpq, std::allocator<mpq> > const&);
template void lu<mpq, mpq>::init_vector_w(unsigned int, indexed_vector<mpq>&);
template void lu<mpq, numeric_pair<mpq> >::find_error_of_yB(std::vector<mpq, std::allocator<mpq> >&, std::vector<mpq, std::allocator<mpq> > const&);
template void lu<mpq, numeric_pair<mpq> >::init_vector_w(unsigned int, indexed_vector<mpq>&);
template void lu<double, double>::find_error_of_yB(std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> > const&);
#ifdef LEAN_DEBUG
template void print_matrix<double, double>(static_matrix<double, double>&);
#endif
*/
#ifdef LEAN_DEBUG
template bool sparse_matrix<double, double>::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const;
template bool sparse_matrix<mpq, mpq>::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const;
#endif
#ifdef LEAN_DEBUG
template bool sparse_matrix<mpq, numeric_pair<mpq> >::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const;
#endif
}

View file

@ -19,19 +19,6 @@
#include "util/numerics/mpfp.h"
#include "util/lp/lp_settings.h"
namespace lean {
template <typename T>
void zero_vector(T * t, unsigned size) {
while (size-- > 0) { // it can be made faster by copying big chunks
t[size] = numeric_traits<T>::zero();
}
}
template <typename T>
T abs (T const & v) { return v >= zero_of_type<T>() ? v : -v; }
template <typename T>
class sparse_vector_iterator; // forward definition
template <typename T>
class sparse_vector {
@ -48,45 +35,13 @@ public:
return numeric_traits<T>::zero();
}
#endif
void divide(T const & a) {
lean_assert(!lp_settings::is_eps_small_general(a, 1e-12));
for (auto & t : m_data) {
t.second /= a;
}
for (auto & t : m_data) { t.second /= a; }
}
unsigned size() const {
return m_data.size();
}
friend sparse_vector_iterator<T>;
};
template <typename T>
class sparse_vector_iterator {
typedef typename std::vector<pair<unsigned, T>>::iterator p_it;
p_it m_it;
p_it m_end;
public:
sparse_vector_iterator(sparse_vector<T> & s_v) : m_it(s_v.m_data.begin()),
m_end(s_v.m_data.end()) {
}
bool done() {
return m_it == m_end;
}
void move() {
m_it++;
}
unsigned index() {
return m_it->first;
}
const T & value() const {
return m_it->second;
}
};
}

View file

@ -0,0 +1,352 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/square_dense_submatrix.h"
namespace lean {
template <typename T, typename X>
square_dense_submatrix<T, X>::square_dense_submatrix (sparse_matrix<T, X> *parent_matrix, unsigned index_start) :
m_index_start(index_start),
m_dim(parent_matrix->dimension() - index_start),
m_v(m_dim * m_dim),
m_parent(parent_matrix),
m_row_permutation(m_parent->dimension()),
m_column_permutation(m_parent->dimension()) {
int row_offset = - static_cast<int>(m_index_start);
for (unsigned i = index_start; i < parent_matrix->dimension(); i++) {
unsigned row = parent_matrix->adjust_row(i);
for (auto & iv : parent_matrix->get_row_values(row)) {
unsigned j = parent_matrix->adjust_column_inverse(iv.m_index);
lean_assert(j>= m_index_start);
m_v[row_offset + j] = iv.m_value;
}
row_offset += m_dim;
}
}
template <typename T, typename X> void square_dense_submatrix<T, X>::init(sparse_matrix<T, X> *parent_matrix, unsigned index_start) {
m_index_start = index_start;
m_dim = parent_matrix->dimension() - index_start;
m_v.resize(m_dim * m_dim);
m_parent = parent_matrix;
m_column_permutation.init(m_parent->dimension());
for (unsigned i = index_start; i < parent_matrix->dimension(); i++) {
unsigned row = parent_matrix->adjust_row(i);
for (auto & iv : parent_matrix->get_row_values(row)) {
unsigned j = parent_matrix->adjust_column_inverse(iv.m_index);
(*this)[i][j] = iv.m_value;
}
}
}
template <typename T, typename X> int square_dense_submatrix<T, X>::find_pivot_column_in_row(unsigned i) const {
int j = -1;
T max = zero_of_type<T>();
lean_assert(i >= m_index_start);
unsigned row_start = (i - m_index_start) * m_dim;
for (unsigned k = i; k < m_parent->dimension(); k++) {
unsigned col = adjust_column(k); // this is where the column is in the row
unsigned offs = row_start + col - m_index_start;
T t = abs(m_v[offs]);
if (t > max) {
j = k;
max = t;
}
}
return j;
}
template <typename T, typename X> void square_dense_submatrix<T, X>::pivot(unsigned i, lp_settings & settings) {
divide_row_by_pivot(i);
for (unsigned k = i + 1; k < m_parent->dimension(); k++)
pivot_row_to_row(i, k, settings);
}
template <typename T, typename X> void square_dense_submatrix<T, X>::pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings) {
lean_assert(i < row);
unsigned pj = adjust_column(i); // the pivot column
unsigned pjd = pj - m_index_start;
unsigned pivot_row_offset = (i-m_index_start)*m_dim;
T pivot = m_v[pivot_row_offset + pjd];
unsigned row_offset= (row-m_index_start)*m_dim;
T m = m_v[row_offset + pjd];
lean_assert(!is_zero(pivot));
m_v[row_offset + pjd] = -m * pivot; // creating L matrix
for (unsigned j = m_index_start; j < m_parent->dimension(); j++) {
if (j == pj) {
pivot_row_offset++;
row_offset++;
continue;
}
auto t = m_v[row_offset] - m_v[pivot_row_offset] * m;
if (settings.abs_val_is_smaller_than_drop_tolerance(t)) {
m_v[row_offset] = zero_of_type<T>();
} else {
m_v[row_offset] = t;
}
row_offset++; pivot_row_offset++;
// at the same time we pivot the L too
}
}
template <typename T, typename X> void square_dense_submatrix<T, X>::divide_row_by_pivot(unsigned i) {
unsigned pj = adjust_column(i); // the pivot column
unsigned irow_offset = (i - m_index_start) * m_dim;
T pivot = m_v[irow_offset + pj - m_index_start];
lean_assert(!is_zero(pivot));
for (unsigned k = m_index_start; k < m_parent->dimension(); k++) {
if (k == pj){
m_v[irow_offset++] = one_of_type<T>() / pivot; // creating the L matrix diagonal
continue;
}
m_v[irow_offset++] /= pivot;
}
}
template <typename T, typename X> void square_dense_submatrix<T, X>::update_parent_matrix(lp_settings & settings) {
for (unsigned i = m_index_start; i < m_parent->dimension(); i++)
update_existing_or_delete_in_parent_matrix_for_row(i, settings);
push_new_elements_to_parent_matrix(settings);
for (unsigned i = m_index_start; i < m_parent->dimension(); i++)
m_parent->set_max_in_row(m_parent->adjust_row(i));
}
template <typename T, typename X> void square_dense_submatrix<T, X>::update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings) {
bool diag_updated = false;
unsigned ai = m_parent->adjust_row(i);
auto & row_vals = m_parent->get_row_values(ai);
for (unsigned k = 0; k < row_vals.size(); k++) {
auto & iv = row_vals[k];
unsigned j = m_parent->adjust_column_inverse(iv.m_index);
if (j < i) {
m_parent->remove_element(row_vals, iv);
k--;
} else if (i == j) {
m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = one_of_type<T>());
diag_updated = true;
} else { // j > i
T & v = (*this)[i][j];
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
m_parent->remove_element(row_vals, iv);
k--;
} else {
m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = v);
v = zero_of_type<T>(); // only new elements are left above the diagonal
}
}
}
if (!diag_updated) {
unsigned aj = m_parent->adjust_column(i);
m_parent->add_new_element(ai, aj, one_of_type<T>());
}
}
template <typename T, typename X> void square_dense_submatrix<T, X>::push_new_elements_to_parent_matrix(lp_settings & settings) {
for (unsigned i = m_index_start; i < m_parent->dimension() - 1; i++) {
unsigned ai = m_parent->adjust_row(i);
for (unsigned j = i + 1; j < m_parent->dimension(); j++) {
T & v = (*this)[i][j];
if (!settings.abs_val_is_smaller_than_drop_tolerance(v)) {
unsigned aj = m_parent->adjust_column(j);
m_parent->add_new_element(ai, aj, v);
}
v = zero_of_type<T>(); // leave only L elements now
}
}
}
template <typename T, typename X>
template <typename L>
L square_dense_submatrix<T, X>::row_by_vector_product(unsigned i, const std::vector<L> & v) {
lean_assert(i >= m_index_start);
unsigned row_in_subm = i - m_index_start;
unsigned row_offset = row_in_subm * m_dim;
L r = zero_of_type<L>();
for (unsigned j = 0; j < m_dim; j++)
r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)];
return r;
}
template <typename T, typename X>
template <typename L>
L square_dense_submatrix<T, X>::column_by_vector_product(unsigned j, const std::vector<L> & v) {
lean_assert(j >= m_index_start);
unsigned offset = j - m_index_start;
L r = zero_of_type<L>();
for (unsigned i = 0; i < m_dim; i++, offset += m_dim)
r += m_v[offset] * v[adjust_row_inverse(m_index_start + i)];
return r;
}
template <typename T, typename X>
template <typename L>
L square_dense_submatrix<T, X>::row_by_indexed_vector_product(unsigned i, const indexed_vector<L> & v) {
lean_assert(i >= m_index_start);
unsigned row_in_subm = i - m_index_start;
unsigned row_offset = row_in_subm * m_dim;
L r = zero_of_type<L>();
for (unsigned j = 0; j < m_dim; j++)
r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)];
return r;
}
template <typename T, typename X>
template <typename L>
void square_dense_submatrix<T, X>::apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
#ifdef LEAN_DEBUG
// dense_matrix<T, X> deb(*this);
// vector<L> deb_w(w.m_data.size());
// for (unsigned i = 0; i < w.m_data.size(); i++)
// deb_w[i] = w[i];
// deb.apply_from_left(deb_w);
#endif // use indexed vector here
#ifndef DO_NOT_USE_INDEX
std::vector<L> t(m_parent->dimension(), zero_of_type<L>());
for (auto k : w.m_index) {
unsigned j = adjust_column(k); // k-th element will contribute only to column j
if (j < m_index_start) {
t[adjust_row_inverse(j)] = w[k];
} else {
const L & v = w[k];
for (unsigned i = 0; i < m_dim; i++) {
unsigned row = adjust_row_inverse(m_index_start + i);
unsigned offs = i * m_dim + j - m_index_start;
t[row] += m_v[offs] * v;
}
}
}
w.m_index.clear();
for (unsigned i = 0; i < m_parent->dimension(); i++) {
const L & v = t[i];
if (!settings.abs_val_is_smaller_than_drop_tolerance(v)){
w.m_index.push_back(i);
w.m_data[i] = v;
} else {
w.m_data[i] = zero_of_type<L>();
}
}
#else
std::vector<L> t(m_parent->dimension());
for (unsigned i = 0; i < m_index_start; i++) {
t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)];
}
for (unsigned i = m_index_start; i < m_parent->dimension(); i++){
t[adjust_row_inverse(i)] = row_by_indexed_vector_product(i, w);
}
for (unsigned i = 0; i < m_parent->dimension(); i++) {
w.set_value(t[i], i);
}
for (unsigned i = 0; i < m_parent->dimension(); i++) {
const L & v = t[i];
if (!is_zero(v))
w.m_index.push_back(i);
w.m_data[i] = v;
}
#endif
#ifdef LEAN_DEBUG
// cout << "w final" << endl;
// print_vector(w.m_data);
// lean_assert(vectors_are_equal<T>(deb_w, w.m_data));
// lean_assert(w.is_OK());
#endif
}
template <typename T, typename X>
template <typename L>
void square_dense_submatrix<T, X>::apply_from_left_to_vector(std::vector<L> & w) {
// lp_settings & settings) {
// dense_matrix<T, L> deb(*this);
// vector<L> deb_w(w);
// deb.apply_from_left_to_X(deb_w, settings);
// // cout << "deb" << endl;
// // print_matrix(deb);
// // cout << "w" << endl;
// // print_vector(w.m_data);
// // cout << "deb_w" << endl;
// // print_vector(deb_w);
std::vector<L> t(m_parent->dimension());
for (unsigned i = 0; i < m_index_start; i++) {
t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)];
}
for (unsigned i = m_index_start; i < m_parent->dimension(); i++){
t[adjust_row_inverse(i)] = row_by_vector_product(i, w);
}
for (unsigned i = 0; i < m_parent->dimension(); i++) {
w[i] = t[i];
}
#ifdef LEAN_DEBUG
// cout << "w final" << endl;
// print_vector(w.m_data);
// lean_assert(vectors_are_equal<L>(deb_w, w));
#endif
}
template <typename T, typename X> bool square_dense_submatrix<T, X>::is_L_matrix() const {
#ifdef LEAN_DEBUG
lean_assert(m_row_permutation.is_identity());
for (unsigned i = 0; i < m_parent->dimension(); i++) {
if (i < m_index_start) {
lean_assert(m_column_permutation[i] == i);
continue;
}
unsigned row_offs = (i-m_index_start)*m_dim;
for (unsigned k = 0; k < m_dim; k++) {
unsigned j = m_index_start + k;
unsigned jex = adjust_column_inverse(j);
if (jex > i) {
lean_assert(is_zero(m_v[row_offs + k]));
} else if (jex == i) {
lean_assert(!is_zero(m_v[row_offs + k]));
}
}
}
#endif
return true;
}
template <typename T, typename X> void square_dense_submatrix<T, X>::apply_from_right(std::vector<T> & w) {
#ifdef LEAN_DEBUG
// dense_matrix<T, X> deb(*this);
// vector<T> deb_w(w);
// deb.apply_from_right(deb_w);
#endif
std::vector<T> t(w.size());
for (unsigned j = 0; j < m_index_start; j++) {
t[adjust_column_inverse(j)] = w[adjust_row_inverse(j)];
}
for (unsigned j = m_index_start; j < m_parent->dimension(); j++) {
t[adjust_column_inverse(j)] = column_by_vector_product(j, w);
}
// std::copy(t.begin(), t.end(), w); // does not compile
lean_assert(w.size() == t.size());
w = t;
#ifdef LEAN_DEBUG
// lean_assert(vector_are_equal<T>(deb_w, w));
#endif
}
#ifdef LEAN_DEBUG
template <typename T, typename X> T square_dense_submatrix<T, X>::get_elem (unsigned i, unsigned j) const {
i = adjust_row(i);
j = adjust_column(j);
if (i < m_index_start || j < m_index_start)
return i == j? one_of_type<T>() : zero_of_type<T>();
unsigned offs = (i - m_index_start)* m_dim + j - m_index_start;
return m_v[offs];
}
#endif
template <typename T, typename X> void square_dense_submatrix<T, X>::conjugate_by_permutation(permutation_matrix<T, X> & q) {
m_row_permutation.multiply_by_permutation_from_left(q);
m_column_permutation.multiply_by_reverse_from_right(q);
}
}

View file

@ -22,6 +22,7 @@
#include "util/lp/lp_settings.h"
#include "util/lp/eta_matrix.h"
#include "util/lp/binary_heap_upair_queue.h"
#include "util/lp/sparse_matrix.h"
namespace lean {
template <typename T, typename X>
class square_dense_submatrix : public tail_matrix<T, X> {
@ -52,39 +53,9 @@ public:
square_dense_submatrix() {}
square_dense_submatrix (sparse_matrix<T, X> *parent_matrix, unsigned index_start) :
m_index_start(index_start),
m_dim(parent_matrix->dimension() - index_start),
m_v(m_dim * m_dim),
m_parent(parent_matrix),
m_row_permutation(m_parent->dimension()),
m_column_permutation(m_parent->dimension()) {
int row_offset = - static_cast<int>(m_index_start);
for (unsigned i = index_start; i < parent_matrix->dimension(); i++) {
unsigned row = parent_matrix->adjust_row(i);
for (auto & iv : parent_matrix->get_row_values(row)) {
unsigned j = parent_matrix->adjust_column_inverse(iv.m_index);
lean_assert(j>= m_index_start);
m_v[row_offset + j] = iv.m_value;
}
row_offset += m_dim;
}
}
square_dense_submatrix (sparse_matrix<T, X> *parent_matrix, unsigned index_start);
void init(sparse_matrix<T, X> *parent_matrix, unsigned index_start) {
m_index_start = index_start;
m_dim = parent_matrix->dimension() - index_start;
m_v.resize(m_dim * m_dim);
m_parent = parent_matrix;
m_column_permutation.init(m_parent->dimension());
for (unsigned i = index_start; i < parent_matrix->dimension(); i++) {
unsigned row = parent_matrix->adjust_row(i);
for (auto & iv : parent_matrix->get_row_values(row)) {
unsigned j = parent_matrix->adjust_column_inverse(iv.m_index);
(*this)[i][j] = iv.m_value;
}
}
}
void init(sparse_matrix<T, X> *parent_matrix, unsigned index_start);
ref operator[] (unsigned i) {
lean_assert(i >= m_index_start);
@ -92,26 +63,11 @@ public:
return ref(i, *this);
}
int find_pivot_column_in_row(unsigned i) const {
int j = -1;
T max = zero_of_type<T>();
lean_assert(i >= m_index_start);
unsigned row_start = (i - m_index_start) * m_dim;
for (unsigned k = i; k < m_parent->dimension(); k++) {
unsigned col = adjust_column(k); // this is where the column is in the row
unsigned offs = row_start + col - m_index_start;
T t = abs(m_v[offs]);
if (t > max) {
j = k;
max = t;
}
}
return j;
}
int find_pivot_column_in_row(unsigned i) const;
void swap_columns(unsigned i, unsigned j) {
if (i == j) return;
m_column_permutation.transpose_from_left(i, j);
if (i != j)
m_column_permutation.transpose_from_left(i, j);
}
unsigned adjust_column(unsigned col) const{
@ -129,250 +85,34 @@ public:
return m_row_permutation.apply_reverse(row);
}
void pivot(unsigned i, lp_settings & settings) {
divide_row_by_pivot(i);
for (unsigned k = i + 1; k < m_parent->dimension(); k++)
pivot_row_to_row(i, k, settings);
}
void pivot(unsigned i, lp_settings & settings);
void pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings) {
lean_assert(i < row);
unsigned pj = adjust_column(i); // the pivot column
unsigned pjd = pj - m_index_start;
unsigned pivot_row_offset = (i-m_index_start)*m_dim;
T pivot = m_v[pivot_row_offset + pjd];
unsigned row_offset= (row-m_index_start)*m_dim;
T m = m_v[row_offset + pjd];
lean_assert(!is_zero(pivot));
m_v[row_offset + pjd] = -m * pivot; // creating L matrix
for (unsigned j = m_index_start; j < m_parent->dimension(); j++) {
if (j == pj) {
pivot_row_offset++;
row_offset++;
continue;
}
auto t = m_v[row_offset] - m_v[pivot_row_offset] * m;
if (settings.abs_val_is_smaller_than_drop_tolerance(t)) {
m_v[row_offset] = zero_of_type<T>();
} else {
m_v[row_offset] = t;
}
row_offset++; pivot_row_offset++;
// at the same time we pivot the L too
}
}
void pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings);;
void divide_row_by_pivot(unsigned i) {
unsigned pj = adjust_column(i); // the pivot column
unsigned irow_offset = (i - m_index_start) * m_dim;
T pivot = m_v[irow_offset + pj - m_index_start];
lean_assert(!is_zero(pivot));
for (unsigned k = m_index_start; k < m_parent->dimension(); k++) {
if (k == pj){
m_v[irow_offset++] = one_of_type<T>() / pivot; // creating the L matrix diagonal
continue;
}
m_v[irow_offset++] /= pivot;
}
}
void divide_row_by_pivot(unsigned i);
void update_parent_matrix(lp_settings & settings) {
for (unsigned i = m_index_start; i < m_parent->dimension(); i++)
update_existing_or_delete_in_parent_matrix_for_row(i, settings);
push_new_elements_to_parent_matrix(settings);
for (unsigned i = m_index_start; i < m_parent->dimension(); i++)
m_parent->set_max_in_row(m_parent->adjust_row(i));
}
void update_parent_matrix(lp_settings & settings);
void update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings) {
bool diag_updated = false;
unsigned ai = m_parent->adjust_row(i);
auto & row_vals = m_parent->get_row_values(ai);
for (unsigned k = 0; k < row_vals.size(); k++) {
auto & iv = row_vals[k];
unsigned j = m_parent->adjust_column_inverse(iv.m_index);
if (j < i) {
m_parent->remove_element(row_vals, iv);
k--;
} else if (i == j) {
m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = one_of_type<T>());
diag_updated = true;
} else { // j > i
T & v = (*this)[i][j];
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
m_parent->remove_element(row_vals, iv);
k--;
} else {
m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = v);
v = zero_of_type<T>(); // only new elements are left above the diagonal
}
}
}
if (!diag_updated) {
unsigned aj = m_parent->adjust_column(i);
m_parent->add_new_element(ai, aj, one_of_type<T>());
}
}
void update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings);
void push_new_elements_to_parent_matrix(lp_settings & settings) {
for (unsigned i = m_index_start; i < m_parent->dimension() - 1; i++) {
unsigned ai = m_parent->adjust_row(i);
for (unsigned j = i + 1; j < m_parent->dimension(); j++) {
T & v = (*this)[i][j];
if (!settings.abs_val_is_smaller_than_drop_tolerance(v)) {
unsigned aj = m_parent->adjust_column(j);
m_parent->add_new_element(ai, aj, v);
}
v = zero_of_type<T>(); // leave only L elements now
}
}
}
void push_new_elements_to_parent_matrix(lp_settings & settings);
template <typename L>
L row_by_vector_product(unsigned i, const std::vector<L> & v) {
lean_assert(i >= m_index_start);
unsigned row_in_subm = i - m_index_start;
unsigned row_offset = row_in_subm * m_dim;
L r = zero_of_type<L>();
for (unsigned j = 0; j < m_dim; j++)
r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)];
return r;
}
L row_by_vector_product(unsigned i, const std::vector<L> & v);
template <typename L>
L column_by_vector_product(unsigned j, const std::vector<L> & v) {
lean_assert(j >= m_index_start);
unsigned offset = j - m_index_start;
L r = zero_of_type<L>();
for (unsigned i = 0; i < m_dim; i++, offset += m_dim)
r += m_v[offset] * v[adjust_row_inverse(m_index_start + i)];
return r;
}
L column_by_vector_product(unsigned j, const std::vector<L> & v);
template <typename L>
L row_by_indexed_vector_product(unsigned i, const indexed_vector<L> & v) {
lean_assert(i >= m_index_start);
unsigned row_in_subm = i - m_index_start;
unsigned row_offset = row_in_subm * m_dim;
L r = zero_of_type<L>();
for (unsigned j = 0; j < m_dim; j++)
r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)];
return r;
}
L row_by_indexed_vector_product(unsigned i, const indexed_vector<L> & v);
template <typename L>
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
#ifdef LEAN_DEBUG
// dense_matrix<T, X> deb(*this);
// vector<L> deb_w(w.m_data.size());
// for (unsigned i = 0; i < w.m_data.size(); i++)
// deb_w[i] = w[i];
// deb.apply_from_left(deb_w);
#endif // use indexed vector here
#ifndef DO_NOT_USE_INDEX
std::vector<L> t(m_parent->dimension(), zero_of_type<L>());
for (auto k : w.m_index) {
unsigned j = adjust_column(k); // k-th element will contribute only to column j
if (j < m_index_start) {
t[adjust_row_inverse(j)] = w[k];
} else {
const L & v = w[k];
for (unsigned i = 0; i < m_dim; i++) {
unsigned row = adjust_row_inverse(m_index_start + i);
unsigned offs = i * m_dim + j - m_index_start;
t[row] += m_v[offs] * v;
}
}
}
w.m_index.clear();
for (unsigned i = 0; i < m_parent->dimension(); i++) {
const L & v = t[i];
if (!settings.abs_val_is_smaller_than_drop_tolerance(v)){
w.m_index.push_back(i);
w.m_data[i] = v;
} else {
w.m_data[i] = zero_of_type<L>();
}
}
#else
std::vector<L> t(m_parent->dimension());
for (unsigned i = 0; i < m_index_start; i++) {
t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)];
}
for (unsigned i = m_index_start; i < m_parent->dimension(); i++){
t[adjust_row_inverse(i)] = row_by_indexed_vector_product(i, w);
}
for (unsigned i = 0; i < m_parent->dimension(); i++) {
w.set_value(t[i], i);
}
for (unsigned i = 0; i < m_parent->dimension(); i++) {
const L & v = t[i];
if (!is_zero(v))
w.m_index.push_back(i);
w.m_data[i] = v;
}
#endif
#ifdef LEAN_DEBUG
// cout << "w final" << endl;
// print_vector(w.m_data);
// lean_assert(vectors_are_equal<T>(deb_w, w.m_data));
// lean_assert(w.is_OK());
#endif
}
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings);
template <typename L>
void apply_from_left_to_vector(std::vector<L> & w) {
// lp_settings & settings) {
// dense_matrix<T, L> deb(*this);
// vector<L> deb_w(w);
// deb.apply_from_left_to_X(deb_w, settings);
// // cout << "deb" << endl;
// // print_matrix(deb);
// // cout << "w" << endl;
// // print_vector(w.m_data);
// // cout << "deb_w" << endl;
// // print_vector(deb_w);
std::vector<L> t(m_parent->dimension());
for (unsigned i = 0; i < m_index_start; i++) {
t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)];
}
for (unsigned i = m_index_start; i < m_parent->dimension(); i++){
t[adjust_row_inverse(i)] = row_by_vector_product(i, w);
}
for (unsigned i = 0; i < m_parent->dimension(); i++) {
w[i] = t[i];
}
#ifdef LEAN_DEBUG
// cout << "w final" << endl;
// print_vector(w.m_data);
// lean_assert(vectors_are_equal<L>(deb_w, w));
#endif
}
void apply_from_left_to_vector(std::vector<L> & w);
bool is_L_matrix() const {
lean_assert(m_row_permutation.is_identity());
for (unsigned i = 0; i < m_parent->dimension(); i++) {
if (i < m_index_start) {
lean_assert(m_column_permutation[i] == i);
continue;
}
unsigned row_offs = (i-m_index_start)*m_dim;
for (unsigned k = 0; k < m_dim; k++) {
unsigned j = m_index_start + k;
unsigned jex = adjust_column_inverse(j);
if (jex > i) {
lean_assert(is_zero(m_v[row_offs + k]));
} else if (jex == i) {
lean_assert(!is_zero(m_v[row_offs + k]));
}
}
}
return true;
}
bool is_L_matrix() const;
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
apply_from_left_local(w, settings);
@ -385,48 +125,15 @@ public:
apply_from_left_to_vector(w);// , settings);
}
void apply_from_right(std::vector<T> & w) {
#ifdef LEAN_DEBUG
// dense_matrix<T, X> deb(*this);
// vector<T> deb_w(w);
// deb.apply_from_right(deb_w);
#endif
std::vector<T> t(w.size());
for (unsigned j = 0; j < m_index_start; j++) {
t[adjust_column_inverse(j)] = w[adjust_row_inverse(j)];
}
for (unsigned j = m_index_start; j < m_parent->dimension(); j++) {
t[adjust_column_inverse(j)] = column_by_vector_product(j, w);
}
// std::copy(t.begin(), t.end(), w); // does not compile
lean_assert(w.size() == t.size());
w = t;
#ifdef LEAN_DEBUG
// lean_assert(vector_are_equal<T>(deb_w, w));
#endif
}
void apply_from_right(std::vector<T> & w);
#ifdef LEAN_DEBUG
T get_elem (unsigned i, unsigned j) const {
i = adjust_row(i);
j = adjust_column(j);
if (i < m_index_start || j < m_index_start)
return i == j? one_of_type<T>() : zero_of_type<T>();
unsigned offs = (i - m_index_start)* m_dim + j - m_index_start;
return m_v[offs];
}
T get_elem (unsigned i, unsigned j) const;
unsigned row_count() const { return m_parent->row_count();}
unsigned column_count() const { return row_count();}
void set_number_of_rows(unsigned /* m */) {}
void set_number_of_columns(unsigned /* n */) {};
void set_number_of_rows(unsigned) {}
void set_number_of_columns(unsigned) {};
#endif
void conjugate_by_permutation(permutation_matrix<T, X> & q) {
m_row_permutation.multiply_by_permutation_from_left(q);
m_column_permutation.multiply_by_reverse_from_right(q);
}
void conjugate_by_permutation(permutation_matrix<T, X> & q);
};
}

View file

@ -0,0 +1,32 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/square_dense_submatrix.cpp"
template void lean::square_dense_submatrix<double, double>::init(lean::sparse_matrix<double, double>*, unsigned int);
template lean::square_dense_submatrix<double, double>::square_dense_submatrix(lean::sparse_matrix<double, double>*, unsigned int);
template void lean::square_dense_submatrix<double, double>::update_parent_matrix(lean::lp_settings&);
template bool lean::square_dense_submatrix<double, double>::is_L_matrix() const;
template void lean::square_dense_submatrix<double, double>::conjugate_by_permutation(lean::permutation_matrix<double, double>&);
template int lean::square_dense_submatrix<double, double>::find_pivot_column_in_row(unsigned int) const;
template void lean::square_dense_submatrix<double, double>::pivot(unsigned int, lean::lp_settings&);
template lean::square_dense_submatrix<lean::mpq, lean::numeric_pair<lean::mpq> >::square_dense_submatrix(lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >*, unsigned int);
template void lean::square_dense_submatrix<lean::mpq, lean::numeric_pair<lean::mpq> >::update_parent_matrix(lean::lp_settings&);
template bool lean::square_dense_submatrix<lean::mpq, lean::numeric_pair<lean::mpq> >::is_L_matrix() const;
template void lean::square_dense_submatrix<lean::mpq, lean::numeric_pair<lean::mpq> >::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
template int lean::square_dense_submatrix<lean::mpq, lean::numeric_pair<lean::mpq> >::find_pivot_column_in_row(unsigned int) const;
template void lean::square_dense_submatrix<lean::mpq, lean::numeric_pair<lean::mpq> >::pivot(unsigned int, lean::lp_settings&);
#ifdef LEAN_DEBUG
template double lean::square_dense_submatrix<double, double>::get_elem(unsigned int, unsigned int) const;
#endif
template void lean::square_dense_submatrix<double, double>::apply_from_right(std::vector<double, std::allocator<double> >&);
template void lean::square_dense_submatrix<double, double>::apply_from_left_local<double>(lean::indexed_vector<double>&, lean::lp_settings&);
template void lean::square_dense_submatrix<double, double>::apply_from_left_to_vector<double>(std::vector<double, std::allocator<double> >&);
template lean::square_dense_submatrix<lean::mpq, lean::mpq>::square_dense_submatrix(lean::sparse_matrix<lean::mpq, lean::mpq>*, unsigned int);
template void lean::square_dense_submatrix<lean::mpq, lean::mpq>::update_parent_matrix(lean::lp_settings&);
template bool lean::square_dense_submatrix<lean::mpq, lean::mpq>::is_L_matrix() const;
template void lean::square_dense_submatrix<lean::mpq, lean::mpq>::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::mpq>&);
template int lean::square_dense_submatrix<lean::mpq, lean::mpq>::find_pivot_column_in_row(unsigned int) const;
template void lean::square_dense_submatrix<lean::mpq, lean::mpq>::pivot(unsigned int, lean::lp_settings&);

View file

@ -0,0 +1,376 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/static_matrix.h"
namespace lean {
// each assignment for this matrix should be issued only once!!!
template <typename T, typename X>
void static_matrix<T, X>::init_row_columns(unsigned m, unsigned n) {
lean_assert(m_rows.size() == 0 && m_columns.size() == 0);
for (unsigned i = 0; i < m; i++){
m_rows.push_back(row_strip());
}
for (unsigned j = 0; j < n; j++){
m_columns.push_back(column_strip());
}
}
// constructor that copies columns of the basis from A
template <typename T, typename X>
static_matrix<T, X>::static_matrix(static_matrix const &A, unsigned * basis) :
m_work_pivot_vector(A.row_count(), numeric_traits<T>::zero()) {
unsigned m = A.row_count();
init_row_columns(m, m);
while (m--) {
for (auto & col : A.m_columns[m]){
set(col.m_i, m, A.get_value_of_column_cell(col));
}
}
}
template <typename T, typename X> void static_matrix<T, X>:: clear() {
m_work_pivot_vector.clear();
m_rows.clear();
m_columns.clear();
#ifdef LEAN_DEBUG
m_domain.clear();
#endif
}
template <typename T, typename X> void static_matrix<T, X>:: init_work_pivot_vector(unsigned m) {
while (m--) m_work_pivot_vector.push_back(numeric_traits<T>::zero());
}
template <typename T, typename X> void static_matrix<T, X>:: init_empty_matrix(unsigned m, unsigned n) {
init_work_pivot_vector(m);
init_row_columns(m, n);
}
template <typename T, typename X>
template <typename L>
L static_matrix<T, X>::dot_product_with_row(unsigned row, const std::vector<L> & w) {
L ret = zero_of_type<L>();
lean_assert(row < m_rows.size());
for (auto & it : m_rows[row]) {
ret += w[it.m_j] * it.get_val();
}
return ret;
};
template <typename T, typename X> unsigned static_matrix<T, X>:: lowest_row_in_column(unsigned col) {
lean_assert(col < column_count());
column_strip & colstrip = m_columns[col];
lean_assert(colstrip.size() > 0);
unsigned ret = 0;
for (auto & t : colstrip) {
if (t.m_i > ret) {
ret = t.m_i;
}
}
return ret;
}
template <typename T, typename X> T static_matrix<T, X>:: dot_product_with_column(const std::vector<T> & y, unsigned j) const {
lean_assert(j < column_count());
T ret = numeric_traits<T>::zero();
for (auto & it : m_columns[j]) {
ret += y[it.m_i] * it.m_value; // get_value_of_column_cell(it);
}
return ret;
}
template <typename T, typename X> void static_matrix<T, X>:: add_columns_at_the_end(unsigned delta) {
for (unsigned i = 0; i < delta; i++)
add_column();
}
template <typename T, typename X> void static_matrix<T, X>:: forget_last_columns(unsigned how_many_to_forget) {
lean_assert(m_columns.size() >= how_many_to_forget);
unsigned j = column_count() - 1;
for (; how_many_to_forget > 0; how_many_to_forget--) {
remove_last_column(j --);
}
}
template <typename T, typename X> void static_matrix<T, X>:: remove_last_column(unsigned j) {
column_strip & col = m_columns.back();
for (auto & it : col) {
auto & row = m_rows[it.m_i];
unsigned offset = row.size() - 1;
for (auto row_it = row.rbegin(); row_it != row.rend(); row_it ++) {
if (row_it->m_j == j) {
row.erase(row.begin() + offset);
break;
}
offset--;
}
}
m_columns.pop_back();
}
template <typename T, typename X>
void static_matrix<T, X>:: scale_row(unsigned row, T const & alpha) {
for (auto & t : m_rows[row]) {
t.set_val(t.get_val() * alpha);
m_columns[t.m_j][t.m_offset].m_value *= alpha;
}
}
template <typename T, typename X>
void static_matrix<T, X>:: divide_row_by_constant(unsigned row, T const & alpha) {
for (auto & t : m_rows[row]) {
t.set_val(t.get_val() / alpha);
m_columns[t.m_j][t.m_offset].m_value /= alpha;
}
}
template <typename T, typename X>
void static_matrix<T, X>:: scale_column(unsigned column, T const & alpha) {
for (auto & t : m_columns[column]) {
t.m_value *= alpha;
auto & r = m_rows[t.m_i][t.m_offset];
r.set_val(r.get_val() *= alpha);
}
}
#ifdef LEAN_DEBUG
template <typename T, typename X> void static_matrix<T, X>:: regen_domain() {
m_domain.clear();
for (int i = 0; i < m_rows.size(); i++){
for (auto & t : m_rows[i]) {
m_domain.insert(std::make_pair(i, t.m_j));
}
}
}
#endif
template <typename T, typename X> void static_matrix<T, X>:: set(unsigned row, unsigned col, T const & val) {
if (numeric_traits<T>::is_zero(val)) return;
lean_assert(row < row_count() && col < column_count());
#ifdef LEAN_DEBUG
pair<unsigned, unsigned> p(row, col);
lean_assert(m_domain.find(p) == m_domain.end());
m_domain.insert(p);
#endif
auto & r = m_rows[row];
unsigned offs_in_cols = m_columns[col].size();
m_columns[col].push_back(make_column_cell(row, r.size(), val));
r.push_back(make_row_cell(col, offs_in_cols, val));
}
template <typename T, typename X>
std::set<pair<unsigned, unsigned>> static_matrix<T, X>::get_domain() {
std::set<pair<unsigned, unsigned>> ret;
for (unsigned i = 0; i < m_rows.size(); i++) {
for (auto it : m_rows[i]) {
ret.insert(std::make_pair(i, it.m_j));
}
}
return ret;
}
template <typename T, typename X> void static_matrix<T, X>:: copy_column_to_vector (unsigned j, indexed_vector<T> & v) const {
lean_assert(j < m_columns.size());
for (auto & it : m_columns[j]) {
if (!is_zero(it.m_value))
v.set_value(it.m_value, it.m_i);
}
}
template <typename T, typename X> void static_matrix<T, X>:: copy_column_to_vector (unsigned j, std::vector<T> & v) const {
v.resize(row_count(), numeric_traits<T>::zero());
for (auto & it : m_columns[j]) {
if (!is_zero(it.m_value))
v[it.m_i]=it.m_value;
}
}
template <typename T, typename X> void static_matrix<T, X>:: add_column_to_vector (const T & a, unsigned j, T * v) const {
for (auto & it : m_columns[j]) {
v[it.m_i] += a * it.m_value;
}
}
template <typename T, typename X> T static_matrix<T, X>:: get_max_abs_in_row(unsigned row) const {
T ret = numeric_traits<T>::zero();
for (auto & t : m_rows[row]) {
T a = abs(t.get_val());
if (a > ret) {
ret = a;
}
}
return ret;
}
template <typename T, typename X> T static_matrix<T, X>:: get_min_abs_in_row(unsigned row) const {
bool first_time = true;
T ret = numeric_traits<T>::zero();
for (auto & t : m_rows[row]) {
T a = abs(t.get_val());
if (first_time) {
ret = a;
first_time = false;
} else if (a < ret) {
ret = a;
}
}
return ret;
}
template <typename T, typename X> T static_matrix<T, X>:: get_max_abs_in_column(unsigned column) const {
T ret = numeric_traits<T>::zero();
for (auto & t : m_columns[column]) {
T a = abs(t.m_value);
if (a > ret) {
ret = a;
}
}
return ret;
}
template <typename T, typename X> T static_matrix<T, X>:: get_min_abs_in_column(unsigned column) const {
bool first_time = true;
T ret = numeric_traits<T>::zero();
for (auto & t : m_columns[column]) {
T a = abs(t.m_value);
if (first_time) {
first_time = false;
ret = a;
} else if (a < ret) {
ret = a;
}
}
return ret;
}
#ifdef LEAN_DEBUG
template <typename T, typename X> void static_matrix<T, X>:: check_consistency() {
std::unordered_map<std::pair<unsigned, unsigned>, T> by_rows;
for (int i = 0; i < m_rows.size(); i++){
for (auto & t : m_rows[i]) {
pair<unsigned, unsigned> p(i, t.m_j);
lean_assert(by_rows.find(p) == by_rows.end());
by_rows[p] = t.get_val();
}
}
std::unordered_map<pair<unsigned, unsigned>, T> by_cols;
for (int i = 0; i < m_columns.size(); i++){
for (auto & t : m_columns[i]) {
pair<unsigned, unsigned> p(t.m_i, i);
lean_assert(by_cols.find(p) == by_cols.end());
by_cols[p] = get_value_of_column_cell(t);
}
}
lean_assert(by_rows.size() == by_cols.size());
for (auto & t : by_rows) {
auto ic = by_cols.find(t.first);
if (ic == by_cols.end()){
std::cout << "rows have pair (" << t.first.first <<"," << t.first.second
<< "), but columns don't " << std::endl;
}
lean_assert(ic != by_cols.end());
lean_assert(t.second == ic->second);
}
}
#endif
template <typename T, typename X> void static_matrix<T, X>:: cross_out_row(unsigned k) {
#ifdef LEAN_DEBUG
check_consistency();
#endif
cross_out_row_from_columns(k, m_rows[k]);
fix_row_indices_in_each_column_for_crossed_row(k);
m_rows.erase(m_rows.begin() + k);
#ifdef LEAN_DEBUG
regen_domain();
check_consistency();
#endif
}
template <typename T, typename X> void static_matrix<T, X>:: fix_row_indices_in_each_column_for_crossed_row(unsigned k) {
for (unsigned j = 0; j < m_columns.size(); j++) {
auto & col = m_columns[j];
for (int i = 0; i < col.size(); i++) {
if (col[i].m_i > k) {
col[i].m_i--;
}
}
}
}
template <typename T, typename X> void static_matrix<T, X>:: cross_out_row_from_columns(unsigned k, row_strip & row) {
for (auto & t : row) {
cross_out_row_from_column(t.m_j, k);
}
}
template <typename T, typename X> void static_matrix<T, X>:: cross_out_row_from_column(unsigned col, unsigned k) {
auto & s = m_columns[col];
for (unsigned i = 0; i < s.size(); i++) {
if (s[i].m_i == k) {
s.erase(s.begin() + i);
break;
}
}
}
template <typename T, typename X> T static_matrix<T, X>:: get_elem(unsigned i, unsigned j) const { // should not be used in efficient code !!!!
for (auto & t : m_rows[i]) {
if (t.m_j == j) {
return t.get_val();
}
}
return numeric_traits<T>::zero();
}
template <typename T, typename X> void static_matrix<T, X>:: scan_row_to_work_vector(unsigned i) {
for (auto & rc : m_rows[i]) {
m_work_pivot_vector[rc.m_j] = rc.get_val();
}
}
template <typename T, typename X> void static_matrix<T, X>:: clean_row_work_vector(unsigned i) {
for (auto & rc : m_rows[i]) {
m_work_pivot_vector[rc.m_j] = numeric_traits<T>::zero();
}
}
template <typename T, typename X> T static_matrix<T, X>:: get_balance() const {
T ret = zero_of_type<T>();
for (unsigned i = 0; i < row_count(); i++) {
ret += get_row_balance(i);
}
return ret;
}
template <typename T, typename X> T static_matrix<T, X>:: get_row_balance(unsigned row) const {
T ret = zero_of_type<T>();
for (auto & t : m_rows[row]) {
if (numeric_traits<T>::is_zero(t.get_val())) continue;
T a = abs(t.get_val());
numeric_traits<T>::log(a);
ret += a * a;
}
return ret;
}
#ifdef LEAN_DEBUG
template <typename T, typename X> bool static_matrix<T, X>:: col_val_equal_to_row_val() const {
for (auto & r : m_rows) {
for (auto & rc : r) {
lean_assert(rc.get_val() == m_columns[rc.m_j][rc.m_offset].m_value);
}
}
return true;
}
#endif
}

View file

@ -19,9 +19,8 @@
#include "util/numerics/float.h"
#include "util/numerics/mpfp.h"
#include "util/lp/sparse_vector.h"
#include "util/lp/matrix_domain.h"
#include "util/lp/indexed_vector.h"
#include "util/lp/permutation_matrix.h"
namespace lean {
template <typename T>
struct column_cell {
@ -41,9 +40,7 @@ public:
}
const T & get_val() const { return m_value;}
T & get_val() { return m_value;}
void set_val(T v) {
m_value = v;
}
void set_val(T v) { m_value = v; }
private:
T m_value;
};
@ -88,15 +85,7 @@ public:
};
public:
void init_row_columns(unsigned m, unsigned n) {
lean_assert(m_rows.size() == 0 && m_columns.size() == 0);
for (unsigned i = 0; i < m; i++){
m_rows.push_back(row_strip());
}
for (unsigned j = 0; j < n; j++){
m_columns.push_back(column_strip());
}
}
void init_row_columns(unsigned m, unsigned n);
// constructor with no parameters
static_matrix() {}
@ -106,351 +95,93 @@ public:
init_row_columns(m, n);
}
// constructor that copies columns of the basis from A
static_matrix(static_matrix const &A, unsigned * basis) :
m_work_pivot_vector(A.row_count(), numeric_traits<T>::zero()) {
unsigned m = A.row_count();
init_row_columns(m, m);
while (m--) {
for (auto & col : A.m_columns[m]){
set(col.m_i, m, A.get_value_of_column_cell(col));
}
}
}
static_matrix(static_matrix const &A, unsigned * basis);
void clear() {
m_work_pivot_vector.clear();
m_rows.clear();
m_columns.clear();
#ifdef LEAN_DEBUG
m_domain.clear();
#endif
}
void clear();
void init_work_pivot_vector(unsigned m) {
while (m--) m_work_pivot_vector.push_back(numeric_traits<T>::zero());
}
void init_work_pivot_vector(unsigned m);
void init_empty_matrix(unsigned m, unsigned n) {
init_work_pivot_vector(m);
init_row_columns(m, n);
}
void init_empty_matrix(unsigned m, unsigned n);
unsigned row_count() const { return m_rows.size(); }
unsigned column_count() const { return m_columns.size(); }
template <typename L>
L dot_product_with_row(unsigned row, const std::vector<L> & w) {
L ret = zero_of_type<L>();
lean_assert(row < m_rows.size());
for (auto & it : m_rows[row]) {
ret += w[it.m_j] * it.get_val();
}
return ret;
};
L dot_product_with_row(unsigned row, const std::vector<L> & w);;
unsigned lowest_row_in_column(unsigned col) {
lean_assert(col < column_count());
column_strip & colstrip = m_columns[col];
lean_assert(colstrip.size() > 0);
unsigned ret = 0;
for (auto & t : colstrip) {
if (t.m_i > ret) {
ret = t.m_i;
}
}
return ret;
}
unsigned lowest_row_in_column(unsigned col);
T dot_product_with_column(const std::vector<T> & y, unsigned j) const {
lean_assert(j < column_count());
T ret = numeric_traits<T>::zero();
for (auto & it : m_columns[j]) {
ret += y[it.m_i] * it.m_value; // get_value_of_column_cell(it);
}
return ret;
}
T dot_product_with_column(const std::vector<T> & y, unsigned j) const;
void add_columns_at_the_end(unsigned delta) {
for (unsigned i = 0; i < delta; i++)
add_column();
}
void add_columns_at_the_end(unsigned delta);
void add_column() {
m_columns.push_back(column_strip());
}
void add_column() {m_columns.push_back(column_strip()); }
void forget_last_columns(unsigned how_many_to_forget) {
lean_assert(m_columns.size() >= how_many_to_forget);
unsigned j = column_count() - 1;
for (; how_many_to_forget > 0; how_many_to_forget--) {
remove_last_column(j --);
}
}
void forget_last_columns(unsigned how_many_to_forget);
void remove_last_column(unsigned j) {
column_strip & col = m_columns.back();
for (auto & it : col) {
auto & row = m_rows[it.m_i];
unsigned offset = row.size() - 1;
for (auto row_it = row.rbegin(); row_it != row.rend(); row_it ++) {
if (row_it->m_j == j) {
row.erase(row.begin() + offset);
break;
}
offset--;
}
}
m_columns.pop_back();
}
void remove_last_column(unsigned j);
void scale_row(unsigned row, T const & alpha) {
for (auto & t : m_rows[row]) {
t.set_val(t.get_val() * alpha);
m_columns[t.m_j][t.m_offset].m_value *= alpha;
}
}
void scale_row(unsigned row, T const & alpha);
void divide_row_by_constant(unsigned row, T const & alpha) {
for (auto & t : m_rows[row]) {
t.set_val(t.get_val() / alpha);
m_columns[t.m_j][t.m_offset].m_value /= alpha;
}
}
void divide_row_by_constant(unsigned row, T const & alpha);
void scale_column(unsigned column, T const & alpha) {
for (auto & t : m_columns[column]) {
t.m_value *= alpha;
auto & r = m_rows[t.m_i][t.m_offset];
r.set_val(r.get_val() *= alpha);
}
}
void scale_column(unsigned column, T const & alpha);
#ifdef LEAN_DEBUG
void regen_domain() {
m_domain.clear();
for (int i = 0; i < m_rows.size(); i++){
for (auto & t : m_rows[i]) {
m_domain.insert(std::make_pair(i, t.m_j));
}
}
}
void regen_domain();
#endif
// offs - offset in columns
row_cell<T> make_row_cell(unsigned row, unsigned offs, T const & val) {
row_cell<T> r(row, offs, val);
return r;
return row_cell<T>(row, offs, val);
}
column_cell<T> make_column_cell(unsigned column, unsigned offset, T const & val) {
column_cell<T> r(column, offset, val);
return r;
return column_cell<T>(column, offset, val);
}
void set(unsigned row, unsigned col, T const & val) {
if (numeric_traits<T>::is_zero(val)) return;
lean_assert(row < row_count() && col < column_count());
#ifdef LEAN_DEBUG
pair<unsigned, unsigned> p(row, col);
lean_assert(m_domain.find(p) == m_domain.end());
m_domain.insert(p);
#endif
auto & r = m_rows[row];
unsigned offs_in_cols = m_columns[col].size();
m_columns[col].push_back(make_column_cell(row, r.size(), val));
r.push_back(make_row_cell(col, offs_in_cols, val));
}
void set(unsigned row, unsigned col, T const & val);
ref operator()(unsigned row, unsigned col) { return ref(*this, row, col); }
std::set<pair<unsigned, unsigned>> get_domain() {
std::set<pair<unsigned, unsigned>> ret;
for (unsigned i = 0; i < m_rows.size(); i++) {
for (auto it : m_rows[i]) {
ret.insert(std::make_pair(i, it.m_j));
}
}
return ret;
}
std::set<pair<unsigned, unsigned>> get_domain();
void copy_column_to_vector (unsigned j, indexed_vector<T> & v) const;
void copy_column_to_vector (unsigned j, std::vector<T> & v) const;
void add_column_to_vector (const T & a, unsigned j, T * v) const;
T get_max_abs_in_row(unsigned row) const;
void copy_column_to_vector (unsigned j, indexed_vector<T> & v) const {
lean_assert(j < m_columns.size());
for (auto & it : m_columns[j]) {
if (!is_zero(it.m_value))
v.set_value(it.m_value, it.m_i);
}
}
void copy_column_to_vector (unsigned j, std::vector<T> & v) const {
v.resize(row_count(), numeric_traits<T>::zero());
for (auto & it : m_columns[j]) {
if (!is_zero(it.m_value))
v[it.m_i]=it.m_value;
}
}
T get_min_abs_in_row(unsigned row) const;
T get_max_abs_in_column(unsigned column) const;
void add_column_to_vector (const T & a, unsigned j, T * v) const {
for (auto & it : m_columns[j]) {
v[it.m_i] += a * it.m_value;
}
}
T get_max_abs_in_row(unsigned row) const {
T ret = numeric_traits<T>::zero();
for (auto & t : m_rows[row]) {
T a = abs(t.get_val());
if (a > ret) {
ret = a;
}
}
return ret;
}
T get_min_abs_in_row(unsigned row) const {
bool first_time = true;
T ret = numeric_traits<T>::zero();
for (auto & t : m_rows[row]) {
T a = abs(t.get_val());
if (first_time) {
ret = a;
first_time = false;
} else if (a < ret) {
ret = a;
}
}
return ret;
}
T get_max_abs_in_column(unsigned column) const {
T ret = numeric_traits<T>::zero();
for (auto & t : m_columns[column]) {
T a = abs(t.m_value);
if (a > ret) {
ret = a;
}
}
return ret;
}
T get_min_abs_in_column(unsigned column) const {
bool first_time = true;
T ret = numeric_traits<T>::zero();
for (auto & t : m_columns[column]) {
T a = abs(t.m_value);
if (first_time) {
first_time = false;
ret = a;
} else if (a < ret) {
ret = a;
}
}
return ret;
}
T get_min_abs_in_column(unsigned column) const;
#ifdef LEAN_DEBUG
void check_consistency() {
std::unordered_map<std::pair<unsigned, unsigned>, T> by_rows;
for (int i = 0; i < m_rows.size(); i++){
for (auto & t : m_rows[i]) {
pair<unsigned, unsigned> p(i, t.m_j);
lean_assert(by_rows.find(p) == by_rows.end());
by_rows[p] = t.get_val();
}
}
std::unordered_map<pair<unsigned, unsigned>, T> by_cols;
for (int i = 0; i < m_columns.size(); i++){
for (auto & t : m_columns[i]) {
pair<unsigned, unsigned> p(t.m_i, i);
lean_assert(by_cols.find(p) == by_cols.end());
by_cols[p] = get_value_of_column_cell(t);
}
}
lean_assert(by_rows.size() == by_cols.size());
for (auto & t : by_rows) {
auto ic = by_cols.find(t.first);
if (ic == by_cols.end()){
std::cout << "rows have pair (" << t.first.first <<"," << t.first.second
<< "), but columns don't " << std::endl;
}
lean_assert(ic != by_cols.end());
lean_assert(t.second == ic->second);
}
}
void check_consistency();
#endif
void cross_out_row(unsigned k) {
#ifdef LEAN_DEBUG
check_consistency();
#endif
cross_out_row_from_columns(k, m_rows[k]);
fix_row_indices_in_each_column_for_crossed_row(k);
m_rows.erase(m_rows.begin() + k);
#ifdef LEAN_DEBUG
regen_domain();
check_consistency();
#endif
}
void cross_out_row(unsigned k);
//
void fix_row_indices_in_each_column_for_crossed_row(unsigned k) {
for (unsigned j = 0; j < m_columns.size(); j++) {
auto & col = m_columns[j];
for (int i = 0; i < col.size(); i++) {
if (col[i].m_i > k) {
col[i].m_i--;
}
}
}
}
void fix_row_indices_in_each_column_for_crossed_row(unsigned k);
void cross_out_row_from_columns(unsigned k, row_strip & row) {
for (auto & t : row) {
cross_out_row_from_column(t.m_j, k);
}
}
void cross_out_row_from_columns(unsigned k, row_strip & row);
void cross_out_row_from_column(unsigned col, unsigned k) {
auto & s = m_columns[col];
for (unsigned i = 0; i < s.size(); i++) {
if (s[i].m_i == k) {
s.erase(s.begin() + i);
break;
}
}
}
void cross_out_row_from_column(unsigned col, unsigned k);
T get_elem(unsigned i, unsigned j) const { // should not be used in efficient code !!!!
for (auto & t : m_rows[i]) {
if (t.m_j == j) {
return t.get_val();
}
}
return numeric_traits<T>::zero();
}
T get_elem(unsigned i, unsigned j) const;
unsigned number_of_non_zeroes_in_column(unsigned j) const {
return m_columns[j].size();
}
unsigned number_of_non_zeroes_in_column(unsigned j) const { return m_columns[j].size(); }
unsigned number_of_non_zeroes_in_row(unsigned i) const {
return m_rows[i].number_of_non_zeros();
}
unsigned number_of_non_zeroes_in_row(unsigned i) const { return m_rows[i].number_of_non_zeros(); }
void scan_row_to_work_vector(unsigned i) {
for (auto & rc : m_rows[i]) {
m_work_pivot_vector[rc.m_j] = rc.get_val();
}
}
void scan_row_to_work_vector(unsigned i);
void clean_row_work_vector(unsigned i) {
for (auto & rc : m_rows[i]) {
m_work_pivot_vector[rc.m_j] = numeric_traits<T>::zero();
}
}
void clean_row_work_vector(unsigned i);
#ifdef LEAN_DEBUG
@ -460,35 +191,12 @@ public:
virtual void set_number_of_columns(unsigned /*n*/) { }
#endif
T get_max_val_in_row(unsigned i) const {
lean_unreachable();
}
T get_max_val_in_row(unsigned i) const { lean_unreachable(); }
T get_balance() const {
T ret = zero_of_type<T>();
for (unsigned i = 0; i < row_count(); i++) {
ret += get_row_balance(i);
}
return ret;
}
T get_balance() const;
T get_row_balance(unsigned row) const {
T ret = zero_of_type<T>();
for (auto & t : m_rows[row]) {
if (numeric_traits<T>::is_zero(t.get_val())) continue;
T a = abs(t.get_val());
numeric_traits<T>::log(a);
ret += a * a;
}
return ret;
}
bool col_val_equal_to_row_val() const {
for (auto & r : m_rows) {
for (auto & rc : r) {
lean_assert(rc.get_val() == m_columns[rc.m_j][rc.m_offset].m_value);
}
}
return true;
}
T get_row_balance(unsigned row) const;
bool col_val_equal_to_row_val() const;
};
}

View file

@ -0,0 +1,72 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/lp/static_matrix.cpp"
#include "util/lp/lp_core_solver_base.h"
#include "util/lp/lp_dual_core_solver.h"
#include "util/lp/lp_dual_simplex.h"
#include "util/lp/lp_primal_core_solver.h"
#include "util/lp/scaler.h"
#include "util/lp/lar_solver.h"
namespace lean {
template double static_matrix<double, double>::dot_product_with_row<double>(unsigned int, std::vector<double, std::allocator<double> > const&);
template mpq static_matrix<mpq, mpq>::dot_product_with_row<mpq>(unsigned int, std::vector<mpq, std::allocator<mpq> > const&);
template numeric_pair<mpq> static_matrix<mpq, numeric_pair<mpq> >::dot_product_with_row<numeric_pair<mpq> >(unsigned int, std::vector<numeric_pair<mpq>, std::allocator<numeric_pair<mpq> > > const&);
template void static_matrix<double, double>::add_column_to_vector(double const&, unsigned int, double*) const;
template void static_matrix<double, double>::add_columns_at_the_end(unsigned int);
template void static_matrix<double, double>::clear();
#ifdef LEAN_DEBUG
template bool static_matrix<double, double>::col_val_equal_to_row_val() const;
#endif
template void static_matrix<double, double>::copy_column_to_vector(unsigned int, indexed_vector<double>&) const;
template void static_matrix<double, double>::copy_column_to_vector(unsigned int, std::vector<double, std::allocator<double> >&) const;
template void static_matrix<double, double>::divide_row_by_constant(unsigned int, double const&);
template double static_matrix<double, double>::dot_product_with_column(std::vector<double, std::allocator<double> > const&, unsigned int) const;
template double static_matrix<double, double>::get_balance() const;
template std::set<pair<unsigned, unsigned>> static_matrix<double, double>::get_domain();
template double static_matrix<double, double>::get_elem(unsigned int, unsigned int) const;
template double static_matrix<double, double>::get_max_abs_in_column(unsigned int) const;
template double static_matrix<double, double>::get_min_abs_in_column(unsigned int) const;
template double static_matrix<double, double>::get_min_abs_in_row(unsigned int) const;
template void static_matrix<double, double>::init_empty_matrix(unsigned int, unsigned int);
template void static_matrix<double, double>::init_row_columns(unsigned int, unsigned int);
template static_matrix<double, double>::ref & static_matrix<double, double>::ref::operator=(double const&);
template void static_matrix<double, double>::scale_column(unsigned int, double const&);
template void static_matrix<double, double>::scale_row(unsigned int, double const&);
template void static_matrix<double, double>::set(unsigned int, unsigned int, double const&);
template static_matrix<double, double>::static_matrix(unsigned int, unsigned int);
template void static_matrix<mpq, mpq>::add_column_to_vector(mpq const&, unsigned int, mpq*) const;
template void static_matrix<mpq, mpq>::add_columns_at_the_end(unsigned int);
#ifdef LEAN_DEBUG
template bool static_matrix<mpq, mpq>::col_val_equal_to_row_val() const;
#endif
template void static_matrix<mpq, mpq>::copy_column_to_vector(unsigned int, indexed_vector<mpq>&) const;
template void static_matrix<mpq, mpq>::divide_row_by_constant(unsigned int, mpq const&);
template mpq static_matrix<mpq, mpq>::dot_product_with_column(std::vector<mpq, std::allocator<mpq> > const&, unsigned int) const;
template mpq static_matrix<mpq, mpq>::get_balance() const;
template mpq static_matrix<mpq, mpq>::get_elem(unsigned int, unsigned int) const;
template mpq static_matrix<mpq, mpq>::get_max_abs_in_column(unsigned int) const;
template mpq static_matrix<mpq, mpq>::get_max_abs_in_row(unsigned int) const;
template double static_matrix<double, double>::get_max_abs_in_row(unsigned int) const;
template mpq static_matrix<mpq, mpq>::get_min_abs_in_column(unsigned int) const;
template mpq static_matrix<mpq, mpq>::get_min_abs_in_row(unsigned int) const;
template void static_matrix<mpq, mpq>::init_row_columns(unsigned int, unsigned int);
template static_matrix<mpq, mpq>::ref& static_matrix<mpq, mpq>::ref::operator=(mpq const&);
template void static_matrix<mpq, mpq>::scale_column(unsigned int, mpq const&);
template void static_matrix<mpq, mpq>::scale_row(unsigned int, mpq const&);
template void static_matrix<mpq, mpq>::set(unsigned int, unsigned int, mpq const&);
template static_matrix<mpq, mpq>::static_matrix(unsigned int, unsigned int);
#ifdef LEAN_DEBUG
template bool static_matrix<mpq, numeric_pair<mpq> >::col_val_equal_to_row_val() const;
#endif
template void static_matrix<mpq, numeric_pair<mpq> >::copy_column_to_vector(unsigned int, indexed_vector<mpq>&) const;
template mpq static_matrix<mpq, numeric_pair<mpq> >::dot_product_with_column(std::vector<mpq, std::allocator<mpq> > const&, unsigned int) const;
template mpq static_matrix<mpq, numeric_pair<mpq> >::get_elem(unsigned int, unsigned int) const;
template void static_matrix<mpq, numeric_pair<mpq> >::init_empty_matrix(unsigned int, unsigned int);
template void static_matrix<mpq, numeric_pair<mpq> >::set(unsigned int, unsigned int, mpq const&);
}

View file

@ -1,65 +0,0 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#pragma once
#include <vector>
#include "util/debug.h"
#include "util/numerics/numeric_traits.h"
#include "util/numerics/xnumeral.h"
#include <set>
#include "util/lp/lp_settings.h"
#include "util/lp/sparse_matrix.h"
#include "util/lp/sparse_vector.h"
#include "util/lp/pivot_chooser_base.h"
namespace lean {
// following "Computing Sparse LU Factorizations for Large-Scale Linear Programming Bases", by Suhl, Suhl
template <typename T, typename X>
class suhl_pivot_chooser : public pivot_chooser_base {
// m_U is a square matrix
sparse_matrix<T, X> & m_U;
lp_settings const & m_settings;
public:
// constructor
suhl_pivot_chooser(sparse_matrix<T, X> & U, lp_settings const & settings): m_U(U), m_settings(settings) {
lean_assert(U.row_count() == U.column_count());
}
bool get_column_singleton(unsigned * i, unsigned *j) {
return m_U.find_column_singleton(i, j);
}
bool get_row_singleton(unsigned* i, unsigned *j) {
return m_U.find_row_singleton(i, j);
}
void pivot_update_kernel(unsigned i, unsigned j) {
}
bool get_pivot_not_adjusted(unsigned *i, unsigned * j) {
if (get_column_singleton(i, j)) {
return true;
}
if (get_row_singleton(i, j)) {
return true;
}
return m_U.get_non_singleton_pivot(i, j, m_settings.depth_of_rook_search, T(m_settings.c_partial_pivoting));
}
int get_pivot(unsigned i, unsigned * j) {
if (get_pivot_not_adjusted(&i, j)) {
*j = m_U.adjust_column_inverse(*j);
return m_U.adjust_row_inverse(i);
}
return -1;
}
};
}

26
src/util/lp/tail_matrix.h Normal file
View file

@ -0,0 +1,26 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#pragma once
#include "util/lp/indexed_vector.h"
#include "util/lp/matrix.h"
// These matrices appear at the end of the list
namespace lean {
template <typename T, typename X>
class tail_matrix
#ifdef LEAN_DEBUG
: public matrix<T, X>
#endif
{
public:
virtual void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) = 0;
virtual void apply_from_left(std::vector<X> & w, lp_settings & settings) = 0;
virtual void apply_from_right(std::vector<T> & w) = 0;
virtual ~tail_matrix() {}
};
}