dev(lp): port to windows (msys2)
Signed-off-by: Lev Nachmanson <levnach@microsoft.com>
This commit is contained in:
parent
99dcad0dda
commit
28bf891b7f
85 changed files with 12230 additions and 10019 deletions
|
|
@ -337,6 +337,8 @@ add_subdirectory(util/sexpr)
|
|||
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:sexpr>)
|
||||
add_subdirectory(util/interval)
|
||||
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:interval>)
|
||||
add_subdirectory(util/lp)
|
||||
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:lp>)
|
||||
add_subdirectory(kernel)
|
||||
set(LEAN_OBJS ${LEAN_OBJS} $<TARGET_OBJECTS:kernel>)
|
||||
add_subdirectory(kernel/inductive)
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
add_executable(lp_tst lp.cpp $<TARGET_OBJECTS:util> $<TARGET_OBJECTS:numerics>)
|
||||
add_executable(lp_tst lp.cpp $<TARGET_OBJECTS:util> $<TARGET_OBJECTS:numerics> $<TARGET_OBJECTS:lp>)
|
||||
target_link_libraries(lp_tst ${EXTRA_LIBS})
|
||||
add_test(lp_tst ${CMAKE_CURRENT_BINARY_DIR}/lp_tst)
|
||||
add_executable(double_compare double_compare.cpp $<TARGET_OBJECTS:util> $<TARGET_OBJECTS:numerics>)
|
||||
|
|
|
|||
|
|
@ -17,7 +17,6 @@ Author: Lev Nachmanson
|
|||
#include <stdlib.h>
|
||||
#include <utility>
|
||||
#include "util/pair.h"
|
||||
#include "util/lp/lp.h"
|
||||
#include "util/lp/lp_primal_simplex.h"
|
||||
#include "tests/util/lp/mps_reader.h"
|
||||
#include "tests/util/lp/smt_reader.h"
|
||||
|
|
@ -69,8 +68,8 @@ void test_matrix(sparse_matrix<T, X> & a) {
|
|||
a.set(row, col, T(0));
|
||||
|
||||
|
||||
unsigned i = rand_r(& seed) % m;
|
||||
unsigned j = rand_r(& seed) % m;
|
||||
unsigned i = my_random() % m;
|
||||
unsigned j = my_random() % m;
|
||||
|
||||
auto t = T(1);
|
||||
|
||||
|
|
@ -248,7 +247,7 @@ void fill_long_row_exp(sparse_matrix<double, double> &m, int i) {
|
|||
int n = m.dimension();
|
||||
|
||||
for (int j = 0; j < n; j ++) {
|
||||
m(i, j) = rand_r(& seed) % 20;
|
||||
m(i, j) = my_random() % 20;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -256,7 +255,7 @@ void fill_long_row_exp(static_matrix<double, double> &m, int i) {
|
|||
int n = m.column_count();
|
||||
|
||||
for (int j = 0; j < n; j ++) {
|
||||
m(i, j) = rand_r(& seed) % 20;
|
||||
m(i, j) = my_random() % 20;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -528,6 +527,8 @@ void test_lp_primal_core_solver() {
|
|||
test_lp_0();
|
||||
test_lp_1();
|
||||
}
|
||||
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X>
|
||||
void test_swap_rows_with_permutation(sparse_matrix<T, X>& m){
|
||||
|
|
@ -538,8 +539,8 @@ void test_swap_rows_with_permutation(sparse_matrix<T, X>& m){
|
|||
print_matrix(m);
|
||||
lean_assert(original == q * m);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
unsigned row1 = lrand48() % dim;
|
||||
unsigned row2 = lrand48() % dim;
|
||||
unsigned row1 = my_random() % dim;
|
||||
unsigned row2 = my_random() % dim;
|
||||
if (row1 == row2) continue;
|
||||
cout << "swap " << row1 << " " << row2 << std::endl;
|
||||
m.swap_rows(row1, row2);
|
||||
|
|
@ -553,36 +554,6 @@ void test_swap_rows_with_permutation(sparse_matrix<T, X>& m){
|
|||
template <typename T, typename X>
|
||||
void fill_matrix(sparse_matrix<T, X>& m); // forward definition
|
||||
#ifdef LEAN_DEBUG
|
||||
void matrix_repro_test() {
|
||||
unsigned dim = 10;
|
||||
sparse_matrix<double, double> m(dim);
|
||||
fill_matrix(m);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
unsigned row1 = lrand48() % dim;
|
||||
unsigned row2 = lrand48() % dim;
|
||||
if (row1 == row2) continue;
|
||||
m.swap_rows(row1, row2);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 100; i++) {
|
||||
unsigned col1 = lrand48() % dim;
|
||||
unsigned col2 = lrand48() % dim;
|
||||
if (col1 == col2) continue;
|
||||
m.swap_columns(col1, col2);
|
||||
}
|
||||
|
||||
dense_matrix<double, double> d(m);
|
||||
for (unsigned i = 0; i < d.row_count(); i++)
|
||||
for (unsigned j = 0; j < d.column_count(); j++)
|
||||
d.set_elem(i, j, m.get_not_adjusted(i, j));
|
||||
|
||||
auto l = d * m.m_column_permutation;
|
||||
l = m.m_row_permutation * l;
|
||||
cout << "matrix_repro_test" << std::endl;
|
||||
print_matrix(l);
|
||||
lean_assert(l == m);
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void test_swap_cols_with_permutation(sparse_matrix<T, X>& m){
|
||||
cout << "testing swaps" << std::endl;
|
||||
|
|
@ -592,8 +563,8 @@ void test_swap_cols_with_permutation(sparse_matrix<T, X>& m){
|
|||
print_matrix(m);
|
||||
lean_assert(original == q * m);
|
||||
for (int i = 0; i < 100; i++) {
|
||||
unsigned row1 = lrand48() % dim;
|
||||
unsigned row2 = lrand48() % dim;
|
||||
unsigned row1 = my_random() % dim;
|
||||
unsigned row2 = my_random() % dim;
|
||||
if (row1 == row2) continue;
|
||||
cout << "swap " << row1 << " " << row2 << std::endl;
|
||||
m.swap_rows(row1, row2);
|
||||
|
|
@ -915,8 +886,7 @@ void test_swap_operations() {
|
|||
test_swap_rows();
|
||||
test_swap_columns();
|
||||
}
|
||||
#endif
|
||||
#ifdef LEAN_DEBUG
|
||||
|
||||
void test_dense_matrix() {
|
||||
dense_matrix<double, double> d(3, 2);
|
||||
d.set_elem(0, 0, 1);
|
||||
|
|
@ -945,104 +915,9 @@ void test_dense_matrix() {
|
|||
p2.set_elem(1, 0, 1);
|
||||
auto c2 = d * p2;
|
||||
}
|
||||
|
||||
void test_conjugate_eta_matrix() {
|
||||
permutation_matrix<double, double> p(5);
|
||||
p[0] = 3; p[1] = 2; p[2] = 1; p[3] = 4;
|
||||
p[4] = 0;
|
||||
|
||||
cout << "p="; p.print();
|
||||
#ifdef LEAN_DEBUG
|
||||
eta_matrix<double, double> l(2, 5);
|
||||
#else
|
||||
eta_matrix<double, double> l(2);
|
||||
#endif
|
||||
for (unsigned i = 3; i <= 4; i++)
|
||||
l.push_back(i, i+2); // i+2 for the value
|
||||
|
||||
l.set_diagonal_element(10);
|
||||
|
||||
cout << "l" << std::endl;
|
||||
print_matrix(l);
|
||||
dense_matrix<double, double> lcopy(l);
|
||||
|
||||
l.conjugate_by_permutation(p);
|
||||
|
||||
permutation_matrix<double, double> pr = p.get_inverse();
|
||||
|
||||
auto conj = lcopy * pr;
|
||||
cout << "U*pr" << std::endl;
|
||||
print_matrix(conj);
|
||||
conj = p * conj;
|
||||
cout << "conj " << std::endl;
|
||||
print_matrix(conj);
|
||||
cout << "l" << std::endl;
|
||||
print_matrix(l);
|
||||
lean_assert(conj == l);
|
||||
}
|
||||
|
||||
void test_conjugate0() {
|
||||
permutation_matrix<double, double> p(5);
|
||||
p[0] = 3; p[1] = 2; p[2] = 1; p[3] = 4;
|
||||
p[4] = 0;
|
||||
|
||||
one_off_diagonal_matrix<double, double> one_off(1, 4, 2, 3);
|
||||
one_off.set_number_of_rows(5);
|
||||
one_off.set_number_of_columns(5);
|
||||
one_off_diagonal_matrix<double, double> one_off_copy(1, 4, 2, 3);
|
||||
one_off_copy.set_number_of_rows(5);
|
||||
one_off_copy.set_number_of_columns(5);
|
||||
one_off.conjugate_by_permutation(p);
|
||||
|
||||
permutation_matrix<double, double> pr = p.get_inverse();
|
||||
|
||||
auto conj = one_off_copy * (pr);
|
||||
conj = p * conj;
|
||||
lean_assert(conj == one_off);
|
||||
}
|
||||
|
||||
void test_conjugate_perm(){
|
||||
permutation_matrix<double, double> p(5);
|
||||
p[0] = 1; p[1] = 2; p[2] = 3; p[3] = 4;
|
||||
p[4] = 0;
|
||||
|
||||
one_off_diagonal_matrix<double, double> one_off(1, 3, 2, 3);
|
||||
one_off.set_number_of_rows(5);
|
||||
one_off.set_number_of_columns(5);
|
||||
one_off_diagonal_matrix<double, double> one_off_copy(1, 3, 2, 3);
|
||||
one_off_copy.set_number_of_rows(5);
|
||||
one_off_copy.set_number_of_columns(5);
|
||||
one_off.conjugate_by_permutation(p);
|
||||
|
||||
permutation_matrix<double, double> pr = p.get_inverse();
|
||||
|
||||
auto conj = one_off_copy * pr;
|
||||
conj = p * conj;
|
||||
lean_assert(conj == one_off);
|
||||
test_conjugate0();
|
||||
}
|
||||
|
||||
void test_conjugate() {
|
||||
permutation_matrix<double, double> p(5);
|
||||
p[0] = 1; p[1] = 2; p[2] = 3; p[3] = 4;
|
||||
p[4] = 0;
|
||||
|
||||
one_off_diagonal_matrix<double, double> one_off(1, 3, 2, 3);
|
||||
one_off.set_number_of_rows(5);
|
||||
one_off.set_number_of_columns(5);
|
||||
one_off_diagonal_matrix<double, double> one_off_copy(1, 3, 2, 3);
|
||||
one_off_copy.set_number_of_rows(5);
|
||||
one_off_copy.set_number_of_columns(5);
|
||||
one_off.conjugate_by_permutation(p);
|
||||
|
||||
permutation_matrix<double, double> pr = p.get_inverse();
|
||||
|
||||
auto conj = one_off_copy * pr;
|
||||
conj = p * conj;
|
||||
lean_assert(conj == one_off);
|
||||
test_conjugate0();
|
||||
}
|
||||
#endif
|
||||
|
||||
std::vector<permutation_matrix<double, double>> vector_of_permutaions() {
|
||||
std::vector<permutation_matrix<double, double>> ret;
|
||||
|
|
@ -1327,9 +1202,9 @@ void test_upair_queue() {
|
|||
binary_heap_upair_queue<int> q(2);
|
||||
unordered_map<upair, int> m;
|
||||
for (int k = 0; k < 100; k++) {
|
||||
int i = lrand48()%n;
|
||||
int j = lrand48()%n;
|
||||
q.enqueue(i, j, lrand48()%n);
|
||||
int i = my_random()%n;
|
||||
int j = my_random()%n;
|
||||
q.enqueue(i, j, my_random()%n);
|
||||
}
|
||||
|
||||
q.remove(5, 5);
|
||||
|
|
@ -1439,15 +1314,15 @@ void solve_mps_with_known_solution(std::string file_name, std::unordered_map<str
|
|||
}
|
||||
|
||||
int get_random_rows() {
|
||||
return 5 + rand_r(& seed) % 2;
|
||||
return 5 + my_random() % 2;
|
||||
}
|
||||
|
||||
int get_random_columns() {
|
||||
return 5 + rand_r(& seed) % 3;
|
||||
return 5 + my_random() % 3;
|
||||
}
|
||||
|
||||
int get_random_int() {
|
||||
return -1 + rand_r(& seed) % 2; // (1.0 + RAND_MAX);
|
||||
return -1 + my_random() % 2; // (1.0 + RAND_MAX);
|
||||
}
|
||||
|
||||
void add_random_row(lp_primal_simplex<double, double> * solver, int cols, int row) {
|
||||
|
|
@ -1634,7 +1509,11 @@ void test_out_dir(string out_dir) {
|
|||
DIR *out_dir_p = opendir(out_dir.c_str());
|
||||
if (out_dir_p == nullptr) {
|
||||
cout << "creating directory " << out_dir << std::endl;
|
||||
#ifdef LEAN_WINDOWS
|
||||
int res = mkdir(out_dir.c_str());
|
||||
#else
|
||||
int res = mkdir(out_dir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH);
|
||||
#endif
|
||||
if (res) {
|
||||
cout << "Cannot open output directory \"" << out_dir << "\"" << std::endl;
|
||||
}
|
||||
|
|
@ -1898,26 +1777,6 @@ void test_replace_column() {
|
|||
}
|
||||
}
|
||||
|
||||
void test_eta_matrix() {
|
||||
sparse_matrix<double, double> m(8);
|
||||
fill_matrix(m);
|
||||
lp_settings settings;
|
||||
for (unsigned j = 0; j < m.dimension(); j++){
|
||||
eta_matrix<double, double> * eta;
|
||||
m.fill_eta_matrix(j, &eta);
|
||||
m.prepare_for_factorization();
|
||||
|
||||
for (auto it = eta->get_sparse_vector_iterator(); !it.done(); it.move()) {
|
||||
m.pivot_row_to_row(j, it.value(), it.index(), settings);
|
||||
double de = eta->get_diagonal_element();
|
||||
m.divide_row_by_constant(j, de, settings);
|
||||
}
|
||||
delete eta;
|
||||
}
|
||||
|
||||
lean_assert(m.is_upper_triangular_and_maximums_are_set_correctly_in_rows(settings));
|
||||
}
|
||||
|
||||
|
||||
void setup_args_parser(argument_parser & parser) {
|
||||
parser.add_option_with_after_string_with_help("--density", "the percentage of non-zeroes in the matrix below which it is not dense");
|
||||
|
|
@ -2154,7 +2013,8 @@ void compare_costs(string glpk_out_file_name,
|
|||
|
||||
|
||||
|
||||
void compare_with_glpk(string glpk_out_file_name, string lp_out_file_name, unsigned & successes, unsigned & failures, string lp_file_name) {
|
||||
void compare_with_glpk(string glpk_out_file_name, string lp_out_file_name, unsigned & successes, unsigned & failures, string /*lp_file_name*/) {
|
||||
#ifdef CHECK_GLPK_SOLUTION
|
||||
std::unordered_map<string, double> * solution_table = get_solution_from_glpsol_output(glpk_out_file_name);
|
||||
if (solution_is_feasible(lp_file_name, *solution_table)) {
|
||||
cout << "glpk solution is feasible" << std::endl;
|
||||
|
|
@ -2162,6 +2022,7 @@ void compare_with_glpk(string glpk_out_file_name, string lp_out_file_name, unsig
|
|||
cout << "glpk solution is infeasible" << std::endl;
|
||||
}
|
||||
delete solution_table;
|
||||
#endif
|
||||
if (compare_statuses(glpk_out_file_name, lp_out_file_name, successes, failures)) {
|
||||
compare_costs(glpk_out_file_name, lp_out_file_name, successes, failures);
|
||||
}
|
||||
|
|
@ -2213,6 +2074,14 @@ void process_test_file(string test_dir, string test_file_name, argument_parser &
|
|||
}
|
||||
}
|
||||
}
|
||||
int my_readdir(DIR *dirp, struct dirent *entry, struct dirent **result) {
|
||||
#ifdef LEAN_WINDOWS
|
||||
*result = readdir(dirp);
|
||||
return *result != nullptr? 0 : 1;
|
||||
#else
|
||||
return readdir_r(dirp, entry, result);
|
||||
#endif
|
||||
}
|
||||
|
||||
std::vector<std::pair<std::string, int>> get_file_list_of_dir(std::string test_file_dir) {
|
||||
DIR *dir;
|
||||
|
|
@ -2224,16 +2093,19 @@ std::vector<std::pair<std::string, int>> get_file_list_of_dir(std::string test_f
|
|||
struct dirent entry;
|
||||
struct dirent* result;
|
||||
int return_code;
|
||||
for (return_code = readdir_r(dir, &entry, &result);
|
||||
result != NULL && return_code == 0;
|
||||
return_code = readdir_r(dir, &entry, &result)) {
|
||||
DIR *tmp_dp = opendir(entry.d_name);
|
||||
for (return_code = my_readdir(dir, &entry, &result);
|
||||
#ifndef LEAN_WINDOWS
|
||||
result != nullptr &&
|
||||
#endif
|
||||
return_code == 0;
|
||||
return_code = my_readdir(dir, &entry, &result)) {
|
||||
DIR *tmp_dp = opendir(result->d_name);
|
||||
struct stat file_record;
|
||||
if (tmp_dp == nullptr) {
|
||||
std::string s = test_file_dir+ "/" + entry.d_name;
|
||||
std::string s = test_file_dir+ "/" + result->d_name;
|
||||
int stat_ret = stat(s.c_str(), & file_record);
|
||||
if (stat_ret!= -1) {
|
||||
ret.push_back(make_pair(entry.d_name, file_record.st_size));
|
||||
ret.push_back(make_pair(result->d_name, file_record.st_size));
|
||||
} else {
|
||||
perror("stat");
|
||||
exit(1);
|
||||
|
|
@ -2577,7 +2449,6 @@ int main(int argn, char * const * argv) {
|
|||
int ret;
|
||||
argument_parser args_parser(argn, argv);
|
||||
setup_args_parser(args_parser);
|
||||
|
||||
if (!args_parser.parse()) {
|
||||
std::cout << args_parser.m_error_message << std::endl;
|
||||
std::cout << args_parser.usage_string();
|
||||
|
|
@ -2675,9 +2546,7 @@ int main(int argn, char * const * argv) {
|
|||
test_replace_column();
|
||||
#ifdef LEAN_DEBUG
|
||||
test_perm_apply_reverse_from_right();
|
||||
test_conjugate_perm();
|
||||
sparse_matrix_with_permutaions_test();
|
||||
test_conjugate();
|
||||
test_dense_matrix();
|
||||
test_swap_operations();
|
||||
test_permutations();
|
||||
|
|
|
|||
|
|
@ -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
|
||||
)
|
||||
|
|
|
|||
201
src/util/lp/binary_heap_priority_queue.cpp
Normal file
201
src/util/lp/binary_heap_priority_queue.cpp
Normal file
|
|
@ -0,0 +1,201 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/binary_heap_priority_queue.h"
|
||||
namespace lean {
|
||||
// is is the child place in heap
|
||||
template <typename T> void binary_heap_priority_queue<T>::swap_with_parent(unsigned i) {
|
||||
unsigned parent = m_heap[i >> 1];
|
||||
put_at(i >> 1, m_heap[i]);
|
||||
put_at(i, parent);
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::put_at(unsigned i, unsigned h) {
|
||||
m_heap[i] = h;
|
||||
m_heap_inverse[h] = i;
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::decrease_priority(unsigned o, T newPriority) {
|
||||
m_priorities[o] = newPriority;
|
||||
int i = m_heap_inverse[o];
|
||||
while (i > 1) {
|
||||
if (m_priorities[m_heap[i]] < m_priorities[m_heap[i >> 1]])
|
||||
swap_with_parent(i);
|
||||
else
|
||||
break;
|
||||
i >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T> bool binary_heap_priority_queue<T>::is_consistent() const {
|
||||
for (int i = 0; i < m_heap_inverse.size(); i++) {
|
||||
int i_index = m_heap_inverse[i];
|
||||
lean_assert(i_index <= static_cast<int>(m_heap_size));
|
||||
lean_assert(i_index == -1 || m_heap[i_index] == i);
|
||||
}
|
||||
for (unsigned i = 1; i < m_heap_size; i++) {
|
||||
unsigned ch = i << 1;
|
||||
for (int k = 0; k < 2; k++) {
|
||||
if (ch > m_heap_size) break;
|
||||
if (!(m_priorities[m_heap[i]] <= m_priorities[m_heap[ch]])){
|
||||
std::cout << "m_heap_size = " << m_heap_size << std::endl;
|
||||
std::cout << "i = " << i << std::endl;
|
||||
std::cout << "m_heap[i] = " << m_heap[i] << std::endl;
|
||||
std::cout << "ch = " << ch << std::endl;
|
||||
std::cout << "m_heap[ch] = " << m_heap[ch] << std::endl;
|
||||
std::cout << "m_priorities[m_heap[i]] = " << m_priorities[m_heap[i]] << std::endl;
|
||||
std::cout << "m_priorities[m_heap[ch]] = " << m_priorities[m_heap[ch]]<< std::endl;
|
||||
return false;
|
||||
}
|
||||
ch++;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
template <typename T> void binary_heap_priority_queue<T>::remove(unsigned o) {
|
||||
T priority_of_o = m_priorities[o];
|
||||
int o_in_heap = m_heap_inverse[o];
|
||||
if (o_in_heap == -1) {
|
||||
return; // nothing to do
|
||||
}
|
||||
lean_assert(o_in_heap <= m_heap_size);
|
||||
if (o_in_heap < m_heap_size) {
|
||||
put_at(o_in_heap, m_heap[m_heap_size--]);
|
||||
if (m_priorities[m_heap[o_in_heap]] > priority_of_o) {
|
||||
fix_heap_under(o_in_heap);
|
||||
} else { // we need to propogate the m_heap[o_in_heap] up
|
||||
unsigned i = o_in_heap;
|
||||
while (i > 1) {
|
||||
unsigned ip = i >> 1;
|
||||
if (m_priorities[m_heap[i]] < m_priorities[m_heap[ip]])
|
||||
swap_with_parent(i);
|
||||
else
|
||||
break;
|
||||
i = ip;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lean_assert(o_in_heap == m_heap_size);
|
||||
m_heap_size--;
|
||||
}
|
||||
m_heap_inverse[o] = -1;
|
||||
// lean_assert(is_consistent());
|
||||
}
|
||||
// n is the initial queue capacity.
|
||||
// The capacity will be enlarged two times automatically if needed
|
||||
template <typename T> binary_heap_priority_queue<T>::binary_heap_priority_queue(unsigned n) :
|
||||
m_priorities(n),
|
||||
m_heap(n + 1), // because the indexing for A starts from 1
|
||||
m_heap_inverse(n, -1)
|
||||
{ }
|
||||
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::resize(unsigned n) {
|
||||
m_priorities.resize(n);
|
||||
m_heap.resize(n + 1);
|
||||
m_heap_inverse.resize(n, -1);
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::put_to_heap(unsigned i, unsigned o) {
|
||||
m_heap[i] = o;
|
||||
m_heap_inverse[o] = i;
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::enqueue_new(unsigned o, const T& priority) {
|
||||
m_heap_size++;
|
||||
int i = m_heap_size;
|
||||
lean_assert(o < m_priorities.size());
|
||||
m_priorities[o] = priority;
|
||||
put_at(i, o);
|
||||
while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) {
|
||||
swap_with_parent(i);
|
||||
i >>= 1;
|
||||
}
|
||||
}
|
||||
// This method can work with an element that is already in the queue.
|
||||
// In this case the priority will be changed and the queue adjusted.
|
||||
template <typename T> void binary_heap_priority_queue<T>::enqueue(unsigned o, const T & priority) {
|
||||
if (o >= m_priorities.size()) {
|
||||
resize(o << 1); // make the size twice larger
|
||||
}
|
||||
if (m_heap_inverse[o] == -1)
|
||||
enqueue_new(o, priority);
|
||||
else
|
||||
change_priority_for_existing(o, priority);
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::change_priority_for_existing(unsigned o, const T & priority) {
|
||||
if (m_priorities[o] > priority) {
|
||||
decrease_priority(o, priority);
|
||||
} else {
|
||||
m_priorities[o] = priority;
|
||||
fix_heap_under(m_heap_inverse[o]);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// return the first element of the queue and removes it from the queue
|
||||
template <typename T> unsigned binary_heap_priority_queue<T>::dequeue_and_get_priority(T & priority) {
|
||||
lean_assert(m_heap_size != 0);
|
||||
int ret = m_heap[1];
|
||||
priority = m_priorities[ret];
|
||||
put_the_last_at_the_top_and_fix_the_heap();
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::fix_heap_under(unsigned i) {
|
||||
while (true) {
|
||||
int smallest = i;
|
||||
int l = i << 1;
|
||||
if (l <= m_heap_size && m_priorities[m_heap[l]] < m_priorities[m_heap[i]])
|
||||
smallest = l;
|
||||
int r = l + 1;
|
||||
if (r <= m_heap_size && m_priorities[m_heap[r]] < m_priorities[m_heap[smallest]])
|
||||
smallest = r;
|
||||
if (smallest != i)
|
||||
swap_with_parent(smallest);
|
||||
else
|
||||
break;
|
||||
i = smallest;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_priority_queue<T>::put_the_last_at_the_top_and_fix_the_heap() {
|
||||
if (m_heap_size > 1) {
|
||||
put_at(1, m_heap[m_heap_size--]);
|
||||
fix_heap_under(1);
|
||||
} else {
|
||||
m_heap_size--;
|
||||
}
|
||||
}
|
||||
/// return the first element of the queue and removes it from the queue
|
||||
template <typename T> unsigned binary_heap_priority_queue<T>::dequeue() {
|
||||
lean_assert(m_heap_size);
|
||||
int ret = m_heap[1];
|
||||
put_the_last_at_the_top_and_fix_the_heap();
|
||||
m_heap_inverse[ret] = -1;
|
||||
return ret;
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T> void binary_heap_priority_queue<T>::print() {
|
||||
std::vector<int> index;
|
||||
std::vector<T> prs;
|
||||
while (size()) {
|
||||
T prior;
|
||||
int j = dequeue_and_get_priority(prior);
|
||||
index.push_back(j);
|
||||
prs.push_back(prior);
|
||||
std::cout << "(" << j << ", " << prior << ")";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
// restore the queue
|
||||
for (int i = 0; i < index.size(); i++)
|
||||
enqueue(index[i], prs[i]);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -6,6 +6,7 @@
|
|||
*/
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "util/debug.h"
|
||||
namespace lean {
|
||||
// the elements with the smallest priority are dequeued first
|
||||
template <typename T>
|
||||
|
|
@ -18,209 +19,43 @@ class binary_heap_priority_queue {
|
|||
unsigned m_heap_size = 0;
|
||||
|
||||
// is is the child place in heap
|
||||
void swap_with_parent(unsigned i) {
|
||||
unsigned parent = m_heap[i >> 1];
|
||||
put_at(i >> 1, m_heap[i]);
|
||||
put_at(i, parent);
|
||||
}
|
||||
|
||||
|
||||
void put_at(unsigned i, unsigned h) {
|
||||
m_heap[i] = h;
|
||||
m_heap_inverse[h] = i;
|
||||
}
|
||||
|
||||
void decrease_priority(unsigned o, T newPriority) {
|
||||
m_priorities[o] = newPriority;
|
||||
int i = m_heap_inverse[o];
|
||||
while (i > 1) {
|
||||
if (m_priorities[m_heap[i]] < m_priorities[m_heap[i >> 1]])
|
||||
swap_with_parent(i);
|
||||
else
|
||||
break;
|
||||
i >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
void swap_with_parent(unsigned i);
|
||||
void put_at(unsigned i, unsigned h);
|
||||
void decrease_priority(unsigned o, T newPriority);
|
||||
public:
|
||||
bool is_consistent() const {
|
||||
for (int i = 0; i < m_heap_inverse.size(); i++) {
|
||||
int i_index = m_heap_inverse[i];
|
||||
lean_assert(i_index <= static_cast<int>(m_heap_size));
|
||||
lean_assert(i_index == -1 || m_heap[i_index] == i);
|
||||
}
|
||||
for (unsigned i = 1; i < m_heap_size; i++) {
|
||||
unsigned ch = i << 1;
|
||||
for (int k = 0; k < 2; k++) {
|
||||
if (ch > m_heap_size) break;
|
||||
if (!(m_priorities[m_heap[i]] <= m_priorities[m_heap[ch]])){
|
||||
std::cout << "m_heap_size = " << m_heap_size << std::endl;
|
||||
std::cout << "i = " << i << std::endl;
|
||||
std::cout << "m_heap[i] = " << m_heap[i] << std::endl;
|
||||
std::cout << "ch = " << ch << std::endl;
|
||||
std::cout << "m_heap[ch] = " << m_heap[ch] << std::endl;
|
||||
std::cout << "m_priorities[m_heap[i]] = " << m_priorities[m_heap[i]] << std::endl;
|
||||
std::cout << "m_priorities[m_heap[ch]] = " << m_priorities[m_heap[ch]]<< std::endl;
|
||||
return false;
|
||||
}
|
||||
ch++;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
bool is_consistent() const;
|
||||
#endif
|
||||
public:
|
||||
void remove(unsigned o) {
|
||||
T priority_of_o = m_priorities[o];
|
||||
int o_in_heap = m_heap_inverse[o];
|
||||
if (o_in_heap == -1) {
|
||||
return; // nothing to do
|
||||
}
|
||||
lean_assert(o_in_heap <= m_heap_size);
|
||||
if (o_in_heap < m_heap_size) {
|
||||
put_at(o_in_heap, m_heap[m_heap_size--]);
|
||||
if (m_priorities[m_heap[o_in_heap]] > priority_of_o) {
|
||||
fix_heap_under(o_in_heap);
|
||||
} else { // we need to propogate the m_heap[o_in_heap] up
|
||||
unsigned i = o_in_heap;
|
||||
while (i > 1) {
|
||||
unsigned ip = i >> 1;
|
||||
if (m_priorities[m_heap[i]] < m_priorities[m_heap[ip]])
|
||||
swap_with_parent(i);
|
||||
else
|
||||
break;
|
||||
i = ip;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
lean_assert(o_in_heap == m_heap_size);
|
||||
m_heap_size--;
|
||||
}
|
||||
m_heap_inverse[o] = -1;
|
||||
// lean_assert(is_consistent());
|
||||
}
|
||||
void remove(unsigned o);
|
||||
unsigned size() const { return m_heap_size; }
|
||||
binary_heap_priority_queue(): m_heap(1) {} // the empty constructror
|
||||
// n is the initial queue capacity.
|
||||
// The capacity will be enlarged two times automatically if needed
|
||||
binary_heap_priority_queue(unsigned n) :
|
||||
m_priorities(n),
|
||||
m_heap(n + 1), // because the indexing for A starts from 1
|
||||
m_heap_inverse(n, -1)
|
||||
{ }
|
||||
binary_heap_priority_queue(unsigned n);
|
||||
|
||||
void clear() {
|
||||
m_heap_size = 0;
|
||||
}
|
||||
void clear() { m_heap_size = 0; }
|
||||
|
||||
void resize(unsigned n) {
|
||||
m_priorities.resize(n);
|
||||
m_heap.resize(n + 1);
|
||||
m_heap_inverse.resize(n, -1);
|
||||
}
|
||||
void resize(unsigned n);
|
||||
void put_to_heap(unsigned i, unsigned o);
|
||||
|
||||
void put_to_heap(unsigned i, unsigned o) {
|
||||
m_heap[i] = o;
|
||||
m_heap_inverse[o] = i;
|
||||
}
|
||||
|
||||
void enqueue_new(unsigned o, const T& priority) {
|
||||
m_heap_size++;
|
||||
int i = m_heap_size;
|
||||
lean_assert(o < m_priorities.size());
|
||||
m_priorities[o] = priority;
|
||||
put_at(i, o);
|
||||
while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) {
|
||||
swap_with_parent(i);
|
||||
i >>= 1;
|
||||
}
|
||||
}
|
||||
void enqueue_new(unsigned o, const T& priority);
|
||||
|
||||
// This method can work with an element that is already in the queue.
|
||||
// In this case the priority will be changed and the queue adjusted.
|
||||
void enqueue(unsigned o, const T & priority) {
|
||||
if (o >= m_priorities.size()) {
|
||||
resize(o << 1); // make the size twice larger
|
||||
}
|
||||
if (m_heap_inverse[o] == -1)
|
||||
enqueue_new(o, priority);
|
||||
else
|
||||
change_priority_for_existing(o, priority);
|
||||
}
|
||||
|
||||
void change_priority_for_existing(unsigned o, const T & priority) {
|
||||
if (m_priorities[o] > priority) {
|
||||
decrease_priority(o, priority);
|
||||
} else {
|
||||
m_priorities[o] = priority;
|
||||
fix_heap_under(m_heap_inverse[o]);
|
||||
}
|
||||
}
|
||||
|
||||
T get_priority(unsigned o) const {
|
||||
lean_assert(m_priorities.size() > o);
|
||||
return m_priorities[o];
|
||||
}
|
||||
bool is_empty() const {
|
||||
return m_heap_size == 0;
|
||||
}
|
||||
void enqueue(unsigned o, const T & priority);
|
||||
void change_priority_for_existing(unsigned o, const T & priority);
|
||||
T get_priority(unsigned o) const { return m_priorities[o]; }
|
||||
bool is_empty() const { return m_heap_size == 0; }
|
||||
|
||||
/// return the first element of the queue and removes it from the queue
|
||||
unsigned dequeue_and_get_priority(T & priority) {
|
||||
lean_assert(m_heap_size != 0);
|
||||
int ret = m_heap[1];
|
||||
priority = m_priorities[ret];
|
||||
put_the_last_at_the_top_and_fix_the_heap();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fix_heap_under(unsigned i) {
|
||||
while (true) {
|
||||
int smallest = i;
|
||||
int l = i << 1;
|
||||
if (l <= m_heap_size && m_priorities[m_heap[l]] < m_priorities[m_heap[i]])
|
||||
smallest = l;
|
||||
int r = l + 1;
|
||||
if (r <= m_heap_size && m_priorities[m_heap[r]] < m_priorities[m_heap[smallest]])
|
||||
smallest = r;
|
||||
if (smallest != i)
|
||||
swap_with_parent(smallest);
|
||||
else
|
||||
break;
|
||||
i = smallest;
|
||||
}
|
||||
}
|
||||
|
||||
void put_the_last_at_the_top_and_fix_the_heap() {
|
||||
if (m_heap_size > 1) {
|
||||
put_at(1, m_heap[m_heap_size--]);
|
||||
fix_heap_under(1);
|
||||
} else {
|
||||
m_heap_size--;
|
||||
}
|
||||
}
|
||||
unsigned dequeue_and_get_priority(T & priority);
|
||||
void fix_heap_under(unsigned i);
|
||||
void put_the_last_at_the_top_and_fix_the_heap();
|
||||
/// return the first element of the queue and removes it from the queue
|
||||
unsigned dequeue() {
|
||||
lean_assert(m_heap_size);
|
||||
int ret = m_heap[1];
|
||||
put_the_last_at_the_top_and_fix_the_heap();
|
||||
m_heap_inverse[ret] = -1;
|
||||
return ret;
|
||||
}
|
||||
|
||||
void print() {
|
||||
std::vector<int> index;
|
||||
std::vector<T> prs;
|
||||
while (size()) {
|
||||
T prior;
|
||||
int j = dequeue_and_get_priority(prior);
|
||||
index.push_back(j);
|
||||
prs.push_back(prior);
|
||||
std::cout << "(" << j << ", " << prior << ")";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
// restore the queue
|
||||
for (int i = 0; i < index.size(); i++)
|
||||
enqueue(index[i], prs[i]);
|
||||
}
|
||||
unsigned dequeue();
|
||||
#ifdef LEAN_DEBUG
|
||||
void print();
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
|
|
|||
21
src/util/lp/binary_heap_priority_queue_instances.cpp
Normal file
21
src/util/lp/binary_heap_priority_queue_instances.cpp
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/numeric_pair.h"
|
||||
#include "util/lp/binary_heap_priority_queue.cpp"
|
||||
namespace lean {
|
||||
template binary_heap_priority_queue<int>::binary_heap_priority_queue(unsigned int);
|
||||
template unsigned binary_heap_priority_queue<int>::dequeue();
|
||||
template void binary_heap_priority_queue<int>::enqueue(unsigned int, int const&);
|
||||
template void binary_heap_priority_queue<int>::remove(unsigned int);
|
||||
template unsigned binary_heap_priority_queue<numeric_pair<mpq> >::dequeue();
|
||||
template void binary_heap_priority_queue<numeric_pair<mpq> >::enqueue(unsigned int, numeric_pair<mpq> const&);
|
||||
template void binary_heap_priority_queue<numeric_pair<mpq> >::resize(unsigned int);
|
||||
template binary_heap_priority_queue<unsigned int>::binary_heap_priority_queue(unsigned int);
|
||||
template unsigned binary_heap_priority_queue<unsigned int>::dequeue();
|
||||
template void binary_heap_priority_queue<unsigned int>::enqueue(unsigned int, unsigned int const&);
|
||||
template void binary_heap_priority_queue<unsigned int>::remove(unsigned int);
|
||||
}
|
||||
250
src/util/lp/binary_heap_upair_queue.cpp
Normal file
250
src/util/lp/binary_heap_upair_queue.cpp
Normal file
|
|
@ -0,0 +1,250 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
|
||||
#include "util/lp/binary_heap_upair_queue.h"
|
||||
namespace lean {
|
||||
template <typename T> binary_heap_upair_queue<T>::binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) {
|
||||
lean_assert(size);
|
||||
for (unsigned i = 0; i < size; i++)
|
||||
m_available_spots.push_back(i);
|
||||
}
|
||||
template <typename T> unsigned
|
||||
binary_heap_upair_queue<T>::dequeue_available_spot() {
|
||||
lean_assert(m_available_spots.empty() == false);
|
||||
unsigned ret = m_available_spots.back();
|
||||
m_available_spots.pop_back();
|
||||
return ret;
|
||||
}
|
||||
template <typename T> void binary_heap_upair_queue<T>::remove(unsigned i, unsigned j) {
|
||||
upair p(i, j);
|
||||
auto it = m_pairs_to_index.find(p);
|
||||
if (it == m_pairs_to_index.end())
|
||||
return; // nothing to do
|
||||
m_q.remove(it->second);
|
||||
m_available_spots.push_back(it->second);
|
||||
m_pairs_to_index.erase(it);
|
||||
}
|
||||
|
||||
|
||||
template <typename T> bool binary_heap_upair_queue<T>::ij_index_is_new(unsigned ij_index) const {
|
||||
for (auto it : m_pairs_to_index) {
|
||||
if (it.second == ij_index)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_upair_queue<T>::enqueue(unsigned i, unsigned j, const T & priority) {
|
||||
upair p(i, j);
|
||||
auto it = m_pairs_to_index.find(p);
|
||||
unsigned ij_index;
|
||||
if (it == m_pairs_to_index.end()) {
|
||||
// it is a new pair, let us find a spot for it
|
||||
if (m_available_spots.empty()) {
|
||||
// we ran out of empty spots
|
||||
unsigned size_was = m_pairs.size();
|
||||
unsigned new_size = size_was << 1;
|
||||
for (unsigned i = size_was; i < new_size; i++)
|
||||
m_available_spots.push_back(i);
|
||||
m_pairs.resize(new_size);
|
||||
}
|
||||
ij_index = dequeue_available_spot();
|
||||
// lean_assert(ij_index<m_pairs.size() && ij_index_is_new(ij_index));
|
||||
m_pairs[ij_index] = p;
|
||||
m_pairs_to_index[p] = ij_index;
|
||||
} else {
|
||||
ij_index = it->second;
|
||||
}
|
||||
m_q.enqueue(ij_index, priority);
|
||||
}
|
||||
|
||||
template <typename T> void binary_heap_upair_queue<T>::dequeue(unsigned & i, unsigned &j) {
|
||||
lean_assert(!m_q.is_empty());
|
||||
unsigned ij_index = m_q.dequeue();
|
||||
upair & p = m_pairs[ij_index];
|
||||
i = p.first;
|
||||
j = p.second;
|
||||
m_available_spots.push_back(ij_index);
|
||||
m_pairs_to_index.erase(p);
|
||||
}
|
||||
|
||||
|
||||
template <typename T> T binary_heap_upair_queue<T>::get_priority(unsigned i, unsigned j) const {
|
||||
auto it = m_pairs_to_index.find(std::make_pair(i, j));
|
||||
if (it == m_pairs_to_index.end())
|
||||
return T(0xFFFFFF); // big number
|
||||
return m_q.get_priority(it->second);
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T> bool binary_heap_upair_queue<T>::pair_to_index_is_a_bijection() const {
|
||||
std::set<int> tmp;
|
||||
for (auto p : m_pairs_to_index) {
|
||||
unsigned j = p.second;
|
||||
auto it = tmp.find(j);
|
||||
if (it != tmp.end()) {
|
||||
std::cout << "for pair (" << p.first.first << ", " << p.first.second << "), the index " << j
|
||||
<< " is already inside " << std::endl;
|
||||
lean_assert(false);
|
||||
} else {
|
||||
tmp.insert(j);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T> bool binary_heap_upair_queue<T>::available_spots_are_correct() const {
|
||||
std::set<int> tmp;
|
||||
for (auto p : m_available_spots){
|
||||
tmp.insert(p);
|
||||
}
|
||||
if (tmp.size() != m_available_spots.size())
|
||||
return false;
|
||||
for (auto it : m_pairs_to_index)
|
||||
if (tmp.find(it.second) != tmp.end())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
/*
|
||||
#include <unordered_set>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
typedef std::pair<unsigned, unsigned> upair;
|
||||
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
class binary_heap_upair_queue {
|
||||
binary_heap_priority_queue<T> m_q;
|
||||
std::unordered_map<upair, unsigned> m_pairs_to_index;
|
||||
std::vector<upair> m_pairs; // inverse to index
|
||||
std::vector<unsigned> m_available_spots;
|
||||
public:
|
||||
binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) {
|
||||
lean_assert(size);
|
||||
for (unsigned i = 0; i < size; i++)
|
||||
m_available_spots.push_back(i);
|
||||
}
|
||||
|
||||
unsigned dequeue_available_spot() {
|
||||
lean_assert(m_available_spots.empty() == false);
|
||||
unsigned ret = m_available_spots.back();
|
||||
m_available_spots.pop_back();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void remove(unsigned i, unsigned j) {
|
||||
upair p(i, j);
|
||||
auto it = m_pairs_to_index.find(p);
|
||||
if (it == m_pairs_to_index.end())
|
||||
return; // nothing to do
|
||||
m_q.remove(it->second);
|
||||
m_available_spots.push_back(it->second);
|
||||
m_pairs_to_index.erase(it);
|
||||
}
|
||||
|
||||
|
||||
bool ij_index_is_new(unsigned ij_index) const {
|
||||
for (auto it : m_pairs_to_index) {
|
||||
if (it.second == ij_index)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void enqueue(unsigned i, unsigned j, const T & priority) {
|
||||
upair p(i, j);
|
||||
auto it = m_pairs_to_index.find(p);
|
||||
unsigned ij_index;
|
||||
if (it == m_pairs_to_index.end()) {
|
||||
// it is a new pair, let us find a spot for it
|
||||
if (m_available_spots.empty()) {
|
||||
// we ran out of empty spots
|
||||
unsigned size_was = m_pairs.size();
|
||||
unsigned new_size = size_was << 1;
|
||||
for (unsigned i = size_was; i < new_size; i++)
|
||||
m_available_spots.push_back(i);
|
||||
m_pairs.resize(new_size);
|
||||
}
|
||||
ij_index = dequeue_available_spot();
|
||||
// lean_assert(ij_index<m_pairs.size() && ij_index_is_new(ij_index));
|
||||
m_pairs[ij_index] = p;
|
||||
m_pairs_to_index[p] = ij_index;
|
||||
} else {
|
||||
ij_index = it->second;
|
||||
}
|
||||
m_q.enqueue(ij_index, priority);
|
||||
}
|
||||
|
||||
void dequeue(unsigned & i, unsigned &j) {
|
||||
lean_assert(!m_q.is_empty());
|
||||
unsigned ij_index = m_q.dequeue();
|
||||
upair & p = m_pairs[ij_index];
|
||||
i = p.first;
|
||||
j = p.second;
|
||||
m_available_spots.push_back(ij_index);
|
||||
m_pairs_to_index.erase(p);
|
||||
}
|
||||
|
||||
bool is_empty() const {
|
||||
return m_q.is_empty();
|
||||
}
|
||||
|
||||
unsigned size() const {
|
||||
return m_q.size();
|
||||
}
|
||||
|
||||
bool contains(unsigned i, unsigned j) const {
|
||||
return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end();
|
||||
}
|
||||
|
||||
T get_priority(unsigned i, unsigned j) const {
|
||||
auto it = m_pairs_to_index.find(std::make_pair(i, j));
|
||||
if (it == m_pairs_to_index.end())
|
||||
return T(0xFFFFFF); // big number
|
||||
return m_q.get_priority(it->second);
|
||||
}
|
||||
|
||||
bool pair_to_index_is_a_bijection() const {
|
||||
std::set<int> tmp;
|
||||
for (auto p : m_pairs_to_index) {
|
||||
unsigned j = p.second;
|
||||
auto it = tmp.find(j);
|
||||
if (it != tmp.end()) {
|
||||
std::cout << "for pair (" << p.first.first << ", " << p.first.second << "), the index " << j
|
||||
<< " is already inside " << std::endl;
|
||||
lean_assert(false);
|
||||
} else {
|
||||
tmp.insert(j);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool available_spots_are_correct() const {
|
||||
std::set<int> tmp;
|
||||
for (auto p : m_available_spots){
|
||||
tmp.insert(p);
|
||||
}
|
||||
if (tmp.size() != m_available_spots.size())
|
||||
return false;
|
||||
for (auto it : m_pairs_to_index)
|
||||
if (tmp.find(it.second) != tmp.end())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool is_correct() const {
|
||||
return m_q.is_consistent() && pair_to_index_is_a_bijection() && available_spots_are_correct();
|
||||
}
|
||||
};
|
||||
}
|
||||
*/
|
||||
|
|
@ -6,12 +6,15 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#include "util/lp/binary_heap_priority_queue.h"
|
||||
#include <unordered_set>
|
||||
#include <unordered_map>
|
||||
#include <queue>
|
||||
#include <vector>
|
||||
#include <set>
|
||||
#include <utility>
|
||||
#include "util/lp/binary_heap_priority_queue.h"
|
||||
#include "util/lp/hash_helper.h"
|
||||
|
||||
typedef std::pair<unsigned, unsigned> upair;
|
||||
|
||||
namespace lean {
|
||||
|
|
@ -22,122 +25,27 @@ class binary_heap_upair_queue {
|
|||
std::vector<upair> m_pairs; // inverse to index
|
||||
std::vector<unsigned> m_available_spots;
|
||||
public:
|
||||
binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) {
|
||||
lean_assert(size);
|
||||
for (unsigned i = 0; i < size; i++)
|
||||
m_available_spots.push_back(i);
|
||||
}
|
||||
|
||||
unsigned dequeue_available_spot() {
|
||||
lean_assert(m_available_spots.empty() == false);
|
||||
unsigned ret = m_available_spots.back();
|
||||
m_available_spots.pop_back();
|
||||
return ret;
|
||||
}
|
||||
|
||||
void remove(unsigned i, unsigned j) {
|
||||
upair p(i, j);
|
||||
auto it = m_pairs_to_index.find(p);
|
||||
if (it == m_pairs_to_index.end())
|
||||
return; // nothing to do
|
||||
m_q.remove(it->second);
|
||||
m_available_spots.push_back(it->second);
|
||||
m_pairs_to_index.erase(it);
|
||||
}
|
||||
|
||||
|
||||
bool ij_index_is_new(unsigned ij_index) const {
|
||||
for (auto it : m_pairs_to_index) {
|
||||
if (it.second == ij_index)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void enqueue(unsigned i, unsigned j, const T & priority) {
|
||||
upair p(i, j);
|
||||
auto it = m_pairs_to_index.find(p);
|
||||
unsigned ij_index;
|
||||
if (it == m_pairs_to_index.end()) {
|
||||
// it is a new pair, let us find a spot for it
|
||||
if (m_available_spots.empty()) {
|
||||
// we ran out of empty spots
|
||||
unsigned size_was = m_pairs.size();
|
||||
unsigned new_size = size_was << 1;
|
||||
for (unsigned i = size_was; i < new_size; i++)
|
||||
m_available_spots.push_back(i);
|
||||
m_pairs.resize(new_size);
|
||||
}
|
||||
ij_index = dequeue_available_spot();
|
||||
// lean_assert(ij_index<m_pairs.size() && ij_index_is_new(ij_index));
|
||||
m_pairs[ij_index] = p;
|
||||
m_pairs_to_index[p] = ij_index;
|
||||
} else {
|
||||
ij_index = it->second;
|
||||
}
|
||||
m_q.enqueue(ij_index, priority);
|
||||
}
|
||||
|
||||
void dequeue(unsigned & i, unsigned &j) {
|
||||
lean_assert(!m_q.is_empty());
|
||||
unsigned ij_index = m_q.dequeue();
|
||||
upair & p = m_pairs[ij_index];
|
||||
i = p.first;
|
||||
j = p.second;
|
||||
m_available_spots.push_back(ij_index);
|
||||
m_pairs_to_index.erase(p);
|
||||
}
|
||||
|
||||
bool is_empty() const {
|
||||
return m_q.is_empty();
|
||||
}
|
||||
|
||||
unsigned size() const {
|
||||
return m_q.size();
|
||||
}
|
||||
|
||||
bool contains(unsigned i, unsigned j) const {
|
||||
return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end();
|
||||
}
|
||||
|
||||
T get_priority(unsigned i, unsigned j) const {
|
||||
auto it = m_pairs_to_index.find(std::make_pair(i, j));
|
||||
if (it == m_pairs_to_index.end())
|
||||
return T(0xFFFFFF); // big number
|
||||
return m_q.get_priority(it->second);
|
||||
}
|
||||
|
||||
bool pair_to_index_is_a_bijection() const {
|
||||
std::set<int> tmp;
|
||||
for (auto p : m_pairs_to_index) {
|
||||
unsigned j = p.second;
|
||||
auto it = tmp.find(j);
|
||||
if (it != tmp.end()) {
|
||||
std::cout << "for pair (" << p.first.first << ", " << p.first.second << "), the index " << j
|
||||
<< " is already inside " << std::endl;
|
||||
lean_assert(false);
|
||||
} else {
|
||||
tmp.insert(j);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool available_spots_are_correct() const {
|
||||
std::set<int> tmp;
|
||||
for (auto p : m_available_spots){
|
||||
tmp.insert(p);
|
||||
}
|
||||
if (tmp.size() != m_available_spots.size())
|
||||
return false;
|
||||
for (auto it : m_pairs_to_index)
|
||||
if (tmp.find(it.second) != tmp.end())
|
||||
return false;
|
||||
return true;
|
||||
binary_heap_upair_queue(unsigned size);
|
||||
|
||||
unsigned dequeue_available_spot();
|
||||
bool is_empty() const { return m_q.is_empty(); }
|
||||
|
||||
unsigned size() const {return m_q.size(); }
|
||||
|
||||
bool contains(unsigned i, unsigned j) const { return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end();
|
||||
}
|
||||
|
||||
void remove(unsigned i, unsigned j);
|
||||
bool ij_index_is_new(unsigned ij_index) const;
|
||||
void enqueue(unsigned i, unsigned j, const T & priority);
|
||||
void dequeue(unsigned & i, unsigned &j);
|
||||
T get_priority(unsigned i, unsigned j) const;
|
||||
#ifdef LEAN_DEBUG
|
||||
bool pair_to_index_is_a_bijection() const;
|
||||
bool available_spots_are_correct() const;
|
||||
bool is_correct() const {
|
||||
return m_q.is_consistent() && pair_to_index_is_a_bijection() && available_spots_are_correct();
|
||||
}
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
|
|
|||
19
src/util/lp/binary_heap_upair_queue_instances.cpp
Normal file
19
src/util/lp/binary_heap_upair_queue_instances.cpp
Normal file
|
|
@ -0,0 +1,19 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/binary_heap_upair_queue.cpp"
|
||||
namespace lean {
|
||||
template binary_heap_upair_queue<int>::binary_heap_upair_queue(unsigned int);
|
||||
template binary_heap_upair_queue<unsigned int>::binary_heap_upair_queue(unsigned int);
|
||||
template unsigned binary_heap_upair_queue<int>::dequeue_available_spot();
|
||||
template unsigned binary_heap_upair_queue<unsigned int>::dequeue_available_spot();
|
||||
template void binary_heap_upair_queue<int>::enqueue(unsigned int, unsigned int, int const&);
|
||||
template void binary_heap_upair_queue<int>::remove(unsigned int, unsigned int);
|
||||
template void binary_heap_upair_queue<unsigned int>::remove(unsigned int, unsigned int);
|
||||
template void binary_heap_upair_queue<int>::dequeue(unsigned int&, unsigned int&);
|
||||
template void binary_heap_upair_queue<unsigned int>::enqueue(unsigned int, unsigned int, unsigned int const&);
|
||||
template void binary_heap_upair_queue<unsigned int>::dequeue(unsigned int&, unsigned int&);
|
||||
}
|
||||
|
|
@ -9,7 +9,10 @@
|
|||
#include <vector>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
|
||||
#include <utility>
|
||||
#include "util/numerics/mpq.h"
|
||||
#include "util/lp/column_info.h"
|
||||
#include "util/lp/hash_helper.h"
|
||||
namespace lean {
|
||||
typedef unsigned var_index;
|
||||
typedef unsigned constraint_index;
|
||||
|
|
@ -18,7 +21,7 @@ enum lconstraint_kind {
|
|||
};
|
||||
|
||||
class lar_normalized_constraint; // forward definition
|
||||
bool compare(const pair<mpq, var_index> & a, const pair<mpq, var_index> & b) {
|
||||
inline bool compare(const std::pair<mpq, var_index> & a, const std::pair<mpq, var_index> & b) {
|
||||
return a.second < b.second;
|
||||
}
|
||||
|
||||
|
|
@ -26,12 +29,12 @@ class canonic_left_side {
|
|||
public:
|
||||
int m_row_index = -1;
|
||||
int m_column_index = -1; // this is the column of the left side variable in the matrix
|
||||
std::vector<pair<mpq, var_index>> m_coeffs;
|
||||
std::vector<std::pair<mpq, var_index>> m_coeffs;
|
||||
column_info<mpq> m_column_info;
|
||||
lar_normalized_constraint * m_low_bound_witness = nullptr;
|
||||
lar_normalized_constraint * m_upper_bound_witness = nullptr;
|
||||
|
||||
canonic_left_side(buffer<pair<mpq, var_index>> buffer) {
|
||||
canonic_left_side(buffer<std::pair<mpq, var_index>> buffer) {
|
||||
for (auto it : buffer) {
|
||||
if (numeric_traits<mpq>::is_zero(it.first)) continue;
|
||||
m_coeffs.push_back(it);
|
||||
|
|
@ -65,7 +68,7 @@ public:
|
|||
|
||||
std::size_t hash_of_ls() const {
|
||||
std::size_t ret = 0;
|
||||
std::hash<pair<mpq, var_index>> hash_fun;
|
||||
std::hash<std::pair<mpq, var_index>> hash_fun;
|
||||
for (auto v : m_coeffs) {
|
||||
ret |= (hash_fun(v) << 2);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -6,12 +6,11 @@
|
|||
*/
|
||||
|
||||
#pragma once
|
||||
#include "util/lp/lp_primal_core_solver.h"
|
||||
#include "util/lp/lp_solver.h"
|
||||
#include <vector>
|
||||
#include <unordered_map>
|
||||
#include <string>
|
||||
#include <algorithm>
|
||||
#include "util/lp/lp_settings.h"
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
class column_info {
|
||||
|
|
@ -27,16 +26,8 @@ class column_info {
|
|||
bool m_is_fixed = false;
|
||||
|
||||
public:
|
||||
column_type get_column_type() {
|
||||
if (m_is_fixed) {
|
||||
return column_type::fixed;
|
||||
}
|
||||
|
||||
if (m_low_bound_is_set) {
|
||||
return m_upper_bound_is_set? boxed: low_bound;
|
||||
}
|
||||
// we are flipping the bounds!
|
||||
return m_upper_bound_is_set? low_bound: free_column;
|
||||
column_type get_column_type() const {
|
||||
return m_is_fixed? fixed : (m_low_bound_is_set? (m_upper_bound_is_set? boxed : low_bound) : (m_upper_bound_is_set? upper_bound: free_column));
|
||||
}
|
||||
|
||||
column_type get_column_type_no_flipping() {
|
||||
|
|
|
|||
339
src/util/lp/core_solver_pretty_printer.cpp
Normal file
339
src/util/lp/core_solver_pretty_printer.cpp
Normal file
|
|
@ -0,0 +1,339 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_core_solver_base.h"
|
||||
#include "util/lp/core_solver_pretty_printer.h"
|
||||
namespace lean {
|
||||
|
||||
|
||||
template <typename T, typename X>
|
||||
core_solver_pretty_printer<T, X>::core_solver_pretty_printer(lp_core_solver_base<T, X > & core_solver): m_core_solver(core_solver),
|
||||
m_column_widths(core_solver.m_A.column_count(), 0),
|
||||
m_A(core_solver.m_A.row_count(), vector<string>(core_solver.m_A.column_count(), "")),
|
||||
m_signs(core_solver.m_A.row_count(), vector<string>(core_solver.m_A.column_count(), " ")),
|
||||
m_costs(ncols(), ""),
|
||||
m_cost_signs(ncols(), " "),
|
||||
m_rs(ncols(), zero_of_type<X>()) {
|
||||
m_w_buff = new T[m_core_solver.m_m];
|
||||
m_ed_buff = new T[m_core_solver.m_m];
|
||||
m_core_solver.save_state(m_w_buff, m_ed_buff);
|
||||
init_m_A_and_signs();
|
||||
init_costs();
|
||||
init_column_widths();
|
||||
init_rs_width();
|
||||
m_cost_title = "costs";
|
||||
m_basis_heading_title = "heading";
|
||||
m_x_title = "x*";
|
||||
m_title_width = std::max(std::max(m_cost_title.size(), std::max(m_basis_heading_title.size(), m_x_title.size())), m_approx_norm_title.size());
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: init_costs() {
|
||||
vector<T> local_y(m_core_solver.m_m);
|
||||
m_core_solver.solve_yB(local_y);
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
if (m_core_solver.m_basis_heading[i] < 0) {
|
||||
T t = m_core_solver.m_costs[i] - m_core_solver.m_A.dot_product_with_column(local_y, i);
|
||||
set_coeff(m_costs, m_cost_signs, i, t, m_core_solver.column_name(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> core_solver_pretty_printer<T, X>:: ~core_solver_pretty_printer() {
|
||||
m_core_solver.restore_state(m_w_buff, m_ed_buff);
|
||||
delete [] m_w_buff;
|
||||
delete [] m_ed_buff;
|
||||
}
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: init_rs_width() {
|
||||
m_rs_width = T_to_string(m_core_solver.get_cost()).size();
|
||||
for (unsigned i = 0; i < nrows(); i++) {
|
||||
auto wt = T_to_string(m_rs[i]).size();
|
||||
if (wt > m_rs_width) {
|
||||
m_rs_width = wt;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> T core_solver_pretty_printer<T, X>:: current_column_norm() {
|
||||
T ret = zero_of_type<T>();
|
||||
for (T & ed : m_core_solver.m_ed)
|
||||
ret += ed * ed;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: init_m_A_and_signs() {
|
||||
for (unsigned column = 0; column < ncols(); column++) {
|
||||
m_core_solver.solve_Bd(column); // puts the result into m_core_solver.m_ed
|
||||
string name = m_core_solver.column_name(column);
|
||||
for (unsigned row = 0; row < nrows(); row ++) {
|
||||
set_coeff(
|
||||
m_A[row],
|
||||
m_signs[row],
|
||||
column,
|
||||
m_core_solver.m_ed[row],
|
||||
name);
|
||||
m_rs[row] += m_core_solver.m_ed[row] * m_core_solver.m_x[column];
|
||||
}
|
||||
m_exact_column_norms.push_back(current_column_norm() + 1);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: init_column_widths() {
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
m_column_widths[i] = get_column_width(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: adjust_width_with_low_bound(unsigned column, unsigned & w) {
|
||||
if (!m_core_solver.low_bounds_are_set()) return;
|
||||
w = std::max(w, (unsigned)T_to_string(m_core_solver.low_bound_value(column)).size());
|
||||
}
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: adjust_width_with_upper_bound(unsigned column, unsigned & w) {
|
||||
w = std::max(w, (unsigned)T_to_string(m_core_solver.upper_bound_value(column)).size());
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: adjust_width_with_bounds(unsigned column, unsigned & w) {
|
||||
switch (m_core_solver.get_column_type(column)) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
adjust_width_with_low_bound(column, w);
|
||||
adjust_width_with_upper_bound(column, w);
|
||||
break;
|
||||
case low_bound:
|
||||
adjust_width_with_low_bound(column, w);
|
||||
break;
|
||||
case upper_bound:
|
||||
adjust_width_with_upper_bound(column, w);
|
||||
break;
|
||||
case free_column:
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> unsigned core_solver_pretty_printer<T, X>:: get_column_width(unsigned column) {
|
||||
unsigned w = std::max(m_costs[column].size(), T_to_string(m_core_solver.m_x[column]).size());
|
||||
adjust_width_with_bounds(column, w);
|
||||
adjust_width_with_basis_heading(column, w);
|
||||
for (unsigned i = 0; i < nrows(); i++) {
|
||||
unsigned cellw = m_A[i][column].size();
|
||||
if (cellw > w) {
|
||||
w = cellw;
|
||||
}
|
||||
}
|
||||
w = std::max(w, (unsigned)T_to_string(m_exact_column_norms[column]).size());
|
||||
w = std::max(w, (unsigned)T_to_string(m_core_solver.m_column_norms[column]).size());
|
||||
return w;
|
||||
}
|
||||
|
||||
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::regular_cell_string(unsigned row, unsigned column, std::string name) {
|
||||
T t = fabs(m_core_solver.m_ed[row]);
|
||||
if ( t == 1) return name;
|
||||
return T_to_string(t) + name;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: set_coeff(vector<string>& row, vector<string> & row_signs, unsigned col, const T & t, string name) {
|
||||
if (numeric_traits<T>::is_zero(t)) {
|
||||
return;
|
||||
}
|
||||
if (col > 0) {
|
||||
if (t > 0) {
|
||||
row_signs[col] = "+";
|
||||
row[col] = t != 1? T_to_string(t) + name : name;
|
||||
} else {
|
||||
row_signs[col] = "-";
|
||||
row[col] = t != -1? T_to_string(-t) + name: name;
|
||||
}
|
||||
} else { // col == 0
|
||||
if (t == -1) {
|
||||
row[col] = "-" + name;
|
||||
} else if (t == 1) {
|
||||
row[col] = name;
|
||||
} else {
|
||||
row[col] = T_to_string(t) + name;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_x() {
|
||||
if (ncols() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
int blanks = m_title_width + 1 - m_x_title.size();
|
||||
std::cout << m_x_title;
|
||||
print_blanks(blanks);
|
||||
|
||||
auto bh = m_core_solver.m_x;
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = T_to_string(bh[i]);
|
||||
int blanks = m_column_widths[i] - s.size();
|
||||
print_blanks(blanks);
|
||||
std::cout << s << " "; // the column interval
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_low_bound_string(unsigned j) {
|
||||
switch (m_core_solver.get_column_type(j)){
|
||||
case boxed:
|
||||
case low_bound:
|
||||
case fixed:
|
||||
if (m_core_solver.low_bounds_are_set())
|
||||
return T_to_string(m_core_solver.low_bound_value(j));
|
||||
else
|
||||
return std::string("0");
|
||||
break;
|
||||
default:
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_upp_bound_string(unsigned j) {
|
||||
switch (m_core_solver.get_column_type(j)){
|
||||
case boxed:
|
||||
case upper_bound:
|
||||
case fixed:
|
||||
return T_to_string(m_core_solver.upper_bound_value(j));
|
||||
break;
|
||||
default:
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_lows() {
|
||||
if (ncols() == 0) {
|
||||
return;
|
||||
}
|
||||
int blanks = m_title_width + 1 - m_low_bounds_title.size();
|
||||
std::cout << m_low_bounds_title;
|
||||
print_blanks(blanks);
|
||||
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = get_low_bound_string(i);
|
||||
int blanks = m_column_widths[i] - s.size();
|
||||
print_blanks(blanks);
|
||||
std::cout << s << " "; // the column interval
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_upps() {
|
||||
if (ncols() == 0) {
|
||||
return;
|
||||
}
|
||||
int blanks = m_title_width + 1 - m_upp_bounds_title.size();
|
||||
std::cout << m_upp_bounds_title;
|
||||
print_blanks(blanks);
|
||||
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = get_upp_bound_string(i);
|
||||
int blanks = m_column_widths[i] - s.size();
|
||||
print_blanks(blanks);
|
||||
std::cout << s << " "; // the column interval
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_exact_norms() {
|
||||
int blanks = m_title_width + 1 - m_exact_norm_title.size();
|
||||
std::cout << m_exact_norm_title;
|
||||
print_blanks(blanks);
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = get_exact_column_norm_string(i);
|
||||
int blanks = m_column_widths[i] - s.size();
|
||||
print_blanks(blanks);
|
||||
std::cout << s << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_approx_norms() {
|
||||
int blanks = m_title_width + 1 - m_approx_norm_title.size();
|
||||
std::cout << m_approx_norm_title;
|
||||
print_blanks(blanks);
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = T_to_string(m_core_solver.m_column_norms[i]);
|
||||
int blanks = m_column_widths[i] - s.size();
|
||||
print_blanks(blanks);
|
||||
std::cout << s << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print() {
|
||||
for (unsigned i = 0; i < nrows(); i++) {
|
||||
print_row(i);
|
||||
}
|
||||
print_bottom_line();
|
||||
print_cost();
|
||||
print_x();
|
||||
print_basis_heading();
|
||||
print_lows();
|
||||
print_upps();
|
||||
print_exact_norms();
|
||||
print_approx_norms();
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_basis_heading() {
|
||||
int blanks = m_title_width + 1 - m_basis_heading_title.size();
|
||||
std::cout << m_basis_heading_title;
|
||||
print_blanks(blanks);
|
||||
|
||||
if (ncols() == 0) {
|
||||
return;
|
||||
}
|
||||
auto bh = m_core_solver.m_basis_heading;
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = T_to_string(bh[i]);
|
||||
int blanks = m_column_widths[i] - s.size();
|
||||
print_blanks(blanks);
|
||||
std::cout << s << " "; // the column interval
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_cost() {
|
||||
int blanks = m_title_width + 1 - m_cost_title.size();
|
||||
std::cout << m_cost_title;
|
||||
print_blanks(blanks);
|
||||
print_given_rows(m_costs, m_cost_signs, m_core_solver.get_cost());
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_given_rows(vector<string> & row, vector<string> & signs, X rst) {
|
||||
for (unsigned col = 0; col < row.size(); col++) {
|
||||
unsigned width = m_column_widths[col];
|
||||
string s = row[col];
|
||||
int number_of_blanks = width - s.size();
|
||||
lean_assert(number_of_blanks >= 0);
|
||||
print_blanks(number_of_blanks);
|
||||
std::cout << s << ' ';
|
||||
if (col < row.size() - 1) {
|
||||
std::cout << signs[col + 1] << ' ';
|
||||
}
|
||||
}
|
||||
std::cout << '=';
|
||||
|
||||
string rs = T_to_string(rst);
|
||||
int nb = m_rs_width - rs.size();
|
||||
lean_assert(nb >= 0);
|
||||
print_blanks(nb + 1);
|
||||
std::cout << rs << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void core_solver_pretty_printer<T, X>:: print_row(unsigned i){
|
||||
print_blanks(m_title_width + 1);
|
||||
auto row = m_A[i];
|
||||
auto sign_row = m_signs[i];
|
||||
auto rs = m_rs[i];
|
||||
print_given_rows(row, sign_row, rs);
|
||||
}
|
||||
}
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
#include <string>
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
#include "util/lp/lp_settings.h"
|
||||
namespace lean {
|
||||
template <typename T, typename X> class lp_core_solver_base; // forward definition
|
||||
|
||||
|
|
@ -46,346 +47,70 @@ class core_solver_pretty_printer {
|
|||
vector<T> m_exact_column_norms;
|
||||
|
||||
public:
|
||||
core_solver_pretty_printer(lp_core_solver_base<T, X > & core_solver): m_core_solver(core_solver),
|
||||
m_column_widths(core_solver.m_A.column_count(), 0),
|
||||
m_A(core_solver.m_A.row_count(), vector<string>(core_solver.m_A.column_count(), "")),
|
||||
m_signs(core_solver.m_A.row_count(), vector<string>(core_solver.m_A.column_count(), " ")),
|
||||
m_costs(ncols(), ""),
|
||||
m_cost_signs(ncols(), " "),
|
||||
m_rs(ncols(), zero_of_type<X>()) {
|
||||
m_w_buff = new T[m_core_solver.m_m];
|
||||
m_ed_buff = new T[m_core_solver.m_m];
|
||||
m_core_solver.save_state(m_w_buff, m_ed_buff);
|
||||
init_m_A_and_signs();
|
||||
init_costs();
|
||||
init_column_widths();
|
||||
init_rs_width();
|
||||
m_cost_title = "costs";
|
||||
m_basis_heading_title = "heading";
|
||||
m_x_title = "x*";
|
||||
m_title_width = std::max(std::max(m_cost_title.size(), std::max(m_basis_heading_title.size(), m_x_title.size())), m_approx_norm_title.size());
|
||||
}
|
||||
core_solver_pretty_printer(lp_core_solver_base<T, X > & core_solver);
|
||||
|
||||
void init_costs() {
|
||||
vector<T> local_y(m_core_solver.m_m);
|
||||
m_core_solver.solve_yB(local_y);
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
if (m_core_solver.m_basis_heading[i] < 0) {
|
||||
T t = m_core_solver.m_costs[i] - m_core_solver.m_A.dot_product_with_column(local_y, i);
|
||||
set_coeff(m_costs, m_cost_signs, i, t, m_core_solver.column_name(i));
|
||||
}
|
||||
}
|
||||
}
|
||||
void init_costs();
|
||||
|
||||
~core_solver_pretty_printer() {
|
||||
m_core_solver.restore_state(m_w_buff, m_ed_buff);
|
||||
delete [] m_w_buff;
|
||||
delete [] m_ed_buff;
|
||||
}
|
||||
void init_rs_width() {
|
||||
m_rs_width = T_to_string(m_core_solver.get_cost()).size();
|
||||
for (unsigned i = 0; i < nrows(); i++) {
|
||||
auto wt = T_to_string(m_rs[i]).size();
|
||||
if (wt > m_rs_width) {
|
||||
m_rs_width = wt;
|
||||
}
|
||||
}
|
||||
}
|
||||
~core_solver_pretty_printer();
|
||||
void init_rs_width();
|
||||
|
||||
T current_column_norm() {
|
||||
T ret = zero_of_type<T>();
|
||||
for (T & ed : m_core_solver.m_ed)
|
||||
ret += ed * ed;
|
||||
return ret;
|
||||
}
|
||||
T current_column_norm();
|
||||
|
||||
void init_m_A_and_signs() {
|
||||
for (unsigned column = 0; column < ncols(); column++) {
|
||||
m_core_solver.solve_Bd(column); // puts the result into m_core_solver.m_ed
|
||||
string name = m_core_solver.column_name(column);
|
||||
for (unsigned row = 0; row < nrows(); row ++) {
|
||||
set_coeff(
|
||||
m_A[row],
|
||||
m_signs[row],
|
||||
column,
|
||||
m_core_solver.m_ed[row],
|
||||
name);
|
||||
m_rs[row] += m_core_solver.m_ed[row] * m_core_solver.m_x[column];
|
||||
}
|
||||
m_exact_column_norms.push_back(current_column_norm() + 1);
|
||||
}
|
||||
}
|
||||
void init_m_A_and_signs();
|
||||
|
||||
void init_column_widths() {
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
m_column_widths[i] = get_column_width(i);
|
||||
}
|
||||
}
|
||||
void init_column_widths();
|
||||
|
||||
void adjust_width_with_low_bound(unsigned column, unsigned & w) {
|
||||
if (!m_core_solver.low_bounds_are_set()) return;
|
||||
w = std::max(w, (unsigned)T_to_string(m_core_solver.low_bound_value(column)).size());
|
||||
}
|
||||
void adjust_width_with_upper_bound(unsigned column, unsigned & w) {
|
||||
w = std::max(w, (unsigned)T_to_string(m_core_solver.upper_bound_value(column)).size());
|
||||
}
|
||||
void adjust_width_with_low_bound(unsigned column, unsigned & w);
|
||||
void adjust_width_with_upper_bound(unsigned column, unsigned & w);
|
||||
|
||||
void adjust_width_with_bounds(unsigned column, unsigned & w) {
|
||||
switch (m_core_solver.get_column_type(column)) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
adjust_width_with_low_bound(column, w);
|
||||
adjust_width_with_upper_bound(column, w);
|
||||
break;
|
||||
case low_bound:
|
||||
adjust_width_with_low_bound(column, w);
|
||||
break;
|
||||
case upper_bound:
|
||||
adjust_width_with_upper_bound(column, w);
|
||||
break;
|
||||
case free_column:
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void adjust_width_with_bounds(unsigned column, unsigned & w);
|
||||
|
||||
void adjust_width_with_basis_heading(unsigned column, unsigned & w) {
|
||||
w = std::max(w, (unsigned)T_to_string(m_core_solver.m_basis_heading[column]).size());
|
||||
}
|
||||
|
||||
unsigned get_column_width(unsigned column) {
|
||||
unsigned w = std::max(m_costs[column].size(), T_to_string(m_core_solver.m_x[column]).size());
|
||||
adjust_width_with_bounds(column, w);
|
||||
adjust_width_with_basis_heading(column, w);
|
||||
for (unsigned i = 0; i < nrows(); i++) {
|
||||
unsigned cellw = m_A[i][column].size();
|
||||
if (cellw > w) {
|
||||
w = cellw;
|
||||
}
|
||||
}
|
||||
w = std::max(w, (unsigned)T_to_string(m_exact_column_norms[column]).size());
|
||||
w = std::max(w, (unsigned)T_to_string(m_core_solver.m_column_norms[column]).size());
|
||||
return w;
|
||||
}
|
||||
unsigned get_column_width(unsigned column);
|
||||
|
||||
unsigned regular_cell_width(unsigned row, unsigned column, std::string name) {
|
||||
return regular_cell_string(row, column, name).size();
|
||||
}
|
||||
|
||||
std::string regular_cell_string(unsigned row, unsigned column, std::string name) {
|
||||
T t = fabs(m_core_solver.m_ed[row]);
|
||||
if ( t == 1) return name;
|
||||
return T_to_string(t) + name;
|
||||
}
|
||||
std::string regular_cell_string(unsigned row, unsigned column, std::string name);
|
||||
|
||||
|
||||
void set_coeff(vector<string>& row, vector<string> & row_signs, unsigned col, const T & t, string name) {
|
||||
if (numeric_traits<T>::is_zero(t)) {
|
||||
return;
|
||||
}
|
||||
if (col > 0) {
|
||||
if (t > 0) {
|
||||
row_signs[col] = "+";
|
||||
row[col] = t != 1? T_to_string(t) + name : name;
|
||||
} else {
|
||||
row_signs[col] = "-";
|
||||
row[col] = t != -1? T_to_string(-t) + name: name;
|
||||
}
|
||||
} else { // col == 0
|
||||
if (t == -1) {
|
||||
row[col] = "-" + name;
|
||||
} else if (t == 1) {
|
||||
row[col] = name;
|
||||
} else {
|
||||
row[col] = T_to_string(t) + name;
|
||||
}
|
||||
}
|
||||
}
|
||||
void set_coeff(vector<string>& row, vector<string> & row_signs, unsigned col, const T & t, string name);
|
||||
|
||||
void print_x() {
|
||||
if (ncols() == 0) {
|
||||
return;
|
||||
}
|
||||
void print_x();
|
||||
|
||||
int blanks = m_title_width + 1 - m_x_title.size();
|
||||
std::cout << m_x_title;
|
||||
print_blanks(blanks);
|
||||
std::string get_low_bound_string(unsigned j);
|
||||
|
||||
auto bh = m_core_solver.m_x;
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = T_to_string(bh[i]);
|
||||
int blanks = m_column_widths[i] - s.size();
|
||||
print_blanks(blanks);
|
||||
std::cout << s << " "; // the column interval
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
std::string get_low_bound_string(unsigned j) {
|
||||
switch (m_core_solver.get_column_type(j)){
|
||||
case boxed:
|
||||
case low_bound:
|
||||
case fixed:
|
||||
if (m_core_solver.low_bounds_are_set())
|
||||
return T_to_string(m_core_solver.low_bound_value(j));
|
||||
else
|
||||
return std::string("0");
|
||||
break;
|
||||
default:
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
|
||||
std::string get_upp_bound_string(unsigned j) {
|
||||
switch (m_core_solver.get_column_type(j)){
|
||||
case boxed:
|
||||
case upper_bound:
|
||||
case fixed:
|
||||
return T_to_string(m_core_solver.upper_bound_value(j));
|
||||
break;
|
||||
default:
|
||||
return std::string();
|
||||
}
|
||||
}
|
||||
std::string get_upp_bound_string(unsigned j);
|
||||
|
||||
|
||||
void print_lows() {
|
||||
if (ncols() == 0) {
|
||||
return;
|
||||
}
|
||||
int blanks = m_title_width + 1 - m_low_bounds_title.size();
|
||||
std::cout << m_low_bounds_title;
|
||||
print_blanks(blanks);
|
||||
void print_lows();
|
||||
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = get_low_bound_string(i);
|
||||
int blanks = m_column_widths[i] - s.size();
|
||||
print_blanks(blanks);
|
||||
std::cout << s << " "; // the column interval
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void print_upps() {
|
||||
if (ncols() == 0) {
|
||||
return;
|
||||
}
|
||||
int blanks = m_title_width + 1 - m_upp_bounds_title.size();
|
||||
std::cout << m_upp_bounds_title;
|
||||
print_blanks(blanks);
|
||||
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = get_upp_bound_string(i);
|
||||
int blanks = m_column_widths[i] - s.size();
|
||||
print_blanks(blanks);
|
||||
std::cout << s << " "; // the column interval
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
void print_upps();
|
||||
|
||||
string get_exact_column_norm_string(unsigned col) {
|
||||
return T_to_string(m_exact_column_norms[col]);
|
||||
}
|
||||
|
||||
void print_exact_norms() {
|
||||
int blanks = m_title_width + 1 - m_exact_norm_title.size();
|
||||
std::cout << m_exact_norm_title;
|
||||
print_blanks(blanks);
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = get_exact_column_norm_string(i);
|
||||
int blanks = m_column_widths[i] - s.size();
|
||||
print_blanks(blanks);
|
||||
std::cout << s << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
void print_exact_norms();
|
||||
|
||||
void print_approx_norms() {
|
||||
int blanks = m_title_width + 1 - m_approx_norm_title.size();
|
||||
std::cout << m_approx_norm_title;
|
||||
print_blanks(blanks);
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = T_to_string(m_core_solver.m_column_norms[i]);
|
||||
int blanks = m_column_widths[i] - s.size();
|
||||
print_blanks(blanks);
|
||||
std::cout << s << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
void print_approx_norms();
|
||||
|
||||
void print() {
|
||||
for (unsigned i = 0; i < nrows(); i++) {
|
||||
print_row(i);
|
||||
}
|
||||
print_bottom_line();
|
||||
print_cost();
|
||||
print_x();
|
||||
print_basis_heading();
|
||||
print_lows();
|
||||
print_upps();
|
||||
print_exact_norms();
|
||||
print_approx_norms();
|
||||
std::cout << std::endl;
|
||||
}
|
||||
void print();
|
||||
|
||||
void print_basis_heading() {
|
||||
int blanks = m_title_width + 1 - m_basis_heading_title.size();
|
||||
std::cout << m_basis_heading_title;
|
||||
print_blanks(blanks);
|
||||
|
||||
if (ncols() == 0) {
|
||||
return;
|
||||
}
|
||||
auto bh = m_core_solver.m_basis_heading;
|
||||
for (unsigned i = 0; i < ncols(); i++) {
|
||||
string s = T_to_string(bh[i]);
|
||||
int blanks = m_column_widths[i] - s.size();
|
||||
print_blanks(blanks);
|
||||
std::cout << s << " "; // the column interval
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
void print_basis_heading();
|
||||
|
||||
void print_bottom_line() {
|
||||
std::cout << "----------------------" << std::endl;
|
||||
}
|
||||
|
||||
void print_cost() {
|
||||
int blanks = m_title_width + 1 - m_cost_title.size();
|
||||
std::cout << m_cost_title;
|
||||
print_blanks(blanks);
|
||||
print_given_rows(m_costs, m_cost_signs, m_core_solver.get_cost());
|
||||
}
|
||||
void print_cost();
|
||||
|
||||
void print_given_rows(vector<string> & row, vector<string> & signs, X rst) {
|
||||
for (unsigned col = 0; col < row.size(); col++) {
|
||||
unsigned width = m_column_widths[col];
|
||||
string s = row[col];
|
||||
int number_of_blanks = width - s.size();
|
||||
lean_assert(number_of_blanks >= 0);
|
||||
print_blanks(number_of_blanks);
|
||||
std::cout << s << ' ';
|
||||
if (col < row.size() - 1) {
|
||||
std::cout << signs[col + 1] << ' ';
|
||||
}
|
||||
}
|
||||
std::cout << '=';
|
||||
void print_given_rows(vector<string> & row, vector<string> & signs, X rst);
|
||||
|
||||
string rs = T_to_string(rst);
|
||||
int nb = m_rs_width - rs.size();
|
||||
lean_assert(nb >= 0);
|
||||
print_blanks(nb + 1);
|
||||
std::cout << rs << std::endl;
|
||||
}
|
||||
|
||||
void print_row(unsigned i){
|
||||
print_blanks(m_title_width + 1);
|
||||
auto row = m_A[i];
|
||||
auto sign_row = m_signs[i];
|
||||
auto rs = m_rs[i];
|
||||
print_given_rows(row, sign_row, rs);
|
||||
}
|
||||
void print_row(unsigned i);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
17
src/util/lp/core_solver_pretty_printer_instances.cpp
Normal file
17
src/util/lp/core_solver_pretty_printer_instances.cpp
Normal file
|
|
@ -0,0 +1,17 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/numeric_pair.h"
|
||||
#include "util/lp/core_solver_pretty_printer.cpp"
|
||||
template lean::core_solver_pretty_printer<double, double>::core_solver_pretty_printer(lean::lp_core_solver_base<double, double> &);
|
||||
template void lean::core_solver_pretty_printer<double, double>::print();
|
||||
template lean::core_solver_pretty_printer<double, double>::~core_solver_pretty_printer();
|
||||
template lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::core_solver_pretty_printer(lean::lp_core_solver_base<lean::mpq, lean::mpq> &);
|
||||
template void lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::print();
|
||||
template lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::~core_solver_pretty_printer();
|
||||
template lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::core_solver_pretty_printer(lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> > &);
|
||||
template lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::~core_solver_pretty_printer();
|
||||
template void lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::print();
|
||||
213
src/util/lp/dense_matrix.cpp
Normal file
213
src/util/lp/dense_matrix.cpp
Normal file
|
|
@ -0,0 +1,213 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#ifdef LEAN_DEBUG
|
||||
#include "util/lp/dense_matrix.h"
|
||||
namespace lean {
|
||||
template <typename T, typename X> dense_matrix<T,X>::dense_matrix(unsigned m, unsigned n) : m_m(m), m_n(n) {
|
||||
m_values = new T[m * n];
|
||||
for (unsigned i = 0; i < m * n; i ++)
|
||||
m_values[i] = numeric_traits<T>::zero();
|
||||
}
|
||||
|
||||
template <typename T, typename X> dense_matrix<T,X> dense_matrix<T,X>::operator*=(matrix<T, X> const & a){
|
||||
lean_assert(column_count() == a.row_count());
|
||||
dense_matrix c(row_count(), a.column_count());
|
||||
for (unsigned i = 0; i < row_count(); i++) {
|
||||
for (unsigned j = 0; j < a.column_count(); j++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (unsigned k = 0; k < a.column_count(); k++) {
|
||||
v += get_elem(i, k) * a(k, j);
|
||||
}
|
||||
c.set_elem(i, j, v);
|
||||
}
|
||||
}
|
||||
*this = c;
|
||||
return *this;
|
||||
}
|
||||
template <typename T, typename X> dense_matrix<T,X>&
|
||||
dense_matrix<T,X>::operator=(matrix<T, X> const & other){
|
||||
if ( this == & other)
|
||||
return *this;
|
||||
m_values = new T[m_m * m_n];
|
||||
for (unsigned i = 0; i < m_m; i ++)
|
||||
for (unsigned j = 0; j < m_n; j++)
|
||||
m_values[i * m_n + j] = other.get_elem(i, j);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename X> dense_matrix<T,X>&
|
||||
dense_matrix<T,X>::operator=(dense_matrix const & other){
|
||||
if ( this == & other)
|
||||
return *this;
|
||||
m_m = other.m_m;
|
||||
m_n = other.m_n;
|
||||
delete [] m_values;
|
||||
m_values = new T[m_m * m_n];
|
||||
for (unsigned i = 0; i < m_m; i ++)
|
||||
for (unsigned j = 0; j < m_n; j++)
|
||||
m_values[i * m_n + j] = other.get_elem(i, j);
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T, typename X> dense_matrix<T,X>::dense_matrix(dense_matrix<T, X> const & other) : m_m(other.row_count()), m_n(other.column_count()) {
|
||||
m_values = new T[m_m * m_n];
|
||||
for (unsigned i = 0; i < m_m; i ++)
|
||||
for (unsigned j = 0; j < m_n; j++)
|
||||
m_values[i * m_n + j] = other.get_elem(i, j);
|
||||
}
|
||||
|
||||
template <typename T, typename X> dense_matrix<T,X>::dense_matrix(matrix<T, X> const & other) :
|
||||
m_m(other.row_count()),
|
||||
m_n(other.column_count()) {
|
||||
m_values = new T[m_m * m_n];
|
||||
for (unsigned i = 0; i < m_m; i++)
|
||||
for (unsigned j = 0; j < m_n; j++)
|
||||
m_values[i * m_n + j] = other.get_elem(i, j);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>:: apply_from_right(T * w) {
|
||||
T * t = new T[m_m];
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (int j = 0; j < m_m; j++) {
|
||||
v += w[j]* get_elem(j, i);
|
||||
}
|
||||
t[i] = v;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_m; i++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
delete [] t;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>:: apply_from_right(std::vector <T> & w) {
|
||||
T * t = new T[m_m];
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (int j = 0; j < m_m; j++) {
|
||||
v += w[j]* get_elem(j, i);
|
||||
}
|
||||
t[i] = v;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_m; i++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
delete [] t;
|
||||
}
|
||||
template <typename T, typename X> T* dense_matrix<T, X>::
|
||||
apply_from_left_with_different_dims(std::vector<T> & w) {
|
||||
T * t = new T[m_m];
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (int j = 0; j < m_n; j++) {
|
||||
v += w[j]* get_elem(i, j);
|
||||
}
|
||||
t[i] = v;
|
||||
}
|
||||
|
||||
return t;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>:: apply_from_left(std::vector<T> & w) {
|
||||
T * t = new T[m_m];
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (int j = 0; j < m_m; j++) {
|
||||
v += w[j]* get_elem(i, j);
|
||||
}
|
||||
t[i] = v;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
delete [] t;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>:: apply_from_left(X * w, lp_settings & ) {
|
||||
T * t = new T[m_m];
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (int j = 0; j < m_m; j++) {
|
||||
v += w[j]* get_elem(i, j);
|
||||
}
|
||||
t[i] = v;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
delete [] t;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>:: apply_from_left_to_X(std::vector<X> & w, lp_settings & ) {
|
||||
std::vector<X> t(m_m);
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
X v = zero_of_type<X>();
|
||||
for (int j = 0; j < m_m; j++) {
|
||||
v += w[j]* get_elem(i, j);
|
||||
}
|
||||
t[i] = v;
|
||||
}
|
||||
|
||||
for (int i = 0; i < m_m; i ++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
}
|
||||
|
||||
// This method pivots row i to row i0 by muliplying row i by
|
||||
// alpha and adding it to row i0.
|
||||
template <typename T, typename X> void dense_matrix<T, X>:: pivot_row_to_row(unsigned i, T alpha, unsigned i0,
|
||||
double & pivot_epsilon) {
|
||||
thread_local T _0 = numeric_traits<T>::zero();
|
||||
for (unsigned j = 0; j < m_n; j++) {
|
||||
m_values[i0 * m_n + j] += m_values[i * m_n + j] * alpha;
|
||||
if (fabs(m_values[i0 + m_n + j]) < pivot_epsilon) {
|
||||
m_values[i0 + m_n + j] = _0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>:: swap_columns(unsigned a, unsigned b) {
|
||||
for (unsigned i = 0; i < m_m; i++) {
|
||||
T t = get_elem(i, a);
|
||||
set_elem(i, a, get_elem(i, b));
|
||||
set_elem(i, b, t);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>:: swap_rows(unsigned a, unsigned b) {
|
||||
for (unsigned i = 0; i < m_n; i++) {
|
||||
T t = get_elem(a, i);
|
||||
set_elem(a, i, get_elem(b, i));
|
||||
set_elem(b, i, t);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void dense_matrix<T, X>:: multiply_row_by_constant(unsigned row, T & t) {
|
||||
for (unsigned i = 0; i < m_n; i++) {
|
||||
set_elem(row, i, t * get_elem(row, i));
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b){
|
||||
dense_matrix<T, X> ret(a.row_count(), b.column_count());
|
||||
for (unsigned i = 0; i < ret.m_m; i++)
|
||||
for (unsigned j = 0; j< ret.m_n; j++) {
|
||||
T v = numeric_traits<T>::zero();
|
||||
for (unsigned k = 0; k < a.column_count(); k ++){
|
||||
v += (a.get_elem(i, k) * b.get_elem(k, j));
|
||||
}
|
||||
ret.set_elem(i, j, v);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
83
src/util/lp/dense_matrix.h
Normal file
83
src/util/lp/dense_matrix.h
Normal file
|
|
@ -0,0 +1,83 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#ifdef LEAN_DEBUG
|
||||
#include "util/numerics/double.h"
|
||||
#include "util/lp/matrix.h"
|
||||
namespace lean {
|
||||
// used for debugging purposes only
|
||||
template <typename T, typename X>
|
||||
class dense_matrix: public matrix<T, X> {
|
||||
public:
|
||||
struct ref {
|
||||
unsigned m_i;
|
||||
dense_matrix & m_s;
|
||||
ref(unsigned i, dense_matrix & s) :m_i(i * s.m_n), m_s(s){}
|
||||
T & operator[] (unsigned j) {
|
||||
return m_s.m_values[m_i + j];
|
||||
}
|
||||
const T & operator[] (unsigned j) const {
|
||||
return m_s.m_v[m_i + j];
|
||||
}
|
||||
};
|
||||
ref operator[] (unsigned i) {
|
||||
return ref(i, *this);
|
||||
}
|
||||
unsigned m_m; // number of rows
|
||||
unsigned m_n; // number of const
|
||||
T* m_values;//
|
||||
dense_matrix(unsigned m, unsigned n);
|
||||
|
||||
dense_matrix operator*=(matrix<T, X> const & a);
|
||||
|
||||
dense_matrix & operator=(matrix<T, X> const & other);
|
||||
|
||||
dense_matrix & operator=(dense_matrix const & other);
|
||||
|
||||
dense_matrix(dense_matrix<T, X> const & other);
|
||||
|
||||
dense_matrix(matrix<T, X> const & other);
|
||||
void apply_from_right(T * w);
|
||||
|
||||
void apply_from_right(std::vector <T> & w);
|
||||
|
||||
T * apply_from_left_with_different_dims(std::vector<T> & w);
|
||||
void apply_from_left(std::vector<T> & w , lp_settings & ) { apply_from_left(w); }
|
||||
|
||||
void apply_from_left(std::vector<T> & w);
|
||||
|
||||
void apply_from_left(X * w, lp_settings & );
|
||||
|
||||
void apply_from_left_to_X(std::vector<X> & w, lp_settings & );
|
||||
|
||||
virtual void set_number_of_rows(unsigned /*m*/) {}
|
||||
virtual void set_number_of_columns(unsigned /*n*/) { }
|
||||
|
||||
T get_elem(unsigned i, unsigned j) const { return m_values[i * m_n + j]; }
|
||||
|
||||
unsigned row_count() const { return m_m; }
|
||||
unsigned column_count() const { return m_n; }
|
||||
|
||||
void set_elem(unsigned i, unsigned j, const T& val) { m_values[i * m_n + j] = val; }
|
||||
|
||||
// This method pivots row i to row i0 by muliplying row i by
|
||||
// alpha and adding it to row i0.
|
||||
void pivot_row_to_row(unsigned i, T alpha, unsigned i0,
|
||||
double & pivot_epsilon);
|
||||
|
||||
void swap_columns(unsigned a, unsigned b);
|
||||
|
||||
void swap_rows(unsigned a, unsigned b);
|
||||
|
||||
void multiply_row_by_constant(unsigned row, T & t);
|
||||
|
||||
~dense_matrix() { delete [] m_values; }
|
||||
};
|
||||
template <typename T, typename X>
|
||||
dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b);
|
||||
}
|
||||
#endif
|
||||
14
src/util/lp/dense_matrix_instances.cpp
Normal file
14
src/util/lp/dense_matrix_instances.cpp
Normal file
|
|
@ -0,0 +1,14 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#ifdef LEAN_DEBUG
|
||||
#include "util/lp/dense_matrix.cpp"
|
||||
template lean::dense_matrix<double, double> lean::operator*<double, double>(lean::matrix<double, double>&, lean::matrix<double, double>&);
|
||||
template void lean::dense_matrix<double, double>::apply_from_left(std::vector<double, std::allocator<double> >&);
|
||||
template lean::dense_matrix<double, double>::dense_matrix(lean::matrix<double, double> const&);
|
||||
template lean::dense_matrix<double, double>::dense_matrix(unsigned int, unsigned int);
|
||||
template lean::dense_matrix<double, double>& lean::dense_matrix<double, double>::operator=(lean::dense_matrix<double, double> const&);
|
||||
#endif
|
||||
98
src/util/lp/eta_matrix.cpp
Normal file
98
src/util/lp/eta_matrix.cpp
Normal file
|
|
@ -0,0 +1,98 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "util/lp/eta_matrix.h"
|
||||
namespace lean {
|
||||
|
||||
// This is the sum of a unit matrix and a one-column matrix
|
||||
template <typename T, typename X>
|
||||
void eta_matrix<T, X>::apply_from_left(std::vector<X> & w, lp_settings & ) {
|
||||
auto w_at_column_index = w[m_column_index];
|
||||
w[m_column_index] /= m_diagonal_element;
|
||||
for (auto & it: m_column_vector.m_data) {
|
||||
w[it.first] += w_at_column_index * it.second;
|
||||
}
|
||||
}
|
||||
template <typename T, typename X>
|
||||
template <typename L>
|
||||
void eta_matrix<T, X>::
|
||||
apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
|
||||
const L w_at_column_index = w[m_column_index];
|
||||
if (is_zero(w_at_column_index)) return;
|
||||
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_column_index] /= m_diagonal_element)) {
|
||||
w[m_column_index] = zero_of_type<L>();
|
||||
w.erase_from_index(m_column_index);
|
||||
}
|
||||
|
||||
for (auto & it : m_column_vector.m_data) {
|
||||
unsigned i = it.first;
|
||||
if (is_zero(w[i])) {
|
||||
L v = w[i] = w_at_column_index * it.second;
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
|
||||
w[i] = zero_of_type<L>();
|
||||
continue;
|
||||
}
|
||||
w.m_index.push_back(i);
|
||||
} else {
|
||||
L v = w[i] += w_at_column_index * it.second;
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
|
||||
w[i] = zero_of_type<L>();
|
||||
w.erase_from_index(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void eta_matrix<T, X>::apply_from_right(std::vector<T> & w) {
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T, X> deb(*this);
|
||||
// auto clone_w = clone_vector<T>(w, get_number_of_rows());
|
||||
// deb.apply_from_right(clone_w);
|
||||
#endif
|
||||
T t = w[m_column_index] / m_diagonal_element;
|
||||
for (auto & it :m_column_vector.m_data) {
|
||||
t += w[it.first] * it.second;
|
||||
}
|
||||
w[m_column_index] = t;
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(clone_w, w, get_number_of_rows()));
|
||||
// delete clone_w;
|
||||
#endif
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X>
|
||||
T eta_matrix<T, X>::get_elem(unsigned i, unsigned j) const {
|
||||
if (j == m_column_index){
|
||||
if (i == j) {
|
||||
return 1 / m_diagonal_element;
|
||||
}
|
||||
return m_column_vector[i];
|
||||
}
|
||||
|
||||
return i == j ? numeric_traits<T>::one() : numeric_traits<T>::zero();
|
||||
}
|
||||
#endif
|
||||
template <typename T, typename X>
|
||||
void eta_matrix<T, X>::conjugate_by_permutation(permutation_matrix<T, X> & p) {
|
||||
// this = p * this * p(-1)
|
||||
#ifdef LEAN_DEBUG
|
||||
// auto rev = p.get_reverse();
|
||||
// auto deb = ((*this) * rev);
|
||||
// deb = p * deb;
|
||||
#endif
|
||||
m_column_index = p.get_rev(m_column_index);
|
||||
for (auto & pair : m_column_vector.m_data) {
|
||||
pair.first = p.get_rev(pair.first);
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(deb == *this);
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
|
@ -7,6 +7,8 @@
|
|||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "util/lp/tail_matrix.h"
|
||||
#include "util/lp/permutation_matrix.h"
|
||||
namespace lean {
|
||||
|
||||
// This is the sum of a unit matrix and a one-column matrix
|
||||
|
|
@ -17,9 +19,9 @@ class eta_matrix
|
|||
unsigned m_length;
|
||||
#endif
|
||||
unsigned m_column_index;
|
||||
public:
|
||||
sparse_vector<T> m_column_vector;
|
||||
T m_diagonal_element;
|
||||
public:
|
||||
#ifdef LEAN_DEBUG
|
||||
eta_matrix(unsigned column_index, unsigned length):
|
||||
#else
|
||||
|
|
@ -49,42 +51,10 @@ public:
|
|||
return m_diagonal_element;
|
||||
}
|
||||
|
||||
void apply_from_left(std::vector<X> & w, lp_settings & ) {
|
||||
auto w_at_column_index = w[m_column_index];
|
||||
w[m_column_index] /= m_diagonal_element;
|
||||
for (auto it = sparse_vector_iterator<T>(m_column_vector); !it.done(); it.move()) {
|
||||
w[it.index()] += w_at_column_index * it.value();
|
||||
}
|
||||
}
|
||||
void apply_from_left(std::vector<X> & w, lp_settings & );
|
||||
|
||||
template <typename L>
|
||||
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
|
||||
const L w_at_column_index = w[m_column_index];
|
||||
if (is_zero(w_at_column_index)) return;
|
||||
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_column_index] /= m_diagonal_element)) {
|
||||
w[m_column_index] = zero_of_type<L>();
|
||||
w.erase_from_index(m_column_index);
|
||||
}
|
||||
|
||||
for (auto it = sparse_vector_iterator<T>(m_column_vector); !it.done(); it.move()) {
|
||||
unsigned i = it.index();
|
||||
if (is_zero(w[i])) {
|
||||
L v = w[i] = w_at_column_index * it.value();
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
|
||||
w[i] = zero_of_type<L>();
|
||||
continue;
|
||||
}
|
||||
w.m_index.push_back(i);
|
||||
} else {
|
||||
L v = w[i] += w_at_column_index * it.value(); // todo indexed_vector
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
|
||||
w[i] = zero_of_type<L>();
|
||||
w.erase_from_index(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings);
|
||||
|
||||
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
|
||||
apply_from_left_local(w, settings);
|
||||
|
|
@ -96,60 +66,18 @@ public:
|
|||
m_column_vector.push_back(row_index, val);
|
||||
}
|
||||
|
||||
void apply_from_right(std::vector<T> & w) {
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T, X> deb(*this);
|
||||
// auto clone_w = clone_vector<T>(w, get_number_of_rows());
|
||||
// deb.apply_from_right(clone_w);
|
||||
#endif
|
||||
T t = w[m_column_index] / m_diagonal_element;
|
||||
for (auto it = sparse_vector_iterator<T>(m_column_vector); !it.done(); it.move()) {
|
||||
t += w[it.index()] * it.value();
|
||||
}
|
||||
w[m_column_index] = t;
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(clone_w, w, get_number_of_rows()));
|
||||
// delete clone_w;
|
||||
#endif
|
||||
}
|
||||
void apply_from_right(std::vector<T> & w);
|
||||
|
||||
T get_elem(unsigned i, unsigned j) const {
|
||||
if (j == m_column_index){
|
||||
if (i == j) {
|
||||
return 1 / m_diagonal_element;
|
||||
}
|
||||
return m_column_vector[i];
|
||||
}
|
||||
|
||||
return i == j ? numeric_traits<T>::one() : numeric_traits<T>::zero();
|
||||
}
|
||||
T get_elem(unsigned i, unsigned j) const;
|
||||
#ifdef LEAN_DEBUG
|
||||
unsigned row_count() const { return m_length; }
|
||||
unsigned column_count() const { return m_length; }
|
||||
void set_number_of_rows(unsigned /*m*/) { }
|
||||
void set_number_of_columns(unsigned /*n*/) { }
|
||||
#endif
|
||||
sparse_vector_iterator<T> get_sparse_vector_iterator() {
|
||||
return sparse_vector_iterator<T>(m_column_vector);
|
||||
}
|
||||
|
||||
void divide_by_diagonal_element() {
|
||||
m_column_vector.divide(m_diagonal_element);
|
||||
}
|
||||
void conjugate_by_permutation(permutation_matrix<T, X> & p) {
|
||||
// this = p * this * p(-1)
|
||||
#ifdef LEAN_DEBUG
|
||||
// auto rev = p.get_reverse();
|
||||
// auto deb = ((*this) * rev);
|
||||
// deb = p * deb;
|
||||
#endif
|
||||
m_column_index = p.get_rev(m_column_index);
|
||||
for (auto & pair : m_column_vector.m_data) {
|
||||
pair.first = p.get_rev(pair.first);
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(deb == *this);
|
||||
#endif
|
||||
}
|
||||
void conjugate_by_permutation(permutation_matrix<T, X> & p);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
25
src/util/lp/eta_matrix_instances.cpp
Normal file
25
src/util/lp/eta_matrix_instances.cpp
Normal file
|
|
@ -0,0 +1,25 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/numeric_pair.h"
|
||||
#include "util/lp/eta_matrix.cpp"
|
||||
#ifdef LEAN_DEBUG
|
||||
template double lean::eta_matrix<double, double>::get_elem(unsigned int, unsigned int) const;
|
||||
template lean::mpq lean::eta_matrix<lean::mpq, lean::mpq>::get_elem(unsigned int, unsigned int) const;
|
||||
template lean::mpq lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::get_elem(unsigned int, unsigned int) const;
|
||||
#endif
|
||||
template void lean::eta_matrix<double, double>::apply_from_left(std::vector<double, std::allocator<double> >&, lean::lp_settings&);
|
||||
template void lean::eta_matrix<double, double>::apply_from_right(std::vector<double, std::allocator<double> >&);
|
||||
template void lean::eta_matrix<double, double>::conjugate_by_permutation(lean::permutation_matrix<double, double>&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_left(std::vector<lean::mpq, std::allocator<lean::mpq> >&, lean::lp_settings&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_right(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::mpq>::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::mpq>&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left(std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, lean::lp_settings&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_right(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
|
||||
template void lean::eta_matrix<double, double>::apply_from_left_local<double>(lean::indexed_vector<double>&, lean::lp_settings&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_left_local<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&);
|
||||
template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left_local<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&);
|
||||
35
src/util/lp/hash_helper.h
Normal file
35
src/util/lp/hash_helper.h
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#pragma once
|
||||
#include <utility>
|
||||
#include <functional>
|
||||
#include "util/numerics/mpq.h"
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<lean::mpq> {
|
||||
inline size_t operator()(const lean::mpq & v) const {
|
||||
return v.hash();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
template <class T>
|
||||
inline void hash_combine(std::size_t & seed, const T & v) {
|
||||
std::hash<T> hasher;
|
||||
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<typename S, typename T> struct hash<pair<S, T>> {
|
||||
inline size_t operator()(const pair<S, T> & v) const {
|
||||
size_t seed = 0;
|
||||
hash_combine(seed, v.first);
|
||||
hash_combine(seed, v.second);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -46,6 +46,7 @@ public:
|
|||
m_value = val;
|
||||
}
|
||||
};
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename X>
|
||||
bool check_vector_for_small_values(indexed_vector<X> & w, lp_settings & settings) {
|
||||
for (unsigned i : w.m_index) {
|
||||
|
|
@ -55,4 +56,5 @@ bool check_vector_for_small_values(indexed_vector<X> & w, lp_settings & settings
|
|||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
94
src/util/lp/indexed_vector.cpp
Normal file
94
src/util/lp/indexed_vector.cpp
Normal file
|
|
@ -0,0 +1,94 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/indexed_vector.h"
|
||||
namespace lean {
|
||||
|
||||
template <typename T>
|
||||
void print_vector(const std::vector<T> & t) {
|
||||
for (unsigned i = 0; i < t.size(); i++)
|
||||
std::cout << t[i] << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void print_vector(const buffer<T> & t) {
|
||||
for (unsigned i = 0; i < t.size(); i++)
|
||||
std::cout << t[i] << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void print_sparse_vector(const std::vector<T> & t) {
|
||||
for (unsigned i = 0; i < t.size(); i++) {
|
||||
if (is_zero(t[i]))continue;
|
||||
std::cout << "[" << i << "] = " << t[i] << ", ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void print_vector(const std::vector<mpq> & t) {
|
||||
for (unsigned i = 0; i < t.size(); i++)
|
||||
std::cout << t[i].get_double() << std::setprecision(3) << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void indexed_vector<T>::resize(unsigned data_size) {
|
||||
m_index.clear();
|
||||
m_data.resize(data_size, numeric_traits<T>::zero());
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void indexed_vector<T>::set_value(T value, unsigned index) {
|
||||
m_data[index] = value;
|
||||
m_index.push_back(index);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void indexed_vector<T>::clear() {
|
||||
for (unsigned i : m_index)
|
||||
m_data[i] = numeric_traits<T>::zero();
|
||||
m_index.clear();
|
||||
}
|
||||
template <typename T>
|
||||
void indexed_vector<T>::clear_all() {
|
||||
unsigned i = m_data.size();
|
||||
while (i--) m_data[i] = numeric_traits<T>::zero();
|
||||
m_index.clear();
|
||||
}
|
||||
template <typename T>
|
||||
void indexed_vector<T>::erase_from_index(unsigned j) {
|
||||
auto it = std::find(m_index.begin(), m_index.end(), j);
|
||||
if (it != m_index.end()) m_index.erase(it);
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T>
|
||||
bool indexed_vector<T>::is_OK() const {
|
||||
int size = 0;
|
||||
for (unsigned i = 0; i < m_data.size(); i++) {
|
||||
if (!is_zero(m_data[i])) {
|
||||
if (std::find(m_index.begin(), m_index.end(), i) == m_index.end())
|
||||
return false;
|
||||
size++;
|
||||
}
|
||||
}
|
||||
return size == m_index.size();
|
||||
}
|
||||
template <typename T>
|
||||
void indexed_vector<T>::print() {
|
||||
std::cout << "m_index " << std::endl;
|
||||
for (unsigned i = 0; i < m_index.size(); i++) {
|
||||
std::cout << m_index[i] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
print_vector(m_data);
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
|
@ -20,42 +20,12 @@
|
|||
#include "util/lp/sparse_vector.h"
|
||||
#include <iomanip>
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
void print_vector(const T * t, unsigned l) {
|
||||
for (unsigned i = 0; i < l; i++)
|
||||
std::cout << t[i] << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void print_vector(const std::vector<T> & t) {
|
||||
for (unsigned i = 0; i < t.size(); i++)
|
||||
std::cout << t[i] << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void print_vector(const buffer<T> & t) {
|
||||
for (unsigned i = 0; i < t.size(); i++)
|
||||
std::cout << t[i] << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void print_sparse_vector(const std::vector<T> & t) {
|
||||
for (unsigned i = 0; i < t.size(); i++) {
|
||||
if (is_zero(t[i]))continue;
|
||||
std::cout << "[" << i << "] = " << t[i] << ", ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void print_vector(const std::vector<mpq> & t) {
|
||||
for (unsigned i = 0; i < t.size(); i++)
|
||||
std::cout << t[i].get_double() << std::setprecision(3) << " ";
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T> void print_vector(const std::vector<T> & t);
|
||||
template <typename T> void print_vector(const buffer<T> & t);
|
||||
template <typename T> void print_sparse_vector(const std::vector<T> & t);
|
||||
|
||||
void print_vector(const std::vector<mpq> & t);
|
||||
template <typename T>
|
||||
class indexed_vector {
|
||||
public:
|
||||
|
|
@ -66,11 +36,7 @@ public:
|
|||
m_data.resize(data_size, numeric_traits<T>::zero());
|
||||
}
|
||||
|
||||
void resize(unsigned data_size) {
|
||||
m_index.clear();
|
||||
m_data.resize(data_size, numeric_traits<T>::zero());
|
||||
}
|
||||
|
||||
void resize(unsigned data_size);
|
||||
unsigned data_size() const {
|
||||
return m_data.size();
|
||||
}
|
||||
|
|
@ -79,25 +45,9 @@ public:
|
|||
return m_index.size();
|
||||
}
|
||||
|
||||
void set_value(T value, unsigned index) {
|
||||
m_data[index] = value;
|
||||
m_index.push_back(index);
|
||||
}
|
||||
|
||||
void clear() {
|
||||
for (unsigned i : m_index) {
|
||||
m_data[i] = numeric_traits<T>::zero();
|
||||
}
|
||||
m_index.clear();
|
||||
}
|
||||
|
||||
void clear_all() {
|
||||
unsigned i = m_data.size();
|
||||
while (i--) m_data[i] = numeric_traits<T>::zero();
|
||||
|
||||
m_index.clear();
|
||||
}
|
||||
|
||||
void set_value(T value, unsigned index);
|
||||
void clear();
|
||||
void clear_all();
|
||||
const T& operator[] (unsigned i) const {
|
||||
return m_data[i];
|
||||
}
|
||||
|
|
@ -107,31 +57,10 @@ public:
|
|||
}
|
||||
|
||||
|
||||
void erase_from_index(unsigned j) {
|
||||
auto it = std::find(m_index.begin(), m_index.end(), j);
|
||||
if (it != m_index.end()) m_index.erase(it);
|
||||
}
|
||||
|
||||
void erase_from_index(unsigned j);
|
||||
#ifdef LEAN_DEBUG
|
||||
bool is_OK() const {
|
||||
int size = 0;
|
||||
for (unsigned i = 0; i < m_data.size(); i++) {
|
||||
if (!is_zero(m_data[i])) {
|
||||
if (std::find(m_index.begin(), m_index.end(), i) == m_index.end())
|
||||
return false;
|
||||
size++;
|
||||
}
|
||||
}
|
||||
return size == m_index.size();
|
||||
}
|
||||
void print() {
|
||||
std::cout << "m_index " << std::endl;
|
||||
for (unsigned i = 0; i < m_index.size(); i++) {
|
||||
std::cout << m_index[i] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
print_vector(m_data);
|
||||
}
|
||||
bool is_OK() const;
|
||||
void print();
|
||||
#endif
|
||||
};
|
||||
}
|
||||
|
|
|
|||
24
src/util/lp/indexed_vector_instances.cpp
Normal file
24
src/util/lp/indexed_vector_instances.cpp
Normal file
|
|
@ -0,0 +1,24 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/indexed_vector.cpp"
|
||||
namespace lean {
|
||||
template void indexed_vector<double>::clear();
|
||||
template void indexed_vector<double>::clear_all();
|
||||
template void indexed_vector<double>::erase_from_index(unsigned int);
|
||||
template void indexed_vector<double>::set_value(double, unsigned int);
|
||||
template void indexed_vector<float>::set_value(float, unsigned int);
|
||||
template void indexed_vector<mpq>::clear();
|
||||
template void indexed_vector<mpq>::clear_all();
|
||||
template void indexed_vector<mpq>::erase_from_index(unsigned int);
|
||||
template void indexed_vector<mpq>::resize(unsigned int);
|
||||
template void indexed_vector<mpq>::set_value(mpq, unsigned int);
|
||||
#ifdef LEAN_DEBUG
|
||||
template bool indexed_vector<double>::is_OK() const;
|
||||
template bool lean::indexed_vector<mpq>::is_OK() const;
|
||||
#endif
|
||||
}
|
||||
47
src/util/lp/lar_constraints.cpp
Normal file
47
src/util/lp/lar_constraints.cpp
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/lar_constraints.h"
|
||||
namespace lean {
|
||||
|
||||
lar_constraint::lar_constraint(const buffer<std::pair<mpq, var_index>> & left_side, lconstraint_kind kind, mpq right_side, constraint_index index) : lar_base_constraint(kind, right_side, index) {
|
||||
for (auto & it : left_side) {
|
||||
auto r = m_left_side.find(it.second);
|
||||
if (r == m_left_side.end()) {
|
||||
m_left_side[it.second] = it.first;
|
||||
} else {
|
||||
r->second += it.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
lar_constraint::lar_constraint(const lar_base_constraint & c): lar_base_constraint(c.m_kind, c.m_right_side, c.m_index) {
|
||||
for (auto t : c.get_left_side_coefficients())
|
||||
m_left_side.insert(std::make_pair(t.second, t.first));
|
||||
}
|
||||
|
||||
|
||||
buffer<std::pair<mpq, var_index>> lar_constraint::get_left_side_coefficients() const {
|
||||
buffer<std::pair<mpq, var_index>> ret;
|
||||
for (auto it : m_left_side) {
|
||||
ret.push_back(std::make_pair(it.second, it.first));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
buffer<std::pair<mpq, var_index>> lar_normalized_constraint::get_left_side_coefficients() const {
|
||||
buffer<std::pair<mpq, var_index>> ret;
|
||||
for (auto t : m_canonic_left_side->m_coeffs) ret.push_back(t);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -16,11 +16,11 @@
|
|||
#include <algorithm>
|
||||
#include "util/lp/canonic_left_side.h"
|
||||
namespace lean {
|
||||
lconstraint_kind flip_kind(lconstraint_kind t) {
|
||||
inline lconstraint_kind flip_kind(lconstraint_kind t) {
|
||||
return static_cast<lconstraint_kind>( - static_cast<int>(t));
|
||||
}
|
||||
|
||||
std::string lconstraint_kind_string(lconstraint_kind t) {
|
||||
inline std::string lconstraint_kind_string(lconstraint_kind t) {
|
||||
switch (t) {
|
||||
case LE: return std::string("<=");
|
||||
case LT: return std::string("<");
|
||||
|
|
@ -35,7 +35,7 @@ class lar_base_constraint {
|
|||
public:
|
||||
lconstraint_kind m_kind;
|
||||
mpq m_right_side;
|
||||
virtual buffer<pair<mpq, var_index>> get_left_side_coefficients() const = 0;
|
||||
virtual buffer<std::pair<mpq, var_index>> get_left_side_coefficients() const = 0;
|
||||
constraint_index m_index; // the index of constraint
|
||||
lar_base_constraint() {}
|
||||
lar_base_constraint(lconstraint_kind kind, mpq right_side, constraint_index index) :m_kind(kind), m_right_side(right_side), m_index(index) {}
|
||||
|
|
@ -48,33 +48,15 @@ class lar_constraint : public lar_base_constraint {
|
|||
public:
|
||||
std::unordered_map<var_index, mpq> m_left_side;
|
||||
lar_constraint() {}
|
||||
lar_constraint(const buffer<pair<mpq, var_index>> & left_side, lconstraint_kind kind, mpq right_side, constraint_index index) : lar_base_constraint(kind, right_side, index) {
|
||||
for (auto & it : left_side) {
|
||||
auto r = m_left_side.find(it.second);
|
||||
if (r == m_left_side.end()) {
|
||||
m_left_side[it.second] = it.first;
|
||||
} else {
|
||||
r->second += it.first;
|
||||
}
|
||||
}
|
||||
}
|
||||
lar_constraint(const buffer<std::pair<mpq, var_index>> & left_side, lconstraint_kind kind, mpq right_side, constraint_index index);
|
||||
|
||||
lar_constraint(const lar_base_constraint & c): lar_base_constraint(c.m_kind, c.m_right_side, c.m_index) {
|
||||
for (auto t : c.get_left_side_coefficients())
|
||||
m_left_side.insert(std::make_pair(t.second, t.first));
|
||||
}
|
||||
lar_constraint(const lar_base_constraint & c);
|
||||
|
||||
unsigned size() const {
|
||||
return m_left_side.size();
|
||||
}
|
||||
|
||||
buffer<pair<mpq, var_index>> get_left_side_coefficients() const {
|
||||
buffer<pair<mpq, var_index>> ret;
|
||||
for (auto it : m_left_side) {
|
||||
ret.push_back(std::make_pair(it.second, it.first));
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
buffer<std::pair<mpq, var_index>> get_left_side_coefficients() const;
|
||||
};
|
||||
|
||||
class lar_normalized_constraint : public lar_base_constraint {
|
||||
|
|
@ -91,12 +73,8 @@ public:
|
|||
|
||||
lar_normalized_constraint() {}
|
||||
|
||||
virtual buffer<pair<mpq, var_index>> get_left_side_coefficients() const {
|
||||
buffer<pair<mpq, var_index>> ret;
|
||||
for (auto t : m_canonic_left_side->m_coeffs) ret.push_back(t);
|
||||
return ret;
|
||||
}
|
||||
virtual unsigned size() const {
|
||||
buffer<std::pair<mpq, var_index>> get_left_side_coefficients() const;
|
||||
unsigned size() const {
|
||||
return m_canonic_left_side->size();
|
||||
}
|
||||
};
|
||||
|
|
|
|||
818
src/util/lp/lar_core_solver.cpp
Normal file
818
src/util/lp/lar_core_solver.cpp
Normal file
|
|
@ -0,0 +1,818 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lar_core_solver.h"
|
||||
namespace lean {
|
||||
template <typename T, typename X>
|
||||
lar_core_solver<T, X>::lar_core_solver(std::vector<X> & x, std::vector<column_type> & column_types,
|
||||
std::vector<X> & low_bounds, std::vector<X> & upper_bounds,
|
||||
std::vector<unsigned> & basis,
|
||||
static_matrix<T, X> & A,
|
||||
lp_settings & settings,
|
||||
std::unordered_map<unsigned, std::string> & column_names,
|
||||
std::vector<X> & right_side,
|
||||
std::vector<T> & costs) : // right_side and costs are redundant
|
||||
lp_core_solver_base<T, X>(A,
|
||||
right_side,
|
||||
basis,
|
||||
x,
|
||||
costs,
|
||||
settings,
|
||||
column_names,
|
||||
column_types,
|
||||
low_bounds,
|
||||
upper_bounds) {
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::init_costs() {
|
||||
lean_assert(this->m_x.size() >= this->m_n);
|
||||
lean_assert(this->m_column_type.size() >= this->m_n);
|
||||
X inf = m_infeasibility;
|
||||
m_infeasibility = zero_of_type<X>();
|
||||
for (unsigned j = this->m_n; j--;)
|
||||
init_cost_for_column(j);
|
||||
if (!(this->m_total_iterations ==0 || inf >= m_infeasibility)) {
|
||||
std::cout << "inf was " << T_to_string(inf) << " and now " << T_to_string(m_infeasibility) << std::endl;
|
||||
}
|
||||
lean_assert(this->m_total_iterations ==0 || inf >= m_infeasibility);
|
||||
if (inf == m_infeasibility)
|
||||
this->m_iters_with_no_cost_growing++;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::init_cost_for_column(unsigned j) {
|
||||
// If j is a breakpoint column, then we set the cost zero.
|
||||
// When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function
|
||||
const X & x = this->m_x[j];
|
||||
// set zero cost for each non-basis column
|
||||
if (this->m_basis_heading[j] < 0) {
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
return;
|
||||
}
|
||||
// j is a basis column
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
if (x > this->m_upper_bound_values[j]) {
|
||||
this->m_costs[j] = 1;
|
||||
m_infeasibility += x - this->m_upper_bound_values[j];
|
||||
} else if (x < this->m_low_bound_values[j]) {
|
||||
m_infeasibility += this->m_low_bound_values[j] - x;
|
||||
this->m_costs[j] = -1;
|
||||
} else {
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
break;
|
||||
case low_bound:
|
||||
if (x < this->m_low_bound_values[j]) {
|
||||
this->m_costs[j] = -1;
|
||||
m_infeasibility += this->m_low_bound_values[j] - x;
|
||||
} else {
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
break;
|
||||
case upper_bound:
|
||||
if (x > this->m_upper_bound_values[j]) {
|
||||
this->m_costs[j] = 1;
|
||||
m_infeasibility += x - this->m_upper_bound_values[j];
|
||||
} else {
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
break;
|
||||
case free_column:
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::init_local() {
|
||||
this->m_start_time = get_millisecond_count();
|
||||
this->m_m = this->m_A.row_count();
|
||||
this->m_n = this->m_A.column_count();
|
||||
this->m_pivot_row_of_B_1.resize(this->m_m);
|
||||
this->m_pivot_row.resize(this->m_n);
|
||||
this->m_b.resize(this->m_m);
|
||||
this->m_y.resize(this->m_m);
|
||||
this->m_w.resize(this->m_m);
|
||||
this->m_d.resize(this->m_n);
|
||||
this->m_ed.resize(this->m_m);
|
||||
this->m_costs.resize(this->m_n);
|
||||
this->m_breakpoint_indices_queue.resize(this->m_n);
|
||||
this->m_copy_of_xB.resize(this->m_n);
|
||||
}
|
||||
|
||||
// returns m_sign_of_alpha_r
|
||||
template <typename T, typename X> int lar_core_solver<T, X>::column_is_out_of_bounds(unsigned j) {
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
if (this->x_below_low_bound(j)) {
|
||||
return -1;
|
||||
}
|
||||
if (this->x_above_upper_bound(j)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
case low_bound:
|
||||
if (this->x_below_low_bound(j)) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
case upper_bound:
|
||||
if (this->x_above_upper_bound(j)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lar_core_solver<T, X>::can_enter_basis_mpq(unsigned j) {
|
||||
switch (this->m_column_type[j]) {
|
||||
case low_bound:
|
||||
lean_assert(this->x_is_at_low_bound(j));
|
||||
return this->m_d[j] < numeric_traits<T>::zero();
|
||||
case upper_bound:
|
||||
lean_assert(this->x_is_at_upper_bound(j));
|
||||
return this->m_d[j] > numeric_traits<T>::zero();
|
||||
case fixed:
|
||||
return false;
|
||||
case boxed:
|
||||
{
|
||||
bool low_bound = this->x_is_at_low_bound(j);
|
||||
lean_assert(low_bound || this->x_is_at_upper_bound(j));
|
||||
return (low_bound && this->m_d[j] < numeric_traits<T>::zero()) || ((!low_bound) && this->m_d[j] > numeric_traits<T>::zero());
|
||||
}
|
||||
case free_column:
|
||||
return !numeric_traits<T>::is_zero(this->m_d[j]);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::calculate_pivot_row(unsigned i) {
|
||||
this->calculate_pivot_row_of_B_1(i);
|
||||
this->calculate_pivot_row_when_pivot_row_of_B1_is_ready();
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> X lar_core_solver<T, X>::get_deb_inf_column(unsigned j) {
|
||||
const X & x = this->m_x[j];
|
||||
switch (this->m_column_type[j]) {
|
||||
case low_bound:
|
||||
if (x < this->m_low_bound_values[j])
|
||||
return this->m_low_bound_values[j] - x;
|
||||
return zero_of_type<X>();
|
||||
case upper_bound:
|
||||
if (x > this->m_upper_bound_values[j])
|
||||
return x - this->m_upper_bound_values[j];
|
||||
return zero_of_type<X>();
|
||||
case fixed:
|
||||
case boxed:
|
||||
{
|
||||
if (x < this->m_low_bound_values[j])
|
||||
return this->m_low_bound_values[j] - x;
|
||||
if (x > this->m_upper_bound_values[j])
|
||||
return x - this->m_upper_bound_values[j];
|
||||
return zero_of_type<X>();
|
||||
}
|
||||
case free_column:
|
||||
{
|
||||
return zero_of_type<X>();
|
||||
}
|
||||
default:
|
||||
lean_assert(false);
|
||||
return zero_of_type<X>();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> X lar_core_solver<T, X>::get_deb_inf() {
|
||||
X ret = zero_of_type<X>();
|
||||
for (unsigned j = 0; j < this->m_n; j++) {
|
||||
X d = get_deb_inf_column(j);
|
||||
// if (! numeric_traits<T>::is_zero(d)) {
|
||||
// std::cout << "column " << j << ", " << this->column_name(j) << " inf is " << d.get_double() << std::endl;
|
||||
// }
|
||||
ret += d;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lar_core_solver<T, X>::debug_profit_delta(unsigned j, const T & delta) {
|
||||
this->update_x(j, delta);
|
||||
bool ret = m_infeasibility > get_deb_inf();
|
||||
if (ret) {
|
||||
std::cout << "found profit for " << this->column_name(j) << " and delta = " << delta.get_double() << std::endl;
|
||||
std::cout << "improvement = " << (m_infeasibility - get_deb_inf()).get_double() << std::endl;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lar_core_solver<T, X>::debug_profit(unsigned j) {
|
||||
if (this->m_column_type[j] == fixed) return false;
|
||||
T delta = numeric_traits<T>::one() / 10000000;
|
||||
delta /= 10000000;
|
||||
return debug_profit_delta(j, -delta) || debug_profit_delta(j, delta);
|
||||
}
|
||||
|
||||
template <typename T, typename X> int lar_core_solver<T, X>::choose_column_entering_basis() {
|
||||
unsigned offset = my_random() % this->m_non_basic_columns.size();
|
||||
unsigned initial_offset_in_non_basis = offset;
|
||||
do {
|
||||
unsigned j = this->m_non_basic_columns[offset];
|
||||
if (can_enter_basis_mpq(j))
|
||||
return j;
|
||||
offset++;
|
||||
if (offset == this->m_non_basic_columns.size()) offset = 0;
|
||||
} while (offset != initial_offset_in_non_basis);
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::one_iteration() {
|
||||
this->m_total_iterations++;
|
||||
lean_assert(this->m_non_basic_columns.size() + this->m_basis.size() == this->m_basis_heading.size());
|
||||
if (is_zero(m_infeasibility)) {
|
||||
this->m_status = OPTIMAL;
|
||||
return;
|
||||
}
|
||||
int entering = choose_column_entering_basis();
|
||||
if (entering == -1) {
|
||||
std::cout << "cannot choose entering" << std::endl;
|
||||
decide_on_status_when_cannot_enter();
|
||||
} else {
|
||||
advance_on_entering(entering);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::decide_on_status_when_cannot_enter() {
|
||||
if (!is_zero(m_infeasibility))
|
||||
this->m_status = INFEASIBLE;
|
||||
else
|
||||
this->m_status = FEASIBLE;
|
||||
std::cout << "status is " << lp_status_to_string(this->m_status) << std::endl;
|
||||
}
|
||||
|
||||
// j is the basic column, x is the value at x[j]
|
||||
// d is the coefficient before m_entering in the row with j as the basis column
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value) {
|
||||
X diff = x - break_value;
|
||||
if (is_zero(diff)) {
|
||||
switch (break_type) {
|
||||
case low_break:
|
||||
if (!same_sign_with_entering_delta(d))
|
||||
return; // no breakpoint
|
||||
break;
|
||||
case upper_break:
|
||||
if (same_sign_with_entering_delta(d))
|
||||
return; // no breakpoint
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
add_breakpoint(j, zero_of_type<X>(), break_type);
|
||||
return;
|
||||
}
|
||||
auto delta_j = diff / d;
|
||||
if (same_sign_with_entering_delta(delta_j))
|
||||
add_breakpoint(j, delta_j, break_type);
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::add_breakpoint(unsigned j, X delta, breakpoint_type type) {
|
||||
m_breakpoints.push_back(breakpoint<X>(j, delta, type));
|
||||
m_breakpoint_indices_queue.enqueue(m_breakpoint_indices_queue.size(), abs(delta));
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::try_add_breakpoint_in_row(unsigned i) {
|
||||
lean_assert(i < this->m_m);
|
||||
const T & d = this->m_ed[i]; // the coefficient before m_entering in the i-th row
|
||||
if (d == 0) return; // the change of x[m_entering] will not change the corresponding basis x
|
||||
unsigned j = this->m_basis[i];
|
||||
const X & x = this->m_x[j];
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
try_add_breakpoint(j, x, d, fixed_break, this->m_low_bound_values[j]);
|
||||
break;
|
||||
case boxed:
|
||||
try_add_breakpoint(j, x, d, low_break, this->m_low_bound_values[j]);
|
||||
try_add_breakpoint(j, x, d, upper_break, this->m_upper_bound_values[j]);
|
||||
break;
|
||||
case low_bound:
|
||||
try_add_breakpoint(j, x, d, low_break, this->m_low_bound_values[j]);
|
||||
break;
|
||||
case upper_bound:
|
||||
try_add_breakpoint(j, x, d, upper_break, this->m_upper_bound_values[j]);
|
||||
break;
|
||||
case free_column:
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> std::string lar_core_solver<T, X>:: break_type_to_string(breakpoint_type type) {
|
||||
switch (type){
|
||||
case low_break: return "low_break";
|
||||
case upper_break: return "upper_break";
|
||||
case fixed_break: return "fixed_break";
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
return "type is not found";
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::print_breakpoint(const breakpoint<X> * b) {
|
||||
std::cout << "(" << this->column_name(b->m_j) << "," << break_type_to_string(b->m_type) << "," << T_to_string(b->m_delta) << ")" << std::endl;
|
||||
print_bound_info_and_x(b->m_j);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::print_bound_info_and_x(unsigned j) {
|
||||
std::cout << "type of " << this->column_name(j) << " is " << column_type_to_string(this->m_column_type[j]) << std::endl;
|
||||
std::cout << "x[" << this->column_name(j) << "] = " << this->m_x[j] << std::endl;
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
std::cout << "[" << this->m_low_bound_values[j] << "," << this->m_upper_bound_values[j] << "]" << std::endl;
|
||||
break;
|
||||
case low_bound:
|
||||
std::cout << "[" << this->m_low_bound_values[j] << ", inf" << std::endl;
|
||||
break;
|
||||
case upper_bound:
|
||||
std::cout << "inf ," << this->m_upper_bound_values[j] << "]" << std::endl;
|
||||
break;
|
||||
case free_column:
|
||||
std::cout << "inf, inf" << std::endl;
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::clear_breakpoints() {
|
||||
m_breakpoints.clear();
|
||||
m_breakpoint_indices_queue.clear();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::fill_breakpoints_array(unsigned entering) {
|
||||
clear_breakpoints();
|
||||
for (unsigned i = this->m_m; i--;)
|
||||
try_add_breakpoint_in_row(i);
|
||||
|
||||
if (this->m_column_type[entering] == boxed) {
|
||||
if (m_sign_of_entering_delta < 0)
|
||||
add_breakpoint(entering, - this->bound_span(entering), low_break);
|
||||
else
|
||||
add_breakpoint(entering, this->bound_span(entering), upper_break);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::advance_on_entering(unsigned entering) {
|
||||
this->solve_Bd(entering); // prepares the entering column to be like the one of the tableau
|
||||
m_sign_of_entering_delta = this->m_d[entering] < zero_of_type<T>() ? 1 : -1;
|
||||
|
||||
fill_breakpoints_array(entering);
|
||||
advance_on_sorted_breakpoints(entering);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::print_cost() {
|
||||
std::cout << "reduced costs " << std::endl;
|
||||
for (unsigned j = 0; j < this->m_n; j++) {
|
||||
if (numeric_traits<T>::is_zero(this->m_d[j])) continue;
|
||||
std::cout << T_to_string(this->m_d[j]) << this->column_name(j) << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta) {
|
||||
if (entering != leaving)
|
||||
this->update_basis_and_x(entering, leaving, delta);
|
||||
else
|
||||
this->update_x(entering, delta);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::advance_on_sorted_breakpoints(unsigned entering) {
|
||||
T slope_at_entering = this->m_d[entering];
|
||||
breakpoint<X> * last_bp = nullptr;
|
||||
while (m_breakpoint_indices_queue.is_empty() == false) {
|
||||
unsigned bi = m_breakpoint_indices_queue.dequeue();
|
||||
breakpoint<X> *b = &m_breakpoints[bi];
|
||||
change_slope_on_breakpoint(entering, b, slope_at_entering);
|
||||
last_bp = b;
|
||||
if (slope_at_entering * m_sign_of_entering_delta > 0) { // the slope started to increase infeasibility
|
||||
break;
|
||||
} else {
|
||||
if (numeric_traits<T>::is_zero(slope_at_entering) && my_random() % 2 == 0) {
|
||||
// it is not cost benefitial to advance the delta more, so just break to increas the randomness
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
update_basis_and_x_with_comparison(entering, last_bp->m_j, last_bp->m_delta);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::change_slope_on_breakpoint(unsigned entering, breakpoint<X> * b, T & slope_at_entering) {
|
||||
if (b->m_j == entering) {
|
||||
lean_assert(b->m_type != fixed_break && (!is_zero(b->m_delta)));
|
||||
slope_at_entering += m_sign_of_entering_delta;
|
||||
return;
|
||||
}
|
||||
|
||||
lean_assert(this->m_basis_heading[b->m_j] >= 0);
|
||||
unsigned i_row = this->m_basis_heading[b->m_j];
|
||||
const T & d = - this->m_ed[i_row];
|
||||
if (numeric_traits<T>::is_zero(d)) return;
|
||||
|
||||
T delta = m_sign_of_entering_delta * abs(d);
|
||||
switch (b->m_type) {
|
||||
case fixed_break:
|
||||
if (is_zero(b->m_delta)) {
|
||||
slope_at_entering += delta;
|
||||
} else {
|
||||
slope_at_entering += 2 * delta;
|
||||
}
|
||||
break;
|
||||
case low_break:
|
||||
case upper_break:
|
||||
slope_at_entering += delta;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lar_core_solver<T, X>::row_is_infeasible(unsigned row, int & inf_sign) {
|
||||
unsigned j = this->m_basis[row];
|
||||
inf_sign = get_infeasibility_sign(j);
|
||||
return inf_sign != 0;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lar_core_solver<T, X>::row_is_evidence(unsigned row, int & inf_sign) {
|
||||
if (!row_is_infeasible(row, inf_sign)) return false;
|
||||
calculate_pivot_row(row);
|
||||
int entering = choose_entering_column_for_row_inf_strategy(inf_sign);
|
||||
if (entering == -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lar_core_solver<T, X>::find_evidence_row() {
|
||||
for (unsigned i = this->m_m; --i;) {
|
||||
int inf_sign;
|
||||
if (row_is_evidence(i, inf_sign)) {
|
||||
fill_evidence(i, inf_sign);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> bool lar_core_solver<T, X>::done() {
|
||||
if (this->m_status == OPTIMAL) return true;
|
||||
if (this->m_status == INFEASIBLE) {
|
||||
if (this->m_settings.row_feasibility == false) {
|
||||
if (find_evidence_row()) {
|
||||
std::cout << "found evidence" << std::endl;
|
||||
} else {
|
||||
std::cout << "did not find evidence" << std::endl;
|
||||
std::cout << "started feasibility_loop at iteration " << this->m_total_iterations << std::endl;
|
||||
unsigned iters = this->m_total_iterations;
|
||||
this->m_status = FEASIBLE;
|
||||
row_feasibility_loop();
|
||||
std::cout << "made another " << this->m_total_iterations - iters << ", percentage is " << 100.0 * (this->m_total_iterations - iters) / this->m_total_iterations << std::endl;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) {
|
||||
std::cout << "m_iters_with_no_cost_growing = " << this->m_iters_with_no_cost_growing << std::endl;
|
||||
this->m_status = ITERATIONS_EXHAUSTED; return true;
|
||||
}
|
||||
if (this->m_total_iterations >= this->m_settings.max_total_number_of_iterations) {
|
||||
std::cout << "max_total_number_of_iterations " << this->m_total_iterations << " is reached " << std::endl;
|
||||
this->m_status = ITERATIONS_EXHAUSTED; return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::move_as_many_as_possible_fixed_columns_to_non_basis() {
|
||||
unsigned i = 0; // points to basis
|
||||
auto& bs = this->m_basis;
|
||||
unsigned j = 0; // points to m_column_types
|
||||
auto & ct = this->m_column_type;
|
||||
std::vector<int> heading(this->m_n, -1);
|
||||
for (int i = 0; i < bs.size(); i ++) heading[bs[j]] = i;
|
||||
lean_assert(this->m_basis.size() == this->m_m);
|
||||
while (i < this->m_m && j < ct.size()) {
|
||||
unsigned basis_j = bs[i];
|
||||
if (ct[basis_j] != fixed) {i++; continue;}
|
||||
do {
|
||||
if (heading[j] == -numeric_traits<T>::one() && ct[j] != fixed)
|
||||
break;
|
||||
j++;
|
||||
} while (j < ct.size());
|
||||
if (j == ct.size()) break;
|
||||
bs[i++] = j++;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lar_core_solver<T, X>::non_basis_columns_are_set_correctly() {
|
||||
for (unsigned j : this->m_non_basic_columns) {
|
||||
if (!this->x_is_at_bound(j))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::prefix() {
|
||||
init_local();
|
||||
this->init();
|
||||
this->init_basis_heading();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::feasibility_loop() {
|
||||
while (true) {
|
||||
init_costs();
|
||||
this->init_reduced_costs_for_one_iteration();
|
||||
if (this->print_statistics_with_cost_and_check_that_the_time_is_over(this->m_total_iterations, m_infeasibility)){
|
||||
this->m_status = lp_status::TIME_EXHAUSTED;
|
||||
return; // this->m_total_iterations;
|
||||
}
|
||||
one_iteration();
|
||||
if (done()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> unsigned lar_core_solver<T, X>::get_number_of_inf_rows() const {
|
||||
unsigned r = 0;
|
||||
for (unsigned k = this->m_m; --k;) {
|
||||
unsigned j = this->m_basis[k];
|
||||
if (get_infeasibility_sign(j)) r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::row_feasibility_loop() {
|
||||
while (true) {
|
||||
if (this->print_statistics_with_iterations_and_check_that_the_time_is_over(this->m_total_iterations++)){
|
||||
this->m_status = lp_status::TIME_EXHAUSTED;
|
||||
return; // this->m_total_iterations;
|
||||
}
|
||||
int inf_sign;
|
||||
int i = find_infeasible_row(inf_sign);
|
||||
if (i == -1) {
|
||||
this->m_status = OPTIMAL;
|
||||
break;
|
||||
}
|
||||
advance_on_infeasible_row(i, inf_sign);
|
||||
if (done())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> int lar_core_solver<T, X>::find_infeasible_row(int & inf_sign) {
|
||||
unsigned offset = my_random() % this->m_m;
|
||||
unsigned initial_offset_in_basis = offset;
|
||||
do {
|
||||
unsigned j = this->m_basis[offset];
|
||||
inf_sign = get_infeasibility_sign(j);
|
||||
if (inf_sign)
|
||||
return offset;
|
||||
if (++offset == this->m_m) offset = 0;
|
||||
} while (offset != initial_offset_in_basis);
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename T, typename X> int lar_core_solver<T, X>::get_infeasibility_sign(unsigned j) const {
|
||||
const auto & x = this->m_x[j];
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
if (x < this->m_low_bound_values[j]) return 1;
|
||||
if (x > this->m_upper_bound_values[j]) return -1;
|
||||
return 0;
|
||||
case low_bound:
|
||||
return x < this->m_low_bound_values[j] ? 1 : 0;
|
||||
case upper_bound:
|
||||
return x > this->m_upper_bound_values[j]? -1 :0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lar_core_solver<T, X>::improves_pivot_row_inf(unsigned j, int inf_sign) {
|
||||
lean_assert(this->m_basis_heading[j] < 0);
|
||||
// we have x[basis[i]] = sum (mj*x[j]), where mj = -m_pivot_row[j]
|
||||
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
return false;
|
||||
case boxed:
|
||||
{
|
||||
lean_assert(this->x_is_at_bound(j));
|
||||
int j_sign = - get_sign(this->m_pivot_row[j]) * inf_sign;
|
||||
if (this->x_is_at_low_bound(j))
|
||||
return j_sign > 0;
|
||||
return j_sign < 0;
|
||||
}
|
||||
case low_bound:
|
||||
{
|
||||
lean_assert(this->x_is_at_low_bound(j));
|
||||
int j_sign = - get_sign(this->m_pivot_row[j]) * inf_sign;
|
||||
return j_sign > 0;
|
||||
}
|
||||
case upper_bound:
|
||||
{
|
||||
lean_assert(this->x_is_at_upper_bound(j));
|
||||
int j_sign = - get_sign(this->m_pivot_row[j]) * inf_sign;
|
||||
return j_sign < 0;
|
||||
}
|
||||
break;
|
||||
case free_column: {
|
||||
return numeric_traits<T>::is_zero(this->m_pivot_row[j]) == false;
|
||||
}
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> int lar_core_solver<T, X>::choose_entering_column_for_row_inf_strategy(int inf_sign) {
|
||||
unsigned offset = my_random() % this->m_non_basic_columns.size();
|
||||
unsigned initial_offset_in_non_basis = offset;
|
||||
do {
|
||||
unsigned j = this->m_non_basic_columns[offset];
|
||||
if (improves_pivot_row_inf(j, inf_sign))
|
||||
return j;
|
||||
if (++offset == this->m_non_basic_columns.size()) offset = 0;
|
||||
} while (offset != initial_offset_in_non_basis);
|
||||
return -1;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::fill_evidence(unsigned row, int inf_sign) {
|
||||
m_infeasible_row_sign = inf_sign;
|
||||
m_infeasible_row.push_back(std::make_pair(numeric_traits<T>::one(), this->m_basis[row]));
|
||||
for (auto j : this->m_non_basic_columns) {
|
||||
T aj = this->m_pivot_row[j];
|
||||
if (!numeric_traits<T>::is_zero(aj)) {
|
||||
m_infeasible_row.push_back(std::make_pair(aj, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::update_delta_of_entering_and_leaving_candidates(X del, X & delta,
|
||||
std::vector<unsigned> & leaving_candidates,
|
||||
unsigned bj) {
|
||||
if (del < delta) {
|
||||
leaving_candidates.clear();
|
||||
leaving_candidates.push_back(bj);
|
||||
delta = del;
|
||||
} else if (del == delta) {
|
||||
leaving_candidates.push_back(bj);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::update_delta_of_entering(int delta_sign, unsigned row, X & delta,
|
||||
std::vector<unsigned> & leaving_candidates) {
|
||||
unsigned bj = this->m_basis[row]; // bj - the basis column for the row
|
||||
const T & ed = this->m_ed[row]; // this is the coefficent before x[entering] in the sum representing the basis column of this row taken with minus
|
||||
if (numeric_traits<T>::is_zero(ed)) return;
|
||||
const X & x = this->m_x[bj]; // the value of the basis column
|
||||
// adjusted sign
|
||||
int adj_sign = ed < zero_of_type<T>() ? delta_sign : - delta_sign;
|
||||
|
||||
switch (this->m_column_type[bj]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
if (adj_sign > 0 && x <= this->m_upper_bound_values[bj])
|
||||
update_delta_of_entering_and_leaving_candidates((this->m_upper_bound_values[bj] - x) / abs(ed), delta, leaving_candidates, bj);
|
||||
else if (adj_sign < 0 && x >= this->m_low_bound_values[bj])
|
||||
update_delta_of_entering_and_leaving_candidates((x - this->m_low_bound_values[bj]) / abs(ed), delta, leaving_candidates, bj);
|
||||
break;
|
||||
case low_bound:
|
||||
if (adj_sign < 0 && x >= this->m_low_bound_values[bj])
|
||||
update_delta_of_entering_and_leaving_candidates((x - this->m_low_bound_values[bj]) / abs(ed), delta, leaving_candidates, bj);
|
||||
break;
|
||||
case upper_bound:
|
||||
if (adj_sign > 0 && x <= this->m_upper_bound_values[bj])
|
||||
update_delta_of_entering_and_leaving_candidates((this->m_upper_bound_values[bj] - x) / abs(ed), delta, leaving_candidates, bj);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> X lar_core_solver<T, X>::find_initial_delta_and_its_sign(unsigned row, unsigned entering,
|
||||
int inf_sign, int & entering_delta_sign,
|
||||
std::vector<unsigned> & leaving_candidates) {
|
||||
lean_assert(inf_sign != 0);
|
||||
unsigned bj = this->m_basis[row]; // this is the infeasible basis column
|
||||
const X & x = this->m_x[bj];
|
||||
entering_delta_sign = - get_sign(this->m_pivot_row[entering]) * inf_sign;
|
||||
lean_assert(entering_delta_sign != 0);
|
||||
X delta = (inf_sign > 0? (this->m_low_bound_values[bj] - x) : (x - this->m_upper_bound_values[bj])) / abs(this->m_pivot_row[entering]);
|
||||
if (this->m_column_type[entering] == boxed) {
|
||||
X span = this->bound_span(entering);
|
||||
if (span < delta) {
|
||||
delta = span;
|
||||
leaving_candidates.push_back(entering);
|
||||
} else {
|
||||
leaving_candidates.push_back(bj);
|
||||
}
|
||||
} else {
|
||||
leaving_candidates.push_back(bj);
|
||||
}
|
||||
|
||||
return delta;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::advance_on_infeasible_row_and_entering(unsigned inf_row, unsigned entering, int inf_sign) {
|
||||
this->solve_Bd(entering); // puts the tableau column of entering into this->m_ed
|
||||
int entering_delta_sign;
|
||||
std::vector<unsigned> leaving_candidates;
|
||||
X delta = find_initial_delta_and_its_sign(inf_row, entering, inf_sign, entering_delta_sign, leaving_candidates);
|
||||
lean_assert(leaving_candidates.size());
|
||||
lean_assert(delta > zero_of_type<X>());
|
||||
unsigned row = my_random() % this->m_m;
|
||||
unsigned initial_row = row;
|
||||
do {
|
||||
if (row != inf_row)
|
||||
update_delta_of_entering(entering_delta_sign, row, delta, leaving_candidates);
|
||||
if (++row == this->m_m) row = 0;
|
||||
} while (row != initial_row);
|
||||
unsigned leaving = find_leaving_for_inf_row_strategy(leaving_candidates);
|
||||
update_basis_and_x_with_comparison(entering, leaving, delta * entering_delta_sign);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::advance_on_infeasible_row(unsigned i, int inf_sign) {
|
||||
calculate_pivot_row(i);
|
||||
int entering = choose_entering_column_for_row_inf_strategy(inf_sign);
|
||||
if (entering == -1) {
|
||||
fill_evidence(i, inf_sign);
|
||||
this->m_status = INFEASIBLE;
|
||||
return;
|
||||
}
|
||||
advance_on_infeasible_row_and_entering(i, entering, inf_sign);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::solve() {
|
||||
prefix();
|
||||
if (is_empty()) {
|
||||
this->m_status = OPTIMAL;
|
||||
return;
|
||||
}
|
||||
this->snap_xN_to_bounds(); // we start with non-basic variables at their bounds
|
||||
lean_assert(non_basis_columns_are_set_correctly());
|
||||
|
||||
if (this->m_settings.row_feasibility) {
|
||||
std::cout << "optimizing by rows " << std::endl;
|
||||
row_feasibility_loop();
|
||||
} else {
|
||||
std::cout << "optimizing total infeasibility" << std::endl;
|
||||
feasibility_loop();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lar_core_solver<T, X>::print_column_info(unsigned j) {
|
||||
std::cout << "type = " << column_type_to_string(this->m_column_type[j]) << std::endl;
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
std::cout << "(" << this->m_low_bound_values[j] << ", " << this->m_upper_bound_values[j] << ")" << std::endl;
|
||||
break;
|
||||
case low_bound:
|
||||
std::cout << this->m_low_bound_values[j] << std::endl;
|
||||
break;
|
||||
case upper_bound:
|
||||
std::cout << this->m_upper_bound_values[j] << std::endl;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -34,263 +34,44 @@ public:
|
|||
lp_settings & settings,
|
||||
std::unordered_map<unsigned, std::string> & column_names,
|
||||
std::vector<X> & right_side,
|
||||
std::vector<T> & costs) : // right_side and costs are redundant
|
||||
lp_core_solver_base<T, X>(A,
|
||||
right_side,
|
||||
basis,
|
||||
x,
|
||||
costs,
|
||||
settings,
|
||||
column_names,
|
||||
column_types,
|
||||
low_bounds,
|
||||
upper_bounds) {
|
||||
}
|
||||
std::vector<T> & costs);
|
||||
|
||||
int get_infeasible_row_sign() const {
|
||||
return m_infeasible_row_sign;
|
||||
}
|
||||
int get_infeasible_row_sign() const { return m_infeasible_row_sign; }
|
||||
|
||||
const std::vector<pair<mpq, unsigned>> & get_infeasibility_info(int & inf_sign) const {
|
||||
inf_sign = m_infeasible_row_sign;
|
||||
return m_infeasible_row;
|
||||
}
|
||||
|
||||
void init_costs() {
|
||||
lean_assert(this->m_x.size() >= this->m_n);
|
||||
lean_assert(this->m_column_type.size() >= this->m_n);
|
||||
X inf = m_infeasibility;
|
||||
m_infeasibility = zero_of_type<X>();
|
||||
for (unsigned j = this->m_n; j--;)
|
||||
init_cost_for_column(j);
|
||||
if (!(this->m_total_iterations ==0 || inf >= m_infeasibility)) {
|
||||
std::cout << "inf was " << T_to_string(inf) << " and now " << T_to_string(m_infeasibility) << std::endl;
|
||||
}
|
||||
lean_assert(this->m_total_iterations ==0 || inf >= m_infeasibility);
|
||||
if (inf == m_infeasibility)
|
||||
this->m_iters_with_no_cost_growing++;
|
||||
}
|
||||
void init_costs();
|
||||
|
||||
|
||||
void init_cost_for_column(unsigned j) {
|
||||
// If j is a breakpoint column, then we set the cost zero.
|
||||
// When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function
|
||||
const X & x = this->m_x[j];
|
||||
// set zero cost for each non-basis column
|
||||
if (this->m_basis_heading[j] < 0) {
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
return;
|
||||
}
|
||||
// j is a basis column
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
if (x > this->m_upper_bound_values[j]) {
|
||||
this->m_costs[j] = 1;
|
||||
m_infeasibility += x - this->m_upper_bound_values[j];
|
||||
} else if (x < this->m_low_bound_values[j]) {
|
||||
m_infeasibility += this->m_low_bound_values[j] - x;
|
||||
this->m_costs[j] = -1;
|
||||
} else {
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
break;
|
||||
case low_bound:
|
||||
if (x < this->m_low_bound_values[j]) {
|
||||
this->m_costs[j] = -1;
|
||||
m_infeasibility += this->m_low_bound_values[j] - x;
|
||||
} else {
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
break;
|
||||
case upper_bound:
|
||||
if (x > this->m_upper_bound_values[j]) {
|
||||
this->m_costs[j] = 1;
|
||||
m_infeasibility += x - this->m_upper_bound_values[j];
|
||||
} else {
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
break;
|
||||
case free_column:
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void init_local() {
|
||||
this->m_start_time = get_millisecond_count();
|
||||
this->m_m = this->m_A.row_count();
|
||||
this->m_n = this->m_A.column_count();
|
||||
this->m_pivot_row_of_B_1.resize(this->m_m);
|
||||
this->m_pivot_row.resize(this->m_n);
|
||||
this->m_b.resize(this->m_m);
|
||||
this->m_y.resize(this->m_m);
|
||||
this->m_w.resize(this->m_m);
|
||||
this->m_d.resize(this->m_n);
|
||||
this->m_ed.resize(this->m_m);
|
||||
this->m_costs.resize(this->m_n);
|
||||
this->m_breakpoint_indices_queue.resize(this->m_n);
|
||||
this->m_copy_of_xB.resize(this->m_n);
|
||||
}
|
||||
void init_cost_for_column(unsigned j);
|
||||
|
||||
void init_local();
|
||||
|
||||
// returns m_sign_of_alpha_r
|
||||
int column_is_out_of_bounds(unsigned j) {
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
if (this->x_below_low_bound(j)) {
|
||||
return -1;
|
||||
}
|
||||
if (this->x_above_upper_bound(j)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
case low_bound:
|
||||
if (this->x_below_low_bound(j)) {
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
case upper_bound:
|
||||
if (this->x_above_upper_bound(j)) {
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
int column_is_out_of_bounds(unsigned j);
|
||||
|
||||
bool can_enter_basis_mpq(unsigned j) {
|
||||
switch (this->m_column_type[j]) {
|
||||
case low_bound:
|
||||
lean_assert(this->x_is_at_low_bound(j));
|
||||
return this->m_d[j] < numeric_traits<T>::zero();
|
||||
case upper_bound:
|
||||
lean_assert(this->x_is_at_upper_bound(j));
|
||||
return this->m_d[j] > numeric_traits<T>::zero();
|
||||
case fixed:
|
||||
return false;
|
||||
case boxed:
|
||||
{
|
||||
bool low_bound = this->x_is_at_low_bound(j);
|
||||
lean_assert(low_bound || this->x_is_at_upper_bound(j));
|
||||
return (low_bound && this->m_d[j] < numeric_traits<T>::zero()) || ((!low_bound) && this->m_d[j] > numeric_traits<T>::zero());
|
||||
}
|
||||
case free_column:
|
||||
return !numeric_traits<T>::is_zero(this->m_d[j]);
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool can_enter_basis_mpq(unsigned j);
|
||||
|
||||
|
||||
void calculate_pivot_row(unsigned i) {
|
||||
this->calculate_pivot_row_of_B_1(i);
|
||||
this->calculate_pivot_row_when_pivot_row_of_B1_is_ready();
|
||||
}
|
||||
void calculate_pivot_row(unsigned i);
|
||||
|
||||
|
||||
X get_deb_inf_column(unsigned j) {
|
||||
const X & x = this->m_x[j];
|
||||
switch (this->m_column_type[j]) {
|
||||
case low_bound:
|
||||
if (x < this->m_low_bound_values[j])
|
||||
return this->m_low_bound_values[j] - x;
|
||||
return zero_of_type<X>();
|
||||
case upper_bound:
|
||||
if (x > this->m_upper_bound_values[j])
|
||||
return x - this->m_upper_bound_values[j];
|
||||
return zero_of_type<X>();
|
||||
case fixed:
|
||||
case boxed:
|
||||
{
|
||||
if (x < this->m_low_bound_values[j])
|
||||
return this->m_low_bound_values[j] - x;
|
||||
if (x > this->m_upper_bound_values[j])
|
||||
return x - this->m_upper_bound_values[j];
|
||||
return zero_of_type<X>();
|
||||
}
|
||||
case free_column:
|
||||
{
|
||||
return zero_of_type<X>();
|
||||
}
|
||||
default:
|
||||
lean_assert(false);
|
||||
return zero_of_type<X>();
|
||||
}
|
||||
}
|
||||
X get_deb_inf_column(unsigned j);
|
||||
|
||||
X get_deb_inf() {
|
||||
X ret = zero_of_type<X>();
|
||||
for (unsigned j = 0; j < this->m_n; j++) {
|
||||
X d = get_deb_inf_column(j);
|
||||
// if (! numeric_traits<T>::is_zero(d)) {
|
||||
// std::cout << "column " << j << ", " << this->column_name(j) << " inf is " << d.get_double() << std::endl;
|
||||
// }
|
||||
ret += d;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
X get_deb_inf();
|
||||
|
||||
bool debug_profit_delta(unsigned j, const T & delta) {
|
||||
this->update_x(j, delta);
|
||||
bool ret = m_infeasibility > get_deb_inf();
|
||||
if (ret) {
|
||||
std::cout << "found profit for " << this->column_name(j) << " and delta = " << delta.get_double() << std::endl;
|
||||
std::cout << "improvement = " << (m_infeasibility - get_deb_inf()).get_double() << std::endl;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
bool debug_profit_delta(unsigned j, const T & delta);
|
||||
|
||||
bool debug_profit(unsigned j) {
|
||||
if (this->m_column_type[j] == fixed) return false;
|
||||
T delta = numeric_traits<T>::one() / 10000000;
|
||||
delta /= 10000000;
|
||||
return debug_profit_delta(j, -delta) || debug_profit_delta(j, delta);
|
||||
}
|
||||
bool debug_profit(unsigned j);
|
||||
|
||||
int choose_column_entering_basis() {
|
||||
unsigned offset = lrand48() % this->m_non_basic_columns.size();
|
||||
unsigned initial_offset_in_non_basis = offset;
|
||||
do {
|
||||
unsigned j = this->m_non_basic_columns[offset];
|
||||
if (can_enter_basis_mpq(j))
|
||||
return j;
|
||||
offset++;
|
||||
if (offset == this->m_non_basic_columns.size()) offset = 0;
|
||||
} while (offset != initial_offset_in_non_basis);
|
||||
return -1;
|
||||
}
|
||||
int choose_column_entering_basis();
|
||||
|
||||
void one_iteration() {
|
||||
this->m_total_iterations++;
|
||||
lean_assert(this->m_non_basic_columns.size() + this->m_basis.size() == this->m_basis_heading.size());
|
||||
if (is_zero(m_infeasibility)) {
|
||||
this->m_status = OPTIMAL;
|
||||
return;
|
||||
}
|
||||
int entering = choose_column_entering_basis();
|
||||
if (entering == -1) {
|
||||
std::cout << "cannot choose entering" << std::endl;
|
||||
decide_on_status_when_cannot_enter();
|
||||
} else {
|
||||
advance_on_entering(entering);
|
||||
}
|
||||
}
|
||||
void one_iteration();
|
||||
|
||||
|
||||
void decide_on_status_when_cannot_enter() {
|
||||
if (!is_zero(m_infeasibility))
|
||||
this->m_status = INFEASIBLE;
|
||||
else
|
||||
this->m_status = FEASIBLE;
|
||||
std::cout << "status is " << lp_status_to_string(this->m_status) << std::endl;
|
||||
}
|
||||
void decide_on_status_when_cannot_enter();
|
||||
template <typename L>
|
||||
bool same_sign_with_entering_delta(const L & a) {
|
||||
return (a > zero_of_type<L>() && m_sign_of_entering_delta > 0) || (a < zero_of_type<L>() && m_sign_of_entering_delta < 0);
|
||||
|
|
@ -298,366 +79,60 @@ public:
|
|||
|
||||
// j is the basic column, x is the value at x[j]
|
||||
// d is the coefficient before m_entering in the row with j as the basis column
|
||||
void try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value) {
|
||||
X diff = x - break_value;
|
||||
if (is_zero(diff)) {
|
||||
switch (break_type) {
|
||||
case low_break:
|
||||
if (!same_sign_with_entering_delta(d))
|
||||
return; // no breakpoint
|
||||
break;
|
||||
case upper_break:
|
||||
if (same_sign_with_entering_delta(d))
|
||||
return; // no breakpoint
|
||||
break;
|
||||
default: break;
|
||||
}
|
||||
add_breakpoint(j, zero_of_type<X>(), break_type);
|
||||
return;
|
||||
}
|
||||
auto delta_j = diff / d;
|
||||
if (same_sign_with_entering_delta(delta_j))
|
||||
add_breakpoint(j, delta_j, break_type);
|
||||
}
|
||||
void try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value);
|
||||
void add_breakpoint(unsigned j, X delta, breakpoint_type type);
|
||||
|
||||
void try_add_breakpoint_in_row(unsigned i);
|
||||
|
||||
std::string break_type_to_string(breakpoint_type type);
|
||||
|
||||
void print_breakpoint(const breakpoint<X> * b);
|
||||
|
||||
void print_bound_info_and_x(unsigned j);
|
||||
|
||||
void clear_breakpoints();
|
||||
|
||||
void fill_breakpoints_array(unsigned entering);
|
||||
|
||||
void advance_on_entering(unsigned entering);
|
||||
|
||||
void print_cost();
|
||||
|
||||
void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta);
|
||||
|
||||
void advance_on_sorted_breakpoints(unsigned entering);
|
||||
|
||||
void change_slope_on_breakpoint(unsigned entering, breakpoint<X> * b, T & slope_at_entering);
|
||||
|
||||
bool row_is_infeasible(unsigned row, int & inf_sign);
|
||||
|
||||
bool row_is_evidence(unsigned row, int & inf_sign);
|
||||
|
||||
bool find_evidence_row();
|
||||
|
||||
|
||||
void add_breakpoint(unsigned j, X delta, breakpoint_type type) {
|
||||
m_breakpoints.push_back(breakpoint<X>(j, delta, type));
|
||||
m_breakpoint_indices_queue.enqueue(m_breakpoint_indices_queue.size(), abs(delta));
|
||||
}
|
||||
bool done();
|
||||
|
||||
void try_add_breakpoint_in_row(unsigned i) {
|
||||
lean_assert(i < this->m_m);
|
||||
const T & d = this->m_ed[i]; // the coefficient before m_entering in the i-th row
|
||||
if (d == 0) return; // the change of x[m_entering] will not change the corresponding basis x
|
||||
unsigned j = this->m_basis[i];
|
||||
const X & x = this->m_x[j];
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
try_add_breakpoint(j, x, d, fixed_break, this->m_low_bound_values[j]);
|
||||
break;
|
||||
case boxed:
|
||||
try_add_breakpoint(j, x, d, low_break, this->m_low_bound_values[j]);
|
||||
try_add_breakpoint(j, x, d, upper_break, this->m_upper_bound_values[j]);
|
||||
break;
|
||||
case low_bound:
|
||||
try_add_breakpoint(j, x, d, low_break, this->m_low_bound_values[j]);
|
||||
break;
|
||||
case upper_bound:
|
||||
try_add_breakpoint(j, x, d, upper_break, this->m_upper_bound_values[j]);
|
||||
break;
|
||||
case free_column:
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
void move_as_many_as_possible_fixed_columns_to_non_basis();
|
||||
|
||||
std::string break_type_to_string(breakpoint_type type) {
|
||||
switch (type){
|
||||
case low_break: return "low_break";
|
||||
case upper_break: return "upper_break";
|
||||
case fixed_break: return "fixed_break";
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
return "type is not found";
|
||||
}
|
||||
bool non_basis_columns_are_set_correctly();
|
||||
|
||||
void print_breakpoint(const breakpoint<X> * b) {
|
||||
std::cout << "(" << this->column_name(b->m_j) << "," << break_type_to_string(b->m_type) << "," << T_to_string(b->m_delta) << ")" << std::endl;
|
||||
print_bound_info_and_x(b->m_j);
|
||||
}
|
||||
void prefix();
|
||||
|
||||
void print_bound_info_and_x(unsigned j) {
|
||||
std::cout << "type of " << this->column_name(j) << " is " << column_type_to_string(this->m_column_type[j]) << std::endl;
|
||||
std::cout << "x[" << this->column_name(j) << "] = " << this->m_x[j] << std::endl;
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
std::cout << "[" << this->m_low_bound_values[j] << "," << this->m_upper_bound_values[j] << "]" << std::endl;
|
||||
break;
|
||||
case low_bound:
|
||||
std::cout << "[" << this->m_low_bound_values[j] << ", inf" << std::endl;
|
||||
break;
|
||||
case upper_bound:
|
||||
std::cout << "inf ," << this->m_upper_bound_values[j] << "]" << std::endl;
|
||||
break;
|
||||
case free_column:
|
||||
std::cout << "inf, inf" << std::endl;
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
break;
|
||||
}
|
||||
}
|
||||
bool is_tiny() const { return this->m_m < 10 && this->m_n < 20; }
|
||||
|
||||
bool is_empty() const { return this->m_m == 0 || this->m_n == 0; }
|
||||
|
||||
void clear_breakpoints() {
|
||||
m_breakpoints.clear();
|
||||
m_breakpoint_indices_queue.clear();
|
||||
}
|
||||
void feasibility_loop();
|
||||
|
||||
void fill_breakpoints_array(unsigned entering) {
|
||||
clear_breakpoints();
|
||||
for (unsigned i = this->m_m; i--;)
|
||||
try_add_breakpoint_in_row(i);
|
||||
|
||||
if (this->m_column_type[entering] == boxed) {
|
||||
if (m_sign_of_entering_delta < 0)
|
||||
add_breakpoint(entering, - this->bound_span(entering), low_break);
|
||||
else
|
||||
add_breakpoint(entering, this->bound_span(entering), upper_break);
|
||||
}
|
||||
}
|
||||
|
||||
void advance_on_entering(unsigned entering) {
|
||||
this->solve_Bd(entering); // prepares the entering column to be like the one of the tableau
|
||||
m_sign_of_entering_delta = this->m_d[entering] < zero_of_type<T>() ? 1 : -1;
|
||||
|
||||
fill_breakpoints_array(entering);
|
||||
advance_on_sorted_breakpoints(entering);
|
||||
}
|
||||
|
||||
void print_cost() {
|
||||
std::cout << "reduced costs " << std::endl;
|
||||
for (unsigned j = 0; j < this->m_n; j++) {
|
||||
if (numeric_traits<T>::is_zero(this->m_d[j])) continue;
|
||||
std::cout << T_to_string(this->m_d[j]) << this->column_name(j) << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta) {
|
||||
if (entering != leaving)
|
||||
this->update_basis_and_x(entering, leaving, delta);
|
||||
else
|
||||
this->update_x(entering, delta);
|
||||
}
|
||||
|
||||
void advance_on_sorted_breakpoints(unsigned entering) {
|
||||
T slope_at_entering = this->m_d[entering];
|
||||
breakpoint<X> * last_bp = nullptr;
|
||||
while (m_breakpoint_indices_queue.is_empty() == false) {
|
||||
unsigned bi = m_breakpoint_indices_queue.dequeue();
|
||||
breakpoint<X> *b = &m_breakpoints[bi];
|
||||
change_slope_on_breakpoint(entering, b, slope_at_entering);
|
||||
last_bp = b;
|
||||
if (slope_at_entering * m_sign_of_entering_delta > 0) { // the slope started to increase infeasibility
|
||||
break;
|
||||
} else {
|
||||
if (numeric_traits<T>::is_zero(slope_at_entering) && lrand48() % 2 == 0) {
|
||||
// it is not cost benefitial to advance the delta more, so just break to increas the randomness
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
update_basis_and_x_with_comparison(entering, last_bp->m_j, last_bp->m_delta);
|
||||
}
|
||||
|
||||
void change_slope_on_breakpoint(unsigned entering, breakpoint<X> * b, T & slope_at_entering) {
|
||||
if (b->m_j == entering) {
|
||||
lean_assert(b->m_type != fixed_break && (!is_zero(b->m_delta)));
|
||||
slope_at_entering += m_sign_of_entering_delta;
|
||||
return;
|
||||
}
|
||||
|
||||
lean_assert(this->m_basis_heading[b->m_j] >= 0);
|
||||
unsigned i_row = this->m_basis_heading[b->m_j];
|
||||
const T & d = - this->m_ed[i_row];
|
||||
if (numeric_traits<T>::is_zero(d)) return;
|
||||
|
||||
T delta = m_sign_of_entering_delta * abs(d);
|
||||
switch (b->m_type) {
|
||||
case fixed_break:
|
||||
if (is_zero(b->m_delta)) {
|
||||
slope_at_entering += delta;
|
||||
} else {
|
||||
slope_at_entering += 2 * delta;
|
||||
}
|
||||
break;
|
||||
case low_break:
|
||||
case upper_break:
|
||||
slope_at_entering += delta;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
bool row_is_infeasible(unsigned row, int & inf_sign) {
|
||||
unsigned j = this->m_basis[row];
|
||||
inf_sign = get_infeasibility_sign(j);
|
||||
return inf_sign != 0;
|
||||
}
|
||||
|
||||
bool row_is_evidence(unsigned row, int & inf_sign) {
|
||||
if (!row_is_infeasible(row, inf_sign)) return false;
|
||||
calculate_pivot_row(row);
|
||||
int entering = choose_entering_column_for_row_inf_strategy(inf_sign);
|
||||
if (entering == -1) {
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool find_evidence_row() {
|
||||
for (unsigned i = this->m_m; --i;) {
|
||||
int inf_sign;
|
||||
if (row_is_evidence(i, inf_sign)) {
|
||||
fill_evidence(i, inf_sign);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
unsigned get_number_of_inf_rows() const;
|
||||
|
||||
|
||||
bool done() {
|
||||
if (this->m_status == OPTIMAL) return true;
|
||||
if (this->m_status == INFEASIBLE) {
|
||||
if (this->m_settings.row_feasibility == false) {
|
||||
if (find_evidence_row()) {
|
||||
std::cout << "found evidence" << std::endl;
|
||||
} else {
|
||||
std::cout << "did not find evidence" << std::endl;
|
||||
std::cout << "started feasibility_loop at iteration " << this->m_total_iterations << std::endl;
|
||||
unsigned iters = this->m_total_iterations;
|
||||
this->m_status = FEASIBLE;
|
||||
row_feasibility_loop();
|
||||
std::cout << "made another " << this->m_total_iterations - iters << ", percentage is " << 100.0 * (this->m_total_iterations - iters) / this->m_total_iterations << std::endl;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void row_feasibility_loop();
|
||||
|
||||
if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) {
|
||||
std::cout << "m_iters_with_no_cost_growing = " << this->m_iters_with_no_cost_growing << std::endl;
|
||||
this->m_status = ITERATIONS_EXHAUSTED; return true;
|
||||
}
|
||||
if (this->m_total_iterations >= this->m_settings.max_total_number_of_iterations) {
|
||||
std::cout << "max_total_number_of_iterations " << this->m_total_iterations << " is reached " << std::endl;
|
||||
this->m_status = ITERATIONS_EXHAUSTED; return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
int find_infeasible_row(int & inf_sign);
|
||||
|
||||
void move_as_many_as_possible_fixed_columns_to_non_basis() {
|
||||
unsigned i = 0; // points to basis
|
||||
auto& bs = this->m_basis;
|
||||
unsigned j = 0; // points to m_column_types
|
||||
auto & ct = this->m_column_type;
|
||||
std::vector<int> heading(this->m_n, -1);
|
||||
for (int i = 0; i < bs.size(); i ++) heading[bs[j]] = i;
|
||||
lean_assert(this->m_basis.size() == this->m_m);
|
||||
while (i < this->m_m && j < ct.size()) {
|
||||
unsigned basis_j = bs[i];
|
||||
if (ct[basis_j] != fixed) {i++; continue;}
|
||||
do {
|
||||
if (heading[j] == -numeric_traits<T>::one() && ct[j] != fixed)
|
||||
break;
|
||||
j++;
|
||||
} while (j < ct.size());
|
||||
if (j == ct.size()) break;
|
||||
bs[i++] = j++;
|
||||
}
|
||||
}
|
||||
|
||||
bool non_basis_columns_are_set_correctly() {
|
||||
for (unsigned j : this->m_non_basic_columns) {
|
||||
if (!this->x_is_at_bound(j))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void prefix() {
|
||||
init_local();
|
||||
this->init();
|
||||
this->init_basis_heading();
|
||||
}
|
||||
|
||||
bool is_tiny() const {
|
||||
return this->m_m < 10 && this->m_n < 20;
|
||||
}
|
||||
|
||||
bool is_empty() const {
|
||||
return this->m_m == 0 || this->m_n == 0;
|
||||
}
|
||||
|
||||
void feasibility_loop() {
|
||||
while (true) {
|
||||
init_costs();
|
||||
this->init_reduced_costs_for_one_iteration();
|
||||
if (this->print_statistics_with_cost_and_check_that_the_time_is_over(this->m_total_iterations, m_infeasibility)){
|
||||
this->m_status = lp_status::TIME_EXHAUSTED;
|
||||
return; // this->m_total_iterations;
|
||||
}
|
||||
one_iteration();
|
||||
if (done()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned get_number_of_inf_rows() const {
|
||||
unsigned r = 0;
|
||||
for (unsigned k = this->m_m; --k;) {
|
||||
unsigned j = this->m_basis[k];
|
||||
if (get_infeasibility_sign(j)) r++;
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
void row_feasibility_loop() {
|
||||
while (true) {
|
||||
if (this->print_statistics_with_iterations_and_check_that_the_time_is_over(this->m_total_iterations++)){
|
||||
this->m_status = lp_status::TIME_EXHAUSTED;
|
||||
return; // this->m_total_iterations;
|
||||
}
|
||||
int inf_sign;
|
||||
int i = find_infeasible_row(inf_sign);
|
||||
if (i == -1) {
|
||||
this->m_status = OPTIMAL;
|
||||
break;
|
||||
}
|
||||
advance_on_infeasible_row(i, inf_sign);
|
||||
if (done())
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
int find_infeasible_row(int & inf_sign) {
|
||||
unsigned offset = lrand48() % this->m_m;
|
||||
unsigned initial_offset_in_basis = offset;
|
||||
do {
|
||||
unsigned j = this->m_basis[offset];
|
||||
inf_sign = get_infeasibility_sign(j);
|
||||
if (inf_sign)
|
||||
return offset;
|
||||
if (++offset == this->m_m) offset = 0;
|
||||
} while (offset != initial_offset_in_basis);
|
||||
return -1;
|
||||
}
|
||||
|
||||
int get_infeasibility_sign(unsigned j) const {
|
||||
const auto & x = this->m_x[j];
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
if (x < this->m_low_bound_values[j]) return 1;
|
||||
if (x > this->m_upper_bound_values[j]) return -1;
|
||||
return 0;
|
||||
case low_bound:
|
||||
return x < this->m_low_bound_values[j] ? 1 : 0;
|
||||
case upper_bound:
|
||||
return x > this->m_upper_bound_values[j]? -1 :0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
int get_infeasibility_sign(unsigned j) const;
|
||||
|
||||
|
||||
template <typename L>
|
||||
|
|
@ -665,202 +140,37 @@ public:
|
|||
return v > zero_of_type<L>() ? 1 : (v < zero_of_type<L>() ? -1 : 0);
|
||||
}
|
||||
|
||||
bool improves_pivot_row_inf(unsigned j, int inf_sign) {
|
||||
lean_assert(this->m_basis_heading[j] < 0);
|
||||
// we have x[basis[i]] = sum (mj*x[j]), where mj = -m_pivot_row[j]
|
||||
bool improves_pivot_row_inf(unsigned j, int inf_sign);
|
||||
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
return false;
|
||||
case boxed:
|
||||
{
|
||||
lean_assert(this->x_is_at_bound(j));
|
||||
int j_sign = - get_sign(this->m_pivot_row[j]) * inf_sign;
|
||||
if (this->x_is_at_low_bound(j))
|
||||
return j_sign > 0;
|
||||
return j_sign < 0;
|
||||
}
|
||||
case low_bound:
|
||||
{
|
||||
lean_assert(this->x_is_at_low_bound(j));
|
||||
int j_sign = - get_sign(this->m_pivot_row[j]) * inf_sign;
|
||||
return j_sign > 0;
|
||||
}
|
||||
case upper_bound:
|
||||
{
|
||||
lean_assert(this->x_is_at_upper_bound(j));
|
||||
int j_sign = - get_sign(this->m_pivot_row[j]) * inf_sign;
|
||||
return j_sign < 0;
|
||||
}
|
||||
break;
|
||||
case free_column: {
|
||||
return numeric_traits<T>::is_zero(this->m_pivot_row[j]) == false;
|
||||
}
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
int choose_entering_column_for_row_inf_strategy(int inf_sign);
|
||||
|
||||
int choose_entering_column_for_row_inf_strategy(int inf_sign) {
|
||||
unsigned offset = lrand48() % this->m_non_basic_columns.size();
|
||||
unsigned initial_offset_in_non_basis = offset;
|
||||
do {
|
||||
unsigned j = this->m_non_basic_columns[offset];
|
||||
if (improves_pivot_row_inf(j, inf_sign))
|
||||
return j;
|
||||
if (++offset == this->m_non_basic_columns.size()) offset = 0;
|
||||
} while (offset != initial_offset_in_non_basis);
|
||||
return -1;
|
||||
}
|
||||
|
||||
void fill_evidence(unsigned row, int inf_sign) {
|
||||
m_infeasible_row_sign = inf_sign;
|
||||
m_infeasible_row.push_back(std::make_pair(numeric_traits<T>::one(), this->m_basis[row]));
|
||||
for (auto j : this->m_non_basic_columns) {
|
||||
T aj = this->m_pivot_row[j];
|
||||
if (!numeric_traits<T>::is_zero(aj)) {
|
||||
m_infeasible_row.push_back(std::make_pair(aj, j));
|
||||
}
|
||||
}
|
||||
}
|
||||
void fill_evidence(unsigned row, int inf_sign);
|
||||
|
||||
|
||||
void update_delta_of_entering_and_leaving_candidates(X del, X & delta,
|
||||
std::vector<unsigned> & leaving_candidates,
|
||||
unsigned bj) {
|
||||
if (del < delta) {
|
||||
leaving_candidates.clear();
|
||||
leaving_candidates.push_back(bj);
|
||||
delta = del;
|
||||
} else if (del == delta) {
|
||||
leaving_candidates.push_back(bj);
|
||||
}
|
||||
}
|
||||
unsigned bj);
|
||||
|
||||
void update_delta_of_entering(int delta_sign, unsigned row, X & delta,
|
||||
std::vector<unsigned> & leaving_candidates) {
|
||||
unsigned bj = this->m_basis[row]; // bj - the basis column for the row
|
||||
const T & ed = this->m_ed[row]; // this is the coefficent before x[entering] in the sum representing the basis column of this row taken with minus
|
||||
if (numeric_traits<T>::is_zero(ed)) return;
|
||||
const X & x = this->m_x[bj]; // the value of the basis column
|
||||
// adjusted sign
|
||||
int adj_sign = ed < zero_of_type<T>() ? delta_sign : - delta_sign;
|
||||
|
||||
switch (this->m_column_type[bj]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
if (adj_sign > 0 && x <= this->m_upper_bound_values[bj])
|
||||
update_delta_of_entering_and_leaving_candidates((this->m_upper_bound_values[bj] - x) / abs(ed), delta, leaving_candidates, bj);
|
||||
else if (adj_sign < 0 && x >= this->m_low_bound_values[bj])
|
||||
update_delta_of_entering_and_leaving_candidates((x - this->m_low_bound_values[bj]) / abs(ed), delta, leaving_candidates, bj);
|
||||
break;
|
||||
case low_bound:
|
||||
if (adj_sign < 0 && x >= this->m_low_bound_values[bj])
|
||||
update_delta_of_entering_and_leaving_candidates((x - this->m_low_bound_values[bj]) / abs(ed), delta, leaving_candidates, bj);
|
||||
break;
|
||||
case upper_bound:
|
||||
if (adj_sign > 0 && x <= this->m_upper_bound_values[bj])
|
||||
update_delta_of_entering_and_leaving_candidates((this->m_upper_bound_values[bj] - x) / abs(ed), delta, leaving_candidates, bj);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
std::vector<unsigned> & leaving_candidates);
|
||||
|
||||
unsigned find_leaving_for_inf_row_strategy(std::vector<unsigned> & leaving_candidates) {
|
||||
lean_assert(leaving_candidates.size());
|
||||
return leaving_candidates[lrand48() % leaving_candidates.size()]; // more randomness
|
||||
return leaving_candidates[my_random() % leaving_candidates.size()]; // more randomness
|
||||
}
|
||||
|
||||
X find_initial_delta_and_its_sign(unsigned row, unsigned entering,
|
||||
int inf_sign, int & entering_delta_sign,
|
||||
std::vector<unsigned> & leaving_candidates) {
|
||||
lean_assert(inf_sign != 0);
|
||||
unsigned bj = this->m_basis[row]; // this is the infeasible basis column
|
||||
const X & x = this->m_x[bj];
|
||||
entering_delta_sign = - get_sign(this->m_pivot_row[entering]) * inf_sign;
|
||||
lean_assert(entering_delta_sign != 0);
|
||||
X delta = (inf_sign > 0? (this->m_low_bound_values[bj] - x) : (x - this->m_upper_bound_values[bj])) / abs(this->m_pivot_row[entering]);
|
||||
if (this->m_column_type[entering] == boxed) {
|
||||
X span = this->bound_span(entering);
|
||||
if (span < delta) {
|
||||
delta = span;
|
||||
leaving_candidates.push_back(entering);
|
||||
} else {
|
||||
leaving_candidates.push_back(bj);
|
||||
}
|
||||
} else {
|
||||
leaving_candidates.push_back(bj);
|
||||
}
|
||||
std::vector<unsigned> & leaving_candidates);
|
||||
|
||||
return delta;
|
||||
}
|
||||
void advance_on_infeasible_row_and_entering(unsigned inf_row, unsigned entering, int inf_sign);
|
||||
|
||||
void advance_on_infeasible_row_and_entering(unsigned inf_row, unsigned entering, int inf_sign) {
|
||||
this->solve_Bd(entering); // puts the tableau column of entering into this->m_ed
|
||||
int entering_delta_sign;
|
||||
std::vector<unsigned> leaving_candidates;
|
||||
X delta = find_initial_delta_and_its_sign(inf_row, entering, inf_sign, entering_delta_sign, leaving_candidates);
|
||||
lean_assert(leaving_candidates.size());
|
||||
lean_assert(delta > zero_of_type<X>());
|
||||
unsigned row = lrand48() % this->m_m;
|
||||
unsigned initial_row = row;
|
||||
do {
|
||||
if (row != inf_row)
|
||||
update_delta_of_entering(entering_delta_sign, row, delta, leaving_candidates);
|
||||
if (++row == this->m_m) row = 0;
|
||||
} while (row != initial_row);
|
||||
unsigned leaving = find_leaving_for_inf_row_strategy(leaving_candidates);
|
||||
update_basis_and_x_with_comparison(entering, leaving, delta * entering_delta_sign);
|
||||
}
|
||||
void advance_on_infeasible_row(unsigned i, int inf_sign);
|
||||
|
||||
void advance_on_infeasible_row(unsigned i, int inf_sign) {
|
||||
calculate_pivot_row(i);
|
||||
int entering = choose_entering_column_for_row_inf_strategy(inf_sign);
|
||||
if (entering == -1) {
|
||||
fill_evidence(i, inf_sign);
|
||||
this->m_status = INFEASIBLE;
|
||||
return;
|
||||
}
|
||||
advance_on_infeasible_row_and_entering(i, entering, inf_sign);
|
||||
}
|
||||
|
||||
void solve() {
|
||||
prefix();
|
||||
if (is_empty()) {
|
||||
this->m_status = OPTIMAL;
|
||||
return;
|
||||
}
|
||||
this->snap_xN_to_bounds(); // we start with non-basic variables at their bounds
|
||||
lean_assert(non_basis_columns_are_set_correctly());
|
||||
|
||||
if (this->m_settings.row_feasibility) {
|
||||
std::cout << "optimizing by rows " << std::endl;
|
||||
row_feasibility_loop();
|
||||
} else {
|
||||
std::cout << "optimizing total infeasibility" << std::endl;
|
||||
feasibility_loop();
|
||||
}
|
||||
}
|
||||
void solve();
|
||||
|
||||
bool low_bounds_are_set() const { return true; }
|
||||
|
||||
void print_column_info(unsigned j) {
|
||||
std::cout << "type = " << column_type_to_string(this->m_column_type[j]) << std::endl;
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
std::cout << "(" << this->m_low_bound_values[j] << ", " << this->m_upper_bound_values[j] << ")" << std::endl;
|
||||
break;
|
||||
case low_bound:
|
||||
std::cout << this->m_low_bound_values[j] << std::endl;
|
||||
break;
|
||||
case upper_bound:
|
||||
std::cout << this->m_upper_bound_values[j] << std::endl;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
void print_column_info(unsigned j);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
13
src/util/lp/lar_core_solver_instances.cpp
Normal file
13
src/util/lp/lar_core_solver_instances.cpp
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lar_core_solver.cpp"
|
||||
template lean::lar_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::lar_core_solver(std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<lean::column_type, std::allocator<lean::column_type> >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<unsigned int, std::allocator<unsigned int> >&, lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, lean::lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template void lean::lar_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::solve();
|
||||
template void lean::lar_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::prefix();
|
||||
template void lean::lar_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::print_column_info(unsigned int);
|
||||
template lean::numeric_pair<lean::mpq> lean::lar_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::get_deb_inf();
|
||||
template bool lean::lar_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::is_empty() const;
|
||||
791
src/util/lp/lar_solver.cpp
Normal file
791
src/util/lp/lar_solver.cpp
Normal file
|
|
@ -0,0 +1,791 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/lar_solver.h"
|
||||
namespace lean {
|
||||
double conversion_helper <double>::get_low_bound(const column_info<mpq> & ci) {
|
||||
if (!ci.low_bound_is_strict())
|
||||
return ci.get_low_bound().get_double();
|
||||
double eps = 0.00001;
|
||||
if (!ci.upper_bound_is_set())
|
||||
return ci.get_low_bound().get_double() + eps;
|
||||
eps = std::min((ci.get_upper_bound() - ci.get_low_bound()).get_double()/1000, eps);
|
||||
return ci.get_low_bound().get_double() + eps;
|
||||
}
|
||||
|
||||
double conversion_helper <double>::get_upper_bound(const column_info<mpq> & ci) {
|
||||
if (!ci.upper_bound_is_strict())
|
||||
return ci.get_upper_bound().get_double();
|
||||
double eps = 0.00001;
|
||||
if (!ci.low_bound_is_set())
|
||||
return ci.get_upper_bound().get_double() - eps;
|
||||
eps = std::min((ci.get_upper_bound() - ci.get_low_bound()).get_double()/1000, eps);
|
||||
return ci.get_upper_bound().get_double() - eps;
|
||||
}
|
||||
|
||||
canonic_left_side * lar_solver::create_or_fetch_existing_left_side(const buffer<pair<mpq, var_index>>& left_side_par) {
|
||||
auto left_side = new canonic_left_side(left_side_par);
|
||||
lean_assert(left_side->size() > 0);
|
||||
auto it = m_canonic_left_sides.find(left_side);
|
||||
if (it == m_canonic_left_sides.end()) {
|
||||
m_canonic_left_sides.insert(left_side);
|
||||
} else {
|
||||
delete left_side;
|
||||
left_side = *it;
|
||||
}
|
||||
return left_side;
|
||||
}
|
||||
|
||||
|
||||
bool lar_solver::var_is_fixed(unsigned j) {
|
||||
auto it = m_map_from_var_index_to_left_side.find(j);
|
||||
lean_assert(it != m_map_from_var_index_to_left_side.end());
|
||||
return it->second->m_column_info.is_fixed();
|
||||
}
|
||||
|
||||
mpq lar_solver::find_ratio(canonic_left_side * ls, const lar_constraint & constraint) {
|
||||
lean_assert(ls->m_coeffs.size() > 0);
|
||||
auto first_pair = ls->m_coeffs[0];
|
||||
lean_assert(first_pair.first == numeric_traits<mpq>::one());
|
||||
var_index i = first_pair.second;
|
||||
auto it = constraint.m_left_side.find(i);
|
||||
lean_assert(it != constraint.m_left_side.end());
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void lar_solver::add_canonic_left_side_for_var(var_index i, std::string var_name) {
|
||||
buffer<pair<mpq, var_index>> b;
|
||||
b.push_back(std::make_pair(numeric_traits<mpq>::one(), i));
|
||||
auto can_ls = new canonic_left_side(b);
|
||||
can_ls->set_name(var_name);
|
||||
|
||||
lean_assert(m_canonic_left_sides.find(can_ls) == m_canonic_left_sides.end());
|
||||
m_canonic_left_sides.insert(can_ls);
|
||||
m_map_from_var_index_to_left_side[i] = can_ls;
|
||||
}
|
||||
|
||||
void lar_solver::map_left_side_to_column_of_A(canonic_left_side* left_side, unsigned & j) {
|
||||
lean_assert(m_column_indices_to_canonic_left_sides.find(j) == m_column_indices_to_canonic_left_sides.end());
|
||||
left_side->m_column_index = j; // assigning this index does not change the hash of canonic_left_side
|
||||
if (left_side->size() > 1) { // if size is one we will not create a row for this left side
|
||||
left_side->m_row_index = m_basis.size();
|
||||
m_basis.push_back(j); // j will be a basis column, so we put it into the basis as well
|
||||
}
|
||||
m_column_indices_to_canonic_left_sides[j++] = left_side; // pointing from the column to the left side
|
||||
}
|
||||
|
||||
|
||||
void lar_solver::map_left_sides_to_columns_of_A() {
|
||||
unsigned j = 0;
|
||||
for (auto it : m_canonic_left_sides)
|
||||
map_left_side_to_column_of_A(it, j);
|
||||
}
|
||||
|
||||
bool valid_index(unsigned j) { return static_cast<int>(j) >= 0;}
|
||||
|
||||
|
||||
// this adds a row to A
|
||||
template <typename U, typename V>
|
||||
void lar_solver::add_row_to_A(static_matrix<U, V> & A, unsigned i, canonic_left_side * ls) {
|
||||
for (auto & t : ls->m_coeffs) {
|
||||
var_index vi = t.second;
|
||||
canonic_left_side *var_left_side = m_map_from_var_index_to_left_side[vi];
|
||||
unsigned column = var_left_side->m_column_index;
|
||||
lean_assert(valid_index(column));
|
||||
A.set(i, column, convert_struct<U, mpq>::convert(t.first));
|
||||
}
|
||||
A.set(i, ls->m_column_index, - one_of_type<U>());
|
||||
}
|
||||
|
||||
template <typename U, typename V>
|
||||
void lar_solver::create_matrix_A(static_matrix<U, V> & A) {
|
||||
unsigned n = m_column_indices_to_canonic_left_sides.size();
|
||||
unsigned m = m_basis.size();
|
||||
A.init_empty_matrix(m, n);
|
||||
unsigned i = 0;
|
||||
for (auto & t : m_column_indices_to_canonic_left_sides)
|
||||
if (valid_index(t.second->m_row_index))
|
||||
add_row_to_A(A, i++, t.second);
|
||||
#ifdef LEAN_DEBUG
|
||||
// print_matrix(m_A);
|
||||
#endif
|
||||
}
|
||||
|
||||
// void lar_solver::fill_column_info_names() {
|
||||
// for (unsigned j = 0; j < m_A.column_count(); j++) {
|
||||
// column_info<mpq> t;
|
||||
// m_column_infos.push_back(t);
|
||||
// if (j < m_map_from_var_index_to_name_left_side_pair.size()) {
|
||||
// m_column_infos.back().set_name(m_map_from_var_index_to_name_left_side_pair[j]);
|
||||
// } else {
|
||||
// string pref("_s_");
|
||||
// m_column_infos.back().set_name(pref + T_to_string(j));
|
||||
// }
|
||||
// m_column_names
|
||||
// }
|
||||
// }
|
||||
void lar_solver::set_upper_bound_for_column_info(lar_normalized_constraint * norm_constr) {
|
||||
const mpq & v = norm_constr->m_right_side;
|
||||
canonic_left_side * ls = norm_constr->m_canonic_left_side;
|
||||
column_info<mpq> & ci = ls->m_column_info;
|
||||
lean_assert(norm_constr->m_kind == LE || norm_constr->m_kind == LT || norm_constr->m_kind == EQ);
|
||||
bool strict = norm_constr->m_kind == LT;
|
||||
if (!ci.upper_bound_is_set()) {
|
||||
ls->m_upper_bound_witness = norm_constr;
|
||||
ci.set_upper_bound(v);
|
||||
ci.set_upper_bound_strict(strict);
|
||||
} else if (ci.get_upper_bound() > v) {
|
||||
ci.set_upper_bound(v);
|
||||
ls->m_upper_bound_witness = norm_constr;
|
||||
ci.set_upper_bound_strict(strict);
|
||||
}
|
||||
if (ci.is_infeasible()) {
|
||||
m_status= INFEASIBLE;
|
||||
m_infeasible_canonic_left_side = ls;
|
||||
return;
|
||||
}
|
||||
try_to_set_fixed(ci);
|
||||
}
|
||||
|
||||
bool lar_solver::try_to_set_fixed(column_info<mpq> & ci) {
|
||||
if (ci.upper_bound_is_set() && ci.low_bound_is_set() && ci.get_upper_bound() == ci.get_low_bound() && !ci.is_fixed()) {
|
||||
ci.set_fixed_value(ci.get_upper_bound());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void lar_solver::set_low_bound_for_column_info(lar_normalized_constraint * norm_constr) {
|
||||
const mpq & v = norm_constr->m_right_side;
|
||||
canonic_left_side * ls = norm_constr->m_canonic_left_side;
|
||||
column_info<mpq> & ci = ls->m_column_info;
|
||||
lean_assert(norm_constr->m_kind == GE || norm_constr->m_kind == GT || norm_constr->m_kind == EQ);
|
||||
bool strict = norm_constr->m_kind == GT;
|
||||
if (!ci.low_bound_is_set()) {
|
||||
ci.set_low_bound(v);
|
||||
ls->m_low_bound_witness = norm_constr;
|
||||
ci.set_low_bound_strict(strict);
|
||||
} else if (ci.get_low_bound() < v) {
|
||||
ci.set_low_bound(v);
|
||||
ls->m_low_bound_witness = norm_constr;
|
||||
ci.set_low_bound_strict(strict);
|
||||
}
|
||||
|
||||
if (ci.is_infeasible()) {
|
||||
m_status= INFEASIBLE;
|
||||
m_infeasible_canonic_left_side = ls;
|
||||
return;
|
||||
}
|
||||
|
||||
try_to_set_fixed(ci);
|
||||
}
|
||||
|
||||
void lar_solver::update_column_info_of_normalized_constraint(lar_normalized_constraint & norm_constr) {
|
||||
lean_assert(norm_constr.size() > 0);
|
||||
switch (norm_constr.m_kind) {
|
||||
case LE:
|
||||
case LT:
|
||||
set_upper_bound_for_column_info(&norm_constr);
|
||||
break;
|
||||
case GE:
|
||||
case GT:
|
||||
set_low_bound_for_column_info(&norm_constr);
|
||||
break;
|
||||
|
||||
case EQ:
|
||||
{
|
||||
set_upper_bound_for_column_info(&norm_constr);
|
||||
set_low_bound_for_column_info(&norm_constr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
column_type lar_solver::get_column_type(column_info<mpq> & ci) {
|
||||
auto ret = ci.get_column_type_no_flipping();
|
||||
if (ret == boxed) { // changing boxed to fixed because of the no span
|
||||
if (ci.get_low_bound() == ci.get_upper_bound())
|
||||
ret = fixed;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void lar_solver::fill_column_names() {
|
||||
m_column_names.clear();
|
||||
for (auto t : m_canonic_left_sides) {
|
||||
auto & ci = t->m_column_info;
|
||||
unsigned j = t->m_column_index;
|
||||
lean_assert(valid_index(j));
|
||||
std::string name = ci.get_name();
|
||||
if (name.size() == 0)
|
||||
name = std::string("_s") + T_to_string(j);
|
||||
m_column_names[j] = name;
|
||||
}
|
||||
}
|
||||
|
||||
void lar_solver::fill_column_types() {
|
||||
m_column_types.clear();
|
||||
m_column_types.resize(m_canonic_left_sides.size(), free_column);
|
||||
for (auto t : m_canonic_left_sides) {
|
||||
auto & ci = t->m_column_info;
|
||||
unsigned j = t->m_column_index;
|
||||
lean_assert(valid_index(j));
|
||||
m_column_types[j] = get_column_type(ci);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
void lar_solver::fill_bounds_for_core_solver(std::vector<V> & lb, std::vector<V> & ub) {
|
||||
unsigned n = m_canonic_left_sides.size(); // this is the number of columns
|
||||
lb.resize(n);
|
||||
ub.resize(n);
|
||||
for (auto t : m_canonic_left_sides) {
|
||||
auto & ci = t->m_column_info;
|
||||
unsigned j = t->m_column_index;
|
||||
lean_assert(valid_index(j));
|
||||
if (ci.low_bound_is_set())
|
||||
lb[j] = conversion_helper<V>::get_low_bound(ci);
|
||||
if (ci.upper_bound_is_set())
|
||||
ub[j] = conversion_helper<V>::get_upper_bound(ci);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename V>
|
||||
void lar_solver::resize_x_and_init_with_zeros(std::vector<V> & x, unsigned n) {
|
||||
x.clear();
|
||||
x.resize(n, zero_of_type<V>()); // init with zeroes
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
void lar_solver::resize_x_and_init_with_signature(std::vector<V> & x, std::vector<V> & low_bound,
|
||||
std::vector<V> & upper_bound, const lar_solution_signature & signature) {
|
||||
x.clear();
|
||||
x.resize(low_bound.size());
|
||||
for (auto & t : signature.non_basic_column_value_positions) {
|
||||
x[t.first] = get_column_val(low_bound, upper_bound, t.second, t.first);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename V> V lar_solver::get_column_val(std::vector<V> & low_bound, std::vector<V> & upper_bound, non_basic_column_value_position pos_type, unsigned j) {
|
||||
switch (pos_type) {
|
||||
case at_low_bound: return low_bound[j];
|
||||
case at_fixed:
|
||||
case at_upper_bound: return upper_bound[j];
|
||||
case free_of_bounds: return zero_of_type<V>();
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
lar_solver::~lar_solver() {
|
||||
std::vector<canonic_left_side*> to_delete;
|
||||
for (auto it : m_canonic_left_sides)
|
||||
to_delete.push_back(it);
|
||||
for (auto t : to_delete)
|
||||
delete t;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
var_index lar_solver::add_var(std::string s) {
|
||||
auto got = m_var_names_to_var_index.find(s);
|
||||
if (got != m_var_names_to_var_index.end()) return got->second;
|
||||
|
||||
var_index i = m_available_var_index++;
|
||||
m_var_names_to_var_index[s] = i;
|
||||
add_canonic_left_side_for_var(i, s);
|
||||
return i;
|
||||
}
|
||||
|
||||
constraint_index lar_solver::add_constraint(const buffer<pair<mpq, var_index>>& left_side, lconstraint_kind kind_par, mpq right_side_par) {
|
||||
if (left_side.size() == 0) {
|
||||
std::cout << "cannot add a constraint without left side" << std::endl;
|
||||
return (constraint_index)(-1);
|
||||
}
|
||||
constraint_index i = m_available_constr_index++;
|
||||
lar_constraint original_constr(left_side, kind_par, right_side_par, i);
|
||||
canonic_left_side * ls = create_or_fetch_existing_left_side(left_side);
|
||||
mpq ratio = find_ratio(ls, original_constr);
|
||||
auto kind = ratio.is_neg()? flip_kind(kind_par): kind_par;
|
||||
mpq right_side = right_side_par / ratio;
|
||||
lar_normalized_constraint normalized_constraint(ls, ratio, kind, right_side, original_constr);
|
||||
|
||||
m_normalized_constraints[i] = normalized_constraint;
|
||||
return i;
|
||||
}
|
||||
|
||||
bool lar_solver::all_constraints_hold() {
|
||||
std::unordered_map<var_index, mpq> var_map;
|
||||
get_model(var_map);
|
||||
for ( auto & it : m_normalized_constraints )
|
||||
if (!constraint_holds(it.second.m_origin_constraint, var_map)) {
|
||||
print_constraint(&it.second.m_origin_constraint);
|
||||
std::cout << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lar_solver::constraint_holds(const lar_constraint & constr, std::unordered_map<var_index, mpq> & var_map) {
|
||||
mpq left_side_val = get_left_side_val(constr, var_map);
|
||||
switch (constr.m_kind) {
|
||||
case LE: return left_side_val <= constr.m_right_side;
|
||||
case LT: return left_side_val <= constr.m_right_side;
|
||||
case GE: return left_side_val >= constr.m_right_side;
|
||||
case GT: return left_side_val >= constr.m_right_side;
|
||||
case EQ: return left_side_val == constr.m_right_side;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
void lar_solver::solve_with_core_solver() {
|
||||
m_mpq_core_solver.solve();
|
||||
m_status = m_mpq_core_solver.m_status;
|
||||
lean_assert(m_status != OPTIMAL || all_constraints_hold());
|
||||
#ifdef LEAN_DEBUG
|
||||
lean_assert(!settings().row_feasibility || m_status != INFEASIBLE || the_evidence_is_correct());
|
||||
#endif
|
||||
}
|
||||
|
||||
bool lar_solver::the_relations_are_of_same_type(const buffer<pair<mpq, unsigned>> & evidence, lconstraint_kind & the_kind_of_sum) {
|
||||
unsigned n_of_G = 0, n_of_L = 0;
|
||||
bool strict = false;
|
||||
for (auto & it : evidence) {
|
||||
mpq coeff = it.first;
|
||||
constraint_index con_ind = it.second;
|
||||
std::cout << coeff.get_double() << std::endl;
|
||||
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
|
||||
print_constraint(&constr); std::cout << std::endl;
|
||||
|
||||
lconstraint_kind kind = coeff.is_pos()? constr.m_kind: flip_kind(constr.m_kind);
|
||||
if (kind == GT || kind == LT)
|
||||
strict = true;
|
||||
if (kind == GE || kind == GT) n_of_G++;
|
||||
else if (kind == LE || kind == LT) n_of_L++;
|
||||
}
|
||||
the_kind_of_sum = n_of_G? GE : (n_of_L? LE : EQ);
|
||||
if (strict)
|
||||
the_kind_of_sum = static_cast<lconstraint_kind>((static_cast<int>(the_kind_of_sum)/2));
|
||||
|
||||
return n_of_G == 0 || n_of_L == 0;
|
||||
}
|
||||
|
||||
void lar_solver::register_in_map(std::unordered_map<var_index, mpq> & coeffs, lar_constraint & cn, const mpq & a) {
|
||||
for (auto & it : cn.m_left_side) {
|
||||
unsigned j = it.first;
|
||||
auto p = coeffs.find(j);
|
||||
if (p == coeffs.end()) coeffs[j] = it.second * a;
|
||||
else p->second += it.second * a;
|
||||
}
|
||||
}
|
||||
bool lar_solver::the_left_sides_sum_to_zero(const buffer<pair<mpq, unsigned>> & evidence) {
|
||||
std::unordered_map<var_index, mpq> coeff_map;
|
||||
for (auto & it : evidence) {
|
||||
mpq coeff = it.first;
|
||||
constraint_index con_ind = it.second;
|
||||
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
|
||||
register_in_map(coeff_map, constr, coeff);
|
||||
}
|
||||
for (auto & it : coeff_map) {
|
||||
if (!numeric_traits<mpq>::is_zero(it.second)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool lar_solver::the_righ_sides_do_not_sum_to_zero(const buffer<pair<mpq, unsigned>> & evidence) {
|
||||
mpq ret = numeric_traits<mpq>::zero();
|
||||
for (auto & it : evidence) {
|
||||
mpq coeff = it.first;
|
||||
constraint_index con_ind = it.second;
|
||||
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
|
||||
ret += constr.m_right_side * coeff;
|
||||
}
|
||||
return !numeric_traits<mpq>::is_zero(ret);
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
bool lar_solver::the_evidence_is_correct() {
|
||||
buffer<pair<mpq, unsigned>> evidence;
|
||||
get_infeasibility_evidence(evidence);
|
||||
lconstraint_kind kind;
|
||||
lean_assert(the_relations_are_of_same_type(evidence, kind));
|
||||
lean_assert(the_left_sides_sum_to_zero(evidence));
|
||||
mpq rs = sum_of_right_sides_of_evidence(evidence);
|
||||
switch (kind) {
|
||||
case LE: lean_assert(rs < zero_of_type<mpq>());
|
||||
break;
|
||||
case LT: lean_assert(rs <= zero_of_type<mpq>());
|
||||
break;
|
||||
case GE: lean_assert(rs > zero_of_type<mpq>());
|
||||
break;
|
||||
case GT: lean_assert(rs >= zero_of_type<mpq>());
|
||||
break;
|
||||
case EQ: lean_assert(rs != zero_of_type<mpq>());
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
void lar_solver::update_column_info_of_normalized_constraints() {
|
||||
for (auto & it : m_normalized_constraints)
|
||||
update_column_info_of_normalized_constraint(it.second);
|
||||
}
|
||||
|
||||
template <typename V>
|
||||
void lar_solver::init_right_sides_with_zeros(std::vector<V> & rs) {
|
||||
rs.clear();
|
||||
rs.resize(m_basis.size(), zero_of_type<V>());
|
||||
}
|
||||
|
||||
mpq lar_solver::sum_of_right_sides_of_evidence(const buffer<pair<mpq, unsigned>> & evidence) {
|
||||
mpq ret = numeric_traits<mpq>::zero();
|
||||
for (auto & it : evidence) {
|
||||
mpq coeff = it.first;
|
||||
constraint_index con_ind = it.second;
|
||||
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
|
||||
ret += constr.m_right_side * coeff;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void lar_solver::prepare_independently_of_numeric_type() {
|
||||
update_column_info_of_normalized_constraints();
|
||||
map_left_sides_to_columns_of_A();
|
||||
fill_column_names();
|
||||
fill_column_types();
|
||||
}
|
||||
|
||||
template <typename U, typename V>
|
||||
void lar_solver::prepare_core_solver_fields(static_matrix<U, V> & A, std::vector<V> & x,
|
||||
std::vector<V> & right_side_vector,
|
||||
std::vector<V> & low_bound,
|
||||
std::vector<V> & upper_bound) {
|
||||
create_matrix_A(A);
|
||||
fill_bounds_for_core_solver(low_bound, upper_bound);
|
||||
if (m_status == INFEASIBLE) {
|
||||
lean_assert(false); // not implemented
|
||||
}
|
||||
init_right_sides_with_zeros(right_side_vector);
|
||||
resize_x_and_init_with_zeros(x, A.column_count());
|
||||
lean_assert(m_basis.size() == A.row_count());
|
||||
}
|
||||
|
||||
template <typename U, typename V>
|
||||
void lar_solver::prepare_core_solver_fields_with_signature(static_matrix<U, V> & A, std::vector<V> & x,
|
||||
std::vector<V> & right_side_vector,
|
||||
std::vector<V> & low_bound,
|
||||
std::vector<V> & upper_bound, const lar_solution_signature & signature) {
|
||||
create_matrix_A(A);
|
||||
fill_bounds_for_core_solver(low_bound, upper_bound);
|
||||
if (m_status == INFEASIBLE) {
|
||||
lean_assert(false); // not implemented
|
||||
}
|
||||
init_right_sides_with_zeros(right_side_vector);
|
||||
resize_x_and_init_with_signature(x, low_bound, upper_bound, signature);
|
||||
}
|
||||
|
||||
void lar_solver::find_solution_signature_with_doubles(lar_solution_signature & signature) {
|
||||
static_matrix<double, double> A;
|
||||
std::vector<double> x, right_side_vector, low_bounds, upper_bounds;
|
||||
prepare_core_solver_fields<double, double>(A, x, right_side_vector, low_bounds, upper_bounds);
|
||||
std::vector<double> column_scale_vector;
|
||||
scaler<double, double > scaler(right_side_vector, A, m_settings.scaling_minimum, m_settings.scaling_maximum, column_scale_vector, this->m_settings);
|
||||
if (!scaler.scale()) {
|
||||
// the scale did not succeed, unscaling
|
||||
A.clear();
|
||||
create_matrix_A(A);
|
||||
for (auto & s : column_scale_vector)
|
||||
s = one_of_type<double>();
|
||||
}
|
||||
std::vector<double> costs(A.column_count());
|
||||
auto core_solver = lp_primal_core_solver<double, double>(A,
|
||||
right_side_vector,
|
||||
x,
|
||||
m_basis,
|
||||
costs,
|
||||
m_column_types,
|
||||
low_bounds,
|
||||
upper_bounds,
|
||||
m_settings,
|
||||
m_column_names);
|
||||
core_solver.find_feasible_solution();
|
||||
extract_signature_from_lp_core_solver(core_solver, signature);
|
||||
}
|
||||
|
||||
template <typename U, typename V>
|
||||
void lar_solver::extract_signature_from_lp_core_solver(lp_primal_core_solver<U, V> & core_solver, lar_solution_signature & signature) {
|
||||
for (auto j : core_solver.m_non_basic_columns)
|
||||
signature.non_basic_column_value_positions[j] = core_solver.get_non_basic_column_value_position(j);
|
||||
}
|
||||
|
||||
void lar_solver::solve_on_signature(const lar_solution_signature & signature) {
|
||||
prepare_core_solver_fields_with_signature(m_A, m_x, m_right_side_vector, m_low_bounds, m_upper_bounds, signature);
|
||||
solve_with_core_solver();
|
||||
}
|
||||
|
||||
void lar_solver::solve() {
|
||||
prepare_independently_of_numeric_type();
|
||||
if (m_settings.use_double_solver_for_lar) {
|
||||
lar_solution_signature solution_signature;
|
||||
find_solution_signature_with_doubles(solution_signature);
|
||||
// here the basis that is kept in m_basis is the same that was used in the double solver
|
||||
solve_on_signature(solution_signature);
|
||||
return;
|
||||
}
|
||||
prepare_core_solver_fields(m_A, m_x, m_right_side_vector, m_low_bounds, m_upper_bounds);
|
||||
solve_with_core_solver();
|
||||
}
|
||||
|
||||
lp_status lar_solver::check() {
|
||||
// for the time being just call solve()
|
||||
solve();
|
||||
return m_status;
|
||||
}
|
||||
void lar_solver::get_infeasibility_evidence(buffer<pair<mpq, constraint_index>> & evidence){
|
||||
if (!m_mpq_core_solver.get_infeasible_row_sign()) {
|
||||
std::cout << "don't have the infeasibility evidence" << std::endl;
|
||||
return;
|
||||
}
|
||||
// the infeasibility sign
|
||||
int inf_sign;
|
||||
auto inf_row = m_mpq_core_solver.get_infeasibility_info(inf_sign);
|
||||
lean_assert(inf_sign != 0);
|
||||
get_infeasibility_evidence_for_inf_sign(evidence, inf_row, inf_sign);
|
||||
}
|
||||
|
||||
void lar_solver::get_infeasibility_evidence_for_inf_sign(buffer<pair<mpq, constraint_index>> & evidence,
|
||||
const std::vector<pair<mpq, unsigned>> & inf_row,
|
||||
int inf_sign) {
|
||||
for (auto & it : inf_row) {
|
||||
mpq coeff = it.first;
|
||||
unsigned j = it.second;
|
||||
canonic_left_side * ls = m_column_indices_to_canonic_left_sides[j];
|
||||
int adj_sign = coeff.is_pos() ? inf_sign : -inf_sign;
|
||||
|
||||
lar_normalized_constraint * bound_constr = adj_sign < 0? ls->m_upper_bound_witness : ls->m_low_bound_witness;
|
||||
lean_assert(bound_constr != nullptr);
|
||||
evidence.push_back(std::make_pair(coeff / bound_constr->m_ratio_to_original, bound_constr->m_index));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
mpq lar_solver::find_delta_for_strict_bounds() {
|
||||
mpq delta = numeric_traits<mpq>::one();
|
||||
for (auto t : m_canonic_left_sides) {
|
||||
auto & ci = t->m_column_info;
|
||||
unsigned j = t->m_column_index;
|
||||
lean_assert (valid_index(j));
|
||||
if (ci.low_bound_is_set())
|
||||
restrict_delta_on_low_bound_column(delta, j);
|
||||
if (ci.upper_bound_is_set())
|
||||
restrict_delta_on_upper_bound(delta, j);
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
|
||||
void lar_solver::restrict_delta_on_low_bound_column(mpq& delta, unsigned j) {
|
||||
numeric_pair<mpq> & x = m_x[j];
|
||||
numeric_pair<mpq> & l = m_low_bounds[j];
|
||||
mpq & xx = x.x;
|
||||
mpq & xy = x.y;
|
||||
mpq & lx = l.x;
|
||||
if (xx == lx) {
|
||||
lean_assert(xy >= numeric_traits<mpq>::zero());
|
||||
} else {
|
||||
lean_assert(xx >= lx); // we need lx <= xx + delta*xy, or delta*xy >= lx - xx, or - delta*xy <= xx - ls.
|
||||
// The right part is not negative. The delta is positive. If xy >= 0 we have the ineqality
|
||||
// otherwise we need to have delta not greater than - (xx - lx)/xy. We use the 2 coefficient to handle the strict case
|
||||
if (xy >= zero_of_type<mpq>()) return;
|
||||
delta = std::min(delta, (lx - xx)/ (2 * xy)); // we need to have delta * xy < xx - lx for the strict case
|
||||
}
|
||||
}
|
||||
void lar_solver::restrict_delta_on_upper_bound(mpq& delta, unsigned j) {
|
||||
numeric_pair<mpq> & x = m_x[j];
|
||||
numeric_pair<mpq> & u = m_upper_bounds[j];
|
||||
mpq & xx = x.x;
|
||||
mpq & xy = x.y;
|
||||
mpq & ux = u.x;
|
||||
if (xx == ux) {
|
||||
lean_assert(xy <= numeric_traits<mpq>::zero());
|
||||
} else {
|
||||
lean_assert(xx < ux);
|
||||
if (xy <= zero_of_type<mpq>()) return;
|
||||
delta = std::min(delta, (ux - xx)/ (2 * xy)); // we need to have delta * xy < ux - xx, for the strict case
|
||||
}
|
||||
}
|
||||
|
||||
void lar_solver::get_model(std::unordered_map<var_index, mpq> & variable_values){
|
||||
lean_assert(m_status == OPTIMAL);
|
||||
mpq delta = find_delta_for_strict_bounds();
|
||||
for (auto & it : m_map_from_var_index_to_left_side) {
|
||||
numeric_pair<mpq> & rp = m_x[it.second->m_column_index];
|
||||
// std::cout << it.second->m_column_info.get_name() << " = " << rp << std::endl;
|
||||
variable_values[it.first] = rp.x + delta * rp.y;
|
||||
}
|
||||
}
|
||||
|
||||
std::string lar_solver::get_variable_name(var_index vi) {
|
||||
if (m_map_from_var_index_to_left_side.size() <= vi) {
|
||||
std::string s = "variable " + T_to_string(vi) + " is not found";
|
||||
return s;
|
||||
}
|
||||
return m_map_from_var_index_to_left_side[vi]->m_column_info.get_name();
|
||||
}
|
||||
|
||||
// ********** print region start
|
||||
void lar_solver::print_constraint(constraint_index ci) {
|
||||
if (m_normalized_constraints.size() <= ci) {
|
||||
std::string s = "constraint " + T_to_string(ci) + " is not found";
|
||||
std::cout << s << std::endl;
|
||||
return;
|
||||
}
|
||||
|
||||
print_constraint(&m_normalized_constraints[ci]);
|
||||
}
|
||||
|
||||
void lar_solver::print_canonic_left_side(const canonic_left_side & c) {
|
||||
bool first = true;
|
||||
for (auto it : c.m_coeffs) {
|
||||
auto val = it.first;
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
if (val.is_pos()) {
|
||||
std::cout << " + ";
|
||||
} else {
|
||||
std::cout << " - ";
|
||||
val = -val;
|
||||
}
|
||||
}
|
||||
if (val != numeric_traits<mpq>::one())
|
||||
std::cout << T_to_string(val);
|
||||
std::cout << m_map_from_var_index_to_left_side[it.second]->m_column_info.get_name();
|
||||
}
|
||||
}
|
||||
|
||||
void lar_solver::print_left_side_of_constraint(const lar_base_constraint * c) {
|
||||
bool first = true;
|
||||
for (auto it : c->get_left_side_coefficients()) {
|
||||
auto val = it.first;
|
||||
if (numeric_traits<mpq>::is_zero(val)) continue;
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
if (val.is_pos()) {
|
||||
std::cout << " + ";
|
||||
} else {
|
||||
std::cout << " - ";
|
||||
val = -val;
|
||||
}
|
||||
}
|
||||
|
||||
if (val != numeric_traits<mpq>::one())
|
||||
std::cout << val;
|
||||
std::cout << m_map_from_var_index_to_left_side[it.second]->m_column_info.get_name();
|
||||
}
|
||||
}
|
||||
|
||||
numeric_pair<mpq> lar_solver::get_infeasibility_from_core_solver(std::unordered_map<std::string, mpq> & solution) {
|
||||
prepare_independently_of_numeric_type();
|
||||
prepare_core_solver_fields(m_A, m_x, m_right_side_vector, m_low_bounds, m_upper_bounds);
|
||||
m_mpq_core_solver.prefix(); // just to fill the core solver
|
||||
|
||||
for (auto ls : m_canonic_left_sides) {
|
||||
lean_assert(valid_index(ls->m_column_index));
|
||||
m_x[ls->m_column_index] = numeric_pair<mpq>(get_canonic_left_side_val(ls, solution), 0);
|
||||
}
|
||||
return m_mpq_core_solver.get_deb_inf();
|
||||
}
|
||||
|
||||
void lar_solver::print_info_on_column(unsigned j) {
|
||||
for (auto ls : m_canonic_left_sides) {
|
||||
if (static_cast<unsigned>(ls->m_column_index) == j) {
|
||||
auto & ci = ls->m_column_info;
|
||||
if (ci.low_bound_is_set()) {
|
||||
std::cout << "l = " << ci.get_low_bound();
|
||||
}
|
||||
if (ci.upper_bound_is_set()) {
|
||||
std::cout << "u = " << ci.get_upper_bound();
|
||||
}
|
||||
std::cout << std::endl;
|
||||
m_mpq_core_solver.print_column_info(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
mpq lar_solver::get_infeasibility_of_solution(std::unordered_map<std::string, mpq> & solution) {
|
||||
std::cout << "solution" << std::endl;
|
||||
for (auto it : solution) {
|
||||
std::cout << it.first << " = " << it.second.get_double() << std::endl;
|
||||
}
|
||||
mpq ret = numeric_traits<mpq>::zero();
|
||||
for (auto it : m_normalized_constraints) {
|
||||
ret += get_infeasibility_of_constraint(it.second, solution);
|
||||
}
|
||||
std::cout << "ret = " << ret.get_double() << std::endl;
|
||||
auto core_inf = get_infeasibility_from_core_solver(solution);
|
||||
std::cout << "core inf = " << T_to_string(core_inf) << std::endl;
|
||||
lean_assert(numeric_pair<mpq>(ret, 0) == core_inf);
|
||||
return ret;
|
||||
}
|
||||
|
||||
mpq lar_solver::get_infeasibility_of_constraint(const lar_normalized_constraint & norm_constr, std::unordered_map<std::string, mpq> & solution) {
|
||||
auto kind = norm_constr.m_kind;
|
||||
mpq left_side_val = get_canonic_left_side_val(norm_constr.m_canonic_left_side, solution);
|
||||
|
||||
switch (kind) {
|
||||
case LT:
|
||||
case LE: return std::max(left_side_val - norm_constr.m_right_side, numeric_traits<mpq>::zero());
|
||||
case GT:
|
||||
case GE: return std::max(- (left_side_val - norm_constr.m_right_side), numeric_traits<mpq>::zero());
|
||||
|
||||
case EQ:
|
||||
return abs(left_side_val - norm_constr.m_right_side);
|
||||
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
mpq lar_solver::get_canonic_left_side_val(canonic_left_side * ls, std::unordered_map<std::string, mpq> & solution) {
|
||||
mpq ret = numeric_traits<mpq>::zero();
|
||||
for (auto it : ls->m_coeffs) {
|
||||
var_index j = it.second;
|
||||
auto vi = m_map_from_var_index_to_left_side.find(j);
|
||||
lean_assert(vi != m_map_from_var_index_to_left_side.end());
|
||||
canonic_left_side * var_ls = vi->second;
|
||||
std::string s = var_ls->m_column_info.get_name();
|
||||
auto t = solution.find(s);
|
||||
lean_assert(t != solution.end());
|
||||
ret += it.first * (t->second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
mpq lar_solver::get_left_side_val(const lar_constraint & cns, const std::unordered_map<var_index, mpq> & var_map) {
|
||||
mpq ret = numeric_traits<mpq>::zero();
|
||||
for (auto it : cns.m_left_side) {
|
||||
var_index j = it.first;
|
||||
auto vi = var_map.find(j);
|
||||
lean_assert(vi != var_map.end());
|
||||
ret += it.second * vi->second;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void lar_solver::print_constraint(const lar_base_constraint * c) {
|
||||
print_left_side_of_constraint(c);
|
||||
std::cout <<" " << lconstraint_kind_string(c->m_kind) << " " << c->m_right_side;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -20,15 +20,7 @@
|
|||
#include "util/lp/numeric_pair.h"
|
||||
#include "util/lp/lar_solution_signature.h"
|
||||
#include "util/lp/scaler.h"
|
||||
|
||||
namespace std {
|
||||
template<>
|
||||
struct hash<lean::mpq> {
|
||||
inline size_t operator()(const lean::mpq & v) const {
|
||||
return v.hash();
|
||||
}
|
||||
};
|
||||
}
|
||||
#include "util/lp/lp_primal_core_solver.h"
|
||||
|
||||
namespace lean {
|
||||
template <typename V>
|
||||
|
|
@ -44,24 +36,8 @@ struct conversion_helper {
|
|||
|
||||
template<>
|
||||
struct conversion_helper <double> {
|
||||
static double get_low_bound(const column_info<mpq> & ci) {
|
||||
if (!ci.low_bound_is_strict())
|
||||
return ci.get_low_bound().get_double();
|
||||
double eps = 0.00001;
|
||||
if (!ci.upper_bound_is_set())
|
||||
return ci.get_low_bound().get_double() + eps;
|
||||
eps = std::min((ci.get_upper_bound() - ci.get_low_bound()).get_double()/1000, eps);
|
||||
return ci.get_low_bound().get_double() + eps;
|
||||
}
|
||||
static double get_upper_bound(const column_info<mpq> & ci) {
|
||||
if (!ci.upper_bound_is_strict())
|
||||
return ci.get_upper_bound().get_double();
|
||||
double eps = 0.00001;
|
||||
if (!ci.low_bound_is_set())
|
||||
return ci.get_upper_bound().get_double() - eps;
|
||||
eps = std::min((ci.get_upper_bound() - ci.get_low_bound()).get_double()/1000, eps);
|
||||
return ci.get_upper_bound().get_double() - eps;
|
||||
}
|
||||
static double get_low_bound(const column_info<mpq> & ci);
|
||||
static double get_upper_bound(const column_info<mpq> & ci);
|
||||
};
|
||||
|
||||
class lar_solver {
|
||||
|
|
@ -85,93 +61,29 @@ class lar_solver {
|
|||
std::vector<numeric_pair<mpq>> m_right_side_vector; // this vector will be all zeroes, it might change when the optimization with fixed variables will used
|
||||
std::vector<mpq> m_costs;
|
||||
canonic_left_side * m_infeasible_canonic_left_side = nullptr; // such can be found at the initialization step
|
||||
canonic_left_side * create_or_fetch_existing_left_side(const buffer<pair<mpq, var_index>>& left_side_par) {
|
||||
auto left_side = new canonic_left_side(left_side_par);
|
||||
lean_assert(left_side->size() > 0);
|
||||
auto it = m_canonic_left_sides.find(left_side);
|
||||
if (it == m_canonic_left_sides.end()) {
|
||||
m_canonic_left_sides.insert(left_side);
|
||||
} else {
|
||||
delete left_side;
|
||||
left_side = *it;
|
||||
}
|
||||
return left_side;
|
||||
}
|
||||
canonic_left_side * create_or_fetch_existing_left_side(const buffer<pair<mpq, var_index>>& left_side_par);
|
||||
|
||||
|
||||
bool var_is_fixed(unsigned j) {
|
||||
auto it = m_map_from_var_index_to_left_side.find(j);
|
||||
lean_assert(it != m_map_from_var_index_to_left_side.end());
|
||||
return it->second->m_column_info.is_fixed();
|
||||
}
|
||||
bool var_is_fixed(unsigned j);
|
||||
|
||||
mpq find_ratio(canonic_left_side * ls, const lar_constraint & constraint) {
|
||||
lean_assert(ls->m_coeffs.size() > 0);
|
||||
auto first_pair = ls->m_coeffs[0];
|
||||
lean_assert(first_pair.first == numeric_traits<mpq>::one());
|
||||
var_index i = first_pair.second;
|
||||
auto it = constraint.m_left_side.find(i);
|
||||
lean_assert(it != constraint.m_left_side.end());
|
||||
return it->second;
|
||||
}
|
||||
mpq find_ratio(canonic_left_side * ls, const lar_constraint & constraint);
|
||||
|
||||
void add_canonic_left_side_for_var(var_index i, std::string var_name) {
|
||||
buffer<pair<mpq, var_index>> b;
|
||||
b.push_back(std::make_pair(numeric_traits<mpq>::one(), i));
|
||||
auto can_ls = new canonic_left_side(b);
|
||||
can_ls->set_name(var_name);
|
||||
void add_canonic_left_side_for_var(var_index i, std::string var_name);
|
||||
|
||||
lean_assert(m_canonic_left_sides.find(can_ls) == m_canonic_left_sides.end());
|
||||
m_canonic_left_sides.insert(can_ls);
|
||||
m_map_from_var_index_to_left_side[i] = can_ls;
|
||||
}
|
||||
|
||||
void map_left_side_to_column_of_A(canonic_left_side* left_side, unsigned & j) {
|
||||
lean_assert(m_column_indices_to_canonic_left_sides.find(j) == m_column_indices_to_canonic_left_sides.end());
|
||||
left_side->m_column_index = j; // assigning this index does not change the hash of canonic_left_side
|
||||
if (left_side->size() > 1) { // if size is one we will not create a row for this left side
|
||||
left_side->m_row_index = m_basis.size();
|
||||
m_basis.push_back(j); // j will be a basis column, so we put it into the basis as well
|
||||
}
|
||||
m_column_indices_to_canonic_left_sides[j++] = left_side; // pointing from the column to the left side
|
||||
}
|
||||
void map_left_side_to_column_of_A(canonic_left_side* left_side, unsigned & j);
|
||||
|
||||
|
||||
void map_left_sides_to_columns_of_A() {
|
||||
unsigned j = 0;
|
||||
for (auto it : m_canonic_left_sides)
|
||||
map_left_side_to_column_of_A(it, j);
|
||||
}
|
||||
void map_left_sides_to_columns_of_A();
|
||||
|
||||
bool valid_index(unsigned j) { return static_cast<int>(j) >= 0;}
|
||||
|
||||
|
||||
// this adds a row to A
|
||||
template <typename U, typename V>
|
||||
void add_row_to_A(static_matrix<U, V> & A, unsigned i, canonic_left_side * ls) {
|
||||
for (auto & t : ls->m_coeffs) {
|
||||
var_index vi = t.second;
|
||||
canonic_left_side *var_left_side = m_map_from_var_index_to_left_side[vi];
|
||||
unsigned column = var_left_side->m_column_index;
|
||||
lean_assert(valid_index(column));
|
||||
A.set(i, column, convert_struct<U, mpq>::convert(t.first));
|
||||
}
|
||||
A.set(i, ls->m_column_index, - one_of_type<U>());
|
||||
}
|
||||
void add_row_to_A(static_matrix<U, V> & A, unsigned i, canonic_left_side * ls);
|
||||
|
||||
template <typename U, typename V>
|
||||
void create_matrix_A(static_matrix<U, V> & A) {
|
||||
unsigned n = m_column_indices_to_canonic_left_sides.size();
|
||||
unsigned m = m_basis.size();
|
||||
A.init_empty_matrix(m, n);
|
||||
unsigned i = 0;
|
||||
for (auto & t : m_column_indices_to_canonic_left_sides)
|
||||
if (valid_index(t.second->m_row_index))
|
||||
add_row_to_A(A, i++, t.second);
|
||||
#ifdef LEAN_DEBUG
|
||||
// print_matrix(m_A);
|
||||
#endif
|
||||
}
|
||||
void create_matrix_A(static_matrix<U, V> & A);
|
||||
|
||||
// void fill_column_info_names() {
|
||||
// for (unsigned j = 0; j < m_A.column_count(); j++) {
|
||||
|
|
@ -186,175 +98,39 @@ class lar_solver {
|
|||
// m_column_names
|
||||
// }
|
||||
// }
|
||||
void set_upper_bound_for_column_info(lar_normalized_constraint * norm_constr) {
|
||||
const mpq & v = norm_constr->m_right_side;
|
||||
canonic_left_side * ls = norm_constr->m_canonic_left_side;
|
||||
column_info<mpq> & ci = ls->m_column_info;
|
||||
lean_assert(norm_constr->m_kind == LE || norm_constr->m_kind == LT || norm_constr->m_kind == EQ);
|
||||
bool strict = norm_constr->m_kind == LT;
|
||||
if (!ci.upper_bound_is_set()) {
|
||||
ls->m_upper_bound_witness = norm_constr;
|
||||
ci.set_upper_bound(v);
|
||||
ci.set_upper_bound_strict(strict);
|
||||
} else if (ci.get_upper_bound() > v) {
|
||||
ci.set_upper_bound(v);
|
||||
ls->m_upper_bound_witness = norm_constr;
|
||||
ci.set_upper_bound_strict(strict);
|
||||
}
|
||||
if (ci.is_infeasible()) {
|
||||
m_status= INFEASIBLE;
|
||||
m_infeasible_canonic_left_side = ls;
|
||||
return;
|
||||
}
|
||||
try_to_set_fixed(ci);
|
||||
}
|
||||
void set_upper_bound_for_column_info(lar_normalized_constraint * norm_constr);
|
||||
|
||||
bool try_to_set_fixed(column_info<mpq> & ci) {
|
||||
if (ci.upper_bound_is_set() && ci.low_bound_is_set() && ci.get_upper_bound() == ci.get_low_bound() && !ci.is_fixed()) {
|
||||
ci.set_fixed_value(ci.get_upper_bound());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool try_to_set_fixed(column_info<mpq> & ci);
|
||||
|
||||
void set_low_bound_for_column_info(lar_normalized_constraint * norm_constr) {
|
||||
const mpq & v = norm_constr->m_right_side;
|
||||
canonic_left_side * ls = norm_constr->m_canonic_left_side;
|
||||
column_info<mpq> & ci = ls->m_column_info;
|
||||
lean_assert(norm_constr->m_kind == GE || norm_constr->m_kind == GT || norm_constr->m_kind == EQ);
|
||||
bool strict = norm_constr->m_kind == GT;
|
||||
if (!ci.low_bound_is_set()) {
|
||||
ci.set_low_bound(v);
|
||||
ls->m_low_bound_witness = norm_constr;
|
||||
ci.set_low_bound_strict(strict);
|
||||
} else if (ci.get_low_bound() < v) {
|
||||
ci.set_low_bound(v);
|
||||
ls->m_low_bound_witness = norm_constr;
|
||||
ci.set_low_bound_strict(strict);
|
||||
}
|
||||
void set_low_bound_for_column_info(lar_normalized_constraint * norm_constr);
|
||||
|
||||
if (ci.is_infeasible()) {
|
||||
m_status= INFEASIBLE;
|
||||
m_infeasible_canonic_left_side = ls;
|
||||
return;
|
||||
}
|
||||
void update_column_info_of_normalized_constraint(lar_normalized_constraint & norm_constr);
|
||||
|
||||
try_to_set_fixed(ci);
|
||||
}
|
||||
column_type get_column_type(column_info<mpq> & ci);
|
||||
|
||||
void update_column_info_of_normalized_constraint(lar_normalized_constraint & norm_constr) {
|
||||
lean_assert(norm_constr.size() > 0);
|
||||
switch (norm_constr.m_kind) {
|
||||
case LE:
|
||||
case LT:
|
||||
set_upper_bound_for_column_info(&norm_constr);
|
||||
break;
|
||||
case GE:
|
||||
case GT:
|
||||
set_low_bound_for_column_info(&norm_constr);
|
||||
break;
|
||||
void fill_column_names();
|
||||
|
||||
case EQ:
|
||||
{
|
||||
set_upper_bound_for_column_info(&norm_constr);
|
||||
set_low_bound_for_column_info(&norm_constr);
|
||||
}
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
column_type get_column_type(column_info<mpq> & ci) {
|
||||
auto ret = ci.get_column_type_no_flipping();
|
||||
if (ret == boxed) { // changing boxed to fixed because of the no span
|
||||
if (ci.get_low_bound() == ci.get_upper_bound())
|
||||
ret = fixed;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fill_column_names() {
|
||||
m_column_names.clear();
|
||||
for (auto t : m_canonic_left_sides) {
|
||||
auto & ci = t->m_column_info;
|
||||
unsigned j = t->m_column_index;
|
||||
lean_assert(valid_index(j));
|
||||
std::string name = ci.get_name();
|
||||
if (name.size() == 0)
|
||||
name = std::string("_s") + T_to_string(j);
|
||||
m_column_names[j] = name;
|
||||
}
|
||||
}
|
||||
|
||||
void fill_column_types() {
|
||||
m_column_types.clear();
|
||||
m_column_types.resize(m_canonic_left_sides.size(), free_column);
|
||||
for (auto t : m_canonic_left_sides) {
|
||||
auto & ci = t->m_column_info;
|
||||
unsigned j = t->m_column_index;
|
||||
lean_assert(valid_index(j));
|
||||
m_column_types[j] = get_column_type(ci);
|
||||
}
|
||||
}
|
||||
void fill_column_types();
|
||||
|
||||
template <typename V>
|
||||
void fill_bounds_for_core_solver(std::vector<V> & lb, std::vector<V> & ub) {
|
||||
unsigned n = m_canonic_left_sides.size(); // this is the number of columns
|
||||
lb.resize(n);
|
||||
ub.resize(n);
|
||||
for (auto t : m_canonic_left_sides) {
|
||||
auto & ci = t->m_column_info;
|
||||
unsigned j = t->m_column_index;
|
||||
lean_assert(valid_index(j));
|
||||
if (ci.low_bound_is_set())
|
||||
lb[j] = conversion_helper<V>::get_low_bound(ci);
|
||||
if (ci.upper_bound_is_set())
|
||||
ub[j] = conversion_helper<V>::get_upper_bound(ci);
|
||||
}
|
||||
}
|
||||
void fill_bounds_for_core_solver(std::vector<V> & lb, std::vector<V> & ub);
|
||||
|
||||
|
||||
template <typename V>
|
||||
void resize_x_and_init_with_zeros(std::vector<V> & x, unsigned n) {
|
||||
x.clear();
|
||||
x.resize(n, zero_of_type<V>()); // init with zeroes
|
||||
}
|
||||
void resize_x_and_init_with_zeros(std::vector<V> & x, unsigned n);
|
||||
|
||||
template <typename V>
|
||||
void resize_x_and_init_with_signature(std::vector<V> & x, std::vector<V> & low_bound,
|
||||
std::vector<V> & upper_bound, const lar_solution_signature & signature) {
|
||||
x.clear();
|
||||
x.resize(low_bound.size());
|
||||
for (auto & t : signature.non_basic_column_value_positions) {
|
||||
x[t.first] = get_column_val(low_bound, upper_bound, t.second, t.first);
|
||||
}
|
||||
}
|
||||
std::vector<V> & upper_bound, const lar_solution_signature & signature);
|
||||
|
||||
template <typename V> V get_column_val(std::vector<V> & low_bound, std::vector<V> & upper_bound, non_basic_column_value_position pos_type, unsigned j) {
|
||||
switch (pos_type) {
|
||||
case at_low_bound: return low_bound[j];
|
||||
case at_fixed:
|
||||
case at_upper_bound: return upper_bound[j];
|
||||
case free_of_bounds: return zero_of_type<V>();
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
template <typename V> V get_column_val(std::vector<V> & low_bound, std::vector<V> & upper_bound, non_basic_column_value_position pos_type, unsigned j);
|
||||
|
||||
public:
|
||||
~lar_solver() {
|
||||
std::vector<canonic_left_side*> to_delete;
|
||||
for (auto it : m_canonic_left_sides)
|
||||
to_delete.push_back(it);
|
||||
for (auto t : to_delete)
|
||||
delete t;
|
||||
}
|
||||
~lar_solver();
|
||||
|
||||
lp_settings & settings() { return m_settings;}
|
||||
|
||||
void clear() {
|
||||
lean_assert(false); // not implemented
|
||||
void clear() {lean_assert(false); // not implemented
|
||||
}
|
||||
|
||||
lar_solver() : m_mpq_core_solver(m_x,
|
||||
|
|
@ -370,501 +146,96 @@ public:
|
|||
}
|
||||
|
||||
|
||||
var_index add_var(std::string s) {
|
||||
auto got = m_var_names_to_var_index.find(s);
|
||||
if (got != m_var_names_to_var_index.end()) return got->second;
|
||||
var_index add_var(std::string s);
|
||||
|
||||
var_index i = m_available_var_index++;
|
||||
m_var_names_to_var_index[s] = i;
|
||||
add_canonic_left_side_for_var(i, s);
|
||||
return i;
|
||||
}
|
||||
|
||||
constraint_index add_constraint(const buffer<pair<mpq, var_index>>& left_side, lconstraint_kind kind_par, mpq right_side_par) {
|
||||
if (left_side.size() == 0) {
|
||||
std::cout << "cannot add a constraint without left side" << std::endl;
|
||||
return (constraint_index)(-1);
|
||||
}
|
||||
constraint_index i = m_available_constr_index++;
|
||||
lar_constraint original_constr(left_side, kind_par, right_side_par, i);
|
||||
canonic_left_side * ls = create_or_fetch_existing_left_side(left_side);
|
||||
mpq ratio = find_ratio(ls, original_constr);
|
||||
auto kind = ratio.is_neg()? flip_kind(kind_par): kind_par;
|
||||
mpq right_side = right_side_par / ratio;
|
||||
lar_normalized_constraint normalized_constraint(ls, ratio, kind, right_side, original_constr);
|
||||
|
||||
m_normalized_constraints[i] = normalized_constraint;
|
||||
return i;
|
||||
}
|
||||
constraint_index add_constraint(const buffer<pair<mpq, var_index>>& left_side, lconstraint_kind kind_par, mpq right_side_par);
|
||||
|
||||
bool is_infeasible(const column_info<mpq> & ci) {
|
||||
return ci.low_bound_is_set() && ci.upper_bound_is_set() && ci.get_low_bound() > ci.get_upper_bound();
|
||||
}
|
||||
|
||||
bool all_constraints_hold() {
|
||||
std::unordered_map<var_index, mpq> var_map;
|
||||
get_model(var_map);
|
||||
for ( auto & it : m_normalized_constraints )
|
||||
if (!constraint_holds(it.second.m_origin_constraint, var_map)) {
|
||||
print_constraint(&it.second.m_origin_constraint);
|
||||
std::cout << std::endl;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool all_constraints_hold();
|
||||
|
||||
bool constraint_holds(const lar_constraint & constr, std::unordered_map<var_index, mpq> & var_map) {
|
||||
mpq left_side_val = get_left_side_val(constr, var_map);
|
||||
switch (constr.m_kind) {
|
||||
case LE: return left_side_val <= constr.m_right_side;
|
||||
case LT: return left_side_val <= constr.m_right_side;
|
||||
case GE: return left_side_val >= constr.m_right_side;
|
||||
case GT: return left_side_val >= constr.m_right_side;
|
||||
case EQ: return left_side_val == constr.m_right_side;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
bool constraint_holds(const lar_constraint & constr, std::unordered_map<var_index, mpq> & var_map);
|
||||
|
||||
lp_status get_status() const { return m_status;}
|
||||
|
||||
void solve_with_core_solver() {
|
||||
m_mpq_core_solver.solve();
|
||||
m_status = m_mpq_core_solver.m_status;
|
||||
lean_assert(m_status != OPTIMAL || all_constraints_hold());
|
||||
lean_assert(!settings().row_feasibility || m_status != INFEASIBLE || the_evidence_is_correct());
|
||||
}
|
||||
void solve_with_core_solver();
|
||||
|
||||
bool the_relations_are_of_same_type(const buffer<pair<mpq, unsigned>> & evidence, lconstraint_kind & the_kind_of_sum) {
|
||||
unsigned n_of_G = 0, n_of_L = 0;
|
||||
bool strict = false;
|
||||
for (auto & it : evidence) {
|
||||
mpq coeff = it.first;
|
||||
constraint_index con_ind = it.second;
|
||||
std::cout << coeff.get_double() << std::endl;
|
||||
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
|
||||
print_constraint(&constr); std::cout << std::endl;
|
||||
bool the_relations_are_of_same_type(const buffer<pair<mpq, unsigned>> & evidence, lconstraint_kind & the_kind_of_sum);
|
||||
|
||||
lconstraint_kind kind = coeff.is_pos()? constr.m_kind: flip_kind(constr.m_kind);
|
||||
if (kind == GT || kind == LT)
|
||||
strict = true;
|
||||
if (kind == GE || kind == GT) n_of_G++;
|
||||
else if (kind == LE || kind == LT) n_of_L++;
|
||||
}
|
||||
the_kind_of_sum = n_of_G? GE : (n_of_L? LE : EQ);
|
||||
if (strict)
|
||||
the_kind_of_sum = static_cast<lconstraint_kind>((static_cast<int>(the_kind_of_sum)/2));
|
||||
void register_in_map(std::unordered_map<var_index, mpq> & coeffs, lar_constraint & cn, const mpq & a);
|
||||
bool the_left_sides_sum_to_zero(const buffer<pair<mpq, unsigned>> & evidence);
|
||||
|
||||
return n_of_G == 0 || n_of_L == 0;
|
||||
}
|
||||
bool the_righ_sides_do_not_sum_to_zero(const buffer<pair<mpq, unsigned>> & evidence);
|
||||
|
||||
void register_in_map(std::unordered_map<var_index, mpq> & coeffs, lar_constraint & cn, const mpq & a) {
|
||||
for (auto & it : cn.m_left_side) {
|
||||
unsigned j = it.first;
|
||||
auto p = coeffs.find(j);
|
||||
if (p == coeffs.end()) coeffs[j] = it.second * a;
|
||||
else p->second += it.second * a;
|
||||
}
|
||||
}
|
||||
bool the_left_sides_sum_to_zero(const buffer<pair<mpq, unsigned>> & evidence) {
|
||||
std::unordered_map<var_index, mpq> coeff_map;
|
||||
for (auto & it : evidence) {
|
||||
mpq coeff = it.first;
|
||||
constraint_index con_ind = it.second;
|
||||
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
|
||||
register_in_map(coeff_map, constr, coeff);
|
||||
}
|
||||
for (auto & it : coeff_map) {
|
||||
if (!numeric_traits<mpq>::is_zero(it.second)) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool the_righ_sides_do_not_sum_to_zero(const buffer<pair<mpq, unsigned>> & evidence) {
|
||||
mpq ret = numeric_traits<mpq>::zero();
|
||||
for (auto & it : evidence) {
|
||||
mpq coeff = it.first;
|
||||
constraint_index con_ind = it.second;
|
||||
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
|
||||
ret += constr.m_right_side * coeff;
|
||||
}
|
||||
return !numeric_traits<mpq>::is_zero(ret);
|
||||
}
|
||||
|
||||
bool the_evidence_is_correct() {
|
||||
buffer<pair<mpq, unsigned>> evidence;
|
||||
get_infeasibility_evidence(evidence);
|
||||
lconstraint_kind kind;
|
||||
lean_assert(the_relations_are_of_same_type(evidence, kind));
|
||||
lean_assert(the_left_sides_sum_to_zero(evidence));
|
||||
mpq rs = sum_of_right_sides_of_evidence(evidence);
|
||||
switch (kind) {
|
||||
case LE: lean_assert(rs < zero_of_type<mpq>());
|
||||
break;
|
||||
case LT: lean_assert(rs <= zero_of_type<mpq>());
|
||||
break;
|
||||
case GE: lean_assert(rs > zero_of_type<mpq>());
|
||||
break;
|
||||
case GT: lean_assert(rs >= zero_of_type<mpq>());
|
||||
break;
|
||||
case EQ: lean_assert(rs != zero_of_type<mpq>());
|
||||
break;
|
||||
default:
|
||||
lean_assert(false);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void update_column_info_of_normalized_constraints() {
|
||||
for (auto & it : m_normalized_constraints)
|
||||
update_column_info_of_normalized_constraint(it.second);
|
||||
}
|
||||
bool the_evidence_is_correct();
|
||||
|
||||
void update_column_info_of_normalized_constraints();
|
||||
|
||||
template <typename V>
|
||||
void init_right_sides_with_zeros(std::vector<V> & rs) {
|
||||
rs.clear();
|
||||
rs.resize(m_basis.size(), zero_of_type<V>());
|
||||
}
|
||||
void init_right_sides_with_zeros(std::vector<V> & rs);
|
||||
|
||||
mpq sum_of_right_sides_of_evidence(const buffer<pair<mpq, unsigned>> & evidence) {
|
||||
mpq ret = numeric_traits<mpq>::zero();
|
||||
for (auto & it : evidence) {
|
||||
mpq coeff = it.first;
|
||||
constraint_index con_ind = it.second;
|
||||
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
|
||||
ret += constr.m_right_side * coeff;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void prepare_independently_of_numeric_type() {
|
||||
update_column_info_of_normalized_constraints();
|
||||
map_left_sides_to_columns_of_A();
|
||||
fill_column_names();
|
||||
fill_column_types();
|
||||
}
|
||||
mpq sum_of_right_sides_of_evidence(const buffer<pair<mpq, unsigned>> & evidence);
|
||||
void prepare_independently_of_numeric_type();
|
||||
|
||||
template <typename U, typename V>
|
||||
void prepare_core_solver_fields(static_matrix<U, V> & A, std::vector<V> & x,
|
||||
std::vector<V> & right_side_vector,
|
||||
std::vector<V> & low_bound,
|
||||
std::vector<V> & upper_bound) {
|
||||
create_matrix_A(A);
|
||||
fill_bounds_for_core_solver(low_bound, upper_bound);
|
||||
if (m_status == INFEASIBLE) {
|
||||
lean_assert(false); // not implemented
|
||||
}
|
||||
init_right_sides_with_zeros(right_side_vector);
|
||||
resize_x_and_init_with_zeros(x, A.column_count());
|
||||
lean_assert(m_basis.size() == A.row_count());
|
||||
}
|
||||
std::vector<V> & upper_bound);
|
||||
|
||||
template <typename U, typename V>
|
||||
void prepare_core_solver_fields_with_signature(static_matrix<U, V> & A, std::vector<V> & x,
|
||||
std::vector<V> & right_side_vector,
|
||||
std::vector<V> & low_bound,
|
||||
std::vector<V> & upper_bound, const lar_solution_signature & signature) {
|
||||
create_matrix_A(A);
|
||||
fill_bounds_for_core_solver(low_bound, upper_bound);
|
||||
if (m_status == INFEASIBLE) {
|
||||
lean_assert(false); // not implemented
|
||||
}
|
||||
init_right_sides_with_zeros(right_side_vector);
|
||||
resize_x_and_init_with_signature(x, low_bound, upper_bound, signature);
|
||||
}
|
||||
std::vector<V> & upper_bound, const lar_solution_signature & signature);
|
||||
|
||||
void find_solution_signature_with_doubles(lar_solution_signature & signature) {
|
||||
static_matrix<double, double> A;
|
||||
std::vector<double> x, right_side_vector, low_bounds, upper_bounds;
|
||||
prepare_core_solver_fields<double, double>(A, x, right_side_vector, low_bounds, upper_bounds);
|
||||
std::vector<double> column_scale_vector;
|
||||
scaler<double, double > scaler(right_side_vector, A, m_settings.scaling_minimum, m_settings.scaling_maximum, column_scale_vector, this->m_settings);
|
||||
if (!scaler.scale()) {
|
||||
// the scale did not succeed, unscaling
|
||||
A.clear();
|
||||
create_matrix_A(A);
|
||||
for (auto & s : column_scale_vector)
|
||||
s = one_of_type<double>();
|
||||
}
|
||||
std::vector<double> costs(A.column_count());
|
||||
auto core_solver = lp_primal_core_solver<double, double>(A,
|
||||
right_side_vector,
|
||||
x,
|
||||
m_basis,
|
||||
costs,
|
||||
m_column_types,
|
||||
low_bounds,
|
||||
upper_bounds,
|
||||
m_settings,
|
||||
m_column_names);
|
||||
core_solver.find_feasible_solution();
|
||||
extract_signature_from_lp_core_solver(core_solver, signature);
|
||||
}
|
||||
void find_solution_signature_with_doubles(lar_solution_signature & signature);
|
||||
|
||||
template <typename U, typename V>
|
||||
void extract_signature_from_lp_core_solver(lp_primal_core_solver<U, V> & core_solver, lar_solution_signature & signature) {
|
||||
for (auto j : core_solver.m_non_basic_columns)
|
||||
signature.non_basic_column_value_positions[j] = core_solver.get_non_basic_column_value_position(j);
|
||||
}
|
||||
void extract_signature_from_lp_core_solver(lp_primal_core_solver<U, V> & core_solver, lar_solution_signature & signature);
|
||||
|
||||
void solve_on_signature(const lar_solution_signature & signature) {
|
||||
prepare_core_solver_fields_with_signature(m_A, m_x, m_right_side_vector, m_low_bounds, m_upper_bounds, signature);
|
||||
solve_with_core_solver();
|
||||
}
|
||||
void solve_on_signature(const lar_solution_signature & signature);
|
||||
|
||||
void solve() {
|
||||
prepare_independently_of_numeric_type();
|
||||
if (m_settings.use_double_solver_for_lar) {
|
||||
lar_solution_signature solution_signature;
|
||||
find_solution_signature_with_doubles(solution_signature);
|
||||
// here the basis that is kept in m_basis is the same that was used in the double solver
|
||||
solve_on_signature(solution_signature);
|
||||
return;
|
||||
}
|
||||
prepare_core_solver_fields(m_A, m_x, m_right_side_vector, m_low_bounds, m_upper_bounds);
|
||||
solve_with_core_solver();
|
||||
}
|
||||
void solve();
|
||||
|
||||
lp_status check() {
|
||||
// for the time being just call solve()
|
||||
solve();
|
||||
return m_status;
|
||||
}
|
||||
void get_infeasibility_evidence(buffer<pair<mpq, constraint_index>> & evidence){
|
||||
if (!m_mpq_core_solver.get_infeasible_row_sign()) {
|
||||
std::cout << "don't have the infeasibility evidence" << std::endl;
|
||||
return;
|
||||
}
|
||||
// the infeasibility sign
|
||||
int inf_sign;
|
||||
auto inf_row = m_mpq_core_solver.get_infeasibility_info(inf_sign);
|
||||
lean_assert(inf_sign != 0);
|
||||
get_infeasibility_evidence_for_inf_sign(evidence, inf_row, inf_sign);
|
||||
}
|
||||
lp_status check();
|
||||
void get_infeasibility_evidence(buffer<pair<mpq, constraint_index>> & evidence);
|
||||
|
||||
void get_infeasibility_evidence_for_inf_sign(buffer<pair<mpq, constraint_index>> & evidence,
|
||||
const std::vector<pair<mpq, unsigned>> & inf_row,
|
||||
int inf_sign) {
|
||||
for (auto & it : inf_row) {
|
||||
mpq coeff = it.first;
|
||||
unsigned j = it.second;
|
||||
canonic_left_side * ls = m_column_indices_to_canonic_left_sides[j];
|
||||
int adj_sign = coeff.is_pos() ? inf_sign : -inf_sign;
|
||||
|
||||
lar_normalized_constraint * bound_constr = adj_sign < 0? ls->m_upper_bound_witness : ls->m_low_bound_witness;
|
||||
lean_assert(bound_constr != nullptr);
|
||||
evidence.push_back(std::make_pair(coeff / bound_constr->m_ratio_to_original, bound_constr->m_index));
|
||||
}
|
||||
}
|
||||
int inf_sign);
|
||||
|
||||
|
||||
mpq find_delta_for_strict_bounds() {
|
||||
mpq delta = numeric_traits<mpq>::one();
|
||||
for (auto t : m_canonic_left_sides) {
|
||||
auto & ci = t->m_column_info;
|
||||
unsigned j = t->m_column_index;
|
||||
lean_assert (valid_index(j));
|
||||
if (ci.low_bound_is_set())
|
||||
restrict_delta_on_low_bound_column(delta, j);
|
||||
if (ci.upper_bound_is_set())
|
||||
restrict_delta_on_upper_bound(delta, j);
|
||||
}
|
||||
return delta;
|
||||
}
|
||||
mpq find_delta_for_strict_bounds();
|
||||
|
||||
void restrict_delta_on_low_bound_column(mpq& delta, unsigned j) {
|
||||
numeric_pair<mpq> & x = m_x[j];
|
||||
numeric_pair<mpq> & l = m_low_bounds[j];
|
||||
mpq & xx = x.x;
|
||||
mpq & xy = x.y;
|
||||
mpq & lx = l.x;
|
||||
if (xx == lx) {
|
||||
lean_assert(xy >= numeric_traits<mpq>::zero());
|
||||
} else {
|
||||
lean_assert(xx >= lx); // we need lx <= xx + delta*xy, or delta*xy >= lx - xx, or - delta*xy <= xx - ls.
|
||||
// The right part is not negative. The delta is positive. If xy >= 0 we have the ineqality
|
||||
// otherwise we need to have delta not greater than - (xx - lx)/xy. We use the 2 coefficient to handle the strict case
|
||||
if (xy >= zero_of_type<mpq>()) return;
|
||||
delta = std::min(delta, (lx - xx)/ (2 * xy)); // we need to have delta * xy < xx - lx for the strict case
|
||||
}
|
||||
}
|
||||
void restrict_delta_on_upper_bound(mpq& delta, unsigned j) {
|
||||
numeric_pair<mpq> & x = m_x[j];
|
||||
numeric_pair<mpq> & u = m_upper_bounds[j];
|
||||
mpq & xx = x.x;
|
||||
mpq & xy = x.y;
|
||||
mpq & ux = u.x;
|
||||
if (xx == ux) {
|
||||
lean_assert(xy <= numeric_traits<mpq>::zero());
|
||||
} else {
|
||||
lean_assert(xx < ux);
|
||||
if (xy <= zero_of_type<mpq>()) return;
|
||||
delta = std::min(delta, (ux - xx)/ (2 * xy)); // we need to have delta * xy < ux - xx, for the strict case
|
||||
}
|
||||
}
|
||||
void restrict_delta_on_low_bound_column(mpq& delta, unsigned j);
|
||||
void restrict_delta_on_upper_bound(mpq& delta, unsigned j);
|
||||
|
||||
void get_model(std::unordered_map<var_index, mpq> & variable_values){
|
||||
lean_assert(m_status == OPTIMAL);
|
||||
mpq delta = find_delta_for_strict_bounds();
|
||||
for (auto & it : m_map_from_var_index_to_left_side) {
|
||||
numeric_pair<mpq> & rp = m_x[it.second->m_column_index];
|
||||
// std::cout << it.second->m_column_info.get_name() << " = " << rp << std::endl;
|
||||
variable_values[it.first] = rp.x + delta * rp.y;
|
||||
}
|
||||
}
|
||||
void get_model(std::unordered_map<var_index, mpq> & variable_values);
|
||||
|
||||
std::string get_variable_name(var_index vi) {
|
||||
if (m_map_from_var_index_to_left_side.size() <= vi) {
|
||||
std::string s = "variable " + T_to_string(vi) + " is not found";
|
||||
return s;
|
||||
}
|
||||
return m_map_from_var_index_to_left_side[vi]->m_column_info.get_name();
|
||||
}
|
||||
std::string get_variable_name(var_index vi);
|
||||
|
||||
// ********** print region start
|
||||
void print_constraint(constraint_index ci) {
|
||||
if (m_normalized_constraints.size() <= ci) {
|
||||
std::string s = "constraint " + T_to_string(ci) + " is not found";
|
||||
std::cout << s << std::endl;
|
||||
return;
|
||||
}
|
||||
void print_constraint(constraint_index ci);
|
||||
|
||||
print_constraint(&m_normalized_constraints[ci]);
|
||||
}
|
||||
void print_canonic_left_side(const canonic_left_side & c);
|
||||
|
||||
void print_canonic_left_side(const canonic_left_side & c) {
|
||||
bool first = true;
|
||||
for (auto it : c.m_coeffs) {
|
||||
auto val = it.first;
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
if (val.is_pos()) {
|
||||
std::cout << " + ";
|
||||
} else {
|
||||
std::cout << " - ";
|
||||
val = -val;
|
||||
}
|
||||
}
|
||||
if (val != numeric_traits<mpq>::one())
|
||||
std::cout << T_to_string(val);
|
||||
std::cout << m_map_from_var_index_to_left_side[it.second]->m_column_info.get_name();
|
||||
}
|
||||
}
|
||||
void print_left_side_of_constraint(const lar_base_constraint * c);
|
||||
|
||||
void print_left_side_of_constraint(const lar_base_constraint * c) {
|
||||
bool first = true;
|
||||
for (auto it : c->get_left_side_coefficients()) {
|
||||
auto val = it.first;
|
||||
if (numeric_traits<mpq>::is_zero(val)) continue;
|
||||
if (first) {
|
||||
first = false;
|
||||
} else {
|
||||
if (val.is_pos()) {
|
||||
std::cout << " + ";
|
||||
} else {
|
||||
std::cout << " - ";
|
||||
val = -val;
|
||||
}
|
||||
}
|
||||
numeric_pair<mpq> get_infeasibility_from_core_solver(std::unordered_map<std::string, mpq> & solution);
|
||||
|
||||
if (val != numeric_traits<mpq>::one())
|
||||
std::cout << val;
|
||||
std::cout << m_map_from_var_index_to_left_side[it.second]->m_column_info.get_name();
|
||||
}
|
||||
}
|
||||
void print_info_on_column(unsigned j);
|
||||
|
||||
numeric_pair<mpq> get_infeasibility_from_core_solver(std::unordered_map<std::string, mpq> & solution) {
|
||||
prepare_independently_of_numeric_type();
|
||||
prepare_core_solver_fields(m_A, m_x, m_right_side_vector, m_low_bounds, m_upper_bounds);
|
||||
m_mpq_core_solver.prefix(); // just to fill the core solver
|
||||
mpq get_infeasibility_of_solution(std::unordered_map<std::string, mpq> & solution);
|
||||
|
||||
for (auto ls : m_canonic_left_sides) {
|
||||
lean_assert(valid_index(ls->m_column_index));
|
||||
m_x[ls->m_column_index] = numeric_pair<mpq>(get_canonic_left_side_val(ls, solution), 0);
|
||||
}
|
||||
return m_mpq_core_solver.get_deb_inf();
|
||||
}
|
||||
mpq get_infeasibility_of_constraint(const lar_normalized_constraint & norm_constr, std::unordered_map<std::string, mpq> & solution);
|
||||
|
||||
void print_info_on_column(unsigned j) {
|
||||
for (auto ls : m_canonic_left_sides) {
|
||||
if (static_cast<unsigned>(ls->m_column_index) == j) {
|
||||
auto & ci = ls->m_column_info;
|
||||
if (ci.low_bound_is_set()) {
|
||||
std::cout << "l = " << ci.get_low_bound();
|
||||
}
|
||||
if (ci.upper_bound_is_set()) {
|
||||
std::cout << "u = " << ci.get_upper_bound();
|
||||
}
|
||||
std::cout << std::endl;
|
||||
m_mpq_core_solver.print_column_info(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
mpq get_canonic_left_side_val(canonic_left_side * ls, std::unordered_map<std::string, mpq> & solution);
|
||||
|
||||
mpq get_infeasibility_of_solution(std::unordered_map<std::string, mpq> & solution) {
|
||||
std::cout << "solution" << std::endl;
|
||||
for (auto it : solution) {
|
||||
std::cout << it.first << " = " << it.second.get_double() << std::endl;
|
||||
}
|
||||
mpq ret = numeric_traits<mpq>::zero();
|
||||
for (auto it : m_normalized_constraints) {
|
||||
ret += get_infeasibility_of_constraint(it.second, solution);
|
||||
}
|
||||
std::cout << "ret = " << ret.get_double() << std::endl;
|
||||
auto core_inf = get_infeasibility_from_core_solver(solution);
|
||||
std::cout << "core inf = " << T_to_string(core_inf) << std::endl;
|
||||
lean_assert(numeric_pair<mpq>(ret, 0) == core_inf);
|
||||
return ret;
|
||||
}
|
||||
mpq get_left_side_val(const lar_constraint & cns, const std::unordered_map<var_index, mpq> & var_map);
|
||||
|
||||
mpq get_infeasibility_of_constraint(const lar_normalized_constraint & norm_constr, std::unordered_map<std::string, mpq> & solution) {
|
||||
auto kind = norm_constr.m_kind;
|
||||
mpq left_side_val = get_canonic_left_side_val(norm_constr.m_canonic_left_side, solution);
|
||||
|
||||
switch (kind) {
|
||||
case LT:
|
||||
case LE: return std::max(left_side_val - norm_constr.m_right_side, numeric_traits<mpq>::zero());
|
||||
case GT:
|
||||
case GE: return std::max(- (left_side_val - norm_constr.m_right_side), numeric_traits<mpq>::zero());
|
||||
|
||||
case EQ:
|
||||
return abs(left_side_val - norm_constr.m_right_side);
|
||||
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
mpq get_canonic_left_side_val(canonic_left_side * ls, std::unordered_map<std::string, mpq> & solution) {
|
||||
mpq ret = numeric_traits<mpq>::zero();
|
||||
for (auto it : ls->m_coeffs) {
|
||||
var_index j = it.second;
|
||||
auto vi = m_map_from_var_index_to_left_side.find(j);
|
||||
lean_assert(vi != m_map_from_var_index_to_left_side.end());
|
||||
canonic_left_side * var_ls = vi->second;
|
||||
std::string s = var_ls->m_column_info.get_name();
|
||||
auto t = solution.find(s);
|
||||
lean_assert(t != solution.end());
|
||||
ret += it.first * (t->second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
mpq get_left_side_val(const lar_constraint & cns, const std::unordered_map<var_index, mpq> & var_map) {
|
||||
mpq ret = numeric_traits<mpq>::zero();
|
||||
for (auto it : cns.m_left_side) {
|
||||
var_index j = it.first;
|
||||
auto vi = var_map.find(j);
|
||||
lean_assert(vi != var_map.end());
|
||||
ret += it.second * vi->second;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void print_constraint(const lar_base_constraint * c) {
|
||||
print_left_side_of_constraint(c);
|
||||
std::cout <<" " << lconstraint_kind_string(c->m_kind) << " " << c->m_right_side;
|
||||
}
|
||||
void print_constraint(const lar_base_constraint * c);
|
||||
unsigned get_total_iterations() const { return m_mpq_core_solver.m_total_iterations; }
|
||||
};
|
||||
}
|
||||
|
|
|
|||
8
src/util/lp/lar_solver_instances.cpp
Normal file
8
src/util/lp/lar_solver_instances.cpp
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#
|
||||
#include "util/lp/lar_solver.cpp"
|
||||
|
|
@ -1,13 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "util/debug.h"
|
||||
#include "util/numerics/numeric_traits.h"
|
||||
#include "util/numerics/xnumeral.h"
|
||||
#include "util/lp/lp_primal_core_solver.h"
|
||||
695
src/util/lp/lp_core_solver_base.cpp
Normal file
695
src/util/lp/lp_core_solver_base.cpp
Normal file
|
|
@ -0,0 +1,695 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_core_solver_base.h"
|
||||
namespace lean {
|
||||
void init_basic_part_of_basis_heading(std::vector<unsigned> & basis, unsigned m, std::vector<int> & basis_heading) {
|
||||
for (unsigned i = 0; i < m; i++) {
|
||||
unsigned column = basis[i];
|
||||
basis_heading[column] = i;
|
||||
}
|
||||
}
|
||||
|
||||
void init_non_basic_part_of_basis_heading(std::vector<int> & basis_heading, std::vector<unsigned> & non_basic_columns, unsigned n) {
|
||||
for (int j = n; j--;){
|
||||
if (basis_heading[j] < 0) {
|
||||
non_basic_columns.push_back(j);
|
||||
// the index of column j in m_non_basic_columns is (- basis_heading[j] - 1)
|
||||
basis_heading[j] = - non_basic_columns.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
void init_basis_heading_and_non_basic_columns_vector(std::vector<unsigned> & basis,
|
||||
unsigned m,
|
||||
std::vector<int> & basis_heading,
|
||||
unsigned n,
|
||||
std::vector<unsigned> & non_basic_columns) {
|
||||
init_basic_part_of_basis_heading(basis, m, basis_heading);
|
||||
init_non_basic_part_of_basis_heading(basis_heading, non_basic_columns, n);
|
||||
}
|
||||
|
||||
template <typename T, typename X> lp_core_solver_base<T, X>::
|
||||
lp_core_solver_base(static_matrix<T, X> & A,
|
||||
std::vector<X> & b, // the right side vector
|
||||
std::vector<unsigned> & basis,
|
||||
std::vector<X> & x,
|
||||
std::vector<T> & costs,
|
||||
lp_settings & settings,
|
||||
const std::unordered_map<unsigned, std::string> & column_names,
|
||||
std::vector<column_type> & column_types,
|
||||
std::vector<X> & low_bound_values,
|
||||
std::vector<X> & upper_bound_values):
|
||||
m_m(A.row_count()),
|
||||
m_n(A.column_count()),
|
||||
m_pivot_row_of_B_1(m_m),
|
||||
m_pivot_row(m_n, zero_of_type<T>()),
|
||||
m_A(A),
|
||||
m_b(b),
|
||||
m_basis(basis),
|
||||
m_x(x),
|
||||
m_costs(costs),
|
||||
m_settings(settings),
|
||||
m_y(m_m),
|
||||
m_status(FEASIBLE),
|
||||
m_factorization(nullptr),
|
||||
m_column_names(column_names),
|
||||
m_w(m_m),
|
||||
m_d(m_n),
|
||||
m_ed(m_m),
|
||||
m_column_type(column_types),
|
||||
m_low_bound_values(low_bound_values),
|
||||
m_upper_bound_values(upper_bound_values),
|
||||
m_column_norms(m_n, T(1)),
|
||||
m_copy_of_xB(m_m) {
|
||||
if (m_m) {
|
||||
lean_assert(m_A.col_val_equal_to_row_val());
|
||||
init();
|
||||
init_basis_heading();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
allocate_basis_heading() { // the rest of initilization will be handled by the factorization class
|
||||
m_basis_heading.clear();
|
||||
m_basis_heading.resize(m_n, -1);
|
||||
}
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
init() {
|
||||
lean_assert(m_costs.size() == m_n);
|
||||
lean_assert(m_basis.size() == m_m);
|
||||
lean_assert(m_b.size() == m_m);
|
||||
allocate_basis_heading();
|
||||
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
|
||||
m_refactor_counter = 0;
|
||||
unsigned seed = 1;
|
||||
my_random_init(&seed);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
fill_cb(T * y){
|
||||
for (unsigned i = 0; i < m_m; i++) {
|
||||
y[i] = m_costs[m_basis[i]];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
fill_cb(std::vector<T> & y){
|
||||
for (unsigned i = 0; i < m_m; i++)
|
||||
y[i] = m_costs[m_basis[i]];
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
solve_yB(std::vector<T> & y) {
|
||||
fill_cb(y); // now y = cB, that is the projection of costs to basis
|
||||
m_factorization->solve_yB(y);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
update_index_of_ed() {
|
||||
m_index_of_ed.clear();
|
||||
unsigned i = m_ed.size();
|
||||
while (i--) {
|
||||
if (!is_zero(m_ed[i]))
|
||||
m_index_of_ed.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
solve_Bd(unsigned entering) {
|
||||
m_factorization->solve_Bd(entering, m_ed, m_w);
|
||||
update_index_of_ed();
|
||||
#ifdef LEAN_DEBUG
|
||||
// auto B = get_B(m_factorization);
|
||||
// vector<T> a(m_m);
|
||||
// m_A.copy_column_to_vector(entering, a);
|
||||
// vector<T> cd(m_ed);
|
||||
// B.apply_from_left(cd, m_settings);
|
||||
// lean_assert(vectors_are_equal(cd , a));
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
pretty_print() {
|
||||
core_solver_pretty_printer<T, X> pp(*this);
|
||||
pp.print();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
save_state(T * w_buffer, T * d_buffer) {
|
||||
copy_m_w(w_buffer);
|
||||
copy_m_ed(d_buffer);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
restore_state(T * w_buffer, T * d_buffer) {
|
||||
restore_m_w(w_buffer);
|
||||
restore_m_ed(d_buffer);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
copy_m_w(T * buffer) {
|
||||
unsigned i = m_m;
|
||||
while (i --) {
|
||||
buffer[i] = m_w[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
restore_m_w(T * buffer) {
|
||||
m_w.m_index.clear();
|
||||
unsigned i = m_m;
|
||||
while (i--) {
|
||||
if (!is_zero(m_w[i] = buffer[i]))
|
||||
m_w.m_index.push_back(i);
|
||||
}
|
||||
}
|
||||
|
||||
// needed for debugging
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
copy_m_ed(T * buffer) {
|
||||
unsigned i = m_m;
|
||||
while (i --) {
|
||||
buffer[i] = m_ed[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
restore_m_ed(T * buffer) {
|
||||
unsigned i = m_m;
|
||||
while (i --) {
|
||||
m_ed[i] = buffer[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
A_mult_x_is_off() {
|
||||
if (precise<T>()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
T feps = convert_struct<T, double>::convert(m_settings.refactor_tolerance);
|
||||
X one = convert_struct<X, double>::convert(1.0);
|
||||
for (unsigned i = 0; i < m_m; i++) {
|
||||
X delta = abs(m_b[i] - m_A.dot_product_with_row(i, m_x));
|
||||
X eps = feps * (one + T(0.1) * abs(m_b[i]));
|
||||
|
||||
if (delta >eps) {
|
||||
std::cout << "x is off (";
|
||||
std::cout << "m_b[" << i << "] = " << m_b[i] << " ";
|
||||
std::cout << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ';
|
||||
std::cout << "delta = " << delta << ' ';
|
||||
std::cout << "iters = " << m_total_iterations << ")" << std::endl;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
// from page 182 of Istvan Maros's book
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
calculate_pivot_row_of_B_1(unsigned pivot_row) {
|
||||
unsigned i = m_m;
|
||||
while (i--) {
|
||||
m_pivot_row_of_B_1[i] = numeric_traits<T>::zero();
|
||||
}
|
||||
m_pivot_row_of_B_1[pivot_row] = numeric_traits<T>::one();
|
||||
m_factorization->solve_yB(m_pivot_row_of_B_1);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
zero_pivot_row() {
|
||||
for (unsigned j : m_pivot_row_index)
|
||||
m_pivot_row[j] = numeric_traits<T>::zero();
|
||||
m_pivot_row_index.clear();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
calculate_pivot_row_when_pivot_row_of_B1_is_ready() {
|
||||
zero_pivot_row();
|
||||
int i = m_m;
|
||||
while (i--) {
|
||||
T pi_1 = m_pivot_row_of_B_1[i];
|
||||
if (numeric_traits<T>::is_zero(pi_1)) {
|
||||
continue;
|
||||
}
|
||||
for (auto & c : m_A.m_rows[i]) {
|
||||
unsigned j = c.m_j;
|
||||
if (m_factorization->m_basis_heading[j] < 0) {
|
||||
m_pivot_row[j] += c.get_val() * pi_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
unsigned j = m_pivot_row.size();
|
||||
while (j--) {
|
||||
if (!is_zero(m_pivot_row[j]))
|
||||
m_pivot_row_index.push_back(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
update_x(unsigned entering, X delta) {
|
||||
if (is_zero(delta)) {
|
||||
return;
|
||||
}
|
||||
m_x[entering] += delta;
|
||||
for (unsigned i : m_index_of_ed) {
|
||||
m_copy_of_xB[i] = m_x[m_basis[i]];
|
||||
m_x[m_basis[i]] -= delta * m_ed[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
print_statistics(X cost) {
|
||||
std::cout << "cost = " << T_to_string(cost) <<
|
||||
", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl;
|
||||
}
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
print_statistics_with_iterations_and_check_that_the_time_is_over(unsigned total_iterations) {
|
||||
if (total_iterations % m_settings.report_frequency == 0) {
|
||||
std::cout << "iterations = " << total_iterations << ", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl;
|
||||
if (time_is_over()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string str, unsigned total_iterations) {
|
||||
if (total_iterations % m_settings.report_frequency == 0) {
|
||||
std::cout << str << " iterations = " << total_iterations << " cost = " << T_to_string(get_cost()) <<", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl;
|
||||
if (time_is_over()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
print_statistics_with_cost_and_check_that_the_time_is_over(unsigned total_iterations, X cost) {
|
||||
if (total_iterations % m_settings.report_frequency == 0) {
|
||||
std::cout << "iterations = " << total_iterations << ", ";
|
||||
print_statistics(cost);
|
||||
if (time_is_over()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
print_statistics_and_check_that_the_time_is_over(unsigned total_iterations) {
|
||||
if (total_iterations % (numeric_traits<T>::precise()? static_cast<unsigned>(m_settings.report_frequency/10) : m_settings.report_frequency) == 0) {
|
||||
std::cout << "iterations = " << total_iterations << ", ";
|
||||
if (time_is_over()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
set_non_basic_x_to_correct_bounds() {
|
||||
for (unsigned j : non_basis()) {
|
||||
switch (m_column_type[j]) {
|
||||
case boxed:
|
||||
m_x[j] = m_d[j] < 0? m_upper_bound_values[j]: m_low_bound_values[j];
|
||||
break;
|
||||
case low_bound:
|
||||
m_x[j] = m_low_bound_values[j];
|
||||
lean_assert(column_is_dual_feasible(j));
|
||||
break;
|
||||
case upper_bound:
|
||||
m_x[j] = m_upper_bound_values[j];
|
||||
lean_assert(column_is_dual_feasible(j));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
column_is_dual_feasible(unsigned j) {
|
||||
switch (m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
return (x_is_at_low_bound(j) && d_is_not_negative(j)) ||
|
||||
(x_is_at_upper_bound(j) && d_is_not_positive(j));
|
||||
case low_bound:
|
||||
return x_is_at_low_bound(j) && d_is_not_negative(j);
|
||||
case upper_bound:
|
||||
std::cout << "upper_bound type should be switched to low_bound" << std::endl;
|
||||
lean_assert(false); // impossible case
|
||||
case free_column:
|
||||
return numeric_traits<X>::is_zero(m_d[j]);
|
||||
default:
|
||||
std::cout << "column = " << j << std::endl;
|
||||
std::cout << "unexpected column type = " << column_type_to_string(m_column_type[j]) << std::endl;
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
d_is_not_negative(unsigned j) {
|
||||
if (numeric_traits<T>::precise()) {
|
||||
return m_d[j] >= numeric_traits<T>::zero();
|
||||
}
|
||||
return m_d[j] > -T(0.00001);
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
d_is_not_positive(unsigned j) {
|
||||
if (numeric_traits<T>::precise()) {
|
||||
return m_d[j] <= numeric_traits<T>::zero();
|
||||
}
|
||||
return m_d[j] < T(0.00001);
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
time_is_over() {
|
||||
int span_in_mills = get_millisecond_span(m_start_time);
|
||||
if (span_in_mills / 1000.0 > m_settings.time_limit) {
|
||||
std::cout << "time is over" << std::endl;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
rs_minus_Anx(std::vector<X> & rs) {
|
||||
unsigned row = m_m;
|
||||
while (row--) {
|
||||
auto &rsv = rs[row] = m_b[row];
|
||||
for (auto & it : m_A.m_rows[row]) {
|
||||
unsigned j = it.m_j;
|
||||
if (m_basis_heading[j] < 0) {
|
||||
rsv -= m_x[j] * it.get_val();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
find_x_by_solving() {
|
||||
solve_Ax_eq_b();
|
||||
bool ret= !A_mult_x_is_off();
|
||||
if (ret)
|
||||
std::cout << "find_x_by_solving succeeded" << std::endl;
|
||||
else
|
||||
std::cout << "find_x_by_solving did not succeed" << std::endl;
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
update_basis_and_x(int entering, int leaving, X const & tt) {
|
||||
if (!is_zero(tt)) {
|
||||
update_x(entering, tt);
|
||||
if (A_mult_x_is_off() && !find_x_by_solving()) {
|
||||
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
|
||||
m_refactor_counter = 0;
|
||||
|
||||
if (!find_x_by_solving()) {
|
||||
restore_x(entering, tt);
|
||||
lean_assert(!A_mult_x_is_off());
|
||||
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
|
||||
m_refactor_counter = 0;
|
||||
m_iters_with_no_cost_growing++;
|
||||
if (m_factorization->get_status() != LU_status::OK) {
|
||||
throw exception(sstream() << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << m_total_iterations);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool refactor = m_refactor_counter++ >= 200;
|
||||
if (!refactor) {
|
||||
const T & pivot = this->m_pivot_row[entering]; // m_ed[m_factorization->basis_heading(leaving)] is the same but the one that we are using is more precise
|
||||
m_factorization->replace_column(leaving, pivot, m_w);
|
||||
if (m_factorization->get_status() == LU_status::OK) {
|
||||
m_factorization->change_basis(entering, leaving);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// need to refactor
|
||||
m_refactor_counter = 0;
|
||||
m_factorization->change_basis(entering, leaving);
|
||||
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
|
||||
if (m_factorization->get_status() != LU_status::OK || A_mult_x_is_off()) {
|
||||
std::cout << "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << m_total_iterations << std::endl;
|
||||
restore_x_and_refactor(entering, leaving, tt);
|
||||
lean_assert(!A_mult_x_is_off());
|
||||
m_iters_with_no_cost_growing++;
|
||||
std::cout << "rolled back after failing of init_factorization()" << std::endl;
|
||||
m_status = UNSTABLE;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
init_basis_heading() {
|
||||
init_basis_heading_and_non_basic_columns_vector(m_basis, m_m, m_basis_heading, m_n, m_non_basic_columns);
|
||||
lean_assert(basis_heading_is_correct());
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
basis_has_no_doubles() {
|
||||
std::set<unsigned> bm;
|
||||
for (unsigned i = 0; i < m_m; i++) {
|
||||
bm.insert(m_basis[i]);
|
||||
}
|
||||
return bm.size() == m_m;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
non_basis_has_no_doubles() {
|
||||
std::set<int> bm;
|
||||
for (auto j : m_non_basic_columns) {
|
||||
bm.insert(j);
|
||||
}
|
||||
return bm.size() == m_non_basic_columns.size();
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
basis_is_correctly_represented_in_heading() {
|
||||
for (unsigned i = 0; i < m_m; i++) {
|
||||
if (m_basis_heading[m_basis[i]] != i)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
non_basis_is_correctly_represented_in_heading() {
|
||||
for (int i = 0; i < m_non_basic_columns.size(); i++) {
|
||||
if (m_basis_heading[m_non_basic_columns[i]] != - i - 1)
|
||||
return false;
|
||||
}
|
||||
for (int j = 0; j < m_A.column_count(); j++) {
|
||||
if (m_basis_heading[j] >= 0) {
|
||||
lean_assert(m_basis_heading[j] < m_A.row_count() && m_basis[m_basis_heading[j]] == j);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_core_solver_base<T, X>::
|
||||
basis_heading_is_correct() {
|
||||
return basis_has_no_doubles() && non_basis_has_no_doubles() && basis_is_correctly_represented_in_heading() && non_basis_is_correctly_represented_in_heading();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
restore_x_and_refactor(int entering, int leaving, X const & t) {
|
||||
m_factorization->restore_basis_change(entering, leaving);
|
||||
restore_x(entering, t);
|
||||
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
|
||||
if (m_factorization->get_status() == LU_status::Degenerated) {
|
||||
std::cout << "cannot refactor" << std::endl;
|
||||
m_status = lp_status::FLOATING_POINT_ERROR;
|
||||
}
|
||||
// solve_Ax_eq_b();
|
||||
if (A_mult_x_is_off()) {
|
||||
std::cout << "cannot restore solution" << std::endl;
|
||||
m_status = lp_status::FLOATING_POINT_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
restore_x(unsigned entering, X const & t) {
|
||||
if (is_zero(t)) return;
|
||||
std::cout << "calling restore for entering " << entering << std::endl;
|
||||
m_x[entering] -= t;
|
||||
for (unsigned i : m_index_of_ed) {
|
||||
m_x[m_basis[i]] = m_copy_of_xB[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
fill_reduced_costs_from_m_y_by_rows() {
|
||||
unsigned j = m_n;
|
||||
while (j--) {
|
||||
if (m_factorization->m_basis_heading[j] < 0)
|
||||
m_d[j] = m_costs[j];
|
||||
else
|
||||
m_d[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
|
||||
unsigned i = m_m;
|
||||
while (i--) {
|
||||
const T & y = m_y[i];
|
||||
if (is_zero(y)) continue;
|
||||
for (auto & it : m_A.m_rows[i]) {
|
||||
j = it.m_j;
|
||||
if (m_factorization->m_basis_heading[j] < 0)
|
||||
m_d[j] -= y * it.get_val();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
copy_rs_to_xB(std::vector<X> & rs) {
|
||||
unsigned j = m_m;
|
||||
while (j--) {
|
||||
m_x[m_basis[j]] = rs[j];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> std::string lp_core_solver_base<T, X>::
|
||||
column_name(unsigned column) const {
|
||||
auto it = m_column_names.find(column);
|
||||
if (it == m_column_names.end()) {
|
||||
std::string name = T_to_string(column);
|
||||
return std::string(std::string("u") + name);
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
copy_right_side(std::vector<X> & rs) {
|
||||
unsigned i = m_m;
|
||||
while (i --) {
|
||||
rs[i] = m_b[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
add_delta_to_xB(std::vector<X> & del) {
|
||||
unsigned i = m_m;
|
||||
while (i--) {
|
||||
this->m_x[this->m_basis[i]] -= del[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
find_error_in_BxB(std::vector<X>& rs){
|
||||
unsigned row = m_m;
|
||||
while (row--) {
|
||||
auto &rsv = rs[row];
|
||||
for (auto & it : m_A.m_rows[row]) {
|
||||
unsigned j = it.m_j;
|
||||
if (m_basis_heading[j] >= 0) {
|
||||
rsv -= m_x[j] * it.get_val();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// recalculates the projection of x to B, such that Ax = b, whereab is the right side
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
solve_Ax_eq_b() {
|
||||
std::vector<X> rs(m_m);
|
||||
rs_minus_Anx(rs);
|
||||
std::vector<X> rrs = rs; // another copy of rs
|
||||
m_factorization->solve_By(rs);
|
||||
copy_rs_to_xB(rs);
|
||||
find_error_in_BxB(rrs);
|
||||
m_factorization->solve_By(rrs);
|
||||
add_delta_to_xB(rrs);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
snap_non_basic_x_to_bound() {
|
||||
for (unsigned j : non_basis()) {
|
||||
switch (m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
if (x_is_at_bound(j))
|
||||
break; // we should preserve x if possible
|
||||
m_x[j] = m_low_bound_values[j];
|
||||
break;
|
||||
case low_bound:
|
||||
if (x_is_at_low_bound(j))
|
||||
break;
|
||||
m_x[j] = m_low_bound_values[j];
|
||||
break;
|
||||
case upper_bound:
|
||||
if (x_is_at_upper_bound(j))
|
||||
break;
|
||||
m_x[j] = m_upper_bound_values[j];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
snap_non_basic_x_to_bound_and_free_to_zeroes() {
|
||||
for (unsigned j : non_basis()) {
|
||||
lean_assert(j < m_x.size());
|
||||
switch (m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
case low_bound:
|
||||
m_x[j] = m_low_bound_values[j];
|
||||
break;
|
||||
case upper_bound:
|
||||
m_x[j] = m_upper_bound_values[j];
|
||||
break;
|
||||
default:
|
||||
m_x[j] = zero_of_type<X>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
snap_xN_to_bounds() {
|
||||
snap_non_basic_x_to_bound();
|
||||
solve_Ax_eq_b();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
snap_xN_to_bounds_and_free_columns_to_zeroes() {
|
||||
snap_non_basic_x_to_bound_and_free_to_zeroes();
|
||||
solve_Ax_eq_b();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_core_solver_base<T, X>::
|
||||
init_reduced_costs_for_one_iteration() {
|
||||
solve_yB(m_y);
|
||||
fill_reduced_costs_from_m_y_by_rows();
|
||||
}
|
||||
|
||||
template <typename T, typename X> non_basic_column_value_position lp_core_solver_base<T, X>::
|
||||
get_non_basic_column_value_position(unsigned j) {
|
||||
switch (m_column_type[j]) {
|
||||
case fixed:
|
||||
return at_fixed;
|
||||
case free_column:
|
||||
return free_of_bounds;
|
||||
case boxed:
|
||||
return x_is_at_low_bound(j)? at_low_bound : at_upper_bound;
|
||||
case low_bound:
|
||||
return at_low_bound;
|
||||
case upper_bound:
|
||||
return at_upper_bound;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -10,34 +10,20 @@
|
|||
#include <string>
|
||||
#include "util/sstream.h"
|
||||
#include "util/exception.h"
|
||||
#include "util/lp/lp.h"
|
||||
#include "util/lp/core_solver_pretty_printer.h"
|
||||
#include "util/lp/numeric_pair.h"
|
||||
#include "util/lp/static_matrix.h"
|
||||
#include "util/lp/lu.h"
|
||||
#include "util/lp/permutation_matrix.h"
|
||||
namespace lean {
|
||||
void init_basic_part_of_basis_heading(std::vector<unsigned> & basis, unsigned m, std::vector<int> & basis_heading) {
|
||||
for (unsigned i = 0; i < m; i++) {
|
||||
unsigned column = basis[i];
|
||||
basis_heading[column] = i;
|
||||
}
|
||||
}
|
||||
void init_basic_part_of_basis_heading(std::vector<unsigned> & basis, unsigned m, std::vector<int> & basis_heading);
|
||||
|
||||
void init_non_basic_part_of_basis_heading(std::vector<int> & basis_heading, std::vector<unsigned> & non_basic_columns, unsigned n) {
|
||||
for (int j = n; j--;){
|
||||
if (basis_heading[j] < 0) {
|
||||
non_basic_columns.push_back(j);
|
||||
// the index of column j in m_non_basic_columns is (- basis_heading[j] - 1)
|
||||
basis_heading[j] = - non_basic_columns.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
void init_non_basic_part_of_basis_heading(std::vector<int> & basis_heading, std::vector<unsigned> & non_basic_columns, unsigned n);
|
||||
void init_basis_heading_and_non_basic_columns_vector(std::vector<unsigned> & basis,
|
||||
unsigned m,
|
||||
std::vector<int> & basis_heading,
|
||||
unsigned n,
|
||||
std::vector<unsigned> & non_basic_columns) {
|
||||
init_basic_part_of_basis_heading(basis, m, basis_heading);
|
||||
init_non_basic_part_of_basis_heading(basis_heading, non_basic_columns, n);
|
||||
}
|
||||
std::vector<unsigned> & non_basic_columns);
|
||||
|
||||
template <typename T, typename X> // X represents the type of the x variable and the bounds
|
||||
class lp_core_solver_base {
|
||||
|
|
@ -82,49 +68,10 @@ public:
|
|||
const std::unordered_map<unsigned, std::string> & column_names,
|
||||
std::vector<column_type> & column_types,
|
||||
std::vector<X> & low_bound_values,
|
||||
std::vector<X> & upper_bound_values):
|
||||
m_m(A.row_count()),
|
||||
m_n(A.column_count()),
|
||||
m_pivot_row_of_B_1(m_m),
|
||||
m_pivot_row(m_n, zero_of_type<T>()),
|
||||
m_A(A),
|
||||
m_b(b),
|
||||
m_basis(basis),
|
||||
m_x(x),
|
||||
m_costs(costs),
|
||||
m_settings(settings),
|
||||
m_y(m_m),
|
||||
m_status(FEASIBLE),
|
||||
m_factorization(nullptr),
|
||||
m_column_names(column_names),
|
||||
m_w(m_m),
|
||||
m_d(m_n),
|
||||
m_ed(m_m),
|
||||
m_column_type(column_types),
|
||||
m_low_bound_values(low_bound_values),
|
||||
m_upper_bound_values(upper_bound_values),
|
||||
m_column_norms(m_n, T(1)),
|
||||
m_copy_of_xB(m_m) {
|
||||
if (m_m) {
|
||||
lean_assert(m_A.col_val_equal_to_row_val());
|
||||
init();
|
||||
init_basis_heading();
|
||||
}
|
||||
}
|
||||
|
||||
void allocate_basis_heading() { // the rest of initilization will be handled by the factorization class
|
||||
m_basis_heading.clear();
|
||||
m_basis_heading.resize(m_n, -1);
|
||||
}
|
||||
void init() {
|
||||
lean_assert(m_costs.size() == m_n);
|
||||
lean_assert(m_basis.size() == m_m);
|
||||
lean_assert(m_b.size() == m_m);
|
||||
allocate_basis_heading();
|
||||
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
|
||||
m_refactor_counter = 0;
|
||||
srand48(1);
|
||||
}
|
||||
std::vector<X> & upper_bound_values);
|
||||
|
||||
void allocate_basis_heading();
|
||||
void init();
|
||||
|
||||
virtual ~lp_core_solver_base() {
|
||||
if (m_factorization != nullptr)
|
||||
|
|
@ -142,236 +89,62 @@ public:
|
|||
return m_status;
|
||||
}
|
||||
|
||||
void fill_cb(T * y){
|
||||
for (unsigned i = 0; i < m_m; i++) {
|
||||
y[i] = m_costs[m_basis[i]];
|
||||
}
|
||||
}
|
||||
void fill_cb(T * y);
|
||||
|
||||
|
||||
void fill_cb(std::vector<T> & y){
|
||||
for (unsigned i = 0; i < m_m; i++)
|
||||
y[i] = m_costs[m_basis[i]];
|
||||
}
|
||||
void fill_cb(std::vector<T> & y);
|
||||
|
||||
void solve_yB(std::vector<T> & y) {
|
||||
fill_cb(y); // now y = cB, that is the projection of costs to basis
|
||||
m_factorization->solve_yB(y);
|
||||
}
|
||||
void solve_yB(std::vector<T> & y);
|
||||
|
||||
void update_index_of_ed() {
|
||||
m_index_of_ed.clear();
|
||||
unsigned i = m_ed.size();
|
||||
while (i--) {
|
||||
if (!is_zero(m_ed[i]))
|
||||
m_index_of_ed.push_back(i);
|
||||
}
|
||||
}
|
||||
void update_index_of_ed();
|
||||
|
||||
void solve_Bd(unsigned entering) {
|
||||
m_factorization->solve_Bd(entering, m_ed, m_w);
|
||||
update_index_of_ed();
|
||||
#ifdef LEAN_DEBUG
|
||||
// auto B = get_B(m_factorization);
|
||||
// vector<T> a(m_m);
|
||||
// m_A.copy_column_to_vector(entering, a);
|
||||
// vector<T> cd(m_ed);
|
||||
// B.apply_from_left(cd, m_settings);
|
||||
// lean_assert(vectors_are_equal(cd , a));
|
||||
#endif
|
||||
}
|
||||
void solve_Bd(unsigned entering);
|
||||
|
||||
void pretty_print() {
|
||||
core_solver_pretty_printer<T, X> pp(*this);
|
||||
pp.print();
|
||||
}
|
||||
void pretty_print();
|
||||
|
||||
void save_state(T * w_buffer, T * d_buffer) {
|
||||
copy_m_w(w_buffer);
|
||||
copy_m_ed(d_buffer);
|
||||
}
|
||||
void save_state(T * w_buffer, T * d_buffer);
|
||||
|
||||
void restore_state(T * w_buffer, T * d_buffer) {
|
||||
restore_m_w(w_buffer);
|
||||
restore_m_ed(d_buffer);
|
||||
}
|
||||
void restore_state(T * w_buffer, T * d_buffer);
|
||||
|
||||
X get_cost() {
|
||||
return dot_product(m_costs, m_x, m_n);
|
||||
}
|
||||
|
||||
void copy_m_w(T * buffer) {
|
||||
unsigned i = m_m;
|
||||
while (i --) {
|
||||
buffer[i] = m_w[i];
|
||||
}
|
||||
}
|
||||
void copy_m_w(T * buffer);
|
||||
|
||||
void restore_m_w(T * buffer) {
|
||||
m_w.m_index.clear();
|
||||
unsigned i = m_m;
|
||||
while (i--) {
|
||||
if (!is_zero(m_w[i] = buffer[i]))
|
||||
m_w.m_index.push_back(i);
|
||||
}
|
||||
}
|
||||
void restore_m_w(T * buffer);
|
||||
|
||||
// needed for debugging
|
||||
void copy_m_ed(T * buffer) {
|
||||
unsigned i = m_m;
|
||||
while (i --) {
|
||||
buffer[i] = m_ed[i];
|
||||
}
|
||||
}
|
||||
void copy_m_ed(T * buffer);
|
||||
|
||||
void restore_m_ed(T * buffer) {
|
||||
unsigned i = m_m;
|
||||
while (i --) {
|
||||
m_ed[i] = buffer[i];
|
||||
}
|
||||
}
|
||||
void restore_m_ed(T * buffer);
|
||||
|
||||
bool A_mult_x_is_off() {
|
||||
if (precise<T>()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
T feps = convert_struct<T, double>::convert(m_settings.refactor_tolerance);
|
||||
X one = convert_struct<X, double>::convert(1.0);
|
||||
for (unsigned i = 0; i < m_m; i++) {
|
||||
X delta = abs(m_b[i] - m_A.dot_product_with_row(i, m_x));
|
||||
X eps = feps * (one + T(0.1) * abs(m_b[i]));
|
||||
|
||||
if (delta >eps) {
|
||||
std::cout << "x is off (";
|
||||
std::cout << "m_b[" << i << "] = " << m_b[i] << " ";
|
||||
std::cout << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ';
|
||||
std::cout << "delta = " << delta << ' ';
|
||||
std::cout << "iters = " << m_total_iterations << ")" << std::endl;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool A_mult_x_is_off();
|
||||
// from page 182 of Istvan Maros's book
|
||||
void calculate_pivot_row_of_B_1(unsigned pivot_row) {
|
||||
unsigned i = m_m;
|
||||
while (i--) {
|
||||
m_pivot_row_of_B_1[i] = numeric_traits<T>::zero();
|
||||
}
|
||||
m_pivot_row_of_B_1[pivot_row] = numeric_traits<T>::one();
|
||||
m_factorization->solve_yB(m_pivot_row_of_B_1);
|
||||
}
|
||||
void calculate_pivot_row_of_B_1(unsigned pivot_row);
|
||||
|
||||
void zero_pivot_row() {
|
||||
for (unsigned j : m_pivot_row_index)
|
||||
m_pivot_row[j] = numeric_traits<T>::zero();
|
||||
m_pivot_row_index.clear();
|
||||
}
|
||||
void zero_pivot_row();
|
||||
|
||||
void calculate_pivot_row_when_pivot_row_of_B1_is_ready() {
|
||||
zero_pivot_row();
|
||||
int i = m_m;
|
||||
while (i--) {
|
||||
T pi_1 = m_pivot_row_of_B_1[i];
|
||||
if (numeric_traits<T>::is_zero(pi_1)) {
|
||||
continue;
|
||||
}
|
||||
for (auto & c : m_A.m_rows[i]) {
|
||||
unsigned j = c.m_j;
|
||||
if (m_factorization->m_basis_heading[j] < 0) {
|
||||
m_pivot_row[j] += c.get_val() * pi_1;
|
||||
}
|
||||
}
|
||||
}
|
||||
void calculate_pivot_row_when_pivot_row_of_B1_is_ready();
|
||||
|
||||
unsigned j = m_pivot_row.size();
|
||||
while (j--) {
|
||||
if (!is_zero(m_pivot_row[j]))
|
||||
m_pivot_row_index.push_back(j);
|
||||
}
|
||||
}
|
||||
|
||||
void update_x(unsigned entering, X delta) {
|
||||
if (is_zero(delta)) {
|
||||
return;
|
||||
}
|
||||
m_x[entering] += delta;
|
||||
for (unsigned i : m_index_of_ed) {
|
||||
m_copy_of_xB[i] = m_x[m_basis[i]];
|
||||
m_x[m_basis[i]] -= delta * m_ed[i];
|
||||
}
|
||||
}
|
||||
void update_x(unsigned entering, X delta);
|
||||
|
||||
T get_var_value(unsigned j) const {
|
||||
return m_x[j];
|
||||
}
|
||||
|
||||
void print_statistics(X cost) {
|
||||
std::cout << "cost = " << T_to_string(cost) <<
|
||||
", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl;
|
||||
}
|
||||
void print_statistics(X cost);
|
||||
|
||||
bool print_statistics_with_iterations_and_check_that_the_time_is_over(unsigned total_iterations) {
|
||||
if (total_iterations % m_settings.report_frequency == 0) {
|
||||
std::cout << "iterations = " << total_iterations << ", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl;
|
||||
if (time_is_over()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool print_statistics_with_iterations_and_check_that_the_time_is_over(unsigned total_iterations);
|
||||
|
||||
bool print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string str, unsigned total_iterations) {
|
||||
if (total_iterations % m_settings.report_frequency == 0) {
|
||||
std::cout << str << " iterations = " << total_iterations << " cost = " << T_to_string(get_cost()) <<", nonzeros = " << m_factorization->get_number_of_nonzeroes() << std::endl;
|
||||
if (time_is_over()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string str, unsigned total_iterations);
|
||||
|
||||
bool print_statistics_with_cost_and_check_that_the_time_is_over(unsigned total_iterations, X cost) {
|
||||
if (total_iterations % m_settings.report_frequency == 0) {
|
||||
std::cout << "iterations = " << total_iterations << ", ";
|
||||
print_statistics(cost);
|
||||
if (time_is_over()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool print_statistics_with_cost_and_check_that_the_time_is_over(unsigned total_iterations, X cost);
|
||||
|
||||
bool print_statistics_and_check_that_the_time_is_over(unsigned total_iterations) {
|
||||
if (total_iterations % (numeric_traits<T>::precise()? static_cast<unsigned>(m_settings.report_frequency/10) : m_settings.report_frequency) == 0) {
|
||||
std::cout << "iterations = " << total_iterations << ", ";
|
||||
if (time_is_over()) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool print_statistics_and_check_that_the_time_is_over(unsigned total_iterations);
|
||||
|
||||
void set_non_basic_x_to_correct_bounds() {
|
||||
for (unsigned j : non_basis()) {
|
||||
switch (m_column_type[j]) {
|
||||
case boxed:
|
||||
m_x[j] = m_d[j] < 0? m_upper_bound_values[j]: m_low_bound_values[j];
|
||||
break;
|
||||
case low_bound:
|
||||
m_x[j] = m_low_bound_values[j];
|
||||
lean_assert(column_is_dual_feasible(j));
|
||||
break;
|
||||
case upper_bound:
|
||||
m_x[j] = m_upper_bound_values[j];
|
||||
lean_assert(column_is_dual_feasible(j));
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void set_non_basic_x_to_correct_bounds();
|
||||
|
||||
bool at_bound(const X &x, const X & bound) const {
|
||||
return !below_bound(x, bound) && !above_bound(x, bound);
|
||||
}
|
||||
|
|
@ -393,8 +166,6 @@ public:
|
|||
bool x_above_upper_bound(unsigned p) {
|
||||
return above_bound(m_x[p], m_upper_bound_values[p]);
|
||||
}
|
||||
|
||||
|
||||
bool x_is_at_low_bound(unsigned j) const {
|
||||
return at_bound(m_x[j], m_low_bound_values[j]);
|
||||
}
|
||||
|
|
@ -406,216 +177,39 @@ public:
|
|||
return x_is_at_low_bound(j) || x_is_at_upper_bound(j);
|
||||
}
|
||||
|
||||
bool column_is_dual_feasible(unsigned j) {
|
||||
switch (m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
return (x_is_at_low_bound(j) && d_is_not_negative(j)) ||
|
||||
(x_is_at_upper_bound(j) && d_is_not_positive(j));
|
||||
case low_bound:
|
||||
return x_is_at_low_bound(j) && d_is_not_negative(j);
|
||||
case upper_bound:
|
||||
std::cout << "upper_bound type should be switched to low_bound" << std::endl;
|
||||
lean_assert(false); // impossible case
|
||||
case free_column:
|
||||
return numeric_traits<X>::is_zero(m_d[j]);
|
||||
default:
|
||||
std::cout << "column = " << j << std::endl;
|
||||
std::cout << "unexpected column type = " << column_type_to_string(m_column_type[j]) << std::endl;
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
bool d_is_not_negative(unsigned j) {
|
||||
if (numeric_traits<T>::precise()) {
|
||||
return m_d[j] >= numeric_traits<T>::zero();
|
||||
}
|
||||
return m_d[j] > -T(0.00001);
|
||||
}
|
||||
bool column_is_dual_feasible(unsigned j);
|
||||
bool d_is_not_negative(unsigned j);
|
||||
|
||||
bool d_is_not_positive(unsigned j) {
|
||||
if (numeric_traits<T>::precise()) {
|
||||
return m_d[j] <= numeric_traits<T>::zero();
|
||||
}
|
||||
return m_d[j] < T(0.00001);
|
||||
}
|
||||
bool d_is_not_positive(unsigned j);
|
||||
|
||||
|
||||
bool time_is_over() {
|
||||
int span_in_mills = get_millisecond_span(m_start_time);
|
||||
if (span_in_mills / 1000.0 > m_settings.time_limit) {
|
||||
std::cout << "time is over" << std::endl;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
bool time_is_over();
|
||||
|
||||
void rs_minus_Anx(std::vector<X> & rs) {
|
||||
unsigned row = m_m;
|
||||
while (row--) {
|
||||
auto &rsv = rs[row] = m_b[row];
|
||||
for (auto & it : m_A.m_rows[row]) {
|
||||
unsigned j = it.m_j;
|
||||
if (m_basis_heading[j] < 0) {
|
||||
rsv -= m_x[j] * it.get_val();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void rs_minus_Anx(std::vector<X> & rs);
|
||||
|
||||
bool find_x_by_solving() {
|
||||
solve_Ax_eq_b();
|
||||
bool ret= !A_mult_x_is_off();
|
||||
if (ret)
|
||||
std::cout << "find_x_by_solving succeeded" << std::endl;
|
||||
else
|
||||
std::cout << "find_x_by_solving did not succeed" << std::endl;
|
||||
return ret;
|
||||
}
|
||||
bool find_x_by_solving();
|
||||
|
||||
bool update_basis_and_x(int entering, int leaving, X const & tt) {
|
||||
if (!is_zero(tt)) {
|
||||
update_x(entering, tt);
|
||||
if (A_mult_x_is_off() && !find_x_by_solving()) {
|
||||
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
|
||||
m_refactor_counter = 0;
|
||||
|
||||
if (!find_x_by_solving()) {
|
||||
restore_x(entering, tt);
|
||||
lean_assert(!A_mult_x_is_off());
|
||||
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
|
||||
m_refactor_counter = 0;
|
||||
m_iters_with_no_cost_growing++;
|
||||
if (m_factorization->get_status() != LU_status::OK) {
|
||||
throw exception(sstream() << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << m_total_iterations);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool refactor = m_refactor_counter++ >= 200;
|
||||
if (!refactor) {
|
||||
const T & pivot = this->m_pivot_row[entering]; // m_ed[m_factorization->basis_heading(leaving)] is the same but the one that we are using is more precise
|
||||
m_factorization->replace_column(leaving, pivot, m_w);
|
||||
if (m_factorization->get_status() == LU_status::OK) {
|
||||
m_factorization->change_basis(entering, leaving);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// need to refactor
|
||||
m_refactor_counter = 0;
|
||||
m_factorization->change_basis(entering, leaving);
|
||||
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
|
||||
if (m_factorization->get_status() != LU_status::OK || A_mult_x_is_off()) {
|
||||
std::cout << "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << m_total_iterations << std::endl;
|
||||
restore_x_and_refactor(entering, leaving, tt);
|
||||
lean_assert(!A_mult_x_is_off());
|
||||
m_iters_with_no_cost_growing++;
|
||||
std::cout << "rolled back after failing of init_factorization()" << std::endl;
|
||||
m_status = UNSTABLE;
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool update_basis_and_x(int entering, int leaving, X const & tt);
|
||||
|
||||
|
||||
void init_basis_heading() {
|
||||
init_basis_heading_and_non_basic_columns_vector(m_basis, m_m, m_basis_heading, m_n, m_non_basic_columns);
|
||||
lean_assert(basis_heading_is_correct());
|
||||
}
|
||||
void init_basis_heading();
|
||||
|
||||
bool basis_has_no_doubles() {
|
||||
std::set<unsigned> bm;
|
||||
for (unsigned i = 0; i < m_m; i++) {
|
||||
bm.insert(m_basis[i]);
|
||||
}
|
||||
return bm.size() == m_m;
|
||||
}
|
||||
bool basis_has_no_doubles();
|
||||
|
||||
bool non_basis_has_no_doubles() {
|
||||
std::set<int> bm;
|
||||
for (auto j : m_non_basic_columns) {
|
||||
bm.insert(j);
|
||||
}
|
||||
return bm.size() == m_non_basic_columns.size();
|
||||
}
|
||||
bool non_basis_has_no_doubles();
|
||||
|
||||
bool basis_is_correctly_represented_in_heading() {
|
||||
for (unsigned i = 0; i < m_m; i++) {
|
||||
if (m_basis_heading[m_basis[i]] != i)
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool non_basis_is_correctly_represented_in_heading() {
|
||||
for (int i = 0; i < m_non_basic_columns.size(); i++) {
|
||||
if (m_basis_heading[m_non_basic_columns[i]] != - i - 1)
|
||||
return false;
|
||||
}
|
||||
for (int j = 0; j < m_A.column_count(); j++) {
|
||||
if (m_basis_heading[j] >= 0) {
|
||||
lean_assert(m_basis_heading[j] < m_A.row_count() && m_basis[m_basis_heading[j]] == j);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool basis_is_correctly_represented_in_heading();
|
||||
bool non_basis_is_correctly_represented_in_heading();
|
||||
|
||||
bool basis_heading_is_correct() {
|
||||
return basis_has_no_doubles() && non_basis_has_no_doubles() && basis_is_correctly_represented_in_heading() && non_basis_is_correctly_represented_in_heading();
|
||||
}
|
||||
bool basis_heading_is_correct();
|
||||
|
||||
void restore_x_and_refactor(int entering, int leaving, X const & t) {
|
||||
m_factorization->restore_basis_change(entering, leaving);
|
||||
restore_x(entering, t);
|
||||
init_factorization(m_factorization, m_A, m_basis, m_basis_heading, m_settings, m_non_basic_columns);
|
||||
if (m_factorization->get_status() == LU_status::Degenerated) {
|
||||
std::cout << "cannot refactor" << std::endl;
|
||||
m_status = lp_status::FLOATING_POINT_ERROR;
|
||||
}
|
||||
// solve_Ax_eq_b();
|
||||
if (A_mult_x_is_off()) {
|
||||
std::cout << "cannot restore solution" << std::endl;
|
||||
m_status = lp_status::FLOATING_POINT_ERROR;
|
||||
return;
|
||||
}
|
||||
}
|
||||
void restore_x_and_refactor(int entering, int leaving, X const & t);
|
||||
|
||||
void restore_x(unsigned entering, X const & t) {
|
||||
if (is_zero(t)) return;
|
||||
std::cout << "calling restore for entering " << entering << std::endl;
|
||||
m_x[entering] -= t;
|
||||
for (unsigned i : m_index_of_ed) {
|
||||
m_x[m_basis[i]] = m_copy_of_xB[i];
|
||||
}
|
||||
}
|
||||
void restore_x(unsigned entering, X const & t);
|
||||
|
||||
void fill_reduced_costs_from_m_y_by_rows() {
|
||||
unsigned j = m_n;
|
||||
while (j--) {
|
||||
if (m_factorization->m_basis_heading[j] < 0)
|
||||
m_d[j] = m_costs[j];
|
||||
else
|
||||
m_d[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
void fill_reduced_costs_from_m_y_by_rows();
|
||||
|
||||
unsigned i = m_m;
|
||||
while (i--) {
|
||||
const T & y = m_y[i];
|
||||
if (is_zero(y)) continue;
|
||||
for (auto & it : m_A.m_rows[i]) {
|
||||
j = it.m_j;
|
||||
if (m_factorization->m_basis_heading[j] < 0)
|
||||
m_d[j] -= y * it.get_val();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void copy_rs_to_xB(std::vector<X> & rs) {
|
||||
unsigned j = m_m;
|
||||
while (j--) {
|
||||
m_x[m_basis[j]] = rs[j];
|
||||
}
|
||||
}
|
||||
void copy_rs_to_xB(std::vector<X> & rs);
|
||||
virtual bool low_bounds_are_set() const { return false; }
|
||||
X low_bound_value(unsigned j) const { return m_low_bound_values[j]; }
|
||||
X upper_bound_value(unsigned j) const { return m_upper_bound_values[j]; }
|
||||
|
|
@ -626,130 +220,29 @@ public:
|
|||
return m_settings.abs_val_is_smaller_than_pivot_tolerance(m_pivot_row[j]);
|
||||
}
|
||||
|
||||
X bound_span(unsigned j) {
|
||||
X bound_span(unsigned j) const {
|
||||
return m_upper_bound_values[j] - m_low_bound_values[j];
|
||||
}
|
||||
|
||||
std::string column_name(unsigned column) const {
|
||||
auto it = m_column_names.find(column);
|
||||
if (it == m_column_names.end()) {
|
||||
std::string name = T_to_string(column);
|
||||
return std::string(std::string("u") + name);
|
||||
}
|
||||
return it->second;
|
||||
}
|
||||
std::string column_name(unsigned column) const;
|
||||
|
||||
void copy_right_side(std::vector<X> & rs) {
|
||||
unsigned i = m_m;
|
||||
while (i --) {
|
||||
rs[i] = m_b[i];
|
||||
}
|
||||
}
|
||||
void copy_right_side(std::vector<X> & rs);
|
||||
|
||||
void add_delta_to_xB(std::vector<X> & del) {
|
||||
unsigned i = m_m;
|
||||
while (i--) {
|
||||
this->m_x[this->m_basis[i]] -= del[i];
|
||||
}
|
||||
}
|
||||
void add_delta_to_xB(std::vector<X> & del);
|
||||
|
||||
void find_error_in_BxB(std::vector<X>& rs){
|
||||
unsigned row = m_m;
|
||||
while (row--) {
|
||||
auto &rsv = rs[row];
|
||||
for (auto & it : m_A.m_rows[row]) {
|
||||
unsigned j = it.m_j;
|
||||
if (m_basis_heading[j] >= 0) {
|
||||
rsv -= m_x[j] * it.get_val();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void find_error_in_BxB(std::vector<X>& rs);
|
||||
|
||||
// recalculates the projection of x to B, such that Ax = b, whereab is the right side
|
||||
void solve_Ax_eq_b() {
|
||||
std::vector<X> rs(m_m);
|
||||
rs_minus_Anx(rs);
|
||||
std::vector<X> rrs = rs; // another copy of rs
|
||||
m_factorization->solve_By(rs);
|
||||
copy_rs_to_xB(rs);
|
||||
find_error_in_BxB(rrs);
|
||||
m_factorization->solve_By(rrs);
|
||||
add_delta_to_xB(rrs);
|
||||
}
|
||||
void solve_Ax_eq_b();
|
||||
|
||||
void snap_non_basic_x_to_bound() {
|
||||
for (unsigned j : non_basis()) {
|
||||
switch (m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
if (x_is_at_bound(j))
|
||||
break; // we should preserve x if possible
|
||||
m_x[j] = m_low_bound_values[j];
|
||||
break;
|
||||
case low_bound:
|
||||
if (x_is_at_low_bound(j))
|
||||
break;
|
||||
m_x[j] = m_low_bound_values[j];
|
||||
break;
|
||||
case upper_bound:
|
||||
if (x_is_at_upper_bound(j))
|
||||
break;
|
||||
m_x[j] = m_upper_bound_values[j];
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void snap_non_basic_x_to_bound_and_free_to_zeroes() {
|
||||
for (unsigned j : non_basis()) {
|
||||
lean_assert(j < m_x.size());
|
||||
switch (m_column_type[j]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
case low_bound:
|
||||
m_x[j] = m_low_bound_values[j];
|
||||
break;
|
||||
case upper_bound:
|
||||
m_x[j] = m_upper_bound_values[j];
|
||||
break;
|
||||
default:
|
||||
m_x[j] = zero_of_type<X>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void snap_xN_to_bounds() {
|
||||
snap_non_basic_x_to_bound();
|
||||
solve_Ax_eq_b();
|
||||
}
|
||||
void snap_non_basic_x_to_bound();
|
||||
void snap_non_basic_x_to_bound_and_free_to_zeroes();
|
||||
void snap_xN_to_bounds();
|
||||
|
||||
void snap_xN_to_bounds_and_free_columns_to_zeroes() {
|
||||
snap_non_basic_x_to_bound_and_free_to_zeroes();
|
||||
solve_Ax_eq_b();
|
||||
}
|
||||
void snap_xN_to_bounds_and_free_columns_to_zeroes();
|
||||
|
||||
void init_reduced_costs_for_one_iteration() {
|
||||
solve_yB(m_y);
|
||||
fill_reduced_costs_from_m_y_by_rows();
|
||||
}
|
||||
void init_reduced_costs_for_one_iteration();
|
||||
|
||||
non_basic_column_value_position get_non_basic_column_value_position(unsigned j) {
|
||||
switch (m_column_type[j]) {
|
||||
case fixed:
|
||||
return at_fixed;
|
||||
case free_column:
|
||||
return free_of_bounds;
|
||||
case boxed:
|
||||
return x_is_at_low_bound(j)? at_low_bound : at_upper_bound;
|
||||
case low_bound:
|
||||
return at_low_bound;
|
||||
case upper_bound:
|
||||
return at_upper_bound;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
non_basic_column_value_position get_non_basic_column_value_position(unsigned j);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
69
src/util/lp/lp_core_solver_base_instances.cpp
Normal file
69
src/util/lp/lp_core_solver_base_instances.cpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_core_solver_base.cpp"
|
||||
template bool lean::lp_core_solver_base<double, double>::A_mult_x_is_off();
|
||||
template bool lean::lp_core_solver_base<double, double>::basis_heading_is_correct();
|
||||
template void lean::lp_core_solver_base<double, double>::calculate_pivot_row_of_B_1(unsigned int);
|
||||
template void lean::lp_core_solver_base<double, double>::calculate_pivot_row_when_pivot_row_of_B1_is_ready();
|
||||
template bool lean::lp_core_solver_base<double, double>::column_is_dual_feasible(unsigned int);
|
||||
template void lean::lp_core_solver_base<double, double>::fill_reduced_costs_from_m_y_by_rows();
|
||||
template bool lean::lp_core_solver_base<double, double>::find_x_by_solving();
|
||||
template lean::non_basic_column_value_position lean::lp_core_solver_base<double, double>::get_non_basic_column_value_position(unsigned int);
|
||||
template void lean::lp_core_solver_base<double, double>::init_reduced_costs_for_one_iteration();
|
||||
template lean::lp_core_solver_base<double, double>::lp_core_solver_base(lean::static_matrix<double, double>&, std::vector<double, std::allocator<double> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&, lean::lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&, std::vector<lean::column_type, std::allocator<lean::column_type> >&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&);
|
||||
|
||||
template bool lean::lp_core_solver_base<double, double>::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string, unsigned int);
|
||||
template void lean::lp_core_solver_base<double, double>::restore_x(unsigned int, double const&);
|
||||
template void lean::lp_core_solver_base<double, double>::set_non_basic_x_to_correct_bounds();
|
||||
template void lean::lp_core_solver_base<double, double>::snap_xN_to_bounds_and_free_columns_to_zeroes();
|
||||
template void lean::lp_core_solver_base<double, double>::solve_Ax_eq_b();
|
||||
template void lean::lp_core_solver_base<double, double>::solve_Bd(unsigned int);
|
||||
template void lean::lp_core_solver_base<double, double>::solve_yB(std::vector<double, std::allocator<double> >&);
|
||||
template bool lean::lp_core_solver_base<double, double>::update_basis_and_x(int, int, double const&);
|
||||
template void lean::lp_core_solver_base<double, double>::update_x(unsigned int, double);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::A_mult_x_is_off();
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::basis_heading_is_correct();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::calculate_pivot_row_of_B_1(unsigned int);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::calculate_pivot_row_when_pivot_row_of_B1_is_ready();
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_is_dual_feasible(unsigned int);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::fill_reduced_costs_from_m_y_by_rows();
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::find_x_by_solving();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::init_reduced_costs_for_one_iteration();
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string, unsigned int);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::restore_x(unsigned int, lean::mpq const&);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::set_non_basic_x_to_correct_bounds();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_Ax_eq_b();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_Bd(unsigned int);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_yB(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::update_basis_and_x(int, int, lean::mpq const&);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::update_x(unsigned int, lean::mpq);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calculate_pivot_row_of_B_1(unsigned int);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calculate_pivot_row_when_pivot_row_of_B1_is_ready();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_basis_heading();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_reduced_costs_for_one_iteration();
|
||||
template lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::lp_core_solver_base(lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, lean::lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&, std::vector<lean::column_type, std::allocator<lean::column_type> >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_cost_and_check_that_the_time_is_over(unsigned int, lean::numeric_pair<lean::mpq>);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::snap_xN_to_bounds();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_Bd(unsigned int);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::update_basis_and_x(int, int, lean::numeric_pair<lean::mpq> const&);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::update_x(unsigned int, lean::numeric_pair<lean::mpq>);
|
||||
template lean::lp_core_solver_base<lean::mpq, lean::mpq>::lp_core_solver_base(lean::static_matrix<lean::mpq, lean::mpq>&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, lean::lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&, std::vector<lean::column_type, std::allocator<lean::column_type> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_iterations_and_check_that_the_time_is_over(unsigned int);
|
||||
template std::string lean::lp_core_solver_base<double, double>::column_name(unsigned int) const;
|
||||
template void lean::lp_core_solver_base<double, double>::pretty_print();
|
||||
template void lean::lp_core_solver_base<double, double>::restore_state(double*, double*);
|
||||
template void lean::lp_core_solver_base<double, double>::save_state(double*, double*);
|
||||
template std::string lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_name(unsigned int) const;
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::pretty_print();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::restore_state(lean::mpq*, lean::mpq*);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::save_state(lean::mpq*, lean::mpq*);
|
||||
template std::string lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::column_name(unsigned int) const;
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::pretty_print();
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::restore_state(lean::mpq*, lean::mpq*);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::save_state(lean::mpq*, lean::mpq*);
|
||||
template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
732
src/util/lp/lp_dual_core_solver.cpp
Normal file
732
src/util/lp/lp_dual_core_solver.cpp
Normal file
|
|
@ -0,0 +1,732 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_dual_core_solver.h"
|
||||
|
||||
namespace lean {
|
||||
template <typename T, typename X>
|
||||
lp_dual_core_solver<T, X>::lp_dual_core_solver(static_matrix<T, X> & A,
|
||||
std::vector<bool> & can_enter_basis,
|
||||
std::vector<X> & b, // the right side std::vector
|
||||
std::vector<X> & x, // the number of elements in x needs to be at least as large as the number of columns in A
|
||||
std::vector<unsigned> & basis,
|
||||
std::vector<T> & costs,
|
||||
std::vector<column_type> & column_type_array,
|
||||
std::vector<X> & low_bound_values,
|
||||
std::vector<X> & upper_bound_values,
|
||||
lp_settings & settings,
|
||||
std::unordered_map<unsigned, std::string> const & column_names):
|
||||
lp_core_solver_base<T, X>(A,
|
||||
b,
|
||||
basis,
|
||||
x,
|
||||
costs,
|
||||
settings,
|
||||
column_names,
|
||||
column_type_array,
|
||||
low_bound_values,
|
||||
upper_bound_values),
|
||||
m_can_enter_basis(can_enter_basis),
|
||||
m_a_wave(this->m_m),
|
||||
m_betas(this->m_m) {
|
||||
m_harris_tolerance = numeric_traits<T>::precise()? numeric_traits<T>::zero() : T(this->m_settings.harris_feasibility_tolerance);
|
||||
this->solve_yB(this->m_y);
|
||||
init_basic_part_of_basis_heading(this->m_basis, this->m_m, this->m_basis_heading);
|
||||
fill_non_basis_with_only_able_to_enter_columns();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_a_wave_by_zeros() {
|
||||
unsigned j = this->m_m;
|
||||
while (j--) {
|
||||
m_a_wave[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_non_basis_with_only_able_to_enter_columns() {
|
||||
auto & nb = this->m_factorization->m_non_basic_columns;
|
||||
nb.clear();
|
||||
unsigned j = this->m_n;
|
||||
while (j--) {
|
||||
if (this->m_basis_heading[j] >= 0 || !m_can_enter_basis[j]) continue;
|
||||
nb.push_back(j);
|
||||
this->m_basis_heading[j] = - nb.size();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::print_nb() {
|
||||
std::cout << "this is nb " << std::endl;
|
||||
for (auto l : this->m_factorization->m_non_basic_columns) {
|
||||
std::cout << l << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::restore_non_basis() {
|
||||
auto & nb = this->m_factorization->m_non_basic_columns;
|
||||
nb.clear();
|
||||
unsigned j = this->m_n;
|
||||
while (j--) {
|
||||
if (this->m_basis_heading[j] >= 0 ) continue;
|
||||
if (m_can_enter_basis[j]) {
|
||||
lean_assert(std::find(nb.begin(), nb.end(), j) == nb.end());
|
||||
nb.push_back(j);
|
||||
this->m_basis_heading[j] = - nb.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: update_basis(int entering, int leaving) {
|
||||
// the second argument is the element of the entering column from the pivot row - its value should be equal to the low diagonal element of the bump after all pivoting is done
|
||||
if (!(this->m_refactor_counter++ >= 200)) {
|
||||
this->m_factorization->replace_column(leaving, this->m_ed[this->m_factorization->basis_heading(leaving)], this->m_w);
|
||||
if (this->m_factorization->get_status() != LU_status::OK) {
|
||||
std::cout << "failed on replace_column( " << leaving << ", " << this->m_ed[this->m_factorization->basis_heading(leaving)] << ") total_iterations = " << this->m_total_iterations << std::endl;
|
||||
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings, this->m_non_basic_columns);
|
||||
this->m_iters_with_no_cost_growing++;
|
||||
this->m_status = UNSTABLE;
|
||||
return false;
|
||||
}
|
||||
this->m_factorization->change_basis(entering, leaving);
|
||||
} else { // need to refactor
|
||||
this->m_factorization->change_basis(entering, leaving);
|
||||
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings, this->m_non_basic_columns);
|
||||
if (this->m_factorization->get_status() != LU_status::OK) {
|
||||
std::cout << "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << this->m_total_iterations << std::endl;
|
||||
this->m_iters_with_no_cost_growing++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::recalculate_xB_and_d() {
|
||||
this->solve_Ax_eq_b();
|
||||
recalculate_d();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::recalculate_d() {
|
||||
this->solve_yB(this->m_y);
|
||||
this->fill_reduced_costs_from_m_y_by_rows();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_betas() {
|
||||
// todo : look at page 194 of Progress in the dual simplex algorithm for solving large scale LP problems : techniques for a fast and stable implementation
|
||||
// the current implementation is not good enough: todo
|
||||
unsigned i = this->m_m;
|
||||
while (i--) {
|
||||
m_betas[i] = 1;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::adjust_xb_for_changed_xn_and_init_betas() {
|
||||
this->solve_Ax_eq_b();
|
||||
init_betas();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::start_with_initial_basis_and_make_it_dual_feasible() {
|
||||
this->set_non_basic_x_to_correct_bounds(); // It is not an efficient version, see 3.29,
|
||||
// however this version does not require that m_x is the solution of Ax = 0 beforehand
|
||||
adjust_xb_for_changed_xn_and_init_betas();
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: done() {
|
||||
if (this->m_status == OPTIMAL) {
|
||||
return true;
|
||||
}
|
||||
if (this->m_total_iterations > this->m_settings.max_total_number_of_iterations) { // debug !!!!
|
||||
this->m_status = ITERATIONS_EXHAUSTED;
|
||||
return true;
|
||||
}
|
||||
return false; // todo, need to be more cases
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_low_bound(unsigned p) {
|
||||
lean_assert(this->m_basis_heading[p] >= 0 && this->m_basis_heading[p] < this->m_m);
|
||||
T del = this->m_x[p] - this->m_low_bound_values[p];
|
||||
del *= del;
|
||||
return del / this->m_betas[this->m_basis_heading[p]];
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_upper_bound(unsigned p) {
|
||||
lean_assert(this->m_basis_heading[p] >= 0 && this->m_basis_heading[p] < this->m_m);
|
||||
T del = this->m_x[p] - this->m_upper_bound_values[p];
|
||||
del *= del;
|
||||
return del / this->m_betas[this->m_basis_heading[p]];
|
||||
}
|
||||
|
||||
// template <typename T, typename X> void lp_dual_core_solver<T, X>::print_x_and_low_bound(unsigned p) {
|
||||
// std::cout << "x l[" << p << "] = " << this->m_x[p] << " " << this->m_low_bound_values[p] << std::endl;
|
||||
// }
|
||||
// template <typename T, typename X> void lp_dual_core_solver<T, X>::print_x_and_upper_bound(unsigned p) {
|
||||
// std::cout << "x u[" << p << "] = " << this->m_x[p] << " " << this->m_upper_bound_values[p] << std::endl;
|
||||
// }
|
||||
|
||||
// returns the
|
||||
template <typename T, typename X> T lp_dual_core_solver<T, X>::pricing_for_row(unsigned i) {
|
||||
unsigned p = this->m_basis[i];
|
||||
switch (this->m_column_type[p]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
if (this->x_below_low_bound(p)) {
|
||||
T del = get_edge_steepness_for_low_bound(p);
|
||||
// std::cout << "case boxed low_bound in pricing" << std::endl;
|
||||
// print_x_and_low_bound(p);
|
||||
return del;
|
||||
}
|
||||
if (this->x_above_upper_bound(p)) {
|
||||
T del = get_edge_steepness_for_upper_bound(p);
|
||||
// std::cout << "case boxed at upper_bound in pricing" << std::endl;
|
||||
// print_x_and_upper_bound(p);
|
||||
return del;
|
||||
}
|
||||
return numeric_traits<T>::zero();
|
||||
case low_bound:
|
||||
if (this->x_below_low_bound(p)) {
|
||||
T del = get_edge_steepness_for_low_bound(p);
|
||||
// std::cout << "case low_bound in pricing" << std::endl;
|
||||
// print_x_and_low_bound(p);
|
||||
return del;
|
||||
}
|
||||
return numeric_traits<T>::zero();
|
||||
break;
|
||||
case upper_bound:
|
||||
if (this->x_above_upper_bound(p)) {
|
||||
T del = get_edge_steepness_for_upper_bound(p);
|
||||
// std::cout << "case upper_bound in pricing" << std::endl;
|
||||
// print_x_and_upper_bound(p);
|
||||
return del;
|
||||
}
|
||||
return numeric_traits<T>::zero();
|
||||
break;
|
||||
case free_column:
|
||||
lean_assert(numeric_traits<T>::is_zero(this->m_d[p]));
|
||||
return numeric_traits<T>::zero();
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows) {
|
||||
m_r = -1;
|
||||
T steepest_edge_max = numeric_traits<T>::zero();
|
||||
unsigned initial_offset_in_rows = offset_in_rows;
|
||||
unsigned i = offset_in_rows;
|
||||
unsigned rows_left = number_of_rows_to_try;
|
||||
do {
|
||||
loop_start:
|
||||
T se = pricing_for_row(i);
|
||||
if (se > steepest_edge_max) {
|
||||
steepest_edge_max = se;
|
||||
m_r = i;
|
||||
}
|
||||
if (++i == this->m_m) {
|
||||
i = 0;
|
||||
}
|
||||
if (rows_left > 0) {
|
||||
rows_left--;
|
||||
}
|
||||
} while (rows_left || (steepest_edge_max < T(1e-5) && i != initial_offset_in_rows));
|
||||
if (m_r == -1) {
|
||||
if (this->m_status != UNSTABLE) {
|
||||
this->m_status = OPTIMAL;
|
||||
}
|
||||
} else {
|
||||
m_p = this->m_basis[m_r];
|
||||
m_delta = get_delta();
|
||||
if (advance_on_known_p()){
|
||||
return;
|
||||
}
|
||||
if (this->m_status == FLOATING_POINT_ERROR) {
|
||||
return;
|
||||
}
|
||||
this->m_status = UNSTABLE;
|
||||
if (i == initial_offset_in_rows) {
|
||||
return; // made a full loop
|
||||
}
|
||||
m_p = m_r = -1;
|
||||
steepest_edge_max = numeric_traits<T>::zero();
|
||||
rows_left = number_of_rows_to_try;
|
||||
goto loop_start;
|
||||
// if (this->m_total_iterations % 80 == 0) {
|
||||
// std::cout << "m_delta = " << m_delta << std::endl;
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: advance_on_known_p() {
|
||||
if (done()) {
|
||||
return true;
|
||||
}
|
||||
this->calculate_pivot_row_of_B_1(m_r);
|
||||
this->calculate_pivot_row_when_pivot_row_of_B1_is_ready();
|
||||
if (!ratio_test()) {
|
||||
return true;
|
||||
}
|
||||
calculate_beta_r_precisely();
|
||||
FTran();
|
||||
DSE_FTran();
|
||||
return basis_change_and_update();
|
||||
}
|
||||
|
||||
template <typename T, typename X> int lp_dual_core_solver<T, X>:: define_sign_of_alpha_r() {
|
||||
switch (this->m_column_type[m_p]) {
|
||||
case boxed:
|
||||
case fixed:
|
||||
if (this->x_below_low_bound(m_p)) {
|
||||
return -1;
|
||||
}
|
||||
if (this->x_above_upper_bound(m_p)) {
|
||||
return 1;
|
||||
}
|
||||
lean_unreachable();
|
||||
case low_bound:
|
||||
if (this->x_below_low_bound(m_p)) {
|
||||
return -1;
|
||||
}
|
||||
lean_unreachable();
|
||||
case upper_bound:
|
||||
if (this->x_above_upper_bound(m_p)) {
|
||||
return 1;
|
||||
}
|
||||
lean_unreachable();
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: can_be_breakpoint(unsigned j) {
|
||||
if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false;
|
||||
switch (this->m_column_type[j]) {
|
||||
case low_bound:
|
||||
lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_low_bound_values[j]));
|
||||
return m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
|
||||
case upper_bound:
|
||||
lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bound_values[j]));
|
||||
return m_sign_of_alpha_r * this->m_pivot_row[j] < 0;
|
||||
case boxed:
|
||||
{
|
||||
bool low_bound = this->x_is_at_low_bound(j);
|
||||
bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
|
||||
return low_bound == grawing;
|
||||
}
|
||||
case fixed: // is always dual feasible so we ingore it
|
||||
return false;
|
||||
case free_column:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_breakpoint_set() {
|
||||
m_breakpoint_set.clear();
|
||||
for (unsigned j : non_basis()) {
|
||||
if (can_be_breakpoint(j)) {
|
||||
m_breakpoint_set.insert(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::FTran() {
|
||||
this->solve_Bd(m_q);
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_dual_core_solver<T, X>::get_delta() {
|
||||
switch (this->m_column_type[m_p]) {
|
||||
case boxed:
|
||||
if (this->x_below_low_bound(m_p)) {
|
||||
return this->m_x[m_p] - this->m_low_bound_values[m_p];
|
||||
}
|
||||
if (this->x_above_upper_bound(m_p)) {
|
||||
return this->m_x[m_p] - this->m_upper_bound_values[m_p];;
|
||||
}
|
||||
lean_unreachable();
|
||||
case low_bound:
|
||||
if (this->x_below_low_bound(m_p)) {
|
||||
return this->m_x[m_p] - this->m_low_bound_values[m_p];
|
||||
}
|
||||
lean_unreachable();
|
||||
case upper_bound:
|
||||
if (this->x_above_upper_bound(m_p)) {
|
||||
return get_edge_steepness_for_upper_bound(m_p);
|
||||
}
|
||||
lean_unreachable();
|
||||
case fixed:
|
||||
return this->m_x[m_p] - this->m_upper_bound_values[m_p];;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::restore_d() {
|
||||
std::cout << "restore_d" << std::endl;
|
||||
this->m_d[m_p] = numeric_traits<T>::zero();
|
||||
for (auto j : non_basis()) {
|
||||
this->m_d[j] += m_theta_D * this->m_pivot_row[j];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: d_is_correct() {
|
||||
this->solve_yB(this->m_y);
|
||||
for (auto j : non_basis()) {
|
||||
T d = this->m_costs[j] - this->m_A.dot_product_with_column(this->m_y, j);
|
||||
if (numeric_traits<T>::get_double(abs(d - this->m_d[j])) >= 0.001) {
|
||||
std::cout << "m_total_iterations = " << this->m_total_iterations << std::endl;
|
||||
std::cout << "d[" << j << "] = " << this->m_d[j] << " but should be " << d << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::xb_minus_delta_p_pivot_column() {
|
||||
unsigned i = this->m_m;
|
||||
while (i--) {
|
||||
this->m_x[this->m_basis[i]] -= m_theta_P * this->m_ed[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::update_betas() { // page 194 of Progress ... todo - once in a while betas have to be reinitialized
|
||||
T one_over_arq = numeric_traits<T>::one() / this->m_pivot_row[m_q];
|
||||
T beta_r = this->m_betas[m_r] = std::max(T(0.0001), (m_betas[m_r] * one_over_arq) * one_over_arq);
|
||||
T k = -2 * one_over_arq;
|
||||
unsigned i = this->m_m;
|
||||
while (i--) {
|
||||
if (i == m_r) continue;
|
||||
T a = this->m_ed[i];
|
||||
m_betas[i] += a * (a * beta_r + k * this->m_pivot_row_of_B_1[i]);
|
||||
if (m_betas[i] < T(0.0001))
|
||||
m_betas[i] = T(0.0001);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::apply_flips() {
|
||||
for (unsigned j : m_flipped_boxed) {
|
||||
lean_assert(this->x_is_at_bound(j));
|
||||
if (this->x_is_at_low_bound(j)) {
|
||||
this->m_x[j] = this->m_upper_bound_values[j];
|
||||
} else {
|
||||
this->m_x[j] = this->m_low_bound_values[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_column_to_bounds(unsigned j) {
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
this->m_x[j] = this->m_low_bound_values[j];
|
||||
break;
|
||||
case boxed:
|
||||
if (this->x_is_at_low_bound(j)) {
|
||||
this->m_x[j] = this->m_low_bound_values[j];
|
||||
} else {
|
||||
this->m_x[j] = this->m_upper_bound_values[j];
|
||||
}
|
||||
break;
|
||||
case low_bound:
|
||||
this->m_x[j] = this->m_low_bound_values[j];
|
||||
break;
|
||||
case upper_bound:
|
||||
this->m_x[j] = this->m_upper_bound_values[j];
|
||||
break;
|
||||
case free_column:
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_to_bounds() {
|
||||
for (auto j : this->non_basis()) {
|
||||
snap_xN_column_to_bounds(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_beta_precisely(unsigned i) {
|
||||
std::vector<T> vec(this->m_m, numeric_traits<T>::zero());
|
||||
vec[i] = numeric_traits<T>::one();
|
||||
this->m_factorization->solve_yB(vec);
|
||||
T beta = numeric_traits<T>::zero();
|
||||
for (T & v : vec) {
|
||||
beta += v * v;
|
||||
}
|
||||
this->m_betas[i] =beta;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::init_betas_precisely() {
|
||||
std::cout << "init beta precisely..." << std::endl;
|
||||
unsigned i = this->m_m;
|
||||
while (i--) {
|
||||
init_beta_precisely(i);
|
||||
}
|
||||
std::cout << "done" << std::endl;
|
||||
}
|
||||
|
||||
// step 7 of the algorithm from Progress
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: basis_change_and_update() {
|
||||
update_betas();
|
||||
update_d_and_xB();
|
||||
m_theta_P = m_delta / this->m_ed[m_r];
|
||||
xb_minus_delta_p_pivot_column();
|
||||
apply_flips();
|
||||
this->m_x[m_q] += m_theta_P;
|
||||
if (!update_basis(m_q, m_p) || this->A_mult_x_is_off() || !problem_is_dual_feasible()) {
|
||||
revert_to_previous_basis();
|
||||
this->m_iters_with_no_cost_growing++;
|
||||
return false;
|
||||
}
|
||||
lean_assert(d_is_correct());
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::revert_to_previous_basis() {
|
||||
std::cout << "recovering basis p = " << m_p << " q = " << m_q << std::endl;
|
||||
this->m_factorization->change_basis(m_p, m_q);
|
||||
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings, this->m_non_basic_columns);
|
||||
if (this->m_factorization->get_status() != LU_status::OK) {
|
||||
this->m_status = FLOATING_POINT_ERROR;
|
||||
return;
|
||||
}
|
||||
this->m_x[m_q] -= m_theta_P;
|
||||
snap_xN_to_bounds();
|
||||
recalculate_xB_and_d();
|
||||
if (this->A_mult_x_is_off()) {
|
||||
this->m_status = FLOATING_POINT_ERROR;
|
||||
return;
|
||||
}
|
||||
init_betas_precisely();
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: problem_is_dual_feasible() {
|
||||
for (unsigned j : non_basis()){
|
||||
if (!this->column_is_dual_feasible(j)) {
|
||||
std::cout << "column " << j << " is not dual feasible" << std::endl;
|
||||
std::cout << "m_d[" << j << "] = " << this->m_d[j] << std::endl;
|
||||
std::cout << "x[" << j << "] = " << this->m_x[j] << std::endl;
|
||||
std::cout << "type = " << column_type_to_string(this->m_column_type[j]) << std::endl;
|
||||
std::cout << "bounds = " << this->m_low_bound_values[j] << "," << this->m_upper_bound_values[j] << std::endl;
|
||||
std::cout << "m_total_iterations = " << this->m_total_iterations << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> unsigned lp_dual_core_solver<T, X>:: get_number_of_rows_to_try_for_leaving() {
|
||||
unsigned s = this->m_m;
|
||||
if (this->m_m > 300) {
|
||||
s = (unsigned)(s / this->m_settings.percent_of_entering_to_check * 100);
|
||||
}
|
||||
return my_random() % s + 1;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: delta_keeps_the_sign(int initial_delta_sign, const T & delta) {
|
||||
if (numeric_traits<T>::precise())
|
||||
return ((delta > numeric_traits<T>::zero()) && (initial_delta_sign == 1)) ||
|
||||
((delta < numeric_traits<T>::zero()) && (initial_delta_sign == -1));
|
||||
|
||||
double del = numeric_traits<T>::get_double(delta);
|
||||
return ( (del > this->m_settings.zero_tolerance) && (initial_delta_sign == 1)) ||
|
||||
((del < - this->m_settings.zero_tolerance) && (initial_delta_sign == -1));
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::set_status_to_tentative_dual_unbounded_or_dual_unbounded() {
|
||||
std::cout << "cost = " << this->get_cost() << std::endl;
|
||||
if (this->m_status == TENTATIVE_DUAL_UNBOUNDED) {
|
||||
std::cout << "setting status to DUAL_UNBOUNDED" << std::endl;
|
||||
this->m_status = DUAL_UNBOUNDED;
|
||||
} else {
|
||||
std::cout << "setting to TENTATIVE_DUAL_UNBOUNDED" << std::endl;
|
||||
this->m_status = TENTATIVE_DUAL_UNBOUNDED;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::add_tight_breakpoints_and_q_to_flipped_set() {
|
||||
m_flipped_boxed.insert(m_q);
|
||||
for (auto j : m_tight_set) {
|
||||
m_flipped_boxed.insert(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_dual_core_solver<T, X>::delta_lost_on_flips_of_tight_breakpoints() {
|
||||
T ret = abs(this->bound_span(m_q) * this->m_pivot_row[m_q]);
|
||||
for (auto j : m_tight_set) {
|
||||
ret += abs(this->bound_span(j) * this->m_pivot_row[j]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: tight_breakpoinst_are_all_boxed() {
|
||||
if (this->m_column_type[m_q] != boxed) return false;
|
||||
for (auto j : m_tight_set) {
|
||||
if (this->m_column_type[j] != boxed) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_dual_core_solver<T, X>::calculate_harris_delta_on_breakpoint_set() {
|
||||
bool first_time = true;
|
||||
T ret = zero_of_type<T>();
|
||||
lean_assert(m_breakpoint_set.size() > 0);
|
||||
for (auto j : m_breakpoint_set) {
|
||||
T t;
|
||||
if (this->x_is_at_low_bound(j)) {
|
||||
t = abs((std::max(this->m_d[j], numeric_traits<T>::zero()) + m_harris_tolerance) / this->m_pivot_row[j]);
|
||||
} else {
|
||||
t = abs((std::min(this->m_d[j], numeric_traits<T>::zero()) - m_harris_tolerance) / this->m_pivot_row[j]);
|
||||
}
|
||||
if (first_time) {
|
||||
ret = t;
|
||||
first_time = false;
|
||||
} else if (t < ret) {
|
||||
ret = t;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_tight_set_on_harris_delta(const T & harris_delta ){
|
||||
m_tight_set.clear();
|
||||
for (auto j : m_breakpoint_set) {
|
||||
if (this->x_is_at_low_bound(j)) {
|
||||
if (abs(std::max(this->m_d[j], numeric_traits<T>::zero()) / this->m_pivot_row[j]) <= harris_delta){
|
||||
m_tight_set.insert(j);
|
||||
}
|
||||
} else {
|
||||
if (abs(std::min(this->m_d[j], numeric_traits<T>::zero() ) / this->m_pivot_row[j]) <= harris_delta){
|
||||
m_tight_set.insert(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_on_tight_set() {
|
||||
m_q = -1;
|
||||
T max_pivot;
|
||||
for (auto j : m_tight_set) {
|
||||
T r = abs(this->m_pivot_row[j]);
|
||||
if (m_q != -1) {
|
||||
if (r > max_pivot) {
|
||||
max_pivot = r;
|
||||
m_q = j;
|
||||
}
|
||||
} else {
|
||||
max_pivot = r;
|
||||
m_q = j;
|
||||
}
|
||||
}
|
||||
m_tight_set.erase(m_q);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_and_tight_set() {
|
||||
T harris_del = calculate_harris_delta_on_breakpoint_set();
|
||||
fill_tight_set_on_harris_delta(harris_del);
|
||||
find_q_on_tight_set();
|
||||
lean_assert(m_q != -1);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::erase_tight_breakpoints_and_q_from_breakpoint_set() {
|
||||
m_breakpoint_set.erase(m_q);
|
||||
for (auto j : m_tight_set) {
|
||||
m_breakpoint_set.erase(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_dual_core_solver<T, X>:: ratio_test() {
|
||||
m_sign_of_alpha_r = define_sign_of_alpha_r();
|
||||
fill_breakpoint_set();
|
||||
m_flipped_boxed.clear();
|
||||
int initial_delta_sign = m_delta >= numeric_traits<T>::zero()? 1: -1;
|
||||
do {
|
||||
if (m_breakpoint_set.size() == 0) {
|
||||
set_status_to_tentative_dual_unbounded_or_dual_unbounded();
|
||||
return false;
|
||||
}
|
||||
this->m_status = FEASIBLE;
|
||||
find_q_and_tight_set();
|
||||
if (!tight_breakpoinst_are_all_boxed()) break;
|
||||
T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign;
|
||||
if (!delta_keeps_the_sign(initial_delta_sign, del)) break;
|
||||
if (m_tight_set.size() + 1 == m_breakpoint_set.size()) {
|
||||
break; // deciding not to flip since we might get stuck without finding m_q, the column entering the basis
|
||||
}
|
||||
// we can flip m_q together with the tight set and look for another breakpoint candidate for m_q and another tight set
|
||||
add_tight_breakpoints_and_q_to_flipped_set();
|
||||
m_delta = del;
|
||||
erase_tight_breakpoints_and_q_from_breakpoint_set();
|
||||
} while (true);
|
||||
m_theta_D = this->m_d[m_q] / this->m_pivot_row[m_q];
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::process_flipped() {
|
||||
init_a_wave_by_zeros();
|
||||
for (auto j : m_flipped_boxed) {
|
||||
update_a_wave(signed_span_of_boxed(j), j);
|
||||
}
|
||||
}
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::update_d_and_xB() {
|
||||
for (auto j : non_basis()) {
|
||||
this->m_d[j] -= m_theta_D * this->m_pivot_row[j];
|
||||
}
|
||||
this->m_d[m_p] = - m_theta_D;
|
||||
if (m_flipped_boxed.size() > 0) {
|
||||
process_flipped();
|
||||
update_xb_after_bound_flips();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::calculate_beta_r_precisely() {
|
||||
T t = numeric_traits<T>::zero();
|
||||
unsigned i = this->m_m;
|
||||
while (i--) {
|
||||
T b = this->m_pivot_row_of_B_1[i];
|
||||
t += b * b;
|
||||
}
|
||||
m_betas[m_r] = t;
|
||||
}
|
||||
// see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms"
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::update_xb_after_bound_flips() {
|
||||
this->m_factorization->solve_By(m_a_wave);
|
||||
unsigned i = this->m_m;
|
||||
while (i--) {
|
||||
this->m_x[this->m_basis[i]] -= m_a_wave[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::one_iteration() {
|
||||
unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving();
|
||||
unsigned offset_in_rows = my_random() % this->m_m;
|
||||
if (this->m_status == TENTATIVE_DUAL_UNBOUNDED) {
|
||||
number_of_rows_to_try = this->m_m;
|
||||
} else {
|
||||
this->m_status = FEASIBLE;
|
||||
}
|
||||
pricing_loop(number_of_rows_to_try, offset_in_rows);
|
||||
lean_assert(problem_is_dual_feasible());
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_core_solver<T, X>::solve() { // see the page 35
|
||||
lean_assert(d_is_correct());
|
||||
lean_assert(problem_is_dual_feasible());
|
||||
this->m_start_time = get_millisecond_count();
|
||||
lean_assert(this->basis_heading_is_correct());
|
||||
this->m_total_iterations = 0;
|
||||
this->m_iters_with_no_cost_growing = 0;
|
||||
do {
|
||||
if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string(), this->m_total_iterations)){
|
||||
this->m_status = lp_status::TIME_EXHAUSTED;
|
||||
return;
|
||||
}
|
||||
one_iteration();
|
||||
this->m_total_iterations++;
|
||||
} while (this->m_status != FLOATING_POINT_ERROR && this->m_status != DUAL_UNBOUNDED && this->m_status != OPTIMAL &&
|
||||
this->m_iters_with_no_cost_growing <= this->m_settings.max_number_of_iterations_with_no_improvements
|
||||
&& this->m_total_iterations <= this->m_settings.max_total_number_of_iterations);
|
||||
}
|
||||
}
|
||||
|
|
@ -43,323 +43,49 @@ public:
|
|||
std::vector<X> & low_bound_values,
|
||||
std::vector<X> & upper_bound_values,
|
||||
lp_settings & settings,
|
||||
std::unordered_map<unsigned, std::string> const & column_names):
|
||||
lp_core_solver_base<T, X>(A,
|
||||
b,
|
||||
basis,
|
||||
x,
|
||||
costs,
|
||||
settings,
|
||||
column_names,
|
||||
column_type_array,
|
||||
low_bound_values,
|
||||
upper_bound_values),
|
||||
m_can_enter_basis(can_enter_basis),
|
||||
m_a_wave(this->m_m),
|
||||
m_betas(this->m_m) {
|
||||
m_harris_tolerance = numeric_traits<T>::precise()? numeric_traits<T>::zero() : T(this->m_settings.harris_feasibility_tolerance);
|
||||
this->solve_yB(this->m_y);
|
||||
init_basic_part_of_basis_heading(this->m_basis, this->m_m, this->m_basis_heading);
|
||||
fill_non_basis_with_only_able_to_enter_columns();
|
||||
}
|
||||
std::unordered_map<unsigned, std::string> const & column_names);
|
||||
|
||||
void init_a_wave_by_zeros() {
|
||||
unsigned j = this->m_m;
|
||||
while (j--) {
|
||||
m_a_wave[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
void init_a_wave_by_zeros();
|
||||
|
||||
void fill_non_basis_with_only_able_to_enter_columns() {
|
||||
auto & nb = this->m_factorization->m_non_basic_columns;
|
||||
nb.clear();
|
||||
unsigned j = this->m_n;
|
||||
while (j--) {
|
||||
if (this->m_basis_heading[j] >= 0 || !m_can_enter_basis[j]) continue;
|
||||
nb.push_back(j);
|
||||
this->m_basis_heading[j] = - nb.size();
|
||||
}
|
||||
}
|
||||
void fill_non_basis_with_only_able_to_enter_columns();
|
||||
|
||||
void print_nb() {
|
||||
std::cout << "this is nb " << std::endl;
|
||||
for (auto l : this->m_factorization->m_non_basic_columns) {
|
||||
std::cout << l << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
void print_nb();
|
||||
|
||||
void restore_non_basis() {
|
||||
auto & nb = this->m_factorization->m_non_basic_columns;
|
||||
nb.clear();
|
||||
unsigned j = this->m_n;
|
||||
while (j--) {
|
||||
if (this->m_basis_heading[j] >= 0 ) continue;
|
||||
if (m_can_enter_basis[j]) {
|
||||
lean_assert(std::find(nb.begin(), nb.end(), j) == nb.end());
|
||||
nb.push_back(j);
|
||||
this->m_basis_heading[j] = - nb.size();
|
||||
}
|
||||
}
|
||||
}
|
||||
void restore_non_basis();
|
||||
|
||||
bool update_basis(int entering, int leaving) {
|
||||
// the second argument is the element of the entering column from the pivot row - its value should be equal to the low diagonal element of the bump after all pivoting is done
|
||||
if (!(this->m_refactor_counter++ >= 200)) {
|
||||
this->m_factorization->replace_column(leaving, this->m_ed[this->m_factorization->basis_heading(leaving)], this->m_w);
|
||||
if (this->m_factorization->get_status() != LU_status::OK) {
|
||||
std::cout << "failed on replace_column( " << leaving << ", " << this->m_ed[this->m_factorization->basis_heading(leaving)] << ") total_iterations = " << this->m_total_iterations << std::endl;
|
||||
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings, this->m_non_basic_columns);
|
||||
this->m_iters_with_no_cost_growing++;
|
||||
this->m_status = UNSTABLE;
|
||||
return false;
|
||||
}
|
||||
this->m_factorization->change_basis(entering, leaving);
|
||||
} else { // need to refactor
|
||||
this->m_factorization->change_basis(entering, leaving);
|
||||
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings, this->m_non_basic_columns);
|
||||
if (this->m_factorization->get_status() != LU_status::OK) {
|
||||
std::cout << "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << this->m_total_iterations << std::endl;
|
||||
this->m_iters_with_no_cost_growing++;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool update_basis(int entering, int leaving);
|
||||
|
||||
void recalculate_xB_and_d() {
|
||||
this->solve_Ax_eq_b();
|
||||
recalculate_d();
|
||||
}
|
||||
void recalculate_xB_and_d();
|
||||
|
||||
void recalculate_d() {
|
||||
this->solve_yB(this->m_y);
|
||||
this->fill_reduced_costs_from_m_y_by_rows();
|
||||
}
|
||||
void recalculate_d();
|
||||
|
||||
std::vector<unsigned> & non_basis() { return this->m_factorization->m_non_basic_columns; }
|
||||
|
||||
void init_betas() {
|
||||
// todo : look at page 194 of Progress in the dual simplex algorithm for solving large scale LP problems : techniques for a fast and stable implementation
|
||||
// the current implementation is not good enough: todo
|
||||
unsigned i = this->m_m;
|
||||
while (i--) {
|
||||
m_betas[i] = 1;
|
||||
}
|
||||
}
|
||||
void init_betas();
|
||||
|
||||
void adjust_xb_for_changed_xn_and_init_betas() {
|
||||
this->solve_Ax_eq_b();
|
||||
init_betas();
|
||||
}
|
||||
void adjust_xb_for_changed_xn_and_init_betas();
|
||||
|
||||
void start_with_initial_basis_and_make_it_dual_feasible() {
|
||||
this->set_non_basic_x_to_correct_bounds(); // It is not an efficient version, see 3.29,
|
||||
// however this version does not require that m_x is the solution of Ax = 0 beforehand
|
||||
adjust_xb_for_changed_xn_and_init_betas();
|
||||
}
|
||||
void start_with_initial_basis_and_make_it_dual_feasible();
|
||||
|
||||
bool done() {
|
||||
if (this->m_status == OPTIMAL) {
|
||||
return true;
|
||||
}
|
||||
if (this->m_total_iterations > this->m_settings.max_total_number_of_iterations) { // debug !!!!
|
||||
this->m_status = ITERATIONS_EXHAUSTED;
|
||||
return true;
|
||||
}
|
||||
return false; // todo, need to be more cases
|
||||
}
|
||||
bool done();
|
||||
|
||||
T get_edge_steepness_for_low_bound(unsigned p) {
|
||||
lean_assert(this->m_basis_heading[p] >= 0 && this->m_basis_heading[p] < this->m_m);
|
||||
T del = this->m_x[p] - this->m_low_bound_values[p];
|
||||
del *= del;
|
||||
return del / this->m_betas[this->m_basis_heading[p]];
|
||||
}
|
||||
T get_edge_steepness_for_low_bound(unsigned p);
|
||||
|
||||
T get_edge_steepness_for_upper_bound(unsigned p) {
|
||||
lean_assert(this->m_basis_heading[p] >= 0 && this->m_basis_heading[p] < this->m_m);
|
||||
T del = this->m_x[p] - this->m_upper_bound_values[p];
|
||||
del *= del;
|
||||
return del / this->m_betas[this->m_basis_heading[p]];
|
||||
}
|
||||
T get_edge_steepness_for_upper_bound(unsigned p);
|
||||
|
||||
// void print_x_and_low_bound(unsigned p) {
|
||||
// std::cout << "x l[" << p << "] = " << this->m_x[p] << " " << this->m_low_bound_values[p] << std::endl;
|
||||
// }
|
||||
// void print_x_and_upper_bound(unsigned p) {
|
||||
// std::cout << "x u[" << p << "] = " << this->m_x[p] << " " << this->m_upper_bound_values[p] << std::endl;
|
||||
// }
|
||||
T pricing_for_row(unsigned i);
|
||||
|
||||
// returns the
|
||||
T pricing_for_row(unsigned i) {
|
||||
unsigned p = this->m_basis[i];
|
||||
switch (this->m_column_type[p]) {
|
||||
case fixed:
|
||||
case boxed:
|
||||
if (this->x_below_low_bound(p)) {
|
||||
T del = get_edge_steepness_for_low_bound(p);
|
||||
// std::cout << "case boxed low_bound in pricing" << std::endl;
|
||||
// print_x_and_low_bound(p);
|
||||
return del;
|
||||
}
|
||||
if (this->x_above_upper_bound(p)) {
|
||||
T del = get_edge_steepness_for_upper_bound(p);
|
||||
// std::cout << "case boxed at upper_bound in pricing" << std::endl;
|
||||
// print_x_and_upper_bound(p);
|
||||
return del;
|
||||
}
|
||||
return numeric_traits<T>::zero();
|
||||
case low_bound:
|
||||
if (this->x_below_low_bound(p)) {
|
||||
T del = get_edge_steepness_for_low_bound(p);
|
||||
// std::cout << "case low_bound in pricing" << std::endl;
|
||||
// print_x_and_low_bound(p);
|
||||
return del;
|
||||
}
|
||||
return numeric_traits<T>::zero();
|
||||
break;
|
||||
case upper_bound:
|
||||
if (this->x_above_upper_bound(p)) {
|
||||
T del = get_edge_steepness_for_upper_bound(p);
|
||||
// std::cout << "case upper_bound in pricing" << std::endl;
|
||||
// print_x_and_upper_bound(p);
|
||||
return del;
|
||||
}
|
||||
return numeric_traits<T>::zero();
|
||||
break;
|
||||
case free_column:
|
||||
lean_assert(numeric_traits<T>::is_zero(this->m_d[p]));
|
||||
return numeric_traits<T>::zero();
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
void pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows);
|
||||
|
||||
void pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows) {
|
||||
m_r = -1;
|
||||
T steepest_edge_max = numeric_traits<T>::zero();
|
||||
unsigned initial_offset_in_rows = offset_in_rows;
|
||||
unsigned i = offset_in_rows;
|
||||
unsigned rows_left = number_of_rows_to_try;
|
||||
do {
|
||||
loop_start:
|
||||
T se = pricing_for_row(i);
|
||||
if (se > steepest_edge_max) {
|
||||
steepest_edge_max = se;
|
||||
m_r = i;
|
||||
}
|
||||
if (++i == this->m_m) {
|
||||
i = 0;
|
||||
}
|
||||
if (rows_left > 0) {
|
||||
rows_left--;
|
||||
}
|
||||
} while (rows_left || (steepest_edge_max < T(1e-5) && i != initial_offset_in_rows));
|
||||
if (m_r == -1) {
|
||||
if (this->m_status != UNSTABLE) {
|
||||
this->m_status = OPTIMAL;
|
||||
}
|
||||
} else {
|
||||
m_p = this->m_basis[m_r];
|
||||
m_delta = get_delta();
|
||||
if (advance_on_known_p()){
|
||||
return;
|
||||
}
|
||||
if (this->m_status == FLOATING_POINT_ERROR) {
|
||||
return;
|
||||
}
|
||||
this->m_status = UNSTABLE;
|
||||
if (i == initial_offset_in_rows) {
|
||||
return; // made a full loop
|
||||
}
|
||||
m_p = m_r = -1;
|
||||
steepest_edge_max = numeric_traits<T>::zero();
|
||||
rows_left = number_of_rows_to_try;
|
||||
goto loop_start;
|
||||
// if (this->m_total_iterations % 80 == 0) {
|
||||
// std::cout << "m_delta = " << m_delta << std::endl;
|
||||
// }
|
||||
}
|
||||
}
|
||||
bool advance_on_known_p();
|
||||
|
||||
bool advance_on_known_p() {
|
||||
if (done()) {
|
||||
return true;
|
||||
}
|
||||
this->calculate_pivot_row_of_B_1(m_r);
|
||||
this->calculate_pivot_row_when_pivot_row_of_B1_is_ready();
|
||||
if (!ratio_test()) {
|
||||
return true;
|
||||
}
|
||||
calculate_beta_r_precisely();
|
||||
FTran();
|
||||
DSE_FTran();
|
||||
return basis_change_and_update();
|
||||
}
|
||||
int define_sign_of_alpha_r();
|
||||
|
||||
int define_sign_of_alpha_r() {
|
||||
switch (this->m_column_type[m_p]) {
|
||||
case boxed:
|
||||
case fixed:
|
||||
if (this->x_below_low_bound(m_p)) {
|
||||
return -1;
|
||||
}
|
||||
if (this->x_above_upper_bound(m_p)) {
|
||||
return 1;
|
||||
}
|
||||
lean_unreachable();
|
||||
case low_bound:
|
||||
if (this->x_below_low_bound(m_p)) {
|
||||
return -1;
|
||||
}
|
||||
lean_unreachable();
|
||||
case upper_bound:
|
||||
if (this->x_above_upper_bound(m_p)) {
|
||||
return 1;
|
||||
}
|
||||
lean_unreachable();
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
bool can_be_breakpoint(unsigned j);
|
||||
|
||||
bool can_be_breakpoint(unsigned j) {
|
||||
if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false;
|
||||
switch (this->m_column_type[j]) {
|
||||
case low_bound:
|
||||
lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_low_bound_values[j]));
|
||||
return m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
|
||||
case upper_bound:
|
||||
lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bound_values[j]));
|
||||
return m_sign_of_alpha_r * this->m_pivot_row[j] < 0;
|
||||
case boxed:
|
||||
{
|
||||
bool low_bound = this->x_is_at_low_bound(j);
|
||||
bool grawing = m_sign_of_alpha_r * this->m_pivot_row[j] > 0;
|
||||
return low_bound == grawing;
|
||||
}
|
||||
case fixed: // is always dual feasible so we ingore it
|
||||
return false;
|
||||
case free_column:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
void fill_breakpoint_set();
|
||||
|
||||
void fill_breakpoint_set() {
|
||||
m_breakpoint_set.clear();
|
||||
for (unsigned j : non_basis()) {
|
||||
if (can_be_breakpoint(j)) {
|
||||
m_breakpoint_set.insert(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FTran() {
|
||||
this->solve_Bd(m_q);
|
||||
}
|
||||
void FTran();
|
||||
|
||||
// this calculation is needed for the steepest edge update,
|
||||
// it hijackes m_pivot_row_of_B_1 for this purpose since we will need it anymore to the end of the cycle
|
||||
|
|
@ -367,410 +93,77 @@ public:
|
|||
this->m_factorization->solve_By(this->m_pivot_row_of_B_1);
|
||||
}
|
||||
|
||||
T get_delta() {
|
||||
switch (this->m_column_type[m_p]) {
|
||||
case boxed:
|
||||
if (this->x_below_low_bound(m_p)) {
|
||||
return this->m_x[m_p] - this->m_low_bound_values[m_p];
|
||||
}
|
||||
if (this->x_above_upper_bound(m_p)) {
|
||||
return this->m_x[m_p] - this->m_upper_bound_values[m_p];;
|
||||
}
|
||||
lean_unreachable();
|
||||
case low_bound:
|
||||
if (this->x_below_low_bound(m_p)) {
|
||||
return this->m_x[m_p] - this->m_low_bound_values[m_p];
|
||||
}
|
||||
lean_unreachable();
|
||||
case upper_bound:
|
||||
if (this->x_above_upper_bound(m_p)) {
|
||||
return get_edge_steepness_for_upper_bound(m_p);
|
||||
}
|
||||
lean_unreachable();
|
||||
case fixed:
|
||||
return this->m_x[m_p] - this->m_upper_bound_values[m_p];;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
T get_delta();
|
||||
|
||||
void restore_d() {
|
||||
std::cout << "restore_d" << std::endl;
|
||||
this->m_d[m_p] = numeric_traits<T>::zero();
|
||||
for (auto j : non_basis()) {
|
||||
this->m_d[j] += m_theta_D * this->m_pivot_row[j];
|
||||
}
|
||||
}
|
||||
void restore_d();
|
||||
|
||||
bool d_is_correct() {
|
||||
this->solve_yB(this->m_y);
|
||||
for (auto j : non_basis()) {
|
||||
T d = this->m_costs[j] - this->m_A.dot_product_with_column(this->m_y, j);
|
||||
if (numeric_traits<T>::get_double(abs(d - this->m_d[j])) >= 0.001) {
|
||||
std::cout << "m_total_iterations = " << this->m_total_iterations << std::endl;
|
||||
std::cout << "d[" << j << "] = " << this->m_d[j] << " but should be " << d << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool d_is_correct();
|
||||
|
||||
void xb_minus_delta_p_pivot_column() {
|
||||
unsigned i = this->m_m;
|
||||
while (i--) {
|
||||
this->m_x[this->m_basis[i]] -= m_theta_P * this->m_ed[i];
|
||||
}
|
||||
}
|
||||
void xb_minus_delta_p_pivot_column();
|
||||
|
||||
void update_betas() { // page 194 of Progress ... todo - once in a while betas have to be reinitialized
|
||||
T one_over_arq = numeric_traits<T>::one() / this->m_pivot_row[m_q];
|
||||
T beta_r = this->m_betas[m_r] = std::max(T(0.0001), (m_betas[m_r] * one_over_arq) * one_over_arq);
|
||||
T k = -2 * one_over_arq;
|
||||
unsigned i = this->m_m;
|
||||
while (i--) {
|
||||
if (i == m_r) continue;
|
||||
T a = this->m_ed[i];
|
||||
m_betas[i] += a * (a * beta_r + k * this->m_pivot_row_of_B_1[i]);
|
||||
if (m_betas[i] < T(0.0001))
|
||||
m_betas[i] = T(0.0001);
|
||||
}
|
||||
}
|
||||
void update_betas();
|
||||
|
||||
void apply_flips() {
|
||||
for (unsigned j : m_flipped_boxed) {
|
||||
lean_assert(this->x_is_at_bound(j));
|
||||
if (this->x_is_at_low_bound(j)) {
|
||||
this->m_x[j] = this->m_upper_bound_values[j];
|
||||
} else {
|
||||
this->m_x[j] = this->m_low_bound_values[j];
|
||||
}
|
||||
}
|
||||
}
|
||||
void apply_flips();
|
||||
|
||||
void snap_xN_column_to_bounds(unsigned j) {
|
||||
switch (this->m_column_type[j]) {
|
||||
case fixed:
|
||||
this->m_x[j] = this->m_low_bound_values[j];
|
||||
break;
|
||||
case boxed:
|
||||
if (this->x_is_at_low_bound(j)) {
|
||||
this->m_x[j] = this->m_low_bound_values[j];
|
||||
} else {
|
||||
this->m_x[j] = this->m_upper_bound_values[j];
|
||||
}
|
||||
break;
|
||||
case low_bound:
|
||||
this->m_x[j] = this->m_low_bound_values[j];
|
||||
break;
|
||||
case upper_bound:
|
||||
this->m_x[j] = this->m_upper_bound_values[j];
|
||||
break;
|
||||
case free_column:
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
void snap_xN_column_to_bounds(unsigned j);
|
||||
|
||||
void snap_xN_to_bounds() {
|
||||
for (auto j : this->non_basis()) {
|
||||
snap_xN_column_to_bounds(j);
|
||||
}
|
||||
}
|
||||
void snap_xN_to_bounds();
|
||||
|
||||
void init_beta_precisely(unsigned i) {
|
||||
std::vector<T> vec(this->m_m, numeric_traits<T>::zero());
|
||||
vec[i] = numeric_traits<T>::one();
|
||||
this->m_factorization->solve_yB(vec);
|
||||
T beta = numeric_traits<T>::zero();
|
||||
for (T & v : vec) {
|
||||
beta += v * v;
|
||||
}
|
||||
this->m_betas[i] =beta;
|
||||
}
|
||||
void init_beta_precisely(unsigned i);
|
||||
|
||||
void init_betas_precisely() {
|
||||
std::cout << "init beta precisely..." << std::endl;
|
||||
unsigned i = this->m_m;
|
||||
while (i--) {
|
||||
init_beta_precisely(i);
|
||||
}
|
||||
std::cout << "done" << std::endl;
|
||||
}
|
||||
void init_betas_precisely();
|
||||
|
||||
// step 7 of the algorithm from Progress
|
||||
bool basis_change_and_update() {
|
||||
update_betas();
|
||||
update_d_and_xB();
|
||||
m_theta_P = m_delta / this->m_ed[m_r];
|
||||
xb_minus_delta_p_pivot_column();
|
||||
apply_flips();
|
||||
this->m_x[m_q] += m_theta_P;
|
||||
if (!update_basis(m_q, m_p) || this->A_mult_x_is_off() || !problem_is_dual_feasible()) {
|
||||
revert_to_previous_basis();
|
||||
this->m_iters_with_no_cost_growing++;
|
||||
return false;
|
||||
}
|
||||
lean_assert(d_is_correct());
|
||||
return true;
|
||||
}
|
||||
bool basis_change_and_update();
|
||||
|
||||
void revert_to_previous_basis() {
|
||||
std::cout << "recovering basis p = " << m_p << " q = " << m_q << std::endl;
|
||||
this->m_factorization->change_basis(m_p, m_q);
|
||||
init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_basis_heading, this->m_settings, this->m_non_basic_columns);
|
||||
if (this->m_factorization->get_status() != LU_status::OK) {
|
||||
this->m_status = FLOATING_POINT_ERROR;
|
||||
return;
|
||||
}
|
||||
this->m_x[m_q] -= m_theta_P;
|
||||
snap_xN_to_bounds();
|
||||
recalculate_xB_and_d();
|
||||
if (this->A_mult_x_is_off()) {
|
||||
this->m_status = FLOATING_POINT_ERROR;
|
||||
return;
|
||||
}
|
||||
init_betas_precisely();
|
||||
}
|
||||
void revert_to_previous_basis();
|
||||
|
||||
bool problem_is_dual_feasible();
|
||||
|
||||
bool problem_is_dual_feasible() {
|
||||
for (unsigned j : non_basis()){
|
||||
if (!this->column_is_dual_feasible(j)) {
|
||||
std::cout << "column " << j << " is not dual feasible" << std::endl;
|
||||
std::cout << "m_d[" << j << "] = " << this->m_d[j] << std::endl;
|
||||
std::cout << "x[" << j << "] = " << this->m_x[j] << std::endl;
|
||||
std::cout << "type = " << column_type_to_string(this->m_column_type[j]) << std::endl;
|
||||
std::cout << "bounds = " << this->m_low_bound_values[j] << "," << this->m_upper_bound_values[j] << std::endl;
|
||||
std::cout << "m_total_iterations = " << this->m_total_iterations << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
unsigned get_number_of_rows_to_try_for_leaving() {
|
||||
unsigned s = this->m_m;
|
||||
if (this->m_m > 300) {
|
||||
s = (unsigned)(s / this->m_settings.percent_of_entering_to_check * 100);
|
||||
}
|
||||
return lrand48() % s + 1;
|
||||
}
|
||||
unsigned get_number_of_rows_to_try_for_leaving();
|
||||
|
||||
void update_a_wave(const T & del, unsigned j) {
|
||||
this->m_A.add_column_to_vector(del, j, & m_a_wave[0]);
|
||||
}
|
||||
|
||||
bool delta_keeps_the_sign(int initial_delta_sign, const T & delta) {
|
||||
if (numeric_traits<T>::precise())
|
||||
return ((delta > numeric_traits<T>::zero()) && (initial_delta_sign == 1)) ||
|
||||
((delta < numeric_traits<T>::zero()) && (initial_delta_sign == -1));
|
||||
bool delta_keeps_the_sign(int initial_delta_sign, const T & delta);
|
||||
|
||||
double del = numeric_traits<T>::get_double(delta);
|
||||
return ( (del > this->m_settings.zero_tolerance) && (initial_delta_sign == 1)) ||
|
||||
((del < - this->m_settings.zero_tolerance) && (initial_delta_sign == -1));
|
||||
}
|
||||
|
||||
void set_status_to_tentative_dual_unbounded_or_dual_unbounded() {
|
||||
std::cout << "cost = " << this->get_cost() << std::endl;
|
||||
if (this->m_status == TENTATIVE_DUAL_UNBOUNDED) {
|
||||
std::cout << "setting status to DUAL_UNBOUNDED" << std::endl;
|
||||
this->m_status = DUAL_UNBOUNDED;
|
||||
} else {
|
||||
std::cout << "setting to TENTATIVE_DUAL_UNBOUNDED" << std::endl;
|
||||
this->m_status = TENTATIVE_DUAL_UNBOUNDED;
|
||||
}
|
||||
}
|
||||
void set_status_to_tentative_dual_unbounded_or_dual_unbounded();
|
||||
|
||||
// it is positive if going from low bound to upper bound and negative if going from upper bound to low bound
|
||||
T signed_span_of_boxed(unsigned j) {
|
||||
return this->x_is_at_low_bound(j)? this->bound_span(j): - this->bound_span(j);
|
||||
}
|
||||
|
||||
void add_tight_breakpoints_and_q_to_flipped_set() {
|
||||
m_flipped_boxed.insert(m_q);
|
||||
for (auto j : m_tight_set) {
|
||||
m_flipped_boxed.insert(j);
|
||||
}
|
||||
}
|
||||
void add_tight_breakpoints_and_q_to_flipped_set();
|
||||
|
||||
T delta_lost_on_flips_of_tight_breakpoints() {
|
||||
T ret = abs(this->bound_span(m_q) * this->m_pivot_row[m_q]);
|
||||
for (auto j : m_tight_set) {
|
||||
ret += abs(this->bound_span(j) * this->m_pivot_row[j]);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
T delta_lost_on_flips_of_tight_breakpoints();
|
||||
|
||||
bool tight_breakpoinst_are_all_boxed() {
|
||||
if (this->m_column_type[m_q] != boxed) return false;
|
||||
for (auto j : m_tight_set) {
|
||||
if (this->m_column_type[j] != boxed) return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool tight_breakpoinst_are_all_boxed();
|
||||
|
||||
T calculate_harris_delta_on_breakpoint_set() {
|
||||
bool first_time = true;
|
||||
T ret = zero_of_type<T>();
|
||||
lean_assert(m_breakpoint_set.size() > 0);
|
||||
for (auto j : m_breakpoint_set) {
|
||||
T t;
|
||||
if (this->x_is_at_low_bound(j)) {
|
||||
t = abs((std::max(this->m_d[j], numeric_traits<T>::zero()) + m_harris_tolerance) / this->m_pivot_row[j]);
|
||||
} else {
|
||||
t = abs((std::min(this->m_d[j], numeric_traits<T>::zero()) - m_harris_tolerance) / this->m_pivot_row[j]);
|
||||
}
|
||||
if (first_time) {
|
||||
ret = t;
|
||||
first_time = false;
|
||||
} else if (t < ret) {
|
||||
ret = t;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
T calculate_harris_delta_on_breakpoint_set();
|
||||
|
||||
void fill_tight_set_on_harris_delta(const T & harris_delta ){
|
||||
m_tight_set.clear();
|
||||
for (auto j : m_breakpoint_set) {
|
||||
if (this->x_is_at_low_bound(j)) {
|
||||
if (abs(std::max(this->m_d[j], numeric_traits<T>::zero()) / this->m_pivot_row[j]) <= harris_delta){
|
||||
m_tight_set.insert(j);
|
||||
}
|
||||
} else {
|
||||
if (abs(std::min(this->m_d[j], numeric_traits<T>::zero() ) / this->m_pivot_row[j]) <= harris_delta){
|
||||
m_tight_set.insert(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void fill_tight_set_on_harris_delta(const T & harris_delta );
|
||||
|
||||
void find_q_on_tight_set() {
|
||||
m_q = -1;
|
||||
T max_pivot;
|
||||
for (auto j : m_tight_set) {
|
||||
T r = abs(this->m_pivot_row[j]);
|
||||
if (m_q != -1) {
|
||||
if (r > max_pivot) {
|
||||
max_pivot = r;
|
||||
m_q = j;
|
||||
}
|
||||
} else {
|
||||
max_pivot = r;
|
||||
m_q = j;
|
||||
}
|
||||
}
|
||||
m_tight_set.erase(m_q);
|
||||
}
|
||||
void find_q_on_tight_set();
|
||||
|
||||
void find_q_and_tight_set() {
|
||||
T harris_del = calculate_harris_delta_on_breakpoint_set();
|
||||
fill_tight_set_on_harris_delta(harris_del);
|
||||
find_q_on_tight_set();
|
||||
lean_assert(m_q != -1);
|
||||
}
|
||||
void find_q_and_tight_set();
|
||||
|
||||
void erase_tight_breakpoints_and_q_from_breakpoint_set() {
|
||||
m_breakpoint_set.erase(m_q);
|
||||
for (auto j : m_tight_set) {
|
||||
m_breakpoint_set.erase(j);
|
||||
}
|
||||
}
|
||||
void erase_tight_breakpoints_and_q_from_breakpoint_set();
|
||||
|
||||
bool ratio_test() {
|
||||
m_sign_of_alpha_r = define_sign_of_alpha_r();
|
||||
fill_breakpoint_set();
|
||||
m_flipped_boxed.clear();
|
||||
int initial_delta_sign = m_delta >= numeric_traits<T>::zero()? 1: -1;
|
||||
do {
|
||||
if (m_breakpoint_set.size() == 0) {
|
||||
set_status_to_tentative_dual_unbounded_or_dual_unbounded();
|
||||
return false;
|
||||
}
|
||||
this->m_status = FEASIBLE;
|
||||
find_q_and_tight_set();
|
||||
if (!tight_breakpoinst_are_all_boxed()) break;
|
||||
T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign;
|
||||
if (!delta_keeps_the_sign(initial_delta_sign, del)) break;
|
||||
if (m_tight_set.size() + 1 == m_breakpoint_set.size()) {
|
||||
break; // deciding not to flip since we might get stuck without finding m_q, the column entering the basis
|
||||
}
|
||||
// we can flip m_q together with the tight set and look for another breakpoint candidate for m_q and another tight set
|
||||
add_tight_breakpoints_and_q_to_flipped_set();
|
||||
m_delta = del;
|
||||
erase_tight_breakpoints_and_q_from_breakpoint_set();
|
||||
} while (true);
|
||||
m_theta_D = this->m_d[m_q] / this->m_pivot_row[m_q];
|
||||
return true;
|
||||
}
|
||||
bool ratio_test();
|
||||
|
||||
void process_flipped() {
|
||||
init_a_wave_by_zeros();
|
||||
for (auto j : m_flipped_boxed) {
|
||||
update_a_wave(signed_span_of_boxed(j), j);
|
||||
}
|
||||
}
|
||||
void update_d_and_xB() {
|
||||
for (auto j : non_basis()) {
|
||||
this->m_d[j] -= m_theta_D * this->m_pivot_row[j];
|
||||
}
|
||||
this->m_d[m_p] = - m_theta_D;
|
||||
if (m_flipped_boxed.size() > 0) {
|
||||
process_flipped();
|
||||
update_xb_after_bound_flips();
|
||||
}
|
||||
}
|
||||
void process_flipped();
|
||||
void update_d_and_xB();
|
||||
|
||||
void calculate_beta_r_precisely() {
|
||||
T t = numeric_traits<T>::zero();
|
||||
unsigned i = this->m_m;
|
||||
while (i--) {
|
||||
T b = this->m_pivot_row_of_B_1[i];
|
||||
t += b * b;
|
||||
}
|
||||
m_betas[m_r] = t;
|
||||
}
|
||||
void calculate_beta_r_precisely();
|
||||
// see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms"
|
||||
|
||||
void update_xb_after_bound_flips() {
|
||||
this->m_factorization->solve_By(m_a_wave);
|
||||
unsigned i = this->m_m;
|
||||
while (i--) {
|
||||
this->m_x[this->m_basis[i]] -= m_a_wave[i];
|
||||
}
|
||||
}
|
||||
void update_xb_after_bound_flips();
|
||||
|
||||
void one_iteration() {
|
||||
unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving();
|
||||
unsigned offset_in_rows = lrand48() % this->m_m;
|
||||
if (this->m_status == TENTATIVE_DUAL_UNBOUNDED) {
|
||||
number_of_rows_to_try = this->m_m;
|
||||
} else {
|
||||
this->m_status = FEASIBLE;
|
||||
}
|
||||
pricing_loop(number_of_rows_to_try, offset_in_rows);
|
||||
lean_assert(problem_is_dual_feasible());
|
||||
}
|
||||
void one_iteration();
|
||||
|
||||
void solve() { // see the page 35
|
||||
lean_assert(d_is_correct());
|
||||
lean_assert(problem_is_dual_feasible());
|
||||
this->m_start_time = get_millisecond_count();
|
||||
lean_assert(this->basis_heading_is_correct());
|
||||
this->m_total_iterations = 0;
|
||||
this->m_iters_with_no_cost_growing = 0;
|
||||
do {
|
||||
if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(std::string(), this->m_total_iterations)){
|
||||
this->m_status = lp_status::TIME_EXHAUSTED;
|
||||
return;
|
||||
}
|
||||
one_iteration();
|
||||
this->m_total_iterations++;
|
||||
} while (this->m_status != FLOATING_POINT_ERROR && this->m_status != DUAL_UNBOUNDED && this->m_status != OPTIMAL &&
|
||||
this->m_iters_with_no_cost_growing <= this->m_settings.max_number_of_iterations_with_no_improvements
|
||||
&& this->m_total_iterations <= this->m_settings.max_total_number_of_iterations);
|
||||
}
|
||||
void solve();
|
||||
|
||||
bool low_bounds_are_set() const { return true; }
|
||||
};
|
||||
|
|
|
|||
15
src/util/lp/lp_dual_core_solver_instances.cpp
Normal file
15
src/util/lp/lp_dual_core_solver_instances.cpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_dual_core_solver.cpp"
|
||||
template lean::lp_dual_core_solver<lean::mpq, lean::mpq>::lp_dual_core_solver(lean::static_matrix<lean::mpq, lean::mpq>&, std::vector<bool, std::allocator<bool> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<lean::column_type, std::allocator<lean::column_type> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, std::vector<lean::mpq, std::allocator<lean::mpq> >&, lean::lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&);
|
||||
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::start_with_initial_basis_and_make_it_dual_feasible();
|
||||
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::solve();
|
||||
template lean::lp_dual_core_solver<double, double>::lp_dual_core_solver(lean::static_matrix<double, double>&, std::vector<bool, std::allocator<bool> >&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<double, std::allocator<double> >&, std::vector<lean::column_type, std::allocator<lean::column_type> >&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&, lean::lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&);
|
||||
template void lean::lp_dual_core_solver<double, double>::start_with_initial_basis_and_make_it_dual_feasible();
|
||||
template void lean::lp_dual_core_solver<double, double>::solve();
|
||||
template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::restore_non_basis();
|
||||
template void lean::lp_dual_core_solver<double, double>::restore_non_basis();
|
||||
368
src/util/lp/lp_dual_simplex.cpp
Normal file
368
src/util/lp/lp_dual_simplex.cpp
Normal file
|
|
@ -0,0 +1,368 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_dual_simplex.h"
|
||||
namespace lean {
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: decide_on_status_after_stage1() {
|
||||
switch (m_core_solver->get_status()) {
|
||||
case OPTIMAL:
|
||||
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
|
||||
this->m_status = FEASIBLE;
|
||||
std::cout << "status is FEASIBLE" << std::endl;
|
||||
} else {
|
||||
std::cout << "status is UNBOUNDED" << std::endl;
|
||||
this->m_status = UNBOUNDED;
|
||||
}
|
||||
break;
|
||||
case DUAL_UNBOUNDED:
|
||||
lean_unreachable();
|
||||
case ITERATIONS_EXHAUSTED:
|
||||
std::cout << "status is ITERATIONS_EXHAUSTED" << std::endl;
|
||||
this->m_status = ITERATIONS_EXHAUSTED;
|
||||
break;
|
||||
case TIME_EXHAUSTED:
|
||||
std::cout << "status is TIME_EXHAUSTED" << std::endl;
|
||||
this->m_status = TIME_EXHAUSTED;
|
||||
break;
|
||||
case FLOATING_POINT_ERROR:
|
||||
std::cout << "status is FLOATING_POINT_ERROR" << std::endl;
|
||||
this->m_status = FLOATING_POINT_ERROR;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: fix_logical_for_stage2(unsigned j) {
|
||||
lean_assert(j >= this->number_of_core_structurals());
|
||||
switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) {
|
||||
case low_bound:
|
||||
m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
m_column_types_of_core_solver[j] = low_bound;
|
||||
m_can_enter_basis[j] = true;
|
||||
break;
|
||||
case fixed:
|
||||
this->m_upper_bounds[j] = m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
m_column_types_of_core_solver[j] = fixed;
|
||||
m_can_enter_basis[j] = false;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: fix_structural_for_stage2(unsigned j) {
|
||||
column_info<T> * ci = this->m_columns[this->m_core_solver_columns_to_external_columns[j]];
|
||||
switch (ci->get_column_type()) {
|
||||
case low_bound:
|
||||
m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
m_column_types_of_core_solver[j] = low_bound;
|
||||
m_can_enter_basis[j] = true;
|
||||
break;
|
||||
case fixed:
|
||||
case upper_bound:
|
||||
lean_unreachable();
|
||||
case boxed:
|
||||
this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j];
|
||||
m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
m_column_types_of_core_solver[j] = boxed;
|
||||
m_can_enter_basis[j] = true;
|
||||
break;
|
||||
case free_column:
|
||||
m_can_enter_basis[j] = true;
|
||||
m_column_types_of_core_solver[j] = free_column;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
T cost_was = this->m_costs[j];
|
||||
this->set_scaled_cost(j);
|
||||
bool in_basis = m_core_solver->m_factorization->m_basis_heading[j] >= 0;
|
||||
if (in_basis && cost_was != this->m_costs[j]) {
|
||||
std::cout << "cost change in basis" << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: unmark_boxed_and_fixed_columns_and_fix_structural_costs() {
|
||||
unsigned j = this->m_A->column_count();
|
||||
while (j-- > this->number_of_core_structurals()) {
|
||||
fix_logical_for_stage2(j);
|
||||
}
|
||||
j = this->number_of_core_structurals();
|
||||
while (j--) {
|
||||
fix_structural_for_stage2(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: restore_right_sides() {
|
||||
unsigned i = this->m_A->row_count();
|
||||
while (i--) {
|
||||
this->m_b[i] = m_b_copy[i];
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: solve_for_stage2() {
|
||||
m_core_solver->restore_non_basis();
|
||||
m_core_solver->solve_yB(m_core_solver->m_y);
|
||||
m_core_solver->fill_reduced_costs_from_m_y_by_rows();
|
||||
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible();
|
||||
m_core_solver->set_status(FEASIBLE);
|
||||
m_core_solver->solve();
|
||||
switch (m_core_solver->get_status()) {
|
||||
case OPTIMAL:
|
||||
this->m_status = OPTIMAL;
|
||||
break;
|
||||
case DUAL_UNBOUNDED:
|
||||
this->m_status = INFEASIBLE;
|
||||
break;
|
||||
case TIME_EXHAUSTED:
|
||||
this->m_status = TIME_EXHAUSTED;
|
||||
break;
|
||||
case FLOATING_POINT_ERROR:
|
||||
this->m_status = FLOATING_POINT_ERROR;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
this->m_second_stage_iterations = m_core_solver->m_total_iterations;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: fill_x_with_zeros() {
|
||||
unsigned j = this->m_A->column_count();
|
||||
while (j--) {
|
||||
this->m_x[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: stage1() {
|
||||
lean_assert(m_core_solver == nullptr);
|
||||
this->m_x.resize(this->m_A->column_count(), numeric_traits<T>::zero());
|
||||
this->print_statistics_on_A();
|
||||
m_core_solver = new lp_dual_core_solver<T, X>(
|
||||
*this->m_A,
|
||||
m_can_enter_basis,
|
||||
this->m_b, // the right side vector
|
||||
this->m_x,
|
||||
this->m_basis,
|
||||
this->m_costs,
|
||||
this->m_column_types_of_core_solver,
|
||||
this->m_low_bounds,
|
||||
this->m_upper_bounds,
|
||||
this->m_settings,
|
||||
this->m_name_map);
|
||||
m_core_solver->fill_reduced_costs_from_m_y_by_rows();
|
||||
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible();
|
||||
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
|
||||
std::cout << "skipping stage 1" << std::endl;
|
||||
m_core_solver->set_status(OPTIMAL);
|
||||
m_core_solver->m_total_iterations = 0;
|
||||
} else {
|
||||
std::cout << "stage 1" << std::endl;
|
||||
m_core_solver->solve();
|
||||
}
|
||||
decide_on_status_after_stage1();
|
||||
this->m_first_stage_iterations = m_core_solver->m_total_iterations;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: stage2() {
|
||||
std::cout << "starting stage2" << std::endl;
|
||||
unmark_boxed_and_fixed_columns_and_fix_structural_costs();
|
||||
restore_right_sides();
|
||||
solve_for_stage2();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: fill_first_stage_solver_fields() {
|
||||
unsigned slack_var = this->number_of_core_structurals();
|
||||
unsigned artificial = this->number_of_core_structurals() + this->m_slacks;
|
||||
|
||||
for (unsigned row = 0; row < this->row_count(); row++) {
|
||||
fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial);
|
||||
}
|
||||
fill_costs_and_bounds_and_column_types_for_the_first_stage_solver();
|
||||
}
|
||||
|
||||
template <typename T, typename X> column_type lp_dual_simplex<T, X>:: get_column_type(unsigned j) {
|
||||
lean_assert(j < this->m_A->column_count());
|
||||
if (j >= this->number_of_core_structurals()) {
|
||||
return m_column_types_of_logicals[j - this->number_of_core_structurals()];
|
||||
}
|
||||
return this->m_columns[this->m_core_solver_columns_to_external_columns[j]]->get_column_type();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) {
|
||||
// see 4.7 in the dissertation of Achim Koberstein
|
||||
lean_assert(this->m_core_solver_columns_to_external_columns.find(j) !=
|
||||
this->m_core_solver_columns_to_external_columns.end());
|
||||
|
||||
T free_bound = T(1e4); // see 4.8
|
||||
unsigned jj = this->m_core_solver_columns_to_external_columns[j];
|
||||
lean_assert(this->m_columns.find(jj) != this->m_columns.end());
|
||||
column_info<T> * ci = this->m_columns[jj];
|
||||
switch (ci->get_column_type()) {
|
||||
case upper_bound:
|
||||
throw exception(sstream() << "unexpected bound type " << j << " "
|
||||
<< column_type_to_string(get_column_type(j)));
|
||||
case low_bound: {
|
||||
m_can_enter_basis[j] = true;
|
||||
this->set_scaled_cost(j);
|
||||
this->m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
this->m_upper_bounds[j] =numeric_traits<T>::one();
|
||||
break;
|
||||
}
|
||||
case free_column: {
|
||||
m_can_enter_basis[j] = true;
|
||||
this->set_scaled_cost(j);
|
||||
this->m_upper_bounds[j] = free_bound;
|
||||
this->m_low_bounds[j] = -free_bound;
|
||||
break;
|
||||
}
|
||||
case boxed:
|
||||
m_can_enter_basis[j] = false;
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
this->m_upper_bounds[j] = this->m_low_bounds[j] = numeric_traits<T>::zero(); // is it needed?
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
m_column_types_of_core_solver[j] = boxed;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) {
|
||||
this->m_costs[j] = 0;
|
||||
lean_assert(get_column_type(j) != upper_bound);
|
||||
if ((m_can_enter_basis[j] = (get_column_type(j) == low_bound))) {
|
||||
m_column_types_of_core_solver[j] = boxed;
|
||||
this->m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
this->m_upper_bounds[j] = numeric_traits<T>::one();
|
||||
} else {
|
||||
m_column_types_of_core_solver[j] = fixed;
|
||||
this->m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
this->m_upper_bounds[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() {
|
||||
unsigned j = this->m_A->column_count();
|
||||
while (j-- > this->number_of_core_structurals()) { // go over logicals here
|
||||
fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j);
|
||||
}
|
||||
j = this->number_of_core_structurals();
|
||||
while (j--) {
|
||||
fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row,
|
||||
unsigned & slack_var,
|
||||
unsigned & artificial) {
|
||||
lean_assert(row < this->row_count());
|
||||
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]];
|
||||
// we need to bring the program to the form Ax = b
|
||||
T rs = this->m_b[row];
|
||||
switch (constraint.m_relation) {
|
||||
case Equal: // no slack variable here
|
||||
set_type_for_logical(artificial, fixed);
|
||||
this->m_basis[row] = artificial;
|
||||
this->m_costs[artificial] = numeric_traits<T>::zero();
|
||||
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
|
||||
artificial++;
|
||||
break;
|
||||
|
||||
case Greater_or_equal:
|
||||
set_type_for_logical(slack_var, low_bound);
|
||||
(*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
|
||||
if (rs > 0) {
|
||||
// adding one artificial
|
||||
set_type_for_logical(artificial, fixed);
|
||||
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
|
||||
this->m_basis[row] = artificial;
|
||||
this->m_costs[artificial] = numeric_traits<T>::zero();
|
||||
artificial++;
|
||||
} else {
|
||||
// we can put a slack_var into the basis, and atemplate <typename T, typename X> void lp_dual_simplex<T, X>:: adding an artificial variable
|
||||
this->m_basis[row] = slack_var;
|
||||
this->m_costs[slack_var] = numeric_traits<T>::zero();
|
||||
}
|
||||
slack_var++;
|
||||
break;
|
||||
case Less_or_equal:
|
||||
// introduce a non-negative slack variable
|
||||
set_type_for_logical(slack_var, low_bound);
|
||||
(*this->m_A)(row, slack_var) = numeric_traits<T>::one();
|
||||
if (rs < 0) {
|
||||
// adding one artificial
|
||||
set_type_for_logical(artificial, fixed);
|
||||
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
|
||||
this->m_basis[row] = artificial;
|
||||
this->m_costs[artificial] = numeric_traits<T>::zero();
|
||||
artificial++;
|
||||
} else {
|
||||
// we can put slack_var into the basis, and atemplate <typename T, typename X> void lp_dual_simplex<T, X>:: adding an artificial variable
|
||||
this->m_basis[row] = slack_var;
|
||||
this->m_costs[slack_var] = numeric_traits<T>::zero();
|
||||
}
|
||||
slack_var++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: augment_matrix_A_and_fill_x_and_allocate_some_fields() {
|
||||
this->count_slacks_and_artificials();
|
||||
this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials);
|
||||
unsigned n = this->m_A->column_count();
|
||||
this->m_column_types_of_core_solver.resize(n);
|
||||
m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials);
|
||||
this->m_costs.resize(n);
|
||||
this->m_upper_bounds.resize(n);
|
||||
this->m_low_bounds.resize(n);
|
||||
m_can_enter_basis.resize(n);
|
||||
this->m_basis.resize(this->m_A->row_count());
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: copy_m_b_aside_and_set_it_to_zeros() {
|
||||
for (int i = 0; i < this->m_b.size(); i++) {
|
||||
m_b_copy.push_back(this->m_b[i]);
|
||||
this->m_b[i] = numeric_traits<T>::zero(); // preparing for the first stage
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_dual_simplex<T, X>:: find_maximal_solution(){
|
||||
if (this->problem_is_empty()) {
|
||||
this->m_status = lp_status::EMPTY;
|
||||
return;
|
||||
}
|
||||
|
||||
this->flip_costs(); // do it for now, todo ( remove the flipping)
|
||||
|
||||
this->cleanup();
|
||||
if (this->m_status == INFEASIBLE) {
|
||||
return;
|
||||
}
|
||||
this->fill_matrix_A_and_init_right_side();
|
||||
this->fill_m_b();
|
||||
this->scale();
|
||||
augment_matrix_A_and_fill_x_and_allocate_some_fields();
|
||||
fill_first_stage_solver_fields();
|
||||
this->fill_column_names_for_core_solver();
|
||||
copy_m_b_aside_and_set_it_to_zeros();
|
||||
stage1();
|
||||
if (this->m_status == FEASIBLE) {
|
||||
stage2();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> T lp_dual_simplex<T, X>:: get_current_cost() const {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto it : this->m_columns) {
|
||||
ret += this->get_column_cost_value(it.first, it.second);
|
||||
}
|
||||
return -ret; // we flip costs for now
|
||||
}
|
||||
}
|
||||
|
|
@ -27,254 +27,33 @@ public:
|
|||
}
|
||||
}
|
||||
|
||||
void decide_on_status_after_stage1() {
|
||||
switch (m_core_solver->get_status()) {
|
||||
case OPTIMAL:
|
||||
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
|
||||
this->m_status = FEASIBLE;
|
||||
std::cout << "status is FEASIBLE" << std::endl;
|
||||
} else {
|
||||
std::cout << "status is UNBOUNDED" << std::endl;
|
||||
this->m_status = UNBOUNDED;
|
||||
}
|
||||
break;
|
||||
case DUAL_UNBOUNDED:
|
||||
lean_unreachable();
|
||||
case ITERATIONS_EXHAUSTED:
|
||||
std::cout << "status is ITERATIONS_EXHAUSTED" << std::endl;
|
||||
this->m_status = ITERATIONS_EXHAUSTED;
|
||||
break;
|
||||
case TIME_EXHAUSTED:
|
||||
std::cout << "status is TIME_EXHAUSTED" << std::endl;
|
||||
this->m_status = TIME_EXHAUSTED;
|
||||
break;
|
||||
case FLOATING_POINT_ERROR:
|
||||
std::cout << "status is FLOATING_POINT_ERROR" << std::endl;
|
||||
this->m_status = FLOATING_POINT_ERROR;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
void decide_on_status_after_stage1();
|
||||
|
||||
void fix_logical_for_stage2(unsigned j) {
|
||||
lean_assert(j >= this->number_of_core_structurals());
|
||||
switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) {
|
||||
case low_bound:
|
||||
m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
m_column_types_of_core_solver[j] = low_bound;
|
||||
m_can_enter_basis[j] = true;
|
||||
break;
|
||||
case fixed:
|
||||
this->m_upper_bounds[j] = m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
m_column_types_of_core_solver[j] = fixed;
|
||||
m_can_enter_basis[j] = false;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
void fix_logical_for_stage2(unsigned j);
|
||||
|
||||
void fix_structural_for_stage2(unsigned j) {
|
||||
column_info<T> * ci = this->m_columns[this->m_core_solver_columns_to_external_columns[j]];
|
||||
switch (ci->get_column_type()) {
|
||||
case low_bound:
|
||||
m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
m_column_types_of_core_solver[j] = low_bound;
|
||||
m_can_enter_basis[j] = true;
|
||||
break;
|
||||
case fixed:
|
||||
case upper_bound:
|
||||
lean_unreachable();
|
||||
case boxed:
|
||||
this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j];
|
||||
m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
m_column_types_of_core_solver[j] = boxed;
|
||||
m_can_enter_basis[j] = true;
|
||||
break;
|
||||
case free_column:
|
||||
m_can_enter_basis[j] = true;
|
||||
m_column_types_of_core_solver[j] = free_column;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
T cost_was = this->m_costs[j];
|
||||
this->set_scaled_cost(j);
|
||||
bool in_basis = m_core_solver->m_factorization->m_basis_heading[j] >= 0;
|
||||
if (in_basis && cost_was != this->m_costs[j]) {
|
||||
std::cout << "cost change in basis" << std::endl;
|
||||
}
|
||||
}
|
||||
void fix_structural_for_stage2(unsigned j);
|
||||
|
||||
void unmark_boxed_and_fixed_columns_and_fix_structural_costs() {
|
||||
unsigned j = this->m_A->column_count();
|
||||
while (j-- > this->number_of_core_structurals()) {
|
||||
fix_logical_for_stage2(j);
|
||||
}
|
||||
j = this->number_of_core_structurals();
|
||||
while (j--) {
|
||||
fix_structural_for_stage2(j);
|
||||
}
|
||||
}
|
||||
void unmark_boxed_and_fixed_columns_and_fix_structural_costs();
|
||||
|
||||
void restore_right_sides() {
|
||||
unsigned i = this->m_A->row_count();
|
||||
while (i--) {
|
||||
this->m_b[i] = m_b_copy[i];
|
||||
}
|
||||
}
|
||||
void restore_right_sides();
|
||||
|
||||
void solve_for_stage2() {
|
||||
m_core_solver->restore_non_basis();
|
||||
m_core_solver->solve_yB(m_core_solver->m_y);
|
||||
m_core_solver->fill_reduced_costs_from_m_y_by_rows();
|
||||
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible();
|
||||
m_core_solver->set_status(FEASIBLE);
|
||||
m_core_solver->solve();
|
||||
switch (m_core_solver->get_status()) {
|
||||
case OPTIMAL:
|
||||
this->m_status = OPTIMAL;
|
||||
break;
|
||||
case DUAL_UNBOUNDED:
|
||||
this->m_status = INFEASIBLE;
|
||||
break;
|
||||
case TIME_EXHAUSTED:
|
||||
this->m_status = TIME_EXHAUSTED;
|
||||
break;
|
||||
case FLOATING_POINT_ERROR:
|
||||
this->m_status = FLOATING_POINT_ERROR;
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
this->m_second_stage_iterations = m_core_solver->m_total_iterations;
|
||||
}
|
||||
void solve_for_stage2();
|
||||
|
||||
void fill_x_with_zeros() {
|
||||
unsigned j = this->m_A->column_count();
|
||||
while (j--) {
|
||||
this->m_x[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
void fill_x_with_zeros();
|
||||
|
||||
void stage1() {
|
||||
lean_assert(m_core_solver == nullptr);
|
||||
this->m_x.resize(this->m_A->column_count(), numeric_traits<T>::zero());
|
||||
this->print_statistics_on_A();
|
||||
m_core_solver = new lp_dual_core_solver<T, X>(
|
||||
*this->m_A,
|
||||
m_can_enter_basis,
|
||||
this->m_b, // the right side vector
|
||||
this->m_x,
|
||||
this->m_basis,
|
||||
this->m_costs,
|
||||
this->m_column_types_of_core_solver,
|
||||
this->m_low_bounds,
|
||||
this->m_upper_bounds,
|
||||
this->m_settings,
|
||||
this->m_name_map);
|
||||
m_core_solver->fill_reduced_costs_from_m_y_by_rows();
|
||||
m_core_solver->start_with_initial_basis_and_make_it_dual_feasible();
|
||||
if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) {
|
||||
std::cout << "skipping stage 1" << std::endl;
|
||||
m_core_solver->set_status(OPTIMAL);
|
||||
m_core_solver->m_total_iterations = 0;
|
||||
} else {
|
||||
std::cout << "stage 1" << std::endl;
|
||||
m_core_solver->solve();
|
||||
}
|
||||
decide_on_status_after_stage1();
|
||||
this->m_first_stage_iterations = m_core_solver->m_total_iterations;
|
||||
}
|
||||
void stage1();
|
||||
|
||||
void stage2() {
|
||||
std::cout << "starting stage2" << std::endl;
|
||||
unmark_boxed_and_fixed_columns_and_fix_structural_costs();
|
||||
restore_right_sides();
|
||||
solve_for_stage2();
|
||||
}
|
||||
void stage2();
|
||||
|
||||
void fill_first_stage_solver_fields() {
|
||||
unsigned slack_var = this->number_of_core_structurals();
|
||||
unsigned artificial = this->number_of_core_structurals() + this->m_slacks;
|
||||
void fill_first_stage_solver_fields();
|
||||
|
||||
for (unsigned row = 0; row < this->row_count(); row++) {
|
||||
fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial);
|
||||
}
|
||||
fill_costs_and_bounds_and_column_types_for_the_first_stage_solver();
|
||||
}
|
||||
column_type get_column_type(unsigned j);
|
||||
|
||||
column_type get_column_type(unsigned j) {
|
||||
lean_assert(j < this->m_A->column_count());
|
||||
if (j >= this->number_of_core_structurals()) {
|
||||
return m_column_types_of_logicals[j - this->number_of_core_structurals()];
|
||||
}
|
||||
return this->m_columns[this->m_core_solver_columns_to_external_columns[j]]->get_column_type();
|
||||
}
|
||||
void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j);
|
||||
|
||||
void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) {
|
||||
// see 4.7 in the dissertation of Achim Koberstein
|
||||
lean_assert(this->m_core_solver_columns_to_external_columns.find(j) !=
|
||||
this->m_core_solver_columns_to_external_columns.end());
|
||||
void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j);
|
||||
|
||||
T free_bound = T(1e4); // see 4.8
|
||||
unsigned jj = this->m_core_solver_columns_to_external_columns[j];
|
||||
lean_assert(this->m_columns.find(jj) != this->m_columns.end());
|
||||
column_info<T> * ci = this->m_columns[jj];
|
||||
switch (ci->get_column_type()) {
|
||||
case upper_bound:
|
||||
throw exception(sstream() << "unexpected bound type " << j << " "
|
||||
<< column_type_to_string(get_column_type(j)));
|
||||
case low_bound: {
|
||||
m_can_enter_basis[j] = true;
|
||||
this->set_scaled_cost(j);
|
||||
this->m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
this->m_upper_bounds[j] =numeric_traits<T>::one();
|
||||
break;
|
||||
}
|
||||
case free_column: {
|
||||
m_can_enter_basis[j] = true;
|
||||
this->set_scaled_cost(j);
|
||||
this->m_upper_bounds[j] = free_bound;
|
||||
this->m_low_bounds[j] = -free_bound;
|
||||
break;
|
||||
}
|
||||
case boxed:
|
||||
m_can_enter_basis[j] = false;
|
||||
this->m_costs[j] = numeric_traits<T>::zero();
|
||||
this->m_upper_bounds[j] = this->m_low_bounds[j] = numeric_traits<T>::zero(); // is it needed?
|
||||
break;
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
m_column_types_of_core_solver[j] = boxed;
|
||||
}
|
||||
|
||||
void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) {
|
||||
this->m_costs[j] = 0;
|
||||
lean_assert(get_column_type(j) != upper_bound);
|
||||
if ((m_can_enter_basis[j] = (get_column_type(j) == low_bound))) {
|
||||
m_column_types_of_core_solver[j] = boxed;
|
||||
this->m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
this->m_upper_bounds[j] = numeric_traits<T>::one();
|
||||
} else {
|
||||
m_column_types_of_core_solver[j] = fixed;
|
||||
this->m_low_bounds[j] = numeric_traits<T>::zero();
|
||||
this->m_upper_bounds[j] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
|
||||
void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() {
|
||||
unsigned j = this->m_A->column_count();
|
||||
while (j-- > this->number_of_core_structurals()) { // go over logicals here
|
||||
fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j);
|
||||
}
|
||||
j = this->number_of_core_structurals();
|
||||
while (j--) {
|
||||
fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j);
|
||||
}
|
||||
}
|
||||
void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver();
|
||||
|
||||
void set_type_for_logical(unsigned j, column_type col_type) {
|
||||
this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type;
|
||||
|
|
@ -282,115 +61,20 @@ public:
|
|||
|
||||
void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row,
|
||||
unsigned & slack_var,
|
||||
unsigned & artificial) {
|
||||
lean_assert(row < this->row_count());
|
||||
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]];
|
||||
// we need to bring the program to the form Ax = b
|
||||
T rs = this->m_b[row];
|
||||
switch (constraint.m_relation) {
|
||||
case Equal: // no slack variable here
|
||||
set_type_for_logical(artificial, fixed);
|
||||
this->m_basis[row] = artificial;
|
||||
this->m_costs[artificial] = numeric_traits<T>::zero();
|
||||
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
|
||||
artificial++;
|
||||
break;
|
||||
unsigned & artificial);
|
||||
|
||||
case Greater_or_equal:
|
||||
set_type_for_logical(slack_var, low_bound);
|
||||
(*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
|
||||
if (rs > 0) {
|
||||
// adding one artificial
|
||||
set_type_for_logical(artificial, fixed);
|
||||
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
|
||||
this->m_basis[row] = artificial;
|
||||
this->m_costs[artificial] = numeric_traits<T>::zero();
|
||||
artificial++;
|
||||
} else {
|
||||
// we can put a slack_var into the basis, and avoid adding an artificial variable
|
||||
this->m_basis[row] = slack_var;
|
||||
this->m_costs[slack_var] = numeric_traits<T>::zero();
|
||||
}
|
||||
slack_var++;
|
||||
break;
|
||||
case Less_or_equal:
|
||||
// introduce a non-negative slack variable
|
||||
set_type_for_logical(slack_var, low_bound);
|
||||
(*this->m_A)(row, slack_var) = numeric_traits<T>::one();
|
||||
if (rs < 0) {
|
||||
// adding one artificial
|
||||
set_type_for_logical(artificial, fixed);
|
||||
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
|
||||
this->m_basis[row] = artificial;
|
||||
this->m_costs[artificial] = numeric_traits<T>::zero();
|
||||
artificial++;
|
||||
} else {
|
||||
// we can put slack_var into the basis, and avoid adding an artificial variable
|
||||
this->m_basis[row] = slack_var;
|
||||
this->m_costs[slack_var] = numeric_traits<T>::zero();
|
||||
}
|
||||
slack_var++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void augment_matrix_A_and_fill_x_and_allocate_some_fields() {
|
||||
this->count_slacks_and_artificials();
|
||||
this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials);
|
||||
unsigned n = this->m_A->column_count();
|
||||
this->m_column_types_of_core_solver.resize(n);
|
||||
m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials);
|
||||
this->m_costs.resize(n);
|
||||
this->m_upper_bounds.resize(n);
|
||||
this->m_low_bounds.resize(n);
|
||||
m_can_enter_basis.resize(n);
|
||||
this->m_basis.resize(this->m_A->row_count());
|
||||
}
|
||||
void augment_matrix_A_and_fill_x_and_allocate_some_fields();
|
||||
|
||||
|
||||
|
||||
void copy_m_b_aside_and_set_it_to_zeros() {
|
||||
for (int i = 0; i < this->m_b.size(); i++) {
|
||||
m_b_copy.push_back(this->m_b[i]);
|
||||
this->m_b[i] = numeric_traits<T>::zero(); // preparing for the first stage
|
||||
}
|
||||
}
|
||||
void copy_m_b_aside_and_set_it_to_zeros();
|
||||
|
||||
void find_maximal_solution(){
|
||||
if (this->problem_is_empty()) {
|
||||
this->m_status = lp_status::EMPTY;
|
||||
return;
|
||||
}
|
||||
|
||||
this->flip_costs(); // do it for now, todo ( remove the flipping)
|
||||
|
||||
this->cleanup();
|
||||
if (this->m_status == INFEASIBLE) {
|
||||
return;
|
||||
}
|
||||
this->fill_matrix_A_and_init_right_side();
|
||||
this->fill_m_b();
|
||||
this->scale();
|
||||
augment_matrix_A_and_fill_x_and_allocate_some_fields();
|
||||
fill_first_stage_solver_fields();
|
||||
this->fill_column_names_for_core_solver();
|
||||
copy_m_b_aside_and_set_it_to_zeros();
|
||||
stage1();
|
||||
if (this->m_status == FEASIBLE) {
|
||||
stage2();
|
||||
}
|
||||
}
|
||||
void find_maximal_solution();
|
||||
|
||||
virtual T get_column_value(unsigned column) const {
|
||||
return this->get_column_value_with_core_solver(column, m_core_solver);
|
||||
}
|
||||
|
||||
T get_current_cost() const {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto it : this->m_columns) {
|
||||
ret += this->get_column_cost_value(it.first, it.second);
|
||||
}
|
||||
return -ret; // we flip costs for now
|
||||
}
|
||||
T get_current_cost() const;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
11
src/util/lp/lp_dual_simplex_instances.cpp
Normal file
11
src/util/lp/lp_dual_simplex_instances.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_dual_simplex.cpp"
|
||||
template lean::mpq lean::lp_dual_simplex<lean::mpq, lean::mpq>::get_current_cost() const;
|
||||
template void lean::lp_dual_simplex<lean::mpq, lean::mpq>::find_maximal_solution();
|
||||
template double lean::lp_dual_simplex<double, double>::get_current_cost() const;
|
||||
template void lean::lp_dual_simplex<double, double>::find_maximal_solution();
|
||||
1046
src/util/lp/lp_primal_core_solver.cpp
Normal file
1046
src/util/lp/lp_primal_core_solver.cpp
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
20
src/util/lp/lp_primal_core_solver_instances.cpp
Normal file
20
src/util/lp/lp_primal_core_solver_instances.cpp
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lar_solver.h"
|
||||
#include "util/lp/lp_primal_core_solver.cpp"
|
||||
namespace lean {
|
||||
// template void lar_solver::find_solution_signature_with_doubles(lar_solution_signature&);
|
||||
template void lp_primal_core_solver<double, double>::find_feasible_solution();
|
||||
template lp_primal_core_solver<double, double>::lp_primal_core_solver(static_matrix<double, double>&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<double, std::allocator<double> >&, std::vector<column_type, std::allocator<column_type> >&, std::vector<double, std::allocator<double> >&, lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&);
|
||||
template lp_primal_core_solver<double, double>::lp_primal_core_solver(static_matrix<double, double>&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<double, std::allocator<double> >&, std::vector<column_type, std::allocator<column_type> >&, std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> >&, lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&);
|
||||
|
||||
template unsigned lp_primal_core_solver<double, double>::solve();
|
||||
template lp_primal_core_solver<mpq, mpq>::lp_primal_core_solver(static_matrix<mpq, mpq>&, std::vector<mpq, std::allocator<mpq> >&, std::vector<mpq, std::allocator<mpq> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<mpq, std::allocator<mpq> >&, std::vector<column_type, std::allocator<column_type> >&, std::vector<mpq, std::allocator<mpq> >&, std::vector<mpq, std::allocator<mpq> >&, lp_settings&, std::unordered_map<unsigned int, std::string, std::hash<unsigned int>, std::equal_to<unsigned int>, std::allocator<std::pair<unsigned int const, std::string> > > const&);
|
||||
template unsigned lp_primal_core_solver<mpq, mpq>::solve();
|
||||
//template void lp_primal_simplex<double, double>::solve_with_total_inf();
|
||||
//template void lp_primal_simplex<mpq, mpq>::solve_with_total_inf();
|
||||
}
|
||||
378
src/util/lp/lp_primal_simplex.cpp
Normal file
378
src/util/lp/lp_primal_simplex.cpp
Normal file
|
|
@ -0,0 +1,378 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_primal_simplex.h"
|
||||
|
||||
namespace lean {
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) {
|
||||
unsigned slack_var = original_number_of_columns;
|
||||
unsigned artificial = original_number_of_columns + this->m_slacks;
|
||||
|
||||
for (unsigned row = 0; row < this->row_count(); row++) {
|
||||
fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: init_buffer(unsigned k, std::vector<T> & r) {
|
||||
for (unsigned i = 0; i < k; i++) {
|
||||
r[i] = 0;
|
||||
}
|
||||
r[k] = 1;
|
||||
for (unsigned i = this->row_count() -1; i > k; i--) {
|
||||
r[i] = 0;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: refactor() {
|
||||
m_core_solver->init_lu();
|
||||
if (m_core_solver->factorization()->get_status() != LU_status::OK) {
|
||||
throw exception("cannot refactor");
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: set_scaled_costs() {
|
||||
unsigned j = this->number_of_core_structurals();
|
||||
while (j-- > 0) {
|
||||
this->set_scaled_cost(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: stage_two() {
|
||||
std::cout << "starting stage 2" << std::endl;
|
||||
lean_assert(!m_core_solver->A_mult_x_is_off());
|
||||
int j = this->m_A->column_count() - 1;
|
||||
unsigned core_solver_cols = this->number_of_core_structurals();
|
||||
|
||||
while (j >= core_solver_cols) {
|
||||
this->m_costs[j--] = numeric_traits<T>::zero();
|
||||
}
|
||||
|
||||
set_scaled_costs();
|
||||
m_core_solver->set_status(lp_status::FEASIBLE);
|
||||
this->m_second_stage_iterations = m_core_solver->solve();
|
||||
this->m_status = m_core_solver->get_status();
|
||||
// std::cout << "status is " << lp_status_to_string(this->m_status) << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> column_info<T> * lp_primal_simplex<T, X>::get_or_create_column_info(unsigned column) {
|
||||
auto it = this->m_columns.find(column);
|
||||
return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info<T>) : it->second;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: fill_acceptable_values_for_x() {
|
||||
for (auto t : this->m_core_solver_columns_to_external_columns) {
|
||||
this->m_x[t.first] = numeric_traits<T>::zero();
|
||||
lean_assert(this->m_x[t.first] >= 0);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) {
|
||||
bound_is_set[i] = true;
|
||||
bounds[i] = numeric_traits<T>::zero();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: fill_costs_and_x_for_first_stage_solver_for_row(
|
||||
int row,
|
||||
unsigned & slack_var,
|
||||
unsigned & artificial) {
|
||||
lean_assert(row >= 0 && row < this->row_count());
|
||||
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]];
|
||||
// we need to bring the program to the form Ax = b
|
||||
T rs = this->m_b[row];
|
||||
T artificial_cost = - numeric_traits<T>::one();
|
||||
switch (constraint.m_relation) {
|
||||
case Equal: // no slack variable here
|
||||
this->m_column_types[artificial] = low_bound;
|
||||
this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero
|
||||
this->m_basis[row] = artificial;
|
||||
if (rs >= 0) {
|
||||
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
|
||||
this->m_x[artificial] = rs;
|
||||
} else {
|
||||
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
|
||||
this->m_x[artificial] = - rs;
|
||||
}
|
||||
artificial++;
|
||||
break;
|
||||
|
||||
case Greater_or_equal:
|
||||
this->m_column_types[slack_var] = low_bound;
|
||||
(*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
|
||||
|
||||
if (rs > 0) {
|
||||
lean_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
|
||||
// adding one artificial
|
||||
this->m_column_types[artificial] = low_bound;
|
||||
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
|
||||
this->m_costs[artificial] = artificial_cost;
|
||||
this->m_basis[row] = artificial;
|
||||
this->m_x[artificial] = rs;
|
||||
artificial++;
|
||||
} else {
|
||||
// we can put a slack_var into the basis, and atemplate <typename T, typename X> void lp_primal_simplex<T, X>:: adding an artificial variable
|
||||
this->m_basis[row] = slack_var;
|
||||
this->m_x[slack_var] = - rs;
|
||||
}
|
||||
slack_var++;
|
||||
break;
|
||||
case Less_or_equal:
|
||||
// introduce a non-negative slack variable
|
||||
this->m_column_types[slack_var] = low_bound;
|
||||
(*this->m_A)(row, slack_var) = numeric_traits<T>::one();
|
||||
|
||||
if (rs < 0) {
|
||||
// adding one artificial
|
||||
lean_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
|
||||
this->m_column_types[artificial] = low_bound;
|
||||
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
|
||||
this->m_costs[artificial] = artificial_cost;
|
||||
this->m_x[artificial] = - rs;
|
||||
this->m_basis[row] = artificial++;
|
||||
} else {
|
||||
// we can put slack_var into the basis, and atemplate <typename T, typename X> void lp_primal_simplex<T, X>:: adding an artificial variable
|
||||
this->m_basis[row] = slack_var;
|
||||
this->m_x[slack_var] = rs;
|
||||
}
|
||||
slack_var++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
template <typename T, typename X> std::string lp_primal_simplex<T, X>::name_of_core_solver_column(unsigned j) { // j here is the core solver index
|
||||
unsigned external_j = this->m_core_solver_columns_to_external_columns[j];
|
||||
auto t = this->m_columns.find(external_j);
|
||||
if (t == this->m_columns.end()) {
|
||||
return std::string("name_not_found");
|
||||
}
|
||||
return t->m_name;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: set_core_solver_bounds() {
|
||||
unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials;
|
||||
this->m_column_types.resize(total_vars);
|
||||
this->m_upper_bounds.resize(total_vars);
|
||||
for (auto cit : this->m_columns) {
|
||||
column_info<T> * ci = cit.second;
|
||||
auto p = this->m_external_columns_to_core_solver_columns.find(cit.first);
|
||||
if (p == this->m_external_columns_to_core_solver_columns.end()) continue;
|
||||
unsigned j = p->second;
|
||||
switch (this->m_column_types[j] = ci->get_column_type()){
|
||||
case fixed:
|
||||
this->m_upper_bounds[j] = numeric_traits<T>::zero();
|
||||
break;
|
||||
case boxed:
|
||||
this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j];
|
||||
break;
|
||||
|
||||
default: break; // do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: find_maximal_solution() {
|
||||
int preprocessing_start_time = get_millisecond_count();
|
||||
if (this->problem_is_empty()) {
|
||||
this->m_status = lp_status::EMPTY;
|
||||
return;
|
||||
}
|
||||
|
||||
this->cleanup();
|
||||
this->fill_matrix_A_and_init_right_side();
|
||||
if (this->m_status == lp_status::INFEASIBLE) {
|
||||
return;
|
||||
}
|
||||
this->m_x.resize(this->m_A->column_count());
|
||||
this->fill_m_b();
|
||||
this->scale();
|
||||
fill_acceptable_values_for_x();
|
||||
this->count_slacks_and_artificials();
|
||||
set_core_solver_bounds();
|
||||
update_time_limit_from_starting_time(preprocessing_start_time);
|
||||
solve_with_total_inf();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: fill_A_x_and_basis_for_stage_one_total_inf() {
|
||||
for (unsigned row = 0; row < this->row_count(); row++)
|
||||
fill_A_x_and_basis_for_stage_one_total_inf_for_row(row);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) {
|
||||
lean_assert(row < this->row_count());
|
||||
auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row);
|
||||
lean_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end());
|
||||
unsigned ext_row = ext_row_it->second;
|
||||
auto constr_it = this->m_constraints.find(ext_row);
|
||||
lean_assert(constr_it != this->m_constraints.end());
|
||||
auto & constraint = constr_it->second;
|
||||
unsigned j = this->m_A->column_count(); // j is a slack variable
|
||||
this->m_A->add_column();
|
||||
// we need to bring the program to the form Ax = b
|
||||
this->m_basis[row] = j;
|
||||
switch (constraint.m_relation) {
|
||||
case Equal:
|
||||
this->m_x[j] = this->m_b[row];
|
||||
(*this->m_A)(row, j) = numeric_traits<T>::one();
|
||||
this->m_column_types[j] = fixed;
|
||||
this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>();
|
||||
break;
|
||||
|
||||
case Greater_or_equal:
|
||||
this->m_x[j] = - this->m_b[row];
|
||||
(*this->m_A)(row, j) = - numeric_traits<T>::one();
|
||||
this->m_column_types[j] = low_bound;
|
||||
this->m_upper_bounds[j] = zero_of_type<X>();
|
||||
break;
|
||||
case Less_or_equal:
|
||||
this->m_x[j] = this->m_b[row];
|
||||
(*this->m_A)(row, j) = numeric_traits<T>::one();
|
||||
this->m_column_types[j] = low_bound;
|
||||
this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_primal_simplex<T, X>:: solve_with_total_inf() {
|
||||
std::cout << "starting solve_with_total_inf()" << std::endl;
|
||||
int total_vars = this->m_A->column_count() + this->row_count();
|
||||
m_low_bounds.clear();
|
||||
m_low_bounds.resize(total_vars, zero_of_type<X>()); // low bounds are shifted ot zero
|
||||
this->m_x.resize(total_vars, numeric_traits<T>::zero());
|
||||
this->m_basis.resize(this->row_count());
|
||||
this->m_costs.clear();
|
||||
this->m_costs.resize(total_vars, zero_of_type<T>());
|
||||
fill_A_x_and_basis_for_stage_one_total_inf();
|
||||
this->print_statistics_on_A();
|
||||
this->fill_column_names_for_core_solver();
|
||||
int j = this->m_A->column_count() - 1;
|
||||
unsigned core_solver_cols = this->number_of_core_structurals();
|
||||
|
||||
while (j >= core_solver_cols)
|
||||
this->m_costs[j--] = numeric_traits<T>::zero();
|
||||
|
||||
set_scaled_costs();
|
||||
m_core_solver = new lp_primal_core_solver<T, X>(*this->m_A,
|
||||
this->m_b,
|
||||
this->m_x,
|
||||
this->m_basis,
|
||||
this->m_costs,
|
||||
this->m_column_types,
|
||||
m_low_bounds,
|
||||
this->m_upper_bounds,
|
||||
this->m_settings, this->m_name_map);
|
||||
m_core_solver->solve();
|
||||
this->m_status = m_core_solver->m_status;
|
||||
this->m_total_iterations = m_core_solver->m_total_iterations;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> lp_primal_simplex<T, X>::~lp_primal_simplex() {
|
||||
if (m_core_solver != nullptr) {
|
||||
delete m_core_solver;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_primal_simplex<T, X>::bounds_hold(std::unordered_map<std::string, T> const & solution) {
|
||||
for (auto it : this->m_columns) {
|
||||
auto sol_it = solution.find(it.second->get_name());
|
||||
if (sol_it == solution.end()) {
|
||||
throw exception(sstream() << "cannot find column " << it.first << " in solutio");
|
||||
}
|
||||
|
||||
if (!it.second->bounds_hold(sol_it->second)) {
|
||||
std::cout << "bounds do not hold for " << it.second->get_name() << std::endl;
|
||||
it.second->bounds_hold(sol_it->second);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_primal_simplex<T, X>::get_row_value(unsigned i, std::unordered_map<std::string, T> const & solution, bool print) {
|
||||
auto it = this->m_A_values.find(i);
|
||||
if (it == this->m_A_values.end()) {
|
||||
throw exception(sstream() << "cannot find row " << i);
|
||||
}
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto & pair : it->second) {
|
||||
auto cit = this->m_columns.find(pair.first);
|
||||
if (cit == this->m_columns.end()){
|
||||
std::cout << "cannot find column " << pair.first << std::endl;
|
||||
}
|
||||
|
||||
column_info<T> * ci = cit->second;
|
||||
auto sol_it = solution.find(ci->get_name());
|
||||
if (sol_it == solution.end()) {
|
||||
std::cout << "cannot find in the solution column " << ci->get_name() << std::endl;
|
||||
}
|
||||
T column_val = sol_it->second;
|
||||
if (print) {
|
||||
std::cout << pair.second << "(" << ci->get_name() << "=" << column_val << ") ";
|
||||
}
|
||||
ret += pair.second * column_val;
|
||||
}
|
||||
if (print) {
|
||||
std::cout << " = " << ret << std::endl;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_primal_simplex<T, X>::row_constraint_holds(unsigned i, std::unordered_map<std::string, T> const & solution, bool print) {
|
||||
T row_val = get_row_value(i, solution, print);
|
||||
auto & constraint = this->m_constraints[i];
|
||||
T rs = constraint.m_rs;
|
||||
switch (constraint.m_relation) {
|
||||
case Equal:
|
||||
if (fabs(numeric_traits<T>::get_double(row_val - rs)) > 0.00001) {
|
||||
if (print) {
|
||||
std::cout << "should be = " << rs << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case Greater_or_equal:
|
||||
if (numeric_traits<T>::get_double(row_val - rs) < -0.00001) {
|
||||
if (print) {
|
||||
std::cout << "should be >= " << rs << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;;
|
||||
|
||||
case Less_or_equal:
|
||||
if (numeric_traits<T>::get_double(row_val - rs) > 0.00001) {
|
||||
if (print) {
|
||||
std::cout << "should be <= " << rs << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;;
|
||||
}
|
||||
lean_unreachable();
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_primal_simplex<T, X>::row_constraints_hold(std::unordered_map<std::string, T> const & solution) {
|
||||
for (auto it : this->m_A_values) {
|
||||
if (!row_constraint_holds(it.first, solution, false)) {
|
||||
row_constraint_holds(it.first, solution, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_primal_simplex<T, X>::get_current_cost() const {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto it : this->m_columns) {
|
||||
ret += this->get_column_cost_value(it.first, it.second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
@ -23,64 +23,21 @@ class lp_primal_simplex: public lp_solver<T, X> {
|
|||
private:
|
||||
unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); }
|
||||
|
||||
void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) {
|
||||
unsigned slack_var = original_number_of_columns;
|
||||
unsigned artificial = original_number_of_columns + this->m_slacks;
|
||||
void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns);
|
||||
|
||||
for (unsigned row = 0; row < this->row_count(); row++) {
|
||||
fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial);
|
||||
}
|
||||
}
|
||||
void init_buffer(unsigned k, std::vector<T> & r);
|
||||
|
||||
void init_buffer(unsigned k, std::vector<T> & r) {
|
||||
for (unsigned i = 0; i < k; i++) {
|
||||
r[i] = 0;
|
||||
}
|
||||
r[k] = 1;
|
||||
for (unsigned i = this->row_count() -1; i > k; i--) {
|
||||
r[i] = 0;
|
||||
}
|
||||
}
|
||||
void refactor();
|
||||
|
||||
void refactor() {
|
||||
m_core_solver->init_lu();
|
||||
if (m_core_solver->factorization()->get_status() != LU_status::OK) {
|
||||
throw exception("cannot refactor");
|
||||
}
|
||||
}
|
||||
void set_scaled_costs();
|
||||
|
||||
void set_scaled_costs() {
|
||||
unsigned j = this->number_of_core_structurals();
|
||||
while (j-- > 0) {
|
||||
this->set_scaled_cost(j);
|
||||
}
|
||||
}
|
||||
|
||||
void stage_two() {
|
||||
std::cout << "starting stage 2" << std::endl;
|
||||
lean_assert(!m_core_solver->A_mult_x_is_off());
|
||||
int j = this->m_A->column_count() - 1;
|
||||
unsigned core_solver_cols = this->number_of_core_structurals();
|
||||
|
||||
while (j >= core_solver_cols) {
|
||||
this->m_costs[j--] = numeric_traits<T>::zero();
|
||||
}
|
||||
|
||||
set_scaled_costs();
|
||||
m_core_solver->set_status(lp_status::FEASIBLE);
|
||||
this->m_second_stage_iterations = m_core_solver->solve();
|
||||
this->m_status = m_core_solver->get_status();
|
||||
// std::cout << "status is " << lp_status_to_string(this->m_status) << std::endl;
|
||||
}
|
||||
void stage_two();
|
||||
public:
|
||||
lp_primal_simplex() {}
|
||||
|
||||
|
||||
|
||||
column_info<T> * get_or_create_column_info(unsigned column) {
|
||||
auto it = this->m_columns.find(column);
|
||||
return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info<T>) : it->second;
|
||||
}
|
||||
column_info<T> * get_or_create_column_info(unsigned column);
|
||||
|
||||
void set_status(lp_status status) {
|
||||
this->m_status = status;
|
||||
|
|
@ -90,327 +47,48 @@ public:
|
|||
return this->m_status;
|
||||
}
|
||||
|
||||
void fill_acceptable_values_for_x() {
|
||||
for (auto t : this->m_core_solver_columns_to_external_columns) {
|
||||
this->m_x[t.first] = numeric_traits<T>::zero();
|
||||
lean_assert(this->m_x[t.first] >= 0);
|
||||
}
|
||||
}
|
||||
void fill_acceptable_values_for_x();
|
||||
|
||||
|
||||
void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) {
|
||||
bound_is_set[i] = true;
|
||||
bounds[i] = numeric_traits<T>::zero();
|
||||
}
|
||||
void set_zero_bound(bool * bound_is_set, T * bounds, unsigned i);
|
||||
|
||||
void fill_costs_and_x_for_first_stage_solver_for_row(
|
||||
int row,
|
||||
unsigned & slack_var,
|
||||
unsigned & artificial) {
|
||||
lean_assert(row >= 0 && row < this->row_count());
|
||||
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]];
|
||||
// we need to bring the program to the form Ax = b
|
||||
T rs = this->m_b[row];
|
||||
T artificial_cost = - numeric_traits<T>::one();
|
||||
switch (constraint.m_relation) {
|
||||
case Equal: // no slack variable here
|
||||
this->m_column_types[artificial] = low_bound;
|
||||
this->m_costs[artificial] = artificial_cost; // we are maximizing, so the artificial, which is non-negatiive, will be pushed to zero
|
||||
this->m_basis[row] = artificial;
|
||||
if (rs >= 0) {
|
||||
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
|
||||
this->m_x[artificial] = rs;
|
||||
} else {
|
||||
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
|
||||
this->m_x[artificial] = - rs;
|
||||
}
|
||||
artificial++;
|
||||
break;
|
||||
|
||||
case Greater_or_equal:
|
||||
this->m_column_types[slack_var] = low_bound;
|
||||
(*this->m_A)(row, slack_var) = - numeric_traits<T>::one();
|
||||
|
||||
if (rs > 0) {
|
||||
lean_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
|
||||
// adding one artificial
|
||||
this->m_column_types[artificial] = low_bound;
|
||||
(*this->m_A)(row, artificial) = numeric_traits<T>::one();
|
||||
this->m_costs[artificial] = artificial_cost;
|
||||
this->m_basis[row] = artificial;
|
||||
this->m_x[artificial] = rs;
|
||||
artificial++;
|
||||
} else {
|
||||
// we can put a slack_var into the basis, and avoid adding an artificial variable
|
||||
this->m_basis[row] = slack_var;
|
||||
this->m_x[slack_var] = - rs;
|
||||
}
|
||||
slack_var++;
|
||||
break;
|
||||
case Less_or_equal:
|
||||
// introduce a non-negative slack variable
|
||||
this->m_column_types[slack_var] = low_bound;
|
||||
(*this->m_A)(row, slack_var) = numeric_traits<T>::one();
|
||||
|
||||
if (rs < 0) {
|
||||
// adding one artificial
|
||||
lean_assert(numeric_traits<T>::is_zero(this->m_x[slack_var]));
|
||||
this->m_column_types[artificial] = low_bound;
|
||||
(*this->m_A)(row, artificial) = - numeric_traits<T>::one();
|
||||
this->m_costs[artificial] = artificial_cost;
|
||||
this->m_x[artificial] = - rs;
|
||||
this->m_basis[row] = artificial++;
|
||||
} else {
|
||||
// we can put slack_var into the basis, and avoid adding an artificial variable
|
||||
this->m_basis[row] = slack_var;
|
||||
this->m_x[slack_var] = rs;
|
||||
}
|
||||
slack_var++;
|
||||
break;
|
||||
}
|
||||
}
|
||||
unsigned & artificial);
|
||||
|
||||
|
||||
|
||||
std::string name_of_core_solver_column(unsigned j) { // j here is the core solver index
|
||||
unsigned external_j = this->m_core_solver_columns_to_external_columns[j];
|
||||
auto t = this->m_columns.find(external_j);
|
||||
if (t == this->m_columns.end()) {
|
||||
return std::string("name_not_found");
|
||||
}
|
||||
return t->m_name;
|
||||
}
|
||||
std::string name_of_core_solver_column(unsigned j);
|
||||
|
||||
|
||||
void set_core_solver_bounds() {
|
||||
unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials;
|
||||
this->m_column_types.resize(total_vars);
|
||||
this->m_upper_bounds.resize(total_vars);
|
||||
for (auto cit : this->m_columns) {
|
||||
column_info<T> * ci = cit.second;
|
||||
auto p = this->m_external_columns_to_core_solver_columns.find(cit.first);
|
||||
if (p == this->m_external_columns_to_core_solver_columns.end()) continue;
|
||||
unsigned j = p->second;
|
||||
switch (this->m_column_types[j] = ci->get_column_type()){
|
||||
case fixed:
|
||||
this->m_upper_bounds[j] = numeric_traits<T>::zero();
|
||||
break;
|
||||
case boxed:
|
||||
this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j];
|
||||
break;
|
||||
|
||||
default: break; // do nothing
|
||||
}
|
||||
}
|
||||
}
|
||||
void set_core_solver_bounds();
|
||||
|
||||
void update_time_limit_from_starting_time(int start_time) {
|
||||
this->m_settings.time_limit -= (get_millisecond_span(start_time) / 1000.);
|
||||
}
|
||||
|
||||
void find_maximal_solution() {
|
||||
int preprocessing_start_time = get_millisecond_count();
|
||||
if (this->problem_is_empty()) {
|
||||
this->m_status = lp_status::EMPTY;
|
||||
return;
|
||||
}
|
||||
void find_maximal_solution();
|
||||
|
||||
this->cleanup();
|
||||
this->fill_matrix_A_and_init_right_side();
|
||||
if (this->m_status == lp_status::INFEASIBLE) {
|
||||
return;
|
||||
}
|
||||
this->m_x.resize(this->m_A->column_count());
|
||||
this->fill_m_b();
|
||||
this->scale();
|
||||
fill_acceptable_values_for_x();
|
||||
this->count_slacks_and_artificials();
|
||||
set_core_solver_bounds();
|
||||
update_time_limit_from_starting_time(preprocessing_start_time);
|
||||
solve_with_total_inf();
|
||||
}
|
||||
void fill_A_x_and_basis_for_stage_one_total_inf();
|
||||
|
||||
void fill_A_x_and_basis_for_stage_one_total_inf() {
|
||||
for (unsigned row = 0; row < this->row_count(); row++)
|
||||
fill_A_x_and_basis_for_stage_one_total_inf_for_row(row);
|
||||
}
|
||||
void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row);
|
||||
|
||||
void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) {
|
||||
lean_assert(row < this->row_count());
|
||||
auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row);
|
||||
lean_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end());
|
||||
unsigned ext_row = ext_row_it->second;
|
||||
auto constr_it = this->m_constraints.find(ext_row);
|
||||
lean_assert(constr_it != this->m_constraints.end());
|
||||
auto & constraint = constr_it->second;
|
||||
unsigned j = this->m_A->column_count(); // j is a slack variable
|
||||
this->m_A->add_column();
|
||||
// we need to bring the program to the form Ax = b
|
||||
this->m_basis[row] = j;
|
||||
switch (constraint.m_relation) {
|
||||
case Equal:
|
||||
this->m_x[j] = this->m_b[row];
|
||||
(*this->m_A)(row, j) = numeric_traits<T>::one();
|
||||
this->m_column_types[j] = fixed;
|
||||
this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>();
|
||||
break;
|
||||
|
||||
case Greater_or_equal:
|
||||
this->m_x[j] = - this->m_b[row];
|
||||
(*this->m_A)(row, j) = - numeric_traits<T>::one();
|
||||
this->m_column_types[j] = low_bound;
|
||||
this->m_upper_bounds[j] = zero_of_type<X>();
|
||||
break;
|
||||
case Less_or_equal:
|
||||
this->m_x[j] = this->m_b[row];
|
||||
(*this->m_A)(row, j) = numeric_traits<T>::one();
|
||||
this->m_column_types[j] = low_bound;
|
||||
this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void solve_with_total_inf() {
|
||||
std::cout << "starting solve_with_total_inf()" << std::endl;
|
||||
int total_vars = this->m_A->column_count() + this->row_count();
|
||||
m_low_bounds.clear();
|
||||
m_low_bounds.resize(total_vars, zero_of_type<X>()); // low bounds are shifted ot zero
|
||||
this->m_x.resize(total_vars, numeric_traits<T>::zero());
|
||||
this->m_basis.resize(this->row_count());
|
||||
this->m_costs.clear();
|
||||
this->m_costs.resize(total_vars, zero_of_type<T>());
|
||||
fill_A_x_and_basis_for_stage_one_total_inf();
|
||||
this->print_statistics_on_A();
|
||||
this->fill_column_names_for_core_solver();
|
||||
int j = this->m_A->column_count() - 1;
|
||||
unsigned core_solver_cols = this->number_of_core_structurals();
|
||||
|
||||
while (j >= core_solver_cols)
|
||||
this->m_costs[j--] = numeric_traits<T>::zero();
|
||||
|
||||
set_scaled_costs();
|
||||
m_core_solver = new lp_primal_core_solver<T, X>(*this->m_A,
|
||||
this->m_b,
|
||||
this->m_x,
|
||||
this->m_basis,
|
||||
this->m_costs,
|
||||
this->m_column_types,
|
||||
m_low_bounds,
|
||||
this->m_upper_bounds,
|
||||
this->m_settings, this->m_name_map);
|
||||
m_core_solver->solve();
|
||||
this->m_status = m_core_solver->m_status;
|
||||
this->m_total_iterations = m_core_solver->m_total_iterations;
|
||||
}
|
||||
void solve_with_total_inf();
|
||||
|
||||
|
||||
~lp_primal_simplex() {
|
||||
if (m_core_solver != nullptr) {
|
||||
delete m_core_solver;
|
||||
}
|
||||
}
|
||||
~lp_primal_simplex();
|
||||
|
||||
bool bounds_hold(std::unordered_map<std::string, T> const & solution) {
|
||||
for (auto it : this->m_columns) {
|
||||
auto sol_it = solution.find(it.second->get_name());
|
||||
if (sol_it == solution.end()) {
|
||||
throw exception(sstream() << "cannot find column " << it.first << " in solutio");
|
||||
}
|
||||
bool bounds_hold(std::unordered_map<std::string, T> const & solution);
|
||||
|
||||
if (!it.second->bounds_hold(sol_it->second)) {
|
||||
std::cout << "bounds do not hold for " << it.second->get_name() << std::endl;
|
||||
it.second->bounds_hold(sol_it->second);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
T get_row_value(unsigned i, std::unordered_map<std::string, T> const & solution, bool print);
|
||||
|
||||
T get_row_value(unsigned i, std::unordered_map<std::string, T> const & solution, bool print) {
|
||||
auto it = this->m_A_values.find(i);
|
||||
if (it == this->m_A_values.end()) {
|
||||
throw exception(sstream() << "cannot find row " << i);
|
||||
}
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto & pair : it->second) {
|
||||
auto cit = this->m_columns.find(pair.first);
|
||||
if (cit == this->m_columns.end()){
|
||||
std::cout << "cannot find column " << pair.first << std::endl;
|
||||
}
|
||||
bool row_constraint_holds(unsigned i, std::unordered_map<std::string, T> const & solution, bool print);
|
||||
|
||||
column_info<T> * ci = cit->second;
|
||||
auto sol_it = solution.find(ci->get_name());
|
||||
if (sol_it == solution.end()) {
|
||||
std::cout << "cannot find in the solution column " << ci->get_name() << std::endl;
|
||||
}
|
||||
T column_val = sol_it->second;
|
||||
if (print) {
|
||||
std::cout << pair.second << "(" << ci->get_name() << "=" << column_val << ") ";
|
||||
}
|
||||
ret += pair.second * column_val;
|
||||
}
|
||||
if (print) {
|
||||
std::cout << " = " << ret << std::endl;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool row_constraint_holds(unsigned i, std::unordered_map<std::string, T> const & solution, bool print) {
|
||||
T row_val = get_row_value(i, solution, print);
|
||||
auto & constraint = this->m_constraints[i];
|
||||
T rs = constraint.m_rs;
|
||||
switch (constraint.m_relation) {
|
||||
case Equal:
|
||||
if (fabs(numeric_traits<T>::get_double(row_val - rs)) > 0.00001) {
|
||||
if (print) {
|
||||
std::cout << "should be = " << rs << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
case Greater_or_equal:
|
||||
if (numeric_traits<T>::get_double(row_val - rs) < -0.00001) {
|
||||
if (print) {
|
||||
std::cout << "should be >= " << rs << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;;
|
||||
|
||||
case Less_or_equal:
|
||||
if (numeric_traits<T>::get_double(row_val - rs) > 0.00001) {
|
||||
if (print) {
|
||||
std::cout << "should be <= " << rs << std::endl;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
return true;;
|
||||
}
|
||||
lean_unreachable();
|
||||
}
|
||||
|
||||
bool row_constraints_hold(std::unordered_map<std::string, T> const & solution) {
|
||||
for (auto it : this->m_A_values) {
|
||||
if (!row_constraint_holds(it.first, solution, false)) {
|
||||
row_constraint_holds(it.first, solution, true);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool row_constraints_hold(std::unordered_map<std::string, T> const & solution);
|
||||
|
||||
|
||||
T * get_array_from_map(std::unordered_map<std::string, T> const & solution) {
|
||||
T * t = new T[solution.size()];
|
||||
for (auto it : solution) {
|
||||
auto g = this->m_names_to_columns.find(it.first);
|
||||
if (g == this->m_names_to_columns.end()) {
|
||||
throw exception(sstream() << "cannot find name " << it.first);
|
||||
}
|
||||
t[g->second] = it.second;
|
||||
}
|
||||
return t;
|
||||
}
|
||||
T * get_array_from_map(std::unordered_map<std::string, T> const & solution);
|
||||
|
||||
bool solution_is_feasible(std::unordered_map<std::string, T> const & solution) {
|
||||
return bounds_hold(solution) && row_constraints_hold(solution);
|
||||
|
|
@ -420,12 +98,6 @@ public:
|
|||
return this->get_column_value_with_core_solver(column, m_core_solver);
|
||||
}
|
||||
|
||||
T get_current_cost() const {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto it : this->m_columns) {
|
||||
ret += this->get_column_cost_value(it.first, it.second);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
T get_current_cost() const;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
15
src/util/lp/lp_primal_simplex_instances.cpp
Normal file
15
src/util/lp/lp_primal_simplex_instances.cpp
Normal file
|
|
@ -0,0 +1,15 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_primal_simplex.cpp"
|
||||
template bool lean::lp_primal_simplex<double, double>::bounds_hold(std::unordered_map<std::string, double, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, double> > > const&);
|
||||
template bool lean::lp_primal_simplex<double, double>::row_constraints_hold(std::unordered_map<std::string, double, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, double> > > const&);
|
||||
template double lean::lp_primal_simplex<double, double>::get_current_cost() const;
|
||||
template lean::lp_primal_simplex<double, double>::~lp_primal_simplex();
|
||||
template lean::lp_primal_simplex<lean::mpq, lean::mpq>::~lp_primal_simplex();
|
||||
template lean::mpq lean::lp_primal_simplex<lean::mpq, lean::mpq>::get_current_cost() const;
|
||||
template void lean::lp_primal_simplex<double, double>::find_maximal_solution();
|
||||
template void lean::lp_primal_simplex<lean::mpq, lean::mpq>::find_maximal_solution();
|
||||
149
src/util/lp/lp_settings.cpp
Normal file
149
src/util/lp/lp_settings.cpp
Normal file
|
|
@ -0,0 +1,149 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/numerics/double.h"
|
||||
#include "util/lp/lp_settings.h"
|
||||
namespace lean {
|
||||
std::string column_type_to_string(column_type t) {
|
||||
switch (t) {
|
||||
case fixed:
|
||||
return std::string("fixed");
|
||||
case boxed:
|
||||
return std::string("boxed");
|
||||
case low_bound:
|
||||
return std::string("low_bound");
|
||||
case upper_bound:
|
||||
return std::string("upper_bound");
|
||||
case free_column:
|
||||
return std::string("free_column");
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
const char* lp_status_to_string(lp_status status) {
|
||||
switch (status) {
|
||||
case UNKNOWN: return "UNKNOWN";
|
||||
case INFEASIBLE: return "INFEASIBLE";
|
||||
case UNBOUNDED: return "UNBOUNDED";
|
||||
case TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED";
|
||||
case DUAL_UNBOUNDED: return "DUAL_UNBOUNDED";
|
||||
case OPTIMAL: return "OPTIMAL";
|
||||
case FEASIBLE: return "FEASIBLE";
|
||||
case FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR";
|
||||
case TIME_EXHAUSTED: return "TIME_EXHAUSTED";
|
||||
case ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED";
|
||||
case EMPTY: return "EMPTY";
|
||||
case UNSTABLE: return "UNSTABLE";
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
|
||||
lp_status lp_status_from_string(std::string status) {
|
||||
if (status == "UNKNOWN") return lp_status::UNKNOWN;
|
||||
if (status == "INFEASIBLE") return lp_status::INFEASIBLE;
|
||||
if (status == "UNBOUNDED") return lp_status::UNBOUNDED;
|
||||
if (status == "OPTIMAL") return lp_status::OPTIMAL;
|
||||
if (status == "FEASIBLE") return lp_status::FEASIBLE;
|
||||
if (status == "FLOATING_POINT_ERROR") return lp_status::FLOATING_POINT_ERROR;
|
||||
if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED;
|
||||
if (status == "ITERATIONS_EXHAUSTED") return lp_status::ITERATIONS_EXHAUSTED;
|
||||
if (status == "EMPTY") return lp_status::EMPTY;
|
||||
lean_unreachable();
|
||||
}
|
||||
int get_millisecond_count() {
|
||||
timeb tb;
|
||||
ftime(&tb);
|
||||
return tb.millitm + (tb.time & 0xfffff) * 1000;
|
||||
}
|
||||
|
||||
int get_millisecond_span(int start_time) {
|
||||
int span = get_millisecond_count() - start_time;
|
||||
if (span < 0)
|
||||
span += 0x100000 * 1000;
|
||||
return span;
|
||||
}
|
||||
void my_random_init(unsigned * seed) {
|
||||
#ifdef LEAN_WINDOWS
|
||||
srand(*seed);
|
||||
#else
|
||||
rand_r(seed);
|
||||
#endif
|
||||
}
|
||||
unsigned my_random() {
|
||||
#ifdef LEAN_WINDOWS
|
||||
return rand();
|
||||
#else
|
||||
return lrand48();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool vectors_are_equal(T * a, std::vector<T> &b, unsigned n) {
|
||||
if (numeric_traits<T>::precise()) {
|
||||
for (unsigned i = 0; i < n; i ++){
|
||||
if (!numeric_traits<T>::is_zero(a[i] - b[i])) {
|
||||
std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = 0; i < n; i ++){
|
||||
if (fabs(numeric_traits<T>::get_double(a[i] - b[i])) > 0.000001) {
|
||||
std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool vectors_are_equal(const std::vector<T> & a, const buffer<T> &b) {
|
||||
unsigned n = a.size();
|
||||
if (n != b.size()) return false;
|
||||
if (numeric_traits<T>::precise()) {
|
||||
for (unsigned i = 0; i < n; i ++){
|
||||
if (!numeric_traits<T>::is_zero(a[i] - b[i])) {
|
||||
std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = 0; i < n; i ++){
|
||||
if (fabs(numeric_traits<T>::get_double(a[i] - b[i])) > 0.000001) {
|
||||
std::cout << "a[" << i <<"] = " << a[i] << ", but " << "b[" << i <<"] = " << b[i] << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool vectors_are_equal(const std::vector<T> & a, const std::vector<T> &b) {
|
||||
unsigned n = a.size();
|
||||
if (n != b.size()) return false;
|
||||
if (numeric_traits<T>::precise()) {
|
||||
for (unsigned i = 0; i < n; i ++){
|
||||
if (!numeric_traits<T>::is_zero(a[i] - b[i])) {
|
||||
std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for (unsigned i = 0; i < n; i ++){
|
||||
if (fabs(numeric_traits<T>::get_double(a[i] - b[i])) > 0.000001) {
|
||||
std::cout << "a[" << i <<"] = " << a[i] << ", but " << "b[" << i <<"] = " << b[i] << std::endl;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -10,7 +10,10 @@
|
|||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <sys/timeb.h>
|
||||
|
||||
#include "util/debug.h"
|
||||
#include "util/numerics/numeric_traits.h"
|
||||
#include "util/sstream.h"
|
||||
#include "util/numerics/mpq.h"
|
||||
|
||||
namespace lean {
|
||||
|
||||
|
|
@ -22,22 +25,7 @@ enum column_type {
|
|||
free_column
|
||||
};
|
||||
|
||||
inline std::string column_type_to_string(column_type t) {
|
||||
switch (t) {
|
||||
case fixed:
|
||||
return std::string("fixed");
|
||||
case boxed:
|
||||
return std::string("boxed");
|
||||
case low_bound:
|
||||
return std::string("low_bound");
|
||||
case upper_bound:
|
||||
return std::string("upper_bound");
|
||||
case free_column:
|
||||
return std::string("free_column");
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
std::string column_type_to_string(column_type t);
|
||||
|
||||
enum lp_status {
|
||||
UNKNOWN,
|
||||
|
|
@ -55,37 +43,9 @@ enum lp_status {
|
|||
UNSTABLE
|
||||
};
|
||||
|
||||
inline const char* lp_status_to_string(lp_status status) {
|
||||
switch (status) {
|
||||
case UNKNOWN: return "UNKNOWN";
|
||||
case INFEASIBLE: return "INFEASIBLE";
|
||||
case UNBOUNDED: return "UNBOUNDED";
|
||||
case TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED";
|
||||
case DUAL_UNBOUNDED: return "DUAL_UNBOUNDED";
|
||||
case OPTIMAL: return "OPTIMAL";
|
||||
case FEASIBLE: return "FEASIBLE";
|
||||
case FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR";
|
||||
case TIME_EXHAUSTED: return "TIME_EXHAUSTED";
|
||||
case ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED";
|
||||
case EMPTY: return "EMPTY";
|
||||
case UNSTABLE: return "UNSTABLE";
|
||||
default:
|
||||
lean_unreachable();
|
||||
}
|
||||
}
|
||||
const char* lp_status_to_string(lp_status status);
|
||||
|
||||
inline lp_status lp_status_from_string(std::string status) {
|
||||
if (status == "UNKNOWN") return lp_status::UNKNOWN;
|
||||
if (status == "INFEASIBLE") return lp_status::INFEASIBLE;
|
||||
if (status == "UNBOUNDED") return lp_status::UNBOUNDED;
|
||||
if (status == "OPTIMAL") return lp_status::OPTIMAL;
|
||||
if (status == "FEASIBLE") return lp_status::FEASIBLE;
|
||||
if (status == "FLOATING_POINT_ERROR") return lp_status::FLOATING_POINT_ERROR;
|
||||
if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED;
|
||||
if (status == "ITERATIONS_EXHAUSTED") return lp_status::ITERATIONS_EXHAUSTED;
|
||||
if (status == "EMPTY") return lp_status::EMPTY;
|
||||
lean_unreachable();
|
||||
}
|
||||
lp_status lp_status_from_string(std::string status);
|
||||
|
||||
enum non_basic_column_value_position { at_low_bound, at_upper_bound, at_fixed, free_of_bounds };
|
||||
|
||||
|
|
@ -198,17 +158,55 @@ struct lp_settings {
|
|||
bool dense_deb;
|
||||
static unsigned ddd; // used for debugging
|
||||
#endif
|
||||
};
|
||||
int get_millisecond_count() {
|
||||
timeb tb;
|
||||
ftime(&tb);
|
||||
return tb.millitm + (tb.time & 0xfffff) * 1000;
|
||||
}; // end of lp_settings class
|
||||
|
||||
int get_millisecond_count();
|
||||
int get_millisecond_span(int start_time);
|
||||
void my_random_init(unsigned * seed);
|
||||
unsigned my_random();
|
||||
|
||||
template <typename T>
|
||||
std::string T_to_string(const T & t) {
|
||||
std::ostringstream strs;
|
||||
strs << t;
|
||||
return strs.str();
|
||||
}
|
||||
|
||||
int get_millisecond_span(int start_time) {
|
||||
int span = get_millisecond_count() - start_time;
|
||||
if (span < 0)
|
||||
span += 0x100000 * 1000;
|
||||
return span;
|
||||
inline std::string T_to_string(const mpq & t) {
|
||||
std::ostringstream strs;
|
||||
strs << t.get_double();
|
||||
return strs.str();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool val_is_smaller_than_eps(T const & t, double const & eps) {
|
||||
if (!numeric_traits<T>::precise()) {
|
||||
return numeric_traits<T>::get_double(t) < eps;
|
||||
}
|
||||
return t <= numeric_traits<T>::zero();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool vectors_are_equal(T * a, std::vector<T> &b, unsigned n);
|
||||
|
||||
template <typename T>
|
||||
bool vectors_are_equal(const std::vector<T> & a, const buffer<T> &b);
|
||||
|
||||
template <typename T>
|
||||
bool vectors_are_equal(const std::vector<T> & a, const std::vector<T> &b);
|
||||
|
||||
template <typename T>
|
||||
T abs (T const & v) { return v >= zero_of_type<T>() ? v : -v; }
|
||||
|
||||
template <typename X>
|
||||
X max_abs_in_vector(std::vector<X>& t){
|
||||
X r(zero_of_type<X>());
|
||||
for (auto & v : t)
|
||||
r = std::max(abs(v) , r);
|
||||
return r;
|
||||
}
|
||||
inline void print_blanks(int n) {
|
||||
while (n--) {std::cout << ' '; }
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
8
src/util/lp/lp_settings_instances.cpp
Normal file
8
src/util/lp/lp_settings_instances.cpp
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lp_settings.cpp"
|
||||
template bool lean::vectors_are_equal<double>(std::vector<double, std::allocator<double> > const&, std::vector<double, std::allocator<double> > const&);
|
||||
547
src/util/lp/lp_solver.cpp
Normal file
547
src/util/lp/lp_solver.cpp
Normal file
|
|
@ -0,0 +1,547 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/lp_solver.h"
|
||||
namespace lean {
|
||||
template <typename T, typename X> column_info<T> * lp_solver<T, X>::get_or_create_column_info(unsigned column) {
|
||||
auto it = m_columns.find(column);
|
||||
return (it == m_columns.end())? ( m_columns[column] = new column_info<T>) : it->second;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> T lp_solver<T, X>::get_column_cost_value(unsigned j, column_info<T> * ci) const {
|
||||
if (ci->is_fixed()) {
|
||||
return ci->get_cost() * ci->get_fixed_value();
|
||||
}
|
||||
return ci->get_cost() * get_column_value(j);
|
||||
}
|
||||
template <typename T, typename X> void lp_solver<T, X>::add_constraint(lp_relation relation, T right_side, unsigned row_index) {
|
||||
lean_assert(m_constraints.find(row_index) == m_constraints.end());
|
||||
lp_constraint<T, X> cs(right_side, relation);
|
||||
m_constraints[row_index] = cs;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::give_symbolic_name_to_column(std::string name, unsigned column) {
|
||||
auto it = m_columns.find(column);
|
||||
column_info<T> *ci;
|
||||
if (it == m_columns.end()){
|
||||
m_columns[column] = ci = new column_info<T>;
|
||||
} else {
|
||||
ci = it->second;
|
||||
}
|
||||
ci->set_name(name);
|
||||
m_names_to_columns[name] = column;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> T lp_solver<T, X>::get_column_value_by_name(std::string name) const {
|
||||
auto it = m_names_to_columns.find(name);
|
||||
if (it == m_names_to_columns.end()) {
|
||||
throw exception(sstream() << "get_column_value_by_name " << name);
|
||||
}
|
||||
return get_column_value(it -> second);
|
||||
}
|
||||
|
||||
// returns -1 if not found
|
||||
template <typename T, typename X> int lp_solver<T, X>::get_column_index_by_name(std::string name) const {
|
||||
auto t = m_names_to_columns.find(name);
|
||||
if (t == m_names_to_columns.end()) {
|
||||
return -1;
|
||||
}
|
||||
return t->second;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> lp_solver<T, X>::~lp_solver(){
|
||||
if (m_A != nullptr) {
|
||||
delete m_A;
|
||||
}
|
||||
for (auto t : m_columns) {
|
||||
delete t.second;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::flip_costs() {
|
||||
for (auto t : m_columns) {
|
||||
column_info<T> *ci = t.second;
|
||||
ci->set_cost(-ci->get_cost());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_solver<T, X>::problem_is_empty() {
|
||||
for (auto & c : m_A_values)
|
||||
if (c.second.size())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::scale() {
|
||||
if (numeric_traits<T>::precise() || m_settings.use_scaling == false) {
|
||||
m_column_scale.clear();
|
||||
m_column_scale.resize(m_A->column_count(), one_of_type<T>());
|
||||
return;
|
||||
}
|
||||
|
||||
T smin = T(m_settings.scaling_minimum);
|
||||
T smax = T(m_settings.scaling_maximum);
|
||||
|
||||
scaler<T, X> scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings);
|
||||
if (!scaler.scale()) {
|
||||
unscale();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::print_rows_scale_stats() {
|
||||
std::cout << "rows max" << std::endl;
|
||||
for (unsigned i = 0; i < m_A->row_count(); i++) {
|
||||
print_row_scale_stats(i);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::print_columns_scale_stats() {
|
||||
std::cout << "columns max" << std::endl;
|
||||
for (unsigned i = 0; i < m_A->column_count(); i++) {
|
||||
print_column_scale_stats(i);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::print_row_scale_stats(unsigned i) {
|
||||
std::cout << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " ";
|
||||
std::cout << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")";
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::print_column_scale_stats(unsigned j) {
|
||||
std::cout << "(" << m_A->get_min_abs_in_row(j) << " ";
|
||||
std::cout << m_A->get_max_abs_in_column(j) << ")";
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::print_scale_stats() {
|
||||
print_rows_scale_stats();
|
||||
print_columns_scale_stats();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::get_max_abs_in_row(std::unordered_map<unsigned, T> & row_map) {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto jp : row_map) {
|
||||
T ac = numeric_traits<T>::abs(jp->second);
|
||||
if (ac > ret) {
|
||||
ret = ac;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::pin_vars_on_row_with_sign(std::unordered_map<unsigned, T> & row, T sign ) {
|
||||
std::unordered_map<unsigned, T> pinned;
|
||||
for (auto t : row) {
|
||||
unsigned j = t.first;
|
||||
column_info<T> * ci = m_columns[j];
|
||||
T a = t.second;
|
||||
if (a * sign > numeric_traits<T>::zero()) {
|
||||
lean_assert(ci->upper_bound_is_set());
|
||||
ci->set_fixed_value(ci->get_upper_bound());
|
||||
} else {
|
||||
lean_assert(ci->low_bound_is_set());
|
||||
ci->set_fixed_value(ci->get_low_bound());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_solver<T, X>::get_minimal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound) {
|
||||
low_bound = numeric_traits<T>::zero();
|
||||
for (auto & t : row) {
|
||||
T a = t.second;
|
||||
column_info<T> * ci = m_columns[t.first];
|
||||
if (a > numeric_traits<T>::zero()) {
|
||||
if (ci->low_bound_is_set()) {
|
||||
low_bound += ci->get_low_bound() * a;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (ci->upper_bound_is_set()) {
|
||||
low_bound += ci->get_upper_bound() * a;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_solver<T, X>::get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound) {
|
||||
low_bound = numeric_traits<T>::zero();
|
||||
for (auto & t : row) {
|
||||
T a = t.second;
|
||||
column_info<T> * ci = m_columns[t.first];
|
||||
if (a < numeric_traits<T>::zero()) {
|
||||
if (ci->low_bound_is_set()) {
|
||||
low_bound += ci->get_low_bound() * a;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (ci->upper_bound_is_set()) {
|
||||
low_bound += ci->get_upper_bound() * a;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_solver<T, X>::row_is_zero(std::unordered_map<unsigned, T> & row) {
|
||||
for (auto & t : row) {
|
||||
if (!is_zero(t.second))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_solver<T, X>::row_e_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
|
||||
T rs = m_constraints[row_index].m_rs;
|
||||
if (row_is_zero(row)) {
|
||||
if (!is_zero(rs))
|
||||
m_status = INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
T low_bound;
|
||||
bool lb = get_minimal_row_value(row, low_bound);
|
||||
if (lb) {
|
||||
T diff = low_bound - rs;
|
||||
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
|
||||
// low_bound > rs + m_settings.refactor_epsilon
|
||||
m_status = INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
|
||||
pin_vars_down_on_row(row);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
T upper_bound;
|
||||
bool ub = get_maximal_row_value(row, upper_bound);
|
||||
if (ub) {
|
||||
T diff = rs - upper_bound;
|
||||
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) {
|
||||
// upper_bound < rs - m_settings.refactor_tolerance
|
||||
m_status = INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
|
||||
pin_vars_up_on_row(row);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, typename X> int lp_solver<T, X>::row_ge_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
|
||||
T rs = m_constraints[row_index].m_rs;
|
||||
if (row_is_zero(row)) {
|
||||
if (rs > zero_of_type<X>())
|
||||
m_status = INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
T upper_bound;
|
||||
if (get_maximal_row_value(row, upper_bound)) {
|
||||
T diff = rs - upper_bound;
|
||||
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) {
|
||||
// upper_bound < rs - m_settings.refactor_tolerance
|
||||
m_status = INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
|
||||
pin_vars_up_on_row(row);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool lp_solver<T, X>::row_le_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
|
||||
T low_bound;
|
||||
T rs = m_constraints[row_index].m_rs;
|
||||
if (row_is_zero(row)) {
|
||||
if (rs < zero_of_type<X>())
|
||||
m_status = INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (get_minimal_row_value(row, low_bound)) {
|
||||
T diff = low_bound - rs;
|
||||
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
|
||||
// low_bound > rs + m_settings.refactor_tolerance
|
||||
m_status = lp_status::INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
|
||||
pin_vars_down_on_row(row);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
// analyse possible max and min values that are derived from var boundaries
|
||||
// Let us say that the we have a "ge" constraint, and the min value is equal to the rs.
|
||||
// Then we know what values of the variables are. For each positive coeff of the row it has to be
|
||||
// the low boundary of the var and for a negative - the upper.
|
||||
|
||||
// this routing also pins the variables to the boundaries
|
||||
template <typename T, typename X> bool lp_solver<T, X>::row_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index ) {
|
||||
auto & constraint = m_constraints[row_index];
|
||||
switch (constraint.m_relation) {
|
||||
case lp_relation::Equal:
|
||||
return row_e_is_obsolete(row, row_index);
|
||||
|
||||
case lp_relation::Greater_or_equal:
|
||||
return row_ge_is_obsolete(row, row_index);
|
||||
|
||||
case lp_relation::Less_or_equal:
|
||||
return row_le_is_obsolete(row, row_index);
|
||||
}
|
||||
lean_unreachable();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::remove_fixed_or_zero_columns() {
|
||||
for (auto & i_row : m_A_values) {
|
||||
remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map<unsigned, T> & row) {
|
||||
auto & constraint = m_constraints[i];
|
||||
std::vector<unsigned> removed;
|
||||
for (auto & col : row) {
|
||||
unsigned j = col.first;
|
||||
lean_assert(m_columns.find(j) != m_columns.end());
|
||||
column_info<T> * ci = m_columns[j];
|
||||
if (ci->is_fixed()) {
|
||||
removed.push_back(j);
|
||||
T aj = col.second;
|
||||
constraint.m_rs -= aj * ci->get_fixed_value();
|
||||
} else {
|
||||
if (numeric_traits<T>::is_zero(col.second)){
|
||||
removed.push_back(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (auto j : removed) {
|
||||
row.erase(j);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> unsigned lp_solver<T, X>::try_to_remove_some_rows() {
|
||||
std::vector<unsigned> rows_to_delete;
|
||||
for (auto & t : m_A_values) {
|
||||
if (row_is_obsolete(t.second, t.first)) {
|
||||
rows_to_delete.push_back(t.first);
|
||||
}
|
||||
|
||||
if (m_status == lp_status::INFEASIBLE) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (rows_to_delete.size() > 0) {
|
||||
for (unsigned k : rows_to_delete) {
|
||||
m_A_values.erase(k);
|
||||
}
|
||||
}
|
||||
remove_fixed_or_zero_columns();
|
||||
return rows_to_delete.size();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::cleanup() {
|
||||
int n = 0; // number of deleted rows
|
||||
int d;
|
||||
while ((d = try_to_remove_some_rows()))
|
||||
n += d;
|
||||
|
||||
if (n == 1)
|
||||
std::cout << "deleted one row" << std::endl;
|
||||
else if (n)
|
||||
std::cout << "deleted " << n << " rows" << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::map_external_rows_to_core_solver_rows() {
|
||||
unsigned size = 0;
|
||||
for (auto & row : m_A_values) {
|
||||
m_external_rows_to_core_solver_rows[row.first] = size;
|
||||
m_core_solver_rows_to_external_rows[size] = row.first;
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::map_external_columns_to_core_solver_columns() {
|
||||
unsigned size = 0;
|
||||
for (auto & row : m_A_values) {
|
||||
for (auto & col : row.second) {
|
||||
if (col.second == numeric_traits<T>::zero() || m_columns[col.first]->is_fixed()) {
|
||||
throw exception("found fixed column");
|
||||
}
|
||||
unsigned j = col.first;
|
||||
auto j_place = m_external_columns_to_core_solver_columns.find(j);
|
||||
if (j_place == m_external_columns_to_core_solver_columns.end()) { // j is a newcomer
|
||||
m_external_columns_to_core_solver_columns[j] = size;
|
||||
m_core_solver_columns_to_external_columns[size++] = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::fill_column_names_for_core_solver() {
|
||||
for (auto it : this->m_columns) {
|
||||
auto p = this->m_external_columns_to_core_solver_columns.find(it.first);
|
||||
if (p != this->m_external_columns_to_core_solver_columns.end()) {
|
||||
this->m_name_map[p->second] = it.second->get_name();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::unscale() {
|
||||
delete m_A;
|
||||
m_A = nullptr;
|
||||
fill_A_from_A_values();
|
||||
restore_column_scales_to_one();
|
||||
fill_m_b();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::fill_A_from_A_values() {
|
||||
m_A = new static_matrix<T, X>(m_A_values.size(), number_of_core_structurals());
|
||||
for (auto & t : m_A_values) {
|
||||
lean_assert(m_external_rows_to_core_solver_rows.find(t.first) != m_external_rows_to_core_solver_rows.end());
|
||||
unsigned row = m_external_rows_to_core_solver_rows[t.first];
|
||||
for (auto k : t.second) {
|
||||
lean_assert(m_external_columns_to_core_solver_columns.find(k.first) != m_external_columns_to_core_solver_columns.end());
|
||||
unsigned col = m_external_columns_to_core_solver_columns[k.first];
|
||||
bool col_is_flipped = m_columns[k.first]->is_flipped();
|
||||
|
||||
if (!col_is_flipped) {
|
||||
(*m_A)(row, col) = k.second;
|
||||
} else {
|
||||
(*m_A)(row, col) = - k.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::fill_matrix_A_and_init_right_side() {
|
||||
map_external_rows_to_core_solver_rows();
|
||||
map_external_columns_to_core_solver_columns();
|
||||
lean_assert(m_A == nullptr);
|
||||
fill_A_from_A_values();
|
||||
m_b.resize(m_A->row_count());
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::count_slacks_and_artificials() {
|
||||
for (int i = row_count() - 1; i >= 0; i--) {
|
||||
count_slacks_and_artificials_for_row(i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::count_slacks_and_artificials_for_row(unsigned i) {
|
||||
lean_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end());
|
||||
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]];
|
||||
T rs;
|
||||
switch (constraint.m_relation) {
|
||||
case Equal:
|
||||
m_artificials++;
|
||||
break;
|
||||
case Greater_or_equal:
|
||||
m_slacks++;
|
||||
rs = this->m_b[i];
|
||||
if (rs > 0) {
|
||||
m_artificials++;
|
||||
}
|
||||
break;
|
||||
case Less_or_equal:
|
||||
m_slacks++;
|
||||
rs = this->m_b[i];
|
||||
if (rs < 0) {
|
||||
m_artificials++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_solver<T, X>::low_bound_shift_for_row(unsigned i) {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
|
||||
auto row = this->m_A_values.find(i);
|
||||
if (row == this->m_A_values.end()) {
|
||||
throw exception("cannot find row");
|
||||
}
|
||||
for (auto col : row->second) {
|
||||
ret += col.second * this->m_columns[col.first]->get_shift();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::fill_m_b() {
|
||||
for (int i = this->row_count() - 1; i >= 0; i--) {
|
||||
lean_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end());
|
||||
unsigned external_i = this->m_core_solver_rows_to_external_rows[i];
|
||||
auto & constraint = this->m_constraints[external_i];
|
||||
this->m_b[i] = constraint.m_rs - low_bound_shift_for_row(external_i);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> T lp_solver<T, X>::get_column_value_with_core_solver(unsigned column, lp_core_solver_base<T, X> * core_solver) const {
|
||||
auto cit = this->m_columns.find(column);
|
||||
if (cit == this->m_columns.end()) {
|
||||
return numeric_traits<T>::zero();
|
||||
}
|
||||
|
||||
column_info<T> * ci = cit->second;
|
||||
|
||||
if (ci->is_fixed()) {
|
||||
return ci->get_fixed_value();
|
||||
}
|
||||
|
||||
auto t = this->m_external_columns_to_core_solver_columns.find(column);
|
||||
if (t != this->m_external_columns_to_core_solver_columns.end()){
|
||||
unsigned cj = t->second;
|
||||
T v = core_solver->get_var_value(cj) * this->m_column_scale[cj];
|
||||
if (ci->is_free()) {
|
||||
return v;
|
||||
}
|
||||
if (!ci->is_flipped()) {
|
||||
return v + ci->get_low_bound();
|
||||
}
|
||||
|
||||
// the flipped case when there is only upper bound
|
||||
return -v + ci->get_upper_bound(); //
|
||||
}
|
||||
|
||||
return numeric_traits<T>::zero(); // returns zero for out of boundary columns
|
||||
}
|
||||
|
||||
template <typename T, typename X> void lp_solver<T, X>::set_scaled_cost(unsigned j) {
|
||||
// grab original costs but modify it with the column scales
|
||||
lean_assert(j < this->m_column_scale.size());
|
||||
column_info<T> * ci = this->m_columns[this->m_core_solver_columns_to_external_columns[j]];
|
||||
T cost = ci->get_cost();
|
||||
if (ci->is_flipped()){
|
||||
cost *= -1;
|
||||
}
|
||||
lean_assert(ci->is_fixed() == false);
|
||||
this->m_costs[j] = cost * this->m_column_scale[j];
|
||||
}
|
||||
}
|
||||
|
|
@ -34,18 +34,10 @@ struct lp_constraint {
|
|||
|
||||
template <typename T, typename X>
|
||||
class lp_solver {
|
||||
column_info<T> * get_or_create_column_info(unsigned column) {
|
||||
auto it = m_columns.find(column);
|
||||
return (it == m_columns.end())? ( m_columns[column] = new column_info<T>) : it->second;
|
||||
}
|
||||
column_info<T> * get_or_create_column_info(unsigned column);
|
||||
|
||||
protected:
|
||||
T get_column_cost_value(unsigned j, column_info<T> * ci) const {
|
||||
if (ci->is_fixed()) {
|
||||
return ci->get_cost() * ci->get_fixed_value();
|
||||
}
|
||||
return ci->get_cost() * get_column_value(j);
|
||||
}
|
||||
T get_column_cost_value(unsigned j, column_info<T> * ci) const;
|
||||
public:
|
||||
unsigned m_total_iterations;
|
||||
static_matrix<T, X>* m_A = nullptr; // this is the matrix of constraints
|
||||
|
|
@ -77,11 +69,7 @@ public:
|
|||
|
||||
unsigned row_count() const { return this->m_A->row_count(); }
|
||||
|
||||
void add_constraint(lp_relation relation, T right_side, unsigned row_index) {
|
||||
lean_assert(m_constraints.find(row_index) == m_constraints.end());
|
||||
lp_constraint<T, X> cs(right_side, relation);
|
||||
m_constraints[row_index] = cs;
|
||||
}
|
||||
void add_constraint(lp_relation relation, T right_side, unsigned row_index);
|
||||
|
||||
void set_cost_for_column(unsigned column, T column_cost) {
|
||||
get_or_create_column_info(column)->set_cost(column_cost);
|
||||
|
|
@ -93,36 +81,14 @@ public:
|
|||
// returns the current cost
|
||||
virtual T get_current_cost() const = 0;
|
||||
// do not have to call it
|
||||
void give_symbolic_name_to_column(std::string name, unsigned column) {
|
||||
auto it = m_columns.find(column);
|
||||
column_info<T> *ci;
|
||||
if (it == m_columns.end()){
|
||||
m_columns[column] = ci = new column_info<T>;
|
||||
} else {
|
||||
ci = it->second;
|
||||
}
|
||||
ci->set_name(name);
|
||||
m_names_to_columns[name] = column;
|
||||
}
|
||||
void give_symbolic_name_to_column(std::string name, unsigned column);
|
||||
|
||||
virtual T get_column_value(unsigned column) const = 0;
|
||||
|
||||
T get_column_value_by_name(std::string name) const {
|
||||
auto it = m_names_to_columns.find(name);
|
||||
if (it == m_names_to_columns.end()) {
|
||||
throw exception(sstream() << "get_column_value_by_name " << name);
|
||||
}
|
||||
return get_column_value(it -> second);
|
||||
}
|
||||
T get_column_value_by_name(std::string name) const;
|
||||
|
||||
// returns -1 if not found
|
||||
virtual int get_column_index_by_name(std::string name) const {
|
||||
auto t = m_names_to_columns.find(name);
|
||||
if (t == m_names_to_columns.end()) {
|
||||
return -1;
|
||||
}
|
||||
return t->second;
|
||||
}
|
||||
virtual int get_column_index_by_name(std::string name) const;
|
||||
|
||||
void set_low_bound(unsigned i, T bound) {
|
||||
column_info<T> *ci = get_or_create_column_info(i);
|
||||
|
|
@ -155,21 +121,9 @@ public:
|
|||
return m_status;
|
||||
}
|
||||
|
||||
virtual ~lp_solver(){
|
||||
if (m_A != nullptr) {
|
||||
delete m_A;
|
||||
}
|
||||
for (auto t : m_columns) {
|
||||
delete t.second;
|
||||
}
|
||||
}
|
||||
virtual ~lp_solver();
|
||||
|
||||
void flip_costs() {
|
||||
for (auto t : m_columns) {
|
||||
column_info<T> *ci = t.second;
|
||||
ci->set_cost(-ci->get_cost());
|
||||
}
|
||||
}
|
||||
void flip_costs();
|
||||
|
||||
virtual void find_maximal_solution() = 0;
|
||||
void set_time_limit(unsigned time_limit_in_seconds) {
|
||||
|
|
@ -184,71 +138,22 @@ public:
|
|||
return m_settings.max_total_number_of_iterations;
|
||||
}
|
||||
protected:
|
||||
bool problem_is_empty() {
|
||||
for (auto & c : m_A_values)
|
||||
if (c.second.size())
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
bool problem_is_empty();
|
||||
|
||||
void scale() {
|
||||
if (numeric_traits<T>::precise() || m_settings.use_scaling == false) {
|
||||
m_column_scale.clear();
|
||||
m_column_scale.resize(m_A->column_count(), one_of_type<T>());
|
||||
return;
|
||||
}
|
||||
|
||||
T smin = T(m_settings.scaling_minimum);
|
||||
T smax = T(m_settings.scaling_maximum);
|
||||
|
||||
scaler<T, X> scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings);
|
||||
if (!scaler.scale()) {
|
||||
unscale();
|
||||
}
|
||||
}
|
||||
void scale();
|
||||
|
||||
|
||||
void print_rows_scale_stats() {
|
||||
std::cout << "rows max" << std::endl;
|
||||
for (unsigned i = 0; i < m_A->row_count(); i++) {
|
||||
print_row_scale_stats(i);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
void print_rows_scale_stats();
|
||||
|
||||
void print_columns_scale_stats() {
|
||||
std::cout << "columns max" << std::endl;
|
||||
for (unsigned i = 0; i < m_A->column_count(); i++) {
|
||||
print_column_scale_stats(i);
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
void print_columns_scale_stats();
|
||||
|
||||
void print_row_scale_stats(unsigned i) {
|
||||
std::cout << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " ";
|
||||
std::cout << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")";
|
||||
}
|
||||
void print_row_scale_stats(unsigned i);
|
||||
|
||||
void print_column_scale_stats(unsigned j) {
|
||||
std::cout << "(" << m_A->get_min_abs_in_row(j) << " ";
|
||||
std::cout << m_A->get_max_abs_in_column(j) << ")";
|
||||
}
|
||||
void print_column_scale_stats(unsigned j);
|
||||
|
||||
void print_scale_stats() {
|
||||
print_rows_scale_stats();
|
||||
print_columns_scale_stats();
|
||||
}
|
||||
void print_scale_stats();
|
||||
|
||||
void get_max_abs_in_row(std::unordered_map<unsigned, T> & row_map) {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto jp : row_map) {
|
||||
T ac = numeric_traits<T>::abs(jp->second);
|
||||
if (ac > ret) {
|
||||
ret = ac;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
void get_max_abs_in_row(std::unordered_map<unsigned, T> & row_map);
|
||||
|
||||
void pin_vars_down_on_row(std::unordered_map<unsigned, T> & row) {
|
||||
pin_vars_on_row_with_sign(row, - numeric_traits<T>::one());
|
||||
|
|
@ -258,164 +163,19 @@ protected:
|
|||
pin_vars_on_row_with_sign(row, numeric_traits<T>::one());
|
||||
}
|
||||
|
||||
void pin_vars_on_row_with_sign(std::unordered_map<unsigned, T> & row, T sign ) {
|
||||
std::unordered_map<unsigned, T> pinned;
|
||||
for (auto t : row) {
|
||||
unsigned j = t.first;
|
||||
column_info<T> * ci = m_columns[j];
|
||||
T a = t.second;
|
||||
if (a * sign > numeric_traits<T>::zero()) {
|
||||
lean_assert(ci->upper_bound_is_set());
|
||||
ci->set_fixed_value(ci->get_upper_bound());
|
||||
} else {
|
||||
lean_assert(ci->low_bound_is_set());
|
||||
ci->set_fixed_value(ci->get_low_bound());
|
||||
}
|
||||
}
|
||||
}
|
||||
void pin_vars_on_row_with_sign(std::unordered_map<unsigned, T> & row, T sign );
|
||||
|
||||
bool get_minimal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound) {
|
||||
low_bound = numeric_traits<T>::zero();
|
||||
for (auto & t : row) {
|
||||
T a = t.second;
|
||||
column_info<T> * ci = m_columns[t.first];
|
||||
if (a > numeric_traits<T>::zero()) {
|
||||
if (ci->low_bound_is_set()) {
|
||||
low_bound += ci->get_low_bound() * a;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (ci->upper_bound_is_set()) {
|
||||
low_bound += ci->get_upper_bound() * a;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool get_minimal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound);
|
||||
|
||||
bool get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound) {
|
||||
low_bound = numeric_traits<T>::zero();
|
||||
for (auto & t : row) {
|
||||
T a = t.second;
|
||||
column_info<T> * ci = m_columns[t.first];
|
||||
if (a < numeric_traits<T>::zero()) {
|
||||
if (ci->low_bound_is_set()) {
|
||||
low_bound += ci->get_low_bound() * a;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (ci->upper_bound_is_set()) {
|
||||
low_bound += ci->get_upper_bound() * a;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound);
|
||||
|
||||
bool row_is_zero(std::unordered_map<unsigned, T> & row) {
|
||||
for (auto & t : row) {
|
||||
if (!is_zero(t.second))
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool row_is_zero(std::unordered_map<unsigned, T> & row);
|
||||
|
||||
bool row_e_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
|
||||
T rs = m_constraints[row_index].m_rs;
|
||||
if (row_is_zero(row)) {
|
||||
if (!is_zero(rs))
|
||||
m_status = INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
bool row_e_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index);
|
||||
|
||||
T low_bound;
|
||||
bool lb = get_minimal_row_value(row, low_bound);
|
||||
if (lb) {
|
||||
T diff = low_bound - rs;
|
||||
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
|
||||
// low_bound > rs + m_settings.refactor_epsilon
|
||||
m_status = INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
|
||||
pin_vars_down_on_row(row);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
int row_ge_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index);
|
||||
|
||||
T upper_bound;
|
||||
bool ub = get_maximal_row_value(row, upper_bound);
|
||||
if (ub) {
|
||||
T diff = rs - upper_bound;
|
||||
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) {
|
||||
// upper_bound < rs - m_settings.refactor_tolerance
|
||||
m_status = INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
|
||||
pin_vars_up_on_row(row);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
int row_ge_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
|
||||
T rs = m_constraints[row_index].m_rs;
|
||||
if (row_is_zero(row)) {
|
||||
if (rs > zero_of_type<X>())
|
||||
m_status = INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
T upper_bound;
|
||||
if (get_maximal_row_value(row, upper_bound)) {
|
||||
T diff = rs - upper_bound;
|
||||
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) {
|
||||
// upper_bound < rs - m_settings.refactor_tolerance
|
||||
m_status = INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
|
||||
pin_vars_up_on_row(row);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool row_le_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
|
||||
T low_bound;
|
||||
T rs = m_constraints[row_index].m_rs;
|
||||
if (row_is_zero(row)) {
|
||||
if (rs < zero_of_type<X>())
|
||||
m_status = INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (get_minimal_row_value(row, low_bound)) {
|
||||
T diff = low_bound - rs;
|
||||
if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
|
||||
// low_bound > rs + m_settings.refactor_tolerance
|
||||
m_status = lp_status::INFEASIBLE;
|
||||
return true;
|
||||
}
|
||||
if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
|
||||
pin_vars_down_on_row(row);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
bool row_le_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index);
|
||||
|
||||
// analyse possible max and min values that are derived from var boundaries
|
||||
// Let us say that the we have a "ge" constraint, and the min value is equal to the rs.
|
||||
|
|
@ -423,116 +183,21 @@ protected:
|
|||
// the low boundary of the var and for a negative - the upper.
|
||||
|
||||
// this routing also pins the variables to the boundaries
|
||||
bool row_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index ) {
|
||||
auto & constraint = m_constraints[row_index];
|
||||
switch (constraint.m_relation) {
|
||||
case lp_relation::Equal:
|
||||
return row_e_is_obsolete(row, row_index);
|
||||
bool row_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index );
|
||||
|
||||
case lp_relation::Greater_or_equal:
|
||||
return row_ge_is_obsolete(row, row_index);
|
||||
void remove_fixed_or_zero_columns();
|
||||
|
||||
case lp_relation::Less_or_equal:
|
||||
return row_le_is_obsolete(row, row_index);
|
||||
}
|
||||
lean_unreachable();
|
||||
}
|
||||
void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map<unsigned, T> & row);
|
||||
|
||||
void remove_fixed_or_zero_columns() {
|
||||
for (auto & i_row : m_A_values) {
|
||||
remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second);
|
||||
}
|
||||
}
|
||||
unsigned try_to_remove_some_rows();
|
||||
|
||||
void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map<unsigned, T> & row) {
|
||||
auto & constraint = m_constraints[i];
|
||||
std::vector<unsigned> removed;
|
||||
for (auto & col : row) {
|
||||
unsigned j = col.first;
|
||||
lean_assert(m_columns.find(j) != m_columns.end());
|
||||
column_info<T> * ci = m_columns[j];
|
||||
if (ci->is_fixed()) {
|
||||
removed.push_back(j);
|
||||
T aj = col.second;
|
||||
constraint.m_rs -= aj * ci->get_fixed_value();
|
||||
} else {
|
||||
if (numeric_traits<T>::is_zero(col.second)){
|
||||
removed.push_back(j);
|
||||
}
|
||||
}
|
||||
}
|
||||
void cleanup();
|
||||
|
||||
for (auto j : removed) {
|
||||
row.erase(j);
|
||||
}
|
||||
}
|
||||
void map_external_rows_to_core_solver_rows();
|
||||
|
||||
unsigned try_to_remove_some_rows() {
|
||||
std::vector<unsigned> rows_to_delete;
|
||||
for (auto & t : m_A_values) {
|
||||
if (row_is_obsolete(t.second, t.first)) {
|
||||
rows_to_delete.push_back(t.first);
|
||||
}
|
||||
void map_external_columns_to_core_solver_columns();
|
||||
|
||||
if (m_status == lp_status::INFEASIBLE) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (rows_to_delete.size() > 0) {
|
||||
for (unsigned k : rows_to_delete) {
|
||||
m_A_values.erase(k);
|
||||
}
|
||||
}
|
||||
remove_fixed_or_zero_columns();
|
||||
return rows_to_delete.size();
|
||||
}
|
||||
|
||||
void cleanup() {
|
||||
int n = 0; // number of deleted rows
|
||||
int d;
|
||||
while ((d = try_to_remove_some_rows()))
|
||||
n += d;
|
||||
|
||||
if (n == 1)
|
||||
std::cout << "deleted one row" << std::endl;
|
||||
else if (n)
|
||||
std::cout << "deleted " << n << " rows" << std::endl;
|
||||
}
|
||||
|
||||
void map_external_rows_to_core_solver_rows() {
|
||||
unsigned size = 0;
|
||||
for (auto & row : m_A_values) {
|
||||
m_external_rows_to_core_solver_rows[row.first] = size;
|
||||
m_core_solver_rows_to_external_rows[size] = row.first;
|
||||
size++;
|
||||
}
|
||||
}
|
||||
|
||||
void map_external_columns_to_core_solver_columns() {
|
||||
unsigned size = 0;
|
||||
for (auto & row : m_A_values) {
|
||||
for (auto & col : row.second) {
|
||||
if (col.second == numeric_traits<T>::zero() || m_columns[col.first]->is_fixed()) {
|
||||
throw exception("found fixed column");
|
||||
}
|
||||
unsigned j = col.first;
|
||||
auto j_place = m_external_columns_to_core_solver_columns.find(j);
|
||||
if (j_place == m_external_columns_to_core_solver_columns.end()) { // j is a newcomer
|
||||
m_external_columns_to_core_solver_columns[j] = size;
|
||||
m_core_solver_columns_to_external_columns[size++] = j;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void fill_column_names_for_core_solver() {
|
||||
for (auto it : this->m_columns) {
|
||||
auto p = this->m_external_columns_to_core_solver_columns.find(it.first);
|
||||
if (p != this->m_external_columns_to_core_solver_columns.end()) {
|
||||
this->m_name_map[p->second] = it.second->get_name();
|
||||
}
|
||||
}
|
||||
}
|
||||
void fill_column_names_for_core_solver();
|
||||
|
||||
unsigned number_of_core_structurals() { return m_external_columns_to_core_solver_columns.size(); }
|
||||
|
||||
|
|
@ -541,141 +206,24 @@ protected:
|
|||
for (unsigned i = 0; i < m_column_scale.size(); i++) m_column_scale[i] = numeric_traits<T>::one();
|
||||
}
|
||||
|
||||
void unscale() {
|
||||
delete m_A;
|
||||
m_A = nullptr;
|
||||
fill_A_from_A_values();
|
||||
restore_column_scales_to_one();
|
||||
fill_m_b();
|
||||
}
|
||||
void unscale();
|
||||
|
||||
void fill_A_from_A_values() {
|
||||
m_A = new static_matrix<T, X>(m_A_values.size(), number_of_core_structurals());
|
||||
for (auto & t : m_A_values) {
|
||||
lean_assert(m_external_rows_to_core_solver_rows.find(t.first) != m_external_rows_to_core_solver_rows.end());
|
||||
unsigned row = m_external_rows_to_core_solver_rows[t.first];
|
||||
for (auto k : t.second) {
|
||||
lean_assert(m_external_columns_to_core_solver_columns.find(k.first) != m_external_columns_to_core_solver_columns.end());
|
||||
unsigned col = m_external_columns_to_core_solver_columns[k.first];
|
||||
bool col_is_flipped = m_columns[k.first]->is_flipped();
|
||||
void fill_A_from_A_values();
|
||||
|
||||
if (!col_is_flipped) {
|
||||
(*m_A)(row, col) = k.second;
|
||||
} else {
|
||||
(*m_A)(row, col) = - k.second;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void fill_matrix_A_and_init_right_side();
|
||||
|
||||
void fill_matrix_A_and_init_right_side() {
|
||||
map_external_rows_to_core_solver_rows();
|
||||
map_external_columns_to_core_solver_columns();
|
||||
lean_assert(m_A == nullptr);
|
||||
fill_A_from_A_values();
|
||||
m_b.resize(m_A->row_count());
|
||||
}
|
||||
void count_slacks_and_artificials();
|
||||
|
||||
void count_slacks_and_artificials() {
|
||||
for (int i = row_count() - 1; i >= 0; i--) {
|
||||
count_slacks_and_artificials_for_row(i);
|
||||
}
|
||||
}
|
||||
void count_slacks_and_artificials_for_row(unsigned i);
|
||||
|
||||
void count_slacks_and_artificials_for_row(unsigned i) {
|
||||
lean_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end());
|
||||
auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]];
|
||||
T rs;
|
||||
switch (constraint.m_relation) {
|
||||
case Equal:
|
||||
m_artificials++;
|
||||
break;
|
||||
case Greater_or_equal:
|
||||
m_slacks++;
|
||||
rs = this->m_b[i];
|
||||
if (rs > 0) {
|
||||
m_artificials++;
|
||||
}
|
||||
break;
|
||||
case Less_or_equal:
|
||||
m_slacks++;
|
||||
rs = this->m_b[i];
|
||||
if (rs < 0) {
|
||||
m_artificials++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
T low_bound_shift_for_row(unsigned i);
|
||||
|
||||
T low_bound_shift_for_row(unsigned i) {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
void fill_m_b();
|
||||
|
||||
auto row = this->m_A_values.find(i);
|
||||
if (row == this->m_A_values.end()) {
|
||||
throw exception("cannot find row");
|
||||
}
|
||||
for (auto col : row->second) {
|
||||
ret += col.second * this->m_columns[col.first]->get_shift();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
void fill_m_b() {
|
||||
for (int i = this->row_count() - 1; i >= 0; i--) {
|
||||
lean_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end());
|
||||
unsigned external_i = this->m_core_solver_rows_to_external_rows[i];
|
||||
auto & constraint = this->m_constraints[external_i];
|
||||
this->m_b[i] = constraint.m_rs - low_bound_shift_for_row(external_i);
|
||||
}
|
||||
}
|
||||
|
||||
T get_column_value_with_core_solver(unsigned column, lp_core_solver_base<T, X> * core_solver) const {
|
||||
auto cit = this->m_columns.find(column);
|
||||
if (cit == this->m_columns.end()) {
|
||||
return numeric_traits<T>::zero();
|
||||
}
|
||||
|
||||
column_info<T> * ci = cit->second;
|
||||
|
||||
if (ci->is_fixed()) {
|
||||
return ci->get_fixed_value();
|
||||
}
|
||||
|
||||
auto t = this->m_external_columns_to_core_solver_columns.find(column);
|
||||
if (t != this->m_external_columns_to_core_solver_columns.end()){
|
||||
unsigned cj = t->second;
|
||||
T v = core_solver->get_var_value(cj) * this->m_column_scale[cj];
|
||||
if (ci->is_free()) {
|
||||
return v;
|
||||
}
|
||||
if (!ci->is_flipped()) {
|
||||
return v + ci->get_low_bound();
|
||||
}
|
||||
|
||||
// the flipped case when there is only upper bound
|
||||
return -v + ci->get_upper_bound(); //
|
||||
}
|
||||
|
||||
return numeric_traits<T>::zero(); // returns zero for out of boundary columns
|
||||
}
|
||||
void set_scaled_cost(unsigned j) {
|
||||
// grab original costs but modify it with the column scales
|
||||
lean_assert(j < this->m_column_scale.size());
|
||||
column_info<T> * ci = this->m_columns[this->m_core_solver_columns_to_external_columns[j]];
|
||||
T cost = ci->get_cost();
|
||||
if (ci->is_flipped()){
|
||||
cost *= -1;
|
||||
}
|
||||
lean_assert(ci->is_fixed() == false);
|
||||
this->m_costs[j] = cost * this->m_column_scale[j];
|
||||
}
|
||||
void print_statistics_on_A() {
|
||||
T get_column_value_with_core_solver(unsigned column, lp_core_solver_base<T, X> * core_solver) const;
|
||||
void set_scaled_cost(unsigned j);
|
||||
void print_statistics_on_A() {
|
||||
std::cout << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << std::endl;
|
||||
// for (unsigned i = 0; i < this->m_A->row_count(); i++) {
|
||||
// if (this->m_A->number_of_non_zeroes_in_row(i) <= 2 ) {
|
||||
// std::cout << "m_p[" << i << "] = " << this->m_A->number_of_non_zeroes_in_row(i) << std::endl;
|
||||
// }
|
||||
// }
|
||||
}
|
||||
public:
|
||||
lp_settings & settings() { return m_settings;}
|
||||
|
|
|
|||
43
src/util/lp/lp_solver_instances.cpp
Normal file
43
src/util/lp/lp_solver_instances.cpp
Normal file
|
|
@ -0,0 +1,43 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/lp_solver.cpp"
|
||||
template void lean::lp_solver<double, double>::add_constraint(lean::lp_relation, double, unsigned int);
|
||||
template void lean::lp_solver<double, double>::cleanup();
|
||||
template void lean::lp_solver<double, double>::count_slacks_and_artificials();
|
||||
template void lean::lp_solver<double, double>::fill_column_names_for_core_solver();
|
||||
template void lean::lp_solver<double, double>::fill_m_b();
|
||||
template void lean::lp_solver<double, double>::fill_matrix_A_and_init_right_side();
|
||||
template void lean::lp_solver<double, double>::flip_costs();
|
||||
template double lean::lp_solver<double, double>::get_column_cost_value(unsigned int, lean::column_info<double>*) const;
|
||||
template int lean::lp_solver<double, double>::get_column_index_by_name(std::string) const;
|
||||
template double lean::lp_solver<double, double>::get_column_value_with_core_solver(unsigned int, lean::lp_core_solver_base<double, double>*) const;
|
||||
template lean::column_info<double>* lean::lp_solver<double, double>::get_or_create_column_info(unsigned int);
|
||||
template void lean::lp_solver<double, double>::give_symbolic_name_to_column(std::string, unsigned int);
|
||||
template void lean::lp_solver<double, double>::print_statistics_on_A();
|
||||
template bool lean::lp_solver<double, double>::problem_is_empty();
|
||||
template void lean::lp_solver<double, double>::scale();
|
||||
template void lean::lp_solver<double, double>::set_scaled_cost(unsigned int);
|
||||
template lean::lp_solver<double, double>::~lp_solver();
|
||||
template void lean::lp_solver<lean::mpq, lean::mpq>::add_constraint(lean::lp_relation, lean::mpq, unsigned int);
|
||||
template void lean::lp_solver<lean::mpq, lean::mpq>::cleanup();
|
||||
template void lean::lp_solver<lean::mpq, lean::mpq>::count_slacks_and_artificials();
|
||||
template void lean::lp_solver<lean::mpq, lean::mpq>::fill_column_names_for_core_solver();
|
||||
template void lean::lp_solver<lean::mpq, lean::mpq>::fill_m_b();
|
||||
template void lean::lp_solver<lean::mpq, lean::mpq>::fill_matrix_A_and_init_right_side();
|
||||
template void lean::lp_solver<lean::mpq, lean::mpq>::flip_costs();
|
||||
template lean::mpq lean::lp_solver<lean::mpq, lean::mpq>::get_column_cost_value(unsigned int, lean::column_info<lean::mpq>*) const;
|
||||
template lean::mpq lean::lp_solver<lean::mpq, lean::mpq>::get_column_value_by_name(std::string) const;
|
||||
template lean::mpq lean::lp_solver<lean::mpq, lean::mpq>::get_column_value_with_core_solver(unsigned int, lean::lp_core_solver_base<lean::mpq, lean::mpq>*) const;
|
||||
template lean::column_info<lean::mpq>* lean::lp_solver<lean::mpq, lean::mpq>::get_or_create_column_info(unsigned int);
|
||||
template void lean::lp_solver<lean::mpq, lean::mpq>::give_symbolic_name_to_column(std::string, unsigned int);
|
||||
template void lean::lp_solver<lean::mpq, lean::mpq>::print_statistics_on_A();
|
||||
template bool lean::lp_solver<lean::mpq, lean::mpq>::problem_is_empty();
|
||||
template void lean::lp_solver<lean::mpq, lean::mpq>::scale();
|
||||
template void lean::lp_solver<lean::mpq, lean::mpq>::set_scaled_cost(unsigned int);
|
||||
template lean::lp_solver<lean::mpq, lean::mpq>::~lp_solver();
|
||||
template double lean::lp_solver<double, double>::get_column_value_by_name(std::string) const;
|
||||
759
src/util/lp/lu.cpp
Normal file
759
src/util/lp/lu.cpp
Normal file
|
|
@ -0,0 +1,759 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/lu.h"
|
||||
namespace lean {
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X> // print the nr x nc submatrix at the top left corner
|
||||
void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc) {
|
||||
std::vector<std::vector<std::string>> A;
|
||||
std::vector<unsigned> widths;
|
||||
for (unsigned i = 0; i < m.row_count() && i < mr ; i++) {
|
||||
A.push_back(std::vector<std::string>());
|
||||
for (unsigned j = 0; j < m.column_count() && j < nc; j++) {
|
||||
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < m.column_count() && j < nc; j++) {
|
||||
widths.push_back(get_width_of_column(j, A));
|
||||
}
|
||||
|
||||
print_matrix_with_widths(A, widths);
|
||||
}
|
||||
|
||||
template<typename T, typename X>
|
||||
void print_matrix(static_matrix<T, X> &m) {
|
||||
std::vector<std::vector<std::string>> A;
|
||||
std::vector<unsigned> widths;
|
||||
std::set<pair<unsigned, unsigned>> domain = m.get_domain();
|
||||
for (unsigned i = 0; i < m.row_count(); i++) {
|
||||
A.push_back(std::vector<std::string>());
|
||||
for (unsigned j = 0; j < m.column_count(); j++) {
|
||||
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < m.column_count(); j++) {
|
||||
widths.push_back(get_width_of_column(j, A));
|
||||
}
|
||||
|
||||
print_matrix_with_widths(A, widths);
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void print_matrix(sparse_matrix<T, X>& m) {
|
||||
std::vector<std::vector<std::string>> A;
|
||||
std::vector<unsigned> widths;
|
||||
for (unsigned i = 0; i < m.row_count(); i++) {
|
||||
A.push_back(std::vector<std::string>());
|
||||
for (unsigned j = 0; j < m.column_count(); j++) {
|
||||
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < m.column_count(); j++) {
|
||||
widths.push_back(get_width_of_column(j, A));
|
||||
}
|
||||
|
||||
print_matrix_with_widths(A, widths);
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename X>
|
||||
X dot_product(const std::vector<T> & a, const std::vector<X> & b, unsigned l) {
|
||||
auto r = zero_of_type<X>();
|
||||
for (unsigned i = 0; i < l; i++) {
|
||||
r += a[i] * b[i];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X>
|
||||
one_elem_on_diag<T,X>::one_elem_on_diag(const one_elem_on_diag & o) {
|
||||
m_i = o.m_i;
|
||||
m_val = o.m_val;
|
||||
#ifdef LEAN_DEBUG
|
||||
m_m = m_n = o.m_m;
|
||||
m_one_over_val = numeric_traits<T>::one() / o.m_val;
|
||||
#endif
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X>
|
||||
T one_elem_on_diag<T, X>::get_elem(unsigned i, unsigned j) const {
|
||||
if (i == j){
|
||||
if (j == m_i) {
|
||||
return m_one_over_val;
|
||||
}
|
||||
return numeric_traits<T>::one();
|
||||
}
|
||||
|
||||
return numeric_traits<T>::zero();
|
||||
}
|
||||
#endif
|
||||
template <typename T, typename X>
|
||||
void one_elem_on_diag<T, X>::apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
|
||||
w[m_i] /= m_val;
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_i])) {
|
||||
w.erase_from_index(m_i);
|
||||
w[m_i] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
|
||||
// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c
|
||||
// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5
|
||||
template <typename T, typename X>
|
||||
lu<T, X>::lu(static_matrix<T, X> const & A,
|
||||
std::vector<unsigned>& basis,
|
||||
std::vector<int> & basis_heading,
|
||||
lp_settings & settings,
|
||||
std::vector<unsigned> & non_basic_columns):
|
||||
m_dim(A.row_count()),
|
||||
m_A(A),
|
||||
m_basis(basis),
|
||||
m_Q(m_dim),
|
||||
m_R(m_dim),
|
||||
m_U(A, basis), // create the square matrix that eventually will be factorized
|
||||
m_settings(settings),
|
||||
m_basis_heading(basis_heading),
|
||||
m_non_basic_columns(non_basic_columns),
|
||||
m_row_eta_work_vector(A.row_count()){
|
||||
#ifdef LEAN_DEBUG
|
||||
debug_test_of_basis(A, basis);
|
||||
#endif
|
||||
create_initial_factorization();
|
||||
if (get_status() != LU_status::OK) {
|
||||
if (get_status() == LU_status::Degenerated) {
|
||||
std::cout << "lu status is Degenerated" << std::endl;
|
||||
} else {
|
||||
std::cout << "lu status is " <<static_cast<int>(get_status()) << std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(check_correctness());
|
||||
#endif
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::debug_test_of_basis(static_matrix<T, X> const & A, std::vector<unsigned> & basis) {
|
||||
std::set<unsigned> set;
|
||||
for (unsigned i = 0; i < A.row_count(); i++) {
|
||||
lean_assert(basis[i]< A.column_count());
|
||||
set.insert(basis[i]);
|
||||
}
|
||||
lean_assert(set.size() == A.row_count());
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::solve_By(std::vector<X> & y) {
|
||||
init_vector_y(y);
|
||||
solve_By_when_y_is_ready(y);
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::solve_Bd_when_w_is_ready(std::vector<T> & d, indexed_vector<T>& w ) { // w - the vector featuring in 24.3
|
||||
for (int i = m_dim - 1; i >= 0; i--) { // index ? todo
|
||||
d[i] = w[i];
|
||||
}
|
||||
solve_By_when_y_is_ready(d);
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
template <typename L>
|
||||
void lu<T, X>::solve_By_when_y_is_ready(std::vector<L> & y) {
|
||||
m_U.double_solve_U_y(y);
|
||||
m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal
|
||||
if (precise<X>()) return;
|
||||
unsigned i = m_dim;
|
||||
while (i--) {
|
||||
if (is_zero(y[i])) continue;
|
||||
if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){
|
||||
y[i] = zero_of_type<L>();
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::print_basis(std::ofstream & f) {
|
||||
f << "basis_start" << std::endl;
|
||||
for (unsigned j : m_basis)
|
||||
f << j << std::endl;
|
||||
f << "basis_end" << std::endl;
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::print_matrix_compact(std::ofstream & f) {
|
||||
f << "matrix_start" << std::endl;
|
||||
f << "nrows " << m_A.row_count() << std::endl;
|
||||
f << "ncolumns " << m_A.column_count() << std::endl;
|
||||
for (unsigned i = 0; i < m_A.row_count(); i++) {
|
||||
auto & row = m_A.m_rows[i];
|
||||
f << "row " << i << std::endl;
|
||||
for (auto & t : row.m_cells) {
|
||||
f << "column " << t.m_j << " value " << t.m_value << std::endl;
|
||||
}
|
||||
f << "row_end" << std::endl;
|
||||
}
|
||||
f << "matrix_end" << std::endl;
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::print(indexed_vector<T> & w) {
|
||||
std::string dump_file_name("/tmp/lu");
|
||||
remove(dump_file_name.c_str());
|
||||
std::ofstream f(dump_file_name);
|
||||
if (!f.is_open()) {
|
||||
std::cout << "cannot open file " << dump_file_name << std::endl;
|
||||
return;
|
||||
}
|
||||
std::cout << "writing lu dump to " << dump_file_name << std::endl;
|
||||
print_matrix_compact(f);
|
||||
print_basis(f);
|
||||
print_indexed_vector(w, f);
|
||||
f.close();
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::solve_Bd(unsigned a_column, std::vector<T> & d, indexed_vector<T> & w) {
|
||||
init_vector_w(a_column, w);
|
||||
solve_Bd_when_w_is_ready(d, w);
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>:: solve_yB_internal(std::vector<T>& y) {
|
||||
// first solve yU = cb*R(-1)
|
||||
m_R.apply_reverse_from_right(y); // got y = cb*R(-1)
|
||||
m_U.solve_y_U(y); // got y*U=cb*R(-1)
|
||||
m_Q.apply_reverse_from_right(y); //
|
||||
for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) {
|
||||
#ifdef LEAN_DEBUG
|
||||
(*e)->set_number_of_columns(m_dim);
|
||||
#endif
|
||||
(*e)->apply_from_right(y);
|
||||
}
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::add_delta_to_solution(std::vector<T>& yc, std::vector<T>& y){
|
||||
unsigned i = y.size();
|
||||
while (i--)
|
||||
y[i]+=yc[i];
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::find_error_of_yB(std::vector<T>& yc, const std::vector<T>& y) {
|
||||
unsigned i = m_dim;
|
||||
while (i--) {
|
||||
yc[i] -= m_A.dot_product_with_column(y, m_basis[i]);
|
||||
}
|
||||
}
|
||||
|
||||
// solves y*B = y
|
||||
// y is the input
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::solve_yB(std::vector<T> & y) {
|
||||
std::vector<T> yc(y); // copy y aside
|
||||
solve_yB_internal(y);
|
||||
find_error_of_yB(yc, y);
|
||||
solve_yB_internal(yc);
|
||||
add_delta_to_solution(yc, y);
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::apply_Q_R_to_U(permutation_matrix<T, X> & r_wave) {
|
||||
m_U.multiply_from_right(r_wave);
|
||||
m_U.multiply_from_left_with_reverse(r_wave);
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::change_basis(unsigned entering, unsigned leaving) {
|
||||
lean_assert(entering < m_A.column_count() && leaving < m_A.column_count());
|
||||
int place_in_basis = m_basis_heading[leaving];
|
||||
int place_in_non_basis = - m_basis_heading[entering] - 1;
|
||||
lean_assert(0 <= place_in_basis && place_in_basis < m_A.column_count());
|
||||
m_basis_heading[entering] = place_in_basis;
|
||||
m_basis_heading[leaving] = -place_in_non_basis - 1;
|
||||
m_basis[place_in_basis] = entering;
|
||||
m_non_basic_columns[place_in_non_basis] = leaving;
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::restore_basis_change(unsigned entering, unsigned leaving) {
|
||||
if (m_basis_heading[entering] < 0) {
|
||||
return; // the basis has not been changed
|
||||
}
|
||||
change_basis(leaving, entering);
|
||||
}
|
||||
|
||||
|
||||
// Solving yB = cb to find the entering variable,
|
||||
// where cb is the cost vector projected to B.
|
||||
// The result is stored in cb.
|
||||
|
||||
// solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering
|
||||
// variable
|
||||
template <typename T, typename X>
|
||||
lu<T, X>::~lu(){
|
||||
for (auto t : m_tail) {
|
||||
delete t;
|
||||
}
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::init_vector_y(std::vector<X> & y) {
|
||||
apply_lp_lists_to_y(y);
|
||||
m_Q.apply_reverse_from_left(y);
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::perform_transformations_on_w(indexed_vector<T>& w) {
|
||||
apply_lp_lists_to_w(w);
|
||||
m_Q.apply_reverse_from_left(w);
|
||||
lean_assert(numeric_traits<T>::precise() || check_vector_for_small_values(w, m_settings));
|
||||
}
|
||||
|
||||
// see Chvatal 24.3
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::init_vector_w(unsigned entering, indexed_vector<T> & w) {
|
||||
w.clear();
|
||||
m_A.copy_column_to_vector(entering, w); // w = a, the column
|
||||
perform_transformations_on_w(w);
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::apply_lp_lists_to_w(indexed_vector<T> & w) {
|
||||
for (unsigned i = 0; i < m_tail.size(); i++) {
|
||||
m_tail[i]->apply_from_left_to_T(w, m_settings);
|
||||
lean_assert(check_vector_for_small_values(w, m_settings));
|
||||
}
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::apply_lp_lists_to_y(std::vector<X>& y) {
|
||||
for (unsigned i = 0; i < m_tail.size(); i++) {
|
||||
m_tail[i]->apply_from_left(y, m_settings);
|
||||
}
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::swap_rows(int j, int k) {
|
||||
if (j != k) {
|
||||
m_Q.transpose_from_left(j, k);
|
||||
m_U.swap_rows(j, k);
|
||||
}
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::swap_columns(int j, int pivot_column) {
|
||||
if (j == pivot_column)
|
||||
return;
|
||||
m_R.transpose_from_right(j, pivot_column);
|
||||
m_U.swap_columns(j, pivot_column);
|
||||
}
|
||||
template <typename T, typename X>
|
||||
bool lu<T, X>::pivot_the_row(int row) {
|
||||
eta_matrix<T, X> * eta_matrix = get_eta_matrix_for_pivot(row);
|
||||
if (eta_matrix == nullptr) {
|
||||
m_U.shorten_active_matrix(row, nullptr);
|
||||
return true;
|
||||
}
|
||||
if (!m_U.pivot_with_eta(row, eta_matrix, m_settings))
|
||||
return false;
|
||||
eta_matrix->conjugate_by_permutation(m_Q);
|
||||
push_matrix_to_tail(eta_matrix);
|
||||
return true;
|
||||
}
|
||||
// we're processing the column j now
|
||||
template <typename T, typename X>
|
||||
eta_matrix<T, X> * lu<T, X>::get_eta_matrix_for_pivot(unsigned j) {
|
||||
eta_matrix<T, X> *ret;
|
||||
m_U.fill_eta_matrix(j, &ret);
|
||||
return ret;
|
||||
}
|
||||
// we're processing the column j now
|
||||
template <typename T, typename X>
|
||||
eta_matrix<T, X> * lu<T, X>::get_eta_matrix_for_pivot(unsigned j, sparse_matrix<T, X>& copy_of_U) {
|
||||
eta_matrix<T, X> *ret;
|
||||
copy_of_U.fill_eta_matrix(j, &ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::print_basis() {
|
||||
std::cout << "basis ";
|
||||
for (unsigned i = 0; i < m_dim; i++) {
|
||||
std::cout << m_basis[i] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::print_basis_heading() {
|
||||
print_basis();
|
||||
for (unsigned i = 0; i < m_A.column_count(); i++) {
|
||||
std::cout << m_basis_heading[i] << ",";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
// see page 407 of Chvatal
|
||||
template <typename T, typename X>
|
||||
unsigned lu<T, X>::transform_U_to_V_by_replacing_column(unsigned leaving, indexed_vector<T> & w) {
|
||||
int leaving_column = m_basis_heading[leaving];
|
||||
// std::cout << "leaving_column = " << leaving_column << std::endl;
|
||||
unsigned column_to_replace = m_R.apply_reverse(leaving_column);
|
||||
// std::cout << "leaving_column modified = " << column_to_replace << std::endl;
|
||||
m_U.replace_column(column_to_replace, w, m_settings);
|
||||
return column_to_replace;
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::check_vector_w(unsigned entering) {
|
||||
T * w = new T[m_dim];
|
||||
m_A.copy_column_to_vector(entering, w);
|
||||
check_apply_lp_lists_to_w(w);
|
||||
delete [] w;
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::check_apply_matrix_to_vector(matrix<T, X> *lp, T *w) {
|
||||
if (lp != nullptr) {
|
||||
lp -> set_number_of_rows(m_dim);
|
||||
lp -> set_number_of_columns(m_dim);
|
||||
apply_to_vector(*lp, w);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::check_apply_lp_lists_to_w(T * w) {
|
||||
for (unsigned i = 0; i < m_tail.size(); i++) {
|
||||
check_apply_matrix_to_vector(m_tail[i], w);
|
||||
}
|
||||
permutation_matrix<T, X> qr = m_Q.get_reverse();
|
||||
apply_to_vector(qr, w);
|
||||
for (int i = m_dim - 1; i >= 0; i--) {
|
||||
lean_assert(abs(w[i] - w[i]) < 0.0000001);
|
||||
}
|
||||
}
|
||||
|
||||
// provide some access operators for testing
|
||||
#endif
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::process_column(int j) {
|
||||
unsigned pi, pj;
|
||||
m_U.get_pivot_for_column(pi, pj, T(m_settings.c_partial_pivoting), j);
|
||||
if (pi == -1) {
|
||||
std::cout << "cannot find the pivot for column " << j << std::endl;
|
||||
m_failure = true;
|
||||
return;
|
||||
}
|
||||
swap_columns(j, pj);
|
||||
swap_rows(j, pi);
|
||||
if (!pivot_the_row(j)) {
|
||||
std::cout << "pivot_the_row(" << j << ") failed" << std::endl;
|
||||
m_failure = true;
|
||||
}
|
||||
}
|
||||
template <typename T, typename X>
|
||||
bool lu<T, X>::is_correct() {
|
||||
#ifdef LEAN_DEBUG
|
||||
if (get_status() != LU_status::OK) {
|
||||
return false;
|
||||
}
|
||||
dense_matrix<T, X> left_side = get_left_side();
|
||||
dense_matrix<T, X> right_side = get_right_side();
|
||||
return left_side == right_side;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X>
|
||||
dense_matrix<T, X> lu<T, X>::tail_product() {
|
||||
lean_assert(tail_size() > 0);
|
||||
dense_matrix<T, X> left_side = permutation_matrix<T, X>(m_dim);
|
||||
for (unsigned i = 0; i < tail_size(); i++) {
|
||||
matrix<T, X>* lp = get_lp_matrix(i);
|
||||
lp->set_number_of_rows(m_dim);
|
||||
lp->set_number_of_columns(m_dim);
|
||||
left_side = ((*lp) * left_side);
|
||||
}
|
||||
return left_side;
|
||||
}
|
||||
template <typename T, typename X>
|
||||
dense_matrix<T, X> lu<T, X>::get_left_side() {
|
||||
dense_matrix<T, X> left_side = get_B(*this);
|
||||
for (unsigned i = 0; i < tail_size(); i++) {
|
||||
matrix<T, X>* lp = get_lp_matrix(i);
|
||||
lp->set_number_of_rows(m_dim);
|
||||
lp->set_number_of_columns(m_dim);
|
||||
left_side = ((*lp) * left_side);
|
||||
}
|
||||
return left_side;
|
||||
}
|
||||
template <typename T, typename X>
|
||||
dense_matrix<T, X> lu<T, X>::get_right_side() {
|
||||
auto ret = U() * R();
|
||||
ret = Q() * ret;
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
|
||||
// needed for debugging purposes
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::copy_w(T *buffer, indexed_vector<T> & w) {
|
||||
unsigned i = m_dim;
|
||||
while (i--) {
|
||||
buffer[i] = w[i];
|
||||
}
|
||||
}
|
||||
|
||||
// needed for debugging purposes
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::restore_w(T *buffer, indexed_vector<T> & w) {
|
||||
unsigned i = m_dim;
|
||||
while (i--) {
|
||||
w[i] = buffer[i];
|
||||
}
|
||||
}
|
||||
template <typename T, typename X>
|
||||
bool lu<T, X>::all_columns_and_rows_are_active() {
|
||||
unsigned i = m_dim;
|
||||
while (i--) {
|
||||
lean_assert(m_U.col_is_active(i));
|
||||
lean_assert(m_U.row_is_active(i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
template <typename T, typename X>
|
||||
bool lu<T, X>::too_dense(unsigned j) const {
|
||||
unsigned r = m_dim - j;
|
||||
if (r < 5)
|
||||
return false;
|
||||
return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j);
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::pivot_in_dense_mode(unsigned i) {
|
||||
int j = m_dense_LU->find_pivot_column_in_row(i);
|
||||
if (j == -1) {
|
||||
m_failure = true;
|
||||
return;
|
||||
}
|
||||
if (i != j) {
|
||||
swap_columns(i, j);
|
||||
m_dense_LU->swap_columns(i, j);
|
||||
}
|
||||
m_dense_LU->pivot(i, m_settings);
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::create_initial_factorization(){
|
||||
m_U.prepare_for_factorization();
|
||||
unsigned j;
|
||||
for (j = 0; j < m_dim; j++) {
|
||||
process_column(j);
|
||||
if (m_failure || too_dense(j + 1)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_failure) {
|
||||
set_status(LU_status::Degenerated);
|
||||
return;
|
||||
}
|
||||
if (j == m_dim) {
|
||||
lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
|
||||
return;
|
||||
}
|
||||
j++;
|
||||
// std::cout << "switching to dense factoring for " << j << endl;
|
||||
m_dense_LU = new square_dense_submatrix<T, X>(&m_U, j);
|
||||
for (; j < m_dim; j++) {
|
||||
pivot_in_dense_mode(j);
|
||||
if (m_failure) {
|
||||
set_status(LU_status::Degenerated);
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_dense_LU->update_parent_matrix(m_settings);
|
||||
lean_assert(m_dense_LU->is_L_matrix());
|
||||
m_dense_LU->conjugate_by_permutation(m_Q);
|
||||
push_matrix_to_tail(m_dense_LU);
|
||||
// lean_assert(is_correct());
|
||||
// lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix<T, X> & r_wave) {
|
||||
lean_assert(bump_start <= bump_end);
|
||||
if (bump_start == bump_end) {
|
||||
return;
|
||||
}
|
||||
|
||||
r_wave[bump_start] = bump_end; // sending the offensive column to the end of the bump
|
||||
|
||||
for ( unsigned i = bump_start + 1 ; i <= bump_end; i++ ) {
|
||||
r_wave[i] = i - 1;
|
||||
}
|
||||
|
||||
m_U.multiply_from_right(r_wave);
|
||||
m_U.multiply_from_left_with_reverse(r_wave);
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) {
|
||||
std::vector<indexed_value<T>> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump));
|
||||
for (auto & iv : last_row_vec) {
|
||||
if (is_zero(iv.m_value)) continue;
|
||||
lean_assert(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value));
|
||||
unsigned adjusted_col = m_U.adjust_column_inverse(iv.m_index);
|
||||
if (adjusted_col < lowest_row_of_the_bump) {
|
||||
m_row_eta_work_vector.set_value(-iv.m_value, adjusted_col);
|
||||
} else {
|
||||
m_row_eta_work_vector.set_value(iv.m_value, adjusted_col); // preparing to calculate the real value in the matrix
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) {
|
||||
// we have the system right side at m_row_eta_work_vector now
|
||||
// solve the system column wise
|
||||
for (unsigned j = replaced_column; j < lowest_row_of_the_bump; j++) {
|
||||
T v = m_row_eta_work_vector[j];
|
||||
if (numeric_traits<T>::is_zero(v)) continue; // this column does not contribute to the solution
|
||||
unsigned aj = m_U.adjust_row(j);
|
||||
std::vector<indexed_value<T>> & row = m_U.get_row_values(aj);
|
||||
for (auto & iv : row) {
|
||||
unsigned col = m_U.adjust_column_inverse(iv.m_index);
|
||||
lean_assert(col >= j || numeric_traits<T>::is_zero(iv.m_value));
|
||||
if (col == j) continue;
|
||||
if (numeric_traits<T>::is_zero(iv.m_value)) {
|
||||
continue;
|
||||
}
|
||||
// the -v is for solving the system ( to zero the last row), and +v is for pivoting
|
||||
T delta = col < lowest_row_of_the_bump? -v * iv.m_value: v * iv.m_value;
|
||||
lean_assert(numeric_traits<T>::is_zero(delta) == false);
|
||||
|
||||
if (numeric_traits<T>::is_zero(m_row_eta_work_vector[col])) {
|
||||
if (!m_settings.abs_val_is_smaller_than_drop_tolerance(delta)){
|
||||
m_row_eta_work_vector.set_value(delta, col);
|
||||
}
|
||||
} else {
|
||||
T t = (m_row_eta_work_vector[col] += delta);
|
||||
if (m_settings.abs_val_is_smaller_than_drop_tolerance(t)){
|
||||
m_row_eta_work_vector[col] = numeric_traits<T>::zero();
|
||||
auto it = std::find(m_row_eta_work_vector.m_index.begin(), m_row_eta_work_vector.m_index.end(), col);
|
||||
if (it != m_row_eta_work_vector.m_index.end())
|
||||
m_row_eta_work_vector.m_index.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lean_assert(m_row_eta_work_vector.is_OK());
|
||||
}
|
||||
// see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last
|
||||
// row at the same time
|
||||
template <typename T, typename X>
|
||||
row_eta_matrix<T, X> *lu<T, X>::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) {
|
||||
if (replaced_column == lowest_row_of_the_bump) return nullptr;
|
||||
scan_last_row_to_work_vector(lowest_row_of_the_bump);
|
||||
pivot_and_solve_the_system(replaced_column, lowest_row_of_the_bump);
|
||||
T denom = std::max(T(1), abs(pivot_elem_for_checking));
|
||||
if (
|
||||
#ifdef LEAN_DEBUG
|
||||
!is_zero(pivot_elem_for_checking) &&
|
||||
#endif
|
||||
!m_settings.abs_val_is_smaller_than_pivot_tolerance((m_row_eta_work_vector[lowest_row_of_the_bump] - pivot_elem_for_checking) / denom)) {
|
||||
// std::cout << "m_row_eta_work_vector[" << lowest_row_of_the_bump << "] = " << T_to_string(m_row_eta_work_vector[lowest_row_of_the_bump]) << ", but pivot = " << T_to_string(pivot_elem_for_checking) << endl;
|
||||
set_status(LU_status::Degenerated);
|
||||
// std::cout << "diagonal element is off" << endl;
|
||||
return nullptr;
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump, m_dim);
|
||||
#else
|
||||
auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump);
|
||||
#endif
|
||||
|
||||
for (auto j : m_row_eta_work_vector.m_index) {
|
||||
if (j < lowest_row_of_the_bump) {
|
||||
auto & v = m_row_eta_work_vector[j];
|
||||
if (!is_zero(v)) {
|
||||
if (!m_settings.abs_val_is_smaller_than_drop_tolerance(v)){
|
||||
ret->push_back(j, v);
|
||||
}
|
||||
v = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
} // now the lowest_row_of_the_bump contains the rest of the row to the right of the bump with correct values
|
||||
return ret;
|
||||
}
|
||||
|
||||
// This method does not update the basis: is_correct() should not be called since it works with the basis.
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::replace_column(unsigned leaving, T pivot_elem, indexed_vector<T> & w){
|
||||
lean_assert(m_basis_heading[leaving] >= 0);
|
||||
unsigned replaced_column = transform_U_to_V_by_replacing_column(leaving, w);
|
||||
unsigned lowest_row_of_the_bump = m_U.lowest_row_in_column(replaced_column);
|
||||
permutation_matrix<T, X> r_wave(m_dim);
|
||||
calculate_r_wave_and_update_U(replaced_column, lowest_row_of_the_bump, r_wave);
|
||||
auto row_eta = get_row_eta_matrix_and_set_row_vector(replaced_column, lowest_row_of_the_bump, pivot_elem);
|
||||
if (get_status() == LU_status::Degenerated) {
|
||||
m_row_eta_work_vector.clear_all();
|
||||
return;
|
||||
}
|
||||
m_Q.multiply_by_permutation_from_right(r_wave);
|
||||
m_R.multiply_by_permutation_reverse_from_left(r_wave);
|
||||
if (row_eta != nullptr) {
|
||||
row_eta->conjugate_by_permutation(m_Q);
|
||||
push_matrix_to_tail(row_eta);
|
||||
}
|
||||
calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump);
|
||||
lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
|
||||
lean_assert(w.is_OK() && m_row_eta_work_vector.is_OK());
|
||||
}
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){
|
||||
T diagonal_elem;
|
||||
if (replaced_column < lowest_row_of_the_bump) {
|
||||
diagonal_elem = m_row_eta_work_vector[lowest_row_of_the_bump];
|
||||
// lean_assert(m_row_eta_work_vector.is_OK());
|
||||
m_U.set_row_from_work_vector_and_clean_work_vector_not_adjusted(m_U.adjust_row(lowest_row_of_the_bump), m_row_eta_work_vector, m_settings);
|
||||
} else {
|
||||
diagonal_elem = m_U(lowest_row_of_the_bump, lowest_row_of_the_bump); // todo - get it more efficiently
|
||||
}
|
||||
if (m_settings.abs_val_is_smaller_than_pivot_tolerance(diagonal_elem)) {
|
||||
set_status(LU_status::Degenerated);
|
||||
return;
|
||||
}
|
||||
|
||||
calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem);
|
||||
// lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void lu<T, X>::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) {
|
||||
auto l = new one_elem_on_diag<T, X>(lowest_row_of_the_bump, diagonal_element);
|
||||
#ifdef LEAN_DEBUG
|
||||
l->set_number_of_columns(m_dim);
|
||||
#endif
|
||||
push_matrix_to_tail(l);
|
||||
m_U.divide_row_by_constant(lowest_row_of_the_bump, diagonal_element, m_settings);
|
||||
l->conjugate_by_permutation(m_Q);
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void init_factorization(lu<T, X>* & factorization, static_matrix<T, X> & m_A, std::vector<unsigned> & m_basis, std::vector<int> & m_basis_heading, lp_settings &m_settings, std::vector<unsigned> & non_basic_columns) {
|
||||
if (factorization != nullptr) {
|
||||
delete factorization;
|
||||
}
|
||||
factorization = new lu<T, X>(m_A, m_basis, m_basis_heading, m_settings, non_basic_columns);
|
||||
if (factorization->get_status() != LU_status::OK) {
|
||||
std::cout << "failing in init_factorization" << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X>
|
||||
dense_matrix<T, X> get_B(lu<T, X>& f) {
|
||||
dense_matrix<T, X> B(f.dimension(), f.dimension());
|
||||
for (unsigned i = 0; i < f.dimension(); i++)
|
||||
for (unsigned j = 0; j < f.dimension(); j++)
|
||||
B.set_elem(i, j, f.B_(i, j));
|
||||
|
||||
return B;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
744
src/util/lp/lu.h
744
src/util/lp/lu.h
|
|
@ -21,77 +21,21 @@
|
|||
#include <fstream>
|
||||
#include "util/lp/row_eta_matrix.h"
|
||||
#include "util/lp/square_dense_submatrix.h"
|
||||
#include "util/lp/dense_matrix.h"
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
std::string T_to_string(const T & t); // forward definition
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X> // print the nr x nc submatrix at the top left corner
|
||||
void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc) {
|
||||
std::vector<std::vector<std::string>> A;
|
||||
std::vector<unsigned> widths;
|
||||
for (unsigned i = 0; i < m.row_count() && i < mr ; i++) {
|
||||
A.push_back(std::vector<std::string>());
|
||||
for (unsigned j = 0; j < m.column_count() && j < nc; j++) {
|
||||
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < m.column_count() && j < nc; j++) {
|
||||
widths.push_back(get_width_of_column(j, A));
|
||||
}
|
||||
|
||||
print_matrix_with_widths(A, widths);
|
||||
}
|
||||
void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc);
|
||||
|
||||
template<typename T, typename X>
|
||||
void print_matrix(static_matrix<T, X> &m) {
|
||||
std::vector<std::vector<std::string>> A;
|
||||
std::vector<unsigned> widths;
|
||||
std::set<pair<unsigned, unsigned>> domain = m.get_domain();
|
||||
for (unsigned i = 0; i < m.row_count(); i++) {
|
||||
A.push_back(std::vector<std::string>());
|
||||
for (unsigned j = 0; j < m.column_count(); j++) {
|
||||
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < m.column_count(); j++) {
|
||||
widths.push_back(get_width_of_column(j, A));
|
||||
}
|
||||
|
||||
print_matrix_with_widths(A, widths);
|
||||
}
|
||||
void print_matrix(static_matrix<T, X> &m);
|
||||
|
||||
template <typename T, typename X>
|
||||
void print_matrix(sparse_matrix<T, X>& m) {
|
||||
std::vector<std::vector<std::string>> A;
|
||||
std::vector<unsigned> widths;
|
||||
for (unsigned i = 0; i < m.row_count(); i++) {
|
||||
A.push_back(std::vector<std::string>());
|
||||
for (unsigned j = 0; j < m.column_count(); j++) {
|
||||
A[i].push_back(T_to_string(static_cast<T>(m(i, j))));
|
||||
}
|
||||
}
|
||||
|
||||
for (unsigned j = 0; j < m.column_count(); j++) {
|
||||
widths.push_back(get_width_of_column(j, A));
|
||||
}
|
||||
|
||||
print_matrix_with_widths(A, widths);
|
||||
}
|
||||
void print_matrix(sparse_matrix<T, X>& m);
|
||||
#endif
|
||||
|
||||
enum class LU_status { OK, Degenerated};
|
||||
|
||||
template <typename T, typename X>
|
||||
X dot_product(const std::vector<T> & a, const std::vector<X> & b, unsigned l) {
|
||||
auto r = zero_of_type<X>();
|
||||
for (unsigned i = 0; i < l; i++) {
|
||||
r += a[i] * b[i];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
X dot_product(const std::vector<T> & a, const std::vector<X> & b, unsigned l);
|
||||
|
||||
template <typename T, typename X>
|
||||
class one_elem_on_diag: public tail_matrix<T, X> {
|
||||
|
|
@ -104,15 +48,7 @@ public:
|
|||
#endif
|
||||
}
|
||||
|
||||
one_elem_on_diag(const one_elem_on_diag & o) {
|
||||
m_i = o.m_i;
|
||||
m_val = o.m_val;
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
m_m = m_n = o.m_m;
|
||||
m_one_over_val = numeric_traits<T>::one() / o.m_val;
|
||||
#endif
|
||||
}
|
||||
one_elem_on_diag(const one_elem_on_diag & o);
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
unsigned m_m;
|
||||
|
|
@ -121,16 +57,7 @@ public:
|
|||
virtual void set_number_of_columns(unsigned n) { m_m = n; m_n = n; }
|
||||
T m_one_over_val;
|
||||
|
||||
T get_elem (unsigned i, unsigned j) const {
|
||||
if (i == j){
|
||||
if (j == m_i) {
|
||||
return m_one_over_val;
|
||||
}
|
||||
return numeric_traits<T>::one();
|
||||
}
|
||||
|
||||
return numeric_traits<T>::zero();
|
||||
}
|
||||
T get_elem (unsigned i, unsigned j) const;
|
||||
|
||||
unsigned row_count() const { return m_m; } // not defined }
|
||||
unsigned column_count() const { return m_m; } // not defined }
|
||||
|
|
@ -143,13 +70,7 @@ public:
|
|||
w[m_i] /= m_val;
|
||||
}
|
||||
|
||||
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
|
||||
w[m_i] /= m_val;
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_i])) { // todo : is it needed?
|
||||
w.erase_from_index(m_i);
|
||||
w[m_i] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings);
|
||||
|
||||
void conjugate_by_permutation(permutation_matrix<T, X> & p) {
|
||||
// this = p * this * p(-1)
|
||||
|
|
@ -166,10 +87,10 @@ public:
|
|||
}
|
||||
}; // end of one_elem_on_diag
|
||||
|
||||
// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c
|
||||
// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5
|
||||
|
||||
enum class LU_status { OK, Degenerated};
|
||||
|
||||
// This class supports updates of the columns of B, and solves systems Bx=b,and yB=c
|
||||
// Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5
|
||||
template <typename T, typename X>
|
||||
class lu {
|
||||
LU_status m_status = LU_status::OK;
|
||||
|
|
@ -198,335 +119,85 @@ public:
|
|||
std::vector<unsigned>& basis,
|
||||
std::vector<int> & basis_heading,
|
||||
lp_settings & settings,
|
||||
std::vector<unsigned> & non_basic_columns):
|
||||
m_dim(A.row_count()),
|
||||
m_A(A),
|
||||
m_basis(basis),
|
||||
m_Q(m_dim),
|
||||
m_R(m_dim),
|
||||
m_U(A, basis), // create the square matrix that eventually will be factorized
|
||||
m_settings(settings),
|
||||
m_basis_heading(basis_heading),
|
||||
m_non_basic_columns(non_basic_columns),
|
||||
m_row_eta_work_vector(A.row_count()){
|
||||
#ifdef LEAN_DEBUG
|
||||
debug_test_of_basis(A, basis);
|
||||
#endif
|
||||
create_initial_factorization();
|
||||
if (get_status() != LU_status::OK) {
|
||||
if (get_status() == LU_status::Degenerated) {
|
||||
std::cout << "lu status is Degenerated" << std::endl;
|
||||
} else {
|
||||
std::cout << "lu status is " <<static_cast<int>(get_status()) << std::endl;
|
||||
}
|
||||
return;
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(check_correctness());
|
||||
#endif
|
||||
}
|
||||
|
||||
void debug_test_of_basis(static_matrix<T, X> const & A, std::vector<unsigned> & basis) {
|
||||
std::set<unsigned> set;
|
||||
for (unsigned i = 0; i < A.row_count(); i++) {
|
||||
lean_assert(basis[i]< A.column_count());
|
||||
set.insert(basis[i]);
|
||||
}
|
||||
lean_assert(set.size() == A.row_count());
|
||||
}
|
||||
std::vector<unsigned> & non_basic_columns);
|
||||
void debug_test_of_basis(static_matrix<T, X> const & A, std::vector<unsigned> & basis);
|
||||
|
||||
|
||||
unsigned non_basic_column_index_in_non_basic_columns(unsigned j) {
|
||||
return - m_basis_heading[j] - 1;
|
||||
}
|
||||
unsigned non_basic_column_index_in_non_basic_columns(unsigned j) { return - m_basis_heading[j] - 1; }
|
||||
|
||||
void solve_By(std::vector<X> & y) {
|
||||
init_vector_y(y);
|
||||
solve_By_when_y_is_ready(y);
|
||||
}
|
||||
void solve_By(std::vector<X> & y);
|
||||
|
||||
void solve_Bd_when_w_is_ready(std::vector<T> & d, indexed_vector<T>& w ) { // w - the vector featuring in 24.3
|
||||
for (int i = m_dim - 1; i >= 0; i--) { // index ? todo
|
||||
d[i] = w[i];
|
||||
}
|
||||
solve_By_when_y_is_ready(d);
|
||||
}
|
||||
void solve_Bd_when_w_is_ready(std::vector<T> & d, indexed_vector<T>& w );
|
||||
|
||||
template <typename L>
|
||||
void solve_By_when_y_is_ready(std::vector<L> & y) {
|
||||
m_U.double_solve_U_y(y);
|
||||
m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal
|
||||
if (precise<X>()) return;
|
||||
unsigned i = m_dim;
|
||||
while (i--) {
|
||||
if (is_zero(y[i])) continue;
|
||||
if (m_settings.abs_val_is_smaller_than_drop_tolerance(y[i])){
|
||||
y[i] = zero_of_type<L>();
|
||||
}
|
||||
}
|
||||
}
|
||||
void solve_By_when_y_is_ready(std::vector<L> & y);
|
||||
void print_indexed_vector(indexed_vector<T> & w, std::ofstream & f);
|
||||
|
||||
void print_basis(std::ofstream & f);
|
||||
void print_matrix_compact(std::ofstream & f);
|
||||
|
||||
void print(indexed_vector<T> & w);
|
||||
void solve_Bd(unsigned a_column, std::vector<T> & d, indexed_vector<T> & w);
|
||||
|
||||
void print_indexed_vector(indexed_vector<T> & w, std::ofstream & f) {
|
||||
f << "vector_start" << std::endl;
|
||||
for (unsigned j : w.m_index) {
|
||||
f << j << " " << w[j] << std::endl;
|
||||
}
|
||||
f << "vector_end" << std::endl;
|
||||
}
|
||||
void print_basis(std::ofstream & f) {
|
||||
f << "basis_start" << std::endl;
|
||||
for (unsigned j : m_basis)
|
||||
f << j << std::endl;
|
||||
f << "basis_end" << std::endl;
|
||||
}
|
||||
void print_matrix_compact(std::ofstream & f) {
|
||||
f << "matrix_start" << std::endl;
|
||||
f << "nrows " << m_A.row_count() << std::endl;
|
||||
f << "ncolumns " << m_A.column_count() << std::endl;
|
||||
for (unsigned i = 0; i < m_A.row_count(); i++) {
|
||||
auto & row = m_A.m_rows[i];
|
||||
f << "row " << i << std::endl;
|
||||
for (auto & t : row.m_cells) {
|
||||
f << "column " << t.m_j << " value " << t.m_value << std::endl;
|
||||
}
|
||||
f << "row_end" << std::endl;
|
||||
}
|
||||
f << "matrix_end" << std::endl;
|
||||
}
|
||||
bool column_can_be_taken_to_basis(unsigned i) { return m_basis_heading[i] < 0; }
|
||||
|
||||
void print(indexed_vector<T> & w) {
|
||||
std::string dump_file_name("/tmp/lu");
|
||||
remove(dump_file_name.c_str());
|
||||
std::ofstream f(dump_file_name);
|
||||
if (!f.is_open()) {
|
||||
std::cout << "cannot open file " << dump_file_name << std::endl;
|
||||
return;
|
||||
}
|
||||
std::cout << "writing lu dump to " << dump_file_name << std::endl;
|
||||
print_matrix_compact(f);
|
||||
print_basis(f);
|
||||
print_indexed_vector(w, f);
|
||||
f.close();
|
||||
}
|
||||
void solve_yB_internal(std::vector<T>& y);
|
||||
|
||||
void solve_Bd(unsigned a_column, std::vector<T> & d, indexed_vector<T> & w) {
|
||||
init_vector_w(a_column, w);
|
||||
solve_Bd_when_w_is_ready(d, w);
|
||||
}
|
||||
void add_delta_to_solution(std::vector<T>& yc, std::vector<T>& y);
|
||||
|
||||
bool column_can_be_taken_to_basis(unsigned i) {
|
||||
return m_basis_heading[i] < 0;
|
||||
}
|
||||
void find_error_of_yB(std::vector<T>& yc, const std::vector<T>& y);
|
||||
|
||||
void solve_yB_internal(std::vector<T>& y) {
|
||||
// first solve yU = cb*R(-1)
|
||||
m_R.apply_reverse_from_right(y); // got y = cb*R(-1)
|
||||
m_U.solve_y_U(y); // got y*U=cb*R(-1)
|
||||
m_Q.apply_reverse_from_right(y); //
|
||||
for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) {
|
||||
#ifdef LEAN_DEBUG
|
||||
(*e)->set_number_of_columns(m_dim);
|
||||
#endif
|
||||
(*e)->apply_from_right(y);
|
||||
}
|
||||
}
|
||||
void solve_yB(std::vector<T> & y);
|
||||
|
||||
void add_delta_to_solution(std::vector<T>& yc, std::vector<T>& y){
|
||||
unsigned i = y.size();
|
||||
while (i--)
|
||||
y[i]+=yc[i];
|
||||
}
|
||||
void apply_Q_R_to_U(permutation_matrix<T, X> & r_wave);
|
||||
|
||||
void find_error_of_yB(std::vector<T>& yc, const std::vector<T>& y) {
|
||||
unsigned i = m_dim;
|
||||
while (i--) {
|
||||
yc[i] -= m_A.dot_product_with_column(y, m_basis[i]);
|
||||
}
|
||||
}
|
||||
void change_basis(unsigned entering, unsigned leaving);
|
||||
|
||||
// solves y*B = y
|
||||
// y is the input
|
||||
|
||||
void solve_yB(std::vector<T> & y) {
|
||||
std::vector<T> yc(y); // copy y aside
|
||||
solve_yB_internal(y);
|
||||
find_error_of_yB(yc, y);
|
||||
solve_yB_internal(yc);
|
||||
add_delta_to_solution(yc, y);
|
||||
}
|
||||
|
||||
void apply_Q_R_to_U(permutation_matrix<T, X> & r_wave) {
|
||||
m_U.multiply_from_right(r_wave);
|
||||
m_U.multiply_from_left_with_reverse(r_wave);
|
||||
}
|
||||
|
||||
void change_basis(unsigned entering, unsigned leaving) {
|
||||
lean_assert(entering < m_A.column_count() && leaving < m_A.column_count());
|
||||
int place_in_basis = m_basis_heading[leaving];
|
||||
int place_in_non_basis = - m_basis_heading[entering] - 1;
|
||||
lean_assert(0 <= place_in_basis && place_in_basis < m_A.column_count());
|
||||
m_basis_heading[entering] = place_in_basis;
|
||||
m_basis_heading[leaving] = -place_in_non_basis - 1;
|
||||
m_basis[place_in_basis] = entering;
|
||||
m_non_basic_columns[place_in_non_basis] = leaving;
|
||||
}
|
||||
|
||||
void restore_basis_change(unsigned entering, unsigned leaving) {
|
||||
if (m_basis_heading[entering] < 0) {
|
||||
return; // the basis has not been changed
|
||||
}
|
||||
change_basis(leaving, entering);
|
||||
}
|
||||
void restore_basis_change(unsigned entering, unsigned leaving);
|
||||
|
||||
LU_status get_status() { return m_status; }
|
||||
|
||||
void set_status(LU_status status) { m_status = status; }
|
||||
|
||||
// Solving yB = cb to find the entering variable,
|
||||
// where cb is the cost vector projected to B.
|
||||
// The result is stored in cb.
|
||||
~lu();
|
||||
|
||||
// solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering
|
||||
// variable
|
||||
T B(unsigned i, unsigned j) { return m_A(i, m_basis[j]); }
|
||||
|
||||
~lu(){
|
||||
for (auto t : m_tail) {
|
||||
delete t;
|
||||
}
|
||||
}
|
||||
void init_vector_y(std::vector<X> & y);
|
||||
|
||||
T B(unsigned i, unsigned j) {
|
||||
return m_A(i, m_basis[j]);
|
||||
}
|
||||
void perform_transformations_on_w(indexed_vector<T>& w);
|
||||
|
||||
void init_vector_y(std::vector<X> & y) {
|
||||
apply_lp_lists_to_y(y);
|
||||
m_Q.apply_reverse_from_left(y);
|
||||
}
|
||||
void init_vector_w(unsigned entering, indexed_vector<T> & w);
|
||||
void apply_lp_lists_to_w(indexed_vector<T> & w);
|
||||
void apply_lp_lists_to_y(std::vector<X>& y);
|
||||
|
||||
void swap_rows(int j, int k);
|
||||
|
||||
void perform_transformations_on_w(indexed_vector<T>& w) {
|
||||
apply_lp_lists_to_w(w);
|
||||
m_Q.apply_reverse_from_left(w);
|
||||
lean_assert(numeric_traits<T>::precise() || check_vector_for_small_values(w, m_settings));
|
||||
}
|
||||
|
||||
// see Chvatal 24.3
|
||||
void init_vector_w(unsigned entering, indexed_vector<T> & w) {
|
||||
w.clear();
|
||||
m_A.copy_column_to_vector(entering, w); // w = a, the column
|
||||
perform_transformations_on_w(w);
|
||||
}
|
||||
|
||||
void apply_lp_lists_to_w(indexed_vector<T> & w) {
|
||||
for (unsigned i = 0; i < m_tail.size(); i++) {
|
||||
m_tail[i]->apply_from_left_to_T(w, m_settings);
|
||||
lean_assert(check_vector_for_small_values(w, m_settings));
|
||||
}
|
||||
}
|
||||
|
||||
void apply_lp_lists_to_y(std::vector<X>& y) {
|
||||
for (unsigned i = 0; i < m_tail.size(); i++) {
|
||||
m_tail[i]->apply_from_left(y, m_settings);
|
||||
}
|
||||
}
|
||||
|
||||
void swap_rows(int j, int k) {
|
||||
if (j != k) {
|
||||
m_Q.transpose_from_left(j, k);
|
||||
m_U.swap_rows(j, k);
|
||||
}
|
||||
}
|
||||
|
||||
void swap_columns(int j, int pivot_column) {
|
||||
if (j == pivot_column)
|
||||
return;
|
||||
m_R.transpose_from_right(j, pivot_column);
|
||||
m_U.swap_columns(j, pivot_column);
|
||||
}
|
||||
void swap_columns(int j, int pivot_column);
|
||||
|
||||
void push_matrix_to_tail(tail_matrix<T, X>* tm) {
|
||||
m_tail.push_back(tm);
|
||||
}
|
||||
|
||||
bool pivot_the_row(int row) {
|
||||
eta_matrix<T, X> * eta_matrix = get_eta_matrix_for_pivot(row);
|
||||
if (eta_matrix == nullptr) {
|
||||
m_U.shorten_active_matrix(row, nullptr);
|
||||
return true;
|
||||
}
|
||||
if (!m_U.pivot_with_eta(row, eta_matrix, m_settings))
|
||||
return false;
|
||||
eta_matrix->conjugate_by_permutation(m_Q);
|
||||
push_matrix_to_tail(eta_matrix);
|
||||
return true;
|
||||
}
|
||||
// we're processing the column j now
|
||||
eta_matrix<T, X> * get_eta_matrix_for_pivot(unsigned j) {
|
||||
eta_matrix<T, X> *ret;
|
||||
m_U.fill_eta_matrix(j, &ret);
|
||||
return ret;
|
||||
}
|
||||
// we're processing the column j now
|
||||
eta_matrix<T, X> * get_eta_matrix_for_pivot(unsigned j, sparse_matrix<T, X>& copy_of_U) {
|
||||
eta_matrix<T, X> *ret;
|
||||
copy_of_U.fill_eta_matrix(j, &ret);
|
||||
return ret;
|
||||
}
|
||||
bool pivot_the_row(int row);
|
||||
|
||||
void print_basis() {
|
||||
std::cout << "basis ";
|
||||
for (unsigned i = 0; i < m_dim; i++) {
|
||||
std::cout << m_basis[i] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
eta_matrix<T, X> * get_eta_matrix_for_pivot(unsigned j);
|
||||
// we're processing the column j now
|
||||
eta_matrix<T, X> * get_eta_matrix_for_pivot(unsigned j, sparse_matrix<T, X>& copy_of_U);
|
||||
|
||||
void print_basis_heading() {
|
||||
print_basis();
|
||||
for (unsigned i = 0; i < m_A.column_count(); i++) {
|
||||
std::cout << m_basis_heading[i] << ",";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
void print_basis();
|
||||
|
||||
void print_basis_heading();
|
||||
|
||||
// see page 407 of Chvatal
|
||||
unsigned transform_U_to_V_by_replacing_column(unsigned leaving, indexed_vector<T> & w) {
|
||||
int leaving_column = m_basis_heading[leaving];
|
||||
// std::cout << "leaving_column = " << leaving_column << std::endl;
|
||||
unsigned column_to_replace = m_R.apply_reverse(leaving_column);
|
||||
// std::cout << "leaving_column modified = " << column_to_replace << std::endl;
|
||||
m_U.replace_column(column_to_replace, w, m_settings);
|
||||
return column_to_replace;
|
||||
}
|
||||
unsigned transform_U_to_V_by_replacing_column(unsigned leaving, indexed_vector<T> & w);
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
void check_vector_w(unsigned entering) {
|
||||
T * w = new T[m_dim];
|
||||
m_A.copy_column_to_vector(entering, w);
|
||||
check_apply_lp_lists_to_w(w);
|
||||
delete [] w;
|
||||
}
|
||||
void check_vector_w(unsigned entering);
|
||||
|
||||
void check_apply_matrix_to_vector(matrix<T, X> *lp, T *w) {
|
||||
if (lp != nullptr) {
|
||||
lp -> set_number_of_rows(m_dim);
|
||||
lp -> set_number_of_columns(m_dim);
|
||||
apply_to_vector(*lp, w);
|
||||
}
|
||||
}
|
||||
void check_apply_matrix_to_vector(matrix<T, X> *lp, T *w);
|
||||
|
||||
void check_apply_lp_lists_to_w(T * w) {
|
||||
for (unsigned i = 0; i < m_tail.size(); i++) {
|
||||
check_apply_matrix_to_vector(m_tail[i], w);
|
||||
}
|
||||
permutation_matrix<T, X> qr = m_Q.get_reverse();
|
||||
apply_to_vector(qr, w);
|
||||
for (int i = m_dim - 1; i >= 0; i--) {
|
||||
lean_assert(abs(w[i] - w[i]) < 0.0000001);
|
||||
}
|
||||
}
|
||||
void check_apply_lp_lists_to_w(T * w);
|
||||
|
||||
// provide some access operators for testing
|
||||
permutation_matrix<T, X> & Q() { return m_Q; }
|
||||
|
|
@ -555,34 +226,9 @@ public:
|
|||
}
|
||||
|
||||
|
||||
void process_column(int j) {
|
||||
unsigned pi, pj;
|
||||
m_U.get_pivot_for_column(pi, pj, T(m_settings.c_partial_pivoting), j);
|
||||
if (pi == -1) {
|
||||
std::cout << "cannot find the pivot for column " << j << std::endl;
|
||||
m_failure = true;
|
||||
return;
|
||||
}
|
||||
swap_columns(j, pj);
|
||||
swap_rows(j, pi);
|
||||
if (!pivot_the_row(j)) {
|
||||
std::cout << "pivot_the_row(" << j << ") failed" << std::endl;
|
||||
m_failure = true;
|
||||
}
|
||||
}
|
||||
void process_column(int j);
|
||||
|
||||
bool is_correct() {
|
||||
#ifdef LEAN_DEBUG
|
||||
if (get_status() != LU_status::OK) {
|
||||
return false;
|
||||
}
|
||||
dense_matrix<T, X> left_side = get_left_side();
|
||||
dense_matrix<T, X> right_side = get_right_side();
|
||||
return left_side == right_side;
|
||||
#else
|
||||
return true;
|
||||
#endif
|
||||
}
|
||||
bool is_correct();
|
||||
|
||||
int basis_heading(unsigned j) {
|
||||
lean_assert(j < m_A.column_count());
|
||||
|
|
@ -590,297 +236,53 @@ public:
|
|||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
dense_matrix<T, X> tail_product() {
|
||||
lean_assert(tail_size() > 0);
|
||||
dense_matrix<T, X> left_side = permutation_matrix<T, X>(m_dim);
|
||||
for (unsigned i = 0; i < tail_size(); i++) {
|
||||
matrix<T, X>* lp = get_lp_matrix(i);
|
||||
lp->set_number_of_rows(m_dim);
|
||||
lp->set_number_of_columns(m_dim);
|
||||
left_side = ((*lp) * left_side);
|
||||
}
|
||||
return left_side;
|
||||
}
|
||||
dense_matrix<T, X> get_left_side() {
|
||||
dense_matrix<T, X> left_side = get_B(*this);
|
||||
for (unsigned i = 0; i < tail_size(); i++) {
|
||||
matrix<T, X>* lp = get_lp_matrix(i);
|
||||
lp->set_number_of_rows(m_dim);
|
||||
lp->set_number_of_columns(m_dim);
|
||||
left_side = ((*lp) * left_side);
|
||||
}
|
||||
return left_side;
|
||||
}
|
||||
dense_matrix<T, X> tail_product();
|
||||
dense_matrix<T, X> get_left_side();
|
||||
|
||||
dense_matrix<T, X> get_right_side() {
|
||||
auto ret = U() * R();
|
||||
ret = Q() * ret;
|
||||
return ret;
|
||||
}
|
||||
dense_matrix<T, X> get_right_side();
|
||||
#endif
|
||||
|
||||
// needed for debugging purposes
|
||||
void copy_w(T *buffer, indexed_vector<T> & w) {
|
||||
unsigned i = m_dim;
|
||||
while (i--) {
|
||||
buffer[i] = w[i];
|
||||
}
|
||||
}
|
||||
void copy_w(T *buffer, indexed_vector<T> & w);
|
||||
|
||||
// needed for debugging purposes
|
||||
void restore_w(T *buffer, indexed_vector<T> & w) {
|
||||
unsigned i = m_dim;
|
||||
while (i--) {
|
||||
w[i] = buffer[i];
|
||||
}
|
||||
}
|
||||
bool all_columns_and_rows_are_active() {
|
||||
unsigned i = m_dim;
|
||||
while (i--) {
|
||||
lean_assert(m_U.col_is_active(i));
|
||||
lean_assert(m_U.row_is_active(i));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
void restore_w(T *buffer, indexed_vector<T> & w);
|
||||
bool all_columns_and_rows_are_active();
|
||||
|
||||
bool too_dense(unsigned j) const {
|
||||
unsigned r = m_dim - j;
|
||||
if (r < 5)
|
||||
return false;
|
||||
return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j);
|
||||
}
|
||||
bool too_dense(unsigned j) const;
|
||||
|
||||
void pivot_in_dense_mode(unsigned i) {
|
||||
int j = m_dense_LU->find_pivot_column_in_row(i);
|
||||
if (j == -1) {
|
||||
m_failure = true;
|
||||
return;
|
||||
}
|
||||
if (i != j) {
|
||||
swap_columns(i, j);
|
||||
m_dense_LU->swap_columns(i, j);
|
||||
}
|
||||
m_dense_LU->pivot(i, m_settings);
|
||||
}
|
||||
void pivot_in_dense_mode(unsigned i);
|
||||
|
||||
void create_initial_factorization(){
|
||||
m_U.prepare_for_factorization();
|
||||
unsigned j;
|
||||
for (j = 0; j < m_dim; j++) {
|
||||
process_column(j);
|
||||
if (m_failure || too_dense(j + 1)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (m_failure) {
|
||||
set_status(LU_status::Degenerated);
|
||||
return;
|
||||
}
|
||||
if (j == m_dim) {
|
||||
lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
|
||||
return;
|
||||
}
|
||||
j++;
|
||||
// std::cout << "switching to dense factoring for " << j << endl;
|
||||
m_dense_LU = new square_dense_submatrix<T, X>(&m_U, j);
|
||||
for (; j < m_dim; j++) {
|
||||
pivot_in_dense_mode(j);
|
||||
if (m_failure) {
|
||||
set_status(LU_status::Degenerated);
|
||||
return;
|
||||
}
|
||||
}
|
||||
m_dense_LU->update_parent_matrix(m_settings);
|
||||
lean_assert(m_dense_LU->is_L_matrix());
|
||||
m_dense_LU->conjugate_by_permutation(m_Q);
|
||||
push_matrix_to_tail(m_dense_LU);
|
||||
// lean_assert(is_correct());
|
||||
// lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
|
||||
}
|
||||
void create_initial_factorization();
|
||||
|
||||
void calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix<T, X> & r_wave) {
|
||||
lean_assert(bump_start <= bump_end);
|
||||
if (bump_start == bump_end) {
|
||||
return;
|
||||
}
|
||||
void calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix<T, X> & r_wave);
|
||||
|
||||
r_wave[bump_start] = bump_end; // sending the offensive column to the end of the bump
|
||||
void scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump);
|
||||
|
||||
for ( unsigned i = bump_start + 1 ; i <= bump_end; i++ ) {
|
||||
r_wave[i] = i - 1;
|
||||
}
|
||||
bool diagonal_element_is_off(T diag_element) { return false; }
|
||||
|
||||
m_U.multiply_from_right(r_wave);
|
||||
m_U.multiply_from_left_with_reverse(r_wave);
|
||||
}
|
||||
|
||||
void scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) {
|
||||
std::vector<indexed_value<T>> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump));
|
||||
for (auto & iv : last_row_vec) {
|
||||
if (is_zero(iv.m_value)) continue;
|
||||
lean_assert(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value));
|
||||
unsigned adjusted_col = m_U.adjust_column_inverse(iv.m_index);
|
||||
if (adjusted_col < lowest_row_of_the_bump) {
|
||||
m_row_eta_work_vector.set_value(-iv.m_value, adjusted_col);
|
||||
} else {
|
||||
m_row_eta_work_vector.set_value(iv.m_value, adjusted_col); // preparing to calculate the real value in the matrix
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool diagonal_element_is_off(T diag_element) {
|
||||
return false;
|
||||
}
|
||||
|
||||
void pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) {
|
||||
// we have the system right side at m_row_eta_work_vector now
|
||||
// solve the system column wise
|
||||
for (unsigned j = replaced_column; j < lowest_row_of_the_bump; j++) {
|
||||
T v = m_row_eta_work_vector[j];
|
||||
if (numeric_traits<T>::is_zero(v)) continue; // this column does not contribute to the solution
|
||||
unsigned aj = m_U.adjust_row(j);
|
||||
std::vector<indexed_value<T>> & row = m_U.get_row_values(aj);
|
||||
for (auto & iv : row) {
|
||||
unsigned col = m_U.adjust_column_inverse(iv.m_index);
|
||||
lean_assert(col >= j || numeric_traits<T>::is_zero(iv.m_value));
|
||||
if (col == j) continue;
|
||||
if (numeric_traits<T>::is_zero(iv.m_value)) {
|
||||
continue;
|
||||
}
|
||||
// the -v is for solving the system ( to zero the last row), and +v is for pivoting
|
||||
T delta = col < lowest_row_of_the_bump? -v * iv.m_value: v * iv.m_value;
|
||||
lean_assert(numeric_traits<T>::is_zero(delta) == false);
|
||||
|
||||
if (numeric_traits<T>::is_zero(m_row_eta_work_vector[col])) {
|
||||
if (!m_settings.abs_val_is_smaller_than_drop_tolerance(delta)){
|
||||
m_row_eta_work_vector.set_value(delta, col);
|
||||
}
|
||||
} else {
|
||||
T t = (m_row_eta_work_vector[col] += delta);
|
||||
if (m_settings.abs_val_is_smaller_than_drop_tolerance(t)){
|
||||
m_row_eta_work_vector[col] = numeric_traits<T>::zero();
|
||||
auto it = std::find(m_row_eta_work_vector.m_index.begin(), m_row_eta_work_vector.m_index.end(), col);
|
||||
if (it != m_row_eta_work_vector.m_index.end())
|
||||
m_row_eta_work_vector.m_index.erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
lean_assert(m_row_eta_work_vector.is_OK());
|
||||
}
|
||||
void pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump);
|
||||
// see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last
|
||||
// row at the same time
|
||||
row_eta_matrix<T, X> *get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking) {
|
||||
if (replaced_column == lowest_row_of_the_bump) return nullptr;
|
||||
scan_last_row_to_work_vector(lowest_row_of_the_bump);
|
||||
pivot_and_solve_the_system(replaced_column, lowest_row_of_the_bump);
|
||||
T denom = std::max(T(1), abs(pivot_elem_for_checking));
|
||||
if (
|
||||
#ifdef LEAN_DEBUG
|
||||
!is_zero(pivot_elem_for_checking) &&
|
||||
#endif
|
||||
!m_settings.abs_val_is_smaller_than_pivot_tolerance((m_row_eta_work_vector[lowest_row_of_the_bump] - pivot_elem_for_checking) / denom)) {
|
||||
// std::cout << "m_row_eta_work_vector[" << lowest_row_of_the_bump << "] = " << T_to_string(m_row_eta_work_vector[lowest_row_of_the_bump]) << ", but pivot = " << T_to_string(pivot_elem_for_checking) << endl;
|
||||
set_status(LU_status::Degenerated);
|
||||
// std::cout << "diagonal element is off" << endl;
|
||||
return nullptr;
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump, m_dim);
|
||||
#else
|
||||
auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump);
|
||||
#endif
|
||||
|
||||
for (auto j : m_row_eta_work_vector.m_index) {
|
||||
if (j < lowest_row_of_the_bump) {
|
||||
auto & v = m_row_eta_work_vector[j];
|
||||
if (!is_zero(v)) {
|
||||
if (!m_settings.abs_val_is_smaller_than_drop_tolerance(v)){
|
||||
ret->push_back(j, v);
|
||||
}
|
||||
v = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
} // now the lowest_row_of_the_bump contains the rest of the row to the right of the bump with correct values
|
||||
return ret;
|
||||
}
|
||||
row_eta_matrix<T, X> *get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T & pivot_elem_for_checking);
|
||||
|
||||
// This method does not update the basis: is_correct() should not be called since it works with the basis.
|
||||
void replace_column(unsigned leaving, T pivot_elem, indexed_vector<T> & w){
|
||||
lean_assert(m_basis_heading[leaving] >= 0);
|
||||
unsigned replaced_column = transform_U_to_V_by_replacing_column(leaving, w);
|
||||
unsigned lowest_row_of_the_bump = m_U.lowest_row_in_column(replaced_column);
|
||||
permutation_matrix<T, X> r_wave(m_dim);
|
||||
calculate_r_wave_and_update_U(replaced_column, lowest_row_of_the_bump, r_wave);
|
||||
auto row_eta = get_row_eta_matrix_and_set_row_vector(replaced_column, lowest_row_of_the_bump, pivot_elem);
|
||||
if (get_status() == LU_status::Degenerated) {
|
||||
m_row_eta_work_vector.clear_all();
|
||||
return;
|
||||
}
|
||||
m_Q.multiply_by_permutation_from_right(r_wave);
|
||||
m_R.multiply_by_permutation_reverse_from_left(r_wave);
|
||||
if (row_eta != nullptr) {
|
||||
row_eta->conjugate_by_permutation(m_Q);
|
||||
push_matrix_to_tail(row_eta);
|
||||
}
|
||||
calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump);
|
||||
lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
|
||||
lean_assert(w.is_OK() && m_row_eta_work_vector.is_OK());
|
||||
}
|
||||
void replace_column(unsigned leaving, T pivot_elem, indexed_vector<T> & w);
|
||||
|
||||
void calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){
|
||||
T diagonal_elem;
|
||||
if (replaced_column < lowest_row_of_the_bump) {
|
||||
diagonal_elem = m_row_eta_work_vector[lowest_row_of_the_bump];
|
||||
// lean_assert(m_row_eta_work_vector.is_OK());
|
||||
m_U.set_row_from_work_vector_and_clean_work_vector_not_adjusted(m_U.adjust_row(lowest_row_of_the_bump), m_row_eta_work_vector, m_settings);
|
||||
} else {
|
||||
diagonal_elem = m_U(lowest_row_of_the_bump, lowest_row_of_the_bump); // todo - get it more efficiently
|
||||
}
|
||||
if (m_settings.abs_val_is_smaller_than_pivot_tolerance(diagonal_elem)) {
|
||||
set_status(LU_status::Degenerated);
|
||||
return;
|
||||
}
|
||||
void calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump);
|
||||
|
||||
calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem);
|
||||
// lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
|
||||
}
|
||||
|
||||
void calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) {
|
||||
auto l = new one_elem_on_diag<T, X>(lowest_row_of_the_bump, diagonal_element);
|
||||
#ifdef LEAN_DEBUG
|
||||
l->set_number_of_columns(m_dim);
|
||||
#endif
|
||||
push_matrix_to_tail(l);
|
||||
m_U.divide_row_by_constant(lowest_row_of_the_bump, diagonal_element, m_settings);
|
||||
l->conjugate_by_permutation(m_Q);
|
||||
}
|
||||
void calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element);
|
||||
|
||||
void prepare_entering(unsigned entering, indexed_vector<T> & w) {
|
||||
init_vector_w(entering, w);
|
||||
}
|
||||
}; // end of lu
|
||||
|
||||
template <typename T, typename X>
|
||||
void init_factorization(lu<T, X>* & factorization, static_matrix<T, X> & m_A, std::vector<unsigned> & m_basis, std::vector<int> & m_basis_heading, lp_settings &m_settings, std::vector<unsigned> & non_basic_columns) {
|
||||
if (factorization != nullptr) {
|
||||
delete factorization;
|
||||
}
|
||||
factorization = new lu<T, X>(m_A, m_basis, m_basis_heading, m_settings, non_basic_columns);
|
||||
if (factorization->get_status() != LU_status::OK) {
|
||||
std::cout << "failing in init_factorization" << std::endl;
|
||||
return;
|
||||
}
|
||||
}
|
||||
void init_factorization(lu<T, X>* & factorization, static_matrix<T, X> & m_A, std::vector<unsigned> & m_basis, std::vector<int> & m_basis_heading, lp_settings &m_settings, std::vector<unsigned> & non_basic_columns);
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X>
|
||||
dense_matrix<T, X> get_B(lu<T, X>& f) {
|
||||
dense_matrix<T, X> B(f.dimension(), f.dimension());
|
||||
for (unsigned i = 0; i < f.dimension(); i++)
|
||||
for (unsigned j = 0; j < f.dimension(); j++)
|
||||
B.set_elem(i, j, f.B_(i, j));
|
||||
|
||||
return B;
|
||||
}
|
||||
dense_matrix<T, X> get_B(lu<T, X>& f);
|
||||
#endif
|
||||
}
|
||||
|
|
|
|||
48
src/util/lp/lu_instances.cpp
Normal file
48
src/util/lp/lu_instances.cpp
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/lu.cpp"
|
||||
template double lean::dot_product<double, double>(std::vector<double, std::allocator<double> > const&, std::vector<double, std::allocator<double> > const&, unsigned int);
|
||||
template void lean::lu<double, double>::change_basis(unsigned int, unsigned int);
|
||||
template lean::lu<double, double>::lu(lean::static_matrix<double, double> const&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<int, std::allocator<int> >&, lean::lp_settings&, std::vector<unsigned int, std::allocator<unsigned int> >&);
|
||||
template void lean::lu<double, double>::push_matrix_to_tail(lean::tail_matrix<double, double>*);
|
||||
template void lean::lu<double, double>::replace_column(unsigned int, double, lean::indexed_vector<double>&);
|
||||
template void lean::lu<double, double>::restore_basis_change(unsigned int, unsigned int);
|
||||
template void lean::lu<double, double>::solve_Bd(unsigned int, std::vector<double, std::allocator<double> >&, lean::indexed_vector<double>&);
|
||||
template lean::lu<double, double>::~lu();
|
||||
template void lean::lu<lean::mpq, lean::mpq>::change_basis(unsigned int, unsigned int);
|
||||
template void lean::lu<lean::mpq, lean::mpq>::push_matrix_to_tail(lean::tail_matrix<lean::mpq, lean::mpq>*);
|
||||
template void lean::lu<lean::mpq, lean::mpq>::restore_basis_change(unsigned int, unsigned int);
|
||||
template void lean::lu<lean::mpq, lean::mpq>::solve_Bd(unsigned int, std::vector<lean::mpq, std::allocator<lean::mpq> >&, lean::indexed_vector<lean::mpq>&);
|
||||
template lean::lu<lean::mpq, lean::mpq>::~lu();
|
||||
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::change_basis(unsigned int, unsigned int);
|
||||
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::push_matrix_to_tail(lean::tail_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >*);
|
||||
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::restore_basis_change(unsigned int, unsigned int);
|
||||
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_Bd(unsigned int, std::vector<lean::mpq, std::allocator<lean::mpq> >&, lean::indexed_vector<lean::mpq>&);
|
||||
template lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::~lu();
|
||||
template lean::mpq lean::dot_product<lean::mpq, lean::mpq>(std::vector<lean::mpq, std::allocator<lean::mpq> > const&, std::vector<lean::mpq, std::allocator<lean::mpq> > const&, unsigned int);
|
||||
template void lean::init_factorization<double, double>(lean::lu<double, double>*&, lean::static_matrix<double, double>&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<int, std::allocator<int> >&, lean::lp_settings&, std::vector<unsigned int, std::allocator<unsigned int> >&);
|
||||
template void lean::init_factorization<lean::mpq, lean::mpq>(lean::lu<lean::mpq, lean::mpq>*&, lean::static_matrix<lean::mpq, lean::mpq>&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<int, std::allocator<int> >&, lean::lp_settings&, std::vector<unsigned int, std::allocator<unsigned int> >&);
|
||||
template void lean::init_factorization<lean::mpq, lean::numeric_pair<lean::mpq> >(lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >*&, lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, std::vector<unsigned int, std::allocator<unsigned int> >&, std::vector<int, std::allocator<int> >&, lean::lp_settings&, std::vector<unsigned int, std::allocator<unsigned int> >&);
|
||||
#ifdef LEAN_DEBUG
|
||||
template void lean::print_matrix<double, double>(lean::sparse_matrix<double, double>&);
|
||||
template void lean::print_matrix<float, float>(lean::sparse_matrix<float, float>&);
|
||||
template void lean::print_matrix<double, double>(lean::static_matrix<double, double>&);
|
||||
template bool lean::lu<double, double>::is_correct();
|
||||
template lean::dense_matrix<double, double> lean::get_B<double, double>(lean::lu<double, double>&);
|
||||
#endif
|
||||
template void lean::lu<lean::mpq, lean::mpq>::solve_yB(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template void lean::lu<double, double>::solve_yB(std::vector<double, std::allocator<double> >&);
|
||||
template void lean::lu<lean::mpq, lean::mpq>::solve_By(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template void lean::lu<double, double>::solve_By(std::vector<double, std::allocator<double> >&);
|
||||
template void lean::lu<lean::mpq, lean::mpq>::replace_column(unsigned int, lean::mpq, lean::indexed_vector<lean::mpq>&);
|
||||
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::replace_column(unsigned int, lean::mpq, lean::indexed_vector<lean::mpq>&);
|
||||
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_By(std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&);
|
||||
template void lean::lu<double, double>::init_vector_w(unsigned int, lean::indexed_vector<double>&);
|
||||
template lean::numeric_pair<lean::mpq> lean::dot_product<lean::mpq, lean::numeric_pair<lean::mpq> >(std::vector<lean::mpq, std::allocator<lean::mpq> > const&, std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > > const&, unsigned int);
|
||||
template bool lean::lu<double, double>::pivot_the_row(int);
|
||||
105
src/util/lp/matrix.cpp
Normal file
105
src/util/lp/matrix.cpp
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#ifdef LEAN_DEBUG
|
||||
#include "util/lp/matrix.h"
|
||||
namespace lean {
|
||||
template <typename T, typename X>
|
||||
bool matrix<T, X>::is_equal(const matrix<T, X>& other) {
|
||||
if (other.row_count() != row_count() || other.column_count() != column_count())
|
||||
return false;
|
||||
for (unsigned i = 0; i < row_count(); i++) {
|
||||
for (unsigned j = 0; j < column_count(); j++) {
|
||||
auto a = get_elem(i, j);
|
||||
auto b = other.get_elem(i, j);
|
||||
if (numeric_traits<T>::precise()) {
|
||||
if (a != b) return false;
|
||||
} else if (fabs(numeric_traits<T>::get_double(a - b)) > 0.000001) {
|
||||
// cout << "returning false from operator== of matrix comparison" << endl;
|
||||
// cout << "this matrix is " << endl;
|
||||
// print_matrix(*this);
|
||||
// cout << "other matrix is " << endl;
|
||||
// print_matrix(other);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void apply_to_vector(matrix<T, X> & m, T * w) {
|
||||
// here m is a square matrix
|
||||
unsigned dim = m.row_count();
|
||||
|
||||
T * wc = new T[dim];
|
||||
|
||||
for (unsigned i = 0; i < dim; i++) {
|
||||
wc[i] = w[i];
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < dim; i++) {
|
||||
T t = numeric_traits<T>::zero();
|
||||
for (unsigned j = 0; j < dim; j++) {
|
||||
t += m(i, j) * wc[j];
|
||||
}
|
||||
w[i] = t;
|
||||
}
|
||||
delete [] wc;
|
||||
}
|
||||
|
||||
|
||||
|
||||
unsigned get_width_of_column(unsigned j, std::vector<std::vector<std::string>> & A) {
|
||||
unsigned r = 0;
|
||||
for (unsigned i = 0; i < A.size(); i++) {
|
||||
unsigned s = A[i][j].size();
|
||||
if (r < s) {
|
||||
r = s;
|
||||
}
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
void print_matrix_with_widths(std::vector<std::vector<std::string>> & A, std::vector<unsigned> & ws) {
|
||||
for (unsigned i = 0; i < A.size(); i++) {
|
||||
for (unsigned j = 0; j < A[i].size(); j++) {
|
||||
print_blanks(ws[j] - A[i][j].size());
|
||||
std::cout << A[i][j] << " ";
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
}
|
||||
|
||||
void print_string_matrix(std::vector<std::vector<std::string>> & A) {
|
||||
std::vector<unsigned> widths;
|
||||
|
||||
for (unsigned j = 0; j < A[0].size(); j++) {
|
||||
widths.push_back(get_width_of_column(j, A));
|
||||
}
|
||||
|
||||
print_matrix_with_widths(A, widths);
|
||||
std::cout << std::endl;
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void print_matrix(matrix<T, X> const & m) {
|
||||
if (&m == nullptr) {
|
||||
std::cout << "null" << std::endl;
|
||||
return;
|
||||
}
|
||||
std::vector<std::vector<std::string>> A(m.row_count());
|
||||
for (unsigned i = 0; i < m.row_count(); i++) {
|
||||
for (unsigned j = 0; j < m.column_count(); j++) {
|
||||
A[i].push_back(T_to_string(m.get_elem(i, j)));
|
||||
}
|
||||
}
|
||||
|
||||
print_string_matrix(A);
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
||||
48
src/util/lp/matrix.h
Normal file
48
src/util/lp/matrix.h
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#ifdef LEAN_DEBUG
|
||||
#pragma once
|
||||
#include "util/numerics/numeric_traits.h"
|
||||
#include "util/numerics/double.h"
|
||||
#include <vector>
|
||||
#include <string>
|
||||
#include "util/lp/lp_settings.h"
|
||||
namespace lean {
|
||||
// used for debugging purposes only
|
||||
template <typename T, typename X>
|
||||
class matrix {
|
||||
public:
|
||||
virtual T get_elem (unsigned i, unsigned j) const = 0;
|
||||
virtual unsigned row_count() const = 0;
|
||||
virtual unsigned column_count() const = 0;
|
||||
virtual void set_number_of_rows(unsigned m) = 0;
|
||||
virtual void set_number_of_columns(unsigned n) = 0;
|
||||
|
||||
virtual ~matrix() {}
|
||||
|
||||
bool is_equal(const matrix<T, X>& other);
|
||||
bool operator == (matrix<T, X> const & other) {
|
||||
return is_equal(other);
|
||||
}
|
||||
T operator()(unsigned i, unsigned j) const { return get_elem(i, j); }
|
||||
};
|
||||
|
||||
template <typename T, typename X>
|
||||
void apply_to_vector(matrix<T, X> & m, T * w);
|
||||
|
||||
|
||||
|
||||
unsigned get_width_of_column(unsigned j, std::vector<std::vector<std::string>> & A);
|
||||
void print_matrix_with_widths(std::vector<std::vector<std::string>> & A, std::vector<unsigned> & ws);
|
||||
void print_string_matrix(std::vector<std::vector<std::string>> & A);
|
||||
|
||||
|
||||
template <typename T, typename X>
|
||||
void print_matrix(matrix<T, X> const & m);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
|
@ -1,76 +0,0 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <vector>
|
||||
#include "util/numerics/numeric_traits.h"
|
||||
#include "util/numerics/xnumeral.h"
|
||||
#include "util/numerics/mpq.h"
|
||||
#include "util/numerics/mpz.h"
|
||||
#include "util/numerics/mpbq.h"
|
||||
#include "util/numerics/double.h"
|
||||
#include "util/numerics/float.h"
|
||||
#include "util/numerics/mpfp.h"
|
||||
#include <unordered_map>
|
||||
#include <utility>
|
||||
|
||||
template <class T>
|
||||
inline void hash_combine(std::size_t & seed, const T & v) {
|
||||
std::hash<T> hasher;
|
||||
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
|
||||
}
|
||||
|
||||
namespace std {
|
||||
template<typename S, typename T> struct hash<pair<S, T>> {
|
||||
inline size_t operator()(const pair<S, T> & v) const {
|
||||
size_t seed = 0;
|
||||
::hash_combine(seed, v.first);
|
||||
::hash_combine(seed, v.second);
|
||||
return seed;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
class matrix_domain {
|
||||
std::vector<std::unordered_map<unsigned, void *>> m_domain;
|
||||
public:
|
||||
matrix_domain(unsigned rows) {
|
||||
while (rows--) {
|
||||
std::unordered_map<unsigned, void *> t;
|
||||
m_domain.push_back(t);
|
||||
}
|
||||
}
|
||||
void * find(unsigned i, unsigned j) const {
|
||||
auto & v = m_domain[i];
|
||||
auto t = v.find(j);
|
||||
if (t == v.end()) {
|
||||
return nullptr;
|
||||
}
|
||||
return t->second;
|
||||
}
|
||||
|
||||
void erase(unsigned i, unsigned j) {
|
||||
lean_assert(find(i, j) != nullptr);
|
||||
m_domain[i].erase(j);
|
||||
}
|
||||
|
||||
void insert(unsigned i, unsigned j, void * cell) {
|
||||
lean_assert(m_domain[i].find(j) == m_domain[i].end());
|
||||
m_domain[i][j] = cell;
|
||||
}
|
||||
|
||||
unsigned size() const {
|
||||
unsigned ret = 0;
|
||||
for (auto & t : m_domain) {
|
||||
ret += t.size();
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
};
|
||||
}
|
||||
11
src/util/lp/matrix_instances.cpp
Normal file
11
src/util/lp/matrix_instances.cpp
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#ifdef LEAN_DEBUG
|
||||
#include "util/lp/matrix.cpp"
|
||||
template void lean::print_matrix<double, double>(lean::matrix<double, double> const&);
|
||||
template bool lean::matrix<double, double>::is_equal(lean::matrix<double, double> const&);
|
||||
#endif
|
||||
|
|
@ -9,6 +9,7 @@
|
|||
#include <algorithm>
|
||||
#include "util/numerics/mpq.h"
|
||||
#include "util/numerics/double.h"
|
||||
#include "util/lp/lp_settings.h"
|
||||
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
|
|
@ -17,7 +18,7 @@ std::string T_to_string(const T & t); // forward definition
|
|||
template <typename X, typename Y>
|
||||
struct convert_struct {
|
||||
static X convert(const Y & y){ return X(y);}
|
||||
static bool is_epsilon_small(const X & x, const double & y) { return abs(get_double(x)) < y; }
|
||||
static bool is_epsilon_small(const X & x, const double & y) { return std::abs(get_double(x)) < y; }
|
||||
static bool below_bound_numeric(const X &, const X &, const Y &) { lean_unreachable(); }
|
||||
static bool above_bound_numeric(const X &, const X &, const Y &) { lean_unreachable(); }
|
||||
};
|
||||
|
|
|
|||
387
src/util/lp/permutation_matrix.cpp
Normal file
387
src/util/lp/permutation_matrix.cpp
Normal file
|
|
@ -0,0 +1,387 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/permutation_matrix.h"
|
||||
namespace lean {
|
||||
template <typename T, typename X> permutation_matrix<T, X>::permutation_matrix(unsigned length): m_length(length), m_permutation(length), m_rev(length) {
|
||||
unsigned i = m_length;
|
||||
while (i--)
|
||||
m_permutation[i] = m_rev[i] = i;
|
||||
}
|
||||
|
||||
template <typename T, typename X> permutation_matrix<T, X>::permutation_matrix(unsigned length, std::vector<unsigned> const & values): m_length(length), m_permutation(length), m_rev(length) {
|
||||
for (unsigned i = 0; i < length; i++) {
|
||||
set_val(i, values[i]);
|
||||
}
|
||||
}
|
||||
// create a unit permutation of the given length
|
||||
template <typename T, typename X> void permutation_matrix<T, X>::init(unsigned length) {
|
||||
m_length = length;
|
||||
m_permutation.resize(length);
|
||||
m_rev.resize(length);
|
||||
unsigned i = length;
|
||||
while (i--)
|
||||
m_permutation[i] = m_rev[i] = i;
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X> void permutation_matrix<T, X>::print() const {
|
||||
std::cout << "[";
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
std::cout << m_permutation[i];
|
||||
if (i < m_length - 1) {
|
||||
std::cout << ",";
|
||||
} else {
|
||||
std::cout << "]";
|
||||
}
|
||||
}
|
||||
std::cout << std::endl;
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename X> template <typename L>
|
||||
void permutation_matrix<T, X>:: apply_from_left_perm(std::vector<L> & w) {
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<L, X> deb(*this);
|
||||
// L * deb_w = clone_vector<L>(w, row_count());
|
||||
// deb.apply_from_left(deb_w);
|
||||
#endif
|
||||
L * t = new L[m_length];
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
t[i] = w[m_permutation[i]];
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
delete [] t;
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<L>(deb_w, w, row_count()));
|
||||
// delete [] deb_w;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename X> template <typename L>
|
||||
void permutation_matrix<T, X>:: apply_from_left_perm(indexed_vector<L> & w, lp_settings &) {
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T, L> deb(*this);
|
||||
// T * deb_w = clone_vector<T>(w.m_data, row_count());
|
||||
// deb.apply_from_right(deb_w);
|
||||
#endif
|
||||
std::vector<L> t(w.m_index.size());
|
||||
std::vector<unsigned> tmp_index(w.m_index.size());
|
||||
copy_aside(t, tmp_index, w);
|
||||
clear_data(w);
|
||||
// set the new values
|
||||
for (unsigned i = t.size(); i > 0;) {
|
||||
i--;
|
||||
unsigned j = m_rev[tmp_index[i]];
|
||||
w[j] = t[i];
|
||||
w.m_index[i] = j;
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(deb_w, w.m_data, row_count()));
|
||||
// delete [] deb_w;
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void permutation_matrix<T, X>:: apply_from_right(std::vector<T> & w) {
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T, X> deb(*this);
|
||||
// T * deb_w = clone_vector<T>(w, row_count());
|
||||
// deb.apply_from_right(deb_w);
|
||||
#endif
|
||||
T * t = new T[m_length];
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
t[i] = w[m_rev[i]];
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
delete [] t;
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(deb_w, w, row_count()));
|
||||
// delete [] deb_w;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename X> template <typename L>
|
||||
void permutation_matrix<T, X>:: copy_aside(std::vector<L> & t, std::vector<unsigned> & tmp_index, indexed_vector<L> & w) {
|
||||
for (unsigned i = t.size(); i > 0;) {
|
||||
i--;
|
||||
unsigned j = w.m_index[i];
|
||||
t[i] = w[j]; // copy aside all non-zeroes
|
||||
tmp_index[i] = j; // and the indices too
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> template <typename L>
|
||||
void permutation_matrix<T, X>:: clear_data(indexed_vector<L> & w) {
|
||||
// clear old non-zeroes
|
||||
for (unsigned i = w.m_index.size(); i > 0;) {
|
||||
i--;
|
||||
unsigned j = w.m_index[i];
|
||||
w[j] = zero_of_type<L>();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> template <typename L>
|
||||
void permutation_matrix<T, X>::apply_reverse_from_left(indexed_vector<L> & w) {
|
||||
// the result will be w = p(-1) * w
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<L, X> deb(get_reverse());
|
||||
// L * deb_w = clone_vector<L>(w.m_data, row_count());
|
||||
// deb.apply_from_left(deb_w);
|
||||
#endif
|
||||
std::vector<L> t(w.m_index.size());
|
||||
std::vector<unsigned> tmp_index(w.m_index.size());
|
||||
|
||||
copy_aside(t, tmp_index, w);
|
||||
clear_data(w);
|
||||
|
||||
// set the new values
|
||||
for (unsigned i = t.size(); i > 0;) {
|
||||
i--;
|
||||
unsigned j = m_permutation[tmp_index[i]];
|
||||
w[j] = t[i];
|
||||
w.m_index[i] = j;
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<L>(deb_w, w.m_data, row_count()));
|
||||
// delete [] deb_w;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename X> template <typename L>
|
||||
void permutation_matrix<T, X>::apply_reverse_from_left(std::vector<L> & w) {
|
||||
// the result will be w = p(-1) * w
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T, X> deb(get_reverse());
|
||||
// T * deb_w = clone_vector<T>(w, row_count());
|
||||
// deb.apply_from_left(deb_w);
|
||||
#endif
|
||||
std::vector<L> t(m_length);
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
t[m_permutation[i]] = w[i];
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(deb_w, w, row_count()));
|
||||
// delete [] deb_w;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename X> template <typename L>
|
||||
void permutation_matrix<T, X>:: apply_reverse_from_right(std::vector<L> & w) {
|
||||
// the result will be w = w * p(-1)
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T, X> deb(get_reverse());
|
||||
// T * deb_w = clone_vector<T>(w, row_count());
|
||||
// deb.apply_from_right(deb_w);
|
||||
#endif
|
||||
L * t = new T[m_length];
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
t[i] = w[m_permutation[i]];
|
||||
}
|
||||
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
delete [] t;
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(deb_w, w, row_count()));
|
||||
// delete deb_w;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename X> void permutation_matrix<T, X>:: transpose_from_left(unsigned i, unsigned j) {
|
||||
// the result will be this = (i,j)*this
|
||||
lean_assert(i < m_length && j < m_length && i != j);
|
||||
auto pi = m_rev[i];
|
||||
auto pj = m_rev[j];
|
||||
set_val(pi, j);
|
||||
set_val(pj, i);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void permutation_matrix<T, X>:: transpose_from_right(unsigned i, unsigned j) {
|
||||
// the result will be this = this * (i,j)
|
||||
lean_assert(i < m_length && j < m_length && i != j);
|
||||
auto pi = m_permutation[i];
|
||||
auto pj = m_permutation[j];
|
||||
set_val(i, pj);
|
||||
set_val(j, pi);
|
||||
}
|
||||
|
||||
template <typename T, typename X> unsigned * permutation_matrix<T, X>::clone_m_permutation() {
|
||||
auto r = new unsigned[m_length];
|
||||
for (int i = m_length - 1; i >= 0; i--) {
|
||||
r[i] = m_permutation[i];
|
||||
}
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_permutation_from_left(permutation_matrix<T, X> & p) {
|
||||
auto clone = clone_m_permutation();
|
||||
lean_assert(p.m_length == m_length);
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
set_val(i, clone[p[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
|
||||
}
|
||||
delete clone;
|
||||
}
|
||||
|
||||
// this is multiplication in the matrix sense
|
||||
template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_permutation_from_right(permutation_matrix<T, X> & p) {
|
||||
auto clone = clone_m_permutation();
|
||||
lean_assert(p.m_length == m_length);
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
set_val(i, p[clone[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
|
||||
}
|
||||
delete clone;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_reverse_from_right(permutation_matrix<T, X> & q){ // todo : condensed permutations ?
|
||||
auto clone = clone_m_permutation();
|
||||
// the result is this = this*q(-1)
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
set_val(i, q.m_rev[clone[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
|
||||
}
|
||||
delete clone;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void permutation_matrix<T, X>:: multiply_by_permutation_reverse_from_left(permutation_matrix<T, X> & r){ // todo : condensed permutations?
|
||||
// the result is this = r(-1)*this
|
||||
auto clone = clone_m_permutation();
|
||||
// the result is this = this*q(-1)
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
set_val(i, clone[r.m_rev[i]]);
|
||||
}
|
||||
delete clone;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void permutation_matrix<T, X>::shrink_by_one_identity() {
|
||||
lean_assert(is_identity());
|
||||
m_length--;
|
||||
delete [] m_permutation;
|
||||
delete [] m_rev;
|
||||
m_permutation = new unsigned[m_length];
|
||||
m_rev = new unsigned[m_length];
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
m_permutation[i] = m_rev[i] = i;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool permutation_matrix<T, X>::is_identity() const {
|
||||
for (unsigned i = 0; i < m_length; i++) {
|
||||
if (m_permutation[i] != i) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X>
|
||||
permutation_generator<T, X>::permutation_generator(unsigned n): m_n(n), m_current(n) {
|
||||
lean_assert(n > 0);
|
||||
if (n > 1) {
|
||||
m_lower = new permutation_generator(n - 1);
|
||||
} else {
|
||||
m_lower = nullptr;
|
||||
}
|
||||
|
||||
m_last = 0;
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
permutation_generator<T, X>::permutation_generator(const permutation_generator & o): m_n(o.m_n), m_done(o.m_done), m_current(o.m_current), m_last(o.m_last) {
|
||||
if (m_lower != nullptr) {
|
||||
m_lower = new permutation_generator(o.m_lower);
|
||||
} else {
|
||||
m_lower = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool
|
||||
permutation_generator<T, X>::move_next() {
|
||||
if (m_done) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_lower == nullptr) {
|
||||
if (m_last == 0) {
|
||||
m_last++;
|
||||
return true;
|
||||
} else {
|
||||
m_done = true;
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (m_last < m_n && m_last > 0) {
|
||||
m_current[m_last - 1] = m_current[m_last];
|
||||
m_current[m_last] = m_n - 1;
|
||||
m_last++;
|
||||
return true;
|
||||
} else {
|
||||
if (m_lower -> move_next()) {
|
||||
auto lower_curr = m_lower -> current();
|
||||
for ( unsigned i = 1; i < m_n; i++ ){
|
||||
m_current[i] = (*lower_curr)[i - 1];
|
||||
}
|
||||
m_current[0] = m_n - 1;
|
||||
m_last = 1;
|
||||
return true;
|
||||
} else {
|
||||
m_done = true;
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
inline unsigned number_of_inversions(permutation_matrix<T, X> & p) {
|
||||
unsigned ret = 0;
|
||||
unsigned n = p.size();
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
for (unsigned j = i + 1; j < n; j++) {
|
||||
if (p[i] > p[j]) {
|
||||
ret++;
|
||||
}
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
T det_val_on_perm(permutation_matrix<T, X>* u, const matrix<T, X>& m) {
|
||||
unsigned n = m.row_count();
|
||||
T ret = numeric_traits<T>::one();
|
||||
for (unsigned i = 0; i < n; i++) {
|
||||
unsigned j = (*u)[i];
|
||||
ret *= m(i, j);
|
||||
}
|
||||
return ret * sign(*u);
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
T determinant(const matrix<T, X>& m) {
|
||||
lean_assert(m.column_count() == m.row_count());
|
||||
unsigned n = m.row_count();
|
||||
permutation_generator<T, X> allp(n);
|
||||
T ret = numeric_traits<T>::zero();
|
||||
while (allp.move_next()){
|
||||
ret += det_val_on_perm(allp.current(), m);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
File diff suppressed because it is too large
Load diff
64
src/util/lp/permutation_matrix_instances.cpp
Normal file
64
src/util/lp/permutation_matrix_instances.cpp
Normal file
|
|
@ -0,0 +1,64 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/permutation_matrix.cpp"
|
||||
#include "util/lp/numeric_pair.h"
|
||||
template void lean::permutation_matrix<double, double>::apply_from_right(std::vector<double, std::allocator<double> >&);
|
||||
template void lean::permutation_matrix<double, double>::init(unsigned int);
|
||||
template bool lean::permutation_matrix<double, double>::is_identity() const;
|
||||
template void lean::permutation_matrix<double, double>::multiply_by_permutation_from_left(lean::permutation_matrix<double, double>&);
|
||||
template void lean::permutation_matrix<double, double>::multiply_by_permutation_reverse_from_left(lean::permutation_matrix<double, double>&);
|
||||
template void lean::permutation_matrix<double, double>::multiply_by_reverse_from_right(lean::permutation_matrix<double, double>&);
|
||||
template lean::permutation_matrix<double, double>::permutation_matrix(unsigned int, std::vector<unsigned int, std::allocator<unsigned int> > const&);
|
||||
template void lean::permutation_matrix<double, double>::transpose_from_left(unsigned int, unsigned int);
|
||||
template void lean::permutation_matrix<float, float>::apply_from_right(std::vector<float, std::allocator<float> >&);
|
||||
template lean::permutation_matrix<float, float>::permutation_matrix(unsigned int);
|
||||
template void lean::permutation_matrix<float, float>::transpose_from_left(unsigned int, unsigned int);
|
||||
template void lean::permutation_matrix<float, float>::transpose_from_right(unsigned int, unsigned int);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_from_right(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template bool lean::permutation_matrix<lean::mpq, lean::mpq>::is_identity() const;
|
||||
template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_permutation_from_left(lean::permutation_matrix<lean::mpq, lean::mpq>&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_permutation_from_right(lean::permutation_matrix<lean::mpq, lean::mpq>&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_permutation_reverse_from_left(lean::permutation_matrix<lean::mpq, lean::mpq>&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_reverse_from_right(lean::permutation_matrix<lean::mpq, lean::mpq>&);
|
||||
template lean::permutation_matrix<lean::mpq, lean::mpq>::permutation_matrix(unsigned int);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::mpq>::transpose_from_left(unsigned int, unsigned int);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::mpq>::transpose_from_right(unsigned int, unsigned int);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_right(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template bool lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::is_identity() const;
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_permutation_from_left(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_permutation_from_right(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_permutation_reverse_from_left(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_reverse_from_right(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
|
||||
template lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::permutation_matrix(unsigned int);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::transpose_from_left(unsigned int, unsigned int);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::transpose_from_right(unsigned int, unsigned int);
|
||||
template void lean::permutation_matrix<double, double>::apply_from_left_perm<double>(lean::indexed_vector<double>&, lean::lp_settings&);
|
||||
template void lean::permutation_matrix<double, double>::apply_from_left_perm<double>(std::vector<double, std::allocator<double> >&);
|
||||
template void lean::permutation_matrix<double, double>::apply_reverse_from_left<double>(lean::indexed_vector<double>&);
|
||||
template void lean::permutation_matrix<double, double>::apply_reverse_from_left<double>(std::vector<double, std::allocator<double> >&);
|
||||
template void lean::permutation_matrix<double, double>::apply_reverse_from_right<double>(std::vector<double, std::allocator<double> >&);
|
||||
template void lean::permutation_matrix<double, double>::transpose_from_right(unsigned int, unsigned int);
|
||||
template void lean::permutation_matrix<float, float>::apply_from_left_perm<float>(lean::indexed_vector<float>&, lean::lp_settings&);
|
||||
template void lean::permutation_matrix<float, float>::apply_from_left_perm<float>(std::vector<float, std::allocator<float> >&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_from_left_perm<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_from_left_perm<lean::mpq>(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_left<lean::mpq>(lean::indexed_vector<lean::mpq>&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_left<lean::mpq>(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_right<lean::mpq>(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left_perm<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left_perm<lean::numeric_pair<lean::mpq> >(std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_left<lean::mpq>(lean::indexed_vector<lean::mpq>&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_left<lean::mpq>(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_left<lean::numeric_pair<lean::mpq> >(std::vector<lean::numeric_pair<lean::mpq>, std::allocator<lean::numeric_pair<lean::mpq> > >&);
|
||||
template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_right<lean::mpq>(std::vector<lean::mpq, std::allocator<lean::mpq> >&);
|
||||
template void lean::permutation_matrix<double, double>::multiply_by_permutation_from_right(lean::permutation_matrix<double, double>&);
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template bool lean::permutation_generator<double, double>::move_next();
|
||||
template lean::permutation_generator<double, double>::permutation_generator(unsigned int);
|
||||
#endif
|
||||
template lean::permutation_matrix<double, double>::permutation_matrix(unsigned int);
|
||||
154
src/util/lp/row_eta_matrix.cpp
Normal file
154
src/util/lp/row_eta_matrix.cpp
Normal file
|
|
@ -0,0 +1,154 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/row_eta_matrix.h"
|
||||
namespace lean {
|
||||
template <typename T, typename X>
|
||||
void row_eta_matrix<T, X>::apply_from_left(std::vector<X> & w, lp_settings &) {
|
||||
// #ifdef LEAN_DEBUG
|
||||
// dense_matrix<T> deb(*this);
|
||||
// auto clone_w = clone_vector<T>(w, m_dimension);
|
||||
// deb.apply_from_left(clone_w, settings);
|
||||
// #endif
|
||||
auto w_at_row = w[m_row];
|
||||
for (auto & it :m_row_vector.m_data) {
|
||||
w_at_row += w[it.first] * it.second;
|
||||
}
|
||||
w[m_row] = w_at_row;
|
||||
// #ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
|
||||
// delete [] clone_w;
|
||||
// #endif
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void row_eta_matrix<T, X>::apply_from_left_local_to_T(indexed_vector<T> & w, lp_settings & settings) {
|
||||
auto w_at_row = w[m_row];
|
||||
bool was_zero_at_m_row = is_zero(w_at_row);
|
||||
|
||||
for (auto & it : m_row_vector.m_data) {
|
||||
w_at_row += w[it.first] * it.second;
|
||||
}
|
||||
|
||||
if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){
|
||||
if (was_zero_at_m_row) {
|
||||
w.m_index.push_back(m_row);
|
||||
}
|
||||
w[m_row] = w_at_row;
|
||||
} else if (!was_zero_at_m_row){
|
||||
w[m_row] = zero_of_type<T>();
|
||||
auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row);
|
||||
w.m_index.erase(it);
|
||||
}
|
||||
lean_assert(check_vector_for_small_values(w, settings));
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void row_eta_matrix<T, X>::apply_from_left_local_to_X(indexed_vector<X> & w, lp_settings & settings) {
|
||||
auto w_at_row = w[m_row];
|
||||
bool was_zero_at_m_row = is_zero(w_at_row);
|
||||
|
||||
for (auto & it : m_row_vector.m_data) {
|
||||
w_at_row += w[it.first] * it.second;
|
||||
}
|
||||
|
||||
if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){
|
||||
if (was_zero_at_m_row) {
|
||||
w.m_index.push_back(m_row);
|
||||
}
|
||||
w[m_row] = w_at_row;
|
||||
} else if (!was_zero_at_m_row){
|
||||
w[m_row] = zero_of_type<X>();
|
||||
auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row);
|
||||
w.m_index.erase(it);
|
||||
}
|
||||
lean_assert(check_vector_for_small_values(w, settings));
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void row_eta_matrix<T, X>::apply_from_right(std::vector<T> & w) {
|
||||
T w_row = w[m_row];
|
||||
if (numeric_traits<T>::is_zero(w_row)) return;
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T> deb(*this);
|
||||
// auto clone_w = clone_vector<T>(w, m_dimension);
|
||||
// deb.apply_from_right(clone_w);
|
||||
#endif
|
||||
for (auto & it : m_row_vector.m_data) {
|
||||
w[it.first] += w_row * it.second;
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
|
||||
// delete clone_w;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void row_eta_matrix<T, X>::apply_from_right(indexed_vector<T> & w) {
|
||||
T w_row = w[m_row];
|
||||
if (numeric_traits<T>::is_zero(w_row)) return;
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T> deb(*this);
|
||||
// auto clone_w = clone_vector<T>(w.m_data, m_dimension);
|
||||
// deb.apply_from_right(clone_w);
|
||||
#endif
|
||||
for (auto & it : m_row_vector.m_data) {
|
||||
T old_val = w[it.first];
|
||||
T v = w[it.index()] += w_row * it.second;
|
||||
if (numeric_traits<T>::is_zero(old_val)) {
|
||||
w.m_index.push_back(it.index());
|
||||
} else if (numeric_traits<T>::is_zero(v)) { // it is a very rare case
|
||||
auto w_it = std::find(w.m_index.begin(), w.m_index.end(), it.index());
|
||||
lean_assert(w_it != w.m_index.end());
|
||||
w.m_index.erase(w_it);
|
||||
}
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(clone_w, w.m_data, m_dimension));
|
||||
// for (unsigned i = 0; i < m_dimension; i++) {
|
||||
// if (!numeric_traits<T>::is_zero(w.m_data[i])) {
|
||||
// lean_assert(std::find(w.m_index.begin(), w.m_index.end(), i) != w.m_index.end());
|
||||
// }
|
||||
// }
|
||||
// delete clone_w;
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void row_eta_matrix<T, X>::conjugate_by_permutation(permutation_matrix<T, X> & p) {
|
||||
// this = p * this * p(-1)
|
||||
#ifdef LEAN_DEBUG
|
||||
// auto rev = p.get_reverse();
|
||||
// auto deb = ((*this) * rev);
|
||||
// deb = p * deb;
|
||||
#endif
|
||||
m_row = p.apply_reverse(m_row);
|
||||
// copy aside the column indices
|
||||
std::vector<unsigned> columns;
|
||||
for (auto & it : m_row_vector.m_data)
|
||||
columns.push_back(it.first);
|
||||
for (unsigned i = columns.size(); i-- > 0;)
|
||||
m_row_vector.m_data[i].first = p.get_rev(columns[i]);
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(deb == *this);
|
||||
#endif
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X>
|
||||
T row_eta_matrix<T, X>::get_elem(unsigned row, unsigned col) const {
|
||||
if (row == m_row){
|
||||
if (col == row) {
|
||||
return numeric_traits<T>::one();
|
||||
}
|
||||
return m_row_vector[col];
|
||||
}
|
||||
|
||||
return col == row ? numeric_traits<T>::one() : numeric_traits<T>::zero();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
@ -19,7 +19,7 @@
|
|||
#include <string>
|
||||
#include "util/lp/sparse_vector.h"
|
||||
#include "util/lp/indexed_vector.h"
|
||||
|
||||
#include "util/lp/permutation_matrix.h"
|
||||
namespace lean {
|
||||
// This is the sum of a unit matrix and a lower triangular matrix
|
||||
// with non-zero elements only in one column
|
||||
|
|
@ -54,145 +54,25 @@ public:
|
|||
return m_row_vector.m_data[m_row];
|
||||
}
|
||||
|
||||
void apply_from_left(std::vector<X> & w, lp_settings &
|
||||
#ifdef LEAN_DEBUG
|
||||
// settings
|
||||
#endif
|
||||
) {
|
||||
// #ifdef LEAN_DEBUG
|
||||
// dense_matrix<T> deb(*this);
|
||||
// auto clone_w = clone_vector<T>(w, m_dimension);
|
||||
// deb.apply_from_left(clone_w, settings);
|
||||
// #endif
|
||||
auto w_at_row = w[m_row];
|
||||
for (auto it = sparse_vector_iterator<T>(m_row_vector); !it.done(); it.move()) {
|
||||
w_at_row += w[it.index()] * it.value();
|
||||
}
|
||||
w[m_row] = w_at_row;
|
||||
// #ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
|
||||
// delete [] clone_w;
|
||||
// #endif
|
||||
}
|
||||
void apply_from_left(std::vector<X> & w, lp_settings &);
|
||||
|
||||
template <typename L>
|
||||
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T> deb(*this);
|
||||
// auto clone_w = clone_vector<T>(w.m_data, m_dimension);
|
||||
// deb.apply_from_left(clone_w);
|
||||
#endif
|
||||
auto w_at_row = w[m_row];
|
||||
bool was_zero_at_m_row = is_zero(w_at_row);
|
||||
|
||||
for (auto it = sparse_vector_iterator<T>(m_row_vector); !it.done(); it.move()) {
|
||||
w_at_row += w[it.index()] * it.value();
|
||||
}
|
||||
|
||||
if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){
|
||||
if (was_zero_at_m_row) {
|
||||
w.m_index.push_back(m_row);
|
||||
}
|
||||
w[m_row] = w_at_row;
|
||||
} else if (!was_zero_at_m_row){
|
||||
w[m_row] = zero_of_type<L>();
|
||||
auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row);
|
||||
w.m_index.erase(it);
|
||||
}
|
||||
lean_assert(check_vector_for_small_values(w, settings));
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(clone_w, w.m_data, m_dimension));
|
||||
// delete clone_w;
|
||||
#endif
|
||||
}
|
||||
void apply_from_left_local_to_T(indexed_vector<T> & w, lp_settings & settings);
|
||||
void apply_from_left_local_to_X(indexed_vector<X> & w, lp_settings & settings);
|
||||
|
||||
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
|
||||
apply_from_left_local(w, settings);
|
||||
apply_from_left_local_to_T(w, settings);
|
||||
}
|
||||
|
||||
void push_back(unsigned row_index, T val ) {
|
||||
m_row_vector.push_back(row_index, val);
|
||||
}
|
||||
|
||||
void apply_from_right(std::vector<T> & w) {
|
||||
T w_row = w[m_row];
|
||||
if (numeric_traits<T>::is_zero(w_row)) return;
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T> deb(*this);
|
||||
// auto clone_w = clone_vector<T>(w, m_dimension);
|
||||
// deb.apply_from_right(clone_w);
|
||||
#endif
|
||||
for (auto it = sparse_vector_iterator<T>(m_row_vector); !it.done(); it.move()) {
|
||||
w[it.index()] += w_row * it.value();
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
|
||||
// delete clone_w;
|
||||
#endif
|
||||
}
|
||||
void apply_from_right(std::vector<T> & w);
|
||||
void apply_from_right(indexed_vector<T> & w);
|
||||
|
||||
void apply_from_right(indexed_vector<T> & w) {
|
||||
T w_row = w[m_row];
|
||||
if (numeric_traits<T>::is_zero(w_row)) return;
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T> deb(*this);
|
||||
// auto clone_w = clone_vector<T>(w.m_data, m_dimension);
|
||||
// deb.apply_from_right(clone_w);
|
||||
#endif
|
||||
for (auto it = sparse_vector_iterator<T>(m_row_vector); !it.done(); it.move()) {
|
||||
T old_val = w[it.index()];
|
||||
T v = w[it.index()] += w_row * it.value();
|
||||
if (numeric_traits<T>::is_zero(old_val)) {
|
||||
w.m_index.push_back(it.index());
|
||||
} else if (numeric_traits<T>::is_zero(v)) { // it is a very rare case
|
||||
auto w_it = std::find(w.m_index.begin(), w.m_index.end(), it.index());
|
||||
lean_assert(w_it != w.m_index.end());
|
||||
w.m_index.erase(w_it);
|
||||
}
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vectors_are_equal<T>(clone_w, w.m_data, m_dimension));
|
||||
// for (unsigned i = 0; i < m_dimension; i++) {
|
||||
// if (!numeric_traits<T>::is_zero(w.m_data[i])) {
|
||||
// lean_assert(std::find(w.m_index.begin(), w.m_index.end(), i) != w.m_index.end());
|
||||
// }
|
||||
// }
|
||||
// delete clone_w;
|
||||
#endif
|
||||
}
|
||||
|
||||
void conjugate_by_permutation(permutation_matrix<T, X> & p) {
|
||||
// this = p * this * p(-1)
|
||||
#ifdef LEAN_DEBUG
|
||||
// auto rev = p.get_reverse();
|
||||
// auto deb = ((*this) * rev);
|
||||
// deb = p * deb;
|
||||
#endif
|
||||
m_row = p.apply_reverse(m_row);
|
||||
// copy aside the column indices
|
||||
std::vector<unsigned> columns;
|
||||
for (auto it = sparse_vector_iterator<T>(m_row_vector); !it.done(); it.move()) {
|
||||
columns.push_back(it.index());
|
||||
}
|
||||
for (unsigned i = columns.size(); i-- > 0;) {
|
||||
m_row_vector.m_data[i].first = p.get_rev(columns[i]);
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(deb == *this);
|
||||
#endif
|
||||
}
|
||||
|
||||
T get_elem(unsigned row, unsigned col) const {
|
||||
if (row == m_row){
|
||||
if (col == row) {
|
||||
return numeric_traits<T>::one();
|
||||
}
|
||||
return m_row_vector[col];
|
||||
}
|
||||
|
||||
return col == row ? numeric_traits<T>::one() : numeric_traits<T>::zero();
|
||||
}
|
||||
void conjugate_by_permutation(permutation_matrix<T, X> & p);
|
||||
#ifdef LEAN_DEBUG
|
||||
T get_elem(unsigned row, unsigned col) const;
|
||||
unsigned row_count() const { return m_dimension; }
|
||||
unsigned column_count() const { return m_dimension; }
|
||||
void set_number_of_rows(unsigned /*m*/) { }
|
||||
|
|
|
|||
31
src/util/lp/row_eta_matrix_instances.cpp
Normal file
31
src/util/lp/row_eta_matrix_instances.cpp
Normal file
|
|
@ -0,0 +1,31 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/row_eta_matrix.cpp"
|
||||
#include "util/lp/lu.h"
|
||||
namespace lean {
|
||||
template void row_eta_matrix<double, double>::conjugate_by_permutation(permutation_matrix<double, double>&);
|
||||
template void row_eta_matrix<mpq, numeric_pair<mpq> >::conjugate_by_permutation(permutation_matrix<mpq, numeric_pair<mpq> >&);
|
||||
template void row_eta_matrix<mpq, mpq>::conjugate_by_permutation(permutation_matrix<mpq, mpq>&);
|
||||
#ifdef LEAN_DEBUG
|
||||
template mpq row_eta_matrix<mpq, mpq>::get_elem(unsigned int, unsigned int) const;
|
||||
template mpq row_eta_matrix<mpq, numeric_pair<mpq> >::get_elem(unsigned int, unsigned int) const;
|
||||
template double row_eta_matrix<double, double>::get_elem(unsigned int, unsigned int) const;
|
||||
#endif
|
||||
template void row_eta_matrix<mpq, mpq>::apply_from_left(std::vector<mpq, std::allocator<mpq> >&, lp_settings&);
|
||||
template void row_eta_matrix<mpq, mpq>::apply_from_right(std::vector<mpq, std::allocator<mpq> >&);
|
||||
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_left(std::vector<numeric_pair<mpq>, std::allocator<numeric_pair<mpq> > >&, lp_settings&);
|
||||
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_right(std::vector<mpq, std::allocator<mpq> >&);
|
||||
template void row_eta_matrix<double, double>::apply_from_left(std::vector<double, std::allocator<double> >&, lp_settings&);
|
||||
template void row_eta_matrix<double, double>::apply_from_right(std::vector<double, std::allocator<double> >&);
|
||||
template void row_eta_matrix<mpq, mpq>::apply_from_left_to_T(indexed_vector<mpq>&, lp_settings&);
|
||||
template void row_eta_matrix<mpq, mpq>::apply_from_left_local_to_T(indexed_vector<mpq>&, lp_settings&);
|
||||
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_left_to_T(indexed_vector<mpq>&, lp_settings&);
|
||||
template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_left_local_to_T(indexed_vector<mpq>&, lp_settings&);
|
||||
template void row_eta_matrix<double, double>::apply_from_left_to_T(indexed_vector<double>&, lp_settings&);
|
||||
template void row_eta_matrix<double, double>::apply_from_left_local_to_T(indexed_vector<double>&, lp_settings&);
|
||||
}
|
||||
236
src/util/lp/scaler.cpp
Normal file
236
src/util/lp/scaler.cpp
Normal file
|
|
@ -0,0 +1,236 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/scaler.h"
|
||||
#include "util/lp/numeric_pair.h"
|
||||
namespace lean {
|
||||
// for scaling an LP
|
||||
template <typename T, typename X> T scaler<T, X>::right_side_balance() {
|
||||
T ret = zero_of_type<T>();
|
||||
unsigned i = m_A.row_count();
|
||||
while (i--) {
|
||||
T rs = abs(convert_struct<T, X>::convert(m_b[i]));
|
||||
if (!is_zero<T>(rs)) {
|
||||
numeric_traits<T>::log(rs);
|
||||
ret += rs * rs;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T scaler<T, X>::A_min() const {
|
||||
T min = zero_of_type<T>();
|
||||
for (unsigned i = 0; i < m_A.row_count(); i++) {
|
||||
T t = m_A.get_min_abs_in_row(i);
|
||||
min = i == 0 ? t : std::min(t, min);
|
||||
}
|
||||
return min;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T scaler<T, X>::A_max() const {
|
||||
T max = zero_of_type<T>();
|
||||
for (unsigned i = 0; i < m_A.row_count(); i++) {
|
||||
T t = m_A.get_max_abs_in_row(i);
|
||||
max = i == 0? t : std::max(t, max);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T scaler<T, X>::get_A_ratio() const {
|
||||
T min = A_min();
|
||||
T max = A_max();
|
||||
T ratio = max / min;
|
||||
return ratio;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T scaler<T, X>::get_max_ratio_on_rows() const {
|
||||
T ret = T(1);
|
||||
unsigned i = m_A.row_count();
|
||||
while (i--) {
|
||||
T t = m_A.get_max_abs_in_row(i)/m_A.get_min_abs_in_row(i);
|
||||
if (t > ret)
|
||||
ret = t;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T scaler<T, X>::get_max_ratio_on_columns() const {
|
||||
T ret = T(1);
|
||||
unsigned i = m_A.column_count();
|
||||
while (i--) {
|
||||
T t = m_A.get_max_abs_in_column(i)/m_A.get_min_abs_in_column(i);
|
||||
if (t > ret)
|
||||
ret = t;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void scaler<T, X>::scale_rows_with_geometric_mean() {
|
||||
unsigned i = m_A.row_count();
|
||||
while (i--) {
|
||||
T max = m_A.get_max_abs_in_row(i);
|
||||
T min = m_A.get_min_abs_in_row(i);
|
||||
lean_assert(max > zero_of_type<T>() && min > zero_of_type<T>());
|
||||
T gm = T(sqrt(numeric_traits<T>::get_double(max*min)));
|
||||
m_A.divide_row_by_constant(i, gm);
|
||||
m_b[i] /= gm;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void scaler<T, X>::scale_columns_with_geometric_mean() {
|
||||
unsigned i = m_A.column_count();
|
||||
while (i--) {
|
||||
T max = m_A.get_max_abs_in_column(i);
|
||||
T min = m_A.get_min_abs_in_column(i);
|
||||
T gm = T(1)/T(sqrt(numeric_traits<T>::get_double(max*min)));
|
||||
m_A.scale_column(i, gm);
|
||||
m_column_scale[i]*=gm;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void scaler<T, X>::scale_once_for_ratio() {
|
||||
T max_ratio_on_rows = get_max_ratio_on_rows();
|
||||
T max_ratio_on_columns = get_max_ratio_on_columns();
|
||||
bool scale_rows_first = max_ratio_on_rows > max_ratio_on_columns;
|
||||
// if max_ratio_on_columns is the largerst then the rows are in worser shape then columns
|
||||
if (scale_rows_first) {
|
||||
scale_rows_with_geometric_mean();
|
||||
scale_columns_with_geometric_mean();
|
||||
} else {
|
||||
scale_columns_with_geometric_mean();
|
||||
scale_rows_with_geometric_mean();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool scaler<T, X>::scale_with_ratio() {
|
||||
T ratio = get_A_ratio();
|
||||
// The ratio is greater than or equal to one. We would like to diminish it and bring it as close to 1 as possible
|
||||
unsigned reps = 20;
|
||||
do {
|
||||
scale_once_for_ratio();
|
||||
T new_r = get_A_ratio();
|
||||
if (new_r >= T(0.9) * ratio)
|
||||
break;
|
||||
} while (reps--);
|
||||
|
||||
bring_rows_and_columns_maximums_to_one();
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void scaler<T, X>::bring_row_maximums_to_one() {
|
||||
unsigned i = m_A.row_count();
|
||||
while (i--) {
|
||||
T t = m_A.get_max_abs_in_row(i);
|
||||
m_A.divide_row_by_constant(i, t);
|
||||
m_b[i] /= t;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void scaler<T, X>::bring_column_maximums_to_one() {
|
||||
unsigned i = m_A.column_count();
|
||||
while (i--) {
|
||||
T t = T(1) / m_A.get_max_abs_in_column(i);
|
||||
m_A.scale_column(i, t);
|
||||
m_column_scale[i] *= t;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void scaler<T, X>::bring_rows_and_columns_maximums_to_one() {
|
||||
if (get_max_ratio_on_rows() > get_max_ratio_on_columns()) {
|
||||
bring_row_maximums_to_one();
|
||||
bring_column_maximums_to_one();
|
||||
} else {
|
||||
bring_column_maximums_to_one();
|
||||
bring_row_maximums_to_one();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool scaler<T, X>::scale_with_log_balance() {
|
||||
T balance = get_balance();
|
||||
T balance_before_scaling = balance;
|
||||
// todo : analyze the scale order : rows-columns, or columns-rows. Iterate if needed
|
||||
for (int i = 0; i < 10; i++) {
|
||||
scale_rows();
|
||||
scale_columns();
|
||||
T nb = get_balance();
|
||||
if (nb < T(0.9) * balance) {
|
||||
balance = nb;
|
||||
} else {
|
||||
balance = nb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return balance <= balance_before_scaling;
|
||||
}
|
||||
// Returns true if and only if the scaling was successful.
|
||||
// It is the caller responsibility to restore the matrix
|
||||
template <typename T, typename X> bool scaler<T, X>::scale() {
|
||||
if (numeric_traits<T>::precise()) return true;
|
||||
if (m_settings.scale_with_ratio)
|
||||
return scale_with_ratio();
|
||||
return scale_with_log_balance();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void scaler<T, X>::scale_rows() {
|
||||
for (unsigned i = 0; i < m_A.row_count(); i++)
|
||||
scale_row(i);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void scaler<T, X>::scale_row(unsigned i) {
|
||||
T row_max = std::max(m_A.get_max_abs_in_row(i), abs(convert_struct<T, X>::convert(m_b[i])));
|
||||
T alpha = numeric_traits<T>::one();
|
||||
if (numeric_traits<T>::is_zero(row_max)) {
|
||||
return;
|
||||
}
|
||||
if (numeric_traits<T>::get_double(row_max) < m_scaling_minimum) {
|
||||
do {
|
||||
alpha *= 2;
|
||||
row_max *= 2;
|
||||
} while (numeric_traits<T>::get_double(row_max) < m_scaling_minimum);
|
||||
m_A.scale_row(i, alpha);
|
||||
m_b[i] *= alpha;
|
||||
} else if (numeric_traits<T>::get_double(row_max) > m_scaling_maximum) {
|
||||
do {
|
||||
alpha /= 2;
|
||||
row_max /= 2;
|
||||
} while (numeric_traits<T>::get_double(row_max) > m_scaling_maximum);
|
||||
m_A.scale_row(i, alpha);
|
||||
m_b[i] *= alpha;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void scaler<T, X>::scale_column(unsigned i) {
|
||||
T column_max = m_A.get_max_abs_in_column(i);
|
||||
T alpha = numeric_traits<T>::one();
|
||||
|
||||
if (numeric_traits<T>::is_zero(column_max)){
|
||||
return; // the column has zeros only
|
||||
}
|
||||
|
||||
if (numeric_traits<T>::get_double(column_max) < m_scaling_minimum) {
|
||||
do {
|
||||
alpha *= 2;
|
||||
column_max *= 2;
|
||||
} while (numeric_traits<T>::get_double(column_max) < m_scaling_minimum);
|
||||
} else if (numeric_traits<T>::get_double(column_max) > m_scaling_maximum) {
|
||||
do {
|
||||
alpha /= 2;
|
||||
column_max /= 2;
|
||||
} while (numeric_traits<T>::get_double(column_max) > m_scaling_maximum);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
m_A.scale_column(i, alpha);
|
||||
m_column_scale[i] = alpha;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void scaler<T, X>::scale_columns() {
|
||||
for (unsigned i = 0; i < m_A.column_count(); i++) {
|
||||
scale_column(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -12,6 +12,7 @@
|
|||
#include <stdio.h> /* printf, fopen */
|
||||
#include <stdlib.h> /* exit, EXIT_FAILURE */
|
||||
#include "util/numerics/double.h"
|
||||
#include "util/lp/static_matrix.h"
|
||||
namespace lean {
|
||||
// for scaling an LP
|
||||
template <typename T, typename X>
|
||||
|
|
@ -36,233 +37,45 @@ public:
|
|||
m_column_scale.resize(m_A.column_count(), numeric_traits<T>::one());
|
||||
}
|
||||
|
||||
T right_side_balance() {
|
||||
T ret = zero_of_type<T>();
|
||||
unsigned i = m_A.row_count();
|
||||
while (i--) {
|
||||
T rs = abs(convert_struct<T, X>::convert(m_b[i]));
|
||||
if (!is_zero<T>(rs)) {
|
||||
numeric_traits<T>::log(rs);
|
||||
ret += rs * rs;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
T right_side_balance();
|
||||
|
||||
T get_balance() {
|
||||
return m_A.get_balance();
|
||||
}
|
||||
T get_balance() { return m_A.get_balance(); }
|
||||
|
||||
T A_min() const {
|
||||
T min = zero_of_type<T>();
|
||||
for (unsigned i = 0; i < m_A.row_count(); i++) {
|
||||
T t = m_A.get_min_abs_in_row(i);
|
||||
min = i == 0 ? t : std::min(t, min);
|
||||
}
|
||||
return min;
|
||||
}
|
||||
T A_min() const;
|
||||
|
||||
T A_max() const {
|
||||
T max = zero_of_type<T>();
|
||||
for (unsigned i = 0; i < m_A.row_count(); i++) {
|
||||
T t = m_A.get_max_abs_in_row(i);
|
||||
max = i == 0? t : std::max(t, max);
|
||||
}
|
||||
return max;
|
||||
}
|
||||
T A_max() const;
|
||||
|
||||
T get_A_ratio() const {
|
||||
T min = A_min();
|
||||
T max = A_max();
|
||||
T ratio = max / min;
|
||||
return ratio;
|
||||
}
|
||||
T get_A_ratio() const;
|
||||
|
||||
T get_max_ratio_on_rows() const {
|
||||
T ret = T(1);
|
||||
unsigned i = m_A.row_count();
|
||||
while (i--) {
|
||||
T t = m_A.get_max_abs_in_row(i)/m_A.get_min_abs_in_row(i);
|
||||
if (t > ret)
|
||||
ret = t;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
T get_max_ratio_on_rows() const;
|
||||
|
||||
T get_max_ratio_on_columns() const {
|
||||
T ret = T(1);
|
||||
unsigned i = m_A.column_count();
|
||||
while (i--) {
|
||||
T t = m_A.get_max_abs_in_column(i)/m_A.get_min_abs_in_column(i);
|
||||
if (t > ret)
|
||||
ret = t;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
T get_max_ratio_on_columns() const;
|
||||
|
||||
void scale_rows_with_geometric_mean() {
|
||||
unsigned i = m_A.row_count();
|
||||
while (i--) {
|
||||
T max = m_A.get_max_abs_in_row(i);
|
||||
T min = m_A.get_min_abs_in_row(i);
|
||||
lean_assert(max > zero_of_type<T>() && min > zero_of_type<T>());
|
||||
T gm = T(sqrt(numeric_traits<T>::get_double(max*min)));
|
||||
m_A.divide_row_by_constant(i, gm);
|
||||
m_b[i] /= gm;
|
||||
}
|
||||
}
|
||||
void scale_rows_with_geometric_mean();
|
||||
|
||||
void scale_columns_with_geometric_mean() {
|
||||
unsigned i = m_A.column_count();
|
||||
while (i--) {
|
||||
T max = m_A.get_max_abs_in_column(i);
|
||||
T min = m_A.get_min_abs_in_column(i);
|
||||
T gm = T(1)/T(sqrt(numeric_traits<T>::get_double(max*min)));
|
||||
m_A.scale_column(i, gm);
|
||||
m_column_scale[i]*=gm;
|
||||
}
|
||||
}
|
||||
void scale_columns_with_geometric_mean();
|
||||
|
||||
void scale_once_for_ratio() {
|
||||
T max_ratio_on_rows = get_max_ratio_on_rows();
|
||||
T max_ratio_on_columns = get_max_ratio_on_columns();
|
||||
bool scale_rows_first = max_ratio_on_rows > max_ratio_on_columns;
|
||||
// if max_ratio_on_columns is the largerst then the rows are in worser shape then columns
|
||||
if (scale_rows_first) {
|
||||
scale_rows_with_geometric_mean();
|
||||
scale_columns_with_geometric_mean();
|
||||
} else {
|
||||
scale_columns_with_geometric_mean();
|
||||
scale_rows_with_geometric_mean();
|
||||
}
|
||||
}
|
||||
void scale_once_for_ratio();
|
||||
|
||||
bool scale_with_ratio() {
|
||||
T ratio = get_A_ratio();
|
||||
// The ratio is greater than or equal to one. We would like to diminish it and bring it as close to 1 as possible
|
||||
unsigned reps = 20;
|
||||
do {
|
||||
scale_once_for_ratio();
|
||||
T new_r = get_A_ratio();
|
||||
if (new_r >= T(0.9) * ratio)
|
||||
break;
|
||||
} while (reps--);
|
||||
bool scale_with_ratio();
|
||||
|
||||
bring_rows_and_columns_maximums_to_one();
|
||||
return true;
|
||||
}
|
||||
void bring_row_maximums_to_one();
|
||||
|
||||
void bring_row_maximums_to_one() {
|
||||
unsigned i = m_A.row_count();
|
||||
while (i--) {
|
||||
T t = m_A.get_max_abs_in_row(i);
|
||||
m_A.divide_row_by_constant(i, t);
|
||||
m_b[i] /= t;
|
||||
}
|
||||
}
|
||||
void bring_column_maximums_to_one();
|
||||
|
||||
void bring_column_maximums_to_one() {
|
||||
unsigned i = m_A.column_count();
|
||||
while (i--) {
|
||||
T t = T(1) / m_A.get_max_abs_in_column(i);
|
||||
m_A.scale_column(i, t);
|
||||
m_column_scale[i] *= t;
|
||||
}
|
||||
}
|
||||
void bring_rows_and_columns_maximums_to_one();
|
||||
|
||||
void bring_rows_and_columns_maximums_to_one() {
|
||||
if (get_max_ratio_on_rows() > get_max_ratio_on_columns()) {
|
||||
bring_row_maximums_to_one();
|
||||
bring_column_maximums_to_one();
|
||||
} else {
|
||||
bring_column_maximums_to_one();
|
||||
bring_row_maximums_to_one();
|
||||
}
|
||||
}
|
||||
|
||||
bool scale_with_log_balance() {
|
||||
T balance = get_balance();
|
||||
T balance_before_scaling = balance;
|
||||
// todo : analyze the scale order : rows-columns, or columns-rows. Iterate if needed
|
||||
for (int i = 0; i < 10; i++) {
|
||||
scale_rows();
|
||||
scale_columns();
|
||||
T nb = get_balance();
|
||||
if (nb < T(0.9) * balance) {
|
||||
balance = nb;
|
||||
} else {
|
||||
balance = nb;
|
||||
break;
|
||||
}
|
||||
}
|
||||
return balance <= balance_before_scaling;
|
||||
}
|
||||
bool scale_with_log_balance();
|
||||
// Returns true if and only if the scaling was successful.
|
||||
// It is the caller responsibility to restore the matrix
|
||||
bool scale() {
|
||||
if (numeric_traits<T>::precise()) return true;
|
||||
if (m_settings.scale_with_ratio)
|
||||
return scale_with_ratio();
|
||||
return scale_with_log_balance();
|
||||
}
|
||||
bool scale();
|
||||
|
||||
void scale_rows() {
|
||||
for (unsigned i = 0; i < m_A.row_count(); i++)
|
||||
scale_row(i);
|
||||
}
|
||||
void scale_rows();
|
||||
|
||||
void scale_row(unsigned i) {
|
||||
T row_max = std::max(m_A.get_max_abs_in_row(i), abs(convert_struct<T, X>::convert(m_b[i])));
|
||||
T alpha = numeric_traits<T>::one();
|
||||
if (numeric_traits<T>::is_zero(row_max)) {
|
||||
return;
|
||||
}
|
||||
if (numeric_traits<T>::get_double(row_max) < m_scaling_minimum) {
|
||||
do {
|
||||
alpha *= 2;
|
||||
row_max *= 2;
|
||||
} while (numeric_traits<T>::get_double(row_max) < m_scaling_minimum);
|
||||
m_A.scale_row(i, alpha);
|
||||
m_b[i] *= alpha;
|
||||
} else if (numeric_traits<T>::get_double(row_max) > m_scaling_maximum) {
|
||||
do {
|
||||
alpha /= 2;
|
||||
row_max /= 2;
|
||||
} while (numeric_traits<T>::get_double(row_max) > m_scaling_maximum);
|
||||
m_A.scale_row(i, alpha);
|
||||
m_b[i] *= alpha;
|
||||
}
|
||||
}
|
||||
void scale_row(unsigned i);
|
||||
|
||||
void scale_column(unsigned i){
|
||||
T column_max = m_A.get_max_abs_in_column(i);
|
||||
T alpha = numeric_traits<T>::one();
|
||||
void scale_column(unsigned i);
|
||||
|
||||
if (numeric_traits<T>::is_zero(column_max)){
|
||||
return; // the column has zeros only
|
||||
}
|
||||
|
||||
if (numeric_traits<T>::get_double(column_max) < m_scaling_minimum) {
|
||||
do {
|
||||
alpha *= 2;
|
||||
column_max *= 2;
|
||||
} while (numeric_traits<T>::get_double(column_max) < m_scaling_minimum);
|
||||
} else if (numeric_traits<T>::get_double(column_max) > m_scaling_maximum) {
|
||||
do {
|
||||
alpha /= 2;
|
||||
column_max /= 2;
|
||||
} while (numeric_traits<T>::get_double(column_max) > m_scaling_maximum);
|
||||
} else {
|
||||
return;
|
||||
}
|
||||
m_A.scale_column(i, alpha);
|
||||
m_column_scale[i] = alpha;
|
||||
}
|
||||
|
||||
void scale_columns() {
|
||||
for (unsigned i = 0; i < m_A.column_count(); i++) {
|
||||
scale_column(i);
|
||||
}
|
||||
}
|
||||
void scale_columns();
|
||||
};
|
||||
}
|
||||
|
|
|
|||
9
src/util/lp/scaler_instances.cpp
Normal file
9
src/util/lp/scaler_instances.cpp
Normal file
|
|
@ -0,0 +1,9 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/scaler.cpp"
|
||||
template bool lean::scaler<double, double>::scale();
|
||||
template bool lean::scaler<lean::mpq, lean::mpq>::scale();
|
||||
1110
src/util/lp/sparse_matrix.cpp
Normal file
1110
src/util/lp/sparse_matrix.cpp
Normal file
File diff suppressed because it is too large
Load diff
File diff suppressed because it is too large
Load diff
99
src/util/lp/sparse_matrix_instances.cpp
Normal file
99
src/util/lp/sparse_matrix_instances.cpp
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/lu.h"
|
||||
#include "util/lp/sparse_matrix.cpp"
|
||||
namespace lean {
|
||||
template double sparse_matrix<double, double>::dot_product_with_row<double>(unsigned int, std::vector<double> const&) const;
|
||||
template void sparse_matrix<double, double>::add_new_element(unsigned int, unsigned int, double);
|
||||
template void sparse_matrix<double, double>::divide_row_by_constant(unsigned int, double&, lp_settings&);
|
||||
template void sparse_matrix<double, double>::fill_eta_matrix(unsigned int, eta_matrix<double, double>**);
|
||||
template const double & sparse_matrix<double, double>::get(unsigned int, unsigned int) const;
|
||||
template unsigned sparse_matrix<double, double>::get_number_of_nonzeroes() const;
|
||||
template unsigned sparse_matrix<double, double>::get_number_of_nonzeroes_below_row(unsigned int) const;
|
||||
template bool sparse_matrix<double, double>::get_pivot_for_column(unsigned int&, unsigned int&, double const&, unsigned int);
|
||||
template unsigned sparse_matrix<double, double>::lowest_row_in_column(unsigned int);
|
||||
template bool sparse_matrix<double, double>::pivot_row_to_row(unsigned int, double, unsigned int, lp_settings&);
|
||||
template bool sparse_matrix<double, double>::pivot_with_eta(unsigned int, eta_matrix<double, double>*, lp_settings&);
|
||||
template void sparse_matrix<double, double>::prepare_for_factorization();
|
||||
template void sparse_matrix<double, double>::remove_element(std::vector<indexed_value<double> >&, indexed_value<double>&);
|
||||
template void sparse_matrix<double, double>::replace_column(unsigned int, indexed_vector<double>&, lp_settings&);
|
||||
template void sparse_matrix<double, double>::set(unsigned int, unsigned int, double);
|
||||
template void sparse_matrix<double, double>::set_max_in_row(std::vector<indexed_value<double> >&);
|
||||
template bool sparse_matrix<double, double>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector<double>&, lp_settings&);
|
||||
template bool sparse_matrix<double, double>::shorten_active_matrix(unsigned int, eta_matrix<double, double>*);
|
||||
template void sparse_matrix<double, double>::solve_y_U(std::vector<double>&) const;
|
||||
template sparse_matrix<double, double>::sparse_matrix(static_matrix<double, double> const&, std::vector<unsigned int>&);
|
||||
template sparse_matrix<double, double>::sparse_matrix(unsigned int);
|
||||
template float const & sparse_matrix<float, float>::get(unsigned int, unsigned int) const;
|
||||
template bool sparse_matrix<float, float>::pivot_row_to_row(unsigned int, float, unsigned int, lp_settings&);
|
||||
template void sparse_matrix<float, float>::replace_column(unsigned int, indexed_vector<float>&, lp_settings&);
|
||||
template void sparse_matrix<float, float>::set(unsigned int, unsigned int, float);
|
||||
template sparse_matrix<float, float>::sparse_matrix(unsigned int);
|
||||
template void sparse_matrix<mpq, mpq>::add_new_element(unsigned int, unsigned int, mpq);
|
||||
template void sparse_matrix<mpq, mpq>::divide_row_by_constant(unsigned int, mpq&, lp_settings&);
|
||||
template void sparse_matrix<mpq, mpq>::fill_eta_matrix(unsigned int, eta_matrix<mpq, mpq>**);
|
||||
template mpq const & sparse_matrix<mpq, mpq>::get(unsigned int, unsigned int) const;
|
||||
template unsigned sparse_matrix<mpq, mpq>::get_number_of_nonzeroes() const;
|
||||
template unsigned sparse_matrix<mpq, mpq>::get_number_of_nonzeroes_below_row(unsigned int) const;
|
||||
template bool sparse_matrix<mpq, mpq>::get_pivot_for_column(unsigned int&, unsigned int&, mpq const&, unsigned int);
|
||||
template unsigned sparse_matrix<mpq, mpq>::lowest_row_in_column(unsigned int);
|
||||
template bool sparse_matrix<mpq, mpq>::pivot_with_eta(unsigned int, eta_matrix<mpq, mpq>*, lp_settings&);
|
||||
template void sparse_matrix<mpq, mpq>::prepare_for_factorization();
|
||||
template void sparse_matrix<mpq, mpq>::remove_element(std::vector<indexed_value<mpq>> &, indexed_value<mpq>&);
|
||||
template void sparse_matrix<mpq, mpq>::replace_column(unsigned int, indexed_vector<mpq>&, lp_settings&);
|
||||
template void sparse_matrix<mpq, mpq>::set_max_in_row(std::vector<indexed_value<mpq>>&);
|
||||
template bool sparse_matrix<mpq, mpq>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector<mpq>&, lp_settings&);
|
||||
template bool sparse_matrix<mpq, mpq>::shorten_active_matrix(unsigned int, eta_matrix<mpq, mpq>*);
|
||||
template void sparse_matrix<mpq, mpq>::solve_y_U(std::vector<mpq>&) const;
|
||||
template sparse_matrix<mpq, mpq>::sparse_matrix(static_matrix<mpq, mpq> const&, std::vector<unsigned int>&);
|
||||
template void sparse_matrix<mpq, numeric_pair<mpq>>::add_new_element(unsigned int, unsigned int, mpq);
|
||||
template void sparse_matrix<mpq, numeric_pair<mpq>>::divide_row_by_constant(unsigned int, mpq&, lp_settings&);
|
||||
template void sparse_matrix<mpq, numeric_pair<mpq>>::fill_eta_matrix(unsigned int, eta_matrix<mpq, numeric_pair<mpq> >**);
|
||||
template const mpq & sparse_matrix<mpq, numeric_pair<mpq>>::get(unsigned int, unsigned int) const;
|
||||
template unsigned sparse_matrix<mpq, numeric_pair<mpq>>::get_number_of_nonzeroes() const;
|
||||
template unsigned sparse_matrix<mpq, numeric_pair<mpq>>::get_number_of_nonzeroes_below_row(unsigned int) const;
|
||||
template bool sparse_matrix<mpq, numeric_pair<mpq>>::get_pivot_for_column(unsigned int&, unsigned int&, mpq const&, unsigned int);
|
||||
template unsigned sparse_matrix<mpq, numeric_pair<mpq>>::lowest_row_in_column(unsigned int);
|
||||
template bool sparse_matrix<mpq, numeric_pair<mpq>>::pivot_with_eta(unsigned int, eta_matrix<mpq, numeric_pair<mpq> >*, lp_settings&);
|
||||
template void sparse_matrix<mpq, numeric_pair<mpq>>::prepare_for_factorization();
|
||||
template void sparse_matrix<mpq, numeric_pair<mpq>>::remove_element(std::vector<indexed_value<mpq>>&, indexed_value<mpq>&);
|
||||
template void sparse_matrix<mpq, numeric_pair<mpq>>::replace_column(unsigned int, indexed_vector<mpq>&, lp_settings&);
|
||||
template void sparse_matrix<mpq, numeric_pair<mpq>>::set_max_in_row(std::vector<indexed_value<mpq>>&);
|
||||
template bool sparse_matrix<mpq, numeric_pair<mpq>>::set_row_from_work_vector_and_clean_work_vector_not_adjusted(unsigned int, indexed_vector<mpq>&, lp_settings&);
|
||||
template bool sparse_matrix<mpq, numeric_pair<mpq>>::shorten_active_matrix(unsigned int, eta_matrix<mpq, numeric_pair<mpq> >*);
|
||||
template void sparse_matrix<mpq, numeric_pair<mpq>>::solve_y_U(std::vector<mpq>&) const;
|
||||
template sparse_matrix<mpq, numeric_pair<mpq>>::sparse_matrix(static_matrix<mpq, numeric_pair<mpq> > const&, std::vector<unsigned int>&);
|
||||
template void sparse_matrix<double, double>::double_solve_U_y<double>(std::vector<double>&);
|
||||
template void sparse_matrix<mpq, mpq>::double_solve_U_y<mpq>(std::vector<mpq>&);
|
||||
template void sparse_matrix<mpq, numeric_pair<mpq>>::double_solve_U_y<mpq>(std::vector<mpq>&);
|
||||
template void sparse_matrix<mpq, numeric_pair<mpq> >::double_solve_U_y<numeric_pair<mpq> >(std::vector<numeric_pair<mpq>>&);
|
||||
|
||||
/////////////////////
|
||||
/*
|
||||
template void lu<double, double>::create_initial_factorization();
|
||||
template void lu<mpq, mpq>::create_initial_factorization();
|
||||
template void lu<mpq, mpq>::replace_column(unsigned int, mpq, indexed_vector<mpq>&);
|
||||
template void lu<mpq, numeric_pair<mpq> >::create_initial_factorization();
|
||||
template void lu<mpq, numeric_pair<mpq> >::replace_column(unsigned int, mpq, indexed_vector<mpq>&);
|
||||
template void lu<double, double>::init_vector_w(unsigned int, indexed_vector<double>&);
|
||||
template void lu<mpq, mpq>::find_error_of_yB(std::vector<mpq, std::allocator<mpq> >&, std::vector<mpq, std::allocator<mpq> > const&);
|
||||
template void lu<mpq, mpq>::init_vector_w(unsigned int, indexed_vector<mpq>&);
|
||||
template void lu<mpq, numeric_pair<mpq> >::find_error_of_yB(std::vector<mpq, std::allocator<mpq> >&, std::vector<mpq, std::allocator<mpq> > const&);
|
||||
template void lu<mpq, numeric_pair<mpq> >::init_vector_w(unsigned int, indexed_vector<mpq>&);
|
||||
template void lu<double, double>::find_error_of_yB(std::vector<double, std::allocator<double> >&, std::vector<double, std::allocator<double> > const&);
|
||||
#ifdef LEAN_DEBUG
|
||||
template void print_matrix<double, double>(static_matrix<double, double>&);
|
||||
#endif
|
||||
*/
|
||||
#ifdef LEAN_DEBUG
|
||||
template bool sparse_matrix<double, double>::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const;
|
||||
template bool sparse_matrix<mpq, mpq>::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const;
|
||||
#endif
|
||||
#ifdef LEAN_DEBUG
|
||||
template bool sparse_matrix<mpq, numeric_pair<mpq> >::is_upper_triangular_and_maximums_are_set_correctly_in_rows(lp_settings&) const;
|
||||
#endif
|
||||
}
|
||||
|
|
@ -19,19 +19,6 @@
|
|||
#include "util/numerics/mpfp.h"
|
||||
#include "util/lp/lp_settings.h"
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
void zero_vector(T * t, unsigned size) {
|
||||
while (size-- > 0) { // it can be made faster by copying big chunks
|
||||
t[size] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T abs (T const & v) { return v >= zero_of_type<T>() ? v : -v; }
|
||||
|
||||
|
||||
template <typename T>
|
||||
class sparse_vector_iterator; // forward definition
|
||||
|
||||
template <typename T>
|
||||
class sparse_vector {
|
||||
|
|
@ -48,45 +35,13 @@ public:
|
|||
return numeric_traits<T>::zero();
|
||||
}
|
||||
#endif
|
||||
|
||||
void divide(T const & a) {
|
||||
lean_assert(!lp_settings::is_eps_small_general(a, 1e-12));
|
||||
for (auto & t : m_data) {
|
||||
t.second /= a;
|
||||
}
|
||||
for (auto & t : m_data) { t.second /= a; }
|
||||
}
|
||||
|
||||
unsigned size() const {
|
||||
return m_data.size();
|
||||
}
|
||||
|
||||
friend sparse_vector_iterator<T>;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class sparse_vector_iterator {
|
||||
typedef typename std::vector<pair<unsigned, T>>::iterator p_it;
|
||||
p_it m_it;
|
||||
p_it m_end;
|
||||
public:
|
||||
sparse_vector_iterator(sparse_vector<T> & s_v) : m_it(s_v.m_data.begin()),
|
||||
m_end(s_v.m_data.end()) {
|
||||
}
|
||||
|
||||
bool done() {
|
||||
return m_it == m_end;
|
||||
}
|
||||
|
||||
void move() {
|
||||
m_it++;
|
||||
}
|
||||
|
||||
unsigned index() {
|
||||
return m_it->first;
|
||||
}
|
||||
|
||||
const T & value() const {
|
||||
return m_it->second;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
|||
352
src/util/lp/square_dense_submatrix.cpp
Normal file
352
src/util/lp/square_dense_submatrix.cpp
Normal file
|
|
@ -0,0 +1,352 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/square_dense_submatrix.h"
|
||||
namespace lean {
|
||||
template <typename T, typename X>
|
||||
square_dense_submatrix<T, X>::square_dense_submatrix (sparse_matrix<T, X> *parent_matrix, unsigned index_start) :
|
||||
m_index_start(index_start),
|
||||
m_dim(parent_matrix->dimension() - index_start),
|
||||
m_v(m_dim * m_dim),
|
||||
m_parent(parent_matrix),
|
||||
m_row_permutation(m_parent->dimension()),
|
||||
m_column_permutation(m_parent->dimension()) {
|
||||
int row_offset = - static_cast<int>(m_index_start);
|
||||
for (unsigned i = index_start; i < parent_matrix->dimension(); i++) {
|
||||
unsigned row = parent_matrix->adjust_row(i);
|
||||
for (auto & iv : parent_matrix->get_row_values(row)) {
|
||||
unsigned j = parent_matrix->adjust_column_inverse(iv.m_index);
|
||||
lean_assert(j>= m_index_start);
|
||||
m_v[row_offset + j] = iv.m_value;
|
||||
}
|
||||
row_offset += m_dim;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void square_dense_submatrix<T, X>::init(sparse_matrix<T, X> *parent_matrix, unsigned index_start) {
|
||||
m_index_start = index_start;
|
||||
m_dim = parent_matrix->dimension() - index_start;
|
||||
m_v.resize(m_dim * m_dim);
|
||||
m_parent = parent_matrix;
|
||||
m_column_permutation.init(m_parent->dimension());
|
||||
for (unsigned i = index_start; i < parent_matrix->dimension(); i++) {
|
||||
unsigned row = parent_matrix->adjust_row(i);
|
||||
for (auto & iv : parent_matrix->get_row_values(row)) {
|
||||
unsigned j = parent_matrix->adjust_column_inverse(iv.m_index);
|
||||
(*this)[i][j] = iv.m_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> int square_dense_submatrix<T, X>::find_pivot_column_in_row(unsigned i) const {
|
||||
int j = -1;
|
||||
T max = zero_of_type<T>();
|
||||
lean_assert(i >= m_index_start);
|
||||
unsigned row_start = (i - m_index_start) * m_dim;
|
||||
for (unsigned k = i; k < m_parent->dimension(); k++) {
|
||||
unsigned col = adjust_column(k); // this is where the column is in the row
|
||||
unsigned offs = row_start + col - m_index_start;
|
||||
T t = abs(m_v[offs]);
|
||||
if (t > max) {
|
||||
j = k;
|
||||
max = t;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void square_dense_submatrix<T, X>::pivot(unsigned i, lp_settings & settings) {
|
||||
divide_row_by_pivot(i);
|
||||
for (unsigned k = i + 1; k < m_parent->dimension(); k++)
|
||||
pivot_row_to_row(i, k, settings);
|
||||
}
|
||||
|
||||
template <typename T, typename X> void square_dense_submatrix<T, X>::pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings) {
|
||||
lean_assert(i < row);
|
||||
unsigned pj = adjust_column(i); // the pivot column
|
||||
unsigned pjd = pj - m_index_start;
|
||||
unsigned pivot_row_offset = (i-m_index_start)*m_dim;
|
||||
T pivot = m_v[pivot_row_offset + pjd];
|
||||
unsigned row_offset= (row-m_index_start)*m_dim;
|
||||
T m = m_v[row_offset + pjd];
|
||||
lean_assert(!is_zero(pivot));
|
||||
m_v[row_offset + pjd] = -m * pivot; // creating L matrix
|
||||
for (unsigned j = m_index_start; j < m_parent->dimension(); j++) {
|
||||
if (j == pj) {
|
||||
pivot_row_offset++;
|
||||
row_offset++;
|
||||
continue;
|
||||
}
|
||||
auto t = m_v[row_offset] - m_v[pivot_row_offset] * m;
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(t)) {
|
||||
m_v[row_offset] = zero_of_type<T>();
|
||||
} else {
|
||||
m_v[row_offset] = t;
|
||||
}
|
||||
row_offset++; pivot_row_offset++;
|
||||
// at the same time we pivot the L too
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void square_dense_submatrix<T, X>::divide_row_by_pivot(unsigned i) {
|
||||
unsigned pj = adjust_column(i); // the pivot column
|
||||
unsigned irow_offset = (i - m_index_start) * m_dim;
|
||||
T pivot = m_v[irow_offset + pj - m_index_start];
|
||||
lean_assert(!is_zero(pivot));
|
||||
for (unsigned k = m_index_start; k < m_parent->dimension(); k++) {
|
||||
if (k == pj){
|
||||
m_v[irow_offset++] = one_of_type<T>() / pivot; // creating the L matrix diagonal
|
||||
continue;
|
||||
}
|
||||
m_v[irow_offset++] /= pivot;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void square_dense_submatrix<T, X>::update_parent_matrix(lp_settings & settings) {
|
||||
for (unsigned i = m_index_start; i < m_parent->dimension(); i++)
|
||||
update_existing_or_delete_in_parent_matrix_for_row(i, settings);
|
||||
push_new_elements_to_parent_matrix(settings);
|
||||
for (unsigned i = m_index_start; i < m_parent->dimension(); i++)
|
||||
m_parent->set_max_in_row(m_parent->adjust_row(i));
|
||||
}
|
||||
|
||||
template <typename T, typename X> void square_dense_submatrix<T, X>::update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings) {
|
||||
bool diag_updated = false;
|
||||
unsigned ai = m_parent->adjust_row(i);
|
||||
auto & row_vals = m_parent->get_row_values(ai);
|
||||
for (unsigned k = 0; k < row_vals.size(); k++) {
|
||||
auto & iv = row_vals[k];
|
||||
unsigned j = m_parent->adjust_column_inverse(iv.m_index);
|
||||
if (j < i) {
|
||||
m_parent->remove_element(row_vals, iv);
|
||||
k--;
|
||||
} else if (i == j) {
|
||||
m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = one_of_type<T>());
|
||||
diag_updated = true;
|
||||
} else { // j > i
|
||||
T & v = (*this)[i][j];
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
|
||||
m_parent->remove_element(row_vals, iv);
|
||||
k--;
|
||||
} else {
|
||||
m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = v);
|
||||
v = zero_of_type<T>(); // only new elements are left above the diagonal
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!diag_updated) {
|
||||
unsigned aj = m_parent->adjust_column(i);
|
||||
m_parent->add_new_element(ai, aj, one_of_type<T>());
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void square_dense_submatrix<T, X>::push_new_elements_to_parent_matrix(lp_settings & settings) {
|
||||
for (unsigned i = m_index_start; i < m_parent->dimension() - 1; i++) {
|
||||
unsigned ai = m_parent->adjust_row(i);
|
||||
for (unsigned j = i + 1; j < m_parent->dimension(); j++) {
|
||||
T & v = (*this)[i][j];
|
||||
if (!settings.abs_val_is_smaller_than_drop_tolerance(v)) {
|
||||
unsigned aj = m_parent->adjust_column(j);
|
||||
m_parent->add_new_element(ai, aj, v);
|
||||
}
|
||||
v = zero_of_type<T>(); // leave only L elements now
|
||||
}
|
||||
}
|
||||
}
|
||||
template <typename T, typename X>
|
||||
template <typename L>
|
||||
L square_dense_submatrix<T, X>::row_by_vector_product(unsigned i, const std::vector<L> & v) {
|
||||
lean_assert(i >= m_index_start);
|
||||
|
||||
unsigned row_in_subm = i - m_index_start;
|
||||
unsigned row_offset = row_in_subm * m_dim;
|
||||
L r = zero_of_type<L>();
|
||||
for (unsigned j = 0; j < m_dim; j++)
|
||||
r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)];
|
||||
return r;
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
template <typename L>
|
||||
L square_dense_submatrix<T, X>::column_by_vector_product(unsigned j, const std::vector<L> & v) {
|
||||
lean_assert(j >= m_index_start);
|
||||
|
||||
unsigned offset = j - m_index_start;
|
||||
L r = zero_of_type<L>();
|
||||
for (unsigned i = 0; i < m_dim; i++, offset += m_dim)
|
||||
r += m_v[offset] * v[adjust_row_inverse(m_index_start + i)];
|
||||
return r;
|
||||
}
|
||||
template <typename T, typename X>
|
||||
template <typename L>
|
||||
L square_dense_submatrix<T, X>::row_by_indexed_vector_product(unsigned i, const indexed_vector<L> & v) {
|
||||
lean_assert(i >= m_index_start);
|
||||
|
||||
unsigned row_in_subm = i - m_index_start;
|
||||
unsigned row_offset = row_in_subm * m_dim;
|
||||
L r = zero_of_type<L>();
|
||||
for (unsigned j = 0; j < m_dim; j++)
|
||||
r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)];
|
||||
return r;
|
||||
}
|
||||
template <typename T, typename X>
|
||||
template <typename L>
|
||||
void square_dense_submatrix<T, X>::apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T, X> deb(*this);
|
||||
// vector<L> deb_w(w.m_data.size());
|
||||
// for (unsigned i = 0; i < w.m_data.size(); i++)
|
||||
// deb_w[i] = w[i];
|
||||
|
||||
// deb.apply_from_left(deb_w);
|
||||
#endif // use indexed vector here
|
||||
|
||||
#ifndef DO_NOT_USE_INDEX
|
||||
std::vector<L> t(m_parent->dimension(), zero_of_type<L>());
|
||||
for (auto k : w.m_index) {
|
||||
unsigned j = adjust_column(k); // k-th element will contribute only to column j
|
||||
if (j < m_index_start) {
|
||||
t[adjust_row_inverse(j)] = w[k];
|
||||
} else {
|
||||
const L & v = w[k];
|
||||
for (unsigned i = 0; i < m_dim; i++) {
|
||||
unsigned row = adjust_row_inverse(m_index_start + i);
|
||||
unsigned offs = i * m_dim + j - m_index_start;
|
||||
t[row] += m_v[offs] * v;
|
||||
}
|
||||
}
|
||||
}
|
||||
w.m_index.clear();
|
||||
for (unsigned i = 0; i < m_parent->dimension(); i++) {
|
||||
const L & v = t[i];
|
||||
if (!settings.abs_val_is_smaller_than_drop_tolerance(v)){
|
||||
w.m_index.push_back(i);
|
||||
w.m_data[i] = v;
|
||||
} else {
|
||||
w.m_data[i] = zero_of_type<L>();
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::vector<L> t(m_parent->dimension());
|
||||
for (unsigned i = 0; i < m_index_start; i++) {
|
||||
t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)];
|
||||
}
|
||||
for (unsigned i = m_index_start; i < m_parent->dimension(); i++){
|
||||
t[adjust_row_inverse(i)] = row_by_indexed_vector_product(i, w);
|
||||
}
|
||||
for (unsigned i = 0; i < m_parent->dimension(); i++) {
|
||||
w.set_value(t[i], i);
|
||||
}
|
||||
for (unsigned i = 0; i < m_parent->dimension(); i++) {
|
||||
const L & v = t[i];
|
||||
if (!is_zero(v))
|
||||
w.m_index.push_back(i);
|
||||
w.m_data[i] = v;
|
||||
}
|
||||
#endif
|
||||
#ifdef LEAN_DEBUG
|
||||
// cout << "w final" << endl;
|
||||
// print_vector(w.m_data);
|
||||
// lean_assert(vectors_are_equal<T>(deb_w, w.m_data));
|
||||
// lean_assert(w.is_OK());
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
template <typename L>
|
||||
void square_dense_submatrix<T, X>::apply_from_left_to_vector(std::vector<L> & w) {
|
||||
// lp_settings & settings) {
|
||||
// dense_matrix<T, L> deb(*this);
|
||||
// vector<L> deb_w(w);
|
||||
// deb.apply_from_left_to_X(deb_w, settings);
|
||||
// // cout << "deb" << endl;
|
||||
// // print_matrix(deb);
|
||||
// // cout << "w" << endl;
|
||||
// // print_vector(w.m_data);
|
||||
// // cout << "deb_w" << endl;
|
||||
// // print_vector(deb_w);
|
||||
std::vector<L> t(m_parent->dimension());
|
||||
for (unsigned i = 0; i < m_index_start; i++) {
|
||||
t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)];
|
||||
}
|
||||
for (unsigned i = m_index_start; i < m_parent->dimension(); i++){
|
||||
t[adjust_row_inverse(i)] = row_by_vector_product(i, w);
|
||||
}
|
||||
for (unsigned i = 0; i < m_parent->dimension(); i++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// cout << "w final" << endl;
|
||||
// print_vector(w.m_data);
|
||||
// lean_assert(vectors_are_equal<L>(deb_w, w));
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename X> bool square_dense_submatrix<T, X>::is_L_matrix() const {
|
||||
#ifdef LEAN_DEBUG
|
||||
lean_assert(m_row_permutation.is_identity());
|
||||
for (unsigned i = 0; i < m_parent->dimension(); i++) {
|
||||
if (i < m_index_start) {
|
||||
lean_assert(m_column_permutation[i] == i);
|
||||
continue;
|
||||
}
|
||||
unsigned row_offs = (i-m_index_start)*m_dim;
|
||||
for (unsigned k = 0; k < m_dim; k++) {
|
||||
unsigned j = m_index_start + k;
|
||||
unsigned jex = adjust_column_inverse(j);
|
||||
if (jex > i) {
|
||||
lean_assert(is_zero(m_v[row_offs + k]));
|
||||
} else if (jex == i) {
|
||||
lean_assert(!is_zero(m_v[row_offs + k]));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
return true;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void square_dense_submatrix<T, X>::apply_from_right(std::vector<T> & w) {
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T, X> deb(*this);
|
||||
// vector<T> deb_w(w);
|
||||
// deb.apply_from_right(deb_w);
|
||||
#endif
|
||||
std::vector<T> t(w.size());
|
||||
for (unsigned j = 0; j < m_index_start; j++) {
|
||||
t[adjust_column_inverse(j)] = w[adjust_row_inverse(j)];
|
||||
}
|
||||
for (unsigned j = m_index_start; j < m_parent->dimension(); j++) {
|
||||
t[adjust_column_inverse(j)] = column_by_vector_product(j, w);
|
||||
}
|
||||
// std::copy(t.begin(), t.end(), w); // does not compile
|
||||
lean_assert(w.size() == t.size());
|
||||
w = t;
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vector_are_equal<T>(deb_w, w));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
|
||||
template <typename T, typename X> T square_dense_submatrix<T, X>::get_elem (unsigned i, unsigned j) const {
|
||||
i = adjust_row(i);
|
||||
j = adjust_column(j);
|
||||
if (i < m_index_start || j < m_index_start)
|
||||
return i == j? one_of_type<T>() : zero_of_type<T>();
|
||||
unsigned offs = (i - m_index_start)* m_dim + j - m_index_start;
|
||||
return m_v[offs];
|
||||
}
|
||||
|
||||
#endif
|
||||
template <typename T, typename X> void square_dense_submatrix<T, X>::conjugate_by_permutation(permutation_matrix<T, X> & q) {
|
||||
m_row_permutation.multiply_by_permutation_from_left(q);
|
||||
m_column_permutation.multiply_by_reverse_from_right(q);
|
||||
}
|
||||
}
|
||||
|
|
@ -22,6 +22,7 @@
|
|||
#include "util/lp/lp_settings.h"
|
||||
#include "util/lp/eta_matrix.h"
|
||||
#include "util/lp/binary_heap_upair_queue.h"
|
||||
#include "util/lp/sparse_matrix.h"
|
||||
namespace lean {
|
||||
template <typename T, typename X>
|
||||
class square_dense_submatrix : public tail_matrix<T, X> {
|
||||
|
|
@ -52,39 +53,9 @@ public:
|
|||
|
||||
square_dense_submatrix() {}
|
||||
|
||||
square_dense_submatrix (sparse_matrix<T, X> *parent_matrix, unsigned index_start) :
|
||||
m_index_start(index_start),
|
||||
m_dim(parent_matrix->dimension() - index_start),
|
||||
m_v(m_dim * m_dim),
|
||||
m_parent(parent_matrix),
|
||||
m_row_permutation(m_parent->dimension()),
|
||||
m_column_permutation(m_parent->dimension()) {
|
||||
int row_offset = - static_cast<int>(m_index_start);
|
||||
for (unsigned i = index_start; i < parent_matrix->dimension(); i++) {
|
||||
unsigned row = parent_matrix->adjust_row(i);
|
||||
for (auto & iv : parent_matrix->get_row_values(row)) {
|
||||
unsigned j = parent_matrix->adjust_column_inverse(iv.m_index);
|
||||
lean_assert(j>= m_index_start);
|
||||
m_v[row_offset + j] = iv.m_value;
|
||||
}
|
||||
row_offset += m_dim;
|
||||
}
|
||||
}
|
||||
square_dense_submatrix (sparse_matrix<T, X> *parent_matrix, unsigned index_start);
|
||||
|
||||
void init(sparse_matrix<T, X> *parent_matrix, unsigned index_start) {
|
||||
m_index_start = index_start;
|
||||
m_dim = parent_matrix->dimension() - index_start;
|
||||
m_v.resize(m_dim * m_dim);
|
||||
m_parent = parent_matrix;
|
||||
m_column_permutation.init(m_parent->dimension());
|
||||
for (unsigned i = index_start; i < parent_matrix->dimension(); i++) {
|
||||
unsigned row = parent_matrix->adjust_row(i);
|
||||
for (auto & iv : parent_matrix->get_row_values(row)) {
|
||||
unsigned j = parent_matrix->adjust_column_inverse(iv.m_index);
|
||||
(*this)[i][j] = iv.m_value;
|
||||
}
|
||||
}
|
||||
}
|
||||
void init(sparse_matrix<T, X> *parent_matrix, unsigned index_start);
|
||||
|
||||
ref operator[] (unsigned i) {
|
||||
lean_assert(i >= m_index_start);
|
||||
|
|
@ -92,26 +63,11 @@ public:
|
|||
return ref(i, *this);
|
||||
}
|
||||
|
||||
int find_pivot_column_in_row(unsigned i) const {
|
||||
int j = -1;
|
||||
T max = zero_of_type<T>();
|
||||
lean_assert(i >= m_index_start);
|
||||
unsigned row_start = (i - m_index_start) * m_dim;
|
||||
for (unsigned k = i; k < m_parent->dimension(); k++) {
|
||||
unsigned col = adjust_column(k); // this is where the column is in the row
|
||||
unsigned offs = row_start + col - m_index_start;
|
||||
T t = abs(m_v[offs]);
|
||||
if (t > max) {
|
||||
j = k;
|
||||
max = t;
|
||||
}
|
||||
}
|
||||
return j;
|
||||
}
|
||||
int find_pivot_column_in_row(unsigned i) const;
|
||||
|
||||
void swap_columns(unsigned i, unsigned j) {
|
||||
if (i == j) return;
|
||||
m_column_permutation.transpose_from_left(i, j);
|
||||
if (i != j)
|
||||
m_column_permutation.transpose_from_left(i, j);
|
||||
}
|
||||
|
||||
unsigned adjust_column(unsigned col) const{
|
||||
|
|
@ -129,250 +85,34 @@ public:
|
|||
return m_row_permutation.apply_reverse(row);
|
||||
}
|
||||
|
||||
void pivot(unsigned i, lp_settings & settings) {
|
||||
divide_row_by_pivot(i);
|
||||
for (unsigned k = i + 1; k < m_parent->dimension(); k++)
|
||||
pivot_row_to_row(i, k, settings);
|
||||
}
|
||||
void pivot(unsigned i, lp_settings & settings);
|
||||
|
||||
void pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings) {
|
||||
lean_assert(i < row);
|
||||
unsigned pj = adjust_column(i); // the pivot column
|
||||
unsigned pjd = pj - m_index_start;
|
||||
unsigned pivot_row_offset = (i-m_index_start)*m_dim;
|
||||
T pivot = m_v[pivot_row_offset + pjd];
|
||||
unsigned row_offset= (row-m_index_start)*m_dim;
|
||||
T m = m_v[row_offset + pjd];
|
||||
lean_assert(!is_zero(pivot));
|
||||
m_v[row_offset + pjd] = -m * pivot; // creating L matrix
|
||||
for (unsigned j = m_index_start; j < m_parent->dimension(); j++) {
|
||||
if (j == pj) {
|
||||
pivot_row_offset++;
|
||||
row_offset++;
|
||||
continue;
|
||||
}
|
||||
auto t = m_v[row_offset] - m_v[pivot_row_offset] * m;
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(t)) {
|
||||
m_v[row_offset] = zero_of_type<T>();
|
||||
} else {
|
||||
m_v[row_offset] = t;
|
||||
}
|
||||
row_offset++; pivot_row_offset++;
|
||||
// at the same time we pivot the L too
|
||||
}
|
||||
}
|
||||
void pivot_row_to_row(unsigned i, unsigned row, lp_settings & settings);;
|
||||
|
||||
void divide_row_by_pivot(unsigned i) {
|
||||
unsigned pj = adjust_column(i); // the pivot column
|
||||
unsigned irow_offset = (i - m_index_start) * m_dim;
|
||||
T pivot = m_v[irow_offset + pj - m_index_start];
|
||||
lean_assert(!is_zero(pivot));
|
||||
for (unsigned k = m_index_start; k < m_parent->dimension(); k++) {
|
||||
if (k == pj){
|
||||
m_v[irow_offset++] = one_of_type<T>() / pivot; // creating the L matrix diagonal
|
||||
continue;
|
||||
}
|
||||
m_v[irow_offset++] /= pivot;
|
||||
}
|
||||
}
|
||||
void divide_row_by_pivot(unsigned i);
|
||||
|
||||
void update_parent_matrix(lp_settings & settings) {
|
||||
for (unsigned i = m_index_start; i < m_parent->dimension(); i++)
|
||||
update_existing_or_delete_in_parent_matrix_for_row(i, settings);
|
||||
push_new_elements_to_parent_matrix(settings);
|
||||
for (unsigned i = m_index_start; i < m_parent->dimension(); i++)
|
||||
m_parent->set_max_in_row(m_parent->adjust_row(i));
|
||||
}
|
||||
void update_parent_matrix(lp_settings & settings);
|
||||
|
||||
void update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings) {
|
||||
bool diag_updated = false;
|
||||
unsigned ai = m_parent->adjust_row(i);
|
||||
auto & row_vals = m_parent->get_row_values(ai);
|
||||
for (unsigned k = 0; k < row_vals.size(); k++) {
|
||||
auto & iv = row_vals[k];
|
||||
unsigned j = m_parent->adjust_column_inverse(iv.m_index);
|
||||
if (j < i) {
|
||||
m_parent->remove_element(row_vals, iv);
|
||||
k--;
|
||||
} else if (i == j) {
|
||||
m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = one_of_type<T>());
|
||||
diag_updated = true;
|
||||
} else { // j > i
|
||||
T & v = (*this)[i][j];
|
||||
if (settings.abs_val_is_smaller_than_drop_tolerance(v)) {
|
||||
m_parent->remove_element(row_vals, iv);
|
||||
k--;
|
||||
} else {
|
||||
m_parent->m_columns[iv.m_index].m_values[iv.m_other].set_value(iv.m_value = v);
|
||||
v = zero_of_type<T>(); // only new elements are left above the diagonal
|
||||
}
|
||||
}
|
||||
}
|
||||
if (!diag_updated) {
|
||||
unsigned aj = m_parent->adjust_column(i);
|
||||
m_parent->add_new_element(ai, aj, one_of_type<T>());
|
||||
}
|
||||
}
|
||||
void update_existing_or_delete_in_parent_matrix_for_row(unsigned i, lp_settings & settings);
|
||||
|
||||
void push_new_elements_to_parent_matrix(lp_settings & settings) {
|
||||
for (unsigned i = m_index_start; i < m_parent->dimension() - 1; i++) {
|
||||
unsigned ai = m_parent->adjust_row(i);
|
||||
for (unsigned j = i + 1; j < m_parent->dimension(); j++) {
|
||||
T & v = (*this)[i][j];
|
||||
if (!settings.abs_val_is_smaller_than_drop_tolerance(v)) {
|
||||
unsigned aj = m_parent->adjust_column(j);
|
||||
m_parent->add_new_element(ai, aj, v);
|
||||
}
|
||||
v = zero_of_type<T>(); // leave only L elements now
|
||||
}
|
||||
}
|
||||
}
|
||||
void push_new_elements_to_parent_matrix(lp_settings & settings);
|
||||
|
||||
template <typename L>
|
||||
L row_by_vector_product(unsigned i, const std::vector<L> & v) {
|
||||
lean_assert(i >= m_index_start);
|
||||
|
||||
unsigned row_in_subm = i - m_index_start;
|
||||
unsigned row_offset = row_in_subm * m_dim;
|
||||
L r = zero_of_type<L>();
|
||||
for (unsigned j = 0; j < m_dim; j++)
|
||||
r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)];
|
||||
return r;
|
||||
}
|
||||
L row_by_vector_product(unsigned i, const std::vector<L> & v);
|
||||
|
||||
template <typename L>
|
||||
L column_by_vector_product(unsigned j, const std::vector<L> & v) {
|
||||
lean_assert(j >= m_index_start);
|
||||
|
||||
unsigned offset = j - m_index_start;
|
||||
L r = zero_of_type<L>();
|
||||
for (unsigned i = 0; i < m_dim; i++, offset += m_dim)
|
||||
r += m_v[offset] * v[adjust_row_inverse(m_index_start + i)];
|
||||
return r;
|
||||
}
|
||||
L column_by_vector_product(unsigned j, const std::vector<L> & v);
|
||||
|
||||
template <typename L>
|
||||
L row_by_indexed_vector_product(unsigned i, const indexed_vector<L> & v) {
|
||||
lean_assert(i >= m_index_start);
|
||||
|
||||
unsigned row_in_subm = i - m_index_start;
|
||||
unsigned row_offset = row_in_subm * m_dim;
|
||||
L r = zero_of_type<L>();
|
||||
for (unsigned j = 0; j < m_dim; j++)
|
||||
r += m_v[row_offset + j] * v[adjust_column_inverse(m_index_start + j)];
|
||||
return r;
|
||||
}
|
||||
L row_by_indexed_vector_product(unsigned i, const indexed_vector<L> & v);
|
||||
|
||||
template <typename L>
|
||||
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T, X> deb(*this);
|
||||
// vector<L> deb_w(w.m_data.size());
|
||||
// for (unsigned i = 0; i < w.m_data.size(); i++)
|
||||
// deb_w[i] = w[i];
|
||||
|
||||
// deb.apply_from_left(deb_w);
|
||||
#endif // use indexed vector here
|
||||
|
||||
#ifndef DO_NOT_USE_INDEX
|
||||
std::vector<L> t(m_parent->dimension(), zero_of_type<L>());
|
||||
for (auto k : w.m_index) {
|
||||
unsigned j = adjust_column(k); // k-th element will contribute only to column j
|
||||
if (j < m_index_start) {
|
||||
t[adjust_row_inverse(j)] = w[k];
|
||||
} else {
|
||||
const L & v = w[k];
|
||||
for (unsigned i = 0; i < m_dim; i++) {
|
||||
unsigned row = adjust_row_inverse(m_index_start + i);
|
||||
unsigned offs = i * m_dim + j - m_index_start;
|
||||
t[row] += m_v[offs] * v;
|
||||
}
|
||||
}
|
||||
}
|
||||
w.m_index.clear();
|
||||
for (unsigned i = 0; i < m_parent->dimension(); i++) {
|
||||
const L & v = t[i];
|
||||
if (!settings.abs_val_is_smaller_than_drop_tolerance(v)){
|
||||
w.m_index.push_back(i);
|
||||
w.m_data[i] = v;
|
||||
} else {
|
||||
w.m_data[i] = zero_of_type<L>();
|
||||
}
|
||||
}
|
||||
#else
|
||||
std::vector<L> t(m_parent->dimension());
|
||||
for (unsigned i = 0; i < m_index_start; i++) {
|
||||
t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)];
|
||||
}
|
||||
for (unsigned i = m_index_start; i < m_parent->dimension(); i++){
|
||||
t[adjust_row_inverse(i)] = row_by_indexed_vector_product(i, w);
|
||||
}
|
||||
for (unsigned i = 0; i < m_parent->dimension(); i++) {
|
||||
w.set_value(t[i], i);
|
||||
}
|
||||
for (unsigned i = 0; i < m_parent->dimension(); i++) {
|
||||
const L & v = t[i];
|
||||
if (!is_zero(v))
|
||||
w.m_index.push_back(i);
|
||||
w.m_data[i] = v;
|
||||
}
|
||||
#endif
|
||||
#ifdef LEAN_DEBUG
|
||||
// cout << "w final" << endl;
|
||||
// print_vector(w.m_data);
|
||||
// lean_assert(vectors_are_equal<T>(deb_w, w.m_data));
|
||||
// lean_assert(w.is_OK());
|
||||
#endif
|
||||
}
|
||||
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings);
|
||||
|
||||
template <typename L>
|
||||
void apply_from_left_to_vector(std::vector<L> & w) {
|
||||
// lp_settings & settings) {
|
||||
// dense_matrix<T, L> deb(*this);
|
||||
// vector<L> deb_w(w);
|
||||
// deb.apply_from_left_to_X(deb_w, settings);
|
||||
// // cout << "deb" << endl;
|
||||
// // print_matrix(deb);
|
||||
// // cout << "w" << endl;
|
||||
// // print_vector(w.m_data);
|
||||
// // cout << "deb_w" << endl;
|
||||
// // print_vector(deb_w);
|
||||
std::vector<L> t(m_parent->dimension());
|
||||
for (unsigned i = 0; i < m_index_start; i++) {
|
||||
t[adjust_row_inverse(i)] = w[adjust_column_inverse(i)];
|
||||
}
|
||||
for (unsigned i = m_index_start; i < m_parent->dimension(); i++){
|
||||
t[adjust_row_inverse(i)] = row_by_vector_product(i, w);
|
||||
}
|
||||
for (unsigned i = 0; i < m_parent->dimension(); i++) {
|
||||
w[i] = t[i];
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
// cout << "w final" << endl;
|
||||
// print_vector(w.m_data);
|
||||
// lean_assert(vectors_are_equal<L>(deb_w, w));
|
||||
#endif
|
||||
}
|
||||
void apply_from_left_to_vector(std::vector<L> & w);
|
||||
|
||||
bool is_L_matrix() const {
|
||||
lean_assert(m_row_permutation.is_identity());
|
||||
for (unsigned i = 0; i < m_parent->dimension(); i++) {
|
||||
if (i < m_index_start) {
|
||||
lean_assert(m_column_permutation[i] == i);
|
||||
continue;
|
||||
}
|
||||
unsigned row_offs = (i-m_index_start)*m_dim;
|
||||
for (unsigned k = 0; k < m_dim; k++) {
|
||||
unsigned j = m_index_start + k;
|
||||
unsigned jex = adjust_column_inverse(j);
|
||||
if (jex > i) {
|
||||
lean_assert(is_zero(m_v[row_offs + k]));
|
||||
} else if (jex == i) {
|
||||
lean_assert(!is_zero(m_v[row_offs + k]));
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
bool is_L_matrix() const;
|
||||
|
||||
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
|
||||
apply_from_left_local(w, settings);
|
||||
|
|
@ -385,48 +125,15 @@ public:
|
|||
apply_from_left_to_vector(w);// , settings);
|
||||
}
|
||||
|
||||
void apply_from_right(std::vector<T> & w) {
|
||||
#ifdef LEAN_DEBUG
|
||||
// dense_matrix<T, X> deb(*this);
|
||||
// vector<T> deb_w(w);
|
||||
// deb.apply_from_right(deb_w);
|
||||
#endif
|
||||
std::vector<T> t(w.size());
|
||||
for (unsigned j = 0; j < m_index_start; j++) {
|
||||
t[adjust_column_inverse(j)] = w[adjust_row_inverse(j)];
|
||||
}
|
||||
for (unsigned j = m_index_start; j < m_parent->dimension(); j++) {
|
||||
t[adjust_column_inverse(j)] = column_by_vector_product(j, w);
|
||||
}
|
||||
// std::copy(t.begin(), t.end(), w); // does not compile
|
||||
lean_assert(w.size() == t.size());
|
||||
w = t;
|
||||
#ifdef LEAN_DEBUG
|
||||
// lean_assert(vector_are_equal<T>(deb_w, w));
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
|
||||
void apply_from_right(std::vector<T> & w);
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
|
||||
T get_elem (unsigned i, unsigned j) const {
|
||||
i = adjust_row(i);
|
||||
j = adjust_column(j);
|
||||
if (i < m_index_start || j < m_index_start)
|
||||
return i == j? one_of_type<T>() : zero_of_type<T>();
|
||||
unsigned offs = (i - m_index_start)* m_dim + j - m_index_start;
|
||||
return m_v[offs];
|
||||
}
|
||||
T get_elem (unsigned i, unsigned j) const;
|
||||
unsigned row_count() const { return m_parent->row_count();}
|
||||
unsigned column_count() const { return row_count();}
|
||||
void set_number_of_rows(unsigned /* m */) {}
|
||||
void set_number_of_columns(unsigned /* n */) {};
|
||||
void set_number_of_rows(unsigned) {}
|
||||
void set_number_of_columns(unsigned) {};
|
||||
#endif
|
||||
void conjugate_by_permutation(permutation_matrix<T, X> & q) {
|
||||
m_row_permutation.multiply_by_permutation_from_left(q);
|
||||
m_column_permutation.multiply_by_reverse_from_right(q);
|
||||
}
|
||||
void conjugate_by_permutation(permutation_matrix<T, X> & q);
|
||||
};
|
||||
}
|
||||
|
|
|
|||
32
src/util/lp/square_dense_submatrix_instances.cpp
Normal file
32
src/util/lp/square_dense_submatrix_instances.cpp
Normal file
|
|
@ -0,0 +1,32 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
#include "util/lp/square_dense_submatrix.cpp"
|
||||
template void lean::square_dense_submatrix<double, double>::init(lean::sparse_matrix<double, double>*, unsigned int);
|
||||
template lean::square_dense_submatrix<double, double>::square_dense_submatrix(lean::sparse_matrix<double, double>*, unsigned int);
|
||||
template void lean::square_dense_submatrix<double, double>::update_parent_matrix(lean::lp_settings&);
|
||||
template bool lean::square_dense_submatrix<double, double>::is_L_matrix() const;
|
||||
template void lean::square_dense_submatrix<double, double>::conjugate_by_permutation(lean::permutation_matrix<double, double>&);
|
||||
template int lean::square_dense_submatrix<double, double>::find_pivot_column_in_row(unsigned int) const;
|
||||
template void lean::square_dense_submatrix<double, double>::pivot(unsigned int, lean::lp_settings&);
|
||||
template lean::square_dense_submatrix<lean::mpq, lean::numeric_pair<lean::mpq> >::square_dense_submatrix(lean::sparse_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >*, unsigned int);
|
||||
template void lean::square_dense_submatrix<lean::mpq, lean::numeric_pair<lean::mpq> >::update_parent_matrix(lean::lp_settings&);
|
||||
template bool lean::square_dense_submatrix<lean::mpq, lean::numeric_pair<lean::mpq> >::is_L_matrix() const;
|
||||
template void lean::square_dense_submatrix<lean::mpq, lean::numeric_pair<lean::mpq> >::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&);
|
||||
template int lean::square_dense_submatrix<lean::mpq, lean::numeric_pair<lean::mpq> >::find_pivot_column_in_row(unsigned int) const;
|
||||
template void lean::square_dense_submatrix<lean::mpq, lean::numeric_pair<lean::mpq> >::pivot(unsigned int, lean::lp_settings&);
|
||||
#ifdef LEAN_DEBUG
|
||||
template double lean::square_dense_submatrix<double, double>::get_elem(unsigned int, unsigned int) const;
|
||||
#endif
|
||||
template void lean::square_dense_submatrix<double, double>::apply_from_right(std::vector<double, std::allocator<double> >&);
|
||||
template void lean::square_dense_submatrix<double, double>::apply_from_left_local<double>(lean::indexed_vector<double>&, lean::lp_settings&);
|
||||
template void lean::square_dense_submatrix<double, double>::apply_from_left_to_vector<double>(std::vector<double, std::allocator<double> >&);
|
||||
template lean::square_dense_submatrix<lean::mpq, lean::mpq>::square_dense_submatrix(lean::sparse_matrix<lean::mpq, lean::mpq>*, unsigned int);
|
||||
template void lean::square_dense_submatrix<lean::mpq, lean::mpq>::update_parent_matrix(lean::lp_settings&);
|
||||
template bool lean::square_dense_submatrix<lean::mpq, lean::mpq>::is_L_matrix() const;
|
||||
template void lean::square_dense_submatrix<lean::mpq, lean::mpq>::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::mpq>&);
|
||||
template int lean::square_dense_submatrix<lean::mpq, lean::mpq>::find_pivot_column_in_row(unsigned int) const;
|
||||
template void lean::square_dense_submatrix<lean::mpq, lean::mpq>::pivot(unsigned int, lean::lp_settings&);
|
||||
376
src/util/lp/static_matrix.cpp
Normal file
376
src/util/lp/static_matrix.cpp
Normal file
|
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/static_matrix.h"
|
||||
|
||||
namespace lean {
|
||||
// each assignment for this matrix should be issued only once!!!
|
||||
template <typename T, typename X>
|
||||
void static_matrix<T, X>::init_row_columns(unsigned m, unsigned n) {
|
||||
lean_assert(m_rows.size() == 0 && m_columns.size() == 0);
|
||||
for (unsigned i = 0; i < m; i++){
|
||||
m_rows.push_back(row_strip());
|
||||
}
|
||||
for (unsigned j = 0; j < n; j++){
|
||||
m_columns.push_back(column_strip());
|
||||
}
|
||||
}
|
||||
|
||||
// constructor that copies columns of the basis from A
|
||||
template <typename T, typename X>
|
||||
static_matrix<T, X>::static_matrix(static_matrix const &A, unsigned * basis) :
|
||||
m_work_pivot_vector(A.row_count(), numeric_traits<T>::zero()) {
|
||||
unsigned m = A.row_count();
|
||||
init_row_columns(m, m);
|
||||
while (m--) {
|
||||
for (auto & col : A.m_columns[m]){
|
||||
set(col.m_i, m, A.get_value_of_column_cell(col));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: clear() {
|
||||
m_work_pivot_vector.clear();
|
||||
m_rows.clear();
|
||||
m_columns.clear();
|
||||
#ifdef LEAN_DEBUG
|
||||
m_domain.clear();
|
||||
#endif
|
||||
}
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: init_work_pivot_vector(unsigned m) {
|
||||
while (m--) m_work_pivot_vector.push_back(numeric_traits<T>::zero());
|
||||
}
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: init_empty_matrix(unsigned m, unsigned n) {
|
||||
init_work_pivot_vector(m);
|
||||
init_row_columns(m, n);
|
||||
}
|
||||
template <typename T, typename X>
|
||||
template <typename L>
|
||||
L static_matrix<T, X>::dot_product_with_row(unsigned row, const std::vector<L> & w) {
|
||||
L ret = zero_of_type<L>();
|
||||
lean_assert(row < m_rows.size());
|
||||
for (auto & it : m_rows[row]) {
|
||||
ret += w[it.m_j] * it.get_val();
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
|
||||
template <typename T, typename X> unsigned static_matrix<T, X>:: lowest_row_in_column(unsigned col) {
|
||||
lean_assert(col < column_count());
|
||||
column_strip & colstrip = m_columns[col];
|
||||
lean_assert(colstrip.size() > 0);
|
||||
unsigned ret = 0;
|
||||
for (auto & t : colstrip) {
|
||||
if (t.m_i > ret) {
|
||||
ret = t.m_i;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T static_matrix<T, X>:: dot_product_with_column(const std::vector<T> & y, unsigned j) const {
|
||||
lean_assert(j < column_count());
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto & it : m_columns[j]) {
|
||||
ret += y[it.m_i] * it.m_value; // get_value_of_column_cell(it);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: add_columns_at_the_end(unsigned delta) {
|
||||
for (unsigned i = 0; i < delta; i++)
|
||||
add_column();
|
||||
}
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: forget_last_columns(unsigned how_many_to_forget) {
|
||||
lean_assert(m_columns.size() >= how_many_to_forget);
|
||||
unsigned j = column_count() - 1;
|
||||
for (; how_many_to_forget > 0; how_many_to_forget--) {
|
||||
remove_last_column(j --);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: remove_last_column(unsigned j) {
|
||||
column_strip & col = m_columns.back();
|
||||
for (auto & it : col) {
|
||||
auto & row = m_rows[it.m_i];
|
||||
unsigned offset = row.size() - 1;
|
||||
for (auto row_it = row.rbegin(); row_it != row.rend(); row_it ++) {
|
||||
if (row_it->m_j == j) {
|
||||
row.erase(row.begin() + offset);
|
||||
break;
|
||||
}
|
||||
offset--;
|
||||
}
|
||||
}
|
||||
m_columns.pop_back();
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void static_matrix<T, X>:: scale_row(unsigned row, T const & alpha) {
|
||||
for (auto & t : m_rows[row]) {
|
||||
t.set_val(t.get_val() * alpha);
|
||||
m_columns[t.m_j][t.m_offset].m_value *= alpha;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void static_matrix<T, X>:: divide_row_by_constant(unsigned row, T const & alpha) {
|
||||
for (auto & t : m_rows[row]) {
|
||||
t.set_val(t.get_val() / alpha);
|
||||
m_columns[t.m_j][t.m_offset].m_value /= alpha;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
void static_matrix<T, X>:: scale_column(unsigned column, T const & alpha) {
|
||||
for (auto & t : m_columns[column]) {
|
||||
t.m_value *= alpha;
|
||||
auto & r = m_rows[t.m_i][t.m_offset];
|
||||
r.set_val(r.get_val() *= alpha);
|
||||
}
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X> void static_matrix<T, X>:: regen_domain() {
|
||||
m_domain.clear();
|
||||
for (int i = 0; i < m_rows.size(); i++){
|
||||
for (auto & t : m_rows[i]) {
|
||||
m_domain.insert(std::make_pair(i, t.m_j));
|
||||
}
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: set(unsigned row, unsigned col, T const & val) {
|
||||
if (numeric_traits<T>::is_zero(val)) return;
|
||||
lean_assert(row < row_count() && col < column_count());
|
||||
#ifdef LEAN_DEBUG
|
||||
pair<unsigned, unsigned> p(row, col);
|
||||
lean_assert(m_domain.find(p) == m_domain.end());
|
||||
m_domain.insert(p);
|
||||
#endif
|
||||
auto & r = m_rows[row];
|
||||
unsigned offs_in_cols = m_columns[col].size();
|
||||
m_columns[col].push_back(make_column_cell(row, r.size(), val));
|
||||
r.push_back(make_row_cell(col, offs_in_cols, val));
|
||||
}
|
||||
|
||||
template <typename T, typename X>
|
||||
std::set<pair<unsigned, unsigned>> static_matrix<T, X>::get_domain() {
|
||||
std::set<pair<unsigned, unsigned>> ret;
|
||||
for (unsigned i = 0; i < m_rows.size(); i++) {
|
||||
for (auto it : m_rows[i]) {
|
||||
ret.insert(std::make_pair(i, it.m_j));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: copy_column_to_vector (unsigned j, indexed_vector<T> & v) const {
|
||||
lean_assert(j < m_columns.size());
|
||||
for (auto & it : m_columns[j]) {
|
||||
if (!is_zero(it.m_value))
|
||||
v.set_value(it.m_value, it.m_i);
|
||||
}
|
||||
}
|
||||
template <typename T, typename X> void static_matrix<T, X>:: copy_column_to_vector (unsigned j, std::vector<T> & v) const {
|
||||
v.resize(row_count(), numeric_traits<T>::zero());
|
||||
for (auto & it : m_columns[j]) {
|
||||
if (!is_zero(it.m_value))
|
||||
v[it.m_i]=it.m_value;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: add_column_to_vector (const T & a, unsigned j, T * v) const {
|
||||
for (auto & it : m_columns[j]) {
|
||||
v[it.m_i] += a * it.m_value;
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> T static_matrix<T, X>:: get_max_abs_in_row(unsigned row) const {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto & t : m_rows[row]) {
|
||||
T a = abs(t.get_val());
|
||||
if (a > ret) {
|
||||
ret = a;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T static_matrix<T, X>:: get_min_abs_in_row(unsigned row) const {
|
||||
bool first_time = true;
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto & t : m_rows[row]) {
|
||||
T a = abs(t.get_val());
|
||||
if (first_time) {
|
||||
ret = a;
|
||||
first_time = false;
|
||||
} else if (a < ret) {
|
||||
ret = a;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> T static_matrix<T, X>:: get_max_abs_in_column(unsigned column) const {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto & t : m_columns[column]) {
|
||||
T a = abs(t.m_value);
|
||||
if (a > ret) {
|
||||
ret = a;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T static_matrix<T, X>:: get_min_abs_in_column(unsigned column) const {
|
||||
bool first_time = true;
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto & t : m_columns[column]) {
|
||||
T a = abs(t.m_value);
|
||||
if (first_time) {
|
||||
first_time = false;
|
||||
ret = a;
|
||||
} else if (a < ret) {
|
||||
ret = a;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X> void static_matrix<T, X>:: check_consistency() {
|
||||
std::unordered_map<std::pair<unsigned, unsigned>, T> by_rows;
|
||||
for (int i = 0; i < m_rows.size(); i++){
|
||||
for (auto & t : m_rows[i]) {
|
||||
pair<unsigned, unsigned> p(i, t.m_j);
|
||||
lean_assert(by_rows.find(p) == by_rows.end());
|
||||
by_rows[p] = t.get_val();
|
||||
}
|
||||
}
|
||||
std::unordered_map<pair<unsigned, unsigned>, T> by_cols;
|
||||
for (int i = 0; i < m_columns.size(); i++){
|
||||
for (auto & t : m_columns[i]) {
|
||||
pair<unsigned, unsigned> p(t.m_i, i);
|
||||
lean_assert(by_cols.find(p) == by_cols.end());
|
||||
by_cols[p] = get_value_of_column_cell(t);
|
||||
}
|
||||
}
|
||||
lean_assert(by_rows.size() == by_cols.size());
|
||||
|
||||
for (auto & t : by_rows) {
|
||||
auto ic = by_cols.find(t.first);
|
||||
if (ic == by_cols.end()){
|
||||
std::cout << "rows have pair (" << t.first.first <<"," << t.first.second
|
||||
<< "), but columns don't " << std::endl;
|
||||
}
|
||||
lean_assert(ic != by_cols.end());
|
||||
lean_assert(t.second == ic->second);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: cross_out_row(unsigned k) {
|
||||
#ifdef LEAN_DEBUG
|
||||
check_consistency();
|
||||
#endif
|
||||
cross_out_row_from_columns(k, m_rows[k]);
|
||||
fix_row_indices_in_each_column_for_crossed_row(k);
|
||||
m_rows.erase(m_rows.begin() + k);
|
||||
#ifdef LEAN_DEBUG
|
||||
regen_domain();
|
||||
check_consistency();
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: fix_row_indices_in_each_column_for_crossed_row(unsigned k) {
|
||||
for (unsigned j = 0; j < m_columns.size(); j++) {
|
||||
auto & col = m_columns[j];
|
||||
for (int i = 0; i < col.size(); i++) {
|
||||
if (col[i].m_i > k) {
|
||||
col[i].m_i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: cross_out_row_from_columns(unsigned k, row_strip & row) {
|
||||
for (auto & t : row) {
|
||||
cross_out_row_from_column(t.m_j, k);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: cross_out_row_from_column(unsigned col, unsigned k) {
|
||||
auto & s = m_columns[col];
|
||||
for (unsigned i = 0; i < s.size(); i++) {
|
||||
if (s[i].m_i == k) {
|
||||
s.erase(s.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> T static_matrix<T, X>:: get_elem(unsigned i, unsigned j) const { // should not be used in efficient code !!!!
|
||||
for (auto & t : m_rows[i]) {
|
||||
if (t.m_j == j) {
|
||||
return t.get_val();
|
||||
}
|
||||
}
|
||||
return numeric_traits<T>::zero();
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: scan_row_to_work_vector(unsigned i) {
|
||||
for (auto & rc : m_rows[i]) {
|
||||
m_work_pivot_vector[rc.m_j] = rc.get_val();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T, typename X> void static_matrix<T, X>:: clean_row_work_vector(unsigned i) {
|
||||
for (auto & rc : m_rows[i]) {
|
||||
m_work_pivot_vector[rc.m_j] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
template <typename T, typename X> T static_matrix<T, X>:: get_balance() const {
|
||||
T ret = zero_of_type<T>();
|
||||
for (unsigned i = 0; i < row_count(); i++) {
|
||||
ret += get_row_balance(i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
template <typename T, typename X> T static_matrix<T, X>:: get_row_balance(unsigned row) const {
|
||||
T ret = zero_of_type<T>();
|
||||
for (auto & t : m_rows[row]) {
|
||||
if (numeric_traits<T>::is_zero(t.get_val())) continue;
|
||||
T a = abs(t.get_val());
|
||||
numeric_traits<T>::log(a);
|
||||
ret += a * a;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
#ifdef LEAN_DEBUG
|
||||
template <typename T, typename X> bool static_matrix<T, X>:: col_val_equal_to_row_val() const {
|
||||
for (auto & r : m_rows) {
|
||||
for (auto & rc : r) {
|
||||
lean_assert(rc.get_val() == m_columns[rc.m_j][rc.m_offset].m_value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
|
@ -19,9 +19,8 @@
|
|||
#include "util/numerics/float.h"
|
||||
#include "util/numerics/mpfp.h"
|
||||
#include "util/lp/sparse_vector.h"
|
||||
#include "util/lp/matrix_domain.h"
|
||||
#include "util/lp/indexed_vector.h"
|
||||
|
||||
#include "util/lp/permutation_matrix.h"
|
||||
namespace lean {
|
||||
template <typename T>
|
||||
struct column_cell {
|
||||
|
|
@ -41,9 +40,7 @@ public:
|
|||
}
|
||||
const T & get_val() const { return m_value;}
|
||||
T & get_val() { return m_value;}
|
||||
void set_val(T v) {
|
||||
m_value = v;
|
||||
}
|
||||
void set_val(T v) { m_value = v; }
|
||||
private:
|
||||
T m_value;
|
||||
};
|
||||
|
|
@ -88,15 +85,7 @@ public:
|
|||
};
|
||||
|
||||
public:
|
||||
void init_row_columns(unsigned m, unsigned n) {
|
||||
lean_assert(m_rows.size() == 0 && m_columns.size() == 0);
|
||||
for (unsigned i = 0; i < m; i++){
|
||||
m_rows.push_back(row_strip());
|
||||
}
|
||||
for (unsigned j = 0; j < n; j++){
|
||||
m_columns.push_back(column_strip());
|
||||
}
|
||||
}
|
||||
void init_row_columns(unsigned m, unsigned n);
|
||||
|
||||
// constructor with no parameters
|
||||
static_matrix() {}
|
||||
|
|
@ -106,351 +95,93 @@ public:
|
|||
init_row_columns(m, n);
|
||||
}
|
||||
// constructor that copies columns of the basis from A
|
||||
static_matrix(static_matrix const &A, unsigned * basis) :
|
||||
m_work_pivot_vector(A.row_count(), numeric_traits<T>::zero()) {
|
||||
unsigned m = A.row_count();
|
||||
init_row_columns(m, m);
|
||||
while (m--) {
|
||||
for (auto & col : A.m_columns[m]){
|
||||
set(col.m_i, m, A.get_value_of_column_cell(col));
|
||||
}
|
||||
}
|
||||
}
|
||||
static_matrix(static_matrix const &A, unsigned * basis);
|
||||
|
||||
void clear() {
|
||||
m_work_pivot_vector.clear();
|
||||
m_rows.clear();
|
||||
m_columns.clear();
|
||||
#ifdef LEAN_DEBUG
|
||||
m_domain.clear();
|
||||
#endif
|
||||
}
|
||||
void clear();
|
||||
|
||||
void init_work_pivot_vector(unsigned m) {
|
||||
while (m--) m_work_pivot_vector.push_back(numeric_traits<T>::zero());
|
||||
}
|
||||
void init_work_pivot_vector(unsigned m);
|
||||
|
||||
void init_empty_matrix(unsigned m, unsigned n) {
|
||||
init_work_pivot_vector(m);
|
||||
init_row_columns(m, n);
|
||||
}
|
||||
void init_empty_matrix(unsigned m, unsigned n);
|
||||
|
||||
unsigned row_count() const { return m_rows.size(); }
|
||||
|
||||
unsigned column_count() const { return m_columns.size(); }
|
||||
template <typename L>
|
||||
L dot_product_with_row(unsigned row, const std::vector<L> & w) {
|
||||
L ret = zero_of_type<L>();
|
||||
lean_assert(row < m_rows.size());
|
||||
for (auto & it : m_rows[row]) {
|
||||
ret += w[it.m_j] * it.get_val();
|
||||
}
|
||||
return ret;
|
||||
};
|
||||
L dot_product_with_row(unsigned row, const std::vector<L> & w);;
|
||||
|
||||
unsigned lowest_row_in_column(unsigned col) {
|
||||
lean_assert(col < column_count());
|
||||
column_strip & colstrip = m_columns[col];
|
||||
lean_assert(colstrip.size() > 0);
|
||||
unsigned ret = 0;
|
||||
for (auto & t : colstrip) {
|
||||
if (t.m_i > ret) {
|
||||
ret = t.m_i;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
unsigned lowest_row_in_column(unsigned col);
|
||||
|
||||
T dot_product_with_column(const std::vector<T> & y, unsigned j) const {
|
||||
lean_assert(j < column_count());
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto & it : m_columns[j]) {
|
||||
ret += y[it.m_i] * it.m_value; // get_value_of_column_cell(it);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
T dot_product_with_column(const std::vector<T> & y, unsigned j) const;
|
||||
|
||||
void add_columns_at_the_end(unsigned delta) {
|
||||
for (unsigned i = 0; i < delta; i++)
|
||||
add_column();
|
||||
}
|
||||
void add_columns_at_the_end(unsigned delta);
|
||||
|
||||
void add_column() {
|
||||
m_columns.push_back(column_strip());
|
||||
}
|
||||
void add_column() {m_columns.push_back(column_strip()); }
|
||||
|
||||
void forget_last_columns(unsigned how_many_to_forget) {
|
||||
lean_assert(m_columns.size() >= how_many_to_forget);
|
||||
unsigned j = column_count() - 1;
|
||||
for (; how_many_to_forget > 0; how_many_to_forget--) {
|
||||
remove_last_column(j --);
|
||||
}
|
||||
}
|
||||
void forget_last_columns(unsigned how_many_to_forget);
|
||||
|
||||
void remove_last_column(unsigned j) {
|
||||
column_strip & col = m_columns.back();
|
||||
for (auto & it : col) {
|
||||
auto & row = m_rows[it.m_i];
|
||||
unsigned offset = row.size() - 1;
|
||||
for (auto row_it = row.rbegin(); row_it != row.rend(); row_it ++) {
|
||||
if (row_it->m_j == j) {
|
||||
row.erase(row.begin() + offset);
|
||||
break;
|
||||
}
|
||||
offset--;
|
||||
}
|
||||
}
|
||||
m_columns.pop_back();
|
||||
}
|
||||
void remove_last_column(unsigned j);
|
||||
|
||||
void scale_row(unsigned row, T const & alpha) {
|
||||
for (auto & t : m_rows[row]) {
|
||||
t.set_val(t.get_val() * alpha);
|
||||
m_columns[t.m_j][t.m_offset].m_value *= alpha;
|
||||
}
|
||||
}
|
||||
void scale_row(unsigned row, T const & alpha);
|
||||
|
||||
void divide_row_by_constant(unsigned row, T const & alpha) {
|
||||
for (auto & t : m_rows[row]) {
|
||||
t.set_val(t.get_val() / alpha);
|
||||
m_columns[t.m_j][t.m_offset].m_value /= alpha;
|
||||
}
|
||||
}
|
||||
void divide_row_by_constant(unsigned row, T const & alpha);
|
||||
|
||||
void scale_column(unsigned column, T const & alpha) {
|
||||
for (auto & t : m_columns[column]) {
|
||||
t.m_value *= alpha;
|
||||
auto & r = m_rows[t.m_i][t.m_offset];
|
||||
r.set_val(r.get_val() *= alpha);
|
||||
}
|
||||
}
|
||||
void scale_column(unsigned column, T const & alpha);
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
void regen_domain() {
|
||||
m_domain.clear();
|
||||
for (int i = 0; i < m_rows.size(); i++){
|
||||
for (auto & t : m_rows[i]) {
|
||||
m_domain.insert(std::make_pair(i, t.m_j));
|
||||
}
|
||||
}
|
||||
}
|
||||
void regen_domain();
|
||||
#endif
|
||||
|
||||
// offs - offset in columns
|
||||
row_cell<T> make_row_cell(unsigned row, unsigned offs, T const & val) {
|
||||
row_cell<T> r(row, offs, val);
|
||||
return r;
|
||||
return row_cell<T>(row, offs, val);
|
||||
}
|
||||
|
||||
column_cell<T> make_column_cell(unsigned column, unsigned offset, T const & val) {
|
||||
column_cell<T> r(column, offset, val);
|
||||
return r;
|
||||
return column_cell<T>(column, offset, val);
|
||||
}
|
||||
|
||||
void set(unsigned row, unsigned col, T const & val) {
|
||||
if (numeric_traits<T>::is_zero(val)) return;
|
||||
lean_assert(row < row_count() && col < column_count());
|
||||
#ifdef LEAN_DEBUG
|
||||
pair<unsigned, unsigned> p(row, col);
|
||||
lean_assert(m_domain.find(p) == m_domain.end());
|
||||
m_domain.insert(p);
|
||||
#endif
|
||||
auto & r = m_rows[row];
|
||||
unsigned offs_in_cols = m_columns[col].size();
|
||||
m_columns[col].push_back(make_column_cell(row, r.size(), val));
|
||||
r.push_back(make_row_cell(col, offs_in_cols, val));
|
||||
}
|
||||
void set(unsigned row, unsigned col, T const & val);
|
||||
|
||||
ref operator()(unsigned row, unsigned col) { return ref(*this, row, col); }
|
||||
|
||||
std::set<pair<unsigned, unsigned>> get_domain() {
|
||||
std::set<pair<unsigned, unsigned>> ret;
|
||||
for (unsigned i = 0; i < m_rows.size(); i++) {
|
||||
for (auto it : m_rows[i]) {
|
||||
ret.insert(std::make_pair(i, it.m_j));
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
std::set<pair<unsigned, unsigned>> get_domain();
|
||||
|
||||
void copy_column_to_vector (unsigned j, indexed_vector<T> & v) const;
|
||||
|
||||
void copy_column_to_vector (unsigned j, std::vector<T> & v) const;
|
||||
|
||||
void add_column_to_vector (const T & a, unsigned j, T * v) const;
|
||||
T get_max_abs_in_row(unsigned row) const;
|
||||
|
||||
void copy_column_to_vector (unsigned j, indexed_vector<T> & v) const {
|
||||
lean_assert(j < m_columns.size());
|
||||
for (auto & it : m_columns[j]) {
|
||||
if (!is_zero(it.m_value))
|
||||
v.set_value(it.m_value, it.m_i);
|
||||
}
|
||||
}
|
||||
void copy_column_to_vector (unsigned j, std::vector<T> & v) const {
|
||||
v.resize(row_count(), numeric_traits<T>::zero());
|
||||
for (auto & it : m_columns[j]) {
|
||||
if (!is_zero(it.m_value))
|
||||
v[it.m_i]=it.m_value;
|
||||
}
|
||||
}
|
||||
T get_min_abs_in_row(unsigned row) const;
|
||||
T get_max_abs_in_column(unsigned column) const;
|
||||
|
||||
void add_column_to_vector (const T & a, unsigned j, T * v) const {
|
||||
for (auto & it : m_columns[j]) {
|
||||
v[it.m_i] += a * it.m_value;
|
||||
}
|
||||
}
|
||||
|
||||
T get_max_abs_in_row(unsigned row) const {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto & t : m_rows[row]) {
|
||||
T a = abs(t.get_val());
|
||||
if (a > ret) {
|
||||
ret = a;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
T get_min_abs_in_row(unsigned row) const {
|
||||
bool first_time = true;
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto & t : m_rows[row]) {
|
||||
T a = abs(t.get_val());
|
||||
if (first_time) {
|
||||
ret = a;
|
||||
first_time = false;
|
||||
} else if (a < ret) {
|
||||
ret = a;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
|
||||
T get_max_abs_in_column(unsigned column) const {
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto & t : m_columns[column]) {
|
||||
T a = abs(t.m_value);
|
||||
if (a > ret) {
|
||||
ret = a;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
T get_min_abs_in_column(unsigned column) const {
|
||||
bool first_time = true;
|
||||
T ret = numeric_traits<T>::zero();
|
||||
for (auto & t : m_columns[column]) {
|
||||
T a = abs(t.m_value);
|
||||
if (first_time) {
|
||||
first_time = false;
|
||||
ret = a;
|
||||
} else if (a < ret) {
|
||||
ret = a;
|
||||
}
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
T get_min_abs_in_column(unsigned column) const;
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
void check_consistency() {
|
||||
std::unordered_map<std::pair<unsigned, unsigned>, T> by_rows;
|
||||
for (int i = 0; i < m_rows.size(); i++){
|
||||
for (auto & t : m_rows[i]) {
|
||||
pair<unsigned, unsigned> p(i, t.m_j);
|
||||
lean_assert(by_rows.find(p) == by_rows.end());
|
||||
by_rows[p] = t.get_val();
|
||||
}
|
||||
}
|
||||
std::unordered_map<pair<unsigned, unsigned>, T> by_cols;
|
||||
for (int i = 0; i < m_columns.size(); i++){
|
||||
for (auto & t : m_columns[i]) {
|
||||
pair<unsigned, unsigned> p(t.m_i, i);
|
||||
lean_assert(by_cols.find(p) == by_cols.end());
|
||||
by_cols[p] = get_value_of_column_cell(t);
|
||||
}
|
||||
}
|
||||
lean_assert(by_rows.size() == by_cols.size());
|
||||
|
||||
for (auto & t : by_rows) {
|
||||
auto ic = by_cols.find(t.first);
|
||||
if (ic == by_cols.end()){
|
||||
std::cout << "rows have pair (" << t.first.first <<"," << t.first.second
|
||||
<< "), but columns don't " << std::endl;
|
||||
}
|
||||
lean_assert(ic != by_cols.end());
|
||||
lean_assert(t.second == ic->second);
|
||||
}
|
||||
}
|
||||
void check_consistency();
|
||||
#endif
|
||||
|
||||
|
||||
void cross_out_row(unsigned k) {
|
||||
#ifdef LEAN_DEBUG
|
||||
check_consistency();
|
||||
#endif
|
||||
cross_out_row_from_columns(k, m_rows[k]);
|
||||
fix_row_indices_in_each_column_for_crossed_row(k);
|
||||
m_rows.erase(m_rows.begin() + k);
|
||||
#ifdef LEAN_DEBUG
|
||||
regen_domain();
|
||||
check_consistency();
|
||||
#endif
|
||||
}
|
||||
void cross_out_row(unsigned k);
|
||||
|
||||
//
|
||||
void fix_row_indices_in_each_column_for_crossed_row(unsigned k) {
|
||||
for (unsigned j = 0; j < m_columns.size(); j++) {
|
||||
auto & col = m_columns[j];
|
||||
for (int i = 0; i < col.size(); i++) {
|
||||
if (col[i].m_i > k) {
|
||||
col[i].m_i--;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void fix_row_indices_in_each_column_for_crossed_row(unsigned k);
|
||||
|
||||
void cross_out_row_from_columns(unsigned k, row_strip & row) {
|
||||
for (auto & t : row) {
|
||||
cross_out_row_from_column(t.m_j, k);
|
||||
}
|
||||
}
|
||||
void cross_out_row_from_columns(unsigned k, row_strip & row);
|
||||
|
||||
void cross_out_row_from_column(unsigned col, unsigned k) {
|
||||
auto & s = m_columns[col];
|
||||
for (unsigned i = 0; i < s.size(); i++) {
|
||||
if (s[i].m_i == k) {
|
||||
s.erase(s.begin() + i);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
void cross_out_row_from_column(unsigned col, unsigned k);
|
||||
|
||||
T get_elem(unsigned i, unsigned j) const { // should not be used in efficient code !!!!
|
||||
for (auto & t : m_rows[i]) {
|
||||
if (t.m_j == j) {
|
||||
return t.get_val();
|
||||
}
|
||||
}
|
||||
return numeric_traits<T>::zero();
|
||||
}
|
||||
T get_elem(unsigned i, unsigned j) const;
|
||||
|
||||
|
||||
unsigned number_of_non_zeroes_in_column(unsigned j) const {
|
||||
return m_columns[j].size();
|
||||
}
|
||||
unsigned number_of_non_zeroes_in_column(unsigned j) const { return m_columns[j].size(); }
|
||||
|
||||
unsigned number_of_non_zeroes_in_row(unsigned i) const {
|
||||
return m_rows[i].number_of_non_zeros();
|
||||
}
|
||||
unsigned number_of_non_zeroes_in_row(unsigned i) const { return m_rows[i].number_of_non_zeros(); }
|
||||
|
||||
void scan_row_to_work_vector(unsigned i) {
|
||||
for (auto & rc : m_rows[i]) {
|
||||
m_work_pivot_vector[rc.m_j] = rc.get_val();
|
||||
}
|
||||
}
|
||||
void scan_row_to_work_vector(unsigned i);
|
||||
|
||||
void clean_row_work_vector(unsigned i) {
|
||||
for (auto & rc : m_rows[i]) {
|
||||
m_work_pivot_vector[rc.m_j] = numeric_traits<T>::zero();
|
||||
}
|
||||
}
|
||||
void clean_row_work_vector(unsigned i);
|
||||
|
||||
|
||||
#ifdef LEAN_DEBUG
|
||||
|
|
@ -460,35 +191,12 @@ public:
|
|||
virtual void set_number_of_columns(unsigned /*n*/) { }
|
||||
#endif
|
||||
|
||||
T get_max_val_in_row(unsigned i) const {
|
||||
lean_unreachable();
|
||||
}
|
||||
T get_max_val_in_row(unsigned i) const { lean_unreachable(); }
|
||||
|
||||
T get_balance() const {
|
||||
T ret = zero_of_type<T>();
|
||||
for (unsigned i = 0; i < row_count(); i++) {
|
||||
ret += get_row_balance(i);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
T get_balance() const;
|
||||
|
||||
T get_row_balance(unsigned row) const {
|
||||
T ret = zero_of_type<T>();
|
||||
for (auto & t : m_rows[row]) {
|
||||
if (numeric_traits<T>::is_zero(t.get_val())) continue;
|
||||
T a = abs(t.get_val());
|
||||
numeric_traits<T>::log(a);
|
||||
ret += a * a;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
bool col_val_equal_to_row_val() const {
|
||||
for (auto & r : m_rows) {
|
||||
for (auto & rc : r) {
|
||||
lean_assert(rc.get_val() == m_columns[rc.m_j][rc.m_offset].m_value);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
T get_row_balance(unsigned row) const;
|
||||
|
||||
bool col_val_equal_to_row_val() const;
|
||||
};
|
||||
}
|
||||
|
|
|
|||
72
src/util/lp/static_matrix_instances.cpp
Normal file
72
src/util/lp/static_matrix_instances.cpp
Normal file
|
|
@ -0,0 +1,72 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#include "util/lp/static_matrix.cpp"
|
||||
#include "util/lp/lp_core_solver_base.h"
|
||||
#include "util/lp/lp_dual_core_solver.h"
|
||||
#include "util/lp/lp_dual_simplex.h"
|
||||
#include "util/lp/lp_primal_core_solver.h"
|
||||
#include "util/lp/scaler.h"
|
||||
#include "util/lp/lar_solver.h"
|
||||
namespace lean {
|
||||
template double static_matrix<double, double>::dot_product_with_row<double>(unsigned int, std::vector<double, std::allocator<double> > const&);
|
||||
template mpq static_matrix<mpq, mpq>::dot_product_with_row<mpq>(unsigned int, std::vector<mpq, std::allocator<mpq> > const&);
|
||||
template numeric_pair<mpq> static_matrix<mpq, numeric_pair<mpq> >::dot_product_with_row<numeric_pair<mpq> >(unsigned int, std::vector<numeric_pair<mpq>, std::allocator<numeric_pair<mpq> > > const&);
|
||||
template void static_matrix<double, double>::add_column_to_vector(double const&, unsigned int, double*) const;
|
||||
template void static_matrix<double, double>::add_columns_at_the_end(unsigned int);
|
||||
template void static_matrix<double, double>::clear();
|
||||
#ifdef LEAN_DEBUG
|
||||
template bool static_matrix<double, double>::col_val_equal_to_row_val() const;
|
||||
#endif
|
||||
template void static_matrix<double, double>::copy_column_to_vector(unsigned int, indexed_vector<double>&) const;
|
||||
template void static_matrix<double, double>::copy_column_to_vector(unsigned int, std::vector<double, std::allocator<double> >&) const;
|
||||
template void static_matrix<double, double>::divide_row_by_constant(unsigned int, double const&);
|
||||
template double static_matrix<double, double>::dot_product_with_column(std::vector<double, std::allocator<double> > const&, unsigned int) const;
|
||||
template double static_matrix<double, double>::get_balance() const;
|
||||
template std::set<pair<unsigned, unsigned>> static_matrix<double, double>::get_domain();
|
||||
template double static_matrix<double, double>::get_elem(unsigned int, unsigned int) const;
|
||||
template double static_matrix<double, double>::get_max_abs_in_column(unsigned int) const;
|
||||
template double static_matrix<double, double>::get_min_abs_in_column(unsigned int) const;
|
||||
template double static_matrix<double, double>::get_min_abs_in_row(unsigned int) const;
|
||||
template void static_matrix<double, double>::init_empty_matrix(unsigned int, unsigned int);
|
||||
template void static_matrix<double, double>::init_row_columns(unsigned int, unsigned int);
|
||||
template static_matrix<double, double>::ref & static_matrix<double, double>::ref::operator=(double const&);
|
||||
template void static_matrix<double, double>::scale_column(unsigned int, double const&);
|
||||
template void static_matrix<double, double>::scale_row(unsigned int, double const&);
|
||||
template void static_matrix<double, double>::set(unsigned int, unsigned int, double const&);
|
||||
template static_matrix<double, double>::static_matrix(unsigned int, unsigned int);
|
||||
template void static_matrix<mpq, mpq>::add_column_to_vector(mpq const&, unsigned int, mpq*) const;
|
||||
template void static_matrix<mpq, mpq>::add_columns_at_the_end(unsigned int);
|
||||
#ifdef LEAN_DEBUG
|
||||
template bool static_matrix<mpq, mpq>::col_val_equal_to_row_val() const;
|
||||
#endif
|
||||
template void static_matrix<mpq, mpq>::copy_column_to_vector(unsigned int, indexed_vector<mpq>&) const;
|
||||
template void static_matrix<mpq, mpq>::divide_row_by_constant(unsigned int, mpq const&);
|
||||
template mpq static_matrix<mpq, mpq>::dot_product_with_column(std::vector<mpq, std::allocator<mpq> > const&, unsigned int) const;
|
||||
template mpq static_matrix<mpq, mpq>::get_balance() const;
|
||||
template mpq static_matrix<mpq, mpq>::get_elem(unsigned int, unsigned int) const;
|
||||
template mpq static_matrix<mpq, mpq>::get_max_abs_in_column(unsigned int) const;
|
||||
template mpq static_matrix<mpq, mpq>::get_max_abs_in_row(unsigned int) const;
|
||||
template double static_matrix<double, double>::get_max_abs_in_row(unsigned int) const;
|
||||
template mpq static_matrix<mpq, mpq>::get_min_abs_in_column(unsigned int) const;
|
||||
template mpq static_matrix<mpq, mpq>::get_min_abs_in_row(unsigned int) const;
|
||||
template void static_matrix<mpq, mpq>::init_row_columns(unsigned int, unsigned int);
|
||||
template static_matrix<mpq, mpq>::ref& static_matrix<mpq, mpq>::ref::operator=(mpq const&);
|
||||
template void static_matrix<mpq, mpq>::scale_column(unsigned int, mpq const&);
|
||||
template void static_matrix<mpq, mpq>::scale_row(unsigned int, mpq const&);
|
||||
template void static_matrix<mpq, mpq>::set(unsigned int, unsigned int, mpq const&);
|
||||
|
||||
template static_matrix<mpq, mpq>::static_matrix(unsigned int, unsigned int);
|
||||
#ifdef LEAN_DEBUG
|
||||
template bool static_matrix<mpq, numeric_pair<mpq> >::col_val_equal_to_row_val() const;
|
||||
#endif
|
||||
template void static_matrix<mpq, numeric_pair<mpq> >::copy_column_to_vector(unsigned int, indexed_vector<mpq>&) const;
|
||||
template mpq static_matrix<mpq, numeric_pair<mpq> >::dot_product_with_column(std::vector<mpq, std::allocator<mpq> > const&, unsigned int) const;
|
||||
template mpq static_matrix<mpq, numeric_pair<mpq> >::get_elem(unsigned int, unsigned int) const;
|
||||
template void static_matrix<mpq, numeric_pair<mpq> >::init_empty_matrix(unsigned int, unsigned int);
|
||||
template void static_matrix<mpq, numeric_pair<mpq> >::set(unsigned int, unsigned int, mpq const&);
|
||||
}
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
|
||||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "util/debug.h"
|
||||
#include "util/numerics/numeric_traits.h"
|
||||
#include "util/numerics/xnumeral.h"
|
||||
|
||||
#include <set>
|
||||
#include "util/lp/lp_settings.h"
|
||||
#include "util/lp/sparse_matrix.h"
|
||||
#include "util/lp/sparse_vector.h"
|
||||
#include "util/lp/pivot_chooser_base.h"
|
||||
|
||||
|
||||
namespace lean {
|
||||
// following "Computing Sparse LU Factorizations for Large-Scale Linear Programming Bases", by Suhl, Suhl
|
||||
template <typename T, typename X>
|
||||
class suhl_pivot_chooser : public pivot_chooser_base {
|
||||
// m_U is a square matrix
|
||||
sparse_matrix<T, X> & m_U;
|
||||
lp_settings const & m_settings;
|
||||
public:
|
||||
// constructor
|
||||
suhl_pivot_chooser(sparse_matrix<T, X> & U, lp_settings const & settings): m_U(U), m_settings(settings) {
|
||||
lean_assert(U.row_count() == U.column_count());
|
||||
}
|
||||
|
||||
bool get_column_singleton(unsigned * i, unsigned *j) {
|
||||
return m_U.find_column_singleton(i, j);
|
||||
}
|
||||
|
||||
bool get_row_singleton(unsigned* i, unsigned *j) {
|
||||
return m_U.find_row_singleton(i, j);
|
||||
}
|
||||
|
||||
void pivot_update_kernel(unsigned i, unsigned j) {
|
||||
}
|
||||
|
||||
bool get_pivot_not_adjusted(unsigned *i, unsigned * j) {
|
||||
if (get_column_singleton(i, j)) {
|
||||
return true;
|
||||
}
|
||||
if (get_row_singleton(i, j)) {
|
||||
return true;
|
||||
}
|
||||
return m_U.get_non_singleton_pivot(i, j, m_settings.depth_of_rook_search, T(m_settings.c_partial_pivoting));
|
||||
}
|
||||
|
||||
int get_pivot(unsigned i, unsigned * j) {
|
||||
if (get_pivot_not_adjusted(&i, j)) {
|
||||
*j = m_U.adjust_column_inverse(*j);
|
||||
return m_U.adjust_row_inverse(i);
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
};
|
||||
}
|
||||
26
src/util/lp/tail_matrix.h
Normal file
26
src/util/lp/tail_matrix.h
Normal file
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include "util/lp/indexed_vector.h"
|
||||
#include "util/lp/matrix.h"
|
||||
// These matrices appear at the end of the list
|
||||
|
||||
namespace lean {
|
||||
template <typename T, typename X>
|
||||
class tail_matrix
|
||||
#ifdef LEAN_DEBUG
|
||||
: public matrix<T, X>
|
||||
#endif
|
||||
{
|
||||
public:
|
||||
virtual void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) = 0;
|
||||
virtual void apply_from_left(std::vector<X> & w, lp_settings & settings) = 0;
|
||||
virtual void apply_from_right(std::vector<T> & w) = 0;
|
||||
virtual ~tail_matrix() {}
|
||||
};
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue