diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index af4e8ef7d5..7018f3d409 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -337,6 +337,8 @@ add_subdirectory(util/sexpr) set(LEAN_OBJS ${LEAN_OBJS} $) add_subdirectory(util/interval) set(LEAN_OBJS ${LEAN_OBJS} $) +add_subdirectory(util/lp) +set(LEAN_OBJS ${LEAN_OBJS} $) add_subdirectory(kernel) set(LEAN_OBJS ${LEAN_OBJS} $) add_subdirectory(kernel/inductive) diff --git a/src/tests/util/lp/CMakeLists.txt b/src/tests/util/lp/CMakeLists.txt index 13a8c30be6..4c7eeb272b 100644 --- a/src/tests/util/lp/CMakeLists.txt +++ b/src/tests/util/lp/CMakeLists.txt @@ -1,4 +1,4 @@ -add_executable(lp_tst lp.cpp $ $) +add_executable(lp_tst lp.cpp $ $ $) target_link_libraries(lp_tst ${EXTRA_LIBS}) add_test(lp_tst ${CMAKE_CURRENT_BINARY_DIR}/lp_tst) add_executable(double_compare double_compare.cpp $ $) diff --git a/src/tests/util/lp/lp.cpp b/src/tests/util/lp/lp.cpp index fa18df0c39..273b1b0680 100644 --- a/src/tests/util/lp/lp.cpp +++ b/src/tests/util/lp/lp.cpp @@ -17,7 +17,6 @@ Author: Lev Nachmanson #include #include #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 & 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 &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 &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 void test_swap_rows_with_permutation(sparse_matrix& m){ @@ -538,8 +539,8 @@ void test_swap_rows_with_permutation(sparse_matrix& 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& m){ template void fill_matrix(sparse_matrix& m); // forward definition #ifdef LEAN_DEBUG -void matrix_repro_test() { - unsigned dim = 10; - sparse_matrix 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 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 void test_swap_cols_with_permutation(sparse_matrix& m){ cout << "testing swaps" << std::endl; @@ -592,8 +563,8 @@ void test_swap_cols_with_permutation(sparse_matrix& 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 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 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 l(2, 5); -#else - eta_matrix 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 lcopy(l); - - l.conjugate_by_permutation(p); - - permutation_matrix 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 p(5); - p[0] = 3; p[1] = 2; p[2] = 1; p[3] = 4; - p[4] = 0; - - one_off_diagonal_matrix one_off(1, 4, 2, 3); - one_off.set_number_of_rows(5); - one_off.set_number_of_columns(5); - one_off_diagonal_matrix 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 pr = p.get_inverse(); - - auto conj = one_off_copy * (pr); - conj = p * conj; - lean_assert(conj == one_off); -} - -void test_conjugate_perm(){ - permutation_matrix p(5); - p[0] = 1; p[1] = 2; p[2] = 3; p[3] = 4; - p[4] = 0; - - one_off_diagonal_matrix one_off(1, 3, 2, 3); - one_off.set_number_of_rows(5); - one_off.set_number_of_columns(5); - one_off_diagonal_matrix 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 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 p(5); - p[0] = 1; p[1] = 2; p[2] = 3; p[3] = 4; - p[4] = 0; - - one_off_diagonal_matrix one_off(1, 3, 2, 3); - one_off.set_number_of_rows(5); - one_off.set_number_of_columns(5); - one_off_diagonal_matrix 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 pr = p.get_inverse(); - - auto conj = one_off_copy * pr; - conj = p * conj; - lean_assert(conj == one_off); - test_conjugate0(); -} -#endif std::vector> vector_of_permutaions() { std::vector> ret; @@ -1327,9 +1202,9 @@ void test_upair_queue() { binary_heap_upair_queue q(2); unordered_map 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 * 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 m(8); - fill_matrix(m); - lp_settings settings; - for (unsigned j = 0; j < m.dimension(); j++){ - eta_matrix * 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 * 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> get_file_list_of_dir(std::string test_file_dir) { DIR *dir; @@ -2224,16 +2093,19 @@ std::vector> 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(); diff --git a/src/util/lp/CMakeLists.txt b/src/util/lp/CMakeLists.txt index ca24943a74..d26b197304 100644 --- a/src/util/lp/CMakeLists.txt +++ b/src/util/lp/CMakeLists.txt @@ -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 + ) diff --git a/src/util/lp/binary_heap_priority_queue.cpp b/src/util/lp/binary_heap_priority_queue.cpp new file mode 100644 index 0000000000..67cb9eeee7 --- /dev/null +++ b/src/util/lp/binary_heap_priority_queue.cpp @@ -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 void binary_heap_priority_queue::swap_with_parent(unsigned i) { + unsigned parent = m_heap[i >> 1]; + put_at(i >> 1, m_heap[i]); + put_at(i, parent); +} + +template void binary_heap_priority_queue::put_at(unsigned i, unsigned h) { + m_heap[i] = h; + m_heap_inverse[h] = i; +} + +template void binary_heap_priority_queue::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 bool binary_heap_priority_queue::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(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 void binary_heap_priority_queue::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 binary_heap_priority_queue::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 void binary_heap_priority_queue::resize(unsigned n) { + m_priorities.resize(n); + m_heap.resize(n + 1); + m_heap_inverse.resize(n, -1); + } + +template void binary_heap_priority_queue::put_to_heap(unsigned i, unsigned o) { + m_heap[i] = o; + m_heap_inverse[o] = i; + } + +template void binary_heap_priority_queue::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 void binary_heap_priority_queue::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 void binary_heap_priority_queue::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 unsigned binary_heap_priority_queue::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 void binary_heap_priority_queue::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 void binary_heap_priority_queue::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 unsigned binary_heap_priority_queue::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 void binary_heap_priority_queue::print() { + std::vector index; + std::vector 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 +} diff --git a/src/util/lp/binary_heap_priority_queue.h b/src/util/lp/binary_heap_priority_queue.h index 541429c645..6b787dd780 100644 --- a/src/util/lp/binary_heap_priority_queue.h +++ b/src/util/lp/binary_heap_priority_queue.h @@ -6,6 +6,7 @@ */ #pragma once #include +#include "util/debug.h" namespace lean { // the elements with the smallest priority are dequeued first template @@ -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(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 index; - std::vector 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 }; } diff --git a/src/util/lp/binary_heap_priority_queue_instances.cpp b/src/util/lp/binary_heap_priority_queue_instances.cpp new file mode 100644 index 0000000000..6e17f2c3c5 --- /dev/null +++ b/src/util/lp/binary_heap_priority_queue_instances.cpp @@ -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::binary_heap_priority_queue(unsigned int); +template unsigned binary_heap_priority_queue::dequeue(); +template void binary_heap_priority_queue::enqueue(unsigned int, int const&); +template void binary_heap_priority_queue::remove(unsigned int); +template unsigned binary_heap_priority_queue >::dequeue(); +template void binary_heap_priority_queue >::enqueue(unsigned int, numeric_pair const&); +template void binary_heap_priority_queue >::resize(unsigned int); +template binary_heap_priority_queue::binary_heap_priority_queue(unsigned int); +template unsigned binary_heap_priority_queue::dequeue(); +template void binary_heap_priority_queue::enqueue(unsigned int, unsigned int const&); +template void binary_heap_priority_queue::remove(unsigned int); +} diff --git a/src/util/lp/binary_heap_upair_queue.cpp b/src/util/lp/binary_heap_upair_queue.cpp new file mode 100644 index 0000000000..2c0e34c47e --- /dev/null +++ b/src/util/lp/binary_heap_upair_queue.cpp @@ -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 binary_heap_upair_queue::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 unsigned + binary_heap_upair_queue::dequeue_available_spot() { + lean_assert(m_available_spots.empty() == false); + unsigned ret = m_available_spots.back(); + m_available_spots.pop_back(); + return ret; +} +template void binary_heap_upair_queue::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 bool binary_heap_upair_queue::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 void binary_heap_upair_queue::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_indexsecond; + } + m_q.enqueue(ij_index, priority); + } + +template void binary_heap_upair_queue::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 T binary_heap_upair_queue::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 bool binary_heap_upair_queue::pair_to_index_is_a_bijection() const { + std::set 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 bool binary_heap_upair_queue::available_spots_are_correct() const { + std::set 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 +#include +#include +#include +#include +typedef std::pair upair; + +namespace lean { +template +class binary_heap_upair_queue { + binary_heap_priority_queue m_q; + std::unordered_map m_pairs_to_index; + std::vector m_pairs; // inverse to index + std::vector 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_indexsecond; + } + 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 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 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(); + } +}; +} +*/ diff --git a/src/util/lp/binary_heap_upair_queue.h b/src/util/lp/binary_heap_upair_queue.h index 8e6f444885..0571944eb0 100644 --- a/src/util/lp/binary_heap_upair_queue.h +++ b/src/util/lp/binary_heap_upair_queue.h @@ -6,12 +6,15 @@ */ #pragma once -#include "util/lp/binary_heap_priority_queue.h" #include +#include #include #include #include #include +#include "util/lp/binary_heap_priority_queue.h" +#include "util/lp/hash_helper.h" + typedef std::pair upair; namespace lean { @@ -22,122 +25,27 @@ class binary_heap_upair_queue { std::vector m_pairs; // inverse to index std::vector 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_indexsecond; - } - 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 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 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 }; } diff --git a/src/util/lp/binary_heap_upair_queue_instances.cpp b/src/util/lp/binary_heap_upair_queue_instances.cpp new file mode 100644 index 0000000000..cf429140a7 --- /dev/null +++ b/src/util/lp/binary_heap_upair_queue_instances.cpp @@ -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::binary_heap_upair_queue(unsigned int); +template binary_heap_upair_queue::binary_heap_upair_queue(unsigned int); +template unsigned binary_heap_upair_queue::dequeue_available_spot(); +template unsigned binary_heap_upair_queue::dequeue_available_spot(); +template void binary_heap_upair_queue::enqueue(unsigned int, unsigned int, int const&); +template void binary_heap_upair_queue::remove(unsigned int, unsigned int); +template void binary_heap_upair_queue::remove(unsigned int, unsigned int); +template void binary_heap_upair_queue::dequeue(unsigned int&, unsigned int&); +template void binary_heap_upair_queue::enqueue(unsigned int, unsigned int, unsigned int const&); +template void binary_heap_upair_queue::dequeue(unsigned int&, unsigned int&); +} diff --git a/src/util/lp/canonic_left_side.h b/src/util/lp/canonic_left_side.h index 3182711646..ac5a4a559e 100644 --- a/src/util/lp/canonic_left_side.h +++ b/src/util/lp/canonic_left_side.h @@ -9,7 +9,10 @@ #include #include #include - +#include +#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 & a, const pair & b) { +inline bool compare(const std::pair & a, const std::pair & 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> m_coeffs; + std::vector> m_coeffs; column_info m_column_info; lar_normalized_constraint * m_low_bound_witness = nullptr; lar_normalized_constraint * m_upper_bound_witness = nullptr; - canonic_left_side(buffer> buffer) { + canonic_left_side(buffer> buffer) { for (auto it : buffer) { if (numeric_traits::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> hash_fun; + std::hash> hash_fun; for (auto v : m_coeffs) { ret |= (hash_fun(v) << 2); } diff --git a/src/util/lp/column_info.h b/src/util/lp/column_info.h index df1697b772..3e31d6c6fc 100644 --- a/src/util/lp/column_info.h +++ b/src/util/lp/column_info.h @@ -6,12 +6,11 @@ */ #pragma once -#include "util/lp/lp_primal_core_solver.h" -#include "util/lp/lp_solver.h" #include #include #include #include +#include "util/lp/lp_settings.h" namespace lean { template 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() { diff --git a/src/util/lp/core_solver_pretty_printer.cpp b/src/util/lp/core_solver_pretty_printer.cpp new file mode 100644 index 0000000000..8817802015 --- /dev/null +++ b/src/util/lp/core_solver_pretty_printer.cpp @@ -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 +core_solver_pretty_printer::core_solver_pretty_printer(lp_core_solver_base & 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(core_solver.m_A.column_count(), "")), + m_signs(core_solver.m_A.row_count(), vector(core_solver.m_A.column_count(), " ")), + m_costs(ncols(), ""), + m_cost_signs(ncols(), " "), + m_rs(ncols(), zero_of_type()) { + 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 void core_solver_pretty_printer:: init_costs() { + vector 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 core_solver_pretty_printer:: ~core_solver_pretty_printer() { + m_core_solver.restore_state(m_w_buff, m_ed_buff); + delete [] m_w_buff; + delete [] m_ed_buff; + } + template void core_solver_pretty_printer:: 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 T core_solver_pretty_printer:: current_column_norm() { + T ret = zero_of_type(); + for (T & ed : m_core_solver.m_ed) + ret += ed * ed; + return ret; + } + + template void core_solver_pretty_printer:: 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 void core_solver_pretty_printer:: init_column_widths() { + for (unsigned i = 0; i < ncols(); i++) { + m_column_widths[i] = get_column_width(i); + } + } + + template void core_solver_pretty_printer:: 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 void core_solver_pretty_printer:: 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 void core_solver_pretty_printer:: 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 unsigned core_solver_pretty_printer:: 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 std::string core_solver_pretty_printer::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 void core_solver_pretty_printer:: set_coeff(vector& row, vector & row_signs, unsigned col, const T & t, string name) { + if (numeric_traits::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 void core_solver_pretty_printer:: 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 std::string core_solver_pretty_printer::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 std::string core_solver_pretty_printer::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 void core_solver_pretty_printer:: 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 void core_solver_pretty_printer:: 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 void core_solver_pretty_printer:: 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 void core_solver_pretty_printer:: 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 void core_solver_pretty_printer:: 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 void core_solver_pretty_printer:: 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 void core_solver_pretty_printer:: 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 void core_solver_pretty_printer:: print_given_rows(vector & row, vector & 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 void core_solver_pretty_printer:: 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); + } +} diff --git a/src/util/lp/core_solver_pretty_printer.h b/src/util/lp/core_solver_pretty_printer.h index 27dc279855..37f285a219 100644 --- a/src/util/lp/core_solver_pretty_printer.h +++ b/src/util/lp/core_solver_pretty_printer.h @@ -9,6 +9,7 @@ #include #include #include +#include "util/lp/lp_settings.h" namespace lean { template class lp_core_solver_base; // forward definition @@ -46,346 +47,70 @@ class core_solver_pretty_printer { vector m_exact_column_norms; public: - core_solver_pretty_printer(lp_core_solver_base & 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(core_solver.m_A.column_count(), "")), - m_signs(core_solver.m_A.row_count(), vector(core_solver.m_A.column_count(), " ")), - m_costs(ncols(), ""), - m_cost_signs(ncols(), " "), - m_rs(ncols(), zero_of_type()) { - 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 & core_solver); - void init_costs() { - vector 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(); - 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& row, vector & row_signs, unsigned col, const T & t, string name) { - if (numeric_traits::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& row, vector & 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 & row, vector & 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 & row, vector & 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); }; } diff --git a/src/util/lp/core_solver_pretty_printer_instances.cpp b/src/util/lp/core_solver_pretty_printer_instances.cpp new file mode 100644 index 0000000000..f43b83f7ab --- /dev/null +++ b/src/util/lp/core_solver_pretty_printer_instances.cpp @@ -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::core_solver_pretty_printer(lean::lp_core_solver_base &); +template void lean::core_solver_pretty_printer::print(); +template lean::core_solver_pretty_printer::~core_solver_pretty_printer(); +template lean::core_solver_pretty_printer::core_solver_pretty_printer(lean::lp_core_solver_base &); +template void lean::core_solver_pretty_printer::print(); +template lean::core_solver_pretty_printer::~core_solver_pretty_printer(); +template lean::core_solver_pretty_printer >::core_solver_pretty_printer(lean::lp_core_solver_base > &); +template lean::core_solver_pretty_printer >::~core_solver_pretty_printer(); +template void lean::core_solver_pretty_printer >::print(); diff --git a/src/util/lp/dense_matrix.cpp b/src/util/lp/dense_matrix.cpp new file mode 100644 index 0000000000..51d64c2ed5 --- /dev/null +++ b/src/util/lp/dense_matrix.cpp @@ -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 dense_matrix::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::zero(); + } + +template dense_matrix dense_matrix::operator*=(matrix 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::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 dense_matrix& + dense_matrix::operator=(matrix 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 dense_matrix& + dense_matrix::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 dense_matrix::dense_matrix(dense_matrix 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 dense_matrix::dense_matrix(matrix 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 void dense_matrix:: apply_from_right(T * w) { + T * t = new T[m_m]; + for (int i = 0; i < m_m; i ++) { + T v = numeric_traits::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 void dense_matrix:: apply_from_right(std::vector & w) { + T * t = new T[m_m]; + for (int i = 0; i < m_m; i ++) { + T v = numeric_traits::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 T* dense_matrix:: + apply_from_left_with_different_dims(std::vector & w) { + T * t = new T[m_m]; + for (int i = 0; i < m_m; i ++) { + T v = numeric_traits::zero(); + for (int j = 0; j < m_n; j++) { + v += w[j]* get_elem(i, j); + } + t[i] = v; + } + + return t; + } + + template void dense_matrix:: apply_from_left(std::vector & w) { + T * t = new T[m_m]; + for (int i = 0; i < m_m; i ++) { + T v = numeric_traits::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 void dense_matrix:: 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::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 void dense_matrix:: apply_from_left_to_X(std::vector & w, lp_settings & ) { + std::vector t(m_m); + for (int i = 0; i < m_m; i ++) { + X v = zero_of_type(); + 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 void dense_matrix:: pivot_row_to_row(unsigned i, T alpha, unsigned i0, + double & pivot_epsilon) { + thread_local T _0 = numeric_traits::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 void dense_matrix:: 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 void dense_matrix:: 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 void dense_matrix:: 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 +dense_matrix operator* (matrix & a, matrix & b){ + dense_matrix 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::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 diff --git a/src/util/lp/dense_matrix.h b/src/util/lp/dense_matrix.h new file mode 100644 index 0000000000..fc358a34ae --- /dev/null +++ b/src/util/lp/dense_matrix.h @@ -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 +class dense_matrix: public matrix { +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 const & a); + + dense_matrix & operator=(matrix const & other); + + dense_matrix & operator=(dense_matrix const & other); + + dense_matrix(dense_matrix const & other); + + dense_matrix(matrix const & other); + void apply_from_right(T * w); + + void apply_from_right(std::vector & w); + + T * apply_from_left_with_different_dims(std::vector & w); + void apply_from_left(std::vector & w , lp_settings & ) { apply_from_left(w); } + + void apply_from_left(std::vector & w); + + void apply_from_left(X * w, lp_settings & ); + + void apply_from_left_to_X(std::vector & 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 +dense_matrix operator* (matrix & a, matrix & b); +} +#endif diff --git a/src/util/lp/dense_matrix_instances.cpp b/src/util/lp/dense_matrix_instances.cpp new file mode 100644 index 0000000000..f4907d268f --- /dev/null +++ b/src/util/lp/dense_matrix_instances.cpp @@ -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 lean::operator*(lean::matrix&, lean::matrix&); +template void lean::dense_matrix::apply_from_left(std::vector >&); +template lean::dense_matrix::dense_matrix(lean::matrix const&); +template lean::dense_matrix::dense_matrix(unsigned int, unsigned int); +template lean::dense_matrix& lean::dense_matrix::operator=(lean::dense_matrix const&); +#endif diff --git a/src/util/lp/eta_matrix.cpp b/src/util/lp/eta_matrix.cpp new file mode 100644 index 0000000000..c8e1de612b --- /dev/null +++ b/src/util/lp/eta_matrix.cpp @@ -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 +#include "util/lp/eta_matrix.h" +namespace lean { + +// This is the sum of a unit matrix and a one-column matrix +template +void eta_matrix::apply_from_left(std::vector & 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 +template +void eta_matrix:: +apply_from_left_local(indexed_vector & 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(); + 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(); + 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(); + w.erase_from_index(i); + } + } + } +} +template +void eta_matrix::apply_from_right(std::vector & w) { +#ifdef LEAN_DEBUG + // dense_matrix deb(*this); + // auto clone_w = clone_vector(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(clone_w, w, get_number_of_rows())); + // delete clone_w; +#endif +} +#ifdef LEAN_DEBUG +template +T eta_matrix::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::one() : numeric_traits::zero(); +} +#endif +template +void eta_matrix::conjugate_by_permutation(permutation_matrix & 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 +} +} diff --git a/src/util/lp/eta_matrix.h b/src/util/lp/eta_matrix.h index b6ce28ac3b..69cbf2e9cd 100644 --- a/src/util/lp/eta_matrix.h +++ b/src/util/lp/eta_matrix.h @@ -7,6 +7,8 @@ #pragma once #include +#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 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 & 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(m_column_vector); !it.done(); it.move()) { - w[it.index()] += w_at_column_index * it.value(); - } - } + void apply_from_left(std::vector & w, lp_settings & ); template - void apply_from_left_local(indexed_vector & 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(); - w.erase_from_index(m_column_index); - } - - for (auto it = sparse_vector_iterator(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(); - 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(); - w.erase_from_index(i); - } - } - } - } + void apply_from_left_local(indexed_vector & w, lp_settings & settings); void apply_from_left_to_T(indexed_vector & 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 & w) { -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(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(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(clone_w, w, get_number_of_rows())); - // delete clone_w; -#endif - } + void apply_from_right(std::vector & 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::one() : numeric_traits::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 get_sparse_vector_iterator() { - return sparse_vector_iterator(m_column_vector); - } - void divide_by_diagonal_element() { m_column_vector.divide(m_diagonal_element); } - void conjugate_by_permutation(permutation_matrix & 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 & p); }; } diff --git a/src/util/lp/eta_matrix_instances.cpp b/src/util/lp/eta_matrix_instances.cpp new file mode 100644 index 0000000000..e59d28d4de --- /dev/null +++ b/src/util/lp/eta_matrix_instances.cpp @@ -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::get_elem(unsigned int, unsigned int) const; +template lean::mpq lean::eta_matrix::get_elem(unsigned int, unsigned int) const; +template lean::mpq lean::eta_matrix >::get_elem(unsigned int, unsigned int) const; +#endif +template void lean::eta_matrix::apply_from_left(std::vector >&, lean::lp_settings&); +template void lean::eta_matrix::apply_from_right(std::vector >&); +template void lean::eta_matrix::conjugate_by_permutation(lean::permutation_matrix&); +template void lean::eta_matrix::apply_from_left(std::vector >&, lean::lp_settings&); +template void lean::eta_matrix::apply_from_right(std::vector >&); +template void lean::eta_matrix::conjugate_by_permutation(lean::permutation_matrix&); +template void lean::eta_matrix >::apply_from_left(std::vector, std::allocator > >&, lean::lp_settings&); +template void lean::eta_matrix >::apply_from_right(std::vector >&); +template void lean::eta_matrix >::conjugate_by_permutation(lean::permutation_matrix >&); +template void lean::eta_matrix::apply_from_left_local(lean::indexed_vector&, lean::lp_settings&); +template void lean::eta_matrix::apply_from_left_local(lean::indexed_vector&, lean::lp_settings&); +template void lean::eta_matrix >::apply_from_left_local(lean::indexed_vector&, lean::lp_settings&); diff --git a/src/util/lp/hash_helper.h b/src/util/lp/hash_helper.h new file mode 100644 index 0000000000..1e75ed776d --- /dev/null +++ b/src/util/lp/hash_helper.h @@ -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 +#include +#include "util/numerics/mpq.h" +namespace std { +template<> +struct hash { + inline size_t operator()(const lean::mpq & v) const { + return v.hash(); + } +}; +} + +template +inline void hash_combine(std::size_t & seed, const T & v) { + std::hash hasher; + seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); +} + +namespace std { +template struct hash> { + inline size_t operator()(const pair & v) const { + size_t seed = 0; + hash_combine(seed, v.first); + hash_combine(seed, v.second); + return seed; + } +}; +} diff --git a/src/util/lp/indexed_value.h b/src/util/lp/indexed_value.h index 2f777a7c88..0d30cf5122 100644 --- a/src/util/lp/indexed_value.h +++ b/src/util/lp/indexed_value.h @@ -46,6 +46,7 @@ public: m_value = val; } }; +#ifdef LEAN_DEBUG template bool check_vector_for_small_values(indexed_vector & w, lp_settings & settings) { for (unsigned i : w.m_index) { @@ -55,4 +56,5 @@ bool check_vector_for_small_values(indexed_vector & w, lp_settings & settings } return true; } +#endif } diff --git a/src/util/lp/indexed_vector.cpp b/src/util/lp/indexed_vector.cpp new file mode 100644 index 0000000000..974a43f6ec --- /dev/null +++ b/src/util/lp/indexed_vector.cpp @@ -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 +void print_vector(const std::vector & t) { + for (unsigned i = 0; i < t.size(); i++) + std::cout << t[i] << " "; + std::cout << std::endl; +} + +template +void print_vector(const buffer & t) { + for (unsigned i = 0; i < t.size(); i++) + std::cout << t[i] << " "; + std::cout << std::endl; +} + +template +void print_sparse_vector(const std::vector & 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 & t) { + for (unsigned i = 0; i < t.size(); i++) + std::cout << t[i].get_double() << std::setprecision(3) << " "; + std::cout << std::endl; +} + + template + void indexed_vector::resize(unsigned data_size) { + m_index.clear(); + m_data.resize(data_size, numeric_traits::zero()); + } + + template + void indexed_vector::set_value(T value, unsigned index) { + m_data[index] = value; + m_index.push_back(index); + } + + template + void indexed_vector::clear() { + for (unsigned i : m_index) + m_data[i] = numeric_traits::zero(); + m_index.clear(); + } + template + void indexed_vector::clear_all() { + unsigned i = m_data.size(); + while (i--) m_data[i] = numeric_traits::zero(); + m_index.clear(); + } + template + void indexed_vector::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 + bool indexed_vector::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 + void indexed_vector::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 + +} diff --git a/src/util/lp/indexed_vector.h b/src/util/lp/indexed_vector.h index 5a8a5e372a..6d0c02876b 100644 --- a/src/util/lp/indexed_vector.h +++ b/src/util/lp/indexed_vector.h @@ -20,42 +20,12 @@ #include "util/lp/sparse_vector.h" #include namespace lean { -template -void print_vector(const T * t, unsigned l) { - for (unsigned i = 0; i < l; i++) - std::cout << t[i] << " "; - std::cout << std::endl; -} - -template -void print_vector(const std::vector & t) { - for (unsigned i = 0; i < t.size(); i++) - std::cout << t[i] << " "; - std::cout << std::endl; -} - -template -void print_vector(const buffer & t) { - for (unsigned i = 0; i < t.size(); i++) - std::cout << t[i] << " "; - std::cout << std::endl; -} - -template -void print_sparse_vector(const std::vector & 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 & t) { - for (unsigned i = 0; i < t.size(); i++) - std::cout << t[i].get_double() << std::setprecision(3) << " "; - std::cout << std::endl; -} +template void print_vector(const std::vector & t); +template void print_vector(const buffer & t); +template void print_sparse_vector(const std::vector & t); + +void print_vector(const std::vector & t); template class indexed_vector { public: @@ -66,11 +36,7 @@ public: m_data.resize(data_size, numeric_traits::zero()); } - void resize(unsigned data_size) { - m_index.clear(); - m_data.resize(data_size, numeric_traits::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::zero(); - } - m_index.clear(); - } - - void clear_all() { - unsigned i = m_data.size(); - while (i--) m_data[i] = numeric_traits::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 }; } diff --git a/src/util/lp/indexed_vector_instances.cpp b/src/util/lp/indexed_vector_instances.cpp new file mode 100644 index 0000000000..20808d9ed3 --- /dev/null +++ b/src/util/lp/indexed_vector_instances.cpp @@ -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::clear(); +template void indexed_vector::clear_all(); +template void indexed_vector::erase_from_index(unsigned int); +template void indexed_vector::set_value(double, unsigned int); +template void indexed_vector::set_value(float, unsigned int); +template void indexed_vector::clear(); +template void indexed_vector::clear_all(); +template void indexed_vector::erase_from_index(unsigned int); +template void indexed_vector::resize(unsigned int); +template void indexed_vector::set_value(mpq, unsigned int); +#ifdef LEAN_DEBUG +template bool indexed_vector::is_OK() const; +template bool lean::indexed_vector::is_OK() const; +#endif +} diff --git a/src/util/lp/lar_constraints.cpp b/src/util/lp/lar_constraints.cpp new file mode 100644 index 0000000000..7ad8c9a4b0 --- /dev/null +++ b/src/util/lp/lar_constraints.cpp @@ -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> & 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> lar_constraint::get_left_side_coefficients() const { + buffer> ret; + for (auto it : m_left_side) { + ret.push_back(std::make_pair(it.second, it.first)); + } + return ret; +} + +buffer> lar_normalized_constraint::get_left_side_coefficients() const { + buffer> ret; + for (auto t : m_canonic_left_side->m_coeffs) ret.push_back(t); + return ret; +} +} + + + + + + diff --git a/src/util/lp/lar_constraints.h b/src/util/lp/lar_constraints.h index 0f299f3506..7c1f9eafbf 100644 --- a/src/util/lp/lar_constraints.h +++ b/src/util/lp/lar_constraints.h @@ -16,11 +16,11 @@ #include #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( - static_cast(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> get_left_side_coefficients() const = 0; + virtual buffer> 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 m_left_side; lar_constraint() {} - lar_constraint(const buffer> & 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> & 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> get_left_side_coefficients() const { - buffer> ret; - for (auto it : m_left_side) { - ret.push_back(std::make_pair(it.second, it.first)); - } - return ret; - } + buffer> get_left_side_coefficients() const; }; class lar_normalized_constraint : public lar_base_constraint { @@ -91,12 +73,8 @@ public: lar_normalized_constraint() {} - virtual buffer> get_left_side_coefficients() const { - buffer> ret; - for (auto t : m_canonic_left_side->m_coeffs) ret.push_back(t); - return ret; - } - virtual unsigned size() const { + buffer> get_left_side_coefficients() const; + unsigned size() const { return m_canonic_left_side->size(); } }; diff --git a/src/util/lp/lar_core_solver.cpp b/src/util/lp/lar_core_solver.cpp new file mode 100644 index 0000000000..d9ecd1d077 --- /dev/null +++ b/src/util/lp/lar_core_solver.cpp @@ -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 +lar_core_solver::lar_core_solver(std::vector & x, std::vector & column_types, + std::vector & low_bounds, std::vector & upper_bounds, + std::vector & basis, + static_matrix & A, + lp_settings & settings, + std::unordered_map & column_names, + std::vector & right_side, + std::vector & costs) : // right_side and costs are redundant + lp_core_solver_base(A, + right_side, + basis, + x, + costs, + settings, + column_names, + column_types, + low_bounds, + upper_bounds) { +} + +template void lar_core_solver::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(); + 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 void lar_core_solver::init_cost_for_column(unsigned j) { + // If j is a breakpoint column, then we set the cost zero. + // When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function + const 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::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::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::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::zero(); + } + break; + case free_column: + this->m_costs[j] = numeric_traits::zero(); + break; + default: + lean_assert(false); + break; + } +} + +template void lar_core_solver::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 int lar_core_solver::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 bool lar_core_solver::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::zero(); + case upper_bound: + lean_assert(this->x_is_at_upper_bound(j)); + return this->m_d[j] > numeric_traits::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::zero()) || ((!low_bound) && this->m_d[j] > numeric_traits::zero()); + } + case free_column: + return !numeric_traits::is_zero(this->m_d[j]); + default: + return false; + } + return false; +} + + +template void lar_core_solver::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 X lar_core_solver::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(); + case upper_bound: + if (x > this->m_upper_bound_values[j]) + return x - this->m_upper_bound_values[j]; + return zero_of_type(); + 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(); + } + case free_column: + { + return zero_of_type(); + } + default: + lean_assert(false); + return zero_of_type(); + } +} + +template X lar_core_solver::get_deb_inf() { + X ret = zero_of_type(); + for (unsigned j = 0; j < this->m_n; j++) { + X d = get_deb_inf_column(j); + // if (! numeric_traits::is_zero(d)) { + // std::cout << "column " << j << ", " << this->column_name(j) << " inf is " << d.get_double() << std::endl; + // } + ret += d; + } + return ret; +} + +template bool lar_core_solver::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 bool lar_core_solver::debug_profit(unsigned j) { + if (this->m_column_type[j] == fixed) return false; + T delta = numeric_traits::one() / 10000000; + delta /= 10000000; + return debug_profit_delta(j, -delta) || debug_profit_delta(j, delta); +} + +template int lar_core_solver::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 void lar_core_solver::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 void lar_core_solver::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 void lar_core_solver::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(), break_type); + return; + } + auto delta_j = diff / d; + if (same_sign_with_entering_delta(delta_j)) + add_breakpoint(j, delta_j, break_type); +} + + +template void lar_core_solver::add_breakpoint(unsigned j, X delta, breakpoint_type type) { + m_breakpoints.push_back(breakpoint(j, delta, type)); + m_breakpoint_indices_queue.enqueue(m_breakpoint_indices_queue.size(), abs(delta)); +} + +template void lar_core_solver::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 std::string lar_core_solver:: 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 void lar_core_solver::print_breakpoint(const breakpoint * 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 void lar_core_solver::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 void lar_core_solver::clear_breakpoints() { + m_breakpoints.clear(); + m_breakpoint_indices_queue.clear(); +} + +template void lar_core_solver::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 void lar_core_solver::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() ? 1 : -1; + + fill_breakpoints_array(entering); + advance_on_sorted_breakpoints(entering); +} + +template void lar_core_solver::print_cost() { + std::cout << "reduced costs " << std::endl; + for (unsigned j = 0; j < this->m_n; j++) { + if (numeric_traits::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 void lar_core_solver::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 void lar_core_solver::advance_on_sorted_breakpoints(unsigned entering) { + T slope_at_entering = this->m_d[entering]; + breakpoint * last_bp = nullptr; + while (m_breakpoint_indices_queue.is_empty() == false) { + unsigned bi = m_breakpoint_indices_queue.dequeue(); + breakpoint *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::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 void lar_core_solver::change_slope_on_breakpoint(unsigned entering, breakpoint * 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::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 bool lar_core_solver::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 bool lar_core_solver::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 bool lar_core_solver::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 bool lar_core_solver::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 void lar_core_solver::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 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::one() && ct[j] != fixed) + break; + j++; + } while (j < ct.size()); + if (j == ct.size()) break; + bs[i++] = j++; + } +} + +template bool lar_core_solver::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 void lar_core_solver::prefix() { + init_local(); + this->init(); + this->init_basis_heading(); +} + +template void lar_core_solver::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 unsigned lar_core_solver::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 void lar_core_solver::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 int lar_core_solver::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 int lar_core_solver::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 bool lar_core_solver::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::is_zero(this->m_pivot_row[j]) == false; + } + default: + lean_unreachable(); + } +} + +template int lar_core_solver::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 void lar_core_solver::fill_evidence(unsigned row, int inf_sign) { + m_infeasible_row_sign = inf_sign; + m_infeasible_row.push_back(std::make_pair(numeric_traits::one(), this->m_basis[row])); + for (auto j : this->m_non_basic_columns) { + T aj = this->m_pivot_row[j]; + if (!numeric_traits::is_zero(aj)) { + m_infeasible_row.push_back(std::make_pair(aj, j)); + } + } +} + + +template void lar_core_solver::update_delta_of_entering_and_leaving_candidates(X del, X & delta, + std::vector & 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 void lar_core_solver::update_delta_of_entering(int delta_sign, unsigned row, X & delta, + std::vector & 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::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() ? 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 X lar_core_solver::find_initial_delta_and_its_sign(unsigned row, unsigned entering, + int inf_sign, int & entering_delta_sign, + std::vector & 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 void lar_core_solver::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 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()); + 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 void lar_core_solver::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 void lar_core_solver::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 void lar_core_solver::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(); + } +} +} + diff --git a/src/util/lp/lar_core_solver.h b/src/util/lp/lar_core_solver.h index b9a8d0241e..ce2ea4060f 100644 --- a/src/util/lp/lar_core_solver.h +++ b/src/util/lp/lar_core_solver.h @@ -34,263 +34,44 @@ public: lp_settings & settings, std::unordered_map & column_names, std::vector & right_side, - std::vector & costs) : // right_side and costs are redundant - lp_core_solver_base(A, - right_side, - basis, - x, - costs, - settings, - column_names, - column_types, - low_bounds, - upper_bounds) { - } + std::vector & 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> & 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(); - 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::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::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::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::zero(); - } - break; - case free_column: - this->m_costs[j] = numeric_traits::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::zero(); - case upper_bound: - lean_assert(this->x_is_at_upper_bound(j)); - return this->m_d[j] > numeric_traits::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::zero()) || ((!low_bound) && this->m_d[j] > numeric_traits::zero()); - } - case free_column: - return !numeric_traits::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(); - case upper_bound: - if (x > this->m_upper_bound_values[j]) - return x - this->m_upper_bound_values[j]; - return zero_of_type(); - 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(); - } - case free_column: - { - return zero_of_type(); - } - default: - lean_assert(false); - return zero_of_type(); - } - } + X get_deb_inf_column(unsigned j); - X get_deb_inf() { - X ret = zero_of_type(); - for (unsigned j = 0; j < this->m_n; j++) { - X d = get_deb_inf_column(j); - // if (! numeric_traits::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::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 bool same_sign_with_entering_delta(const L & a) { return (a > zero_of_type() && m_sign_of_entering_delta > 0) || (a < zero_of_type() && 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(), 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 * 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 * 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(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 * 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() ? 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::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 * last_bp = nullptr; - while (m_breakpoint_indices_queue.is_empty() == false) { - unsigned bi = m_breakpoint_indices_queue.dequeue(); - breakpoint *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::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 * 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::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 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::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 @@ -665,202 +140,37 @@ public: return v > zero_of_type() ? 1 : (v < zero_of_type() ? -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::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::one(), this->m_basis[row])); - for (auto j : this->m_non_basic_columns) { - T aj = this->m_pivot_row[j]; - if (!numeric_traits::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 & 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 & 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::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() ? 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 & leaving_candidates); unsigned find_leaving_for_inf_row_strategy(std::vector & 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 & 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 & 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 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()); - 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); }; } diff --git a/src/util/lp/lar_core_solver_instances.cpp b/src/util/lp/lar_core_solver_instances.cpp new file mode 100644 index 0000000000..387890619c --- /dev/null +++ b/src/util/lp/lar_core_solver_instances.cpp @@ -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 >::lar_core_solver(std::vector, std::allocator > >&, std::vector >&, std::vector, std::allocator > >&, std::vector, std::allocator > >&, std::vector >&, lean::static_matrix >&, lean::lp_settings&, std::unordered_map, std::equal_to, std::allocator > >&, std::vector, std::allocator > >&, std::vector >&); +template void lean::lar_core_solver >::solve(); +template void lean::lar_core_solver >::prefix(); +template void lean::lar_core_solver >::print_column_info(unsigned int); +template lean::numeric_pair lean::lar_core_solver >::get_deb_inf(); +template bool lean::lar_core_solver >::is_empty() const; diff --git a/src/util/lp/lar_solver.cpp b/src/util/lp/lar_solver.cpp new file mode 100644 index 0000000000..30b60425af --- /dev/null +++ b/src/util/lp/lar_solver.cpp @@ -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 ::get_low_bound(const column_info & 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 ::get_upper_bound(const column_info & 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>& 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::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> b; + b.push_back(std::make_pair(numeric_traits::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(j) >= 0;} + + +// this adds a row to A +template +void lar_solver::add_row_to_A(static_matrix & 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::convert(t.first)); + } + A.set(i, ls->m_column_index, - one_of_type()); +} + +template +void lar_solver::create_matrix_A(static_matrix & 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 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 & 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 & 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 & 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 & 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 +void lar_solver::fill_bounds_for_core_solver(std::vector & lb, std::vector & 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::get_low_bound(ci); + if (ci.upper_bound_is_set()) + ub[j] = conversion_helper::get_upper_bound(ci); + } +} + + +template +void lar_solver::resize_x_and_init_with_zeros(std::vector & x, unsigned n) { + x.clear(); + x.resize(n, zero_of_type()); // init with zeroes +} + +template +void lar_solver::resize_x_and_init_with_signature(std::vector & x, std::vector & low_bound, + std::vector & 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 V lar_solver::get_column_val(std::vector & low_bound, std::vector & 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(); + default: + lean_unreachable(); + } +} + +lar_solver::~lar_solver() { + std::vector 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>& 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_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_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> & 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((static_cast(the_kind_of_sum)/2)); + + return n_of_G == 0 || n_of_L == 0; +} + +void lar_solver::register_in_map(std::unordered_map & 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> & evidence) { + std::unordered_map 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::is_zero(it.second)) return false; + } + return true; +} + +bool lar_solver::the_righ_sides_do_not_sum_to_zero(const buffer> & evidence) { + mpq ret = numeric_traits::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::is_zero(ret); +} +#ifdef LEAN_DEBUG +bool lar_solver::the_evidence_is_correct() { + buffer> 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()); + break; + case LT: lean_assert(rs <= zero_of_type()); + break; + case GE: lean_assert(rs > zero_of_type()); + break; + case GT: lean_assert(rs >= zero_of_type()); + break; + case EQ: lean_assert(rs != zero_of_type()); + 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 +void lar_solver::init_right_sides_with_zeros(std::vector & rs) { + rs.clear(); + rs.resize(m_basis.size(), zero_of_type()); +} + +mpq lar_solver::sum_of_right_sides_of_evidence(const buffer> & evidence) { + mpq ret = numeric_traits::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 +void lar_solver::prepare_core_solver_fields(static_matrix & A, std::vector & x, + std::vector & right_side_vector, + std::vector & low_bound, + std::vector & 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 +void lar_solver::prepare_core_solver_fields_with_signature(static_matrix & A, std::vector & x, + std::vector & right_side_vector, + std::vector & low_bound, + std::vector & 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 A; + std::vector x, right_side_vector, low_bounds, upper_bounds; + prepare_core_solver_fields(A, x, right_side_vector, low_bounds, upper_bounds); + std::vector column_scale_vector; + scaler 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(); + } + std::vector costs(A.column_count()); + auto core_solver = lp_primal_core_solver(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 +void lar_solver::extract_signature_from_lp_core_solver(lp_primal_core_solver & 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> & 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> & evidence, + const std::vector> & 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::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 & x = m_x[j]; + numeric_pair & 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::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()) 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 & x = m_x[j]; + numeric_pair & 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::zero()); + } else { + lean_assert(xx < ux); + if (xy <= zero_of_type()) 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 & 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 & 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::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::is_zero(val)) continue; + if (first) { + first = false; + } else { + if (val.is_pos()) { + std::cout << " + "; + } else { + std::cout << " - "; + val = -val; + } + } + + if (val != numeric_traits::one()) + std::cout << val; + std::cout << m_map_from_var_index_to_left_side[it.second]->m_column_info.get_name(); + } +} + +numeric_pair lar_solver::get_infeasibility_from_core_solver(std::unordered_map & 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(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(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 & solution) { + std::cout << "solution" << std::endl; + for (auto it : solution) { + std::cout << it.first << " = " << it.second.get_double() << std::endl; + } + mpq ret = numeric_traits::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(ret, 0) == core_inf); + return ret; +} + +mpq lar_solver::get_infeasibility_of_constraint(const lar_normalized_constraint & norm_constr, std::unordered_map & 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::zero()); + case GT: + case GE: return std::max(- (left_side_val - norm_constr.m_right_side), numeric_traits::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 & solution) { + mpq ret = numeric_traits::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_map) { + mpq ret = numeric_traits::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; +} +} + diff --git a/src/util/lp/lar_solver.h b/src/util/lp/lar_solver.h index 2591a9fe79..9b6f3d8791 100644 --- a/src/util/lp/lar_solver.h +++ b/src/util/lp/lar_solver.h @@ -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 { - inline size_t operator()(const lean::mpq & v) const { - return v.hash(); - } -}; -} +#include "util/lp/lp_primal_core_solver.h" namespace lean { template @@ -44,24 +36,8 @@ struct conversion_helper { template<> struct conversion_helper { - static double get_low_bound(const column_info & 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 & 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 & ci); + static double get_upper_bound(const column_info & ci); }; class lar_solver { @@ -85,93 +61,29 @@ class lar_solver { std::vector> m_right_side_vector; // this vector will be all zeroes, it might change when the optimization with fixed variables will used std::vector 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>& 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>& 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::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> b; - b.push_back(std::make_pair(numeric_traits::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(j) >= 0;} // this adds a row to A template - void add_row_to_A(static_matrix & 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::convert(t.first)); - } - A.set(i, ls->m_column_index, - one_of_type()); - } + void add_row_to_A(static_matrix & A, unsigned i, canonic_left_side * ls); template - void create_matrix_A(static_matrix & 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 & 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 & 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 & 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 & 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 & 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 & 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 & 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 - void fill_bounds_for_core_solver(std::vector & lb, std::vector & 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::get_low_bound(ci); - if (ci.upper_bound_is_set()) - ub[j] = conversion_helper::get_upper_bound(ci); - } - } + void fill_bounds_for_core_solver(std::vector & lb, std::vector & ub); template - void resize_x_and_init_with_zeros(std::vector & x, unsigned n) { - x.clear(); - x.resize(n, zero_of_type()); // init with zeroes - } + void resize_x_and_init_with_zeros(std::vector & x, unsigned n); template void resize_x_and_init_with_signature(std::vector & x, std::vector & low_bound, - std::vector & 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 & upper_bound, const lar_solution_signature & signature); - template V get_column_val(std::vector & low_bound, std::vector & 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(); - default: - lean_unreachable(); - } - } + template V get_column_val(std::vector & low_bound, std::vector & upper_bound, non_basic_column_value_position pos_type, unsigned j); public: - ~lar_solver() { - std::vector 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>& 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>& left_side, lconstraint_kind kind_par, mpq right_side_par); bool is_infeasible(const column_info & 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_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_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_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> & 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> & 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((static_cast(the_kind_of_sum)/2)); + void register_in_map(std::unordered_map & coeffs, lar_constraint & cn, const mpq & a); + bool the_left_sides_sum_to_zero(const buffer> & evidence); - return n_of_G == 0 || n_of_L == 0; - } + bool the_righ_sides_do_not_sum_to_zero(const buffer> & evidence); - void register_in_map(std::unordered_map & 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> & evidence) { - std::unordered_map 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::is_zero(it.second)) return false; - } - return true; - } - - bool the_righ_sides_do_not_sum_to_zero(const buffer> & evidence) { - mpq ret = numeric_traits::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::is_zero(ret); - } - - bool the_evidence_is_correct() { - buffer> 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()); - break; - case LT: lean_assert(rs <= zero_of_type()); - break; - case GE: lean_assert(rs > zero_of_type()); - break; - case GT: lean_assert(rs >= zero_of_type()); - break; - case EQ: lean_assert(rs != zero_of_type()); - 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 - void init_right_sides_with_zeros(std::vector & rs) { - rs.clear(); - rs.resize(m_basis.size(), zero_of_type()); - } + void init_right_sides_with_zeros(std::vector & rs); - mpq sum_of_right_sides_of_evidence(const buffer> & evidence) { - mpq ret = numeric_traits::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> & evidence); + void prepare_independently_of_numeric_type(); template void prepare_core_solver_fields(static_matrix & A, std::vector & x, std::vector & right_side_vector, std::vector & low_bound, - std::vector & 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 & upper_bound); template void prepare_core_solver_fields_with_signature(static_matrix & A, std::vector & x, std::vector & right_side_vector, std::vector & low_bound, - std::vector & 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 & upper_bound, const lar_solution_signature & signature); - void find_solution_signature_with_doubles(lar_solution_signature & signature) { - static_matrix A; - std::vector x, right_side_vector, low_bounds, upper_bounds; - prepare_core_solver_fields(A, x, right_side_vector, low_bounds, upper_bounds); - std::vector column_scale_vector; - scaler 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(); - } - std::vector costs(A.column_count()); - auto core_solver = lp_primal_core_solver(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 - void extract_signature_from_lp_core_solver(lp_primal_core_solver & 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 & 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> & 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> & evidence); void get_infeasibility_evidence_for_inf_sign(buffer> & evidence, const std::vector> & 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::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 & x = m_x[j]; - numeric_pair & 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::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()) 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 & x = m_x[j]; - numeric_pair & 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::zero()); - } else { - lean_assert(xx < ux); - if (xy <= zero_of_type()) 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 & 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 & 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 & 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::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::is_zero(val)) continue; - if (first) { - first = false; - } else { - if (val.is_pos()) { - std::cout << " + "; - } else { - std::cout << " - "; - val = -val; - } - } + numeric_pair get_infeasibility_from_core_solver(std::unordered_map & solution); - if (val != numeric_traits::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 get_infeasibility_from_core_solver(std::unordered_map & 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 & solution); - for (auto ls : m_canonic_left_sides) { - lean_assert(valid_index(ls->m_column_index)); - m_x[ls->m_column_index] = numeric_pair(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 & solution); - void print_info_on_column(unsigned j) { - for (auto ls : m_canonic_left_sides) { - if (static_cast(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 & solution); - mpq get_infeasibility_of_solution(std::unordered_map & solution) { - std::cout << "solution" << std::endl; - for (auto it : solution) { - std::cout << it.first << " = " << it.second.get_double() << std::endl; - } - mpq ret = numeric_traits::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(ret, 0) == core_inf); - return ret; - } + mpq get_left_side_val(const lar_constraint & cns, const std::unordered_map & var_map); - mpq get_infeasibility_of_constraint(const lar_normalized_constraint & norm_constr, std::unordered_map & 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::zero()); - case GT: - case GE: return std::max(- (left_side_val - norm_constr.m_right_side), numeric_traits::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 & solution) { - mpq ret = numeric_traits::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_map) { - mpq ret = numeric_traits::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; } }; } diff --git a/src/util/lp/lar_solver_instances.cpp b/src/util/lp/lar_solver_instances.cpp new file mode 100644 index 0000000000..84f7f4e06e --- /dev/null +++ b/src/util/lp/lar_solver_instances.cpp @@ -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" diff --git a/src/util/lp/lp.h b/src/util/lp/lp.h deleted file mode 100644 index e85aa48cd4..0000000000 --- a/src/util/lp/lp.h +++ /dev/null @@ -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 -#include "util/debug.h" -#include "util/numerics/numeric_traits.h" -#include "util/numerics/xnumeral.h" -#include "util/lp/lp_primal_core_solver.h" diff --git a/src/util/lp/lp_core_solver_base.cpp b/src/util/lp/lp_core_solver_base.cpp new file mode 100644 index 0000000000..9f8bf8ae69 --- /dev/null +++ b/src/util/lp/lp_core_solver_base.cpp @@ -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 & basis, unsigned m, std::vector & 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 & basis_heading, std::vector & 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 & basis, + unsigned m, + std::vector & basis_heading, + unsigned n, + std::vector & 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 lp_core_solver_base:: +lp_core_solver_base(static_matrix & A, + std::vector & b, // the right side vector + std::vector & basis, + std::vector & x, + std::vector & costs, + lp_settings & settings, + const std::unordered_map & column_names, + std::vector & column_types, + std::vector & low_bound_values, + std::vector & 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()), + 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 void lp_core_solver_base:: +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 void lp_core_solver_base:: +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 void lp_core_solver_base:: +fill_cb(T * y){ + for (unsigned i = 0; i < m_m; i++) { + y[i] = m_costs[m_basis[i]]; + } +} + + +template void lp_core_solver_base:: +fill_cb(std::vector & y){ + for (unsigned i = 0; i < m_m; i++) + y[i] = m_costs[m_basis[i]]; +} + +template void lp_core_solver_base:: +solve_yB(std::vector & y) { + fill_cb(y); // now y = cB, that is the projection of costs to basis + m_factorization->solve_yB(y); +} + +template void lp_core_solver_base:: +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 void lp_core_solver_base:: +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 a(m_m); + // m_A.copy_column_to_vector(entering, a); + // vector cd(m_ed); + // B.apply_from_left(cd, m_settings); + // lean_assert(vectors_are_equal(cd , a)); +#endif +} + +template void lp_core_solver_base:: +pretty_print() { + core_solver_pretty_printer pp(*this); + pp.print(); +} + +template void lp_core_solver_base:: +save_state(T * w_buffer, T * d_buffer) { + copy_m_w(w_buffer); + copy_m_ed(d_buffer); +} + +template void lp_core_solver_base:: +restore_state(T * w_buffer, T * d_buffer) { + restore_m_w(w_buffer); + restore_m_ed(d_buffer); +} + +template void lp_core_solver_base:: +copy_m_w(T * buffer) { + unsigned i = m_m; + while (i --) { + buffer[i] = m_w[i]; + } +} + +template void lp_core_solver_base:: +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 void lp_core_solver_base:: +copy_m_ed(T * buffer) { + unsigned i = m_m; + while (i --) { + buffer[i] = m_ed[i]; + } +} + +template void lp_core_solver_base:: +restore_m_ed(T * buffer) { + unsigned i = m_m; + while (i --) { + m_ed[i] = buffer[i]; + } +} + +template bool lp_core_solver_base:: +A_mult_x_is_off() { + if (precise()) { + return false; + } + + T feps = convert_struct::convert(m_settings.refactor_tolerance); + X one = convert_struct::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 void lp_core_solver_base:: +calculate_pivot_row_of_B_1(unsigned pivot_row) { + unsigned i = m_m; + while (i--) { + m_pivot_row_of_B_1[i] = numeric_traits::zero(); + } + m_pivot_row_of_B_1[pivot_row] = numeric_traits::one(); + m_factorization->solve_yB(m_pivot_row_of_B_1); +} + +template void lp_core_solver_base:: +zero_pivot_row() { + for (unsigned j : m_pivot_row_index) + m_pivot_row[j] = numeric_traits::zero(); + m_pivot_row_index.clear(); +} + +template void lp_core_solver_base:: +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::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 void lp_core_solver_base:: +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 void lp_core_solver_base:: +print_statistics(X cost) { + std::cout << "cost = " << T_to_string(cost) << + ", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl; +} +template bool lp_core_solver_base:: +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 bool lp_core_solver_base:: +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 bool lp_core_solver_base:: +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 bool lp_core_solver_base:: +print_statistics_and_check_that_the_time_is_over(unsigned total_iterations) { + if (total_iterations % (numeric_traits::precise()? static_cast(m_settings.report_frequency/10) : m_settings.report_frequency) == 0) { + std::cout << "iterations = " << total_iterations << ", "; + if (time_is_over()) { + return true; + } + } + return false; +} + +template void lp_core_solver_base:: +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 bool lp_core_solver_base:: +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::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 bool lp_core_solver_base:: +d_is_not_negative(unsigned j) { + if (numeric_traits::precise()) { + return m_d[j] >= numeric_traits::zero(); + } + return m_d[j] > -T(0.00001); +} + +template bool lp_core_solver_base:: +d_is_not_positive(unsigned j) { + if (numeric_traits::precise()) { + return m_d[j] <= numeric_traits::zero(); + } + return m_d[j] < T(0.00001); +} + + +template bool lp_core_solver_base:: +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 void lp_core_solver_base:: +rs_minus_Anx(std::vector & 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 bool lp_core_solver_base:: +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 bool lp_core_solver_base:: +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 void lp_core_solver_base:: +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 bool lp_core_solver_base:: +basis_has_no_doubles() { + std::set bm; + for (unsigned i = 0; i < m_m; i++) { + bm.insert(m_basis[i]); + } + return bm.size() == m_m; +} + +template bool lp_core_solver_base:: +non_basis_has_no_doubles() { + std::set bm; + for (auto j : m_non_basic_columns) { + bm.insert(j); + } + return bm.size() == m_non_basic_columns.size(); +} + +template bool lp_core_solver_base:: +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 bool lp_core_solver_base:: +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 bool lp_core_solver_base:: +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 void lp_core_solver_base:: +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 void lp_core_solver_base:: +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 void lp_core_solver_base:: +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::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 void lp_core_solver_base:: +copy_rs_to_xB(std::vector & rs) { + unsigned j = m_m; + while (j--) { + m_x[m_basis[j]] = rs[j]; + } +} + +template std::string lp_core_solver_base:: +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 void lp_core_solver_base:: +copy_right_side(std::vector & rs) { + unsigned i = m_m; + while (i --) { + rs[i] = m_b[i]; + } +} + +template void lp_core_solver_base:: +add_delta_to_xB(std::vector & del) { + unsigned i = m_m; + while (i--) { + this->m_x[this->m_basis[i]] -= del[i]; + } +} + +template void lp_core_solver_base:: +find_error_in_BxB(std::vector& 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 void lp_core_solver_base:: +solve_Ax_eq_b() { + std::vector rs(m_m); + rs_minus_Anx(rs); + std::vector 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 void lp_core_solver_base:: +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 void lp_core_solver_base:: +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(); + break; + } + } +} +template void lp_core_solver_base:: +snap_xN_to_bounds() { + snap_non_basic_x_to_bound(); + solve_Ax_eq_b(); +} + +template void lp_core_solver_base:: +snap_xN_to_bounds_and_free_columns_to_zeroes() { + snap_non_basic_x_to_bound_and_free_to_zeroes(); + solve_Ax_eq_b(); +} + +template void lp_core_solver_base:: +init_reduced_costs_for_one_iteration() { + solve_yB(m_y); + fill_reduced_costs_from_m_y_by_rows(); +} + +template non_basic_column_value_position lp_core_solver_base:: +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(); + } +} +} diff --git a/src/util/lp/lp_core_solver_base.h b/src/util/lp/lp_core_solver_base.h index 5090d6288b..68847c43d2 100644 --- a/src/util/lp/lp_core_solver_base.h +++ b/src/util/lp/lp_core_solver_base.h @@ -10,34 +10,20 @@ #include #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 & basis, unsigned m, std::vector & 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 & basis, unsigned m, std::vector & basis_heading); -void init_non_basic_part_of_basis_heading(std::vector & basis_heading, std::vector & 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 & basis_heading, std::vector & non_basic_columns, unsigned n); void init_basis_heading_and_non_basic_columns_vector(std::vector & basis, unsigned m, std::vector & basis_heading, unsigned n, - std::vector & 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 & non_basic_columns); template // 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 & column_names, std::vector & column_types, std::vector & low_bound_values, - std::vector & 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()), - 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 & 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 & y){ - for (unsigned i = 0; i < m_m; i++) - y[i] = m_costs[m_basis[i]]; - } + void fill_cb(std::vector & y); - void solve_yB(std::vector & 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 & 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 a(m_m); - // m_A.copy_column_to_vector(entering, a); - // vector 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 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()) { - return false; - } - - T feps = convert_struct::convert(m_settings.refactor_tolerance); - X one = convert_struct::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::zero(); - } - m_pivot_row_of_B_1[pivot_row] = numeric_traits::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::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::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::precise()? static_cast(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::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::precise()) { - return m_d[j] >= numeric_traits::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::precise()) { - return m_d[j] <= numeric_traits::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 & 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 & 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 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 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::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 & rs) { - unsigned j = m_m; - while (j--) { - m_x[m_basis[j]] = rs[j]; - } - } + void copy_rs_to_xB(std::vector & 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 & rs) { - unsigned i = m_m; - while (i --) { - rs[i] = m_b[i]; - } - } + void copy_right_side(std::vector & rs); - void add_delta_to_xB(std::vector & del) { - unsigned i = m_m; - while (i--) { - this->m_x[this->m_basis[i]] -= del[i]; - } - } + void add_delta_to_xB(std::vector & del); - void find_error_in_BxB(std::vector& 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& rs); // recalculates the projection of x to B, such that Ax = b, whereab is the right side - void solve_Ax_eq_b() { - std::vector rs(m_m); - rs_minus_Anx(rs); - std::vector 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(); - 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); }; } diff --git a/src/util/lp/lp_core_solver_base_instances.cpp b/src/util/lp/lp_core_solver_base_instances.cpp new file mode 100644 index 0000000000..7273555d6c --- /dev/null +++ b/src/util/lp/lp_core_solver_base_instances.cpp @@ -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::A_mult_x_is_off(); +template bool lean::lp_core_solver_base::basis_heading_is_correct(); +template void lean::lp_core_solver_base::calculate_pivot_row_of_B_1(unsigned int); +template void lean::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(); +template bool lean::lp_core_solver_base::column_is_dual_feasible(unsigned int); +template void lean::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); +template bool lean::lp_core_solver_base::find_x_by_solving(); +template lean::non_basic_column_value_position lean::lp_core_solver_base::get_non_basic_column_value_position(unsigned int); +template void lean::lp_core_solver_base::init_reduced_costs_for_one_iteration(); +template lean::lp_core_solver_base::lp_core_solver_base(lean::static_matrix&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, lean::lp_settings&, std::unordered_map, std::equal_to, std::allocator > > const&, std::vector >&, std::vector >&, std::vector >&); + +template bool lean::lp_core_solver_base::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::restore_x(unsigned int, double const&); +template void lean::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); +template void lean::lp_core_solver_base::snap_xN_to_bounds_and_free_columns_to_zeroes(); +template void lean::lp_core_solver_base::solve_Ax_eq_b(); +template void lean::lp_core_solver_base::solve_Bd(unsigned int); +template void lean::lp_core_solver_base::solve_yB(std::vector >&); +template bool lean::lp_core_solver_base::update_basis_and_x(int, int, double const&); +template void lean::lp_core_solver_base::update_x(unsigned int, double); +template bool lean::lp_core_solver_base::A_mult_x_is_off(); +template bool lean::lp_core_solver_base::basis_heading_is_correct(); +template void lean::lp_core_solver_base::calculate_pivot_row_of_B_1(unsigned int); +template void lean::lp_core_solver_base::calculate_pivot_row_when_pivot_row_of_B1_is_ready(); +template bool lean::lp_core_solver_base::column_is_dual_feasible(unsigned int); +template void lean::lp_core_solver_base::fill_reduced_costs_from_m_y_by_rows(); +template bool lean::lp_core_solver_base::find_x_by_solving(); +template void lean::lp_core_solver_base::init_reduced_costs_for_one_iteration(); +template bool lean::lp_core_solver_base::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::restore_x(unsigned int, lean::mpq const&); +template void lean::lp_core_solver_base::set_non_basic_x_to_correct_bounds(); +template void lean::lp_core_solver_base::solve_Ax_eq_b(); +template void lean::lp_core_solver_base::solve_Bd(unsigned int); +template void lean::lp_core_solver_base::solve_yB(std::vector >&); +template bool lean::lp_core_solver_base::update_basis_and_x(int, int, lean::mpq const&); +template void lean::lp_core_solver_base::update_x(unsigned int, lean::mpq); +template void lean::lp_core_solver_base >::calculate_pivot_row_of_B_1(unsigned int); +template void lean::lp_core_solver_base >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(); +template void lean::lp_core_solver_base >::init(); +template void lean::lp_core_solver_base >::init_basis_heading(); +template void lean::lp_core_solver_base >::init_reduced_costs_for_one_iteration(); +template lean::lp_core_solver_base >::lp_core_solver_base(lean::static_matrix >&, std::vector, std::allocator > >&, std::vector >&, std::vector, std::allocator > >&, std::vector >&, lean::lp_settings&, std::unordered_map, std::equal_to, std::allocator > > const&, std::vector >&, std::vector, std::allocator > >&, std::vector, std::allocator > >&); +template bool lean::lp_core_solver_base >::print_statistics_with_cost_and_check_that_the_time_is_over(unsigned int, lean::numeric_pair); +template void lean::lp_core_solver_base >::snap_xN_to_bounds(); +template void lean::lp_core_solver_base >::solve_Bd(unsigned int); +template bool lean::lp_core_solver_base >::update_basis_and_x(int, int, lean::numeric_pair const&); +template void lean::lp_core_solver_base >::update_x(unsigned int, lean::numeric_pair); +template lean::lp_core_solver_base::lp_core_solver_base(lean::static_matrix&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, lean::lp_settings&, std::unordered_map, std::equal_to, std::allocator > > const&, std::vector >&, std::vector >&, std::vector >&); +template bool lean::lp_core_solver_base >::print_statistics_with_iterations_and_check_that_the_time_is_over(unsigned int); +template std::string lean::lp_core_solver_base::column_name(unsigned int) const; +template void lean::lp_core_solver_base::pretty_print(); +template void lean::lp_core_solver_base::restore_state(double*, double*); +template void lean::lp_core_solver_base::save_state(double*, double*); +template std::string lean::lp_core_solver_base::column_name(unsigned int) const; +template void lean::lp_core_solver_base::pretty_print(); +template void lean::lp_core_solver_base::restore_state(lean::mpq*, lean::mpq*); +template void lean::lp_core_solver_base::save_state(lean::mpq*, lean::mpq*); +template std::string lean::lp_core_solver_base >::column_name(unsigned int) const; +template void lean::lp_core_solver_base >::pretty_print(); +template void lean::lp_core_solver_base >::restore_state(lean::mpq*, lean::mpq*); +template void lean::lp_core_solver_base >::save_state(lean::mpq*, lean::mpq*); +template void lean::lp_core_solver_base >::solve_yB(std::vector >&); diff --git a/src/util/lp/lp_dual_core_solver.cpp b/src/util/lp/lp_dual_core_solver.cpp new file mode 100644 index 0000000000..a81398f03a --- /dev/null +++ b/src/util/lp/lp_dual_core_solver.cpp @@ -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 +lp_dual_core_solver::lp_dual_core_solver(static_matrix & A, + std::vector & can_enter_basis, + std::vector & b, // the right side std::vector + std::vector & x, // the number of elements in x needs to be at least as large as the number of columns in A + std::vector & basis, + std::vector & costs, + std::vector & column_type_array, + std::vector & low_bound_values, + std::vector & upper_bound_values, + lp_settings & settings, + std::unordered_map const & column_names): + lp_core_solver_base(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::precise()? numeric_traits::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 void lp_dual_core_solver::init_a_wave_by_zeros() { + unsigned j = this->m_m; + while (j--) { + m_a_wave[j] = numeric_traits::zero(); + } +} + +template void lp_dual_core_solver::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 void lp_dual_core_solver::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 void lp_dual_core_solver::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 bool lp_dual_core_solver:: 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 void lp_dual_core_solver::recalculate_xB_and_d() { + this->solve_Ax_eq_b(); + recalculate_d(); +} + +template void lp_dual_core_solver::recalculate_d() { + this->solve_yB(this->m_y); + this->fill_reduced_costs_from_m_y_by_rows(); +} + +template void lp_dual_core_solver::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 void lp_dual_core_solver::adjust_xb_for_changed_xn_and_init_betas() { + this->solve_Ax_eq_b(); + init_betas(); +} + +template void lp_dual_core_solver::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 bool lp_dual_core_solver:: 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 T lp_dual_core_solver::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 T lp_dual_core_solver::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 void lp_dual_core_solver::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 void lp_dual_core_solver::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 T lp_dual_core_solver::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::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::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::zero(); + break; + case free_column: + lean_assert(numeric_traits::is_zero(this->m_d[p])); + return numeric_traits::zero(); + default: + lean_unreachable(); + } +} + +template void lp_dual_core_solver::pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows) { + m_r = -1; + T steepest_edge_max = numeric_traits::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::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 bool lp_dual_core_solver:: 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 int lp_dual_core_solver:: 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 bool lp_dual_core_solver:: 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 void lp_dual_core_solver::fill_breakpoint_set() { + m_breakpoint_set.clear(); + for (unsigned j : non_basis()) { + if (can_be_breakpoint(j)) { + m_breakpoint_set.insert(j); + } + } +} + +template void lp_dual_core_solver::FTran() { + this->solve_Bd(m_q); +} + +template T lp_dual_core_solver::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 void lp_dual_core_solver::restore_d() { + std::cout << "restore_d" << std::endl; + this->m_d[m_p] = numeric_traits::zero(); + for (auto j : non_basis()) { + this->m_d[j] += m_theta_D * this->m_pivot_row[j]; + } +} + +template bool lp_dual_core_solver:: 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::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 void lp_dual_core_solver::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 void lp_dual_core_solver::update_betas() { // page 194 of Progress ... todo - once in a while betas have to be reinitialized + T one_over_arq = numeric_traits::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 void lp_dual_core_solver::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 void lp_dual_core_solver::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 void lp_dual_core_solver::snap_xN_to_bounds() { + for (auto j : this->non_basis()) { + snap_xN_column_to_bounds(j); + } +} + +template void lp_dual_core_solver::init_beta_precisely(unsigned i) { + std::vector vec(this->m_m, numeric_traits::zero()); + vec[i] = numeric_traits::one(); + this->m_factorization->solve_yB(vec); + T beta = numeric_traits::zero(); + for (T & v : vec) { + beta += v * v; + } + this->m_betas[i] =beta; +} + +template void lp_dual_core_solver::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 bool lp_dual_core_solver:: 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 void lp_dual_core_solver::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 bool lp_dual_core_solver:: 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 unsigned lp_dual_core_solver:: 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 bool lp_dual_core_solver:: delta_keeps_the_sign(int initial_delta_sign, const T & delta) { + if (numeric_traits::precise()) + return ((delta > numeric_traits::zero()) && (initial_delta_sign == 1)) || + ((delta < numeric_traits::zero()) && (initial_delta_sign == -1)); + + double del = numeric_traits::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 void lp_dual_core_solver::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 void lp_dual_core_solver::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 T lp_dual_core_solver::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 bool lp_dual_core_solver:: 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 T lp_dual_core_solver::calculate_harris_delta_on_breakpoint_set() { + bool first_time = true; + T ret = zero_of_type(); + 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::zero()) + m_harris_tolerance) / this->m_pivot_row[j]); + } else { + t = abs((std::min(this->m_d[j], numeric_traits::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 void lp_dual_core_solver::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::zero()) / this->m_pivot_row[j]) <= harris_delta){ + m_tight_set.insert(j); + } + } else { + if (abs(std::min(this->m_d[j], numeric_traits::zero() ) / this->m_pivot_row[j]) <= harris_delta){ + m_tight_set.insert(j); + } + } + } +} + +template void lp_dual_core_solver::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 void lp_dual_core_solver::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 void lp_dual_core_solver::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 bool lp_dual_core_solver:: 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::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 void lp_dual_core_solver::process_flipped() { + init_a_wave_by_zeros(); + for (auto j : m_flipped_boxed) { + update_a_wave(signed_span_of_boxed(j), j); + } +} +template void lp_dual_core_solver::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 void lp_dual_core_solver::calculate_beta_r_precisely() { + T t = numeric_traits::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 void lp_dual_core_solver::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 void lp_dual_core_solver::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 void lp_dual_core_solver::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); +} +} diff --git a/src/util/lp/lp_dual_core_solver.h b/src/util/lp/lp_dual_core_solver.h index 405be4b2ad..9f16010391 100644 --- a/src/util/lp/lp_dual_core_solver.h +++ b/src/util/lp/lp_dual_core_solver.h @@ -43,323 +43,49 @@ public: std::vector & low_bound_values, std::vector & upper_bound_values, lp_settings & settings, - std::unordered_map const & column_names): - lp_core_solver_base(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::precise()? numeric_traits::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 const & column_names); - void init_a_wave_by_zeros() { - unsigned j = this->m_m; - while (j--) { - m_a_wave[j] = numeric_traits::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 & 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::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::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::zero(); - break; - case free_column: - lean_assert(numeric_traits::is_zero(this->m_d[p])); - return numeric_traits::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::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::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::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::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::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 vec(this->m_m, numeric_traits::zero()); - vec[i] = numeric_traits::one(); - this->m_factorization->solve_yB(vec); - T beta = numeric_traits::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::precise()) - return ((delta > numeric_traits::zero()) && (initial_delta_sign == 1)) || - ((delta < numeric_traits::zero()) && (initial_delta_sign == -1)); + bool delta_keeps_the_sign(int initial_delta_sign, const T & delta); - double del = numeric_traits::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(); - 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::zero()) + m_harris_tolerance) / this->m_pivot_row[j]); - } else { - t = abs((std::min(this->m_d[j], numeric_traits::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::zero()) / this->m_pivot_row[j]) <= harris_delta){ - m_tight_set.insert(j); - } - } else { - if (abs(std::min(this->m_d[j], numeric_traits::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::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::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; } }; diff --git a/src/util/lp/lp_dual_core_solver_instances.cpp b/src/util/lp/lp_dual_core_solver_instances.cpp new file mode 100644 index 0000000000..46306fed00 --- /dev/null +++ b/src/util/lp/lp_dual_core_solver_instances.cpp @@ -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::lp_dual_core_solver(lean::static_matrix&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, lean::lp_settings&, std::unordered_map, std::equal_to, std::allocator > > const&); +template void lean::lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible(); +template void lean::lp_dual_core_solver::solve(); +template lean::lp_dual_core_solver::lp_dual_core_solver(lean::static_matrix&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, lean::lp_settings&, std::unordered_map, std::equal_to, std::allocator > > const&); +template void lean::lp_dual_core_solver::start_with_initial_basis_and_make_it_dual_feasible(); +template void lean::lp_dual_core_solver::solve(); +template void lean::lp_dual_core_solver::restore_non_basis(); +template void lean::lp_dual_core_solver::restore_non_basis(); diff --git a/src/util/lp/lp_dual_simplex.cpp b/src/util/lp/lp_dual_simplex.cpp new file mode 100644 index 0000000000..3ea5828f70 --- /dev/null +++ b/src/util/lp/lp_dual_simplex.cpp @@ -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 void lp_dual_simplex:: 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 void lp_dual_simplex:: 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::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::zero(); + m_column_types_of_core_solver[j] = fixed; + m_can_enter_basis[j] = false; + break; + default: + lean_unreachable(); + } +} + +template void lp_dual_simplex:: fix_structural_for_stage2(unsigned j) { + column_info * 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::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::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 void lp_dual_simplex:: 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 void lp_dual_simplex:: restore_right_sides() { + unsigned i = this->m_A->row_count(); + while (i--) { + this->m_b[i] = m_b_copy[i]; + } +} + +template void lp_dual_simplex:: 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 void lp_dual_simplex:: fill_x_with_zeros() { + unsigned j = this->m_A->column_count(); + while (j--) { + this->m_x[j] = numeric_traits::zero(); + } +} + +template void lp_dual_simplex:: stage1() { + lean_assert(m_core_solver == nullptr); + this->m_x.resize(this->m_A->column_count(), numeric_traits::zero()); + this->print_statistics_on_A(); + m_core_solver = new lp_dual_core_solver( + *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 void lp_dual_simplex:: stage2() { + std::cout << "starting stage2" << std::endl; + unmark_boxed_and_fixed_columns_and_fix_structural_costs(); + restore_right_sides(); + solve_for_stage2(); +} + +template void lp_dual_simplex:: 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 column_type lp_dual_simplex:: 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 void lp_dual_simplex:: 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 * 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::zero(); + this->m_upper_bounds[j] =numeric_traits::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::zero(); + this->m_upper_bounds[j] = this->m_low_bounds[j] = numeric_traits::zero(); // is it needed? + break; + default: + lean_unreachable(); + } + m_column_types_of_core_solver[j] = boxed; +} + +template void lp_dual_simplex:: 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::zero(); + this->m_upper_bounds[j] = numeric_traits::one(); + } else { + m_column_types_of_core_solver[j] = fixed; + this->m_low_bounds[j] = numeric_traits::zero(); + this->m_upper_bounds[j] = numeric_traits::zero(); + } +} + +template void lp_dual_simplex:: 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 void lp_dual_simplex:: 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::zero(); + (*this->m_A)(row, artificial) = numeric_traits::one(); + artificial++; + break; + + case Greater_or_equal: + set_type_for_logical(slack_var, low_bound); + (*this->m_A)(row, slack_var) = - numeric_traits::one(); + if (rs > 0) { + // adding one artificial + set_type_for_logical(artificial, fixed); + (*this->m_A)(row, artificial) = numeric_traits::one(); + this->m_basis[row] = artificial; + this->m_costs[artificial] = numeric_traits::zero(); + artificial++; + } else { + // we can put a slack_var into the basis, and atemplate void lp_dual_simplex:: adding an artificial variable + this->m_basis[row] = slack_var; + this->m_costs[slack_var] = numeric_traits::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::one(); + if (rs < 0) { + // adding one artificial + set_type_for_logical(artificial, fixed); + (*this->m_A)(row, artificial) = - numeric_traits::one(); + this->m_basis[row] = artificial; + this->m_costs[artificial] = numeric_traits::zero(); + artificial++; + } else { + // we can put slack_var into the basis, and atemplate void lp_dual_simplex:: adding an artificial variable + this->m_basis[row] = slack_var; + this->m_costs[slack_var] = numeric_traits::zero(); + } + slack_var++; + break; + } +} + +template void lp_dual_simplex:: 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 void lp_dual_simplex:: 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::zero(); // preparing for the first stage + } +} + +template void lp_dual_simplex:: 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 T lp_dual_simplex:: get_current_cost() const { + T ret = numeric_traits::zero(); + for (auto it : this->m_columns) { + ret += this->get_column_cost_value(it.first, it.second); + } + return -ret; // we flip costs for now +} +} diff --git a/src/util/lp/lp_dual_simplex.h b/src/util/lp/lp_dual_simplex.h index bdebdbd7ee..dbec4c3014 100644 --- a/src/util/lp/lp_dual_simplex.h +++ b/src/util/lp/lp_dual_simplex.h @@ -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::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::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 * 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::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::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::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::zero()); - this->print_statistics_on_A(); - m_core_solver = new lp_dual_core_solver( - *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 * 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::zero(); - this->m_upper_bounds[j] =numeric_traits::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::zero(); - this->m_upper_bounds[j] = this->m_low_bounds[j] = numeric_traits::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::zero(); - this->m_upper_bounds[j] = numeric_traits::one(); - } else { - m_column_types_of_core_solver[j] = fixed; - this->m_low_bounds[j] = numeric_traits::zero(); - this->m_upper_bounds[j] = numeric_traits::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::zero(); - (*this->m_A)(row, artificial) = numeric_traits::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::one(); - if (rs > 0) { - // adding one artificial - set_type_for_logical(artificial, fixed); - (*this->m_A)(row, artificial) = numeric_traits::one(); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::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::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::one(); - if (rs < 0) { - // adding one artificial - set_type_for_logical(artificial, fixed); - (*this->m_A)(row, artificial) = - numeric_traits::one(); - this->m_basis[row] = artificial; - this->m_costs[artificial] = numeric_traits::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::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::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::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; }; } diff --git a/src/util/lp/lp_dual_simplex_instances.cpp b/src/util/lp/lp_dual_simplex_instances.cpp new file mode 100644 index 0000000000..7f8ab2172b --- /dev/null +++ b/src/util/lp/lp_dual_simplex_instances.cpp @@ -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::get_current_cost() const; +template void lean::lp_dual_simplex::find_maximal_solution(); +template double lean::lp_dual_simplex::get_current_cost() const; +template void lean::lp_dual_simplex::find_maximal_solution(); diff --git a/src/util/lp/lp_primal_core_solver.cpp b/src/util/lp/lp_primal_core_solver.cpp new file mode 100644 index 0000000000..e4de5d59e8 --- /dev/null +++ b/src/util/lp/lp_primal_core_solver.cpp @@ -0,0 +1,1046 @@ +/* + 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_core_solver.h" +namespace lean { + +// This core solver solves (Ax=b, low_bound_values \leq x \leq upper_bound_values, maximize costs*x ) +// The right side b is given implicitly by x and the basis +template +int lp_primal_core_solver::choose_entering_column(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) + lean_assert(number_of_benefitial_columns_to_go_over); + unsigned offset_in_nb = my_random() % this->m_non_basic_columns.size(); + unsigned initial_offset_in_non_basis = offset_in_nb; + T steepest_edge = zero_of_type(); + int entering = -1; + do { + unsigned j = this->m_factorization->m_non_basic_columns[offset_in_nb]; + push_forward_offset_in_non_basis(offset_in_nb); + if (m_forbidden_enterings.find(j) != m_forbidden_enterings.end()) { + continue; + } + T dj = this->m_d[j]; + switch (this->m_column_type[j]) { + case fixed: continue; + case free_column: + if (dj > m_epsilon_of_reduced_cost || dj < -m_epsilon_of_reduced_cost) break; + continue; + case low_bound: + if (dj > m_epsilon_of_reduced_cost) break; + continue; + case upper_bound: + if (dj < -m_epsilon_of_reduced_cost) break; + continue; + case boxed: + if (dj > m_epsilon_of_reduced_cost) { + if (this->m_x[j] < this->m_upper_bound_values[j] - this->bound_span(j)/2) + break; + continue; + } else if (dj < - m_epsilon_of_reduced_cost) { + if (this->m_x[j] > this->m_low_bound_values[j] + this->bound_span(j)/2) break; + } + continue; + default: + lean_unreachable(); + } + + // if we are here then j is a candidate to enter the basis + T t = dj * dj / this->m_column_norms[j]; + if (t > steepest_edge) { + steepest_edge = t; + entering = j; + if (number_of_benefitial_columns_to_go_over) + number_of_benefitial_columns_to_go_over--; + } + } while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb); + if (entering != -1) + m_sign_of_entering_delta = this->m_d[entering] > 0? 1 : -1; + return entering; +} + + +template int +lp_primal_core_solver:: find_leaving_and_t_precisely(unsigned entering, X & t){ + int leaving = -1; + T abs_of_d_of_leaving = numeric_traits::zero(); + for (unsigned i = 0; i < this->m_m; i++) { + if (is_zero(this->m_ed[i])) continue; + unsigned j = this->m_basis[i]; + get_bound_on_variable_and_update_leaving_precisely(j, leaving, - this->m_ed[i] * m_sign_of_entering_delta, t, abs_of_d_of_leaving); + if (leaving != -1 && is_zero(t)) { + return leaving; + } + } + + get_bound_on_variable_and_update_leaving_precisely(entering, leaving, T(m_sign_of_entering_delta), t, abs_of_d_of_leaving); + return leaving; +} + +template X lp_primal_core_solver:: get_harris_theta() { + X theta = positive_infinity(); + unsigned i = this->m_m; + while (i--) { + if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; + limit_theta_on_basis_column(this->m_basis[i], - this->m_ed[i] * m_sign_of_entering_delta, theta); + if (is_zero(theta)) break; + } + return theta; +} + + +template int lp_primal_core_solver:: find_leaving_on_harris_theta(X const & harris_theta, X & t) { + int leaving = -1; + T pivot_abs_max; + // we know already that there is no bound flip on entering + // we also know that harris_theta is limited, so we will find a leaving + zero_harris_eps(); + unsigned i = my_random() % this->m_m; + unsigned initial_i = i; + int column_with_non_zero_cost = -1; + do { + if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) { + if (++i == this->m_m) + i = 0; + continue; + } + X ratio = positive_infinity(); + unsigned j = this->m_basis[i]; + limit_theta_on_basis_column(j, - this->m_ed[i] * m_sign_of_entering_delta, ratio); + if (ratio <= harris_theta) { + if (!m_recalc_reduced_costs && get_current_x_is_infeasible()) { // when we have made several basic variables feasible we need to recalculate the costs and the reduced costs: here we are catching this case + if (!is_zero(this->m_costs[j])) { + if (column_with_non_zero_cost != -1) + m_recalc_reduced_costs = true; + else + column_with_non_zero_cost = j; + } + } + if (leaving == -1 || abs(this->m_ed[i]) > pivot_abs_max) { + t = ratio; + leaving = j; + pivot_abs_max = abs(this->m_ed[i]); + } + } + if (++i == this->m_m) i = 0; + } while ( i != initial_i); + restore_harris_eps(); + return leaving; +} + + +template bool lp_primal_core_solver:: try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t) { + if (this->m_column_type[entering] != boxed) + return false; + + if (m_sign_of_entering_delta > 0) { + t = this->m_upper_bound_values[entering] - this->m_x[entering]; + if (t <= theta){ + lean_assert(t >= zero_of_type()); + return true; + } + } else { // m_sign_of_entering_delta == -1 + t = this->m_x[entering] - this->m_low_bound_values[entering]; + if (t <= theta) { + lean_assert(t >= zero_of_type()); + return true; + } + } + + return false; +} + +template int lp_primal_core_solver:: find_leaving_and_t(unsigned entering, X & t){ + if (numeric_traits::precise()) return find_leaving_and_t_precisely(entering, t); + X theta = get_harris_theta(); + lean_assert(theta >= zero_of_type()); + if (try_jump_to_another_bound_on_entering(entering, theta, t)) return entering; + if (theta == positive_infinity()) return -1; // unlimited + return find_leaving_on_harris_theta(theta, t); +} + + +// m is the multiplier. updating t in a way that holds the following +// x[j] + t * m >= m_low_bound_values[j] ( if m < 0 ) +// or +// x[j] + t * m <= this->m_upper_bound_values[j] ( if m > 0) +template void lp_primal_core_solver:: get_bound_on_variable_and_update_leaving_precisely(unsigned j, int & leaving, T m, X & t, T & abs_of_d_of_leaving) { + lean_assert(leaving == -1 || t > zero_of_type()); + if (m > 0) { + if (this->m_column_type[j] != boxed) { + return; + } + X tt = (this->m_upper_bound_values[j] - this->m_x[j]) / m; + if (leaving == -1 || tt < t || (tt == t && m > abs_of_d_of_leaving)) { + t = tt; + leaving = j; + abs_of_d_of_leaving = m; + if (t < zero_of_type()) + t = zero_of_type(); + } + } else if (m < 0){ + if (this->m_column_type[j] == free_column) { + return; + } + X tt = (- this->m_x[j]) / m; + if (leaving == -1 || tt < t || (tt == t && - m > abs_of_d_of_leaving)) { + t = tt; + leaving = j; + abs_of_d_of_leaving = - m; + if (t < zero_of_type()) + t = zero_of_type(); + } + } +} + +template X lp_primal_core_solver:: get_max_bound(std::vector & b) { + X ret = zero_of_type(); + for (auto & v : b) { + X a = abs(v); + if (a > ret) ret = a; + } + return ret; +} + +// stage1 constructor +template lp_primal_core_solver:: lp_primal_core_solver(static_matrix & A, + std::vector & b, // the right side vector + std::vector & x, // the number of elements in x needs to be at least as large as the number of columns in A + std::vector & basis, + std::vector & costs, + std::vector & column_type_array, + std::vector & low_bound_values, + std::vector & upper_bound_values, + lp_settings & settings, + std::unordered_map const & column_names): + lp_core_solver_base(A, b, + basis, + x, + costs, + settings, + column_names, + column_type_array, + low_bound_values, + upper_bound_values), + m_beta(A.row_count()) { + this->m_status = UNKNOWN; + this->m_column_norm_update_counter = settings.column_norms_update_frequency; +} + +// constructor +template lp_primal_core_solver:: +lp_primal_core_solver(static_matrix & A, + std::vector & b, // the right side vector + std::vector & x, // the number of elements in x needs to be at least as large as the number of columns in A + std::vector & basis, + std::vector & costs, + std::vector & column_type_array, + std::vector & upper_bound_values, + lp_settings & settings, + std::unordered_map const & column_names): + lp_core_solver_base(A, b, + basis, + x, + costs, + settings, + column_names, + column_type_array, + m_low_bound_values_dummy, + upper_bound_values), + m_beta(A.row_count()) { + m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); + lean_assert(initial_x_is_correct()); + m_low_bound_values_dummy.resize(A.column_count(), zero_of_type()); + m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); + this->m_column_norm_update_counter = settings.column_norms_update_frequency; +#ifdef LEAN_DEBUG + // check_correctness(); +#endif +} + +template void lp_primal_core_solver:: init_lu() { + 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_refactor_counter = 0; +} + +template bool lp_primal_core_solver:: initial_x_is_correct() { + std::set basis_set; + for (int i = 0; i < this->m_A.row_count(); i++) { + basis_set.insert(this->m_basis[i]); + } + for (int j = 0; j < this->m_n; j++) { + if (column_has_low_bound(j) && this->m_x[j] < numeric_traits::zero()) { + std::cout << "low bound for variable " << j << " does not hold: this->m_x[" << j << "] = " << this->m_x[j] << " is negative " << std::endl; + return false; + } + + if (column_has_upper_bound(j) && this->m_x[j] > this->m_upper_bound_values[j]) { + std::cout << "upper bound for " << j << " does not hold: " << this->m_upper_bound_values[j] << ">" << this->m_x[j] << std::endl; + return false; + } + + if (basis_set.find(j) != basis_set.end()) continue; + if (this->m_column_type[j] == low_bound) { + if (numeric_traits::zero() != this->m_x[j]) { + std::cout << "only low bound is set for " << j << " but low bound value " << numeric_traits::zero() << " is not equal to " << this->m_x[j] << std::endl; + return false; + } + } + if (this->m_column_type[j] == boxed) { + if (this->m_upper_bound_values[j] != this->m_x[j] && !numeric_traits::is_zero(this->m_x[j])) { + return false; + } + } + } + return true; +} + +#ifdef LEAN_DEBUG +template void lp_primal_core_solver:: check_Ax_equal_b() { + dense_matrix d(this->m_A); + T * ls = d.apply_from_left_with_different_dims(this->m_x); + lean_assert(vectors_are_equal(ls, this->m_b, this->m_m)); + delete [] ls; +} +template void lp_primal_core_solver:: check_the_bounds() { + for (unsigned i = 0; i < this->m_n; i++) { + check_bound(i); + } +} + +template void lp_primal_core_solver:: check_bound(unsigned i) { + lean_assert (!(column_has_low_bound(i) && (numeric_traits::zero() > this->m_x[i]))); + lean_assert (!(column_has_upper_bound(i) && (this->m_upper_bound_values[i] < this->m_x[i]))); +} + +template void lp_primal_core_solver:: check_correctness() { + check_the_bounds(); + check_Ax_equal_b(); +} +#endif + +// from page 183 of Istvan Maros's book +// the basis structures have not changed yet +template void lp_primal_core_solver:: update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving) { + // the basis heading has changed already +#ifdef LEAN_DEBUG + auto & basis_heading = this->m_factorization->m_basis_heading; + lean_assert(basis_heading[entering] >= 0 && basis_heading[entering] < this->m_ed.size()); + lean_assert(basis_heading[leaving] < 0); +#endif + T pivot = this->m_pivot_row[entering]; + T dq = this->m_d[entering]/pivot; + for (auto j : this->m_pivot_row_index) { + // for (auto j : this->m_non_basic_columns) + if (this->m_basis_heading[j] >= 0) continue; + if (j != leaving) + this->m_d[j] -= dq * this->m_pivot_row[j]; + } + this->m_d[leaving] = -dq; + if (get_current_x_is_infeasible()) { + this->m_d[leaving] -= this->m_costs[leaving]; + this->m_costs[leaving] = zero_of_type(); + } + this->m_d[entering] = numeric_traits::zero(); +} + +// return 0 if the reduced cost at entering is close enough to the refreshed +// 1 if it is way off, and 2 if it is unprofitable +template int lp_primal_core_solver:: refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) { + T reduced_at_entering_was = this->m_d[entering]; // can benefit from going over non-zeros of m_ed + lean_assert(abs(reduced_at_entering_was) > m_epsilon_of_reduced_cost); + T refreshed_cost = this->m_costs[entering]; + unsigned i = this->m_m; + while (i--) refreshed_cost -= this->m_costs[this->m_basis[i]] * this->m_ed[i]; + this->m_d[entering] = refreshed_cost; + T delta = abs(reduced_at_entering_was - refreshed_cost); + if (delta * 2 > abs(reduced_at_entering_was)) { + this->m_status = UNSTABLE; + if (reduced_at_entering_was > m_epsilon_of_reduced_cost) { + if (refreshed_cost <= zero_of_type()) + return 2; // abort entering + } else { + if (refreshed_cost > -m_epsilon_of_reduced_cost) + return 2; // abort entiring + } + return 1; // go on with this entering + } else { + if (reduced_at_entering_was > m_epsilon_of_reduced_cost) { + if (refreshed_cost <= zero_of_type()) + return 2; // abort entering + } else { + if (refreshed_cost > -m_epsilon_of_reduced_cost) + return 2; // abort entiring + } + } + return 0; +} + +template void lp_primal_core_solver:: normalize_costs_and_backup_costs() { + T cost_max = std::max(max_abs_in_vector(this->m_costs), T(1)); + lean_assert(m_costs_backup.size() == 0); + for (unsigned j = 0; j < this->m_costs.size(); j++) + m_costs_backup.push_back(this->m_costs[j] /= cost_max); + m_using_inf_costs = false; +} + +template void lp_primal_core_solver:: init_run() { + this->m_start_time = get_millisecond_count(); + this->m_total_iterations = 0; + this->m_iters_with_no_cost_growing = 0; + normalize_costs_and_backup_costs(); + set_current_x_is_feasible(); + init_reduced_costs(); +} + +template int lp_primal_core_solver:: pivots_in_column_and_row_are_different(int entering, int leaving) const { + const T & column_p = this->m_ed[this->m_basis_heading[leaving]]; + const T & row_p = this->m_pivot_row[entering]; + if (is_zero(column_p) || is_zero(row_p)) return true; // pivots cannot be zero + // the pivots have to have the same sign + if (column_p < 0) { + if (row_p > 0) + return 2; + } else { // column_p > 0 + if (row_p < 0) + return 2; + } + T diff_normalized = abs((column_p - row_p) / (numeric_traits::one() + abs(row_p))); + if ( !this->m_settings.abs_val_is_smaller_than_harris_tolerance(diff_normalized / T(10))) + return 1; + return 0; +} + +template void lp_primal_core_solver:: calc_working_vector_beta_for_column_norms(){ + unsigned i = this->m_m; + while (i--) + m_beta[i] = this->m_ed[i]; + this->m_factorization->solve_yB(m_beta); +} + +template void lp_primal_core_solver:: advance_on_entering_and_leaving(int entering, int leaving, X & t) { + lean_assert(t >= zero_of_type()); + lean_assert(leaving >= 0 && entering >= 0); + lean_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes + if (entering == leaving) { + lean_assert(!this->A_mult_x_is_off() ); + this->update_x(entering, t * m_sign_of_entering_delta); + if (this->A_mult_x_is_off() && !this->find_x_by_solving()) { + init_lu(); + if (!this->find_x_by_solving()) { + this->restore_x(entering, t * m_sign_of_entering_delta); + m_forbidden_enterings.insert(entering); + this->m_iters_with_no_cost_growing++; + std::cout << "failing in advance_on_entering_and_leaving for entering == leaving = " << leaving << std::endl; + return; + } + } + set_current_x_is_feasible(); + if (need_to_switch_costs()) + init_reduced_costs(); + this->m_iters_with_no_cost_growing = 0; + return; + } + unsigned pivot_row = this->m_factorization->basis_heading(leaving); + this->calculate_pivot_row_of_B_1(pivot_row); + this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(); + int pivot_compare_result = pivots_in_column_and_row_are_different(entering, leaving); + if (!pivot_compare_result){;} + else if (pivot_compare_result == 2) { // the sign is changed, cannot continue + m_forbidden_enterings.insert(entering); + this->m_status = UNSTABLE; + this->m_iters_with_no_cost_growing++; + return; + } else { + lean_assert(pivot_compare_result == 1); + init_lu(); + } + calc_working_vector_beta_for_column_norms(); + if (!this->update_basis_and_x(entering, leaving, t * m_sign_of_entering_delta)) { + if (this->m_status == FLOATING_POINT_ERROR) + return; + set_current_x_is_feasible(); + init_reduced_costs(); + m_forbidden_enterings.insert(entering); + return; + } + if (!is_zero(t)) { + this->m_iters_with_no_cost_growing = 0; + set_current_x_is_feasible(); + } + update_or_init_column_norms(entering, leaving); + if (need_to_switch_costs() || m_recalc_reduced_costs) + init_reduced_costs(); + else + update_reduced_costs_from_pivot_row(entering, leaving); + lean_assert(!need_to_switch_costs()); + m_forbidden_enterings.clear(); +} + +// void recalc_reduced_costs() { +// if (current_x_is_feasible()) +// init_infeasibility_costs(); +// this->init_reduced_costs_for_one_iteration(); +// } + +template void lp_primal_core_solver:: advance_on_entering(int entering) { + lean_assert(entering > -1); + this->solve_Bd(entering); + int refresh_result = refresh_reduced_cost_at_entering_and_check_that_it_is_off(entering); + if (refresh_result) { + init_lu(); + init_reduced_costs(); + if (refresh_result == 2) { + this->m_iters_with_no_cost_growing++; + return; + } + } + X t; + int leaving = find_leaving_and_t(entering, t); + if (leaving == -1){ + if (get_current_x_is_infeasible()) { + if (this->m_status == UNSTABLE) { + std::cout << "setting status to FLOATING_POINT_ERROR" << std::endl; + this->m_status = FLOATING_POINT_ERROR; + return; + } + init_reduced_costs(); + this->m_status = UNSTABLE; + return; + } + if (this->m_status == TENTATIVE_UNBOUNDED) { + this->m_status = UNBOUNDED; + } else { + this->m_status = TENTATIVE_UNBOUNDED; + } + return; + } + advance_on_entering_and_leaving(entering, leaving, t); +} + +template void lp_primal_core_solver:: push_forward_offset_in_non_basis(unsigned & offset_in_nb) { + if (++offset_in_nb == this->m_non_basic_columns.size()) + offset_in_nb = 0; +} + +template unsigned lp_primal_core_solver:: get_number_of_non_basic_column_to_try_for_enter() { + unsigned ret = this->m_factorization->m_non_basic_columns.size(); + if (this->m_status == TENTATIVE_UNBOUNDED) + return ret; // we really need to find entering with a large reduced cost + if (ret > 300) { + ret = (unsigned)(ret * this->m_settings.percent_of_entering_to_check / 100); + } + return std::max(static_cast(my_random() % ret), 1u); +} + +template void lp_primal_core_solver:: print_column_norms() { + std::cout << " column norms " << std::endl; + for (unsigned j = 0; j < this->m_n; j++) { + std::cout << this->m_column_norms[j] << " "; + } + std::cout << std::endl; + std::cout << std::endl; +} + +// returns the number of iterations +template unsigned lp_primal_core_solver:: solve() { + init_run(); + lean_assert(!this->A_mult_x_is_off()); + do { + if (this->m_total_iterations % this->m_settings.report_frequency == 0) { + std::ostringstream string_stream; + string_stream << (m_using_inf_costs? "stage 1" : "stage 2"); + std::string stream_string = string_stream.str(); + if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(stream_string, this->m_total_iterations)) { + this->m_status = lp_status::TIME_EXHAUSTED; + return this->m_total_iterations; + } + } + lean_assert(m_current_x_is_feasible == calc_current_x_is_feasible()); + one_iteration(); + switch (this->m_status) { + case OPTIMAL: // double check that we are at optimum + case INFEASIBLE: + m_forbidden_enterings.clear(); + init_lu(); + lean_assert(this->m_factorization->get_status() == LU_status::OK); + set_current_x_is_feasible(); + init_reduced_costs(); + if (choose_entering_column(1) == -1) { + decide_on_status_when_cannot_find_entering(); + break; + } + this->m_status = UNKNOWN; + break; + case TENTATIVE_UNBOUNDED: + m_forbidden_enterings.clear(); + init_lu(); + lean_assert(this->m_factorization->get_status() == LU_status::OK); + init_reduced_costs(); + break; + case UNBOUNDED: + break; + + case UNSTABLE: + // m_forbidden_enterings.clear(); + init_lu(); + init_reduced_costs(); + this->m_status = UNKNOWN; + break; + + default: + break; // do nothing + } + } while (this->m_status != FLOATING_POINT_ERROR && this->m_status != UNBOUNDED + && + this->m_status != OPTIMAL + && + this->m_status != INFEASIBLE + && + 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 + && + !(m_current_x_is_feasible && m_exit_on_feasible_solution)); + return this->m_total_iterations; +} + +template void lp_primal_core_solver:: delete_factorization() { + if (this->m_factorization != nullptr) { + delete this->m_factorization; + this->m_factorization = nullptr; + } +} + +// according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" +template void lp_primal_core_solver:: init_column_norms() { + m_column_norm_update_counter = 0; + for (unsigned j : this->m_non_basic_columns) + this->m_column_norms[j] = 1; +} + +// debug only +template T lp_primal_core_solver:: calculate_column_norm_exactly(unsigned j) { + indexed_vector w(this->m_m); + this->m_A.copy_column_to_vector(j, w); + std::vector d(this->m_m); + this->m_factorization->solve_Bd_when_w_is_ready(d, w); + T ret = zero_of_type(); + for (auto v : d) + ret += v*v; + return ret+1; +} + +template void lp_primal_core_solver:: update_or_init_column_norms(unsigned entering, unsigned leaving) { + if (++m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) { + init_column_norms(); + } else { + update_column_norms(entering, leaving); + } +} + +// following Swietanowski - A new steepest ... +template void lp_primal_core_solver:: update_column_norms(unsigned entering, unsigned leaving) { + T pivot = this->m_pivot_row[entering]; + T g_ent = std::max(calculate_norm_of_entering_exactly() / pivot /pivot, T(0.000001)); + this->m_column_norms[leaving] = g_ent; + + for (unsigned j : this->m_pivot_row_index) { + if (j == leaving) + continue; + const T & t = this->m_pivot_row[j]; + T s = this->m_A.dot_product_with_column(m_beta, j); + T k = -2 / pivot; + T tp = t/pivot; + if (this->m_column_type[j] == fixed) { + this->m_column_norms[j] = T(1); // We would like to kick out fixed vars from the basis, so making the norm small increases this chance since the steepest edge expression will be large + } else { + this->m_column_norms[j] = std::max(this->m_column_norms[j] + t * (t * g_ent + k * s), // see Achim Koberstein dissertation (8.1) + 1 + tp * tp); + } + } +} + +template T lp_primal_core_solver:: calculate_norm_of_entering_exactly() { + T r = numeric_traits::one(); + unsigned i = this->m_m; + while (i--) { + T t = this->m_ed[i]; + r += t * t; + } + return r; +} + +// calling it stage1 is too cryptic +template void lp_primal_core_solver:: find_feasible_solution() { + m_exit_on_feasible_solution = true; + this->snap_xN_to_bounds_and_free_columns_to_zeroes(); + solve(); +} + +template void lp_primal_core_solver:: calculate_infeasibility() { + m_infeasibility = zero_of_type(); + unsigned i = this->m_m; + while (i--) { + add_column_infeasibility(this->m_basis[i]); + } +} + +template void lp_primal_core_solver:: add_column_infeasibility(unsigned j) { + const X & x = this->m_x[j]; + switch (this->m_column_type[j]) { + case fixed: + case boxed: + if (this->above_bound(x, this->m_upper_bound_values[j])) { + m_infeasibility -= x - this->m_upper_bound_values[j]; + } else if (this->below_bound(x, this->m_low_bound_values[j])) { + m_infeasibility -= this->m_low_bound_values[j] - x; + } + break; + case low_bound: + if (this->below_bound(x, this->m_low_bound_values[j])) { + m_infeasibility -= this->m_low_bound_values[j] - x; + } + break; + case upper_bound: + if (this->above_bound(x, this->m_upper_bound_values[j])) { + m_infeasibility -= x - this->m_upper_bound_values[j]; + } + break; + case free_column: + break; + } +} + +template void lp_primal_core_solver:: one_iteration() { + this->m_total_iterations++; + unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); + int entering = choose_entering_column(number_of_benefitial_columns_to_go_over); + if (entering == -1) + decide_on_status_when_cannot_find_entering(); + else + advance_on_entering(entering); +} + +template void lp_primal_core_solver:: 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 void lp_primal_core_solver:: 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 void lp_primal_core_solver:: clear_breakpoints() { + m_breakpoints.clear(); + m_breakpoint_indices_queue.clear();// m_breakpoints.clear(); +} + +template void lp_primal_core_solver:: change_slope_on_breakpoint(unsigned entering, breakpoint * 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::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 void lp_primal_core_solver:: advance_on_sorted_breakpoints(unsigned entering) { + T slope_at_entering = this->m_d[entering]; + breakpoint * last_bp = nullptr; + while (m_breakpoint_indices_queue.is_empty() == false) { + unsigned bi = m_breakpoint_indices_queue.dequeue(); + breakpoint *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::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 void lp_primal_core_solver:: 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 bool lp_primal_core_solver:: column_is_feasible(unsigned j) const { + const X& x = this->m_x[j]; + switch (this->m_column_type[j]) { + case fixed: + case boxed: + if (this->above_bound(x, this->m_upper_bound_values[j])) { + return false; + } else if (this->below_bound(x, this->m_low_bound_values[j])) { + return false; + } else { + return true; + } + break; + case low_bound: + if (this->below_bound(x, this->m_low_bound_values[j])) { + return false; + } else { + return true; + } + break; + case upper_bound: + if (this->above_bound(x, this->m_upper_bound_values[j])) { + return false; + } else { + return true; + } + break; + case free_column: + return true; + break; + default: + lean_unreachable(); + } +} + +template bool lp_primal_core_solver:: calc_current_x_is_feasible() const { + unsigned i = this->m_m; + while (i--) { + if (!column_is_feasible(this->m_basis[i])) + return false; + } + return true; +} + + +template bool lp_primal_core_solver:: can_enter_basis(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::zero(); + case upper_bound: + lean_assert(this->x_is_at_upper_bound(j)); + return this->m_d[j] < numeric_traits::zero(); + case fixed: + return false; + case boxed: + { + bool at_low_bound = this->x_is_at_low_bound(j); + lean_assert(at_low_bound || this->x_is_at_upper_bound(j)); + return at_low_bound ? this->m_d[j] > numeric_traits::zero() : this->m_d[j] < numeric_traits::zero(); + } + case free_column: + return !numeric_traits::is_zero(this->m_d[j]); + default: + return false; + } + return false; +} + + + +template bool lp_primal_core_solver:: done() { + if (this->m_status == OPTIMAL ||this->m_status == FLOATING_POINT_ERROR) return true; + if (this->m_status == INFEASIBLE) { + 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 void lp_primal_core_solver:: init_infeasibility_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(); + for (unsigned j = this->m_n; j--;) + init_infeasibility_cost_for_column(j); + // lean_assert(this->m_total_iterations == 0 || (inf <= m_infeasibility + convert_struct::convert(this->m_settings.tolerance_for_artificials))); + // if (inf == m_infeasibility) + // this->m_iters_with_no_cost_growing++; + m_using_inf_costs = true; +} + +template void lp_primal_core_solver:: print_column(unsigned j) { + std::cout << this->column_name(j) << " " << j << " " << column_type_to_string(this->m_column_type[j]) << " x = " << this->m_x[j] << " " << "c = " << this->m_costs[j];; + switch (this->m_column_type[j]) { + case fixed: + case boxed: + std::cout << "( " << this->m_low_bound_values[j] << " " << this->m_x[j] << " " << this->m_upper_bound_values[j] << ")" << std::endl; + break; + case upper_bound: + std::cout << "( _" << this->m_x[j] << " " << this->m_upper_bound_values[j] << ")" << std::endl; + break; + case low_bound: + std::cout << "( " << this->m_low_bound_values[j] << " " << this->m_x[j] << " " << "_ )" << std::endl; + break; + case free_column: + std::cout << "( _" << this->m_x[j] << "_)" << std::endl; + default: + lean_unreachable(); + } +} + +template void lp_primal_core_solver:: init_infeasibility_cost_for_column(unsigned j) { + // we are going to maximize the cost + // When j is feasible, even at the boundary, then we set the cost of the column to zero. + 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] = zero_of_type(); + return; + } + + // j is a basis column + switch (this->m_column_type[j]) { + case fixed: + case boxed: + if (this->above_bound(x, this->m_upper_bound_values[j])) { + this->m_costs[j] = -1; + m_infeasibility -= x - this->m_upper_bound_values[j]; + } else if (this->below_bound(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] = zero_of_type(); + } + break; + case low_bound: + if (this->below_bound(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] = zero_of_type(); + } + break; + case upper_bound: + if (this->above_bound(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] = zero_of_type(); + } + break; + case free_column: + this->m_costs[j] = zero_of_type(); + break; + default: + lean_assert(false); + break; + } +} + +template void lp_primal_core_solver:: add_breakpoint(unsigned j, X delta, breakpoint_type type) { + m_breakpoints.push_back(breakpoint(j, delta, type)); + m_breakpoint_indices_queue.enqueue(m_breakpoint_indices_queue.size(), abs(delta)); +} + +// 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 void lp_primal_core_solver:: 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(), break_type); + return; + } + auto delta_j = diff / d; + if (same_sign_with_entering_delta(delta_j)) + add_breakpoint(j, delta_j, break_type); +} +template void lp_primal_core_solver:: init_costs_from_backup() { + this->m_costs = m_costs_backup; + m_using_inf_costs = false; +} + +template void lp_primal_core_solver:: init_reduced_costs() { + lean_assert(m_current_x_is_feasible == calc_current_x_is_feasible()); + if (get_current_x_is_infeasible()) { + init_infeasibility_costs(); + } else if (m_using_inf_costs) { + init_costs_from_backup(); + lean_assert(m_using_inf_costs == false); + } + this->init_reduced_costs_for_one_iteration(); +} +} diff --git a/src/util/lp/lp_primal_core_solver.h b/src/util/lp/lp_primal_core_solver.h index 57e1323e1e..4e885ec85a 100644 --- a/src/util/lp/lp_primal_core_solver.h +++ b/src/util/lp/lp_primal_core_solver.h @@ -30,7 +30,7 @@ namespace lean { // The right side b is given implicitly by x and the basis template class lp_primal_core_solver:public lp_core_solver_base { -public: // todo : move public lower ; it is for debug only +public: // m_sign_of_entering is set to 1 if the entering variable needs // to grow and is set to -1 otherwise unsigned m_column_norm_update_counter; @@ -47,179 +47,175 @@ public: // todo : move public lower ; it is for debug only bool m_exit_on_feasible_solution = false; std::vector m_costs_backup; bool m_current_x_is_feasible; + T m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); - int choose_entering_column(unsigned number_of_benefitial_columns_to_go_over) { // at this moment m_y = cB * B(-1) - lean_assert(number_of_benefitial_columns_to_go_over); - unsigned offset_in_nb = lrand48() % this->m_non_basic_columns.size(); - unsigned initial_offset_in_non_basis = offset_in_nb; - T steepest_edge = zero_of_type(); - int entering = -1; - do { - unsigned j = this->m_factorization->m_non_basic_columns[offset_in_nb]; - push_forward_offset_in_non_basis(offset_in_nb); - if (m_forbidden_enterings.find(j) != m_forbidden_enterings.end()) { - continue; - } - T dj = this->m_d[j]; - switch (this->m_column_type[j]) { - case fixed: continue; - case free_column: - if (dj > m_epsilon_of_reduced_cost || dj < -m_epsilon_of_reduced_cost) break; - continue; - case low_bound: - if (dj > m_epsilon_of_reduced_cost) break; - continue; - case upper_bound: - if (dj < -m_epsilon_of_reduced_cost) break; - continue; - case boxed: - if (dj > m_epsilon_of_reduced_cost) { - if (this->m_x[j] < this->m_upper_bound_values[j] - this->bound_span(j)/2) - break; - continue; - } else if (dj < - m_epsilon_of_reduced_cost) { - if (this->m_x[j] > this->m_low_bound_values[j] + this->bound_span(j)/2) break; - } - continue; - default: - lean_unreachable(); - } + int choose_entering_column(unsigned number_of_benefitial_columns_to_go_over); - // if we are here then j is a candidate to enter the basis - T t = dj * dj / this->m_column_norms[j]; - if (t > steepest_edge) { - steepest_edge = t; - entering = j; - if (number_of_benefitial_columns_to_go_over) - number_of_benefitial_columns_to_go_over--; - } - } while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb); - if (entering != -1) - m_sign_of_entering_delta = this->m_d[entering] > 0? 1 : -1; - return entering; -} - - - - int find_leaving_and_t_precisely(unsigned entering, X & t){ - int leaving = -1; - T abs_of_d_of_leaving = numeric_traits::zero(); - for (unsigned i = 0; i < this->m_m; i++) { - if (is_zero(this->m_ed[i])) continue; - unsigned j = this->m_basis[i]; - get_bound_on_variable_and_update_leaving_precisely(j, leaving, - this->m_ed[i] * m_sign_of_entering_delta, t, abs_of_d_of_leaving); - if (leaving != -1 && is_zero(t)) { - return leaving; - } - } - - get_bound_on_variable_and_update_leaving_precisely(entering, leaving, T(m_sign_of_entering_delta), t, abs_of_d_of_leaving); - return leaving; - } + int find_leaving_and_t_precisely(unsigned entering, X & t); static X positive_infinity() { return convert_struct::convert(UINT_MAX); } - X get_harris_theta() { - X theta = positive_infinity(); - unsigned i = this->m_m; - while (i--) { - if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) continue; - limit_theta_on_basis_column(this->m_basis[i], - this->m_ed[i] * m_sign_of_entering_delta, theta); - if (is_zero(theta)) break; - } - return theta; - } - - void restore_harris_eps() { - m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); - } + X get_harris_theta(); + void restore_harris_eps() { m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); } void zero_harris_eps() { m_converted_harris_eps = zero_of_type(); } - - bool get_current_x_is_feasible() const { - return m_current_x_is_feasible; - } - - bool get_current_x_is_infeasible() const { - return !m_current_x_is_feasible; - } - - int find_leaving_on_harris_theta(X const & harris_theta, X & t) { - int leaving = -1; - T pivot_abs_max; - T pivot_abs_max_fixed; - // we know already that there is no bound flip on entering - // we also know that harris_theta is limited, so we will find a leaving - zero_harris_eps(); - unsigned i = lrand48() % this->m_m; - unsigned initial_i = i; - int column_with_non_zero_cost = -1; - do { - if (this->m_settings.abs_val_is_smaller_than_pivot_tolerance(this->m_ed[i])) { - if (++i == this->m_m) - i = 0; - continue; - } - X ratio = positive_infinity(); - unsigned j = this->m_basis[i]; - limit_theta_on_basis_column(j, - this->m_ed[i] * m_sign_of_entering_delta, ratio); - if (ratio <= harris_theta) { - if (!m_recalc_reduced_costs && get_current_x_is_infeasible()) { // when we have made several basic variables feasible we need to recalculate the costs and the reduced costs: here we are catching this case - if (!is_zero(this->m_costs[j])) { - if (column_with_non_zero_cost != -1) - m_recalc_reduced_costs = true; - else - column_with_non_zero_cost = j; - } - } - if (leaving == -1 || abs(this->m_ed[i]) > pivot_abs_max) { - t = ratio; - leaving = j; - pivot_abs_max = abs(this->m_ed[i]); - } - } - if (++i == this->m_m) i = 0; - } while ( i != initial_i); - restore_harris_eps(); - return leaving; - } - - - bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t) { - if (this->m_column_type[entering] != boxed) - return false; - - if (m_sign_of_entering_delta > 0) { - t = this->m_upper_bound_values[entering] - this->m_x[entering]; - if (t <= theta){ - lean_assert(t >= zero_of_type()); - return true; - } - } else { // m_sign_of_entering_delta == -1 - t = this->m_x[entering] - this->m_low_bound_values[entering]; - if (t <= theta) { - lean_assert(t >= zero_of_type()); - return true; - } - } - - return false; - } - - int find_leaving_and_t(unsigned entering, X & t){ - if (numeric_traits::precise()) return find_leaving_and_t_precisely(entering, t); - X theta = get_harris_theta(); - lean_assert(theta >= zero_of_type()); - if (try_jump_to_another_bound_on_entering(entering, theta, t)) return entering; - if (theta == positive_infinity()) return -1; // unlimited - return find_leaving_on_harris_theta(theta, t); - } + bool get_current_x_is_feasible() const { return m_current_x_is_feasible; } + bool get_current_x_is_infeasible() const { return !m_current_x_is_feasible; } + int find_leaving_on_harris_theta(X const & harris_theta, X & t); + bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t); + int find_leaving_and_t(unsigned entering, X & t); void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(unsigned j, const T & m, X & theta) { lean_assert(m < 0 && this->m_column_type[j] == upper_bound); limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bound_values[j], theta); + } + + + void limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(unsigned j, const T & m, X & theta) { + lean_assert(m < 0 && this->m_column_type[j] == low_bound); + limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_low_bound_values[j], theta); + } + + + void limit_theta_on_basis_column_for_inf_case_m_pos_low_bound(unsigned j, const T & m, X & theta) { + lean_assert(m > 0 && this->m_column_type[j] == low_bound); + limit_inf_on_low_bound_m_pos(m, this->m_x[j], this->m_low_bound_values[j], theta); + } + + void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(unsigned j, const T & m, X & theta) { + lean_assert(m > 0 && this->m_column_type[j] == upper_bound); + limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bound_values[j], theta); + }; + + X harris_eps_for_bound(const X & bound) const { return ( convert_struct::convert(1) + abs(bound)/10) * m_converted_harris_eps/3; + } + + + void get_bound_on_variable_and_update_leaving_precisely(unsigned j, int & leaving, T m, X & t, T & abs_of_d_of_leaving); + + bool column_is_free(unsigned j) { return this->m_column_type[j] == free; } + + bool column_has_upper_bound(unsigned j) { + return ((unsigned)this->m_column_type[j]) < 2; + } + + bool column_has_low_bound(unsigned j) { return this->m_column_type[j] != free_column; } + + std::vector m_low_bound_values_dummy; // needed for the base class only + + X get_max_bound(std::vector & b); + + // stage1 constructor + lp_primal_core_solver(static_matrix & A, + std::vector & b, // the right side vector + std::vector & x, // the number of elements in x needs to be at least as large as the number of columns in A + std::vector & basis, + std::vector & costs, + std::vector & column_type_array, + std::vector & low_bound_values, + std::vector & upper_bound_values, + lp_settings & settings, + std::unordered_map const & column_names); // constructor + lp_primal_core_solver(static_matrix & A, + std::vector & b, // the right side vector + std::vector & x, // the number of elements in x needs to be at least as large as the number of columns in A + std::vector & basis, + std::vector & costs, + std::vector & column_type_array, + std::vector & upper_bound_values, + lp_settings & settings, + std::unordered_map const & column_names); + void init_lu(); + bool initial_x_is_correct(); +#ifdef LEAN_DEBUG + void check_Ax_equal_b(); + void check_the_bounds(); + void check_bound(unsigned i); + void check_correctness(); +#endif + + // from page 183 of Istvan Maros's book + // the basis structures have not changed yet + void update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving); + + // return 0 if the reduced cost at entering is close enough to the refreshed + // 1 if it is way off, and 2 if it is unprofitable + int refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering); + + void normalize_costs_and_backup_costs(); + + void init_run(); + + int pivots_in_column_and_row_are_different(int entering, int leaving) const; + + void calc_working_vector_beta_for_column_norms(); + + void advance_on_entering_and_leaving(int entering, int leaving, X & t); + + bool need_to_switch_costs() const { + lean_assert(calc_current_x_is_feasible() == m_current_x_is_feasible); + return get_current_x_is_feasible() == m_using_inf_costs; + } + + + void advance_on_entering(int entering); + + void push_forward_offset_in_non_basis(unsigned & offset_in_nb); + + unsigned get_number_of_non_basic_column_to_try_for_enter(); + + void print_column_norms(); + + void set_current_x_is_feasible() { m_current_x_is_feasible = calc_current_x_is_feasible(); } + // returns the number of iterations + unsigned solve(); + + lu * factorization() {return this->m_factorization;} + + void delete_factorization(); + + // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" + void init_column_norms(); + + T calculate_column_norm_exactly(unsigned j); + + void update_or_init_column_norms(unsigned entering, unsigned leaving); + + // following Swietanowski - A new steepest ... + void update_column_norms(unsigned entering, unsigned leaving); + + T calculate_norm_of_entering_exactly(); + + // calling it stage1 is too cryptic + void find_feasible_solution(); + + bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} + + void calculate_infeasibility(); + + void add_column_infeasibility(unsigned j); + + void one_iteration(); + + void fill_breakpoints_array(unsigned entering); + + void try_add_breakpoint_in_row(unsigned i); + + void clear_breakpoints(); + + void change_slope_on_breakpoint(unsigned entering, breakpoint * b, T & slope_at_entering); + void advance_on_sorted_breakpoints(unsigned entering); + + void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta); + bool column_is_feasible(unsigned j) const; + + bool calc_current_x_is_feasible() const; + + void decide_on_status_when_cannot_find_entering() { + lean_assert(!need_to_switch_costs()); + this->m_status = get_current_x_is_feasible()? OPTIMAL: INFEASIBLE; } void limit_theta_on_basis_column_for_feas_case_m_neg(unsigned j, const T & m, X & theta) { @@ -240,22 +236,6 @@ public: // todo : move public lower ; it is for debug only if (theta < zero_of_type()) theta = zero_of_type(); } - void limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(unsigned j, const T & m, X & theta) { - lean_assert(m < 0 && this->m_column_type[j] == low_bound); - limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_low_bound_values[j], theta); - } - - void limit_theta_on_basis_column_for_inf_case_m_pos_low_bound(unsigned j, const T & m, X & theta) { - lean_assert(m > 0 && this->m_column_type[j] == low_bound); - limit_inf_on_low_bound_m_pos(m, this->m_x[j], this->m_low_bound_values[j], theta); - } - - void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(unsigned j, const T & m, X & theta) { - lean_assert(m > 0 && this->m_column_type[j] == upper_bound); - limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bound_values[j], theta); - } - - bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta) { // x gets smaller lean_assert(m < 0); @@ -298,18 +278,6 @@ public: // todo : move public lower ; it is for debug only theta = std::min((bound - x - eps) / m, theta); } - - - void limit_theta_on_basis_column_for_inf_case_m_neg_fixed(unsigned j, const T & m, X & theta) { - lean_assert(m < 0 && this->m_column_type[j] == fixed); - limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_upper_bound_values[j], theta); - } - - void limit_theta_on_basis_column_for_inf_case_m_pos_fixed(unsigned j, const T & m, X & theta) { - lean_assert(m > 0 && this->m_column_type[j] == fixed); - limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bound_values[j], theta); - } - void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta) { // lean_assert(m > 0 && this->m_column_type[j] == boxed); const X & x = this->m_x[j]; @@ -362,10 +330,6 @@ public: // todo : move public lower ; it is for debug only if (theta < zero_of_type()) theta = zero_of_type(); } - T m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); - X harris_eps_for_bound(const X & bound) const { - return ( convert_struct::convert(1) + abs(bound)/10) * m_converted_harris_eps/3; - } // j is a basic column or the entering, in any case x[j] has to stay feasible. // m is the multiplier. updating t in a way that holds the following // x[j] + t * m >= - harris_feasibility_tolerance ( if m < 0 ) @@ -430,970 +394,29 @@ public: // todo : move public lower ; it is for debug only theta = zero_of_type(); } - // m is the multiplier. updating t in a way that holds the following - // x[j] + t * m >= m_low_bound_values[j] ( if m < 0 ) - // or - // x[j] + t * m <= this->m_upper_bound_values[j] ( if m > 0) - void get_bound_on_variable_and_update_leaving_precisely(unsigned j, int & leaving, T m, X & t, T & abs_of_d_of_leaving) { - lean_assert(leaving == -1 || t > zero_of_type()); - if (m > 0) { - if (this->m_column_type[j] != boxed) { - return; - } - X tt = (this->m_upper_bound_values[j] - this->m_x[j]) / m; - if (leaving == -1 || tt < t || (tt == t && m > abs_of_d_of_leaving)) { - t = tt; - leaving = j; - abs_of_d_of_leaving = m; - if (t < zero_of_type()) - t = zero_of_type(); - } - } else if (m < 0){ - if (this->m_column_type[j] == free_column) { - return; - } - X tt = (- this->m_x[j]) / m; - if (leaving == -1 || tt < t || (tt == t && - m > abs_of_d_of_leaving)) { - t = tt; - leaving = j; - abs_of_d_of_leaving = - m; - if (t < zero_of_type()) - t = zero_of_type(); - } - } - } - bool column_is_free(unsigned j) { - return this->m_column_type[j] == free; - } + bool can_enter_basis(unsigned j); + bool done(); - bool column_has_upper_bound(unsigned j) { - return ((unsigned)this->m_column_type[j]) < 2; - } + void init_infeasibility_costs(); - bool column_has_low_bound(unsigned j) { - return this->m_column_type[j] != free_column; - } + void print_column(unsigned j); - bool bound_holds(unsigned entering) { - if (numeric_traits::precise()) return true; - T v = this->m_x[entering]; - T eps = T(this->m_settings.harris_feasibility_tolerance); + void init_infeasibility_cost_for_column(unsigned j); - switch (this->m_column_type[entering]) { - case low_bound: - lean_assert(v >= -eps); - break; - case boxed: - lean_assert(v >= -eps); - lean_assert(v <= this->m_upper_bound_values[entering] + eps); - break; - default: - break; - } - return true; - } - - - std::vector m_low_bound_values_dummy; // needed for the base class only - - X get_max_bound(std::vector & b) { - X ret = zero_of_type(); - for (auto & v : b) { - X a = abs(v); - if (a > ret) ret = a; - } - return ret; - } - - // stage1 constructor - lp_primal_core_solver(static_matrix & A, - std::vector & b, // the right side vector - std::vector & x, // the number of elements in x needs to be at least as large as the number of columns in A - std::vector & basis, - std::vector & costs, - std::vector & column_type_array, - std::vector & low_bound_values, - std::vector & upper_bound_values, - lp_settings & settings, - std::unordered_map const & column_names): - lp_core_solver_base(A, b, - basis, - x, - costs, - settings, - column_names, - column_type_array, - low_bound_values, - upper_bound_values), - m_beta(A.row_count()) { - this->m_status = UNKNOWN; - this->m_column_norm_update_counter = settings.column_norms_update_frequency; - } - - // constructor - lp_primal_core_solver(static_matrix & A, - std::vector & b, // the right side vector - std::vector & x, // the number of elements in x needs to be at least as large as the number of columns in A - std::vector & basis, - std::vector & costs, - std::vector & column_type_array, - std::vector & upper_bound_values, - lp_settings & settings, - std::unordered_map const & column_names): - lp_core_solver_base(A, b, - basis, - x, - costs, - settings, - column_names, - column_type_array, - m_low_bound_values_dummy, - upper_bound_values), - m_beta(A.row_count()) { - m_converted_harris_eps = convert_struct::convert(this->m_settings.harris_feasibility_tolerance); - lean_assert(initial_x_is_correct()); - m_low_bound_values_dummy.resize(A.column_count(), zero_of_type()); - m_enter_price_eps = numeric_traits::precise() ? numeric_traits::zero() : T(1e-5); - this->m_column_norm_update_counter = settings.column_norms_update_frequency; -#ifdef LEAN_DEBUG - // check_correctness(); -#endif - } - - void init_lu() { - 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_refactor_counter = 0; - } - - bool initial_x_is_correct() { - std::set basis_set; - for (int i = 0; i < this->m_A.row_count(); i++) { - basis_set.insert(this->m_basis[i]); - } - for (int j = 0; j < this->m_n; j++) { - if (column_has_low_bound(j) && this->m_x[j] < numeric_traits::zero()) { - std::cout << "low bound for variable " << j << " does not hold: this->m_x[" << j << "] = " << this->m_x[j] << " is negative " << std::endl; - return false; - } - - if (column_has_upper_bound(j) && this->m_x[j] > this->m_upper_bound_values[j]) { - std::cout << "upper bound for " << j << " does not hold: " << this->m_upper_bound_values[j] << ">" << this->m_x[j] << std::endl; - return false; - } - - if (basis_set.find(j) != basis_set.end()) continue; - if (this->m_column_type[j] == low_bound) { - if (numeric_traits::zero() != this->m_x[j]) { - std::cout << "only low bound is set for " << j << " but low bound value " << numeric_traits::zero() << " is not equal to " << this->m_x[j] << std::endl; - return false; - } - } - if (this->m_column_type[j] == boxed) { - if (this->m_upper_bound_values[j] != this->m_x[j] && !numeric_traits::is_zero(this->m_x[j])) { - return false; - } - } - } - return true; - } - -#ifdef LEAN_DEBUG - void check_Ax_equal_b() { - dense_matrix d(this->m_A); - T * ls = d.apply_from_left_with_different_dims(this->m_x); - lean_assert(vectors_are_equal(ls, this->m_b, this->m_m)); - delete [] ls; - } - void check_the_bounds() { - for (unsigned i = 0; i < this->m_n; i++) { - check_bound(i); - } - } - - void check_bound(unsigned i) { - lean_assert (!(column_has_low_bound(i) && (numeric_traits::zero() > this->m_x[i]))); - lean_assert (!(column_has_upper_bound(i) && (this->m_upper_bound_values[i] < this->m_x[i]))); - } - - void check_correctness() { - check_the_bounds(); - check_Ax_equal_b(); - } -#endif - - // from page 183 of Istvan Maros's book - // the basis structures have not changed yet - void update_reduced_costs_from_pivot_row(unsigned entering, int leaving) { - // the basis heading has changed already -#ifdef LEAN_DEBUG - auto & basis_heading = this->m_factorization->m_basis_heading; - lean_assert(basis_heading[entering] >= 0 && basis_heading[entering] < this->m_ed.size()); - lean_assert(basis_heading[leaving] < 0); -#endif - T pivot = this->m_pivot_row[entering]; - T dq = this->m_d[entering]/pivot; - for (auto j : this->m_pivot_row_index) { - // for (auto j : this->m_non_basic_columns) - if (this->m_basis_heading[j] >= 0) continue; - if (j != leaving) - this->m_d[j] -= dq * this->m_pivot_row[j]; - } - this->m_d[leaving] = -dq; - if (get_current_x_is_infeasible()) { - this->m_d[leaving] -= this->m_costs[leaving]; - this->m_costs[leaving] = zero_of_type(); - } - this->m_d[entering] = numeric_traits::zero(); - } - - // return 0 if the reduced cost at entering is close enough to the refreshed - // 1 if it is way off, and 2 if it is unprofitable - int refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering) { - T reduced_at_entering_was = this->m_d[entering]; // can benefit from going over non-zeros of m_ed - lean_assert(abs(reduced_at_entering_was) > m_epsilon_of_reduced_cost); - T refreshed_cost = this->m_costs[entering]; - unsigned i = this->m_m; - while (i--) refreshed_cost -= this->m_costs[this->m_basis[i]] * this->m_ed[i]; - this->m_d[entering] = refreshed_cost; - T delta = abs(reduced_at_entering_was - refreshed_cost); - if (delta * 2 > abs(reduced_at_entering_was)) { - this->m_status = UNSTABLE; - if (reduced_at_entering_was > m_epsilon_of_reduced_cost) { - if (refreshed_cost <= zero_of_type()) - return 2; // abort entering - } else { - if (refreshed_cost > -m_epsilon_of_reduced_cost) - return 2; // abort entiring - } - return 1; // go on with this entering - } else { - if (reduced_at_entering_was > m_epsilon_of_reduced_cost) { - if (refreshed_cost <= zero_of_type()) - return 2; // abort entering - } else { - if (refreshed_cost > -m_epsilon_of_reduced_cost) - return 2; // abort entiring - } - } - return 0; - } - - void normalize_costs_and_backup_costs() { - T cost_max = std::max(max_abs_in_vector(this->m_costs), T(1)); - lean_assert(m_costs_backup.size() == 0); - for (unsigned j = 0; j < this->m_costs.size(); j++) - m_costs_backup.push_back(this->m_costs[j] /= cost_max); - m_using_inf_costs = false; - } - - void init_run() { - this->m_start_time = get_millisecond_count(); - this->m_total_iterations = 0; - this->m_iters_with_no_cost_growing = 0; - normalize_costs_and_backup_costs(); - set_current_x_is_feasible(); - init_reduced_costs(); - } - - int pivots_in_column_and_row_are_different(int entering, int leaving) const { - const T & column_p = this->m_ed[this->m_basis_heading[leaving]]; - const T & row_p = this->m_pivot_row[entering]; - if (is_zero(column_p) || is_zero(row_p)) return true; // pivots cannot be zero - // the pivots have to have the same sign - if (column_p < 0) { - if (row_p > 0) - return 2; - } else { // column_p > 0 - if (row_p < 0) - return 2; - } - T diff_normalized = abs((column_p - row_p) / (numeric_traits::one() + abs(row_p))); - if ( !this->m_settings.abs_val_is_smaller_than_harris_tolerance(diff_normalized / T(10))) - return 1; - return 0; - } - - void calc_working_vector_beta_for_column_norms(){ - unsigned i = this->m_m; - while (i--) - m_beta[i] = this->m_ed[i]; - this->m_factorization->solve_yB(m_beta); - } - - void advance_on_entering_and_leaving(int entering, int leaving, X & t) { - lean_assert(t >= zero_of_type()); - lean_assert(leaving >= 0 && entering >= 0); - lean_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes - if (entering == leaving) { - lean_assert(!this->A_mult_x_is_off() ); - this->update_x(entering, t * m_sign_of_entering_delta); - if (this->A_mult_x_is_off() && !this->find_x_by_solving()) { - init_lu(); - if (!this->find_x_by_solving()) { - this->restore_x(entering, t * m_sign_of_entering_delta); - m_forbidden_enterings.insert(entering); - this->m_iters_with_no_cost_growing++; - std::cout << "failing in advance_on_entering_and_leaving for entering == leaving = " << leaving << std::endl; - return; - } - } - set_current_x_is_feasible(); - if (need_to_switch_costs()) - init_reduced_costs(); - this->m_iters_with_no_cost_growing = 0; - return; - } - unsigned pivot_row = this->m_factorization->basis_heading(leaving); - this->calculate_pivot_row_of_B_1(pivot_row); - this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(); - int pivot_compare_result = pivots_in_column_and_row_are_different(entering, leaving); - if (!pivot_compare_result){;} - else if (pivot_compare_result == 2) { // the sign is changed, cannot continue - m_forbidden_enterings.insert(entering); - this->m_status = UNSTABLE; - this->m_iters_with_no_cost_growing++; - return; - } else { - lean_assert(pivot_compare_result == 1); - init_lu(); - } - calc_working_vector_beta_for_column_norms(); - if (!this->update_basis_and_x(entering, leaving, t * m_sign_of_entering_delta)) { - if (this->m_status == FLOATING_POINT_ERROR) - return; - set_current_x_is_feasible(); - init_reduced_costs(); - m_forbidden_enterings.insert(entering); - return; - } - if (!is_zero(t)) { - this->m_iters_with_no_cost_growing = 0; - set_current_x_is_feasible(); - } - update_or_init_column_norms(entering, leaving); - if (need_to_switch_costs() || m_recalc_reduced_costs) - init_reduced_costs(); - else - update_reduced_costs_from_pivot_row(entering, leaving); - lean_assert(!need_to_switch_costs()); - m_forbidden_enterings.clear(); - } - - bool need_to_switch_costs() { - lean_assert(calc_current_x_is_feasible() == m_current_x_is_feasible); - return get_current_x_is_feasible() == m_using_inf_costs; - } - // void recalc_reduced_costs() { - // if (current_x_is_feasible()) - // init_infeasibility_costs(); - // this->init_reduced_costs_for_one_iteration(); - // } - - void advance_on_entering(int entering) { - lean_assert(entering > -1); - this->solve_Bd(entering); - int refresh_result = refresh_reduced_cost_at_entering_and_check_that_it_is_off(entering); - if (refresh_result) { - init_lu(); - init_reduced_costs(); - if (refresh_result == 2) { - this->m_iters_with_no_cost_growing++; - return; - } - } - X t; - int leaving = find_leaving_and_t(entering, t); - if (leaving == -1){ - if (get_current_x_is_infeasible()) { - if (this->m_status == UNSTABLE) { - std::cout << "setting status to FLOATING_POINT_ERROR" << std::endl; - this->m_status = FLOATING_POINT_ERROR; - return; - } - init_reduced_costs(); - this->m_status = UNSTABLE; - return; - } - if (this->m_status == TENTATIVE_UNBOUNDED) { - this->m_status = UNBOUNDED; - } else { - this->m_status = TENTATIVE_UNBOUNDED; - } - return; - } - advance_on_entering_and_leaving(entering, leaving, t); - } - - void push_forward_offset_in_non_basis(unsigned & offset_in_nb) { - if (++offset_in_nb == this->m_non_basic_columns.size()) - offset_in_nb = 0; - } - - unsigned get_number_of_non_basic_column_to_try_for_enter() { - unsigned ret = this->m_factorization->m_non_basic_columns.size(); - if (this->m_status == TENTATIVE_UNBOUNDED) - return ret; // we really need to find entering with a large reduced cost - if (ret > 300) { - ret = (unsigned)(ret * this->m_settings.percent_of_entering_to_check / 100); - } - return std::max(static_cast(lrand48() % ret), 1u); - } - - void print_column_norms() { - std::cout << " column norms " << std::endl; - for (unsigned j = 0; j < this->m_n; j++) { - std::cout << this->m_column_norms[j] << " "; - } - std::cout << std::endl; - std::cout << std::endl; - } - - void set_current_x_is_feasible() { - m_current_x_is_feasible = calc_current_x_is_feasible(); - } - // returns the number of iterations - unsigned solve() { - init_run(); - lean_assert(!this->A_mult_x_is_off()); - do { - if (this->m_total_iterations % this->m_settings.report_frequency == 0) { - std::ostringstream string_stream; - string_stream << (m_using_inf_costs? "stage 1" : "stage 2"); - std::string stream_string = string_stream.str(); - if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(stream_string, this->m_total_iterations)) { - this->m_status = lp_status::TIME_EXHAUSTED; - return this->m_total_iterations; - } - } - lean_assert(m_current_x_is_feasible == calc_current_x_is_feasible()); - one_iteration(); - switch (this->m_status) { - case OPTIMAL: // double check that we are at optimum - case INFEASIBLE: - m_forbidden_enterings.clear(); - init_lu(); - lean_assert(this->m_factorization->get_status() == LU_status::OK); - set_current_x_is_feasible(); - init_reduced_costs(); - if (choose_entering_column(1) == -1) { - decide_on_status_when_cannot_find_entering(); - break; - } - this->m_status = UNKNOWN; - break; - case TENTATIVE_UNBOUNDED: - m_forbidden_enterings.clear(); - init_lu(); - lean_assert(this->m_factorization->get_status() == LU_status::OK); - init_reduced_costs(); - break; - case UNBOUNDED: - break; - - case UNSTABLE: - // m_forbidden_enterings.clear(); - init_lu(); - init_reduced_costs(); - this->m_status = UNKNOWN; - break; - - default: - break; // do nothing - } - } while (this->m_status != FLOATING_POINT_ERROR && this->m_status != UNBOUNDED - && - this->m_status != OPTIMAL - && - this->m_status != INFEASIBLE - && - 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 - && - !(m_current_x_is_feasible && m_exit_on_feasible_solution)); - return this->m_total_iterations; - } - - lu * factorization() { - return this->m_factorization; - } - - - void forget_last_columns(unsigned how_many_to_forget) { - lean_assert(this->m_n > how_many_to_forget); -#ifdef LEAN_DEBUG - for (unsigned i = 0; i < this->m_A.row_count(); i++) { - lean_assert(this->m_basis[i] < this->m_n - how_many_to_forget); - } -#endif - this->m_A.forget_last_columns(how_many_to_forget); - this->m_n -= how_many_to_forget; - this->m_non_basic_columns.clear(); - this->init_basis_heading(); - } - - void reduce_basis_by_one(unsigned col) { - // at this moment this->m_basis has one column too many - for (unsigned i = this->m_basis_heading[col]; i < this->m_m; i++) { - unsigned t = this->m_basis[i] = this->m_basis[i + 1]; - this->m_basis_heading[t] = i; - } - this->m_basis_heading[col] = -1; - } - - // k is the redundant row index - void cross_out_redundant_row_and_remove_basis_column(unsigned k, unsigned col) { - this->m_A.cross_out_row(k); - this->m_m--; - reduce_basis_by_one(col); - } - - void delete_factorization() { - if (this->m_factorization != nullptr) { - delete this->m_factorization; - this->m_factorization = nullptr; - } - } - - // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming" - void init_column_norms() { - m_column_norm_update_counter = 0; - for (unsigned j : this->m_non_basic_columns) - this->m_column_norms[j] = 1; - } - - // debug only - T calculate_column_norm_exactly(unsigned j) { - indexed_vector w(this->m_m); - this->m_A.copy_column_to_vector(j, w); - std::vector d(this->m_m); - this->m_factorization->solve_Bd_when_w_is_ready(d, w); - T ret = zero_of_type(); - for (auto v : d) - ret += v*v; - return ret+1; - } - - void update_or_init_column_norms(unsigned entering, unsigned leaving) { - if (++m_column_norm_update_counter == this->m_settings.column_norms_update_frequency) { - init_column_norms(); - } else { - update_column_norms(entering, leaving); - } - } - - // following Swietanowski - A new steepest ... - void update_column_norms(unsigned entering, unsigned leaving) { - T pivot = this->m_pivot_row[entering]; - T g_ent = std::max(calculate_norm_of_entering_exactly() / pivot /pivot, T(0.000001)); - this->m_column_norms[leaving] = g_ent; - - for (unsigned j : this->m_pivot_row_index) { - if (j == leaving) - continue; - const T & t = this->m_pivot_row[j]; - T s = this->m_A.dot_product_with_column(m_beta, j); - T k = -2 / pivot; - T tp = t/pivot; - if (this->m_column_type[j] == fixed) { - this->m_column_norms[j] = T(1); // We would like to kick out fixed vars from the basis, so making the norm small increases this chance since the steepest edge expression will be large - } else { - this->m_column_norms[j] = std::max(this->m_column_norms[j] + t * (t * g_ent + k * s), // see Achim Koberstein dissertation (8.1) - 1 + tp * tp); - } - } - } - - T calculate_norm_of_entering_exactly() { - T r = numeric_traits::one(); - unsigned i = this->m_m; - while (i--) { - T t = this->m_ed[i]; - r += t * t; - } - return r; - } - - // calling it stage1 is too cryptic - void find_feasible_solution() { - m_exit_on_feasible_solution = true; - this->snap_xN_to_bounds_and_free_columns_to_zeroes(); - solve(); - } - - bool is_tiny() const { - return this->m_m < 10 && this->m_n < 20; - } - - void calculate_infeasibility() { - m_infeasibility = zero_of_type(); - unsigned i = this->m_m; - while (i--) { - add_column_infeasibility(this->m_basis[i]); - } - } - - void add_column_infeasibility(unsigned j){ - const X & x = this->m_x[j]; - switch (this->m_column_type[j]) { - case fixed: - case boxed: - if (this->above_bound(x, this->m_upper_bound_values[j])) { - m_infeasibility -= x - this->m_upper_bound_values[j]; - } else if (this->below_bound(x, this->m_low_bound_values[j])) { - m_infeasibility -= this->m_low_bound_values[j] - x; - } - break; - case low_bound: - if (this->below_bound(x, this->m_low_bound_values[j])) { - m_infeasibility -= this->m_low_bound_values[j] - x; - } - break; - case upper_bound: - if (this->above_bound(x, this->m_upper_bound_values[j])) { - m_infeasibility -= x - this->m_upper_bound_values[j]; - } - break; - case free_column: - break; - } - } - - void one_iteration() { - this->m_total_iterations++; - unsigned number_of_benefitial_columns_to_go_over = get_number_of_non_basic_column_to_try_for_enter(); - int entering = choose_entering_column(number_of_benefitial_columns_to_go_over); - if (entering == -1) - decide_on_status_when_cannot_find_entering(); - else - advance_on_entering(entering); - } - - 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 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 clear_breakpoints() { - m_breakpoints.clear(); - m_breakpoint_indices_queue.clear();// m_breakpoints.clear(); - } - - void change_slope_on_breakpoint(unsigned entering, breakpoint * 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::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(); - } - } - - - void advance_on_sorted_breakpoints(unsigned entering) { - T slope_at_entering = this->m_d[entering]; - breakpoint * last_bp = nullptr; - while (m_breakpoint_indices_queue.is_empty() == false) { - unsigned bi = m_breakpoint_indices_queue.dequeue(); - breakpoint *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::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 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); - } - bool column_is_feasible(unsigned j){ - const X& x = this->m_x[j]; - switch (this->m_column_type[j]) { - case fixed: - case boxed: - if (this->above_bound(x, this->m_upper_bound_values[j])) { - return false; - } else if (this->below_bound(x, this->m_low_bound_values[j])) { - return false; - } else { - return true; - } - break; - case low_bound: - if (this->below_bound(x, this->m_low_bound_values[j])) { - return false; - } else { - return true; - } - break; - case upper_bound: - if (this->above_bound(x, this->m_upper_bound_values[j])) { - return false; - } else { - return true; - } - break; - case free_column: - return true; - break; - default: - lean_unreachable(); - } - } - - bool calc_current_x_is_feasible() { - unsigned i = this->m_m; - while (i--) { - if (!column_is_feasible(this->m_basis[i])) - return false; - } - return true; - } - - void decide_on_status_when_cannot_find_entering() { - lean_assert(!need_to_switch_costs()); - this->m_status = get_current_x_is_feasible()? OPTIMAL: INFEASIBLE; - } - - bool can_enter_basis(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::zero(); - case upper_bound: - lean_assert(this->x_is_at_upper_bound(j)); - return this->m_d[j] < numeric_traits::zero(); - case fixed: - return false; - case boxed: - { - bool at_low_bound = this->x_is_at_low_bound(j); - lean_assert(at_low_bound || this->x_is_at_upper_bound(j)); - return at_low_bound ? this->m_d[j] > numeric_traits::zero() : this->m_d[j] < numeric_traits::zero(); - } - case free_column: - return !numeric_traits::is_zero(this->m_d[j]); - default: - return false; - } - return false; - } - - - - bool done() { - if (this->m_status == OPTIMAL ||this->m_status == FLOATING_POINT_ERROR) return true; - if (this->m_status == INFEASIBLE) { - 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; - } - - void init_infeasibility_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(); - for (unsigned j = this->m_n; j--;) - init_infeasibility_cost_for_column(j); - // lean_assert(this->m_total_iterations == 0 || (inf <= m_infeasibility + convert_struct::convert(this->m_settings.tolerance_for_artificials))); - // if (inf == m_infeasibility) - // this->m_iters_with_no_cost_growing++; - m_using_inf_costs = true; - } - - void print_column(unsigned j) { - std::cout << this->column_name(j) << " " << j << " " << column_type_to_string(this->m_column_type[j]) << " x = " << this->m_x[j] << " " << "c = " << this->m_costs[j];; - switch (this->m_column_type[j]) { - case fixed: - case boxed: - std::cout << "( " << this->m_low_bound_values[j] << " " << this->m_x[j] << " " << this->m_upper_bound_values[j] << ")" << std::endl; - break; - case upper_bound: - std::cout << "( _" << this->m_x[j] << " " << this->m_upper_bound_values[j] << ")" << std::endl; - break; - case low_bound: - std::cout << "( " << this->m_low_bound_values[j] << " " << this->m_x[j] << " " << "_ )" << std::endl; - break; - case free_column: - std::cout << "( _" << this->m_x[j] << "_)" << std::endl; - default: - lean_unreachable(); - } - } - - void init_infeasibility_cost_for_column(unsigned j) { - // we are going to maximize the cost - // When j is feasible, even at the boundary, then we set the cost of the column to zero. - 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] = zero_of_type(); - return; - } - - // j is a basis column - switch (this->m_column_type[j]) { - case fixed: - case boxed: - if (this->above_bound(x, this->m_upper_bound_values[j])) { - this->m_costs[j] = -1; - m_infeasibility -= x - this->m_upper_bound_values[j]; - } else if (this->below_bound(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] = zero_of_type(); - } - break; - case low_bound: - if (this->below_bound(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] = zero_of_type(); - } - break; - case upper_bound: - if (this->above_bound(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] = zero_of_type(); - } - break; - case free_column: - this->m_costs[j] = zero_of_type(); - break; - default: - lean_assert(false); - break; - } - } - - void add_breakpoint(unsigned j, X delta, breakpoint_type type) { - m_breakpoints.push_back(breakpoint(j, delta, type)); - m_breakpoint_indices_queue.enqueue(m_breakpoint_indices_queue.size(), abs(delta)); - } + void add_breakpoint(unsigned j, X delta, breakpoint_type type); // j is the basic column, x is the value at x[j] // d is the coefficient before m_entering in the row with j as the basis column - void try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value) { - 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(), 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); template bool same_sign_with_entering_delta(const L & a) { return (a > zero_of_type() && m_sign_of_entering_delta > 0) || (a < zero_of_type() && m_sign_of_entering_delta < 0); - } // todo: check that it works correctly - - void init_costs_from_backup() { - this->m_costs = m_costs_backup; - m_using_inf_costs = false; } - void init_reduced_costs() { - lean_assert(m_current_x_is_feasible == calc_current_x_is_feasible()); - if (get_current_x_is_infeasible()) { - init_infeasibility_costs(); - } else if (m_using_inf_costs) { - init_costs_from_backup(); - lean_assert(m_using_inf_costs == false); - } - this->init_reduced_costs_for_one_iteration(); - } + void init_costs_from_backup(); + + void init_reduced_costs(); bool low_bounds_are_set() const { return true; } friend core_solver_pretty_printer; diff --git a/src/util/lp/lp_primal_core_solver_instances.cpp b/src/util/lp/lp_primal_core_solver_instances.cpp new file mode 100644 index 0000000000..cd57449773 --- /dev/null +++ b/src/util/lp/lp_primal_core_solver_instances.cpp @@ -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::find_feasible_solution(); +template lp_primal_core_solver::lp_primal_core_solver(static_matrix&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, lp_settings&, std::unordered_map, std::equal_to, std::allocator > > const&); +template lp_primal_core_solver::lp_primal_core_solver(static_matrix&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, lp_settings&, std::unordered_map, std::equal_to, std::allocator > > const&); + +template unsigned lp_primal_core_solver::solve(); +template lp_primal_core_solver::lp_primal_core_solver(static_matrix&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, std::vector >&, lp_settings&, std::unordered_map, std::equal_to, std::allocator > > const&); +template unsigned lp_primal_core_solver::solve(); + //template void lp_primal_simplex::solve_with_total_inf(); + //template void lp_primal_simplex::solve_with_total_inf(); +} diff --git a/src/util/lp/lp_primal_simplex.cpp b/src/util/lp/lp_primal_simplex.cpp new file mode 100644 index 0000000000..ebe63441f1 --- /dev/null +++ b/src/util/lp/lp_primal_simplex.cpp @@ -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 void lp_primal_simplex:: 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 void lp_primal_simplex:: init_buffer(unsigned k, std::vector & 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 void lp_primal_simplex:: refactor() { + m_core_solver->init_lu(); + if (m_core_solver->factorization()->get_status() != LU_status::OK) { + throw exception("cannot refactor"); + } +} + +template void lp_primal_simplex:: set_scaled_costs() { + unsigned j = this->number_of_core_structurals(); + while (j-- > 0) { + this->set_scaled_cost(j); + } +} + +template void lp_primal_simplex:: 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::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 column_info * lp_primal_simplex::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) : it->second; +} + +template void lp_primal_simplex:: fill_acceptable_values_for_x() { + for (auto t : this->m_core_solver_columns_to_external_columns) { + this->m_x[t.first] = numeric_traits::zero(); + lean_assert(this->m_x[t.first] >= 0); + } +} + + +template void lp_primal_simplex:: set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) { + bound_is_set[i] = true; + bounds[i] = numeric_traits::zero(); +} + +template void lp_primal_simplex:: 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::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::one(); + this->m_x[artificial] = rs; + } else { + (*this->m_A)(row, artificial) = - numeric_traits::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::one(); + + if (rs > 0) { + lean_assert(numeric_traits::is_zero(this->m_x[slack_var])); + // adding one artificial + this->m_column_types[artificial] = low_bound; + (*this->m_A)(row, artificial) = numeric_traits::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 void lp_primal_simplex:: 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::one(); + + if (rs < 0) { + // adding one artificial + lean_assert(numeric_traits::is_zero(this->m_x[slack_var])); + this->m_column_types[artificial] = low_bound; + (*this->m_A)(row, artificial) = - numeric_traits::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 void lp_primal_simplex:: adding an artificial variable + this->m_basis[row] = slack_var; + this->m_x[slack_var] = rs; + } + slack_var++; + break; + } +} + + + +template std::string lp_primal_simplex::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 void lp_primal_simplex:: 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 * 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::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 void lp_primal_simplex:: 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 void lp_primal_simplex:: 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 void lp_primal_simplex:: 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::one(); + this->m_column_types[j] = fixed; + this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type(); + break; + + case Greater_or_equal: + this->m_x[j] = - this->m_b[row]; + (*this->m_A)(row, j) = - numeric_traits::one(); + this->m_column_types[j] = low_bound; + this->m_upper_bounds[j] = zero_of_type(); + break; + case Less_or_equal: + this->m_x[j] = this->m_b[row]; + (*this->m_A)(row, j) = numeric_traits::one(); + this->m_column_types[j] = low_bound; + this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type(); + break; + } +} + +template void lp_primal_simplex:: 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()); // low bounds are shifted ot zero + this->m_x.resize(total_vars, numeric_traits::zero()); + this->m_basis.resize(this->row_count()); + this->m_costs.clear(); + this->m_costs.resize(total_vars, zero_of_type()); + 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::zero(); + + set_scaled_costs(); + m_core_solver = new lp_primal_core_solver(*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 lp_primal_simplex::~lp_primal_simplex() { + if (m_core_solver != nullptr) { + delete m_core_solver; + } +} + +template bool lp_primal_simplex::bounds_hold(std::unordered_map 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 T lp_primal_simplex::get_row_value(unsigned i, std::unordered_map 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::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 * 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 bool lp_primal_simplex::row_constraint_holds(unsigned i, std::unordered_map 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::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::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::get_double(row_val - rs) > 0.00001) { + if (print) { + std::cout << "should be <= " << rs << std::endl; + } + return false; + } + return true;; + } + lean_unreachable(); +} + +template bool lp_primal_simplex::row_constraints_hold(std::unordered_map 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 T lp_primal_simplex::get_current_cost() const { + T ret = numeric_traits::zero(); + for (auto it : this->m_columns) { + ret += this->get_column_cost_value(it.first, it.second); + } + return ret; +} +} diff --git a/src/util/lp/lp_primal_simplex.h b/src/util/lp/lp_primal_simplex.h index 5b983f2740..5e801890de 100644 --- a/src/util/lp/lp_primal_simplex.h +++ b/src/util/lp/lp_primal_simplex.h @@ -23,64 +23,21 @@ class lp_primal_simplex: public lp_solver { 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 & r); - void init_buffer(unsigned k, std::vector & 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::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 * 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) : it->second; - } + column_info * 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::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::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::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::one(); - this->m_x[artificial] = rs; - } else { - (*this->m_A)(row, artificial) = - numeric_traits::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::one(); - - if (rs > 0) { - lean_assert(numeric_traits::is_zero(this->m_x[slack_var])); - // adding one artificial - this->m_column_types[artificial] = low_bound; - (*this->m_A)(row, artificial) = numeric_traits::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::one(); - - if (rs < 0) { - // adding one artificial - lean_assert(numeric_traits::is_zero(this->m_x[slack_var])); - this->m_column_types[artificial] = low_bound; - (*this->m_A)(row, artificial) = - numeric_traits::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 * 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::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::one(); - this->m_column_types[j] = fixed; - this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type(); - break; - - case Greater_or_equal: - this->m_x[j] = - this->m_b[row]; - (*this->m_A)(row, j) = - numeric_traits::one(); - this->m_column_types[j] = low_bound; - this->m_upper_bounds[j] = zero_of_type(); - break; - case Less_or_equal: - this->m_x[j] = this->m_b[row]; - (*this->m_A)(row, j) = numeric_traits::one(); - this->m_column_types[j] = low_bound; - this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type(); - 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()); // low bounds are shifted ot zero - this->m_x.resize(total_vars, numeric_traits::zero()); - this->m_basis.resize(this->row_count()); - this->m_costs.clear(); - this->m_costs.resize(total_vars, zero_of_type()); - 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::zero(); - - set_scaled_costs(); - m_core_solver = new lp_primal_core_solver(*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 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 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 const & solution, bool print); - T get_row_value(unsigned i, std::unordered_map 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::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 const & solution, bool print); - column_info * 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 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::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::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::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 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 const & solution); - T * get_array_from_map(std::unordered_map 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 const & solution); bool solution_is_feasible(std::unordered_map 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::zero(); - for (auto it : this->m_columns) { - ret += this->get_column_cost_value(it.first, it.second); - } - return ret; - } + T get_current_cost() const; }; } diff --git a/src/util/lp/lp_primal_simplex_instances.cpp b/src/util/lp/lp_primal_simplex_instances.cpp new file mode 100644 index 0000000000..c56dfd6ea8 --- /dev/null +++ b/src/util/lp/lp_primal_simplex_instances.cpp @@ -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::bounds_hold(std::unordered_map, std::equal_to, std::allocator > > const&); +template bool lean::lp_primal_simplex::row_constraints_hold(std::unordered_map, std::equal_to, std::allocator > > const&); +template double lean::lp_primal_simplex::get_current_cost() const; +template lean::lp_primal_simplex::~lp_primal_simplex(); +template lean::lp_primal_simplex::~lp_primal_simplex(); +template lean::mpq lean::lp_primal_simplex::get_current_cost() const; +template void lean::lp_primal_simplex::find_maximal_solution(); +template void lean::lp_primal_simplex::find_maximal_solution(); diff --git a/src/util/lp/lp_settings.cpp b/src/util/lp/lp_settings.cpp new file mode 100644 index 0000000000..ef1c13421b --- /dev/null +++ b/src/util/lp/lp_settings.cpp @@ -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 +bool vectors_are_equal(T * a, std::vector &b, unsigned n) { + if (numeric_traits::precise()) { + for (unsigned i = 0; i < n; i ++){ + if (!numeric_traits::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::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 +bool vectors_are_equal(const std::vector & a, const buffer &b) { + unsigned n = a.size(); + if (n != b.size()) return false; + if (numeric_traits::precise()) { + for (unsigned i = 0; i < n; i ++){ + if (!numeric_traits::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::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 +bool vectors_are_equal(const std::vector & a, const std::vector &b) { + unsigned n = a.size(); + if (n != b.size()) return false; + if (numeric_traits::precise()) { + for (unsigned i = 0; i < n; i ++){ + if (!numeric_traits::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::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; +} +} + diff --git a/src/util/lp/lp_settings.h b/src/util/lp/lp_settings.h index 3d6ca8b521..1f3a25a997 100644 --- a/src/util/lp/lp_settings.h +++ b/src/util/lp/lp_settings.h @@ -10,7 +10,10 @@ #include #include #include - +#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 +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 +bool val_is_smaller_than_eps(T const & t, double const & eps) { + if (!numeric_traits::precise()) { + return numeric_traits::get_double(t) < eps; + } + return t <= numeric_traits::zero(); +} + +template +bool vectors_are_equal(T * a, std::vector &b, unsigned n); + +template +bool vectors_are_equal(const std::vector & a, const buffer &b); + +template +bool vectors_are_equal(const std::vector & a, const std::vector &b); + +template +T abs (T const & v) { return v >= zero_of_type() ? v : -v; } + +template +X max_abs_in_vector(std::vector& t){ + X r(zero_of_type()); + for (auto & v : t) + r = std::max(abs(v) , r); + return r; +} +inline void print_blanks(int n) { + while (n--) {std::cout << ' '; } } } + diff --git a/src/util/lp/lp_settings_instances.cpp b/src/util/lp/lp_settings_instances.cpp new file mode 100644 index 0000000000..3c5eb4a1b4 --- /dev/null +++ b/src/util/lp/lp_settings_instances.cpp @@ -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(std::vector > const&, std::vector > const&); diff --git a/src/util/lp/lp_solver.cpp b/src/util/lp/lp_solver.cpp new file mode 100644 index 0000000000..83f0a491b4 --- /dev/null +++ b/src/util/lp/lp_solver.cpp @@ -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 column_info * lp_solver::get_or_create_column_info(unsigned column) { + auto it = m_columns.find(column); + return (it == m_columns.end())? ( m_columns[column] = new column_info) : it->second; +} + + +template T lp_solver::get_column_cost_value(unsigned j, column_info * ci) const { + if (ci->is_fixed()) { + return ci->get_cost() * ci->get_fixed_value(); + } + return ci->get_cost() * get_column_value(j); +} +template void lp_solver::add_constraint(lp_relation relation, T right_side, unsigned row_index) { + lean_assert(m_constraints.find(row_index) == m_constraints.end()); + lp_constraint cs(right_side, relation); + m_constraints[row_index] = cs; +} + +template void lp_solver::give_symbolic_name_to_column(std::string name, unsigned column) { + auto it = m_columns.find(column); + column_info *ci; + if (it == m_columns.end()){ + m_columns[column] = ci = new column_info; + } else { + ci = it->second; + } + ci->set_name(name); + m_names_to_columns[name] = column; +} + + +template T lp_solver::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 int lp_solver::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 lp_solver::~lp_solver(){ + if (m_A != nullptr) { + delete m_A; + } + for (auto t : m_columns) { + delete t.second; + } +} + +template void lp_solver::flip_costs() { + for (auto t : m_columns) { + column_info *ci = t.second; + ci->set_cost(-ci->get_cost()); + } +} + +template bool lp_solver::problem_is_empty() { + for (auto & c : m_A_values) + if (c.second.size()) + return false; + return true; +} + +template void lp_solver::scale() { + if (numeric_traits::precise() || m_settings.use_scaling == false) { + m_column_scale.clear(); + m_column_scale.resize(m_A->column_count(), one_of_type()); + return; + } + + T smin = T(m_settings.scaling_minimum); + T smax = T(m_settings.scaling_maximum); + + scaler scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings); + if (!scaler.scale()) { + unscale(); + } +} + + +template void lp_solver::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 void lp_solver::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 void lp_solver::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 void lp_solver::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 void lp_solver::print_scale_stats() { + print_rows_scale_stats(); + print_columns_scale_stats(); +} + +template void lp_solver::get_max_abs_in_row(std::unordered_map & row_map) { + T ret = numeric_traits::zero(); + for (auto jp : row_map) { + T ac = numeric_traits::abs(jp->second); + if (ac > ret) { + ret = ac; + } + } + return ret; +} + +template void lp_solver::pin_vars_on_row_with_sign(std::unordered_map & row, T sign ) { + std::unordered_map pinned; + for (auto t : row) { + unsigned j = t.first; + column_info * ci = m_columns[j]; + T a = t.second; + if (a * sign > numeric_traits::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 bool lp_solver::get_minimal_row_value(std::unordered_map & row, T & low_bound) { + low_bound = numeric_traits::zero(); + for (auto & t : row) { + T a = t.second; + column_info * ci = m_columns[t.first]; + if (a > numeric_traits::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 bool lp_solver::get_maximal_row_value(std::unordered_map & row, T & low_bound) { + low_bound = numeric_traits::zero(); + for (auto & t : row) { + T a = t.second; + column_info * ci = m_columns[t.first]; + if (a < numeric_traits::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 bool lp_solver::row_is_zero(std::unordered_map & row) { + for (auto & t : row) { + if (!is_zero(t.second)) + return false; + } + return true; +} + +template bool lp_solver::row_e_is_obsolete(std::unordered_map & 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 int lp_solver::row_ge_is_obsolete(std::unordered_map & row, unsigned row_index) { + T rs = m_constraints[row_index].m_rs; + if (row_is_zero(row)) { + if (rs > zero_of_type()) + 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 bool lp_solver::row_le_is_obsolete(std::unordered_map & 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()) + 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 bool lp_solver::row_is_obsolete(std::unordered_map & 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 void lp_solver::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 void lp_solver::remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map & row) { + auto & constraint = m_constraints[i]; + std::vector removed; + for (auto & col : row) { + unsigned j = col.first; + lean_assert(m_columns.find(j) != m_columns.end()); + column_info * 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::is_zero(col.second)){ + removed.push_back(j); + } + } + } + + for (auto j : removed) { + row.erase(j); + } +} + +template unsigned lp_solver::try_to_remove_some_rows() { + std::vector 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 void lp_solver::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 void lp_solver::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 void lp_solver::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::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 void lp_solver::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 void lp_solver::unscale() { + delete m_A; + m_A = nullptr; + fill_A_from_A_values(); + restore_column_scales_to_one(); + fill_m_b(); +} + +template void lp_solver::fill_A_from_A_values() { + m_A = new static_matrix(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 void lp_solver::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 void lp_solver::count_slacks_and_artificials() { + for (int i = row_count() - 1; i >= 0; i--) { + count_slacks_and_artificials_for_row(i); + } +} + +template void lp_solver::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 T lp_solver::low_bound_shift_for_row(unsigned i) { + T ret = numeric_traits::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 void lp_solver::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 T lp_solver::get_column_value_with_core_solver(unsigned column, lp_core_solver_base * core_solver) const { + auto cit = this->m_columns.find(column); + if (cit == this->m_columns.end()) { + return numeric_traits::zero(); + } + + column_info * 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::zero(); // returns zero for out of boundary columns +} + +template void lp_solver::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 * 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]; +} +} diff --git a/src/util/lp/lp_solver.h b/src/util/lp/lp_solver.h index 7fe50160c2..b2aca9c587 100644 --- a/src/util/lp/lp_solver.h +++ b/src/util/lp/lp_solver.h @@ -34,18 +34,10 @@ struct lp_constraint { template class lp_solver { - column_info * get_or_create_column_info(unsigned column) { - auto it = m_columns.find(column); - return (it == m_columns.end())? ( m_columns[column] = new column_info) : it->second; - } + column_info * get_or_create_column_info(unsigned column); protected: - T get_column_cost_value(unsigned j, column_info * 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 * ci) const; public: unsigned m_total_iterations; static_matrix* 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 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 *ci; - if (it == m_columns.end()){ - m_columns[column] = ci = new column_info; - } 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 *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 *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::precise() || m_settings.use_scaling == false) { - m_column_scale.clear(); - m_column_scale.resize(m_A->column_count(), one_of_type()); - return; - } - - T smin = T(m_settings.scaling_minimum); - T smax = T(m_settings.scaling_maximum); - - scaler 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 & row_map) { - T ret = numeric_traits::zero(); - for (auto jp : row_map) { - T ac = numeric_traits::abs(jp->second); - if (ac > ret) { - ret = ac; - } - } - return ret; - } + void get_max_abs_in_row(std::unordered_map & row_map); void pin_vars_down_on_row(std::unordered_map & row) { pin_vars_on_row_with_sign(row, - numeric_traits::one()); @@ -258,164 +163,19 @@ protected: pin_vars_on_row_with_sign(row, numeric_traits::one()); } - void pin_vars_on_row_with_sign(std::unordered_map & row, T sign ) { - std::unordered_map pinned; - for (auto t : row) { - unsigned j = t.first; - column_info * ci = m_columns[j]; - T a = t.second; - if (a * sign > numeric_traits::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 & row, T sign ); - bool get_minimal_row_value(std::unordered_map & row, T & low_bound) { - low_bound = numeric_traits::zero(); - for (auto & t : row) { - T a = t.second; - column_info * ci = m_columns[t.first]; - if (a > numeric_traits::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 & row, T & low_bound); - bool get_maximal_row_value(std::unordered_map & row, T & low_bound) { - low_bound = numeric_traits::zero(); - for (auto & t : row) { - T a = t.second; - column_info * ci = m_columns[t.first]; - if (a < numeric_traits::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 & row, T & low_bound); - bool row_is_zero(std::unordered_map & row) { - for (auto & t : row) { - if (!is_zero(t.second)) - return false; - } - return true; - } + bool row_is_zero(std::unordered_map & row); - bool row_e_is_obsolete(std::unordered_map & 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 & 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 & 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 & row, unsigned row_index) { - T rs = m_constraints[row_index].m_rs; - if (row_is_zero(row)) { - if (rs > zero_of_type()) - 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 & 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()) - 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 & 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 & 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 & 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 & 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 & row) { - auto & constraint = m_constraints[i]; - std::vector removed; - for (auto & col : row) { - unsigned j = col.first; - lean_assert(m_columns.find(j) != m_columns.end()); - column_info * 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::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 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::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::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(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::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 * core_solver) const { - auto cit = this->m_columns.find(column); - if (cit == this->m_columns.end()) { - return numeric_traits::zero(); - } - - column_info * 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::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 * 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 * 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;} diff --git a/src/util/lp/lp_solver_instances.cpp b/src/util/lp/lp_solver_instances.cpp new file mode 100644 index 0000000000..2244ca9f39 --- /dev/null +++ b/src/util/lp/lp_solver_instances.cpp @@ -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::add_constraint(lean::lp_relation, double, unsigned int); +template void lean::lp_solver::cleanup(); +template void lean::lp_solver::count_slacks_and_artificials(); +template void lean::lp_solver::fill_column_names_for_core_solver(); +template void lean::lp_solver::fill_m_b(); +template void lean::lp_solver::fill_matrix_A_and_init_right_side(); +template void lean::lp_solver::flip_costs(); +template double lean::lp_solver::get_column_cost_value(unsigned int, lean::column_info*) const; +template int lean::lp_solver::get_column_index_by_name(std::string) const; +template double lean::lp_solver::get_column_value_with_core_solver(unsigned int, lean::lp_core_solver_base*) const; +template lean::column_info* lean::lp_solver::get_or_create_column_info(unsigned int); +template void lean::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); +template void lean::lp_solver::print_statistics_on_A(); +template bool lean::lp_solver::problem_is_empty(); +template void lean::lp_solver::scale(); +template void lean::lp_solver::set_scaled_cost(unsigned int); +template lean::lp_solver::~lp_solver(); +template void lean::lp_solver::add_constraint(lean::lp_relation, lean::mpq, unsigned int); +template void lean::lp_solver::cleanup(); +template void lean::lp_solver::count_slacks_and_artificials(); +template void lean::lp_solver::fill_column_names_for_core_solver(); +template void lean::lp_solver::fill_m_b(); +template void lean::lp_solver::fill_matrix_A_and_init_right_side(); +template void lean::lp_solver::flip_costs(); +template lean::mpq lean::lp_solver::get_column_cost_value(unsigned int, lean::column_info*) const; +template lean::mpq lean::lp_solver::get_column_value_by_name(std::string) const; +template lean::mpq lean::lp_solver::get_column_value_with_core_solver(unsigned int, lean::lp_core_solver_base*) const; +template lean::column_info* lean::lp_solver::get_or_create_column_info(unsigned int); +template void lean::lp_solver::give_symbolic_name_to_column(std::string, unsigned int); +template void lean::lp_solver::print_statistics_on_A(); +template bool lean::lp_solver::problem_is_empty(); +template void lean::lp_solver::scale(); +template void lean::lp_solver::set_scaled_cost(unsigned int); +template lean::lp_solver::~lp_solver(); +template double lean::lp_solver::get_column_value_by_name(std::string) const; diff --git a/src/util/lp/lu.cpp b/src/util/lp/lu.cpp new file mode 100644 index 0000000000..4b2bcb8cda --- /dev/null +++ b/src/util/lp/lu.cpp @@ -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 // print the nr x nc submatrix at the top left corner +void print_submatrix(sparse_matrix & m, unsigned mr, unsigned nc) { + std::vector> A; + std::vector widths; + for (unsigned i = 0; i < m.row_count() && i < mr ; i++) { + A.push_back(std::vector()); + for (unsigned j = 0; j < m.column_count() && j < nc; j++) { + A[i].push_back(T_to_string(static_cast(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 +void print_matrix(static_matrix &m) { + std::vector> A; + std::vector widths; + std::set> domain = m.get_domain(); + for (unsigned i = 0; i < m.row_count(); i++) { + A.push_back(std::vector()); + for (unsigned j = 0; j < m.column_count(); j++) { + A[i].push_back(T_to_string(static_cast(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 +void print_matrix(sparse_matrix& m) { + std::vector> A; + std::vector widths; + for (unsigned i = 0; i < m.row_count(); i++) { + A.push_back(std::vector()); + for (unsigned j = 0; j < m.column_count(); j++) { + A[i].push_back(T_to_string(static_cast(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 +X dot_product(const std::vector & a, const std::vector & b, unsigned l) { + auto r = zero_of_type(); + for (unsigned i = 0; i < l; i++) { + r += a[i] * b[i]; + } + return r; +} + + +template +one_elem_on_diag::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::one() / o.m_val; +#endif +} + +#ifdef LEAN_DEBUG +template +T one_elem_on_diag::get_elem(unsigned i, unsigned j) const { + if (i == j){ + if (j == m_i) { + return m_one_over_val; + } + return numeric_traits::one(); + } + + return numeric_traits::zero(); +} +#endif +template +void one_elem_on_diag::apply_from_left_to_T(indexed_vector & 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::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 +lu::lu(static_matrix const & A, + std::vector& basis, + std::vector & basis_heading, + lp_settings & settings, + std::vector & 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 " <(get_status()) << std::endl; + } + return; + } +#ifdef LEAN_DEBUG + // lean_assert(check_correctness()); +#endif +} +template +void lu::debug_test_of_basis(static_matrix const & A, std::vector & basis) { + std::set 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 +void lu::solve_By(std::vector & y) { + init_vector_y(y); + solve_By_when_y_is_ready(y); +} +template +void lu::solve_Bd_when_w_is_ready(std::vector & d, indexed_vector& 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 +template +void lu::solve_By_when_y_is_ready(std::vector & y) { + m_U.double_solve_U_y(y); + m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal + if (precise()) 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(); + } + } +} +template +void lu::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 +void lu::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 +void lu::print(indexed_vector & 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 +void lu::solve_Bd(unsigned a_column, std::vector & d, indexed_vector & w) { + init_vector_w(a_column, w); + solve_Bd_when_w_is_ready(d, w); +} + +template +void lu:: solve_yB_internal(std::vector& 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 +void lu::add_delta_to_solution(std::vector& yc, std::vector& y){ + unsigned i = y.size(); + while (i--) + y[i]+=yc[i]; +} + +template +void lu::find_error_of_yB(std::vector& yc, const std::vector& 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 +void lu::solve_yB(std::vector & y) { + std::vector 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 +void lu::apply_Q_R_to_U(permutation_matrix & r_wave) { + m_U.multiply_from_right(r_wave); + m_U.multiply_from_left_with_reverse(r_wave); +} +template +void lu::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 +void lu::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 +lu::~lu(){ + for (auto t : m_tail) { + delete t; + } +} +template +void lu::init_vector_y(std::vector & y) { + apply_lp_lists_to_y(y); + m_Q.apply_reverse_from_left(y); +} + +template +void lu::perform_transformations_on_w(indexed_vector& w) { + apply_lp_lists_to_w(w); + m_Q.apply_reverse_from_left(w); + lean_assert(numeric_traits::precise() || check_vector_for_small_values(w, m_settings)); +} + +// see Chvatal 24.3 +template +void lu::init_vector_w(unsigned entering, indexed_vector & w) { + w.clear(); + m_A.copy_column_to_vector(entering, w); // w = a, the column + perform_transformations_on_w(w); +} +template +void lu::apply_lp_lists_to_w(indexed_vector & 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 +void lu::apply_lp_lists_to_y(std::vector& y) { + for (unsigned i = 0; i < m_tail.size(); i++) { + m_tail[i]->apply_from_left(y, m_settings); + } +} +template +void lu::swap_rows(int j, int k) { + if (j != k) { + m_Q.transpose_from_left(j, k); + m_U.swap_rows(j, k); + } +} +template +void lu::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 +bool lu::pivot_the_row(int row) { + eta_matrix * 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 +eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j) { + eta_matrix *ret; + m_U.fill_eta_matrix(j, &ret); + return ret; +} +// we're processing the column j now +template +eta_matrix * lu::get_eta_matrix_for_pivot(unsigned j, sparse_matrix& copy_of_U) { + eta_matrix *ret; + copy_of_U.fill_eta_matrix(j, &ret); + return ret; +} + +template +void lu::print_basis() { + std::cout << "basis "; + for (unsigned i = 0; i < m_dim; i++) { + std::cout << m_basis[i] << " "; + } + std::cout << std::endl; +} +template +void lu::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 +unsigned lu::transform_U_to_V_by_replacing_column(unsigned leaving, indexed_vector & 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 +void lu::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 +void lu::check_apply_matrix_to_vector(matrix *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 +void lu::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 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 +void lu::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 +bool lu::is_correct() { +#ifdef LEAN_DEBUG + if (get_status() != LU_status::OK) { + return false; + } + dense_matrix left_side = get_left_side(); + dense_matrix right_side = get_right_side(); + return left_side == right_side; +#else + return true; +#endif +} + + +#ifdef LEAN_DEBUG +template +dense_matrix lu::tail_product() { + lean_assert(tail_size() > 0); + dense_matrix left_side = permutation_matrix(m_dim); + for (unsigned i = 0; i < tail_size(); i++) { + matrix* 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 +dense_matrix lu::get_left_side() { + dense_matrix left_side = get_B(*this); + for (unsigned i = 0; i < tail_size(); i++) { + matrix* 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 +dense_matrix lu::get_right_side() { + auto ret = U() * R(); + ret = Q() * ret; + return ret; +} +#endif + +// needed for debugging purposes +template +void lu::copy_w(T *buffer, indexed_vector & w) { + unsigned i = m_dim; + while (i--) { + buffer[i] = w[i]; + } +} + +// needed for debugging purposes +template +void lu::restore_w(T *buffer, indexed_vector & w) { + unsigned i = m_dim; + while (i--) { + w[i] = buffer[i]; + } +} +template +bool lu::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 +bool lu::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 +void lu::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 +void lu::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(&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 +void lu::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix & 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 +void lu::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) { + std::vector> & 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 +void lu::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::is_zero(v)) continue; // this column does not contribute to the solution + unsigned aj = m_U.adjust_row(j); + std::vector> & 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::is_zero(iv.m_value)); + if (col == j) continue; + if (numeric_traits::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::is_zero(delta) == false); + + if (numeric_traits::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::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 +row_eta_matrix *lu::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(replaced_column, lowest_row_of_the_bump, m_dim); +#else + auto ret = new row_eta_matrix(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::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 +void lu::replace_column(unsigned leaving, T pivot_elem, indexed_vector & 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 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 +void lu::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 +void lu::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) { + auto l = new one_elem_on_diag(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 +void init_factorization(lu* & factorization, static_matrix & m_A, std::vector & m_basis, std::vector & m_basis_heading, lp_settings &m_settings, std::vector & non_basic_columns) { + if (factorization != nullptr) { + delete factorization; + } + factorization = new lu(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 +dense_matrix get_B(lu& f) { + dense_matrix 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 +} diff --git a/src/util/lp/lu.h b/src/util/lp/lu.h index 7948617b27..c235947d77 100644 --- a/src/util/lp/lu.h +++ b/src/util/lp/lu.h @@ -21,77 +21,21 @@ #include #include "util/lp/row_eta_matrix.h" #include "util/lp/square_dense_submatrix.h" +#include "util/lp/dense_matrix.h" namespace lean { -template -std::string T_to_string(const T & t); // forward definition #ifdef LEAN_DEBUG template // print the nr x nc submatrix at the top left corner -void print_submatrix(sparse_matrix & m, unsigned mr, unsigned nc) { - std::vector> A; - std::vector widths; - for (unsigned i = 0; i < m.row_count() && i < mr ; i++) { - A.push_back(std::vector()); - for (unsigned j = 0; j < m.column_count() && j < nc; j++) { - A[i].push_back(T_to_string(static_cast(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 & m, unsigned mr, unsigned nc); template -void print_matrix(static_matrix &m) { - std::vector> A; - std::vector widths; - std::set> domain = m.get_domain(); - for (unsigned i = 0; i < m.row_count(); i++) { - A.push_back(std::vector()); - for (unsigned j = 0; j < m.column_count(); j++) { - A[i].push_back(T_to_string(static_cast(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 &m); template -void print_matrix(sparse_matrix& m) { - std::vector> A; - std::vector widths; - for (unsigned i = 0; i < m.row_count(); i++) { - A.push_back(std::vector()); - for (unsigned j = 0; j < m.column_count(); j++) { - A[i].push_back(T_to_string(static_cast(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& m); #endif -enum class LU_status { OK, Degenerated}; - template -X dot_product(const std::vector & a, const std::vector & b, unsigned l) { - auto r = zero_of_type(); - for (unsigned i = 0; i < l; i++) { - r += a[i] * b[i]; - } - return r; -} - +X dot_product(const std::vector & a, const std::vector & b, unsigned l); template class one_elem_on_diag: public tail_matrix { @@ -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::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::one(); - } - - return numeric_traits::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 & 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::zero(); - } - } + void apply_from_left_to_T(indexed_vector & w, lp_settings & settings); void conjugate_by_permutation(permutation_matrix & 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 class lu { LU_status m_status = LU_status::OK; @@ -198,335 +119,85 @@ public: std::vector& basis, std::vector & basis_heading, lp_settings & settings, - std::vector & 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 " <(get_status()) << std::endl; - } - return; - } -#ifdef LEAN_DEBUG - // lean_assert(check_correctness()); -#endif - } - - void debug_test_of_basis(static_matrix const & A, std::vector & basis) { - std::set 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 & non_basic_columns); + void debug_test_of_basis(static_matrix const & A, std::vector & 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 & y) { - init_vector_y(y); - solve_By_when_y_is_ready(y); - } + void solve_By(std::vector & y); - void solve_Bd_when_w_is_ready(std::vector & d, indexed_vector& 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 & d, indexed_vector& w ); template - void solve_By_when_y_is_ready(std::vector & y) { - m_U.double_solve_U_y(y); - m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal - if (precise()) 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(); - } - } - } + void solve_By_when_y_is_ready(std::vector & y); + void print_indexed_vector(indexed_vector & w, std::ofstream & f); + + void print_basis(std::ofstream & f); + void print_matrix_compact(std::ofstream & f); + void print(indexed_vector & w); + void solve_Bd(unsigned a_column, std::vector & d, indexed_vector & w); - void print_indexed_vector(indexed_vector & 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 & 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& y); - void solve_Bd(unsigned a_column, std::vector & d, indexed_vector & w) { - init_vector_w(a_column, w); - solve_Bd_when_w_is_ready(d, w); - } + void add_delta_to_solution(std::vector& yc, std::vector& y); - bool column_can_be_taken_to_basis(unsigned i) { - return m_basis_heading[i] < 0; - } + void find_error_of_yB(std::vector& yc, const std::vector& y); - void solve_yB_internal(std::vector& 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 & y); - void add_delta_to_solution(std::vector& yc, std::vector& y){ - unsigned i = y.size(); - while (i--) - y[i]+=yc[i]; - } + void apply_Q_R_to_U(permutation_matrix & r_wave); - void find_error_of_yB(std::vector& yc, const std::vector& 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 & y) { - std::vector 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 & 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 & y); - T B(unsigned i, unsigned j) { - return m_A(i, m_basis[j]); - } + void perform_transformations_on_w(indexed_vector& w); - void init_vector_y(std::vector & y) { - apply_lp_lists_to_y(y); - m_Q.apply_reverse_from_left(y); - } + void init_vector_w(unsigned entering, indexed_vector & w); + void apply_lp_lists_to_w(indexed_vector & w); + void apply_lp_lists_to_y(std::vector& y); + void swap_rows(int j, int k); - void perform_transformations_on_w(indexed_vector& w) { - apply_lp_lists_to_w(w); - m_Q.apply_reverse_from_left(w); - lean_assert(numeric_traits::precise() || check_vector_for_small_values(w, m_settings)); - } - - // see Chvatal 24.3 - void init_vector_w(unsigned entering, indexed_vector & 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 & 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& 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* tm) { m_tail.push_back(tm); } - bool pivot_the_row(int row) { - eta_matrix * 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 * get_eta_matrix_for_pivot(unsigned j) { - eta_matrix *ret; - m_U.fill_eta_matrix(j, &ret); - return ret; - } - // we're processing the column j now - eta_matrix * get_eta_matrix_for_pivot(unsigned j, sparse_matrix& copy_of_U) { - eta_matrix *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 * get_eta_matrix_for_pivot(unsigned j); + // we're processing the column j now + eta_matrix * get_eta_matrix_for_pivot(unsigned j, sparse_matrix& 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 & 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 & 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 *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 *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 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 & 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 left_side = get_left_side(); - dense_matrix 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 tail_product() { - lean_assert(tail_size() > 0); - dense_matrix left_side = permutation_matrix(m_dim); - for (unsigned i = 0; i < tail_size(); i++) { - matrix* 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 get_left_side() { - dense_matrix left_side = get_B(*this); - for (unsigned i = 0; i < tail_size(); i++) { - matrix* 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 tail_product(); + dense_matrix get_left_side(); - dense_matrix get_right_side() { - auto ret = U() * R(); - ret = Q() * ret; - return ret; - } + dense_matrix get_right_side(); #endif // needed for debugging purposes - void copy_w(T *buffer, indexed_vector & w) { - unsigned i = m_dim; - while (i--) { - buffer[i] = w[i]; - } - } + void copy_w(T *buffer, indexed_vector & w); // needed for debugging purposes - void restore_w(T *buffer, indexed_vector & 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 & 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(&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 & 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 & 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> & 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::is_zero(v)) continue; // this column does not contribute to the solution - unsigned aj = m_U.adjust_row(j); - std::vector> & 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::is_zero(iv.m_value)); - if (col == j) continue; - if (numeric_traits::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::is_zero(delta) == false); - - if (numeric_traits::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::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 *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(replaced_column, lowest_row_of_the_bump, m_dim); -#else - auto ret = new row_eta_matrix(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::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 *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 & 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 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 & 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(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 & w) { init_vector_w(entering, w); } }; // end of lu + template -void init_factorization(lu* & factorization, static_matrix & m_A, std::vector & m_basis, std::vector & m_basis_heading, lp_settings &m_settings, std::vector & non_basic_columns) { - if (factorization != nullptr) { - delete factorization; - } - factorization = new lu(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* & factorization, static_matrix & m_A, std::vector & m_basis, std::vector & m_basis_heading, lp_settings &m_settings, std::vector & non_basic_columns); #ifdef LEAN_DEBUG template -dense_matrix get_B(lu& f) { - dense_matrix 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 get_B(lu& f); #endif } diff --git a/src/util/lp/lu_instances.cpp b/src/util/lp/lu_instances.cpp new file mode 100644 index 0000000000..7b76005d92 --- /dev/null +++ b/src/util/lp/lu_instances.cpp @@ -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(std::vector > const&, std::vector > const&, unsigned int); +template void lean::lu::change_basis(unsigned int, unsigned int); +template lean::lu::lu(lean::static_matrix const&, std::vector >&, std::vector >&, lean::lp_settings&, std::vector >&); +template void lean::lu::push_matrix_to_tail(lean::tail_matrix*); +template void lean::lu::replace_column(unsigned int, double, lean::indexed_vector&); +template void lean::lu::restore_basis_change(unsigned int, unsigned int); +template void lean::lu::solve_Bd(unsigned int, std::vector >&, lean::indexed_vector&); +template lean::lu::~lu(); +template void lean::lu::change_basis(unsigned int, unsigned int); +template void lean::lu::push_matrix_to_tail(lean::tail_matrix*); +template void lean::lu::restore_basis_change(unsigned int, unsigned int); +template void lean::lu::solve_Bd(unsigned int, std::vector >&, lean::indexed_vector&); +template lean::lu::~lu(); +template void lean::lu >::change_basis(unsigned int, unsigned int); +template void lean::lu >::push_matrix_to_tail(lean::tail_matrix >*); +template void lean::lu >::restore_basis_change(unsigned int, unsigned int); +template void lean::lu >::solve_Bd(unsigned int, std::vector >&, lean::indexed_vector&); +template lean::lu >::~lu(); +template lean::mpq lean::dot_product(std::vector > const&, std::vector > const&, unsigned int); +template void lean::init_factorization(lean::lu*&, lean::static_matrix&, std::vector >&, std::vector >&, lean::lp_settings&, std::vector >&); +template void lean::init_factorization(lean::lu*&, lean::static_matrix&, std::vector >&, std::vector >&, lean::lp_settings&, std::vector >&); +template void lean::init_factorization >(lean::lu >*&, lean::static_matrix >&, std::vector >&, std::vector >&, lean::lp_settings&, std::vector >&); +#ifdef LEAN_DEBUG +template void lean::print_matrix(lean::sparse_matrix&); +template void lean::print_matrix(lean::sparse_matrix&); +template void lean::print_matrix(lean::static_matrix&); +template bool lean::lu::is_correct(); +template lean::dense_matrix lean::get_B(lean::lu&); +#endif +template void lean::lu::solve_yB(std::vector >&); +template void lean::lu::solve_yB(std::vector >&); +template void lean::lu::solve_By(std::vector >&); +template void lean::lu::solve_By(std::vector >&); +template void lean::lu::replace_column(unsigned int, lean::mpq, lean::indexed_vector&); +template void lean::lu >::replace_column(unsigned int, lean::mpq, lean::indexed_vector&); +template void lean::lu >::solve_yB(std::vector >&); +template void lean::lu >::solve_By(std::vector, std::allocator > >&); +template void lean::lu::init_vector_w(unsigned int, lean::indexed_vector&); +template lean::numeric_pair lean::dot_product >(std::vector > const&, std::vector, std::allocator > > const&, unsigned int); +template bool lean::lu::pivot_the_row(int); diff --git a/src/util/lp/matrix.cpp b/src/util/lp/matrix.cpp new file mode 100644 index 0000000000..a27ed0eaea --- /dev/null +++ b/src/util/lp/matrix.cpp @@ -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 +bool matrix::is_equal(const matrix& 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::precise()) { + if (a != b) return false; + } else if (fabs(numeric_traits::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 +void apply_to_vector(matrix & 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::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> & 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> & A, std::vector & 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> & A) { + std::vector 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 +void print_matrix(matrix const & m) { + if (&m == nullptr) { + std::cout << "null" << std::endl; + return; + } + std::vector> 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 diff --git a/src/util/lp/matrix.h b/src/util/lp/matrix.h new file mode 100644 index 0000000000..6d806352ba --- /dev/null +++ b/src/util/lp/matrix.h @@ -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 +#include +#include "util/lp/lp_settings.h" +namespace lean { +// used for debugging purposes only +template +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& other); + bool operator == (matrix const & other) { + return is_equal(other); + } + T operator()(unsigned i, unsigned j) const { return get_elem(i, j); } +}; + +template +void apply_to_vector(matrix & m, T * w); + + + + unsigned get_width_of_column(unsigned j, std::vector> & A); + void print_matrix_with_widths(std::vector> & A, std::vector & ws); + void print_string_matrix(std::vector> & A); + + +template +void print_matrix(matrix const & m); + +} +#endif diff --git a/src/util/lp/matrix_domain.h b/src/util/lp/matrix_domain.h deleted file mode 100644 index f3767a6163..0000000000 --- a/src/util/lp/matrix_domain.h +++ /dev/null @@ -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 -#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 -#include - -template -inline void hash_combine(std::size_t & seed, const T & v) { - std::hash hasher; - seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2); -} - -namespace std { -template struct hash> { - inline size_t operator()(const pair & v) const { - size_t seed = 0; - ::hash_combine(seed, v.first); - ::hash_combine(seed, v.second); - return seed; - } -}; -} - -namespace lean { -template -class matrix_domain { - std::vector> m_domain; -public: - matrix_domain(unsigned rows) { - while (rows--) { - std::unordered_map 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; - } -}; -} diff --git a/src/util/lp/matrix_instances.cpp b/src/util/lp/matrix_instances.cpp new file mode 100644 index 0000000000..3929178935 --- /dev/null +++ b/src/util/lp/matrix_instances.cpp @@ -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(lean::matrix const&); +template bool lean::matrix::is_equal(lean::matrix const&); +#endif diff --git a/src/util/lp/numeric_pair.h b/src/util/lp/numeric_pair.h index 2efd551162..e3fc7d3573 100644 --- a/src/util/lp/numeric_pair.h +++ b/src/util/lp/numeric_pair.h @@ -9,6 +9,7 @@ #include #include "util/numerics/mpq.h" #include "util/numerics/double.h" +#include "util/lp/lp_settings.h" namespace lean { template @@ -17,7 +18,7 @@ std::string T_to_string(const T & t); // forward definition template 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(); } }; diff --git a/src/util/lp/permutation_matrix.cpp b/src/util/lp/permutation_matrix.cpp new file mode 100644 index 0000000000..aadf3e8464 --- /dev/null +++ b/src/util/lp/permutation_matrix.cpp @@ -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 permutation_matrix::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 permutation_matrix::permutation_matrix(unsigned length, std::vector 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 void permutation_matrix::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 void permutation_matrix::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 template +void permutation_matrix:: apply_from_left_perm(std::vector & w) { +#ifdef LEAN_DEBUG + // dense_matrix deb(*this); + // L * deb_w = clone_vector(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(deb_w, w, row_count())); + // delete [] deb_w; +#endif +} + +template template +void permutation_matrix:: apply_from_left_perm(indexed_vector & w, lp_settings &) { +#ifdef LEAN_DEBUG + // dense_matrix deb(*this); + // T * deb_w = clone_vector(w.m_data, row_count()); + // deb.apply_from_right(deb_w); +#endif + std::vector t(w.m_index.size()); + std::vector 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(deb_w, w.m_data, row_count())); + // delete [] deb_w; +#endif +} + + +template void permutation_matrix:: apply_from_right(std::vector & w) { +#ifdef LEAN_DEBUG + // dense_matrix deb(*this); + // T * deb_w = clone_vector(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(deb_w, w, row_count())); + // delete [] deb_w; +#endif +} + +template template +void permutation_matrix:: copy_aside(std::vector & t, std::vector & tmp_index, indexed_vector & 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 template +void permutation_matrix:: clear_data(indexed_vector & 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(); + } +} + +template template +void permutation_matrix::apply_reverse_from_left(indexed_vector & w) { + // the result will be w = p(-1) * w +#ifdef LEAN_DEBUG + // dense_matrix deb(get_reverse()); + // L * deb_w = clone_vector(w.m_data, row_count()); + // deb.apply_from_left(deb_w); +#endif + std::vector t(w.m_index.size()); + std::vector 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(deb_w, w.m_data, row_count())); + // delete [] deb_w; +#endif +} + +template template +void permutation_matrix::apply_reverse_from_left(std::vector & w) { + // the result will be w = p(-1) * w +#ifdef LEAN_DEBUG + // dense_matrix deb(get_reverse()); + // T * deb_w = clone_vector(w, row_count()); + // deb.apply_from_left(deb_w); +#endif + std::vector 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(deb_w, w, row_count())); + // delete [] deb_w; +#endif +} + +template template +void permutation_matrix:: apply_reverse_from_right(std::vector & w) { + // the result will be w = w * p(-1) +#ifdef LEAN_DEBUG + // dense_matrix deb(get_reverse()); + // T * deb_w = clone_vector(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(deb_w, w, row_count())); + // delete deb_w; +#endif +} + +template void permutation_matrix:: 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 void permutation_matrix:: 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 unsigned * permutation_matrix::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 void permutation_matrix::multiply_by_permutation_from_left(permutation_matrix & 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 void permutation_matrix::multiply_by_permutation_from_right(permutation_matrix & 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 void permutation_matrix::multiply_by_reverse_from_right(permutation_matrix & 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 void permutation_matrix:: multiply_by_permutation_reverse_from_left(permutation_matrix & 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 void permutation_matrix::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 bool permutation_matrix::is_identity() const { + for (unsigned i = 0; i < m_length; i++) { + if (m_permutation[i] != i) { + return false; + } + } + return true; +} + + +#ifdef LEAN_DEBUG +template +permutation_generator::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 +permutation_generator::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 bool +permutation_generator::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 +inline unsigned number_of_inversions(permutation_matrix & 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 +T det_val_on_perm(permutation_matrix* u, const matrix& m) { + unsigned n = m.row_count(); + T ret = numeric_traits::one(); + for (unsigned i = 0; i < n; i++) { + unsigned j = (*u)[i]; + ret *= m(i, j); + } + return ret * sign(*u); +} + +template +T determinant(const matrix& m) { + lean_assert(m.column_count() == m.row_count()); + unsigned n = m.row_count(); + permutation_generator allp(n); + T ret = numeric_traits::zero(); + while (allp.move_next()){ + ret += det_val_on_perm(allp.current(), m); + } + return ret; +} +#endif +} diff --git a/src/util/lp/permutation_matrix.h b/src/util/lp/permutation_matrix.h index 43115f28ba..2f3931c5f4 100644 --- a/src/util/lp/permutation_matrix.h +++ b/src/util/lp/permutation_matrix.h @@ -6,6 +6,7 @@ */ #pragma once #include +#include #include "util/debug.h" #include "util/numerics/numeric_traits.h" #include "util/numerics/xnumeral.h" @@ -19,1131 +20,157 @@ #include "util/lp/sparse_vector.h" #include "util/lp/indexed_vector.h" #include "util/lp/lp_settings.h" -#include +#include "util/lp/matrix.h" +#include "util/lp/tail_matrix.h" namespace lean { -template -std::string T_to_string(const T & t) { - // return is_zero(t)?string(" "):string("."); - std::ostringstream strs; - strs << t; - std::string str = strs.str(); - return str; -} - -template <> -std::string T_to_string(const int & t) { - std::ostringstream strs; - strs << t; - std::string str = strs.str(); - return str; -} -template <> -std::string T_to_string(const unsigned & t) { - std::ostringstream strs; - strs << t; - std::string str = strs.str(); - return str; -} - - -std::string T_to_string(const mpq & t) { - std::ostringstream strs; - strs << t.get_double(); - std::string str = strs.str(); - return str; -} - -template -bool val_is_smaller_than_eps(T const & t, double const & eps) { - if (!numeric_traits::precise()) { - return numeric_traits::get_double(t) < eps; - } - return t <= numeric_traits::zero(); -} - - -inline bool is_even(int k) { - return (k/2)*2 == k; -} - -template -bool vectors_are_equal(T * a, T *b, unsigned n) { - double sum = 0; - double max = 0; - if (numeric_traits::precise()) { - for (unsigned i = 0; i < n; i ++){ - if (!numeric_traits::is_zero(a[i] - b[i])) { - std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl; - return false; - } - } - } else { - for (unsigned i = 0; i < n; i ++){ - double t = fabs(numeric_traits::get_double(a[i] - b[i])); - if (t > 0.000001) { - std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl; - return false; - } - if (t > max) { - max = t; - } - sum += t; - } - } - if (sum > 0.00001) { - return false; - } - // cout << "vectors_are_equal :" << "sum = " << sum << ", max = " << max << endl; - return true; -} - -template -bool vectors_are_equal(T * a, std::vector &b, unsigned n) { - if (numeric_traits::precise()) { - for (unsigned i = 0; i < n; i ++){ - if (!numeric_traits::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::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 -bool vectors_are_equal(const std::vector & a, const buffer &b) { - unsigned n = a.size(); - if (n != b.size()) return false; - if (numeric_traits::precise()) { - for (unsigned i = 0; i < n; i ++){ - if (!numeric_traits::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::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 -bool vectors_are_equal(const std::vector & a, const std::vector &b) { - unsigned n = a.size(); - if (n != b.size()) return false; - if (numeric_traits::precise()) { - for (unsigned i = 0; i < n; i ++){ - if (!numeric_traits::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::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 -X max_abs_in_vector(std::vector& t){ - X r(zero_of_type()); - for (auto & v : t) - r = std::max(abs(v) , r); - return r; -} - -template -X min_abs_in_vector(std::vector& t){ - X r(zero_of_type()); - for (auto & v : t) { - if (is_zero(v)) continue; - if (is_zero(r)) { - r = abs(v); - } else { - r = std::min(abs(v) , r); - } - } - return r; -} - #ifdef LEAN_DEBUG -// used for debugging purposes only -template -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& other) { - if (other.row_count() != row_count() || other.column_count() != column_count()) - return false; - lp_settings settings; - 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::precise()) { - if (a != b) return false; - } else if (fabs(numeric_traits::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; - } - bool operator == (matrix const & other) { - return is_equal(other); - } - T operator()(unsigned i, unsigned j) const { return get_elem(i, j); } -}; + inline bool is_even(int k) { return (k/2)*2 == k; } #endif -// These matrices appear at the end of the list -template -class tail_matrix -#ifdef LEAN_DEBUG - : public matrix -#endif -{ -public: - virtual void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) = 0; - virtual void apply_from_left(std::vector & w, lp_settings & settings) = 0; - virtual void apply_from_right(std::vector & w) = 0; - virtual ~tail_matrix() {} -}; + template + class permutation_matrix + : public tail_matrix { + unsigned m_length; + std::vector m_permutation; + std::vector m_rev; -#ifdef LEAN_DEBUG -template -void apply_to_vector(matrix & m, T * w) { - // here m is a square matrix - unsigned dim = m.row_count(); + class ref { + permutation_matrix & m_p; + unsigned m_i; + public: + ref(permutation_matrix & m, unsigned i):m_p(m), m_i(i) {} - T * wc = new T[dim]; + ref & operator=(unsigned v) { m_p.set_val(m_i, v); return *this; } - for (unsigned i = 0; i < dim; i++) { - wc[i] = w[i]; - } - - for (unsigned i = 0; i < dim; i++) { - T t = numeric_traits::zero(); - for (unsigned j = 0; j < dim; j++) { - t += m(i, j) * wc[j]; - } - w[i] = t; - } - delete [] wc; -} - - -// used for debugging purposes only -template -class dense_matrix: public matrix { -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) : 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::zero(); - } - - dense_matrix operator*=(matrix 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::zero(); - for (unsigned k = 0; k < a.column_count(); k++) { - v += get_elem(i, k) * a(k, j); - } - c.set_elem(i, j, v); + ref & operator=(ref const & v) { + m_p.set_val(m_i, v.m_p.m_permutation[v.m_i]); + return *this; } - } - *this = c; - return *this; - } + operator unsigned & () const { return m_p.m_permutation[m_i]; } + }; - dense_matrix & operator=(matrix 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; - } - - dense_matrix & 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; - } - - dense_matrix(dense_matrix 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); - } - - dense_matrix(matrix 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); - } - - void apply_from_right(T * w) { - T * t = new T[m_m]; - for (int i = 0; i < m_m; i ++) { - T v = numeric_traits::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; - } - - void apply_from_right(std::vector & w) { - T * t = new T[m_m]; - for (int i = 0; i < m_m; i ++) { - T v = numeric_traits::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; - } - - T * apply_from_left_with_different_dims(std::vector & w) { - T * t = new T[m_m]; - for (int i = 0; i < m_m; i ++) { - T v = numeric_traits::zero(); - for (int j = 0; j < m_n; j++) { - v += w[j]* get_elem(i, j); - } - t[i] = v; - } - - return t; - } - void apply_from_left(std::vector & w , lp_settings & ) { - apply_from_left(w); - } - - void apply_from_left(std::vector & w) { - T * t = new T[m_m]; - for (int i = 0; i < m_m; i ++) { - T v = numeric_traits::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; - } - - void 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::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; - } - - void apply_from_left_to_X(std::vector & w, lp_settings & ) { - std::vector t(m_m); - for (int i = 0; i < m_m; i ++) { - X v = zero_of_type(); - 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]; - } - } - - 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) { - thread_local T _0 = numeric_traits::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; - } - } - } - - void 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); - } - } - - void 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); - } - } - - void 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)); - } - } - - ~dense_matrix() { - delete [] m_values; - } -}; - -template -dense_matrix operator* (matrix & a, matrix & b){ - dense_matrix 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::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; -} - -inline unsigned get_width_of_column(unsigned j, std::vector> & 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; -} -#endif -inline void print_blanks(int n) { - lean_assert(n >= 0); - while (n--) { - std::cout << ' '; - } -} -#ifdef LEAN_DEBUG -inline void print_matrix_with_widths(std::vector> & A, std::vector & 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; - } -} - -inline void print_string_matrix(std::vector> & A) { - std::vector 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 -void print_matrix(matrix const & m) { - if (&m == nullptr) { - std::cout << "null" << std::endl; - return; - } - std::vector> 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 - -template -class permutation_matrix - : public tail_matrix { - unsigned m_length; - std::vector m_permutation; - std::vector m_rev; - - class ref { - permutation_matrix & m_p; - unsigned m_i; public: - ref(permutation_matrix & m, unsigned i):m_p(m), m_i(i) {} + permutation_matrix() : m_length(0) {} + permutation_matrix(unsigned length); - ref & operator=(unsigned v) { m_p.set_val(m_i, v); return *this; } + permutation_matrix(unsigned length, std::vector const & values); + // create a unit permutation of the given length + void init(unsigned length); - ref & operator=(ref const & v) { - m_p.set_val(m_i, v.m_p.m_permutation[v.m_i]); - return *this; - } - operator unsigned & () const { return m_p.m_permutation[m_i]; } - }; - -public: - permutation_matrix() : m_length(0) {} - 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; - } - - permutation_matrix(unsigned length, std::vector 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 - void 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; - } - - unsigned get_rev(unsigned i) { - return m_rev[i]; - } + unsigned get_rev(unsigned i) { return m_rev[i]; } #ifdef LEAN_DEBUG - permutation_matrix get_inverse() const { - return permutation_matrix(m_length, m_rev); - } - void 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 << "]"; - } + permutation_matrix get_inverse() const { + return permutation_matrix(m_length, m_rev); } - std::cout << std::endl; - } + void print() const; #endif - ref operator[](unsigned i) { return ref(*this, i); } + ref operator[](unsigned i) { return ref(*this, i); } - unsigned operator[](unsigned i) const { return m_permutation[i]; } + unsigned operator[](unsigned i) const { return m_permutation[i]; } - template - void apply_from_left_perm(std::vector & w) { + template + void apply_from_left_perm(std::vector & w); + + void apply_from_left(std::vector & w, lp_settings &) { apply_from_left_perm(w); } + + void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) { apply_from_left_perm(w, settings); } + + + template + void apply_from_left_perm(indexed_vector & w, lp_settings &); + + + void apply_from_right(std::vector & w); + + template + void copy_aside(std::vector & t, std::vector & tmp_index, indexed_vector & w); + + template + void clear_data(indexed_vector & w); + + template + void apply_reverse_from_left(indexed_vector & w); + + template + void apply_reverse_from_left(std::vector & w); + template + void apply_reverse_from_right(std::vector & w) ; + + void set_val(unsigned i, unsigned pi) { + lean_assert(i < m_length && pi < m_length); m_permutation[i] = pi; m_rev[pi] = i; } + + void transpose_from_left(unsigned i, unsigned j); + + unsigned apply_reverse(unsigned i) const { return m_rev[i]; } + + void transpose_from_right(unsigned i, unsigned j); #ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // L * deb_w = clone_vector(w, row_count()); - // deb.apply_from_left(deb_w); + T get_elem(unsigned i, unsigned j) const{ + return m_permutation[i] == j? numeric_traits::one() : numeric_traits::zero(); + } + unsigned row_count() const{ return m_length; } + unsigned column_count() const { return m_length; } + virtual void set_number_of_rows(unsigned /*m*/) { } + virtual void set_number_of_columns(unsigned /*n*/) { } #endif - L * t = new L[m_length]; - for (unsigned i = 0; i < m_length; i++) { - t[i] = w[m_permutation[i]]; - } + unsigned * clone_m_permutation(); - for (unsigned i = 0; i < m_length; i++) { - w[i] = t[i]; - } - delete [] t; -#ifdef LEAN_DEBUG - // lean_assert(vectors_are_equal(deb_w, w, row_count())); - // delete [] deb_w; -#endif - } + void multiply_by_permutation_from_left(permutation_matrix & p); - void apply_from_left(std::vector & w, lp_settings &) { - apply_from_left_perm(w); - } + // this is multiplication in the matrix sense + void multiply_by_permutation_from_right(permutation_matrix & p); - void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) { - apply_from_left_perm(w, settings); - } + void multiply_by_reverse_from_right(permutation_matrix & q); + void multiply_by_permutation_reverse_from_left(permutation_matrix & r); - template - void apply_from_left_perm(indexed_vector & w, lp_settings &) { -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // T * deb_w = clone_vector(w.m_data, row_count()); - // deb.apply_from_right(deb_w); -#endif - std::vector t(w.m_index.size()); - std::vector 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(deb_w, w.m_data, row_count())); - // delete [] deb_w; -#endif - } + void shrink_by_one_identity(); + bool is_identity() const; - void apply_from_right(std::vector & w) { -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // T * deb_w = clone_vector(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]]; - } + unsigned size() const { return m_length; } - for (unsigned i = 0; i < m_length; i++) { - w[i] = t[i]; - } - delete [] t; -#ifdef LEAN_DEBUG - // lean_assert(vectors_are_equal(deb_w, w, row_count())); - // delete [] deb_w; -#endif - } - - template - void copy_aside(std::vector & t, std::vector & tmp_index, indexed_vector & 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 - void clear_data(indexed_vector & 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(); - } - } - - template - void apply_reverse_from_left(indexed_vector & w) { - // the result will be w = p(-1) * w -#ifdef LEAN_DEBUG - // dense_matrix deb(get_reverse()); - // L * deb_w = clone_vector(w.m_data, row_count()); - // deb.apply_from_left(deb_w); -#endif - std::vector t(w.m_index.size()); - std::vector 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(deb_w, w.m_data, row_count())); - // delete [] deb_w; -#endif - } - - template - void apply_reverse_from_left(std::vector & w) { - // the result will be w = p(-1) * w -#ifdef LEAN_DEBUG - // dense_matrix deb(get_reverse()); - // T * deb_w = clone_vector(w, row_count()); - // deb.apply_from_left(deb_w); -#endif - std::vector 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(deb_w, w, row_count())); - // delete [] deb_w; -#endif - } - template - void apply_reverse_from_right(std::vector & w) { - // the result will be w = w * p(-1) -#ifdef LEAN_DEBUG - // dense_matrix deb(get_reverse()); - // T * deb_w = clone_vector(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(deb_w, w, row_count())); - // delete deb_w; -#endif - } - - void set_val(unsigned i, unsigned pi) { - lean_assert(i < m_length && pi < m_length); - m_permutation[i] = pi; - m_rev[pi] = i; - } - - void 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); - } - - unsigned apply_reverse(unsigned i) const { - return m_rev[i]; - } - - void 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); - } -#ifdef LEAN_DEBUG - T get_elem(unsigned i, unsigned j) const{ - return m_permutation[i] == j? numeric_traits::one() : numeric_traits::zero(); - } - unsigned row_count() const{ return m_length; } - unsigned column_count() const { return m_length; } - virtual void set_number_of_rows(unsigned /*m*/) { } - virtual void set_number_of_columns(unsigned /*n*/) { } -#endif - - unsigned * 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; - } - - void multiply_by_permutation_from_left(permutation_matrix & 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 - void multiply_by_permutation_from_right(permutation_matrix & 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; - } - - void multiply_by_reverse_from_right(permutation_matrix & 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; - } - - void multiply_by_permutation_reverse_from_left(permutation_matrix & 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; - } - - void 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; - } - } - - bool is_identity() const { - for (unsigned i = 0; i < m_length; i++) { - if (m_permutation[i] != i) { - return false; - } - } - return true; - } - - unsigned size() const { return m_length; } - - unsigned * values() const { return m_permutation; } -}; // end of the permutation class + unsigned * values() const { return m_permutation; } + }; // end of the permutation class #ifdef LEAN_DEBUG -template -class permutation_generator { - unsigned m_n; - permutation_generator* m_lower; - bool m_done = false; - permutation_matrix m_current; - unsigned m_last; -public: - 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; - } - - 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) { + template + class permutation_generator { + unsigned m_n; + permutation_generator* m_lower; + bool m_done = false; + permutation_matrix m_current; + unsigned m_last; + public: + permutation_generator(unsigned n); + permutation_generator(const permutation_generator & o); + bool move_next(); + + ~permutation_generator() { if (m_lower != nullptr) { - m_lower = new permutation_generator(o.m_lower); - } else { - m_lower = nullptr; - } + delete m_lower; } - - bool 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; - } - } - } } - - ~permutation_generator() { - if (m_lower != nullptr) { - delete m_lower; - } - } - + permutation_matrix *current() { return &m_current; } -}; + }; -template -inline unsigned number_of_inversions(permutation_matrix & 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++; - } - } + template + inline unsigned number_of_inversions(permutation_matrix & p); + + template + int sign(permutation_matrix & p) { + return is_even(number_of_inversions(p))? 1: -1; } - return ret; -} + + template + T det_val_on_perm(permutation_matrix* u, const matrix& m); -template -int sign(permutation_matrix & p) { - return is_even(number_of_inversions(p))? 1: -1; -} - -template -T det_val_on_perm(permutation_matrix* u, const matrix& m) { - unsigned n = m.row_count(); - T ret = numeric_traits::one(); - for (unsigned i = 0; i < n; i++) { - unsigned j = (*u)[i]; - ret *= m(i, j); - } - return ret * sign(*u); -} - -template -T determinant(const matrix& m) { - lean_assert(m.column_count() == m.row_count()); - unsigned n = m.row_count(); - permutation_generator allp(n); - T ret = numeric_traits::zero(); - while (allp.move_next()){ - ret += det_val_on_perm(allp.current(), m); - } - return ret; -} - - -#endif -// this matrix has ones on the diagonal, outside of a column -// and one non-zero element off the diagonal in the column - -template -struct one_off_diagonal_matrix: - public tail_matrix { - unsigned m_i; // the element row - unsigned m_j; // the element column - T m_val_jj; // the value in the column's diagonal element - T m_val_ij; // the value off the diagonal element -public: - one_off_diagonal_matrix(unsigned i, unsigned j, T val_at_jj, T val_at_ij): m_i(i), m_j(j), m_val_jj(val_at_jj), m_val_ij(val_at_ij) { - } - - one_off_diagonal_matrix(const one_off_diagonal_matrix * o):m_i(o.m_i), m_j(o.m_j), m_val_jj(o.m_val_jj), m_val_ij(o.m_val_ij) { -#ifdef LEAN_DEBUG - m_m = m_n = o.m_m; + template + T determinant(const matrix& m); #endif } - - void conjugate_by_permutation(permutation_matrix & p) { - // this = p * this * p(-1) -#ifdef LEAN_DEBUG - // auto rev = p.get_reverse(); - // auto deb = ((*this) * rev); - // deb = p * deb; -#endif - m_j = p.get_rev(m_j); - m_i = p.get_rev(m_i); -#ifdef LEAN_DEBUG - // lean_assert(deb == *this); -#endif - } -#ifdef LEAN_DEBUG - unsigned m_m; - unsigned m_n; - virtual void set_number_of_rows(unsigned m) { m_n = m_m = m; } - virtual void set_number_of_columns(unsigned n) {m_m = m_n = n; } - - T get_elem (unsigned i, unsigned j) const { - if (j == m_j){ - if (i == m_j){ - return 1 / m_val_jj; - } - if (i == m_i) { - return m_val_ij; - } - return numeric_traits::zero(); - } - - return i == j ? numeric_traits::one() : numeric_traits::zero(); - } - - unsigned row_count() const { return m_m; } // not defined } - unsigned column_count() const { return m_n; } // not defined } -#endif - void apply_from_left(std::vector & w, lp_settings &) { -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // T * deb_w = clone_vector(w, row_count()); - // deb.apply_from_left(deb_w); -#endif - auto wj = w[m_j]; - w[m_j] = wj / m_val_jj; - w[m_i] += wj * m_val_ij; -#ifdef LEAN_DEBUG - // lean_assert(vectors_are_equal(deb_w, w, row_count())); - // delete deb_w; -#endif - } - - template - void apply_from_left_local(indexed_vector & w, lp_settings & settings) { -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // T * deb_w = clone_vector(w.m_data, row_count()); - // deb.apply_from_left(deb_w); -#endif - if (is_zero(w[m_j])) { - return; - } - - bool m_i_is_zero = is_zero(w[m_i]); - L wj = w[m_j]; - w[m_j] = wj / m_val_jj; - if (m_i_is_zero) { - w[m_i] = wj * m_val_ij; - w.m_index.push_back(m_i); - } else { - w[m_i] += wj * m_val_ij; // we can get a zero here - do we need to check for it - } - - lean_assert(check_vector_for_small_values(w, settings )); - -#ifdef LEAN_DEBUG - // lean_assert(vectors_are_equal(deb_w, w.m_data, row_count())); - // delete deb_w; -#endif - } - - void apply_from_left_to_T(indexed_vector &w, lp_settings &settings) { - apply_from_left_local(w, settings); - } - void apply_from_right(std::vector & w) { -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // T * deb_w = clone_vector(w, row_count()); - // deb.apply_from_right(deb_w); -#endif - w[m_j] = w[m_j] / m_val_jj + m_val_ij * w[m_i]; -#ifdef LEAN_DEBUG - // lean_assert(vectors_are_equal(deb_w, w, row_count())); - // delete deb_w; -#endif - } -}; // end of one_off_diagonal_matrix - -} diff --git a/src/util/lp/permutation_matrix_instances.cpp b/src/util/lp/permutation_matrix_instances.cpp new file mode 100644 index 0000000000..4832c0cb27 --- /dev/null +++ b/src/util/lp/permutation_matrix_instances.cpp @@ -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::apply_from_right(std::vector >&); +template void lean::permutation_matrix::init(unsigned int); +template bool lean::permutation_matrix::is_identity() const; +template void lean::permutation_matrix::multiply_by_permutation_from_left(lean::permutation_matrix&); +template void lean::permutation_matrix::multiply_by_permutation_reverse_from_left(lean::permutation_matrix&); +template void lean::permutation_matrix::multiply_by_reverse_from_right(lean::permutation_matrix&); +template lean::permutation_matrix::permutation_matrix(unsigned int, std::vector > const&); +template void lean::permutation_matrix::transpose_from_left(unsigned int, unsigned int); +template void lean::permutation_matrix::apply_from_right(std::vector >&); +template lean::permutation_matrix::permutation_matrix(unsigned int); +template void lean::permutation_matrix::transpose_from_left(unsigned int, unsigned int); +template void lean::permutation_matrix::transpose_from_right(unsigned int, unsigned int); +template void lean::permutation_matrix::apply_from_right(std::vector >&); +template bool lean::permutation_matrix::is_identity() const; +template void lean::permutation_matrix::multiply_by_permutation_from_left(lean::permutation_matrix&); +template void lean::permutation_matrix::multiply_by_permutation_from_right(lean::permutation_matrix&); +template void lean::permutation_matrix::multiply_by_permutation_reverse_from_left(lean::permutation_matrix&); +template void lean::permutation_matrix::multiply_by_reverse_from_right(lean::permutation_matrix&); +template lean::permutation_matrix::permutation_matrix(unsigned int); +template void lean::permutation_matrix::transpose_from_left(unsigned int, unsigned int); +template void lean::permutation_matrix::transpose_from_right(unsigned int, unsigned int); +template void lean::permutation_matrix >::apply_from_right(std::vector >&); +template bool lean::permutation_matrix >::is_identity() const; +template void lean::permutation_matrix >::multiply_by_permutation_from_left(lean::permutation_matrix >&); +template void lean::permutation_matrix >::multiply_by_permutation_from_right(lean::permutation_matrix >&); +template void lean::permutation_matrix >::multiply_by_permutation_reverse_from_left(lean::permutation_matrix >&); +template void lean::permutation_matrix >::multiply_by_reverse_from_right(lean::permutation_matrix >&); +template lean::permutation_matrix >::permutation_matrix(unsigned int); +template void lean::permutation_matrix >::transpose_from_left(unsigned int, unsigned int); +template void lean::permutation_matrix >::transpose_from_right(unsigned int, unsigned int); +template void lean::permutation_matrix::apply_from_left_perm(lean::indexed_vector&, lean::lp_settings&); +template void lean::permutation_matrix::apply_from_left_perm(std::vector >&); +template void lean::permutation_matrix::apply_reverse_from_left(lean::indexed_vector&); +template void lean::permutation_matrix::apply_reverse_from_left(std::vector >&); +template void lean::permutation_matrix::apply_reverse_from_right(std::vector >&); +template void lean::permutation_matrix::transpose_from_right(unsigned int, unsigned int); +template void lean::permutation_matrix::apply_from_left_perm(lean::indexed_vector&, lean::lp_settings&); +template void lean::permutation_matrix::apply_from_left_perm(std::vector >&); +template void lean::permutation_matrix::apply_from_left_perm(lean::indexed_vector&, lean::lp_settings&); +template void lean::permutation_matrix::apply_from_left_perm(std::vector >&); +template void lean::permutation_matrix::apply_reverse_from_left(lean::indexed_vector&); +template void lean::permutation_matrix::apply_reverse_from_left(std::vector >&); +template void lean::permutation_matrix::apply_reverse_from_right(std::vector >&); +template void lean::permutation_matrix >::apply_from_left_perm(lean::indexed_vector&, lean::lp_settings&); +template void lean::permutation_matrix >::apply_from_left_perm >(std::vector, std::allocator > >&); +template void lean::permutation_matrix >::apply_reverse_from_left(lean::indexed_vector&); +template void lean::permutation_matrix >::apply_reverse_from_left(std::vector >&); +template void lean::permutation_matrix >::apply_reverse_from_left >(std::vector, std::allocator > >&); +template void lean::permutation_matrix >::apply_reverse_from_right(std::vector >&); +template void lean::permutation_matrix::multiply_by_permutation_from_right(lean::permutation_matrix&); + +#ifdef LEAN_DEBUG +template bool lean::permutation_generator::move_next(); +template lean::permutation_generator::permutation_generator(unsigned int); +#endif +template lean::permutation_matrix::permutation_matrix(unsigned int); diff --git a/src/util/lp/row_eta_matrix.cpp b/src/util/lp/row_eta_matrix.cpp new file mode 100644 index 0000000000..3a00fa883b --- /dev/null +++ b/src/util/lp/row_eta_matrix.cpp @@ -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 +void row_eta_matrix::apply_from_left(std::vector & w, lp_settings &) { +// #ifdef LEAN_DEBUG +// dense_matrix deb(*this); +// auto clone_w = clone_vector(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(clone_w, w, m_dimension)); +// delete [] clone_w; +// #endif + } + +template + void row_eta_matrix::apply_from_left_local_to_T(indexed_vector & 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(); + 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 + void row_eta_matrix::apply_from_left_local_to_X(indexed_vector & 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(); + 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 + void row_eta_matrix::apply_from_right(std::vector & w) { + T w_row = w[m_row]; + if (numeric_traits::is_zero(w_row)) return; +#ifdef LEAN_DEBUG + // dense_matrix deb(*this); + // auto clone_w = clone_vector(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(clone_w, w, m_dimension)); + // delete clone_w; +#endif + } + + template + void row_eta_matrix::apply_from_right(indexed_vector & w) { + T w_row = w[m_row]; + if (numeric_traits::is_zero(w_row)) return; +#ifdef LEAN_DEBUG + // dense_matrix deb(*this); + // auto clone_w = clone_vector(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::is_zero(old_val)) { + w.m_index.push_back(it.index()); + } else if (numeric_traits::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(clone_w, w.m_data, m_dimension)); + // for (unsigned i = 0; i < m_dimension; i++) { + // if (!numeric_traits::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 + void row_eta_matrix::conjugate_by_permutation(permutation_matrix & 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 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 + T row_eta_matrix::get_elem(unsigned row, unsigned col) const { + if (row == m_row){ + if (col == row) { + return numeric_traits::one(); + } + return m_row_vector[col]; + } + + return col == row ? numeric_traits::one() : numeric_traits::zero(); + } +#endif +} + diff --git a/src/util/lp/row_eta_matrix.h b/src/util/lp/row_eta_matrix.h index a7a06330b5..7f41febe29 100644 --- a/src/util/lp/row_eta_matrix.h +++ b/src/util/lp/row_eta_matrix.h @@ -19,7 +19,7 @@ #include #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 & w, lp_settings & -#ifdef LEAN_DEBUG - // settings -#endif -) { -// #ifdef LEAN_DEBUG -// dense_matrix deb(*this); -// auto clone_w = clone_vector(w, m_dimension); -// deb.apply_from_left(clone_w, settings); -// #endif - auto w_at_row = w[m_row]; - for (auto it = sparse_vector_iterator(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(clone_w, w, m_dimension)); -// delete [] clone_w; -// #endif - } + void apply_from_left(std::vector & w, lp_settings &); - template - void apply_from_left_local(indexed_vector & w, lp_settings & settings) { -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(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(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(); - 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(clone_w, w.m_data, m_dimension)); - // delete clone_w; -#endif - } + void apply_from_left_local_to_T(indexed_vector & w, lp_settings & settings); + void apply_from_left_local_to_X(indexed_vector & w, lp_settings & settings); void apply_from_left_to_T(indexed_vector & 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 & w) { - T w_row = w[m_row]; - if (numeric_traits::is_zero(w_row)) return; -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(w, m_dimension); - // deb.apply_from_right(clone_w); -#endif - for (auto it = sparse_vector_iterator(m_row_vector); !it.done(); it.move()) { - w[it.index()] += w_row * it.value(); - } -#ifdef LEAN_DEBUG - // lean_assert(vectors_are_equal(clone_w, w, m_dimension)); - // delete clone_w; -#endif - } + void apply_from_right(std::vector & w); + void apply_from_right(indexed_vector & w); - void apply_from_right(indexed_vector & w) { - T w_row = w[m_row]; - if (numeric_traits::is_zero(w_row)) return; -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // auto clone_w = clone_vector(w.m_data, m_dimension); - // deb.apply_from_right(clone_w); -#endif - for (auto it = sparse_vector_iterator(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::is_zero(old_val)) { - w.m_index.push_back(it.index()); - } else if (numeric_traits::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(clone_w, w.m_data, m_dimension)); - // for (unsigned i = 0; i < m_dimension; i++) { - // if (!numeric_traits::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 & 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 columns; - for (auto it = sparse_vector_iterator(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::one(); - } - return m_row_vector[col]; - } - - return col == row ? numeric_traits::one() : numeric_traits::zero(); - } + void conjugate_by_permutation(permutation_matrix & 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*/) { } diff --git a/src/util/lp/row_eta_matrix_instances.cpp b/src/util/lp/row_eta_matrix_instances.cpp new file mode 100644 index 0000000000..de5e25aeb8 --- /dev/null +++ b/src/util/lp/row_eta_matrix_instances.cpp @@ -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::conjugate_by_permutation(permutation_matrix&); +template void row_eta_matrix >::conjugate_by_permutation(permutation_matrix >&); +template void row_eta_matrix::conjugate_by_permutation(permutation_matrix&); +#ifdef LEAN_DEBUG +template mpq row_eta_matrix::get_elem(unsigned int, unsigned int) const; +template mpq row_eta_matrix >::get_elem(unsigned int, unsigned int) const; +template double row_eta_matrix::get_elem(unsigned int, unsigned int) const; +#endif +template void row_eta_matrix::apply_from_left(std::vector >&, lp_settings&); +template void row_eta_matrix::apply_from_right(std::vector >&); +template void row_eta_matrix >::apply_from_left(std::vector, std::allocator > >&, lp_settings&); +template void row_eta_matrix >::apply_from_right(std::vector >&); +template void row_eta_matrix::apply_from_left(std::vector >&, lp_settings&); +template void row_eta_matrix::apply_from_right(std::vector >&); +template void row_eta_matrix::apply_from_left_to_T(indexed_vector&, lp_settings&); +template void row_eta_matrix::apply_from_left_local_to_T(indexed_vector&, lp_settings&); +template void row_eta_matrix >::apply_from_left_to_T(indexed_vector&, lp_settings&); +template void row_eta_matrix >::apply_from_left_local_to_T(indexed_vector&, lp_settings&); +template void row_eta_matrix::apply_from_left_to_T(indexed_vector&, lp_settings&); +template void row_eta_matrix::apply_from_left_local_to_T(indexed_vector&, lp_settings&); +} diff --git a/src/util/lp/scaler.cpp b/src/util/lp/scaler.cpp new file mode 100644 index 0000000000..4fc0cc879b --- /dev/null +++ b/src/util/lp/scaler.cpp @@ -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 T scaler::right_side_balance() { + T ret = zero_of_type(); + unsigned i = m_A.row_count(); + while (i--) { + T rs = abs(convert_struct::convert(m_b[i])); + if (!is_zero(rs)) { + numeric_traits::log(rs); + ret += rs * rs; + } + } + return ret; +} + +template T scaler::A_min() const { + T min = zero_of_type(); + 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 T scaler::A_max() const { + T max = zero_of_type(); + 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 T scaler::get_A_ratio() const { + T min = A_min(); + T max = A_max(); + T ratio = max / min; + return ratio; +} + +template T scaler::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 T scaler::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 void scaler::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() && min > zero_of_type()); + T gm = T(sqrt(numeric_traits::get_double(max*min))); + m_A.divide_row_by_constant(i, gm); + m_b[i] /= gm; + } +} + +template void scaler::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::get_double(max*min))); + m_A.scale_column(i, gm); + m_column_scale[i]*=gm; + } +} + +template void scaler::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 bool scaler::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 void scaler::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 void scaler::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 void scaler::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 bool scaler::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 bool scaler::scale() { + if (numeric_traits::precise()) return true; + if (m_settings.scale_with_ratio) + return scale_with_ratio(); + return scale_with_log_balance(); +} + +template void scaler::scale_rows() { + for (unsigned i = 0; i < m_A.row_count(); i++) + scale_row(i); +} + +template void scaler::scale_row(unsigned i) { + T row_max = std::max(m_A.get_max_abs_in_row(i), abs(convert_struct::convert(m_b[i]))); + T alpha = numeric_traits::one(); + if (numeric_traits::is_zero(row_max)) { + return; + } + if (numeric_traits::get_double(row_max) < m_scaling_minimum) { + do { + alpha *= 2; + row_max *= 2; + } while (numeric_traits::get_double(row_max) < m_scaling_minimum); + m_A.scale_row(i, alpha); + m_b[i] *= alpha; + } else if (numeric_traits::get_double(row_max) > m_scaling_maximum) { + do { + alpha /= 2; + row_max /= 2; + } while (numeric_traits::get_double(row_max) > m_scaling_maximum); + m_A.scale_row(i, alpha); + m_b[i] *= alpha; + } +} + +template void scaler::scale_column(unsigned i) { + T column_max = m_A.get_max_abs_in_column(i); + T alpha = numeric_traits::one(); + + if (numeric_traits::is_zero(column_max)){ + return; // the column has zeros only + } + + if (numeric_traits::get_double(column_max) < m_scaling_minimum) { + do { + alpha *= 2; + column_max *= 2; + } while (numeric_traits::get_double(column_max) < m_scaling_minimum); + } else if (numeric_traits::get_double(column_max) > m_scaling_maximum) { + do { + alpha /= 2; + column_max /= 2; + } while (numeric_traits::get_double(column_max) > m_scaling_maximum); + } else { + return; + } + m_A.scale_column(i, alpha); + m_column_scale[i] = alpha; +} + +template void scaler::scale_columns() { + for (unsigned i = 0; i < m_A.column_count(); i++) { + scale_column(i); + } +} +} diff --git a/src/util/lp/scaler.h b/src/util/lp/scaler.h index 5a64a25eb1..bfb7ccf3ad 100644 --- a/src/util/lp/scaler.h +++ b/src/util/lp/scaler.h @@ -12,6 +12,7 @@ #include /* printf, fopen */ #include /* exit, EXIT_FAILURE */ #include "util/numerics/double.h" +#include "util/lp/static_matrix.h" namespace lean { // for scaling an LP template @@ -36,233 +37,45 @@ public: m_column_scale.resize(m_A.column_count(), numeric_traits::one()); } - T right_side_balance() { - T ret = zero_of_type(); - unsigned i = m_A.row_count(); - while (i--) { - T rs = abs(convert_struct::convert(m_b[i])); - if (!is_zero(rs)) { - numeric_traits::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(); - 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(); - 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() && min > zero_of_type()); - T gm = T(sqrt(numeric_traits::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::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::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::convert(m_b[i]))); - T alpha = numeric_traits::one(); - if (numeric_traits::is_zero(row_max)) { - return; - } - if (numeric_traits::get_double(row_max) < m_scaling_minimum) { - do { - alpha *= 2; - row_max *= 2; - } while (numeric_traits::get_double(row_max) < m_scaling_minimum); - m_A.scale_row(i, alpha); - m_b[i] *= alpha; - } else if (numeric_traits::get_double(row_max) > m_scaling_maximum) { - do { - alpha /= 2; - row_max /= 2; - } while (numeric_traits::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::one(); + void scale_column(unsigned i); - if (numeric_traits::is_zero(column_max)){ - return; // the column has zeros only - } - - if (numeric_traits::get_double(column_max) < m_scaling_minimum) { - do { - alpha *= 2; - column_max *= 2; - } while (numeric_traits::get_double(column_max) < m_scaling_minimum); - } else if (numeric_traits::get_double(column_max) > m_scaling_maximum) { - do { - alpha /= 2; - column_max /= 2; - } while (numeric_traits::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(); }; } diff --git a/src/util/lp/scaler_instances.cpp b/src/util/lp/scaler_instances.cpp new file mode 100644 index 0000000000..634408fab4 --- /dev/null +++ b/src/util/lp/scaler_instances.cpp @@ -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::scale(); +template bool lean::scaler::scale(); diff --git a/src/util/lp/sparse_matrix.cpp b/src/util/lp/sparse_matrix.cpp new file mode 100644 index 0000000000..0a9b48cc70 --- /dev/null +++ b/src/util/lp/sparse_matrix.cpp @@ -0,0 +1,1110 @@ +/* + 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/sparse_matrix.h" +namespace lean { + template + void sparse_matrix::copy_column_from_static_matrix(unsigned col, static_matrix const &A, unsigned col_index_in_the_new_matrix) { + std::vector> const & A_col_vector = A.m_columns[col]; + unsigned size = A_col_vector.size(); + std::vector> & new_column_vector = m_columns[col_index_in_the_new_matrix].m_values; + for (unsigned l = 0; l < size; l++) { + column_cell const & col_cell = A_col_vector[l]; + unsigned col_offset = new_column_vector.size(); + std::vector> & row_vector = m_rows[col_cell.m_i]; + unsigned row_offset = row_vector.size(); + new_column_vector.push_back(indexed_value(col_cell.m_value, col_cell.m_i, row_offset)); + row_vector.push_back(indexed_value(col_cell.m_value, col_index_in_the_new_matrix, col_offset)); + } + } + + template + void sparse_matrix::copy_B(static_matrix const &A, std::vector & basis) { + unsigned m = A.row_count(); // this should be the size of basis + for (unsigned j = m; j-- > 0;) { + copy_column_from_static_matrix(basis[j], A, j); + } + } + + // constructor that copies columns of the basis from A + template + sparse_matrix::sparse_matrix(static_matrix const &A, std::vector & basis) : + m_pivot_queue(A.row_count()), + m_row_permutation(A.row_count()), + m_column_permutation(A.row_count()), + m_work_pivot_vector(A.row_count()) { + init_row_headers(); + init_column_headers(); + copy_B(A, basis); + } + + template + void sparse_matrix::set_with_no_adjusting_for_row(unsigned row, unsigned col, T val) { // should not be used in efficient code + std::vector> & row_vec = m_rows[row]; + for (auto & iv : row_vec) { + if (iv.m_index == col) { + iv.set_value(val); + return; + } + } + // have not found the column between the indices + row_vec.push_back(indexed_value(val, col)); // what about m_other ??? + } + + template + void sparse_matrix::set_with_no_adjusting_for_col(unsigned row, unsigned col, T val) { // should not be used in efficient code + std::vector> & col_vec = m_columns[col].m_values; + for (auto & iv : col_vec) { + if (iv.m_index == row) { + iv.set_value(val); + return; + } + } + // have not found the column between the indices + col_vec.push_back(indexed_value(val, row)); // what about m_other ??? + } + + + template + void sparse_matrix::set_with_no_adjusting(unsigned row, unsigned col, T val) { // should not be used in efficient code + set_with_no_adjusting_for_row(row, col, val); + set_with_no_adjusting_for_col(row, col, val); + } + + template + void sparse_matrix::set(unsigned row, unsigned col, T val) { // should not be used in efficient code + lean_assert(row < dimension() && col < dimension()); + // m_dense.set_elem(row, col, val); + row = adjust_row(row); + col = adjust_column(col); + set_with_no_adjusting(row, col, val); + // lean_assert(*this == m_dense); + } + + template + T const & sparse_matrix::get_not_adjusted(unsigned row, unsigned col) const { + for (indexed_value const & iv : m_rows[row]) { + if (iv.m_index == col) { + return iv.m_value; + } + } + return numeric_traits::zero(); + } + + template + T const & sparse_matrix::get(unsigned row, unsigned col) const { // should not be used in efficient code + row = adjust_row(row); + auto & row_chunk = m_rows[row]; + col = adjust_column(col); + for (indexed_value const & iv : row_chunk) { + if (iv.m_index == col) { + return iv.m_value; + } + } + return numeric_traits::zero(); + } + + // constructor creating a zero matrix of dim*dim + template + sparse_matrix::sparse_matrix(unsigned dim) : + m_pivot_queue(dim), // dim will be the initial size of the queue + m_row_permutation(dim), + m_column_permutation(dim), + m_work_pivot_vector(dim) { + init_row_headers(); + init_column_headers(); + } + + template + void sparse_matrix::init_row_headers() { + for (unsigned l = 0; l < m_row_permutation.size(); l++) { + m_rows.push_back(std::vector>()); + } + } + + template + void sparse_matrix::init_column_headers() { // we alway have only square sparse_matrix + for (unsigned l = 0; l < m_row_permutation.size(); l++) { + m_columns.push_back(col_header()); + } + } + + template + unsigned sparse_matrix::lowest_row_in_column(unsigned j) { + auto & mc = get_column_values(adjust_column(j)); + unsigned ret = 0; + for (auto & iv : mc) { + unsigned row = adjust_row_inverse(iv.m_index); + if (row > ret) { + ret = row; + } + } + return ret; + } + + template + void sparse_matrix::remove_element(std::vector> & row_vals, unsigned row_offset, std::vector> & column_vals, unsigned column_offset) { + if (column_offset != column_vals.size() - 1) { + auto & column_iv = column_vals[column_offset] = column_vals.back(); // copy from the tail + column_iv_other(column_iv).m_other = column_offset; + if (row_offset != row_vals.size() - 1) { + auto & row_iv = row_vals[row_offset] = row_vals.back(); // copy from the tail + row_iv_other(row_iv).m_other = row_offset; + } + } else if (row_offset != row_vals.size() - 1) { + auto & row_iv = row_vals[row_offset] = row_vals.back(); // copy from the tail + row_iv_other(row_iv).m_other = row_offset; + } + // do nothing - just decrease the sizes + column_vals.pop_back(); + row_vals.pop_back(); + } + + template + void sparse_matrix::remove_element(std::vector> & row_chunk, indexed_value & row_el_iv) { + auto & column_chunk = get_column_values(row_el_iv.m_index); + indexed_value & col_el_iv = column_chunk[row_el_iv.m_other]; + remove_element(row_chunk, col_el_iv.m_other, column_chunk, row_el_iv.m_other); + } + + template + void sparse_matrix::put_max_index_to_0(std::vector> & row_vals, unsigned max_index) { + if (max_index == 0) return; + indexed_value * max_iv = & row_vals[max_index]; + indexed_value * start_iv = & row_vals[0]; + // update the "other" columns elements which are bound to the start_iv and max_iv + m_columns[max_iv->m_index].m_values[max_iv->m_other].m_other = 0; + m_columns[start_iv->m_index].m_values[start_iv->m_other].m_other = max_index; + + // swap the elements + indexed_value t = * max_iv; + * max_iv = * start_iv; + * start_iv = t; + } + + template + void sparse_matrix::set_max_in_row(std::vector> & row_vals) { + if (row_vals.size() == 0) + return; + T max_val = numeric_traits::zero(); + int max_index = 0; + for (unsigned i = row_vals.size(); i-- > 0;) { + T iabs = abs(row_vals[i].m_value); + if (iabs > max_val) { + max_val = iabs; + max_index = i; + } + } + put_max_index_to_0(row_vals, max_index); + } + + template + bool sparse_matrix::pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings) { + T pivot = eta_matrix->get_diagonal_element(); + for (auto & it : eta_matrix->m_column_vector.m_data) { + if (!pivot_row_to_row(i, it.second, it.first, settings)) + return false; + } + divide_row_by_constant(i, pivot, settings); + if (!shorten_active_matrix(i, eta_matrix)) { + return false; + } + + return true; + } + + template + void sparse_matrix::scan_row_to_work_vector(unsigned row, unsigned pivot_column) { + for (auto & iv : m_rows[row]) { + if (iv.m_index != pivot_column) { + m_work_pivot_vector.set_value(iv.m_value, iv.m_index); + } + } + } + + // This method pivots row i to row i0 by muliplying row i by + // alpha and adding it to row i0. + // After pivoting the row i0 has a max abs value set correctly at the beginning of m_start, + // Returns false if the resulting row is all zeroes, and true otherwise + template + bool sparse_matrix::pivot_row_to_row(unsigned i, T alpha, unsigned i0, lp_settings & settings ) { + lean_assert(i < dimension() && i0 < dimension()); + lean_assert(i != i0); + unsigned pivot_col = adjust_column(i); + i = adjust_row(i); + i0 = adjust_row(i0); + scan_row_to_work_vector(i0, pivot_col); + + auto & row_vals = m_rows[i]; + // lean_assert(row_vals.size() > 0); + for (auto & iv : row_vals) { + unsigned j = iv.m_index; + if (j == pivot_col) continue; + T work_array_at_j = m_work_pivot_vector[j]; + if (numeric_traits::is_zero(work_array_at_j)) { + T val = alpha * iv.m_value; + if (settings.abs_val_is_smaller_than_drop_tolerance(val)) { + continue; + } + m_work_pivot_vector.set_value(val, j); + } else { // the index is already inside + // lean_assert(std::find(m_work_pivot_vector.m_index.begin(), + // m_work_pivot_vector.m_index.end(), j) != m_work_pivot_vector.m_index.end()); + T val = alpha * iv.m_value + work_array_at_j; + if (settings.abs_val_is_smaller_than_drop_tolerance(val)) { + m_work_pivot_vector[j] = numeric_traits::zero(); + } else { + m_work_pivot_vector[j] = val; + } + } + } + +#ifdef LEAN_DEBUG + lean_assert(numeric_traits::is_zero(m_work_pivot_vector[pivot_col])); +#endif + if (!set_row_from_work_vector_and_clean_work_vector(i0)) { + return false; + } + return true; + } + + // set the max val as well + // returns false if the resulting row is all zeroes, and true otherwise + template + bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, + lp_settings & settings) { + remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(i0, work_vec, settings); + // all non-zero elements in m_work_pivot_vector are new + for (unsigned j : work_vec.m_index) { + if (numeric_traits::is_zero(work_vec[j])) { + continue; + } + lean_assert(!settings.abs_val_is_smaller_than_drop_tolerance(work_vec[j])); + add_new_element(i0, adjust_column(j), work_vec[j]); + work_vec[j] = numeric_traits::zero(); + } + work_vec.m_index.clear(); + auto & row_vals = m_rows[i0]; + if (row_vals.size() == 0) { + return false; + } + set_max_in_row(row_vals); // it helps to find larger pivots + return true; + } + + + // set the max val as well + // returns false if the resulting row is all zeroes, and true otherwise + template + bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector(unsigned i0) { + remove_zero_elements_and_set_data_on_existing_elements(i0); + // all non-zero elements in m_work_pivot_vector are new + for (unsigned j : m_work_pivot_vector.m_index) { + if (numeric_traits::is_zero(m_work_pivot_vector[j])) { + continue; + } + add_new_element(i0, j, m_work_pivot_vector[j]); + m_work_pivot_vector[j] = numeric_traits::zero(); + } + m_work_pivot_vector.m_index.clear(); + auto & row_vals = m_rows[i0]; + if (row_vals.size() == 0) { + return false; + } + set_max_in_row(row_vals); // it helps to find larger pivots + return true; + } + + template + void sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements(unsigned row) { + auto & row_vals = m_rows[row]; + for (unsigned k = row_vals.size(); k-- > 0;) { // we cannot simply run the iterator since we are removing + // elements from row_vals + auto & row_el_iv = row_vals[k]; + unsigned j = row_el_iv.m_index; + if (is_zero(m_work_pivot_vector[j])) { + remove_element(row_vals, row_el_iv); + } else { + m_columns[j].m_values[row_el_iv.m_other].set_value(m_work_pivot_vector[j]); + row_el_iv.set_value(m_work_pivot_vector[j]); + m_work_pivot_vector[j] = zero_of_type(); + } + } + } + + // work_vec here has not adjusted column indices + template + void sparse_matrix::remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings) { + auto & row_vals = m_rows[row]; + for (unsigned k = row_vals.size(); k-- > 0;) { // we cannot simply run the iterator since we are removing + // elements from row_vals + auto & row_el_iv = row_vals[k]; + unsigned j = row_el_iv.m_index; + unsigned rj = adjust_column_inverse(j); + T val = work_vec[rj]; + if (settings.abs_val_is_smaller_than_drop_tolerance(val)) { + remove_element(row_vals, row_el_iv); + lean_assert(numeric_traits::is_zero(val)); + } else { + m_columns[j].m_values[row_el_iv.m_other].set_value(row_el_iv.m_value = val); + work_vec[rj] = numeric_traits::zero(); + } + } + } + + + // adding delta columns at the end of the matrix + template + void sparse_matrix::add_columns_at_the_end(unsigned delta) { + for (unsigned i = 0; i < delta; i++) { + col_header col_head; + m_columns.push_back(col_head); + } + m_column_permutation.enlarge(delta); + } + + template + void sparse_matrix::delete_column(int i) { + lean_assert(i < dimension()); + for (auto cell = m_columns[i].m_head; cell != nullptr;) { + auto next_cell = cell->m_down; + kill_cell(cell); + cell = next_cell; + } + } + + template + void sparse_matrix::divide_row_by_constant(unsigned i, T & t, lp_settings & settings) { + lean_assert(!settings.abs_val_is_smaller_than_zero_tolerance(t)); + i = adjust_row(i); + for (auto & iv : m_rows[i]) { + T v = iv.m_value / t; + if (settings.abs_val_is_smaller_than_drop_tolerance(v)){ + v = numeric_traits::zero(); + } + m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = v); + } + } + + + // solving x * this = y, and putting the answer into y + // the matrix here has to be upper triangular + template + void sparse_matrix::solve_y_U(std::vector & y) const { // works by rows +#ifdef LEAN_DEBUG + // T * rs = clone_vector(y, dimension()); +#endif + unsigned end = dimension() - 1; + for (unsigned i = 0; i < end; i++) { + // all y[i] has correct values already + const T & yv = y[i]; + if (numeric_traits::is_zero(yv)) continue; + auto & mc = get_row_values(adjust_row(i)); + for (auto & c : mc) { + unsigned col = adjust_column_inverse(c.m_index); + if (col != i) { + y[col] -= c.m_value * yv; + } + } + } +#ifdef LEAN_DEBUG + // dense_matrix deb(*this); + // T * clone_y = clone_vector(y, dimension()); + // deb.apply_from_right(clone_y); + // lean_assert(vectors_are_equal(rs, clone_y, dimension())); + // delete [] clone_y; + // delete [] rs; +#endif + } + + // fills the indices for such that y[i] can be not a zero + // sort them so the smaller indices come first + // void fill_reachable_indices(std::set & rset, T *y) { + // std::queue q; + // int m = dimension(); + // for (int i = m - 1; i >= 0; i--) { + // if (!numeric_traits::is_zero(y[i])){ + // for (cell * c = m_columns[adjust_column(i)].m_head; c != nullptr; c = c->m_down) { + // unsigned row = adjust_row_inverse(c->m_i); + // q.push(row); + // } + // } + // } + // while (!q.empty()) { + // unsigned i = q.front(); + // q.pop(); + // for (cell * c = m_columns[adjust_column(i)].m_head; c != nullptr; c = c->m_down) { + // unsigned row = adjust_row_inverse(c->m_i); + // if (rset.find(row) == rset.end()){ + // rset.insert(row); + // q.push(row); + // } + // } + // } + // } + + // solving this * x = y, and putting the answer into y + // the matrix here has to be upper triangular + template + void sparse_matrix::solve_U_y_(T * y) { // the row wise version +#ifdef LEAN_DEBUG + // T * rs = clone_vector(y, dimension()); +#endif + lean_assert(dimension() == dimension()); + for (int i = dimension() - 2 ; i >= 0; i--) { + auto & mc = get_row_values(adjust_row(i)); + for (auto & c : mc) { + unsigned col = m_column_permutation[c.m_index]; + lean_assert(col > i || (col == i && close(c.m_value, numeric_traits::one()))); + if (col == i) { + continue; + } + y[i] -= y[col] * c.m_value; + } + } +#ifdef LEAN_DEBUG + // dense_matrix deb(*this); + // T * clone_y = clone_vector(y, dimension()); + // deb.apply_from_left(clone_y); + // lean_assert(vectors_are_equal(rs, clone_y, dimension())); + // delete [] rs; + // delete [] clone_y; +#endif + } + // solving this * x = y, and putting the answer into y + // the matrix here has to be upper triangular + template + void sparse_matrix::solve_U_y(T * y) { // the columns have to be correct - it is a column wise version +#ifdef LEAN_DEBUG + // T * rs = clone_vector(y, dimension()); +#endif + for (unsigned j = dimension() - 1; j > 0; j--) { + T yj = y[j]; + if (numeric_traits::is_zero(yj)) continue; + auto & mc = m_columns[adjust_column(j)].m_values; + for (auto & iv : mc) { + unsigned i = adjust_row_inverse(iv.m_index); + if (i != j) { + y[i] -= iv.m_value * yj; + } + } + } +#ifdef LEAN_DEBUG + // dense_matrix deb(*this); + // T * clone_y = clone_vector(y, dimension()); + // deb.apply_from_left(clone_y); + // lean_assert(vectors_are_equal(rs, clone_y, dimension())); +#endif + } + + + template + template + void sparse_matrix::find_error_in_solution_U_y(std::vector& y_orig, std::vector & y) { + unsigned i = dimension(); + while (i--) { + y_orig[i] -= dot_product_with_row(i, y); + } + } + + template + template + void sparse_matrix::add_delta_to_solution(const std::vector& del, std::vector & y) { + unsigned i = dimension(); + while (i--) { + y[i] += del[i]; + } + } + template + template + void sparse_matrix::double_solve_U_y(std::vector& y){ + std::vector y_orig(y); // copy y aside + solve_U_y(y); + find_error_in_solution_U_y(y_orig, y); + // y_orig contains the error now + solve_U_y(y_orig); + add_delta_to_solution(y_orig, y); + } + // solving this * x = y, and putting the answer into y + // the matrix here has to be upper triangular + template + template + void sparse_matrix::solve_U_y(std::vector & y) { // it is a column wise version +#ifdef LEAN_DEBUG + // T * rs = clone_vector(y, dimension()); +#endif + + for (unsigned j = dimension(); j--; ) { + const L & yj = y[j]; + if (is_zero(yj)) continue; + auto & mc = m_columns[adjust_column(j)].m_values; + for (auto & iv : mc) { + unsigned i = adjust_row_inverse(iv.m_index); + if (i != j) { + y[i] -= iv.m_value * yj; + } + } + } +#ifdef LEAN_DEBUG + // dense_matrix deb(*this); + // T * clone_y = clone_vector(y, dimension()); + // deb.apply_from_left(clone_y); + // lean_assert(vectors_are_equal(rs, clone_y, dimension())); +#endif + } + + template + template + L sparse_matrix::dot_product_with_row (unsigned row, const std::vector & y) const { + L ret = zero_of_type(); + auto & mc = get_row_values(adjust_row(row)); + for (auto & c : mc) { + unsigned col = m_column_permutation[c.m_index]; + ret += c.m_value * y[col]; + } + return ret; + } + + template + unsigned sparse_matrix::get_number_of_nonzeroes() const { + unsigned ret = 0; + for (unsigned i = dimension(); i--; ) { + ret += number_of_non_zeroes_in_row(i); + } + return ret; + } + + template + unsigned sparse_matrix::get_number_of_nonzeroes_below_row(unsigned row) const { + unsigned ret = 0; + for (unsigned i = dimension() - 1; + static_cast(i) >= static_cast(row); i--) { + ret += number_of_non_zeroes_in_row(adjust_row(i)); + } + return ret; + } + + template + bool sparse_matrix::get_non_zero_column_in_row(unsigned i, unsigned *j) const { + // go over the i-th row + auto & mc = get_row_values(adjust_row(i)); + if (mc.size() > 0) { + *j = m_column_permutation[mc[0].m_index]; + return true; + } + return false; + } + + template + void sparse_matrix::remove_element_that_is_not_in_w(std::vector> & column_vals, indexed_value & col_el_iv) { + auto & row_chunk = m_rows[col_el_iv.m_index]; + indexed_value & row_el_iv = row_chunk[col_el_iv.m_other]; + unsigned index_in_row = col_el_iv.m_other; + remove_element(row_chunk, col_el_iv.m_other, column_vals, row_el_iv.m_other); + if (index_in_row == 0) + set_max_in_row(row_chunk); + } + + + // w contains the new column + // the old column inside of the matrix has not been changed yet + template + void sparse_matrix::remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w) { + // -------------------------------- + // column_vals represents the old column + auto & column_vals = m_columns[column_to_replace].m_values; + for (unsigned k = column_vals.size(); k-- > 0;) { + indexed_value & col_el_iv = column_vals[k]; + unsigned i = col_el_iv.m_index; + T w_data_at_i = w[adjust_row_inverse(i)]; + if (numeric_traits::is_zero(w_data_at_i)) { + remove_element_that_is_not_in_w(column_vals, col_el_iv); + } else { + auto& row_chunk = m_rows[i]; + unsigned index_in_row = col_el_iv.m_other; + if (index_in_row == 0) { + bool look_for_max = abs(w_data_at_i) < abs(row_chunk[0].m_value); + row_chunk[0].set_value(col_el_iv.m_value = w_data_at_i); + if (look_for_max) + set_max_in_row(i); + } else { + row_chunk[index_in_row].set_value(col_el_iv.m_value = w_data_at_i); + if (abs(w_data_at_i) > abs(row_chunk[0].m_value)) + put_max_index_to_0(row_chunk, index_in_row); + } + w[adjust_row_inverse(i)] = numeric_traits::zero(); + } + } + } + + template + void sparse_matrix::add_new_element(unsigned row, unsigned col, T val) { + auto & row_vals = m_rows[row]; + auto & col_vals = m_columns[col].m_values; + unsigned row_el_offs = row_vals.size(); + unsigned col_el_offs = col_vals.size(); + row_vals.push_back(indexed_value(val, col, col_el_offs)); + col_vals.push_back(indexed_value(val, row, row_el_offs)); + } + + // w contains the "rest" of the new column; all common elements of w and the old column has been zeroed + // the old column inside of the matrix has not been changed yet + template + void sparse_matrix::add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings) { + for (unsigned i : w.m_index) { + T w_at_i = w[i]; + if (numeric_traits::is_zero(w_at_i)) continue; // was dealt with already + if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_i)) { + unsigned ai = adjust_row(i); + add_new_element(ai, column_to_replace, w_at_i); + auto & row_chunk = m_rows[ai]; + lean_assert(row_chunk.size() > 0); + if (abs(w_at_i) > abs(row_chunk[0].m_value)) + put_max_index_to_0(row_chunk, row_chunk.size() - 1); + } + w[i] = numeric_traits::zero(); + } + w.m_index.clear(); + } + + template + void sparse_matrix::replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings) { + column_to_replace = adjust_column(column_to_replace); + remove_elements_that_are_not_in_w_and_update_common_elements(column_to_replace, w); + add_new_elements_of_w_and_clear_w(column_to_replace, w, settings); + } + + template + unsigned sparse_matrix::pivot_score(unsigned i, unsigned j) { + // It goes like this (rnz-1)(cnz-1) is the Markovitz number, that is the max number of + // new non zeroes we can obtain after the pivoting. + // In addition we will get another cnz - 1 elements in the eta matrix created for this pivot, + // which gives rnz(cnz-1). For example, is 0 for a column singleton, but not for + // a row singleton ( which is not a column singleton). + + auto col_header = m_columns[j]; + + return get_row_values(i).size() * (col_header.m_values.size() - col_header.m_shortened_markovitz - 1); + } + + template + void sparse_matrix::enqueue_domain_into_pivot_queue() { + lean_assert(m_pivot_queue.size() == 0); + for (unsigned i = 0; i < dimension(); i++) { + auto & rh = m_rows[i]; + unsigned rnz = rh.size(); + for (auto iv : rh) { + unsigned j = iv.m_index; + m_pivot_queue.enqueue(i, j, rnz * (m_columns[j].m_values.size() - 1)); + } + } + } + + template + void sparse_matrix::set_max_in_rows() { + unsigned i = dimension(); + while (i--) + set_max_in_row(i); + } + + + template + void sparse_matrix::zero_shortened_markovitz_numbers() { + for (auto & ch : m_columns) + ch.zero_shortened_markovitz(); + } + + template + void sparse_matrix::prepare_for_factorization() { + zero_shortened_markovitz_numbers(); + set_max_in_rows(); + enqueue_domain_into_pivot_queue(); + } + + template + void sparse_matrix::recover_pivot_queue(std::vector & rejected_pivots) { + for (auto p : rejected_pivots) { + m_pivot_queue.enqueue(p.first, p.second, pivot_score(p.first, p.second)); + } + } + + template + int sparse_matrix::elem_is_too_small(unsigned i, unsigned j, const T & c_partial_pivoting) { + auto & row_chunk = m_rows[i]; + + if (j == row_chunk[0].m_index) { + return 0; // the max element is at the head + } + T max = abs(row_chunk[0].m_value); + for (unsigned k = 1; k < row_chunk.size(); k++) { + auto &iv = row_chunk[k]; + if (iv.m_index == j) + return abs(iv.m_value) * c_partial_pivoting < max ? 1: 0; + } + return 2; // the element became zero but it still sits in the active pivots? + } + + template + bool sparse_matrix::remove_row_from_active_pivots_and_shorten_columns(unsigned row) { + unsigned arow = adjust_row(row); + for (auto & iv : m_rows[arow]) { + m_pivot_queue.remove(arow, iv.m_index); + if (adjust_column_inverse(iv.m_index) <= row) + continue; // this column will be removed anyway + auto & col = m_columns[iv.m_index]; + + col.shorten_markovich_by_one(); + if (col.m_values.size() <= col.m_shortened_markovitz) + return false; // got a zero column + } + return true; + } + + template + void sparse_matrix::remove_pivot_column(unsigned row) { + unsigned acol = adjust_column(row); + for (auto & iv : m_columns[acol].m_values) + if (adjust_row_inverse(iv.m_index) >= row) + m_pivot_queue.remove(iv.m_index, acol); + } + + template + void sparse_matrix::update_active_pivots(unsigned row) { + unsigned arow = adjust_row(row); + for (auto & iv : m_rows[arow]) { + col_header & ch = m_columns[iv.m_index]; + int cols = ch.m_values.size() - ch.m_shortened_markovitz - 1; + lean_assert(cols >= 0); + for (auto &ivc : ch.m_values) { + unsigned i = ivc.m_index; + if (adjust_row_inverse(i) <= row) continue; // the i is not an active row + m_pivot_queue.enqueue(i, iv.m_index, m_rows[i].size()*cols); + } + } + } + + template + bool sparse_matrix::shorten_active_matrix(unsigned row, eta_matrix *eta_matrix) { + if (!remove_row_from_active_pivots_and_shorten_columns(row)) + return false; + remove_pivot_column(row); + // need to know the max priority of the queue here + update_active_pivots(row); + if (eta_matrix == nullptr) return true; + // it looks like double work, but the pivot scores have changed for all rows + // touched by eta_matrix + for (auto & it: eta_matrix->m_column_vector.m_data) { + unsigned row = adjust_row(it.first); + auto & row_values = m_rows[row]; + unsigned rnz = row_values.size(); + for (auto & iv : row_values) { + col_header& ch = m_columns[iv.m_index]; + int cnz = ch.m_values.size() - ch.m_shortened_markovitz - 1; + lean_assert(cnz >= 0); + m_pivot_queue.enqueue(row, iv.m_index, rnz * cnz); + } + } + + return true; + } + + template + unsigned sparse_matrix::pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k) { + auto &cols = m_columns[j].m_values; + unsigned cnz = cols.size(); + for (auto & iv : cols) { + if (adjust_row_inverse(iv.m_index) < k) + cnz--; + } + lean_assert(cnz > 0); + return m_rows[i].m_values.size() * (cnz - 1); + } +#ifdef LEAN_DEBUG + template + bool sparse_matrix::can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k) { + unsigned arow = adjust_row(row); + auto & row_vals = m_rows[arow].m_values; + auto & begin_iv = row_vals[0]; + T row_max = abs(begin_iv.m_value); + lean_assert(adjust_column_inverse(begin_iv.m_index) >= k); + if (pivot_score_without_shortened_counters(arow, begin_iv.m_index, k) < score) { + print_active_matrix(k); + return true; + } + for (unsigned jj = 1; jj < row_vals.size(); jj++) { + auto & iv = row_vals[jj]; + lean_assert(adjust_column_inverse(iv.m_index) >= k); + lean_assert(abs(iv.m_value) <= row_max); + if (c_partial_pivoting * abs(iv.m_value) < row_max) continue; + if (pivot_score_without_shortened_counters(arow, iv.m_index, k) < score) { + print_active_matrix(k); + return true; + } + } + return false; + } + + template + bool sparse_matrix::really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k) { + unsigned queue_pivot_score = pivot_score_without_shortened_counters(i, j, k); + for (unsigned ii = k; ii < dimension(); ii++) { + lean_assert(!can_improve_score_for_row(ii, queue_pivot_score, c_partial_pivoting, k)); + } + return true; + } + template + void sparse_matrix::print_active_matrix(unsigned k) { + std::cout << "active matrix for k = " << k << std::endl; + if (k >= dimension()) { + std::cout << "empty" << std::endl; + return; + } + unsigned dim = dimension() - k; + dense_matrix b(dim, dim); + for (unsigned i = 0; i < dim; i++) + for (unsigned j = 0; j < dim; j++ ) + b.set_elem(i, j, zero_of_type()); + for (int i = k; i < dimension(); i++) { + unsigned col = adjust_column(i); + for (auto &iv : get_column_values(col)) { + unsigned row = iv.m_index; + unsigned row_ex = this->adjust_row_inverse(row); + if (row_ex < k) continue; + auto v = this->get_not_adjusted(row, col); + b.set_elem(row_ex - k, i -k, v); + } + } + print_matrix(b); + } + + template + bool sparse_matrix::pivot_queue_is_correct_for_row(unsigned i, unsigned k) { + unsigned arow = adjust_row(i); + for (auto & iv : m_rows[arow].m_values) { + lean_assert(pivot_score_without_shortened_counters(arow, iv.m_index, k + 1) == + m_pivot_queue.get_priority(arow, iv.m_index)); + } + return true; + } + + template + bool sparse_matrix::pivot_queue_is_correct_after_pivoting(int k) { + for (unsigned i = k + 1; i < dimension(); i++ ) + lean_assert(pivot_queue_is_correct_for_row(i, k)); + lean_assert(m_pivot_queue.is_correct()); + return true; + } +#endif + + + template + bool sparse_matrix::get_pivot_for_column(unsigned &i, unsigned &j, T const & c_partial_pivoting, unsigned k) { + std::vector pivots_candidates_that_are_too_small; + while (!m_pivot_queue.is_empty()) { + m_pivot_queue.dequeue(i, j); + unsigned i_inv = adjust_row_inverse(i); + if (i_inv < k) continue; + unsigned j_inv = adjust_column_inverse(j); + if (j_inv < k) continue; + int small = elem_is_too_small(i, j, c_partial_pivoting); + if (!small) { +#ifdef LEAN_DEBUG + // if (!really_best_pivot(i, j, c_partial_pivoting, k)) { + // print_active_matrix(k); + // lean_assert(false); + // } +#endif + + recover_pivot_queue(pivots_candidates_that_are_too_small); + i = i_inv; + j = j_inv; + return true; + } + if (small != 2) // 2 means that the pair is not in the matrix + pivots_candidates_that_are_too_small.emplace_back(i, j); + } + recover_pivot_queue(pivots_candidates_that_are_too_small); + return false; + } + + template + bool sparse_matrix::elem_is_too_small(std::vector> & row_chunk, indexed_value & iv, T const & c_partial_pivoting) { + if (&iv == &row_chunk[0]) { + return false; // the max element is at the head + } + T val = abs(iv.m_value); + T max = abs(row_chunk[0].m_value); + return val * c_partial_pivoting < max; + } + + template + bool sparse_matrix::shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column) { + std::vector> & row_chunk = get_row_values(i); + + for (indexed_value & iv : row_chunk) { + unsigned j = iv.m_index; + if (j == pivot_column) { + lean_assert(!col_is_active(j)); + continue; + } + m_columns[j].shorten_markovich_by_one(); + + if (m_columns[j].m_shortened_markovitz >= get_column_values(j).size()) { // got the zero column under the row! + return false; + } + } + return true; + } + + template + void sparse_matrix::fill_eta_matrix(unsigned j, eta_matrix ** eta) { + std::vector> & col_chunk = get_column_values(adjust_column(j)); + bool is_unit = true; + for (auto & iv : col_chunk) { + unsigned i = adjust_row_inverse(iv.m_index); + if (i > j) { + is_unit = false; + break; + } + if (i == j && iv.m_value != 1) { + is_unit = false; + break; + } + } + + if (is_unit) { + *eta = nullptr; + return; + } + +#ifdef LEAN_DEBUG + *eta = new eta_matrix(j, dimension()); +#else + *eta = new eta_matrix(j); +#endif + for (auto & iv : col_chunk) { + unsigned i = adjust_row_inverse(iv.m_index); + if (i < j) { + continue; + } + if (i > j) { + (*eta)->push_back(i, - iv.m_value); + } else { // i == j + (*eta)->set_diagonal_element(iv.m_value); + } + } + (*eta)->divide_by_diagonal_element(); + } +#ifdef LEAN_DEBUG + template + bool sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const { + for (unsigned i = 0; i < dimension(); i++) { + std::vector> const & row_chunk = get_row_values(i); + lean_assert(row_chunk.size()); + T const & max = abs(row_chunk[0].m_value); + unsigned ai = adjust_row_inverse(i); + for (auto & iv : row_chunk) { + lean_assert(abs(iv.m_value) <= max); + unsigned aj = adjust_column_inverse(iv.m_index); + if (!(ai <= aj || numeric_traits::is_zero(iv.m_value))) + return false; + if (aj == ai) { + if (iv.m_value != 1) { + std::cout << "value at diagonal = " << iv.m_value << std::endl; + return false; + } + } + if (settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value) && (!is_zero(iv.m_value))) + return false; + } + } + return true; + } + + template + bool sparse_matrix::is_upper_triangular_until(unsigned k) const { + for (unsigned j = 0; j < dimension() && j < k; j++) { + unsigned aj = adjust_column(j); + auto & col = get_column_values(aj); + for (auto & iv : col) { + unsigned row = adjust_row_inverse(iv.m_index); + if (row > j) + return false; + } + } + return true; + } + + template + void sparse_matrix::check_column_vs_rows(unsigned col) { + auto & mc = get_column_values(col); + for (indexed_value & column_iv : mc) { + indexed_value & row_iv = column_iv_other(column_iv); + if (row_iv.m_index != col) { + std::cout << "m_other in row does not belong to column " << col << ", but to column " << row_iv.m_index << std::endl; + lean_assert(false); + } + + if (& row_iv_other(row_iv) != &column_iv) { + std::cout << "row and col do not point to each other" << std::endl; + lean_assert(false); + } + + if (row_iv.m_value != column_iv.m_value) { + std::cout << "the data from col " << col << " for row " << column_iv.m_index << " is different in the column " << std::endl; + std::cout << "in the col it is " << column_iv.m_value << ", but in the row it is " << row_iv.m_value << std::endl; + lean_assert(false); + } + } + } + + template + void sparse_matrix::check_row_vs_columns(unsigned row) { + auto & mc = get_row_values(row); + for (indexed_value & row_iv : mc) { + indexed_value & column_iv = row_iv_other(row_iv); + + if (column_iv.m_index != row) { + std::cout << "col_iv does not point to correct row " << row << " but to " << column_iv.m_index << std::endl; + lean_assert(false); + } + + if (& row_iv != & column_iv_other(column_iv)) { + std::cout << "row and col do not point to each other" << std::endl; + lean_assert(false); + } + + if (row_iv.m_value != column_iv.m_value) { + std::cout << "the data from col " << column_iv.m_index << " for row " << row << " is different in the column " << std::endl; + std::cout << "in the col it is " << column_iv.m_value << ", but in the row it is " << row_iv.m_value << std::endl; + lean_assert(false); + } + } + } + + template + void sparse_matrix::check_rows_vs_columns() { + for (unsigned i = 0; i < dimension(); i++) { + check_row_vs_columns(i); + } + } + + template + void sparse_matrix::check_columns_vs_rows() { + for (unsigned i = 0; i < dimension(); i++) { + check_column_vs_rows(i); + } + } + template + void sparse_matrix::check_matrix() { + check_rows_vs_columns(); + check_columns_vs_rows(); + } +#endif +} + diff --git a/src/util/lp/sparse_matrix.h b/src/util/lp/sparse_matrix.h index b08186d765..bb54ea8c22 100644 --- a/src/util/lp/sparse_matrix.h +++ b/src/util/lp/sparse_matrix.h @@ -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/numeric_pair.h" namespace lean { // it is a square matrix template @@ -76,40 +77,12 @@ public: return m_column_permutation[col]; } - void copy_column_from_static_matrix(unsigned col, static_matrix const &A, unsigned col_index_in_the_new_matrix) { - std::vector> const & A_col_vector = A.m_columns[col]; - unsigned size = A_col_vector.size(); - std::vector> & new_column_vector = m_columns[col_index_in_the_new_matrix].m_values; - for (unsigned l = 0; l < size; l++) { - column_cell const & col_cell = A_col_vector[l]; - unsigned col_offset = new_column_vector.size(); - std::vector> & row_vector = m_rows[col_cell.m_i]; - unsigned row_offset = row_vector.size(); - new_column_vector.push_back(indexed_value(col_cell.m_value, col_cell.m_i, row_offset)); - row_vector.push_back(indexed_value(col_cell.m_value, col_index_in_the_new_matrix, col_offset)); - } - } - - void copy_B(static_matrix const &A, std::vector & basis) { - unsigned m = A.row_count(); // this should be the size of basis - for (unsigned j = m; j-- > 0;) { - copy_column_from_static_matrix(basis[j], A, j); - } - } + void copy_column_from_static_matrix(unsigned col, static_matrix const &A, unsigned col_index_in_the_new_matrix); + void copy_B(static_matrix const &A, std::vector & basis); public: // constructor that copies columns of the basis from A - sparse_matrix(static_matrix const &A, std::vector & basis) : - m_pivot_queue(A.row_count()), - m_row_permutation(A.row_count()), - m_column_permutation(A.row_count()), - m_work_pivot_vector(A.row_count()) { - init_row_headers(); - init_column_headers(); - copy_B(A, basis); - } - - // helper access definitions for debugging region + sparse_matrix(static_matrix const &A, std::vector & basis); class ref_matrix_element { sparse_matrix & m_matrix; @@ -130,65 +103,15 @@ public: ref_matrix_element operator[](unsigned col) const { return ref_matrix_element(m_matrix, m_row, col); } }; - void set_with_no_adjusting_for_row(unsigned row, unsigned col, T val) { // should not be used in efficient code - std::vector> & row_vec = m_rows[row]; - for (auto & iv : row_vec) { - if (iv.m_index == col) { - iv.set_value(val); - return; - } - } - // have not found the column between the indices - row_vec.push_back(indexed_value(val, col)); // what about m_other ??? - } + void set_with_no_adjusting_for_row(unsigned row, unsigned col, T val); + void set_with_no_adjusting_for_col(unsigned row, unsigned col, T val); - void set_with_no_adjusting_for_col(unsigned row, unsigned col, T val) { // should not be used in efficient code - std::vector> & col_vec = m_columns[col].m_values; - for (auto & iv : col_vec) { - if (iv.m_index == row) { - iv.set_value(val); - return; - } - } - // have not found the column between the indices - col_vec.push_back(indexed_value(val, row)); // what about m_other ??? - } + void set_with_no_adjusting(unsigned row, unsigned col, T val); - - void set_with_no_adjusting(unsigned row, unsigned col, T val) { // should not be used in efficient code - set_with_no_adjusting_for_row(row, col, val); - set_with_no_adjusting_for_col(row, col, val); - } - - void set(unsigned row, unsigned col, T val) { // should not be used in efficient code - lean_assert(row < dimension() && col < dimension()); - // m_dense.set_elem(row, col, val); - row = adjust_row(row); - col = adjust_column(col); - set_with_no_adjusting(row, col, val); - // lean_assert(*this == m_dense); - } - - T const & get_not_adjusted(unsigned row, unsigned col) const { - for (indexed_value const & iv : m_rows[row]) { - if (iv.m_index == col) { - return iv.m_value; - } - } - return numeric_traits::zero(); - } - - T const & get(unsigned row, unsigned col) const { // should not be used in efficient code - row = adjust_row(row); - auto & row_chunk = m_rows[row]; - col = adjust_column(col); - for (indexed_value const & iv : row_chunk) { - if (iv.m_index == col) { - return iv.m_value; - } - } - return numeric_traits::zero(); - } + void set(unsigned row, unsigned col, T val); + + T const & get_not_adjusted(unsigned row, unsigned col) const; + T const & get(unsigned row, unsigned col) const; ref_row operator[](unsigned row) { return ref_row(*this, row); } @@ -196,10 +119,6 @@ public: T operator() (unsigned row, unsigned col) const { return get(row, col); } - // starting inner classes - - // end of access for debugging helpers - std::vector> & get_row_values(unsigned row) { return m_rows[row]; } @@ -217,14 +136,7 @@ public: } // constructor creating a zero matrix of dim*dim - sparse_matrix(unsigned dim) : - m_pivot_queue(dim), // dim will be the initial size of the queue - m_row_permutation(dim), - m_column_permutation(dim), - m_work_pivot_vector(dim) { - init_row_headers(); - init_column_headers(); - } + sparse_matrix(unsigned dim); @@ -235,255 +147,56 @@ public: unsigned column_count() const {return dimension();} #endif - void init_row_headers() { - for (unsigned l = 0; l < m_row_permutation.size(); l++) { - m_rows.push_back(std::vector>()); - } - } - - void init_column_headers() { // do we alway have only a square sparse_matrix? - for (unsigned l = 0; l < m_row_permutation.size(); l++) { - m_columns.push_back(col_header()); - } - } - - unsigned lowest_row_in_column(unsigned j) { - auto & mc = get_column_values(adjust_column(j)); - unsigned ret = 0; - for (auto & iv : mc) { - unsigned row = adjust_row_inverse(iv.m_index); - if (row > ret) { - ret = row; - } - } - return ret; - } - - unsigned number_of_non_zeroes_in_row(unsigned i) const { - lean_assert(i < dimension()); - return m_rows[i].size(); - } - - unsigned number_of_non_zeroes_in_column(unsigned i) const { - lean_assert(i < dimension()); - return m_columns[i].m_values.size(); - } + void init_row_headers(); + void init_column_headers(); + unsigned lowest_row_in_column(unsigned j); indexed_value & column_iv_other(indexed_value & iv) { return m_rows[iv.m_index][iv.m_other]; } indexed_value & row_iv_other(indexed_value & iv) { - lean_assert(m_columns[iv.m_index].m_values.size() > iv.m_other); return m_columns[iv.m_index].m_values[iv.m_other]; } + void remove_element(std::vector> & row_vals, unsigned row_offset, std::vector> & column_vals, unsigned column_offset); - void remove_element(std::vector> & row_vals, unsigned row_offset, std::vector> & column_vals, unsigned column_offset) { - if (column_offset != column_vals.size() - 1) { - auto & column_iv = column_vals[column_offset] = column_vals.back(); // copy from the tail - column_iv_other(column_iv).m_other = column_offset; - if (row_offset != row_vals.size() - 1) { - auto & row_iv = row_vals[row_offset] = row_vals.back(); // copy from the tail - row_iv_other(row_iv).m_other = row_offset; - } - } else if (row_offset != row_vals.size() - 1) { - auto & row_iv = row_vals[row_offset] = row_vals.back(); // copy from the tail - row_iv_other(row_iv).m_other = row_offset; - } - // do nothing - just decrease the sizes - column_vals.pop_back(); - row_vals.pop_back(); - } + void remove_element(std::vector> & row_chunk, indexed_value & row_el_iv); - void remove_element(std::vector> & row_chunk, indexed_value & row_el_iv) { - auto & column_chunk = get_column_values(row_el_iv.m_index); - indexed_value & col_el_iv = column_chunk[row_el_iv.m_other]; - remove_element(row_chunk, col_el_iv.m_other, column_chunk, row_el_iv.m_other); - } - - void put_max_index_to_0(std::vector> & row_vals, unsigned max_index) { - if (max_index == 0) return; - indexed_value * max_iv = & row_vals[max_index]; - indexed_value * start_iv = & row_vals[0]; - // update the "other" columns elements which are bound to the start_iv and max_iv - m_columns[max_iv->m_index].m_values[max_iv->m_other].m_other = 0; - m_columns[start_iv->m_index].m_values[start_iv->m_other].m_other = max_index; - - // swap the elements - indexed_value t = * max_iv; - * max_iv = * start_iv; - * start_iv = t; - } + void put_max_index_to_0(std::vector> & row_vals, unsigned max_index); void set_max_in_row(unsigned row) { set_max_in_row(m_rows[row]); } - void set_max_in_row(std::vector> & row_vals) { - if (row_vals.size() == 0) - return; - T max_val = numeric_traits::zero(); - int max_index = 0; - for (unsigned i = row_vals.size(); i-- > 0;) { - T iabs = abs(row_vals[i].m_value); - if (iabs > max_val) { - max_val = iabs; - max_index = i; - } - } - put_max_index_to_0(row_vals, max_index); - } + void set_max_in_row(std::vector> & row_vals); - bool pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings) { - T pivot = eta_matrix->get_diagonal_element(); - for (auto it = eta_matrix->get_sparse_vector_iterator(); !it.done(); it.move()) { - if (!pivot_row_to_row(i, it.value(), it.index(), settings)) - return false; - } - divide_row_by_constant(i, pivot, settings); - if (!shorten_active_matrix(i, eta_matrix)) { - return false; - } + bool pivot_with_eta(unsigned i, eta_matrix *eta_matrix, lp_settings & settings); - return true; - } - - void scan_row_to_work_vector(unsigned row, unsigned pivot_column) { - for (auto & iv : m_rows[row]) { - if (iv.m_index != pivot_column) { - m_work_pivot_vector.set_value(iv.m_value, iv.m_index); - } - } - } + void scan_row_to_work_vector(unsigned row, unsigned pivot_column); // This method pivots row i to row i0 by muliplying row i by // alpha and adding it to row i0. // After pivoting the row i0 has a max abs value set correctly at the beginning of m_start, // Returns false if the resulting row is all zeroes, and true otherwise - bool pivot_row_to_row(unsigned i, T alpha, unsigned i0, lp_settings & settings ) { - lean_assert(i < dimension() && i0 < dimension()); - lean_assert(i != i0); - unsigned pivot_col = adjust_column(i); - i = adjust_row(i); - i0 = adjust_row(i0); - scan_row_to_work_vector(i0, pivot_col); - - auto & row_vals = m_rows[i]; - // lean_assert(row_vals.size() > 0); - for (auto & iv : row_vals) { - unsigned j = iv.m_index; - if (j == pivot_col) continue; - T work_array_at_j = m_work_pivot_vector[j]; - if (numeric_traits::is_zero(work_array_at_j)) { - T val = alpha * iv.m_value; - if (settings.abs_val_is_smaller_than_drop_tolerance(val)) { - continue; - } - m_work_pivot_vector.set_value(val, j); - } else { // the index is already inside - // lean_assert(std::find(m_work_pivot_vector.m_index.begin(), - // m_work_pivot_vector.m_index.end(), j) != m_work_pivot_vector.m_index.end()); - T val = alpha * iv.m_value + work_array_at_j; - if (settings.abs_val_is_smaller_than_drop_tolerance(val)) { - m_work_pivot_vector[j] = numeric_traits::zero(); - } else { - m_work_pivot_vector[j] = val; - } - } - } - -#ifdef LEAN_DEBUG - lean_assert(numeric_traits::is_zero(m_work_pivot_vector[pivot_col])); -#endif - if (!set_row_from_work_vector_and_clean_work_vector(i0)) { - return false; - } - return true; - } + bool pivot_row_to_row(unsigned i, T alpha, unsigned i0, lp_settings & settings ); // set the max val as well // returns false if the resulting row is all zeroes, and true otherwise bool set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned i0, indexed_vector & work_vec, - lp_settings & settings) { - remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(i0, work_vec, settings); - // all non-zero elements in m_work_pivot_vector are new - for (unsigned j : work_vec.m_index) { - if (numeric_traits::is_zero(work_vec[j])) { - continue; - } - lean_assert(!settings.abs_val_is_smaller_than_drop_tolerance(work_vec[j])); - add_new_element(i0, adjust_column(j), work_vec[j]); - work_vec[j] = numeric_traits::zero(); - } - work_vec.m_index.clear(); - auto & row_vals = m_rows[i0]; - if (row_vals.size() == 0) { - return false; - } - set_max_in_row(row_vals); // it helps to find larger pivots - return true; - } + lp_settings & settings); // set the max val as well // returns false if the resulting row is all zeroes, and true otherwise - bool set_row_from_work_vector_and_clean_work_vector(unsigned i0) { - remove_zero_elements_and_set_data_on_existing_elements(i0); - // all non-zero elements in m_work_pivot_vector are new - for (unsigned j : m_work_pivot_vector.m_index) { - if (numeric_traits::is_zero(m_work_pivot_vector[j])) { - continue; - } - add_new_element(i0, j, m_work_pivot_vector[j]); - m_work_pivot_vector[j] = numeric_traits::zero(); - } - m_work_pivot_vector.m_index.clear(); - auto & row_vals = m_rows[i0]; - if (row_vals.size() == 0) { - return false; - } - set_max_in_row(row_vals); // it helps to find larger pivots - return true; - } + bool set_row_from_work_vector_and_clean_work_vector(unsigned i0); - void remove_zero_elements_and_set_data_on_existing_elements(unsigned row) { - auto & row_vals = m_rows[row]; - for (unsigned k = row_vals.size(); k-- > 0;) { // we cannot simply run the iterator since we are removing - // elements from row_vals - auto & row_el_iv = row_vals[k]; - unsigned j = row_el_iv.m_index; - if (is_zero(m_work_pivot_vector[j])) { - remove_element(row_vals, row_el_iv); - } else { - m_columns[j].m_values[row_el_iv.m_other].set_value(m_work_pivot_vector[j]); - row_el_iv.set_value(m_work_pivot_vector[j]); - m_work_pivot_vector[j] = zero_of_type(); - } - } - } + void remove_zero_elements_and_set_data_on_existing_elements(unsigned row); // work_vec here has not adjusted column indices - void remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings) { - auto & row_vals = m_rows[row]; - for (unsigned k = row_vals.size(); k-- > 0;) { // we cannot simply run the iterator since we are removing - // elements from row_vals - auto & row_el_iv = row_vals[k]; - unsigned j = row_el_iv.m_index; - unsigned rj = adjust_column_inverse(j); - T val = work_vec[rj]; - if (settings.abs_val_is_smaller_than_drop_tolerance(val)) { - remove_element(row_vals, row_el_iv); - lean_assert(numeric_traits::is_zero(val)); - } else { - m_columns[j].m_values[row_el_iv.m_other].set_value(row_el_iv.m_value = val); - work_vec[rj] = numeric_traits::zero(); - } - } - } - + void remove_zero_elements_and_set_data_on_existing_elements_not_adjusted(unsigned row, indexed_vector & work_vec, lp_settings & settings); void multiply_from_right(permutation_matrix& p) { // m_dense = m_dense * p; @@ -504,32 +217,9 @@ public: } // adding delta columns at the end of the matrix - void add_columns_at_the_end(unsigned delta) { - for (unsigned i = 0; i < delta; i++) { - col_header col_head; - m_columns.push_back(col_head); - } - m_column_permutation.enlarge(delta); - } + void add_columns_at_the_end(unsigned delta); - void delete_column(int i) { - lean_assert(i < dimension()); - for (auto cell = m_columns[i].m_head; cell != nullptr;) { - auto next_cell = cell->m_down; - kill_cell(cell); - cell = next_cell; - } - } - - bool is_a_unit_matrix() { - for (int i = 0; i < m_rows.size(); i++) { - auto * cell = m_rows[i]->m_head; - if (cell == nullptr || cell->m_j != i || cell->m_value != 1 || cell->m_right != nullptr) { - return false; - } - } - return true; - } + void delete_column(int i); void swap_columns(unsigned a, unsigned b) { // cout << "swaapoiiin" << std::endl; @@ -545,28 +235,9 @@ public: // lean_assert(*this == m_dense); } - void divide_row_by_constant(unsigned i, T & t, lp_settings & settings) { - lean_assert(!settings.abs_val_is_smaller_than_zero_tolerance(t)); - i = adjust_row(i); - for (auto & iv : m_rows[i]) { - T v = iv.m_value / t; - if (settings.abs_val_is_smaller_than_drop_tolerance(v)){ - v = numeric_traits::zero(); - } - m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = v); - } - } + void divide_row_by_constant(unsigned i, T & t, lp_settings & settings); - T dot_product_with_column(T const * y, unsigned j) { - lean_assert(j < dimension()); - T ret = numeric_traits::zero(); - auto & mc = m_columns[adjust_column(j)].m_chunk; - for (int i = mc.m_length - 1; i >= 0; i--) { - auto & iv = mc.m_start[i]; - ret += y[m_row_permutation.apply_reverse(iv.m_index)] * iv.m_value; - } - return ret; - } + T dot_product_with_column(T const * y, unsigned j); bool close(T a, T b) { return // (numeric_traits::precise() && numeric_traits::is_zero(a - b)) @@ -576,162 +247,31 @@ public: // solving x * this = y, and putting the answer into y // the matrix here has to be upper triangular - void solve_y_U(std::vector & y) const { // works by rows -#ifdef LEAN_DEBUG - // T * rs = clone_vector(y, dimension()); -#endif - unsigned end = dimension() - 1; - for (unsigned i = 0; i < end; i++) { - // all y[i] has correct values already - const T & yv = y[i]; - if (numeric_traits::is_zero(yv)) continue; - auto & mc = get_row_values(adjust_row(i)); - for (auto & c : mc) { - unsigned col = adjust_column_inverse(c.m_index); - if (col != i) { - y[col] -= c.m_value * yv; - } - } - } -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // T * clone_y = clone_vector(y, dimension()); - // deb.apply_from_right(clone_y); - // lean_assert(vectors_are_equal(rs, clone_y, dimension())); - // delete [] clone_y; - // delete [] rs; -#endif - } + void solve_y_U(std::vector & y) const; // fills the indices for such that y[i] can be not a zero // sort them so the smaller indices come first - void fill_reachable_indices(std::set & rset, T *y) { - // std::queue q; - // int m = dimension(); - // for (int i = m - 1; i >= 0; i--) { - // if (!numeric_traits::is_zero(y[i])){ - // for (cell * c = m_columns[adjust_column(i)].m_head; c != nullptr; c = c->m_down) { - // unsigned row = adjust_row_inverse(c->m_i); - // q.push(row); - // } - // } - // } - // while (!q.empty()) { - // unsigned i = q.front(); - // q.pop(); - // for (cell * c = m_columns[adjust_column(i)].m_head; c != nullptr; c = c->m_down) { - // unsigned row = adjust_row_inverse(c->m_i); - // if (rset.find(row) == rset.end()){ - // rset.insert(row); - // q.push(row); - // } - // } - // } - } + void fill_reachable_indices(std::set & rset, T *y); // solving this * x = y, and putting the answer into y // the matrix here has to be upper triangular - void solve_U_y_(T * y) { // the row wise version -#ifdef LEAN_DEBUG - // T * rs = clone_vector(y, dimension()); -#endif - lean_assert(dimension() == dimension()); - for (int i = dimension() - 2 ; i >= 0; i--) { - auto & mc = get_row_values(adjust_row(i)); - for (auto & c : mc) { - unsigned col = m_column_permutation[c.m_index]; - lean_assert(col > i || (col == i && close(c.m_value, numeric_traits::one()))); - if (col == i) { - continue; - } - y[i] -= y[col] * c.m_value; - } - } -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // T * clone_y = clone_vector(y, dimension()); - // deb.apply_from_left(clone_y); - // lean_assert(vectors_are_equal(rs, clone_y, dimension())); - // delete [] rs; - // delete [] clone_y; -#endif - } + void solve_U_y_(T * y); // solving this * x = y, and putting the answer into y // the matrix here has to be upper triangular - void solve_U_y(T * y) { // the columns have to be correct - it is a column wise version -#ifdef LEAN_DEBUG - // T * rs = clone_vector(y, dimension()); -#endif - for (unsigned j = dimension() - 1; j > 0; j--) { - T yj = y[j]; - if (numeric_traits::is_zero(yj)) continue; - auto & mc = m_columns[adjust_column(j)].m_values; - for (auto & iv : mc) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i != j) { - y[i] -= iv.m_value * yj; - } - } - } -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // T * clone_y = clone_vector(y, dimension()); - // deb.apply_from_left(clone_y); - // lean_assert(vectors_are_equal(rs, clone_y, dimension())); -#endif - } + void solve_U_y(T * y); template - void find_error_in_solution_U_y(std::vector& y_orig, std::vector & y) { - unsigned i = dimension(); - while (i--) { - y_orig[i] -= dot_product_with_row(i, y); - } - } + void find_error_in_solution_U_y(std::vector& y_orig, std::vector & y); template - void add_delta_to_solution(const std::vector& del, std::vector & y) { - unsigned i = dimension(); - while (i--) { - y[i] += del[i]; - } - } + void add_delta_to_solution(const std::vector& del, std::vector & y); template - void double_solve_U_y(std::vector& y){ - std::vector y_orig(y); // copy y aside - solve_U_y(y); - find_error_in_solution_U_y(y_orig, y); - // y_orig contains the error now - solve_U_y(y_orig); - add_delta_to_solution(y_orig, y); - } + void double_solve_U_y(std::vector& y); // solving this * x = y, and putting the answer into y // the matrix here has to be upper triangular template - void solve_U_y(std::vector & y) { // it is a column wise version -#ifdef LEAN_DEBUG - // T * rs = clone_vector(y, dimension()); -#endif - - for (unsigned j = dimension(); j--; ) { - const L & yj = y[j]; - if (is_zero(yj)) continue; - auto & mc = m_columns[adjust_column(j)].m_values; - for (auto & iv : mc) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i != j) { - y[i] -= iv.m_value * yj; - } - } - } -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // T * clone_y = clone_vector(y, dimension()); - // deb.apply_from_left(clone_y); - // lean_assert(vectors_are_equal(rs, clone_y, dimension())); -#endif - } + void solve_U_y(std::vector & y); #ifdef LEAN_DEBUG T get_elem(unsigned i, unsigned j) const { return get(i, j); } @@ -741,413 +281,74 @@ public: virtual void set_number_of_columns(unsigned /*n*/) { } #endif template - L dot_product_with_row (unsigned row, const std::vector & y) const { - L ret = zero_of_type(); - auto & mc = get_row_values(adjust_row(row)); - for (auto & c : mc) { - unsigned col = m_column_permutation[c.m_index]; - ret += c.m_value * y[col]; - } - return ret; - } + L dot_product_with_row (unsigned row, const std::vector & y) const; - unsigned get_number_of_nonzeroes() const { - unsigned ret = 0; - for (unsigned i = dimension(); i--; ) { - ret += number_of_non_zeroes_in_row(i); - } - return ret; - } + unsigned get_number_of_nonzeroes() const; - unsigned get_number_of_nonzeroes_below_row(unsigned row) const { - unsigned ret = 0; - for (unsigned i = dimension() - 1; - static_cast(i) >= static_cast(row); i--) { - ret += number_of_non_zeroes_in_row(adjust_row(i)); - } - return ret; - } + unsigned get_number_of_nonzeroes_below_row(unsigned row) const; - bool get_non_zero_column_in_row(unsigned i, unsigned *j) const { - // go over the i-th row - auto & mc = get_row_values(adjust_row(i)); - if (mc.size() > 0) { - *j = m_column_permutation[mc[0].m_index]; - return true; - } - return false; - } + bool get_non_zero_column_in_row(unsigned i, unsigned *j) const; - void remove_element_that_is_not_in_w(std::vector> & column_vals, indexed_value & col_el_iv) { - auto & row_chunk = m_rows[col_el_iv.m_index]; - indexed_value & row_el_iv = row_chunk[col_el_iv.m_other]; - unsigned index_in_row = col_el_iv.m_other; - remove_element(row_chunk, col_el_iv.m_other, column_vals, row_el_iv.m_other); - if (index_in_row == 0) - set_max_in_row(row_chunk); - } + void remove_element_that_is_not_in_w(std::vector> & column_vals, indexed_value & col_el_iv); // w contains the new column // the old column inside of the matrix has not been changed yet - void remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w) { - // -------------------------------- - // column_vals represents the old column - auto & column_vals = m_columns[column_to_replace].m_values; - for (unsigned k = column_vals.size(); k-- > 0;) { - indexed_value & col_el_iv = column_vals[k]; - unsigned i = col_el_iv.m_index; - T w_data_at_i = w[adjust_row_inverse(i)]; - if (numeric_traits::is_zero(w_data_at_i)) { - remove_element_that_is_not_in_w(column_vals, col_el_iv); - } else { - auto& row_chunk = m_rows[i]; - unsigned index_in_row = col_el_iv.m_other; - if (index_in_row == 0) { - bool look_for_max = abs(w_data_at_i) < abs(row_chunk[0].m_value); - row_chunk[0].set_value(col_el_iv.m_value = w_data_at_i); - if (look_for_max) - set_max_in_row(i); - } else { - row_chunk[index_in_row].set_value(col_el_iv.m_value = w_data_at_i); - if (abs(w_data_at_i) > abs(row_chunk[0].m_value)) - put_max_index_to_0(row_chunk, index_in_row); - } - w[adjust_row_inverse(i)] = numeric_traits::zero(); - } - } - } + void remove_elements_that_are_not_in_w_and_update_common_elements(unsigned column_to_replace, indexed_vector & w); - void add_new_element(unsigned row, unsigned col, T val) { - auto & row_vals = m_rows[row]; - auto & col_vals = m_columns[col].m_values; - unsigned row_el_offs = row_vals.size(); - unsigned col_el_offs = col_vals.size(); - row_vals.push_back(indexed_value(val, col, col_el_offs)); - col_vals.push_back(indexed_value(val, row, row_el_offs)); - } + void add_new_element(unsigned row, unsigned col, T val); // w contains the "rest" of the new column; all common elements of w and the old column has been zeroed // the old column inside of the matrix has not been changed yet - void add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings) { - for (unsigned i : w.m_index) { - T w_at_i = w[i]; - if (numeric_traits::is_zero(w_at_i)) continue; // was dealt with already - if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_i)) { - unsigned ai = adjust_row(i); - add_new_element(ai, column_to_replace, w_at_i); - auto & row_chunk = m_rows[ai]; - lean_assert(row_chunk.size() > 0); - if (abs(w_at_i) > abs(row_chunk[0].m_value)) - put_max_index_to_0(row_chunk, row_chunk.size() - 1); - } - w[i] = numeric_traits::zero(); - } - w.m_index.clear(); - } + void add_new_elements_of_w_and_clear_w(unsigned column_to_replace, indexed_vector & w, lp_settings & settings); - void replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings) { - column_to_replace = adjust_column(column_to_replace); - remove_elements_that_are_not_in_w_and_update_common_elements(column_to_replace, w); - add_new_elements_of_w_and_clear_w(column_to_replace, w, settings); - } + void replace_column(unsigned column_to_replace, indexed_vector & w, lp_settings &settings); - T const & get_max_in_row(unsigned row) const { - return m_rows[adjust_row(row)][0].m_value; - } + unsigned pivot_score(unsigned i, unsigned j); - T const & get_max_in_row_without_adjusting(unsigned row) const { - return m_rows[row][0].m_value; - } + void enqueue_domain_into_pivot_queue(); + void set_max_in_rows(); - unsigned pivot_score(unsigned i, unsigned j) { - // It goes like this (rnz-1)(cnz-1) is the Markovitz number, that is the max number of - // new non zeroes we can obtain after the pivoting. - // In addition we will get another cnz - 1 elements in the eta matrix created for this pivot, - // which gives rnz(cnz-1). For example, is 0 for a column singleton, but not for - // a row singleton ( which is not a column singleton). + void zero_shortened_markovitz_numbers(); - auto col_header = m_columns[j]; + void prepare_for_factorization(); - return get_row_values(i).size() * (col_header.m_values.size() - col_header.m_shortened_markovitz - 1); - } + void recover_pivot_queue(std::vector & rejected_pivots); - void enqueue_domain_into_pivot_queue() { - lean_assert(m_pivot_queue.size() == 0); - for (unsigned i = 0; i < dimension(); i++) { - auto & rh = m_rows[i]; - unsigned rnz = rh.size(); - for (auto iv : rh) { - unsigned j = iv.m_index; - m_pivot_queue.enqueue(i, j, rnz * (m_columns[j].m_values.size() - 1)); - } - } - } + int elem_is_too_small(unsigned i, unsigned j, const T & c_partial_pivoting); - void set_max_in_rows() { - unsigned i = dimension(); - while (i--) - set_max_in_row(i); - } + bool remove_row_from_active_pivots_and_shorten_columns(unsigned row); + void remove_pivot_column(unsigned row); - void zero_shortened_markovitz_numbers() { - for (auto & ch : m_columns) - ch.zero_shortened_markovitz(); - } + void update_active_pivots(unsigned row); - void prepare_for_factorization() { - zero_shortened_markovitz_numbers(); - set_max_in_rows(); - enqueue_domain_into_pivot_queue(); - } + bool shorten_active_matrix(unsigned row, eta_matrix *eta_matrix); - void recover_pivot_queue(std::vector & rejected_pivots) { - for (auto p : rejected_pivots) { - m_pivot_queue.enqueue(p.first, p.second, pivot_score(p.first, p.second)); - } - } - - int elem_is_too_small(unsigned i, unsigned j, const T & c_partial_pivoting) { - auto & row_chunk = m_rows[i]; - - if (j == row_chunk[0].m_index) { - return 0; // the max element is at the head - } - T max = abs(row_chunk[0].m_value); - for (unsigned k = 1; k < row_chunk.size(); k++) { - auto &iv = row_chunk[k]; - if (iv.m_index == j) - return abs(iv.m_value) * c_partial_pivoting < max ? 1: 0; - } - return 2; // the element became zero but it still sits in the active pivots? - } - - bool remove_row_from_active_pivots_and_shorten_columns(unsigned row) { - unsigned arow = adjust_row(row); - for (auto & iv : m_rows[arow]) { - m_pivot_queue.remove(arow, iv.m_index); - if (adjust_column_inverse(iv.m_index) <= row) - continue; // this column will be removed anyway - auto & col = m_columns[iv.m_index]; - - col.shorten_markovich_by_one(); - if (col.m_values.size() <= col.m_shortened_markovitz) - return false; // got a zero column - } - return true; - } - - void remove_pivot_column(unsigned row) { - unsigned acol = adjust_column(row); - for (auto & iv : m_columns[acol].m_values) - if (adjust_row_inverse(iv.m_index) >= row) - m_pivot_queue.remove(iv.m_index, acol); - } - - void update_active_pivots(unsigned row) { - unsigned arow = adjust_row(row); - for (auto & iv : m_rows[arow]) { - col_header & ch = m_columns[iv.m_index]; - int cols = ch.m_values.size() - ch.m_shortened_markovitz - 1; - lean_assert(cols >= 0); - for (auto &ivc : ch.m_values) { - unsigned i = ivc.m_index; - if (adjust_row_inverse(i) <= row) continue; // the i is not an active row - m_pivot_queue.enqueue(i, iv.m_index, m_rows[i].size()*cols); - } - } - } - - bool shorten_active_matrix(unsigned row, eta_matrix *eta_matrix) { - if (!remove_row_from_active_pivots_and_shorten_columns(row)) - return false; - remove_pivot_column(row); - // need to know the max priority of the queue here - update_active_pivots(row); - if (eta_matrix == nullptr) return true; - // it looks like double work, but the pivot scores have changed for all rows - // touched by eta_matrix - for (auto it = eta_matrix->get_sparse_vector_iterator(); !it.done(); it.move()) { - unsigned row = adjust_row(it.index()); - auto & row_values = m_rows[row]; - unsigned rnz = row_values.size(); - for (auto & iv : row_values) { - col_header& ch = m_columns[iv.m_index]; - int cnz = ch.m_values.size() - ch.m_shortened_markovitz - 1; - lean_assert(cnz >= 0); - m_pivot_queue.enqueue(row, iv.m_index, rnz * cnz); - } - } - - return true; - } - - unsigned pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k) { - auto &cols = m_columns[j].m_values; - unsigned cnz = cols.size(); - for (auto & iv : cols) { - if (adjust_row_inverse(iv.m_index) < k) - cnz--; - } - lean_assert(cnz > 0); - return m_rows[i].m_values.size() * (cnz - 1); - } + unsigned pivot_score_without_shortened_counters(unsigned i, unsigned j, unsigned k); #ifdef LEAN_DEBUG - bool can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k) { - unsigned arow = adjust_row(row); - auto & row_vals = m_rows[arow].m_values; - auto & begin_iv = row_vals[0]; - T row_max = abs(begin_iv.m_value); - lean_assert(adjust_column_inverse(begin_iv.m_index) >= k); - if (pivot_score_without_shortened_counters(arow, begin_iv.m_index, k) < score) { - print_active_matrix(k); - return true; - } - for (unsigned jj = 1; jj < row_vals.size(); jj++) { - auto & iv = row_vals[jj]; - lean_assert(adjust_column_inverse(iv.m_index) >= k); - lean_assert(abs(iv.m_value) <= row_max); - if (c_partial_pivoting * abs(iv.m_value) < row_max) continue; - if (pivot_score_without_shortened_counters(arow, iv.m_index, k) < score) { - print_active_matrix(k); - return true; - } - } - return false; - } - bool really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k) { - unsigned queue_pivot_score = pivot_score_without_shortened_counters(i, j, k); - for (unsigned ii = k; ii < dimension(); ii++) { - lean_assert(!can_improve_score_for_row(ii, queue_pivot_score, c_partial_pivoting, k)); - } - return true; - } - void print_active_matrix(unsigned k) { - std::cout << "active matrix for k = " << k << std::endl; - if (k >= dimension()) { - std::cout << "empty" << std::endl; - return; - } - unsigned dim = dimension() - k; - dense_matrix b(dim, dim); - for (unsigned i = 0; i < dim; i++) - for (unsigned j = 0; j < dim; j++ ) - b.set_elem(i, j, zero_of_type()); - for (int i = k; i < dimension(); i++) { - unsigned col = adjust_column(i); - for (auto &iv : get_column_values(col)) { - unsigned row = iv.m_index; - unsigned row_ex = this->adjust_row_inverse(row); - if (row_ex < k) continue; - auto v = this->get_not_adjusted(row, col); - b.set_elem(row_ex - k, i -k, v); - } - } - print_matrix(b); - } + bool can_improve_score_for_row(unsigned row, unsigned score, T const & c_partial_pivoting, unsigned k); + bool really_best_pivot(unsigned i, unsigned j, T const & c_partial_pivoting, unsigned k); + void print_active_matrix(unsigned k); #endif - bool pivot_queue_is_correct_for_row(unsigned i, unsigned k) { - unsigned arow = adjust_row(i); - for (auto & iv : m_rows[arow].m_values) { - lean_assert(pivot_score_without_shortened_counters(arow, iv.m_index, k + 1) == - m_pivot_queue.get_priority(arow, iv.m_index)); - } - return true; + bool pivot_queue_is_correct_for_row(unsigned i, unsigned k); + + bool pivot_queue_is_correct_after_pivoting(int k); + + bool get_pivot_for_column(unsigned &i, unsigned &j, T const & c_partial_pivoting, unsigned k); + + bool elem_is_too_small(std::vector> & row_chunk, indexed_value & iv, T const & c_partial_pivoting); + + unsigned number_of_non_zeroes_in_row(unsigned row) const { + return m_rows[row].size(); } - bool pivot_queue_is_correct_after_pivoting(int k) { - for (unsigned i = k + 1; i < dimension(); i++ ) - lean_assert(pivot_queue_is_correct_for_row(i, k)); - lean_assert(m_pivot_queue.is_correct()); - return true; - } - - - bool get_pivot_for_column(unsigned &i, unsigned &j, T const & c_partial_pivoting, unsigned k) { - std::vector pivots_candidates_that_are_too_small; - while (!m_pivot_queue.is_empty()) { - m_pivot_queue.dequeue(i, j); - unsigned i_inv = adjust_row_inverse(i); - if (i_inv < k) continue; - unsigned j_inv = adjust_column_inverse(j); - if (j_inv < k) continue; - int small = elem_is_too_small(i, j, c_partial_pivoting); - if (!small) { -#ifdef LEAN_DEBUG - // if (!really_best_pivot(i, j, c_partial_pivoting, k)) { - // print_active_matrix(k); - // lean_assert(false); - // } -#endif - - recover_pivot_queue(pivots_candidates_that_are_too_small); - i = i_inv; - j = j_inv; - return true; - } - if (small != 2) // 2 means that the pair is not in the matrix - pivots_candidates_that_are_too_small.emplace_back(i, j); - } - recover_pivot_queue(pivots_candidates_that_are_too_small); - return false; - /* - unsigned m = dimension(); - unsigned markovitz_number_min = m * m; - unsigned iterations = 0; - for (unsigned k = 2; k <= m; k++){ - if (markovitz_number_min < (k-1) * (k-1)) - return true; - - if (get_pivot_in_k_short_columns(k, markovitz_number_min, i, j, c_partial_pivoting, iterations) - || - get_pivot_in_k_short_rows(k, markovitz_number_min, i, j, c_partial_pivoting, iterations)) { - return true; - } - if (iterations > search_depth * 2 && markovitz_number_min < m * m) - return true; - } - return markovitz_number_min < m * m; - */ - } - - bool elem_is_too_small(std::vector> & row_chunk, indexed_value & iv, T const & c_partial_pivoting) { - if (&iv == &row_chunk[0]) { - return false; // the max element is at the head - } - T val = abs(iv.m_value); - T max = abs(row_chunk[0].m_value); - return val * c_partial_pivoting < max; - } - - unsigned number_of_non_zeros_in_row(unsigned row) const { - return m_rows[row].m_values.size(); - } - - unsigned number_of_non_zeros_in_column(unsigned col) const { + unsigned number_of_non_zeroes_in_column(unsigned col) const { return m_columns[col].m_values.size(); } - - - bool shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column) { - std::vector> & row_chunk = get_row_values(i); - - for (indexed_value & iv : row_chunk) { - unsigned j = iv.m_index; - if (j == pivot_column) { - lean_assert(!col_is_active(j)); - continue; - } - m_columns[j].shorten_markovich_by_one(); - - if (m_columns[j].m_shortened_markovitz >= get_column_values(j).size()) { // got the zero column under the row! - return false; - } - } - return true; - } + bool shorten_columns_by_pivot_row(unsigned i, unsigned pivot_column); bool col_is_active(unsigned j, unsigned pivot) { return adjust_column_inverse(j) > pivot; @@ -1157,161 +358,20 @@ public: return adjust_row_inverse(i) > pivot; } - void fill_eta_matrix(unsigned j, eta_matrix ** eta) { - std::vector> & col_chunk = get_column_values(adjust_column(j)); - bool is_unit = true; - for (auto & iv : col_chunk) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i > j) { - is_unit = false; - break; - } - if (i == j && iv.m_value != 1) { - is_unit = false; - break; - } - } - - if (is_unit) { - *eta = nullptr; - return; - } - + void fill_eta_matrix(unsigned j, eta_matrix ** eta); #ifdef LEAN_DEBUG - *eta = new eta_matrix(j, dimension()); -#else - *eta = new eta_matrix(j); -#endif - for (auto & iv : col_chunk) { - unsigned i = adjust_row_inverse(iv.m_index); - if (i < j) { - continue; - } - if (i > j) { - (*eta)->push_back(i, - iv.m_value); - } else { // i == j - (*eta)->set_diagonal_element(iv.m_value); - } - } - (*eta)->divide_by_diagonal_element(); - } + bool is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const; - bool is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings & settings) const { - for (unsigned i = 0; i < dimension(); i++) { - std::vector> const & row_chunk = get_row_values(i); - lean_assert(row_chunk.size()); - T const & max = abs(row_chunk[0].m_value); - unsigned ai = adjust_row_inverse(i); - for (auto & iv : row_chunk) { - lean_assert(abs(iv.m_value) <= max); - unsigned aj = adjust_column_inverse(iv.m_index); - if (!(ai <= aj || numeric_traits::is_zero(iv.m_value))) - return false; - if (aj == ai) { - if (iv.m_value != 1) { - std::cout << "value at diagonal = " << iv.m_value << std::endl; - return false; - } - } - if (settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value) && (!is_zero(iv.m_value))) - return false; - } - } - return true; - } + bool is_upper_triangular_until(unsigned k) const; + void check_column_vs_rows(unsigned col); - bool is_upper_triangular_until(unsigned k) const { - for (unsigned j = 0; j < dimension() && j < k; j++) { - unsigned aj = adjust_column(j); - auto & col = get_column_values(aj); - for (auto & iv : col) { - unsigned row = adjust_row_inverse(iv.m_index); - if (row > j) - return false; - } - } - return true; - } + void check_row_vs_columns(unsigned row); + void check_rows_vs_columns(); + void check_columns_vs_rows(); - void check_column_vs_rows(unsigned col) { - auto & mc = get_column_values(col); - for (indexed_value & column_iv : mc) { - indexed_value & row_iv = column_iv_other(column_iv); - if (row_iv.m_index != col) { - std::cout << "m_other in row does not belong to column " << col << ", but to column " << row_iv.m_index << std::endl; - lean_assert(false); - } - - if (& row_iv_other(row_iv) != &column_iv) { - std::cout << "row and col do not point to each other" << std::endl; - lean_assert(false); - } - - if (row_iv.m_value != column_iv.m_value) { - std::cout << "the data from col " << col << " for row " << column_iv.m_index << " is different in the column " << std::endl; - std::cout << "in the col it is " << column_iv.m_value << ", but in the row it is " << row_iv.m_value << std::endl; - lean_assert(false); - } - } - } - - void check_row_vs_columns(unsigned row) { - auto & mc = get_row_values(row); - for (indexed_value & row_iv : mc) { - indexed_value & column_iv = row_iv_other(row_iv); - - if (column_iv.m_index != row) { - std::cout << "col_iv does not point to correct row " << row << " but to " << column_iv.m_index << std::endl; - lean_assert(false); - } - - if (& row_iv != & column_iv_other(column_iv)) { - std::cout << "row and col do not point to each other" << std::endl; - lean_assert(false); - } - - if (row_iv.m_value != column_iv.m_value) { - std::cout << "the data from col " << column_iv.m_index << " for row " << row << " is different in the column " << std::endl; - std::cout << "in the col it is " << column_iv.m_value << ", but in the row it is " << row_iv.m_value << std::endl; - lean_assert(false); - } - } - } - - void check_rows_vs_columns() { - for (unsigned i = 0; i < dimension(); i++) { - check_row_vs_columns(i); - } - } - - void check_columns_vs_rows() { - for (unsigned i = 0; i < dimension(); i++) { - check_column_vs_rows(i); - } - } - - void map_domain_to_vector(std::unordered_map, unsigned> & domain, - std::vector> & vec) { - lean_assert(domain.size() == 0 && vec.size() == 0); - for (unsigned i = 0; i < dimension(); i++) { - std::vector> & row = get_row_values(i); - for (auto & iv : row) { - unsigned j = iv.m_index; - unsigned nz = vec.size(); - pair p(i, j); - domain[p] = nz; - vec.push_back(p); - } - } - } - -#ifdef LEAN_DEBUG - void check_matrix() { - check_rows_vs_columns(); - check_columns_vs_rows(); - } + void check_matrix(); #endif }; }; diff --git a/src/util/lp/sparse_matrix_instances.cpp b/src/util/lp/sparse_matrix_instances.cpp new file mode 100644 index 0000000000..b4f241b9dc --- /dev/null +++ b/src/util/lp/sparse_matrix_instances.cpp @@ -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::dot_product_with_row(unsigned int, std::vector const&) const; + template void sparse_matrix::add_new_element(unsigned int, unsigned int, double); + template void sparse_matrix::divide_row_by_constant(unsigned int, double&, lp_settings&); + template void sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); + template const double & sparse_matrix::get(unsigned int, unsigned int) const; + template unsigned sparse_matrix::get_number_of_nonzeroes() const; + template unsigned sparse_matrix::get_number_of_nonzeroes_below_row(unsigned int) const; + template bool sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, double const&, unsigned int); +template unsigned sparse_matrix::lowest_row_in_column(unsigned int); +template bool sparse_matrix::pivot_row_to_row(unsigned int, double, unsigned int, lp_settings&); +template bool sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); +template void sparse_matrix::prepare_for_factorization(); +template void sparse_matrix::remove_element(std::vector >&, indexed_value&); +template void sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); +template void sparse_matrix::set(unsigned int, unsigned int, double); +template void sparse_matrix::set_max_in_row(std::vector >&); +template bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); +template bool sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); +template void sparse_matrix::solve_y_U(std::vector&) const; +template sparse_matrix::sparse_matrix(static_matrix const&, std::vector&); +template sparse_matrix::sparse_matrix(unsigned int); +template float const & sparse_matrix::get(unsigned int, unsigned int) const; +template bool sparse_matrix::pivot_row_to_row(unsigned int, float, unsigned int, lp_settings&); +template void sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); +template void sparse_matrix::set(unsigned int, unsigned int, float); +template sparse_matrix::sparse_matrix(unsigned int); +template void sparse_matrix::add_new_element(unsigned int, unsigned int, mpq); +template void sparse_matrix::divide_row_by_constant(unsigned int, mpq&, lp_settings&); +template void sparse_matrix::fill_eta_matrix(unsigned int, eta_matrix**); +template mpq const & sparse_matrix::get(unsigned int, unsigned int) const; +template unsigned sparse_matrix::get_number_of_nonzeroes() const; +template unsigned sparse_matrix::get_number_of_nonzeroes_below_row(unsigned int) const; +template bool sparse_matrix::get_pivot_for_column(unsigned int&, unsigned int&, mpq const&, unsigned int); +template unsigned sparse_matrix::lowest_row_in_column(unsigned int); +template bool sparse_matrix::pivot_with_eta(unsigned int, eta_matrix*, lp_settings&); +template void sparse_matrix::prepare_for_factorization(); +template void sparse_matrix::remove_element(std::vector> &, indexed_value&); +template void sparse_matrix::replace_column(unsigned int, indexed_vector&, lp_settings&); +template void sparse_matrix::set_max_in_row(std::vector>&); +template bool sparse_matrix::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); +template bool sparse_matrix::shorten_active_matrix(unsigned int, eta_matrix*); +template void sparse_matrix::solve_y_U(std::vector&) const; +template sparse_matrix::sparse_matrix(static_matrix const&, std::vector&); +template void sparse_matrix>::add_new_element(unsigned int, unsigned int, mpq); +template void sparse_matrix>::divide_row_by_constant(unsigned int, mpq&, lp_settings&); +template void sparse_matrix>::fill_eta_matrix(unsigned int, eta_matrix >**); +template const mpq & sparse_matrix>::get(unsigned int, unsigned int) const; +template unsigned sparse_matrix>::get_number_of_nonzeroes() const; +template unsigned sparse_matrix>::get_number_of_nonzeroes_below_row(unsigned int) const; +template bool sparse_matrix>::get_pivot_for_column(unsigned int&, unsigned int&, mpq const&, unsigned int); +template unsigned sparse_matrix>::lowest_row_in_column(unsigned int); +template bool sparse_matrix>::pivot_with_eta(unsigned int, eta_matrix >*, lp_settings&); +template void sparse_matrix>::prepare_for_factorization(); +template void sparse_matrix>::remove_element(std::vector>&, indexed_value&); +template void sparse_matrix>::replace_column(unsigned int, indexed_vector&, lp_settings&); +template void sparse_matrix>::set_max_in_row(std::vector>&); +template bool sparse_matrix>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector&, lp_settings&); +template bool sparse_matrix>::shorten_active_matrix(unsigned int, eta_matrix >*); +template void sparse_matrix>::solve_y_U(std::vector&) const; +template sparse_matrix>::sparse_matrix(static_matrix > const&, std::vector&); +template void sparse_matrix::double_solve_U_y(std::vector&); +template void sparse_matrix::double_solve_U_y(std::vector&); +template void sparse_matrix>::double_solve_U_y(std::vector&); + template void sparse_matrix >::double_solve_U_y >(std::vector>&); + + ///////////////////// + /* +template void lu::create_initial_factorization(); +template void lu::create_initial_factorization(); +template void lu::replace_column(unsigned int, mpq, indexed_vector&); +template void lu >::create_initial_factorization(); +template void lu >::replace_column(unsigned int, mpq, indexed_vector&); +template void lu::init_vector_w(unsigned int, indexed_vector&); +template void lu::find_error_of_yB(std::vector >&, std::vector > const&); +template void lu::init_vector_w(unsigned int, indexed_vector&); +template void lu >::find_error_of_yB(std::vector >&, std::vector > const&); +template void lu >::init_vector_w(unsigned int, indexed_vector&); +template void lu::find_error_of_yB(std::vector >&, std::vector > const&); +#ifdef LEAN_DEBUG +template void print_matrix(static_matrix&); +#endif + */ +#ifdef LEAN_DEBUG +template bool sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; +template bool sparse_matrix::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; +#endif +#ifdef LEAN_DEBUG +template bool sparse_matrix >::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const; +#endif +} diff --git a/src/util/lp/sparse_vector.h b/src/util/lp/sparse_vector.h index 4769b978d8..421f5ccd49 100644 --- a/src/util/lp/sparse_vector.h +++ b/src/util/lp/sparse_vector.h @@ -19,19 +19,6 @@ #include "util/numerics/mpfp.h" #include "util/lp/lp_settings.h" namespace lean { -template -void zero_vector(T * t, unsigned size) { - while (size-- > 0) { // it can be made faster by copying big chunks - t[size] = numeric_traits::zero(); - } -} - -template -T abs (T const & v) { return v >= zero_of_type() ? v : -v; } - - -template -class sparse_vector_iterator; // forward definition template class sparse_vector { @@ -48,45 +35,13 @@ public: return numeric_traits::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; -}; - -template -class sparse_vector_iterator { - typedef typename std::vector>::iterator p_it; - p_it m_it; - p_it m_end; -public: - sparse_vector_iterator(sparse_vector & 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; - } }; } diff --git a/src/util/lp/square_dense_submatrix.cpp b/src/util/lp/square_dense_submatrix.cpp new file mode 100644 index 0000000000..c8dbde86b2 --- /dev/null +++ b/src/util/lp/square_dense_submatrix.cpp @@ -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 +square_dense_submatrix::square_dense_submatrix (sparse_matrix *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(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 void square_dense_submatrix::init(sparse_matrix *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 int square_dense_submatrix::find_pivot_column_in_row(unsigned i) const { + int j = -1; + T max = zero_of_type(); + 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 void square_dense_submatrix::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 void square_dense_submatrix::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(); + } else { + m_v[row_offset] = t; + } + row_offset++; pivot_row_offset++; + // at the same time we pivot the L too + } +} + +template void square_dense_submatrix::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() / pivot; // creating the L matrix diagonal + continue; + } + m_v[irow_offset++] /= pivot; + } +} + +template void square_dense_submatrix::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 void square_dense_submatrix::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()); + 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(); // 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()); + } +} + +template void square_dense_submatrix::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(); // leave only L elements now + } + } +} +template +template +L square_dense_submatrix::row_by_vector_product(unsigned i, const std::vector & 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(); + 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 +template +L square_dense_submatrix::column_by_vector_product(unsigned j, const std::vector & v) { + lean_assert(j >= m_index_start); + + unsigned offset = j - m_index_start; + L r = zero_of_type(); + 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 +template +L square_dense_submatrix::row_by_indexed_vector_product(unsigned i, const indexed_vector & 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(); + 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 +template +void square_dense_submatrix::apply_from_left_local(indexed_vector & w, lp_settings & settings) { +#ifdef LEAN_DEBUG + // dense_matrix deb(*this); + // vector 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 t(m_parent->dimension(), zero_of_type()); + 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(); + } + } +#else + std::vector 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(deb_w, w.m_data)); + // lean_assert(w.is_OK()); +#endif +} + +template +template +void square_dense_submatrix::apply_from_left_to_vector(std::vector & w) { + // lp_settings & settings) { + // dense_matrix deb(*this); + // vector 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 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(deb_w, w)); +#endif +} + + template bool square_dense_submatrix::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 void square_dense_submatrix::apply_from_right(std::vector & w) { +#ifdef LEAN_DEBUG + // dense_matrix deb(*this); + // vector deb_w(w); + // deb.apply_from_right(deb_w); +#endif + std::vector 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(deb_w, w)); +#endif +} + + + + +#ifdef LEAN_DEBUG + +template T square_dense_submatrix::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() : zero_of_type(); + unsigned offs = (i - m_index_start)* m_dim + j - m_index_start; + return m_v[offs]; +} + +#endif +template void square_dense_submatrix::conjugate_by_permutation(permutation_matrix & q) { + m_row_permutation.multiply_by_permutation_from_left(q); + m_column_permutation.multiply_by_reverse_from_right(q); +} +} diff --git a/src/util/lp/square_dense_submatrix.h b/src/util/lp/square_dense_submatrix.h index af0fc6b155..8ea8d81fda 100644 --- a/src/util/lp/square_dense_submatrix.h +++ b/src/util/lp/square_dense_submatrix.h @@ -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 class square_dense_submatrix : public tail_matrix { @@ -52,39 +53,9 @@ public: square_dense_submatrix() {} - square_dense_submatrix (sparse_matrix *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(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 *parent_matrix, unsigned index_start); - void init(sparse_matrix *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 *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(); - 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(); - } 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() / 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()); - 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(); // 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()); - } - } + 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(); // leave only L elements now - } - } - } + void push_new_elements_to_parent_matrix(lp_settings & settings); + template - L row_by_vector_product(unsigned i, const std::vector & 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(); - 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 & v); template - L column_by_vector_product(unsigned j, const std::vector & v) { - lean_assert(j >= m_index_start); - - unsigned offset = j - m_index_start; - L r = zero_of_type(); - 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 & v); template - L row_by_indexed_vector_product(unsigned i, const indexed_vector & 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(); - 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 & v); template - void apply_from_left_local(indexed_vector & w, lp_settings & settings) { -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // vector 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 t(m_parent->dimension(), zero_of_type()); - 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(); - } - } -#else - std::vector 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(deb_w, w.m_data)); - // lean_assert(w.is_OK()); -#endif - } + void apply_from_left_local(indexed_vector & w, lp_settings & settings); template - void apply_from_left_to_vector(std::vector & w) { - // lp_settings & settings) { - // dense_matrix deb(*this); - // vector 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 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(deb_w, w)); -#endif - } + void apply_from_left_to_vector(std::vector & 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 & 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 & w) { -#ifdef LEAN_DEBUG - // dense_matrix deb(*this); - // vector deb_w(w); - // deb.apply_from_right(deb_w); -#endif - std::vector 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(deb_w, w)); -#endif - } - - - + void apply_from_right(std::vector & 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() : zero_of_type(); - 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 & 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 & q); }; } diff --git a/src/util/lp/square_dense_submatrix_instances.cpp b/src/util/lp/square_dense_submatrix_instances.cpp new file mode 100644 index 0000000000..6c942d3106 --- /dev/null +++ b/src/util/lp/square_dense_submatrix_instances.cpp @@ -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::init(lean::sparse_matrix*, unsigned int); +template lean::square_dense_submatrix::square_dense_submatrix(lean::sparse_matrix*, unsigned int); +template void lean::square_dense_submatrix::update_parent_matrix(lean::lp_settings&); +template bool lean::square_dense_submatrix::is_L_matrix() const; +template void lean::square_dense_submatrix::conjugate_by_permutation(lean::permutation_matrix&); +template int lean::square_dense_submatrix::find_pivot_column_in_row(unsigned int) const; +template void lean::square_dense_submatrix::pivot(unsigned int, lean::lp_settings&); +template lean::square_dense_submatrix >::square_dense_submatrix(lean::sparse_matrix >*, unsigned int); +template void lean::square_dense_submatrix >::update_parent_matrix(lean::lp_settings&); +template bool lean::square_dense_submatrix >::is_L_matrix() const; +template void lean::square_dense_submatrix >::conjugate_by_permutation(lean::permutation_matrix >&); +template int lean::square_dense_submatrix >::find_pivot_column_in_row(unsigned int) const; +template void lean::square_dense_submatrix >::pivot(unsigned int, lean::lp_settings&); +#ifdef LEAN_DEBUG +template double lean::square_dense_submatrix::get_elem(unsigned int, unsigned int) const; +#endif +template void lean::square_dense_submatrix::apply_from_right(std::vector >&); +template void lean::square_dense_submatrix::apply_from_left_local(lean::indexed_vector&, lean::lp_settings&); +template void lean::square_dense_submatrix::apply_from_left_to_vector(std::vector >&); +template lean::square_dense_submatrix::square_dense_submatrix(lean::sparse_matrix*, unsigned int); +template void lean::square_dense_submatrix::update_parent_matrix(lean::lp_settings&); +template bool lean::square_dense_submatrix::is_L_matrix() const; +template void lean::square_dense_submatrix::conjugate_by_permutation(lean::permutation_matrix&); +template int lean::square_dense_submatrix::find_pivot_column_in_row(unsigned int) const; +template void lean::square_dense_submatrix::pivot(unsigned int, lean::lp_settings&); diff --git a/src/util/lp/static_matrix.cpp b/src/util/lp/static_matrix.cpp new file mode 100644 index 0000000000..ee9f1cd375 --- /dev/null +++ b/src/util/lp/static_matrix.cpp @@ -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 +void static_matrix::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 +static_matrix::static_matrix(static_matrix const &A, unsigned * basis) : + m_work_pivot_vector(A.row_count(), numeric_traits::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 void static_matrix:: clear() { + m_work_pivot_vector.clear(); + m_rows.clear(); + m_columns.clear(); +#ifdef LEAN_DEBUG + m_domain.clear(); +#endif +} + +template void static_matrix:: init_work_pivot_vector(unsigned m) { + while (m--) m_work_pivot_vector.push_back(numeric_traits::zero()); +} + +template void static_matrix:: init_empty_matrix(unsigned m, unsigned n) { + init_work_pivot_vector(m); + init_row_columns(m, n); +} +template +template + L static_matrix::dot_product_with_row(unsigned row, const std::vector & w) { + L ret = zero_of_type(); + lean_assert(row < m_rows.size()); + for (auto & it : m_rows[row]) { + ret += w[it.m_j] * it.get_val(); + } + return ret; +}; + +template unsigned static_matrix:: 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 T static_matrix:: dot_product_with_column(const std::vector & y, unsigned j) const { + lean_assert(j < column_count()); + T ret = numeric_traits::zero(); + for (auto & it : m_columns[j]) { + ret += y[it.m_i] * it.m_value; // get_value_of_column_cell(it); + } + return ret; +} + +template void static_matrix:: add_columns_at_the_end(unsigned delta) { + for (unsigned i = 0; i < delta; i++) + add_column(); +} + +template void static_matrix:: 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 void static_matrix:: 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 +void static_matrix:: 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 +void static_matrix:: 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 +void static_matrix:: 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 void static_matrix:: 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 void static_matrix:: set(unsigned row, unsigned col, T const & val) { + if (numeric_traits::is_zero(val)) return; + lean_assert(row < row_count() && col < column_count()); +#ifdef LEAN_DEBUG + pair 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 +std::set> static_matrix::get_domain() { + std::set> 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 void static_matrix:: copy_column_to_vector (unsigned j, indexed_vector & 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 void static_matrix:: copy_column_to_vector (unsigned j, std::vector & v) const { + v.resize(row_count(), numeric_traits::zero()); + for (auto & it : m_columns[j]) { + if (!is_zero(it.m_value)) + v[it.m_i]=it.m_value; + } + } + +template void static_matrix:: 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 T static_matrix:: get_max_abs_in_row(unsigned row) const { + T ret = numeric_traits::zero(); + for (auto & t : m_rows[row]) { + T a = abs(t.get_val()); + if (a > ret) { + ret = a; + } + } + return ret; +} + +template T static_matrix:: get_min_abs_in_row(unsigned row) const { + bool first_time = true; + T ret = numeric_traits::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 T static_matrix:: get_max_abs_in_column(unsigned column) const { + T ret = numeric_traits::zero(); + for (auto & t : m_columns[column]) { + T a = abs(t.m_value); + if (a > ret) { + ret = a; + } + } + return ret; +} + +template T static_matrix:: get_min_abs_in_column(unsigned column) const { + bool first_time = true; + T ret = numeric_traits::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 void static_matrix:: check_consistency() { + std::unordered_map, T> by_rows; + for (int i = 0; i < m_rows.size(); i++){ + for (auto & t : m_rows[i]) { + pair p(i, t.m_j); + lean_assert(by_rows.find(p) == by_rows.end()); + by_rows[p] = t.get_val(); + } + } + std::unordered_map, T> by_cols; + for (int i = 0; i < m_columns.size(); i++){ + for (auto & t : m_columns[i]) { + pair 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 void static_matrix:: 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 void static_matrix:: 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 void static_matrix:: cross_out_row_from_columns(unsigned k, row_strip & row) { + for (auto & t : row) { + cross_out_row_from_column(t.m_j, k); + } +} + +template void static_matrix:: 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 T static_matrix:: 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::zero(); +} + + +template void static_matrix:: scan_row_to_work_vector(unsigned i) { + for (auto & rc : m_rows[i]) { + m_work_pivot_vector[rc.m_j] = rc.get_val(); + } +} + +template void static_matrix:: clean_row_work_vector(unsigned i) { + for (auto & rc : m_rows[i]) { + m_work_pivot_vector[rc.m_j] = numeric_traits::zero(); + } +} + + +template T static_matrix:: get_balance() const { + T ret = zero_of_type(); + for (unsigned i = 0; i < row_count(); i++) { + ret += get_row_balance(i); + } + return ret; +} + +template T static_matrix:: get_row_balance(unsigned row) const { + T ret = zero_of_type(); + for (auto & t : m_rows[row]) { + if (numeric_traits::is_zero(t.get_val())) continue; + T a = abs(t.get_val()); + numeric_traits::log(a); + ret += a * a; + } + return ret; +} +#ifdef LEAN_DEBUG +template bool static_matrix:: 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 +} diff --git a/src/util/lp/static_matrix.h b/src/util/lp/static_matrix.h index 88e054e7e8..33f6121881 100644 --- a/src/util/lp/static_matrix.h +++ b/src/util/lp/static_matrix.h @@ -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 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::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::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 - L dot_product_with_row(unsigned row, const std::vector & w) { - L ret = zero_of_type(); - 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 & 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 & y, unsigned j) const { - lean_assert(j < column_count()); - T ret = numeric_traits::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 & 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 make_row_cell(unsigned row, unsigned offs, T const & val) { - row_cell r(row, offs, val); - return r; + return row_cell(row, offs, val); } column_cell make_column_cell(unsigned column, unsigned offset, T const & val) { - column_cell r(column, offset, val); - return r; + return column_cell(column, offset, val); } - void set(unsigned row, unsigned col, T const & val) { - if (numeric_traits::is_zero(val)) return; - lean_assert(row < row_count() && col < column_count()); -#ifdef LEAN_DEBUG - pair 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> get_domain() { - std::set> 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> get_domain(); + void copy_column_to_vector (unsigned j, indexed_vector & v) const; + + void copy_column_to_vector (unsigned j, std::vector & 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 & 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 & v) const { - v.resize(row_count(), numeric_traits::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::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::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::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::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, T> by_rows; - for (int i = 0; i < m_rows.size(); i++){ - for (auto & t : m_rows[i]) { - pair p(i, t.m_j); - lean_assert(by_rows.find(p) == by_rows.end()); - by_rows[p] = t.get_val(); - } - } - std::unordered_map, T> by_cols; - for (int i = 0; i < m_columns.size(); i++){ - for (auto & t : m_columns[i]) { - pair 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::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::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(); - 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(); - for (auto & t : m_rows[row]) { - if (numeric_traits::is_zero(t.get_val())) continue; - T a = abs(t.get_val()); - numeric_traits::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; }; } diff --git a/src/util/lp/static_matrix_instances.cpp b/src/util/lp/static_matrix_instances.cpp new file mode 100644 index 0000000000..ef296e9e42 --- /dev/null +++ b/src/util/lp/static_matrix_instances.cpp @@ -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::dot_product_with_row(unsigned int, std::vector > const&); +template mpq static_matrix::dot_product_with_row(unsigned int, std::vector > const&); +template numeric_pair static_matrix >::dot_product_with_row >(unsigned int, std::vector, std::allocator > > const&); +template void static_matrix::add_column_to_vector(double const&, unsigned int, double*) const; +template void static_matrix::add_columns_at_the_end(unsigned int); +template void static_matrix::clear(); +#ifdef LEAN_DEBUG +template bool static_matrix::col_val_equal_to_row_val() const; +#endif +template void static_matrix::copy_column_to_vector(unsigned int, indexed_vector&) const; +template void static_matrix::copy_column_to_vector(unsigned int, std::vector >&) const; +template void static_matrix::divide_row_by_constant(unsigned int, double const&); +template double static_matrix::dot_product_with_column(std::vector > const&, unsigned int) const; +template double static_matrix::get_balance() const; +template std::set> static_matrix::get_domain(); +template double static_matrix::get_elem(unsigned int, unsigned int) const; +template double static_matrix::get_max_abs_in_column(unsigned int) const; +template double static_matrix::get_min_abs_in_column(unsigned int) const; +template double static_matrix::get_min_abs_in_row(unsigned int) const; +template void static_matrix::init_empty_matrix(unsigned int, unsigned int); +template void static_matrix::init_row_columns(unsigned int, unsigned int); +template static_matrix::ref & static_matrix::ref::operator=(double const&); +template void static_matrix::scale_column(unsigned int, double const&); +template void static_matrix::scale_row(unsigned int, double const&); +template void static_matrix::set(unsigned int, unsigned int, double const&); +template static_matrix::static_matrix(unsigned int, unsigned int); +template void static_matrix::add_column_to_vector(mpq const&, unsigned int, mpq*) const; +template void static_matrix::add_columns_at_the_end(unsigned int); +#ifdef LEAN_DEBUG +template bool static_matrix::col_val_equal_to_row_val() const; +#endif +template void static_matrix::copy_column_to_vector(unsigned int, indexed_vector&) const; +template void static_matrix::divide_row_by_constant(unsigned int, mpq const&); +template mpq static_matrix::dot_product_with_column(std::vector > const&, unsigned int) const; +template mpq static_matrix::get_balance() const; +template mpq static_matrix::get_elem(unsigned int, unsigned int) const; +template mpq static_matrix::get_max_abs_in_column(unsigned int) const; +template mpq static_matrix::get_max_abs_in_row(unsigned int) const; +template double static_matrix::get_max_abs_in_row(unsigned int) const; +template mpq static_matrix::get_min_abs_in_column(unsigned int) const; +template mpq static_matrix::get_min_abs_in_row(unsigned int) const; +template void static_matrix::init_row_columns(unsigned int, unsigned int); +template static_matrix::ref& static_matrix::ref::operator=(mpq const&); +template void static_matrix::scale_column(unsigned int, mpq const&); +template void static_matrix::scale_row(unsigned int, mpq const&); +template void static_matrix::set(unsigned int, unsigned int, mpq const&); + +template static_matrix::static_matrix(unsigned int, unsigned int); +#ifdef LEAN_DEBUG +template bool static_matrix >::col_val_equal_to_row_val() const; +#endif +template void static_matrix >::copy_column_to_vector(unsigned int, indexed_vector&) const; +template mpq static_matrix >::dot_product_with_column(std::vector > const&, unsigned int) const; +template mpq static_matrix >::get_elem(unsigned int, unsigned int) const; +template void static_matrix >::init_empty_matrix(unsigned int, unsigned int); +template void static_matrix >::set(unsigned int, unsigned int, mpq const&); + } diff --git a/src/util/lp/suhl_pivot_chooser.h b/src/util/lp/suhl_pivot_chooser.h deleted file mode 100644 index 5be9527f1c..0000000000 --- a/src/util/lp/suhl_pivot_chooser.h +++ /dev/null @@ -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 -#include "util/debug.h" -#include "util/numerics/numeric_traits.h" -#include "util/numerics/xnumeral.h" - -#include -#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 -class suhl_pivot_chooser : public pivot_chooser_base { -// m_U is a square matrix - sparse_matrix & m_U; - lp_settings const & m_settings; -public: -// constructor - suhl_pivot_chooser(sparse_matrix & 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; - } -}; -} diff --git a/src/util/lp/tail_matrix.h b/src/util/lp/tail_matrix.h new file mode 100644 index 0000000000..35214395b3 --- /dev/null +++ b/src/util/lp/tail_matrix.h @@ -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 +class tail_matrix +#ifdef LEAN_DEBUG + : public matrix +#endif +{ +public: + virtual void apply_from_left_to_T(indexed_vector & w, lp_settings & settings) = 0; + virtual void apply_from_left(std::vector & w, lp_settings & settings) = 0; + virtual void apply_from_right(std::vector & w) = 0; + virtual ~tail_matrix() {} +}; +}