feat(lp): add LP solver and incremental LU factorization
Signed-off-by: Lev Nachmanson <levnach@microsoft.com>
This commit is contained in:
parent
04eaf184a9
commit
fbb3ed8911
65 changed files with 42468 additions and 6 deletions
|
|
@ -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"))
|
||||
|
|
|
|||
6
src/tests/util/lp/CMakeLists.txt
Normal file
6
src/tests/util/lp/CMakeLists.txt
Normal 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)
|
||||
136
src/tests/util/lp/argument_parser.h
Normal file
136
src/tests/util/lp/argument_parser.h
Normal 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
140
src/tests/util/lp/compare_with_glpk.sh
Executable file
140
src/tests/util/lp/compare_with_glpk.sh
Executable 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
|
||||
51
src/tests/util/lp/double_compare.cpp
Normal file
51
src/tests/util/lp/double_compare.cpp
Normal 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;
|
||||
}
|
||||
44
src/tests/util/lp/init_module.cpp
Normal file
44
src/tests/util/lp/init_module.cpp
Normal 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();
|
||||
}
|
||||
}
|
||||
12
src/tests/util/lp/init_module.h
Normal file
12
src/tests/util/lp/init_module.h
Normal 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
36
src/tests/util/lp/l0.mps
Normal 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
|
||||
61
src/tests/util/lp/l0redund.mps
Normal file
61
src/tests/util/lp/l0redund.mps
Normal 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
25
src/tests/util/lp/l1.mps
Normal 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
27
src/tests/util/lp/l2.mps
Normal 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
28
src/tests/util/lp/l3.mps
Normal 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
|
||||
27
src/tests/util/lp/l3flipped.mps
Normal file
27
src/tests/util/lp/l3flipped.mps
Normal 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
22
src/tests/util/lp/l4.mps
Normal 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
|
||||
22
src/tests/util/lp/l4fix.mps
Normal file
22
src/tests/util/lp/l4fix.mps
Normal 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
2690
src/tests/util/lp/lp.cpp
Normal file
File diff suppressed because it is too large
Load diff
838
src/tests/util/lp/mps_reader.h
Normal file
838
src/tests/util/lp/mps_reader.h
Normal 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 <rim(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;
|
||||
}
|
||||
};
|
||||
}
|
||||
600
src/tests/util/lp/murtagh.mps
Normal file
600
src/tests/util/lp/murtagh.mps
Normal 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
|
||||
54
src/tests/util/lp/plan.mps
Normal file
54
src/tests/util/lp/plan.mps
Normal 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
57
src/tests/util/lp/run_netlib.sh
Executable 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
|
||||
29
src/tests/util/lp/samp1.mps
Normal file
29
src/tests/util/lp/samp1.mps
Normal 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
|
||||
27
src/tests/util/lp/samp2.mps
Normal file
27
src/tests/util/lp/samp2.mps
Normal 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
|
||||
382
src/tests/util/lp/smt_reader.h
Normal file
382
src/tests/util/lp/smt_reader.h
Normal 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
74
src/tests/util/lp/test_file_reader.h
Normal file
74
src/tests/util/lp/test_file_reader.h
Normal 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();
|
||||
}
|
||||
};
|
||||
}
|
||||
23587
src/tests/util/lp/test_files/acc-tight5.mps
Normal file
23587
src/tests/util/lp/test_files/acc-tight5.mps
Normal file
File diff suppressed because it is too large
Load diff
BIN
src/tests/util/lp/test_files/netlib.tar.gz
Normal file
BIN
src/tests/util/lp/test_files/netlib.tar.gz
Normal file
Binary file not shown.
1
src/util/lp/CMakeLists.txt
Normal file
1
src/util/lp/CMakeLists.txt
Normal file
|
|
@ -0,0 +1 @@
|
|||
add_library(lp OBJECT lp.cpp)
|
||||
230
src/util/lp/binary_heap_priority_queue.h
Normal file
230
src/util/lp/binary_heap_priority_queue.h
Normal 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]);
|
||||
}
|
||||
};
|
||||
}
|
||||
142
src/util/lp/binary_heap_upair_queue.h
Normal file
142
src/util/lp/binary_heap_upair_queue.h
Normal 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
22
src/util/lp/breakpoint.h
Normal 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) {}
|
||||
};
|
||||
}
|
||||
84
src/util/lp/canonic_left_side.h
Normal file
84
src/util/lp/canonic_left_side.h
Normal 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
199
src/util/lp/column_info.h
Normal 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
388
src/util/lp/core_solver_pretty_printer.h
Normal file
388
src/util/lp/core_solver_pretty_printer.h
Normal 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
179
src/util/lp/eta_matrix.h
Normal 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
|
||||
}
|
||||
};
|
||||
}
|
||||
58
src/util/lp/indexed_value.h
Normal file
58
src/util/lp/indexed_value.h
Normal 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;
|
||||
}
|
||||
}
|
||||
144
src/util/lp/indexed_vector.h
Normal file
144
src/util/lp/indexed_vector.h
Normal 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
|
||||
};
|
||||
}
|
||||
116
src/util/lp/lar_constraints.h
Normal file
116
src/util/lp/lar_constraints.h
Normal 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();
|
||||
}
|
||||
};
|
||||
}
|
||||
870
src/util/lp/lar_core_solver.h
Normal file
870
src/util/lp/lar_core_solver.h
Normal 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;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
18
src/util/lp/lar_solution_signature.h
Normal file
18
src/util/lp/lar_solution_signature.h
Normal 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
884
src/util/lp/lar_solver.h
Normal 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
13
src/util/lp/lp.h
Normal file
|
|
@ -0,0 +1,13 @@
|
|||
/*
|
||||
Copyright (c) 2013 Microsoft Corporation. All rights reserved.
|
||||
Released under Apache 2.0 license as described in the file LICENSE.
|
||||
|
||||
Author: Lev Nachmanson
|
||||
*/
|
||||
|
||||
#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"
|
||||
757
src/util/lp/lp_core_solver_base.h
Normal file
757
src/util/lp/lp_core_solver_base.h
Normal 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";
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
833
src/util/lp/lp_dual_core_solver.h
Normal file
833
src/util/lp/lp_dual_core_solver.h
Normal 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; }
|
||||
};
|
||||
}
|
||||
411
src/util/lp/lp_dual_simplex.h
Normal file
411
src/util/lp/lp_dual_simplex.h
Normal 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
|
||||
}
|
||||
};
|
||||
}
|
||||
1424
src/util/lp/lp_primal_core_solver.h
Normal file
1424
src/util/lp/lp_primal_core_solver.h
Normal file
File diff suppressed because it is too large
Load diff
465
src/util/lp/lp_primal_simplex.h
Normal file
465
src/util/lp/lp_primal_simplex.h
Normal 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
216
src/util/lp/lp_settings.h
Normal 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
688
src/util/lp/lp_solver.h
Normal 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
886
src/util/lp/lu.h
Normal 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
|
||||
}
|
||||
78
src/util/lp/matrix_domain.h
Normal file
78
src/util/lp/matrix_domain.h
Normal 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
239
src/util/lp/numeric_pair.h
Normal 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);}
|
||||
}
|
||||
1238
src/util/lp/permutation_matrix.h
Normal file
1238
src/util/lp/permutation_matrix.h
Normal file
File diff suppressed because it is too large
Load diff
202
src/util/lp/row_eta_matrix.h
Normal file
202
src/util/lp/row_eta_matrix.h
Normal 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
269
src/util/lp/scaler.h
Normal 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
1321
src/util/lp/sparse_matrix.h
Normal file
File diff suppressed because it is too large
Load diff
93
src/util/lp/sparse_vector.h
Normal file
93
src/util/lp/sparse_vector.h
Normal 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
434
src/util/lp/square_dense_submatrix.h
Normal file
434
src/util/lp/square_dense_submatrix.h
Normal 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
501
src/util/lp/static_matrix.h
Normal 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
65
src/util/lp/suhl_pivot_chooser.h
Normal file
65
src/util/lp/suhl_pivot_chooser.h
Normal 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;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
@ -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;
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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) {
|
||||
|
|
|
|||
|
|
@ -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; }
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue