feat(lp): add LP solver and incremental LU factorization

Signed-off-by: Lev Nachmanson <levnach@microsoft.com>
This commit is contained in:
Lev Nachmanson 2013-10-14 10:57:55 -07:00 committed by Leonardo de Moura
parent 04eaf184a9
commit fbb3ed8911
65 changed files with 42468 additions and 6 deletions

View file

@ -412,6 +412,7 @@ add_subdirectory(tests/shell)
if (NOT("${CROSS_COMPILE}" MATCHES "ON"))
add_subdirectory(tests/shared)
endif()
add_subdirectory(tests/util/lp)
# Include style check
if (NOT(${CMAKE_SYSTEM_NAME} MATCHES "Windows"))

View file

@ -0,0 +1,6 @@
add_executable(lp_tst lp.cpp $<TARGET_OBJECTS:util> $<TARGET_OBJECTS:numerics>)
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>)
target_link_libraries(double_compare ${EXTRA_LIBS})
add_test(double_compare ${CMAKE_CURRENT_BINARY_DIR}/double_compare)

View file

@ -0,0 +1,136 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include <unordered_map>
#include <vector>
#include <string>
#include <set>
#include <iostream>
using namespace std;
namespace lean {
class argument_parser {
unordered_map<string, string> m_options;
unordered_map<string, string> m_options_with_after_string;
std::set<string> m_used_options;
unordered_map<string, string> m_used_options_with_after_string;
vector<string> m_free_args;
vector<string> m_args;
public:
string m_error_message;
argument_parser(unsigned argn, char * const* args) {
for (unsigned i = 0; i < argn; i++) {
m_args.push_back(string(args[i]));
}
}
void add_option(string s) {
add_option_with_help_string(s, "");
}
void add_option_with_help_string(string s, string help_string) {
m_options[s]=help_string;
}
void add_option_with_after_string(string s) {
add_option_with_after_string_with_help(s, "");
}
void add_option_with_after_string_with_help(string s, string help_string) {
m_options_with_after_string[s]=help_string;
}
bool parse() {
bool status_is_ok = true;
for (unsigned i = 0; i < m_args.size(); i++) {
string ar = m_args[i];
if (m_options.find(ar) != m_options.end() )
m_used_options.insert(ar);
else if (m_options_with_after_string.find(ar) != m_options_with_after_string.end()) {
if (i == m_args.size() - 1) {
m_error_message = "Argument is missing after "+ar;
return false;
}
i++;
m_used_options_with_after_string[ar] = m_args[i];
} else {
if (starts_with(ar, "-") || starts_with(ar, "//")) {
m_error_message = "Unknown option " + ar;
status_is_ok = false;
}
m_free_args.push_back(ar);
}
}
return status_is_ok;
}
bool contains(unordered_map<string, string> & m, string s) {
return m.find(s) != m.end();
}
bool contains(std::set<string> & m, string s) {
return m.find(s) != m.end();
}
bool option_is_used(string option) {
return contains(m_used_options, option) || contains(m_used_options_with_after_string, option);
}
string get_option_value(string option) {
auto t = m_used_options_with_after_string.find(option);
if (t != m_used_options_with_after_string.end()){
return t->second;
}
return string();
}
bool starts_with(string s, char const * prefix) {
return starts_with(s, string(prefix));
}
bool starts_with(string s, string prefix) {
return s.substr(0, prefix.size()) == prefix;
}
string usage_string() {
string ret = "";
vector<string> unknown_options;
for (auto t : m_free_args) {
if (starts_with(t, "-") || starts_with(t, "\\")) {
unknown_options.push_back(t);
}
}
if (unknown_options.size()) {
ret = "Unknown options:";
}
for (auto unknownOption : unknown_options) {
ret += unknownOption;
ret += ",";
}
ret += "\n";
ret += "Usage:\n";
for (auto allowed_option : m_options)
ret += allowed_option.first + " " + (allowed_option.second.size() == 0 ? string("") : string("/") + allowed_option.second) + string("\n");
for (auto s : m_options_with_after_string) {
ret += s.first + " " + (s.second.size() == 0? " \"option value\"":("\""+ s.second+"\"")) + "\n";
}
return ret;
}
void print() {
for (string s : m_used_options) {
cout << s << endl;
}
for (auto & t : m_used_options_with_after_string) {
cout << t.first << " " << t.second << endl;
}
}
};
}

View file

@ -0,0 +1,140 @@
#!/bin/bash
if [ $# -ne 2 -a $# -ne 3 ]; then
echo "Usage: compare_with_glpk.sh --min [--mpq] mps_file_name"
echo "or compare_with_glpk.sh --max [--mpq] mps_file_name"
exit 1
fi
minmax_option=$1
if [ $minmax_option != "--min" -a $minmax_option != "--max" ]; then
echo "Usage: compare_with_glpk.sh --min [--mpq] mps_file_name"
echo "or compare_with_glpk.sh --max [--mpq] mps_file_name"
exit 1
fi
if [ $minmax_option == "--min" ]; then
prefix="_min";
else
if [ $minmax_option == "--max" ]; then
prefix="_max"
else
echo "Usage: compare_with_glpk.sh minmax_option mps_file_name"
echo "where minmax_option can be --min or --max"
exit 1
fi
fi
mpq_option=""
if [ $# -eq 3 ]; then
mpq_option=$2
if [ $mpq_option != "--mpq" ]; then
echo "Usage: compare_with_glpk.sh --min [--mpq] mps_file_name"
echo "or compare_with_glpk.sh --max [--mpq] mps_file_name"
exit 1
fi
mps_input_file=$3
else
mps_input_file=$2
fi
filename=$(basename "$mps_input_file")
extension="${filename##*.}"
filename="${filename%.*}"
#testpath="${mps_input_file%/*}"
testpath="/tmp"
glpsol_output="${testpath}""/""${filename}"$prefix".out"
lp_tst_output="${testpath}""/""${filename}"$prefix".lp_tst.out""$mpq_option"
lp_tst=$HOME"/projects/lean/build/release/tests/util/lp/lp_tst"
cost_compare=$HOME"/projects/lean/build/release/tests/util/lp/double_compare"
if [ $# -ne 3 ]; then
glpsol --nointopt --nomip $minmax_option --tmlim 60 -o "${glpsol_output}" "${mps_input_file}" > /dev/null
status=$?
if [ $status -ne 0 ]; then
echo "glpsol failed"
exit 2
fi
fi
function get_status_from_output() {
local __resultvar=$1
local status_str=$(grep Status $2)
local tokens=( $status_str )
local myresult=${tokens[1]}
eval $__resultvar="'$myresult'"
}
function get_cost_from_glpout() {
local __resultvar=$1
local status_str=$(grep Objective "${glpsol_output}")
local tokens=( $status_str )
local myresult=${tokens[3]}
eval $__resultvar="'$myresult'"
}
get_status_from_output glp_status $glpsol_output
get_cost_from_glpout glp_cost
# ~/projects/lean/build/debug/tests/util/lp/double_compare 24501.2549 2.450125496e+04 0.01
# compare_return=$?
# if [ $compare_return -eq 2 ]; then
# echo "Wrong usage of double_compare";
# else
# if [ $compare_return -ne 0 ]; then
# echo "the difference is too large"
# else
# echo "the difference is OK"
# fi
# fi
if [ $# -eq 2 ] ; then
$lp_tst --file $mps_input_file --time_limit 60 $minmax_option > $lp_tst_output
else
$lp_tst --file $mps_input_file --time_limit 12000 $minmax_option "--mpq" > $lp_tst_output
fi
status=$?
if [ $status -ne 0 ]; then
echo "lp_tst failed"
exit 1
fi
get_status_from_output lp_tst_status $lp_tst_output
if [ $glp_status != $lp_tst_status ]; then
if [ "$glp_status" = UNDEFINED ] && [ "$lp_tst_status" = UNBOUNDED ]; then
exit 0
else
if [ "$glp_status" = UNDEFINED ] && [ "$lp_tst_status" = INFEASIBLE ]; then
exit 0
else
echo "glpsol and lp_tst disagree: glpsol status is ""$glp_status"
echo "but lp_tst status is ""$lp_tst_status"
exit 1
fi
fi
fi
if [ $glp_status != OPTIMAL ]; then
cout "exiting 0"
exit 0
fi
function get_cost_from_lp_tst_out() {
local __resultvar=$1
local cost_str=$(grep cost "${lp_tst_output}"| tail -1)
local tokens=( $cost_str )
local myresult=${tokens[2]}
eval $__resultvar="'$myresult'"
}
get_cost_from_lp_tst_out lp_tst_cost
$cost_compare $lp_tst_cost $glp_cost
status=$?
if [ $status -ne 0 ]; then
echo "the costs differ too much"
echo "glp cost is ""$glp_cost"", but lp_tst_cost is ""$lp_tst_cost"
exit 1
fi
exit 0

View file

@ -0,0 +1,51 @@
/*
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
// This file is needed for testing. It can be called with two or three
// strings represinting double numbers. If the absolute value of the
// difference between the first two numbers is less than the third
// one, then 0 is returned. Otherwise 1 is returned. If the number of
// arguments is two and the numbers are small then 0 returned. If the
// numbers are not too small then 0 returned when the absolute value
// of their difference is smaller than one percent of their maximal
// abs value. If the number parameters is not 2 or 3, or the arguments
// cannot be converted to doubles then -1 is returned
#include <iostream>
using namespace std;
#include <stdlib.h>
#include <math.h>
int main(int argn, char * const * argv) {
if (argn != 4 && argn != 3) {
cout << "Usage is \"n1 n2 [n3]" << endl;
return 2;
}
double a = atof(argv[1]);
double b = atof(argv[2]);
if (argn == 4) {
double c = atof(argv[3]);
if (fabs(a - b) < c) {
return 0;
}
return 1;
}
// argn == 3
double maxval = max(fabs(a), fabs(b));
if (maxval < 0.000001) {
return 0;
}
double one_percent = maxval / 100;
if (fabs(a - b) > one_percent) {
return 1;
}
return 0;
}

View file

@ -0,0 +1,44 @@
/*
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#include "util/ascii.h"
#include "util/debug.h"
#include "util/trace.h"
#include "util/serializer.h"
#include "util/thread_script_state.h"
#include "util/script_state.h"
#include "util/name.h"
#include "util/name_generator.h"
#include "util/lean_path.h"
#include "util/thread.h"
#include "util/memory_pool.h"
namespace lean {
void initialize_util_module() {
initialize_debug();
initialize_trace();
initialize_serializer();
initialize_thread();
initialize_ascii();
initialize_thread_script_state();
initialize_script_state();
initialize_name();
initialize_name_generator();
initialize_lean_path();
}
void finalize_util_module() {
finalize_lean_path();
finalize_name_generator();
finalize_name();
finalize_script_state();
finalize_thread_script_state();
finalize_ascii();
finalize_thread();
finalize_serializer();
finalize_trace();
finalize_debug();
}
}

View file

@ -0,0 +1,12 @@
/*
Copyright (c) 2014 Microsoft Corporation. All rights reserved.
Released under Apache 2.0 license as described in the file LICENSE.
Author: Lev Nachmanson
*/
#pragma once
namespace lean {
void initialize_util_module();
void finalize_util_module();
}

36
src/tests/util/lp/l0.mps Normal file
View file

@ -0,0 +1,36 @@
NAME l0 (135 a)
ROWS
N Z
E R1
E R2
COLUMNS
X1 R1 1.0
X1 Z -3.0
X2 Z -1.0 R2 1.0
X3 Z -1.0 R1 3 R2 -2
X4 Z 2 R1 1 R2 -1
X5 Z -1 R1 -5 R2 4
X6 Z 1 R1 -2 R2 1
X7 Z 1 R1 4 R2 -3
X8 Z -4 R1 -6 R2 5
RHS
RHS1 R1 7
RHS1 R2 -3
BOUNDS
LO BND1 X1 0
UP BND1 X1 8
LO BND1 X2 0
UP BND1 X2 6
LO BND1 X3 0
UP BND1 X3 4
LO BND1 X4 0
UP BND1 X4 15
LO BND1 X5 0
UP BND1 X5 2
LO BND1 X6 0
UP BND1 X6 10
LO BND1 X7 0
UP BND1 X7 10
LO BND1 X8 0
UP BND1 X8 3
ENDATA

View file

@ -0,0 +1,61 @@
NAME l0
ROWS
N Z
E R1
E R4R
E R3
E R4
E R11
E R1R
E R2R
E R3R
E R11R
E R2
COLUMNS
X1 R1 1.0 R11 1.0
X1 Z -3.0
X2 Z -1.0 R4R 1.0 R3 1.0 R4 1.0
X3 Z -1.0 R1 3 R4R -2 R3 -2 R4 -2
X4 Z 2 R1 1 R11 1 R4R -1 R3 -1 R4 -1
X5 Z -1 R1 -5 R11 -5 R4R 4 R3 4 R4 4
X6 Z 1 R1 -2 R11 -2 R4R 1 R3 1 R4 1
X7 Z 1 R1 4 R11 4 R4R -3 R3 -3 R4 -3
X8 Z -4 R1 -6 R11 -6 R4R 5 R3 5 R4 5
X1 R1R 1.0 R11R 1.0
X1 Z -3.0
X2 Z -1.0 R2R 1.0 R3R 1.0 R2 1.0
X3 Z -1.0 R1R 3 R2R -2 R3R -2 R2 -2
X4 Z 2 R1R 1 R11R 1 R2R -1 R3R -1 R2 -1
X5 Z -1 R1R -5 R11R -5 R2R 4 R3R 4 R2 4
X6 Z 1 R1R -2 R11R -2 R2R 1 R3R 1 R2 1
X7 Z 1 R1R 4 R11R 4 R2R -3 R3R -3 R2 -3
X8 Z -4 R1R -6 R11R -6 R2R 5 R3R 5 R2 5
RHS
RHS1 R1 7
RHS1 R4R -3
RHS1 R3 -3
RHS1 R4 -3
RHS1 R11 7
RHS1 R1R 7
RHS1 R2R -3
RHS1 R3R -3
RHS1 R2 -3
RHS1 R11R 7
BOUNDS
LO BND1 X1 0
UP BND1 X1 8
LO BND1 X2 0
UP BND1 X2 6
LO BND1 X3 0
UP BND1 X3 4
LO BND1 X4 0
UP BND1 X4 15
LO BND1 X5 0
UP BND1 X5 2
LO BND1 X6 0
UP BND1 X6 10
LO BND1 X7 0
UP BND1 X7 10
LO BND1 X8 0
UP BND1 X8 3
ENDATA

25
src/tests/util/lp/l1.mps Normal file
View file

@ -0,0 +1,25 @@
*SOURCE Chvatal, page 135(b)
NAME l1
ROWS
N Z
L R1
L R2
L R3
L R4
COLUMNS
X1 Z 3 R1 1 R2 1 R3 1 R4 1
X2 Z 1 R1 4 R2 3 R3 2 R4 3
X3 Z 4 R1 3 R2 -1 R3 3 R4 1
X4 Z 2 R1 3 R2 1 R3 2 R4 1
RHS
RHS1 R1 2
RHS1 R2 -2
RHS1 R3 3
RHS1 R4 -3
BOUNDS
FR BND1 X1
FR BND1 X2
LO BND1 X3 0
LO BND1 X4 0
ENDATA

27
src/tests/util/lp/l2.mps Normal file
View file

@ -0,0 +1,27 @@
NAME l2 ( from chvatal's book page 135, Problem c. )
ROWS
N Z
L R1
L R2
E R3
COLUMNS
X1 Z 5 R1 3 R2 -5 R3 1
X2 Z 2 R1 1 R2 4 R3 1
X3 Z -3 R1 -4 R2 2 R3 2
X4 Z 3 R1 2 R2 -3 R3 1
X5 Z 6 R1 5 R2 2 R3 1
X6 Z 1 R1 1 R2 3 R3 2
RHS
RHS1 R1 3
RHS1 R2 25
RHS1 R3 4
BOUNDS
LO BND1 X1 0
LO BND1 X2 2
UP BND1 X2 10
UP BND1 X3 0
LO BND1 X4 -3
UP BND1 X4 3
FR BND1 X5
FR BND1 X6
ENDATA

28
src/tests/util/lp/l3.mps Normal file
View file

@ -0,0 +1,28 @@
NAME l3 ( from chvatal's book page 135, Problem (d) the problem is infeasible )
ROWS
N Z
E R1
E R2
E R3
E R4
L R5
COLUMNS
X1 Z 8 R1 3 R2 -1 R3 1 R4 2 R5 2
X2 Z 6 R1 2 R2 2 R3 0 R4 -3 R5 0
X3 Z -2 R1 -1 R2 -2 R3 0 R4 2 R5 1
X4 Z 6 R1 3 R2 0 R3 -3 R4 3 R5 0
X5 Z -3 R1 2 R2 0 R3 2 R4 0 R5 -1
RHS
RHS1 R1 7
RHS1 R2 10
RHS1 R3 7
RHS1 R4 -10
RHS1 R5 -6
BOUNDS
LO BND1 X1 0
LO BND1 X2 2
FR BND1 X3
FR BND1 X4
FR BND1 X5
ENDATA

View file

@ -0,0 +1,27 @@
NAME l3 ( from chvatal's book page 135, Problem d. )
ROWS
N Z
E R1
E R2
E R3
E R4
L R5
COLUMNS
X1 Z 8 R1 3 R2 -1 R3 1 R4 2 R5 2
X2 Z -6 R1 -2 R2 -2 R3 0 R4 3 R5 0
X3 Z -2 R1 -1 R2 -2 R3 0 R4 2 R5 1
X4 Z 6 R1 3 R2 0 R3 -3 R4 3 R5 0
X5 Z -3 R1 2 R2 0 R3 2 R4 0 R5 -1
RHS
RHS1 R1 7
RHS1 R2 10
RHS1 R3 7
RHS1 R4 -10
RHS1 R5 -6
BOUNDS
LO BND1 X1 0
UP BND1 X2 -2
FR BND1 X3
FR BND1 X4
FR BND1 X5
ENDATA

22
src/tests/util/lp/l4.mps Normal file
View file

@ -0,0 +1,22 @@
NAME l3 ( from chvatal's book page 135, Problem e. )
ROWS
N Z
E R1
E R2
E R3
E R4
COLUMNS
X1 Z 1 R1 3 R2 4 R3 2 R4 1
X2 Z 0 R1 5 R2 6 R3 4 R4 1
X3 Z 0 R1 3 R2 5 R3 1 R4 2
X4 Z 0 R1 2 R2 3 R3 1 R4 1
X5 Z 0 R1 1 R2 5 R3 -3 R4 4
X6 Z 0 R1 2 R2 5 R3 -1 R4 3
X7 Z 0 R1 1 R2 6 R3 -4 R4 5
RHS
RHS1 R1 6
RHS1 R2 11
RHS1 R3 1
RHS1 R4 5
BOUNDS
ENDATA

View file

@ -0,0 +1,22 @@
NAME l3 ( from chvatal's book page 135, Problem e. )
ROWS
N Z
E R1
E R2
E R3
E R4
COLUMNS
X1 Z 1 R1 3 R2 4 R3 2 R4 1
X2 Z 0 R1 5 R2 6 R3 4 R4 1
X3 Z 0 R1 3 R2 5 R3 1 R4 2
X4 Z 0 R1 2 R2 3 R3 1 R4 1
X5 Z 0 R1 1 R2 5 R3 -3 R4 4
X6 Z 0 R1 2 R2 5 R3 -1 R4 3
X7 Z 0 R1 1 R2 6 R3 -4 R4 5
RHS
RHS1 R1 6
RHS1 R2 11
RHS1 R3 1
RHS1 R4 5
BOUNDS
ENDATA

2690
src/tests/util/lp/lp.cpp Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,838 @@
/*
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
// reads an MPS file reperesenting a Mixed Integer Program
#include <string>
#include <vector>
#include <unordered_map>
#include "util/lp/lp_primal_simplex.h"
#include "util/lp/lp_dual_simplex.h"
#include "util/lp/lar_solver.h"
#include <iostream>
#include <fstream>
#include <util/numerics/mpq.h>
#include <functional>
#include <algorithm>
namespace lean {
using namespace std;
// trim from start
inline string &ltrim(std::string &s) {
s.erase(s.begin(), find_if(s.begin(), s.end(), std::not1(std::ptr_fun<int, int>(std::isspace))));
return s;
}
// trim from end
inline string &rtrim(std::string &s) {
s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun<int, int>(std::isspace))).base(), s.end());
return s;
}
// trim from both ends
inline string &trim(std::string &s) {
return ltrim(rtrim(s));
}
inline string trim(std::string const &r) {
string s = r;
return ltrim(rtrim(s));
}
inline vector<string> string_split(const string &source, const char *delimiter, bool keep_empty) {
vector<string> results;
size_t prev = 0;
size_t next = 0;
while ((next = source.find_first_of(delimiter, prev)) != std::string::npos) {
if (keep_empty || (next - prev != 0)) {
results.push_back(source.substr(prev, next - prev));
}
prev = next + 1;
}
if (prev < source.size()) {
results.push_back(source.substr(prev));
}
return results;
}
inline vector<string> split_and_trim(string line) {
auto split = string_split(line, " \t", false);
vector<string> ret;
for (auto s : split) {
ret.push_back(trim(s));
}
return ret;
}
template <typename T, typename X>
class mps_reader {
enum row_type { Cost, Less_or_equal, Greater_or_equal, Equal };
struct bound {
bool m_low_is_set = true;
T m_low;
bool m_upper_is_set = false;
T m_upper;
bool m_value_is_fixed = false;
T m_fixed_value;
bool m_free = false;
// constructor
bound() : m_low(numeric_traits<T>::zero()) {} // it seems all mps files I have seen have the default low value 0 on a variable
};
struct column {
string m_name;
bound * m_bound = nullptr;
unsigned m_index;
column(string name, unsigned index): m_name(name), m_index(index) {
}
};
struct row {
row_type m_type;
string m_name;
unordered_map<string, T> m_row_columns;
T m_right_side = numeric_traits<T>::zero();
unsigned m_index;
T m_range = numeric_traits<T>::zero();
row(row_type type, string name, unsigned index) : m_type(type), m_name(name), m_index(index) {
}
};
string m_file_name;
bool m_is_OK = true;
unordered_map<string, row *> m_rows;
unordered_map<string, column *> m_columns;
string m_line;
string m_name;
string m_cost_row_name;
ifstream m_file_stream;
// needed to adjust the index row
unsigned m_cost_line_count = 0;
unsigned m_line_number = 0;
void set_m_ok_to_false() {
cout << "setting m_is_OK to false" << endl;
m_is_OK = false;
}
string get_string_from_position(unsigned offset) {
unsigned i = offset;
for (; i < m_line.size(); i++){
if (m_line[i] == ' ')
break;
}
lean_assert(m_line.size() >= offset);
lean_assert(m_line.size() >> i);
lean_assert(i >= offset);
return m_line.substr(offset, i - offset);
}
void set_boundary_for_column(unsigned col, bound * b, lp_solver<T, X> * solver){
if (b == nullptr) {
solver->set_low_bound(col, numeric_traits<T>::zero());
return;
}
if (b->m_free) {
return;
}
if (b->m_low_is_set) {
solver->set_low_bound(col, b->m_low);
}
if (b->m_upper_is_set) {
solver->set_upper_bound(col, b->m_upper);
}
if (b->m_value_is_fixed) {
solver->set_fixed_value(col, b->m_fixed_value);
}
}
bool all_white_space() {
for (unsigned i = 0; i < m_line.size(); i++) {
char c = m_line[i];
if (c != ' ' && c != '\t') {
return false;
}
}
return true;
}
void read_line() {
while (m_is_OK) {
if (!getline(m_file_stream, m_line)) {
m_line_number++;
set_m_ok_to_false();
cout << "cannot read from file" << endl;
}
m_line_number++;
if (m_line.size() != 0 && m_line[0] != '*' && !all_white_space())
break;
}
}
void read_name() {
do {
read_line();
if (!m_line.find("NAME") == 0) {
continue;
}
m_line = m_line.substr(4);
m_name = trim(m_line);
break;
} while (m_is_OK);
}
void read_rows() {
// look for start of the rows
read_line();
do {
if (m_line.find("ROWS") >= 0) {
break;
}
} while (m_is_OK);
do {
read_line();
if (m_line.find("COLUMNS") == 0) {
break;
}
add_row();
} while (m_is_OK);
}
void read_column_by_columns(string column_name, string column_data) {
// uph, let us try to work with columns
if (column_data.size() >= 22) {
string ss = column_data.substr(0, 8);
string row_name = trim(ss);
auto t = m_rows.find(row_name);
if (t == m_rows.end()) {
cout << "cannot find " << row_name << endl;
goto fail;
} else {
row * row = t->second;
row->m_row_columns[column_name] = atof(column_data.substr(8).c_str());
if (column_data.size() > 24) {
column_data = column_data.substr(25);
if (column_data.size() >= 22) {
read_column_by_columns(column_name, column_data);
}
}
}
} else {
fail:
set_m_ok_to_false();
cout << "cannot understand this line" << endl;
cout << "line = " << m_line << ", line number is " << m_line_number << endl;
return;
}
}
void read_column(string column_name, string column_data){
auto tokens = split_and_trim(column_data);
for (int i = 0; i < tokens.size() - 1; i+= 2) {
auto row_name = tokens[i];
if (row_name == "'MARKER'") return; // it is the integrality marker, no real data here
auto t = m_rows.find(row_name);
if (t == m_rows.end()) {
read_column_by_columns(column_name, column_data);
return;
}
row *r = t->second;
r->m_row_columns[column_name] = atof(tokens[i + 1].c_str());
}
}
void read_columns(){
string column_name;
do {
read_line();
if (m_line.find("RHS") == 0) {
// cout << "found RHS" << endl;
break;
}
if (m_line.size() < 22) {
cout << "line is too short for a column" << endl;
cout << m_line << endl;
cout << "line number is " << m_line_number << endl;
set_m_ok_to_false();
return;
}
string column_name_tmp = trim(m_line.substr(4, 8));
if (!column_name_tmp.empty()) {
column_name = column_name_tmp;
}
auto col_it = m_columns.find(column_name);
mps_reader::column * col;
if (col_it == m_columns.end()) {
col = new mps_reader::column(column_name, m_columns.size());
m_columns[column_name] = col;
// cout << column_name << '[' << col->m_index << ']'<< endl;
} else {
col = col_it->second;
}
read_column(column_name, m_line.substr(14));
} while (m_is_OK);
}
void read_rhs() {
do {
read_line();
if (m_line.find("BOUNDS") == 0 || m_line.find("ENDATA") == 0 || m_line.find("RANGES") == 0) {
break;
}
fill_rhs();
} while (m_is_OK);
}
void fill_rhs_by_columns(string rhsides) {
// uph, let us try to work with columns
if (rhsides.size() >= 22) {
string ss = rhsides.substr(0, 8);
string row_name = trim(ss);
auto t = m_rows.find(row_name);
if (t == m_rows.end()) {
cout << "cannot find " << row_name << endl;
goto fail;
} else {
row * row = t->second;
row->m_right_side = atof(rhsides.substr(8).c_str());
if (rhsides.size() > 24) {
rhsides = rhsides.substr(25);
if (rhsides.size() >= 22) {
fill_rhs_by_columns(rhsides);
}
}
}
} else {
fail:
set_m_ok_to_false();
cout << "cannot understand this line" << endl;
cout << "line = " << m_line << ", line number is " << m_line_number << endl;
return;
}
}
void fill_rhs() {
if (m_line.size() < 14) {
cout << "line is too short" << endl;
cout << m_line << endl;
cout << "line number is " << m_line_number << endl;
set_m_ok_to_false();
return;
}
string rhsides = m_line.substr(14);
vector<string> splitted_line = split_and_trim(rhsides);
for (unsigned i = 0; i < splitted_line.size() - 1; i += 2) {
auto t = m_rows.find(splitted_line[i]);
if (t == m_rows.end()) {
fill_rhs_by_columns(rhsides);
return;
}
row * row = t->second;
row->m_right_side = atof(splitted_line[i + 1].c_str());
}
}
void read_bounds() {
if (!m_line.find("BOUNDS") == 0) {
return;
}
do {
read_line();
if (m_line[0] != ' ') {
break;
}
create_or_update_bound();
} while (m_is_OK);
}
void read_ranges() {
if (!m_line.find("RANGES") == 0) {
return;
}
do {
read_line();
auto sl = split_and_trim(m_line);
if (sl.size() < 2) {
break;
}
read_range(sl);
} while (m_is_OK);
}
void read_bound_by_columns(string colstr) {
if (colstr.size() < 14) {
cout << "line is too short" << endl;
cout << m_line << endl;
cout << "line number is " << m_line_number << endl;
set_m_ok_to_false();
return;
}
// uph, let us try to work with columns
if (colstr.size() >= 22) {
string ss = colstr.substr(0, 8);
string column_name = trim(ss);
auto t = m_columns.find(column_name);
if (t == m_columns.end()) {
cout << "cannot find " << column_name << endl;
goto fail;
} else {
vector<string> bound_string;
bound_string.push_back(column_name);
if (colstr.size() > 14) {
bound_string.push_back(colstr.substr(14));
}
mps_reader::column * col = t->second;
bound * b = col->m_bound;
if (b == nullptr) {
col->m_bound = b = new bound();
}
update_bound(b, bound_string);
}
} else {
fail:
set_m_ok_to_false();
cout << "cannot understand this line" << endl;
cout << "line = " << m_line << ", line number is " << m_line_number << endl;
return;
}
}
void update_bound(bound * b, vector<string> bound_string) {
/*
UP means an upper bound is applied to the variable. A bound of type LO means a lower bound is applied. A bound type of FX ("fixed") means that the variable has upper and lower bounds equal to a single value. A bound type of FR ("free") means the variable has neither lower nor upper bounds and so can take on negative values. A variation on that is MI for free negative, giving an upper bound of 0 but no lower bound. Bound type PL is for a free positive for zero to plus infinity, but as this is the normal default, it is seldom used. There are also bound types for use in MIP models - BV for binary, being 0 or 1. UI for upper integer and LI for lower integer. SC stands for semi-continuous and indicates that the variable may be zero, but if not must be equal to at least the value given.
*/
string bound_type = get_string_from_position(1);
if (bound_type == "BV") {
b->m_upper_is_set = true;
b->m_upper = 1;
return;
}
if (bound_type == "UP" || bound_type == "UI" || bound_type == "LIMITMAX") {
if (bound_string.size() <= 1){
set_m_ok_to_false();
return;
}
double val = atof(bound_string[1].c_str());
b->m_upper_is_set = true;
b->m_upper= val;
} else if (bound_type == "LO" || bound_type == "LI") {
if (bound_string.size() <= 1){
set_m_ok_to_false();
return;
}
double val = atof(bound_string[1].c_str());
b->m_low_is_set = true;
b->m_low = val;
} else if (bound_type == "FR") {
b->m_free = true;
} else if (bound_type == "FX") {
if (bound_string.size() <= 1){
set_m_ok_to_false();
return;
}
double val = atof(bound_string[1].c_str());
b->m_value_is_fixed = true;
b->m_fixed_value = val;
} else if (bound_type == "PL") {
b->m_low_is_set = true;
b->m_low = 0;
} else if (bound_type == "MI") {
b->m_upper_is_set = true;
b->m_upper = 0;
} else {
cout << "unexpected bound type " << bound_type << " at line " << m_line_number << endl;
set_m_ok_to_false();
throw;
}
}
void create_or_update_bound() {
const unsigned name_offset = 14;
lean_assert(m_line.size() >= 14);
vector<string> bound_string = split_and_trim(m_line.substr(name_offset, m_line.size()));
if (bound_string.size() == 0) {
set_m_ok_to_false();
cout << "error at line " << m_line_number << endl;
throw m_line;
}
string name = bound_string[0];
auto it = m_columns.find(name);
if (it == m_columns.end()){
read_bound_by_columns(m_line.substr(14));
return;
}
mps_reader::column * col = it->second;
bound * b = col->m_bound;
if (b == nullptr) {
col->m_bound = b = new bound();
}
update_bound(b, bound_string);
}
void read_range_by_columns(string rhsides) {
if (m_line.size() < 14) {
cout << "line is too short" << endl;
cout << m_line << endl;
cout << "line number is " << m_line_number << endl;
set_m_ok_to_false();
return;
}
// uph, let us try to work with columns
if (rhsides.size() >= 22) {
string ss = rhsides.substr(0, 8);
string row_name = trim(ss);
auto t = m_rows.find(row_name);
if (t == m_rows.end()) {
cout << "cannot find " << row_name << endl;
goto fail;
} else {
row * row = t->second;
row->m_range = atof(rhsides.substr(8).c_str());
maybe_modify_current_row_and_add_row_for_range(row);
if (rhsides.size() > 24) {
rhsides = rhsides.substr(25);
if (rhsides.size() >= 22) {
read_range_by_columns(rhsides);
}
}
}
} else {
fail:
set_m_ok_to_false();
cout << "cannot understand this line" << endl;
cout << "line = " << m_line << ", line number is " << m_line_number << endl;
return;
}
}
void read_range(vector<string> & splitted_line){
for (unsigned i = 1; i < splitted_line.size() - 1; i += 2) {
auto it = m_rows.find(splitted_line[i]);
if (it == m_rows.end()) {
read_range_by_columns(m_line.substr(14));
return;
}
row * row = it->second;
row->m_range = atof(splitted_line[i + 1].c_str());
maybe_modify_current_row_and_add_row_for_range(row);
}
}
void maybe_modify_current_row_and_add_row_for_range(row * row_with_range) {
unsigned index= m_rows.size() - m_cost_line_count;
string row_name = row_with_range->m_name + "_range";
row * other_bound_range_row;
switch (row_with_range->m_type) {
case row_type::Greater_or_equal:
m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index);
other_bound_range_row->m_right_side = row_with_range->m_right_side + abs(row_with_range->m_range);
break;
case row_type::Less_or_equal:
m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index);
other_bound_range_row->m_right_side = row_with_range->m_right_side - abs(row_with_range->m_range);
break;
case row_type::Equal:
if (row_with_range->m_range > 0) {
row_with_range->m_type = row_type::Greater_or_equal; // the existing row type change
m_rows[row_name] = other_bound_range_row = new row(row_type::Less_or_equal, row_name, index);
} else { // row->m_range < 0;
row_with_range->m_type = row_type::Less_or_equal; // the existing row type change
m_rows[row_name] = other_bound_range_row = new row(row_type::Greater_or_equal, row_name, index);
}
other_bound_range_row->m_right_side = row_with_range->m_right_side + row_with_range->m_range;
break;
default:
cout << "unexpected bound type " << row_with_range->m_type << " at line " << m_line_number << endl;
set_m_ok_to_false();
throw;
}
for (auto s : row_with_range->m_row_columns) {
lean_assert(m_columns.find(s.first) != m_columns.end());
other_bound_range_row->m_row_columns[s.first] = s.second;
}
}
void add_row() {
if (m_line.length() < 2) {
return;
}
m_line = trim(m_line);
char c = m_line[0];
m_line = m_line.substr(1);
m_line = trim(m_line);
add_row(c);
}
void add_row(char c) {
unsigned index= m_rows.size() - m_cost_line_count;
switch (c) {
case 'E':
m_rows[m_line] = new row(row_type::Equal, m_line, index);
break;
case 'L':
m_rows[m_line] = new row(row_type::Less_or_equal, m_line, index);
break;
case 'G':
m_rows[m_line] = new row(row_type::Greater_or_equal, m_line, index);
break;
case 'N':
m_rows[m_line] = new row(row_type::Cost, m_line, index);
m_cost_row_name = m_line;
m_cost_line_count++;
break;
}
}
unsigned range_count() {
unsigned ret = 0;
for (auto s : m_rows) {
if (s.second->m_range != 0) {
ret++;
}
}
return ret;
}
/*
If rhs is a constraint's right-hand-side value and range is the constraint's range value, then the range interval is defined according to the following table:
sense interval
G [rhs, rhs + |range|]
L [rhs - |range|, rhs]
E [rhs, rhs + |range|] if range ¡Ý 0 [rhs - |range|, rhs] if range < 0
where |range| is range's absolute value.
*/
lp_relation get_relation_from_row(mps_reader::row_type row_type) {
switch (row_type) {
case mps_reader::row_type::Less_or_equal: return lp_relation::Less_or_equal;
case mps_reader::row_type::Greater_or_equal: return lp_relation::Greater_or_equal;
case mps_reader::row_type::Equal: return lp_relation::Equal;
default:
std::cout << "Unexpected row_type " << row_type << endl;
set_m_ok_to_false();
throw;
}
}
unsigned solver_row_count() {
return m_rows.size() - m_cost_line_count + range_count();
}
void fill_solver_on_row(row * row, lp_solver<T, X> *solver) {
if (row->m_name != m_cost_row_name) {
solver->add_constraint(get_relation_from_row(row->m_type), row->m_right_side, row->m_index);
for (auto s : row->m_row_columns) {
lean_assert(m_columns.find(s.first) != m_columns.end());
solver->set_row_column_coefficient(row->m_index, m_columns[s.first]->m_index, s.second);
}
} else {
set_solver_cost(row, solver);
}
}
T abs(T & t) { return t < numeric_traits<T>::zero() ? -t: t; }
void fill_solver_on_rows(lp_solver<T, X> * solver) {
for (auto row_it : m_rows) {
fill_solver_on_row(row_it.second, solver);
}
}
void fill_solver_on_columns(lp_solver<T, X> * solver){
for (auto s : m_columns) {
mps_reader::column * col = s.second;
unsigned index = col->m_index;
set_boundary_for_column(index, col->m_bound, solver);
// optional call
solver->give_symbolic_name_to_column(col->m_name, col->m_index);
}
}
void fill_solver(lp_solver<T, X> *solver) {
fill_solver_on_rows(solver);
fill_solver_on_columns(solver);
}
void set_solver_cost(row * row, lp_solver<T, X> *solver) {
for (auto s : row->m_row_columns) {
string name = s.first;
lean_assert(m_columns.find(name) != m_columns.end());
mps_reader::column * col = m_columns[name];
solver->set_cost_for_column(col->m_index, s.second);
}
}
public:
vector<string> column_names() {
vector<string> v;
for (auto s : m_columns) {
v.push_back(s.first);
}
return v;
}
~mps_reader() {
for (auto s : m_rows) {
delete s.second;
}
for (auto s : m_columns) {
auto col = s.second;
auto b = col->m_bound;
if (b != nullptr) {
delete b;
}
delete col;
}
}
mps_reader(string file_name):
m_file_name(file_name), m_file_stream(file_name) {
}
void read() {
if (!m_file_stream.is_open()){
set_m_ok_to_false();
return;
}
read_name();
read_rows();
read_columns();
read_rhs();
if (m_line.find("BOUNDS") == 0) {
read_bounds();
read_ranges();
} else if (m_line.find("RANGES") == 0) {
read_ranges();
read_bounds();
}
}
bool is_ok() {
return m_is_OK;
}
lp_solver<T, X> * create_solver(bool dual) {
lp_solver<T, X> * solver = dual? (lp_solver<T, X>*)new lp_dual_simplex<T, X>() : new lp_primal_simplex<T, X>();
fill_solver(solver);
return solver;
}
lconstraint_kind get_lar_relation_from_row(mps_reader::row_type row_type) {
switch (row_type) {
case Less_or_equal: return LE;
case Greater_or_equal: return GE;
case Equal: return EQ;
default:
std::cout << "Unexpected row_type " << row_type << endl;
set_m_ok_to_false();
throw;
}
}
void fill_lar_solver_on_row(row * row, lar_solver *solver) {
if (row->m_name != m_cost_row_name) {
auto kind = get_lar_relation_from_row(row->m_type);
buffer<pair<mpq, var_index>> ls;
for (auto s : row->m_row_columns) {
var_index i = solver->add_var(s.first);
ls.push_back(make_pair(s.second, i));
}
solver->add_constraint(ls, kind, row->m_right_side);
} else {
// ignore the cost row
}
}
void fill_lar_solver_on_rows(lar_solver * solver) {
for (auto row_it : m_rows) {
fill_lar_solver_on_row(row_it.second, solver);
}
}
void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) {
buffer<pair<mpq, var_index>> ls;
var_index i = solver->add_var(col->m_name);
ls.push_back(make_pair(numeric_traits<T>::one(), i));
solver->add_constraint(ls, GE, b->m_low);
}
void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) {
var_index i = solver->add_var(col->m_name);
buffer<pair<mpq, var_index>> ls;
ls.push_back(make_pair(numeric_traits<T>::one(), i));
solver->add_constraint(ls, LE, b->m_upper);
}
void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) {
var_index i = solver->add_var(col->m_name);
buffer<pair<mpq, var_index>> ls;
ls.push_back(make_pair(numeric_traits<T>::one(), i));
solver->add_constraint(ls, EQ, b->m_fixed_value);
}
void fill_lar_solver_on_columns(lar_solver * solver) {
for (auto s : m_columns) {
mps_reader::column * col = s.second;
solver->add_var(col->m_name);
auto b = col->m_bound;
if (b == nullptr) return;
if (b->m_free) continue;
if (b->m_low_is_set) {
create_low_constraint_for_var(col, b, solver);
}
if (b->m_upper_is_set) {
create_upper_constraint_for_var(col, b, solver);
}
if (b->m_value_is_fixed) {
create_equality_contraint_for_var(col, b, solver);
}
}
}
void fill_lar_solver(lar_solver * solver) {
fill_lar_solver_on_columns(solver);
fill_lar_solver_on_rows(solver);
}
lar_solver * create_lar_solver() {
lar_solver * solver = new lar_solver();
fill_lar_solver(solver);
return solver;
}
};
}

View file

@ -0,0 +1,600 @@
*NAME: OIL
*ROWS: 74
*COLUMNS: 81
*NONZERO: 504
*OPT SOLN: 126.057
*SOURCE: Bruce Murtagh, "Advanced Linear Programming"
*APPLICATION: oil refinery model
*COMMENTS: problem is maximization
*
NAME OIL REFINERY EXAMPLE
ROWS
N PROFIT
L MVOLBOL
L MVOLCOL
E MVOLLNC
E MVOLLNB
E MVOLSRK
E MVOLSRD
E MVOLVBB
E MVOLVBC
E MVOLRCR
E MVOLHVO
E UBALKWH
E UBALH2O
E UBALSTM
E UBALFUL
E MVOLB95
E MVOLB90
E MVOLLHG
E MVOLC3S
E MVOLNC4
E MVOLLSR
E MVOLHSR
E MVOLIC4
L VCAPSGP
E MVOLRFG
E MSCFHYL
E MVOLR90
E MVOLR95
E MVOLF90
E MVOLF95
L VCAPRFG
E MVOLLCO
E MVOLHHG
E MVOLHCD
L VCAPHVO
L VCAPHOL
E MVOLC3U
E MVOLC4U
E MVOLFCG
E MVOLSLR
L VCAPCCU
E MVOLLA3
E MVOLLA4
L VCAPALK
L XLPRPRE
L XHPRPRE
L XTELPRE
L XRVPPRE
L X200PRE
L X230PRE
E EVOLPRE
L XPSCPRE
L XRSCREG
L XLPRINT
L XHPRINT
L XTELINT
L XRVPINT
L X200INT
L X230INT
E EVOLINT
L XLPRREG
L XHPRREG
L XTELREG
L XRVPREG
L X200REG
L X230REG
E EVOLREG
E EVOLLPG
E EVOLJP4
L XRVXJP4
L XRVNJP4
E EVOLDSL
E EVOLRSD
L XVISRSD
COLUMNS
VCRDBOL MVOLBOL 1.0
VCRDBOL MVOLLNB -.537
VCRDBOL MVOLSRK -.131
VCRDBOL MVOLSRD -.1155
VCRDBOL MVOLVBB -.037
VCRDBOL MVOLRCR -.0365
VCRDBOL MVOLHVO -.143
VCRDBOL UBALKWH .302
VCRDBOL UBALH2O .150
VCRDBOL UBALSTM .003
VCRDBOL UBALFUL .0587
VCRDBOL PROFIT -12.8
VCRDCOL MVOLCOL 1.
VCRDCOL MVOLLNC -.2931
VCRDCOL MVOLSRK -.1170
VCRDCOL MVOLSRD -.0649
VCRDCOL MVOLVBC -.18
VCRDCOL MVOLRCR -.1233
VCRDCOL MVOLHVO -.2217
VCRDCOL UBALKWH .384
VCRDCOL UBALH2O .185
VCRDCOL UBALSTM .003
VCRDCOL UBALFUL .1053
VCRDCOL PROFIT -11.48
VSGPLNC MVOLLNC 1.
VSGPLNC MVOLC3S -.0112
VSGPLNC MVOLNC4 -.0378
VSGPLNC MVOLLSR -.1502
VSGPLNC MVOLHSR -.7953
VSGPLNC MVOLIC4 -.0099
VSGPLNC UBALKWH .721
VSGPLNC UBALH2O .185
VSGPLNC UBALSTM .013
VSGPLNC UBALFUL .0488
VSGPLNC VCAPSGP 1.
VSGPLNB MVOLLNB 1.
VSGPLNB MVOLC3S -.0277
VSGPLNB MVOLNC4 -.0563
VSGPLNB MVOLLSR -.199
VSGPLNB MVOLHSR -.6873
VSGPLNB MVOLIC4 -.017
VSGPLNB UBALKWH .495
VSGPLNB UBALH2O .209
VSGPLNB UBALSTM .013
VSGPLNB UBALFUL .0506
VSGPLNB VCAPSGP 1.
VSGPLHG MVOLLHG 1.0
VSGPLHG MVOLC3S -.175
VSGPLHG MVOLNC4 -.27
VSGPLHG MVOLLSR -.028
VSGPLHG MVOLIC4 -.455
VSGPLHG UBALKWH .495
VSGPLHG UBALH2O .209
VSGPLHG UBALSTM .013
VSGPLHG UBALFUL .0448
VSGPB95 MVOLB95 1.
VSGPB95 MVOLC3S -.2836
VSGPB95 MVOLNC4 -.3285
VSGPB95 MVOLLSR -.0241
VSGPB95 MVOLIC4 -.2502
VSGPB95 UBALKWH .495
VSGPB95 UBALH2O .209
VSGPB95 UBALSTM .013
VSGPB95 UBALFUL .0506
VSGPB90 MVOLB90 1.
VSGPB90 MVOLC3S -.271
VSGPB90 MVOLNC4 -.3289
VSGPB90 MVOLLSR -.0255
VSGPB90 MVOLIC4 -.2656
VSGPB90 UBALKWH .495
VSGPB90 UBALH2O .209
VSGPB90 UBALSTM .013
VSGPB90 UBALFUL .0506
VH2RHSR MVOLHSR 1.
VH2RHSR MVOLRFG -1.
VH2RHSR MSCFHYL .0327
VH2RHSR UBALKWH .793
VH2RHSR UBALH2O .045
VH2RHSR UBALFUL .094
VH2RHSR PROFIT -.0176
VRFFRF1 MVOLRFG 1.0
VRFFRF1 MVOLR90 -1.0
VRFFRF2 MVOLRFG 1.0
VRFFRF2 MVOLR95 -1.0
VRFFHH1 MVOLR90 -1.0
VRFFHH1 MVOLHHG 1.0
VRFFHH2 MVOLR95 -1.0
VRFFHH2 MVOLHHG 1.0
VRFGR90 MVOLR90 1.0
VRFGR90 MVOLB90 -.0404
VRFGR90 MVOLF90 -0.8564
VRFGR90 MSCFHYL -0.8239
VRFGR90 UBALKWH .792
VRFGR90 UBALH2O .297
VRFGR90 UBALSTM 0.0063
VRFGR90 UBALFUL -0.156
VRFGR90 VCAPRFG 1.0
VRFGR90 PROFIT -0.1512
VRFGR95 MVOLR95 1.0
VRFGR95 MVOLB95 -0.0588
VRFGR95 MVOLF95 -0.8145
VRFGR95 MSCFHYL -.7689
VRFGR95 UBALKWH 1.03
VRFGR95 UBALH2O .387
VRFGR95 UBALSTM 0.008
VRFGR95 UBALFUL -.2112
VRFGR95 VCAPRFG 1.3
VRFGR95 PROFIT -0.304
VHOLLCO MVOLLCO 1.0
VHOLLCO MVOLHHG -.6627
VHOLLCO MVOLLHG -0.2414
VHOLLCO MVOLHCD -.2930
VHOLLCO MSCFHYL 2.3
VHOLLCO UBALFUL -.2054
VHOLLCO UBALH2O 0.826
VHOLLCO UBALKWH 14.61
VHOLLCO VCAPHOL 1.0
VHOLLCO PROFIT -0.2112
VHOLSRD MVOLSRD 1.0
VHOLSRD MVOLHHG -.6627
VHOLSRD MVOLLHG -0.2414
VHOLSRD MVOLHCD -.2930
VHOLSRD MSCFHYL 2.3
VHOLSRD UBALFUL -.2054
VHOLSRD UBALH2O 0.826
VHOLSRD UBALKWH 14.61
VHOLSRD VCAPHOL 1.0
VHOLSRD PROFIT -0.2112
VHOLRCR MVOLRCR 1.0
VHOLRCR MVOLHHG -.5875
VHOLRCR MVOLLHG -0.3321
VHOLRCR MVOLHCD -.3620
VHOLRCR MSCFHYL 2.3
VHOLRCR UBALFUL -.2054
VHOLRCR UBALH2O 0.826
VHOLRCR UBALKWH 14.61
VHOLRCR VCAPHOL 1.0
VHOLRCR PROFIT -0.2112
VHOLHVO MVOLHVO 1.0
VHOLHVO MVOLHHG -.5875
VHOLHVO MVOLLHG -0.3321
VHOLHVO MVOLHCD -.3620
VHOLHVO MSCFHYL 2.3
VHOLHVO UBALFUL -.2054
VHOLHVO UBALH2O 0.826
VHOLHVO UBALKWH 14.61
VHOLHVO VCAPHVO 1.0
VHOLHVO VCAPHOL 1.0
VHOLHVO PROFIT -0.2112
VCCUSRK MVOLSRK 1.0
VCCUSRK MVOLNC4 -0.0184
VCCUSRK MVOLC3S -0.0303
VCCUSRK MVOLIC4 -0.0564
VCCUSRK MVOLC3U -0.0655
VCCUSRK MVOLC4U -0.0780
VCCUSRK MVOLFCG -0.4750
VCCUSRK MVOLLCO -0.3050
VCCUSRK UBALSTM -.0654
VCCUSRK UBALFUL -.2703
VCCUSRK UBALH2O .632
VCCUSRK UBALKWH .6807
VCCUSRK VCAPCCU 1.
VCCUSRK PROFIT -.2112
VCCUSRD MVOLSRD 1.
VCCUSRD MVOLNC4 -.0184
VCCUSRD MVOLC3S -.0303
VCCUSRD MVOLIC4 -.0564
VCCUSRD MVOLC3U -.0655
VCCUSRD MVOLC4U -.0780
VCCUSRD MVOLFCG -.4750
VCCUSRD MVOLLCO -.3050
VCCUSRD UBALSTM -.0654
VCCUSRD UBALFUL -.2703
VCCUSRD UBALH2O 0.632
VCCUSRD UBALKWH .6807
VCCUSRD VCAPCCU 1.
VCCUSRD PROFIT -.2112
VCCURCR MVOLRCR 1.0
VCCURCR MVOLNC4 -.0185
VCCURCR MVOLC3S -.0328
VCCURCR MVOLIC4 -.0568
VCCURCR MVOLC3U -.0658
VCCURCR MVOLC4U -.0806
VCCURCR MVOLFCG -.4934
VCCURCR MVOLLCO -.2922
VCCURCR MVOLSLR -.0096
VCCURCR UBALSTM -.0654
VCCURCR UBALFUL -.2703
VCCURCR UBALH2O 0.632
VCCURCR UBALKWH .6807
VCCURCR VCAPCCU 1.
VCCURCR PROFIT -.2112
VCCUHVO MVOLHVO 1.0
VCCUHVO MVOLNC4 -.0185
VCCUHVO MVOLC3S -.0328
VCCUHVO MVOLIC4 -.0568
VCCUHVO MVOLC3U -.0658
VCCUHVO MVOLC4U -.0806
VCCUHVO MVOLFCG -.4934
VCCUHVO MVOLLCO -.2922
VCCUHVO MVOLSLR -.0096
VCCUHVO UBALSTM -.0654
VCCUHVO UBALFUL -.2703
VCCUHVO UBALH2O 0.632
VCCUHVO UBALKWH .6807
VCCUHVO VCAPHVO 1.
VCCUHVO VCAPCCU 1.
VCCUHVO PROFIT -.2112
VALKLA3 MVOLIC4 .7600
VALKLA3 MVOLC3U .5714
VALKLA3 MVOLLA3 -1.0
VALKLA3 UBALSTM .1869
VALKLA3 UBALFUL .2796
VALKLA3 UBALH2O 2.241
VALKLA3 UBALKWH 2.766
VALKLA3 VCAPALK 1.0
VALKLA3 PROFIT -.512
VALKLA4 MVOLIC4 .6571
VALKLA4 MVOLC4U .5714
VALKLA4 MVOLC3S -.0571
VALKLA4 MVOLNC4 -.0114
VALKLA4 MVOLLA4 -1.0
VALKLA4 UBALSTM .1724
VALKLA4 UBALFUL .2579
VALKLA4 UBALH2O 2.067
VALKLA4 UBALKWH 2.552
VALKLA4 VCAPALK 1.0
VALKLA4 PROFIT -.472
VALKIC4 MVOLIC4 1.0
VALKIC4 MVOLNC4 -1.0
VALKC3U MVOLC3U 1.0
VALKC3U MVOLC3S -1.0
VALKC4U MVOLC4U 1.0
VALKC4U MVOLNC4 -1.0
UTILC3S MVOLC3S 1.
UTILC3S UBALFUL -3.814
UTILNC4 MVOLNC4 1.
UTILNC4 UBALFUL -4.316
UTILIC4 MVOLIC4 1.
UTILIC4 UBALFUL -4.153
UTILC3U MVOLC3U 1.
UTILC3U UBALFUL -3.808
UTILC4U MVOLC4U 1.
UTILC4U UBALFUL -4.44
UTILHYL MSCFHYL 1.
UTILHYL UBALFUL -.305
UTILSTM UBALSTM -1.
UTILSTM UBALFUL 1.42
UTILSTM PROFIT -.16
PURCPC4 MVOLIC4 -.5
PURCPC4 MVOLNC4 -.5
PURCPC4 PROFIT -12.
PURCH2O UBALH2O -1.
PURCH2O PROFIT -.0528
PURCKWH UBALKWH -1.
PURCKWH PROFIT -.04
PURCFUL UBALFUL -1.
PURCFUL PROFIT -1.6
PURCFLR UBALFUL 1.
BLPGC3S MVOLC3S 1.0
BLPGC3S EVOLLPG -1.0
BLPGNC4 MVOLNC4 1.0
BLPGNC4 EVOLLPG -1.0
SELLLPG EVOLLPG 1.0
SELLLPG PROFIT 11.0
BUP4LSR MVOLLSR 1.0
BUP4LSR EVOLJP4 -1.0
BUP4LSR XRVXJP4 14.0
BUP4LSR XRVNJP4 -14.0
BUP4HSR MVOLHSR 1.0
BUP4HSR EVOLJP4 -1.0
BUP4HSR XRVXJP4 0.8
BUP4HSR XRVNJP4 -0.8
SELLJP4 EVOLJP4 1.0
SELLJP4 XRVXJP4 -3.0
SELLJP4 XRVNJP4 2.0
SELLJP4 PROFIT 16.8
BDSLSRK MVOLSRK 1.0
BDSLSRK EVOLDSL -1.0
BDSLSRD MVOLSRD 1.0
BDSLSRD EVOLDSL -1.0
SELLDSL EVOLDSL 1.0
SELLDSL PROFIT 14.4
BPRELSR MVOLLSR 1.
BPRELSR XLPRPRE -7.95
BPRELSR XHPRPRE -8.70
BPRELSR XTELPRE -3.00
BPRELSR XRVPPRE 14.00
BPRELSR X200PRE 1.
BPRELSR X230PRE -1.
BPRELSR EVOLPRE -1.
BPREHCD MVOLHCD 1.0
BPREHCD XLPRPRE -8.84
BPREHCD XHPRPRE -9.45
BPREHCD XTELPRE -3.00
BPREHCD XRVPPRE 12.00
BPREHCD X200PRE 1.
BPREHCD X230PRE -1.
BPREHCD EVOLPRE -1.
BPREF95 MVOLF95 1.0
BPREF95 XLPRPRE -9.43
BPREF95 XHPRPRE -9.57
BPREF95 XTELPRE -3.
BPREF95 XRVPPRE 3.5
BPREF95 X200PRE .233
BPREF95 X230PRE -.358
BPREF95 EVOLPRE -1.
BPREF90 MVOLF90 1.0
BPREF90 XLPRPRE -9.03
BPREF90 XHPRPRE -9.32
BPREF90 XTELPRE -3.0
BPREF90 XRVPPRE 3.5
BPREF90 X200PRE .205
BPREF90 X230PRE -.333
BPREF90 EVOLPRE -1.
BPREFCG MVOLFCG 1.0
BPREFCG XLPRPRE -9.23
BPREFCG XHPRPRE -9.22
BPREFCG XTELPRE -3.
BPREFCG XRVPPRE 6.
BPREFCG X200PRE .381
BPREFCG X230PRE -.509
BPREFCG EVOLPRE -1.
BPRELA3 MVOLLA3 1.0
BPRELA3 XLPRPRE -9.4
BPRELA3 XHPRPRE -9.85
BPRELA3 XTELPRE -3.0
BPRELA3 XRVPPRE 2.5
BPRELA3 X200PRE 0.39
BPRELA3 X230PRE -0.77
BPRELA3 EVOLPRE -1.0
BPRELA4 MVOLLA4 1.0
BPRELA4 XLPRPRE -9.74
BPRELA4 XHPRPRE -10.1
BPRELA4 XTELPRE -3.0
BPRELA4 XRVPPRE 3.3
BPRELA4 X200PRE 0.233
BPRELA4 X230PRE -0.58
BPRELA4 EVOLPRE -1.0
BPRENC4 MVOLNC4 1.0
BPRENC4 XLPRPRE -9.74
BPRENC4 XHPRPRE -9.9
BPRENC4 XTELPRE -3.0
BPRENC4 XRVPPRE 66.0
BPRENC4 X200PRE 1.0
BPRENC4 X230PRE -1.0
BPRENC4 EVOLPRE -1.0
BPRETEL XLPRPRE -0.493
BPRETEL XHPRPRE -0.165
BPRETEL XTELPRE 1.0
BPRETEL PROFIT -0.3696
SELLPRE XLPRPRE 10.03
SELLPRE XHPRPRE 10.03
SELLPRE XRVPPRE -9.5
SELLPRE X200PRE -0.5
SELLPRE X230PRE 0.5
SELLPRE XPSCPRE 0.64
SELLPRE XRSCREG 0.35
SELLPRE EVOLPRE 1.0
SELLPRE PROFIT 21.44
BINTLSR MVOLLSR 1.0
BINTLSR XLPRINT -7.98
BINTLSR XHPRINT -8.58
BINTLSR XTELINT -3.0
BINTLSR XRVPINT 14.0
BINTLSR X200INT 1.0
BINTLSR X230INT -1.0
BINTLSR EVOLINT -1.0
BINTHCD MVOLHCD 1.
BINTHCD XLPRINT -8.87
BINTHCD XHPRINT -9.33
BINTHCD XTELINT -3.0
BINTHCD XRVPINT 12.0
BINTHCD X200INT 1.0
BINTHCD X230INT -1.
BINTHCD EVOLINT -1.0
BINTF95 MVOLF95 1.
BINTF95 XLPRINT -9.46
BINTF95 XHPRINT -9.45
BINTF95 XTELINT -3.0
BINTF95 XRVPINT 3.5
BINTF95 X200INT .233
BINTF95 X230INT -.358
BINTF95 EVOLINT -1.0
BINTF90 MVOLF90 1.
BINTF90 XLPRINT -9.06
BINTF90 XHPRINT -9.20
BINTF90 XTELINT -3.0
BINTF90 XRVPINT 3.5
BINTF90 X200INT .205
BINTF90 X230INT -.333
BINTF90 EVOLINT -1.0
BINTFCG MVOLFCG 1.
BINTFCG XLPRINT -9.26
BINTFCG XHPRINT -9.13
BINTFCG XTELINT -3.0
BINTFCG XRVPINT 6.
BINTFCG X200INT .318
BINTFCG X230INT -.509
BINTFCG EVOLINT -1.0
BINTNC4 MVOLNC4 1.
BINTNC4 XLPRINT -9.77
BINTNC4 XHPRINT -9.78
BINTNC4 XTELINT -3.0
BINTNC4 XRVPINT 66.
BINTNC4 X200INT 1.0
BINTNC4 X230INT -1.
BINTNC4 EVOLINT -1.0
BINTTEL XLPRINT -.435
BINTTEL XHPRINT -.208
BINTTEL XTELINT 1.
BINTTEL PROFIT -.3696
SELLINT XLPRINT 9.65
SELLINT XHPRINT 9.65
SELLINT XRVPINT -9.5
SELLINT X200INT -0.5
SELLINT X230INT 0.5
SELLINT XPSCPRE -.36
SELLINT XRSCREG 0.35
SELLINT EVOLINT 1.0
SELLINT PROFIT 20.32
BREGLSR MVOLLSR 1.0
BREGLSR XLPRREG -7.99
BREGLSR XHPRREG -8.59
BREGLSR XTELREG -3.0
BREGLSR XRVPREG 14.0
BREGLSR X200REG 1.0
BREGLSR X230REG -1.0
BREGLSR EVOLREG -1.0
BREGHCD MVOLHCD 1.0
BREGHCD XLPRREG -8.88
BREGHCD XHPRREG -9.34
BREGHCD XTELREG -3.0
BREGHCD XRVPREG 12.0
BREGHCD X200REG 1.0
BREGHCD X230REG -1.0
BREGHCD EVOLREG -1.0
BREGF95 MVOLF95 1.0
BREGF95 XLPRREG -9.47
BREGF95 XHPRREG -9.46
BREGF95 XTELREG -3.0
BREGF95 XRVPREG 3.5
BREGF95 X200REG .233
BREGF95 X230REG -0.358
BREGF95 EVOLREG -1.0
BREGF90 MVOLF90 1.0
BREGF90 XLPRREG -9.07
BREGF90 XHPRREG -9.21
BREGF90 XTELREG -3.0
BREGF90 XRVPREG 3.5
BREGF90 X200REG .205
BREGF90 X230REG -0.333
BREGF90 EVOLREG -1.0
BREGFCG MVOLFCG 1.0
BREGFCG XLPRREG -9.27
BREGFCG XHPRREG -9.14
BREGFCG XTELREG -3.0
BREGFCG XRVPREG 6.0
BREGFCG X200REG 0.318
BREGFCG X230REG -0.509
BREGFCG EVOLREG -1.0
BREGNC4 MVOLNC4 1.0
BREGNC4 XLPRREG -9.78
BREGNC4 XHPRREG -9.79
BREGNC4 XTELREG -3.0
BREGNC4 XRVPREG 66.0
BREGNC4 X200REG 1.0
BREGNC4 X230REG -1.0
BREGNC4 EVOLREG -1.0
BREGTEL XLPRREG -0.426
BREGTEL XHPRREG -.204
BREGTEL XTELREG 1.0
BREGTEL PROFIT -0.3696
SELLREG XLPRREG 9.05
SELLREG XHPRREG 9.05
SELLREG XRVPREG -9.5
SELLREG X200REG -0.5
SELLREG X230REG 0.5
SELLREG XPSCPRE -0.36
SELLREG XRSCREG -0.65
SELLREG EVOLREG 1.0
SELLREG PROFIT 18.04
BRSDVBB MVOLVBB 1.0
BRSDVBB EVOLRSD -1.0
BRSDVBB XVISRSD 10.1
BRSDVBC MVOLVBC 1.0
BRSDVBC EVOLRSD -1.0
BRSDVBC XVISRSD 12.63
BRSDRCR MVOLRCR 1.0
BRSDRCR EVOLRSD -1.0
BRSDRCR XVISRSD 6.9
BRSDHVO MVOLHVO 1.0
BRSDHVO EVOLRSD -1.0
BRSDHVO XVISRSD 8.05
BRSDHVO VCAPHVO 1.0
BRSDSLR MVOLSLR 1.0
BRSDSLR EVOLRSD -1.0
BRSDSLR XVISRSD 8.05
BRSDLCO MVOLLCO 1.0
BRSDLCO EVOLRSD -1.0
BRSDLCO XVISRSD 4.4
SELLRSD EVOLRSD 1.0
SELLRSD XVISRSD -10.1
SELLRSD PROFIT 8.00
RHS
LIMITMAX MVOLBOL 26.316
LIMITMAX MVOLCOL 21.052
LIMITMAX VCAPSGP 23.25
LIMITMAX VCAPHVO 5.25
LIMITMAX VCAPRFG 13.455
LIMITMAX VCAPHOL 3.87
LIMITMAX VCAPCCU 7.26
LIMITMAX VCAPALK 10.
ENDATA

View file

@ -0,0 +1,54 @@
*000000001111111111222222222233333333334444444444555555555566
*234567890123456789012345678901234567890123456789012345678901
NAME PLAN
ROWS
N VALUE
E YIELD
L FE
L CU
L MN
L MG
G AL
L SI
COLUMNS
BIN1 VALUE .03000 YIELD 1.00000
FE .15000 CU .03000
MN .02000 MG .02000
AL .70000 SI .02000
BIN2 VALUE .08000 YIELD 1.00000
FE .04000 CU .05000
MN .04000 MG .03000
AL .75000 SI .06000
BIN3 VALUE .17000 YIELD 1.00000
FE .02000 CU .08000
MN .01000 AL .80000
SI .08000
BIN4 VALUE .12000 YIELD 1.00000
FE .04000 CU .02000
MN .02000 AL .75000
SI .12000
BIN5 VALUE .15000 YIELD 1.00000
FE .02000 CU .06000
MN .02000 MG .01000
AL .80000 SI .02000
ALUM VALUE .21000 YIELD 1.00000
FE .01000 CU .01000
AL .97000 SI .01000
SILICON VALUE .38000 YIELD 1.00000
FE .03000 SI .97000
RHS
RHS1 YIELD 2000.00000 FE 60.00000
CU 100.00000 MN 40.00000
SI 300.00000
MG 30.00000 AL 1500.00000
RANGES
RNG1 SI 50.00000
BOUNDS
UP BND1 BIN1 200.00000
UP BIN2 2500.00000
LO BIN3 400.00000
UP BIN3 800.00000
LO BIN4 100.00000
UP BIN4 700.00000
UP BIN5 1500.00000
ENDATA

57
src/tests/util/lp/run_netlib.sh Executable file
View file

@ -0,0 +1,57 @@
#!/bin/bash
failures=0
successes=0
inconclusive=0
total=0
if [ $# -eq 1 ]; then
mpq_option=$1
if [ $mpq_option != "--mpq" ]; then
echo "Usage: run_netlib.sh [--mpq]"
exit 1
fi
else
mpq_option="none"
if [ $# -ne 0 ] ; then
echo "Too many arguments. Usage: run_netlib.sh [--mpq]"
exit 1
fi
fi
date
function analyze_run_result() {
status=$?
if [ $status -ne 0 ]; then
if [ $status -ne 2 ]; then
let "failures += 1"
echo "Failure"
else
echo "Inconclusive"
let "inconclusive += 1"
fi
else
let "successes += 1"
echo "Success"
fi
}
for f in test_files/netlib/*.SIF ; do
let "total += 2"
echo "processing "$f" for minimum"
./compare_with_glpk.sh --min $f
analyze_run_result
echo "processing "$f" for maximum"
./compare_with_glpk.sh --max $f
analyze_run_result
if [ $mpq_option == "--mpq" ]; then
let "total += 1"
echo "processing "$f" for rationals for minimum"
./compare_with_glpk.sh --min --mpq $f
analyze_run_result
fi
done
echo "Total runs="$total", failures="$failures", successes="$successes", inconclusive="$inconclusive
date

View file

@ -0,0 +1,29 @@
NAME SAMP1
ROWS
N Z
G R1
G R2
G R3
COLUMNS
X1 R1 2.0 R2 1.0
X1 R3 5.0 Z 3.0
MARK0001 'MARKER' 'INTORG'
X2 R1 -1.0 R2 -1.0
X2 R3 3.0 Z 7.0
X3 R1 1.0 R2 -6.0
X3 Z -1.0
MARK0002 'MARKER' 'INTEND'
X4 R1 -1.0 R2 4.0
X4 R3 1.0 Z 1.0
RHS
RHS1 R1 1.0
RHS1 R2 8.0
RHS1 R3 5.0
BOUNDS
UP BND1 X1 4.0
LO BND1 X2 2.0
UP BND1 X2 5.0
UP BND1 X3 1.0
LO BND1 X4 3.0
UP BND1 X4 8.0
ENDATA

View file

@ -0,0 +1,27 @@
NAME SAMP2
ROWS
N Z
G R1
G R2
G R3
COLUMNS
X1 R1 2.0 R2 1.0
X1 R3 5.0 Z 3.0
X2 R1 -1.0 R2 -1.0
X2 R3 3.0 Z 7.0
X3 R1 1.0 R2 -6.0
X3 Z -1.0
X4 R1 -1.0 R2 4.0
X4 R3 1.0 Z 1.0
RHS
RHS1 R1 1.0
RHS1 R2 8.0
RHS1 R3 5.0
BOUNDS
UP BND1 X1 4.0
LO BND1 X2 2.0
UI BND1 X2 5.0
BV BND1 X3
LO BND1 X4 3.0
UP BND1 X4 8.0
ENDATA

View file

@ -0,0 +1,382 @@
/*
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
// reads an MPS file reperesenting a Mixed Integer Program
#include <string>
#include <vector>
#include <unordered_map>
#include "util/lp/lp_primal_simplex.h"
#include "util/lp/lp_dual_simplex.h"
#include "util/lp/lar_solver.h"
#include <iostream>
#include <fstream>
#include <util/numerics/mpq.h>
#include <functional>
#include <algorithm>
#include "tests/util/lp/mps_reader.h"
#include "util/lp/lar_constraints.h"
#include <sstream>
#include <cstdlib>
namespace lean {
using namespace std;
template<typename T>
T from_string(const std::string& str) {
std::istringstream ss(str);
T ret;
ss >> ret;
return ret;
}
class smt_reader {
public:
struct lisp_elem {
string m_head;
vector<lisp_elem> m_elems;
void print() {
if (m_elems.size()) {
cout << '(';
cout << m_head << ' ';
for (auto & el : m_elems)
el.print();
cout << ')';
} else {
cout << " " << m_head;
}
}
unsigned size() const { return m_elems.size(); }
bool is_simple() const { return size() == 0; }
};
struct formula_constraint {
lconstraint_kind m_kind;
vector<pair<mpq, string>> m_coeffs;
mpq m_right_side = numeric_traits<mpq>::zero();
void add_pair(mpq c, string name) {
m_coeffs.push_back(make_pair(c, name));
}
};
lisp_elem m_formula_lisp_elem;
vector<formula_constraint> m_constraints;
string m_file_name;
ifstream m_file_stream;
string m_line;
bool m_is_OK = true;
unsigned m_line_number = 0;
smt_reader(string file_name):
m_file_name(file_name), m_file_stream(file_name) {
}
void set_error() {
cout << "setting error" << endl;
m_is_OK = false;
}
bool is_ok() {
return m_is_OK;
}
bool prefix(const char * pr) {
return m_line.find(pr) == 0;
}
int first_separator() {
unsigned blank_pos = m_line.find(' ');
unsigned br_pos = m_line.find('(');
unsigned reverse_br_pos = m_line.find(')');
return min(blank_pos, min(br_pos, reverse_br_pos));
}
void fill_lisp_elem(lisp_elem & lm) {
if (m_line[0] == '(')
fill_nested_elem(lm);
else
fill_simple_elem(lm);
}
void fill_simple_elem(lisp_elem & lm) {
int separator = first_separator();
lean_assert(-1 != separator && separator != 0);
lm.m_head = m_line.substr(0, separator);
m_line = m_line.substr(separator);
}
void fill_nested_elem(lisp_elem & lm) {
lean_assert(m_line[0] == '(');
m_line = m_line.substr(1);
int separator = first_separator();
lm.m_head = m_line.substr(0, separator);
m_line = m_line.substr(lm.m_head.size());
eat_blanks();
while (m_line.size()) {
if (m_line[0] == '(') {
lisp_elem el;
fill_nested_elem(el);
lm.m_elems.push_back(el);
} else {
if (m_line[0] == ')') {
m_line = m_line.substr(1);
break;
}
lisp_elem el;
fill_simple_elem(el);
lm.m_elems.push_back(el);
}
eat_blanks();
}
}
void eat_blanks() {
while (m_line.size()) {
if (m_line[0] == ' ')
m_line = m_line.substr(1);
else
break;
}
}
void fill_formula_elem() {
fill_lisp_elem(m_formula_lisp_elem);
}
void parse_line() {
if (m_line.find(":formula") == 0) {
int first_br = m_line.find('(');
if (first_br == -1) {
cout << "empty formula" << endl;
return;
}
m_line = m_line.substr(first_br);
fill_formula_elem();
}
}
void set_constraint_kind(formula_constraint & c, lisp_elem & el) {
if (el.m_head == "=") {
c.m_kind = EQ;
} else if (el.m_head == ">=") {
c.m_kind = GE;
} else if (el.m_head == "<=") {
c.m_kind = LE;
} else if (el.m_head == ">") {
c.m_kind = GT;
} else if (el.m_head == "<") {
c.m_kind = LT;
} else {
cout << "kind " << el.m_head << " is not supported " << endl;
set_error();
}
}
void adjust_rigth_side(formula_constraint & /* c*/, lisp_elem & /*el*/) {
// lean_assert(el.m_head == "0"); // do nothing for the time being
}
void set_constraint_coeffs(formula_constraint & c, lisp_elem & el) {
lean_assert(el.m_elems.size() == 2);
set_constraint_coeffs_on_coeff_element(c, el.m_elems[0]);
adjust_rigth_side(c, el.m_elems[1]);
}
bool is_integer(string & s) {
if (s.size() == 0) return false;
return atoi(s.c_str()) != 0 || isdigit(s.c_str()[0]);
}
void add_complex_sum_elem(formula_constraint & c, lisp_elem & el) {
if (el.m_head == "*") {
add_mult_elem(c, el.m_elems);
} else if (el.m_head == "~") {
lisp_elem & minel = el.m_elems[0];
lean_assert(minel.is_simple());
c.m_right_side += mpq(str_to_int(minel.m_head));
} else {
cout << "unexpected input " << el.m_head << endl;
set_error();
return;
}
}
string get_name(lisp_elem & name) {
lean_assert(name.is_simple());
lean_assert(!is_integer(name.m_head));
return name.m_head;
}
void add_mult_elem(formula_constraint & c, vector<lisp_elem> & els) {
lean_assert(els.size() == 2);
mpq coeff = get_coeff(els[0]);
string col_name = get_name(els[1]);
c.add_pair(coeff, col_name);
}
mpq get_coeff(lisp_elem & le) {
if (le.is_simple()) {
return mpq(str_to_int(le.m_head));
} else {
lean_assert(le.m_head == "~");
lean_assert(le.size() == 1);
lisp_elem & el = le.m_elems[0];
lean_assert(el.is_simple());
return -mpq(str_to_int(el.m_head));
}
}
int str_to_int(string & s) {
lean_assert(is_integer(s));
return atoi(s.c_str());
}
void add_sum_elem(formula_constraint & c, lisp_elem & el) {
if (el.size()) {
add_complex_sum_elem(c, el);
} else {
lean_assert(is_integer(el.m_head));
int v = atoi(el.m_head.c_str());
mpq vr(v);
c.m_right_side -= vr;
}
}
void add_sum(formula_constraint & c, vector<lisp_elem> & sum_els) {
for (auto & el : sum_els)
add_sum_elem(c, el);
}
void set_constraint_coeffs_on_coeff_element(formula_constraint & c, lisp_elem & el) {
if (el.m_head == "*") {
add_mult_elem(c, el.m_elems);
} else if (el.m_head == "+") {
add_sum(c, el.m_elems);
} else {
lean_assert(false); // unexpected input
}
}
void create_constraint(lisp_elem & el) {
formula_constraint c;
set_constraint_kind(c, el);
set_constraint_coeffs(c, el);
m_constraints.push_back(c);
}
void fill_constraints() {
if (m_formula_lisp_elem.m_head != "and") {
cout << "unexpected top element " << m_formula_lisp_elem.m_head << endl;
set_error();
return;
}
for (auto & el : m_formula_lisp_elem.m_elems)
create_constraint(el);
}
void read() {
if (!m_file_stream.is_open()){
cout << "cannot open file " << m_file_name << endl;
set_error();
return;
}
while (m_is_OK && getline(m_file_stream, m_line)) {
parse_line();
m_line_number++;
}
m_file_stream.close();
fill_constraints();
}
/*
void fill_lar_solver_on_row(row * row, lar_solver *solver) {
if (row->m_name != m_cost_row_name) {
lar_constraint c(get_lar_relation_from_row(row->m_type), row->m_right_side);
for (auto s : row->m_row_columns) {
var_index i = solver->add_var(s.first);
c.add_variable_to_constraint(i, s.second);
}
solver->add_constraint(&c);
} else {
// ignore the cost row
}
}
void fill_lar_solver_on_rows(lar_solver * solver) {
for (auto row_it : m_rows) {
fill_lar_solver_on_row(row_it.second, solver);
}
}
void create_low_constraint_for_var(column* col, bound * b, lar_solver *solver) {
lar_constraint c(GE, b->m_low);
var_index i = solver->add_var(col->m_name);
c.add_variable_to_constraint(i, numeric_traits<T>::one());
solver->add_constraint(&c);
}
void create_upper_constraint_for_var(column* col, bound * b, lar_solver *solver) {
lar_constraint c(LE, b->m_upper);
var_index i = solver->add_var(col->m_name);
c.add_variable_to_constraint(i, numeric_traits<T>::one());
solver->add_constraint(&c);
}
void create_equality_contraint_for_var(column* col, bound * b, lar_solver *solver) {
lar_constraint c(EQ, b->m_fixed_value);
var_index i = solver->add_var(col->m_name);
c.add_variable_to_constraint(i, numeric_traits<T>::one());
solver->add_constraint(&c);
}
void fill_lar_solver_on_columns(lar_solver * solver) {
for (auto s : m_columns) {
mps_reader::column * col = s.second;
solver->add_var(col->m_name);
auto b = col->m_bound;
if (b == nullptr) return;
if (b->m_free) continue;
if (b->m_low_is_set) {
create_low_constraint_for_var(col, b, solver);
}
if (b->m_upper_is_set) {
create_upper_constraint_for_var(col, b, solver);
}
if (b->m_value_is_fixed) {
create_equality_contraint_for_var(col, b, solver);
}
}
}
*/
void add_constraint_to_solver(lar_solver * solver, formula_constraint & fc) {
buffer<pair<mpq, var_index>> ls;
for (auto & it : fc.m_coeffs) {
ls.push_back(make_pair(it.first, solver->add_var(it.second)));
}
solver->add_constraint(ls, fc.m_kind, fc.m_right_side);
}
void fill_lar_solver(lar_solver * solver) {
for (formula_constraint & fc : m_constraints)
add_constraint_to_solver(solver, fc);
}
lar_solver * create_lar_solver() {
lar_solver * ls = new lar_solver();
fill_lar_solver(ls);
return ls;
}
};
}

View file

@ -0,0 +1,74 @@
/*
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
// reads a text file
#include <string>
#include <vector>
#include <unordered_map>
#include <iostream>
#include <fstream>
#include <util/numerics/mpq.h>
#include "util/lp/lp_solver.h"
namespace lean {
using namespace std;
template <typename T>
struct test_result {
lp_status m_status;
T m_cost;
unordered_map<string, T> column_values;
};
template <typename T>
class test_file_reader {
struct raw_blob {
vector<string> m_unparsed_strings;
vector<raw_blob> m_blobs;
};
struct test_file_blob {
string m_name;
string m_content;
unordered_map<string, string> m_table;
unordered_map<string, test_file_blob> m_blobs;
test_result<T> * get_test_result() {
test_result<T> * tr = new test_result<T>();
throw "not impl";
return tr;
}
};
ifstream m_file_stream;
public:
// constructor
test_file_reader(string file_name) : m_file_stream(file_name) {
if (!m_file_stream.is_open()) {
cout << "cannot open file " << "\'" << file_name << "\'" << endl;
}
}
raw_blob scan_to_row_blob() {
}
test_file_blob scan_row_blob_to_test_file_blob(raw_blob rblob) {
}
test_result<T> * get_test_result() {
if (!m_file_stream.is_open()) {
return nullptr;
}
raw_blob rblob = scan_to_row_blob();
test_file_blob tblob = scan_row_blob_to_test_file_blob(rblob);
return tblob.get_test_result();
}
};
}

File diff suppressed because it is too large Load diff

Binary file not shown.

View file

@ -0,0 +1 @@
add_library(lp OBJECT lp.cpp)

View file

@ -0,0 +1,230 @@
/*
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>
namespace lean {
using std::vector;
// the elements with the smallest priority are dequeued first
template <typename T>
class binary_heap_priority_queue {
vector<T> m_priorities;
// indexing for A starts from 1
vector<unsigned> m_heap; // keeps the elements of the queue
vector<int> m_heap_inverse; // o = m_heap[m_heap_inverse[o]]
unsigned m_heap_size = 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;
}
}
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]])){
cout << "m_heap_size = " << m_heap_size << endl;
cout << "i = " << i << endl;
cout << "m_heap[i] = " << m_heap[i] << endl;
cout << "ch = " << ch << endl;
cout << "m_heap[ch] = " << m_heap[ch] << endl;
cout << "m_priorities[m_heap[i]] = " << m_priorities[m_heap[i]] << endl;
cout << "m_priorities[m_heap[ch]] = " << m_priorities[m_heap[ch]]<< endl;
return false;
}
ch++;
}
}
return true;
}
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());
}
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)
{ }
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 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;
}
}
// 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;
}
/// return the first element of the queue and removes it from the queue
unsigned dequeue_and_get_priority(T & priority) {
if (m_heap_size == 0) {
throw "invalid size";
}
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--;
}
}
/// 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() {
vector<int> index;
vector<T> prs;
while (size()) {
T prior;
int j = dequeue_and_get_priority(prior);
index.push_back(j);
prs.push_back(prior);
cout << "(" << j << ", " << prior << ")";
}
cout << endl;
// restore the queue
for (int i = 0; i < index.size(); i++)
enqueue(index[i], prs[i]);
}
};
}

View file

@ -0,0 +1,142 @@
/*
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/binary_heap_priority_queue.h"
#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;
unordered_map<upair, unsigned> m_pairs_to_index;
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()) {
cout << "for pair (" << p.first.first << ", " << p.first.second << "), the index " << j << " is already inside " << 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();
}
};
}

22
src/util/lp/breakpoint.h Normal file
View file

@ -0,0 +1,22 @@
/*
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
namespace lean {
enum breakpoint_type {
low_break, upper_break, fixed_break
};
template <typename X>
struct breakpoint {
unsigned m_j; // the basic column
breakpoint_type m_type;
X m_delta;
breakpoint(){}
breakpoint(unsigned j, X delta, breakpoint_type type):m_j(j), m_type(type), m_delta(delta) {}
};
}

View file

@ -0,0 +1,84 @@
/*
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 <string>
#include <algorithm>
namespace lean {
typedef unsigned var_index;
typedef unsigned constraint_index;
enum lconstraint_kind {
LE = -2, LT = -1 , GE = 2, GT = 1, EQ = 0
};
class lar_normalized_constraint; // forward definition
bool compare(const pair<mpq, var_index> & a, const pair<mpq, var_index> & b) {
return a.second < b.second;
}
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;
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) {
for (auto it : buffer) {
if (numeric_traits<mpq>::is_zero(it.first)) continue;
m_coeffs.push_back(it);
}
std::sort(m_coeffs.begin(), m_coeffs.end(), compare);
normalize();
}
void set_name(string name) {
m_column_info.set_name(name);
}
unsigned size() const { return m_coeffs.size(); }
void normalize() {
if (m_coeffs.size() == 0) return;
auto t = m_coeffs[0].first;
for (auto & it : m_coeffs)
it.first /= t;
}
bool operator==(const canonic_left_side& a) const {
if (m_coeffs.size() != a.m_coeffs.size()) return false;
for (unsigned i = 0; i < m_coeffs.size(); i++) {
if (m_coeffs[i] != a.m_coeffs[i])
return false;
}
return true;
}
std::size_t hash_of_ls() const {
std::size_t ret = 0;
std::hash<pair<mpq, var_index>> hash_fun;
for (auto v : m_coeffs) {
ret |= (hash_fun(v) << 2);
}
return ret;
}
};
struct hash_and_equal_of_canonic_left_side_struct {
std::size_t operator() (const canonic_left_side* ls) const {
return ls->hash_of_ls();
}
bool operator() (const canonic_left_side* a, const canonic_left_side* b) const {
return (*a) == (*b);
}
};
}

199
src/util/lp/column_info.h Normal file
View file

@ -0,0 +1,199 @@
/*
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/lp_primal_core_solver.h"
#include "util/lp/lp_solver.h"
#include <vector>
#include <unordered_map>
#include <string>
#include <algorithm>
namespace lean {
using std::vector;
using std::tuple;
template <typename T>
class column_info {
string m_name;
bool m_low_bound_is_set = false;
bool m_low_bound_is_strict = false;
bool m_upper_bound_is_set = false;
bool m_upper_bound_is_strict = false;
T m_low_bound;
T m_upper_bound;
T m_cost = numeric_traits<T>::zero();
T m_fixed_value;
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_no_flipping() {
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? upper_bound
: free_column;
}
T get_low_bound() const {
lean_assert(m_low_bound_is_set);
return m_low_bound;
}
T get_upper_bound() const {
lean_assert(m_upper_bound_is_set);
return m_upper_bound;
}
bool low_bound_is_set() const {
return m_low_bound_is_set;
}
bool upper_bound_is_set() const {
return m_upper_bound_is_set;
}
T get_shift() {
if (is_fixed()) {
return m_fixed_value;
}
if (is_flipped()){
return m_upper_bound;
}
return m_low_bound_is_set? m_low_bound : numeric_traits<T>::zero();
}
bool is_flipped() {
return m_upper_bound_is_set && !m_low_bound_is_set;
}
bool adjusted_low_bound_is_set() {
return !is_flipped()? low_bound_is_set(): upper_bound_is_set();
}
bool adjusted_upper_bound_is_set() {
return !is_flipped()? upper_bound_is_set(): low_bound_is_set();
}
T get_adjusted_upper_bound() {
return get_upper_bound() - get_low_bound();
}
bool is_fixed() const {
return m_is_fixed;
}
bool is_free() {
return !m_low_bound_is_set && !m_upper_bound_is_set;
}
void set_fixed_value(T v) {
m_is_fixed = true;
m_fixed_value = v;
}
T get_fixed_value() const {
lean_assert(m_is_fixed)
return m_fixed_value;
}
T get_cost() const {
return m_cost;
}
void set_cost(T const & cost) {
m_cost = cost;
}
void set_name(string const & s) {
m_name = s;
}
string get_name() const {
return m_name;
}
void set_low_bound(T const & l) {
m_low_bound = l;
m_low_bound_is_set = true;
}
void set_upper_bound(T const & l) {
m_upper_bound = l;
m_upper_bound_is_set = true;
}
void unset_low_bound() {
m_low_bound_is_set = false;
}
void unset_upper_bound() {
m_upper_bound_is_set = false;
}
void unset_fixed() {
m_is_fixed = false;
}
bool low_bound_holds(T v) {
return !low_bound_is_set() || v >= m_low_bound -T(0.0000001);
}
bool upper_bound_holds(T v) {
return !upper_bound_is_set() || v <= m_upper_bound + T(0.000001);
}
bool bounds_hold(T v) {
return low_bound_holds(v) && upper_bound_holds(v);
}
bool adjusted_bounds_hold(T v) {
return adjusted_low_bound_holds(v) && adjusted_upper_bound_holds(v);
}
bool adjusted_low_bound_holds(T v) {
return !adjusted_low_bound_is_set() || v >= -T(0.0000001);
}
bool adjusted_upper_bound_holds(T v) {
return !adjusted_upper_bound_is_set() || v <= get_adjusted_upper_bound() + T(0.000001);
}
bool is_infeasible() {
return upper_bound_is_set() && low_bound_is_set() && get_upper_bound() < get_low_bound();
}
bool low_bound_is_strict() const {
return m_low_bound_is_strict;
}
void set_low_bound_strict(bool val) {
m_low_bound_is_strict = val;
}
bool upper_bound_is_strict() const {
return m_upper_bound_is_strict;
}
void set_upper_bound_strict(bool val) {
m_upper_bound_is_strict = val;
}
};
}

View file

@ -0,0 +1,388 @@
/*
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 <string>
#include <algorithm>
namespace lean {
template <typename T, typename X> class lp_core_solver_base; // forward definition
template <typename T, typename X>
class core_solver_pretty_printer {
lp_core_solver_base<T, X> & m_core_solver;
vector<unsigned> m_column_widths;
vector<vector<string>> m_A;
vector<vector<string>> m_signs;
vector<string> m_costs;
vector<string> m_cost_signs;
vector<string> m_lows; // low bounds
vector<string> m_upps; // upper bounds
vector<string> m_lows_signs;
vector<string> m_upps_signs;
unsigned m_rs_width;
vector<X> m_rs;
unsigned m_title_width;
std::string m_cost_title;
std::string m_basis_heading_title;
std::string m_x_title;
std::string m_low_bounds_title = "low";
std::string m_upp_bounds_title = "upp";
std::string m_exact_norm_title = "exact cn";
std::string m_approx_norm_title = "approx cn";
unsigned ncols() { return m_core_solver.m_A.column_count(); }
unsigned nrows() { return m_core_solver.m_A.row_count(); }
unsigned m_artificial_start = UINT_MAX;
T * m_w_buff;
T * m_ed_buff;
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());
}
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));
}
}
}
~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;
}
}
}
T current_column_norm() {
T ret = zero_of_type<T>();
for (T & ed : m_core_solver.m_ed)
ret += ed * ed;
return ret;
}
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_column_widths() {
for (unsigned i = 0; i < ncols(); i++) {
m_column_widths[i] = get_column_width(i);
}
}
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_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_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 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;
}
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 print_x() {
if (ncols() == 0) {
return;
}
int blanks = m_title_width + 1 - m_x_title.size();
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);
cout << s << " "; // the column interval
}
cout << 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 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();
}
}
void print_lows() {
if (ncols() == 0) {
return;
}
int blanks = m_title_width + 1 - m_low_bounds_title.size();
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);
cout << s << " "; // the column interval
}
cout << endl;
}
void print_upps() {
if (ncols() == 0) {
return;
}
int blanks = m_title_width + 1 - m_upp_bounds_title.size();
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);
cout << s << " "; // the column interval
}
cout << endl;
}
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();
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);
cout << s << " ";
}
cout << endl;
}
void print_approx_norms() {
int blanks = m_title_width + 1 - m_approx_norm_title.size();
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);
cout << s << " ";
}
cout << endl;
}
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();
cout << endl;
}
void print_basis_heading() {
int blanks = m_title_width + 1 - m_basis_heading_title.size();
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);
cout << s << " "; // the column interval
}
cout << endl;
}
void print_bottom_line() {
std::cout << "----------------------" << std::endl;
}
void print_cost() {
int blanks = m_title_width + 1 - m_cost_title.size();
cout << m_cost_title;
print_blanks(blanks);
print_given_rows(m_costs, m_cost_signs, m_core_solver.get_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);
cout << s << ' ';
if (col < row.size() - 1) {
cout << signs[col + 1] << ' ';
}
}
cout << '=';
string rs = T_to_string(rst);
int nb = m_rs_width - rs.size();
lean_assert(nb >= 0);
print_blanks(nb + 1);
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);
}
};
}

179
src/util/lp/eta_matrix.h Normal file
View file

@ -0,0 +1,179 @@
/*
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
namespace lean {
// This is the sum of a unit matrix and a one-column matrix
template <typename T, typename X>
class eta_matrix
: public tail_matrix<T, X> {
#ifndef NDEBUG
unsigned m_length;
#endif
unsigned m_column_index;
sparse_vector<T> m_column_vector;
T m_diagonal_element;
public:
#ifndef NDEBUG
eta_matrix(unsigned column_index, unsigned length):
#else
eta_matrix(unsigned column_index):
#endif
#ifndef NDEBUG
m_length(length),
#endif
m_column_index(column_index) {
}
// T & get_column_element(unsigned j) {
// return m_column[j];
// }
void print() {
print_matrix(*this);
}
bool is_unit() {
return m_column_vector.size() == 0 && m_diagonal_element == 1;
}
void set_diagonal_element(T const & diagonal_element) {
lean_assert(!lp_settings::is_eps_small_general(diagonal_element, 1e-12));
m_diagonal_element = diagonal_element;
}
const T & get_diagonal_element() const {
return m_diagonal_element;
}
void apply_from_left(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();
}
}
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_to_T(indexed_vector<T> & w, lp_settings & settings) {
apply_from_left_local(w, settings);
}
void push_back(unsigned row_index, T val ) {
lean_assert(row_index != m_column_index);
m_column_vector.push_back(row_index, val);
}
void apply_from_right(vector<T> & w) {
#ifndef NDEBUG
// 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;
#ifndef NDEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w, get_number_of_rows()));
// delete clone_w;
#endif
}
// void apply_from_right(indexed_vector<T> & w) {
// #ifndef NDEBUG
// // 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();
// }
// if (numeric_traits<T>::is_zero(w[m_column_index])) {
// w.m_index.push_back(m_column_index);
// }
// w[m_column_index] = t; // we might get a zero here
// #ifndef NDEBUG
// // lean_assert(vectors_are_equal<T>(clone_w, w, get_number_of_rows()));
// // delete clone_w;
// #endif
// }
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();
}
#ifndef NDEBUG
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)
#ifndef NDEBUG
// 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);
}
#ifndef NDEBUG
// lean_assert(deb == *this);
#endif
}
};
}

View file

@ -0,0 +1,58 @@
/*
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/numerics/float.h"
#include "util/numerics/double.h"
#include "util/numerics/mpq.h"
namespace lean {
template <typename T>
class indexed_value {
public:
T m_value;
// the idea is that m_index for a row element gives its column, and for a column element its row
unsigned m_index;
// m_other point is the offset of the corresponding element in its vector : for a row element it point to the column element offset,
// for a column element it points to the row element offset
unsigned m_other;
indexed_value() {}
indexed_value(T v, unsigned i) : m_value(v), m_index(i) {}
indexed_value(T v, unsigned i, unsigned other) :
m_value(v), m_index(i), m_other(other) {
}
indexed_value(const indexed_value & iv) {
m_value = iv.m_value;
m_index = iv.m_index;
m_other = iv.m_other;
}
indexed_value & operator=(const indexed_value & right_side) {
m_value = right_side.m_value;
m_index = right_side.m_index;
m_other = right_side.m_other;
return *this;
}
T value() const {
return m_value;
}
void set_value(T val) {
m_value = val;
}
};
template <typename X>
bool check_vector_for_small_values(indexed_vector<X> & w, lp_settings & settings) {
for (unsigned i : w.m_index) {
const X & v = w[i];
if ((!is_zero(v)) && settings.abs_val_is_smaller_than_drop_tolerance(v))
return false;
}
return true;
}
}

View file

@ -0,0 +1,144 @@
/*
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/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 <string>
#include "util/lp/sparse_vector.h"
#include <iomanip>
namespace lean {
using std::string;
using std::cout;
using std::endl;
using std::vector;
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 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 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 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>
class indexed_vector {
public:
// m_index points to non-zero elements of m_data
buffer<T> m_data;
vector<unsigned> m_index;
indexed_vector(unsigned data_size) {
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());
}
unsigned data_size() const {
return m_data.size();
}
unsigned size() {
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();
}
const T& operator[] (unsigned i) const {
return m_data[i];
}
T& operator[] (unsigned i) {
return m_data[i];
}
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);
}
#ifndef NDEBUG
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() {
cout << "m_index " << endl;
for (unsigned i = 0; i < m_index.size(); i++) {
cout << m_index[i] << " ";
}
cout << endl;
print_vector(m_data);
}
#endif
};
}

View file

@ -0,0 +1,116 @@
/*
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/buffer.h"
#include "util/numerics/numeric_traits.h"
#include "util/numerics/xnumeral.h"
#include <unordered_map>
#include <string>
#include <algorithm>
#include "util/lp/canonic_left_side.h"
namespace lean {
lconstraint_kind flip_kind(lconstraint_kind t) {
return static_cast<lconstraint_kind>( - static_cast<int>(t));
}
std::string lconstraint_kind_string(lconstraint_kind t) {
switch (t) {
case LE: return string("<=");
case LT: return string("<");
case GE: return string(">=");
case GT: return string(">");
case EQ: return string("=");
default:
throw "unexpected";
}
}
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;
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) {}
virtual unsigned size() const = 0;
virtual ~lar_base_constraint(){}
};
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 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));
}
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;
}
};
class lar_normalized_constraint : public lar_base_constraint {
public:
canonic_left_side* m_canonic_left_side;
mpq m_ratio_to_original; // by multiplying this constraint by m_ratio_to_original we get the original one
lar_constraint m_origin_constraint;
lar_normalized_constraint(canonic_left_side * ls, mpq ratio, lconstraint_kind kind, mpq right_side, const lar_constraint & origin):
lar_base_constraint(kind, right_side, origin.m_index),
m_canonic_left_side(ls),
m_ratio_to_original(ratio),
m_origin_constraint(origin) {
}
lar_normalized_constraint() {}
// lar_normalized_constraint & operator=(lar_normalized_constraint & other) {
// m_canonic_left_side = other.m_canonic_left_side;
// m_origin_constraint = other.m_origin_constraint;
// m_kind = other.m_kind;
// m_right_side = other.m_right_side;
// return *this;
// }
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 {
return m_canonic_left_side->size();
}
};
}

View file

@ -0,0 +1,870 @@
/*
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 <string>
#include "util/lp/lp_core_solver_base.h"
#include <algorithm>
#include "util/lp/indexed_vector.h"
#include "util/lp/binary_heap_priority_queue.h"
#include "util/lp/breakpoint.h"
namespace lean {
template <typename T, typename X>
class lar_core_solver : public lp_core_solver_base<T, X> {
// m_sign_of_entering is set to 1 if the entering variable needs
// to grow and is set to -1 otherwise
int m_sign_of_entering_delta;
X m_infeasibility;
vector<unsigned> m_tight_basic_columns;
vector<breakpoint<X>> m_breakpoints;
binary_heap_priority_queue<X> m_breakpoint_indices_queue;
vector<pair<mpq, unsigned>> m_infeasible_row;
int m_infeasible_row_sign = 0;
// with a breakpoint at this delta
public:
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, string> & column_names,
vector<X> & right_side,
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) {
}
int get_infeasible_row_sign() const {
return m_infeasible_row_sign;
}
const 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)) {
cout << "inf was " << T_to_string(inf) << " and now " << T_to_string(m_infeasibility) << endl;
}
lean_assert(this->m_total_iterations ==0 || inf >= m_infeasibility);
if (inf == m_infeasibility)
this->m_iters_with_no_cost_growing++;
}
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);
}
// 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;
}
}
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;
}
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();
}
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() {
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)) {
// cout << "column " << j << ", " << this->column_name(j) << " inf is " << d.get_double() << endl;
// }
ret += d;
}
return ret;
}
bool debug_profit_delta(unsigned j, const T & delta) {
this->update_x(j, delta);
bool ret = m_infeasibility > get_deb_inf();
if (ret) {
cout << "found profit for " << this->column_name(j) << " and delta = " << delta.get_double() << endl;
cout << "improvement = " << (m_infeasibility - get_deb_inf()).get_double() << endl;
}
return ret;
}
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);
}
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;
}
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) {
cout << "cannot choose entering" << endl;
decide_on_status_when_cannot_enter();
} else {
advance_on_entering(entering);
}
}
void decide_on_status_when_cannot_enter() {
if (!is_zero(m_infeasibility))
this->m_status = INFEASIBLE;
else
this->m_status = FEASIBLE;
cout << "status is " << lp_status_to_string(this->m_status) << endl;
}
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);
}
// 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 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));
}
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;
}
}
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";
}
void print_breakpoint(const breakpoint<X> * b) {
cout << "(" << this->column_name(b->m_j) << "," << break_type_to_string(b->m_type) << "," << T_to_string(b->m_delta) << ")" << endl;
print_bound_info_and_x(b->m_j);
}
void print_bound_info_and_x(unsigned j) {
cout << "type of " << this->column_name(j) << " is " << column_type_to_string(this->m_column_type[j]) << endl;
cout << "x[" << this->column_name(j) << "] = " << this->m_x[j] << endl;
switch (this->m_column_type[j]) {
case fixed:
case boxed:
cout << "[" << this->m_low_bound_values[j] << "," << this->m_upper_bound_values[j] << "]" << endl;
break;
case low_bound:
cout << "[" << this->m_low_bound_values[j] << ", inf" << endl;
break;
case upper_bound:
cout << "inf ," << this->m_upper_bound_values[j] << "]" << endl;
break;
case free_column:
cout << "inf, inf" << endl;
break;
default:
lean_assert(false);
break;
}
}
void clear_breakpoints() {
m_breakpoints.clear();
m_breakpoint_indices_queue.clear();
}
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() {
cout << "reduced costs " << endl;
for (unsigned j = 0; j < this->m_n; j++) {
if (numeric_traits<T>::is_zero(this->m_d[j])) continue;
cout << T_to_string(this->m_d[j]) << this->column_name(j) << " ";
}
cout << 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:
throw 1;
lean_assert(false); // such a type does not exist
}
}
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;
}
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()) {
cout << "found evidence" << endl;
} else {
cout << "did not find evidence" << endl;
cout << "started feasibility_loop at iteration " << this->m_total_iterations << endl;
unsigned iters = this->m_total_iterations;
this->m_status = FEASIBLE;
row_feasibility_loop();
cout << "made another " << this->m_total_iterations - iters << ", percentage is " << 100.0 * (this->m_total_iterations - iters) / this->m_total_iterations << endl;
}
}
return true;
}
if (this->m_iters_with_no_cost_growing >= this->m_settings.max_number_of_iterations_with_no_improvements) {
cout << "m_iters_with_no_cost_growing = " << this->m_iters_with_no_cost_growing << endl;
this->m_status = ITERATIONS_EXHAUSTED; return true;
}
if (this->m_total_iterations >= this->m_settings.max_total_number_of_iterations) {
cout << "max_total_number_of_iterations " << this->m_total_iterations << " is reached " << endl;
this->m_status = ITERATIONS_EXHAUSTED; return true;
}
return false;
}
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;
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;
}
}
template <typename L>
int get_sign(const L & v) {
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]
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_assert(false);
throw "cannot be here";
break;
}
}
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 update_delta_of_entering_and_leaving_candidates(X del, X & delta,
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);
}
}
void update_delta_of_entering(int delta_sign, unsigned row, X & delta,
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;
}
}
unsigned find_leaving_for_inf_row_strategy(vector<unsigned> & leaving_candidates) {
lean_assert(leaving_candidates.size());
return leaving_candidates[lrand48() % leaving_candidates.size()]; // more randomness
}
X find_initial_delta_and_its_sign(unsigned row, unsigned entering,
int inf_sign, int & entering_delta_sign,
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;
}
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;
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) {
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) {
cout << "optimizing by rows " << endl;
row_feasibility_loop();
} else {
cout << "optimizing total infeasibility" << endl;
feasibility_loop();
}
}
bool low_bounds_are_set() const { return true; }
void print_column_info(unsigned j) {
cout << "type = " << column_type_to_string(this->m_column_type[j]) << endl;
switch (this->m_column_type[j]) {
case fixed:
case boxed:
cout << "(" << this->m_low_bound_values[j] << ", " << this->m_upper_bound_values[j] << ")" << endl;
break;
case low_bound:
cout << this->m_low_bound_values[j] << endl;
break;
case upper_bound:
cout << this->m_upper_bound_values[j] << endl;
break;
default:
lean_assert(false);
break;
}
}
};
}

View file

@ -0,0 +1,18 @@
/*
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/lp/lp_settings.h"
#include <unordered_map>
namespace lean {
struct lar_solution_signature {
std::unordered_map<unsigned, non_basic_column_value_position> non_basic_column_value_positions;
lp_status status;
};
}

884
src/util/lp/lar_solver.h Normal file
View file

@ -0,0 +1,884 @@
/*
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/buffer.h"
#include "util/numerics/numeric_traits.h"
#include "util/numerics/xnumeral.h"
#include <unordered_map>
#include <unordered_set>
#include <string>
#include "util/lp/lar_constraints.h"
#include <functional>
#include "util/lp/lar_core_solver.h"
#include <algorithm>
#include "util/lp/numeric_pair.h"
#include "util/lp/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();
}
};
}
namespace lean {
using std::string;
using std::cout;
using std::endl;
using std::vector;
using std::unordered_map;
using std::unordered_set;
template <typename V>
struct conversion_helper {
static V get_low_bound(const column_info<mpq> & ci) {
return V(ci.get_low_bound(), ci.low_bound_is_strict()? 1 : 0);
}
static V get_upper_bound(const column_info<mpq> & ci) {
return V(ci.get_upper_bound(), ci.upper_bound_is_strict()? -1 : 0);
}
};
template<>
struct conversion_helper <double> {
static double get_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;
}
};
class lar_solver {
unsigned m_available_var_index = 0;
unsigned m_available_constr_index = 0;
lp_status m_status = UNKNOWN;
unordered_map<string, var_index> m_var_names_to_var_index;
unordered_map<var_index, canonic_left_side*> m_map_from_var_index_to_left_side;
unordered_set<canonic_left_side*, hash_and_equal_of_canonic_left_side_struct, hash_and_equal_of_canonic_left_side_struct> m_canonic_left_sides;
unordered_map<unsigned, canonic_left_side*> m_column_indices_to_canonic_left_sides;
unordered_map<constraint_index, lar_normalized_constraint> m_normalized_constraints;
static_matrix<mpq, numeric_pair<mpq>> m_A;
lar_core_solver<mpq, numeric_pair<mpq>> m_mpq_core_solver;
lp_settings m_settings;
vector<column_type> m_column_types; // this field is passed to the core solver
vector<numeric_pair<mpq>> m_low_bounds; // passed to the core solver
vector<numeric_pair<mpq>> m_upper_bounds; // passed to the core solver
vector<unsigned> m_basis;
vector<numeric_pair<mpq>> m_x; // the solution
unordered_map<unsigned, string> m_column_names;
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
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;
}
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();
}
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;
}
void add_canonic_left_side_for_var(var_index i, 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 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_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 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 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);
#ifndef NDEBUG
// print_matrix(m_A);
#endif
}
// void 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 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 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 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 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:
throw "unexpected";
}
}
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));
string name = ci.get_name();
if (name.size() == 0)
name = 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);
}
}
template <typename V>
void fill_bounds_for_core_solver(vector<V> & lb, 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 resize_x_and_init_with_zeros(vector<V> & x, unsigned n) {
x.clear();
x.resize(n, zero_of_type<V>()); // init with zeroes
}
template <typename V>
void resize_x_and_init_with_signature(vector<V> & x, vector<V> & low_bound,
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 get_column_val(vector<V> & low_bound, 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:
throw "unexpected type";
}
}
public:
~lar_solver() {
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;
}
lp_settings & settings() { return m_settings;}
void clear() {
lean_assert(false); // not implemented
}
lar_solver() : m_mpq_core_solver(m_x,
m_column_types,
m_low_bounds,
m_upper_bounds,
m_basis,
m_A,
m_settings,
m_column_names,
m_right_side_vector,
m_costs) {
}
var_index add_var(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 add_constraint(const buffer<pair<mpq, var_index>>& left_side, lconstraint_kind kind_par, mpq right_side_par) {
if (left_side.size() == 0) {
cout << "cannot add a constraint without left side" << 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 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() {
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);
cout << endl;
return false;
}
return true;
}
bool constraint_holds(const lar_constraint & constr, 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:
throw "unexpected";
return false;
}
}
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());
}
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;
cout << coeff.get_double() << endl;
lar_constraint & constr = m_normalized_constraints[con_ind].m_origin_constraint;
print_constraint(&constr); cout << 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 : LE;
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 register_in_map(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) {
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);
}
template <typename V>
void init_right_sides_with_zeros(vector<V> & rs) {
rs.clear();
rs.resize(m_basis.size(), zero_of_type<V>());
}
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();
}
template <typename U, typename V>
void prepare_core_solver_fields(static_matrix<U, V> & A, vector<V> & x,
vector<V> & right_side_vector,
vector<V> & low_bound,
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 prepare_core_solver_fields_with_signature(static_matrix<U, V> & A, vector<V> & x,
vector<V> & right_side_vector,
vector<V> & low_bound,
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 find_solution_signature_with_doubles(lar_solution_signature & signature) {
static_matrix<double, double> A;
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);
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>();
}
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 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 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() {
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 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()) {
cout << "don't have the infeasibility evidence" << 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 get_infeasibility_evidence_for_inf_sign(buffer<pair<mpq, constraint_index>> & evidence,
const 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 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 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 get_model(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];
// cout << it.second->m_column_info.get_name() << " = " << rp << endl;
variable_values[it.first] = rp.x + delta * rp.y;
}
}
string get_variable_name(var_index vi) {
if (m_map_from_var_index_to_left_side.size() <= vi) {
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 print_constraint(constraint_index ci) {
if (m_normalized_constraints.size() <= ci) {
string s = "constraint " + T_to_string(ci) + " is not found";
cout << s << endl;
return;
}
print_constraint(&m_normalized_constraints[ci]);
}
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()) {
cout << " + ";
} else {
cout << " - ";
val = -val;
}
}
if (val != numeric_traits<mpq>::one())
cout << T_to_string(val);
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) {
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()) {
cout << " + ";
} else {
cout << " - ";
val = -val;
}
}
if (val != numeric_traits<mpq>::one())
cout << val;
cout << m_map_from_var_index_to_left_side[it.second]->m_column_info.get_name();
}
}
numeric_pair<mpq> get_infeasibility_from_core_solver(unordered_map<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 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()) {
cout << "l = " << ci.get_low_bound();
}
if (ci.upper_bound_is_set()) {
cout << "u = " << ci.get_upper_bound();
}
cout << endl;
m_mpq_core_solver.print_column_info(j);
}
}
}
mpq get_infeasibility_of_solution(unordered_map<string, mpq> & solution) {
cout << "solution" << endl;
for (auto it : solution) {
cout << it.first << " = " << it.second.get_double() << endl;
}
mpq ret = numeric_traits<mpq>::zero();
for (auto it : m_normalized_constraints) {
ret += get_infeasibility_of_constraint(it.second, solution);
}
cout << "ret = " << ret.get_double() << endl;
auto core_inf = get_infeasibility_from_core_solver(solution);
cout << "core inf = " << T_to_string(core_inf) << endl;
lean_assert(numeric_pair<mpq>(ret, 0) == core_inf);
return ret;
}
mpq get_infeasibility_of_constraint(const lar_normalized_constraint & norm_constr, unordered_map<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:
throw "unexpected";
return numeric_traits<mpq>::zero();
}
}
mpq get_canonic_left_side_val(canonic_left_side * ls, unordered_map<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;
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 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);
cout <<" " << lconstraint_kind_string(c->m_kind) << " " << c->m_right_side;
}
unsigned get_total_iterations() const { return m_mpq_core_solver.m_total_iterations; }
};
}

13
src/util/lp/lp.h Normal file
View file

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

View file

@ -0,0 +1,757 @@
/*
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 <string>
#include "util/lp/lp.h"
#include "util/lp/core_solver_pretty_printer.h"
#include <set>
#include <vector>
#include "util/lp/numeric_pair.h"
namespace lean {
void init_basic_part_of_basis_heading(std::vector<unsigned> & basis, unsigned m, 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(vector<int> & basis_heading, 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,
vector<int> & basis_heading,
unsigned n,
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> // X represents the type of the x variable and the bounds
class lp_core_solver_base {
public:
unsigned m_m; // it is the length of basis. The matrix m_A has m_m rows and the dimension of the matrix A is m_m
unsigned m_n; // the number of columns in the matrix m_A
std::vector<T> m_pivot_row_of_B_1; // the pivot row of the reverse of B
std::vector<T> m_pivot_row; // this is the real pivot row of the simplex tableu
vector<unsigned> m_pivot_row_index;
static_matrix<T, X> & m_A; // the matrix A
vector<X> & m_b; // the right side
std::vector<unsigned> & m_basis;
std::vector<int> m_basis_heading;
std::vector<X> & m_x; // a feasible solution, the fist time set in the constructor
std::vector<T> & m_costs;
lp_settings & m_settings;
std::vector<T> m_y; // the buffer for yB = cb
lp_status m_status;
// a device that is able to solve Bx=c, xB=d, and change the basis
lu<T, X> * m_factorization;
const unordered_map<unsigned, string> & m_column_names;
indexed_vector<T> m_w; // the vector featuring in 24.3 of the Chvatal book
std::vector<T> m_d; // the vector of reduced costs
std::vector<T> m_ed; // the solution of B*m_ed = a
vector<unsigned> m_index_of_ed;
unsigned m_total_iterations = 0;
int m_start_time;
unsigned m_iters_with_no_cost_growing = 0;
vector<unsigned> m_non_basic_columns;
vector<column_type> & m_column_type;
vector<X> & m_low_bound_values;
vector<X> & m_upper_bound_values;
vector<T> m_column_norms; // the approximate squares of column norms that help choosing a profitable column
vector<X> m_copy_of_xB;
unsigned m_refactor_counter = 200;
lp_core_solver_base(static_matrix<T, X> & A,
vector<X> & b, // the right side vector
std::vector<unsigned> & basis,
std::vector<X> & x,
std::vector<T> & costs,
lp_settings & settings,
const unordered_map<unsigned, string> & column_names,
vector<column_type> & column_types,
vector<X> & low_bound_values,
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);
}
virtual ~lp_core_solver_base() {
if (m_factorization != nullptr)
delete m_factorization;
}
vector<unsigned> & non_basis() {
return m_factorization->m_non_basic_columns;
}
void set_status(lp_status status) {
m_status = status;
}
lp_status get_status() {
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(std::vector<T> & y){
for (unsigned i = 0; i < m_m; i++)
y[i] = m_costs[m_basis[i]];
}
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 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 solve_Bd(unsigned entering) {
m_factorization->solve_Bd(entering, m_ed, m_w);
update_index_of_ed();
#ifndef NDEBUG
// 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 pretty_print() {
core_solver_pretty_printer<T, X> pp(*this);
pp.print();
}
void save_state(T * w_buffer, T * d_buffer) {
copy_m_w(w_buffer);
copy_m_ed(d_buffer);
}
void restore_state(T * w_buffer, T * d_buffer) {
restore_m_w(w_buffer);
restore_m_ed(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 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
void copy_m_ed(T * buffer) {
unsigned i = m_m;
while (i --) {
buffer[i] = m_ed[i];
}
}
void restore_m_ed(T * buffer) {
unsigned i = m_m;
while (i --) {
m_ed[i] = buffer[i];
}
}
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) {
cout << "x is off (";
cout << "m_b[" << i << "] = " << m_b[i] << " ";
cout << "left side = " << m_A.dot_product_with_row(i, m_x) << ' ';
cout << "delta = " << delta << ' ';
cout << "iters = " << m_total_iterations << ")" << endl;
return true;
}
}
return false;
}
// 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 zero_pivot_row() {
for (unsigned j : m_pivot_row_index)
m_pivot_row[j] = numeric_traits<T>::zero();
m_pivot_row_index.clear();
}
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;
}
}
}
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];
}
}
T get_var_value(unsigned j) const {
return m_x[j];
}
void print_statistics(X cost) {
cout << "cost = " << T_to_string(cost) <<
", nonzeros = " << m_factorization->get_number_of_nonzeroes() << endl;
}
bool print_statistics_with_iterations_and_check_that_the_time_is_over(unsigned total_iterations) {
if (total_iterations % m_settings.report_frequency == 0) {
cout << "iterations = " << total_iterations << ", nonzeros = " << m_factorization->get_number_of_nonzeroes() << 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(string str, unsigned total_iterations) {
if (total_iterations % m_settings.report_frequency == 0) {
cout << str << " iterations = " << total_iterations << " cost = " << T_to_string(get_cost()) <<", nonzeros = " << m_factorization->get_number_of_nonzeroes() << endl;
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) {
if (total_iterations % m_settings.report_frequency == 0) {
cout << "iterations = " << total_iterations << ", ";
print_statistics(cost);
if (time_is_over()) {
return true;
}
}
return false;
}
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) {
cout << "iterations = " << total_iterations << ", ";
if (time_is_over()) {
return true;
}
}
return false;
}
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;
}
}
}
bool at_bound(const X &x, const X & bound) const {
return !below_bound(x, bound) && !above_bound(x, bound);
}
bool below_bound(const X & x, const X & bound) const {
if (precise<X>()) return x < bound;
return below_bound_numeric<X>(x, bound, m_settings.primal_feasibility_tolerance);
}
bool above_bound(const X & x, const X & bound) const {
if (precise<X>()) return x > bound;
return above_bound_numeric<X>(x, bound, m_settings.primal_feasibility_tolerance);
}
bool x_below_low_bound(unsigned p) {
return below_bound(m_x[p], m_low_bound_values[p]);
}
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]);
}
bool x_is_at_upper_bound(unsigned j) const {
return at_bound(m_x[j], m_upper_bound_values[j]);
}
bool x_is_at_bound(unsigned j) const {
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:
cout << "upper_bound type should be switched to low_bound" << endl;
lean_assert(false); // impossible case
case free_column:
return numeric_traits<X>::is_zero(m_d[j]);
default:
cout << "column = " << j << endl;
cout << "unexpected column type = " << column_type_to_string(m_column_type[j]) << endl;
lean_assert(false);
throw "unexpected column type";
break;
}
}
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 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 time_is_over() {
int span_in_mills = get_millisecond_span(m_start_time);
if (span_in_mills / 1000.0 > m_settings.time_limit) {
cout << "time is over" << endl;
return true;
}
return false;
}
void rs_minus_Anx(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();
}
}
}
}
bool find_x_by_solving() {
solve_Ax_eq_b();
bool ret= !A_mult_x_is_off();
if (ret)
cout << "find_x_by_solving succeeded" << endl;
else
cout << "find_x_by_solving did not succeed" << endl;
return ret;
}
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) {
cout << "failing refactor on off_result for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << m_total_iterations << endl;
throw "";
}
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()) {
cout << "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << m_total_iterations << endl;
restore_x_and_refactor(entering, leaving, tt);
lean_assert(!A_mult_x_is_off());
m_iters_with_no_cost_growing++;
cout << "rolled back after failing of init_factorization()" << endl;
m_status = UNSTABLE;
return false;
}
return true;
}
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());
}
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 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 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_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();
}
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) {
cout << "cannot refactor" << endl;
m_status = lp_status::FLOATING_POINT_ERROR;
}
// solve_Ax_eq_b();
if (A_mult_x_is_off()) {
cout << "cannot restore solution" << endl;
m_status = lp_status::FLOATING_POINT_ERROR;
return;
}
}
void restore_x(unsigned entering, X const & t) {
if (is_zero(t)) return;
cout << "calling restore for entering " << entering << endl;
m_x[entering] -= t;
for (unsigned i : m_index_of_ed) {
m_x[m_basis[i]] = m_copy_of_xB[i];
}
}
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();
}
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(vector<X> & rs) {
unsigned j = m_m;
while (j--) {
m_x[m_basis[j]] = rs[j];
}
}
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]; }
column_type get_column_type(unsigned j) const {return m_column_type[j]; }
bool pivot_row_element_is_too_small_for_ratio_test(unsigned j) {
return m_settings.abs_val_is_smaller_than_pivot_tolerance(m_pivot_row[j]);
}
X bound_span(unsigned j) {
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(string("u") + name);
}
return it->second;
}
void copy_right_side(vector<X> & rs) {
unsigned i = m_m;
while (i --) {
rs[i] = m_b[i];
}
}
void add_delta_to_xB(vector<X> & del) {
unsigned i = m_m;
while (i--) {
this->m_x[this->m_basis[i]] -= del[i];
}
}
void find_error_in_BxB(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
void solve_Ax_eq_b() {
vector<X> rs(m_m);
rs_minus_Anx(rs);
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 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_xN_to_bounds_and_free_columns_to_zeroes() {
snap_non_basic_x_to_bound_and_free_to_zeroes();
solve_Ax_eq_b();
}
void init_reduced_costs_for_one_iteration() {
solve_yB(m_y);
fill_reduced_costs_from_m_y_by_rows();
}
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:
throw "unexpected column type";
}
}
};
}

View file

@ -0,0 +1,833 @@
/*
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/static_matrix.h"
#include "util/lp/lp_core_solver_base.h"
#include <string>
#include <limits>
#include <set>
#include <algorithm>
#include <vector>
namespace lean {
template <typename T, typename X>
class lp_dual_core_solver:public lp_core_solver_base<T, X> {
public:
vector<bool> & m_can_enter_basis;
int m_r; // the row of the leaving column
int m_p; // leaving column; that is m_p = m_basis[m_r]
T m_delta; // the offset of the leaving basis variable
int m_sign_of_alpha_r; // see page 27
T m_theta_D;
T m_theta_P;
int m_q;
// todo : replace by a vector later
std::set<unsigned> m_breakpoint_set; // it is F in "Progress in the dual simplex method ..."
std::set<unsigned> m_flipped_boxed;
std::set<unsigned> m_tight_set; // it is the set of all breakpoints that become tight when m_q becomes tight
vector<T> m_a_wave;
vector<T> m_betas; // m_betas[i] is approximately a square of the norm of the i-th row of the reverse of B
T m_harris_tolerance;
lp_dual_core_solver(static_matrix<T, X> & A,
vector<bool> & can_enter_basis,
vector<X> & b, // the right side vector
vector<X> & x, // the number of elements in x needs to be at least as large as the number of columns in A
vector<unsigned> & basis,
vector<T> & costs,
vector<column_type> & column_type_array,
vector<X> & low_bound_values,
vector<X> & upper_bound_values,
lp_settings & settings,
unordered_map<unsigned, 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();
}
void init_a_wave_by_zeros() {
unsigned j = this->m_m;
while (j--) {
m_a_wave[j] = numeric_traits<T>::zero();
}
}
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 print_nb() {
cout << "this is nb " << endl;
for (auto l : this->m_factorization->m_non_basic_columns) {
cout << l << " ";
}
cout << endl;
}
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();
}
}
}
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) {
cout << "failed on replace_column( " << leaving << ", " << this->m_ed[this->m_factorization->basis_heading(leaving)] << ") total_iterations = " << this->m_total_iterations << 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) {
cout << "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << this->m_total_iterations << endl;
this->m_iters_with_no_cost_growing++;
return false;
}
}
return true;
}
void recalculate_xB_and_d() {
this->solve_Ax_eq_b();
recalculate_d();
}
void recalculate_d() {
this->solve_yB(this->m_y);
this->fill_reduced_costs_from_m_y_by_rows();
}
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 adjust_xb_for_changed_xn_and_init_betas() {
this->solve_Ax_eq_b();
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();
}
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
}
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_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]];
}
// void print_x_and_low_bound(unsigned p) {
// cout << "x l[" << p << "] = " << this->m_x[p] << " " << this->m_low_bound_values[p] << endl;
// }
// void print_x_and_upper_bound(unsigned p) {
// cout << "x u[" << p << "] = " << this->m_x[p] << " " << this->m_upper_bound_values[p] << endl;
// }
// 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);
// cout << "case boxed low_bound in pricing" << 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);
// cout << "case boxed at upper_bound in pricing" << 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);
// cout << "case low_bound in pricing" << 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);
// cout << "case upper_bound in pricing" << 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:
cout << "row = " << i << " p = " << p << endl;
cout << "unexpected column type = " << column_type_to_string(this->m_column_type[p]) << endl;
lean_assert(false);
throw "unexpected column type";
break;
}
}
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) {
// cout << "m_delta = " << m_delta << endl;
// }
}
}
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() {
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_assert(false); // wrong choice of leaving row
throw "wrong choice of leaving row";
case low_bound:
if (this->x_below_low_bound(m_p)) {
return -1;
}
lean_assert(false); // wrong choice of leaving row
throw "wrong choice of leaving row";
case upper_bound:
if (this->x_above_upper_bound(m_p)) {
return 1;
}
lean_assert(false); // wrong choice of leaving row
throw "wrong choice of leaving row";
default:
cout << column_type_to_string(this->m_column_type[m_p]) << endl;
lean_assert(false); // wrong choice of leaving row
throw "wrong choice of leaving row";
break;
}
}
// void try_update_theta_D_and_q_for_boxed(unsigned j) {
// if (this->m_settings.abs_val_is_smaller_than_zero_tolerance(this->m_pivot_row[j])) {
// return;
// }
// T ratio = abs(this->m_d[j] / this->m_pivot_row[j]);
// if (ratio < m_theta_D) {
// m_theta_D = ratio;
// m_q = j;
// m_delta -= abs(bound_span(j) * this->m_pivot_row[j]);
// }
// }
// void try_update_theta_D_and_q(unsigned j) {
// if (this->m_settings.abs_val_is_smaller_than_zero_tolerance(this->m_pivot_row[j])) {
// return;
// cout << " investigate " << endl;
// throw "try_update_theta_D_and_q";
// }
// T ratio = this->m_d[j] / (m_sign_of_alpha_r * this->m_pivot_row[j]);
// if (ratio < m_theta_D) {
// m_theta_D = ratio;
// m_q = 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() {
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);
}
// 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
void DSE_FTran() { // todo, see algorithm 7 from page 35
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];;
}
cout << "incorrect m_p = " << m_p << endl;
lean_assert(false);
throw "incorrect m_p";
case low_bound:
if (this->x_below_low_bound(m_p)) {
return this->m_x[m_p] - this->m_low_bound_values[m_p];
}
cout << "incorrect m_p = " << m_p << endl;
lean_assert(false);
throw "incorrect m_p";
case upper_bound:
if (this->x_above_upper_bound(m_p)) {
return get_edge_steepness_for_upper_bound(m_p);
}
cout << "incorrect m_p = " << m_p << endl;
lean_assert(false);
throw "incorrect m_p";
case fixed:
return this->m_x[m_p] - this->m_upper_bound_values[m_p];;
default:
cout << "unsigned column type " << this->m_column_type[m_p] << endl;
throw "unhandled column type";
break;
}
}
void restore_d() {
cout << "restore_d" << 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];
}
}
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) {
cout << "m_total_iterations = " << this->m_total_iterations << endl;
cout << "d[" << j << "] = " << this->m_d[j] << " but should be " << d << endl;
return false;
}
}
return true;
}
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 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 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 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:
cout << "column = " << j << endl;
cout << "unexpected column type = " << column_type_to_string(this->m_column_type[j]) << endl;
lean_assert(false);
throw "unexpected column type";
break;
}
}
void snap_xN_to_bounds() {
for (auto j : this->non_basis()) {
snap_xN_column_to_bounds(j);
}
}
void init_beta_precisely(unsigned i) {
vector<T> vec(this->m_m, numeric_traits<T>::zero());
vec[i] = numeric_traits<T>::one();
this->m_factorization->solve_yB(vec);
T beta = numeric_traits<T>::zero();
for (T & v : vec) {
beta += v * v;
}
this->m_betas[i] =beta;
}
void init_betas_precisely() {
cout << "init beta precisely..." << endl;
unsigned i = this->m_m;
while (i--) {
init_beta_precisely(i);
}
cout << "done" << endl;
}
// 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;
}
void revert_to_previous_basis() {
cout << "recovering basis p = " << m_p << " q = " << m_q << 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();
}
bool problem_is_dual_feasible() {
for (unsigned j : non_basis()){
if (!this->column_is_dual_feasible(j)) {
cout << "column " << j << " is not dual feasible" << endl;
cout << "m_d[" << j << "] = " << this->m_d[j] << endl;
cout << "x[" << j << "] = " << this->m_x[j] << endl;
cout << "type = " << column_type_to_string(this->m_column_type[j]) << endl;
cout << "bounds = " << this->m_low_bound_values[j] << "," << this->m_upper_bound_values[j] << endl;
cout << "m_total_iterations = " << this->m_total_iterations << endl;
// cout << "m_betas ";
// print_vector(m_betas);
// column_is_dual_feasible(j); // debug
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;
}
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));
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() {
cout << "cost = " << this->get_cost() << endl;
if (this->m_status == TENTATIVE_DUAL_UNBOUNDED) {
cout << "setting status to DUAL_UNBOUNDED" << endl;
this->m_status = DUAL_UNBOUNDED;
} else {
cout << "setting to TENTATIVE_DUAL_UNBOUNDED" << endl;
this->m_status = TENTATIVE_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);
}
}
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;
}
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;
}
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;
}
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 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_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 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);
}
}
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;
}
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 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"
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 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 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(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);
}
bool low_bounds_are_set() const { return true; }
};
}

View file

@ -0,0 +1,411 @@
/*
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/lp_solver.h"
#include "util/lp/lp_dual_core_solver.h"
#include <vector>
namespace lean {
template <typename T, typename X>
class lp_dual_simplex: public lp_solver<T, X> {
lp_dual_core_solver<T, X> * m_core_solver = nullptr;
vector<T> m_b_copy;
std::vector<T> m_low_bounds; // We don't have a convention here that all low bounds are zeros. At least it does not hold for the first stage solver
std::vector<column_type> m_column_types_of_core_solver;
std::vector<column_type> m_column_types_of_logicals;
std::vector<bool> m_can_enter_basis;
public:
~lp_dual_simplex() {
if (m_core_solver != nullptr) {
delete m_core_solver;
}
}
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;
cout << "status is FEASIBLE" << endl;
} else {
cout << "status is UNBOUNDED" << endl;
this->m_status = UNBOUNDED;
}
break;
case DUAL_UNBOUNDED:
cout << "the status cannot be DUAL_UNBOUNDED since the price is not positive!" << endl;
lean_assert(false);
throw "unexpected status DUAL_UNBOUNDED";
break;
case ITERATIONS_EXHAUSTED:
cout << "status is ITERATIONS_EXHAUSTED" << endl;
this->m_status = ITERATIONS_EXHAUSTED;
break;
case TIME_EXHAUSTED:
cout << "status is TIME_EXHAUSTED" << endl;
this->m_status = TIME_EXHAUSTED;
break;
case FLOATING_POINT_ERROR:
cout << "status is FLOATING_POINT_ERROR" << endl;
this->m_status = FLOATING_POINT_ERROR;
break;
default:
cout << "the status is not expected: " << lp_status_to_string(m_core_solver->get_status()) << endl;
lean_assert(false);
throw "unexpected status";
}
}
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:
throw 1;
lean_assert(false);
}
}
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_assert(false);
throw "unexpected bound type";
break;
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_assert(false);
throw "unexpected bound type";
}
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]) {
cout << "cost change in basis" << endl;
}
}
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 restore_right_sides() {
unsigned i = this->m_A->row_count();
while (i--) {
this->m_b[i] = m_b_copy[i];
}
}
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:
cout << "status of core solver is " << lp_status_to_string(m_core_solver->get_status()) << endl;
lean_assert(false);
throw "unexpected status";
}
this->m_second_stage_iterations = m_core_solver->m_total_iterations;
}
void fill_x_with_zeros() {
unsigned j = this->m_A->column_count();
while (j--) {
this->m_x[j] = numeric_traits<T>::zero();
}
}
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())) {
cout << "skipping stage 1" << endl;
m_core_solver->set_status(OPTIMAL);
m_core_solver->m_total_iterations = 0;
} else {
cout << "stage 1" << endl;
m_core_solver->solve();
}
decide_on_status_after_stage1();
this->m_first_stage_iterations = m_core_solver->m_total_iterations;
}
void stage2() {
cout << "starting stage2" << endl;
unmark_boxed_and_fixed_columns_and_fix_structural_costs();
restore_right_sides();
solve_for_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;
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) {
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) {
// 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:
cout << j << endl;
cout << column_type_to_string(get_column_type(j)) << endl;
cout << "throwing upper_bound case " << endl;
throw "unexpected bound type"; // we flip columns so this case is impossible
break;
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_assert(false);
throw "unexpected column type";
break;
}
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 set_type_for_logical(unsigned j, column_type col_type) {
this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type;
}
void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row,
unsigned & slack_var,
unsigned & artificial) {
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 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 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 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();
}
}
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
}
};
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,465 @@
/*
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/lp_primal_core_solver.h"
#include "util/lp/lp_solver.h"
#include <vector>
#include <unordered_map>
#include <string>
#include <algorithm>
#include "util/lp/column_info.h"
namespace lean {
using std::vector;
using std::tuple;
template <typename T, typename X>
class lp_primal_simplex: public lp_solver<T, X> {
lp_primal_core_solver<T, X> * m_core_solver = nullptr;
vector<X> m_low_bounds;
private:
unsigned original_rows() { return this->m_external_rows_to_core_solver_rows.size(); }
void fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) {
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);
}
}
void init_buffer(unsigned k, vector<T> & r) {
for (unsigned i = 0; i < k; i++) {
r[i] = 0;
}
r[k] = 1;
for (unsigned i = this->row_count() -1; i > k; i--) {
r[i] = 0;
}
}
void refactor() {
m_core_solver->init_lu();
if (m_core_solver->factorization()->get_status() != LU_status::OK) {
cout << "cannot refactor \n";
throw "cannot refactor";
}
}
void set_scaled_costs() {
unsigned j = this->number_of_core_structurals();
while (j-- > 0) {
this->set_scaled_cost(j);
}
}
void stage_two() {
cout << "starting stage 2" << 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();
// cout << "status is " << lp_status_to_string(this->m_status) << endl;
}
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;
}
void set_status(lp_status status) {
this->m_status = status;
}
lp_status get_status() {
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 set_zero_bound(bool * bound_is_set, T * bounds, unsigned i) {
bound_is_set[i] = true;
bounds[i] = numeric_traits<T>::zero();
}
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;
}
}
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 string("name_not_found");
}
return t->m_name;
}
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 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;
}
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_on_total_inf() {
for (unsigned row = 0; row < this->row_count(); row++)
fill_A_x_and_basis_for_stage_on_total_inf_for_row(row);
}
void fill_A_x_and_basis_for_stage_on_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() {
cout << "starting solve_with_total_inf()" << 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_on_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 stage_one_of_total_inf() {
// cout << "starting stage_one_of_total_inf()" << 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_on_total_inf();
// this->print_statistics_on_A();
// this->fill_column_names_for_core_solver();
// 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->find_feasible_solution();
// this->m_first_stage_iterations = m_core_solver->m_total_iterations;
// this->m_status = m_core_solver->m_status == lp_status::OPTIMAL? lp_status::FEASIBLE: m_core_solver->m_status; // just map FEASIBLE to OPTIMAL and do not change the rest
// }
~lp_primal_simplex() {
if (m_core_solver != nullptr) {
delete m_core_solver;
}
}
bool bounds_hold(unordered_map<string, T> const & solution) {
for (auto it : this->m_columns) {
auto sol_it = solution.find(it.second->get_name());
if (sol_it == solution.end()) {
cout << "cannot find column " << it.first << " in solution " << endl;
throw;
}
if (!it.second->bounds_hold(sol_it->second)) {
cout << "bounds do not hold for " << it.second->get_name() << endl;
it.second->bounds_hold(sol_it->second);
return false;
}
}
return true;
}
T get_row_value(unsigned i, unordered_map<string, T> const & solution, bool print) {
auto it = this->m_A_values.find(i);
if (it == this->m_A_values.end()) {
cout << "cannot find row " << i << endl;
throw "get_row_value";
}
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()){
cout << "cannot find column " << pair.first << endl;
}
column_info<T> * ci = cit->second;
auto sol_it = solution.find(ci->get_name());
if (sol_it == solution.end()) {
cout << "cannot find in the solution column " << ci->get_name() << endl;
}
T column_val = sol_it->second;
if (print) {
cout << pair.second << "(" << ci->get_name() << "=" << column_val << ") ";
}
ret += pair.second * column_val;
}
if (print) {
cout << " = " << ret << endl;
}
return ret;
}
bool row_constraint_holds(unsigned i, unordered_map<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) {
cout << "should be = " << rs << endl;
}
return false;
}
return true;
case Greater_or_equal:
if (numeric_traits<T>::get_double(row_val - rs) < -0.00001) {
if (print) {
cout << "should be >= " << rs << endl;
}
return false;
}
return true;;
case Less_or_equal:
if (numeric_traits<T>::get_double(row_val - rs) > 0.00001) {
if (print) {
cout << "should be <= " << rs << endl;
}
return false;
}
return true;;
}
cout << "throw in row_constraint_holds " << endl;
throw "wrong case";
}
bool row_constraints_hold(unordered_map<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;
}
T * get_array_from_map(unordered_map<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()) {
string s = string("cannot find name ") + " "+ it.first;
cout << "throw in get_array_from_map" << endl;
throw s;
}
t[g->second] = it.second;
}
return t;
}
bool solution_is_feasible(unordered_map<string, T> const & solution) {
return bounds_hold(solution) && row_constraints_hold(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;
}
};
}

216
src/util/lp/lp_settings.h Normal file
View file

@ -0,0 +1,216 @@
/*
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 <string>
#include <algorithm>
#include <limits>
#include <sys/timeb.h>
namespace lean {
enum column_type {
fixed,
boxed,
low_bound,
upper_bound,
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:
throw "unexpected";
}
}
enum lp_status {
UNKNOWN,
INFEASIBLE,
TENTATIVE_UNBOUNDED,
UNBOUNDED,
TENTATIVE_DUAL_UNBOUNDED,
DUAL_UNBOUNDED,
OPTIMAL,
FEASIBLE,
FLOATING_POINT_ERROR,
TIME_EXHAUSTED,
ITERATIONS_EXHAUSTED,
EMPTY,
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:
std::cout << "throwing in lp_status_to_string " << std::endl;
throw "lp_status_to_string";
}
}
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;
std::cout << "unexpected status " << status << std::endl;
throw "wrong status in a test file";
}
enum non_basic_column_value_position { at_low_bound, at_upper_bound, at_fixed, free_of_bounds };
template <typename X> bool is_epsilon_small(const X & v, const double& eps); // forward definition
template <typename X> bool precise() { return numeric_traits<X>::precise();}
template <typename X> X zero_of_type() { return numeric_traits<X>::zero(); }
template <typename X> X one_of_type() { return numeric_traits<X>::one(); }
template <typename X> bool is_zero(const X & v) { return numeric_traits<X>::is_zero(v); }
template <typename X> double get_double(const X & v) { return numeric_traits<X>::get_double(v); }
struct lp_settings {
// when the absolute value of an element is less than pivot_epsilon
// in pivoting, we treat it as a zero
double pivot_epsilon = 0.00000001;
// see Chatal, page 115
double positive_price_epsilon = 1e-7;
// a quatation "if some choice of the entering vairable leads to an eta matrix
// whose diagonal element in the eta column is less than e2 (entering_diag_epsilon) in magnitude, the this choice is rejected ...
double entering_diag_epsilon = 1e-8;
double c_partial_pivoting = 10; // this is the constant c from page 410
unsigned depth_of_rook_search = 4;
bool using_partial_pivoting = true;
// dissertation of Achim Koberstein
// if Bx - b is different at any component more that refactor_epsilon then we refactor
double refactor_tolerance = 1e-4;
double pivot_tolerance = 1e-6;
double zero_tolerance = 1e-12;
double drop_tolerance = 1e-14;
double tolerance_for_artificials = 1e-4;
double can_be_taken_to_basis_tolerance = 0.00001;
unsigned percent_of_entering_to_check = 5; // we try to find a profitable column in a percentage of the columns
bool use_scaling = true;
double scaling_maximum = 1;
double scaling_minimum = 0.5;
double harris_feasibility_tolerance = 1e-7; // page 179 of Istvan Maros
double ignore_epsilon_of_harris = 10e-5;
unsigned max_number_of_iterations_with_no_improvements = 2000000;
unsigned max_total_number_of_iterations = 20000000;
double time_limit = std::numeric_limits<double>::max(); // the maximum time limit of the total run time in seconds
// dual section
double dual_feasibility_tolerance = 1e-7; // // page 71 of the PhD thesis of Achim Koberstein
double primal_feasibility_tolerance = 1e-7; // page 71 of the PhD thesis of Achim Koberstein
double relative_primal_feasibility_tolerance = 1e-9; // page 71 of the PhD thesis of Achim Koberstein
template <typename T> static bool is_eps_small_general(const T & t, const double & eps) {
return (!numeric_traits<T>::precise())? is_epsilon_small<T>(t, eps) : numeric_traits<T>::is_zero(t);
}
template <typename T>
bool abs_val_is_smaller_than_dual_feasibility_tolerance(T const & t) {
return is_eps_small_general<T>(t, dual_feasibility_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_primal_feasibility_tolerance(T const & t) {
return is_eps_small_general<T>(t, dual_feasibility_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_can_be_taken_to_basis_tolerance(T const & t) {
return is_eps_small_general<T>(t, can_be_taken_to_basis_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_drop_tolerance(T const & t) {
return is_eps_small_general<T>(t, drop_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_zero_tolerance(T const & t) {
return is_eps_small_general<T>(t, zero_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_refactor_tolerance(T const & t) {
return is_eps_small_general<T>(t, refactor_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_pivot_tolerance(T const & t) {
return is_eps_small_general<T>(t, pivot_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_harris_tolerance(T const & t) {
return is_eps_small_general<T>(t, harris_feasibility_tolerance);
}
template <typename T>
bool abs_val_is_smaller_than_ignore_epslilon_for_harris(T const & t) {
return is_eps_small_general<T>(t, ignore_epsilon_of_harris);
}
template <typename T>
bool abs_val_is_smaller_than_artificial_tolerance(T const & t) {
return is_eps_small_general<T>(t, tolerance_for_artificials);
}
// the method of lar solver to use
bool row_feasibility = true;
bool use_double_solver_for_lar = true;
int report_frequency = 1000;
unsigned column_norms_update_frequency = 1000;
bool scale_with_ratio = true;
double density_threshold = 0.7; // need to tune it up, todo
#ifndef NDEBUG
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;
}
int get_millisecond_span(int start_time) {
int span = get_millisecond_count() - start_time;
if (span < 0)
span += 0x100000 * 1000;
return span;
}
}

688
src/util/lp/lp_solver.h Normal file
View file

@ -0,0 +1,688 @@
/*
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/lp_settings.h"
#include <string>
#include <unordered_map>
#include "util/lp/column_info.h"
#include "util/lp/static_matrix.h"
#include <algorithm>
#include "util/lp/lp_core_solver_base.h"
#include <vector>
#include "util/lp/scaler.h"
namespace lean {
enum lp_relation {
Less_or_equal,
Equal,
Greater_or_equal
};
template <typename T, typename X>
struct lp_constraint {
X m_rs; // right side of the constraint
lp_relation m_relation;
lp_constraint() {} // empty constructor
lp_constraint(T rs, lp_relation relation): m_rs(rs), m_relation(relation) {}
};
template <typename T, typename X>
class lp_solver {
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;
}
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);
}
public:
unsigned m_total_iterations;
static_matrix<T, X>* m_A = nullptr; // this is the matrix of constraints
vector<T> m_b; // the right side vector
unsigned m_first_stage_iterations = 0;
unsigned m_second_stage_iterations = 0;
unordered_map<unsigned, lp_constraint<T, X>> m_constraints;
unordered_map<unsigned, column_info<T>*> m_columns;
unordered_map<unsigned, unordered_map<unsigned, T> > m_A_values;
unordered_map<string, unsigned> m_names_to_columns; // don't have to use it
unordered_map<unsigned, unsigned> m_external_rows_to_core_solver_rows;
unordered_map<unsigned, unsigned> m_core_solver_rows_to_external_rows;
unordered_map<unsigned, unsigned> m_external_columns_to_core_solver_columns;
unordered_map<unsigned, unsigned> m_core_solver_columns_to_external_columns;
vector<T> m_column_scale;
unordered_map<unsigned, std::string> m_name_map;
unsigned m_artificials = 0;
unsigned m_slacks = 0;
vector<column_type> m_column_types;
vector<T> m_costs;
vector<T> m_x;
vector<T> m_upper_bounds;
vector<unsigned> m_basis;
lp_status m_status = lp_status::UNKNOWN;
lp_settings m_settings;
lp_solver() {}
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 set_cost_for_column(unsigned column, T column_cost) {
get_or_create_column_info(column)->set_cost(column_cost);
}
void set_row_column_coefficient(unsigned row, unsigned column, T const & val) {
m_A_values[row][column] = val;
}
// returns the current cost
virtual T get_current_cost() const = 0;
// do not have to call it
void give_symbolic_name_to_column(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;
}
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()) {
cout << "throwing in get_column_value_by_name" << endl;
throw "get_column_value_by_name";
}
return get_column_value(it -> second);
}
// 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;
}
void set_low_bound(unsigned i, T bound) {
column_info<T> *ci = get_or_create_column_info(i);
ci->set_low_bound(bound);
}
void set_upper_bound(unsigned i, T bound) {
column_info<T> *ci = get_or_create_column_info(i);
ci->set_upper_bound(bound);
}
void unset_low_bound(unsigned i) {
get_or_create_column_info(i)->unset_low_bound();
}
void unset_upper_bound(unsigned i) {
get_or_create_column_info(i)->unset_upper_bound();
}
void set_fixed_value(unsigned i, T val) {
column_info<T> *ci = get_or_create_column_info(i);
ci->set_fixed_value(val);
}
void unset_fixed_value(unsigned i) {
get_or_create_column_info(i)->unset_fixed();
}
lp_status get_status() const {
return m_status;
}
virtual ~lp_solver(){
if (m_A != nullptr) {
delete m_A;
}
for (auto t : m_columns) {
delete t.second;
}
}
void flip_costs() {
for (auto t : m_columns) {
column_info<T> *ci = t.second;
ci->set_cost(-ci->get_cost());
}
}
virtual void find_maximal_solution() = 0;
void set_time_limit(unsigned time_limit_in_seconds) {
m_settings.time_limit = time_limit_in_seconds;
}
void set_max_iterations_per_stage(unsigned max_iterations) {
m_settings.max_total_number_of_iterations = max_iterations;
}
void get_max_iterations_per_stage() const {
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;
}
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 print_rows_scale_stats() {
cout << "rows max" << endl;
for (unsigned i = 0; i < m_A->row_count(); i++) {
print_row_scale_stats(i);
}
cout << endl;
}
void print_columns_scale_stats() {
cout << "columns max" << endl;
for (unsigned i = 0; i < m_A->column_count(); i++) {
print_column_scale_stats(i);
}
cout << endl;
}
void print_row_scale_stats(unsigned i) {
cout << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " ";
cout << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")";
}
void print_column_scale_stats(unsigned j) {
cout << "(" << m_A->get_min_abs_in_row(j) << " ";
cout << m_A->get_max_abs_in_column(j) << ")";
}
void print_scale_stats() {
print_rows_scale_stats();
print_columns_scale_stats();
}
void get_max_abs_in_row(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 pin_vars_down_on_row(unordered_map<unsigned, T> & row) {
pin_vars_on_row_with_sign(row, - numeric_traits<T>::one());
}
void pin_vars_up_on_row(unordered_map<unsigned, T> & row) {
pin_vars_on_row_with_sign(row, numeric_traits<T>::one());
}
void pin_vars_on_row_with_sign(unordered_map<unsigned, T> & row, T sign ) {
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());
}
}
}
bool get_minimal_row_value(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(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 row_is_zero(unordered_map<unsigned, T> & row) {
for (auto & t : row) {
if (!is_zero(t.second))
return false;
}
return true;
}
bool row_e_is_obsolete(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;
}
int row_ge_is_obsolete(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(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
bool row_is_obsolete(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);
}
throw "unexpected relation";
}
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);
}
}
void remove_fixed_or_zero_columns_from_row(unsigned i, unordered_map<unsigned, T> & row) {
auto & constraint = m_constraints[i];
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);
}
}
unsigned try_to_remove_some_rows() {
vector<unsigned> rows_to_delete;
for (auto & t : m_A_values) {
if (row_is_obsolete(t.second, t.first)) {
rows_to_delete.push_back(t.first);
}
if (m_status == lp_status::INFEASIBLE) {
return 0;
}
}
if (rows_to_delete.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)
cout << "deleted one row" << endl;
else if (n)
cout << "deleted " << n << " rows" << 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()) {
cout << "found fixed column " << endl;
throw "map_external_columns_to_core_solver_columns";
}
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();
}
}
}
unsigned number_of_core_structurals() { return m_external_columns_to_core_solver_columns.size(); }
void restore_column_scales_to_one() {
for (unsigned i = 0; i < m_column_scale.size(); i++) m_column_scale[i] = numeric_traits<T>::one();
}
void unscale() {
delete m_A;
m_A = nullptr;
fill_A_from_A_values();
restore_column_scales_to_one();
fill_m_b();
}
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();
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() {
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() {
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) {
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 ret = numeric_traits<T>::zero();
auto row = this->m_A_values.find(i);
if (row == this->m_A_values.end()) {
throw "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() {
cout << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << endl;
// for (unsigned i = 0; i < this->m_A->row_count(); i++) {
// if (this->m_A->number_of_non_zeroes_in_row(i) <= 2 ) {
// cout << "m_p[" << i << "] = " << this->m_A->number_of_non_zeroes_in_row(i) << endl;
// }
// }
}
public:
lp_settings & settings() { return m_settings;}
};
}

886
src/util/lp/lu.h Normal file
View file

@ -0,0 +1,886 @@
/*
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 <algorithm>
#include <set>
#include "util/lp/sparse_matrix.h"
#include "util/lp/static_matrix.h"
#include <string>
#include "util/lp/numeric_pair.h"
#include <iostream>
#include <fstream>
#include "util/lp/row_eta_matrix.h"
#include "util/lp/square_dense_submatrix.h"
namespace lean {
template <typename T>
std::string T_to_string(const T & t); // forward definition
#ifndef NDEBUG
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) {
vector<vector<string>> A;
vector<unsigned> widths;
for (unsigned i = 0; i < m.row_count() && i < mr ; i++) {
A.push_back(vector<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) {
vector<vector<string>> A;
vector<unsigned> widths;
std::set<pair<unsigned, unsigned>> domain = m.get_domain();
for (unsigned i = 0; i < m.row_count(); i++) {
A.push_back(vector<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) {
vector<vector<string>> A;
vector<unsigned> widths;
for (unsigned i = 0; i < m.row_count(); i++) {
A.push_back(vector<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
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;
}
template <typename T, typename X>
class one_elem_on_diag: public tail_matrix<T, X> {
unsigned m_i;
T m_val;
public:
one_elem_on_diag(unsigned i, T val) : m_i(i), m_val(val) {
#ifndef NDEBUG
m_one_over_val = numeric_traits<T>::one() / m_val;
#endif
}
one_elem_on_diag(const one_elem_on_diag & o) {
m_i = o.m_i;
m_val = o.m_val;
#ifndef NDEBUG
m_m = m_n = o.m_m;
m_one_over_val = numeric_traits<T>::one() / o.m_val;
#endif
}
#ifndef NDEBUG
unsigned m_m;
unsigned m_n;
virtual void set_number_of_rows(unsigned m) { m_m = m; m_n = m; }
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();
}
unsigned row_count() const { return m_m; } // not defined }
unsigned column_count() const { return m_m; } // not defined }
#endif
void apply_from_left(vector<X> & w, lp_settings &) {
w[m_i] /= m_val;
}
void apply_from_right(vector<T> & w) {
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 conjugate_by_permutation(permutation_matrix<T, X> & p) {
// this = p * this * p(-1)
#ifndef NDEBUG
// auto rev = p.get_reverse();
// auto deb = ((*this) * rev);
// deb = p * deb;
#endif
m_i = p.apply_reverse(m_i);
#ifndef NDEBUG
// lean_assert(*this == deb);
#endif
}
}; // 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
template <typename T, typename X>
class lu {
LU_status m_status = LU_status::OK;
public:
// the fields
unsigned m_dim;
static_matrix<T, X> const &m_A;
vector<unsigned>& m_basis;
permutation_matrix<T, X> m_Q;
permutation_matrix<T, X> m_R;
sparse_matrix<T, X> m_U;
square_dense_submatrix<T, X>* m_dense_LU;
// m_tail is composed of tail_matrices:
// one_off_diagonal_matrix, and transposition_matrix
vector<tail_matrix<T, X> *> m_tail;
lp_settings & m_settings;
vector<int> & m_basis_heading;
bool m_failure = false;
vector<unsigned> & m_non_basic_columns;
indexed_vector<T> m_row_eta_work_vector;
// constructor
// if A is an m by n matrix then basis has length m and values in [0,n); the values are all different
// they represent the set of m columns
lu(static_matrix<T, X> const & A,
vector<unsigned>& basis,
vector<int> & basis_heading,
lp_settings & settings,
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()){
#ifndef NDEBUG
debug_test_of_basis(A, basis);
#endif
create_initial_factorization();
if (get_status() != LU_status::OK) {
if (get_status() == LU_status::Degenerated) {
cout << "lu status is Degenerated" << endl;
} else {
cout << "lu status is " <<static_cast<int>(get_status()) << endl;
}
return;
}
#ifndef NDEBUG
// 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());
}
unsigned non_basic_column_index_in_non_basic_columns(unsigned j) {
return - m_basis_heading[j] - 1;
}
void solve_By(vector<X> & y) {
init_vector_y(y);
solve_By_when_y_is_ready(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);
}
template <typename L>
void solve_By_when_y_is_ready(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 print_indexed_vector(indexed_vector<T> & w, std::ofstream & f) {
f << "vector_start" << endl;
for (unsigned j : w.m_index) {
f << j << " " << w[j] << endl;
}
f << "vector_end" << endl;
}
void print_basis(std::ofstream & f) {
f << "basis_start" << endl;
for (unsigned j : m_basis)
f << j << endl;
f << "basis_end" << endl;
}
void print_matrix_compact(std::ofstream & f) {
f << "matrix_start" << endl;
f << "nrows " << m_A.row_count() << endl;
f << "ncolumns " << m_A.column_count() << endl;
for (unsigned i = 0; i < m_A.row_count(); i++) {
auto & row = m_A.m_rows[i];
f << "row " << i << endl;
for (auto & t : row.m_cells) {
f << "column " << t.m_j << " value " << t.m_value << endl;
}
f << "row_end" << endl;
}
f << "matrix_end" << endl;
}
void print(indexed_vector<T> & w) {
string dump_file_name("/tmp/lu");
remove(dump_file_name.c_str());
std::ofstream f(dump_file_name);
if (!f.is_open()) {
cout << "cannot open file " << dump_file_name << endl;
return;
}
cout << "writing lu dump to " << dump_file_name << endl;
print_matrix_compact(f);
print_basis(f);
print_indexed_vector(w, f);
f.close();
}
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);
}
bool column_can_be_taken_to_basis(unsigned i) {
return m_basis_heading[i] < 0;
}
void solve_yB_internal(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) {
#ifndef NDEBUG
(*e)->set_number_of_columns(m_dim);
#endif
(*e)->apply_from_right(y);
}
}
void add_delta_to_solution(vector<T>& yc, vector<T>& y){
unsigned i = y.size();
while (i--)
y[i]+=yc[i];
}
void find_error_of_yB(vector<T>& yc, const 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
void solve_yB(vector<T> & y) {
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);
}
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.
// solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering
// variable
~lu(){
for (auto t : m_tail) {
delete t;
}
}
T B(unsigned i, unsigned j) {
return m_A(i, m_basis[j]);
}
void init_vector_y(vector<X> & y) {
apply_lp_lists_to_y(y);
m_Q.apply_reverse_from_left(y);
}
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(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 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;
}
void print_basis() {
std::cout << "basis ";
for (unsigned i = 0; i < m_dim; i++) {
std::cout << m_basis[i] << " ";
}
std::cout << std::endl;
}
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;
}
// 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;
}
#ifndef NDEBUG
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_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_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
permutation_matrix<T, X> & Q() { return m_Q; }
permutation_matrix<T, X> & R() { return m_R; }
matrix<T, X> & U() { return m_U; }
unsigned tail_size() { return m_tail.size(); }
tail_matrix<T, X> * get_lp_matrix(unsigned i) {
return m_tail[i];
}
T B_(unsigned i, unsigned j) {
return m_A.get_elem(i, m_basis[j]);
}
unsigned dimension() { return m_dim; }
#endif
void mark_fixed_variable(unsigned column) {
m_basis_heading[column] = -2;
}
unsigned get_number_of_nonzeroes() {
return m_U.get_number_of_nonzeroes();
}
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) {
cout << "cannot find the pivot for column " << j << endl;
m_failure = true;
return;
}
swap_columns(j, pj);
swap_rows(j, pi);
if (!pivot_the_row(j)) {
cout << "pivot_the_row(" << j << ") failed" << endl;
m_failure = true;
}
}
bool is_correct() {
#ifndef NDEBUG
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
}
int basis_heading(unsigned j) {
lean_assert(j < m_A.column_count());
return m_basis_heading[j];
}
#ifndef NDEBUG
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> get_right_side() {
auto ret = U() * R();
ret = Q() * ret;
return ret;
}
#endif
// needed for debugging purposes
void copy_w(T *buffer, indexed_vector<T> & w) {
unsigned i = m_dim;
while (i--) {
buffer[i] = w[i];
}
}
// 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;
}
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);
}
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 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++;
// 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 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);
}
void scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) {
vector<indexed_value<T>> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump));
for (auto & iv : last_row_vec) {
if (is_zero(iv.m_value)) continue;
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);
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
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 (
#ifndef NDEBUG
!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)) {
// 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);
// cout << "diagonal element is off" << endl;
return nullptr;
}
#ifndef NDEBUG
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.
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 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));
}
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);
#ifndef NDEBUG
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 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, vector<int> & m_basis_heading, lp_settings &m_settings, 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) {
cout << "failing in init_factorization" << endl;
return;
}
}
#ifndef NDEBUG
template <typename T, typename X>
dense_matrix<T, X> get_B(lu<T, X>& f) {
dense_matrix<T, X> B(f.dimension(), f.dimension());
for (unsigned i = 0; i < f.dimension(); i++)
for (unsigned j = 0; j < f.dimension(); j++)
B.set_elem(i, j, f.B_(i, j));
return B;
}
#endif
}

View file

@ -0,0 +1,78 @@
/*
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 {
using std::unordered_map;
template <typename T>
class matrix_domain {
vector<unordered_map<unsigned, void *>> m_domain;
public:
matrix_domain(unsigned rows) {
while (rows--) {
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;
}
};
}

239
src/util/lp/numeric_pair.h Normal file
View file

@ -0,0 +1,239 @@
/*
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 <string>
#include <algorithm>
#include "util/numerics/mpq.h"
#include "util/numerics/double.h"
namespace lean {
template <typename T>
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 below_bound_numeric(const X &, const X &, const Y &) { throw "don't call";}
static bool above_bound_numeric(const X &, const X &, const Y &) { throw "don't call";}
};
template <>
struct convert_struct<double, mpq> {
static double convert(const mpq & q) {return q.get_double();}
};
template <typename T>
struct numeric_pair {
T x;
T y;
// empty constructor
numeric_pair() {}
// another constructor
numeric_pair(T xp, T yp) : x(xp), y(yp) {}
template <typename X, typename Y> numeric_pair(X xp, Y yp) : numeric_pair(convert_struct<T, X>::convert(xp), convert_struct<T, Y>::convert(yp)) {}
bool operator<(const numeric_pair& a) const {
return x < a.x || (x == a.x && y < a.y);
}
bool operator>(const numeric_pair& a) const {
return x > a.x || (x == a.x && y > a.y);
}
bool operator==(const numeric_pair& a) const {
return a.x == x && a.y == y;
}
bool operator!=(const numeric_pair& a) const {
return !(*this == a);
}
bool operator<=(const numeric_pair& a) const {
return *this < a || *this == a;
}
bool operator>=(const numeric_pair& a) const {
return *this > a || a == *this;
}
numeric_pair operator*(const T & a) const {
return numeric_pair(a * x, a * y);
}
numeric_pair operator/(const T & a) const {
T a_as_T(a);
return numeric_pair(x / a_as_T, y / a_as_T);
}
numeric_pair operator/(const numeric_pair &) const {
throw "should not be called";
}
numeric_pair operator+(const numeric_pair & a) const {
return numeric_pair(a.x + x, a.y + y);
}
numeric_pair operator*(const numeric_pair & /*a*/) const {
throw "should not be called";
}
numeric_pair& operator+=(const numeric_pair & a) {
x += a.x;
y += a.y;
return *this;
}
numeric_pair& operator-=(const numeric_pair & a) {
x -= a.x;
y -= a.y;
return *this;
}
numeric_pair& operator/=(const T & a) {
x /= a;
y /= a;
return *this;
}
numeric_pair& operator*=(const T & a) {
x *= a;
y *= a;
return *this;
}
numeric_pair operator-(const numeric_pair & a) const {
return numeric_pair(x - a.x, y - a.y);
}
numeric_pair operator-() const {
return numeric_pair(-x, -y);
}
static bool precize() { return numeric_traits<T>::precize();}
std::string to_string() const { return std::string("(") + T_to_string(x) + ", " + T_to_string(y) + ")"; }
};
template <typename T>
std::ostream& operator<<(std::ostream& os, numeric_pair<T> const & obj) {
os << obj.to_string();
return os;
}
template <typename T, typename X>
numeric_pair<T> operator*(const X & a, const numeric_pair<T> & r) {
return numeric_pair<T>(a * r.x, a * r.y);
}
template <typename T, typename X>
numeric_pair<T> operator*(const numeric_pair<T> & r, const X & a) {
return numeric_pair<T>(a * r.x, a * r.y);
}
// template <numeric_pair, typename T> bool precise() { return numeric_traits<T>::precise();}
template <typename T> double get_double(const numeric_pair<T> & ) { throw "do not call"; }
template <typename T>
class numeric_traits<numeric_pair<T>> {
public:
static bool precise() { return numeric_traits<T>::precise();}
static numeric_pair<T> zero() { return numeric_pair<T>(numeric_traits<T>::zero(), numeric_traits<T>::zero()); }
static bool is_zero(const numeric_pair<T> & v) { return numeric_traits<T>::is_zero(v.x) && numeric_traits<T>::is_zero(v.y); }
static double get_double(const numeric_pair<T> & v){ return numeric_traits<T>::get_double(v.x); } // just return the double of the first coordinate
static double one() { throw "do not call"; }
};
template <>
struct convert_struct<double, numeric_pair<double>> {
static double convert(const numeric_pair<double> & q) {return q.x;}
};
template <typename X> bool is_epsilon_small(const X & v, const double& eps); // forward definition { return convert_struct<X, double>::is_epsilon_small(v, eps);}
template <typename T>
struct convert_struct<numeric_pair<T>, double> {
static numeric_pair<T> convert(const double & q) {
return numeric_pair<T>(convert_struct<T, double>::convert(q), zero_of_type<T>());
}
static bool is_epsilon_small(const numeric_pair<T> & p, const double & eps) {
return convert_struct<T, double>::is_epsilon_small(p.x, eps) && convert_struct<T, double>::is_epsilon_small(p.y, eps);
}
static bool below_bound_numeric(const numeric_pair<T> &, const numeric_pair<T> &, const double &) {
throw "do not call";
}
static bool above_bound_numeric(const numeric_pair<T> &, const numeric_pair<T> &, const double &) {
throw "do not call";
}
};
template <>
struct convert_struct<numeric_pair<double>, double> {
static numeric_pair<double> convert(const double & q) {
return numeric_pair<double>(q, 0.0);
}
static bool is_epsilon_small(const numeric_pair<double> & p, const double & eps) {
return std::abs(p.x) < eps && std::abs(p.y) < eps;
}
static int compare_on_coord(const double & x, const double & bound, const double eps) {
lean_assert(eps > 0);
if (bound == 0) return (x < - eps)? -1: (x > eps? 1 : 0); // it is an important special case
double relative = (bound > 0)? - eps: eps;
return (x < bound * (1.0 + relative) - eps)? -1 : ((x > bound * (1.0 - relative) + eps)? 1 : 0);
}
static bool below_bound_numeric(const numeric_pair<double> & x, const numeric_pair<double> & bound, const double & eps) {
int r = compare_on_coord(x.x, bound.x, eps);
if (r == 1) return false;
if (r == -1) return true;
// the first coordinates are almost the same
lean_assert(r == 0);
return compare_on_coord(x.y, bound.y, eps) == -1;
}
static bool above_bound_numeric(const numeric_pair<double> & x, const numeric_pair<double> & bound, const double & eps) {
int r = compare_on_coord(x.x, bound.x, eps);
if (r == -1) return false;
if (r == 1) return true;
// the first coordinates are almost the same
lean_assert(r == 0);
return compare_on_coord(x.y, bound.y, eps) == 1;
}
};
template <>
struct convert_struct<double, double> {
static bool is_epsilon_small(const double& x, const double & eps) {
return x < eps && x > -eps;
}
static double convert(const double & y){ return y;}
static bool below_bound_numeric(const double & x, const double & bound, const double & eps) {
lean_assert(eps > 0);
if (bound == 0) return x < - eps;
double relative = (bound > 0)? - eps: eps;
return x < bound * (1.0 + relative) - eps;
}
static bool above_bound_numeric(const double & x, const double & bound, const double & eps) {
lean_assert(eps > 0);
if (bound == 0) return x > eps;
double relative = (bound > 0)? eps: - eps;
return x > bound * (1.0 + relative) + eps;
}
};
template <typename X> bool is_epsilon_small(const X & v, const double &eps) { return convert_struct<X, double>::is_epsilon_small(v, eps);}
template <typename X> bool below_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct<X, double>::below_bound_numeric(x, bound, eps);}
template <typename X> bool above_bound_numeric(const X & x, const X & bound, const double& eps) { return convert_struct<X, double>::above_bound_numeric(x, bound, eps);}
}

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,202 @@
/*
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/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 <string>
#include "util/lp/sparse_vector.h"
#include "util/lp/indexed_vector.h"
namespace lean {
// This is the sum of a unit matrix and a lower triangular matrix
// with non-zero elements only in one column
template <typename T, typename X>
class row_eta_matrix
: public tail_matrix<T, X> {
#ifndef NDEBUG
unsigned m_dimension;
#endif
unsigned m_row_start;
unsigned m_row;
sparse_vector<T> m_row_vector;
public:
#ifndef NDEBUG
row_eta_matrix(unsigned row_start, unsigned row, unsigned dim):
#else
row_eta_matrix(unsigned row_start, unsigned row):
#endif
#ifndef NDEBUG
m_dimension(dim),
#endif
m_row_start(row_start), m_row(row) {
lean_assert(dim > 0);
}
void print() {
print_matrix(*this);
}
const T & get_diagonal_element() const {
return m_row_vector.m_data[m_row];
}
void apply_from_left(vector<X> & w, lp_settings &
#ifndef NDEBUG
// settings
#endif
) {
// #ifndef NDEBUG
// 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;
// #ifndef NDEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
// delete [] clone_w;
// #endif
}
template <typename L>
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
#ifndef NDEBUG
// 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));
#ifndef NDEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w.m_data, m_dimension));
// delete clone_w;
#endif
}
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
apply_from_left_local(w, settings);
}
void push_back(unsigned row_index, T val ) {
m_row_vector.push_back(row_index, val);
}
void apply_from_right(vector<T> & w) {
T w_row = w[m_row];
if (numeric_traits<T>::is_zero(w_row)) return;
#ifndef NDEBUG
// 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();
}
#ifndef NDEBUG
// lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
// delete clone_w;
#endif
}
void apply_from_right(indexed_vector<T> & w) {
T w_row = w[m_row];
if (numeric_traits<T>::is_zero(w_row)) return;
#ifndef NDEBUG
// 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);
}
}
#ifndef NDEBUG
// 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)
#ifndef NDEBUG
// 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
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]);
}
#ifndef NDEBUG
// 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();
}
#ifndef NDEBUG
unsigned row_count() const { return m_dimension; }
unsigned column_count() const { return m_dimension; }
void set_number_of_rows(unsigned /*m*/) { }
void set_number_of_columns(unsigned /*n*/) { }
#endif
}; // end of row_eta_matrix
}

269
src/util/lp/scaler.h Normal file
View file

@ -0,0 +1,269 @@
/*
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/numerics/double.h"
#include <vector>
#include <math.h>
#include <algorithm>
#include <stdio.h> /* printf, fopen */
#include <stdlib.h> /* exit, EXIT_FAILURE */
namespace lean {
// for scaling an LP
template <typename T, typename X>
class scaler {
vector<X> & m_b; // right side
static_matrix<T, X> &m_A; // the constraint matrix
const T & m_scaling_minimum;
const T & m_scaling_maximum;
vector<T>& m_column_scale;
lp_settings & m_settings;
public:
// constructor
scaler(vector<X> & b, static_matrix<T, X> &A, const T & scaling_minimum, const T & scaling_maximum, vector<T> & column_scale,
lp_settings & settings):
m_b(b),
m_A(A),
m_scaling_minimum(scaling_minimum),
m_scaling_maximum(scaling_maximum),
m_column_scale(column_scale),
m_settings(settings) {
lean_assert(m_column_scale.size() == 0);
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 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_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 get_A_ratio() const {
T min = A_min();
T max = A_max();
T ratio = max / min;
return ratio;
}
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_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;
}
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_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_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();
}
}
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--);
bring_rows_and_columns_maximums_to_one();
return true;
}
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() {
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() {
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;
}
// 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();
}
void scale_rows() {
for (unsigned i = 0; i < m_A.row_count(); i++)
scale_row(i);
}
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_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;
}
void scale_columns() {
for (unsigned i = 0; i < m_A.column_count(); i++) {
scale_column(i);
}
}
};
}

1321
src/util/lp/sparse_matrix.h Normal file

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,93 @@
/*
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/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 "util/lp/lp_settings.h"
namespace lean {
using std::vector;
using std::pair;
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 {
public:
vector<pair<unsigned, T>> m_data;
void push_back(unsigned index, T val) {
m_data.emplace_back(index, val);
}
#ifndef NDEBUG
T operator[] (unsigned i) const {
for (auto t : m_data) {
if (t.first == i) return t.second;
}
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;
}
}
unsigned size() const {
return m_data.size();
}
friend sparse_vector_iterator<T>;
};
template <typename T>
class sparse_vector_iterator {
typedef typename vector< pair<unsigned, T> >::iterator p_it;
p_it m_it;
p_it m_end;
public:
sparse_vector_iterator(sparse_vector<T> & s_v) : m_it(s_v.m_data.begin()),
m_end(s_v.m_data.end()) {
}
bool done() {
return m_it == m_end;
}
void move() {
m_it++;
}
unsigned index() {
return m_it->first;
}
const T & value() const {
return m_it->second;
}
};
}

View file

@ -0,0 +1,434 @@
/*
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/float.h"
#include "util/lp/permutation_matrix.h"
#include <unordered_map>
#include "util/lp/static_matrix.h"
#include <set>
#include <utility>
#include <string>
#include <algorithm>
#include <queue>
#include "util/lp/indexed_value.h"
#include "util/lp/indexed_vector.h"
#include <functional>
#include "util/lp/lp_settings.h"
#include "util/lp/eta_matrix.h"
#include "util/lp/binary_heap_upair_queue.h"
namespace lean {
using std::vector;
using std::cout;
template <typename T, typename X>
class square_dense_submatrix : public tail_matrix<T, X> {
// the submatrix uses the permutations of the parent matrix to access the elements
struct ref {
unsigned m_i_offset;
square_dense_submatrix & m_s;
ref(unsigned i, square_dense_submatrix & s) :
m_i_offset((i - s.m_index_start) * s.m_dim), m_s(s){}
T & operator[] (unsigned j) {
lean_assert(j >= m_s.m_index_start);
return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start];
}
const T & operator[] (unsigned j) const {
lean_assert(j >= m_s.m_index_start);
return m_s.m_v[m_i_offset + m_s.adjust_column(j) - m_s.m_index_start];
}
};
public: // debug
unsigned m_index_start;
unsigned m_dim;
vector<T> m_v;
sparse_matrix<T, X> * m_parent = nullptr;
permutation_matrix<T, X> m_row_permutation;
public:
permutation_matrix<T, X> m_column_permutation;
bool is_active() const { return m_parent != nullptr; }
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;
}
}
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;
}
}
}
ref operator[] (unsigned i) {
lean_assert(i >= m_index_start);
lean_assert(i < m_parent->dimension());
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;
}
void swap_columns(unsigned i, unsigned j) {
if (i == j) return;
m_column_permutation.transpose_from_left(i, j);
}
unsigned adjust_column(unsigned col) const{
return m_column_permutation.apply_reverse(col);
}
unsigned adjust_column_inverse(unsigned col) const{
return m_column_permutation[col];
}
unsigned adjust_row(unsigned row) const{
return m_row_permutation[row];
}
unsigned adjust_row_inverse(unsigned row) const{
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_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 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 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_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 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 L>
L row_by_vector_product(unsigned i, const 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 L>
L column_by_vector_product(unsigned j, const 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 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;
}
template <typename L>
void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) {
#ifndef NDEBUG
// 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
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
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
#ifndef NDEBUG
// 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 L>
void apply_from_left_to_vector(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);
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];
}
#ifndef NDEBUG
// cout << "w final" << endl;
// print_vector(w.m_data);
// lean_assert(vectors_are_equal<L>(deb_w, w));
#endif
}
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;
}
void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) {
apply_from_left_local(w, settings);
}
void apply_from_right(indexed_vector<T> & w) {
lean_assert(false); // not implemented
}
void apply_from_left(vector<X> & w, lp_settings & /*settings*/) {
apply_from_left_to_vector(w);// , settings);
}
void apply_from_right(vector<T> & w) {
#ifndef NDEBUG
// dense_matrix<T, X> deb(*this);
// vector<T> deb_w(w);
// deb.apply_from_right(deb_w);
#endif
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;
#ifndef NDEBUG
// lean_assert(vectors_are_equal<T>(deb_w, w));
#endif
}
#ifndef NDEBUG
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];
}
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 */) {};
#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);
}
};
}

501
src/util/lp/static_matrix.h Normal file
View file

@ -0,0 +1,501 @@
/*
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 <set>
#include "util/lp/sparse_vector.h"
#include <unordered_map>
#include <utility>
#include "util/lp/matrix_domain.h"
#include "util/lp/indexed_vector.h"
namespace lean {
template <typename T>
struct column_cell {
unsigned m_i; // points to the row
unsigned m_offset; // the offset of the element in the matrix row
T m_value;
column_cell(unsigned i, unsigned offset, T v) : m_i(i), m_offset(offset), m_value(v) {
}
};
template <typename T>
class row_cell {
public:
unsigned m_j; // points to the column
unsigned m_offset; // offset in column
row_cell(unsigned j, unsigned offset, T const & val) : m_j(j), m_offset(offset), m_value(val) {
}
const T & get_val() const { return m_value;}
T & get_val() { return m_value;}
void set_val(T v) {
m_value = v;
}
private:
T m_value;
};
// each assignment for this matrix should be issued only once!!!
template <typename T, typename X>
class static_matrix
#ifndef NDEBUG
: public matrix<T, X>
#endif
{
#ifndef NDEBUG
std::set<pair<unsigned, unsigned>> m_domain;
#endif
public:
typedef vector<row_cell<T>> row_strip;
typedef vector<column_cell<T>> column_strip;
vector<T> m_work_pivot_vector;
vector<row_strip> m_rows;
vector<column_strip> m_columns;
// starting inner classes
class ref {
static_matrix & m_matrix;
unsigned m_row;
unsigned m_col;
public:
ref(static_matrix & m, unsigned row, unsigned col):m_matrix(m), m_row(row), m_col(col) {}
ref & operator=(T const & v) { m_matrix.set( m_row, m_col, v); return *this; }
ref & operator=(ref const & v) { m_matrix.set(m_row, m_col, v.m_matrix.get(v.m_row, v.m_col)); return *this; }
operator T () const { return m_matrix.get_elem(m_row, m_col); }
};
class ref_row {
static_matrix & m_matrix;
unsigned m_row;
public:
ref_row(static_matrix & m, unsigned row):m_matrix(m), m_row(row) {}
ref operator[](unsigned col) const { return ref(m_matrix, m_row, col); }
};
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());
}
}
// constructor with no parameters
static_matrix() {}
// constructor
static_matrix(unsigned m, unsigned n): m_work_pivot_vector(m, numeric_traits<T>::zero()) {
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));
}
}
}
void clear() {
m_work_pivot_vector.clear();
m_rows.clear();
m_columns.clear();
#ifndef NDEBUG
m_domain.clear();
#endif
}
void init_work_pivot_vector(unsigned m) {
while (m--) m_work_pivot_vector.push_back(numeric_traits<T>::zero());
}
void init_empty_matrix(unsigned m, unsigned n) {
init_work_pivot_vector(m);
init_row_columns(m, 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;
};
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;
}
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;
}
void add_columns_at_the_end(unsigned delta) {
for (unsigned i = 0; i < delta; i++)
add_column();
}
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 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 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 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 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);
}
}
#ifndef NDEBUG
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));
}
}
}
#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;
}
column_cell<T> make_column_cell(unsigned column, unsigned offset, T const & val) {
column_cell<T> r(column, offset, val);
return r;
}
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());
#ifndef NDEBUG
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));
}
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;
}
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, 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;
}
}
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;
}
#ifndef NDEBUG
void check_consistency() {
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();
}
}
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()){
cout << "rows have pair (" << t.first.first <<"," << t.first.second << "), but columns don't " << endl;
}
lean_assert(ic != by_cols.end());
lean_assert(t.second == ic->second);
}
}
#endif
void cross_out_row(unsigned k) {
#ifndef NDEBUG
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);
#ifndef NDEBUG
regen_domain();
check_consistency();
#endif
}
//
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 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_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;
}
}
}
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();
}
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();
}
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 clean_row_work_vector(unsigned i) {
for (auto & rc : m_rows[i]) {
m_work_pivot_vector[rc.m_j] = numeric_traits<T>::zero();
}
}
#ifndef NDEBUG
unsigned get_number_of_rows() const { return row_count(); }
unsigned get_number_of_columns() const { return column_count(); }
virtual void set_number_of_rows(unsigned /*m*/) { }
virtual void set_number_of_columns(unsigned /*n*/) { }
#endif
// const T & get_value_of_column_cell(column_cell<T> const & cc) const {
// lean_assert(cc.m_i < m_rows.size());
// lean_assert(cc.m_offset < m_rows[cc.m_i].size());
// return m_rows[cc.m_i][cc.m_offset].get_val();
// }
T get_max_val_in_row(unsigned i) const {
lean_assert(false);
throw 0;// not implemented
}
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_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;
}
};
}

View file

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

View file

@ -24,8 +24,6 @@ void double_abs(double & v) { v = std::abs(v); }
void double_ceil(double & v) { v = std::ceil(v); }
void double_floor(double & v) { v = std::floor(v); }
static double g_zero = 0.0;
double const & numeric_traits<double>::zero() {
return g_zero;
}
double numeric_traits<double>::g_zero = 0.0;
double numeric_traits<double>::g_one = 1.0;
};

View file

@ -42,8 +42,10 @@ public:
static void neg(double & v) { v = -v; }
static void inv(double & v) { v = 1.0/v; }
static void reset(double & v) { v = 0.0; }
static double const & zero();
static double g_zero;
static double const & zero() { return g_zero; }
static double g_one;
static double const & one() { return g_one; }
static void power(double & v, unsigned k) { double_power(v, k); }
static void abs(double & v) { double_abs(v); }
static void ceil(double & v) { double_ceil(v); }
@ -55,6 +57,8 @@ public:
return v1 > v2 ? v1 : v2;
}
static double const & get_double(double const & d) { return d;}
// constants
static const double constexpr pi_l = (3373259426.0 + 273688.0 / (1<<21)) / (1<<30);
static const double constexpr pi_n = (3373259426.0 + 273688.0 / (1<<21)) / (1<<30);

View file

@ -28,4 +28,9 @@ static float g_zero = 0.0;
float const & numeric_traits<float>::zero() {
return g_zero;
}
static float g_one = 1.0;
float const & numeric_traits<float>::one() {
return g_one;
}
};

View file

@ -43,11 +43,13 @@ public:
static void inv(float & v) { v = 1.0/v; }
static void reset(float & v) { v = 0.0; }
static float const & zero();
static float const & one();
static void power(float & v, unsigned k) { float_power(v, k); }
static void abs(float & v) { float_abs(v); }
static void ceil(float & v) { float_ceil(v); }
static void floor(float & v) { float_floor(v); }
static double get_double(float const & d) { return static_cast<double>(d); }
static float const & min(float const & v1, float const & v2) {
return v1 < v2 ? v1 : v2;

View file

@ -137,6 +137,11 @@ mpq const & numeric_traits<mpq>::zero() {
return *g_zero;
}
static mpq * g_one = nullptr;
mpq const & numeric_traits<mpq>::one() {
return *g_one;
}
serializer & operator<<(serializer & s, mpq const & n) {
std::ostringstream out;
out << n;
@ -146,12 +151,14 @@ serializer & operator<<(serializer & s, mpq const & n) {
void initialize_mpq() {
g_zero = new mpq();
g_one = new mpq(1);
numeric_traits<mpq>::initialize();
}
void finalize_mpq() {
numeric_traits<mpq>::finalize();
delete g_zero;
delete g_one;
}
mpq read_mpq(deserializer & d) {

View file

@ -239,11 +239,13 @@ public:
static void inv(mpq & v) { v.inv(); }
static void reset(mpq & v) { v = 0; }
static mpq const & zero();
static mpq const & one();
static void power(mpq & v, unsigned k) { _power(v, v, k); }
static void abs(mpq & v) { v.abs(); }
static void ceil(mpq & v) { v.ceil(); }
static void floor(mpq & v) { v.floor(); }
static double get_double(mpq const & d) { return d.get_double(); }
// constants
static inline mpq pi_lower() { return *pi_l; }