mirror of
				https://github.com/Z3Prover/z3
				synced 2025-10-31 11:42:28 +00:00 
			
		
		
		
	Merge pull request #1013 from NikolajBjorner/master
Integrate new LRA solver capabilities by Lev Nachmanson. The LRA solver relies on a portfolio of Simplex implementations. For large problems it resorts to revised Simplex with different search options. It uses doubles for large systems. For smaller problems it uses a Dual Simplex tableau representation that outperforms the scalable solver when many small calls are used. It uses infinite precision rationals by default. The theory_lra class provides the interface to the scalable Simplex solver.
This commit is contained in:
		
						commit
						e0ac5bd585
					
				
					 120 changed files with 23072 additions and 15 deletions
				
			
		|  | @ -187,6 +187,7 @@ endif() | |||
| # Note for some reason we have to leave off ``-D`` here otherwise | ||||
| # we get ``-D-DZ3DEBUG`` passed to the compiler | ||||
| list(APPEND Z3_COMPONENT_CXX_DEFINES $<$<CONFIG:Debug>:Z3DEBUG>) | ||||
| list(APPEND Z3_COMPONENT_CXX_DEFINES $<$<CONFIG:Debug>:LEAN_DEBUG>) | ||||
| list(APPEND Z3_COMPONENT_CXX_DEFINES $<$<CONFIG:Release>:_EXTERNAL_RELEASE>) | ||||
| list(APPEND Z3_COMPONENT_CXX_DEFINES $<$<CONFIG:RelWithDebInfo>:_EXTERNAL_RELEASE>) | ||||
| 
 | ||||
|  | @ -254,6 +255,7 @@ else() | |||
|   message(FATAL_ERROR "Platform \"${CMAKE_SYSTEM_NAME}\" not recognised") | ||||
| endif() | ||||
| 
 | ||||
| list(APPEND Z3_COMPONENT_EXTRA_INCLUDE_DIRS "${CMAKE_SOURCE_DIR}/src") | ||||
| 
 | ||||
| ################################################################################ | ||||
| # GNU multiple precision library support | ||||
|  | @ -297,6 +299,7 @@ if (USE_OPENMP) | |||
|     message(WARNING "OpenMP support was requested but your compiler doesn't support it") | ||||
|   endif() | ||||
| endif() | ||||
| 
 | ||||
| if (OPENMP_FOUND) | ||||
|     list(APPEND Z3_COMPONENT_CXX_FLAGS ${OpenMP_CXX_FLAGS}) | ||||
|     # GCC and Clang need to have additional flags passed to the linker. | ||||
|  |  | |||
|  | @ -35,6 +35,7 @@ endforeach() | |||
| # raised if you try to declare a component is dependent on another component | ||||
| # that has not yet been declared. | ||||
| add_subdirectory(util) | ||||
| add_subdirectory(util/lp) | ||||
| add_subdirectory(math/polynomial) | ||||
| add_subdirectory(sat) | ||||
| add_subdirectory(nlsat) | ||||
|  |  | |||
|  | @ -27,6 +27,7 @@ add_executable(shell | |||
|   opt_frontend.cpp | ||||
|   smtlib_frontend.cpp | ||||
|   z3_log_frontend.cpp | ||||
|   lp_frontend.cpp | ||||
| # FIXME: shell should really link against libz3 but it can't due to requiring | ||||
| # use of some hidden symbols. Also libz3 has the ``api_dll`` component which | ||||
| # we don't want (I think). | ||||
|  |  | |||
|  | @ -55,6 +55,7 @@ z3_add_component(smt | |||
|     theory_dl.cpp | ||||
|     theory_dummy.cpp | ||||
|     theory_fpa.cpp | ||||
|     theory_lra.cpp | ||||
|     theory_opt.cpp | ||||
|     theory_pb.cpp | ||||
|     theory_seq.cpp | ||||
|  | @ -69,6 +70,7 @@ z3_add_component(smt | |||
|     euclid | ||||
|     fpa | ||||
|     grobner | ||||
|     lp | ||||
|     macros | ||||
|     normal_forms | ||||
|     parser_util | ||||
|  |  | |||
|  | @ -117,6 +117,7 @@ add_executable(test-z3 | |||
|   upolynomial.cpp | ||||
|   var_subst.cpp | ||||
|   vector.cpp | ||||
|   lp.cpp | ||||
|   ${z3_test_extra_object_files} | ||||
| ) | ||||
| z3_add_install_tactic_rule(${z3_test_deps}) | ||||
|  | @ -128,3 +129,14 @@ target_link_libraries(test-z3 PRIVATE ${Z3_DEPENDENT_LIBS}) | |||
| target_include_directories(test-z3 PRIVATE ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS}) | ||||
| z3_append_linker_flag_list_to_target(test-z3 ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) | ||||
| z3_add_component_dependencies_to_target(test-z3 ${z3_test_expanded_deps}) | ||||
| 
 | ||||
| add_executable(lp_tst lp_main.cpp lp.cpp $<TARGET_OBJECTS:util>   $<TARGET_OBJECTS:lp>) | ||||
| target_compile_definitions(lp_tst PRIVATE ${Z3_COMPONENT_CXX_DEFINES}) | ||||
| target_compile_options(lp_tst PRIVATE ${Z3_COMPONENT_CXX_FLAGS}) | ||||
| target_include_directories(lp_tst PRIVATE ${Z3_COMPONENT_EXTRA_INCLUDE_DIRS}) | ||||
| target_link_libraries(lp_tst PRIVATE ${Z3_DEPENDENT_LIBS}) | ||||
| z3_append_linker_flag_list_to_target(lp_tst ${Z3_DEPENDENT_EXTRA_CXX_LINK_FLAGS}) | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  |  | |||
|  | @ -11,6 +11,7 @@ from mk_util import * | |||
| def init_project_def(): | ||||
|     set_version(4, 5, 1, 0) | ||||
|     add_lib('util', []) | ||||
|     add_lib('lp', ['util'], 'util/lp') | ||||
|     add_lib('polynomial', ['util'], 'math/polynomial') | ||||
|     add_lib('sat', ['util']) | ||||
|     add_lib('nlsat', ['polynomial', 'sat']) | ||||
|  | @ -52,7 +53,7 @@ def init_project_def(): | |||
|     add_lib('smt_params', ['ast', 'simplifier', 'pattern', 'bit_blaster'], 'smt/params') | ||||
|     add_lib('proto_model', ['model', 'simplifier', 'smt_params'], 'smt/proto_model') | ||||
|     add_lib('smt', ['bit_blaster', 'macros', 'normal_forms', 'cmd_context', 'proto_model', | ||||
|                     'substitution', 'grobner', 'euclid', 'simplex', 'proof_checker', 'pattern', 'parser_util', 'fpa']) | ||||
|                     'substitution', 'grobner', 'euclid', 'simplex', 'proof_checker', 'pattern', 'parser_util', 'fpa', 'lp']) | ||||
|     add_lib('bv_tactics', ['tactic', 'bit_blaster', 'core_tactics'], 'tactic/bv') | ||||
|     add_lib('fuzzing', ['ast'], 'test/fuzzing') | ||||
|     add_lib('smt_tactic', ['smt'], 'smt/tactic') | ||||
|  |  | |||
|  | @ -774,8 +774,13 @@ def extract_c_includes(fname): | |||
|     linenum = 1 | ||||
|     for line in f: | ||||
|         m1 = std_inc_pat.match(line) | ||||
|         if m1: | ||||
|             result.append(m1.group(1)) | ||||
|         if m1:  | ||||
|             root_file_name = m1.group(1) | ||||
|             slash_pos =  root_file_name.rfind('/') | ||||
|             if slash_pos >= 0  and root_file_name.find("..") < 0 : #it is a hack for lp include files that behave as continued from "src" | ||||
|                 print(root_file_name) | ||||
|                 root_file_name = root_file_name[slash_pos+1:] | ||||
|             result.append(root_file_name) | ||||
|         elif not system_inc_pat.match(line) and non_std_inc_pat.match(line): | ||||
|             raise MKException("Invalid #include directive at '%s':%s" % (fname, line)) | ||||
|         linenum = linenum + 1 | ||||
|  | @ -999,6 +1004,7 @@ class Component: | |||
|         out.write('%s =' % include_defs) | ||||
|         for dep in self.deps: | ||||
|             out.write(' -I%s' % get_component(dep).to_src_dir) | ||||
|         out.write(' -I..\src') | ||||
|         out.write('\n') | ||||
|         mk_dir(os.path.join(BUILD_DIR, self.build_dir)) | ||||
|         if VS_PAR and IS_WINDOWS: | ||||
|  |  | |||
|  | @ -305,6 +305,7 @@ public: | |||
|     MATCH_UNARY(is_uminus); | ||||
|     MATCH_UNARY(is_to_real); | ||||
|     MATCH_UNARY(is_to_int); | ||||
|     MATCH_UNARY(is_is_int); | ||||
|     MATCH_BINARY(is_sub); | ||||
|     MATCH_BINARY(is_add); | ||||
|     MATCH_BINARY(is_mul); | ||||
|  | @ -377,6 +378,9 @@ public: | |||
|     app * mk_real(int i) { | ||||
|         return mk_numeral(rational(i), false); | ||||
|     } | ||||
|     app * mk_real(rational const& r) { | ||||
|         return mk_numeral(r, false); | ||||
|     } | ||||
|     app * mk_le(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LE, arg1, arg2); } | ||||
|     app * mk_ge(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_GE, arg1, arg2); } | ||||
|     app * mk_lt(expr * arg1, expr * arg2) const { return m_manager.mk_app(m_afid, OP_LT, arg1, arg2); } | ||||
|  |  | |||
|  | @ -26,6 +26,7 @@ Notes: | |||
| #include "theory_diff_logic.h" | ||||
| #include "theory_dense_diff_logic.h" | ||||
| #include "theory_pb.h" | ||||
| #include "theory_lra.h" | ||||
| #include "ast_pp.h" | ||||
| #include "ast_smt_pp.h" | ||||
| #include "pp_params.hpp" | ||||
|  | @ -143,6 +144,9 @@ namespace opt { | |||
|         else if (typeid(smt::theory_dense_si&) == typeid(*arith_theory)) {    | ||||
|             return dynamic_cast<smt::theory_dense_si&>(*arith_theory);  | ||||
|         } | ||||
|         else if (typeid(smt::theory_lra&) == typeid(*arith_theory)) { | ||||
|             return dynamic_cast<smt::theory_lra&>(*arith_theory);  | ||||
|         } | ||||
|         else { | ||||
|             UNREACHABLE(); | ||||
|             return dynamic_cast<smt::theory_mi_arith&>(*arith_theory);  | ||||
|  | @ -401,6 +405,14 @@ namespace opt { | |||
|             return th.mk_ge(m_fm, v, val); | ||||
|         } | ||||
| 
 | ||||
| 
 | ||||
|         if (typeid(smt::theory_lra) == typeid(opt)) { | ||||
|             smt::theory_lra& th = dynamic_cast<smt::theory_lra&>(opt);  | ||||
|             SASSERT(val.is_finite()); | ||||
|             return th.mk_ge(m_fm, v, val.get_numeral());             | ||||
|         } | ||||
| 
 | ||||
|         // difference logic?
 | ||||
|         if (typeid(smt::theory_dense_si) == typeid(opt) && | ||||
|             val.get_infinitesimal().is_zero()) { | ||||
|             smt::theory_dense_si& th = dynamic_cast<smt::theory_dense_si&>(opt); | ||||
|  |  | |||
|  | @ -35,8 +35,9 @@ Revision History: | |||
| #include"error_codes.h" | ||||
| #include"gparams.h" | ||||
| #include"env_params.h" | ||||
| #include "lp_frontend.h" | ||||
| 
 | ||||
| typedef enum { IN_UNSPECIFIED, IN_SMTLIB, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_Z3_LOG } input_kind; | ||||
| typedef enum { IN_UNSPECIFIED, IN_SMTLIB, IN_SMTLIB_2, IN_DATALOG, IN_DIMACS, IN_WCNF, IN_OPB, IN_Z3_LOG, IN_MPS } input_kind; | ||||
| 
 | ||||
| std::string         g_aux_input_file; | ||||
| char const *        g_input_file          = 0; | ||||
|  | @ -342,6 +343,10 @@ int STD_CALL main(int argc, char ** argv) { | |||
|                 else if (strcmp(ext, "smt") == 0) { | ||||
|                     g_input_kind = IN_SMTLIB; | ||||
|                 } | ||||
|                 else if (strcmp(ext, "mps") == 0 || strcmp(ext, "sif") == 0 || | ||||
|                          strcmp(ext, "MPS") == 0 || strcmp(ext, "SIF") == 0) { | ||||
|                     g_input_kind = IN_MPS; | ||||
|                 } | ||||
|             } | ||||
|     } | ||||
|         switch (g_input_kind) { | ||||
|  | @ -367,6 +372,9 @@ int STD_CALL main(int argc, char ** argv) { | |||
|         case IN_Z3_LOG: | ||||
|             replay_z3_log(g_input_file); | ||||
|             break; | ||||
|         case IN_MPS: | ||||
|             return_value = read_mps_file(g_input_file); | ||||
|             break; | ||||
|         default: | ||||
|             UNREACHABLE(); | ||||
|         } | ||||
|  |  | |||
|  | @ -20,6 +20,7 @@ Revision History: | |||
| #include"smt_setup.h" | ||||
| #include"static_features.h" | ||||
| #include"theory_arith.h" | ||||
| #include"theory_lra.h" | ||||
| #include"theory_dense_diff_logic.h" | ||||
| #include"theory_diff_logic.h" | ||||
| #include"theory_utvpi.h" | ||||
|  | @ -442,7 +443,7 @@ namespace smt { | |||
|         m_params.m_arith_propagate_eqs = false; | ||||
|         m_params.m_eliminate_term_ite  = true; | ||||
|         m_params.m_nnf_cnf             = false; | ||||
|         setup_mi_arith(); | ||||
|         setup_r_arith(); | ||||
|     } | ||||
| 
 | ||||
|     void setup::setup_QF_LRA(static_features const & st) { | ||||
|  | @ -467,7 +468,7 @@ namespace smt { | |||
|             m_params.m_restart_adaptive      = false; | ||||
|         } | ||||
|         m_params.m_arith_small_lemma_size = 32; | ||||
|         setup_mi_arith(); | ||||
|         setup_r_arith(); | ||||
|     } | ||||
| 
 | ||||
|     void setup::setup_QF_LIA() { | ||||
|  | @ -539,7 +540,7 @@ namespace smt { | |||
|         m_params.m_relevancy_lvl       = 0; | ||||
|         m_params.m_arith_reflect       = false;  | ||||
|         m_params.m_nnf_cnf             = false; | ||||
|         setup_mi_arith(); | ||||
|         setup_r_arith(); | ||||
|     } | ||||
| 
 | ||||
|     void setup::setup_QF_BV() { | ||||
|  | @ -718,6 +719,13 @@ namespace smt { | |||
|         m_context.register_plugin(alloc(smt::theory_i_arith, m_manager, m_params)); | ||||
|     } | ||||
| 
 | ||||
|     void setup::setup_r_arith() { | ||||
|         m_context.register_plugin(alloc(smt::theory_mi_arith, m_manager, m_params)); | ||||
|          | ||||
|         // Disabled in initial commit of LRA additions
 | ||||
|         // m_context.register_plugin(alloc(smt::theory_lra, m_manager, m_params));
 | ||||
|     } | ||||
| 
 | ||||
|     void setup::setup_mi_arith() { | ||||
|         if (m_params.m_arith_mode == AS_OPTINF) { | ||||
|             m_context.register_plugin(alloc(smt::theory_inf_arith, m_manager, m_params));             | ||||
|  |  | |||
|  | @ -99,6 +99,7 @@ namespace smt { | |||
|         void setup_card(); | ||||
|         void setup_i_arith(); | ||||
|         void setup_mi_arith(); | ||||
|         void setup_r_arith(); | ||||
|         void setup_fpa(); | ||||
|         void setup_str(); | ||||
| 
 | ||||
|  |  | |||
|  | @ -54,7 +54,7 @@ namespace smt { | |||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void theory::display_app(std::ostream & out, app * n) const { | ||||
|     std::ostream& theory::display_app(std::ostream & out, app * n) const { | ||||
|         func_decl * d = n->get_decl(); | ||||
|         if (n->get_num_args() == 0) { | ||||
|             out << d->get_name(); | ||||
|  | @ -73,9 +73,10 @@ namespace smt { | |||
|         else { | ||||
|             out << "#" << n->get_id(); | ||||
|         } | ||||
|         return out; | ||||
|     } | ||||
| 
 | ||||
|     void theory::display_flat_app(std::ostream & out, app * n) const { | ||||
|     std::ostream& theory::display_flat_app(std::ostream & out, app * n) const { | ||||
|         func_decl * d = n->get_decl(); | ||||
|         if (n->get_num_args() == 0) { | ||||
|             out << d->get_name(); | ||||
|  | @ -106,6 +107,7 @@ namespace smt { | |||
|         else { | ||||
|             out << "#" << n->get_id(); | ||||
|         } | ||||
|         return out; | ||||
|     } | ||||
|      | ||||
|     bool theory::is_relevant_and_shared(enode * n) const { | ||||
|  |  | |||
|  | @ -337,14 +337,14 @@ namespace smt { | |||
|          | ||||
|         virtual void collect_statistics(::statistics & st) const { | ||||
|         } | ||||
| 
 | ||||
|         void display_app(std::ostream & out, app * n) const; | ||||
| 
 | ||||
|         void display_flat_app(std::ostream & out, app * n) const; | ||||
|          | ||||
|         void display_var_def(std::ostream & out, theory_var v) const { return display_app(out, get_enode(v)->get_owner()); } | ||||
|         std::ostream& display_app(std::ostream & out, app * n) const; | ||||
|          | ||||
|         void display_var_flat_def(std::ostream & out, theory_var v) const { return display_flat_app(out, get_enode(v)->get_owner()); } | ||||
|         std::ostream& display_flat_app(std::ostream & out, app * n) const; | ||||
|          | ||||
|         std::ostream& display_var_def(std::ostream & out, theory_var v) const { return display_app(out, get_enode(v)->get_owner()); } | ||||
|          | ||||
|         std::ostream& display_var_flat_def(std::ostream & out, theory_var v) const { return display_flat_app(out, get_enode(v)->get_owner());  } | ||||
| 
 | ||||
|         /**
 | ||||
|            \brief Assume eqs between variable that are equal with respect to the given table. | ||||
|  |  | |||
|  | @ -239,6 +239,7 @@ int main(int argc, char ** argv) { | |||
|     TST(pdr); | ||||
|     TST_ARGV(ddnf); | ||||
|     TST(model_evaluator); | ||||
|     TST_ARGV(lp); | ||||
|     TST(get_consequences); | ||||
|     TST(pb2bv); | ||||
|     TST_ARGV(cnf_backbones); | ||||
|  |  | |||
							
								
								
									
										71
									
								
								src/util/lp/binary_heap_priority_queue.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								src/util/lp/binary_heap_priority_queue.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,71 @@ | |||
| 
 | ||||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include "util/debug.h" | ||||
| #include "util/lp/lp_utils.h" | ||||
| namespace lean { | ||||
| // 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); | ||||
|     void put_at(unsigned i, unsigned h); | ||||
|     void decrease_priority(unsigned o, T newPriority); | ||||
| public: | ||||
| #ifdef LEAN_DEBUG | ||||
|     bool is_consistent() const; | ||||
| #endif | ||||
| public: | ||||
|     void remove(unsigned o); | ||||
|     unsigned size() const { return m_heap_size; } | ||||
|     binary_heap_priority_queue(): m_heap(1) {} // the empty constructror
 | ||||
|     // n is the initial queue capacity.
 | ||||
|     // The capacity will be enlarged two times automatically if needed
 | ||||
|     binary_heap_priority_queue(unsigned n); | ||||
| 
 | ||||
|     void clear() { | ||||
|         for (unsigned i = 0; i < m_heap_size; i++) { | ||||
|             unsigned o = m_heap[i+1]; | ||||
|             m_heap_inverse[o] = -1; | ||||
|         } | ||||
|         m_heap_size = 0; | ||||
|     } | ||||
| 
 | ||||
|     void resize(unsigned n); | ||||
|     void put_to_heap(unsigned i, unsigned o); | ||||
| 
 | ||||
|     void enqueue_new(unsigned o, const T& priority); | ||||
| 
 | ||||
|     // This method can work with an element that is already in the queue.
 | ||||
|     // In this case the priority will be changed and the queue adjusted.
 | ||||
|     void enqueue(unsigned o, const T & priority); | ||||
|     void change_priority_for_existing(unsigned o, const T & priority); | ||||
|     T get_priority(unsigned o) const { return m_priorities[o]; } | ||||
|     bool is_empty() const { return m_heap_size == 0; } | ||||
| 
 | ||||
|     /// return the first element of the queue and removes it from the queue
 | ||||
|     unsigned dequeue_and_get_priority(T & priority); | ||||
|     void fix_heap_under(unsigned i); | ||||
|     void put_the_last_at_the_top_and_fix_the_heap(); | ||||
|     /// return the first element of the queue and removes it from the queue
 | ||||
|     unsigned dequeue(); | ||||
|     unsigned peek() const { | ||||
|         lean_assert(m_heap_size > 0); | ||||
|         return m_heap[1]; | ||||
|     } | ||||
| #ifdef LEAN_DEBUG | ||||
|     void print(std::ostream & out); | ||||
| #endif | ||||
| }; | ||||
| } | ||||
							
								
								
									
										193
									
								
								src/util/lp/binary_heap_priority_queue.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										193
									
								
								src/util/lp/binary_heap_priority_queue.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,193 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/binary_heap_priority_queue.h" | ||||
| namespace lean { | ||||
| // is is the child place in heap
 | ||||
| template <typename T> void binary_heap_priority_queue<T>::swap_with_parent(unsigned i) { | ||||
|     unsigned parent = m_heap[i >> 1]; | ||||
|     put_at(i >> 1, m_heap[i]); | ||||
|     put_at(i, parent); | ||||
| } | ||||
| 
 | ||||
| template <typename T> void binary_heap_priority_queue<T>::put_at(unsigned i, unsigned h) { | ||||
|     m_heap[i] = h; | ||||
|     m_heap_inverse[h] = i; | ||||
| } | ||||
| 
 | ||||
| template <typename T> void binary_heap_priority_queue<T>::decrease_priority(unsigned o, T newPriority) { | ||||
|     m_priorities[o] = newPriority; | ||||
|     int i = m_heap_inverse[o]; | ||||
|     while (i > 1) { | ||||
|         if (m_priorities[m_heap[i]] < m_priorities[m_heap[i >> 1]]) | ||||
|             swap_with_parent(i); | ||||
|         else | ||||
|             break; | ||||
|         i >>= 1; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T> bool binary_heap_priority_queue<T>::is_consistent() const { | ||||
|     for (int i = 0; i < m_heap_inverse.size(); i++) { | ||||
|         int i_index = m_heap_inverse[i]; | ||||
|         lean_assert(i_index <= static_cast<int>(m_heap_size)); | ||||
|         lean_assert(i_index == -1 || m_heap[i_index] == i); | ||||
|     } | ||||
|     for (unsigned i = 1; i < m_heap_size; i++) { | ||||
|         unsigned ch = i << 1; | ||||
|         for (int k = 0; k < 2; k++) { | ||||
|             if (ch > m_heap_size) break; | ||||
|             if (!(m_priorities[m_heap[i]] <= m_priorities[m_heap[ch]])){ | ||||
|                 return false; | ||||
|             } | ||||
|             ch++; | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| #endif | ||||
| template <typename T> void binary_heap_priority_queue<T>::remove(unsigned o) { | ||||
|     T priority_of_o = m_priorities[o]; | ||||
|     int o_in_heap = m_heap_inverse[o]; | ||||
|     if (o_in_heap == -1)  { | ||||
|         return;  // nothing to do
 | ||||
|     } | ||||
|     lean_assert(static_cast<unsigned>(o_in_heap) <= m_heap_size); | ||||
|     if (static_cast<unsigned>(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(static_cast<unsigned>(o_in_heap) == m_heap_size); | ||||
|         m_heap_size--; | ||||
|     } | ||||
|     m_heap_inverse[o] = -1; | ||||
|     // lean_assert(is_consistent());
 | ||||
| } | ||||
| // n is the initial queue capacity.
 | ||||
| // The capacity will be enlarged two times automatically if needed
 | ||||
| template <typename T> binary_heap_priority_queue<T>::binary_heap_priority_queue(unsigned n) : | ||||
|     m_priorities(n), | ||||
|     m_heap(n + 1), // because the indexing for A starts from 1
 | ||||
|     m_heap_inverse(n, -1) | ||||
| { } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T> void binary_heap_priority_queue<T>::resize(unsigned n) { | ||||
|     m_priorities.resize(n); | ||||
|     m_heap.resize(n + 1); | ||||
|     m_heap_inverse.resize(n, -1); | ||||
| } | ||||
| 
 | ||||
| template <typename T> void binary_heap_priority_queue<T>::put_to_heap(unsigned i, unsigned o) { | ||||
|     m_heap[i] = o; | ||||
|     m_heap_inverse[o] = i; | ||||
| } | ||||
| 
 | ||||
| template <typename T> void binary_heap_priority_queue<T>::enqueue_new(unsigned o, const T& priority) { | ||||
|     m_heap_size++; | ||||
|     int i = m_heap_size; | ||||
|     lean_assert(o < m_priorities.size()); | ||||
|     m_priorities[o] = priority; | ||||
|     put_at(i, o); | ||||
|     while (i > 1 && m_priorities[m_heap[i >> 1]] > priority) { | ||||
|         swap_with_parent(i); | ||||
|         i >>= 1; | ||||
|     } | ||||
| } | ||||
| // This method can work with an element that is already in the queue.
 | ||||
| // In this case the priority will be changed and the queue adjusted.
 | ||||
| template <typename T> void binary_heap_priority_queue<T>::enqueue(unsigned o, const T & priority) { | ||||
|     if (o >= m_priorities.size()) { | ||||
|         resize(o << 1); // make the size twice larger
 | ||||
|     } | ||||
|     if (m_heap_inverse[o] == -1) | ||||
|         enqueue_new(o, priority); | ||||
|     else | ||||
|         change_priority_for_existing(o, priority); | ||||
| } | ||||
| 
 | ||||
| template <typename T> void binary_heap_priority_queue<T>::change_priority_for_existing(unsigned o, const T & priority) { | ||||
|     if (m_priorities[o] > priority) { | ||||
|         decrease_priority(o, priority); | ||||
|     } else { | ||||
|         m_priorities[o] = priority; | ||||
|         fix_heap_under(m_heap_inverse[o]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| /// return the first element of the queue and removes it from the queue
 | ||||
| template <typename T> unsigned binary_heap_priority_queue<T>::dequeue_and_get_priority(T & priority) { | ||||
|     lean_assert(m_heap_size != 0); | ||||
|     int ret = m_heap[1]; | ||||
|     priority = m_priorities[ret]; | ||||
|     put_the_last_at_the_top_and_fix_the_heap(); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template <typename T> void binary_heap_priority_queue<T>::fix_heap_under(unsigned i) { | ||||
|     while (true) { | ||||
|         unsigned smallest = i; | ||||
|         unsigned l = i << 1; | ||||
|         if (l <= m_heap_size && m_priorities[m_heap[l]] < m_priorities[m_heap[i]]) | ||||
|             smallest = l; | ||||
|         unsigned r = l + 1; | ||||
|         if (r <= m_heap_size && m_priorities[m_heap[r]] < m_priorities[m_heap[smallest]]) | ||||
|             smallest = r; | ||||
|         if (smallest != i) | ||||
|             swap_with_parent(smallest); | ||||
|         else | ||||
|             break; | ||||
|         i = smallest; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T> void binary_heap_priority_queue<T>::put_the_last_at_the_top_and_fix_the_heap() { | ||||
|     if (m_heap_size > 1) { | ||||
|         put_at(1, m_heap[m_heap_size--]); | ||||
|         fix_heap_under(1); | ||||
|     } else { | ||||
|         m_heap_size--; | ||||
|     } | ||||
| } | ||||
| /// return the first element of the queue and removes it from the queue
 | ||||
| template <typename T> unsigned binary_heap_priority_queue<T>::dequeue() { | ||||
|     lean_assert(m_heap_size > 0); | ||||
|     int ret = m_heap[1]; | ||||
|     put_the_last_at_the_top_and_fix_the_heap(); | ||||
|     m_heap_inverse[ret] = -1; | ||||
|     return ret; | ||||
| } | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T> void binary_heap_priority_queue<T>::print(std::ostream & out) { | ||||
|     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); | ||||
|         out << "(" << j << ", " << prior << ")"; | ||||
|     } | ||||
|     out << std::endl; | ||||
|     // restore the queue
 | ||||
|     for (int i = 0; i < index.size(); i++) | ||||
|         enqueue(index[i], prs[i]); | ||||
| } | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										26
									
								
								src/util/lp/binary_heap_priority_queue_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										26
									
								
								src/util/lp/binary_heap_priority_queue_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,26 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/numeric_pair.h" | ||||
| #include "util/lp/binary_heap_priority_queue.hpp" | ||||
| namespace lean { | ||||
| template binary_heap_priority_queue<int>::binary_heap_priority_queue(unsigned int); | ||||
| template unsigned binary_heap_priority_queue<int>::dequeue(); | ||||
| template void binary_heap_priority_queue<int>::enqueue(unsigned int, int const&); | ||||
| template void binary_heap_priority_queue<double>::enqueue(unsigned int, double const&); | ||||
| template void binary_heap_priority_queue<mpq>::enqueue(unsigned int, mpq const&); | ||||
| template void binary_heap_priority_queue<int>::remove(unsigned int); | ||||
| template unsigned binary_heap_priority_queue<numeric_pair<mpq> >::dequeue(); | ||||
| template unsigned binary_heap_priority_queue<double>::dequeue(); | ||||
| template unsigned binary_heap_priority_queue<mpq>::dequeue(); | ||||
| template void binary_heap_priority_queue<numeric_pair<mpq> >::enqueue(unsigned int, numeric_pair<mpq> const&); | ||||
| template void binary_heap_priority_queue<numeric_pair<mpq> >::resize(unsigned int); | ||||
| template void lean::binary_heap_priority_queue<double>::resize(unsigned int); | ||||
| template binary_heap_priority_queue<unsigned int>::binary_heap_priority_queue(unsigned int); | ||||
| template void binary_heap_priority_queue<unsigned>::resize(unsigned int); | ||||
| template unsigned binary_heap_priority_queue<unsigned int>::dequeue(); | ||||
| template void binary_heap_priority_queue<unsigned int>::enqueue(unsigned int, unsigned int const&); | ||||
| template void binary_heap_priority_queue<unsigned int>::remove(unsigned int); | ||||
| template void lean::binary_heap_priority_queue<mpq>::resize(unsigned int); | ||||
| } | ||||
							
								
								
									
										50
									
								
								src/util/lp/binary_heap_upair_queue.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/util/lp/binary_heap_upair_queue.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include <unordered_set> | ||||
| #include <unordered_map> | ||||
| #include <queue> | ||||
| #include "util/vector.h" | ||||
| #include <set> | ||||
| #include <utility> | ||||
| #include "util/lp/binary_heap_priority_queue.h" | ||||
| 
 | ||||
| 
 | ||||
| typedef std::pair<unsigned, unsigned> upair; | ||||
| 
 | ||||
| namespace lean { | ||||
| template <typename  T> | ||||
| class binary_heap_upair_queue { | ||||
|     binary_heap_priority_queue<T> m_q; | ||||
|     std::unordered_map<upair, unsigned> m_pairs_to_index; | ||||
|     vector<upair> m_pairs; // inverse to index
 | ||||
|     vector<unsigned> m_available_spots; | ||||
| public: | ||||
|     binary_heap_upair_queue(unsigned size); | ||||
| 
 | ||||
|     unsigned dequeue_available_spot(); | ||||
|     bool is_empty() const { return m_q.is_empty(); } | ||||
| 
 | ||||
|     unsigned size() const {return m_q.size(); } | ||||
| 
 | ||||
|     bool contains(unsigned i, unsigned j) const { return m_pairs_to_index.find(std::make_pair(i, j)) != m_pairs_to_index.end(); | ||||
|     } | ||||
| 
 | ||||
|     void remove(unsigned i, unsigned j); | ||||
|     bool ij_index_is_new(unsigned ij_index) const; | ||||
|     void enqueue(unsigned i, unsigned j, const T & priority); | ||||
|     void dequeue(unsigned & i, unsigned &j); | ||||
|     T get_priority(unsigned i, unsigned j) const; | ||||
| #ifdef LEAN_DEBUG | ||||
|     bool pair_to_index_is_a_bijection() const; | ||||
|     bool available_spots_are_correct() const; | ||||
|     bool is_correct() const { | ||||
|         return m_q.is_consistent() && pair_to_index_is_a_bijection() && available_spots_are_correct(); | ||||
|     } | ||||
| #endif | ||||
|     void resize(unsigned size) { m_q.resize(size); } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										110
									
								
								src/util/lp/binary_heap_upair_queue.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/util/lp/binary_heap_upair_queue.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,110 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #include <set> | ||||
| #include "util/lp/lp_utils.h" | ||||
| #include "util/lp/binary_heap_upair_queue.h" | ||||
| namespace lean { | ||||
| template <typename T> binary_heap_upair_queue<T>::binary_heap_upair_queue(unsigned size) : m_q(size), m_pairs(size) { | ||||
|     for (unsigned i = 0; i < size; i++) | ||||
|         m_available_spots.push_back(i); | ||||
| } | ||||
| 
 | ||||
| template <typename T> unsigned | ||||
| binary_heap_upair_queue<T>::dequeue_available_spot() { | ||||
|     lean_assert(m_available_spots.empty() == false); | ||||
|     unsigned ret = m_available_spots.back(); | ||||
|     m_available_spots.pop_back(); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template <typename T> void binary_heap_upair_queue<T>::remove(unsigned i, unsigned j) { | ||||
|     upair p(i, j); | ||||
|     auto it = m_pairs_to_index.find(p); | ||||
|     if (it == m_pairs_to_index.end()) | ||||
|         return; // nothing to do
 | ||||
|     m_q.remove(it->second); | ||||
|     m_available_spots.push_back(it->second); | ||||
|     m_pairs_to_index.erase(it); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T> bool binary_heap_upair_queue<T>::ij_index_is_new(unsigned ij_index) const { | ||||
|     for (auto it : m_pairs_to_index) { | ||||
|         if (it.second == ij_index) | ||||
|             return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T> void binary_heap_upair_queue<T>::enqueue(unsigned i, unsigned j, const T & priority) { | ||||
|     upair p(i, j); | ||||
|     auto it = m_pairs_to_index.find(p); | ||||
|     unsigned ij_index; | ||||
|     if (it == m_pairs_to_index.end()) { | ||||
|         // it is a new pair, let us find a spot for it
 | ||||
|         if (m_available_spots.empty()) { | ||||
|             // we ran out of empty spots
 | ||||
|             unsigned size_was = static_cast<unsigned>(m_pairs.size()); | ||||
|             unsigned new_size = size_was << 1; | ||||
|             for (unsigned i = size_was; i < new_size; i++) | ||||
|                 m_available_spots.push_back(i); | ||||
|             m_pairs.resize(new_size); | ||||
|         } | ||||
|         ij_index = dequeue_available_spot(); | ||||
|         // lean_assert(ij_index<m_pairs.size() && ij_index_is_new(ij_index));
 | ||||
|         m_pairs[ij_index] = p; | ||||
|         m_pairs_to_index[p] = ij_index; | ||||
|     } else { | ||||
|         ij_index = it->second; | ||||
|     } | ||||
|     m_q.enqueue(ij_index, priority); | ||||
| } | ||||
| 
 | ||||
| template <typename T> void binary_heap_upair_queue<T>::dequeue(unsigned & i, unsigned &j) { | ||||
|     lean_assert(!m_q.is_empty()); | ||||
|     unsigned ij_index = m_q.dequeue(); | ||||
|     upair & p = m_pairs[ij_index]; | ||||
|     i = p.first; | ||||
|     j = p.second; | ||||
|     m_available_spots.push_back(ij_index); | ||||
|     m_pairs_to_index.erase(p); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T> T binary_heap_upair_queue<T>::get_priority(unsigned i, unsigned j) const { | ||||
|     auto it =  m_pairs_to_index.find(std::make_pair(i, j)); | ||||
|     if (it == m_pairs_to_index.end()) | ||||
|         return T(0xFFFFFF); // big number
 | ||||
|     return m_q.get_priority(it->second); | ||||
| } | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T> bool binary_heap_upair_queue<T>::pair_to_index_is_a_bijection() const { | ||||
|     std::set<int> tmp; | ||||
|     for (auto p : m_pairs_to_index) { | ||||
|         unsigned j = p.second; | ||||
|         unsigned size = tmp.size(); | ||||
|         tmp.insert(j); | ||||
|         if (tmp.size() == size) | ||||
|             return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T> bool binary_heap_upair_queue<T>::available_spots_are_correct() const { | ||||
|     std::set<int> tmp; | ||||
|     for (auto p : m_available_spots){ | ||||
|         tmp.insert(p); | ||||
|     } | ||||
|     if (tmp.size() != m_available_spots.size()) | ||||
|         return false; | ||||
|     for (auto it : m_pairs_to_index) | ||||
|         if (tmp.find(it.second) != tmp.end()) | ||||
|             return false; | ||||
|     return true; | ||||
| } | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										17
									
								
								src/util/lp/binary_heap_upair_queue_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										17
									
								
								src/util/lp/binary_heap_upair_queue_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,17 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/binary_heap_upair_queue.hpp" | ||||
| namespace lean { | ||||
| template binary_heap_upair_queue<int>::binary_heap_upair_queue(unsigned int); | ||||
| template binary_heap_upair_queue<unsigned int>::binary_heap_upair_queue(unsigned int); | ||||
| template unsigned binary_heap_upair_queue<int>::dequeue_available_spot(); | ||||
| template unsigned binary_heap_upair_queue<unsigned int>::dequeue_available_spot(); | ||||
| template void binary_heap_upair_queue<int>::enqueue(unsigned int, unsigned int, int const&); | ||||
| template void binary_heap_upair_queue<int>::remove(unsigned int, unsigned int); | ||||
| template void binary_heap_upair_queue<unsigned int>::remove(unsigned int, unsigned int); | ||||
| template void binary_heap_upair_queue<int>::dequeue(unsigned int&, unsigned int&); | ||||
| template void binary_heap_upair_queue<unsigned int>::enqueue(unsigned int, unsigned int, unsigned int const&); | ||||
| template void binary_heap_upair_queue<unsigned int>::dequeue(unsigned int&, unsigned int&); | ||||
| } | ||||
							
								
								
									
										333
									
								
								src/util/lp/bound_analyzer_on_row.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										333
									
								
								src/util/lp/bound_analyzer_on_row.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,333 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/linear_combination_iterator.h" | ||||
| #include "implied_bound.h" | ||||
| #include "test_bound_analyzer.h" | ||||
| #include <functional> | ||||
| #include "util/lp/bound_propagator.h" | ||||
| // We have an equality : sum by j of row[j]*x[j] = rs
 | ||||
| // We try to pin a var by pushing the total by using the variable bounds
 | ||||
| // In a loop we drive the partial sum down, denoting the variables of this process by _u.
 | ||||
| // In the same loop trying to pin variables by pushing the partial sum up, denoting the variable related to it by _l
 | ||||
| namespace lean { | ||||
| 
 | ||||
| class bound_analyzer_on_row { | ||||
|      | ||||
|     linear_combination_iterator<mpq> & m_it; | ||||
|     unsigned m_row_or_term_index; | ||||
|     int m_column_of_u = -1; // index of an unlimited from above monoid
 | ||||
|     // -1 means that such a value is not found, -2 means that at least two of such monoids were found
 | ||||
|     int m_column_of_l = -1; // index of an unlimited from below monoid
 | ||||
|     impq m_rs; | ||||
|     bound_propagator & m_bp; | ||||
| public : | ||||
|     // constructor
 | ||||
|     bound_analyzer_on_row( | ||||
|                           linear_combination_iterator<mpq> &it, | ||||
|                           const numeric_pair<mpq>& rs, | ||||
|                           unsigned row_or_term_index, | ||||
|                           bound_propagator & bp | ||||
|                           ) | ||||
|         : | ||||
|         m_it(it), | ||||
|         m_row_or_term_index(row_or_term_index), | ||||
|         m_rs(rs), | ||||
|         m_bp(bp) | ||||
|     {} | ||||
| 
 | ||||
| 
 | ||||
|     unsigned j; | ||||
|     void analyze() { | ||||
|          | ||||
|         mpq a; unsigned j; | ||||
|         while (((m_column_of_l != -2) || (m_column_of_u != -2)) && m_it.next(a, j)) | ||||
|             analyze_bound_on_var_on_coeff(j, a); | ||||
| 
 | ||||
|         if (m_column_of_u >= 0) | ||||
|             limit_monoid_u_from_below(); | ||||
|         else if (m_column_of_u == -1) | ||||
|             limit_all_monoids_from_below(); | ||||
| 
 | ||||
|         if (m_column_of_l >= 0) | ||||
|             limit_monoid_l_from_above(); | ||||
|         else if (m_column_of_l == -1) | ||||
|             limit_all_monoids_from_above(); | ||||
|     } | ||||
| 
 | ||||
|     bool bound_is_available(unsigned j, bool low_bound) { | ||||
|         return (low_bound && low_bound_is_available(j)) || | ||||
|             (!low_bound && upper_bound_is_available(j)); | ||||
|     } | ||||
| 
 | ||||
|     bool upper_bound_is_available(unsigned j) const { | ||||
|         switch (m_bp.get_column_type(j)) | ||||
|         { | ||||
|         case column_type::fixed: | ||||
|         case column_type::boxed: | ||||
|         case column_type::upper_bound: | ||||
|             return true; | ||||
|         default: | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool low_bound_is_available(unsigned j) const { | ||||
|         switch (m_bp.get_column_type(j)) | ||||
|         { | ||||
|         case column_type::fixed: | ||||
|         case column_type::boxed: | ||||
|         case column_type::low_bound: | ||||
|             return true; | ||||
|         default: | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     const impq & ub(unsigned j) const { | ||||
|         lean_assert(upper_bound_is_available(j)); | ||||
|         return m_bp.get_upper_bound(j); | ||||
|     } | ||||
|     const impq & lb(unsigned j) const { | ||||
|         lean_assert(low_bound_is_available(j)); | ||||
|         return m_bp.get_low_bound(j); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     const mpq & monoid_max_no_mult(bool a_is_pos, unsigned j, bool & strict) const { | ||||
|         if (a_is_pos) { | ||||
|             strict = !is_zero(ub(j).y); | ||||
|             return ub(j).x; | ||||
|         } | ||||
|         strict = !is_zero(lb(j).y); | ||||
|         return lb(j).x; | ||||
|     } | ||||
|     mpq monoid_max(const mpq & a, unsigned j) const { | ||||
|         if (is_pos(a)) { | ||||
|             return a * ub(j).x; | ||||
|         } | ||||
|         return a * lb(j).x; | ||||
|     } | ||||
| 	mpq monoid_max(const mpq & a, unsigned j, bool & strict) const { | ||||
| 		if (is_pos(a)) { | ||||
| 			strict = !is_zero(ub(j).y); | ||||
| 			return a * ub(j).x; | ||||
| 		} | ||||
| 		strict = !is_zero(lb(j).y); | ||||
| 		return a * lb(j).x; | ||||
| 	} | ||||
| 	const mpq & monoid_min_no_mult(bool a_is_pos, unsigned j, bool & strict) const { | ||||
| 		if (!a_is_pos) { | ||||
| 			strict = !is_zero(ub(j).y); | ||||
| 			return ub(j).x; | ||||
| 		} | ||||
| 		strict = !is_zero(lb(j).y); | ||||
| 		return lb(j).x; | ||||
| 	} | ||||
| 
 | ||||
|     mpq monoid_min(const mpq & a, unsigned j, bool& strict) const { | ||||
|         if (is_neg(a)) { | ||||
|             strict = !is_zero(ub(j).y); | ||||
|             return a * ub(j).x; | ||||
|         } | ||||
|          | ||||
|         strict = !is_zero(lb(j).y); | ||||
|         return a * lb(j).x; | ||||
|     } | ||||
| 
 | ||||
|     mpq monoid_min(const mpq & a, unsigned j) const { | ||||
|         if (is_neg(a)) { | ||||
|             return a * ub(j).x; | ||||
|         } | ||||
|          | ||||
|         return a * lb(j).x; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     void limit_all_monoids_from_above() { | ||||
|         int strict = 0; | ||||
|         mpq total; | ||||
|         lean_assert(is_zero(total)); | ||||
|         m_it.reset(); | ||||
|         mpq a; unsigned j; | ||||
|         while (m_it.next(a, j)) { | ||||
|             bool str; | ||||
|             total -= monoid_min(a, j, str); | ||||
|             if (str) | ||||
|                 strict++; | ||||
|         } | ||||
| 
 | ||||
|         m_it.reset(); | ||||
|         while (m_it.next(a, j)) { | ||||
|             bool str; | ||||
| 			bool a_is_pos = is_pos(a); | ||||
|             mpq bound = total / a + monoid_min_no_mult(a_is_pos, j, str); | ||||
|             if (a_is_pos) { | ||||
|                 limit_j(j, bound, true, false, strict - static_cast<int>(str) > 0); | ||||
|             } | ||||
|             else { | ||||
|                 limit_j(j, bound, false, true, strict - static_cast<int>(str) > 0); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void limit_all_monoids_from_below() { | ||||
|         int strict = 0; | ||||
|         mpq total; | ||||
|         lean_assert(is_zero(total)); | ||||
|         m_it.reset(); | ||||
|         mpq a; unsigned j; | ||||
|         while (m_it.next(a, j)) { | ||||
|             bool str; | ||||
|             total -= monoid_max(a, j, str); | ||||
|             if (str) | ||||
|                 strict++; | ||||
|         } | ||||
|         m_it.reset(); | ||||
|         while (m_it.next(a, j)) { | ||||
|             bool str; | ||||
| 			bool a_is_pos = is_pos(a); | ||||
| 			mpq bound = total / a + monoid_max_no_mult(a_is_pos, j, str); | ||||
|             bool astrict = strict - static_cast<int>(str) > 0;  | ||||
|             if (a_is_pos) { | ||||
|                 limit_j(j, bound, true, true, astrict); | ||||
|             } | ||||
|             else { | ||||
|                 limit_j(j, bound, false, false, astrict); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     void limit_monoid_u_from_below() { | ||||
|         // we are going to limit from below the monoid m_column_of_u,
 | ||||
|         // every other monoid is impossible to limit from below
 | ||||
|         mpq u_coeff, a; | ||||
|         unsigned j; | ||||
|         mpq bound = -m_rs.x; | ||||
|         m_it.reset(); | ||||
|         bool strict = false; | ||||
|         while (m_it.next(a, j)) { | ||||
|             if (j == static_cast<unsigned>(m_column_of_u)) { | ||||
|                 u_coeff = a; | ||||
|                 continue; | ||||
|             } | ||||
|             bool str; | ||||
|             bound -= monoid_max(a, j, str); | ||||
|             if (str) | ||||
|                 strict = true; | ||||
|         } | ||||
| 
 | ||||
|         bound /= u_coeff; | ||||
|          | ||||
|         if (numeric_traits<impq>::is_pos(u_coeff)) { | ||||
|             limit_j(m_column_of_u, bound, true, true, strict); | ||||
|         } else { | ||||
|             limit_j(m_column_of_u, bound, false, false, strict); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     void limit_monoid_l_from_above() { | ||||
|         // we are going to limit from above the monoid m_column_of_l,
 | ||||
|         // every other monoid is impossible to limit from above
 | ||||
|         mpq l_coeff, a; | ||||
|         unsigned j; | ||||
|         mpq bound = -m_rs.x; | ||||
|         bool strict = false; | ||||
|         m_it.reset(); | ||||
|         while (m_it.next(a, j)) { | ||||
|             if (j == static_cast<unsigned>(m_column_of_l)) { | ||||
|                 l_coeff = a; | ||||
|                 continue; | ||||
|             } | ||||
| 
 | ||||
|             bool str; | ||||
|             bound -= monoid_min(a, j, str); | ||||
|             if (str) | ||||
|                 strict = true; | ||||
|         } | ||||
| 
 | ||||
|         bound /= l_coeff; | ||||
|         if (is_pos(l_coeff)) { | ||||
|             limit_j(m_column_of_l, bound, true, false, strict); | ||||
|         } else { | ||||
|             limit_j(m_column_of_l, bound, false, true, strict); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     // // it is the coefficent before the bounded column
 | ||||
|     // void provide_evidence(bool coeff_is_pos) {
 | ||||
|     //     /*
 | ||||
|     //     auto & be = m_ibounds.back();
 | ||||
|     //     bool low_bound = be.m_low_bound;
 | ||||
|     //     if (!coeff_is_pos)
 | ||||
|     //         low_bound = !low_bound;
 | ||||
|     //     auto it = m_it.clone();
 | ||||
|     //     mpq a; unsigned j;
 | ||||
|     //     while (it->next(a, j)) {
 | ||||
|     //         if (be.m_j == j) continue;
 | ||||
|     //         lean_assert(bound_is_available(j, is_neg(a) ? low_bound : !low_bound));
 | ||||
|     //         be.m_vector_of_bound_signatures.emplace_back(a, j, numeric_traits<impq>::
 | ||||
|     //                                                      is_neg(a)? low_bound: !low_bound);
 | ||||
|     //     }
 | ||||
|     //     delete it;
 | ||||
|     //     */
 | ||||
|     // }
 | ||||
| 
 | ||||
|     void limit_j(unsigned j, const mpq& u, bool coeff_before_j_is_pos, bool is_low_bound, bool strict){ | ||||
|         m_bp.try_add_bound(u, j, is_low_bound, coeff_before_j_is_pos, m_row_or_term_index, strict); | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     void advance_u(unsigned j) { | ||||
|         if (m_column_of_u == -1) | ||||
|             m_column_of_u = j; | ||||
|         else | ||||
|             m_column_of_u = -2; | ||||
|     } | ||||
|      | ||||
|     void advance_l(unsigned j) { | ||||
|         if (m_column_of_l == -1) | ||||
|             m_column_of_l = j; | ||||
|         else | ||||
|             m_column_of_l = -2; | ||||
|     } | ||||
|      | ||||
|     void analyze_bound_on_var_on_coeff(int j, const mpq &a) { | ||||
|         switch (m_bp.get_column_type(j)) { | ||||
|         case column_type::low_bound: | ||||
|             if (numeric_traits<mpq>::is_pos(a)) | ||||
|                 advance_u(j); | ||||
|             else  | ||||
|                 advance_l(j); | ||||
|             break; | ||||
|         case column_type::upper_bound: | ||||
|             if(numeric_traits<mpq>::is_neg(a)) | ||||
|                 advance_u(j); | ||||
|             else | ||||
|                 advance_l(j); | ||||
|             break; | ||||
|         case column_type::free_column: | ||||
|             advance_u(j); | ||||
|             advance_l(j); | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     static void analyze_row(linear_combination_iterator<mpq> &it, | ||||
|                             const numeric_pair<mpq>& rs, | ||||
|                             unsigned row_or_term_index, | ||||
|                             bound_propagator & bp | ||||
|                             ) { | ||||
|         bound_analyzer_on_row a(it, rs, row_or_term_index, bp); | ||||
|         a.analyze(); | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
| } | ||||
							
								
								
									
										47
									
								
								src/util/lp/bound_propagator.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/util/lp/bound_propagator.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/lar_solver.h" | ||||
| namespace lean { | ||||
| bound_propagator::bound_propagator(lar_solver & ls): | ||||
|     m_lar_solver(ls) {} | ||||
| column_type bound_propagator::get_column_type(unsigned j) const { | ||||
|     return m_lar_solver.m_mpq_lar_core_solver.m_column_types()[j]; | ||||
| } | ||||
| const impq & bound_propagator::get_low_bound(unsigned j) const { | ||||
|     return m_lar_solver.m_mpq_lar_core_solver.m_r_low_bounds()[j]; | ||||
| } | ||||
| const impq & bound_propagator::get_upper_bound(unsigned j) const { | ||||
|     return m_lar_solver.m_mpq_lar_core_solver.m_r_upper_bounds()[j]; | ||||
| } | ||||
| void bound_propagator::try_add_bound(const mpq & v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict) { | ||||
|     j = m_lar_solver.adjust_column_index_to_term_index(j); | ||||
|     lconstraint_kind kind = is_low? GE : LE; | ||||
|     if (strict) | ||||
|         kind = static_cast<lconstraint_kind>(kind / 2); | ||||
|      | ||||
|     if (!bound_is_interesting(j, kind, v)) | ||||
|         return; | ||||
|      unsigned k; // index to ibounds
 | ||||
|      if (is_low) { | ||||
|          if (try_get_val(m_improved_low_bounds, j, k)) { | ||||
|              auto & found_bound = m_ibounds[k]; | ||||
|              if (v > found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict)) | ||||
|                  found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); | ||||
|          } else { | ||||
|              m_improved_low_bounds[j] = m_ibounds.size(); | ||||
|              m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); | ||||
|          } | ||||
|      } else { // the upper bound case
 | ||||
|          if (try_get_val(m_improved_upper_bounds, j, k)) { | ||||
|              auto & found_bound = m_ibounds[k]; | ||||
|              if (v < found_bound.m_bound || (v == found_bound.m_bound && found_bound.m_strict == false && strict)) | ||||
|                  found_bound = implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict); | ||||
|          } else { | ||||
|              m_improved_upper_bounds[j] = m_ibounds.size(); | ||||
|              m_ibounds.push_back(implied_bound(v, j, is_low, coeff_before_j_is_pos, row_or_term_index, strict)); | ||||
|         } | ||||
|      } | ||||
| } | ||||
| } | ||||
							
								
								
									
										27
									
								
								src/util/lp/bound_propagator.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										27
									
								
								src/util/lp/bound_propagator.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,27 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/lp/lp_settings.h" | ||||
| namespace lean { | ||||
| class lar_solver; | ||||
| class bound_propagator { | ||||
|     std::unordered_map<unsigned, unsigned> m_improved_low_bounds; // these maps map a column index to the corresponding index in ibounds
 | ||||
|     std::unordered_map<unsigned, unsigned> m_improved_upper_bounds; | ||||
|     lar_solver & m_lar_solver; | ||||
| public: | ||||
|     vector<implied_bound> m_ibounds; | ||||
| public: | ||||
|     bound_propagator(lar_solver & ls); | ||||
|     column_type get_column_type(unsigned) const; | ||||
|     const impq & get_low_bound(unsigned) const; | ||||
|     const impq & get_upper_bound(unsigned) const; | ||||
|     void try_add_bound(const mpq & v, unsigned j, bool is_low, bool coeff_before_j_is_pos, unsigned row_or_term_index, bool strict); | ||||
|     virtual bool bound_is_interesting(unsigned vi, | ||||
|                                       lean::lconstraint_kind kind, | ||||
|                                       const rational & bval) {return true;} | ||||
|     unsigned number_of_found_bounds() const { return m_ibounds.size(); } | ||||
|     virtual void consume(mpq const& v, unsigned j) { std::cout << "doh\n"; } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										20
									
								
								src/util/lp/breakpoint.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/util/lp/breakpoint.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   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) {} | ||||
| }; | ||||
| } | ||||
							
								
								
									
										235
									
								
								src/util/lp/column_info.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										235
									
								
								src/util/lp/column_info.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,235 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include <unordered_map> | ||||
| #include <string> | ||||
| #include <algorithm> | ||||
| #include "util/lp/lp_settings.h" | ||||
| namespace lean { | ||||
| inline bool is_valid(unsigned j) { return static_cast<int>(j) >= 0;} | ||||
| 
 | ||||
| template <typename T> | ||||
| class column_info { | ||||
|     std::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; | ||||
|     unsigned m_column_index = static_cast<unsigned>(-1); | ||||
| public: | ||||
|     bool operator==(const column_info & c) const { | ||||
|         return     m_name == c.m_name && | ||||
|             m_low_bound_is_set == c.m_low_bound_is_set && | ||||
|             m_low_bound_is_strict == c.m_low_bound_is_strict && | ||||
|             m_upper_bound_is_set == c.m_upper_bound_is_set&& | ||||
|             m_upper_bound_is_strict == c.m_upper_bound_is_strict&& | ||||
|             (!m_low_bound_is_set || m_low_bound == c.m_low_bound) && | ||||
|             (!m_upper_bound_is_set || m_upper_bound == c.m_upper_bound) && | ||||
|             m_cost == c.m_cost&& | ||||
|             m_is_fixed == c.m_is_fixed && | ||||
|             (!m_is_fixed || m_fixed_value == c.m_fixed_value) && | ||||
|             m_column_index == c.m_column_index; | ||||
|     } | ||||
|     bool operator!=(const column_info & c) const { return !((*this) == c); } | ||||
|     void set_column_index(unsigned j) { | ||||
|         m_column_index = j; | ||||
|     } | ||||
|     // the default constructor
 | ||||
|     column_info() {} | ||||
| 
 | ||||
|     column_info(unsigned column_index) : m_column_index(column_index) { | ||||
|     } | ||||
| 
 | ||||
|     column_info(const column_info & ci) { | ||||
|         m_name = ci.m_name; | ||||
|         m_low_bound_is_set = ci.m_low_bound_is_set; | ||||
|         m_low_bound_is_strict = ci.m_low_bound_is_strict; | ||||
|         m_upper_bound_is_set = ci.m_upper_bound_is_set; | ||||
|         m_upper_bound_is_strict = ci.m_upper_bound_is_strict; | ||||
|         m_low_bound = ci.m_low_bound; | ||||
|         m_upper_bound = ci.m_upper_bound; | ||||
|         m_cost = ci.m_cost; | ||||
|         m_fixed_value = ci.m_fixed_value; | ||||
|         m_is_fixed = ci.m_is_fixed; | ||||
|         m_column_index = ci.m_column_index; | ||||
|     } | ||||
| 
 | ||||
|     unsigned get_column_index() const { | ||||
|         return m_column_index; | ||||
|     } | ||||
| 
 | ||||
|     column_type get_column_type() const { | ||||
|         return m_is_fixed? column_type::fixed : (m_low_bound_is_set? (m_upper_bound_is_set? column_type::boxed : column_type::low_bound) : (m_upper_bound_is_set? column_type::upper_bound: column_type::free_column)); | ||||
|     } | ||||
| 
 | ||||
|     column_type get_column_type_no_flipping() const { | ||||
|         if (m_is_fixed) { | ||||
|             return column_type::fixed; | ||||
|         } | ||||
| 
 | ||||
|         if (m_low_bound_is_set) { | ||||
|             return m_upper_bound_is_set? column_type::boxed: column_type::low_bound; | ||||
|         } | ||||
|         // we are flipping the bounds!
 | ||||
|         return m_upper_bound_is_set? column_type::upper_bound | ||||
|             : column_type::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(std::string const & s) { | ||||
|         m_name = s; | ||||
|     } | ||||
| 
 | ||||
|     std::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() { | ||||
|         if ((!upper_bound_is_set()) || (!low_bound_is_set())) | ||||
|             return false; | ||||
|         // ok, both bounds are set
 | ||||
|         bool at_least_one_is_strict = upper_bound_is_strict() || low_bound_is_strict(); | ||||
|         if (!at_least_one_is_strict) | ||||
|             return get_upper_bound() < get_low_bound(); | ||||
|         // at least on bound is strict
 | ||||
|         return get_upper_bound() <= get_low_bound(); // the equality is impossible
 | ||||
|     } | ||||
|     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; | ||||
|     } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										82
									
								
								src/util/lp/column_namer.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										82
									
								
								src/util/lp/column_namer.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,82 @@ | |||
| #pragma once | ||||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <string> | ||||
| #include "util/lp/linear_combination_iterator.h" | ||||
| namespace lean { | ||||
| class column_namer { | ||||
| public: | ||||
|     virtual std::string get_column_name(unsigned j) const = 0; | ||||
|     template <typename T> | ||||
|     void print_linear_iterator(linear_combination_iterator<T>* it, std::ostream & out) const { | ||||
|         vector<std::pair<T, unsigned>> coeff; | ||||
|         T a; | ||||
|         unsigned i; | ||||
|         while (it->next(a, i)) { | ||||
|             coeff.emplace_back(a, i); | ||||
|         } | ||||
|         print_linear_combination_of_column_indices(coeff, out); | ||||
|     } | ||||
|     template <typename T> | ||||
|     void print_linear_iterator_indices_only(linear_combination_iterator<T>* it, std::ostream & out) const { | ||||
|         vector<std::pair<T, unsigned>> coeff; | ||||
|         T a; | ||||
|         unsigned i; | ||||
|         while (it->next(a, i)) { | ||||
|             coeff.emplace_back(a, i); | ||||
|         } | ||||
|         print_linear_combination_of_column_indices_only(coeff, out); | ||||
|     } | ||||
|      | ||||
|     template <typename T> | ||||
|     void print_linear_combination_of_column_indices_only(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const { | ||||
|         bool first = true; | ||||
|         for (const auto & it : coeffs) { | ||||
|             auto val = it.first; | ||||
|             if (first) { | ||||
|                 first = false; | ||||
|             } else { | ||||
|                 if (numeric_traits<T>::is_pos(val)) { | ||||
|                     out << " + "; | ||||
|                 } else { | ||||
|                     out << " - "; | ||||
|                     val = -val; | ||||
|                 } | ||||
|             } | ||||
|             if (val == -numeric_traits<T>::one()) | ||||
|                 out << " - "; | ||||
|             else if (val != numeric_traits<T>::one()) | ||||
|                 out << T_to_string(val); | ||||
|          | ||||
|             out << "_" << it.second; | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     template <typename T> | ||||
|     void print_linear_combination_of_column_indices(const vector<std::pair<T, unsigned>> & coeffs, std::ostream & out) const { | ||||
|         bool first = true; | ||||
|         for (const auto & it : coeffs) { | ||||
|             auto val = it.first; | ||||
|             if (first) { | ||||
|                 first = false; | ||||
|             } else { | ||||
|                 if (numeric_traits<T>::is_pos(val)) { | ||||
|                     out << " + "; | ||||
|                 } else { | ||||
|                     out << " - "; | ||||
|                     val = -val; | ||||
|                 } | ||||
|             } | ||||
|             if (val == -numeric_traits<T>::one()) | ||||
|                 out << " - "; | ||||
|             else if (val != numeric_traits<T>::one()) | ||||
|                 out << val; | ||||
|          | ||||
|             out << get_column_name(it.second); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| }; | ||||
| } | ||||
							
								
								
									
										118
									
								
								src/util/lp/core_solver_pretty_printer.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										118
									
								
								src/util/lp/core_solver_pretty_printer.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,118 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include <limits> | ||||
| #include <string> | ||||
| #include <algorithm> | ||||
| #include "util/vector.h" | ||||
| #include <ostream> | ||||
| #include "util/lp/lp_settings.h" | ||||
| #include "util/lp/indexed_vector.h" | ||||
| namespace lean { | ||||
| template <typename T, typename X> class lp_core_solver_base; // forward definition
 | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| class core_solver_pretty_printer { | ||||
|     std::ostream & m_out; | ||||
|     template<typename A> using vector = vector<A>; | ||||
|     typedef std::string string; | ||||
|     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 = std::numeric_limits<unsigned>::max(); | ||||
|     indexed_vector<T> m_w_buff; | ||||
|     indexed_vector<T> m_ed_buff; | ||||
|     vector<T> m_exact_column_norms; | ||||
| 
 | ||||
| public: | ||||
|     core_solver_pretty_printer(lp_core_solver_base<T, X > & core_solver, std::ostream & out); | ||||
| 
 | ||||
|     void init_costs(); | ||||
| 
 | ||||
|     ~core_solver_pretty_printer(); | ||||
|     void init_rs_width(); | ||||
| 
 | ||||
|     T current_column_norm(); | ||||
| 
 | ||||
|     void init_m_A_and_signs(); | ||||
| 
 | ||||
|     void init_column_widths(); | ||||
| 
 | ||||
|     void adjust_width_with_low_bound(unsigned column, unsigned & w); | ||||
|     void adjust_width_with_upper_bound(unsigned column, unsigned & w); | ||||
| 
 | ||||
|     void adjust_width_with_bounds(unsigned column, unsigned & w); | ||||
| 
 | ||||
|     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 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); | ||||
| 
 | ||||
| 
 | ||||
|     void set_coeff(vector<string>& row, vector<string> & row_signs, unsigned col, const T & t, string name); | ||||
| 
 | ||||
|     void print_x(); | ||||
| 
 | ||||
|     std::string get_low_bound_string(unsigned j); | ||||
| 
 | ||||
|     std::string get_upp_bound_string(unsigned j); | ||||
| 
 | ||||
| 
 | ||||
|     void print_lows(); | ||||
| 
 | ||||
|     void print_upps(); | ||||
| 
 | ||||
|     string get_exact_column_norm_string(unsigned col) { | ||||
|         return T_to_string(m_exact_column_norms[col]); | ||||
|     } | ||||
| 
 | ||||
|     void print_exact_norms(); | ||||
| 
 | ||||
|     void print_approx_norms(); | ||||
| 
 | ||||
|     void print(); | ||||
| 
 | ||||
|     void print_basis_heading(); | ||||
| 
 | ||||
|     void print_bottom_line() { | ||||
|         m_out << "----------------------" << std::endl; | ||||
|     } | ||||
| 
 | ||||
|     void print_cost(); | ||||
| 
 | ||||
|     void print_given_rows(vector<string> & row, vector<string> & signs, X rst); | ||||
| 
 | ||||
|     void print_row(unsigned i); | ||||
| 
 | ||||
| }; | ||||
| } | ||||
							
								
								
									
										377
									
								
								src/util/lp/core_solver_pretty_printer.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										377
									
								
								src/util/lp/core_solver_pretty_printer.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,377 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <limits> | ||||
| #include <string> | ||||
| #include <algorithm> | ||||
| #include "util/lp/lp_utils.h" | ||||
| #include "util/lp/lp_core_solver_base.h" | ||||
| #include "util/lp/core_solver_pretty_printer.h" | ||||
| #include "util/lp/numeric_pair.h" | ||||
| namespace lean { | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| core_solver_pretty_printer<T, X>::core_solver_pretty_printer(lp_core_solver_base<T, X > & core_solver, std::ostream & out): | ||||
|     m_out(out), | ||||
|     m_core_solver(core_solver), | ||||
|     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(core_solver.m_w), | ||||
|     m_ed_buff(core_solver.m_ed) { | ||||
|     m_column_widths.resize(core_solver.m_A.column_count(), 0), | ||||
|     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 = static_cast<unsigned>(std::max(std::max(m_cost_title.size(), std::max(m_basis_heading_title.size(), m_x_title.size())), m_approx_norm_title.size())); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_costs() { | ||||
|     if (!m_core_solver.use_tableau()) { | ||||
|         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)); | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         for (unsigned i = 0; i < ncols(); i++) { | ||||
|             if (m_core_solver.m_basis_heading[i] < 0) { | ||||
|                 set_coeff(m_costs, m_cost_signs, i, m_core_solver.m_d[i], m_core_solver.column_name(i)); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> core_solver_pretty_printer<T, X>::~core_solver_pretty_printer() { | ||||
|     m_core_solver.m_w = m_w_buff; | ||||
|     m_core_solver.m_ed = m_ed_buff; | ||||
| } | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_rs_width() { | ||||
|     m_rs_width = static_cast<unsigned>(T_to_string(m_core_solver.get_cost()).size()); | ||||
|     for (unsigned i = 0; i < nrows(); i++) { | ||||
|         unsigned wt = static_cast<unsigned>(T_to_string(m_rs[i]).size()); | ||||
|         if (wt > m_rs_width) { | ||||
|             m_rs_width = wt; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> T core_solver_pretty_printer<T, X>::current_column_norm() { | ||||
|     T ret = zero_of_type<T>(); | ||||
|     for (auto i : m_core_solver.m_ed.m_index) | ||||
|         ret += m_core_solver.m_ed[i] * m_core_solver.m_ed[i]; | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_m_A_and_signs() {  | ||||
|     if (numeric_traits<T>::precise() && m_core_solver.m_settings.use_tableau()) { | ||||
|         for (unsigned column = 0; column < ncols(); column++) { | ||||
|             vector<T> t(nrows(), zero_of_type<T>()); | ||||
|             for (const auto & c : m_core_solver.m_A.m_columns[column]){ | ||||
|                 t[c.m_i] = m_core_solver.m_A.get_val(c); | ||||
|             } | ||||
|                  | ||||
|             string name = m_core_solver.column_name(column); | ||||
|             for (unsigned row = 0; row < nrows(); row ++) { | ||||
|                 set_coeff( | ||||
|                           m_A[row], | ||||
|                           m_signs[row], | ||||
|                           column, | ||||
|                           t[row], | ||||
|                           name); | ||||
|                 m_rs[row] += t[row] * m_core_solver.m_x[column]; | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         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]; | ||||
|             } | ||||
|             if (!m_core_solver.use_tableau()) | ||||
|                 m_exact_column_norms.push_back(current_column_norm() + T(1)); // a conversion missing 1 -> T
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::init_column_widths() { | ||||
|     for (unsigned i = 0; i < ncols(); i++) { | ||||
|         m_column_widths[i] = get_column_width(i); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_low_bound(unsigned column, unsigned & w) { | ||||
|     if (!m_core_solver.low_bounds_are_set()) return; | ||||
|     w = std::max(w, (unsigned)T_to_string(m_core_solver.low_bound_value(column)).size()); | ||||
| } | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_upper_bound(unsigned column, unsigned & w) { | ||||
|     w = std::max(w, (unsigned)T_to_string(m_core_solver.upper_bound_value(column)).size()); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::adjust_width_with_bounds(unsigned column, unsigned & w) { | ||||
|     switch (m_core_solver.get_column_type(column)) { | ||||
|     case column_type::fixed: | ||||
|     case column_type::boxed: | ||||
|         adjust_width_with_low_bound(column, w); | ||||
|         adjust_width_with_upper_bound(column, w); | ||||
|         break; | ||||
|     case column_type::low_bound: | ||||
|         adjust_width_with_low_bound(column, w); | ||||
|         break; | ||||
|     case column_type::upper_bound: | ||||
|         adjust_width_with_upper_bound(column, w); | ||||
|         break; | ||||
|     case column_type::free_column: | ||||
|         break; | ||||
|     default: | ||||
|         lean_assert(false); | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> unsigned core_solver_pretty_printer<T, X>:: get_column_width(unsigned column) { | ||||
|     unsigned w = static_cast<unsigned>(std::max((size_t)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 = static_cast<unsigned>(m_A[i][column].size()); | ||||
|         if (cellw > w) { | ||||
|             w = cellw; | ||||
|         } | ||||
|     } | ||||
|     if (!m_core_solver.use_tableau()) { | ||||
|         w = std::max(w, (unsigned)T_to_string(m_exact_column_norms[column]).size()); | ||||
|         if (m_core_solver.m_column_norms.size() > 0) | ||||
|             w = std::max(w, (unsigned)T_to_string(m_core_solver.m_column_norms[column]).size()); | ||||
|     } | ||||
|     return w; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::regular_cell_string(unsigned row, unsigned /* column */, std::string name) { | ||||
|     T t = fabs(m_core_solver.m_ed[row]); | ||||
|     if ( t == 1) return name; | ||||
|     return T_to_string(t) + name; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::set_coeff(vector<string>& row, vector<string> & row_signs, unsigned col, const T & t, string name) { | ||||
|     if (numeric_traits<T>::is_zero(t)) { | ||||
|         return; | ||||
|     } | ||||
|     if (col > 0) { | ||||
|         if (t > 0) { | ||||
|             row_signs[col] = "+"; | ||||
|             row[col] = t != 1? T_to_string(t) + name : name; | ||||
|         } else { | ||||
|             row_signs[col] = "-"; | ||||
|             row[col] = t != -1? T_to_string(-t) + name: name; | ||||
|         } | ||||
|     } else { // col == 0
 | ||||
|         if (t == -1) { | ||||
|             row[col] = "-" + name; | ||||
|         } else if (t == 1) { | ||||
|             row[col] = name; | ||||
|         } else { | ||||
|             row[col] = T_to_string(t) + name; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_x() { | ||||
|     if (ncols() == 0) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     int blanks = m_title_width + 1 - static_cast<int>(m_x_title.size()); | ||||
|     m_out << m_x_title; | ||||
|     print_blanks(blanks, m_out); | ||||
| 
 | ||||
|     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] - static_cast<int>(s.size()); | ||||
|         print_blanks(blanks, m_out); | ||||
|         m_out << s << "   "; // the column interval
 | ||||
|     } | ||||
|     m_out << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_low_bound_string(unsigned j) { | ||||
|     switch (m_core_solver.get_column_type(j)){ | ||||
|     case column_type::boxed: | ||||
|     case column_type::low_bound: | ||||
|     case column_type::fixed: | ||||
|         if (m_core_solver.low_bounds_are_set()) | ||||
|             return T_to_string(m_core_solver.low_bound_value(j)); | ||||
|         else | ||||
|             return std::string("0"); | ||||
|         break; | ||||
|     default: | ||||
|         return std::string(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> std::string core_solver_pretty_printer<T, X>::get_upp_bound_string(unsigned j) { | ||||
|     switch (m_core_solver.get_column_type(j)){ | ||||
|     case column_type::boxed: | ||||
|     case column_type::upper_bound: | ||||
|     case column_type::fixed: | ||||
|         return T_to_string(m_core_solver.upper_bound_value(j)); | ||||
|         break; | ||||
|     default: | ||||
|         return std::string(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_lows() { | ||||
|     if (ncols() == 0) { | ||||
|         return; | ||||
|     } | ||||
|     int blanks = m_title_width + 1 - static_cast<unsigned>(m_low_bounds_title.size()); | ||||
|     m_out << m_low_bounds_title; | ||||
|     print_blanks(blanks, m_out); | ||||
| 
 | ||||
|     for (unsigned i = 0; i < ncols(); i++) { | ||||
|         string s = get_low_bound_string(i); | ||||
|         int blanks = m_column_widths[i] - static_cast<unsigned>(s.size()); | ||||
|         print_blanks(blanks, m_out); | ||||
|         m_out << s << "   "; // the column interval
 | ||||
|     } | ||||
|     m_out << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_upps() { | ||||
|     if (ncols() == 0) { | ||||
|         return; | ||||
|     } | ||||
|     int blanks = m_title_width + 1 - static_cast<unsigned>(m_upp_bounds_title.size()); | ||||
|     m_out << m_upp_bounds_title; | ||||
|     print_blanks(blanks, m_out); | ||||
| 
 | ||||
|     for (unsigned i = 0; i < ncols(); i++) { | ||||
|         string s = get_upp_bound_string(i); | ||||
|         int blanks = m_column_widths[i] - static_cast<unsigned>(s.size()); | ||||
|         print_blanks(blanks, m_out); | ||||
|         m_out << s << "   "; // the column interval
 | ||||
|     } | ||||
|     m_out << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_exact_norms() { | ||||
|     if (m_core_solver.use_tableau()) return; | ||||
|     int blanks = m_title_width + 1 - static_cast<int>(m_exact_norm_title.size()); | ||||
|     m_out << m_exact_norm_title; | ||||
|     print_blanks(blanks, m_out); | ||||
|     for (unsigned i = 0; i < ncols(); i++) { | ||||
|         string s = get_exact_column_norm_string(i); | ||||
|         int blanks = m_column_widths[i] - static_cast<int>(s.size()); | ||||
|         print_blanks(blanks, m_out); | ||||
|         m_out << s << "   "; | ||||
|     } | ||||
|     m_out << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_approx_norms() { | ||||
|     if (m_core_solver.use_tableau()) return; | ||||
|     int blanks = m_title_width + 1 - static_cast<int>(m_approx_norm_title.size()); | ||||
|     m_out << m_approx_norm_title; | ||||
|     print_blanks(blanks, m_out); | ||||
|     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] - static_cast<int>(s.size()); | ||||
|         print_blanks(blanks, m_out); | ||||
|         m_out << s << "   "; | ||||
|     } | ||||
|     m_out << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::print() { | ||||
|     for (unsigned i = 0; i < nrows(); i++) { | ||||
|         print_row(i); | ||||
|     } | ||||
|     print_bottom_line(); | ||||
|     print_cost(); | ||||
|     print_x(); | ||||
|     print_basis_heading(); | ||||
|     print_lows(); | ||||
|     print_upps(); | ||||
|     print_exact_norms(); | ||||
|     if (m_core_solver.m_column_norms.size() > 0) | ||||
|         print_approx_norms(); | ||||
|     m_out << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_basis_heading() { | ||||
|     int blanks = m_title_width + 1 - static_cast<int>(m_basis_heading_title.size()); | ||||
|     m_out << m_basis_heading_title; | ||||
|     print_blanks(blanks, m_out); | ||||
| 
 | ||||
|     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] - static_cast<unsigned>(s.size()); | ||||
|         print_blanks(blanks, m_out); | ||||
|         m_out << s << "   "; // the column interval
 | ||||
|     } | ||||
|     m_out << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_cost() { | ||||
|     int blanks = m_title_width + 1 - static_cast<int>(m_cost_title.size()); | ||||
|     m_out << m_cost_title; | ||||
|     print_blanks(blanks, m_out); | ||||
|     print_given_rows(m_costs, m_cost_signs, m_core_solver.get_cost()); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_given_rows(vector<string> & row, vector<string> & signs, X rst) { | ||||
|     for (unsigned col = 0; col < row.size(); col++) { | ||||
|         unsigned width = m_column_widths[col]; | ||||
|         string s = row[col]; | ||||
|         int number_of_blanks = width - static_cast<unsigned>(s.size()); | ||||
|         lean_assert(number_of_blanks >= 0); | ||||
|         print_blanks(number_of_blanks, m_out); | ||||
|         m_out << s << ' '; | ||||
|         if (col < row.size() - 1) { | ||||
|             m_out << signs[col + 1] << ' '; | ||||
|         } | ||||
|     } | ||||
|     m_out << '='; | ||||
| 
 | ||||
|     string rs = T_to_string(rst); | ||||
|     int nb = m_rs_width - static_cast<int>(rs.size()); | ||||
|     lean_assert(nb >= 0); | ||||
|     print_blanks(nb + 1, m_out); | ||||
|     m_out << rs << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void core_solver_pretty_printer<T, X>::print_row(unsigned i){ | ||||
|     print_blanks(m_title_width + 1, m_out); | ||||
|     auto row = m_A[i]; | ||||
|     auto sign_row = m_signs[i]; | ||||
|     auto rs = m_rs[i]; | ||||
|     print_given_rows(row, sign_row, rs); | ||||
| } | ||||
| } | ||||
							
								
								
									
										15
									
								
								src/util/lp/core_solver_pretty_printer_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/util/lp/core_solver_pretty_printer_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,15 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/numeric_pair.h" | ||||
| #include "util/lp/core_solver_pretty_printer.hpp" | ||||
| template lean::core_solver_pretty_printer<double, double>::core_solver_pretty_printer(lean::lp_core_solver_base<double, double> &, std::ostream & out); | ||||
| template void lean::core_solver_pretty_printer<double, double>::print(); | ||||
| template lean::core_solver_pretty_printer<double, double>::~core_solver_pretty_printer(); | ||||
| template lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::core_solver_pretty_printer(lean::lp_core_solver_base<lean::mpq, lean::mpq> &, std::ostream & out); | ||||
| template void lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::print(); | ||||
| template lean::core_solver_pretty_printer<lean::mpq, lean::mpq>::~core_solver_pretty_printer(); | ||||
| template lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::core_solver_pretty_printer(lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> > &, std::ostream & out); | ||||
| template lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::~core_solver_pretty_printer(); | ||||
| template void lean::core_solver_pretty_printer<lean::mpq, lean::numeric_pair<lean::mpq> >::print(); | ||||
							
								
								
									
										92
									
								
								src/util/lp/dense_matrix.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										92
									
								
								src/util/lp/dense_matrix.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,92 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #ifdef LEAN_DEBUG | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/matrix.h" | ||||
| namespace lean { | ||||
| // used for debugging purposes only
 | ||||
| template <typename T, typename X> | ||||
| class dense_matrix: public matrix<T, X> { | ||||
| public: | ||||
|     struct ref { | ||||
|         unsigned m_i; | ||||
|         dense_matrix & m_s; | ||||
|         ref(unsigned i, dense_matrix & s) :m_i(i * s.m_n), m_s(s){} | ||||
|         T & operator[] (unsigned j) { | ||||
|             return m_s.m_values[m_i + j]; | ||||
|         } | ||||
|         const T & operator[] (unsigned j) const { | ||||
|             return m_s.m_v[m_i + j]; | ||||
|         } | ||||
|     }; | ||||
|     ref operator[] (unsigned i) { | ||||
|         return ref(i, *this); | ||||
|     } | ||||
|     unsigned m_m; // number of rows
 | ||||
|     unsigned m_n; // number of const
 | ||||
|     vector<T> m_values; | ||||
|     dense_matrix(unsigned m, unsigned n); | ||||
| 
 | ||||
|     dense_matrix operator*=(matrix<T, X> const & a) { | ||||
|         lean_assert(column_count() == a.row_count()); | ||||
|         dense_matrix c(row_count(), a.column_count()); | ||||
|         for (unsigned i = 0; i < row_count(); i++) { | ||||
|             for (unsigned j = 0; j < a.column_count(); j++) { | ||||
|                 T v = numeric_traits<T>::zero(); | ||||
|                 for (unsigned k = 0; k < a.column_count(); k++) { | ||||
|                     v += get_elem(i, k) * a(k, j); | ||||
|                 } | ||||
|                 c.set_elem(i, j, v); | ||||
|             } | ||||
|         } | ||||
|         *this = c; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     dense_matrix & operator=(matrix<T, X> const & other); | ||||
| 
 | ||||
|     dense_matrix & operator=(dense_matrix const & other); | ||||
| 
 | ||||
|     dense_matrix(matrix<T, X> const * other); | ||||
|     void apply_from_right(T * w); | ||||
| 
 | ||||
|     void apply_from_right(vector <T> & w); | ||||
| 
 | ||||
|     T * apply_from_left_with_different_dims(vector<T> &  w); | ||||
|     void apply_from_left(vector<T> & w , lp_settings & ) { apply_from_left(w); } | ||||
| 
 | ||||
|     void apply_from_left(vector<T> & w); | ||||
| 
 | ||||
|     void apply_from_left(X * w, lp_settings & ); | ||||
| 
 | ||||
|     void apply_from_left_to_X(vector<X> & w, lp_settings & ); | ||||
| 
 | ||||
|     virtual void set_number_of_rows(unsigned /*m*/) {} | ||||
|     virtual void set_number_of_columns(unsigned /*n*/) { } | ||||
| 
 | ||||
|     T get_elem(unsigned i, unsigned j) const { return m_values[i * m_n + j]; } | ||||
| 
 | ||||
|     unsigned row_count() const { return m_m; } | ||||
|     unsigned column_count() const { return m_n; } | ||||
| 
 | ||||
|     void set_elem(unsigned i, unsigned j, const T& val) {  m_values[i * m_n + j] = val;  } | ||||
| 
 | ||||
|     // This method pivots row i to row i0 by muliplying row i by
 | ||||
|     //   alpha and adding it to row i0.
 | ||||
|     void pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, | ||||
|                           const double & pivot_epsilon); | ||||
| 
 | ||||
|     void swap_columns(unsigned a, unsigned b); | ||||
| 
 | ||||
|     void swap_rows(unsigned a, unsigned b); | ||||
| 
 | ||||
|     void multiply_row_by_constant(unsigned row, T & t); | ||||
| 
 | ||||
| }; | ||||
| template <typename T, typename X> | ||||
| dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b); | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										186
									
								
								src/util/lp/dense_matrix.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										186
									
								
								src/util/lp/dense_matrix.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,186 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/lp_settings.h" | ||||
| #ifdef LEAN_DEBUG | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/numeric_pair.h" | ||||
| #include "util/lp/dense_matrix.h" | ||||
| namespace lean { | ||||
| template <typename T> void print_vector(const vector<T> & t, std::ostream & out); | ||||
| template <typename T, typename X> dense_matrix<T, X>::dense_matrix(unsigned m, unsigned n) : m_m(m), m_n(n), m_values(m * n, numeric_traits<T>::zero()) { | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> dense_matrix<T, X>& | ||||
| dense_matrix<T, X>::operator=(matrix<T, X> const & other){ | ||||
|     if ( this == & other) | ||||
|         return *this; | ||||
|     m_values = new T[m_m * m_n]; | ||||
|     for (unsigned i = 0; i < m_m; i ++) | ||||
|         for (unsigned j = 0; j < m_n; j++) | ||||
|             m_values[i * m_n + j] = other.get_elem(i, j); | ||||
|     return *this; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> dense_matrix<T, X>& | ||||
| dense_matrix<T, X>::operator=(dense_matrix const & other){ | ||||
|     if ( this == & other) | ||||
|         return *this; | ||||
|     m_m = other.m_m; | ||||
|     m_n = other.m_n; | ||||
|     m_values.resize(m_m * m_n); | ||||
|     for (unsigned i = 0; i < m_m; i ++) | ||||
|         for (unsigned j = 0; j < m_n; j++) | ||||
|             m_values[i * m_n + j] = other.get_elem(i, j); | ||||
|     return *this; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> dense_matrix<T, X>::dense_matrix(matrix<T, X> const * other) : | ||||
|     m_m(other->row_count()), | ||||
|     m_n(other->column_count()) { | ||||
|     m_values.resize(m_m*m_n); | ||||
|     for (unsigned i = 0; i < m_m; i++) | ||||
|         for (unsigned j = 0; j < m_n; j++) | ||||
|             m_values[i * m_n + j] = other->get_elem(i, j); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void dense_matrix<T, X>::apply_from_right(T * w) { | ||||
|     T * t = new T[m_m]; | ||||
|     for (int i = 0; i < m_m; i ++) { | ||||
|         T v = numeric_traits<T>::zero(); | ||||
|         for (int j = 0; j < m_m; j++) { | ||||
|             v += w[j]* get_elem(j, i); | ||||
|         } | ||||
|         t[i] = v; | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < m_m; i++) { | ||||
|         w[i] = t[i]; | ||||
|     } | ||||
|     delete [] t; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void dense_matrix<T, X>::apply_from_right(vector <T> & w) { | ||||
|     vector<T> t(m_m, numeric_traits<T>::zero()); | ||||
|     for (unsigned i = 0; i < m_m; i ++) { | ||||
|         auto & v = t[i]; | ||||
|         for (unsigned j = 0; j < m_m; j++) | ||||
|             v += w[j]* get_elem(j, i); | ||||
|     } | ||||
| 
 | ||||
|     for (unsigned i = 0; i < m_m; i++) | ||||
|         w[i] = t[i]; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> T* dense_matrix<T, X>:: | ||||
| apply_from_left_with_different_dims(vector<T> &  w) { | ||||
|     T * t = new T[m_m]; | ||||
|     for (int i = 0; i < m_m; i ++) { | ||||
|         T v = numeric_traits<T>::zero(); | ||||
|         for (int j = 0; j < m_n; j++) { | ||||
|             v += w[j]* get_elem(i, j); | ||||
|         } | ||||
|         t[i] = v; | ||||
|     } | ||||
| 
 | ||||
|     return t; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void dense_matrix<T, X>::apply_from_left(vector<T> & w) { | ||||
|     T * t = new T[m_m]; | ||||
|     for (unsigned i = 0; i < m_m; i ++) { | ||||
|         T v = numeric_traits<T>::zero(); | ||||
|         for (unsigned j = 0; j < m_m; j++) { | ||||
|             v += w[j]* get_elem(i, j); | ||||
|         } | ||||
|         t[i] = v; | ||||
|     } | ||||
| 
 | ||||
|     for (unsigned i = 0; i < m_m; i ++) { | ||||
|         w[i] = t[i]; | ||||
|     } | ||||
|     delete [] t; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void dense_matrix<T, X>::apply_from_left(X * w, lp_settings & ) { | ||||
|     T * t = new T[m_m]; | ||||
|     for (int i = 0; i < m_m; i ++) { | ||||
|         T v = numeric_traits<T>::zero(); | ||||
|         for (int j = 0; j < m_m; j++) { | ||||
|             v += w[j]* get_elem(i, j); | ||||
|         } | ||||
|         t[i] = v; | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < m_m; i ++) { | ||||
|         w[i] = t[i]; | ||||
|     } | ||||
|     delete [] t; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void dense_matrix<T, X>::apply_from_left_to_X(vector<X> & w, lp_settings & ) { | ||||
|     vector<X> t(m_m); | ||||
|     for (int i = 0; i < m_m; i ++) { | ||||
|         X v = zero_of_type<X>(); | ||||
|         for (int j = 0; j < m_m; j++) { | ||||
|             v += w[j]* get_elem(i, j); | ||||
|         } | ||||
|         t[i] = v; | ||||
|     } | ||||
| 
 | ||||
|     for (int i = 0; i < m_m; i ++) { | ||||
|         w[i] = t[i]; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // This method pivots row i to row i0 by muliplying row i by
 | ||||
| //   alpha and adding it to row i0.
 | ||||
| template <typename T, typename X> void dense_matrix<T, X>::pivot_row_to_row(unsigned i, const T& alpha, unsigned i0, | ||||
|                                                                             const double & pivot_epsilon) { | ||||
|     for (unsigned j = 0; j < m_n; j++) { | ||||
|         m_values[i0 * m_n + j] += m_values[i * m_n + j] * alpha; | ||||
|         if (fabs(m_values[i0 + m_n + j]) < pivot_epsilon) { | ||||
|             m_values[i0 + m_n + j] = numeric_traits<T>::zero();; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void dense_matrix<T, X>::swap_columns(unsigned a, unsigned b) { | ||||
|     for (unsigned i = 0; i < m_m; i++) { | ||||
|         T t = get_elem(i, a); | ||||
|         set_elem(i, a, get_elem(i, b)); | ||||
|         set_elem(i, b, t); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void dense_matrix<T, X>::swap_rows(unsigned a, unsigned b) { | ||||
|     for (unsigned i = 0; i < m_n; i++) { | ||||
|         T t = get_elem(a, i); | ||||
|         set_elem(a, i, get_elem(b, i)); | ||||
|         set_elem(b, i, t); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void dense_matrix<T, X>::multiply_row_by_constant(unsigned row, T & t) { | ||||
|     for (unsigned i = 0; i < m_n; i++) { | ||||
|         set_elem(row, i, t * get_elem(row, i)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| dense_matrix<T, X> operator* (matrix<T, X> & a, matrix<T, X> & b){ | ||||
|     lean_assert(a.column_count() == b.row_count()); | ||||
|     dense_matrix<T, X> ret(a.row_count(), b.column_count()); | ||||
|     for (unsigned i = 0; i < ret.m_m; i++) | ||||
|         for (unsigned j = 0; j< ret.m_n; j++) { | ||||
|             T v = numeric_traits<T>::zero(); | ||||
|             for (unsigned k = 0; k < a.column_count(); k ++){ | ||||
|                 v += (a.get_elem(i, k) * b.get_elem(k, j)); | ||||
|             } | ||||
|             ret.set_elem(i, j, v); | ||||
|         } | ||||
|     return  ret; | ||||
| } | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										24
									
								
								src/util/lp/dense_matrix_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/util/lp/dense_matrix_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/lp_settings.h" | ||||
| #include "util/lp/dense_matrix.hpp" | ||||
| #ifdef LEAN_DEBUG | ||||
| #include "util/vector.h" | ||||
| template lean::dense_matrix<double, double> lean::operator*<double, double>(lean::matrix<double, double>&, lean::matrix<double, double>&); | ||||
| template void lean::dense_matrix<double, double>::apply_from_left(vector<double> &); | ||||
| template lean::dense_matrix<double, double>::dense_matrix(lean::matrix<double, double> const*); | ||||
| template lean::dense_matrix<double, double>::dense_matrix(unsigned int, unsigned int); | ||||
| template lean::dense_matrix<double, double>& lean::dense_matrix<double, double>::operator=(lean::dense_matrix<double, double> const&); | ||||
| template lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::dense_matrix(lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> > const*); | ||||
| template void lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left(vector<lean::mpq>&); | ||||
| template lean::dense_matrix<lean::mpq, lean::mpq> lean::operator*<lean::mpq, lean::mpq>(lean::matrix<lean::mpq, lean::mpq>&, lean::matrix<lean::mpq, lean::mpq>&); | ||||
| template lean::dense_matrix<lean::mpq, lean::mpq> & lean::dense_matrix<lean::mpq, lean::mpq>::operator=(lean::dense_matrix<lean::mpq, lean::mpq> const&); | ||||
| template lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::dense_matrix(unsigned int, unsigned int); | ||||
| template lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >& lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::operator=(lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> > const&); | ||||
| template lean::dense_matrix<lean::mpq, lean::numeric_pair<lean::mpq> > lean::operator*<lean::mpq, lean::numeric_pair<lean::mpq> >(lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&); | ||||
| template void lean::dense_matrix<lean::mpq, lean::numeric_pair< lean::mpq> >::apply_from_right( vector< lean::mpq> &); | ||||
| template void lean::dense_matrix<double,double>::apply_from_right(class vector<double> &); | ||||
| template void lean::dense_matrix<lean::mpq, lean::mpq>::apply_from_left(vector<lean::mpq>&); | ||||
| #endif | ||||
							
								
								
									
										83
									
								
								src/util/lp/eta_matrix.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								src/util/lp/eta_matrix.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,83 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/tail_matrix.h" | ||||
| #include "util/lp/permutation_matrix.h" | ||||
| namespace lean { | ||||
| 
 | ||||
| // This is the sum of a unit matrix and a one-column matrix
 | ||||
| template <typename T, typename X> | ||||
| class eta_matrix | ||||
|     : public tail_matrix<T, X> { | ||||
| #ifdef LEAN_DEBUG | ||||
|     unsigned m_length; | ||||
| #endif | ||||
|     unsigned m_column_index; | ||||
| public: | ||||
|     sparse_vector<T> m_column_vector; | ||||
|     T m_diagonal_element; | ||||
| #ifdef LEAN_DEBUG | ||||
|     eta_matrix(unsigned column_index, unsigned length): | ||||
| #else | ||||
|         eta_matrix(unsigned column_index): | ||||
| #endif | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
|         m_length(length), | ||||
| #endif | ||||
|         m_column_index(column_index) {} | ||||
| 
 | ||||
|     bool is_dense() const { return false; } | ||||
| 
 | ||||
|     void print(std::ostream & out) { | ||||
|         print_matrix(*this, out); | ||||
|     } | ||||
| 
 | ||||
|     bool is_unit() { | ||||
|         return m_column_vector.size() == 0 && m_diagonal_element == 1; | ||||
|     } | ||||
| 
 | ||||
|     bool set_diagonal_element(T const & diagonal_element) { | ||||
|         m_diagonal_element = diagonal_element; | ||||
|         return !lp_settings::is_eps_small_general(diagonal_element, 1e-12); | ||||
|     } | ||||
| 
 | ||||
|     const T & get_diagonal_element() const { | ||||
|         return m_diagonal_element; | ||||
|     } | ||||
| 
 | ||||
|     void apply_from_left(vector<X> & w, lp_settings & ); | ||||
| 
 | ||||
|     template <typename L> | ||||
|     void apply_from_left_local(indexed_vector<L> & w, lp_settings & settings); | ||||
| 
 | ||||
|     void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) { | ||||
|         apply_from_left_local(w, settings); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     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); | ||||
|     void apply_from_right(indexed_vector<T> & w); | ||||
| 
 | ||||
|     T get_elem(unsigned i, unsigned j) const; | ||||
| #ifdef LEAN_DEBUG | ||||
|     unsigned row_count() const { return m_length; } | ||||
|     unsigned column_count() const { return m_length; } | ||||
|     void set_number_of_rows(unsigned m) { m_length = m; } | ||||
|     void set_number_of_columns(unsigned n) { m_length = n; } | ||||
| #endif | ||||
|     void divide_by_diagonal_element() { | ||||
|         m_column_vector.divide(m_diagonal_element); | ||||
|     } | ||||
|     void conjugate_by_permutation(permutation_matrix<T, X> & p); | ||||
| }; | ||||
| } | ||||
							
								
								
									
										136
									
								
								src/util/lp/eta_matrix.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										136
									
								
								src/util/lp/eta_matrix.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,136 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/eta_matrix.h" | ||||
| namespace lean { | ||||
| 
 | ||||
| // This is the sum of a unit matrix and a one-column matrix
 | ||||
| template <typename T, typename X> | ||||
| void eta_matrix<T, X>::apply_from_left(vector<X> & w, lp_settings & ) { | ||||
|     auto & w_at_column_index = w[m_column_index]; | ||||
|     for (auto & it : m_column_vector.m_data) { | ||||
|         w[it.first] += w_at_column_index * it.second; | ||||
|     } | ||||
|     w_at_column_index /= m_diagonal_element; | ||||
| } | ||||
| template <typename T, typename X> | ||||
| template <typename L> | ||||
| void eta_matrix<T, X>:: | ||||
| apply_from_left_local(indexed_vector<L> & w, lp_settings & settings) { | ||||
|     const L w_at_column_index = w[m_column_index]; | ||||
|     if (is_zero(w_at_column_index)) return; | ||||
| 
 | ||||
|     if (settings.abs_val_is_smaller_than_drop_tolerance(w[m_column_index] /= m_diagonal_element)) { | ||||
|         w[m_column_index] = zero_of_type<L>(); | ||||
|         w.erase_from_index(m_column_index); | ||||
|     } | ||||
| 
 | ||||
|     for (auto & it : m_column_vector.m_data) { | ||||
|         unsigned i = it.first; | ||||
|         if (is_zero(w[i])) { | ||||
|             L v = w[i] = w_at_column_index * it.second; | ||||
|             if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { | ||||
|                 w[i] = zero_of_type<L>(); | ||||
|                 continue; | ||||
|             } | ||||
|             w.m_index.push_back(i); | ||||
|         } else  { | ||||
|             L v = w[i] += w_at_column_index * it.second; | ||||
|             if (settings.abs_val_is_smaller_than_drop_tolerance(v)) { | ||||
|                 w[i] = zero_of_type<L>(); | ||||
|                 w.erase_from_index(i); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void eta_matrix<T, X>::apply_from_right(vector<T> & w) { | ||||
| #ifdef LEAN_DEBUG | ||||
|     // dense_matrix<T, X> deb(*this);
 | ||||
|     // auto clone_w = clone_vector<T>(w, get_number_of_rows());
 | ||||
|     // deb.apply_from_right(clone_w);
 | ||||
| #endif | ||||
|     T t = w[m_column_index] / m_diagonal_element; | ||||
|     for (auto & it : m_column_vector.m_data) { | ||||
|         t += w[it.first] * it.second; | ||||
|     } | ||||
|     w[m_column_index] = t; | ||||
| #ifdef LEAN_DEBUG | ||||
|     // lean_assert(vectors_are_equal<T>(clone_w, w, get_number_of_rows()));
 | ||||
|     // delete clone_w;
 | ||||
| #endif | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void eta_matrix<T, X>::apply_from_right(indexed_vector<T> & w) { | ||||
|     if (w.m_index.size() == 0) | ||||
|         return; | ||||
| #ifdef LEAN_DEBUG | ||||
|     // vector<T> wcopy(w.m_data);
 | ||||
|     // apply_from_right(wcopy);
 | ||||
| #endif | ||||
|     T & t = w[m_column_index]; | ||||
|     t /= m_diagonal_element; | ||||
|     bool was_in_index = (!numeric_traits<T>::is_zero(t)); | ||||
|      | ||||
|     for (auto & it : m_column_vector.m_data) { | ||||
|         t += w[it.first] * it.second; | ||||
|     } | ||||
| 
 | ||||
|     if (numeric_traits<T>::precise() ) { | ||||
|         if (!numeric_traits<T>::is_zero(t)) { | ||||
|             if (!was_in_index) | ||||
|                 w.m_index.push_back(m_column_index); | ||||
|         } else { | ||||
|             if (was_in_index) | ||||
|                 w.erase_from_index(m_column_index); | ||||
|         } | ||||
|     } else { | ||||
|         if (!lp_settings::is_eps_small_general(t, 1e-14)) { | ||||
|             if (!was_in_index) | ||||
|                 w.m_index.push_back(m_column_index); | ||||
|         } else { | ||||
|             if (was_in_index) | ||||
|                 w.erase_from_index(m_column_index); | ||||
|             t = zero_of_type<T>(); | ||||
|         } | ||||
|     } | ||||
|      | ||||
| #ifdef LEAN_DEBUG | ||||
|     // lean_assert(w.is_OK());
 | ||||
|     // lean_assert(vectors_are_equal<T>(wcopy, w.m_data));
 | ||||
| #endif | ||||
| } | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T, typename X> | ||||
| T eta_matrix<T, X>::get_elem(unsigned i, unsigned j) const { | ||||
|     if (j == m_column_index){ | ||||
|         if (i == j) { | ||||
|             return 1 / m_diagonal_element; | ||||
|         } | ||||
|         return m_column_vector[i]; | ||||
|     } | ||||
| 
 | ||||
|     return i == j ? numeric_traits<T>::one() : numeric_traits<T>::zero(); | ||||
| } | ||||
| #endif | ||||
| template <typename T, typename X> | ||||
| void eta_matrix<T, X>::conjugate_by_permutation(permutation_matrix<T, X> & p) { | ||||
|     // this = p * this * p(-1)
 | ||||
| #ifdef LEAN_DEBUG | ||||
|     // auto rev = p.get_reverse();
 | ||||
|     // auto deb = ((*this) * rev);
 | ||||
|     // deb = p * deb;
 | ||||
| #endif | ||||
|     m_column_index = p.get_rev(m_column_index); | ||||
|     for (auto & pair : m_column_vector.m_data) { | ||||
|         pair.first = p.get_rev(pair.first); | ||||
|     } | ||||
| #ifdef LEAN_DEBUG | ||||
|     // lean_assert(deb == *this);
 | ||||
| #endif | ||||
| } | ||||
| } | ||||
							
								
								
									
										28
									
								
								src/util/lp/eta_matrix_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										28
									
								
								src/util/lp/eta_matrix_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,28 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <memory> | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/numeric_pair.h" | ||||
| #include "util/lp/eta_matrix.hpp" | ||||
| #ifdef LEAN_DEBUG | ||||
| template double lean::eta_matrix<double, double>::get_elem(unsigned int, unsigned int) const; | ||||
| template lean::mpq lean::eta_matrix<lean::mpq, lean::mpq>::get_elem(unsigned int, unsigned int) const; | ||||
| template lean::mpq lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::get_elem(unsigned int, unsigned int) const; | ||||
| #endif | ||||
| template void lean::eta_matrix<double, double>::apply_from_left(vector<double>&, lean::lp_settings&); | ||||
| template void lean::eta_matrix<double, double>::apply_from_right(vector<double>&); | ||||
| template void lean::eta_matrix<double, double>::conjugate_by_permutation(lean::permutation_matrix<double, double>&); | ||||
| template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_left(vector<lean::mpq>&, lean::lp_settings&); | ||||
| template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_right(vector<lean::mpq>&); | ||||
| template void lean::eta_matrix<lean::mpq, lean::mpq>::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::mpq>&); | ||||
| template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left(vector<lean::numeric_pair<lean::mpq> >&, lean::lp_settings&); | ||||
| template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_right(vector<lean::mpq>&); | ||||
| template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::conjugate_by_permutation(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&); | ||||
| template void lean::eta_matrix<double, double>::apply_from_left_local<double>(lean::indexed_vector<double>&, lean::lp_settings&); | ||||
| template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_left_local<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&); | ||||
| template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_left_local<lean::mpq>(lean::indexed_vector<lean::mpq>&, lean::lp_settings&); | ||||
| template void lean::eta_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_right(lean::indexed_vector<lean::mpq>&); | ||||
| template void lean::eta_matrix<lean::mpq, lean::mpq>::apply_from_right(lean::indexed_vector<lean::mpq>&); | ||||
| template void lean::eta_matrix<double, double>::apply_from_right(lean::indexed_vector<double>&); | ||||
							
								
								
									
										39
									
								
								src/util/lp/hash_helper.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										39
									
								
								src/util/lp/hash_helper.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,39 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include <utility> | ||||
| #include <functional> | ||||
| #include "util/numerics/mpq.h" | ||||
| #ifdef __CLANG__ | ||||
| #pragma clang diagnostic push | ||||
| #pragma clang diagnostic ignored "-Wmismatched-tags" | ||||
| #endif | ||||
| namespace std { | ||||
| template<> | ||||
| struct hash<lean::mpq> { | ||||
|     inline size_t operator()(const lean::mpq & v) const { | ||||
|         return v.hash(); | ||||
|     } | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| template <class T> | ||||
| inline void hash_combine(std::size_t & seed, const T & v) { | ||||
|     seed ^= std::hash<T>()(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; | ||||
|     } | ||||
| }; | ||||
| } | ||||
| #ifdef __CLANG__ | ||||
| #pragma clang diagnostic pop | ||||
| #endif | ||||
							
								
								
									
										42
									
								
								src/util/lp/implied_bound.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/util/lp/implied_bound.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/lp/lp_settings.h" | ||||
| #include "util/lp/lar_constraints.h" | ||||
| namespace lean { | ||||
| struct implied_bound { | ||||
|     mpq m_bound; | ||||
|     unsigned m_j; // the column for which the bound has been found
 | ||||
|     bool m_is_low_bound; | ||||
|     bool m_coeff_before_j_is_pos; | ||||
|     unsigned m_row_or_term_index; | ||||
|     bool m_strict; | ||||
|      | ||||
|     lconstraint_kind kind() const { | ||||
|         lconstraint_kind k = m_is_low_bound? GE : LE; | ||||
|         if (m_strict) | ||||
|             k = static_cast<lconstraint_kind>(k / 2); | ||||
|         return k; | ||||
|     } | ||||
|     bool operator==(const implied_bound & o) const { | ||||
|         return m_j == o.m_j && m_is_low_bound == o.m_is_low_bound && m_bound == o.m_bound && | ||||
|             m_coeff_before_j_is_pos == o.m_coeff_before_j_is_pos && | ||||
|             m_row_or_term_index == o.m_row_or_term_index && m_strict == o.m_strict; | ||||
|     } | ||||
|     implied_bound(){} | ||||
|     implied_bound(const mpq & a, | ||||
|                   unsigned j, | ||||
|                   bool low_bound, | ||||
|                   bool coeff_before_j_is_pos, | ||||
|                   unsigned row_or_term_index, | ||||
|                   bool strict): | ||||
|         m_bound(a), | ||||
|         m_j(j), | ||||
|         m_is_low_bound(low_bound), | ||||
|         m_coeff_before_j_is_pos(coeff_before_j_is_pos), | ||||
|         m_row_or_term_index(row_or_term_index), | ||||
|         m_strict(strict) {} | ||||
| }; | ||||
| } | ||||
							
								
								
									
										55
									
								
								src/util/lp/indexed_value.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										55
									
								
								src/util/lp/indexed_value.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,55 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| 
 | ||||
| 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; | ||||
|     } | ||||
| 
 | ||||
|     const T & value() const { | ||||
|         return m_value; | ||||
|     } | ||||
| 
 | ||||
|     void set_value(T val) { | ||||
|         m_value = val; | ||||
|     } | ||||
| }; | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename X> | ||||
| bool check_vector_for_small_values(indexed_vector<X> & w, lp_settings & settings) { | ||||
|     for (unsigned i : w.m_index) { | ||||
|         const X & v = w[i]; | ||||
|         if ((!is_zero(v)) && settings.abs_val_is_smaller_than_drop_tolerance(v)) | ||||
|             return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										169
									
								
								src/util/lp/indexed_vector.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										169
									
								
								src/util/lp/indexed_vector.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,169 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include "util/debug.h" | ||||
| #include <string> | ||||
| #include <iomanip> | ||||
| #include "util/lp/lp_utils.h" | ||||
| #include "util/lp/lp_settings.h" | ||||
| #include <unordered_set> | ||||
| namespace lean { | ||||
| 
 | ||||
| template <typename T> void print_vector(const vector<T> & t, std::ostream & out); | ||||
| template <typename T> void print_vector(const buffer<T> & t, std::ostream & out); | ||||
| template <typename T> void print_sparse_vector(const vector<T> & t, std::ostream & out); | ||||
| 
 | ||||
| void print_vector(const vector<mpq> & t, std::ostream & out); | ||||
| template <typename T> | ||||
| class indexed_vector { | ||||
| public: | ||||
|     // m_index points to non-zero elements of m_data
 | ||||
|     vector<T> m_data; | ||||
|     vector<unsigned> m_index; | ||||
|     indexed_vector(unsigned data_size) { | ||||
|         m_data.resize(data_size, numeric_traits<T>::zero()); | ||||
|     } | ||||
| 
 | ||||
|     indexed_vector& operator=(const indexed_vector<T>& y) { | ||||
|         for (unsigned i: m_index) | ||||
|             m_data[i] = zero_of_type<T>(); | ||||
| 
 | ||||
|         m_index = y.m_index; | ||||
| 
 | ||||
|         m_data.resize(y.data_size()); | ||||
|         for (unsigned i : m_index) | ||||
|             m_data[i] = y[i]; | ||||
|         return *this; | ||||
|     } | ||||
| 
 | ||||
|     bool operator==(const indexed_vector<T>& y) const { | ||||
|         std::unordered_set<unsigned> y_index; | ||||
|         for (unsigned i : y.m_index) | ||||
|             y_index.insert(i); | ||||
| 
 | ||||
|         std::unordered_set<unsigned> this_index; | ||||
|         for (unsigned i : m_index) | ||||
|             this_index.insert(i); | ||||
| 
 | ||||
|         for (unsigned i : y.m_index) { | ||||
|             if (this_index.find(i) == this_index.end()) | ||||
|                 return false; | ||||
|         } | ||||
|             | ||||
|         for (unsigned i : m_index) { | ||||
|             if (y_index.find(i) == y_index.end()) | ||||
|                 return false; | ||||
|         } | ||||
| 
 | ||||
|         return vectors_are_equal(m_data, m_data); | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     indexed_vector() {} | ||||
| 
 | ||||
|     void resize(unsigned data_size); | ||||
|     unsigned data_size() const { | ||||
|         return m_data.size(); | ||||
|     } | ||||
| 
 | ||||
|     unsigned size() { | ||||
|         return m_index.size(); | ||||
|     } | ||||
| 
 | ||||
|     void set_value(const T& value, unsigned index); | ||||
|     void set_value_as_in_dictionary(unsigned index) { | ||||
|         lean_assert(index < m_data.size()); | ||||
|         T & loc = m_data[index]; | ||||
|         if (is_zero(loc)) { | ||||
|             m_index.push_back(index); | ||||
|             loc = one_of_type<T>(); // use as a characteristic function
 | ||||
|         }  | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     void clear(); | ||||
|     void clear_all(); | ||||
|     const T& operator[] (unsigned i) const { | ||||
|         return m_data[i]; | ||||
|     } | ||||
| 
 | ||||
|     T& operator[] (unsigned i)  { | ||||
|         return m_data[i]; | ||||
|     } | ||||
| 
 | ||||
|     void clean_up() { | ||||
| #if 0==1
 | ||||
|         for (unsigned k = 0; k < m_index.size(); k++) { | ||||
|             unsigned i = m_index[k]; | ||||
|             T & v = m_data[i]; | ||||
|             if (lp_settings::is_eps_small_general(v, 1e-14)) { | ||||
|                 v = zero_of_type<T>(); | ||||
|                 m_index.erase(m_index.begin() + k--); | ||||
|             } | ||||
|         } | ||||
| #endif | ||||
|        vector<unsigned> index_copy; | ||||
|        for (unsigned i : m_index) { | ||||
|            T & v = m_data[i]; | ||||
|            if (!lp_settings::is_eps_small_general(v, 1e-14)) { | ||||
|                index_copy.push_back(i); | ||||
|            } else if (!numeric_traits<T>::is_zero(v)) { | ||||
|                v = zero_of_type<T>(); | ||||
|            } | ||||
|        } | ||||
|        m_index = index_copy; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     void erase_from_index(unsigned j); | ||||
| 
 | ||||
|     void add_value_at_index_with_drop_tolerance(unsigned j, const T& val_to_add) { | ||||
|         T & v = m_data[j]; | ||||
|         bool was_zero = is_zero(v); | ||||
|         v += val_to_add; | ||||
|         if (lp_settings::is_eps_small_general(v, 1e-14)) { | ||||
|             v = zero_of_type<T>(); | ||||
|             if (!was_zero) { | ||||
|                 erase_from_index(j); | ||||
|             } | ||||
|         } else { | ||||
|             if (was_zero) | ||||
|                 m_index.push_back(j); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void add_value_at_index(unsigned j, const T& val_to_add) { | ||||
|         T & v = m_data[j]; | ||||
|         bool was_zero = is_zero(v); | ||||
|         v += val_to_add; | ||||
|         if (is_zero(v)) { | ||||
|             if (!was_zero) | ||||
|                 erase_from_index(j); | ||||
|         } else { | ||||
|             if (was_zero) | ||||
|                 m_index.push_back(j); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void restore_index_and_clean_from_data() { | ||||
|         m_index.resize(0); | ||||
|         for (unsigned i = 0; i < m_data.size(); i++) { | ||||
|             T & v = m_data[i]; | ||||
|             if (lp_settings::is_eps_small_general(v, 1e-14)) { | ||||
|                 v = zero_of_type<T>(); | ||||
|             } else { | ||||
|                 m_index.push_back(i); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
| #ifdef LEAN_DEBUG | ||||
|     bool is_OK() const; | ||||
|     void print(std::ostream & out); | ||||
| #endif | ||||
| }; | ||||
| } | ||||
							
								
								
									
										110
									
								
								src/util/lp/indexed_vector.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										110
									
								
								src/util/lp/indexed_vector.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,110 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/indexed_vector.h" | ||||
| #include "util/lp/lp_settings.h" | ||||
| namespace lean { | ||||
| 
 | ||||
| template <typename T> | ||||
| void print_vector(const vector<T> & t, std::ostream & out) { | ||||
|     for (unsigned i = 0; i < t.size(); i++) | ||||
|         out << t[i] << " "; | ||||
|     out << std::endl; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T> | ||||
| void print_vector(const buffer<T> & t, std::ostream & out) { | ||||
|     for (unsigned i = 0; i < t.size(); i++) | ||||
|         out << t[i] << " "; | ||||
|     out << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| void print_sparse_vector(const vector<T> & t, std::ostream & out) { | ||||
|     for (unsigned i = 0; i < t.size(); i++) { | ||||
|         if (is_zero(t[i]))continue; | ||||
|         out << "[" << i << "] = " << t[i] << ", "; | ||||
|     } | ||||
|     out << std::endl; | ||||
| } | ||||
| 
 | ||||
| void print_vector(const vector<mpq> & t, std::ostream & out) { | ||||
|     for (unsigned i = 0; i < t.size(); i++) | ||||
|         out << t[i].get_double() << std::setprecision(3) << " "; | ||||
|     out << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| void indexed_vector<T>::resize(unsigned data_size) { | ||||
|     clear(); | ||||
|     m_data.resize(data_size, numeric_traits<T>::zero()); | ||||
|     lean_assert(is_OK()); | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| void indexed_vector<T>::set_value(const T& value, unsigned index) { | ||||
|     m_data[index] = value; | ||||
|     lean_assert(std::find(m_index.begin(), m_index.end(), index) == m_index.end()); | ||||
|     m_index.push_back(index); | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| void indexed_vector<T>::clear() { | ||||
|     for (unsigned i : m_index) | ||||
|         m_data[i] = numeric_traits<T>::zero(); | ||||
|     m_index.resize(0); | ||||
| } | ||||
| template <typename T> | ||||
| void indexed_vector<T>::clear_all() { | ||||
|     unsigned i = m_data.size(); | ||||
|     while (i--)  m_data[i] = numeric_traits<T>::zero(); | ||||
|     m_index.resize(0); | ||||
| } | ||||
| template <typename T> | ||||
| void indexed_vector<T>::erase_from_index(unsigned j) { | ||||
|     auto it = std::find(m_index.begin(), m_index.end(), j); | ||||
|     if (it != m_index.end()) | ||||
|         m_index.erase(it); | ||||
| } | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T> | ||||
| bool indexed_vector<T>::is_OK() const { | ||||
|     return true; | ||||
|     const double drop_eps = 1e-14; | ||||
|     for (unsigned i = 0; i < m_data.size(); i++) { | ||||
|         if (!is_zero(m_data[i]) && lp_settings::is_eps_small_general(m_data[i], drop_eps)) { | ||||
|             return false; | ||||
|         } | ||||
|         if (lp_settings::is_eps_small_general(m_data[i], drop_eps) != (std::find(m_index.begin(), m_index.end(), i) == m_index.end())) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     std::unordered_set<unsigned> s; | ||||
|     for (unsigned i : m_index) { | ||||
|         //no duplicates!!!
 | ||||
|         if (s.find(i) != s.end()) | ||||
|             return false; | ||||
|         s.insert(i); | ||||
|         if (i >= m_data.size()) | ||||
|             return false; | ||||
|     } | ||||
| 
 | ||||
|     return true; | ||||
| } | ||||
| template <typename T> | ||||
| void indexed_vector<T>::print(std::ostream & out) { | ||||
|     out << "m_index " << std::endl; | ||||
|     for (unsigned i = 0; i < m_index.size(); i++) { | ||||
|         out << m_index[i] << " "; | ||||
|     } | ||||
|     out << std::endl; | ||||
|     print_vector(m_data, out); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										36
									
								
								src/util/lp/indexed_vector_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										36
									
								
								src/util/lp/indexed_vector_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,36 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/indexed_vector.hpp" | ||||
| namespace lean { | ||||
| template void indexed_vector<double>::clear(); | ||||
| template void indexed_vector<double>::clear_all(); | ||||
| template void indexed_vector<double>::erase_from_index(unsigned int); | ||||
| template void indexed_vector<double>::set_value(const double&, unsigned int); | ||||
| template void indexed_vector<mpq>::clear(); | ||||
| template void indexed_vector<unsigned>::clear(); | ||||
| template void indexed_vector<mpq>::clear_all(); | ||||
| template void indexed_vector<mpq>::erase_from_index(unsigned int); | ||||
| template void indexed_vector<mpq>::resize(unsigned int); | ||||
| template void indexed_vector<unsigned>::resize(unsigned int); | ||||
| template void indexed_vector<mpq>::set_value(const mpq&, unsigned int); | ||||
| template void indexed_vector<unsigned>::set_value(const unsigned&, unsigned int); | ||||
| #ifdef LEAN_DEBUG | ||||
| template bool indexed_vector<double>::is_OK() const; | ||||
| template bool indexed_vector<mpq>::is_OK() const; | ||||
| template bool indexed_vector<lean::numeric_pair<mpq> >::is_OK() const; | ||||
| template void lean::indexed_vector< lean::mpq>::print(std::basic_ostream<char,struct std::char_traits<char> > &); | ||||
| template void lean::indexed_vector<double>::print(std::basic_ostream<char,struct std::char_traits<char> > &); | ||||
| template void lean::indexed_vector<lean::numeric_pair<lean::mpq> >::print(std::ostream&); | ||||
| #endif | ||||
| } | ||||
| template void lean::print_vector<double>(vector<double> const&, std::ostream&); | ||||
| template void lean::print_vector<unsigned int>(vector<unsigned int> const&, std::ostream&); | ||||
| template void lean::print_vector<std::string>(vector<std::string> const&, std::ostream&); | ||||
| template void lean::print_vector<lean::numeric_pair<lean::mpq> >(vector<lean::numeric_pair<lean::mpq>> const&, std::ostream&); | ||||
| template void lean::indexed_vector<double>::resize(unsigned int); | ||||
| template void lean::print_vector< lean::mpq>(vector< lean::mpq> const &, std::basic_ostream<char, std::char_traits<char> > &); | ||||
| template void lean::print_vector<std::pair<lean::mpq, unsigned int> >(vector<std::pair<lean::mpq, unsigned int>> const&, std::ostream&); | ||||
| template void lean::indexed_vector<lean::numeric_pair<lean::mpq> >::erase_from_index(unsigned int); | ||||
							
								
								
									
										66
									
								
								src/util/lp/int_set.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										66
									
								
								src/util/lp/int_set.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,66 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/indexed_vector.h" | ||||
| #include <ostream> | ||||
| namespace lean { | ||||
| // serves at a set of non-negative integers smaller than the set size
 | ||||
| class int_set { | ||||
|     vector<int> m_data; | ||||
| public: | ||||
|     vector<int> m_index; | ||||
|     int_set(unsigned size): m_data(size, -1) {} | ||||
|     int_set() {} | ||||
|     bool contains(unsigned j) const { | ||||
|         if (j >= m_data.size()) | ||||
|             return false; | ||||
|         return m_data[j] >= 0; | ||||
|     } | ||||
|     void insert(unsigned j) { | ||||
|         lean_assert(j < m_data.size()); | ||||
|         if (contains(j)) return; | ||||
|         m_data[j] = m_index.size(); | ||||
|         m_index.push_back(j); | ||||
|     } | ||||
|     void erase(unsigned j) { | ||||
|         if (!contains(j)) return; | ||||
|         unsigned pos_j = m_data[j]; | ||||
|         unsigned last_pos = m_index.size() - 1; | ||||
|         int last_j = m_index[last_pos]; | ||||
|         if (last_pos != pos_j) { | ||||
|             // move last to j spot
 | ||||
|             m_data[last_j] = pos_j; | ||||
|             m_index[pos_j] = last_j; | ||||
|         } | ||||
|         m_index.pop_back(); | ||||
|         m_data[j] = -1; | ||||
|     } | ||||
| 
 | ||||
|     void resize(unsigned size) { | ||||
|         m_data.resize(size, -1); | ||||
|     } | ||||
| 
 | ||||
|     void increase_size_by_one() { | ||||
|         resize(m_data.size() + 1); | ||||
|     } | ||||
| 
 | ||||
|     unsigned data_size() const {  return m_data.size(); } | ||||
|     unsigned size() const { return m_index.size();} | ||||
|     bool is_empty() const { return size() == 0; } | ||||
|     void clear() { | ||||
|         for (unsigned j : m_index) | ||||
|             m_data[j] = -1; | ||||
|         m_index.resize(0); | ||||
|     } | ||||
|     void print(std::ostream & out ) const { | ||||
|         for (unsigned j : m_index) { | ||||
|             out << j << " "; | ||||
|         } | ||||
|         out << std::endl; | ||||
|     } | ||||
|      | ||||
| }; | ||||
| } | ||||
							
								
								
									
										50
									
								
								src/util/lp/iterator_on_column.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										50
									
								
								src/util/lp/iterator_on_column.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,50 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/lp/linear_combination_iterator.h" | ||||
| #include "util/lp/static_matrix.h" | ||||
| #include "util/lp/lar_term.h" | ||||
| namespace lean { | ||||
| template <typename T, typename X> | ||||
| struct iterator_on_column:linear_combination_iterator<T> { | ||||
|     const vector<column_cell>& m_column; // the offset in term coeffs
 | ||||
|     const static_matrix<T, X> & m_A; | ||||
|     int m_i = -1; // the initial offset in the column
 | ||||
|     unsigned size() const { return m_column.size(); } | ||||
|     iterator_on_column(const vector<column_cell>& column, const static_matrix<T,X> & A) // the offset in term coeffs
 | ||||
|         : | ||||
|         m_column(column), | ||||
|         m_A(A), | ||||
|         m_i(-1) {} | ||||
|      | ||||
|     bool next(mpq & a, unsigned & i) { | ||||
|         if (++m_i >= static_cast<int>(m_column.size())) | ||||
|             return false; | ||||
| 
 | ||||
|         const column_cell& c = m_column[m_i]; | ||||
|         a = m_A.get_val(c); | ||||
|         i = c.m_i; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     bool next(unsigned & i) { | ||||
|         if (++m_i >= static_cast<int>(m_column.size())) | ||||
|             return false; | ||||
| 
 | ||||
|         const column_cell& c = m_column[m_i]; | ||||
|         i = c.m_i; | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     void reset() { | ||||
|         m_i = -1; | ||||
|     } | ||||
| 
 | ||||
|     linear_combination_iterator<mpq> * clone() { | ||||
|         iterator_on_column * r = new iterator_on_column(m_column, m_A); | ||||
|         return r; | ||||
|     } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										35
									
								
								src/util/lp/iterator_on_indexed_vector.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								src/util/lp/iterator_on_indexed_vector.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,35 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/lp/linear_combination_iterator.h" | ||||
| namespace lean { | ||||
| template <typename T> | ||||
| struct iterator_on_indexed_vector:linear_combination_iterator<T> { | ||||
|     const indexed_vector<T> & m_v; | ||||
|     unsigned m_offset = 0; | ||||
|     iterator_on_indexed_vector(const indexed_vector<T> & v) : m_v(v){} | ||||
|     unsigned size() const { return m_v.m_index.size(); } | ||||
|     bool next(T & a, unsigned & i) { | ||||
|         if (m_offset >= m_v.m_index.size()) | ||||
|             return false; | ||||
|         i = m_v.m_index[m_offset++]; | ||||
|         a = m_v.m_data[i]; | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     bool next(unsigned & i) { | ||||
|         if (m_offset >= m_v.m_index.size()) | ||||
|             return false; | ||||
|         i = m_v.m_index[m_offset++]; | ||||
|         return true; | ||||
|     } | ||||
|     void reset() { | ||||
|         m_offset = 0; | ||||
|     } | ||||
|     linear_combination_iterator<T>* clone() { | ||||
|         return new iterator_on_indexed_vector(m_v); | ||||
|     } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										42
									
								
								src/util/lp/iterator_on_pivot_row.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								src/util/lp/iterator_on_pivot_row.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,42 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/lp/iterator_on_indexed_vector.h" | ||||
| namespace lean { | ||||
| template <typename T> | ||||
| struct iterator_on_pivot_row:linear_combination_iterator<T> { | ||||
|     bool m_basis_returned = false; | ||||
|     const indexed_vector<T> & m_v; | ||||
|     unsigned m_basis_j; | ||||
|     iterator_on_indexed_vector<T> m_it; | ||||
|     unsigned size() const { return m_it.size(); } | ||||
|     iterator_on_pivot_row(const indexed_vector<T> & v, unsigned basis_j) : m_v(v), m_basis_j(basis_j), m_it(v) {} | ||||
|     bool next(T & a, unsigned & i) { | ||||
|         if (m_basis_returned == false) { | ||||
|             m_basis_returned = true; | ||||
|             a = one_of_type<T>(); | ||||
|             i = m_basis_j; | ||||
|             return true; | ||||
|         } | ||||
|         return m_it.next(a, i); | ||||
|     } | ||||
|     bool next(unsigned & i) { | ||||
|         if (m_basis_returned == false) { | ||||
|             m_basis_returned = true; | ||||
|             i = m_basis_j; | ||||
|             return true; | ||||
|         } | ||||
|         return m_it.next(i); | ||||
|     } | ||||
|     void reset() { | ||||
|         m_basis_returned = false; | ||||
|         m_it.reset(); | ||||
|     } | ||||
|     linear_combination_iterator<T> * clone() { | ||||
|         iterator_on_pivot_row * r = new iterator_on_pivot_row(m_v, m_basis_j); | ||||
|         return r; | ||||
|     } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										37
									
								
								src/util/lp/iterator_on_row.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								src/util/lp/iterator_on_row.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,37 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/lp/linear_combination_iterator.h" | ||||
| namespace lean { | ||||
| template <typename T> | ||||
| struct iterator_on_row:linear_combination_iterator<T> { | ||||
|     const vector<row_cell<T>> & m_row; | ||||
|     unsigned m_i= 0; // offset
 | ||||
|     iterator_on_row(const vector<row_cell<T>> & row) : m_row(row) | ||||
|     {} | ||||
|     unsigned size() const { return m_row.size(); } | ||||
|     bool next(T & a, unsigned & i) { | ||||
|         if (m_i == m_row.size()) | ||||
|             return false; | ||||
|         auto &c = m_row[m_i++]; | ||||
|         i = c.m_j; | ||||
|         a = c.get_val(); | ||||
|         return true; | ||||
|     } | ||||
|     bool next(unsigned & i) { | ||||
|         if (m_i == m_row.size()) | ||||
|             return false; | ||||
|         auto &c = m_row[m_i++]; | ||||
|         i = c.m_j; | ||||
|         return true; | ||||
|     } | ||||
|     void reset() { | ||||
|         m_i = 0; | ||||
|     } | ||||
|     linear_combination_iterator<T>* clone() { | ||||
|         return new iterator_on_row(m_row); | ||||
|     } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										56
									
								
								src/util/lp/iterator_on_term_with_basis_var.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										56
									
								
								src/util/lp/iterator_on_term_with_basis_var.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,56 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/lp/linear_combination_iterator.h" | ||||
| #include "util/lp/numeric_pair.h" | ||||
| #include "util/lp/lar_term.h" | ||||
| namespace lean { | ||||
| struct iterator_on_term_with_basis_var:linear_combination_iterator<mpq> { | ||||
|     std::unordered_map<unsigned, mpq>::const_iterator m_i; // the offset in term coeffs
 | ||||
|     bool m_term_j_returned = false; | ||||
|     const lar_term & m_term; | ||||
|     unsigned m_term_j; | ||||
|     unsigned size() const {return static_cast<unsigned>(m_term.m_coeffs.size() + 1);} | ||||
|     iterator_on_term_with_basis_var(const lar_term & t, unsigned term_j) : | ||||
|         m_i(t.m_coeffs.begin()), | ||||
|         m_term(t), | ||||
|         m_term_j(term_j) {} | ||||
| 
 | ||||
|     bool next(mpq & a, unsigned & i) { | ||||
|         if (m_term_j_returned == false) { | ||||
|             m_term_j_returned = true; | ||||
|             a = - one_of_type<mpq>(); | ||||
|             i = m_term_j; | ||||
|             return true; | ||||
|         } | ||||
|         if (m_i == m_term.m_coeffs.end()) | ||||
|             return false; | ||||
|         i = m_i->first; | ||||
|         a = m_i->second; | ||||
|         m_i++; | ||||
|         return true; | ||||
|     } | ||||
|     bool next(unsigned & i) { | ||||
|         if (m_term_j_returned == false) { | ||||
|             m_term_j_returned = true; | ||||
|             i = m_term_j; | ||||
|             return true; | ||||
|         } | ||||
|         if (m_i == m_term.m_coeffs.end()) | ||||
|             return false; | ||||
|         i = m_i->first; | ||||
|         m_i++; | ||||
|         return true; | ||||
|     } | ||||
|     void reset() { | ||||
|         m_term_j_returned = false; | ||||
|         m_i = m_term.m_coeffs.begin(); | ||||
|     } | ||||
|     linear_combination_iterator<mpq> * clone() { | ||||
|         iterator_on_term_with_basis_var * r = new iterator_on_term_with_basis_var(m_term, m_term_j); | ||||
|         return r; | ||||
|     } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										86
									
								
								src/util/lp/lar_constraints.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										86
									
								
								src/util/lp/lar_constraints.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,86 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include <utility> | ||||
| #include <unordered_map> | ||||
| #include <string> | ||||
| #include <algorithm> | ||||
| #include "util/lp/lp_utils.h" | ||||
| #include "util/lp/ul_pair.h" | ||||
| #include "util/lp/lar_term.h" | ||||
| namespace lean { | ||||
| inline lconstraint_kind flip_kind(lconstraint_kind t) { | ||||
|     return static_cast<lconstraint_kind>( - static_cast<int>(t)); | ||||
| } | ||||
| 
 | ||||
| inline std::string lconstraint_kind_string(lconstraint_kind t) { | ||||
|     switch (t) { | ||||
|     case LE: return std::string("<="); | ||||
|     case LT: return std::string("<"); | ||||
|     case GE: return std::string(">="); | ||||
|     case GT: return std::string(">"); | ||||
|     case EQ: return std::string("="); | ||||
|     } | ||||
|     lean_unreachable(); | ||||
|     return std::string(); // it is unreachable
 | ||||
| } | ||||
| 
 | ||||
| class lar_base_constraint { | ||||
| public: | ||||
|     lconstraint_kind m_kind; | ||||
|     mpq m_right_side; | ||||
|     virtual vector<std::pair<mpq, var_index>> get_left_side_coefficients() const = 0; | ||||
|     lar_base_constraint() {} | ||||
|     lar_base_constraint(lconstraint_kind kind, const mpq& right_side) :m_kind(kind), m_right_side(right_side) {} | ||||
| 
 | ||||
|     virtual unsigned size() const = 0; | ||||
|     virtual ~lar_base_constraint(){} | ||||
|     virtual mpq get_free_coeff_of_left_side() const { return zero_of_type<mpq>();} | ||||
| }; | ||||
| 
 | ||||
| struct lar_var_constraint: public lar_base_constraint { | ||||
|     unsigned m_j; | ||||
|     vector<std::pair<mpq, var_index>> get_left_side_coefficients() const { | ||||
|         vector<std::pair<mpq, var_index>> ret; | ||||
|         ret.push_back(std::make_pair(one_of_type<mpq>(), m_j)); | ||||
|         return ret; | ||||
|     } | ||||
|     unsigned size() const { return 1;} | ||||
|     lar_var_constraint(unsigned j, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_j(j) { } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| struct lar_term_constraint: public lar_base_constraint { | ||||
|     const lar_term * m_term; | ||||
|     vector<std::pair<mpq, var_index>> get_left_side_coefficients() const { | ||||
|         return m_term->coeffs_as_vector(); | ||||
|     } | ||||
|     unsigned size() const { return m_term->size();} | ||||
|     lar_term_constraint(const lar_term *t, lconstraint_kind kind, const mpq& right_side) : lar_base_constraint(kind, right_side), m_term(t) { } | ||||
|     virtual mpq get_free_coeff_of_left_side() const { return m_term->m_v;} | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| class lar_constraint : public lar_base_constraint { | ||||
| public: | ||||
|     vector<std::pair<mpq, var_index>> m_coeffs; | ||||
|     lar_constraint() {}  | ||||
|     lar_constraint(const vector<std::pair<mpq, var_index>> & left_side, lconstraint_kind kind, const mpq & right_side) | ||||
|         :  lar_base_constraint(kind, right_side), m_coeffs(left_side) {} | ||||
|      | ||||
|     lar_constraint(const lar_base_constraint & c) { | ||||
|         lean_assert(false); // should not be called : todo!
 | ||||
|     } | ||||
| 
 | ||||
|     unsigned size() const { | ||||
|         return static_cast<unsigned>(m_coeffs.size()); | ||||
|     } | ||||
| 
 | ||||
|     vector<std::pair<mpq, var_index>> get_left_side_coefficients() const { return m_coeffs; } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										802
									
								
								src/util/lp/lar_core_solver.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										802
									
								
								src/util/lp/lar_core_solver.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,802 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include <string> | ||||
| #include <utility> | ||||
| #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" | ||||
| #include "util/lp/stacked_unordered_set.h" | ||||
| #include "util/lp/lp_primal_core_solver.h" | ||||
| #include "util/lp/stacked_vector.h" | ||||
| #include "util/lp/lar_solution_signature.h" | ||||
| #include "util/lp/iterator_on_column.h" | ||||
| #include "util/lp/iterator_on_indexed_vector.h" | ||||
| #include "util/lp/stacked_value.h" | ||||
| namespace lean { | ||||
| 
 | ||||
| class lar_core_solver  { | ||||
|     // 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; | ||||
|     vector<std::pair<mpq, unsigned>> m_infeasible_linear_combination; | ||||
|     int m_infeasible_sum_sign = 0; // todo: get rid of this field
 | ||||
|     vector<numeric_pair<mpq>> m_right_sides_dummy; | ||||
|     vector<mpq> m_costs_dummy; | ||||
|     vector<double> m_d_right_sides_dummy; | ||||
|     vector<double> m_d_costs_dummy; | ||||
| public: | ||||
|     stacked_value<simplex_strategy_enum> m_stacked_simplex_strategy; | ||||
|     stacked_vector<column_type> m_column_types; | ||||
|     // r - solver fields, for rational numbers
 | ||||
|     vector<numeric_pair<mpq>> m_r_x; // the solution
 | ||||
|     stacked_vector<numeric_pair<mpq>> m_r_low_bounds; | ||||
|     stacked_vector<numeric_pair<mpq>> m_r_upper_bounds; | ||||
|     static_matrix<mpq, numeric_pair<mpq>> m_r_A; | ||||
|     stacked_vector<unsigned> m_r_pushed_basis; | ||||
|     vector<unsigned> m_r_basis; | ||||
|     vector<unsigned> m_r_nbasis; | ||||
|     vector<int> m_r_heading; | ||||
|     stacked_vector<unsigned> m_r_columns_nz; | ||||
|     stacked_vector<unsigned> m_r_rows_nz; | ||||
|      | ||||
|     // d - solver fields, for doubles
 | ||||
|     vector<double> m_d_x; // the solution in doubles
 | ||||
|     vector<double> m_d_low_bounds; | ||||
|     vector<double> m_d_upper_bounds; | ||||
|     static_matrix<double, double> m_d_A; | ||||
|     stacked_vector<unsigned> m_d_pushed_basis; | ||||
|     vector<unsigned> m_d_basis; | ||||
|     vector<unsigned> m_d_nbasis; | ||||
|     vector<int> m_d_heading; | ||||
| 
 | ||||
| 
 | ||||
|     lp_primal_core_solver<mpq, numeric_pair<mpq>> m_r_solver; // solver in rational numbers
 | ||||
| 
 | ||||
|     lp_primal_core_solver<double, double> m_d_solver; // solver in doubles
 | ||||
|      | ||||
|     lar_core_solver( | ||||
|                     lp_settings & settings, | ||||
|                     const column_namer & column_names | ||||
|                     ); | ||||
| 
 | ||||
|     lp_settings & settings() { return m_r_solver.m_settings;} | ||||
|     const lp_settings & settings() const { return m_r_solver.m_settings;} | ||||
|      | ||||
|     int get_infeasible_sum_sign() const { return m_infeasible_sum_sign;   } | ||||
| 
 | ||||
|     const vector<std::pair<mpq, unsigned>> & get_infeasibility_info(int & inf_sign) const { | ||||
|         inf_sign = m_infeasible_sum_sign; | ||||
|         return m_infeasible_linear_combination; | ||||
|     } | ||||
| 
 | ||||
|     void fill_not_improvable_zero_sum_from_inf_row(); | ||||
|      | ||||
|     column_type get_column_type(unsigned j) { return m_column_types[j];} | ||||
|      | ||||
|     void init_costs(bool first_time); | ||||
| 
 | ||||
|     void init_cost_for_column(unsigned j); | ||||
| 
 | ||||
|     // returns m_sign_of_alpha_r
 | ||||
|     int column_is_out_of_bounds(unsigned j); | ||||
| 
 | ||||
|     void calculate_pivot_row(unsigned i); | ||||
| 
 | ||||
|     void print_pivot_row(std::ostream & out, unsigned row_index) const  { // remove later debug !!!!
 | ||||
|         for (unsigned j : m_r_solver.m_pivot_row.m_index) { | ||||
|             if (numeric_traits<mpq>::is_pos(m_r_solver.m_pivot_row.m_data[j])) | ||||
|                 out << "+"; | ||||
|             out << m_r_solver.m_pivot_row.m_data[j] << m_r_solver.column_name(j) << " "; | ||||
|         } | ||||
|          | ||||
|         out << " +" << m_r_solver.column_name(m_r_solver.m_basis[row_index]) << std::endl; | ||||
|          | ||||
|         for (unsigned j : m_r_solver.m_pivot_row.m_index) { | ||||
|             m_r_solver.print_column_bound_info(j, out); | ||||
|         } | ||||
|         m_r_solver.print_column_bound_info(m_r_solver.m_basis[row_index], out); | ||||
|          | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     void advance_on_sorted_breakpoints(unsigned entering); | ||||
| 
 | ||||
|     void change_slope_on_breakpoint(unsigned entering, breakpoint<numeric_pair<mpq>> * b, mpq & slope_at_entering); | ||||
| 
 | ||||
|     bool row_is_infeasible(unsigned row); | ||||
| 
 | ||||
|     bool row_is_evidence(unsigned row); | ||||
| 
 | ||||
|     bool find_evidence_row(); | ||||
| 
 | ||||
|     void prefix_r(); | ||||
| 
 | ||||
|     void prefix_d(); | ||||
| 
 | ||||
|     unsigned m_m() const { | ||||
|         return m_r_A.row_count(); | ||||
|     } | ||||
| 
 | ||||
|     unsigned m_n() const { | ||||
|         return m_r_A.column_count(); | ||||
|     } | ||||
|      | ||||
|     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; } | ||||
| 
 | ||||
|     template <typename L> | ||||
|     int get_sign(const L & v) { | ||||
|         return v > zero_of_type<L>() ? 1 : (v < zero_of_type<L>() ? -1 : 0); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     void fill_evidence(unsigned row); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     void solve(); | ||||
| 
 | ||||
|     bool low_bounds_are_set() const { return true; } | ||||
| 
 | ||||
|     const indexed_vector<mpq> & get_pivot_row() const { | ||||
|         return m_r_solver.m_pivot_row; | ||||
|     } | ||||
|      | ||||
|     void fill_not_improvable_zero_sum(); | ||||
| 
 | ||||
|     void pop_basis(unsigned k) { | ||||
|         if (!settings().use_tableau()) { | ||||
|             m_r_pushed_basis.pop(k);         | ||||
|             m_r_basis = m_r_pushed_basis(); | ||||
|             m_r_solver.init_basis_heading_and_non_basic_columns_vector(); | ||||
|             m_d_pushed_basis.pop(k); | ||||
|             m_d_basis = m_d_pushed_basis(); | ||||
|             m_d_solver.init_basis_heading_and_non_basic_columns_vector(); | ||||
|         } else { | ||||
|             m_d_basis = m_r_basis; | ||||
|             m_d_nbasis = m_r_nbasis; | ||||
|             m_d_heading = m_r_heading; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void push() { | ||||
|         lean_assert(m_r_solver.basis_heading_is_correct()); | ||||
|         lean_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); | ||||
|         lean_assert(m_column_types.size() == m_r_A.column_count()); | ||||
|         m_stacked_simplex_strategy = settings().simplex_strategy(); | ||||
|         m_stacked_simplex_strategy.push(); | ||||
|         m_column_types.push(); | ||||
|         // rational
 | ||||
|         if (!settings().use_tableau())  | ||||
|             m_r_A.push(); | ||||
|         m_r_low_bounds.push(); | ||||
|         m_r_upper_bounds.push(); | ||||
|         if (!settings().use_tableau()) { | ||||
|             push_vector(m_r_pushed_basis, m_r_basis); | ||||
|             push_vector(m_r_columns_nz, m_r_solver.m_columns_nz); | ||||
|             push_vector(m_r_rows_nz, m_r_solver.m_rows_nz); | ||||
|         } | ||||
|          | ||||
|         m_d_A.push(); | ||||
|         if (!settings().use_tableau()) | ||||
|             push_vector(m_d_pushed_basis, m_d_basis); | ||||
|     } | ||||
| 
 | ||||
|     template <typename K>  | ||||
|     void push_vector(stacked_vector<K> & pushed_vector, const vector<K> & vector) { | ||||
|         lean_assert(pushed_vector.size() <= vector.size()); | ||||
|         for (unsigned i = 0; i < vector.size();i++) { | ||||
|             if (i == pushed_vector.size()) { | ||||
|                 pushed_vector.push_back(vector[i]); | ||||
|             } else { | ||||
|                 pushed_vector[i] = vector[i]; | ||||
|             } | ||||
|         } | ||||
|         pushed_vector.push(); | ||||
|     } | ||||
| 
 | ||||
|     void pop_markowitz_counts(unsigned k) { | ||||
|         m_r_columns_nz.pop(k); | ||||
|         m_r_rows_nz.pop(k); | ||||
|         m_r_solver.m_columns_nz.resize(m_r_columns_nz.size()); | ||||
|         m_r_solver.m_rows_nz.resize(m_r_rows_nz.size()); | ||||
|         for (unsigned i = 0; i < m_r_columns_nz.size(); i++) | ||||
|             m_r_solver.m_columns_nz[i] = m_r_columns_nz[i]; | ||||
|         for (unsigned i = 0; i < m_r_rows_nz.size(); i++) | ||||
|             m_r_solver.m_rows_nz[i] = m_r_rows_nz[i]; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     void pop(unsigned k) { | ||||
|         m_stacked_simplex_strategy.pop(k); | ||||
|         bool use_tableau = m_stacked_simplex_strategy() != simplex_strategy_enum::no_tableau; | ||||
|         // rationals
 | ||||
|         if (!settings().use_tableau())  | ||||
|             m_r_A.pop(k); | ||||
|         m_r_low_bounds.pop(k); | ||||
|         m_r_upper_bounds.pop(k); | ||||
|         m_column_types.pop(k); | ||||
|          | ||||
|         if (m_r_solver.m_factorization != nullptr) { | ||||
|             delete m_r_solver.m_factorization; | ||||
|             m_r_solver.m_factorization = nullptr; | ||||
|         } | ||||
|         m_r_x.resize(m_r_A.column_count()); | ||||
|         m_r_solver.m_costs.resize(m_r_A.column_count()); | ||||
|         m_r_solver.m_d.resize(m_r_A.column_count()); | ||||
|         if(!use_tableau) | ||||
|             pop_markowitz_counts(k); | ||||
|         m_d_A.pop(k); | ||||
|         if (m_d_solver.m_factorization != nullptr) { | ||||
|             delete m_d_solver.m_factorization; | ||||
|             m_d_solver.m_factorization = nullptr; | ||||
|         } | ||||
|          | ||||
|         m_d_x.resize(m_d_A.column_count()); | ||||
|         pop_basis(k); | ||||
| 
 | ||||
|         lean_assert(m_r_solver.basis_heading_is_correct()); | ||||
|         lean_assert(!need_to_presolve_with_double_solver() || m_d_solver.basis_heading_is_correct()); | ||||
|     } | ||||
| 
 | ||||
|     bool need_to_presolve_with_double_solver() const { | ||||
|         return settings().presolve_with_double_solver_for_lar && !settings().use_tableau(); | ||||
|     } | ||||
| 
 | ||||
|     template <typename L> | ||||
|     bool is_zero_vector(const vector<L> & b) { | ||||
|         for (const L & m: b) | ||||
|             if (!is_zero(m)) return false; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     bool update_xj_and_get_delta(unsigned j, non_basic_column_value_position pos_type, numeric_pair<mpq> & delta) { | ||||
|         auto & x = m_r_x[j]; | ||||
|         switch (pos_type) { | ||||
|         case at_low_bound: | ||||
|             if (x == m_r_solver.m_low_bounds[j]) | ||||
|                 return false; | ||||
|             delta = m_r_solver.m_low_bounds[j] - x; | ||||
|             m_r_solver.m_x[j] = m_r_solver.m_low_bounds[j]; | ||||
|             break; | ||||
|         case at_fixed: | ||||
|         case at_upper_bound: | ||||
|             if (x == m_r_solver.m_upper_bounds[j]) | ||||
|                 return false; | ||||
|             delta = m_r_solver.m_upper_bounds[j] - x; | ||||
|             x = m_r_solver.m_upper_bounds[j]; | ||||
|             break; | ||||
|         case free_of_bounds: { | ||||
|             return false; | ||||
|         } | ||||
|         case not_at_bound: | ||||
|             switch (m_column_types[j]) { | ||||
|             case column_type::free_column: | ||||
|                 return false; | ||||
|             case column_type::upper_bound: | ||||
|                 delta = m_r_solver.m_upper_bounds[j] - x; | ||||
|                 x = m_r_solver.m_upper_bounds[j]; | ||||
|                 break; | ||||
|             case column_type::low_bound: | ||||
|                 delta = m_r_solver.m_low_bounds[j] - x; | ||||
|                 x = m_r_solver.m_low_bounds[j]; | ||||
|                 break; | ||||
|             case column_type::boxed: | ||||
|                 if (x > m_r_solver.m_upper_bounds[j]) { | ||||
|                     delta = m_r_solver.m_upper_bounds[j] - x; | ||||
|                     x += m_r_solver.m_upper_bounds[j]; | ||||
|                 } else { | ||||
|                     delta = m_r_solver.m_low_bounds[j] - x; | ||||
|                     x = m_r_solver.m_low_bounds[j]; | ||||
|                 } | ||||
|                 break; | ||||
|             case column_type::fixed: | ||||
|                 delta = m_r_solver.m_low_bounds[j] - x; | ||||
|                 x = m_r_solver.m_low_bounds[j]; | ||||
|                 break; | ||||
| 
 | ||||
|             default: | ||||
|                 lean_assert(false); | ||||
|             } | ||||
|             break; | ||||
|         default: | ||||
|             lean_unreachable(); | ||||
|         } | ||||
|         m_r_solver.remove_column_from_inf_set(j); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|      | ||||
|     void prepare_solver_x_with_signature_tableau(const lar_solution_signature & signature) { | ||||
|         lean_assert(m_r_solver.inf_set_is_correct()); | ||||
|         for (auto &t : signature) { | ||||
|             unsigned j = t.first; | ||||
|             if (m_r_heading[j] >= 0) | ||||
|                 continue; | ||||
|             auto pos_type = t.second; | ||||
|             numeric_pair<mpq> delta; | ||||
|             if (!update_xj_and_get_delta(j, pos_type, delta)) | ||||
|                 continue; | ||||
|             for (const auto & cc : m_r_solver.m_A.m_columns[j]){ | ||||
|                 unsigned i = cc.m_i; | ||||
|                 unsigned jb = m_r_solver.m_basis[i]; | ||||
|                 m_r_solver.m_x[jb] -= delta * m_r_solver.m_A.get_val(cc); | ||||
|                 m_r_solver.update_column_in_inf_set(jb); | ||||
|             } | ||||
|             lean_assert(m_r_solver.A_mult_x_is_off() == false); | ||||
|         } | ||||
|         lean_assert(m_r_solver.inf_set_is_correct()); | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     template <typename L, typename K> | ||||
|     void prepare_solver_x_with_signature(const lar_solution_signature & signature, lp_primal_core_solver<L,K> & s) { | ||||
|         for (auto &t : signature) { | ||||
|             unsigned j = t.first; | ||||
|             lean_assert(m_r_heading[j] < 0); | ||||
|             auto pos_type = t.second; | ||||
|             switch (pos_type) { | ||||
|             case at_low_bound: | ||||
|                 s.m_x[j] = s.m_low_bounds[j]; | ||||
|                 break; | ||||
|             case at_fixed: | ||||
|             case at_upper_bound: | ||||
|                 s.m_x[j] = s.m_upper_bounds[j]; | ||||
|                 break; | ||||
|             case free_of_bounds: { | ||||
|                 s.m_x[j] = zero_of_type<K>(); | ||||
|                 continue; | ||||
|             } | ||||
|             case not_at_bound: | ||||
|                   switch (m_column_types[j]) { | ||||
|                   case column_type::free_column: | ||||
|                       lean_assert(false); // unreachable
 | ||||
|                   case column_type::upper_bound: | ||||
|                       s.m_x[j] = s.m_upper_bounds[j]; | ||||
|                       break; | ||||
|                   case column_type::low_bound: | ||||
|                       s.m_x[j] = s.m_low_bounds[j]; | ||||
|                       break; | ||||
|                   case column_type::boxed: | ||||
|                       if (my_random() % 2) { | ||||
|                           s.m_x[j] = s.m_low_bounds[j]; | ||||
|                       } else { | ||||
|                           s.m_x[j] = s.m_upper_bounds[j]; | ||||
|                       } | ||||
|                       break; | ||||
|                   case column_type::fixed: | ||||
|                       s.m_x[j] = s.m_low_bounds[j]; | ||||
|                       break; | ||||
|                   default: | ||||
|                       lean_assert(false); | ||||
|                   } | ||||
|                   break; | ||||
|             default: | ||||
|                 lean_unreachable(); | ||||
|             } | ||||
|         } | ||||
| 
 | ||||
|         lean_assert(is_zero_vector(s.m_b)); | ||||
|         s.solve_Ax_eq_b(); | ||||
|     } | ||||
| 
 | ||||
|     template <typename L, typename K>  | ||||
|     void catch_up_in_lu_in_reverse(const vector<unsigned> & trace_of_basis_change, lp_primal_core_solver<L,K> & cs) { | ||||
|         // recover the previous working basis
 | ||||
|         for (unsigned i = trace_of_basis_change.size(); i > 0;  i-= 2) { | ||||
|             unsigned entering = trace_of_basis_change[i-1]; | ||||
|             unsigned leaving = trace_of_basis_change[i-2]; | ||||
|             cs.change_basis_unconditionally(entering, leaving); | ||||
|         } | ||||
|         cs.init_lu(); | ||||
|     } | ||||
| 
 | ||||
|     //basis_heading is the basis heading of the solver owning trace_of_basis_change
 | ||||
|     // here we compact the trace as we go to avoid unnecessary column changes
 | ||||
|     template <typename L, typename K>  | ||||
|     void catch_up_in_lu(const vector<unsigned> & trace_of_basis_change, const vector<int> & basis_heading, lp_primal_core_solver<L,K> & cs) { | ||||
|         if (cs.m_factorization == nullptr || cs.m_factorization->m_refactor_counter + trace_of_basis_change.size()/2 >= 200) { | ||||
|             for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { | ||||
|                 unsigned entering = trace_of_basis_change[i]; | ||||
|                 unsigned leaving = trace_of_basis_change[i+1]; | ||||
|                 cs.change_basis_unconditionally(entering, leaving); | ||||
|             } | ||||
|             if (cs.m_factorization != nullptr) | ||||
|                 delete cs.m_factorization; | ||||
|             cs.m_factorization = nullptr; | ||||
|         } else { | ||||
|             indexed_vector<L> w(cs.m_A.row_count()); | ||||
|             // the queues of delayed indices
 | ||||
|             std::queue<unsigned> entr_q, leav_q; | ||||
|             auto * l = cs.m_factorization; | ||||
|             lean_assert(l->get_status() == LU_status::OK); | ||||
|             for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { | ||||
|                 unsigned entering = trace_of_basis_change[i]; | ||||
|                 unsigned leaving = trace_of_basis_change[i+1]; | ||||
|                 bool good_e = basis_heading[entering] >= 0 && cs.m_basis_heading[entering] < 0; | ||||
|                 bool good_l = basis_heading[leaving] < 0 && cs.m_basis_heading[leaving] >= 0; | ||||
|                 if (!good_e && !good_l) continue; | ||||
|                 if (good_e && !good_l) { | ||||
|                     while (!leav_q.empty() && cs.m_basis_heading[leav_q.front()] < 0) | ||||
|                         leav_q.pop(); | ||||
|                     if (!leav_q.empty()) { | ||||
|                         leaving = leav_q.front(); | ||||
|                         leav_q.pop(); | ||||
|                     } else { | ||||
|                         entr_q.push(entering); | ||||
|                         continue; | ||||
|                     } | ||||
|                 } else if (!good_e && good_l) { | ||||
|                     while (!entr_q.empty() && cs.m_basis_heading[entr_q.front()] >= 0) | ||||
|                         entr_q.pop(); | ||||
|                     if (!entr_q.empty()) { | ||||
|                         entering = entr_q.front(); | ||||
|                         entr_q.pop(); | ||||
|                     } else { | ||||
|                         leav_q.push(leaving); | ||||
|                         continue; | ||||
|                     } | ||||
|                 } | ||||
|                 lean_assert(cs.m_basis_heading[entering] < 0); | ||||
|                 lean_assert(cs.m_basis_heading[leaving] >= 0); | ||||
|                 if (l->get_status() == LU_status::OK) { | ||||
|                     l->prepare_entering(entering, w); // to init vector w
 | ||||
|                     l->replace_column(zero_of_type<L>(), w, cs.m_basis_heading[leaving]); | ||||
|                 } | ||||
|                 cs.change_basis_unconditionally(entering, leaving); | ||||
|             } | ||||
|             if (l->get_status() != LU_status::OK) { | ||||
|                 delete l; | ||||
|                 cs.m_factorization = nullptr; | ||||
|             } | ||||
|         } | ||||
|         if (cs.m_factorization == nullptr) { | ||||
|             if (numeric_traits<L>::precise()) | ||||
|                 init_factorization(cs.m_factorization, cs.m_A, cs.m_basis, settings()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool no_r_lu() const { | ||||
|         return m_r_solver.m_factorization == nullptr || m_r_solver.m_factorization->get_status() == LU_status::Degenerated;  | ||||
|     } | ||||
| 
 | ||||
|     void solve_on_signature_tableau(const lar_solution_signature & signature, const vector<unsigned> & changes_of_basis) { | ||||
|         r_basis_is_OK(); | ||||
|         lean_assert(settings().use_tableau()); | ||||
|         bool r = catch_up_in_lu_tableau(changes_of_basis, m_d_solver.m_basis_heading); | ||||
| 
 | ||||
|         if (!r) { // it is the case where m_d_solver gives a degenerated basis
 | ||||
|             prepare_solver_x_with_signature_tableau(signature); // still are going to use the signature partially
 | ||||
|             m_r_solver.find_feasible_solution(); | ||||
|             m_d_basis = m_r_basis; | ||||
|             m_d_heading = m_r_heading; | ||||
|             m_d_nbasis = m_r_nbasis; | ||||
|             delete m_d_solver.m_factorization; | ||||
|             m_d_solver.m_factorization = nullptr; | ||||
|         } else { | ||||
|             prepare_solver_x_with_signature_tableau(signature); | ||||
|             m_r_solver.start_tracing_basis_changes(); | ||||
|             m_r_solver.find_feasible_solution(); | ||||
|             if (settings().get_cancel_flag()) | ||||
|                 return; | ||||
|             m_r_solver.stop_tracing_basis_changes(); | ||||
|             // and now catch up in the double solver
 | ||||
|             lean_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); | ||||
|             catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); | ||||
|         } | ||||
|         lean_assert(r_basis_is_OK()); | ||||
|     } | ||||
| 
 | ||||
|     bool adjust_x_of_column(unsigned j) { | ||||
|         /*
 | ||||
|         if (m_r_solver.m_basis_heading[j] >= 0) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         if (m_r_solver.column_is_feasible(j)) { | ||||
|             return false; | ||||
|         } | ||||
| 
 | ||||
|         m_r_solver.snap_column_to_bound_tableau(j); | ||||
|         lean_assert(m_r_solver.column_is_feasible(j)); | ||||
|         m_r_solver.m_inf_set.erase(j); | ||||
|         */ | ||||
|         lean_assert(false); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     bool catch_up_in_lu_tableau(const vector<unsigned> & trace_of_basis_change, const vector<int> & basis_heading) { | ||||
|         lean_assert(r_basis_is_OK()); | ||||
|         // the queues of delayed indices
 | ||||
|         std::queue<unsigned> entr_q, leav_q; | ||||
|         for (unsigned i = 0; i < trace_of_basis_change.size(); i+= 2) { | ||||
|             unsigned entering = trace_of_basis_change[i]; | ||||
|             unsigned leaving = trace_of_basis_change[i+1]; | ||||
|             bool good_e = basis_heading[entering] >= 0 && m_r_solver.m_basis_heading[entering] < 0; | ||||
|             bool good_l = basis_heading[leaving] < 0 && m_r_solver.m_basis_heading[leaving] >= 0; | ||||
|             if (!good_e && !good_l) continue; | ||||
|             if (good_e && !good_l) { | ||||
|                 while (!leav_q.empty() && m_r_solver.m_basis_heading[leav_q.front()] < 0) | ||||
|                     leav_q.pop(); | ||||
|                 if (!leav_q.empty()) { | ||||
|                     leaving = leav_q.front(); | ||||
|                     leav_q.pop(); | ||||
|                 } else { | ||||
|                     entr_q.push(entering); | ||||
|                     continue; | ||||
|                 } | ||||
|             } else if (!good_e && good_l) { | ||||
|                 while (!entr_q.empty() && m_r_solver.m_basis_heading[entr_q.front()] >= 0) | ||||
|                     entr_q.pop(); | ||||
|                 if (!entr_q.empty()) { | ||||
|                     entering = entr_q.front(); | ||||
|                     entr_q.pop(); | ||||
|                 } else { | ||||
|                     leav_q.push(leaving); | ||||
|                     continue; | ||||
|                 } | ||||
|             } | ||||
|             lean_assert(m_r_solver.m_basis_heading[entering] < 0); | ||||
|             lean_assert(m_r_solver.m_basis_heading[leaving] >= 0); | ||||
|             m_r_solver.change_basis_unconditionally(entering, leaving); | ||||
|             if(!m_r_solver.pivot_column_tableau(entering, m_r_solver.m_basis_heading[entering])) { | ||||
| 				// unroll the last step
 | ||||
|                 m_r_solver.change_basis_unconditionally(leaving, entering); | ||||
| #ifdef LEAN_DEBUG | ||||
|                 bool t = | ||||
| #endif | ||||
|                     m_r_solver.pivot_column_tableau(leaving, m_r_solver.m_basis_heading[leaving]); | ||||
| #ifdef LEAN_DEBUG | ||||
|                 lean_assert(t); | ||||
| #endif  | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|         lean_assert(r_basis_is_OK()); | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     bool r_basis_is_OK() const { | ||||
| #ifdef LEAN_DEBUG | ||||
|         if (!m_r_solver.m_settings.use_tableau()) | ||||
|             return true; | ||||
|         for (unsigned j : m_r_solver.m_basis) { | ||||
|             lean_assert(m_r_solver.m_A.m_columns[j].size() == 1); | ||||
|             lean_assert(m_r_solver.m_A.get_val(m_r_solver.m_A.m_columns[j][0]) == one_of_type<mpq>()); | ||||
|         } | ||||
|         for (unsigned j =0; j < m_r_solver.m_basis_heading.size(); j++) { | ||||
|             if (m_r_solver.m_basis_heading[j] >= 0) continue; | ||||
|             if (m_r_solver.m_column_types[j] == column_type::fixed) continue; | ||||
|             lean_assert(static_cast<unsigned>(- m_r_solver.m_basis_heading[j] - 1) < m_r_solver.m_column_types.size()); | ||||
|             lean_assert( m_r_solver.m_basis_heading[j] <= -1); | ||||
|         } | ||||
| #endif | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     void solve_on_signature(const lar_solution_signature & signature, const vector<unsigned> & changes_of_basis) { | ||||
|         lean_assert(!settings().use_tableau()); | ||||
|         if (m_r_solver.m_factorization == nullptr) { | ||||
|             for (unsigned j = 0; j < changes_of_basis.size(); j+=2) { | ||||
|                 unsigned entering = changes_of_basis[j]; | ||||
|                 unsigned leaving = changes_of_basis[j + 1]; | ||||
|                 m_r_solver.change_basis_unconditionally(entering, leaving); | ||||
|             } | ||||
|             init_factorization(m_r_solver.m_factorization, m_r_A, m_r_basis, settings()); | ||||
|         } else { | ||||
|             catch_up_in_lu(changes_of_basis, m_d_solver.m_basis_heading, m_r_solver); | ||||
|         } | ||||
| 
 | ||||
|         if (no_r_lu()) { // it is the case where m_d_solver gives a degenerated basis, we need to roll back
 | ||||
|             std::cout << "no_r_lu" << std::endl; | ||||
|             catch_up_in_lu_in_reverse(changes_of_basis, m_r_solver); | ||||
|             m_r_solver.find_feasible_solution(); | ||||
|             m_d_basis = m_r_basis; | ||||
|             m_d_heading = m_r_heading; | ||||
|             m_d_nbasis = m_r_nbasis; | ||||
|             delete m_d_solver.m_factorization; | ||||
|             m_d_solver.m_factorization = nullptr; | ||||
|         } else { | ||||
|             prepare_solver_x_with_signature(signature, m_r_solver); | ||||
|             m_r_solver.start_tracing_basis_changes(); | ||||
|             m_r_solver.find_feasible_solution(); | ||||
|             if (settings().get_cancel_flag()) | ||||
|                 return; | ||||
|             m_r_solver.stop_tracing_basis_changes(); | ||||
|             // and now catch up in the double solver
 | ||||
|             lean_assert(m_r_solver.total_iterations() >= m_r_solver.m_trace_of_basis_change_vector.size() /2); | ||||
|             catch_up_in_lu(m_r_solver.m_trace_of_basis_change_vector, m_r_solver.m_basis_heading, m_d_solver); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void create_double_matrix(static_matrix<double, double> & A) { | ||||
|         for (unsigned i = 0; i < m_r_A.row_count(); i++) { | ||||
|             auto & row = m_r_A.m_rows[i]; | ||||
|             for (row_cell<mpq> & c : row) { | ||||
|                 A.set(i, c.m_j, c.get_val().get_double()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void fill_basis_d( | ||||
|                       vector<unsigned>& basis_d, | ||||
|                       vector<int>& heading_d, | ||||
|                       vector<unsigned>& nbasis_d){ | ||||
|         basis_d = m_r_basis; | ||||
|         heading_d = m_r_heading; | ||||
|         nbasis_d = m_r_nbasis; | ||||
|     } | ||||
| 
 | ||||
|     template <typename L, typename K> | ||||
|     void extract_signature_from_lp_core_solver(const lp_primal_core_solver<L, K> & solver, lar_solution_signature & signature) { | ||||
|         signature.clear(); | ||||
|         lean_assert(signature.size() == 0); | ||||
|         for (unsigned j = 0; j < solver.m_basis_heading.size(); j++) { | ||||
|             if (solver.m_basis_heading[j] < 0) { | ||||
|                 signature[j] = solver.get_non_basic_column_value_position(j); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void get_bounds_for_double_solver() { | ||||
|         unsigned n = m_n(); | ||||
|         m_d_low_bounds.resize(n); | ||||
|         m_d_upper_bounds.resize(n); | ||||
|         double delta = find_delta_for_strict_boxed_bounds().get_double(); | ||||
|         if (delta > 0.000001) | ||||
|             delta = 0.000001; | ||||
|         for (unsigned j = 0; j < n; j++) { | ||||
|             if (low_bound_is_set(j)) { | ||||
|                 const auto & lb = m_r_solver.m_low_bounds[j]; | ||||
|                 m_d_low_bounds[j] = lb.x.get_double() + delta * lb.y.get_double(); | ||||
|             } | ||||
|             if (upper_bound_is_set(j)) { | ||||
|                 const auto & ub = m_r_solver.m_upper_bounds[j]; | ||||
|                 m_d_upper_bounds[j] = ub.x.get_double() + delta * ub.y.get_double(); | ||||
|                 lean_assert(!low_bound_is_set(j) || (m_d_upper_bounds[j] >= m_d_low_bounds[j])); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void scale_problem_for_doubles( | ||||
|                         static_matrix<double, double>& A,         | ||||
|                         vector<double> & low_bounds, | ||||
|                         vector<double> & upper_bounds) { | ||||
|         vector<double> column_scale_vector; | ||||
|         vector<double> right_side_vector(A.column_count()); | ||||
|         settings().reps_in_scaler = 5; | ||||
|         scaler<double, double > scaler(right_side_vector, | ||||
|                                        A, | ||||
|                                        settings().scaling_minimum, | ||||
|                                        settings().scaling_maximum, | ||||
|                                        column_scale_vector, | ||||
|                                        settings()); | ||||
|         if (! scaler.scale()) { | ||||
|             // the scale did not succeed, unscaling
 | ||||
|             A.clear(); | ||||
|             create_double_matrix(A); | ||||
|         } else { | ||||
|             for (unsigned j = 0; j < A.column_count(); j++) { | ||||
|                 if (m_r_solver.column_has_upper_bound(j)) { | ||||
|                     upper_bounds[j] /= column_scale_vector[j]; | ||||
|                 } | ||||
|                 if (m_r_solver.column_has_low_bound(j)) { | ||||
|                     low_bounds[j] /= column_scale_vector[j]; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|          | ||||
|     } | ||||
|     // returns the trace of basis changes
 | ||||
|     vector<unsigned> find_solution_signature_with_doubles(lar_solution_signature & signature) { | ||||
|         if (m_d_solver.m_factorization == nullptr || m_d_solver.m_factorization->get_status() != LU_status::OK) { | ||||
|             vector<unsigned> ret; | ||||
|             return ret; | ||||
|         } | ||||
|         get_bounds_for_double_solver(); | ||||
| 
 | ||||
|         extract_signature_from_lp_core_solver(m_r_solver, signature); | ||||
|         prepare_solver_x_with_signature(signature, m_d_solver); | ||||
|         m_d_solver.start_tracing_basis_changes(); | ||||
|         m_d_solver.find_feasible_solution(); | ||||
|         if (settings().get_cancel_flag()) | ||||
|             return vector<unsigned>(); | ||||
|              | ||||
|         m_d_solver.stop_tracing_basis_changes(); | ||||
|         extract_signature_from_lp_core_solver(m_d_solver, signature); | ||||
|         return m_d_solver.m_trace_of_basis_change_vector; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     bool low_bound_is_set(unsigned j) const { | ||||
|         switch (m_column_types[j]) { | ||||
|         case column_type::free_column: | ||||
|         case column_type::upper_bound: | ||||
|             return false; | ||||
|         case column_type::low_bound: | ||||
|         case column_type::boxed: | ||||
|         case column_type::fixed: | ||||
|             return true; | ||||
|         default: | ||||
|             lean_assert(false); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|      | ||||
|     bool upper_bound_is_set(unsigned j) const { | ||||
|         switch (m_column_types[j]) { | ||||
|         case column_type::free_column: | ||||
|         case column_type::low_bound: | ||||
|             return false; | ||||
|         case column_type::upper_bound: | ||||
|         case column_type::boxed: | ||||
|         case column_type::fixed: | ||||
|             return true; | ||||
|         default: | ||||
|             lean_assert(false); | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     void update_delta(mpq& delta, numeric_pair<mpq> const& l, numeric_pair<mpq> const& u) const { | ||||
|         lean_assert(l <= u); | ||||
|         if (l.x < u.x && l.y > u.y) { | ||||
|             mpq delta1 = (u.x - l.x) / (l.y - u.y); | ||||
|             if (delta1 < delta) { | ||||
|                 delta = delta1; | ||||
|             } | ||||
|         } | ||||
|         lean_assert(l.x + delta * l.y <= u.x + delta * u.y); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     mpq find_delta_for_strict_boxed_bounds() const{ | ||||
|         mpq delta = numeric_traits<mpq>::one(); | ||||
|         for (unsigned j = 0; j < m_r_A.column_count(); j++ ) { | ||||
|             if (m_column_types()[j] != column_type::boxed) | ||||
|                 continue; | ||||
|             update_delta(delta, m_r_low_bounds[j], m_r_upper_bounds[j]); | ||||
| 
 | ||||
|         } | ||||
|         return delta; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     mpq find_delta_for_strict_bounds() const{ | ||||
|         mpq delta = numeric_traits<mpq>::one(); | ||||
|         for (unsigned j = 0; j < m_r_A.column_count(); j++ ) { | ||||
|             if (low_bound_is_set(j)) | ||||
|                 update_delta(delta, m_r_low_bounds[j], m_r_x[j]); | ||||
|             if (upper_bound_is_set(j)) | ||||
|                 update_delta(delta, m_r_x[j], m_r_upper_bounds[j]); | ||||
|         } | ||||
|         return delta; | ||||
|     } | ||||
| 
 | ||||
|     void init_column_row_nz_for_r_solver() { | ||||
|         m_r_solver.init_column_row_non_zeroes(); | ||||
|     } | ||||
| 
 | ||||
|     linear_combination_iterator<mpq> * get_column_iterator(unsigned j) { | ||||
|         if (settings().use_tableau()) { | ||||
|             return new iterator_on_column<mpq, numeric_pair<mpq>>(m_r_solver.m_A.m_columns[j], m_r_solver.m_A); | ||||
|         } else { | ||||
|             m_r_solver.solve_Bd(j); | ||||
|             return new iterator_on_indexed_vector<mpq>(m_r_solver.m_ed); | ||||
|         } | ||||
|     } | ||||
|      | ||||
| }; | ||||
| } | ||||
							
								
								
									
										292
									
								
								src/util/lp/lar_core_solver.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										292
									
								
								src/util/lp/lar_core_solver.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,292 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <string> | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/lar_core_solver.h" | ||||
| #include "util/lp/lar_solution_signature.h" | ||||
| namespace lean { | ||||
| lar_core_solver::lar_core_solver( | ||||
|                                  lp_settings & settings, | ||||
|                                  const column_namer & column_names | ||||
| ): | ||||
|     m_r_solver(m_r_A, | ||||
|                     m_right_sides_dummy, | ||||
|                     m_r_x, | ||||
|                     m_r_basis, | ||||
|                     m_r_nbasis, | ||||
|                     m_r_heading, | ||||
|                     m_costs_dummy, | ||||
|                     m_column_types(), | ||||
|                     m_r_low_bounds(), | ||||
|                     m_r_upper_bounds(), | ||||
|                     settings, | ||||
|                column_names), | ||||
|     m_d_solver(m_d_A, | ||||
|                     m_d_right_sides_dummy, | ||||
|                     m_d_x, | ||||
|                     m_d_basis, | ||||
|                     m_d_nbasis, | ||||
|                     m_d_heading, | ||||
|                     m_d_costs_dummy, | ||||
|                     m_column_types(), | ||||
|                     m_d_low_bounds, | ||||
|                     m_d_upper_bounds, | ||||
|                     settings, | ||||
|                     column_names){} | ||||
| 
 | ||||
| void lar_core_solver::init_costs(bool first_time) { | ||||
|     lean_assert(false); // should not be called
 | ||||
|     // lean_assert(this->m_x.size() >= this->m_n());
 | ||||
|     // lean_assert(this->m_column_types.size() >= this->m_n());
 | ||||
|     // if (first_time)
 | ||||
|     //     this->m_costs.resize(this->m_n());
 | ||||
|     // X inf = this->m_infeasibility;
 | ||||
|     // this->m_infeasibility = zero_of_type<X>();
 | ||||
|     // for (unsigned j = this->m_n(); j--;)
 | ||||
|     //     init_cost_for_column(j);
 | ||||
|     // if (!(first_time || inf >= this->m_infeasibility)) {
 | ||||
|     //     LP_OUT(this->m_settings, "iter = " << this->total_iterations() << std::endl);
 | ||||
|     //     LP_OUT(this->m_settings, "inf was " << T_to_string(inf) << " and now " << T_to_string(this->m_infeasibility) << std::endl);
 | ||||
|     //     lean_assert(false);
 | ||||
|     // }
 | ||||
|     // if (inf == this->m_infeasibility)
 | ||||
|     //     this->m_iters_with_no_cost_growing++;
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void lar_core_solver::init_cost_for_column(unsigned j) { | ||||
|     /*
 | ||||
|     // If j is a breakpoint column, then we set the cost zero.
 | ||||
|     // When anylyzing an entering column candidate we update the cost of the breakpoints columns to get the left or the right derivative if the infeasibility function
 | ||||
|     const numeric_pair<mpq> & 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_types[j]) { | ||||
|     case fixed: | ||||
|     case column_type::boxed: | ||||
|         if (x > this->m_upper_bounds[j]) { | ||||
|             this->m_costs[j] = 1; | ||||
|             this->m_infeasibility += x - this->m_upper_bounds[j]; | ||||
|         } else if (x < this->m_low_bounds[j]) { | ||||
|             this->m_infeasibility += this->m_low_bounds[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_bounds[j]) { | ||||
|             this->m_costs[j] = -1; | ||||
|             this->m_infeasibility += this->m_low_bounds[j] - x; | ||||
|         } else { | ||||
|             this->m_costs[j] = numeric_traits<T>::zero(); | ||||
|         } | ||||
|         break; | ||||
|     case upper_bound: | ||||
|         if (x > this->m_upper_bounds[j]) { | ||||
|             this->m_costs[j] = 1; | ||||
|             this->m_infeasibility += x - this->m_upper_bounds[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; | ||||
|         }*/ | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // returns m_sign_of_alpha_r
 | ||||
| int lar_core_solver::column_is_out_of_bounds(unsigned j) { | ||||
|     /*
 | ||||
|     switch (this->m_column_type[j]) { | ||||
|     case fixed: | ||||
|     case column_type::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; | ||||
|         }*/ | ||||
|     lean_assert(false); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void lar_core_solver::calculate_pivot_row(unsigned i) { | ||||
|     lean_assert(!m_r_solver.use_tableau()); | ||||
|     lean_assert(m_r_solver.m_pivot_row.is_OK()); | ||||
|     m_r_solver.m_pivot_row_of_B_1.clear(); | ||||
|     m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m()); | ||||
|     m_r_solver.m_pivot_row.clear(); | ||||
|     m_r_solver.m_pivot_row.resize(m_r_solver.m_n()); | ||||
|     if (m_r_solver.m_settings.use_tableau()) { | ||||
|         unsigned basis_j = m_r_solver.m_basis[i]; | ||||
|         for (auto & c : m_r_solver.m_A.m_rows[i]) { | ||||
|             if (c.m_j != basis_j) | ||||
|                 m_r_solver.m_pivot_row.set_value(c.get_val(), c.m_j); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     m_r_solver.calculate_pivot_row_of_B_1(i); | ||||
|     m_r_solver.calculate_pivot_row_when_pivot_row_of_B1_is_ready(i); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|  void lar_core_solver::prefix_r() { | ||||
|      if (!m_r_solver.m_settings.use_tableau()) { | ||||
|         m_r_solver.m_copy_of_xB.resize(m_r_solver.m_n()); | ||||
|         m_r_solver.m_ed.resize(m_r_solver.m_m()); | ||||
|         m_r_solver.m_pivot_row.resize(m_r_solver.m_n());  | ||||
|         m_r_solver.m_pivot_row_of_B_1.resize(m_r_solver.m_m()); | ||||
|         m_r_solver.m_w.resize(m_r_solver.m_m()); | ||||
|         m_r_solver.m_y.resize(m_r_solver.m_m()); | ||||
|         m_r_solver.m_rows_nz.resize(m_r_solver.m_m(), 0); | ||||
|         m_r_solver.m_columns_nz.resize(m_r_solver.m_n(), 0);  | ||||
|         init_column_row_nz_for_r_solver(); | ||||
|     } | ||||
| 
 | ||||
|     m_r_solver.m_b.resize(m_r_solver.m_m()); | ||||
|     if (m_r_solver.m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { | ||||
|         if(m_r_solver.m_settings.use_breakpoints_in_feasibility_search) | ||||
|             m_r_solver.m_breakpoint_indices_queue.resize(m_r_solver.m_n()); | ||||
|         m_r_solver.m_costs.resize(m_r_solver.m_n()); | ||||
|         m_r_solver.m_d.resize(m_r_solver.m_n()); | ||||
|         m_r_solver.m_using_infeas_costs = true; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|  void lar_core_solver::prefix_d() { | ||||
|     m_d_solver.m_b.resize(m_d_solver.m_m()); | ||||
|     m_d_solver.m_breakpoint_indices_queue.resize(m_d_solver.m_n()); | ||||
|     m_d_solver.m_copy_of_xB.resize(m_d_solver.m_n()); | ||||
|     m_d_solver.m_costs.resize(m_d_solver.m_n()); | ||||
|     m_d_solver.m_d.resize(m_d_solver.m_n()); | ||||
|     m_d_solver.m_ed.resize(m_d_solver.m_m()); | ||||
|     m_d_solver.m_pivot_row.resize(m_d_solver.m_n()); | ||||
|     m_d_solver.m_pivot_row_of_B_1.resize(m_d_solver.m_m()); | ||||
|     m_d_solver.m_w.resize(m_d_solver.m_m()); | ||||
|     m_d_solver.m_y.resize(m_d_solver.m_m()); | ||||
|     m_d_solver.m_steepest_edge_coefficients.resize(m_d_solver.m_n()); | ||||
|     m_d_solver.m_column_norms.clear(); | ||||
|     m_d_solver.m_column_norms.resize(m_d_solver.m_n(), 2); | ||||
|     m_d_solver.m_inf_set.clear(); | ||||
|     m_d_solver.m_inf_set.resize(m_d_solver.m_n()); | ||||
| } | ||||
| 
 | ||||
| void lar_core_solver::fill_not_improvable_zero_sum_from_inf_row() { | ||||
|     lean_assert(m_r_solver.A_mult_x_is_off() == false); | ||||
|     unsigned bj = m_r_basis[m_r_solver.m_inf_row_index_for_tableau]; | ||||
|     m_infeasible_sum_sign =  m_r_solver.inf_sign_of_column(bj); | ||||
|     m_infeasible_linear_combination.clear(); | ||||
|     for (auto & rc : m_r_solver.m_A.m_rows[m_r_solver.m_inf_row_index_for_tableau]) { | ||||
|         m_infeasible_linear_combination.push_back(std::make_pair( rc.get_val(), rc.m_j)); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void lar_core_solver::fill_not_improvable_zero_sum() { | ||||
|     if (m_r_solver.m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) { | ||||
|         fill_not_improvable_zero_sum_from_inf_row(); | ||||
|         return; | ||||
|     } | ||||
|     //  reusing the existing mechanism for row_feasibility_loop
 | ||||
|     m_infeasible_sum_sign = m_r_solver.m_settings.use_breakpoints_in_feasibility_search? -1 : 1; | ||||
|     m_infeasible_linear_combination.clear(); | ||||
|     for (auto j : m_r_solver.m_basis) { | ||||
|         const mpq & cost_j = m_r_solver.m_costs[j]; | ||||
|         if (!numeric_traits<mpq>::is_zero(cost_j)) { | ||||
|             m_infeasible_linear_combination.push_back(std::make_pair(cost_j, j)); | ||||
|         } | ||||
|     } | ||||
|     // m_costs are expressed by m_d ( additional costs), substructing the latter gives 0
 | ||||
|     for (unsigned j = 0; j < m_r_solver.m_n(); j++) { | ||||
|         if (m_r_solver.m_basis_heading[j] >= 0) continue; | ||||
|         const mpq & d_j = m_r_solver.m_d[j]; | ||||
|         if (!numeric_traits<mpq>::is_zero(d_j)) { | ||||
|             m_infeasible_linear_combination.push_back(std::make_pair(-d_j, j)); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void lar_core_solver::solve() { | ||||
|     lean_assert(m_r_solver.non_basic_columns_are_set_correctly()); | ||||
|     lean_assert(m_r_solver.inf_set_is_correct()); | ||||
|     if (m_r_solver.current_x_is_feasible() && m_r_solver.m_look_for_feasible_solution_only) { | ||||
|         m_r_solver.set_status(OPTIMAL); | ||||
|         return; | ||||
|     } | ||||
|     ++settings().st().m_need_to_solve_inf; | ||||
|     lean_assert(!m_r_solver.A_mult_x_is_off()); | ||||
|     lean_assert((!settings().use_tableau()) || r_basis_is_OK()); | ||||
|     if (need_to_presolve_with_double_solver()) { | ||||
|         prefix_d(); | ||||
|         lar_solution_signature solution_signature; | ||||
|         vector<unsigned> changes_of_basis = find_solution_signature_with_doubles(solution_signature); | ||||
|         if (m_d_solver.get_status() == TIME_EXHAUSTED) { | ||||
|             m_r_solver.set_status(TIME_EXHAUSTED); | ||||
|             return; | ||||
|         } | ||||
|         if (settings().use_tableau()) | ||||
|             solve_on_signature_tableau(solution_signature, changes_of_basis); | ||||
|         else  | ||||
|             solve_on_signature(solution_signature, changes_of_basis); | ||||
|         lean_assert(!settings().use_tableau() || r_basis_is_OK()); | ||||
|     } else { | ||||
|         if (!settings().use_tableau()) { | ||||
|             bool snapped = m_r_solver.snap_non_basic_x_to_bound();    | ||||
|             lean_assert(m_r_solver.non_basic_columns_are_set_correctly()); | ||||
|             if (snapped) | ||||
|                 m_r_solver.solve_Ax_eq_b(); | ||||
|         } | ||||
|         if (m_r_solver.m_look_for_feasible_solution_only) | ||||
|             m_r_solver.find_feasible_solution(); | ||||
|         else | ||||
|             m_r_solver.solve(); | ||||
|         lean_assert(!settings().use_tableau() || r_basis_is_OK()); | ||||
|     } | ||||
|     if (m_r_solver.get_status() == INFEASIBLE) { | ||||
|         fill_not_improvable_zero_sum(); | ||||
|     } else if (m_r_solver.get_status() != UNBOUNDED) { | ||||
|         m_r_solver.set_status(OPTIMAL); | ||||
|     } | ||||
|     lean_assert(r_basis_is_OK()); | ||||
|     lean_assert(m_r_solver.non_basic_columns_are_set_correctly()); | ||||
|     lean_assert(m_r_solver.inf_set_is_correct()); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										10
									
								
								src/util/lp/lar_core_solver_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/util/lp/lar_core_solver_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <utility> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include "util/vector.h" | ||||
| #include <functional> | ||||
| #include "util/lp/lar_core_solver.hpp" | ||||
							
								
								
									
										13
									
								
								src/util/lp/lar_solution_signature.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								src/util/lp/lar_solution_signature.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,13 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include "util/debug.h" | ||||
| #include "util/lp/lp_settings.h" | ||||
| #include <unordered_map> | ||||
| namespace lean { | ||||
| typedef std::unordered_map<unsigned, non_basic_column_value_position> lar_solution_signature; | ||||
| } | ||||
							
								
								
									
										2135
									
								
								src/util/lp/lar_solver.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										2135
									
								
								src/util/lp/lar_solver.h
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										64
									
								
								src/util/lp/lar_term.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										64
									
								
								src/util/lp/lar_term.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,64 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/lp/indexed_vector.h" | ||||
| namespace lean { | ||||
| struct lar_term { | ||||
|     // the term evaluates to sum of m_coeffs + m_v
 | ||||
|     std::unordered_map<unsigned, mpq> m_coeffs; | ||||
|     mpq m_v; | ||||
|     lar_term() {} | ||||
|     void add_to_map(unsigned j, const mpq& c) { | ||||
|         auto it = m_coeffs.find(j); | ||||
|         if (it == m_coeffs.end()) { | ||||
|             m_coeffs.emplace(j, c); | ||||
|         } else { | ||||
|             it->second += c; | ||||
|             if (is_zero(it->second)) | ||||
|                 m_coeffs.erase(it); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     unsigned size() const { return static_cast<unsigned>(m_coeffs.size()); } | ||||
|      | ||||
|     const std::unordered_map<unsigned, mpq> & coeffs() const { | ||||
|         return m_coeffs; | ||||
|     } | ||||
|      | ||||
|     lar_term(const vector<std::pair<mpq, unsigned>>& coeffs, | ||||
|              const mpq & v) : m_v(v) { | ||||
|         for (const auto & p : coeffs) { | ||||
|             add_to_map(p.second, p.first); | ||||
|         } | ||||
|     } | ||||
|     bool operator==(const lar_term & a) const {  return false; } // take care not to create identical terms
 | ||||
|     bool operator!=(const lar_term & a) const {  return ! (*this == a);} | ||||
|     // some terms get used in add constraint
 | ||||
|     // it is the same as the offset in the m_constraints
 | ||||
| 
 | ||||
|     vector<std::pair<mpq, unsigned>> coeffs_as_vector() const { | ||||
|         vector<std::pair<mpq, unsigned>> ret; | ||||
|         for (const auto & p :  m_coeffs) { | ||||
|             ret.push_back(std::make_pair(p.second, p.first)); | ||||
|         } | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|     // j is the basic variable to substitute
 | ||||
|     void subst(unsigned j, indexed_vector<mpq> & li) { | ||||
|         auto it = m_coeffs.find(j); | ||||
|         if (it == m_coeffs.end()) return; | ||||
|         const mpq & b = it->second; | ||||
|         for (unsigned it_j :li.m_index) { | ||||
|             add_to_map(it_j, - b * li.m_data[it_j]); | ||||
|         } | ||||
|         m_coeffs.erase(it); | ||||
|     } | ||||
|      | ||||
|     bool contains(unsigned j) const { | ||||
|         return m_coeffs.find(j) != m_coeffs.end(); | ||||
|     } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										47
									
								
								src/util/lp/linear_combination_iterator.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										47
									
								
								src/util/lp/linear_combination_iterator.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,47 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| namespace lean { | ||||
| template <typename T> | ||||
| struct linear_combination_iterator { | ||||
|     virtual bool next(T & a, unsigned & i) = 0; | ||||
|     virtual bool next(unsigned & i) = 0; | ||||
|     virtual void reset() = 0; | ||||
|     virtual linear_combination_iterator * clone() = 0; | ||||
|     virtual ~linear_combination_iterator(){} | ||||
|     virtual unsigned size() const = 0; | ||||
| }; | ||||
| template <typename T> | ||||
| struct linear_combination_iterator_on_vector : linear_combination_iterator<T> { | ||||
|     vector<std::pair<T, unsigned>> & m_vector; | ||||
|     int m_offset = 0; | ||||
|     bool next(T & a, unsigned & i) { | ||||
|         if(m_offset >= m_vector.size()) | ||||
|             return false; | ||||
|         auto & p = m_vector[m_offset]; | ||||
|         a = p.first; | ||||
|         i = p.second; | ||||
|         m_offset++; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     bool next(unsigned & i) { | ||||
|         if(m_offset >= m_vector.size()) | ||||
|             return false; | ||||
|         auto & p = m_vector[m_offset]; | ||||
|         i = p.second; | ||||
|         m_offset++; | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     void reset() {m_offset = 0;} | ||||
|     linear_combination_iterator<T> * clone() { | ||||
|         return new linear_combination_iterator_on_vector(m_vector); | ||||
|     } | ||||
|     linear_combination_iterator_on_vector(vector<std::pair<T, unsigned>> & vec): m_vector(vec) {} | ||||
|     unsigned size() const { return m_vector.size(); } | ||||
| }; | ||||
| 
 | ||||
| } | ||||
							
								
								
									
										683
									
								
								src/util/lp/lp_core_solver_base.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										683
									
								
								src/util/lp/lp_core_solver_base.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,683 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include <set> | ||||
| #include "util/vector.h" | ||||
| #include <string> | ||||
| #include "util/lp/lp_utils.h" | ||||
| #include "util/lp/core_solver_pretty_printer.h" | ||||
| #include "util/lp/numeric_pair.h" | ||||
| #include "util/lp/static_matrix.h" | ||||
| #include "util/lp/lu.h" | ||||
| #include "util/lp/permutation_matrix.h" | ||||
| #include "util/lp/column_namer.h" | ||||
| namespace lean { | ||||
| 
 | ||||
| template <typename T, typename X> // X represents the type of the x variable and the bounds
 | ||||
| class lp_core_solver_base {     | ||||
|     unsigned m_total_iterations = 0; | ||||
|     unsigned inc_total_iterations() { ++m_settings.st().m_total_iterations; return m_total_iterations++; } | ||||
| private: | ||||
|     lp_status m_status; | ||||
| public: | ||||
|     bool current_x_is_feasible() const { return m_inf_set.size() == 0; } | ||||
|     bool current_x_is_infeasible() const { return m_inf_set.size() != 0; } | ||||
|     int_set m_inf_set; | ||||
|     bool m_using_infeas_costs = false; | ||||
| 
 | ||||
| 
 | ||||
|     vector<unsigned> m_columns_nz; // m_columns_nz[i] keeps an approximate value of non zeroes the i-th column
 | ||||
|     vector<unsigned> m_rows_nz; // m_rows_nz[i] keeps an approximate value of non zeroes in the i-th row
 | ||||
|     indexed_vector<T> m_pivot_row_of_B_1;  // the pivot row of the reverse of B
 | ||||
|     indexed_vector<T> m_pivot_row; // this is the real pivot row of the simplex tableu
 | ||||
|     static_matrix<T, X> & m_A; // the matrix A
 | ||||
|     vector<X> & m_b; // the right side
 | ||||
|     vector<unsigned> & m_basis; | ||||
|     vector<unsigned>& m_nbasis; | ||||
|     vector<int>& m_basis_heading; | ||||
|     vector<X> & m_x; // a feasible solution, the fist time set in the constructor
 | ||||
|     vector<T> & m_costs; | ||||
|     lp_settings & m_settings; | ||||
|     vector<T> m_y; // the buffer for yB = cb
 | ||||
|     // a device that is able to solve Bx=c, xB=d, and change the basis
 | ||||
|     lu<T, X> * m_factorization = nullptr; | ||||
|     const column_namer & m_column_names; | ||||
|     indexed_vector<T> m_w; // the vector featuring in 24.3 of the Chvatal book
 | ||||
|     vector<T> m_d; // the vector of reduced costs
 | ||||
|     indexed_vector<T> m_ed; // the solution of B*m_ed = a
 | ||||
|     unsigned m_iters_with_no_cost_growing = 0; | ||||
|     const vector<column_type> & m_column_types; | ||||
|     const vector<X> & m_low_bounds; | ||||
|     const vector<X> & m_upper_bounds; | ||||
|     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_basis_sort_counter = 0; | ||||
|     vector<T> m_steepest_edge_coefficients; | ||||
|     vector<unsigned> m_trace_of_basis_change_vector; // the even positions are entering, the odd positions are leaving
 | ||||
|     bool m_tracing_basis_changes = false; | ||||
|     int_set* m_pivoted_rows = nullptr; | ||||
|     bool m_look_for_feasible_solution_only = false; | ||||
|     void start_tracing_basis_changes() { | ||||
|         m_trace_of_basis_change_vector.resize(0); | ||||
|         m_tracing_basis_changes = true; | ||||
|     } | ||||
|          | ||||
|     void stop_tracing_basis_changes() { | ||||
|         m_tracing_basis_changes = false; | ||||
|     } | ||||
| 
 | ||||
|     void trace_basis_change(unsigned entering, unsigned leaving) { | ||||
|         unsigned size = m_trace_of_basis_change_vector.size(); | ||||
|         if (size >= 2 && m_trace_of_basis_change_vector[size-2] == leaving | ||||
|                 &&  m_trace_of_basis_change_vector[size -1] == entering) { | ||||
|             m_trace_of_basis_change_vector.pop_back(); | ||||
|             m_trace_of_basis_change_vector.pop_back(); | ||||
|         } else { | ||||
|             m_trace_of_basis_change_vector.push_back(entering); | ||||
|             m_trace_of_basis_change_vector.push_back(leaving); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     unsigned m_m() const { return m_A.row_count(); } // 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() const { return m_A.column_count(); } // the number of columns in the matrix m_A
 | ||||
| 
 | ||||
|     lp_core_solver_base(static_matrix<T, X> & A, | ||||
|                         vector<X> & b, // the right side vector
 | ||||
|                         vector<unsigned> & basis, | ||||
|                         vector<unsigned> & nbasis, | ||||
|                         vector<int> & heading, | ||||
|                         vector<X> & x, | ||||
|                         vector<T> & costs, | ||||
|                         lp_settings & settings, | ||||
|                         const column_namer& column_names, | ||||
|                         const vector<column_type> & column_types, | ||||
|                         const vector<X> & low_bound_values, | ||||
|                         const vector<X> & upper_bound_values); | ||||
| 
 | ||||
|     void allocate_basis_heading(); | ||||
|     void init(); | ||||
| 
 | ||||
|     virtual ~lp_core_solver_base() { | ||||
|         if (m_factorization != nullptr) | ||||
|             delete m_factorization; | ||||
|      } | ||||
| 
 | ||||
|     vector<unsigned> & non_basis() { | ||||
|         return m_nbasis; | ||||
|     } | ||||
| 
 | ||||
|     const vector<unsigned> & non_basis() const { return m_nbasis; } | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|     void set_status(lp_status status) { | ||||
|         m_status = status; | ||||
|     } | ||||
|     lp_status get_status() const{ | ||||
|         return m_status; | ||||
|     } | ||||
| 
 | ||||
|     void fill_cb(T * y); | ||||
| 
 | ||||
|     void fill_cb(vector<T> & y); | ||||
| 
 | ||||
|     void solve_yB(vector<T> & y); | ||||
| 
 | ||||
|     void solve_Bd(unsigned entering); | ||||
| 
 | ||||
|     void solve_Bd(unsigned entering, indexed_vector<T> & column); | ||||
| 
 | ||||
|     void pretty_print(std::ostream & out); | ||||
| 
 | ||||
|     void save_state(T * w_buffer, T * d_buffer); | ||||
| 
 | ||||
|     void restore_state(T * w_buffer, T * d_buffer); | ||||
| 
 | ||||
|     X get_cost() { | ||||
|         return dot_product(m_costs, m_x); | ||||
|     } | ||||
| 
 | ||||
|     void copy_m_w(T * buffer); | ||||
| 
 | ||||
|     void restore_m_w(T * buffer); | ||||
| 
 | ||||
|     // needed for debugging
 | ||||
|     void copy_m_ed(T * buffer); | ||||
| 
 | ||||
|     void restore_m_ed(T * buffer); | ||||
| 
 | ||||
|     bool A_mult_x_is_off() const; | ||||
| 
 | ||||
|     bool A_mult_x_is_off_on_index(const vector<unsigned> & index) const; | ||||
|     // from page 182 of Istvan Maros's book
 | ||||
|     void calculate_pivot_row_of_B_1(unsigned pivot_row); | ||||
| 
 | ||||
|     void calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned pivot_row); | ||||
| 
 | ||||
|     void update_x(unsigned entering, const X & delta); | ||||
| 
 | ||||
|     const T & get_var_value(unsigned j) const { | ||||
|         return m_x[j]; | ||||
|     } | ||||
| 
 | ||||
|     void print_statistics(char const* str, X cost, std::ostream & message_stream); | ||||
| 
 | ||||
|     bool print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream & message_stream); | ||||
| 
 | ||||
|     bool print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const* str, std::ostream & message_stream); | ||||
| 
 | ||||
|     bool print_statistics_with_cost_and_check_that_the_time_is_over(X cost, std::ostream & message_stream); | ||||
| 
 | ||||
|     unsigned total_iterations() const { return m_total_iterations; } | ||||
| 
 | ||||
|     void set_total_iterations(unsigned s) { m_total_iterations = s; } | ||||
| 
 | ||||
|     void set_non_basic_x_to_correct_bounds(); | ||||
| 
 | ||||
|     bool at_bound(const X &x, const X & bound) const { | ||||
|         return !below_bound(x, bound) && !above_bound(x, bound); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     bool need_to_pivot_to_basis_tableau() const { | ||||
|         lean_assert(m_A.is_correct()); | ||||
|         unsigned m = m_A.row_count(); | ||||
|         for (unsigned i = 0; i < m; i++) { | ||||
|             unsigned bj = m_basis[i]; | ||||
|             lean_assert(m_A.m_columns[bj].size() > 0); | ||||
|             if (m_A.m_columns[bj].size() > 1 || m_A.get_val(m_A.m_columns[bj][0]) != one_of_type<mpq>()) return true; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
|      | ||||
|     bool reduced_costs_are_correct_tableau() const { | ||||
|         if (m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) | ||||
|             return true; | ||||
|         lean_assert(m_A.is_correct()); | ||||
|         if (m_using_infeas_costs) { | ||||
|             if (infeasibility_costs_are_correct() == false) { | ||||
|                 std::cout << "infeasibility_costs_are_correct() does not hold" << std::endl; | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|              | ||||
|         unsigned n = m_A.column_count(); | ||||
|         for (unsigned j = 0; j < n; j++) { | ||||
|             if (m_basis_heading[j] >= 0) { | ||||
|                 if (!is_zero(m_d[j])) { | ||||
|                      | ||||
|                     std::cout << "case a\n"; | ||||
|                     print_column_info(j, std::cout); | ||||
|                     return false; | ||||
|                 } | ||||
|             } else { | ||||
|                 auto d = m_costs[j]; | ||||
|                 for (auto & cc : this->m_A.m_columns[j]) { | ||||
|                     d -= this->m_costs[this->m_basis[cc.m_i]] * this->m_A.get_val(cc); | ||||
|                 } | ||||
|                 if (m_d[j] != d) { | ||||
|                     std::cout << "case b\n"; | ||||
|                     print_column_info(j, std::cout); | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     bool below_bound(const X & x, const X & bound) const { | ||||
|         if (precise()) 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()) return x > bound; | ||||
|         return above_bound_numeric<X>(x, bound, m_settings.primal_feasibility_tolerance); | ||||
|     } | ||||
| 
 | ||||
|     bool x_below_low_bound(unsigned p) const { | ||||
|         return below_bound(m_x[p], m_low_bounds[p]); | ||||
|     } | ||||
| 
 | ||||
|     bool infeasibility_costs_are_correct() const; | ||||
|     bool infeasibility_cost_is_correct_for_column(unsigned j) const; | ||||
|      | ||||
|     bool x_above_low_bound(unsigned p) const { | ||||
|         return above_bound(m_x[p], m_low_bounds[p]); | ||||
|     } | ||||
| 
 | ||||
|     bool x_below_upper_bound(unsigned p) const { | ||||
|         return below_bound(m_x[p], m_upper_bounds[p]); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     bool x_above_upper_bound(unsigned p) const { | ||||
|         return above_bound(m_x[p], m_upper_bounds[p]); | ||||
|     } | ||||
|     bool x_is_at_low_bound(unsigned j) const { | ||||
|         return at_bound(m_x[j], m_low_bounds[j]); | ||||
|     } | ||||
|     bool x_is_at_upper_bound(unsigned j) const { | ||||
|         return at_bound(m_x[j], m_upper_bounds[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_feasible(unsigned j) const; | ||||
| 
 | ||||
|     bool calc_current_x_is_feasible_include_non_basis() const; | ||||
| 
 | ||||
|     bool inf_set_is_correct() const; | ||||
|      | ||||
|     bool column_is_dual_feasible(unsigned j) const; | ||||
| 
 | ||||
|     bool d_is_not_negative(unsigned j) const; | ||||
| 
 | ||||
|     bool d_is_not_positive(unsigned j) const; | ||||
| 
 | ||||
| 
 | ||||
|     bool time_is_over(); | ||||
| 
 | ||||
|     void rs_minus_Anx(vector<X> & rs); | ||||
| 
 | ||||
|     bool find_x_by_solving(); | ||||
| 
 | ||||
|     bool update_basis_and_x(int entering, int leaving, X const & tt); | ||||
| 
 | ||||
|     bool basis_has_no_doubles() const; | ||||
| 
 | ||||
|     bool non_basis_has_no_doubles() const; | ||||
| 
 | ||||
|     bool basis_is_correctly_represented_in_heading() const ; | ||||
|     bool non_basis_is_correctly_represented_in_heading() const ; | ||||
| 
 | ||||
|     bool basis_heading_is_correct() const; | ||||
| 
 | ||||
|     void restore_x_and_refactor(int entering, int leaving, X const & t); | ||||
| 
 | ||||
|     void restore_x(unsigned entering, X const & t); | ||||
| 
 | ||||
|     void fill_reduced_costs_from_m_y_by_rows(); | ||||
| 
 | ||||
|     void copy_rs_to_xB(vector<X> & rs); | ||||
|     virtual bool low_bounds_are_set() const { return false; } | ||||
|     X low_bound_value(unsigned j) const { return m_low_bounds[j]; } | ||||
|     X upper_bound_value(unsigned j) const { return m_upper_bounds[j]; } | ||||
| 
 | ||||
|     column_type get_column_type(unsigned j) const {return m_column_types[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) const { | ||||
|         return m_upper_bounds[j] - m_low_bounds[j]; | ||||
|     } | ||||
| 
 | ||||
|     std::string column_name(unsigned column) const; | ||||
| 
 | ||||
|     void copy_right_side(vector<X> & rs); | ||||
| 
 | ||||
|     void add_delta_to_xB(vector<X> & del); | ||||
| 
 | ||||
|     void find_error_in_BxB(vector<X>& rs); | ||||
| 
 | ||||
|     // recalculates the projection of x to B, such that Ax = b, whereab is the right side
 | ||||
|     void solve_Ax_eq_b(); | ||||
| 
 | ||||
|     bool snap_non_basic_x_to_bound() { | ||||
|         bool ret = false; | ||||
|         for (unsigned j : non_basis()) | ||||
|             ret = snap_column_to_bound(j) || ret; | ||||
|         return ret; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|      | ||||
|     bool snap_column_to_bound(unsigned j) { | ||||
|         switch (m_column_types[j]) { | ||||
|         case column_type::fixed: | ||||
|             if (x_is_at_bound(j)) | ||||
|                 break; | ||||
|             m_x[j] = m_low_bounds[j]; | ||||
|             return true; | ||||
|         case column_type::boxed: | ||||
|             if (x_is_at_bound(j)) | ||||
|                 break; // we should preserve x if possible
 | ||||
|             // snap randomly
 | ||||
|             if (my_random() % 2 == 1)  | ||||
|                 m_x[j] = m_low_bounds[j]; | ||||
|             else | ||||
|                 m_x[j] = m_upper_bounds[j]; | ||||
|             return true; | ||||
|         case column_type::low_bound: | ||||
|             if (x_is_at_low_bound(j)) | ||||
|                 break; | ||||
|             m_x[j] = m_low_bounds[j]; | ||||
|             return true; | ||||
|         case column_type::upper_bound: | ||||
|             if (x_is_at_upper_bound(j)) | ||||
|                 break; | ||||
|             m_x[j] = m_upper_bounds[j]; | ||||
|             return true; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     bool make_column_feasible(unsigned j, numeric_pair<mpq> & delta) { | ||||
|         lean_assert(m_basis_heading[j] < 0); | ||||
|         auto & x = m_x[j]; | ||||
|         switch (m_column_types[j]) { | ||||
|         case column_type::fixed: | ||||
|             lean_assert(m_low_bounds[j] == m_upper_bounds[j]); | ||||
|             if (x != m_low_bounds[j]) { | ||||
|                 delta = m_low_bounds[j] - x; | ||||
|                 x = m_low_bounds[j]; | ||||
|                 return true; | ||||
|             } | ||||
|             break; | ||||
|         case column_type::boxed: | ||||
|             if (x < m_low_bounds[j]) { | ||||
|                 delta = m_low_bounds[j] - x; | ||||
|                 x = m_low_bounds[j]; | ||||
|                 return true; | ||||
|             } | ||||
|             if (x > m_upper_bounds[j]) { | ||||
|                 delta = m_upper_bounds[j] - x; | ||||
|                 x = m_upper_bounds[j]; | ||||
|                 return true; | ||||
|             } | ||||
|             break; | ||||
|         case column_type::low_bound: | ||||
|             if (x < m_low_bounds[j]) { | ||||
|                 delta = m_low_bounds[j] - x; | ||||
|                 x = m_low_bounds[j]; | ||||
|                 return true; | ||||
|             } | ||||
|             break; | ||||
|         case column_type::upper_bound: | ||||
|             if (x > m_upper_bounds[j]) { | ||||
|                 delta = m_upper_bounds[j] - x; | ||||
|                 x = m_upper_bounds[j]; | ||||
|                 return true; | ||||
|             } | ||||
|             break; | ||||
|         case column_type::free_column: | ||||
|             break; | ||||
|         default: | ||||
|             lean_assert(false); | ||||
|             break; | ||||
|         } | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     void snap_non_basic_x_to_bound_and_free_to_zeroes(); | ||||
|     void snap_xN_to_bounds_and_fill_xB(); | ||||
| 
 | ||||
|     void snap_xN_to_bounds_and_free_columns_to_zeroes(); | ||||
| 
 | ||||
|     void init_reduced_costs_for_one_iteration(); | ||||
| 
 | ||||
|     non_basic_column_value_position get_non_basic_column_value_position(unsigned j) const; | ||||
| 
 | ||||
|     void init_lu(); | ||||
|     int pivots_in_column_and_row_are_different(int entering, int leaving) const; | ||||
|     void pivot_fixed_vars_from_basis(); | ||||
|     bool pivot_for_tableau_on_basis(); | ||||
|     bool pivot_row_for_tableau_on_basis(unsigned row); | ||||
|     void init_basic_part_of_basis_heading() { | ||||
|         unsigned m = m_basis.size(); | ||||
|         for (unsigned i = 0; i < m; i++) { | ||||
|             unsigned column = m_basis[i]; | ||||
|             m_basis_heading[column] = i; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void init_non_basic_part_of_basis_heading() { | ||||
|         this->m_nbasis.clear(); | ||||
|         for (int j = m_basis_heading.size(); j--;){ | ||||
|             if (m_basis_heading[j] < 0) { | ||||
|                 m_nbasis.push_back(j); | ||||
|                 // the index of column j in m_nbasis is (- basis_heading[j] - 1)
 | ||||
|                 m_basis_heading[j] = - static_cast<int>(m_nbasis.size()); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     void init_basis_heading_and_non_basic_columns_vector() { | ||||
|         m_basis_heading.resize(0); | ||||
|         m_basis_heading.resize(m_n(), -1); | ||||
|         init_basic_part_of_basis_heading(); | ||||
|         init_non_basic_part_of_basis_heading(); | ||||
|     } | ||||
| 
 | ||||
|     void change_basis_unconditionally(unsigned entering, unsigned leaving) { | ||||
|         lean_assert(m_basis_heading[entering] < 0); | ||||
|         int place_in_non_basis = -1 - m_basis_heading[entering]; | ||||
|         if (static_cast<unsigned>(place_in_non_basis) >= m_nbasis.size()) { | ||||
|               // entering variable in not in m_nbasis, we need to put it back;
 | ||||
|             m_basis_heading[entering] = place_in_non_basis = m_nbasis.size(); | ||||
|             m_nbasis.push_back(entering); | ||||
|         } | ||||
|          | ||||
|         int place_in_basis =  m_basis_heading[leaving]; | ||||
|         m_basis_heading[entering] = place_in_basis; | ||||
|         m_basis[place_in_basis] = entering; | ||||
|         m_basis_heading[leaving] = -place_in_non_basis - 1; | ||||
|         m_nbasis[place_in_non_basis] = leaving; | ||||
|         if (m_tracing_basis_changes) | ||||
|             trace_basis_change(entering, leaving); | ||||
|          | ||||
|     } | ||||
|      | ||||
|     void change_basis(unsigned entering, unsigned leaving) { | ||||
|         lean_assert(m_basis_heading[entering] < 0); | ||||
|          | ||||
|         int place_in_basis =  m_basis_heading[leaving]; | ||||
|         int place_in_non_basis = - m_basis_heading[entering] - 1; | ||||
|         m_basis_heading[entering] = place_in_basis; | ||||
|         m_basis[place_in_basis] = entering; | ||||
| 
 | ||||
|         m_basis_heading[leaving] = -place_in_non_basis - 1; | ||||
|         m_nbasis[place_in_non_basis] = leaving; | ||||
|          | ||||
|         if (m_tracing_basis_changes) | ||||
|             trace_basis_change(entering, leaving); | ||||
|     } | ||||
| 
 | ||||
|     void restore_basis_change(unsigned entering, unsigned leaving) { | ||||
|         if (m_basis_heading[entering] < 0) { | ||||
|             return; // the basis has not been changed
 | ||||
|         } | ||||
|         change_basis_unconditionally(leaving, entering); | ||||
|     } | ||||
| 
 | ||||
|     bool non_basic_column_is_set_correctly(unsigned j) const { | ||||
|         if (j >= this->m_n()) | ||||
|             return false; | ||||
|         switch (this->m_column_types[j]) { | ||||
|         case column_type::fixed: | ||||
|         case column_type::boxed: | ||||
|             if (!this->x_is_at_bound(j)) | ||||
|                 return false; | ||||
|             break; | ||||
|         case column_type::low_bound: | ||||
|             if (!this->x_is_at_low_bound(j)) | ||||
|                 return false; | ||||
|             break; | ||||
|         case column_type::upper_bound: | ||||
|             if (!this->x_is_at_upper_bound(j)) | ||||
|                 return false; | ||||
|             break; | ||||
|         case column_type::free_column: | ||||
|             break; | ||||
|         default: | ||||
|             lean_assert(false); | ||||
|             break; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|     bool non_basic_columns_are_set_correctly() const { | ||||
|         for (unsigned j : this->m_nbasis) | ||||
|             if (!column_is_feasible(j)) { | ||||
|                 print_column_info(j, std::cout); | ||||
|                 return false; | ||||
|             } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     void print_column_bound_info(unsigned j, std::ostream & out) const { | ||||
|         out << column_name(j) << " type = " << column_type_to_string(m_column_types[j]) << std::endl; | ||||
|         switch (m_column_types[j]) { | ||||
|         case column_type::fixed: | ||||
|         case column_type::boxed: | ||||
|             out << "(" << m_low_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl; | ||||
|             break; | ||||
|         case column_type::low_bound: | ||||
|             out << m_low_bounds[j] << std::endl; | ||||
|             break; | ||||
|         case column_type::upper_bound: | ||||
|             out << m_upper_bounds[j] << std::endl; | ||||
|             break; | ||||
|         default: | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void print_column_info(unsigned j, std::ostream & out) const { | ||||
|         out << "column_index = " << j << ", name = "<< column_name(j) << " type = " << column_type_to_string(m_column_types[j]) << std::endl; | ||||
|         switch (m_column_types[j]) { | ||||
|         case column_type::fixed: | ||||
|         case column_type::boxed: | ||||
|             out << "(" << m_low_bounds[j] << ", " << m_upper_bounds[j] << ")" << std::endl; | ||||
|             break; | ||||
|         case column_type::low_bound: | ||||
|             out << m_low_bounds[j] << std::endl; | ||||
|             break; | ||||
|         case column_type::upper_bound: | ||||
|             out << m_upper_bounds[j] << std::endl; | ||||
|             break; | ||||
|         case column_type::free_column: | ||||
|             break; | ||||
|         default: | ||||
|             lean_assert(false); | ||||
|         } | ||||
|         std::cout << "basis heading = " << m_basis_heading[j] << std::endl; | ||||
|         std::cout << "x = " << m_x[j] << std::endl; | ||||
|         /*
 | ||||
|         std::cout << "cost = " << m_costs[j] << std::endl; | ||||
|         std:: cout << "m_d = " << m_d[j] << std::endl;*/ | ||||
|     } | ||||
| 
 | ||||
|     bool column_is_free(unsigned j) { return this->m_column_type[j] == free; } | ||||
| 
 | ||||
|     bool column_has_upper_bound(unsigned j) { | ||||
|         switch(m_column_types[j]) { | ||||
|         case column_type::free_column: | ||||
|         case column_type::low_bound: | ||||
|             return false; | ||||
|         default: | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     bool bounds_for_boxed_are_set_correctly() const { | ||||
|         for (unsigned j = 0; j < m_column_types.size(); j++) { | ||||
|             if (m_column_types[j] != column_type::boxed) continue; | ||||
|             if (m_low_bounds[j] > m_upper_bounds[j]) | ||||
|                 return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
|      | ||||
|     bool column_has_low_bound(unsigned j) { | ||||
|         switch(m_column_types[j]) { | ||||
|         case column_type::free_column: | ||||
|         case column_type::upper_bound: | ||||
|             return false; | ||||
|         default: | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // only check for basic columns
 | ||||
|     bool calc_current_x_is_feasible() const { | ||||
|         unsigned i = this->m_m(); | ||||
|         while (i--) { | ||||
|             if (!column_is_feasible(m_basis[i])) | ||||
|                 return false; | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     int find_pivot_index_in_row(unsigned i, const vector<column_cell> & col) const { | ||||
|         for (const auto & c: col) { | ||||
|             if (c.m_i == i) | ||||
|                 return c.m_offset; | ||||
|         } | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     void transpose_rows_tableau(unsigned i, unsigned ii); | ||||
|      | ||||
|     void pivot_to_reduced_costs_tableau(unsigned i, unsigned j); | ||||
| 
 | ||||
|     bool pivot_column_tableau(unsigned j, unsigned row_index); | ||||
|     bool divide_row_by_pivot(unsigned pivot_row, unsigned pivot_col); | ||||
|      | ||||
|     bool precise() const { return numeric_traits<T>::precise(); } | ||||
| 
 | ||||
|     simplex_strategy_enum simplex_strategy() const { return | ||||
|             m_settings.simplex_strategy(); | ||||
|     } | ||||
| 
 | ||||
|     bool use_tableau() const { return m_settings.use_tableau(); } | ||||
|      | ||||
|     template <typename K> | ||||
|     static void swap(vector<K> &v, unsigned i, unsigned j) { | ||||
|         auto t = v[i]; | ||||
|         v[i] = v[j]; | ||||
|         v[j] = t; | ||||
|     } | ||||
|          | ||||
|     // called when transposing row i and ii
 | ||||
|     void transpose_basis(unsigned i, unsigned ii) { | ||||
|         swap(m_basis, i, ii); | ||||
|         swap(m_basis_heading, m_basis[i], m_basis[ii]); | ||||
|     } | ||||
| 
 | ||||
|     bool column_is_in_inf_set(unsigned j) const { | ||||
|         return m_inf_set.contains(j); | ||||
|     } | ||||
| 
 | ||||
|     void update_column_in_inf_set(unsigned j) { | ||||
|         if (column_is_feasible(j)) { | ||||
|             m_inf_set.erase(j); | ||||
|         } else { | ||||
|             m_inf_set.insert(j); | ||||
|         } | ||||
|     } | ||||
|     void insert_column_into_inf_set(unsigned j) { | ||||
|         m_inf_set.insert(j); | ||||
|         lean_assert(!column_is_feasible(j)); | ||||
|     } | ||||
|     void remove_column_from_inf_set(unsigned j) { | ||||
|         m_inf_set.erase(j); | ||||
|         lean_assert(column_is_feasible(j)); | ||||
|     } | ||||
|     bool costs_on_nbasis_are_zeros() const { | ||||
|         lean_assert(this->basis_heading_is_correct()); | ||||
|         for (unsigned j = 0; j < this->m_n(); j++) { | ||||
|             if (this->m_basis_heading[j] < 0) | ||||
|                 lean_assert(is_zero(this->m_costs[j])); | ||||
|         } | ||||
|         return true; | ||||
| } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										1007
									
								
								src/util/lp/lp_core_solver_base.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1007
									
								
								src/util/lp/lp_core_solver_base.hpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										131
									
								
								src/util/lp/lp_core_solver_base_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										131
									
								
								src/util/lp/lp_core_solver_base_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,131 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <utility> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include "util/vector.h" | ||||
| #include <functional> | ||||
| #include "util/lp/lp_core_solver_base.hpp" | ||||
| template bool lean::lp_core_solver_base<double, double>::A_mult_x_is_off() const; | ||||
| template bool lean::lp_core_solver_base<double, double>::A_mult_x_is_off_on_index(const vector<unsigned> &) const; | ||||
| template bool lean::lp_core_solver_base<double, double>::basis_heading_is_correct() const; | ||||
| template void lean::lp_core_solver_base<double, double>::calculate_pivot_row_of_B_1(unsigned int); | ||||
| template void lean::lp_core_solver_base<double, double>::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); | ||||
| template bool lean::lp_core_solver_base<double, double>::column_is_dual_feasible(unsigned int) const; | ||||
| template void lean::lp_core_solver_base<double, double>::fill_reduced_costs_from_m_y_by_rows(); | ||||
| template bool lean::lp_core_solver_base<double, double>::find_x_by_solving(); | ||||
| template lean::non_basic_column_value_position lean::lp_core_solver_base<double, double>::get_non_basic_column_value_position(unsigned int) const; | ||||
| template lean::non_basic_column_value_position lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::get_non_basic_column_value_position(unsigned int) const; | ||||
| template lean::non_basic_column_value_position lean::lp_core_solver_base<lean::mpq, lean::mpq>::get_non_basic_column_value_position(unsigned int) const; | ||||
| template void lean::lp_core_solver_base<double, double>::init_reduced_costs_for_one_iteration(); | ||||
| template lean::lp_core_solver_base<double, double>::lp_core_solver_base( | ||||
|     lean::static_matrix<double, double>&, vector<double>&,  | ||||
|     vector<unsigned int >&, | ||||
|     vector<unsigned> &, vector<int> &, | ||||
|     vector<double >&,  | ||||
|     vector<double >&,  | ||||
|     lean::lp_settings&, const column_namer&, const vector<lean::column_type >&, | ||||
|     const vector<double >&, | ||||
|     const vector<double >&); | ||||
| 
 | ||||
| template bool lean::lp_core_solver_base<double, double>::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); | ||||
| template void lean::lp_core_solver_base<double, double>::restore_x(unsigned int, double const&); | ||||
| template void lean::lp_core_solver_base<double, double>::set_non_basic_x_to_correct_bounds(); | ||||
| template void lean::lp_core_solver_base<double, double>::snap_xN_to_bounds_and_free_columns_to_zeroes(); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::snap_xN_to_bounds_and_free_columns_to_zeroes(); | ||||
| template void lean::lp_core_solver_base<double, double>::solve_Ax_eq_b(); | ||||
| template void lean::lp_core_solver_base<double, double>::solve_Bd(unsigned int); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq>>::solve_Bd(unsigned int, indexed_vector<lean::mpq>&); | ||||
| template void lean::lp_core_solver_base<double, double>::solve_yB(vector<double >&); | ||||
| template bool lean::lp_core_solver_base<double, double>::update_basis_and_x(int, int, double const&); | ||||
| template void lean::lp_core_solver_base<double, double>::update_x(unsigned int, const double&); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::A_mult_x_is_off() const; | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::A_mult_x_is_off_on_index(const vector<unsigned> &) const; | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::basis_heading_is_correct() const ; | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::calculate_pivot_row_of_B_1(unsigned int); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_is_dual_feasible(unsigned int) const; | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::fill_reduced_costs_from_m_y_by_rows(); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::find_x_by_solving(); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::init_reduced_costs_for_one_iteration(); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over(char const*, std::ostream &); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::restore_x(unsigned int, lean::mpq const&); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::set_non_basic_x_to_correct_bounds(); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_Ax_eq_b(); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_Bd(unsigned int); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::solve_yB(vector<lean::mpq>&); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::update_basis_and_x(int, int, lean::mpq const&); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::update_x(unsigned int, const lean::mpq&); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calculate_pivot_row_of_B_1(unsigned int); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calculate_pivot_row_when_pivot_row_of_B1_is_ready(unsigned); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init(); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_basis_heading_and_non_basic_columns_vector(); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_reduced_costs_for_one_iteration(); | ||||
| template lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::lp_core_solver_base(lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, vector<lean::numeric_pair<lean::mpq> >&, vector<unsigned int >&, vector<unsigned> &, vector<int> &, vector<lean::numeric_pair<lean::mpq> >&, vector<lean::mpq>&, lean::lp_settings&, const column_namer&, const vector<lean::column_type >&, | ||||
|                                                                                                    const vector<lean::numeric_pair<lean::mpq> >&, | ||||
|                                                                                                    const vector<lean::numeric_pair<lean::mpq> >&); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_cost_and_check_that_the_time_is_over(lean::numeric_pair<lean::mpq>, std::ostream&); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::snap_xN_to_bounds_and_fill_xB(); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_Bd(unsigned int); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::update_basis_and_x(int, int, lean::numeric_pair<lean::mpq> const&); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::update_x(unsigned int, const lean::numeric_pair<lean::mpq>&); | ||||
| template lean::lp_core_solver_base<lean::mpq, lean::mpq>::lp_core_solver_base( | ||||
|                                                                               lean::static_matrix<lean::mpq, lean::mpq>&, | ||||
|                                                                               vector<lean::mpq>&, | ||||
|                                                                               vector<unsigned int >&, | ||||
|                                                                               vector<unsigned> &, vector<int> &, | ||||
|                                                                               vector<lean::mpq>&, | ||||
|                                                                               vector<lean::mpq>&, | ||||
|                                                                               lean::lp_settings&, | ||||
|                                                                               const column_namer&, | ||||
|                                                                               const vector<lean::column_type >&, | ||||
|                                                                               const vector<lean::mpq>&, | ||||
|                                                                               const vector<lean::mpq>&); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_statistics_with_iterations_and_check_that_the_time_is_over(std::ostream &); | ||||
| template std::string lean::lp_core_solver_base<double, double>::column_name(unsigned int) const; | ||||
| template void lean::lp_core_solver_base<double, double>::pretty_print(std::ostream & out); | ||||
| template void lean::lp_core_solver_base<double, double>::restore_state(double*, double*); | ||||
| template void lean::lp_core_solver_base<double, double>::save_state(double*, double*); | ||||
| template std::string lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_name(unsigned int) const; | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::pretty_print(std::ostream & out); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::restore_state(lean::mpq*, lean::mpq*); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::save_state(lean::mpq*, lean::mpq*); | ||||
| template std::string lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::column_name(unsigned int) const; | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::pretty_print(std::ostream & out); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::restore_state(lean::mpq*, lean::mpq*); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::save_state(lean::mpq*, lean::mpq*); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB(vector<lean::mpq>&); | ||||
| template void lean::lp_core_solver_base<double, double>::init_lu(); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::mpq>::init_lu(); | ||||
| template int lean::lp_core_solver_base<double, double>::pivots_in_column_and_row_are_different(int, int) const; | ||||
| template int lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::pivots_in_column_and_row_are_different(int, int) const; | ||||
| template int lean::lp_core_solver_base<lean::mpq, lean::mpq>::pivots_in_column_and_row_are_different(int, int) const; | ||||
| template bool lean::lp_core_solver_base<double, double>::calc_current_x_is_feasible_include_non_basis(void)const; | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::calc_current_x_is_feasible_include_non_basis(void)const; | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::calc_current_x_is_feasible_include_non_basis() const; | ||||
| template void  lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::pivot_fixed_vars_from_basis(); | ||||
| template bool lean::lp_core_solver_base<double, double>::column_is_feasible(unsigned int) const; | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::column_is_feasible(unsigned int) const; | ||||
| // template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::print_linear_combination_of_column_indices(vector<std::pair<lean::mpq, unsigned int>, std::allocator<std::pair<lean::mpq, unsigned int> > > const&, std::ostream&) const;
 | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::column_is_feasible(unsigned int) const; | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::snap_non_basic_x_to_bound(); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::init_lu(); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::A_mult_x_is_off_on_index(vector<unsigned int> const&) const; | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::find_x_by_solving(); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::restore_x(unsigned int, lean::numeric_pair<lean::mpq> const&); | ||||
| template bool lean::lp_core_solver_base<double, double>::pivot_for_tableau_on_basis(); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::pivot_for_tableau_on_basis(); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq>>::pivot_for_tableau_on_basis(); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq>>::pivot_column_tableau(unsigned int, unsigned int); | ||||
| template bool lean::lp_core_solver_base<double, double>::pivot_column_tableau(unsigned int, unsigned int); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::pivot_column_tableau(unsigned int, unsigned int); | ||||
| template void lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::transpose_rows_tableau(unsigned int, unsigned int); | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::inf_set_is_correct() const; | ||||
| template bool lean::lp_core_solver_base<double, double>::inf_set_is_correct() const; | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq>::inf_set_is_correct() const; | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::numeric_pair<lean::mpq> >::infeasibility_costs_are_correct() const; | ||||
| template bool lean::lp_core_solver_base<lean::mpq, lean::mpq >::infeasibility_costs_are_correct() const; | ||||
| template bool lean::lp_core_solver_base<double, double >::infeasibility_costs_are_correct() const; | ||||
							
								
								
									
										197
									
								
								src/util/lp/lp_dual_core_solver.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										197
									
								
								src/util/lp/lp_dual_core_solver.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,197 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   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 "util/vector.h" | ||||
| 
 | ||||
| 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; | ||||
|     std::set<unsigned> m_forbidden_rows; | ||||
| 
 | ||||
|     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<unsigned> & nbasis, | ||||
|                         vector<int> & heading, | ||||
|                         vector<T> & costs, | ||||
|                         vector<column_type> & column_type_array, | ||||
|                         vector<X> & low_bound_values, | ||||
|                         vector<X> & upper_bound_values, | ||||
|                         lp_settings & settings, | ||||
|                         const column_namer & column_names): | ||||
|         lp_core_solver_base<T, X>(A, | ||||
|                                   b, | ||||
|                                   basis, | ||||
|                                   nbasis, | ||||
|                                   heading, | ||||
|                                   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); | ||||
|         this->init_basic_part_of_basis_heading(); | ||||
|         fill_non_basis_with_only_able_to_enter_columns(); | ||||
|     } | ||||
| 
 | ||||
|     void init_a_wave_by_zeros(); | ||||
| 
 | ||||
|     void fill_non_basis_with_only_able_to_enter_columns() { | ||||
|         auto & nb = this->m_nbasis; | ||||
|         nb.reset(); | ||||
|         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] = - static_cast<int>(nb.size()); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void restore_non_basis(); | ||||
| 
 | ||||
|     bool update_basis(int entering, int leaving); | ||||
| 
 | ||||
|     void recalculate_xB_and_d(); | ||||
| 
 | ||||
|     void recalculate_d(); | ||||
| 
 | ||||
|     void init_betas(); | ||||
| 
 | ||||
|     void adjust_xb_for_changed_xn_and_init_betas(); | ||||
| 
 | ||||
|     void start_with_initial_basis_and_make_it_dual_feasible(); | ||||
| 
 | ||||
|     bool done(); | ||||
| 
 | ||||
|     T get_edge_steepness_for_low_bound(unsigned p); | ||||
| 
 | ||||
|     T get_edge_steepness_for_upper_bound(unsigned p); | ||||
| 
 | ||||
|     T pricing_for_row(unsigned i); | ||||
| 
 | ||||
|     void pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows); | ||||
| 
 | ||||
|     bool advance_on_known_p(); | ||||
| 
 | ||||
|     int define_sign_of_alpha_r(); | ||||
| 
 | ||||
|     bool can_be_breakpoint(unsigned j); | ||||
| 
 | ||||
|     void fill_breakpoint_set(); | ||||
| 
 | ||||
|     void DSE_FTran(); | ||||
|     T get_delta(); | ||||
| 
 | ||||
|     void restore_d(); | ||||
| 
 | ||||
|     bool d_is_correct(); | ||||
| 
 | ||||
|     void xb_minus_delta_p_pivot_column(); | ||||
| 
 | ||||
|     void update_betas(); | ||||
| 
 | ||||
|     void apply_flips(); | ||||
| 
 | ||||
|     void snap_xN_column_to_bounds(unsigned j); | ||||
| 
 | ||||
|     void snap_xN_to_bounds(); | ||||
| 
 | ||||
|     void init_beta_precisely(unsigned i); | ||||
| 
 | ||||
|     void init_betas_precisely(); | ||||
| 
 | ||||
|     // step 7 of the algorithm from Progress
 | ||||
|     bool basis_change_and_update(); | ||||
| 
 | ||||
|     void revert_to_previous_basis(); | ||||
| 
 | ||||
|     non_basic_column_value_position m_entering_boundary_position; | ||||
|     bool update_basis_and_x_local(int entering, int leaving, X const & tt); | ||||
|     void recover_leaving(); | ||||
| 
 | ||||
|     bool problem_is_dual_feasible() const; | ||||
| 
 | ||||
|     bool snap_runaway_nonbasic_column(unsigned); | ||||
| 
 | ||||
|     bool snap_runaway_nonbasic_columns(); | ||||
| 
 | ||||
|     unsigned get_number_of_rows_to_try_for_leaving(); | ||||
| 
 | ||||
|     void update_a_wave(const T & del, unsigned j) { | ||||
|         this->m_A.add_column_to_vector(del, j, & m_a_wave[0]); | ||||
|     } | ||||
| 
 | ||||
|     bool delta_keeps_the_sign(int initial_delta_sign, const T & delta); | ||||
| 
 | ||||
|     void set_status_to_tentative_dual_unbounded_or_dual_unbounded(); | ||||
| 
 | ||||
|     // it is positive if going from low bound to upper bound and negative if going from upper bound to low bound
 | ||||
|     T signed_span_of_boxed(unsigned j) { | ||||
|         return this->x_is_at_low_bound(j)? this->bound_span(j): - this->bound_span(j); | ||||
|     } | ||||
| 
 | ||||
|     void add_tight_breakpoints_and_q_to_flipped_set(); | ||||
| 
 | ||||
|     T delta_lost_on_flips_of_tight_breakpoints(); | ||||
| 
 | ||||
|     bool tight_breakpoinst_are_all_boxed(); | ||||
| 
 | ||||
|     T calculate_harris_delta_on_breakpoint_set(); | ||||
| 
 | ||||
|     void fill_tight_set_on_harris_delta(const T & harris_delta ); | ||||
| 
 | ||||
|     void find_q_on_tight_set(); | ||||
| 
 | ||||
|     void find_q_and_tight_set(); | ||||
| 
 | ||||
|     void erase_tight_breakpoints_and_q_from_breakpoint_set(); | ||||
| 
 | ||||
|     bool ratio_test(); | ||||
| 
 | ||||
|     void process_flipped(); | ||||
|     void update_d_and_xB(); | ||||
| 
 | ||||
|     void calculate_beta_r_precisely(); | ||||
|     // see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms"
 | ||||
| 
 | ||||
|     void update_xb_after_bound_flips(); | ||||
| 
 | ||||
|     void one_iteration(); | ||||
| 
 | ||||
|     void solve(); | ||||
| 
 | ||||
|     bool low_bounds_are_set() const { return true; } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										743
									
								
								src/util/lp/lp_dual_core_solver.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										743
									
								
								src/util/lp/lp_dual_core_solver.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,743 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <algorithm> | ||||
| #include <string> | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/lp_dual_core_solver.h" | ||||
| 
 | ||||
| namespace lean { | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::init_a_wave_by_zeros() { | ||||
|     unsigned j = this->m_m(); | ||||
|     while (j--) { | ||||
|         m_a_wave[j] = numeric_traits<T>::zero(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::restore_non_basis() { | ||||
|     auto & nb = this->m_nbasis; | ||||
|     nb.reset(); | ||||
|     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] = - static_cast<int>(nb.size()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_dual_core_solver<T, X>::update_basis(int entering, int leaving) { | ||||
|     // the second argument is the element of the entering column from the pivot row - its value should be equal to the low diagonal element of the bump after all pivoting is done
 | ||||
|     if (this->m_refactor_counter++ < 200) { | ||||
|         this->m_factorization->replace_column(this->m_ed[this->m_factorization->basis_heading(leaving)], this->m_w); | ||||
|         if (this->m_factorization->get_status() == LU_status::OK) { | ||||
|             this->m_factorization->change_basis(entering, leaving); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
|     // 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_refactor_counter = 0; | ||||
|     if (this->m_factorization->get_status() != LU_status::OK) { | ||||
|         LP_OUT(this->m_settings, "failing refactor for entering = " << entering << ", leaving = " << leaving << " total_iterations = " << this->total_iterations() << std::endl); | ||||
|         this->m_iters_with_no_cost_growing++; | ||||
|         return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::recalculate_xB_and_d() { | ||||
|     this->solve_Ax_eq_b(); | ||||
|     recalculate_d(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::recalculate_d() { | ||||
|     this->solve_yB(this->m_y); | ||||
|     this->fill_reduced_costs_from_m_y_by_rows(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::init_betas() { | ||||
|     // todo : look at page 194 of Progress in the dual simplex algorithm for solving large scale LP problems : techniques for a fast and stable implementation
 | ||||
|     // the current implementation is not good enough: todo
 | ||||
|     unsigned i = this->m_m(); | ||||
|     while (i--) { | ||||
|         m_betas[i] = 1; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::adjust_xb_for_changed_xn_and_init_betas() { | ||||
|     this->solve_Ax_eq_b(); | ||||
|     init_betas(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::start_with_initial_basis_and_make_it_dual_feasible() { | ||||
|     this->set_non_basic_x_to_correct_bounds(); // It is not an efficient version, see 3.29,
 | ||||
|     // however this version does not require that m_x is the solution of Ax = 0 beforehand
 | ||||
|     adjust_xb_for_changed_xn_and_init_betas(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_dual_core_solver<T, X>::done() { | ||||
|     if (this->get_status() == OPTIMAL) { | ||||
|         return true; | ||||
|     } | ||||
|     if (this->total_iterations() > this->m_settings.max_total_number_of_iterations) { // debug !!!!
 | ||||
|         this->set_status(ITERATIONS_EXHAUSTED); | ||||
|         return true; | ||||
|     } | ||||
|     return false; // todo, need to be more cases
 | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_low_bound(unsigned p) { | ||||
|     lean_assert(this->m_basis_heading[p] >= 0 && static_cast<unsigned>(this->m_basis_heading[p]) < this->m_m()); | ||||
|     T del = this->m_x[p] - this->m_low_bounds[p]; | ||||
|     del *= del; | ||||
|     return del / this->m_betas[this->m_basis_heading[p]]; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> T lp_dual_core_solver<T, X>::get_edge_steepness_for_upper_bound(unsigned p) { | ||||
|     lean_assert(this->m_basis_heading[p] >= 0 && static_cast<unsigned>(this->m_basis_heading[p]) < this->m_m()); | ||||
|     T del = this->m_x[p] - this->m_upper_bounds[p]; | ||||
|     del *= del; | ||||
|     return del / this->m_betas[this->m_basis_heading[p]]; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> T lp_dual_core_solver<T, X>::pricing_for_row(unsigned i) { | ||||
|     unsigned p = this->m_basis[i]; | ||||
|     switch (this->m_column_types[p]) { | ||||
|     case column_type::fixed: | ||||
|     case column_type::boxed: | ||||
|         if (this->x_below_low_bound(p)) { | ||||
|             T del =  get_edge_steepness_for_low_bound(p); | ||||
|             return del; | ||||
|         } | ||||
|         if (this->x_above_upper_bound(p)) { | ||||
|             T del =  get_edge_steepness_for_upper_bound(p); | ||||
|             return del; | ||||
|         } | ||||
|         return numeric_traits<T>::zero(); | ||||
|     case column_type::low_bound: | ||||
|         if (this->x_below_low_bound(p)) { | ||||
|             T del =  get_edge_steepness_for_low_bound(p); | ||||
|             return del; | ||||
|         } | ||||
|         return numeric_traits<T>::zero(); | ||||
|         break; | ||||
|     case column_type::upper_bound: | ||||
|         if (this->x_above_upper_bound(p)) { | ||||
|             T del =  get_edge_steepness_for_upper_bound(p); | ||||
|             return del; | ||||
|         } | ||||
|         return numeric_traits<T>::zero(); | ||||
|         break; | ||||
|     case column_type::free_column: | ||||
|         lean_assert(numeric_traits<T>::is_zero(this->m_d[p])); | ||||
|         return numeric_traits<T>::zero(); | ||||
|     default: | ||||
|         lean_unreachable(); | ||||
|     } | ||||
|     lean_unreachable(); | ||||
|     return numeric_traits<T>::zero(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::pricing_loop(unsigned number_of_rows_to_try, unsigned offset_in_rows) { | ||||
|     m_r = -1; | ||||
|     T steepest_edge_max = numeric_traits<T>::zero(); | ||||
|     unsigned initial_offset_in_rows = offset_in_rows; | ||||
|     unsigned i = offset_in_rows; | ||||
|     unsigned rows_left = number_of_rows_to_try; | ||||
|     do  { | ||||
|         if (m_forbidden_rows.find(i) != m_forbidden_rows.end()) { | ||||
|             if (++i == this->m_m()) { | ||||
|                 i = 0; | ||||
|             } | ||||
|             continue; | ||||
|         } | ||||
|         T se = pricing_for_row(i); | ||||
|         if (se > steepest_edge_max) { | ||||
|             steepest_edge_max = se; | ||||
|             m_r = i; | ||||
|             if (rows_left > 0) { | ||||
|                 rows_left--; | ||||
|             } | ||||
|         } | ||||
|         if (++i == this->m_m()) { | ||||
|             i = 0; | ||||
|         } | ||||
|     } while (i != initial_offset_in_rows && rows_left); | ||||
|     if (m_r == -1) { | ||||
|         if (this->get_status() != UNSTABLE) { | ||||
|             this->set_status(OPTIMAL); | ||||
|         } | ||||
|     } else { | ||||
|         m_p = this->m_basis[m_r]; | ||||
|         m_delta = get_delta(); | ||||
|         if (advance_on_known_p()){ | ||||
|             m_forbidden_rows.clear(); | ||||
|             return; | ||||
|         } | ||||
|         // failure in advance_on_known_p
 | ||||
|         if (this->get_status() == FLOATING_POINT_ERROR) { | ||||
|             return; | ||||
|         } | ||||
|         this->set_status(UNSTABLE); | ||||
|         m_forbidden_rows.insert(m_r); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|     // 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
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::DSE_FTran() { // todo, see algorithm 7 from page 35
 | ||||
|     this->m_factorization->solve_By_for_T_indexed_only(this->m_pivot_row_of_B_1, this->m_settings); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_dual_core_solver<T, X>::advance_on_known_p() { | ||||
|     if (done()) { | ||||
|         return true; | ||||
|     } | ||||
|     this->calculate_pivot_row_of_B_1(m_r); | ||||
|     this->calculate_pivot_row_when_pivot_row_of_B1_is_ready(m_r); | ||||
|     if (!ratio_test()) { | ||||
|         return true; | ||||
|     } | ||||
|     calculate_beta_r_precisely(); | ||||
|     this->solve_Bd(m_q); // FTRAN
 | ||||
|     int pivot_compare_result = this->pivots_in_column_and_row_are_different(m_q, m_p); | ||||
|     if (!pivot_compare_result){;} | ||||
|     else if (pivot_compare_result == 2) { // the sign is changed, cannot continue
 | ||||
|         lean_unreachable(); // not implemented yet
 | ||||
|     } else { | ||||
|         lean_assert(pivot_compare_result == 1); | ||||
|         this->init_lu(); | ||||
|     } | ||||
|     DSE_FTran(); | ||||
|     return basis_change_and_update(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> int lp_dual_core_solver<T, X>::define_sign_of_alpha_r() { | ||||
|     switch (this->m_column_types[m_p]) { | ||||
|     case column_type::boxed: | ||||
|     case column_type::fixed: | ||||
|         if (this->x_below_low_bound(m_p)) { | ||||
|             return -1; | ||||
|         } | ||||
|         if (this->x_above_upper_bound(m_p)) { | ||||
|             return 1; | ||||
|         } | ||||
|         lean_unreachable(); | ||||
|     case column_type::low_bound: | ||||
|         if (this->x_below_low_bound(m_p)) { | ||||
|             return -1; | ||||
|         } | ||||
|         lean_unreachable(); | ||||
|     case column_type::upper_bound: | ||||
|         if (this->x_above_upper_bound(m_p)) { | ||||
|             return 1; | ||||
|         } | ||||
|         lean_unreachable(); | ||||
|     default: | ||||
|         lean_unreachable(); | ||||
|     } | ||||
|     lean_unreachable(); | ||||
|     return 0; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_dual_core_solver<T, X>::can_be_breakpoint(unsigned j) { | ||||
|     if (this->pivot_row_element_is_too_small_for_ratio_test(j)) return false; | ||||
|     switch (this->m_column_types[j]) { | ||||
|     case column_type::low_bound: | ||||
|         lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_low_bounds[j])); | ||||
|         return m_sign_of_alpha_r * this->m_pivot_row[j]  > 0; | ||||
|     case column_type::upper_bound: | ||||
|         lean_assert(this->m_settings.abs_val_is_smaller_than_harris_tolerance(this->m_x[j] - this->m_upper_bounds[j])); | ||||
|         return m_sign_of_alpha_r * this->m_pivot_row[j] < 0; | ||||
|     case column_type::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 column_type::fixed: // is always dual feasible so we ingore it
 | ||||
|         return false; | ||||
|     case column_type::free_column: | ||||
|         return true; | ||||
|     default: | ||||
|         return false; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_breakpoint_set() { | ||||
|     m_breakpoint_set.clear(); | ||||
|     for (unsigned j : this->non_basis()) { | ||||
|         if (can_be_breakpoint(j)) { | ||||
|             m_breakpoint_set.insert(j); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // template <typename T, typename X> void lp_dual_core_solver<T, X>::FTran() {
 | ||||
| //     this->solve_Bd(m_q);
 | ||||
| // }
 | ||||
| 
 | ||||
| template <typename T, typename X> T lp_dual_core_solver<T, X>::get_delta() { | ||||
|     switch (this->m_column_types[m_p]) { | ||||
|     case column_type::boxed: | ||||
|         if (this->x_below_low_bound(m_p)) { | ||||
|             return this->m_x[m_p] - this->m_low_bounds[m_p]; | ||||
|         } | ||||
|         if (this->x_above_upper_bound(m_p)) { | ||||
|             return this->m_x[m_p] - this->m_upper_bounds[m_p]; | ||||
|         } | ||||
|         lean_unreachable(); | ||||
|     case column_type::low_bound: | ||||
|         if (this->x_below_low_bound(m_p)) { | ||||
|             return this->m_x[m_p] - this->m_low_bounds[m_p]; | ||||
|         } | ||||
|         lean_unreachable(); | ||||
|     case column_type::upper_bound: | ||||
|         if (this->x_above_upper_bound(m_p)) { | ||||
|             return get_edge_steepness_for_upper_bound(m_p); | ||||
|         } | ||||
|         lean_unreachable(); | ||||
|     case column_type::fixed: | ||||
|         return this->m_x[m_p] - this->m_upper_bounds[m_p]; | ||||
|     default: | ||||
|         lean_unreachable(); | ||||
|     } | ||||
|     lean_unreachable(); | ||||
|     return zero_of_type<T>(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::restore_d() { | ||||
|     this->m_d[m_p] = numeric_traits<T>::zero(); | ||||
|     for (auto j : this->non_basis()) { | ||||
|         this->m_d[j] += m_theta_D * this->m_pivot_row[j]; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_dual_core_solver<T, X>::d_is_correct() { | ||||
|     this->solve_yB(this->m_y); | ||||
|     for  (auto j : this->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) { | ||||
|             LP_OUT(this->m_settings, "total_iterations = " << this->total_iterations() << std::endl | ||||
|                 << "d[" << j << "] = " << this->m_d[j] << " but should be " << d << std::endl); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::xb_minus_delta_p_pivot_column() { | ||||
|     unsigned i = this->m_m(); | ||||
|     while (i--) { | ||||
|         this->m_x[this->m_basis[i]] -= m_theta_P * this->m_ed[i]; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::update_betas() { // page 194 of Progress ... todo - once in a while betas have to be reinitialized
 | ||||
|     T one_over_arq = numeric_traits<T>::one() / this->m_pivot_row[m_q]; | ||||
|     T beta_r = this->m_betas[m_r] = std::max(T(0.0001), (m_betas[m_r] * one_over_arq) *  one_over_arq); | ||||
|     T k = -2 * one_over_arq; | ||||
|     unsigned i = this->m_m(); | ||||
|     while (i--) { | ||||
|         if (static_cast<int>(i) == m_r) continue; | ||||
|         T a = this->m_ed[i]; | ||||
|         m_betas[i] += a * (a * beta_r + k * this->m_pivot_row_of_B_1[i]); | ||||
|         if (m_betas[i] < T(0.0001)) | ||||
|             m_betas[i] = T(0.0001); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::apply_flips() { | ||||
|     for (unsigned j : m_flipped_boxed) { | ||||
|         lean_assert(this->x_is_at_bound(j)); | ||||
|         if (this->x_is_at_low_bound(j)) { | ||||
|             this->m_x[j] = this->m_upper_bounds[j]; | ||||
|         } else { | ||||
|             this->m_x[j] = this->m_low_bounds[j]; | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_column_to_bounds(unsigned j) { | ||||
|     switch (this->m_column_type[j]) { | ||||
|     case column_type::fixed: | ||||
|         this->m_x[j] = this->m_low_bounds[j]; | ||||
|         break; | ||||
|     case column_type::boxed: | ||||
|         if (this->x_is_at_low_bound(j)) { | ||||
|             this->m_x[j] = this->m_low_bounds[j]; | ||||
|         } else { | ||||
|             this->m_x[j] = this->m_upper_bounds[j]; | ||||
|         } | ||||
|         break; | ||||
|     case column_type::low_bound: | ||||
|         this->m_x[j] = this->m_low_bounds[j]; | ||||
|         break; | ||||
|     case column_type::upper_bound: | ||||
|         this->m_x[j] = this->m_upper_bounds[j]; | ||||
|         break; | ||||
|     case column_type::free_column: | ||||
|         break; | ||||
|     default: | ||||
|         lean_unreachable(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::snap_xN_to_bounds() { | ||||
|     for (auto j : this->non_basis()) { | ||||
|         snap_xN_column_to_bounds(j); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::init_beta_precisely(unsigned i) { | ||||
|     vector<T> vec(this->m_m(), numeric_traits<T>::zero()); | ||||
|     vec[i] = numeric_traits<T>::one(); | ||||
|     this->m_factorization->solve_yB_with_error_check(vec, this->m_basis); | ||||
|     T beta = numeric_traits<T>::zero(); | ||||
|     for (T & v : vec) { | ||||
|         beta += v * v; | ||||
|     } | ||||
|     this->m_betas[i] =beta; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::init_betas_precisely() { | ||||
|     unsigned i = this->m_m(); | ||||
|     while (i--) { | ||||
|         init_beta_precisely(i); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // step 7 of the algorithm from Progress
 | ||||
| template <typename T, typename X> bool lp_dual_core_solver<T, X>::basis_change_and_update() { | ||||
|     update_betas(); | ||||
|     update_d_and_xB(); | ||||
|     //    m_theta_P = m_delta / this->m_ed[m_r];
 | ||||
|     m_theta_P = m_delta / this->m_pivot_row[m_q]; | ||||
|     //    xb_minus_delta_p_pivot_column();
 | ||||
|     apply_flips(); | ||||
|     if (!this->update_basis_and_x(m_q, m_p, m_theta_P)) { | ||||
|           init_betas_precisely(); | ||||
|           return false; | ||||
|     } | ||||
| 
 | ||||
|     if (snap_runaway_nonbasic_column(m_p)) { | ||||
|         if (!this->find_x_by_solving()) { | ||||
|             revert_to_previous_basis(); | ||||
|             this->m_iters_with_no_cost_growing++; | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     if (!problem_is_dual_feasible()) { | ||||
|         // todo : shift the costs!!!!
 | ||||
|         revert_to_previous_basis(); | ||||
|         this->m_iters_with_no_cost_growing++; | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     lean_assert(d_is_correct()); | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::recover_leaving() { | ||||
|     switch (m_entering_boundary_position) { | ||||
|     case at_low_bound: | ||||
|     case at_fixed: | ||||
|         this->m_x[m_q] = this->m_low_bounds[m_q]; | ||||
|         break; | ||||
|     case at_upper_bound: | ||||
|         this->m_x[m_q] = this->m_upper_bounds[m_q]; | ||||
|         break; | ||||
|     case free_of_bounds: | ||||
|         this->m_x[m_q] = zero_of_type<X>(); | ||||
|     default: | ||||
|         lean_unreachable(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::revert_to_previous_basis() { | ||||
|     LP_OUT(this->m_settings, "revert to previous basis on ( " << m_p << ", " << m_q << ")" << std::endl); | ||||
|     this->change_basis_unconditionally(m_p, m_q); | ||||
|     init_factorization(this->m_factorization, this->m_A, this->m_basis, this->m_settings); | ||||
|     if (this->m_factorization->get_status() != LU_status::OK) { | ||||
|         this->set_status(FLOATING_POINT_ERROR); // complete failure
 | ||||
|         return; | ||||
|     } | ||||
|     recover_leaving(); | ||||
|     if (!this->find_x_by_solving()) { | ||||
|         this->set_status(FLOATING_POINT_ERROR); | ||||
|         return; | ||||
|     } | ||||
|     recalculate_xB_and_d(); | ||||
|     init_betas_precisely(); | ||||
| } | ||||
| 
 | ||||
| // returns true if the column has been snapped
 | ||||
| template <typename T, typename X> bool lp_dual_core_solver<T, X>::snap_runaway_nonbasic_column(unsigned j) { | ||||
|     switch (this->m_column_types[j]) { | ||||
|     case column_type::fixed: | ||||
|     case column_type::low_bound: | ||||
|         if (!this->x_is_at_low_bound(j)) { | ||||
|             this->m_x[j] = this->m_low_bounds[j]; | ||||
|             return true; | ||||
|         } | ||||
|         break; | ||||
|     case column_type::boxed: | ||||
|         { | ||||
|             bool closer_to_low_bound = abs(this->m_low_bounds[j] - this->m_x[j]) < abs(this->m_upper_bounds[j] - this->m_x[j]); | ||||
|             if (closer_to_low_bound) { | ||||
|                 if (!this->x_is_at_low_bound(j)) { | ||||
|                     this->m_x[j] = this->m_low_bounds[j]; | ||||
|                     return true; | ||||
|                 } | ||||
|             } else { | ||||
|                 if (!this->x_is_at_upper_bound(j)) { | ||||
|                     this->m_x[j] = this->m_low_bounds[j]; | ||||
|                     return true; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         break; | ||||
|     case column_type::upper_bound: | ||||
|         if (!this->x_is_at_upper_bound(j)) { | ||||
|             this->m_x[j] = this->m_upper_bounds[j]; | ||||
|             return true; | ||||
|         } | ||||
|         break; | ||||
|     default: | ||||
|         break; | ||||
|     } | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_dual_core_solver<T, X>::problem_is_dual_feasible() const { | ||||
|     for (unsigned j : this->non_basis()){ | ||||
|         if (!this->column_is_dual_feasible(j)) { | ||||
|             // std::cout << "column " << j << " is not dual feasible" << std::endl;
 | ||||
|             // std::cout << "m_d[" << j << "] = " << this->m_d[j] << std::endl;
 | ||||
|             // std::cout << "x[" << j << "] = " << this->m_x[j] << std::endl;
 | ||||
|             // std::cout << "type = " << column_type_to_string(this->m_column_type[j]) << std::endl;
 | ||||
|             // std::cout << "bounds = " << this->m_low_bounds[j] << "," << this->m_upper_bounds[j] << std::endl;
 | ||||
|             // std::cout << "total_iterations = " << this->total_iterations() << std::endl;
 | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> unsigned lp_dual_core_solver<T, X>::get_number_of_rows_to_try_for_leaving() { | ||||
|     unsigned s = this->m_m(); | ||||
|     if (this->m_m() > 300) { | ||||
|         s = (unsigned)((s / 100.0) * this->m_settings.percent_of_entering_to_check); | ||||
|     } | ||||
|     return my_random() % s + 1; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_dual_core_solver<T, X>::delta_keeps_the_sign(int initial_delta_sign, const T & delta) { | ||||
|     if (numeric_traits<T>::precise()) | ||||
|         return ((delta > numeric_traits<T>::zero()) && (initial_delta_sign == 1)) || | ||||
|             ((delta < numeric_traits<T>::zero()) && (initial_delta_sign == -1)); | ||||
| 
 | ||||
|     double del = numeric_traits<T>::get_double(delta); | ||||
|     return ( (del > this->m_settings.zero_tolerance) && (initial_delta_sign == 1)) || | ||||
|         ((del < - this->m_settings.zero_tolerance) && (initial_delta_sign == -1)); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::set_status_to_tentative_dual_unbounded_or_dual_unbounded() { | ||||
|     if (this->get_status() == TENTATIVE_DUAL_UNBOUNDED) { | ||||
|         this->set_status(DUAL_UNBOUNDED); | ||||
|     } else { | ||||
|         this->set_status(TENTATIVE_DUAL_UNBOUNDED); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::add_tight_breakpoints_and_q_to_flipped_set() { | ||||
|     m_flipped_boxed.insert(m_q); | ||||
|     for (auto j : m_tight_set) { | ||||
|         m_flipped_boxed.insert(j); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> T lp_dual_core_solver<T, X>::delta_lost_on_flips_of_tight_breakpoints() { | ||||
|     T ret = abs(this->bound_span(m_q) * this->m_pivot_row[m_q]); | ||||
|     for (auto j : m_tight_set) { | ||||
|         ret += abs(this->bound_span(j) * this->m_pivot_row[j]); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_dual_core_solver<T, X>::tight_breakpoinst_are_all_boxed() { | ||||
|     if (this->m_column_types[m_q] != column_type::boxed) return false; | ||||
|     for (auto j : m_tight_set) { | ||||
|         if (this->m_column_types[j] != column_type::boxed) return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> T lp_dual_core_solver<T, X>::calculate_harris_delta_on_breakpoint_set() { | ||||
|     bool first_time = true; | ||||
|     T ret = zero_of_type<T>(); | ||||
|     lean_assert(m_breakpoint_set.size() > 0); | ||||
|     for (auto j : m_breakpoint_set) { | ||||
|         T t; | ||||
|         if (this->x_is_at_low_bound(j)) { | ||||
|             t = abs((std::max(this->m_d[j], numeric_traits<T>::zero()) + m_harris_tolerance) / this->m_pivot_row[j]); | ||||
|         } else { | ||||
|             t = abs((std::min(this->m_d[j], numeric_traits<T>::zero()) - m_harris_tolerance) / this->m_pivot_row[j]); | ||||
|         } | ||||
|         if (first_time) { | ||||
|             ret = t; | ||||
|             first_time = false; | ||||
|         } else if (t < ret) { | ||||
|             ret = t; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::fill_tight_set_on_harris_delta(const T & harris_delta ){ | ||||
|     m_tight_set.clear(); | ||||
|     for (auto j : m_breakpoint_set) { | ||||
|         if (this->x_is_at_low_bound(j)) { | ||||
|             if (abs(std::max(this->m_d[j], numeric_traits<T>::zero()) / this->m_pivot_row[j]) <= harris_delta){ | ||||
|                 m_tight_set.insert(j); | ||||
|             } | ||||
|         } else { | ||||
|             if (abs(std::min(this->m_d[j], numeric_traits<T>::zero() ) / this->m_pivot_row[j]) <= harris_delta){ | ||||
|                 m_tight_set.insert(j); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_on_tight_set() { | ||||
|     m_q = -1; | ||||
|     T max_pivot; | ||||
|     for (auto j : m_tight_set) { | ||||
|         T r = abs(this->m_pivot_row[j]); | ||||
|         if (m_q != -1) { | ||||
|             if (r > max_pivot) { | ||||
|                 max_pivot = r; | ||||
|                 m_q = j; | ||||
|             } | ||||
|         } else { | ||||
|             max_pivot = r; | ||||
|             m_q = j; | ||||
|         } | ||||
|     } | ||||
|     m_tight_set.erase(m_q); | ||||
|     lean_assert(m_q != -1); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::find_q_and_tight_set() { | ||||
|     T harris_del = calculate_harris_delta_on_breakpoint_set(); | ||||
|     fill_tight_set_on_harris_delta(harris_del); | ||||
|     find_q_on_tight_set(); | ||||
|     m_entering_boundary_position = this->get_non_basic_column_value_position(m_q); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::erase_tight_breakpoints_and_q_from_breakpoint_set() { | ||||
|     m_breakpoint_set.erase(m_q); | ||||
|     for (auto j : m_tight_set) { | ||||
|         m_breakpoint_set.erase(j); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_dual_core_solver<T, X>::ratio_test() { | ||||
|     m_sign_of_alpha_r = define_sign_of_alpha_r(); | ||||
|     fill_breakpoint_set(); | ||||
|     m_flipped_boxed.clear(); | ||||
|     int initial_delta_sign = m_delta >= numeric_traits<T>::zero()? 1: -1; | ||||
|     do { | ||||
|         if (m_breakpoint_set.size() == 0) { | ||||
|             set_status_to_tentative_dual_unbounded_or_dual_unbounded(); | ||||
|             return false; | ||||
|         } | ||||
|         this->set_status(FEASIBLE); | ||||
|         find_q_and_tight_set(); | ||||
|         if (!tight_breakpoinst_are_all_boxed())  break; | ||||
|         T del = m_delta - delta_lost_on_flips_of_tight_breakpoints() * initial_delta_sign; | ||||
|         if (!delta_keeps_the_sign(initial_delta_sign, del)) break; | ||||
|         if (m_tight_set.size() + 1 == m_breakpoint_set.size()) { | ||||
|             break; // deciding not to flip since we might get stuck without finding m_q, the column entering the basis
 | ||||
|         } | ||||
|         // we can flip m_q together with the tight set and look for another breakpoint candidate for m_q and another tight set
 | ||||
|         add_tight_breakpoints_and_q_to_flipped_set(); | ||||
|         m_delta = del; | ||||
|         erase_tight_breakpoints_and_q_from_breakpoint_set(); | ||||
|     } while (true); | ||||
|     m_theta_D = this->m_d[m_q] / this->m_pivot_row[m_q]; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::process_flipped() { | ||||
|     init_a_wave_by_zeros(); | ||||
|     for (auto j : m_flipped_boxed) { | ||||
|         update_a_wave(signed_span_of_boxed(j), j); | ||||
|     } | ||||
| } | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::update_d_and_xB() { | ||||
|     for (auto j : this->non_basis()) { | ||||
|         this->m_d[j] -= m_theta_D * this->m_pivot_row[j]; | ||||
|     } | ||||
|     this->m_d[m_p] = - m_theta_D; | ||||
|     if (m_flipped_boxed.size() > 0) { | ||||
|         process_flipped(); | ||||
|         update_xb_after_bound_flips(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::calculate_beta_r_precisely() { | ||||
|     T t = numeric_traits<T>::zero(); | ||||
|     unsigned i = this->m_m(); | ||||
|     while (i--) { | ||||
|         T b = this->m_pivot_row_of_B_1[i]; | ||||
|         t += b * b; | ||||
|     } | ||||
|     m_betas[m_r] = t; | ||||
| } | ||||
| // see "Progress in the dual simplex method for large scale LP problems: practical dual phase 1 algorithms"
 | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::update_xb_after_bound_flips() { | ||||
|     this->m_factorization->solve_By(m_a_wave); | ||||
|     unsigned i = this->m_m(); | ||||
|     while (i--) { | ||||
|         this->m_x[this->m_basis[i]] -= m_a_wave[i]; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::one_iteration() { | ||||
|     unsigned number_of_rows_to_try = get_number_of_rows_to_try_for_leaving(); | ||||
|     unsigned offset_in_rows = my_random() % this->m_m(); | ||||
|     if (this->get_status() == TENTATIVE_DUAL_UNBOUNDED) { | ||||
|         number_of_rows_to_try = this->m_m(); | ||||
|     } else { | ||||
|         this->set_status(FEASIBLE); | ||||
|     } | ||||
|     pricing_loop(number_of_rows_to_try, offset_in_rows); | ||||
|     lean_assert(problem_is_dual_feasible()); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_core_solver<T, X>::solve() { // see the page 35
 | ||||
|     lean_assert(d_is_correct()); | ||||
|     lean_assert(problem_is_dual_feasible()); | ||||
|     lean_assert(this->basis_heading_is_correct()); | ||||
|     this->set_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("", *this->m_settings.get_message_ostream())){ | ||||
|             return; | ||||
|         } | ||||
|         one_iteration(); | ||||
|     } while (this->get_status() != FLOATING_POINT_ERROR && this->get_status() != DUAL_UNBOUNDED && this->get_status() != OPTIMAL && | ||||
|              this->m_iters_with_no_cost_growing <= this->m_settings.max_number_of_iterations_with_no_improvements | ||||
|              && this->total_iterations() <= this->m_settings.max_total_number_of_iterations); | ||||
| } | ||||
| } | ||||
							
								
								
									
										29
									
								
								src/util/lp/lp_dual_core_solver_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										29
									
								
								src/util/lp/lp_dual_core_solver_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,29 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <utility> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include "util/vector.h" | ||||
| #include <functional> | ||||
| #include "util/lp/lp_dual_core_solver.hpp" | ||||
| template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::start_with_initial_basis_and_make_it_dual_feasible(); | ||||
| template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::solve(); | ||||
| template lean::lp_dual_core_solver<double, double>::lp_dual_core_solver(lean::static_matrix<double, double>&, vector<bool>&, | ||||
|                                                                         vector<double>&, | ||||
|                                                                         vector<double>&, | ||||
|                                                                         vector<unsigned int>&, | ||||
|                                                                         vector<unsigned> &, | ||||
|                                                                         vector<int> &, | ||||
|                                                                         vector<double>&, | ||||
|                                                                         vector<lean::column_type>&, | ||||
|                                                                         vector<double>&, | ||||
|                                                                         vector<double>&, | ||||
|                                                                         lean::lp_settings&, const lean::column_namer&); | ||||
| template void lean::lp_dual_core_solver<double, double>::start_with_initial_basis_and_make_it_dual_feasible(); | ||||
| template void lean::lp_dual_core_solver<double, double>::solve(); | ||||
| template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::restore_non_basis(); | ||||
| template void lean::lp_dual_core_solver<double, double>::restore_non_basis(); | ||||
| template void lean::lp_dual_core_solver<double, double>::revert_to_previous_basis(); | ||||
| template void lean::lp_dual_core_solver<lean::mpq, lean::mpq>::revert_to_previous_basis(); | ||||
							
								
								
									
										79
									
								
								src/util/lp/lp_dual_simplex.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/util/lp/lp_dual_simplex.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/lp_utils.h" | ||||
| #include "util/lp/lp_solver.h" | ||||
| #include "util/lp/lp_dual_core_solver.h" | ||||
| 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; | ||||
|     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
 | ||||
|     vector<column_type> m_column_types_of_core_solver; | ||||
|     vector<column_type> m_column_types_of_logicals; | ||||
|     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(); | ||||
| 
 | ||||
|     void fix_logical_for_stage2(unsigned j); | ||||
| 
 | ||||
|     void fix_structural_for_stage2(unsigned j); | ||||
| 
 | ||||
|     void unmark_boxed_and_fixed_columns_and_fix_structural_costs(); | ||||
| 
 | ||||
|     void restore_right_sides(); | ||||
| 
 | ||||
|     void solve_for_stage2(); | ||||
| 
 | ||||
|     void fill_x_with_zeros(); | ||||
| 
 | ||||
|     void stage1(); | ||||
| 
 | ||||
|     void stage2(); | ||||
| 
 | ||||
|     void fill_first_stage_solver_fields(); | ||||
| 
 | ||||
|     column_type get_column_type(unsigned j); | ||||
| 
 | ||||
|     void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j); | ||||
| 
 | ||||
|     void fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j); | ||||
| 
 | ||||
|     void fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); | ||||
| 
 | ||||
|     void set_type_for_logical(unsigned j, column_type col_type) { | ||||
|         this->m_column_types_of_logicals[j - this->number_of_core_structurals()] = col_type; | ||||
|     } | ||||
| 
 | ||||
|     void fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, | ||||
|                                                               unsigned & slack_var, | ||||
|                                                                      unsigned & artificial); | ||||
| 
 | ||||
|     void augment_matrix_A_and_fill_x_and_allocate_some_fields(); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     void copy_m_b_aside_and_set_it_to_zeros(); | ||||
| 
 | ||||
|     void find_maximal_solution(); | ||||
| 
 | ||||
|     virtual T get_column_value(unsigned column) const { | ||||
|         return this->get_column_value_with_core_solver(column, m_core_solver); | ||||
|     } | ||||
| 
 | ||||
|     T get_current_cost() const; | ||||
| }; | ||||
| } | ||||
							
								
								
									
										362
									
								
								src/util/lp/lp_dual_simplex.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										362
									
								
								src/util/lp/lp_dual_simplex.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,362 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/lp_dual_simplex.h" | ||||
| namespace lean{ | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::decide_on_status_after_stage1() { | ||||
|     switch (m_core_solver->get_status()) { | ||||
|     case OPTIMAL: | ||||
|         if (this->m_settings.abs_val_is_smaller_than_artificial_tolerance(m_core_solver->get_cost())) { | ||||
|             this->m_status = FEASIBLE; | ||||
|         } else { | ||||
|             this->m_status = UNBOUNDED; | ||||
|         } | ||||
|         break; | ||||
|     case DUAL_UNBOUNDED: | ||||
|         lean_unreachable(); | ||||
|     case ITERATIONS_EXHAUSTED: | ||||
|         this->m_status = ITERATIONS_EXHAUSTED; | ||||
|         break; | ||||
|     case TIME_EXHAUSTED: | ||||
|         this->m_status = TIME_EXHAUSTED; | ||||
|         break; | ||||
|     case FLOATING_POINT_ERROR: | ||||
|         this->m_status = FLOATING_POINT_ERROR; | ||||
|         break; | ||||
|     default: | ||||
|         lean_unreachable(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::fix_logical_for_stage2(unsigned j) { | ||||
|     lean_assert(j >= this->number_of_core_structurals()); | ||||
|     switch (m_column_types_of_logicals[j - this->number_of_core_structurals()]) { | ||||
|     case column_type::low_bound: | ||||
|         m_low_bounds[j] = numeric_traits<T>::zero(); | ||||
|         m_column_types_of_core_solver[j] = column_type::low_bound; | ||||
|         m_can_enter_basis[j] = true; | ||||
|         break; | ||||
|     case column_type::fixed: | ||||
|         this->m_upper_bounds[j] = m_low_bounds[j] = numeric_traits<T>::zero(); | ||||
|         m_column_types_of_core_solver[j] = column_type::fixed; | ||||
|         m_can_enter_basis[j] = false; | ||||
|         break; | ||||
|     default: | ||||
|         lean_unreachable(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::fix_structural_for_stage2(unsigned j) { | ||||
|     column_info<T> * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]; | ||||
|     switch (ci->get_column_type()) { | ||||
|     case column_type::low_bound: | ||||
|         m_low_bounds[j] = numeric_traits<T>::zero(); | ||||
|         m_column_types_of_core_solver[j] = column_type::low_bound; | ||||
|         m_can_enter_basis[j] = true; | ||||
|         break; | ||||
|     case column_type::fixed: | ||||
|     case column_type::upper_bound: | ||||
|         lean_unreachable(); | ||||
|     case column_type::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] = column_type::boxed; | ||||
|         m_can_enter_basis[j] = true; | ||||
|         break; | ||||
|     case column_type::free_column: | ||||
|         m_can_enter_basis[j] = true; | ||||
|         m_column_types_of_core_solver[j] = column_type::free_column; | ||||
|         break; | ||||
|     default: | ||||
|         lean_unreachable(); | ||||
|     } | ||||
|     //    T cost_was = this->m_costs[j];
 | ||||
|     this->set_scaled_cost(j); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::unmark_boxed_and_fixed_columns_and_fix_structural_costs() { | ||||
|     unsigned j = this->m_A->column_count(); | ||||
|     while (j-- > this->number_of_core_structurals()) { | ||||
|         fix_logical_for_stage2(j); | ||||
|     } | ||||
|     j = this->number_of_core_structurals(); | ||||
|     while (j--) { | ||||
|         fix_structural_for_stage2(j); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::restore_right_sides() { | ||||
|     unsigned i = this->m_A->row_count(); | ||||
|     while (i--) { | ||||
|         this->m_b[i] = m_b_copy[i]; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::solve_for_stage2() { | ||||
|     m_core_solver->restore_non_basis(); | ||||
|     m_core_solver->solve_yB(m_core_solver->m_y); | ||||
|     m_core_solver->fill_reduced_costs_from_m_y_by_rows(); | ||||
|     m_core_solver->start_with_initial_basis_and_make_it_dual_feasible(); | ||||
|     m_core_solver->set_status(FEASIBLE); | ||||
|     m_core_solver->solve(); | ||||
|     switch (m_core_solver->get_status()) { | ||||
|     case OPTIMAL: | ||||
|         this->m_status = OPTIMAL; | ||||
|         break; | ||||
|     case DUAL_UNBOUNDED: | ||||
|         this->m_status = INFEASIBLE; | ||||
|         break; | ||||
|     case TIME_EXHAUSTED: | ||||
|         this->m_status = TIME_EXHAUSTED; | ||||
|         break; | ||||
|     case FLOATING_POINT_ERROR: | ||||
|         this->m_status = FLOATING_POINT_ERROR; | ||||
|         break; | ||||
|     default: | ||||
|         lean_unreachable(); | ||||
|     } | ||||
|     this->m_second_stage_iterations = m_core_solver->total_iterations(); | ||||
|     this->m_total_iterations = (this->m_first_stage_iterations + this->m_second_stage_iterations); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::fill_x_with_zeros() { | ||||
|     unsigned j = this->m_A->column_count(); | ||||
|     while (j--) { | ||||
|         this->m_x[j] = numeric_traits<T>::zero(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::stage1() { | ||||
|     lean_assert(m_core_solver == nullptr); | ||||
|     this->m_x.resize(this->m_A->column_count(), numeric_traits<T>::zero()); | ||||
|     if (this->m_settings.get_message_ostream() != nullptr) | ||||
|         this->print_statistics_on_A(*this->m_settings.get_message_ostream()); | ||||
|     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_nbasis, | ||||
|                                                   this->m_heading, | ||||
|                                                   this->m_costs, | ||||
|                                                   this->m_column_types_of_core_solver, | ||||
|                                                   this->m_low_bounds, | ||||
|                                                   this->m_upper_bounds, | ||||
|                                                   this->m_settings, | ||||
|                                                   *this); | ||||
|     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())) { | ||||
|         // skipping stage 1
 | ||||
|         m_core_solver->set_status(OPTIMAL); | ||||
|         m_core_solver->set_total_iterations(0); | ||||
|     } else { | ||||
|         m_core_solver->solve(); | ||||
|     } | ||||
|     decide_on_status_after_stage1(); | ||||
|     this->m_first_stage_iterations = m_core_solver->total_iterations(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::stage2() { | ||||
|     unmark_boxed_and_fixed_columns_and_fix_structural_costs(); | ||||
|     restore_right_sides(); | ||||
|     solve_for_stage2(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_solver_fields() { | ||||
|     unsigned slack_var = this->number_of_core_structurals(); | ||||
|     unsigned artificial = this->number_of_core_structurals() + this->m_slacks; | ||||
| 
 | ||||
|     for (unsigned row = 0; row < this->row_count(); row++) { | ||||
|         fill_first_stage_solver_fields_for_row_slack_and_artificial(row, slack_var, artificial); | ||||
|     } | ||||
|     fill_costs_and_bounds_and_column_types_for_the_first_stage_solver(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> column_type lp_dual_simplex<T, X>::get_column_type(unsigned j) { | ||||
|     lean_assert(j < this->m_A->column_count()); | ||||
|     if (j >= this->number_of_core_structurals()) { | ||||
|         return m_column_types_of_logicals[j - this->number_of_core_structurals()]; | ||||
|     } | ||||
|     return this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]]->get_column_type(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(unsigned j) { | ||||
|     // see 4.7 in the dissertation of Achim Koberstein
 | ||||
|     lean_assert(this->m_core_solver_columns_to_external_columns.find(j) != | ||||
|                 this->m_core_solver_columns_to_external_columns.end()); | ||||
| 
 | ||||
|     T free_bound = T(1e4); // see 4.8
 | ||||
|     unsigned jj = this->m_core_solver_columns_to_external_columns[j]; | ||||
|     lean_assert(this->m_map_from_var_index_to_column_info.find(jj) != this->m_map_from_var_index_to_column_info.end()); | ||||
|     column_info<T> * ci = this->m_map_from_var_index_to_column_info[jj]; | ||||
|     switch (ci->get_column_type()) { | ||||
|     case column_type::upper_bound: { | ||||
|         std::stringstream s; | ||||
|         s << "unexpected bound type " << j << " " | ||||
|           << column_type_to_string(get_column_type(j)); | ||||
|         throw_exception(s.str()); | ||||
|         break; | ||||
|     } | ||||
|     case column_type::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 column_type::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 column_type::boxed: | ||||
|         m_can_enter_basis[j] = false; | ||||
|         this->m_costs[j] = numeric_traits<T>::zero(); | ||||
|         this->m_upper_bounds[j] = this->m_low_bounds[j] =  numeric_traits<T>::zero(); // is it needed?
 | ||||
|         break; | ||||
|     default: | ||||
|         lean_unreachable(); | ||||
|     } | ||||
|     m_column_types_of_core_solver[j] = column_type::boxed; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(unsigned j) { | ||||
|     this->m_costs[j] = 0; | ||||
|     lean_assert(get_column_type(j) != column_type::upper_bound); | ||||
|     if ((m_can_enter_basis[j] = (get_column_type(j) == column_type::low_bound))) { | ||||
|         m_column_types_of_core_solver[j] = column_type::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] = column_type::fixed; | ||||
|         this->m_low_bounds[j] = numeric_traits<T>::zero(); | ||||
|         this->m_upper_bounds[j] = numeric_traits<T>::zero(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::fill_costs_and_bounds_and_column_types_for_the_first_stage_solver() { | ||||
|     unsigned j = this->m_A->column_count(); | ||||
|     while (j-- > this->number_of_core_structurals()) { // go over logicals here
 | ||||
|         fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_logical_column(j); | ||||
|     } | ||||
|     j = this->number_of_core_structurals(); | ||||
|     while (j--) { | ||||
|         fill_costs_bounds_types_and_can_enter_basis_for_the_first_stage_solver_structural_column(j); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::fill_first_stage_solver_fields_for_row_slack_and_artificial(unsigned row, | ||||
|                                                                                                                           unsigned & slack_var, | ||||
|                                                                                                                           unsigned & artificial) { | ||||
|     lean_assert(row < this->row_count()); | ||||
|     auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; | ||||
|     // we need to bring the program to the form Ax = b
 | ||||
|     T rs = this->m_b[row]; | ||||
|     switch (constraint.m_relation) { | ||||
|     case Equal: // no slack variable here
 | ||||
|         set_type_for_logical(artificial, column_type::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, column_type::low_bound); | ||||
|         (*this->m_A)(row, slack_var) = - numeric_traits<T>::one(); | ||||
|         if (rs > 0) { | ||||
|             // adding one artificial
 | ||||
|             set_type_for_logical(artificial, column_type::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, column_type::low_bound); | ||||
|         (*this->m_A)(row, slack_var) = numeric_traits<T>::one(); | ||||
|         if (rs < 0) { | ||||
|             // adding one artificial
 | ||||
|             set_type_for_logical(artificial, column_type::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; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::augment_matrix_A_and_fill_x_and_allocate_some_fields() { | ||||
|     this->count_slacks_and_artificials(); | ||||
|     this->m_A->add_columns_at_the_end(this->m_slacks + this->m_artificials); | ||||
|     unsigned n = this->m_A->column_count(); | ||||
|     this->m_column_types_of_core_solver.resize(n); | ||||
|     m_column_types_of_logicals.resize(this->m_slacks + this->m_artificials); | ||||
|     this->m_costs.resize(n); | ||||
|     this->m_upper_bounds.resize(n); | ||||
|     this->m_low_bounds.resize(n); | ||||
|     m_can_enter_basis.resize(n); | ||||
|     this->m_basis.resize(this->m_A->row_count()); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::copy_m_b_aside_and_set_it_to_zeros() { | ||||
|     for (unsigned i = 0; i < this->m_b.size(); i++) { | ||||
|         m_b_copy.push_back(this->m_b[i]); | ||||
|         this->m_b[i] = numeric_traits<T>::zero(); // preparing for the first stage
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_dual_simplex<T, X>::find_maximal_solution(){ | ||||
|     if (this->problem_is_empty()) { | ||||
|         this->m_status = lp_status::EMPTY; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     this->flip_costs(); // do it for now, todo ( remove the flipping)
 | ||||
| 
 | ||||
|     this->cleanup(); | ||||
|     if (this->m_status == INFEASIBLE) { | ||||
|         return; | ||||
|     } | ||||
|     this->fill_matrix_A_and_init_right_side(); | ||||
|     this->fill_m_b(); | ||||
|     this->scale(); | ||||
|     augment_matrix_A_and_fill_x_and_allocate_some_fields(); | ||||
|     fill_first_stage_solver_fields(); | ||||
|     copy_m_b_aside_and_set_it_to_zeros(); | ||||
|     stage1(); | ||||
|     if (this->m_status == FEASIBLE) { | ||||
|         stage2(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> T lp_dual_simplex<T, X>::get_current_cost() const { | ||||
|     T ret = numeric_traits<T>::zero(); | ||||
|     for (auto it : this->m_map_from_var_index_to_column_info) { | ||||
|         ret += this->get_column_cost_value(it.first, it.second); | ||||
|     } | ||||
|     return -ret; // we flip costs for now
 | ||||
| } | ||||
| } | ||||
							
								
								
									
										9
									
								
								src/util/lp/lp_dual_simplex_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										9
									
								
								src/util/lp/lp_dual_simplex_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,9 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/lp_dual_simplex.hpp" | ||||
| template lean::mpq lean::lp_dual_simplex<lean::mpq, lean::mpq>::get_current_cost() const; | ||||
| template void lean::lp_dual_simplex<lean::mpq, lean::mpq>::find_maximal_solution(); | ||||
| template double lean::lp_dual_simplex<double, double>::get_current_cost() const; | ||||
| template void lean::lp_dual_simplex<double, double>::find_maximal_solution(); | ||||
							
								
								
									
										986
									
								
								src/util/lp/lp_primal_core_solver.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										986
									
								
								src/util/lp/lp_primal_core_solver.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,986 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include <list> | ||||
| #include <limits> | ||||
| #include <unordered_map> | ||||
| #include <sstream> | ||||
| #include <string> | ||||
| #include "util/vector.h" | ||||
| #include <set> | ||||
| #include <math.h> | ||||
| #include <cstdlib> | ||||
| #include <algorithm> | ||||
| #include "util/lp/lu.h" | ||||
| #include "util/lp/lp_solver.h" | ||||
| #include "util/lp/static_matrix.h" | ||||
| #include "util/lp/core_solver_pretty_printer.h" | ||||
| #include "util/lp/lp_core_solver_base.h" | ||||
| #include "util/lp/breakpoint.h" | ||||
| #include "util/lp/binary_heap_priority_queue.h" | ||||
| #include "util/lp/int_set.h" | ||||
| #include "util/lp/iterator_on_row.h" | ||||
| namespace lean { | ||||
| 
 | ||||
| // This core solver solves (Ax=b, low_bound_values \leq x \leq upper_bound_values, maximize costs*x )
 | ||||
| // The right side b is given implicitly by x and the basis
 | ||||
| template <typename T, typename X> | ||||
| class lp_primal_core_solver:public lp_core_solver_base<T, X> { | ||||
| public: | ||||
|     // m_sign_of_entering is set to 1 if the entering variable needs
 | ||||
|     // to grow and is set to -1  otherwise
 | ||||
|     unsigned m_column_norm_update_counter; | ||||
|     T m_enter_price_eps; | ||||
|     int m_sign_of_entering_delta; | ||||
|     vector<breakpoint<X>> m_breakpoints; | ||||
|     binary_heap_priority_queue<X> m_breakpoint_indices_queue; | ||||
|     indexed_vector<T> m_beta; // see Swietanowski working vector beta for column norms
 | ||||
|     T m_epsilon_of_reduced_cost = T(1)/T(10000000); | ||||
|     vector<T> m_costs_backup; | ||||
|     T m_converted_harris_eps; | ||||
|     unsigned m_inf_row_index_for_tableau; | ||||
|     bool m_bland_mode_tableau; | ||||
|     int_set m_left_basis_tableau; | ||||
|     unsigned m_bland_mode_threshold = 1000; | ||||
|     unsigned m_left_basis_repeated; | ||||
|     vector<unsigned> m_leaving_candidates; | ||||
|     //    T m_converted_harris_eps = convert_struct<T, double>::convert(this->m_settings.harris_feasibility_tolerance);
 | ||||
|     std::list<unsigned> m_non_basis_list; | ||||
|     void sort_non_basis(); | ||||
|     void sort_non_basis_rational(); | ||||
|     int choose_entering_column(unsigned number_of_benefitial_columns_to_go_over); | ||||
|     int choose_entering_column_tableau(); | ||||
|     int choose_entering_column_presize(unsigned number_of_benefitial_columns_to_go_over); | ||||
|     int find_leaving_and_t_with_breakpoints(unsigned entering, X & t); | ||||
|     // int find_inf_row() {
 | ||||
|     //     // mimicing CLP : todo : use a heap
 | ||||
|     //     int j = -1;
 | ||||
|     //     for (unsigned k : this->m_inf_set.m_index) {
 | ||||
|     //         if (k < static_cast<unsigned>(j))
 | ||||
|     //             j = static_cast<int>(k);
 | ||||
|     //     }
 | ||||
|     //     if (j == -1)
 | ||||
|     //         return -1;
 | ||||
|     //     return this->m_basis_heading[j];
 | ||||
|     //     #if 0 
 | ||||
|     //     vector<int> choices;
 | ||||
|     //     unsigned len = 100000000; 
 | ||||
|     //     for (unsigned j : this->m_inf_set.m_index) {
 | ||||
|     //         int i = this->m_basis_heading[j];
 | ||||
|     //         lean_assert(i >= 0);
 | ||||
|     //         unsigned row_len = this->m_A.m_rows[i].size();
 | ||||
|     //         if (row_len < len) {
 | ||||
|     //             choices.clear();
 | ||||
|     //             choices.push_back(i);
 | ||||
|     //             len = row_len;
 | ||||
|     //             if (my_random() % 10) break;
 | ||||
|     //         } else if (row_len == len) {
 | ||||
|     //             choices.push_back(i);
 | ||||
|     //             if (my_random() % 10) break;
 | ||||
|     //         }
 | ||||
|     //     }
 | ||||
| 
 | ||||
|     //     if (choices.size() == 0)
 | ||||
|     //         return -1;
 | ||||
| 
 | ||||
|     //     if (choices.size() == 1)
 | ||||
|     //         return choices[0];
 | ||||
|          | ||||
|     //     unsigned k = my_random() % choices.size();
 | ||||
|     //     return choices[k];
 | ||||
|     //     #endif
 | ||||
|     // }
 | ||||
| 
 | ||||
| 
 | ||||
|     bool column_is_benefitial_for_entering_basis_on_sign_row_strategy(unsigned j, int sign) const { | ||||
|         // sign = 1 means the x of the basis column of the row has to grow to become feasible, when the coeff before j is neg, or x - has to diminish when the coeff is pos
 | ||||
|         // we have xbj = -aj * xj
 | ||||
|         lean_assert(this->m_basis_heading[j] < 0); | ||||
|         lean_assert(this->column_is_feasible(j)); | ||||
|         switch (this->m_column_types[j]) { | ||||
|         case column_type::free_column: return true; | ||||
|         case column_type::fixed: return false; | ||||
|         case column_type::low_bound: | ||||
|             if (sign < 0) | ||||
|                 return true; | ||||
|             return !this->x_is_at_low_bound(j); | ||||
|         case column_type::upper_bound: | ||||
|             if (sign > 0) | ||||
|                 return true; | ||||
|             return !this->x_is_at_upper_bound(j); | ||||
|         case column_type::boxed: | ||||
|             if (sign < 0) | ||||
|                 return !this->x_is_at_low_bound(j); | ||||
|             return !this->x_is_at_upper_bound(j); | ||||
|         } | ||||
| 
 | ||||
|         lean_assert(false); // cannot be here
 | ||||
|         return false; | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     bool needs_to_grow(unsigned bj) const { | ||||
|         lean_assert(!this->column_is_feasible(bj)); | ||||
|         switch(this->m_column_types[bj]) { | ||||
|         case column_type::free_column: | ||||
|             return false; | ||||
|         case column_type::fixed:  | ||||
|         case column_type::low_bound: | ||||
|         case column_type::boxed: | ||||
|             return this-> x_below_low_bound(bj); | ||||
|         default: | ||||
|             return false; | ||||
|         } | ||||
|         lean_assert(false); // unreachable
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     int inf_sign_of_column(unsigned bj) const { | ||||
|         lean_assert(!this->column_is_feasible(bj)); | ||||
|         switch(this->m_column_types[bj]) { | ||||
|         case column_type::free_column: | ||||
|             return 0; | ||||
|         case column_type::low_bound: | ||||
|             return 1; | ||||
|         case column_type::fixed:  | ||||
|         case column_type::boxed:             | ||||
|             return this->x_above_upper_bound(bj)? -1: 1; | ||||
|         default: | ||||
|             return -1; | ||||
|         } | ||||
|         lean_assert(false); // unreachable
 | ||||
|         return 0; | ||||
|          | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     bool monoid_can_decrease(const row_cell<T> & rc) const { | ||||
|         unsigned j = rc.m_j; | ||||
|         lean_assert(this->column_is_feasible(j)); | ||||
|         switch (this->m_column_types[j]) { | ||||
|         case column_type::free_column: | ||||
|             return true; | ||||
|         case column_type::fixed: | ||||
|             return false; | ||||
|         case column_type::low_bound: | ||||
|             if (is_pos(rc.get_val())) { | ||||
|                 return this->x_above_low_bound(j); | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         case column_type::upper_bound: | ||||
|             if (is_pos(rc.get_val())) { | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             return this->x_below_upper_bound(j); | ||||
|         case column_type::boxed: | ||||
|             if (is_pos(rc.get_val())) { | ||||
|                 return this->x_above_low_bound(j); | ||||
|             } | ||||
| 
 | ||||
|             return this->x_below_upper_bound(j); | ||||
|         default: | ||||
|             return false; | ||||
|         } | ||||
|         lean_assert(false); // unreachable
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     bool monoid_can_increase(const row_cell<T> & rc) const { | ||||
|         unsigned j = rc.m_j; | ||||
|         lean_assert(this->column_is_feasible(j)); | ||||
|         switch (this->m_column_types[j]) { | ||||
|         case column_type::free_column: | ||||
|             return true; | ||||
|         case column_type::fixed: | ||||
|             return false; | ||||
|         case column_type::low_bound: | ||||
|             if (is_neg(rc.get_val())) { | ||||
|                 return this->x_above_low_bound(j); | ||||
|             } | ||||
| 
 | ||||
|             return true; | ||||
|         case column_type::upper_bound: | ||||
|             if (is_neg(rc.get_val())) { | ||||
|                 return true; | ||||
|             } | ||||
| 
 | ||||
|             return this->x_below_upper_bound(j); | ||||
|         case column_type::boxed: | ||||
|             if (is_neg(rc.get_val())) { | ||||
|                 return this->x_above_low_bound(j); | ||||
|             } | ||||
| 
 | ||||
|             return this->x_below_upper_bound(j); | ||||
|         default: | ||||
|             return false; | ||||
|         } | ||||
|         lean_assert(false); // unreachable
 | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     unsigned get_number_of_basic_vars_that_might_become_inf(unsigned j) const { // consider looking at the signs here: todo
 | ||||
|         unsigned r = 0; | ||||
|         for (auto & cc : this->m_A.m_columns[j]) { | ||||
|             unsigned k = this->m_basis[cc.m_i]; | ||||
|             if (this->m_column_types[k] != column_type::free_column) | ||||
|                 r++; | ||||
|         } | ||||
|         return r; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     int find_beneficial_column_in_row_tableau_rows_bland_mode(int i, T & a_ent) { | ||||
|         int j = -1; | ||||
|         unsigned bj = this->m_basis[i]; | ||||
|         bool bj_needs_to_grow = needs_to_grow(bj); | ||||
|         for (const row_cell<T>& rc : this->m_A.m_rows[i]) { | ||||
|             if (rc.m_j == bj) | ||||
|                 continue; | ||||
|             if (bj_needs_to_grow) { | ||||
|                 if (!monoid_can_decrease(rc)) | ||||
|                     continue; | ||||
|             } else { | ||||
|                 if (!monoid_can_increase(rc)) | ||||
|                     continue; | ||||
|             } | ||||
|             if (rc.m_j < static_cast<unsigned>(j) ) { | ||||
|                 j = rc.m_j; | ||||
|                 a_ent = rc.m_value; | ||||
|             } | ||||
|         } | ||||
|         if (j == -1) { | ||||
|             m_inf_row_index_for_tableau = i; | ||||
|         } | ||||
|              | ||||
|         return j; | ||||
|     } | ||||
|      | ||||
|     int find_beneficial_column_in_row_tableau_rows(int i, T & a_ent) { | ||||
|         if (m_bland_mode_tableau) | ||||
|             return find_beneficial_column_in_row_tableau_rows_bland_mode(i, a_ent); | ||||
|         // a short row produces short infeasibility explanation and benefits at least one pivot operation
 | ||||
|         vector<const row_cell<T>*> choices; | ||||
|         unsigned num_of_non_free_basics = 1000000; | ||||
|         unsigned len = 100000000; | ||||
|         unsigned bj = this->m_basis[i]; | ||||
|         bool bj_needs_to_grow = needs_to_grow(bj); | ||||
|         for (const row_cell<T>& rc : this->m_A.m_rows[i]) { | ||||
|             unsigned j = rc.m_j; | ||||
|             if (j == bj) | ||||
|                 continue; | ||||
|             if (bj_needs_to_grow) { | ||||
|                 if (!monoid_can_decrease(rc)) | ||||
|                     continue; | ||||
|             } else { | ||||
|                 if (!monoid_can_increase(rc)) | ||||
|                     continue; | ||||
|             } | ||||
|             unsigned damage = get_number_of_basic_vars_that_might_become_inf(j); | ||||
|             if (damage < num_of_non_free_basics) { | ||||
|                 num_of_non_free_basics = damage; | ||||
|                 len = this->m_A.m_columns[j].size(); | ||||
|                 choices.clear(); | ||||
|                 choices.push_back(&rc); | ||||
|             } else if (damage == num_of_non_free_basics && | ||||
|                        this->m_A.m_columns[j].size() <= len && (my_random() % 2)) { | ||||
|                 choices.push_back(&rc); | ||||
|                 len = this->m_A.m_columns[j].size(); | ||||
|             } | ||||
|         } | ||||
|          | ||||
| 
 | ||||
|         if (choices.size() == 0) { | ||||
|             m_inf_row_index_for_tableau = i; | ||||
|             return -1; | ||||
|         } | ||||
|         const row_cell<T>* rc = choices.size() == 1? choices[0] : | ||||
|             choices[my_random() % choices.size()]; | ||||
| 
 | ||||
|         a_ent = rc->m_value; | ||||
|         return rc->m_j; | ||||
|     } | ||||
|     static X positive_infinity() { | ||||
|         return convert_struct<X, unsigned>::convert(std::numeric_limits<unsigned>::max()); | ||||
|     } | ||||
| 
 | ||||
|     bool get_harris_theta(X & theta); | ||||
| 
 | ||||
|     void restore_harris_eps() { m_converted_harris_eps = convert_struct<T, double>::convert(this->m_settings.harris_feasibility_tolerance); } | ||||
|     void zero_harris_eps() { m_converted_harris_eps = zero_of_type<T>(); } | ||||
|     int find_leaving_on_harris_theta(X const & harris_theta, X & t); | ||||
|     bool try_jump_to_another_bound_on_entering(unsigned entering, const X & theta, X & t, bool & unlimited); | ||||
|     bool try_jump_to_another_bound_on_entering_unlimited(unsigned entering, X & t); | ||||
|     int find_leaving_and_t(unsigned entering, X & t); | ||||
|     int find_leaving_and_t_precise(unsigned entering, X & t); | ||||
|     int find_leaving_and_t_tableau(unsigned entering, X & t); | ||||
|   | ||||
|     void limit_theta(const X & lim, X & theta, bool & unlimited) { | ||||
|         if (unlimited) { | ||||
|             theta = lim; | ||||
|             unlimited = false; | ||||
|         } else { | ||||
|             theta = std::min(lim, theta); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { | ||||
|         lean_assert(m < 0 && this->m_column_types[j] == column_type::upper_bound); | ||||
|         limit_inf_on_upper_bound_m_neg(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     void limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(unsigned j, const T & m, X & theta, bool & unlimited) { | ||||
|         lean_assert(m < 0 && this->m_column_types[j] == column_type::low_bound); | ||||
|         limit_inf_on_bound_m_neg(m, this->m_x[j], this->m_low_bounds[j], theta, unlimited); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     void limit_theta_on_basis_column_for_inf_case_m_pos_low_bound(unsigned j, const T & m, X & theta, bool & unlimited) { | ||||
|         lean_assert(m > 0 && this->m_column_types[j] == column_type::low_bound); | ||||
|         limit_inf_on_low_bound_m_pos(m, this->m_x[j], this->m_low_bounds[j], theta, unlimited); | ||||
|     } | ||||
| 
 | ||||
|     void limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(unsigned j, const T & m, X & theta, bool & unlimited) { | ||||
|         lean_assert(m > 0 && this->m_column_types[j] == column_type::upper_bound); | ||||
|         limit_inf_on_bound_m_pos(m, this->m_x[j], this->m_upper_bounds[j], theta, unlimited); | ||||
|     }; | ||||
| 
 | ||||
|     X harris_eps_for_bound(const X & bound) const { return ( convert_struct<X, int>::convert(1) +  abs(bound)/10) * m_converted_harris_eps/3; | ||||
|     } | ||||
| 
 | ||||
|     void get_bound_on_variable_and_update_leaving_precisely(unsigned j, vector<unsigned> & leavings, T m, X & t, T & abs_of_d_of_leaving); | ||||
| 
 | ||||
|     vector<T> m_low_bounds_dummy; // needed for the base class only
 | ||||
| 
 | ||||
|     X get_max_bound(vector<X> & b); | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
|     void check_Ax_equal_b(); | ||||
|     void check_the_bounds(); | ||||
|     void check_bound(unsigned i); | ||||
|     void check_correctness(); | ||||
| #endif | ||||
| 
 | ||||
|     // from page 183 of Istvan Maros's book
 | ||||
|     // the basis structures have not changed yet
 | ||||
|     void update_reduced_costs_from_pivot_row(unsigned entering, unsigned leaving); | ||||
| 
 | ||||
|     // return 0 if the reduced cost at entering is close enough to the refreshed
 | ||||
|     // 1 if it is way off, and 2 if it is unprofitable
 | ||||
|     int refresh_reduced_cost_at_entering_and_check_that_it_is_off(unsigned entering); | ||||
| 
 | ||||
|     void backup_and_normalize_costs(); | ||||
| 
 | ||||
|     void init_run(); | ||||
| 
 | ||||
|     void calc_working_vector_beta_for_column_norms(); | ||||
| 
 | ||||
|     void advance_on_entering_and_leaving(int entering, int leaving, X & t); | ||||
|     void advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t); | ||||
|     void advance_on_entering_equal_leaving(int entering, X & t); | ||||
|     void advance_on_entering_equal_leaving_tableau(int entering, X & t); | ||||
| 
 | ||||
|     bool need_to_switch_costs() const { | ||||
|         if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows) | ||||
|             return false; | ||||
|         //        lean_assert(calc_current_x_is_feasible() == current_x_is_feasible());
 | ||||
|         return this->current_x_is_feasible() == this->m_using_infeas_costs; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     void advance_on_entering(int entering); | ||||
|     void advance_on_entering_tableau(int entering); | ||||
|     void advance_on_entering_precise(int entering); | ||||
|     void push_forward_offset_in_non_basis(unsigned & offset_in_nb); | ||||
| 
 | ||||
|     unsigned get_number_of_non_basic_column_to_try_for_enter(); | ||||
| 
 | ||||
|     void print_column_norms(std::ostream & out); | ||||
| 
 | ||||
|     // returns the number of iterations
 | ||||
|     unsigned solve(); | ||||
| 
 | ||||
|     lu<T, X> * factorization() {return this->m_factorization;} | ||||
| 
 | ||||
|     void delete_factorization(); | ||||
| 
 | ||||
|     // according to Swietanowski, " A new steepest edge approximation for the simplex method for linear programming"
 | ||||
|     void init_column_norms(); | ||||
| 
 | ||||
|     T calculate_column_norm_exactly(unsigned j); | ||||
| 
 | ||||
|     void update_or_init_column_norms(unsigned entering, unsigned leaving); | ||||
| 
 | ||||
|     // following Swietanowski - A new steepest ...
 | ||||
|     void update_column_norms(unsigned entering, unsigned leaving); | ||||
| 
 | ||||
|     T calculate_norm_of_entering_exactly(); | ||||
| 
 | ||||
|     void find_feasible_solution(); | ||||
| 
 | ||||
|     bool is_tiny() const {return this->m_m < 10 && this->m_n < 20;} | ||||
| 
 | ||||
|     void one_iteration(); | ||||
|     void one_iteration_tableau(); | ||||
| 
 | ||||
|     void advance_on_entering_and_leaving_tableau_rows(int entering, int leaving, const X &theta ) { | ||||
|         this->update_basis_and_x_tableau(entering, leaving, theta); | ||||
|         this->update_column_in_inf_set(entering); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     int find_leaving_tableau_rows(X & new_val_for_leaving) { | ||||
|         int j = -1; | ||||
|         for (unsigned k : this->m_inf_set.m_index) { | ||||
|             if (k < static_cast<unsigned>(j)) | ||||
|                 j = static_cast<int>(k); | ||||
|         } | ||||
|         if (j == -1) | ||||
|             return -1; | ||||
| 
 | ||||
|         lean_assert(!this->column_is_feasible(j)); | ||||
|         switch (this->m_column_types[j]) { | ||||
|         case column_type::fixed: | ||||
|         case column_type::upper_bound: | ||||
|             new_val_for_leaving = this->m_upper_bounds[j]; | ||||
|             break; | ||||
|         case column_type::low_bound: | ||||
|             new_val_for_leaving = this->m_low_bounds[j]; | ||||
|             break; | ||||
|         case column_type::boxed: | ||||
|             if (this->x_above_upper_bound(j)) | ||||
|                 new_val_for_leaving = this->m_upper_bounds[j]; | ||||
|             else | ||||
|                 new_val_for_leaving = this->m_low_bounds[j]; | ||||
|             break; | ||||
|         default: | ||||
|             lean_assert(false); | ||||
|         } | ||||
|         return j; | ||||
|     } | ||||
|      | ||||
|     void one_iteration_tableau_rows() { | ||||
|         X new_val_for_leaving; | ||||
|         int leaving = find_leaving_tableau_rows(new_val_for_leaving); | ||||
|         if (leaving == -1) { | ||||
|             this->set_status(OPTIMAL); | ||||
|             return; | ||||
|         } | ||||
| 
 | ||||
|         if (!m_bland_mode_tableau) { | ||||
|             if (m_left_basis_tableau.contains(leaving)) { | ||||
|                 if (++m_left_basis_repeated > m_bland_mode_threshold) { | ||||
|                     m_bland_mode_tableau = true; | ||||
|                 } | ||||
|             } else { | ||||
|                 m_left_basis_tableau.insert(leaving); | ||||
|             } | ||||
|         } | ||||
|         T a_ent; | ||||
|         int entering = find_beneficial_column_in_row_tableau_rows(this->m_basis_heading[leaving], a_ent); | ||||
|         if (entering == -1) { | ||||
|             this->set_status(INFEASIBLE); | ||||
|             return; | ||||
|         } | ||||
|         X theta = (this->m_x[leaving] - new_val_for_leaving) / a_ent; | ||||
|         advance_on_entering_and_leaving_tableau_rows(entering, leaving, theta ); | ||||
|         lean_assert(this->m_x[leaving] == new_val_for_leaving); | ||||
|         if (this->current_x_is_feasible()) | ||||
|             this->set_status(OPTIMAL); | ||||
|     } | ||||
| 
 | ||||
|     void fill_breakpoints_array(unsigned entering); | ||||
| 
 | ||||
|     void try_add_breakpoint_in_row(unsigned i); | ||||
| 
 | ||||
|     void clear_breakpoints(); | ||||
| 
 | ||||
|     void change_slope_on_breakpoint(unsigned entering, breakpoint<X> * b, T & slope_at_entering); | ||||
|     void advance_on_sorted_breakpoints(unsigned entering); | ||||
| 
 | ||||
|     void update_basis_and_x_with_comparison(unsigned entering, unsigned leaving, X delta); | ||||
| 
 | ||||
|     void decide_on_status_when_cannot_find_entering() { | ||||
|         lean_assert(!need_to_switch_costs()); | ||||
|         this->set_status(this->current_x_is_feasible()? OPTIMAL: INFEASIBLE); | ||||
|     } | ||||
| 
 | ||||
|     // void limit_theta_on_basis_column_for_feas_case_m_neg(unsigned j, const T & m, X & theta) {
 | ||||
|     //     lean_assert(m < 0);
 | ||||
|     //     lean_assert(this->m_column_type[j] == low_bound || this->m_column_type[j] == boxed);
 | ||||
|     //     const X & eps = harris_eps_for_bound(this->m_low_bounds[j]);
 | ||||
|     //     if (this->above_bound(this->m_x[j], this->m_low_bounds[j])) {
 | ||||
|     //         theta = std::min((this->m_low_bounds[j] -this->m_x[j] - eps) / m, theta);
 | ||||
|     //         if (theta < zero_of_type<X>()) theta = zero_of_type<X>();
 | ||||
|     //     }
 | ||||
|     // }
 | ||||
| 
 | ||||
|     void limit_theta_on_basis_column_for_feas_case_m_neg_no_check(unsigned j, const T & m, X & theta, bool & unlimited) { | ||||
|         lean_assert(m < 0); | ||||
|         const X& eps = harris_eps_for_bound(this->m_low_bounds[j]); | ||||
|         limit_theta((this->m_low_bounds[j] - this->m_x[j] - eps) / m, theta, unlimited); | ||||
|         if (theta < zero_of_type<X>()) theta = zero_of_type<X>(); | ||||
|     } | ||||
| 
 | ||||
|     bool limit_inf_on_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { | ||||
|         // x gets smaller
 | ||||
|         lean_assert(m < 0); | ||||
|         if (numeric_traits<T>::precise()) { | ||||
|             if (this->below_bound(x, bound)) return false; | ||||
|             if (this->above_bound(x, bound)) { | ||||
|                 limit_theta((bound - x) / m, theta, unlimited); | ||||
|             } else { | ||||
|                 theta = zero_of_type<X>(); | ||||
|                 unlimited = false; | ||||
|             } | ||||
|         } else { | ||||
|             const X& eps = harris_eps_for_bound(bound); | ||||
|             if (this->below_bound(x, bound)) return false; | ||||
|             if (this->above_bound(x, bound)) { | ||||
|                 limit_theta((bound - x - eps) / m, theta, unlimited); | ||||
|             } else { | ||||
|                 theta = zero_of_type<X>(); | ||||
|                 unlimited = false; | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     bool limit_inf_on_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { | ||||
|         // x gets larger
 | ||||
|         lean_assert(m > 0); | ||||
|         if (numeric_traits<T>::precise()) { | ||||
|             if (this->above_bound(x, bound)) return false; | ||||
|             if (this->below_bound(x, bound)) { | ||||
|                 limit_theta((bound - x) / m, theta, unlimited); | ||||
|             } else { | ||||
|                 theta = zero_of_type<X>(); | ||||
|                 unlimited = false; | ||||
|             } | ||||
|         } else { | ||||
|             const X& eps = harris_eps_for_bound(bound); | ||||
|             if (this->above_bound(x, bound)) return false; | ||||
|             if (this->below_bound(x, bound)) { | ||||
|                 limit_theta((bound - x + eps) / m, theta, unlimited); | ||||
|             } else { | ||||
|                 theta = zero_of_type<X>(); | ||||
|                 unlimited = false; | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     void limit_inf_on_low_bound_m_pos(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { | ||||
|         if (numeric_traits<T>::precise()) { | ||||
|             // x gets larger
 | ||||
|             lean_assert(m > 0); | ||||
|             if (this->below_bound(x, bound)) { | ||||
|                 limit_theta((bound - x) / m, theta, unlimited); | ||||
|             } | ||||
|         } | ||||
|         else { | ||||
|             // x gets larger
 | ||||
|             lean_assert(m > 0); | ||||
|             const X& eps = harris_eps_for_bound(bound); | ||||
|             if (this->below_bound(x, bound)) { | ||||
|                 limit_theta((bound - x + eps) / m, theta, unlimited); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void limit_inf_on_upper_bound_m_neg(const T & m, const X & x, const X & bound, X & theta, bool & unlimited) { | ||||
|         // x gets smaller
 | ||||
|         lean_assert(m < 0); | ||||
|         const X& eps = harris_eps_for_bound(bound); | ||||
|         if (this->above_bound(x, bound)) { | ||||
|             limit_theta((bound - x - eps) / m, theta, unlimited); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void limit_theta_on_basis_column_for_inf_case_m_pos_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { | ||||
|         //        lean_assert(m > 0 && this->m_column_type[j] == column_type::boxed);
 | ||||
|         const X & x = this->m_x[j]; | ||||
|         const X & lbound = this->m_low_bounds[j]; | ||||
| 
 | ||||
|         if (this->below_bound(x, lbound)) { | ||||
|             const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); | ||||
|             limit_theta((lbound - x + eps) / m, theta, unlimited); | ||||
|         } else { | ||||
|             const X & ubound = this->m_upper_bounds[j]; | ||||
|             if (this->below_bound(x, ubound)){ | ||||
|                 const X& eps = harris_eps_for_bound(ubound); | ||||
|                 limit_theta((ubound - x + eps) / m, theta, unlimited); | ||||
|             } else if (!this->above_bound(x, ubound)) { | ||||
|                 theta = zero_of_type<X>(); | ||||
|                 unlimited = false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void limit_theta_on_basis_column_for_inf_case_m_neg_boxed(unsigned j, const T & m, X & theta, bool & unlimited) { | ||||
|         //  lean_assert(m < 0 && this->m_column_type[j] == column_type::boxed);
 | ||||
|         const X & x = this->m_x[j]; | ||||
|         const X & ubound = this->m_upper_bounds[j]; | ||||
|         if (this->above_bound(x, ubound)) { | ||||
|             const X& eps = harris_eps_for_bound(ubound); | ||||
|             limit_theta((ubound - x - eps) / m, theta, unlimited); | ||||
|         } else { | ||||
|             const X & lbound = this->m_low_bounds[j]; | ||||
|             if (this->above_bound(x, lbound)){ | ||||
|                 const X& eps = harris_eps_for_bound(lbound); | ||||
|                 limit_theta((lbound - x - eps) / m, theta, unlimited); | ||||
|             } else if (!this->below_bound(x, lbound)) { | ||||
|                 theta = zero_of_type<X>(); | ||||
|                 unlimited = false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     void limit_theta_on_basis_column_for_feas_case_m_pos(unsigned j, const T & m, X & theta, bool & unlimited) { | ||||
|         lean_assert(m > 0); | ||||
|         const T& eps = harris_eps_for_bound(this->m_upper_bounds[j]); | ||||
|         if (this->below_bound(this->m_x[j], this->m_upper_bounds[j])) { | ||||
|             limit_theta((this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); | ||||
|             if (theta < zero_of_type<X>()) { | ||||
|                 theta = zero_of_type<X>(); | ||||
|                 unlimited = false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void limit_theta_on_basis_column_for_feas_case_m_pos_no_check(unsigned j, const T & m, X & theta, bool & unlimited ) { | ||||
|         lean_assert(m > 0); | ||||
|         const X& eps = harris_eps_for_bound(this->m_upper_bounds[j]); | ||||
|         limit_theta( (this->m_upper_bounds[j] - this->m_x[j] + eps) / m, theta, unlimited); | ||||
|         if (theta < zero_of_type<X>()) { | ||||
|             theta = zero_of_type<X>(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     // j is a basic column or the entering, in any case x[j] has to stay feasible.
 | ||||
|     // m is the multiplier. updating t in a way that holds the following
 | ||||
|     // x[j] + t * m >=  this->m_low_bounds[j]- harris_feasibility_tolerance ( if m < 0 )
 | ||||
|     // or
 | ||||
|     // x[j] + t * m <= this->m_upper_bounds[j] + harris_feasibility_tolerance ( if m > 0)
 | ||||
|     void limit_theta_on_basis_column(unsigned j, T m, X & theta, bool & unlimited) { | ||||
|         switch (this->m_column_types[j]) { | ||||
|         case column_type::free_column: break; | ||||
|         case column_type::upper_bound: | ||||
|             if (this->current_x_is_feasible()) { | ||||
|                 if (m > 0) | ||||
|                     limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, unlimited); | ||||
|             } else { // inside of feasibility_loop
 | ||||
|                 if (m > 0) | ||||
|                     limit_theta_on_basis_column_for_inf_case_m_pos_upper_bound(j, m, theta, unlimited); | ||||
|                 else | ||||
|                     limit_theta_on_basis_column_for_inf_case_m_neg_upper_bound(j, m, theta, unlimited); | ||||
|             } | ||||
|             break; | ||||
|         case column_type::low_bound: | ||||
|             if (this->current_x_is_feasible()) { | ||||
|                 if (m < 0) | ||||
|                     limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); | ||||
|             } else { | ||||
|                 if (m < 0) | ||||
|                     limit_theta_on_basis_column_for_inf_case_m_neg_low_bound(j, m, theta, unlimited); | ||||
|                 else | ||||
|                     limit_theta_on_basis_column_for_inf_case_m_pos_low_bound(j, m, theta, unlimited); | ||||
|             } | ||||
|             break; | ||||
|             // case fixed:
 | ||||
|             //     if (get_this->current_x_is_feasible()) {
 | ||||
|             //         theta = zero_of_type<X>();
 | ||||
|             //         break;
 | ||||
|             //     }
 | ||||
|             //     if (m < 0)
 | ||||
|             //         limit_theta_on_basis_column_for_inf_case_m_neg_fixed(j, m, theta);
 | ||||
|             //     else
 | ||||
|             //         limit_theta_on_basis_column_for_inf_case_m_pos_fixed(j, m, theta);
 | ||||
|             //     break;
 | ||||
|         case column_type::fixed: | ||||
|         case column_type::boxed: | ||||
|             if (this->current_x_is_feasible()) { | ||||
|                 if (m > 0) { | ||||
|                     limit_theta_on_basis_column_for_feas_case_m_pos_no_check(j, m, theta, unlimited); | ||||
|                 } else { | ||||
|                     limit_theta_on_basis_column_for_feas_case_m_neg_no_check(j, m, theta, unlimited); | ||||
|                 } | ||||
|             } else { | ||||
|                 if (m > 0) { | ||||
|                     limit_theta_on_basis_column_for_inf_case_m_pos_boxed(j, m, theta, unlimited); | ||||
|                 } else { | ||||
|                     limit_theta_on_basis_column_for_inf_case_m_neg_boxed(j, m, theta, unlimited); | ||||
|                 } | ||||
|             } | ||||
| 
 | ||||
|             break; | ||||
|         default: | ||||
|             lean_unreachable(); | ||||
|         } | ||||
|         if (!unlimited && theta < zero_of_type<X>()) { | ||||
|             theta = zero_of_type<X>(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     bool column_is_benefitial_for_entering_basis(unsigned j) const; | ||||
|     bool column_is_benefitial_for_entering_basis_precise(unsigned j) const; | ||||
| 
 | ||||
|     bool column_is_benefitial_for_entering_on_breakpoints(unsigned j) const; | ||||
| 
 | ||||
| 
 | ||||
|     bool can_enter_basis(unsigned j); | ||||
|     bool done(); | ||||
|     void init_infeasibility_costs(); | ||||
|      | ||||
|     void init_infeasibility_cost_for_column(unsigned j); | ||||
|     T get_infeasibility_cost_for_column(unsigned j) const; | ||||
|     void init_infeasibility_costs_for_changed_basis_only(); | ||||
| 
 | ||||
|     void print_column(unsigned j, std::ostream & out); | ||||
|     void add_breakpoint(unsigned j, X delta, breakpoint_type type); | ||||
| 
 | ||||
|     // j is the basic column, x is the value at x[j]
 | ||||
|     // d is the coefficient before m_entering in the row with j as the basis column
 | ||||
|     void try_add_breakpoint(unsigned j, const X & x, const T & d, breakpoint_type break_type, const X & break_value); | ||||
|     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); | ||||
|     } | ||||
| 
 | ||||
|     void init_reduced_costs(); | ||||
| 
 | ||||
|     bool low_bounds_are_set() const { return true; } | ||||
| 
 | ||||
|     int advance_on_sorted_breakpoints(unsigned entering, X & t); | ||||
|      | ||||
|     std::string break_type_to_string(breakpoint_type type); | ||||
| 
 | ||||
|     void print_breakpoint(const breakpoint<X> * b, std::ostream & out); | ||||
| 
 | ||||
|     void print_bound_info_and_x(unsigned j, std::ostream & out); | ||||
| 
 | ||||
|     void init_infeasibility_after_update_x_if_inf(unsigned leaving) { | ||||
|         if (this->m_using_infeas_costs) { | ||||
|             init_infeasibility_costs_for_changed_basis_only(); | ||||
|             this->m_costs[leaving] = zero_of_type<T>(); | ||||
|             this->m_inf_set.erase(leaving); | ||||
|         }  | ||||
|     } | ||||
|      | ||||
|     void init_inf_set() { | ||||
|         this->m_inf_set.clear(); | ||||
|         for (unsigned j = 0; j < this->m_n(); j++) { | ||||
|             if (this->m_basis_heading[j] < 0) | ||||
|                 continue; | ||||
|             if (!this->column_is_feasible(j)) | ||||
|                 this->m_inf_set.insert(j); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     int get_column_out_of_bounds_delta_sign(unsigned j) { | ||||
|         switch (this->m_column_types[j]) { | ||||
|         case column_type::fixed: | ||||
|         case column_type::boxed: | ||||
|             if (this->x_below_low_bound(j)) | ||||
|                 return -1; | ||||
|             if (this->x_above_upper_bound(j)) | ||||
|                 return 1; | ||||
|             break; | ||||
|         case column_type::low_bound: | ||||
|             if (this->x_below_low_bound(j)) | ||||
|                 return -1; | ||||
|             break; | ||||
|         case column_type::upper_bound: | ||||
|             if (this->x_above_upper_bound(j)) | ||||
|                 return 1; | ||||
|             break; | ||||
|         case column_type::free_column: | ||||
|             return 0; | ||||
|         default: | ||||
|             lean_assert(false); | ||||
|         } | ||||
|         return 0; | ||||
|     } | ||||
| 
 | ||||
|     void init_column_row_non_zeroes() { | ||||
|         this->m_columns_nz.resize(this->m_A.column_count()); | ||||
|         this->m_rows_nz.resize(this->m_A.row_count()); | ||||
|         for (unsigned i = 0; i < this->m_A.column_count(); i++) { | ||||
|             if (this->m_columns_nz[i] == 0) | ||||
|                 this->m_columns_nz[i] = this->m_A.m_columns[i].size(); | ||||
|         } | ||||
|         for (unsigned i = 0; i < this->m_A.row_count(); i++) { | ||||
|          if (this->m_rows_nz[i] == 0) | ||||
|             this->m_rows_nz[i] = this->m_A.m_rows[i].size(); | ||||
|         } | ||||
|     } | ||||
|      | ||||
| 
 | ||||
|     int x_at_bound_sign(unsigned j) { | ||||
|         switch (this->m_column_types[j]) { | ||||
|         case column_type::fixed: | ||||
|             return 0; | ||||
|         case column_type::boxed: | ||||
|             if (this->x_is_at_low_bound(j)) | ||||
|                 return 1; | ||||
|             return -1; | ||||
|             break; | ||||
|         case column_type::low_bound: | ||||
|             return 1; | ||||
|             break; | ||||
|         case column_type::upper_bound: | ||||
|             return -1; | ||||
|             break; | ||||
|         default: | ||||
|             lean_assert(false); | ||||
|         } | ||||
|         return 0; | ||||
| 
 | ||||
|     } | ||||
| 
 | ||||
|     unsigned solve_with_tableau(); | ||||
| 
 | ||||
|     bool basis_column_is_set_correctly(unsigned j) const { | ||||
|         return this->m_A.m_columns[j].size() == 1; | ||||
|              | ||||
|     } | ||||
|      | ||||
|     bool basis_columns_are_set_correctly() const { | ||||
|         for (unsigned j : this->m_basis) | ||||
|             if(!basis_column_is_set_correctly(j)) | ||||
|                 return false; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     void init_run_tableau(); | ||||
|     void update_x_tableau(unsigned entering, const X & delta); | ||||
|     void update_inf_cost_for_column_tableau(unsigned j); | ||||
| 
 | ||||
| // the delta is between the old and the new cost (old - new)
 | ||||
|     void update_reduced_cost_for_basic_column_cost_change(const T & delta, unsigned j) { | ||||
|         lean_assert(this->m_basis_heading[j] >= 0); | ||||
|         unsigned i = static_cast<unsigned>(this->m_basis_heading[j]); | ||||
|         for (const row_cell<T> & rc : this->m_A.m_rows[i]) { | ||||
|             unsigned k = rc.m_j; | ||||
|             if (k == j) | ||||
|                 continue; | ||||
|             this->m_d[k] += delta * rc.get_val(); | ||||
|         } | ||||
|     } | ||||
|      | ||||
|     bool update_basis_and_x_tableau(int entering, int leaving, X const & tt); | ||||
|     void init_reduced_costs_tableau(); | ||||
|     void init_tableau_rows() { | ||||
|         m_bland_mode_tableau = false; | ||||
|         m_left_basis_tableau.clear(); | ||||
|         m_left_basis_tableau.resize(this->m_A.column_count()); | ||||
|         m_left_basis_repeated = 0; | ||||
|     } | ||||
| // stage1 constructor
 | ||||
|     lp_primal_core_solver(static_matrix<T, X> & A, | ||||
|                           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<unsigned> & nbasis, | ||||
|                           vector<int> & heading, | ||||
|                           vector<T> & costs, | ||||
|                           const vector<column_type> & column_type_array, | ||||
|                           const vector<X> & low_bound_values, | ||||
|                           const vector<X> & upper_bound_values, | ||||
|                           lp_settings & settings, | ||||
|                           const column_namer& column_names): | ||||
|         lp_core_solver_base<T, X>(A, b, | ||||
|                                   basis, | ||||
|                                   nbasis, | ||||
|                                   heading, | ||||
|                                   x, | ||||
|                                   costs, | ||||
|                                   settings, | ||||
|                                   column_names, | ||||
|                                   column_type_array, | ||||
|                                   low_bound_values, | ||||
|                                   upper_bound_values), | ||||
|         m_beta(A.row_count()) { | ||||
| 
 | ||||
|         if (!(numeric_traits<T>::precise())) { | ||||
|             m_converted_harris_eps = convert_struct<T, double>::convert(this->m_settings.harris_feasibility_tolerance); | ||||
|         } else { | ||||
|             m_converted_harris_eps = zero_of_type<T>(); | ||||
|         } | ||||
|         this->set_status(UNKNOWN); | ||||
|     } | ||||
| 
 | ||||
|     // constructor
 | ||||
|     lp_primal_core_solver(static_matrix<T, X> & A, | ||||
|                           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<unsigned> & nbasis, | ||||
|                           vector<int> & heading, | ||||
|                           vector<T> & costs, | ||||
|                           const vector<column_type> & column_type_array, | ||||
|                           const vector<X> & upper_bound_values, | ||||
|                           lp_settings & settings, | ||||
|                           const column_namer& column_names): | ||||
|         lp_core_solver_base<T, X>(A, b, | ||||
|                                   basis, | ||||
|                                   nbasis, | ||||
|                                   heading, | ||||
|                                   x, | ||||
|                                   costs, | ||||
|                                   settings, | ||||
|                                   column_names, | ||||
|                                   column_type_array, | ||||
|                                   m_low_bounds_dummy, | ||||
|                                   upper_bound_values), | ||||
|         m_beta(A.row_count()), | ||||
|         m_converted_harris_eps(convert_struct<T, double>::convert(this->m_settings.harris_feasibility_tolerance)) { | ||||
|         lean_assert(initial_x_is_correct()); | ||||
|         m_low_bounds_dummy.resize(A.column_count(), zero_of_type<T>()); | ||||
|         m_enter_price_eps = numeric_traits<T>::precise() ? numeric_traits<T>::zero() : T(1e-5); | ||||
| #ifdef LEAN_DEBUG | ||||
|         // check_correctness();
 | ||||
| #endif | ||||
|     } | ||||
| 
 | ||||
|     bool initial_x_is_correct() { | ||||
|         std::set<unsigned> basis_set; | ||||
|         for (unsigned i = 0; i < this->m_A.row_count(); i++) { | ||||
|             basis_set.insert(this->m_basis[i]); | ||||
|         } | ||||
|         for (unsigned j = 0; j < this->m_n(); j++) { | ||||
|             if (this->column_has_low_bound(j) && this->m_x[j] < numeric_traits<T>::zero()) { | ||||
|                 LP_OUT(this->m_settings, "low bound for variable " << j << " does not hold: this->m_x[" << j << "] = " << this->m_x[j] << " is negative " << std::endl); | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             if (this->column_has_upper_bound(j) && this->m_x[j] > this->m_upper_bounds[j]) { | ||||
|                 LP_OUT(this->m_settings, "upper bound for " << j << " does not hold: "  << this->m_upper_bounds[j] << ">" << this->m_x[j] << std::endl); | ||||
|                 return false; | ||||
|             } | ||||
| 
 | ||||
|             if (basis_set.find(j) != basis_set.end()) continue; | ||||
|             if (this->m_column_types[j] == column_type::low_bound)  { | ||||
|                 if (numeric_traits<T>::zero() != this->m_x[j]) { | ||||
|                     LP_OUT(this->m_settings, "only low bound is set for " << j << " but low bound value " << numeric_traits<T>::zero() << " is not equal to " << this->m_x[j] << std::endl); | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|             if (this->m_column_types[j] == column_type::boxed) { | ||||
|                 if (this->m_upper_bounds[j] != this->m_x[j] && !numeric_traits<T>::is_zero(this->m_x[j])) { | ||||
|                     return false; | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     friend core_solver_pretty_printer<T, X>; | ||||
| }; | ||||
| } | ||||
							
								
								
									
										1374
									
								
								src/util/lp/lp_primal_core_solver.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										1374
									
								
								src/util/lp/lp_primal_core_solver.hpp
									
										
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because it is too large
												Load diff
											
										
									
								
							
							
								
								
									
										24
									
								
								src/util/lp/lp_primal_core_solver_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								src/util/lp/lp_primal_core_solver_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,24 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <utility> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include "util/vector.h" | ||||
| #include <functional> | ||||
| #include "util/lp/lar_solver.h" | ||||
| #include "util/lp/lp_primal_core_solver.hpp" | ||||
| #include "util/lp/lp_primal_core_solver_tableau.hpp" | ||||
| namespace lean { | ||||
| template void lp_primal_core_solver<double, double>::find_feasible_solution(); | ||||
| template void lean::lp_primal_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::find_feasible_solution(); | ||||
| 
 | ||||
| template unsigned lp_primal_core_solver<double, double>::solve(); | ||||
| template unsigned lp_primal_core_solver<double, double>::solve_with_tableau(); | ||||
| template unsigned lp_primal_core_solver<mpq, mpq>::solve(); | ||||
| template void lean::lp_primal_core_solver<double, double>::clear_breakpoints(); | ||||
| template bool lean::lp_primal_core_solver<lean::mpq, lean::mpq>::update_basis_and_x_tableau(int, int, lean::mpq const&); | ||||
| template bool lean::lp_primal_core_solver<double, double>::update_basis_and_x_tableau(int, int, double const&); | ||||
| template bool lean::lp_primal_core_solver<lean::mpq, lean::numeric_pair<lean::mpq> >::update_basis_and_x_tableau(int, int, lean::numeric_pair<lean::mpq> const&); | ||||
| } | ||||
							
								
								
									
										393
									
								
								src/util/lp/lp_primal_core_solver_tableau.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										393
									
								
								src/util/lp/lp_primal_core_solver_tableau.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,393 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| // this is a part of lp_primal_core_solver that deals with the tableau
 | ||||
| #include "util/lp/lp_primal_core_solver.h" | ||||
| namespace lean { | ||||
| template <typename T, typename X> void lp_primal_core_solver<T, X>::one_iteration_tableau() { | ||||
|     int entering = choose_entering_column_tableau(); | ||||
|     if (entering == -1) { | ||||
|         decide_on_status_when_cannot_find_entering(); | ||||
|     } | ||||
|     else { | ||||
|         advance_on_entering_tableau(entering); | ||||
|     } | ||||
|     lean_assert(this->inf_set_is_correct()); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_entering_tableau(int entering) { | ||||
|     X t; | ||||
|     int leaving = find_leaving_and_t_tableau(entering, t); | ||||
|     if (leaving == -1) { | ||||
|         this->set_status(UNBOUNDED); | ||||
|         return; | ||||
|     } | ||||
|     advance_on_entering_and_leaving_tableau(entering, leaving, t); | ||||
| } | ||||
| /*
 | ||||
| template <typename T, typename X> int lp_primal_core_solver<T, X>::choose_entering_column_tableau_rows() { | ||||
|     int i = find_inf_row(); | ||||
|     if (i == -1) | ||||
|         return -1; | ||||
|     return find_shortest_beneficial_column_in_row(i); | ||||
|  } | ||||
| */ | ||||
|  template <typename T, typename X> int lp_primal_core_solver<T, X>::choose_entering_column_tableau() { | ||||
|     //this moment m_y = cB * B(-1)
 | ||||
|     unsigned number_of_benefitial_columns_to_go_over =  get_number_of_non_basic_column_to_try_for_enter(); | ||||
|      | ||||
|     lean_assert(numeric_traits<T>::precise()); | ||||
|     if (number_of_benefitial_columns_to_go_over == 0) | ||||
|         return -1; | ||||
|     if (this->m_basis_sort_counter == 0) { | ||||
|         sort_non_basis(); | ||||
|         this->m_basis_sort_counter = 20; | ||||
|     } | ||||
|     else { | ||||
|         this->m_basis_sort_counter--; | ||||
|     } | ||||
|     unsigned j_nz = this->m_m() + 1; // this number is greater than the max column size
 | ||||
|     std::list<unsigned>::iterator entering_iter = m_non_basis_list.end(); | ||||
|     for (auto non_basis_iter = m_non_basis_list.begin(); number_of_benefitial_columns_to_go_over && non_basis_iter != m_non_basis_list.end(); ++non_basis_iter) { | ||||
|         unsigned j = *non_basis_iter; | ||||
|         if (!column_is_benefitial_for_entering_basis(j)) | ||||
|             continue; | ||||
| 
 | ||||
|         // if we are here then j is a candidate to enter the basis
 | ||||
|         unsigned t = this->m_A.number_of_non_zeroes_in_column(j); | ||||
|         if (t < j_nz) { | ||||
|             j_nz = t; | ||||
|             entering_iter = non_basis_iter; | ||||
|             if (number_of_benefitial_columns_to_go_over) | ||||
|                 number_of_benefitial_columns_to_go_over--; | ||||
|         } | ||||
|         else if (t == j_nz && my_random() % 2 == 0) { | ||||
|             entering_iter = non_basis_iter; | ||||
|         } | ||||
|     }// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb);
 | ||||
|     if (entering_iter == m_non_basis_list.end()) | ||||
|         return -1; | ||||
|     unsigned entering = *entering_iter; | ||||
|     m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1; | ||||
|     if (this->m_using_infeas_costs && this->m_settings.use_breakpoints_in_feasibility_search) | ||||
|         m_sign_of_entering_delta = -m_sign_of_entering_delta; | ||||
|     m_non_basis_list.erase(entering_iter); | ||||
|     m_non_basis_list.push_back(entering); | ||||
|     return entering; | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| unsigned lp_primal_core_solver<T, X>::solve_with_tableau() { | ||||
|     init_run_tableau(); | ||||
|     if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) { | ||||
|         this->set_status(FEASIBLE); | ||||
|         return 0; | ||||
|     } | ||||
|          | ||||
|     if ((!numeric_traits<T>::precise()) && this->A_mult_x_is_off()) { | ||||
|         this->set_status(FLOATING_POINT_ERROR); | ||||
|         return 0; | ||||
|     } | ||||
|     do { | ||||
|         if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->m_using_infeas_costs? "inf t" : "feas t"), * this->m_settings.get_message_ostream())) { | ||||
|             return this->total_iterations(); | ||||
|         } | ||||
|         if (this->m_settings.use_tableau_rows()) | ||||
|             one_iteration_tableau_rows(); | ||||
|         else  | ||||
|             one_iteration_tableau(); | ||||
|         switch (this->get_status()) { | ||||
|         case OPTIMAL:  // double check that we are at optimum
 | ||||
|         case INFEASIBLE: | ||||
|             if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) | ||||
|                 break; | ||||
|             if (!numeric_traits<T>::precise()) { | ||||
|                 if(this->m_look_for_feasible_solution_only) | ||||
|                     break; | ||||
|                 this->init_lu(); | ||||
|                  | ||||
|                 if (this->m_factorization->get_status() != LU_status::OK) { | ||||
|                     this->set_status(FLOATING_POINT_ERROR); | ||||
|                     break; | ||||
|                 } | ||||
|                 init_reduced_costs(); | ||||
|                 if (choose_entering_column(1) == -1) { | ||||
|                     decide_on_status_when_cannot_find_entering(); | ||||
|                     break; | ||||
|                 } | ||||
|                 this->set_status(UNKNOWN); | ||||
|             } else { // precise case
 | ||||
|                 if ((!this->infeasibility_costs_are_correct())) { | ||||
|                     init_reduced_costs_tableau(); // forcing recalc
 | ||||
|                     if (choose_entering_column_tableau() == -1) { | ||||
|                         decide_on_status_when_cannot_find_entering(); | ||||
|                         break; | ||||
|                     } | ||||
|                     this->set_status(UNKNOWN); | ||||
|                 } | ||||
|             } | ||||
|             break; | ||||
|         case TENTATIVE_UNBOUNDED: | ||||
|             this->init_lu(); | ||||
|             if (this->m_factorization->get_status() != LU_status::OK) { | ||||
|                 this->set_status(FLOATING_POINT_ERROR); | ||||
|                 break; | ||||
|             } | ||||
|                  | ||||
|             init_reduced_costs(); | ||||
|             break; | ||||
|         case UNBOUNDED: | ||||
|             if (this->current_x_is_infeasible()) { | ||||
|                 init_reduced_costs(); | ||||
|                 this->set_status(UNKNOWN); | ||||
|             } | ||||
|             break; | ||||
| 
 | ||||
|         case UNSTABLE: | ||||
|             lean_assert(! (numeric_traits<T>::precise())); | ||||
|             this->init_lu(); | ||||
|             if (this->m_factorization->get_status() != LU_status::OK) { | ||||
|                 this->set_status(FLOATING_POINT_ERROR); | ||||
|                 break; | ||||
|             } | ||||
|             init_reduced_costs(); | ||||
|             break; | ||||
| 
 | ||||
|         default: | ||||
|             break; // do nothing
 | ||||
|         } | ||||
|     } while (this->get_status() != FLOATING_POINT_ERROR | ||||
|              && | ||||
|              this->get_status() != UNBOUNDED | ||||
|              && | ||||
|              this->get_status() != OPTIMAL | ||||
|              && | ||||
|              this->get_status() != INFEASIBLE | ||||
|              && | ||||
|              this->m_iters_with_no_cost_growing <= this->m_settings.max_number_of_iterations_with_no_improvements | ||||
|              && | ||||
|              this->total_iterations() <= this->m_settings.max_total_number_of_iterations | ||||
|              && | ||||
|              !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)); | ||||
| 
 | ||||
|     lean_assert(this->get_status() == FLOATING_POINT_ERROR | ||||
|                 || | ||||
|                 this->current_x_is_feasible() == false | ||||
|                 || | ||||
|                 this->calc_current_x_is_feasible_include_non_basis()); | ||||
|     return this->total_iterations(); | ||||
| 
 | ||||
| } | ||||
| template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t) { | ||||
|     lean_assert(this->A_mult_x_is_off() == false); | ||||
|     lean_assert(leaving >= 0 && entering >= 0); | ||||
|     lean_assert((this->m_settings.simplex_strategy() == | ||||
|                 simplex_strategy_enum::tableau_rows) || | ||||
|                 m_non_basis_list.back() == static_cast<unsigned>(entering)); | ||||
|     lean_assert(this->m_using_infeas_costs || !is_neg(t)); | ||||
|     lean_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes
 | ||||
|     if (entering == leaving) { | ||||
|         advance_on_entering_equal_leaving_tableau(entering, t); | ||||
|         return; | ||||
|     } | ||||
|     if (!is_zero(t)) { | ||||
|         if (this->current_x_is_feasible() || !this->m_settings.use_breakpoints_in_feasibility_search ) { | ||||
|             if (m_sign_of_entering_delta == -1) | ||||
|                 t = -t; | ||||
|         } | ||||
|         this->update_basis_and_x_tableau(entering, leaving, t); | ||||
|         lean_assert(this->A_mult_x_is_off() == false); | ||||
|         this->m_iters_with_no_cost_growing = 0; | ||||
|     } else { | ||||
|         this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); | ||||
|         this->change_basis(entering, leaving); | ||||
|     } | ||||
| 
 | ||||
|     if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) | ||||
|         return; | ||||
| 
 | ||||
|     if (this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) { | ||||
|         if (need_to_switch_costs()) { | ||||
|             this->init_reduced_costs_tableau(); | ||||
|         } | ||||
|          | ||||
|         lean_assert(!need_to_switch_costs()); | ||||
|         std::list<unsigned>::iterator it = m_non_basis_list.end(); | ||||
|         it--; | ||||
|         * it = static_cast<unsigned>(leaving); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lp_primal_core_solver<T, X>::advance_on_entering_equal_leaving_tableau(int entering, X & t) { | ||||
|     lean_assert(!this->A_mult_x_is_off() ); | ||||
|     this->update_x_tableau(entering, t * m_sign_of_entering_delta);  | ||||
|     if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible()) | ||||
|         return; | ||||
|      | ||||
|     if (need_to_switch_costs()) { | ||||
|         init_reduced_costs_tableau(); | ||||
|     } | ||||
|     this->m_iters_with_no_cost_growing = 0; | ||||
| } | ||||
| template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_and_t_tableau(unsigned entering, X & t) { | ||||
|     unsigned k = 0; | ||||
|     bool unlimited = true; | ||||
|     unsigned row_min_nz = this->m_n() + 1; | ||||
|     m_leaving_candidates.clear(); | ||||
|     auto & col = this->m_A.m_columns[entering]; | ||||
|     unsigned col_size = col.size(); | ||||
|     for (;k < col_size && unlimited; k++) { | ||||
|         const column_cell & c = col[k]; | ||||
|         unsigned i = c.m_i; | ||||
|         const T & ed = this->m_A.get_val(c); | ||||
|         lean_assert(!numeric_traits<T>::is_zero(ed)); | ||||
|         unsigned j = this->m_basis[i]; | ||||
|         limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited); | ||||
|         if (!unlimited) { | ||||
|             m_leaving_candidates.push_back(j); | ||||
|             row_min_nz = this->m_A.m_rows[i].size(); | ||||
|         } | ||||
|     } | ||||
|     if (unlimited) { | ||||
|         if (try_jump_to_another_bound_on_entering_unlimited(entering, t)) | ||||
|             return entering; | ||||
|         return -1; | ||||
|     } | ||||
| 
 | ||||
|     X ratio; | ||||
|     for (;k < col_size; k++) { | ||||
|         const column_cell & c = col[k]; | ||||
|         unsigned i = c.m_i; | ||||
|         const T & ed = this->m_A.get_val(c); | ||||
|          lean_assert(!numeric_traits<T>::is_zero(ed)); | ||||
|         unsigned j = this->m_basis[i]; | ||||
|         unlimited = true; | ||||
|         limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited); | ||||
|         if (unlimited) continue; | ||||
|         unsigned i_nz = this->m_A.m_rows[i].size(); | ||||
|         if (ratio < t) { | ||||
|             t = ratio; | ||||
|             m_leaving_candidates.clear(); | ||||
|             m_leaving_candidates.push_back(j); | ||||
|             row_min_nz = i_nz; | ||||
|         } else if (ratio == t && i_nz < row_min_nz) { | ||||
|             m_leaving_candidates.clear(); | ||||
|             m_leaving_candidates.push_back(j); | ||||
|             row_min_nz = this->m_A.m_rows[i].size(); | ||||
|         } else if (ratio == t && i_nz == row_min_nz) { | ||||
|             m_leaving_candidates.push_back(j); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     ratio = t; | ||||
|     unlimited = false; | ||||
|     if (try_jump_to_another_bound_on_entering(entering, t, ratio, unlimited)) { | ||||
|         t = ratio; | ||||
|         return entering; | ||||
|     } | ||||
|     if (m_leaving_candidates.size() == 1) | ||||
|         return m_leaving_candidates[0]; | ||||
|     k = my_random() % m_leaving_candidates.size(); | ||||
|     return m_leaving_candidates[k]; | ||||
| } | ||||
| template <typename T, typename X> void lp_primal_core_solver<T, X>::init_run_tableau() { | ||||
|         //        print_matrix(&(this->m_A), std::cout);
 | ||||
|         lean_assert(this->A_mult_x_is_off() == false); | ||||
|         lean_assert(basis_columns_are_set_correctly()); | ||||
|         this->m_basis_sort_counter = 0; // to initiate the sort of the basis
 | ||||
|         this->set_total_iterations(0); | ||||
|         this->m_iters_with_no_cost_growing = 0; | ||||
| 		lean_assert(this->inf_set_is_correct()); | ||||
|         if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) | ||||
|             return; | ||||
|         if (this->m_settings.backup_costs) | ||||
|             backup_and_normalize_costs(); | ||||
|         m_epsilon_of_reduced_cost = numeric_traits<X>::precise() ? zero_of_type<T>() : T(1) / T(10000000); | ||||
|         if (this->m_settings.use_breakpoints_in_feasibility_search) | ||||
|             m_breakpoint_indices_queue.resize(this->m_n()); | ||||
|         if (!numeric_traits<X>::precise()) { | ||||
|             this->m_column_norm_update_counter = 0; | ||||
|             init_column_norms(); | ||||
|         } | ||||
|         if (this->m_settings.m_simplex_strategy == simplex_strategy_enum::tableau_rows) | ||||
|             init_tableau_rows(); | ||||
|         lean_assert(this->reduced_costs_are_correct_tableau()); | ||||
|         lean_assert(!this->need_to_pivot_to_basis_tableau()); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_primal_core_solver<T, X>:: | ||||
| update_basis_and_x_tableau(int entering, int leaving, X const & tt) { | ||||
|     lean_assert(this->use_tableau()); | ||||
|     update_x_tableau(entering, tt); | ||||
|     this->pivot_column_tableau(entering, this->m_basis_heading[leaving]); | ||||
|     this->change_basis(entering, leaving); | ||||
|     return true; | ||||
| } | ||||
| template <typename T, typename X> void lp_primal_core_solver<T, X>:: | ||||
| update_x_tableau(unsigned entering, const X& delta) { | ||||
|     if (!this->m_using_infeas_costs) { | ||||
|         this->m_x[entering] += delta; | ||||
|         for (const auto & c : this->m_A.m_columns[entering]) { | ||||
|             unsigned i = c.m_i; | ||||
|             this->m_x[this->m_basis[i]] -= delta * this->m_A.get_val(c); | ||||
|             this->update_column_in_inf_set(this->m_basis[i]); | ||||
|         } | ||||
|     } else { // m_using_infeas_costs == true
 | ||||
|         this->m_x[entering] += delta; | ||||
|         lean_assert(this->column_is_feasible(entering)); | ||||
|         lean_assert(this->m_costs[entering] == zero_of_type<T>()); | ||||
|         // m_d[entering] can change because of the cost change for basic columns.
 | ||||
|         for (const auto & c : this->m_A.m_columns[entering]) { | ||||
|             unsigned i = c.m_i; | ||||
|             unsigned j = this->m_basis[i]; | ||||
|             this->m_x[j] -= delta * this->m_A.get_val(c); | ||||
|             update_inf_cost_for_column_tableau(j); | ||||
|             if (is_zero(this->m_costs[j])) | ||||
|                 this->m_inf_set.erase(j); | ||||
|             else | ||||
|                 this->m_inf_set.insert(j); | ||||
|         } | ||||
|     } | ||||
|     lean_assert(this->A_mult_x_is_off() == false); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_core_solver<T, X>:: | ||||
| update_inf_cost_for_column_tableau(unsigned j) { | ||||
|     lean_assert(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows); | ||||
|     lean_assert(this->m_using_infeas_costs); | ||||
|     T new_cost = get_infeasibility_cost_for_column(j); | ||||
|     T delta = this->m_costs[j] - new_cost; | ||||
|     if (is_zero(delta)) | ||||
|         return; | ||||
|     this->m_costs[j] = new_cost; | ||||
|     update_reduced_cost_for_basic_column_cost_change(delta, j); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_core_solver<T, X>::init_reduced_costs_tableau() { | ||||
|     if (this->current_x_is_infeasible() && !this->m_using_infeas_costs) { | ||||
|         init_infeasibility_costs(); | ||||
|     } else if (this->current_x_is_feasible() && this->m_using_infeas_costs) { | ||||
|         if (this->m_look_for_feasible_solution_only) | ||||
|             return; | ||||
|         this->m_costs = m_costs_backup; | ||||
|         this->m_using_infeas_costs = false; | ||||
|     } | ||||
|     unsigned size = this->m_basis_heading.size(); | ||||
|     for (unsigned j = 0; j < size; j++) { | ||||
|         if (this->m_basis_heading[j] >= 0) | ||||
|             this->m_d[j] = zero_of_type<T>(); | ||||
|         else { | ||||
|             T& d = this->m_d[j] = this->m_costs[j]; | ||||
|             for (auto & cc : this->m_A.m_columns[j]) { | ||||
|                 d -= this->m_costs[this->m_basis[cc.m_i]] * this->m_A.get_val(cc); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
							
								
								
									
										96
									
								
								src/util/lp/lp_primal_simplex.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										96
									
								
								src/util/lp/lp_primal_simplex.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,96 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include <unordered_map> | ||||
| #include <string> | ||||
| #include <algorithm> | ||||
| #include "util/lp/lp_utils.h" | ||||
| #include "util/lp/column_info.h" | ||||
| #include "util/lp/lp_primal_core_solver.h" | ||||
| #include "util/lp/lp_solver.h" | ||||
| #include "util/lp/iterator_on_row.h" | ||||
| namespace lean { | ||||
| 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); | ||||
| 
 | ||||
|     void init_buffer(unsigned k, vector<T> & r); | ||||
| 
 | ||||
|     void refactor(); | ||||
| 
 | ||||
|     void set_scaled_costs(); | ||||
| public: | ||||
|     lp_primal_simplex() {} | ||||
| 
 | ||||
|     column_info<T> * get_or_create_column_info(unsigned column); | ||||
| 
 | ||||
|     void set_status(lp_status status) { | ||||
|         this->m_status = status; | ||||
|     } | ||||
| 
 | ||||
|     lp_status get_status() { | ||||
|         return this->m_status; | ||||
|     } | ||||
| 
 | ||||
|     void fill_acceptable_values_for_x(); | ||||
| 
 | ||||
| 
 | ||||
|     void set_zero_bound(bool * bound_is_set, T * bounds,  unsigned i); | ||||
| 
 | ||||
|     void fill_costs_and_x_for_first_stage_solver_for_row( | ||||
|                                                          int row, | ||||
|                                                          unsigned & slack_var, | ||||
|                                                          unsigned & artificial); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|      | ||||
|     void set_core_solver_bounds(); | ||||
| 
 | ||||
|     void update_time_limit_from_starting_time(int start_time) { | ||||
|         this->m_settings.time_limit -= (get_millisecond_span(start_time) / 1000.); | ||||
|     } | ||||
| 
 | ||||
|     void find_maximal_solution(); | ||||
| 
 | ||||
|     void fill_A_x_and_basis_for_stage_one_total_inf(); | ||||
| 
 | ||||
|     void fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row); | ||||
| 
 | ||||
|     void solve_with_total_inf(); | ||||
| 
 | ||||
| 
 | ||||
|     ~lp_primal_simplex(); | ||||
| 
 | ||||
|     bool bounds_hold(std::unordered_map<std::string, T> const & solution); | ||||
| 
 | ||||
|     T get_row_value(unsigned i, std::unordered_map<std::string, T> const & solution, std::ostream * out); | ||||
| 
 | ||||
|     bool row_constraint_holds(unsigned i, std::unordered_map<std::string, T> const & solution, std::ostream * out); | ||||
| 
 | ||||
|     bool row_constraints_hold(std::unordered_map<std::string, T> const & solution); | ||||
| 
 | ||||
| 
 | ||||
|     T * get_array_from_map(std::unordered_map<std::string, T> const & solution); | ||||
| 
 | ||||
|     bool solution_is_feasible(std::unordered_map<std::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; | ||||
| 
 | ||||
|      | ||||
| }; | ||||
| } | ||||
							
								
								
									
										355
									
								
								src/util/lp/lp_primal_simplex.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										355
									
								
								src/util/lp/lp_primal_simplex.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,355 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <string> | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/lp_primal_simplex.h" | ||||
| 
 | ||||
| namespace lean { | ||||
| template <typename T, typename X> void lp_primal_simplex<T, X>::fill_costs_and_x_for_first_stage_solver(unsigned original_number_of_columns) { | ||||
|     unsigned slack_var = original_number_of_columns; | ||||
|     unsigned artificial = original_number_of_columns + this->m_slacks; | ||||
| 
 | ||||
|     for (unsigned row = 0; row < this->row_count(); row++) { | ||||
|         fill_costs_and_x_for_first_stage_solver_for_row(row, slack_var, artificial); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_simplex<T, X>::init_buffer(unsigned k, vector<T> & r) { | ||||
|     for (unsigned i = 0; i < k; i++) { | ||||
|         r[i] = 0; | ||||
|     } | ||||
|     r[k] = 1; | ||||
|     for (unsigned i = this->row_count() -1; i > k; i--) { | ||||
|         r[i] = 0; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_simplex<T, X>::refactor() { | ||||
|     m_core_solver->init_lu(); | ||||
|     if (m_core_solver->factorization()->get_status() != LU_status::OK) { | ||||
|         throw_exception("cannot refactor"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_simplex<T, X>::set_scaled_costs() { | ||||
|     unsigned j = this->number_of_core_structurals(); | ||||
|     while (j-- > 0) { | ||||
|         this->set_scaled_cost(j); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X>     column_info<T> * lp_primal_simplex<T, X>::get_or_create_column_info(unsigned column) { | ||||
|     auto it = this->m_columns.find(column); | ||||
|     return (it == this->m_columns.end())? ( this->m_columns[column] = new column_info<T>) : it->second; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_simplex<T, X>::fill_acceptable_values_for_x() { | ||||
|     for (auto t : this->m_core_solver_columns_to_external_columns) { | ||||
|         this->m_x[t.first] = numeric_traits<T>::zero(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_simplex<T, X>::set_zero_bound(bool * bound_is_set, T * bounds,  unsigned i) { | ||||
|     bound_is_set[i] = true; | ||||
|     bounds[i] = numeric_traits<T>::zero(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_simplex<T, X>::fill_costs_and_x_for_first_stage_solver_for_row( | ||||
|                                                                                                                 int row, | ||||
|                                                                                                                 unsigned & slack_var, | ||||
|                                                                                                                 unsigned & artificial) { | ||||
|     lean_assert(row >= 0 && row < this->row_count()); | ||||
|     auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[row]]; | ||||
|     // we need to bring the program to the form Ax = b
 | ||||
|     T rs = this->m_b[row]; | ||||
|     T artificial_cost =  - numeric_traits<T>::one(); | ||||
|     switch (constraint.m_relation) { | ||||
|     case Equal: // no slack variable here
 | ||||
|         this->m_column_types[artificial] = column_type::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] = column_type::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] = column_type::low_bound; | ||||
|             (*this->m_A)(row, artificial) = numeric_traits<T>::one(); | ||||
|             this->m_costs[artificial] = artificial_cost; | ||||
|             this->m_basis[row] = artificial; | ||||
|             this->m_x[artificial] = rs; | ||||
|             artificial++; | ||||
|         } else { | ||||
|             // we can put a slack_var into the basis, and atemplate <typename T, typename X> void lp_primal_simplex<T, X>::adding an artificial variable
 | ||||
|             this->m_basis[row] = slack_var; | ||||
|             this->m_x[slack_var] = - rs; | ||||
|         } | ||||
|         slack_var++; | ||||
|         break; | ||||
|     case Less_or_equal: | ||||
|         // introduce a non-negative slack variable
 | ||||
|         this->m_column_types[slack_var] = column_type::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] = column_type::low_bound; | ||||
|             (*this->m_A)(row, artificial) = - numeric_traits<T>::one(); | ||||
|             this->m_costs[artificial] = artificial_cost; | ||||
|             this->m_x[artificial] = - rs; | ||||
|             this->m_basis[row] = artificial++; | ||||
|         } else { | ||||
|             // we can put slack_var into the basis, and atemplate <typename T, typename X> void lp_primal_simplex<T, X>::adding an artificial variable
 | ||||
|             this->m_basis[row] = slack_var; | ||||
|             this->m_x[slack_var] = rs; | ||||
|         } | ||||
|         slack_var++; | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_simplex<T, X>::set_core_solver_bounds() { | ||||
|     unsigned total_vars = this->m_A->column_count() + this->m_slacks + this->m_artificials; | ||||
|     this->m_column_types.resize(total_vars); | ||||
|     this->m_upper_bounds.resize(total_vars); | ||||
|     for (auto cit : this->m_map_from_var_index_to_column_info) { | ||||
|         column_info<T> * ci = cit.second; | ||||
|         unsigned j = ci->get_column_index(); | ||||
|         if (!is_valid(j)) | ||||
|             continue; // the variable is not mapped to a column
 | ||||
|         switch (this->m_column_types[j] = ci->get_column_type()){ | ||||
|         case column_type::fixed: | ||||
|             this->m_upper_bounds[j] = numeric_traits<T>::zero(); | ||||
|             break; | ||||
|         case column_type::boxed: | ||||
|             this->m_upper_bounds[j] = ci->get_adjusted_upper_bound() / this->m_column_scale[j]; | ||||
|             break; | ||||
| 
 | ||||
|         default: break; // do nothing
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_simplex<T, X>::find_maximal_solution() { | ||||
|     int preprocessing_start_time = get_millisecond_count(); | ||||
|     if (this->problem_is_empty()) { | ||||
|         this->m_status = lp_status::EMPTY; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     this->cleanup(); | ||||
|     this->fill_matrix_A_and_init_right_side(); | ||||
|     if (this->m_status == lp_status::INFEASIBLE) { | ||||
|         return; | ||||
|     } | ||||
|     this->m_x.resize(this->m_A->column_count()); | ||||
|     this->fill_m_b(); | ||||
|     this->scale(); | ||||
|     fill_acceptable_values_for_x(); | ||||
|     this->count_slacks_and_artificials(); | ||||
|     set_core_solver_bounds(); | ||||
|     update_time_limit_from_starting_time(preprocessing_start_time); | ||||
|     solve_with_total_inf(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_simplex<T, X>::fill_A_x_and_basis_for_stage_one_total_inf() { | ||||
|     for (unsigned row = 0; row < this->row_count(); row++) | ||||
|         fill_A_x_and_basis_for_stage_one_total_inf_for_row(row); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_simplex<T, X>::fill_A_x_and_basis_for_stage_one_total_inf_for_row(unsigned row) { | ||||
|     lean_assert(row < this->row_count()); | ||||
|     auto ext_row_it = this->m_core_solver_rows_to_external_rows.find(row); | ||||
|     lean_assert(ext_row_it != this->m_core_solver_rows_to_external_rows.end()); | ||||
|     unsigned ext_row = ext_row_it->second; | ||||
|     auto constr_it = this->m_constraints.find(ext_row); | ||||
|     lean_assert(constr_it != this->m_constraints.end()); | ||||
|     auto & constraint = constr_it->second; | ||||
|     unsigned j = this->m_A->column_count(); // j is a slack variable
 | ||||
|     this->m_A->add_column(); | ||||
|     // we need to bring the program to the form Ax = b
 | ||||
|     this->m_basis[row] = j; | ||||
|     switch (constraint.m_relation) { | ||||
|     case Equal: | ||||
|         this->m_x[j] = this->m_b[row]; | ||||
|         (*this->m_A)(row, j) = numeric_traits<T>::one(); | ||||
|         this->m_column_types[j] = column_type::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] = column_type::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] = column_type::low_bound; | ||||
|         this->m_upper_bounds[j] = m_low_bounds[j] = zero_of_type<X>(); | ||||
|         break; | ||||
|     default: | ||||
|         lean_unreachable(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_primal_simplex<T, X>::solve_with_total_inf() { | ||||
|     int total_vars = this->m_A->column_count() + this->row_count(); | ||||
|     if (total_vars == 0) { | ||||
|         this->m_status = OPTIMAL; | ||||
|         return; | ||||
|     } | ||||
|     m_low_bounds.clear(); | ||||
|     m_low_bounds.resize(total_vars, zero_of_type<X>());  // low bounds are shifted ot zero
 | ||||
|     this->m_x.resize(total_vars, numeric_traits<T>::zero()); | ||||
|     this->m_basis.resize(this->row_count()); | ||||
|     this->m_costs.clear(); | ||||
|     this->m_costs.resize(total_vars, zero_of_type<T>()); | ||||
|     fill_A_x_and_basis_for_stage_one_total_inf(); | ||||
|     if (this->m_settings.get_message_ostream() != nullptr) | ||||
|         this->print_statistics_on_A(*this->m_settings.get_message_ostream()); | ||||
|     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_nbasis, | ||||
|                                                     this->m_heading, | ||||
|                                                     this->m_costs, | ||||
|                                                     this->m_column_types, | ||||
|                                                     m_low_bounds, | ||||
|                                                     this->m_upper_bounds, | ||||
|                                                     this->m_settings, *this); | ||||
|     m_core_solver->solve(); | ||||
|     this->set_status(m_core_solver->get_status()); | ||||
|     this->m_total_iterations = m_core_solver->total_iterations();    | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> lp_primal_simplex<T, X>::~lp_primal_simplex() { | ||||
|     if (m_core_solver != nullptr) { | ||||
|         delete m_core_solver; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_primal_simplex<T, X>::bounds_hold(std::unordered_map<std::string, T> const & solution) { | ||||
|     for (auto it : this->m_map_from_var_index_to_column_info) { | ||||
|         auto sol_it = solution.find(it.second->get_name()); | ||||
|         if (sol_it == solution.end()) { | ||||
|             std::stringstream s; | ||||
|             s << "cannot find column " << it.first << " in solution"; | ||||
|             throw_exception(s.str() ); | ||||
|         } | ||||
| 
 | ||||
|         if (!it.second->bounds_hold(sol_it->second)) { | ||||
|             //            std::cout << "bounds do not hold for " << it.second->get_name() << std::endl;
 | ||||
|             it.second->bounds_hold(sol_it->second); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> T lp_primal_simplex<T, X>::get_row_value(unsigned i, std::unordered_map<std::string, T> const & solution, std::ostream * out) { | ||||
|     auto it = this->m_A_values.find(i); | ||||
|     if (it == this->m_A_values.end()) { | ||||
|         std::stringstream s; | ||||
|         s << "cannot find row " << i; | ||||
|         throw_exception(s.str() ); | ||||
|     } | ||||
|     T ret = numeric_traits<T>::zero(); | ||||
|     for (auto & pair : it->second) { | ||||
|         auto cit = this->m_map_from_var_index_to_column_info.find(pair.first); | ||||
|         lean_assert(cit != this->m_map_from_var_index_to_column_info.end()); | ||||
|         column_info<T> * ci = cit->second; | ||||
|         auto sol_it = solution.find(ci->get_name()); | ||||
|         lean_assert(sol_it != solution.end()); | ||||
|         T column_val = sol_it->second; | ||||
|         if (out != nullptr) { | ||||
|             (*out) << pair.second << "(" << ci->get_name() << "=" << column_val << ") "; | ||||
|         } | ||||
|         ret += pair.second * column_val; | ||||
|     } | ||||
|     if (out != nullptr) { | ||||
|         (*out) << " = " << ret << std::endl; | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_primal_simplex<T, X>::row_constraint_holds(unsigned i, std::unordered_map<std::string, T> const & solution, std::ostream *out) { | ||||
|     T row_val = get_row_value(i, solution, out); | ||||
|     auto & constraint = this->m_constraints[i]; | ||||
|     T rs = constraint.m_rs; | ||||
|     bool print = out != nullptr; | ||||
|     switch (constraint.m_relation) { | ||||
|     case Equal: | ||||
|         if (fabs(numeric_traits<T>::get_double(row_val - rs)) > 0.00001) { | ||||
|             if (print) { | ||||
|                 (*out) << "should be = " << rs << std::endl; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         return true; | ||||
|     case Greater_or_equal: | ||||
|         if (numeric_traits<T>::get_double(row_val - rs) < -0.00001) { | ||||
|             if (print) { | ||||
|                 (*out) << "should be >= " << rs << std::endl; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         return true;; | ||||
| 
 | ||||
|     case Less_or_equal: | ||||
|         if (numeric_traits<T>::get_double(row_val - rs) > 0.00001) { | ||||
|             if (print) { | ||||
|                 (*out) << "should be <= " << rs << std::endl; | ||||
|             } | ||||
|             return false; | ||||
|         } | ||||
|         return true;; | ||||
|     } | ||||
|     lean_unreachable(); | ||||
|     return false; // it is unreachable
 | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_primal_simplex<T, X>::row_constraints_hold(std::unordered_map<std::string, T> const & solution) { | ||||
|     for (auto it : this->m_A_values) { | ||||
|         if (!row_constraint_holds(it.first, solution, nullptr)) { | ||||
|             row_constraint_holds(it.first, solution, nullptr); | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> T lp_primal_simplex<T, X>::get_current_cost() const { | ||||
|     T ret = numeric_traits<T>::zero(); | ||||
|     for (auto it : this->m_map_from_var_index_to_column_info) { | ||||
|         ret += this->get_column_cost_value(it.first, it.second); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| } | ||||
							
								
								
									
										20
									
								
								src/util/lp/lp_primal_simplex_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										20
									
								
								src/util/lp/lp_primal_simplex_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,20 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <utility> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include "util/vector.h" | ||||
| #include <functional> | ||||
| #include "util/lp/lp_primal_simplex.hpp" | ||||
| template bool lean::lp_primal_simplex<double, double>::bounds_hold(std::unordered_map<std::string, double, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, double> > > const&); | ||||
| template bool lean::lp_primal_simplex<double, double>::row_constraints_hold(std::unordered_map<std::string, double, std::hash<std::string>, std::equal_to<std::string>, std::allocator<std::pair<std::string const, double> > > const&); | ||||
| template double lean::lp_primal_simplex<double, double>::get_current_cost() const; | ||||
| template double lean::lp_primal_simplex<double, double>::get_column_value(unsigned int) const; | ||||
| template lean::lp_primal_simplex<double, double>::~lp_primal_simplex(); | ||||
| template lean::lp_primal_simplex<lean::mpq, lean::mpq>::~lp_primal_simplex(); | ||||
| template lean::mpq lean::lp_primal_simplex<lean::mpq, lean::mpq>::get_current_cost() const; | ||||
| template lean::mpq lean::lp_primal_simplex<lean::mpq, lean::mpq>::get_column_value(unsigned int) const; | ||||
| template void lean::lp_primal_simplex<double, double>::find_maximal_solution(); | ||||
| template void lean::lp_primal_simplex<lean::mpq, lean::mpq>::find_maximal_solution(); | ||||
							
								
								
									
										339
									
								
								src/util/lp/lp_settings.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										339
									
								
								src/util/lp/lp_settings.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,339 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include <string> | ||||
| #include <algorithm> | ||||
| #include <limits> | ||||
| #include <sys/timeb.h> | ||||
| #include <iomanip> | ||||
| #include "util/lp/lp_utils.h" | ||||
| 
 | ||||
| namespace lean { | ||||
| typedef unsigned var_index; | ||||
| typedef unsigned constraint_index; | ||||
| typedef unsigned row_index; | ||||
| enum class column_type  { | ||||
|     free_column = 0, | ||||
|         low_bound = 1, | ||||
|         upper_bound = 2, | ||||
|         boxed = 3, | ||||
|         fixed = 4 | ||||
|         }; | ||||
| 
 | ||||
| enum class simplex_strategy_enum { | ||||
|     tableau_rows = 0, | ||||
|     tableau_costs = 1, | ||||
|     no_tableau = 2 | ||||
| }; | ||||
| 
 | ||||
| std::string column_type_to_string(column_type t); | ||||
| 
 | ||||
| enum lp_status { | ||||
|     UNKNOWN, | ||||
|     INFEASIBLE, | ||||
|     TENTATIVE_UNBOUNDED, | ||||
|     UNBOUNDED, | ||||
|     TENTATIVE_DUAL_UNBOUNDED, | ||||
|     DUAL_UNBOUNDED, | ||||
|     OPTIMAL, | ||||
|     FEASIBLE, | ||||
|     FLOATING_POINT_ERROR, | ||||
|     TIME_EXHAUSTED, | ||||
|     ITERATIONS_EXHAUSTED, | ||||
|     EMPTY, | ||||
|     UNSTABLE | ||||
| }; | ||||
| 
 | ||||
| // when the ratio of the vector lenth to domain size to is greater than the return value we switch to solve_By_for_T_indexed_only
 | ||||
| template <typename X> | ||||
| unsigned ratio_of_index_size_to_all_size() { | ||||
|     if (numeric_traits<X>::precise()) | ||||
|         return 10; | ||||
|     return 120; | ||||
| } | ||||
| 
 | ||||
| const char* lp_status_to_string(lp_status status); | ||||
| 
 | ||||
| inline std::ostream& operator<<(std::ostream& out, lp_status status) { | ||||
|     return out << lp_status_to_string(status); | ||||
| } | ||||
| 
 | ||||
| lp_status lp_status_from_string(std::string status); | ||||
| 
 | ||||
| enum non_basic_column_value_position { at_low_bound, at_upper_bound, at_fixed, free_of_bounds, not_at_bound }; | ||||
| 
 | ||||
| template <typename X> bool is_epsilon_small(const X & v, const double& eps);    // forward definition
 | ||||
| 
 | ||||
| int get_millisecond_count(); | ||||
| int get_millisecond_span(int start_time); | ||||
| unsigned my_random(); | ||||
| void my_random_init(long unsigned seed); | ||||
| 
 | ||||
| 
 | ||||
| class lp_resource_limit { | ||||
| public: | ||||
|     virtual bool get_cancel_flag() = 0; | ||||
| }; | ||||
| 
 | ||||
| struct stats { | ||||
|     unsigned m_total_iterations; | ||||
|     unsigned m_iters_with_no_cost_growing; | ||||
|     unsigned m_num_factorizations; | ||||
|     unsigned m_num_of_implied_bounds; | ||||
|     unsigned m_need_to_solve_inf; | ||||
|     stats() { reset(); } | ||||
|     void reset() { memset(this, 0, sizeof(*this)); } | ||||
| }; | ||||
| 
 | ||||
| struct lp_settings { | ||||
| private: | ||||
|     class default_lp_resource_limit : public lp_resource_limit { | ||||
|         lp_settings& m_settings; | ||||
|         int m_start_time; | ||||
|     public: | ||||
|         default_lp_resource_limit(lp_settings& s): m_settings(s), m_start_time(get_millisecond_count()) {} | ||||
|         virtual bool get_cancel_flag() { | ||||
|             int span_in_mills = get_millisecond_span(m_start_time); | ||||
|             return (span_in_mills / 1000.0  > m_settings.time_limit); | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     default_lp_resource_limit m_default_resource_limit; | ||||
|     lp_resource_limit* m_resource_limit; | ||||
|     // used for debug output
 | ||||
|     std::ostream* m_debug_out = &std::cout; | ||||
|     // used for messages, for example, the computation progress messages
 | ||||
|     std::ostream* m_message_out = &std::cout; | ||||
| 
 | ||||
|     stats  m_stats; | ||||
| 
 | ||||
| public: | ||||
|     unsigned reps_in_scaler = 20; | ||||
|     // 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; | ||||
|     int 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
 | ||||
| 
 | ||||
|     bool m_bound_propagation = true; | ||||
|      | ||||
|     bool bound_progation() const { | ||||
|         return m_bound_propagation; | ||||
|     } | ||||
| 
 | ||||
|     bool& bound_propagation() { | ||||
|         return m_bound_propagation; | ||||
|     } | ||||
|      | ||||
|     lp_settings() : m_default_resource_limit(*this), m_resource_limit(&m_default_resource_limit) {} | ||||
| 
 | ||||
|     void set_resource_limit(lp_resource_limit& lim) { m_resource_limit = &lim; } | ||||
|     bool get_cancel_flag() const { return m_resource_limit->get_cancel_flag(); } | ||||
| 
 | ||||
|     void set_debug_ostream(std::ostream* out) { m_debug_out = out; } | ||||
|     void set_message_ostream(std::ostream* out) { m_message_out = out; } | ||||
|      | ||||
|     std::ostream* get_debug_ostream() { return m_debug_out; } | ||||
|     std::ostream* get_message_ostream() { return m_message_out; } | ||||
|     stats& st() { return m_stats; } | ||||
|     stats const& st() const { return m_stats; } | ||||
| 
 | ||||
|     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, primal_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) const { | ||||
|         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 presolve_with_double_solver_for_lar = true; | ||||
|     simplex_strategy_enum m_simplex_strategy = simplex_strategy_enum::tableau_rows; | ||||
|     simplex_strategy_enum simplex_strategy() const { | ||||
|         return m_simplex_strategy; | ||||
|     } | ||||
| 
 | ||||
|     simplex_strategy_enum & simplex_strategy() { | ||||
|         return m_simplex_strategy; | ||||
|     } | ||||
| 
 | ||||
|     bool use_tableau() const { | ||||
|         return m_simplex_strategy != simplex_strategy_enum::no_tableau; | ||||
|     } | ||||
| 
 | ||||
|     bool use_tableau_rows() const { | ||||
|         return m_simplex_strategy == simplex_strategy_enum::tableau_rows; | ||||
|     } | ||||
|      | ||||
|     int report_frequency = 1000; | ||||
|     bool print_statistics = false; | ||||
|     unsigned column_norms_update_frequency = 12000; | ||||
|     bool scale_with_ratio = true; | ||||
|     double density_threshold = 0.7; // need to tune it up, todo
 | ||||
| #ifdef LEAN_DEBUG | ||||
|     static unsigned ddd; // used for debugging    
 | ||||
| #endif | ||||
|     bool use_breakpoints_in_feasibility_search = false; | ||||
|     unsigned random_seed = 1; | ||||
|     static unsigned long random_next; | ||||
|     unsigned max_row_length_for_bound_propagation = 300; | ||||
|     bool backup_costs = true; | ||||
| }; // end of lp_settings class
 | ||||
| 
 | ||||
| 
 | ||||
| #define LP_OUT(_settings_, _msg_) { if (_settings_.get_debug_ostream()) { *_settings_.get_debug_ostream() << _msg_; } } | ||||
| 
 | ||||
| template <typename T> | ||||
| std::string T_to_string(const T & t) { | ||||
|     std::ostringstream strs; | ||||
|     strs << t; | ||||
|     return strs.str(); | ||||
| } | ||||
| 
 | ||||
| inline std::string T_to_string(const numeric_pair<mpq> & t) { | ||||
|     std::ostringstream strs; | ||||
|     double r = (t.x + t.y / mpq(1000)).get_double(); | ||||
|     strs << r; | ||||
|     return strs.str(); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| inline std::string T_to_string(const mpq & t) { | ||||
|     std::ostringstream strs; | ||||
|     strs << t.get_double(); | ||||
|     return strs.str(); | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| bool val_is_smaller_than_eps(T const & t, double const & eps) { | ||||
|     if (!numeric_traits<T>::precise()) { | ||||
|         return numeric_traits<T>::get_double(t) < eps; | ||||
|     } | ||||
|     return t <= numeric_traits<T>::zero(); | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| bool vectors_are_equal(T * a, vector<T>  &b, unsigned n); | ||||
| 
 | ||||
| template <typename T> | ||||
| bool vectors_are_equal(const vector<T> & a, const buffer<T>  &b); | ||||
| 
 | ||||
| template <typename T> | ||||
| bool vectors_are_equal(const vector<T> & a, const vector<T> &b); | ||||
| 
 | ||||
| template <typename T> | ||||
| T abs (T const & v) { return v >= zero_of_type<T>() ? v : -v; } | ||||
| 
 | ||||
| template <typename X> | ||||
| X max_abs_in_vector(vector<X>& t){ | ||||
|     X r(zero_of_type<X>()); | ||||
|     for (auto & v : t) | ||||
|         r = std::max(abs(v) , r); | ||||
|     return r; | ||||
| } | ||||
| inline void print_blanks(int n, std::ostream & out) { | ||||
|     while (n--) {out << ' '; } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // after a push of the last element we ensure that the vector increases
 | ||||
| // we also suppose that before the last push the vector was increasing
 | ||||
| inline void ensure_increasing(vector<unsigned> & v) { | ||||
|     lean_assert(v.size() > 0); | ||||
|     unsigned j = v.size() - 1; | ||||
|     for (; j > 0; j-- ) | ||||
|         if (v[j] <= v[j - 1]) { | ||||
|             // swap
 | ||||
|             unsigned t = v[j]; | ||||
|             v[j] = v[j-1]; | ||||
|             v[j-1] = t; | ||||
|         } else { | ||||
|             break; | ||||
|         } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| #if LEAN_DEBUG | ||||
| bool D(); | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										133
									
								
								src/util/lp/lp_settings.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										133
									
								
								src/util/lp/lp_settings.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,133 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <cmath> | ||||
| #include <string> | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/lp_settings.h" | ||||
| namespace lean { | ||||
| std::string column_type_to_string(column_type t) { | ||||
|     switch (t) { | ||||
|     case column_type::fixed:       return "fixed"; | ||||
|     case column_type::boxed:       return "boxed"; | ||||
|     case column_type::low_bound:   return "low_bound"; | ||||
|     case column_type::upper_bound: return "upper_bound"; | ||||
|     case column_type::free_column: return "free_column"; | ||||
|     default:  lean_unreachable(); | ||||
|     } | ||||
|     return "unknown"; // it is unreachable
 | ||||
| } | ||||
| 
 | ||||
| const char* lp_status_to_string(lp_status status) { | ||||
|     switch (status) { | ||||
|     case UNKNOWN: return "UNKNOWN"; | ||||
|     case INFEASIBLE: return "INFEASIBLE"; | ||||
|     case UNBOUNDED: return "UNBOUNDED"; | ||||
|     case TENTATIVE_DUAL_UNBOUNDED: return "TENTATIVE_DUAL_UNBOUNDED"; | ||||
|     case DUAL_UNBOUNDED: return "DUAL_UNBOUNDED"; | ||||
|     case OPTIMAL: return "OPTIMAL"; | ||||
|     case FEASIBLE: return "FEASIBLE"; | ||||
|     case FLOATING_POINT_ERROR: return "FLOATING_POINT_ERROR"; | ||||
|     case TIME_EXHAUSTED: return "TIME_EXHAUSTED"; | ||||
|     case ITERATIONS_EXHAUSTED: return "ITERATIONS_EXHAUSTED"; | ||||
|     case EMPTY: return "EMPTY"; | ||||
|     case UNSTABLE: return "UNSTABLE"; | ||||
|     default: | ||||
|         lean_unreachable(); | ||||
|     } | ||||
|     return "UNKNOWN";  // it is unreachable
 | ||||
| } | ||||
| 
 | ||||
| lp_status lp_status_from_string(std::string status) { | ||||
|     if (status == "UNKNOWN") return  lp_status::UNKNOWN; | ||||
|     if (status == "INFEASIBLE") return lp_status::INFEASIBLE; | ||||
|     if (status == "UNBOUNDED") return lp_status::UNBOUNDED; | ||||
|     if (status == "OPTIMAL") return lp_status::OPTIMAL; | ||||
|     if (status == "FEASIBLE") return lp_status::FEASIBLE; | ||||
|     if (status == "FLOATING_POINT_ERROR") return lp_status::FLOATING_POINT_ERROR; | ||||
|     if (status == "TIME_EXHAUSTED") return lp_status::TIME_EXHAUSTED; | ||||
|     if (status == "ITERATIONS_EXHAUSTED") return lp_status::ITERATIONS_EXHAUSTED; | ||||
|     if (status == "EMPTY") return lp_status::EMPTY; | ||||
|     lean_unreachable(); | ||||
|     return lp_status::UNKNOWN; // it is unreachable
 | ||||
| } | ||||
| int get_millisecond_count() { | ||||
|     timeb tb; | ||||
|     ftime(&tb); | ||||
|     return tb.millitm + (tb.time & 0xfffff) * 1000; | ||||
| } | ||||
| 
 | ||||
| int get_millisecond_span(int start_time) { | ||||
|     int span = get_millisecond_count() - start_time; | ||||
|     if (span < 0) | ||||
|         span += 0x100000 * 1000; | ||||
|     return span; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| void my_random_init(long unsigned seed) { | ||||
|     lp_settings::random_next = seed;    | ||||
| } | ||||
| 
 | ||||
| unsigned my_random() { | ||||
|     lp_settings::random_next = lp_settings::random_next * 1103515245 + 12345; | ||||
|     return((unsigned)(lp_settings::random_next/65536) % 32768); | ||||
| } | ||||
| 
 | ||||
| template <typename T> | ||||
| bool vectors_are_equal(T * a, vector<T>  &b, unsigned n) { | ||||
|     if (numeric_traits<T>::precise()) { | ||||
|         for (unsigned i = 0; i < n; i ++){ | ||||
|             if (!numeric_traits<T>::is_zero(a[i] - b[i])) { | ||||
|                 // std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
 | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         for (unsigned i = 0; i < n; i ++){ | ||||
|             if (std::abs(numeric_traits<T>::get_double(a[i] - b[i])) > 0.000001) { | ||||
|                 // std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
 | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T> | ||||
| bool vectors_are_equal(const vector<T> & a, const vector<T>  &b) { | ||||
|     unsigned n = static_cast<unsigned>(a.size()); | ||||
|     if (n != b.size()) return false; | ||||
|     if (numeric_traits<T>::precise()) { | ||||
|         for (unsigned i = 0; i < n; i ++){ | ||||
|             if (!numeric_traits<T>::is_zero(a[i] - b[i])) { | ||||
|                 // std::cout << "a[" << i <<"]" << a[i] << ", " << "b[" << i <<"]" << b[i] << std::endl;
 | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|     } else { | ||||
|         for (unsigned i = 0; i < n; i ++){ | ||||
|             double da = numeric_traits<T>::get_double(a[i]); | ||||
|             double db = numeric_traits<T>::get_double(b[i]); | ||||
|             double amax = std::max(fabs(da), fabs(db)); | ||||
|             if (amax > 1) { | ||||
|                 da /= amax; | ||||
|                 db /= amax; | ||||
|             } | ||||
|                  | ||||
|             if (fabs(da - db) > 0.000001) { | ||||
|                 // std::cout << "a[" << i <<"] = " << a[i] << ", but " << "b[" << i <<"] = " << b[i] << std::endl;
 | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| unsigned long lp_settings::random_next = 1; | ||||
| #ifdef LEAN_DEBUG | ||||
| unsigned lp_settings::ddd = 0; | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										10
									
								
								src/util/lp/lp_settings_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								src/util/lp/lp_settings_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,10 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/vector.h" | ||||
| #include <memory> | ||||
| #include "util/lp/lp_settings.hpp" | ||||
| template bool lean::vectors_are_equal<double>(vector<double> const&, vector<double> const&); | ||||
| template bool lean::vectors_are_equal<lean::mpq>(vector<lean::mpq > const&, vector<lean::mpq> const&); | ||||
| 
 | ||||
							
								
								
									
										253
									
								
								src/util/lp/lp_solver.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										253
									
								
								src/util/lp/lp_solver.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,253 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include <string> | ||||
| #include <unordered_map> | ||||
| #include <algorithm> | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/lp_settings.h" | ||||
| #include "util/lp/column_info.h" | ||||
| #include "util/lp/static_matrix.h" | ||||
| #include "util/lp/lp_core_solver_base.h" | ||||
| #include "util/lp/scaler.h" | ||||
| #include "util/lp/linear_combination_iterator.h" | ||||
| #include "util/lp/bound_analyzer_on_row.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 : public column_namer { | ||||
|     column_info<T> * get_or_create_column_info(unsigned column); | ||||
| 
 | ||||
| protected: | ||||
|     T get_column_cost_value(unsigned j, column_info<T> * ci) const; | ||||
| public: | ||||
|     unsigned m_total_iterations; | ||||
|     static_matrix<T, X>* m_A = nullptr; // this is the matrix of constraints
 | ||||
|     vector<T> m_b; // the right side vector
 | ||||
|     unsigned m_first_stage_iterations = 0; | ||||
|     unsigned m_second_stage_iterations = 0; | ||||
|     std::unordered_map<unsigned, lp_constraint<T, X>> m_constraints; | ||||
|     std::unordered_map<var_index, column_info<T>*> m_map_from_var_index_to_column_info; | ||||
|     std::unordered_map<unsigned, std::unordered_map<unsigned, T> > m_A_values; | ||||
|     std::unordered_map<std::string, unsigned> m_names_to_columns; // don't have to use it
 | ||||
|     std::unordered_map<unsigned, unsigned> m_external_rows_to_core_solver_rows; | ||||
|     std::unordered_map<unsigned, unsigned> m_core_solver_rows_to_external_rows; | ||||
|     std::unordered_map<unsigned, unsigned> m_core_solver_columns_to_external_columns; | ||||
|     vector<T> m_column_scale; | ||||
|     std::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; | ||||
|     vector<unsigned> m_nbasis; | ||||
|     vector<int> m_heading; | ||||
| 
 | ||||
| 
 | ||||
|     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); | ||||
| 
 | ||||
|     void set_cost_for_column(unsigned column, T  column_cost) { | ||||
|         get_or_create_column_info(column)->set_cost(column_cost); | ||||
|     } | ||||
|     std::string get_column_name(unsigned j) const override; | ||||
| 
 | ||||
|     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(std::string name, unsigned column); | ||||
| 
 | ||||
|     virtual T get_column_value(unsigned column) const = 0; | ||||
| 
 | ||||
|     T get_column_value_by_name(std::string name) const; | ||||
| 
 | ||||
|     // returns -1 if not found
 | ||||
|     virtual int get_column_index_by_name(std::string name) const; | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     void set_status(lp_status st)  { | ||||
|         m_status = st; | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     virtual ~lp_solver(); | ||||
| 
 | ||||
|     void flip_costs(); | ||||
| 
 | ||||
|     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; | ||||
|     } | ||||
| 
 | ||||
|     unsigned get_max_iterations_per_stage() const { | ||||
|         return m_settings.max_total_number_of_iterations; | ||||
|     } | ||||
| protected: | ||||
|     bool problem_is_empty(); | ||||
| 
 | ||||
|     void scale(); | ||||
| 
 | ||||
| 
 | ||||
|     void print_rows_scale_stats(std::ostream & out); | ||||
| 
 | ||||
|     void print_columns_scale_stats(std::ostream & out); | ||||
| 
 | ||||
|     void print_row_scale_stats(unsigned i, std::ostream & out); | ||||
| 
 | ||||
|     void print_column_scale_stats(unsigned j, std::ostream & out); | ||||
| 
 | ||||
|     void print_scale_stats(std::ostream & out); | ||||
| 
 | ||||
|     void get_max_abs_in_row(std::unordered_map<unsigned, T> & row_map); | ||||
| 
 | ||||
|     void pin_vars_down_on_row(std::unordered_map<unsigned, T> & row) { | ||||
|         pin_vars_on_row_with_sign(row, - numeric_traits<T>::one()); | ||||
|     } | ||||
| 
 | ||||
|     void pin_vars_up_on_row(std::unordered_map<unsigned, T> & row) { | ||||
|         pin_vars_on_row_with_sign(row, numeric_traits<T>::one()); | ||||
|     } | ||||
| 
 | ||||
|     void pin_vars_on_row_with_sign(std::unordered_map<unsigned, T> & row, T sign ); | ||||
| 
 | ||||
|     bool get_minimal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound); | ||||
| 
 | ||||
|     bool get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound); | ||||
| 
 | ||||
|     bool row_is_zero(std::unordered_map<unsigned, T> & row); | ||||
| 
 | ||||
|     bool row_e_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index); | ||||
| 
 | ||||
|     bool row_ge_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index); | ||||
| 
 | ||||
|     bool row_le_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index); | ||||
| 
 | ||||
|     // analyse possible max and min values that are derived from var boundaries
 | ||||
|     // Let us say that the we have a "ge" constraint, and the min value is equal to the rs.
 | ||||
|     // 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(std::unordered_map<unsigned, T> & row, unsigned row_index ); | ||||
| 
 | ||||
|     void remove_fixed_or_zero_columns(); | ||||
| 
 | ||||
|     void remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map<unsigned, T> & row); | ||||
| 
 | ||||
|     unsigned try_to_remove_some_rows(); | ||||
| 
 | ||||
|     void cleanup(); | ||||
| 
 | ||||
|     void map_external_rows_to_core_solver_rows(); | ||||
| 
 | ||||
|     void map_external_columns_to_core_solver_columns(); | ||||
| 
 | ||||
|     unsigned number_of_core_structurals() { | ||||
|         return static_cast<unsigned>(m_core_solver_columns_to_external_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(); | ||||
| 
 | ||||
|     void fill_A_from_A_values(); | ||||
| 
 | ||||
|     void fill_matrix_A_and_init_right_side(); | ||||
| 
 | ||||
|     void count_slacks_and_artificials(); | ||||
| 
 | ||||
|     void count_slacks_and_artificials_for_row(unsigned i); | ||||
| 
 | ||||
|     T low_bound_shift_for_row(unsigned i); | ||||
| 
 | ||||
|     void fill_m_b(); | ||||
| 
 | ||||
|     T get_column_value_with_core_solver(unsigned column, lp_core_solver_base<T, X> * core_solver) const; | ||||
|     void set_scaled_cost(unsigned j); | ||||
|     void print_statistics_on_A(std::ostream & out)  { | ||||
|         out << "extended A[" << this->m_A->row_count() << "," << this->m_A->column_count() << "]" << std::endl; | ||||
|     } | ||||
| 
 | ||||
|     struct row_tighten_stats { | ||||
|         unsigned n_of_new_bounds = 0; | ||||
|         unsigned n_of_fixed = 0; | ||||
|         bool is_obsolete = false; | ||||
|     }; | ||||
|      | ||||
| 
 | ||||
| 
 | ||||
| public: | ||||
|     lp_settings & settings() { return m_settings;} | ||||
|     void print_model(std::ostream & s) const { | ||||
|         s << "objective = " << get_current_cost() << std::endl; | ||||
|         s << "column values\n"; | ||||
|         for (auto & it : m_names_to_columns) { | ||||
|             s << it.first << " = " << get_column_value(it.second) << std::endl; | ||||
|         } | ||||
|     } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										554
									
								
								src/util/lp/lp_solver.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										554
									
								
								src/util/lp/lp_solver.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,554 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <string> | ||||
| #include <algorithm> | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/lp_solver.h" | ||||
| namespace lean { | ||||
| template <typename T, typename X> column_info<T> * lp_solver<T, X>::get_or_create_column_info(unsigned column) { | ||||
|     auto it = m_map_from_var_index_to_column_info.find(column); | ||||
|     return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info<T>(static_cast<unsigned>(-1))) : it->second; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| std::string lp_solver<T, X>::get_column_name(unsigned j) const { // j here is the core solver index
 | ||||
|     auto it = this->m_core_solver_columns_to_external_columns.find(j); | ||||
|     if (it == this->m_core_solver_columns_to_external_columns.end()) | ||||
|         return std::string("x")+T_to_string(j); | ||||
|     unsigned external_j = it->second; | ||||
|     auto t = this->m_map_from_var_index_to_column_info.find(external_j); | ||||
|     if (t == this->m_map_from_var_index_to_column_info.end()) { | ||||
|         return std::string("x") +T_to_string(external_j); | ||||
|     } | ||||
|     return t->second->get_name(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> T lp_solver<T, X>::get_column_cost_value(unsigned j, column_info<T> * ci) const { | ||||
|     if (ci->is_fixed()) { | ||||
|         return ci->get_cost() * ci->get_fixed_value(); | ||||
|     } | ||||
|     return ci->get_cost() * get_column_value(j); | ||||
| } | ||||
| template <typename T, typename X> void lp_solver<T, X>::add_constraint(lp_relation relation, T  right_side, unsigned row_index) { | ||||
|     lean_assert(m_constraints.find(row_index) == m_constraints.end()); | ||||
|     lp_constraint<T, X> cs(right_side, relation); | ||||
|     m_constraints[row_index] = cs; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::give_symbolic_name_to_column(std::string name, unsigned column) { | ||||
|     auto it = m_map_from_var_index_to_column_info.find(column); | ||||
|     column_info<T> *ci; | ||||
|     if (it == m_map_from_var_index_to_column_info.end()){ | ||||
|         m_map_from_var_index_to_column_info[column] = ci = new column_info<T>; | ||||
|     } else { | ||||
|         ci = it->second; | ||||
|     } | ||||
|     ci->set_name(name); | ||||
|     m_names_to_columns[name] = column; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X>  T lp_solver<T, X>::get_column_value_by_name(std::string name) const { | ||||
|     auto it = m_names_to_columns.find(name); | ||||
|     if (it == m_names_to_columns.end()) { | ||||
|         std::stringstream s; | ||||
|         s << "get_column_value_by_name " << name; | ||||
|         throw_exception(s.str()); | ||||
|     } | ||||
|     return get_column_value(it -> second); | ||||
| } | ||||
| 
 | ||||
| // returns -1 if not found
 | ||||
| template <typename T, typename X> int lp_solver<T, X>::get_column_index_by_name(std::string name) const { | ||||
|     auto t = m_names_to_columns.find(name); | ||||
|     if (t == m_names_to_columns.end()) { | ||||
|         return -1; | ||||
|     } | ||||
|     return t->second; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X>  lp_solver<T, X>::~lp_solver(){ | ||||
|     if (m_A != nullptr) { | ||||
|         delete m_A; | ||||
|     } | ||||
|     for (auto t : m_map_from_var_index_to_column_info) { | ||||
|         delete t.second; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::flip_costs() { | ||||
|     for (auto t : m_map_from_var_index_to_column_info) { | ||||
|         column_info<T> *ci = t.second; | ||||
|         ci->set_cost(-ci->get_cost()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X>    bool lp_solver<T, X>::problem_is_empty() { | ||||
|     for (auto & c : m_A_values) | ||||
|         if (c.second.size()) | ||||
|             return false; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::scale() { | ||||
|     if (numeric_traits<T>::precise() || m_settings.use_scaling == false) { | ||||
|         m_column_scale.clear(); | ||||
|         m_column_scale.resize(m_A->column_count(), one_of_type<T>()); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     T smin = T(m_settings.scaling_minimum); | ||||
|     T smax = T(m_settings.scaling_maximum); | ||||
| 
 | ||||
|     scaler<T, X> scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings); | ||||
|     if (!scaler.scale()) { | ||||
|         unscale(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::print_rows_scale_stats(std::ostream & out) { | ||||
|     out << "rows max" << std::endl; | ||||
|     for (unsigned i = 0; i < m_A->row_count(); i++) { | ||||
|         print_row_scale_stats(i, out); | ||||
|     } | ||||
|     out << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::print_columns_scale_stats(std::ostream & out) { | ||||
|     out << "columns max" << std::endl; | ||||
|     for (unsigned i = 0; i < m_A->column_count(); i++) { | ||||
|         print_column_scale_stats(i, out); | ||||
|     } | ||||
|     out << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::print_row_scale_stats(unsigned i, std::ostream & out) { | ||||
|     out << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " "; | ||||
|     out << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")"; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::print_column_scale_stats(unsigned j, std::ostream & out) { | ||||
|     out << "(" << m_A->get_min_abs_in_row(j) << " "; | ||||
|     out <<  m_A->get_max_abs_in_column(j) << ")"; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::print_scale_stats(std::ostream & out) { | ||||
|     print_rows_scale_stats(out); | ||||
|     print_columns_scale_stats(out); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::get_max_abs_in_row(std::unordered_map<unsigned, T> & row_map) { | ||||
|     T ret = numeric_traits<T>::zero(); | ||||
|     for (auto jp : row_map) { | ||||
|         T ac = numeric_traits<T>::abs(jp->second); | ||||
|         if (ac > ret) { | ||||
|             ret = ac; | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::pin_vars_on_row_with_sign(std::unordered_map<unsigned, T> & row, T sign ) { | ||||
|     for (auto t : row) { | ||||
|         unsigned j = t.first; | ||||
|         column_info<T> * ci = m_map_from_var_index_to_column_info[j]; | ||||
|         T a = t.second; | ||||
|         if (a * sign > numeric_traits<T>::zero()) { | ||||
|             lean_assert(ci->upper_bound_is_set()); | ||||
|             ci->set_fixed_value(ci->get_upper_bound()); | ||||
|         } else { | ||||
|             lean_assert(ci->low_bound_is_set()); | ||||
|             ci->set_fixed_value(ci->get_low_bound()); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X>    bool lp_solver<T, X>::get_minimal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound) { | ||||
|     low_bound = numeric_traits<T>::zero(); | ||||
|     for (auto & t : row) { | ||||
|         T a = t.second; | ||||
|         column_info<T> * ci = m_map_from_var_index_to_column_info[t.first]; | ||||
|         if (a > numeric_traits<T>::zero()) { | ||||
|             if (ci->low_bound_is_set()) { | ||||
|                 low_bound += ci->get_low_bound() * a; | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|         } else { | ||||
|             if (ci->upper_bound_is_set()) { | ||||
|                 low_bound += ci->get_upper_bound() * a; | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X>    bool lp_solver<T, X>::get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & low_bound) { | ||||
|     low_bound = numeric_traits<T>::zero(); | ||||
|     for (auto & t : row) { | ||||
|         T a = t.second; | ||||
|         column_info<T> * ci = m_map_from_var_index_to_column_info[t.first]; | ||||
|         if (a < numeric_traits<T>::zero()) { | ||||
|             if (ci->low_bound_is_set()) { | ||||
|                 low_bound += ci->get_low_bound() * a; | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|         } else { | ||||
|             if (ci->upper_bound_is_set()) { | ||||
|                 low_bound += ci->get_upper_bound() * a; | ||||
|             } else { | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X>    bool lp_solver<T, X>::row_is_zero(std::unordered_map<unsigned, T> & row) { | ||||
|     for (auto & t : row) { | ||||
|         if (!is_zero(t.second)) | ||||
|             return false; | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X>    bool lp_solver<T, X>::row_e_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) { | ||||
|     T rs = m_constraints[row_index].m_rs; | ||||
|     if (row_is_zero(row)) { | ||||
|         if (!is_zero(rs)) | ||||
|             m_status = INFEASIBLE; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     T low_bound; | ||||
|     bool lb = get_minimal_row_value(row, low_bound); | ||||
|     if (lb) { | ||||
|         T diff = low_bound - rs; | ||||
|         if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ | ||||
|             // low_bound > rs + m_settings.refactor_epsilon
 | ||||
|             m_status = INFEASIBLE; | ||||
|             return true; | ||||
|         } | ||||
|         if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ | ||||
|             pin_vars_down_on_row(row); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     T upper_bound; | ||||
|     bool ub = get_maximal_row_value(row, upper_bound); | ||||
|     if (ub) { | ||||
|         T diff = rs - upper_bound; | ||||
|         if (!val_is_smaller_than_eps(diff,  m_settings.refactor_tolerance)) { | ||||
|             // upper_bound < rs - m_settings.refactor_tolerance
 | ||||
|             m_status = INFEASIBLE; | ||||
|             return true; | ||||
|         } | ||||
|         if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ | ||||
|             pin_vars_up_on_row(row); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X>  bool lp_solver<T, X>::row_ge_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) { | ||||
|     T rs = m_constraints[row_index].m_rs; | ||||
|     if (row_is_zero(row)) { | ||||
|         if (rs > zero_of_type<X>()) | ||||
|             m_status = INFEASIBLE; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     T upper_bound; | ||||
|     if (get_maximal_row_value(row, upper_bound)) { | ||||
|         T diff = rs - upper_bound; | ||||
|         if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) { | ||||
|             // upper_bound < rs - m_settings.refactor_tolerance
 | ||||
|             m_status = INFEASIBLE; | ||||
|             return true; | ||||
|         } | ||||
|         if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ | ||||
|             pin_vars_up_on_row(row); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool lp_solver<T, X>::row_le_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) { | ||||
|     T low_bound; | ||||
|     T rs = m_constraints[row_index].m_rs; | ||||
|     if (row_is_zero(row)) { | ||||
|         if (rs < zero_of_type<X>()) | ||||
|             m_status = INFEASIBLE; | ||||
|         return true; | ||||
|     } | ||||
| 
 | ||||
|     if (get_minimal_row_value(row, low_bound)) { | ||||
|         T diff = low_bound - rs; | ||||
|         if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){ | ||||
|             // low_bound > rs + m_settings.refactor_tolerance
 | ||||
|             m_status = lp_status::INFEASIBLE; | ||||
|             return true; | ||||
|         } | ||||
|         if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){ | ||||
|             pin_vars_down_on_row(row); | ||||
|             return true; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     return false; | ||||
| } | ||||
| 
 | ||||
| // analyse possible max and min values that are derived from var boundaries
 | ||||
| // Let us say that the we have a "ge" constraint, and the min value is equal to the rs.
 | ||||
| // Then we know what values of the variables are. For each positive coeff of the row it has to be
 | ||||
| // the low boundary of the var and for a negative - the upper.
 | ||||
| 
 | ||||
| // this routing also pins the variables to the boundaries
 | ||||
| template <typename T, typename X>    bool lp_solver<T, X>::row_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index ) { | ||||
|     auto & constraint = m_constraints[row_index]; | ||||
|     switch (constraint.m_relation) { | ||||
|     case lp_relation::Equal: | ||||
|         return row_e_is_obsolete(row, row_index); | ||||
| 
 | ||||
|     case lp_relation::Greater_or_equal: | ||||
|         return row_ge_is_obsolete(row, row_index); | ||||
| 
 | ||||
|     case lp_relation::Less_or_equal: | ||||
|         return row_le_is_obsolete(row, row_index); | ||||
|     } | ||||
|     lean_unreachable(); | ||||
|     return false; // it is unreachable
 | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::remove_fixed_or_zero_columns() { | ||||
|     for (auto & i_row : m_A_values) { | ||||
|         remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::remove_fixed_or_zero_columns_from_row(unsigned i, std::unordered_map<unsigned, T> & row) { | ||||
|     auto & constraint = m_constraints[i]; | ||||
|     vector<unsigned> removed; | ||||
|     for (auto & col : row) { | ||||
|         unsigned j = col.first; | ||||
|         lean_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end()); | ||||
|         column_info<T> * ci = m_map_from_var_index_to_column_info[j]; | ||||
|         if (ci->is_fixed()) { | ||||
|             removed.push_back(j); | ||||
|             T aj = col.second; | ||||
|             constraint.m_rs -= aj * ci->get_fixed_value(); | ||||
|         } else { | ||||
|             if (numeric_traits<T>::is_zero(col.second)){ | ||||
|                 removed.push_back(j); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     for (auto j : removed) { | ||||
|         row.erase(j); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> unsigned lp_solver<T, X>::try_to_remove_some_rows() { | ||||
|     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 static_cast<unsigned>(rows_to_delete.size()); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::cleanup() { | ||||
|     int n = 0; // number of deleted rows
 | ||||
|     int d; | ||||
|     while ((d = try_to_remove_some_rows() > 0)) | ||||
|         n += d; | ||||
| 
 | ||||
|      if (n == 1) { | ||||
|          LP_OUT(m_settings, "deleted one row" << std::endl); | ||||
|      } else if (n) { | ||||
|          LP_OUT(m_settings, "deleted " << n << " rows" << std::endl); | ||||
|      } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::map_external_rows_to_core_solver_rows() { | ||||
|     unsigned size = 0; | ||||
|     for (auto & row : m_A_values) { | ||||
|         m_external_rows_to_core_solver_rows[row.first] = size; | ||||
|         m_core_solver_rows_to_external_rows[size] = row.first; | ||||
|         size++; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::map_external_columns_to_core_solver_columns() { | ||||
|     unsigned size = 0; | ||||
|     for (auto & row : m_A_values) { | ||||
|         for (auto & col : row.second) { | ||||
|             if (col.second == numeric_traits<T>::zero() || m_map_from_var_index_to_column_info[col.first]->is_fixed()) { | ||||
|                 throw_exception("found fixed column"); | ||||
|             } | ||||
|             unsigned j = col.first; | ||||
|             auto column_info_it = m_map_from_var_index_to_column_info.find(j); | ||||
|             lean_assert(column_info_it != m_map_from_var_index_to_column_info.end()); | ||||
| 
 | ||||
|             auto j_column = column_info_it->second->get_column_index(); | ||||
|             if (!is_valid(j_column)) { // j is a newcomer
 | ||||
|                 m_map_from_var_index_to_column_info[j]->set_column_index(size); | ||||
|                 m_core_solver_columns_to_external_columns[size++] = j; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::unscale() { | ||||
|     delete m_A; | ||||
|     m_A = nullptr; | ||||
|     fill_A_from_A_values(); | ||||
|     restore_column_scales_to_one(); | ||||
|     fill_m_b(); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::fill_A_from_A_values() { | ||||
|     m_A = new static_matrix<T, X>(static_cast<unsigned>(m_A_values.size()), number_of_core_structurals()); | ||||
|     for (auto & t : m_A_values) { | ||||
|         auto row_it = m_external_rows_to_core_solver_rows.find(t.first); | ||||
|         lean_assert(row_it != m_external_rows_to_core_solver_rows.end()); | ||||
|         unsigned row =  row_it->second; | ||||
|         for (auto k : t.second) { | ||||
|             auto column_info_it = m_map_from_var_index_to_column_info.find(k.first); | ||||
|             lean_assert(column_info_it != m_map_from_var_index_to_column_info.end()); | ||||
|             column_info<T> *ci = column_info_it->second; | ||||
|             unsigned col = ci->get_column_index(); | ||||
|             lean_assert(is_valid(col)); | ||||
|             bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped(); | ||||
|             if (!col_is_flipped) { | ||||
|                 (*m_A)(row, col) = k.second; | ||||
|             } else { | ||||
|                 (*m_A)(row, col) = - k.second; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::fill_matrix_A_and_init_right_side() { | ||||
|     map_external_rows_to_core_solver_rows(); | ||||
|     map_external_columns_to_core_solver_columns(); | ||||
|     lean_assert(m_A == nullptr); | ||||
|     fill_A_from_A_values(); | ||||
|     m_b.resize(m_A->row_count()); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::count_slacks_and_artificials() { | ||||
|     for (int i = row_count() - 1; i >= 0; i--) { | ||||
|         count_slacks_and_artificials_for_row(i); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::count_slacks_and_artificials_for_row(unsigned i) { | ||||
|     lean_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); | ||||
|     auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]]; | ||||
|     switch (constraint.m_relation) { | ||||
|     case Equal: | ||||
|         m_artificials++; | ||||
|         break; | ||||
|     case Greater_or_equal: | ||||
|         m_slacks++; | ||||
|         if (this->m_b[i] > 0) { | ||||
|             m_artificials++; | ||||
|         } | ||||
|         break; | ||||
|     case Less_or_equal: | ||||
|         m_slacks++; | ||||
|         if (this->m_b[i] < 0) { | ||||
|             m_artificials++; | ||||
|         } | ||||
|         break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X>    T lp_solver<T, X>::low_bound_shift_for_row(unsigned i) { | ||||
|     T ret = numeric_traits<T>::zero(); | ||||
| 
 | ||||
|     auto row = this->m_A_values.find(i); | ||||
|     if (row == this->m_A_values.end()) { | ||||
|         throw_exception("cannot find row"); | ||||
|     } | ||||
|     for (auto col : row->second) { | ||||
|         ret += col.second * this->m_map_from_var_index_to_column_info[col.first]->get_shift(); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::fill_m_b() { | ||||
|     for (int i = this->row_count() - 1; i >= 0; i--) { | ||||
|         lean_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end()); | ||||
|         unsigned external_i = this->m_core_solver_rows_to_external_rows[i]; | ||||
|         auto & constraint = this->m_constraints[external_i]; | ||||
|         this->m_b[i] = constraint.m_rs - low_bound_shift_for_row(external_i); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> T lp_solver<T, X>::get_column_value_with_core_solver(unsigned column, lp_core_solver_base<T, X> * core_solver) const { | ||||
|     auto cit = this->m_map_from_var_index_to_column_info.find(column); | ||||
|     if (cit == this->m_map_from_var_index_to_column_info.end()) { | ||||
|         return numeric_traits<T>::zero(); | ||||
|     } | ||||
| 
 | ||||
|     column_info<T> * ci = cit->second; | ||||
| 
 | ||||
|     if (ci->is_fixed()) { | ||||
|         return ci->get_fixed_value(); | ||||
|     } | ||||
| 
 | ||||
|     unsigned cj = ci->get_column_index(); | ||||
|     if (cj != static_cast<unsigned>(-1)) { | ||||
|         T v = core_solver->get_var_value(cj) * this->m_column_scale[cj]; | ||||
|         if (ci->is_free()) { | ||||
|             return v; | ||||
|         } | ||||
|         if (!ci->is_flipped()) { | ||||
|             return v + ci->get_low_bound(); | ||||
|         } | ||||
| 
 | ||||
|         // the flipped case when there is only upper bound
 | ||||
|         return -v + ci->get_upper_bound(); //
 | ||||
|     } | ||||
| 
 | ||||
|     return numeric_traits<T>::zero(); // returns zero for out of boundary columns
 | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void lp_solver<T, X>::set_scaled_cost(unsigned j) { | ||||
|     // grab original costs but modify it with the column scales
 | ||||
|     lean_assert(j < this->m_column_scale.size()); | ||||
|     column_info<T> * ci = this->m_map_from_var_index_to_column_info[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]; | ||||
| } | ||||
| } | ||||
							
								
								
									
										40
									
								
								src/util/lp/lp_solver_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										40
									
								
								src/util/lp/lp_solver_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,40 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <string> | ||||
| #include "util/lp/lp_solver.hpp" | ||||
| template void lean::lp_solver<double, double>::add_constraint(lean::lp_relation, double, unsigned int); | ||||
| template void lean::lp_solver<double, double>::cleanup(); | ||||
| template void lean::lp_solver<double, double>::count_slacks_and_artificials(); | ||||
| template void lean::lp_solver<double, double>::fill_m_b(); | ||||
| template void lean::lp_solver<double, double>::fill_matrix_A_and_init_right_side(); | ||||
| template void lean::lp_solver<double, double>::flip_costs(); | ||||
| template double lean::lp_solver<double, double>::get_column_cost_value(unsigned int, lean::column_info<double>*) const; | ||||
| template int lean::lp_solver<double, double>::get_column_index_by_name(std::string) const; | ||||
| template double lean::lp_solver<double, double>::get_column_value_with_core_solver(unsigned int, lean::lp_core_solver_base<double, double>*) const; | ||||
| template lean::column_info<double>* lean::lp_solver<double, double>::get_or_create_column_info(unsigned int); | ||||
| template void lean::lp_solver<double, double>::give_symbolic_name_to_column(std::string, unsigned int); | ||||
| template void lean::lp_solver<double, double>::print_statistics_on_A(std::ostream & out); | ||||
| template bool lean::lp_solver<double, double>::problem_is_empty(); | ||||
| template void lean::lp_solver<double, double>::scale(); | ||||
| template void lean::lp_solver<double, double>::set_scaled_cost(unsigned int); | ||||
| template lean::lp_solver<double, double>::~lp_solver(); | ||||
| template void lean::lp_solver<lean::mpq, lean::mpq>::add_constraint(lean::lp_relation, lean::mpq, unsigned int); | ||||
| template void lean::lp_solver<lean::mpq, lean::mpq>::cleanup(); | ||||
| template void lean::lp_solver<lean::mpq, lean::mpq>::count_slacks_and_artificials(); | ||||
| template void lean::lp_solver<lean::mpq, lean::mpq>::fill_m_b(); | ||||
| template void lean::lp_solver<lean::mpq, lean::mpq>::fill_matrix_A_and_init_right_side(); | ||||
| template void lean::lp_solver<lean::mpq, lean::mpq>::flip_costs(); | ||||
| template lean::mpq lean::lp_solver<lean::mpq, lean::mpq>::get_column_cost_value(unsigned int, lean::column_info<lean::mpq>*) const; | ||||
| template int lean::lp_solver<lean::mpq, lean::mpq>::get_column_index_by_name(std::string) const; | ||||
| template lean::mpq lean::lp_solver<lean::mpq, lean::mpq>::get_column_value_by_name(std::string) const; | ||||
| template lean::mpq lean::lp_solver<lean::mpq, lean::mpq>::get_column_value_with_core_solver(unsigned int, lean::lp_core_solver_base<lean::mpq, lean::mpq>*) const; | ||||
| template lean::column_info<lean::mpq>* lean::lp_solver<lean::mpq, lean::mpq>::get_or_create_column_info(unsigned int); | ||||
| template void lean::lp_solver<lean::mpq, lean::mpq>::give_symbolic_name_to_column(std::string, unsigned int); | ||||
| template void lean::lp_solver<lean::mpq, lean::mpq>::print_statistics_on_A(std::ostream & out); | ||||
| template bool lean::lp_solver<lean::mpq, lean::mpq>::problem_is_empty(); | ||||
| template void lean::lp_solver<lean::mpq, lean::mpq>::scale(); | ||||
| template void lean::lp_solver<lean::mpq, lean::mpq>::set_scaled_cost(unsigned int); | ||||
| template lean::lp_solver<lean::mpq, lean::mpq>::~lp_solver(); | ||||
| template double lean::lp_solver<double, double>::get_column_value_by_name(std::string) const; | ||||
							
								
								
									
										11
									
								
								src/util/lp/lp_utils.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								src/util/lp/lp_utils.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,11 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/lp_utils.h" | ||||
| #ifdef lp_for_z3 | ||||
| namespace lean { | ||||
| double numeric_traits<double>::g_zero = 0.0; | ||||
| double numeric_traits<double>::g_one = 1.0; | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										141
									
								
								src/util/lp/lp_utils.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										141
									
								
								src/util/lp/lp_utils.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,141 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
|   This file should be present in z3 and in Lean. | ||||
| */ | ||||
| #pragma once | ||||
| #include <string> | ||||
| #include "util/lp/numeric_pair.h" | ||||
| #include "util/debug.h" | ||||
| #include <unordered_map> | ||||
| template <typename A, typename B> | ||||
| bool try_get_val(const std::unordered_map<A,B> & map, const A& key, B & val) { | ||||
|     const auto it = map.find(key); | ||||
|     if (it == map.end()) return false; | ||||
|     val = it->second; | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename A, typename B> | ||||
| bool contains(const std::unordered_map<A, B> & map, const A& key) { | ||||
| 	return map.find(key) != map.end(); | ||||
| } | ||||
| 
 | ||||
| #ifdef lp_for_z3 | ||||
| 
 | ||||
| #ifdef Z3DEBUG | ||||
| #define LEAN_DEBUG 1 | ||||
| #endif | ||||
| 
 | ||||
| namespace lean { | ||||
|     inline void throw_exception(const std::string & str) { | ||||
|         throw default_exception(str); | ||||
|     } | ||||
|     typedef z3_exception exception; | ||||
| 
 | ||||
| #define lean_assert(_x_) { SASSERT(_x_); } | ||||
|     inline void lean_unreachable() { lean_assert(false); } | ||||
|     template <typename X> inline X zero_of_type() { return numeric_traits<X>::zero(); } | ||||
|     template <typename X> inline X one_of_type() { return numeric_traits<X>::one(); } | ||||
|     template <typename X> inline bool is_zero(const X & v) { return numeric_traits<X>::is_zero(v); } | ||||
|     template <typename X> inline bool is_pos(const X & v) { return numeric_traits<X>::is_pos(v); } | ||||
|     template <typename X> inline bool is_neg(const X & v) { return numeric_traits<X>::is_neg(v); } | ||||
| 
 | ||||
|     template <typename X> inline bool precise() { return numeric_traits<X>::precise(); } | ||||
| } | ||||
| namespace std { | ||||
| template<> | ||||
| struct hash<rational> { | ||||
|     inline size_t operator()(const rational & v) const { | ||||
|         return v.hash(); | ||||
|     } | ||||
| }; | ||||
| } | ||||
| 
 | ||||
| template <class T> | ||||
| inline void hash_combine(std::size_t & seed, const T & v) { | ||||
|     seed ^= std::hash<T>()(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; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template<> | ||||
| struct hash<lean::numeric_pair<lean::mpq>> { | ||||
|     inline size_t operator()(const lean::numeric_pair<lean::mpq> & v) const { | ||||
|         size_t seed = 0; | ||||
|         hash_combine(seed, v.x); | ||||
|         hash_combine(seed, v.y); | ||||
|         return seed; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| } | ||||
| #else // else  of #if  lp_for_z3
 | ||||
| #include <utility> | ||||
| #include <functional> | ||||
| //include "util/numerics/mpq.h"
 | ||||
| //include "util/numerics/numeric_traits.h"
 | ||||
| //include "util/numerics/double.h"
 | ||||
| 
 | ||||
| #ifdef __CLANG__ | ||||
| #pragma clang diagnostic push | ||||
| #pragma clang diagnostic ignored "-Wmismatched-tags" | ||||
| #endif | ||||
| namespace std { | ||||
| template<> | ||||
| struct hash<lean::mpq> { | ||||
|     inline size_t operator()(const lean::mpq & v) const { | ||||
|         return v.hash(); | ||||
|     } | ||||
| }; | ||||
| } | ||||
| namespace lean { | ||||
| template <typename X> inline bool  precise() { return numeric_traits<X>::precise();} | ||||
| template <typename X> inline X one_of_type() { return numeric_traits<X>::one(); } | ||||
| template <typename X> inline bool is_zero(const X & v) { return numeric_traits<X>::is_zero(v); } | ||||
| template <typename X> inline double  get_double(const X & v) { return numeric_traits<X>::get_double(v); } | ||||
| template <typename T> inline T zero_of_type() {return numeric_traits<T>::zero();} | ||||
| inline void throw_exception(std::string str) { throw exception(str); } | ||||
| template <typename T> inline T from_string(std::string const & ) { lean_unreachable();} | ||||
| template <> double inline from_string<double>(std::string const & str) { return atof(str.c_str());} | ||||
| template <> mpq inline from_string<mpq>(std::string const & str) { | ||||
|     return mpq(atof(str.c_str())); | ||||
| } | ||||
| 
 | ||||
| } // closing lean
 | ||||
| template <class T> | ||||
| inline void hash_combine(std::size_t & seed, const T & v) { | ||||
|     seed ^= std::hash<T>()(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; | ||||
|     } | ||||
| }; | ||||
| template<> | ||||
| struct hash<lean::numeric_pair<lean::mpq>> { | ||||
|     inline size_t operator()(const lean::numeric_pair<lean::mpq> & v) const { | ||||
|         size_t seed = 0; | ||||
|         hash_combine(seed, v.x); | ||||
|         hash_combine(seed, v.y); | ||||
|         return seed; | ||||
|     } | ||||
| }; | ||||
| } // std
 | ||||
| #ifdef __CLANG__ | ||||
| #pragma clang diagnostic pop | ||||
| #endif | ||||
| #endif | ||||
							
								
								
									
										359
									
								
								src/util/lp/lu.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										359
									
								
								src/util/lp/lu.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,359 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| #include "util/vector.h" | ||||
| #include "util/debug.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" | ||||
| #include "util/lp/dense_matrix.h" | ||||
| namespace lean { | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T, typename X> // print the nr x nc submatrix at the top left corner
 | ||||
| void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc); | ||||
| 
 | ||||
| template<typename T, typename X> | ||||
| void print_matrix(static_matrix<T, X> &m, std::ostream & out); | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void print_matrix(sparse_matrix<T, X>& m, std::ostream & out); | ||||
| #endif | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| X dot_product(const vector<T> & a, const vector<X> & b) { | ||||
|     lean_assert(a.size() == b.size()); | ||||
|     auto r = zero_of_type<X>(); | ||||
|     for (unsigned i = 0; i < a.size(); 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) { | ||||
| #ifdef LEAN_DEBUG | ||||
|         m_one_over_val = numeric_traits<T>::one() / m_val; | ||||
| #endif | ||||
|     } | ||||
| 
 | ||||
|     bool is_dense() const { return false; } | ||||
| 
 | ||||
|     one_elem_on_diag(const one_elem_on_diag & o); | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
|     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; | ||||
| 
 | ||||
|     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_right(indexed_vector<T> & w) { | ||||
|         if (is_zero(w.m_data[m_i])) | ||||
|             return; | ||||
|         auto & v = w.m_data[m_i] /= m_val; | ||||
|         if (lp_settings::is_eps_small_general(v, 1e-14)) { | ||||
|             w.erase_from_index(m_i); | ||||
|             v = zero_of_type<T>(); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|      | ||||
|     void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings); | ||||
| 
 | ||||
|     void conjugate_by_permutation(permutation_matrix<T, X> & p) { | ||||
|         // this = p * this * p(-1)
 | ||||
| #ifdef LEAN_DEBUG | ||||
|         // auto rev = p.get_reverse();
 | ||||
|         // auto deb = ((*this) * rev);
 | ||||
|         // deb = p * deb;
 | ||||
| #endif | ||||
|         m_i = p.apply_reverse(m_i); | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
|         // lean_assert(*this == deb);
 | ||||
| #endif | ||||
|     } | ||||
| }; // end of one_elem_on_diag
 | ||||
| 
 | ||||
| enum class LU_status { OK, Degenerated}; | ||||
| 
 | ||||
| // This class supports updates of the columns of B, and solves systems Bx=b,and yB=c
 | ||||
| // Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5
 | ||||
| template <typename T, typename X> | ||||
| class lu { | ||||
|     LU_status m_status = LU_status::OK; | ||||
| public: | ||||
|     // the fields
 | ||||
|     unsigned m_dim; | ||||
|     static_matrix<T, X> const &m_A; | ||||
|     permutation_matrix<T, X> m_Q; | ||||
|     permutation_matrix<T, X> m_R; | ||||
|     permutation_matrix<T, X> m_r_wave; | ||||
|     sparse_matrix<T, X> m_U; | ||||
|     square_dense_submatrix<T, X>* m_dense_LU; | ||||
|      | ||||
|     vector<tail_matrix<T, X> *> m_tail; | ||||
|     lp_settings & m_settings; | ||||
|     bool m_failure = false; | ||||
|     indexed_vector<T> m_row_eta_work_vector; | ||||
|     indexed_vector<T> m_w_for_extension; | ||||
|     indexed_vector<T> m_y_copy; | ||||
|     indexed_vector<unsigned> m_ii; //to optimize the work with the m_index fields
 | ||||
|     unsigned m_refactor_counter = 0; | ||||
|     // 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, | ||||
|        lp_settings & settings); | ||||
|     void debug_test_of_basis(static_matrix<T, X> const & A, vector<unsigned> & basis); | ||||
|     void solve_Bd_when_w_is_ready(vector<T> & d, indexed_vector<T>& w ); | ||||
|     void solve_By(indexed_vector<X> & y); | ||||
| 
 | ||||
|     void solve_By(vector<X> & y); | ||||
| 
 | ||||
|     void solve_By_for_T_indexed_only(indexed_vector<T>& y, const lp_settings &); | ||||
| 
 | ||||
|     template <typename L> | ||||
|     void solve_By_when_y_is_ready(indexed_vector<L> & y); | ||||
|     void solve_By_when_y_is_ready_for_X(vector<X> & y); | ||||
|     void solve_By_when_y_is_ready_for_T(vector<T> & y, vector<unsigned> & index); | ||||
|     void print_indexed_vector(indexed_vector<T> & w, std::ofstream & f); | ||||
| 
 | ||||
|     void print_matrix_compact(std::ostream & f); | ||||
| 
 | ||||
|     void print(indexed_vector<T> & w, const vector<unsigned>& basis); | ||||
|     void solve_Bd(unsigned a_column, vector<T> & d, indexed_vector<T> & w); | ||||
|     void solve_Bd(unsigned a_column, indexed_vector<T> & d, indexed_vector<T> & w); | ||||
|     void solve_Bd_faster(unsigned a_column, indexed_vector<T> & d); // d is the right side on the input and the solution at the exit
 | ||||
| 
 | ||||
|     void  solve_yB(vector<T>& y); | ||||
| 
 | ||||
|     void  solve_yB_indexed(indexed_vector<T>& y); | ||||
| 
 | ||||
|     void add_delta_to_solution_indexed(indexed_vector<T>& y); | ||||
| 
 | ||||
|     void add_delta_to_solution(const vector<T>& yc, vector<T>& y); | ||||
| 
 | ||||
|      | ||||
|     void find_error_of_yB(vector<T>& yc, const vector<T>& y, | ||||
|                           const vector<unsigned>& basis); | ||||
| 
 | ||||
|     void find_error_of_yB_indexed(const indexed_vector<T>& y, | ||||
|                                   const vector<int>& heading, const lp_settings& settings); | ||||
| 
 | ||||
|      | ||||
|     void solve_yB_with_error_check(vector<T> & y, const vector<unsigned>& basis); | ||||
| 
 | ||||
|     void solve_yB_with_error_check_indexed(indexed_vector<T> & y, const vector<int>& heading, const vector<unsigned> & basis, const lp_settings &); | ||||
| 
 | ||||
|     void apply_Q_R_to_U(permutation_matrix<T, X> & r_wave); | ||||
| 
 | ||||
| 
 | ||||
|     LU_status get_status() { return m_status; } | ||||
| 
 | ||||
|     void set_status(LU_status status) { | ||||
|         m_status = status; | ||||
|     } | ||||
| 
 | ||||
|     ~lu(); | ||||
| 
 | ||||
|     void init_vector_y(vector<X> & y); | ||||
| 
 | ||||
|     void perform_transformations_on_w(indexed_vector<T>& w); | ||||
| 
 | ||||
|     void init_vector_w(unsigned entering, indexed_vector<T> & w); | ||||
|     void apply_lp_list_to_w(indexed_vector<T> & w); | ||||
|     void apply_lp_list_to_y(vector<X>& y); | ||||
| 
 | ||||
|     void swap_rows(int j, int k); | ||||
| 
 | ||||
|     void swap_columns(int j, int pivot_column); | ||||
| 
 | ||||
|     void push_matrix_to_tail(tail_matrix<T, X>* tm) { | ||||
|         m_tail.push_back(tm); | ||||
|     } | ||||
| 
 | ||||
|     bool pivot_the_row(int row); | ||||
| 
 | ||||
|     eta_matrix<T, X> * get_eta_matrix_for_pivot(unsigned j); | ||||
|     // we're processing the column j now
 | ||||
|     eta_matrix<T, X> * get_eta_matrix_for_pivot(unsigned j, sparse_matrix<T, X>& copy_of_U); | ||||
| 
 | ||||
|     // see page 407 of Chvatal
 | ||||
|     unsigned transform_U_to_V_by_replacing_column(indexed_vector<T> & w, unsigned leaving_column_of_U); | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
|     void check_vector_w(unsigned entering); | ||||
| 
 | ||||
|     void check_apply_matrix_to_vector(matrix<T, X> *lp, T *w); | ||||
| 
 | ||||
|     void check_apply_lp_lists_to_w(T * w); | ||||
| 
 | ||||
|     // 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,  const vector<unsigned>& basis) { | ||||
|         return m_A.get_elem(i, basis[j]); | ||||
|     } | ||||
| 
 | ||||
|     unsigned dimension() { return m_dim; } | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
|     unsigned get_number_of_nonzeroes() { | ||||
|         return m_U.get_number_of_nonzeroes(); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     void process_column(int j); | ||||
| 
 | ||||
|     bool is_correct(const vector<unsigned>& basis); | ||||
| 
 | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
|     dense_matrix<T, X> tail_product(); | ||||
|     dense_matrix<T, X>  get_left_side(const vector<unsigned>& basis); | ||||
| 
 | ||||
|     dense_matrix<T, X>  get_right_side(); | ||||
| #endif | ||||
| 
 | ||||
|     // needed for debugging purposes
 | ||||
|     void copy_w(T *buffer, indexed_vector<T> & w); | ||||
| 
 | ||||
|     // needed for debugging purposes
 | ||||
|     void restore_w(T *buffer, indexed_vector<T> & w); | ||||
|     bool all_columns_and_rows_are_active(); | ||||
| 
 | ||||
|     bool too_dense(unsigned j) const; | ||||
| 
 | ||||
|     void pivot_in_dense_mode(unsigned i); | ||||
| 
 | ||||
|     void create_initial_factorization(); | ||||
| 
 | ||||
|     void calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix<T, X> & r_wave); | ||||
| 
 | ||||
|     void scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump); | ||||
| 
 | ||||
|     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); | ||||
|     // 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); | ||||
| 
 | ||||
|     void replace_column(T pivot_elem, indexed_vector<T> & w, unsigned leaving_column_of_U); | ||||
| 
 | ||||
|     void calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump); | ||||
| 
 | ||||
|     void calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element); | ||||
| 
 | ||||
|     void prepare_entering(unsigned entering, indexed_vector<T> & w) { | ||||
|         init_vector_w(entering, w); | ||||
|     } | ||||
|     bool need_to_refactor() { return m_refactor_counter >= 200; } | ||||
|      | ||||
|     void adjust_dimension_with_matrix_A() { | ||||
|         lean_assert(m_A.row_count() >= m_dim); | ||||
|         m_dim = m_A.row_count(); | ||||
|         m_U.resize(m_dim); | ||||
|         m_Q.resize(m_dim); | ||||
|         m_R.resize(m_dim); | ||||
|         m_row_eta_work_vector.resize(m_dim); | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     std::unordered_set<unsigned> get_set_of_columns_to_replace_for_add_last_rows(const vector<int> & heading) const { | ||||
|         std::unordered_set<unsigned> columns_to_replace; | ||||
|         unsigned m = m_A.row_count(); | ||||
|         unsigned m_prev = m_U.dimension(); | ||||
| 
 | ||||
|         lean_assert(m_A.column_count() == heading.size()); | ||||
| 
 | ||||
|         for (unsigned i = m_prev; i < m; i++) { | ||||
|             for (const row_cell<T> & c : m_A.m_rows[i]) { | ||||
|                 int h = heading[c.m_j]; | ||||
|                 if (h < 0) { | ||||
|                     continue; | ||||
|                 } | ||||
|                 columns_to_replace.insert(c.m_j); | ||||
|             } | ||||
|         } | ||||
|         return columns_to_replace; | ||||
|     } | ||||
|      | ||||
|     void add_last_rows_to_B(const vector<int> & heading, const std::unordered_set<unsigned> & columns_to_replace) { | ||||
|         unsigned m = m_A.row_count(); | ||||
|         lean_assert(m_A.column_count() == heading.size()); | ||||
|         adjust_dimension_with_matrix_A(); | ||||
|         m_w_for_extension.resize(m); | ||||
|         // At this moment the LU is correct      
 | ||||
|         // for B extended by only by ones at the diagonal in the lower right corner
 | ||||
| 
 | ||||
|         for (unsigned j :columns_to_replace) { | ||||
|             lean_assert(heading[j] >= 0); | ||||
|             replace_column_with_only_change_at_last_rows(j, heading[j]); | ||||
|             if (get_status() == LU_status::Degenerated) | ||||
|                 break; | ||||
|         } | ||||
|     } | ||||
|     // column j is a basis column, and there is a change in the last rows
 | ||||
|     void replace_column_with_only_change_at_last_rows(unsigned j, unsigned column_to_change_in_U) { | ||||
|         init_vector_w(j, m_w_for_extension); | ||||
|         replace_column(zero_of_type<T>(), m_w_for_extension, column_to_change_in_U); | ||||
|     } | ||||
| 
 | ||||
|     bool has_dense_submatrix() const { | ||||
|         for (auto m : m_tail) | ||||
|             if (m->is_dense()) | ||||
|                 return true; | ||||
|         return false; | ||||
|     } | ||||
|      | ||||
| }; // end of lu
 | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void init_factorization(lu<T, X>* & factorization, static_matrix<T, X> & m_A, vector<unsigned> & m_basis, lp_settings &m_settings); | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T, typename X> | ||||
| dense_matrix<T, X>  get_B(lu<T, X>& f, const vector<unsigned>& basis); | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										940
									
								
								src/util/lp/lu.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										940
									
								
								src/util/lp/lu.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,940 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <string> | ||||
| #include <algorithm> | ||||
| #include <set> | ||||
| #include "util/vector.h" | ||||
| #include <utility> | ||||
| #include "util/debug.h" | ||||
| #include "util/lp/lu.h" | ||||
| namespace lean { | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T, typename X> // print the nr x nc submatrix at the top left corner
 | ||||
| void print_submatrix(sparse_matrix<T, X> & m, unsigned mr, unsigned nc, std::ostream & out) { | ||||
|     vector<vector<std::string>> A; | ||||
|     vector<unsigned> widths; | ||||
|     for (unsigned i = 0; i < m.row_count() && i < mr ; i++) { | ||||
|         A.push_back(vector<std::string>()); | ||||
|         for (unsigned j = 0; j < m.column_count() && j < nc; j++) { | ||||
|             A[i].push_back(T_to_string(static_cast<T>(m(i, j)))); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     for (unsigned j = 0; j < m.column_count() && j < nc; j++) { | ||||
|         widths.push_back(get_width_of_column(j, A)); | ||||
|     } | ||||
| 
 | ||||
|     print_matrix_with_widths(A, widths, out); | ||||
| } | ||||
| 
 | ||||
| template<typename T, typename X> | ||||
| void print_matrix(static_matrix<T, X> &m, std::ostream & out) { | ||||
|     vector<vector<std::string>> A; | ||||
|     vector<unsigned> widths; | ||||
|     std::set<std::pair<unsigned, unsigned>> domain = m.get_domain(); | ||||
|     for (unsigned i = 0; i < m.row_count(); i++) { | ||||
|         A.push_back(vector<std::string>()); | ||||
|         for (unsigned j = 0; j < m.column_count(); j++) { | ||||
|             A[i].push_back(T_to_string(static_cast<T>(m(i, j)))); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     for (unsigned j = 0; j < m.column_count(); j++) { | ||||
|         widths.push_back(get_width_of_column(j, A)); | ||||
|     } | ||||
| 
 | ||||
|     print_matrix_with_widths(A, widths, out); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void print_matrix(sparse_matrix<T, X>& m, std::ostream & out) { | ||||
|     vector<vector<std::string>> A; | ||||
|     vector<unsigned> widths; | ||||
|     for (unsigned i = 0; i < m.row_count(); i++) { | ||||
|         A.push_back(vector<std::string>()); | ||||
|         for (unsigned j = 0; j < m.column_count(); j++) { | ||||
|             A[i].push_back(T_to_string(static_cast<T>(m(i, j)))); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     for (unsigned j = 0; j < m.column_count(); j++) { | ||||
|         widths.push_back(get_width_of_column(j, A)); | ||||
|     } | ||||
| 
 | ||||
|     print_matrix_with_widths(A, widths, out); | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| one_elem_on_diag<T, X>::one_elem_on_diag(const one_elem_on_diag & o) { | ||||
|     m_i = o.m_i; | ||||
|     m_val = o.m_val; | ||||
| #ifdef LEAN_DEBUG | ||||
|     m_m = m_n = o.m_m; | ||||
|     m_one_over_val = numeric_traits<T>::one() / o.m_val; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T, typename X> | ||||
| T one_elem_on_diag<T, X>::get_elem(unsigned i, unsigned j) const { | ||||
|     if (i == j){ | ||||
|         if (j == m_i) { | ||||
|             return m_one_over_val; | ||||
|         } | ||||
|         return numeric_traits<T>::one(); | ||||
|     } | ||||
| 
 | ||||
|     return numeric_traits<T>::zero(); | ||||
| } | ||||
| #endif | ||||
| template <typename T, typename X> | ||||
| void one_elem_on_diag<T, X>::apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) { | ||||
|     T & t = w[m_i]; | ||||
|     if (numeric_traits<T>::is_zero(t)) { | ||||
|         return; | ||||
|     } | ||||
|     t /= m_val; | ||||
|     if (numeric_traits<T>::precise()) return; | ||||
|     if (settings.abs_val_is_smaller_than_drop_tolerance(t)) { | ||||
|         w.erase_from_index(m_i); | ||||
|         t = numeric_traits<T>::zero(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // This class supports updates of the columns of B, and solves systems Bx=b,and yB=c
 | ||||
| // Using Suhl-Suhl method described in the dissertation of Achim Koberstein, Chapter 5
 | ||||
| template <typename T, typename X> | ||||
| lu<T, X>::lu(static_matrix<T, X> const & A, | ||||
|              vector<unsigned>& basis, | ||||
|              lp_settings & settings): | ||||
|     m_dim(A.row_count()), | ||||
|     m_A(A), | ||||
|     m_Q(m_dim), | ||||
|     m_R(m_dim), | ||||
|     m_r_wave(m_dim), | ||||
|     m_U(A, basis), // create the square matrix that eventually will be factorized
 | ||||
|     m_settings(settings), | ||||
|     m_row_eta_work_vector(A.row_count()){ | ||||
|     lean_assert(!(numeric_traits<T>::precise() && settings.use_tableau())); | ||||
| #ifdef LEAN_DEBUG | ||||
|     debug_test_of_basis(A, basis); | ||||
| #endif | ||||
|     ++m_settings.st().m_num_factorizations; | ||||
|     create_initial_factorization(); | ||||
| #ifdef LEAN_DEBUG | ||||
|     // lean_assert(check_correctness());
 | ||||
| #endif | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::debug_test_of_basis(static_matrix<T, X> const & A, vector<unsigned> & basis) { | ||||
|     std::set<unsigned> set; | ||||
|     for (unsigned i = 0; i < A.row_count(); i++) { | ||||
|         lean_assert(basis[i]< A.column_count()); | ||||
|         set.insert(basis[i]); | ||||
|     } | ||||
|     lean_assert(set.size() == A.row_count()); | ||||
| } | ||||
| 
 | ||||
|  template <typename T, typename X> | ||||
|  void lu<T, X>::solve_By(indexed_vector<X> & y) { | ||||
|      lean_assert(false); // not implemented
 | ||||
|      // init_vector_y(y);
 | ||||
|      // solve_By_when_y_is_ready(y);
 | ||||
|  } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::solve_By(vector<X> & y) { | ||||
|     init_vector_y(y); | ||||
|     solve_By_when_y_is_ready_for_X(y); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::solve_By_when_y_is_ready_for_X(vector<X> & y) { | ||||
|     if (numeric_traits<T>::precise()) { | ||||
|         m_U.solve_U_y(y); | ||||
|         m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal
 | ||||
|         return; | ||||
|     } | ||||
|     m_U.double_solve_U_y(y); | ||||
|     m_R.apply_reverse_from_left_to_X(y); // see 24.3 from Chvatal
 | ||||
|     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<X>(); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::solve_By_when_y_is_ready_for_T(vector<T> & y, vector<unsigned> & index) { | ||||
|     if (numeric_traits<T>::precise()) { | ||||
|         m_U.solve_U_y(y); | ||||
|         m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal
 | ||||
|         unsigned j = m_dim; | ||||
|         while (j--) { | ||||
|             if (!is_zero(y[j])) | ||||
|                 index.push_back(j); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     m_U.double_solve_U_y(y); | ||||
|     m_R.apply_reverse_from_left_to_T(y); // see 24.3 from Chvatal
 | ||||
|     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<T>(); | ||||
|         } else { | ||||
|             index.push_back(i); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::solve_By_for_T_indexed_only(indexed_vector<T> & y, const lp_settings & settings) { | ||||
|     if (numeric_traits<T>::precise()) { | ||||
|         vector<unsigned> active_rows; | ||||
|         m_U.solve_U_y_indexed_only(y, settings, active_rows); | ||||
|         m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal
 | ||||
|         return; | ||||
|     } | ||||
|     m_U.double_solve_U_y(y, m_settings); | ||||
|     m_R.apply_reverse_from_left(y); // see 24.3 from Chvatal
 | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::print_matrix_compact(std::ostream & f) { | ||||
|     f << "matrix_start" << std::endl; | ||||
|     f << "nrows " << m_A.row_count() << std::endl; | ||||
|     f << "ncolumns " << m_A.column_count() << std::endl; | ||||
|     for (unsigned i = 0; i < m_A.row_count(); i++) { | ||||
|         auto & row = m_A.m_rows[i]; | ||||
|         f << "row " << i << std::endl; | ||||
|         for (auto & t : row) { | ||||
|             f << "column " << t.m_j << " value " << t.m_value << std::endl; | ||||
|         } | ||||
|         f << "row_end" << std::endl; | ||||
|     } | ||||
|     f << "matrix_end" << std::endl; | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::print(indexed_vector<T> & w, const vector<unsigned>& basis) { | ||||
|     std::string dump_file_name("/tmp/lu"); | ||||
|     remove(dump_file_name.c_str()); | ||||
|     std::ofstream f(dump_file_name); | ||||
|     if (!f.is_open()) { | ||||
|         LP_OUT(m_settings, "cannot open file " << dump_file_name << std::endl); | ||||
|         return; | ||||
|     } | ||||
|     LP_OUT(m_settings, "writing lu dump to " << dump_file_name << std::endl); | ||||
|     print_matrix_compact(f); | ||||
|     print_vector(basis, f); | ||||
|     print_indexed_vector(w, f); | ||||
|     f.close(); | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::solve_Bd(unsigned a_column, indexed_vector<T> & d, indexed_vector<T> & w) { | ||||
|     init_vector_w(a_column, w); | ||||
| 
 | ||||
|     if (w.m_index.size() * ratio_of_index_size_to_all_size<T>() < d.m_data.size()) { // this const might need some tuning
 | ||||
|         d = w; | ||||
|         solve_By_for_T_indexed_only(d, m_settings); | ||||
|     } else { | ||||
|         d.m_data = w.m_data; | ||||
|         d.m_index.clear(); | ||||
|         solve_By_when_y_is_ready_for_T(d.m_data, d.m_index); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::solve_Bd_faster(unsigned a_column, indexed_vector<T> & d) { // puts the a_column into d
 | ||||
|     init_vector_w(a_column, d); | ||||
|     solve_By_for_T_indexed_only(d, m_settings); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::solve_yB(vector<T>& y) { | ||||
|     // first solve yU = cb*R(-1)
 | ||||
|     m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1)
 | ||||
|     m_U.solve_y_U(y); // got y*U=cb*R(-1)
 | ||||
|     m_Q.apply_reverse_from_right_to_T(y); //
 | ||||
|     for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) { | ||||
| #ifdef LEAN_DEBUG | ||||
|         (*e)->set_number_of_columns(m_dim); | ||||
| #endif | ||||
|         (*e)->apply_from_right(y); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::solve_yB_indexed(indexed_vector<T>& y) { | ||||
|     lean_assert(y.is_OK()); | ||||
|     // first solve yU = cb*R(-1)
 | ||||
|     m_R.apply_reverse_from_right_to_T(y); // got y = cb*R(-1)
 | ||||
|     lean_assert(y.is_OK()); | ||||
|     m_U.solve_y_U_indexed(y, m_settings); // got y*U=cb*R(-1)
 | ||||
|     lean_assert(y.is_OK()); | ||||
|     m_Q.apply_reverse_from_right_to_T(y); | ||||
|     lean_assert(y.is_OK()); | ||||
|     for (auto e = m_tail.rbegin(); e != m_tail.rend(); ++e) { | ||||
| #ifdef LEAN_DEBUG | ||||
|         (*e)->set_number_of_columns(m_dim); | ||||
| #endif | ||||
|         (*e)->apply_from_right(y); | ||||
|         lean_assert(y.is_OK()); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::add_delta_to_solution(const vector<T>& yc, vector<T>& y){ | ||||
|     unsigned i = static_cast<unsigned>(y.size()); | ||||
|     while (i--) | ||||
|         y[i]+=yc[i]; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::add_delta_to_solution_indexed(indexed_vector<T>& y) { | ||||
|     // the delta sits in m_y_copy, put result into y
 | ||||
|     lean_assert(y.is_OK()); | ||||
|     lean_assert(m_y_copy.is_OK()); | ||||
|     m_ii.clear(); | ||||
|     m_ii.resize(y.data_size()); | ||||
|     for (unsigned i : y.m_index) | ||||
|         m_ii.set_value(1, i); | ||||
|     for (unsigned i : m_y_copy.m_index) { | ||||
|         y.m_data[i] += m_y_copy[i]; | ||||
|         if (m_ii[i] == 0) | ||||
|             m_ii.set_value(1, i); | ||||
|     } | ||||
|     lean_assert(m_ii.is_OK()); | ||||
|     y.m_index.clear(); | ||||
| 
 | ||||
|     for (unsigned i : m_ii.m_index) { | ||||
|         T & v = y.m_data[i]; | ||||
|         if (!lp_settings::is_eps_small_general(v, 1e-14)) | ||||
|             y.m_index.push_back(i); | ||||
|         else if (!numeric_traits<T>::is_zero(v)) | ||||
|             v = zero_of_type<T>(); | ||||
|     } | ||||
|          | ||||
|     lean_assert(y.is_OK()); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::find_error_of_yB(vector<T>& yc, const vector<T>& y, const vector<unsigned>& m_basis) { | ||||
|     unsigned i = m_dim; | ||||
|     while (i--) { | ||||
|         yc[i] -= m_A.dot_product_with_column(y, m_basis[i]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::find_error_of_yB_indexed(const indexed_vector<T>& y, const vector<int>& heading, const lp_settings& settings) { | ||||
| #if 0 == 1
 | ||||
|     // it is a non efficient version
 | ||||
|     indexed_vector<T> yc = m_y_copy; | ||||
|     yc.m_index.clear(); | ||||
|     lean_assert(!numeric_traits<T>::precise()); | ||||
|     { | ||||
| 
 | ||||
|         vector<unsigned> d_basis(y.m_data.size()); | ||||
|         for (unsigned j = 0; j < heading.size(); j++) { | ||||
|             if (heading[j] >= 0) { | ||||
|                 d_basis[heading[j]] = j; | ||||
|             } | ||||
|         } | ||||
|          | ||||
|          | ||||
|         unsigned i = m_dim; | ||||
|         while (i--) { | ||||
|             T & v = yc.m_data[i] -= m_A.dot_product_with_column(y.m_data, d_basis[i]); | ||||
|             if (settings.abs_val_is_smaller_than_drop_tolerance(v)) | ||||
|                 v = zero_of_type<T>(); | ||||
|             else | ||||
|                 yc.m_index.push_back(i); | ||||
|         } | ||||
|     } | ||||
| #endif | ||||
|     lean_assert(m_ii.is_OK()); | ||||
|     m_ii.clear(); | ||||
|     m_ii.resize(y.data_size()); | ||||
|     lean_assert(m_y_copy.is_OK()); | ||||
|     // put the error into m_y_copy
 | ||||
|     for (auto k : y.m_index) { | ||||
|         auto & row = m_A.m_rows[k]; | ||||
|         const T & y_k = y.m_data[k]; | ||||
|         for (auto & c : row) { | ||||
|             unsigned j = c.m_j; | ||||
|             int hj = heading[j]; | ||||
|             if (hj < 0) continue; | ||||
|             if (m_ii.m_data[hj] == 0) | ||||
|                 m_ii.set_value(1, hj); | ||||
|             m_y_copy.m_data[hj] -= c.get_val() * y_k; | ||||
|         } | ||||
|     } | ||||
|     // add the index of m_y_copy to m_ii
 | ||||
|     for (unsigned i : m_y_copy.m_index) { | ||||
|         if (m_ii.m_data[i] == 0) | ||||
|             m_ii.set_value(1, i); | ||||
|     } | ||||
|      | ||||
|     // there is no guarantee that m_y_copy is OK here, but its index
 | ||||
|     // is contained in m_ii index
 | ||||
|     m_y_copy.m_index.clear(); | ||||
|     // setup the index of m_y_copy
 | ||||
|     for (auto k : m_ii.m_index) { | ||||
|         T& v = m_y_copy.m_data[k]; | ||||
|         if (settings.abs_val_is_smaller_than_drop_tolerance(v)) | ||||
|             v = zero_of_type<T>(); | ||||
|         else { | ||||
|             m_y_copy.set_value(v, k); | ||||
|         } | ||||
|     } | ||||
|     lean_assert(m_y_copy.is_OK()); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| // solves y*B = y
 | ||||
| // y is the input
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::solve_yB_with_error_check_indexed(indexed_vector<T> & y, const vector<int>& heading,  const vector<unsigned> & basis, const lp_settings & settings) { | ||||
|     if (numeric_traits<T>::precise()) { | ||||
|         if (y.m_index.size() * ratio_of_index_size_to_all_size<T>() * 3 < m_A.column_count()) { | ||||
|             solve_yB_indexed(y); | ||||
|         } else { | ||||
|             solve_yB(y.m_data); | ||||
|             y.restore_index_and_clean_from_data(); | ||||
|         } | ||||
|         return; | ||||
|     } | ||||
|     lean_assert(m_y_copy.is_OK()); | ||||
|     lean_assert(y.is_OK()); | ||||
|     if (y.m_index.size() * ratio_of_index_size_to_all_size<T>() < m_A.column_count()) { | ||||
|         m_y_copy = y; | ||||
|         solve_yB_indexed(y); | ||||
|         lean_assert(y.is_OK()); | ||||
|         if (y.m_index.size() * ratio_of_index_size_to_all_size<T>() >= m_A.column_count()) { | ||||
|             find_error_of_yB(m_y_copy.m_data, y.m_data, basis); | ||||
|             solve_yB(m_y_copy.m_data); | ||||
|             add_delta_to_solution(m_y_copy.m_data, y.m_data); | ||||
|             y.restore_index_and_clean_from_data(); | ||||
|             m_y_copy.clear_all(); | ||||
|         } else { | ||||
|             find_error_of_yB_indexed(y, heading, settings); // this works with m_y_copy
 | ||||
|             solve_yB_indexed(m_y_copy); | ||||
|             add_delta_to_solution_indexed(y); | ||||
|         } | ||||
|         lean_assert(m_y_copy.is_OK()); | ||||
|     } else { | ||||
|         solve_yB_with_error_check(y.m_data, basis); | ||||
|         y.restore_index_and_clean_from_data(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // solves y*B = y
 | ||||
| // y is the input
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::solve_yB_with_error_check(vector<T> & y, const vector<unsigned>& basis) { | ||||
|     if (numeric_traits<T>::precise()) { | ||||
|         solve_yB(y); | ||||
|         return; | ||||
|     } | ||||
|     auto & yc = m_y_copy.m_data; | ||||
|     yc =y; // copy y aside
 | ||||
|     solve_yB(y); | ||||
|     find_error_of_yB(yc, y, basis); | ||||
|     solve_yB(yc); | ||||
|     add_delta_to_solution(yc, y); | ||||
|     m_y_copy.clear_all(); | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::apply_Q_R_to_U(permutation_matrix<T, X> & r_wave) { | ||||
|     m_U.multiply_from_right(r_wave); | ||||
|     m_U.multiply_from_left_with_reverse(r_wave); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| // Solving yB = cb to find the entering variable,
 | ||||
| // where cb is the cost vector projected to B.
 | ||||
| // The result is stored in cb.
 | ||||
| 
 | ||||
| // solving Bd = a ( to find the column d of B^{-1} A_N corresponding to the entering
 | ||||
| // variable
 | ||||
| template <typename T, typename X> | ||||
| lu<T, X>::~lu(){ | ||||
|     for (auto t : m_tail) { | ||||
|         delete t; | ||||
|     } | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::init_vector_y(vector<X> & y) { | ||||
|     apply_lp_list_to_y(y); | ||||
|     m_Q.apply_reverse_from_left_to_X(y); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::perform_transformations_on_w(indexed_vector<T>& w) { | ||||
|     apply_lp_list_to_w(w); | ||||
|     m_Q.apply_reverse_from_left(w); | ||||
|     // TBD does not compile: lean_assert(numeric_traits<T>::precise() || check_vector_for_small_values(w, m_settings));
 | ||||
| } | ||||
| 
 | ||||
| // see Chvatal 24.3
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::init_vector_w(unsigned entering, indexed_vector<T> & w) { | ||||
|     w.clear(); | ||||
|     m_A.copy_column_to_indexed_vector(entering, w); // w = a, the column
 | ||||
|     perform_transformations_on_w(w); | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::apply_lp_list_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); | ||||
|         // TBD does not compile: lean_assert(check_vector_for_small_values(w, m_settings));
 | ||||
|     } | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::apply_lp_list_to_y(vector<X>& y) { | ||||
|     for (unsigned i = 0; i < m_tail.size(); i++) { | ||||
|         m_tail[i]->apply_from_left(y, m_settings); | ||||
|     } | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::swap_rows(int j, int k) { | ||||
|     if (j != k) { | ||||
|         m_Q.transpose_from_left(j, k); | ||||
|         m_U.swap_rows(j, k); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::swap_columns(int j, int pivot_column) { | ||||
|     if (j == pivot_column) | ||||
|         return; | ||||
|     m_R.transpose_from_right(j, pivot_column); | ||||
|     m_U.swap_columns(j, pivot_column); | ||||
| } | ||||
| template <typename T, typename X> | ||||
| bool lu<T, X>::pivot_the_row(int row) { | ||||
|     eta_matrix<T, X> * eta_matrix = get_eta_matrix_for_pivot(row); | ||||
|     if (get_status() != LU_status::OK) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (eta_matrix == nullptr) { | ||||
|         m_U.shorten_active_matrix(row, nullptr); | ||||
|         return true; | ||||
|     } | ||||
|     if (!m_U.pivot_with_eta(row, eta_matrix, m_settings)) | ||||
|         return false; | ||||
|     eta_matrix->conjugate_by_permutation(m_Q); | ||||
|     push_matrix_to_tail(eta_matrix); | ||||
|     return true; | ||||
| } | ||||
| // we're processing the column j now
 | ||||
| template <typename T, typename X> | ||||
| eta_matrix<T, X> * lu<T, X>::get_eta_matrix_for_pivot(unsigned j) { | ||||
|     eta_matrix<T, X> *ret; | ||||
|     if(!m_U.fill_eta_matrix(j, &ret)) { | ||||
|         set_status(LU_status::Degenerated); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| // we're processing the column j now
 | ||||
| template <typename T, typename X> | ||||
| eta_matrix<T, X> * lu<T, X>::get_eta_matrix_for_pivot(unsigned j, sparse_matrix<T, X>& copy_of_U) { | ||||
|     eta_matrix<T, X> *ret; | ||||
|     copy_of_U.fill_eta_matrix(j, &ret); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| // see page 407 of Chvatal
 | ||||
| template <typename T, typename X> | ||||
| unsigned lu<T, X>::transform_U_to_V_by_replacing_column(indexed_vector<T> & w, | ||||
|                                                         unsigned leaving_column) { | ||||
|     unsigned column_to_replace = m_R.apply_reverse(leaving_column); | ||||
|     m_U.replace_column(column_to_replace, w, m_settings); | ||||
|     return column_to_replace; | ||||
| } | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::check_vector_w(unsigned entering) { | ||||
|     T * w = new T[m_dim]; | ||||
|     m_A.copy_column_to_vector(entering, w); | ||||
|     check_apply_lp_lists_to_w(w); | ||||
|     delete [] w; | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::check_apply_matrix_to_vector(matrix<T, X> *lp, T *w) { | ||||
|     if (lp != nullptr) { | ||||
|         lp -> set_number_of_rows(m_dim); | ||||
|         lp -> set_number_of_columns(m_dim); | ||||
|         apply_to_vector(*lp, w); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::check_apply_lp_lists_to_w(T * w) { | ||||
|     for (unsigned i = 0; i < m_tail.size(); i++) { | ||||
|         check_apply_matrix_to_vector(m_tail[i], w); | ||||
|     } | ||||
|     permutation_matrix<T, X> qr = m_Q.get_reverse(); | ||||
|     apply_to_vector(qr, w); | ||||
|     for (int i = m_dim - 1; i >= 0; i--) { | ||||
|         lean_assert(abs(w[i] - w[i]) < 0.0000001); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #endif | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::process_column(int j) { | ||||
|     unsigned pi, pj; | ||||
|     bool success = m_U.get_pivot_for_column(pi, pj, m_settings.c_partial_pivoting, j); | ||||
|     if (!success) { | ||||
|         LP_OUT(m_settings, "get_pivot returned false: cannot find the pivot for column " << j << std::endl); | ||||
|         m_failure = true; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     if (static_cast<int>(pi) == -1) { | ||||
|         LP_OUT(m_settings, "cannot find the pivot for column " << j << std::endl); | ||||
|         m_failure = true; | ||||
|         return; | ||||
|     } | ||||
|     swap_columns(j, pj); | ||||
|     swap_rows(j, pi); | ||||
|     if (!pivot_the_row(j)) { | ||||
|         //      LP_OUT(m_settings, "pivot_the_row(" << j << ") failed" << std::endl);
 | ||||
|         m_failure = true; | ||||
|     } | ||||
| } | ||||
| template <typename T, typename X> | ||||
| bool lu<T, X>::is_correct(const vector<unsigned>& basis) { | ||||
| #ifdef LEAN_DEBUG | ||||
|     if (get_status() != LU_status::OK) { | ||||
|         return false; | ||||
|     } | ||||
|     dense_matrix<T, X> left_side = get_left_side(basis); | ||||
|     dense_matrix<T, X> right_side = get_right_side(); | ||||
|     return left_side == right_side; | ||||
| #else | ||||
|     return true; | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T, typename X> | ||||
| dense_matrix<T, X> lu<T, X>::tail_product() { | ||||
|     lean_assert(tail_size() > 0); | ||||
|     dense_matrix<T, X> left_side = permutation_matrix<T, X>(m_dim); | ||||
|     for (unsigned i = 0; i < tail_size(); i++) { | ||||
|         matrix<T, X>* lp =  get_lp_matrix(i); | ||||
|         lp->set_number_of_rows(m_dim); | ||||
|         lp->set_number_of_columns(m_dim); | ||||
|         left_side = ((*lp) * left_side); | ||||
|     } | ||||
|     return left_side; | ||||
| } | ||||
| template <typename T, typename X> | ||||
| dense_matrix<T, X> lu<T, X>::get_left_side(const vector<unsigned>& basis) { | ||||
|     dense_matrix<T, X> left_side = get_B(*this, basis); | ||||
|     for (unsigned i = 0; i < tail_size(); i++) { | ||||
|         matrix<T, X>* lp =  get_lp_matrix(i); | ||||
|         lp->set_number_of_rows(m_dim); | ||||
|         lp->set_number_of_columns(m_dim); | ||||
|         left_side = ((*lp) * left_side); | ||||
|     } | ||||
|     return left_side; | ||||
| } | ||||
| template <typename T, typename X> | ||||
| dense_matrix<T, X>  lu<T, X>::get_right_side() { | ||||
|     auto ret = U() * R(); | ||||
|     ret = Q() * ret; | ||||
|     return ret; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| // needed for debugging purposes
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::copy_w(T *buffer, indexed_vector<T> & w) { | ||||
|     unsigned i = m_dim; | ||||
|     while (i--) { | ||||
|         buffer[i] = w[i]; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // needed for debugging purposes
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::restore_w(T *buffer, indexed_vector<T> & w) { | ||||
|     unsigned i = m_dim; | ||||
|     while (i--) { | ||||
|         w[i] = buffer[i]; | ||||
|     } | ||||
| } | ||||
| template <typename T, typename X> | ||||
| bool lu<T, X>::all_columns_and_rows_are_active() { | ||||
|     unsigned i = m_dim; | ||||
|     while (i--) { | ||||
|         lean_assert(m_U.col_is_active(i)); | ||||
|         lean_assert(m_U.row_is_active(i)); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| template <typename T, typename X> | ||||
| bool lu<T, X>::too_dense(unsigned j) const { | ||||
|     unsigned r = m_dim - j; | ||||
|     if (r < 5) | ||||
|         return false; | ||||
|      // if (j * 5 < m_dim * 4) // start looking for dense only at the bottom  of the rows
 | ||||
|      //    return false;
 | ||||
|     //    return r * r * m_settings.density_threshold <= m_U.get_number_of_nonzeroes_below_row(j);
 | ||||
|     return r * r * m_settings.density_threshold <= m_U.get_n_of_active_elems(); | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::pivot_in_dense_mode(unsigned i) { | ||||
|     int j = m_dense_LU->find_pivot_column_in_row(i); | ||||
|     if (j == -1) { | ||||
|         m_failure = true; | ||||
|         return; | ||||
|     } | ||||
|     if (i != static_cast<unsigned>(j)) { | ||||
|         swap_columns(i, j); | ||||
|         m_dense_LU->swap_columns(i, j); | ||||
|     } | ||||
|     m_dense_LU->pivot(i, m_settings); | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::create_initial_factorization(){ | ||||
|     m_U.prepare_for_factorization(); | ||||
|     unsigned j; | ||||
|     for (j = 0; j < m_dim; j++) { | ||||
|         process_column(j); | ||||
|         if (m_failure) { | ||||
|             set_status(LU_status::Degenerated); | ||||
|             return; | ||||
|         } | ||||
|         if (too_dense(j)) { | ||||
|             break; | ||||
|         } | ||||
|     } | ||||
|     if (j == m_dim) { | ||||
|         // TBD does not compile: lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
 | ||||
|         //        lean_assert(is_correct());
 | ||||
|         // lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
 | ||||
|         return; | ||||
|     } | ||||
|     j++; | ||||
|     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); | ||||
|     m_refactor_counter = 0; | ||||
|     // lean_assert(is_correct());
 | ||||
|     // lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
 | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::calculate_r_wave_and_update_U(unsigned bump_start, unsigned bump_end, permutation_matrix<T, X> & r_wave) { | ||||
|     if (bump_start > bump_end) { | ||||
|         set_status(LU_status::Degenerated); | ||||
|         return; | ||||
|     } | ||||
|     if (bump_start == bump_end) { | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     r_wave[bump_start] = bump_end; // sending the offensive column to the end of the bump
 | ||||
| 
 | ||||
|     for ( unsigned i = bump_start + 1 ; i <= bump_end; i++ ) { | ||||
|         r_wave[i] = i - 1; | ||||
|     } | ||||
| 
 | ||||
|     m_U.multiply_from_right(r_wave); | ||||
|     m_U.multiply_from_left_with_reverse(r_wave); | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::scan_last_row_to_work_vector(unsigned lowest_row_of_the_bump) { | ||||
|     vector<indexed_value<T>> & last_row_vec = m_U.get_row_values(m_U.adjust_row(lowest_row_of_the_bump)); | ||||
|     for (auto & iv : last_row_vec) { | ||||
|         if (is_zero(iv.m_value)) continue; | ||||
|         lean_assert(!m_settings.abs_val_is_smaller_than_drop_tolerance(iv.m_value)); | ||||
|         unsigned adjusted_col = m_U.adjust_column_inverse(iv.m_index); | ||||
|         if (adjusted_col < lowest_row_of_the_bump) { | ||||
|             m_row_eta_work_vector.set_value(-iv.m_value, adjusted_col); | ||||
|         } else  { | ||||
|             m_row_eta_work_vector.set_value(iv.m_value, adjusted_col); // preparing to calculate the real value in the matrix
 | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::pivot_and_solve_the_system(unsigned replaced_column, unsigned lowest_row_of_the_bump) { | ||||
|     // we have the system right side at m_row_eta_work_vector now
 | ||||
|     // solve the system column wise
 | ||||
|     for (unsigned j = replaced_column; j < lowest_row_of_the_bump; j++) { | ||||
|         T v = m_row_eta_work_vector[j]; | ||||
|         if (numeric_traits<T>::is_zero(v)) continue; // this column does not contribute to the solution
 | ||||
|         unsigned aj = m_U.adjust_row(j); | ||||
|         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); | ||||
| 
 | ||||
| 
 | ||||
|              | ||||
|            // m_row_eta_work_vector.add_value_at_index_with_drop_tolerance(col, delta);
 | ||||
|             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); | ||||
|                 } | ||||
|                 } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| // see Achim Koberstein's thesis page 58, but here we solve the system and pivot to the last
 | ||||
| // row at the same time
 | ||||
| template <typename T, typename X> | ||||
| row_eta_matrix<T, X> *lu<T, X>::get_row_eta_matrix_and_set_row_vector(unsigned replaced_column, unsigned lowest_row_of_the_bump, const T &  pivot_elem_for_checking) { | ||||
|     if (replaced_column == lowest_row_of_the_bump) return nullptr; | ||||
|     scan_last_row_to_work_vector(lowest_row_of_the_bump); | ||||
|     pivot_and_solve_the_system(replaced_column, lowest_row_of_the_bump); | ||||
|     if (numeric_traits<T>::precise() == false && !is_zero(pivot_elem_for_checking)) { | ||||
|         T denom = std::max(T(1), abs(pivot_elem_for_checking)); | ||||
|         if ( | ||||
|             !m_settings.abs_val_is_smaller_than_pivot_tolerance((m_row_eta_work_vector[lowest_row_of_the_bump] - pivot_elem_for_checking) / denom)) { | ||||
|             set_status(LU_status::Degenerated); | ||||
|             //        LP_OUT(m_settings, "diagonal element is off" << std::endl);
 | ||||
|             return nullptr; | ||||
|         } | ||||
|     } | ||||
| #ifdef LEAN_DEBUG | ||||
|     auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump, m_dim); | ||||
| #else | ||||
|     auto ret = new row_eta_matrix<T, X>(replaced_column, lowest_row_of_the_bump); | ||||
| #endif | ||||
| 
 | ||||
|     for (auto j : m_row_eta_work_vector.m_index) { | ||||
|         if (j < lowest_row_of_the_bump) { | ||||
|             auto & v = m_row_eta_work_vector[j]; | ||||
|             if (!is_zero(v)) { | ||||
|                 if (!m_settings.abs_val_is_smaller_than_drop_tolerance(v)){ | ||||
|                     ret->push_back(j, v); | ||||
|                 } | ||||
|                 v = numeric_traits<T>::zero(); | ||||
|             } | ||||
|         } | ||||
|     } // now the lowest_row_of_the_bump contains the rest of the row to the right of the bump with correct values
 | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::replace_column(T pivot_elem_for_checking, indexed_vector<T> & w, unsigned leaving_column_of_U){ | ||||
|     m_refactor_counter++; | ||||
|     unsigned replaced_column =  transform_U_to_V_by_replacing_column( w, leaving_column_of_U); | ||||
|     unsigned lowest_row_of_the_bump = m_U.lowest_row_in_column(replaced_column); | ||||
|     m_r_wave.init(m_dim); | ||||
|     calculate_r_wave_and_update_U(replaced_column, lowest_row_of_the_bump, m_r_wave); | ||||
|     auto row_eta = get_row_eta_matrix_and_set_row_vector(replaced_column, lowest_row_of_the_bump, pivot_elem_for_checking); | ||||
| 
 | ||||
|     if (get_status() == LU_status::Degenerated) { | ||||
|         m_row_eta_work_vector.clear_all(); | ||||
|         return; | ||||
|     } | ||||
|     m_Q.multiply_by_permutation_from_right(m_r_wave); | ||||
|     m_R.multiply_by_permutation_reverse_from_left(m_r_wave); | ||||
|     if (row_eta != nullptr) { | ||||
|         row_eta->conjugate_by_permutation(m_Q); | ||||
|         push_matrix_to_tail(row_eta); | ||||
|     } | ||||
|     calculate_Lwave_Pwave_for_bump(replaced_column, lowest_row_of_the_bump); | ||||
|     // lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
 | ||||
|     // lean_assert(w.is_OK() && m_row_eta_work_vector.is_OK());
 | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::calculate_Lwave_Pwave_for_bump(unsigned replaced_column, unsigned lowest_row_of_the_bump){ | ||||
|     T diagonal_elem; | ||||
|     if (replaced_column < lowest_row_of_the_bump) { | ||||
|         diagonal_elem = m_row_eta_work_vector[lowest_row_of_the_bump]; | ||||
|         //          lean_assert(m_row_eta_work_vector.is_OK());
 | ||||
|         m_U.set_row_from_work_vector_and_clean_work_vector_not_adjusted(m_U.adjust_row(lowest_row_of_the_bump), m_row_eta_work_vector, m_settings); | ||||
|     } else { | ||||
|         diagonal_elem = m_U(lowest_row_of_the_bump, lowest_row_of_the_bump); // todo - get it more efficiently
 | ||||
|     } | ||||
|     if (m_settings.abs_val_is_smaller_than_pivot_tolerance(diagonal_elem)) { | ||||
|         set_status(LU_status::Degenerated); | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     calculate_Lwave_Pwave_for_last_row(lowest_row_of_the_bump, diagonal_elem); | ||||
|     //         lean_assert(m_U.is_upper_triangular_and_maximums_are_set_correctly_in_rows(m_settings));
 | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void lu<T, X>::calculate_Lwave_Pwave_for_last_row(unsigned lowest_row_of_the_bump, T diagonal_element) { | ||||
|     auto l = new one_elem_on_diag<T, X>(lowest_row_of_the_bump, diagonal_element); | ||||
| #ifdef LEAN_DEBUG | ||||
|     l->set_number_of_columns(m_dim); | ||||
| #endif | ||||
|     push_matrix_to_tail(l); | ||||
|     m_U.divide_row_by_constant(lowest_row_of_the_bump, diagonal_element, m_settings); | ||||
|     l->conjugate_by_permutation(m_Q); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void init_factorization(lu<T, X>* & factorization, static_matrix<T, X> & m_A, vector<unsigned> & m_basis, lp_settings &m_settings) { | ||||
|     if (factorization != nullptr) | ||||
|         delete factorization; | ||||
|     factorization = new lu<T, X>(m_A, m_basis, m_settings); | ||||
|     // if (factorization->get_status() != LU_status::OK) 
 | ||||
|     //     LP_OUT(m_settings, "failing in init_factorization" << std::endl);
 | ||||
| } | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T, typename X> | ||||
| dense_matrix<T, X>  get_B(lu<T, X>& f, const vector<unsigned>& basis) { | ||||
|     lean_assert(basis.size() == f.dimension()); | ||||
|     lean_assert(basis.size() == f.m_U.dimension()); | ||||
|     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, basis)); | ||||
| 
 | ||||
|     return B; | ||||
| } | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										63
									
								
								src/util/lp/lu_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								src/util/lp/lu_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,63 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <utility> | ||||
| #include <memory> | ||||
| #include <string> | ||||
| #include "util/vector.h" | ||||
| #include "util/debug.h" | ||||
| #include "util/lp/lu.hpp" | ||||
| template double lean::dot_product<double, double>(vector<double> const&, vector<double> const&); | ||||
| template lean::lu<double, double>::lu(lean::static_matrix<double, double> const&, vector<unsigned int>&, lean::lp_settings&); | ||||
| template void lean::lu<double, double>::push_matrix_to_tail(lean::tail_matrix<double, double>*); | ||||
| template void lean::lu<double, double>::replace_column(double, lean::indexed_vector<double>&, unsigned); | ||||
| template void lean::lu<double, double>::solve_Bd(unsigned int, lean::indexed_vector<double>&, lean::indexed_vector<double>&); | ||||
| template lean::lu<double, double>::~lu(); | ||||
| template void lean::lu<lean::mpq, lean::mpq>::push_matrix_to_tail(lean::tail_matrix<lean::mpq, lean::mpq>*); | ||||
| template void lean::lu<lean::mpq, lean::mpq>::solve_Bd(unsigned int, lean::indexed_vector<lean::mpq>&, lean::indexed_vector<lean::mpq>&); | ||||
| template lean::lu<lean::mpq, lean::mpq>::~lu(); | ||||
| template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::push_matrix_to_tail(lean::tail_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >*); | ||||
| template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_Bd(unsigned int, lean::indexed_vector<lean::mpq>&, lean::indexed_vector<lean::mpq>&); | ||||
| template lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::~lu(); | ||||
| template lean::mpq lean::dot_product<lean::mpq, lean::mpq>(vector<lean::mpq > const&, vector<lean::mpq > const&); | ||||
| template void lean::init_factorization<double, double>(lean::lu<double, double>*&, lean::static_matrix<double, double>&, vector<unsigned int>&, lean::lp_settings&); | ||||
| template void lean::init_factorization<lean::mpq, lean::mpq>(lean::lu<lean::mpq, lean::mpq>*&, lean::static_matrix<lean::mpq, lean::mpq>&, vector<unsigned int>&, lean::lp_settings&); | ||||
| template void lean::init_factorization<lean::mpq, lean::numeric_pair<lean::mpq> >(lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >*&, lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, vector<unsigned int>&, lean::lp_settings&); | ||||
| #ifdef LEAN_DEBUG | ||||
| template void lean::print_matrix<double, double>(lean::sparse_matrix<double, double>&, std::ostream & out); | ||||
| template void lean::print_matrix<lean::mpq, lean::mpq>(lean::static_matrix<lean::mpq, lean::mpq>&, std::ostream&); | ||||
| template void lean::print_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >(lean::static_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&, std::ostream&); | ||||
| template void lean::print_matrix<double, double>(lean::static_matrix<double, double>&, std::ostream & out); | ||||
| template bool lean::lu<double, double>::is_correct(const vector<unsigned>& basis); | ||||
| template bool lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::is_correct( vector<unsigned int> const &); | ||||
| template lean::dense_matrix<double, double> lean::get_B<double, double>(lean::lu<double, double>&, const vector<unsigned>& basis); | ||||
| template lean::dense_matrix<lean::mpq, lean::mpq> lean::get_B<lean::mpq, lean::mpq>(lean::lu<lean::mpq, lean::mpq>&, vector<unsigned int> const&); | ||||
| 
 | ||||
| #endif | ||||
| 
 | ||||
| template bool lean::lu<double, double>::pivot_the_row(int); // NOLINT
 | ||||
| template void lean::lu<double, double>::init_vector_w(unsigned int, lean::indexed_vector<double>&); | ||||
| template void lean::lu<double, double>::solve_By(vector<double>&); | ||||
| template void lean::lu<double, double>::solve_By_when_y_is_ready_for_X(vector<double>&); | ||||
| template void lean::lu<double, double>::solve_yB_with_error_check(vector<double>&, const vector<unsigned>& basis); | ||||
| template void lean::lu<double, double>::solve_yB_with_error_check_indexed(lean::indexed_vector<double>&, vector<int> const&,  const vector<unsigned> & basis, const lp_settings&); | ||||
| template void lean::lu<lean::mpq, lean::mpq>::replace_column(lean::mpq, lean::indexed_vector<lean::mpq>&, unsigned); | ||||
| template void lean::lu<lean::mpq, lean::mpq>::solve_By(vector<lean::mpq >&); | ||||
| template void lean::lu<lean::mpq, lean::mpq>::solve_By_when_y_is_ready_for_X(vector<lean::mpq >&); | ||||
| template void lean::lu<lean::mpq, lean::mpq>::solve_yB_with_error_check(vector<lean::mpq >&, const vector<unsigned>& basis); | ||||
| template void lean::lu<lean::mpq, lean::mpq>::solve_yB_with_error_check_indexed(lean::indexed_vector<lean::mpq>&, vector< int > const&,  const vector<unsigned> & basis, const lp_settings&); | ||||
| template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB_with_error_check_indexed(lean::indexed_vector<lean::mpq>&, vector< int > const&,  const vector<unsigned> & basis, const lp_settings&); | ||||
| template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::init_vector_w(unsigned int, lean::indexed_vector<lean::mpq>&); | ||||
| template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::replace_column(lean::mpq, lean::indexed_vector<lean::mpq>&, unsigned); | ||||
| template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_Bd_faster(unsigned int, lean::indexed_vector<lean::mpq>&); | ||||
| template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_By(vector<lean::numeric_pair<lean::mpq> >&); | ||||
| template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_By_when_y_is_ready_for_X(vector<lean::numeric_pair<lean::mpq> >&); | ||||
| template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB_with_error_check(vector<lean::mpq >&, const vector<unsigned>& basis); | ||||
| template void lean::lu<lean::mpq, lean::mpq>::solve_By(lean::indexed_vector<lean::mpq>&); | ||||
| template void lean::lu<double, double>::solve_By(lean::indexed_vector<double>&); | ||||
| template void lean::lu<double, double>::solve_yB_indexed(lean::indexed_vector<double>&); | ||||
| template void lean::lu<lean::mpq, lean::mpq>::solve_yB_indexed(lean::indexed_vector<lean::mpq>&); | ||||
| template void lean::lu<lean::mpq, lean::numeric_pair<lean::mpq> >::solve_yB_indexed(lean::indexed_vector<lean::mpq>&); | ||||
| template void lean::lu<lean::mpq, lean::mpq>::solve_By_for_T_indexed_only(lean::indexed_vector<lean::mpq>&, lean::lp_settings const&); | ||||
| template void lean::lu<double, double>::solve_By_for_T_indexed_only(lean::indexed_vector<double>&, lean::lp_settings const&); | ||||
							
								
								
									
										44
									
								
								src/util/lp/matrix.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										44
									
								
								src/util/lp/matrix.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,44 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #ifdef Z3DEBUG | ||||
| #pragma once | ||||
| #include "util/lp/numeric_pair.h" | ||||
| #include "util/vector.h" | ||||
| #include <string> | ||||
| #include "util/lp/lp_settings.h" | ||||
| namespace lean { | ||||
| // used for debugging purposes only
 | ||||
| template <typename T, typename X> | ||||
| class matrix { | ||||
| public: | ||||
|     virtual T get_elem (unsigned i, unsigned j) const = 0; | ||||
|     virtual unsigned row_count() const  = 0; | ||||
|     virtual unsigned column_count() const = 0; | ||||
|     virtual void set_number_of_rows(unsigned m)  = 0; | ||||
|     virtual void set_number_of_columns(unsigned n) = 0; | ||||
| 
 | ||||
|     virtual ~matrix() {} | ||||
| 
 | ||||
|     bool is_equal(const matrix<T, X>& other); | ||||
|     bool operator == (matrix<T, X> const & other) { | ||||
|         return is_equal(other); | ||||
|     } | ||||
|     T operator()(unsigned i, unsigned j) const { return get_elem(i, j); } | ||||
| }; | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void apply_to_vector(matrix<T, X> & m, T * w); | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| unsigned get_width_of_column(unsigned j, vector<vector<std::string>> & A); | ||||
| void print_matrix_with_widths(vector<vector<std::string>> & A, vector<unsigned> & ws, std::ostream & out); | ||||
| void print_string_matrix(vector<vector<std::string>> & A); | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void print_matrix(matrix<T, X> const * m, std::ostream & out); | ||||
| 
 | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										105
									
								
								src/util/lp/matrix.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										105
									
								
								src/util/lp/matrix.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,105 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #ifdef Z3DEBUG | ||||
| #include <cmath> | ||||
| #include <string> | ||||
| #include "util/lp/matrix.h" | ||||
| namespace lean { | ||||
| template <typename T, typename X> | ||||
| bool matrix<T, X>::is_equal(const matrix<T, X>& other) { | ||||
|     if (other.row_count() != row_count() || other.column_count() != column_count()) | ||||
|         return false; | ||||
|     for (unsigned i = 0; i < row_count(); i++) { | ||||
|         for (unsigned j = 0; j < column_count(); j++) { | ||||
|             auto a = get_elem(i, j); | ||||
|             auto b = other.get_elem(i, j); | ||||
|             if (numeric_traits<T>::precise()) { | ||||
|                 if (a != b) return false; | ||||
|             } else if (fabs(numeric_traits<T>::get_double(a - b)) > 0.000001) { | ||||
|                 // cout << "returning false from operator== of matrix comparison" << endl;
 | ||||
|                 // cout << "this matrix is " << endl;
 | ||||
|                 // print_matrix(*this);
 | ||||
|                 // cout << "other matrix is " << endl;
 | ||||
|                 // print_matrix(other);
 | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void apply_to_vector(matrix<T, X> & m, T * w) { | ||||
|     // here m is a square matrix
 | ||||
|     unsigned dim = m.row_count(); | ||||
| 
 | ||||
|     T * wc = new T[dim]; | ||||
| 
 | ||||
|     for (unsigned i = 0; i < dim; i++) { | ||||
|         wc[i] = w[i]; | ||||
|     } | ||||
| 
 | ||||
|     for (unsigned i = 0; i < dim; i++) { | ||||
|         T t = numeric_traits<T>::zero(); | ||||
|         for (unsigned j = 0; j < dim; j++) { | ||||
|             t += m(i, j) * wc[j]; | ||||
|         } | ||||
|         w[i] = t; | ||||
|     } | ||||
|     delete [] wc; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| unsigned get_width_of_column(unsigned j, vector<vector<std::string>> & A) { | ||||
|     unsigned r = 0; | ||||
|     for (unsigned i = 0; i < A.size(); i++) { | ||||
|         vector<std::string> & t = A[i]; | ||||
|         std::string str= t[j]; | ||||
|         unsigned s = str.size(); | ||||
|         if (r < s) { | ||||
|             r = s; | ||||
|         } | ||||
|     } | ||||
|     return r; | ||||
| } | ||||
| 
 | ||||
| void print_matrix_with_widths(vector<vector<std::string>> & A, vector<unsigned> & ws, std::ostream & out) { | ||||
|     for (unsigned i = 0; i < A.size(); i++) { | ||||
|         for (unsigned j = 0; j < A[i].size(); j++) { | ||||
|             print_blanks(ws[j] - A[i][j].size(), out); | ||||
|             out << A[i][j] << " "; | ||||
|         } | ||||
|         out << std::endl; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void print_string_matrix(vector<vector<std::string>> & A, std::ostream & out) { | ||||
|     vector<unsigned> widths; | ||||
| 
 | ||||
|     if (A.size() > 0) | ||||
|         for (unsigned j = 0; j < A[0].size(); j++) { | ||||
|             widths.push_back(get_width_of_column(j, A)); | ||||
|         } | ||||
| 
 | ||||
|     print_matrix_with_widths(A, widths, out); | ||||
|     out << std::endl; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void print_matrix(matrix<T, X> const * m, std::ostream & out) { | ||||
|     vector<vector<std::string>> A(m->row_count()); | ||||
|     for (unsigned i = 0; i < m->row_count(); i++) { | ||||
|         for (unsigned j = 0; j < m->column_count(); j++) { | ||||
|             A[i].push_back(T_to_string(m->get_elem(i, j))); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     print_string_matrix(A, out); | ||||
| } | ||||
| 
 | ||||
| } | ||||
| #endif | ||||
							
								
								
									
										16
									
								
								src/util/lp/matrix_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										16
									
								
								src/util/lp/matrix_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,16 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/lp_settings.h" | ||||
| #ifdef LEAN_DEBUG | ||||
| #include "util/lp/matrix.hpp" | ||||
| #include "util/lp/static_matrix.h" | ||||
| #include <string> | ||||
| template void lean::print_matrix<double, double>(lean::matrix<double, double> const*, std::ostream & out); | ||||
| template bool lean::matrix<double, double>::is_equal(lean::matrix<double, double> const&); | ||||
| template void lean::print_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >(lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> > const *, std::basic_ostream<char, std::char_traits<char> > &); | ||||
| template void lean::print_matrix<lean::mpq, lean::mpq>(lean::matrix<lean::mpq, lean::mpq> const*, std::ostream&); | ||||
| template bool lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::is_equal(lean::matrix<lean::mpq, lean::numeric_pair<lean::mpq> > const&); | ||||
| template bool lean::matrix<lean::mpq, lean::mpq>::is_equal(lean::matrix<lean::mpq, lean::mpq> const&); | ||||
| #endif | ||||
							
								
								
									
										867
									
								
								src/util/lp/mps_reader.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										867
									
								
								src/util/lp/mps_reader.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,867 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| 
 | ||||
| // reads an MPS file reperesenting a Mixed Integer Program
 | ||||
| #include <functional> | ||||
| #include <algorithm> | ||||
| #include <string> | ||||
| #include "util/vector.h" | ||||
| #include <unordered_map> | ||||
| #include <iostream> | ||||
| #include <fstream> | ||||
| #include <locale> | ||||
| #include "util/lp/lp_primal_simplex.h" | ||||
| #include "util/lp/lp_dual_simplex.h" | ||||
| #include "util/lp/lar_solver.h" | ||||
| #include "util/lp/lp_utils.h" | ||||
| #include "util/lp/lp_solver.h" | ||||
| namespace lean { | ||||
| inline bool my_white_space(const char & a) { | ||||
|     return a == ' ' || a == '\t'; | ||||
| } | ||||
| inline size_t number_of_whites(const std::string & s)  { | ||||
|     size_t i = 0; | ||||
|     for(;i < s.size(); i++) | ||||
|         if (!my_white_space(s[i])) return i; | ||||
|     return i; | ||||
| } | ||||
| inline size_t number_of_whites_from_end(const std::string & s)  { | ||||
|     size_t ret = 0; | ||||
|     for(int i = static_cast<int>(s.size()) - 1;i >= 0; i--) | ||||
|         if (my_white_space(s[i])) ret++;else break; | ||||
|      | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
|     // trim from start
 | ||||
| inline std::string <rim(std::string &s) { | ||||
|     s.erase(0, number_of_whites(s)); | ||||
|     return s; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
|     // trim from end
 | ||||
| inline std::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());
 | ||||
|     s.erase(s.end() - number_of_whites_from_end(s), s.end()); | ||||
|     return s; | ||||
| } | ||||
|     // trim from both ends
 | ||||
| inline std::string &trim(std::string &s) { | ||||
|     return ltrim(rtrim(s)); | ||||
| } | ||||
| 
 | ||||
| inline std::string trim(std::string const &r) { | ||||
|         std::string s = r; | ||||
|         return ltrim(rtrim(s)); | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| inline vector<std::string> string_split(const std::string &source, const char *delimiter, bool keep_empty)  { | ||||
|     vector<std::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<std::string> split_and_trim(std::string line) { | ||||
|     auto split = string_split(line, " \t", false); | ||||
|     vector<std::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 { | ||||
|         std::string m_name; | ||||
|         bound * m_bound = nullptr; | ||||
|         unsigned m_index; | ||||
|         column(std::string name, unsigned index): m_name(name), m_index(index) { | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     struct row { | ||||
|         row_type m_type; | ||||
|         std::string m_name; | ||||
|         std::unordered_map<std::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, std::string name, unsigned index) : m_type(type), m_name(name), m_index(index) { | ||||
|         } | ||||
|     }; | ||||
| 
 | ||||
|     std::string m_file_name; | ||||
|     bool m_is_OK = true; | ||||
|     std::unordered_map<std::string, row *> m_rows; | ||||
|     std::unordered_map<std::string, column *> m_columns; | ||||
|     std::unordered_map<std::string, unsigned> m_names_to_var_index; | ||||
|     std::string m_line; | ||||
|     std::string m_name; | ||||
|     std::string m_cost_row_name; | ||||
|     std::ifstream m_file_stream; | ||||
|     // needed to adjust the index row
 | ||||
|     unsigned m_cost_line_count = 0; | ||||
|     unsigned m_line_number = 0; | ||||
|     std::ostream * m_message_stream = & std::cout; | ||||
| 
 | ||||
|     void set_m_ok_to_false() { | ||||
|         *m_message_stream << "setting m_is_OK to false" << std::endl; | ||||
|         m_is_OK = false; | ||||
|     } | ||||
| 
 | ||||
|     std::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(); | ||||
|                 *m_message_stream << "cannot read from file" << std::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 (static_cast<int>(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(std::string column_name, std::string column_data) { | ||||
|          // uph, let us try to work with columns
 | ||||
|         if (column_data.size() >= 22) { | ||||
|             std::string ss = column_data.substr(0, 8); | ||||
|             std::string row_name = trim(ss); | ||||
|             auto t = m_rows.find(row_name); | ||||
| 
 | ||||
|             if (t == m_rows.end()) { | ||||
|                 *m_message_stream << "cannot find " << row_name << std::endl; | ||||
|                 goto fail; | ||||
|             } else { | ||||
|                 row * row = t->second; | ||||
|                 row->m_row_columns[column_name] = numeric_traits<T>::from_string(column_data.substr(8)); | ||||
|                 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(); | ||||
|             *m_message_stream << "cannot understand this line" << std::endl; | ||||
|             *m_message_stream << "line = " << m_line <<  ", line number is " << m_line_number << std::endl; | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void read_column(std::string column_name, std::string column_data){ | ||||
|         auto tokens = split_and_trim(column_data); | ||||
|         for (unsigned 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] = numeric_traits<T>::from_string(tokens[i + 1]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void read_columns(){ | ||||
|         std::string column_name; | ||||
|         do { | ||||
|             read_line(); | ||||
|             if (m_line.find("RHS") == 0) { | ||||
|                 //  cout << "found RHS" << std::endl;
 | ||||
|                 break; | ||||
|             } | ||||
|             if (m_line.size() < 22) { | ||||
|                 (*m_message_stream) << "line is too short for a column" << std::endl; | ||||
|                 (*m_message_stream) << m_line << std::endl; | ||||
|                 (*m_message_stream) << "line number is " << m_line_number << std::endl; | ||||
|                 set_m_ok_to_false(); | ||||
|                 return; | ||||
|             } | ||||
|             std::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, static_cast<unsigned>(m_columns.size())); | ||||
|                 m_columns[column_name] = col; | ||||
|                 // (*m_message_stream) << column_name << '[' << col->m_index << ']'<< std::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(std::string rhsides) { | ||||
|         // uph, let us try to work with columns
 | ||||
|         if (rhsides.size() >= 22) { | ||||
|             std::string ss = rhsides.substr(0, 8); | ||||
|             std::string row_name = trim(ss); | ||||
|             auto t = m_rows.find(row_name); | ||||
| 
 | ||||
|             if (t == m_rows.end()) { | ||||
|                 (*m_message_stream) << "cannot find " << row_name << std::endl; | ||||
|                 goto fail; | ||||
|             } else { | ||||
|                 row * row = t->second; | ||||
|                 row->m_right_side = numeric_traits<T>::from_string(rhsides.substr(8)); | ||||
|                 if (rhsides.size() > 24) { | ||||
|                     rhsides = rhsides.substr(25); | ||||
|                     if (rhsides.size() >= 22) { | ||||
|                         fill_rhs_by_columns(rhsides); | ||||
|                     } | ||||
|                 } | ||||
|             } | ||||
|         } else { | ||||
|         fail: | ||||
|             set_m_ok_to_false(); | ||||
|             (*m_message_stream) << "cannot understand this line" << std::endl; | ||||
|             (*m_message_stream) << "line = " << m_line <<  ", line number is " << m_line_number << std::endl; | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void fill_rhs()  { | ||||
|         if (m_line.size() < 14) { | ||||
|             (*m_message_stream) << "line is too short" << std::endl; | ||||
|             (*m_message_stream) << m_line << std::endl; | ||||
|             (*m_message_stream) << "line number is " << m_line_number << std::endl; | ||||
|             set_m_ok_to_false(); | ||||
|             return; | ||||
|         } | ||||
|         std::string rhsides = m_line.substr(14); | ||||
|         vector<std::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 = numeric_traits<T>::from_string(splitted_line[i + 1]); | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     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(std::string colstr) { | ||||
|         if (colstr.size() < 14) { | ||||
|             (*m_message_stream) << "line is too short" << std::endl; | ||||
|             (*m_message_stream) << m_line << std::endl; | ||||
|             (*m_message_stream) << "line number is " << m_line_number << std::endl; | ||||
|             set_m_ok_to_false(); | ||||
|             return; | ||||
|         } | ||||
|          // uph, let us try to work with columns
 | ||||
|         if (colstr.size() >= 22) { | ||||
|             std::string ss = colstr.substr(0, 8); | ||||
|             std::string column_name = trim(ss); | ||||
|             auto t = m_columns.find(column_name); | ||||
| 
 | ||||
|             if (t == m_columns.end()) { | ||||
|                 (*m_message_stream) << "cannot find " << column_name << std::endl; | ||||
|                 goto fail; | ||||
|             } else { | ||||
|                 vector<std::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(); | ||||
|             (*m_message_stream) << "cannot understand this line" << std::endl; | ||||
|             (*m_message_stream) << "line = " << m_line <<  ", line number is " << m_line_number << std::endl; | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void update_bound(bound * b, vector<std::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. | ||||
|         */ | ||||
| 
 | ||||
|         std::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; | ||||
|             } | ||||
|             b->m_upper_is_set = true; | ||||
|             b->m_upper= numeric_traits<T>::from_string(bound_string[1]); | ||||
|         } else if (bound_type == "LO" || bound_type == "LI") { | ||||
|             if (bound_string.size() <= 1){ | ||||
|                 set_m_ok_to_false(); | ||||
|                 return; | ||||
|             } | ||||
| 
 | ||||
|             b->m_low_is_set = true; | ||||
|             b->m_low = numeric_traits<T>::from_string(bound_string[1]); | ||||
|         } 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; | ||||
|             } | ||||
| 
 | ||||
|             b->m_value_is_fixed = true; | ||||
|             b->m_fixed_value =  numeric_traits<T>::from_string(bound_string[1]); | ||||
|         } 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 { | ||||
|             (*m_message_stream) << "unexpected bound type " << bound_type << " at line " << m_line_number << std::endl; | ||||
|             set_m_ok_to_false(); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     void create_or_update_bound() { | ||||
|         const unsigned name_offset = 14; | ||||
|         lean_assert(m_line.size() >= 14); | ||||
|         vector<std::string> bound_string = split_and_trim(m_line.substr(name_offset, m_line.size())); | ||||
| 
 | ||||
|         if (bound_string.size() == 0) { | ||||
|             set_m_ok_to_false(); | ||||
|             (*m_message_stream) << "error at line " << m_line_number << std::endl; | ||||
|             throw m_line; | ||||
|         } | ||||
| 
 | ||||
|         std::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(std::string rhsides) { | ||||
|         if (m_line.size() < 14) { | ||||
|             (*m_message_stream) << "line is too short" << std::endl; | ||||
|             (*m_message_stream) << m_line << std::endl; | ||||
|             (*m_message_stream) << "line number is " << m_line_number << std::endl; | ||||
|             set_m_ok_to_false(); | ||||
|             return; | ||||
|         } | ||||
|          // uph, let us try to work with columns
 | ||||
|         if (rhsides.size() >= 22) { | ||||
|             std::string ss = rhsides.substr(0, 8); | ||||
|             std::string row_name = trim(ss); | ||||
|             auto t = m_rows.find(row_name); | ||||
| 
 | ||||
|             if (t == m_rows.end()) { | ||||
|                 (*m_message_stream) << "cannot find " << row_name << std::endl; | ||||
|                 goto fail; | ||||
|             } else { | ||||
|                 row * row = t->second; | ||||
|                 row->m_range = numeric_traits<T>::from_string(rhsides.substr(8)); | ||||
|                 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(); | ||||
|             (*m_message_stream) << "cannot understand this line" << std::endl; | ||||
|             (*m_message_stream) << "line = " << m_line <<  ", line number is " << m_line_number << std::endl; | ||||
|             return; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
| 
 | ||||
|     void read_range(vector<std::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 = numeric_traits<T>::from_string(splitted_line[i + 1]); | ||||
|             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= static_cast<unsigned>(m_rows.size() - m_cost_line_count); | ||||
|         std::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: | ||||
|             (*m_message_stream) << "unexpected bound type " << row_with_range->m_type << " at line " << m_line_number << std::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= static_cast<unsigned>(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(row_type rt) { | ||||
|         switch (rt) { | ||||
|         case mps_reader::Less_or_equal: return lp_relation::Less_or_equal; | ||||
|         case mps_reader::Greater_or_equal: return lp_relation::Greater_or_equal; | ||||
|         case mps_reader::Equal: return lp_relation::Equal; | ||||
|         default: | ||||
|             (*m_message_stream) << "Unexpected rt " << rt << std::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) { | ||||
|             std::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: | ||||
| 
 | ||||
|     void set_message_stream(std::ostream * o) { | ||||
|         lean_assert(o != nullptr); | ||||
|         m_message_stream = o; | ||||
|     } | ||||
|     vector<std::string> column_names() { | ||||
|         vector<std::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(std::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(row_type rt) { | ||||
|         switch (rt) { | ||||
|         case Less_or_equal: return LE; | ||||
|         case Greater_or_equal: return GE; | ||||
|         case Equal: return EQ; | ||||
|         default: | ||||
|             (*m_message_stream) << "Unexpected rt " << rt << std::endl; | ||||
|             set_m_ok_to_false(); | ||||
|             throw; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     unsigned get_var_index(std::string s) { | ||||
|         auto it = m_names_to_var_index.find(s); | ||||
|         if (it != m_names_to_var_index.end()) | ||||
|             return it->second; | ||||
|         unsigned ret = m_names_to_var_index.size(); | ||||
|         m_names_to_var_index[s] = ret; | ||||
|         return ret; | ||||
|     } | ||||
|      | ||||
|     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); | ||||
|             vector<std::pair<mpq, var_index>> ls; | ||||
|             for (auto s : row->m_row_columns) { | ||||
|                 var_index i = solver->add_var(get_var_index(s.first)); | ||||
|                 ls.push_back(std::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) { | ||||
|         vector<std::pair<mpq, var_index>> ls; | ||||
|         var_index i = solver->add_var(col->m_index); | ||||
|         ls.push_back(std::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_index); | ||||
|         vector<std::pair<mpq, var_index>> ls; | ||||
|         ls.push_back(std::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_index); | ||||
|         vector<std::pair<mpq, var_index>> ls; | ||||
|         ls.push_back(std::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_index); | ||||
|             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; | ||||
|     } | ||||
| }; | ||||
| } | ||||
							
								
								
									
										329
									
								
								src/util/lp/numeric_pair.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										329
									
								
								src/util/lp/numeric_pair.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,329 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
|   The idea is that it is only one different file in Lean and z3 source inside of LP | ||||
| */ | ||||
| #pragma once | ||||
| #define lp_for_z3 | ||||
| #include <string> | ||||
| #include <cmath> | ||||
| #include <algorithm> | ||||
| #ifdef lp_for_z3 | ||||
| #include "../rational.h" | ||||
| #include "../sstream.h" | ||||
| #include "../z3_exception.h" | ||||
| 
 | ||||
| #else | ||||
|  // include "util/numerics/mpq.h"
 | ||||
|  // include "util/numerics/numeric_traits.h"
 | ||||
| #endif | ||||
| namespace lean { | ||||
| #ifdef lp_for_z3 // rename rationals
 | ||||
|     typedef rational mpq; | ||||
| #else | ||||
|     typedef lean::mpq mpq; | ||||
| #endif | ||||
| 
 | ||||
| 
 | ||||
| template <typename T> | ||||
| std::string T_to_string(const T & t); // forward definition
 | ||||
| #ifdef lp_for_z3 | ||||
| template <typename T> class numeric_traits {}; | ||||
| 
 | ||||
| template <>  class numeric_traits<unsigned> { | ||||
| public: | ||||
|     static bool precise() { return true; } | ||||
|     static unsigned const zero() { return 0; } | ||||
|     static unsigned const one() { return 1; } | ||||
|     static bool is_zero(unsigned v) { return v == 0; } | ||||
|     static double const get_double(unsigned const & d) { return d; } | ||||
| }; | ||||
| 
 | ||||
| template <>  class numeric_traits<double> { | ||||
|     public: | ||||
|         static bool precise() { return false; } | ||||
|         static double g_zero; | ||||
|         static double const &zero() { return g_zero;  } | ||||
|         static double g_one; | ||||
|         static double const &one() { return g_one; } | ||||
|         static bool is_zero(double v) { return v == 0.0; } | ||||
|         static double const & get_double(double const & d) { return d;} | ||||
|         static double log(double const & d) { NOT_IMPLEMENTED_YET(); return d;} | ||||
|         static double from_string(std::string const & str) { return atof(str.c_str()); } | ||||
|         static bool is_pos(const double & d) {return d > 0.0;} | ||||
|         static bool is_neg(const double & d) {return d < 0.0;} | ||||
|     }; | ||||
| 
 | ||||
|     template<> | ||||
|     class numeric_traits<rational> { | ||||
|     public: | ||||
|         static bool precise() { return true; } | ||||
|         static rational const & zero() { return rational::zero(); } | ||||
|         static rational const & one() { return rational::one(); } | ||||
|         static bool is_zero(const rational & v) { return v.is_zero(); } | ||||
|         static double const  get_double(const rational  & d) { return d.get_double();} | ||||
|         static rational log(rational const& r) { UNREACHABLE(); return r; } | ||||
|         static rational from_string(std::string const & str) { return rational(str.c_str()); } | ||||
|         static bool is_pos(const rational & d) {return d.is_pos();} | ||||
|         static bool is_neg(const rational & d) {return d.is_neg();} | ||||
|     }; | ||||
| #endif | ||||
| 
 | ||||
| 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 std::abs(numeric_traits<X>::get_double(x)) < y; } | ||||
|     static bool below_bound_numeric(const X &, const X &, const Y &) { /*lean_unreachable();*/ return false;} | ||||
|     static bool above_bound_numeric(const X &, const X &, const Y &) { /*lean_unreachable();*/ return false; } | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| template <> | ||||
| struct convert_struct<double, mpq> { | ||||
|     static double convert(const mpq & q) {return q.get_double();} | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| template <> | ||||
| struct convert_struct<mpq, unsigned> { | ||||
|     static mpq convert(unsigned q) {return mpq(q);} | ||||
| }; | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| 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> | ||||
|     numeric_pair(const X & n) : x(n), y(0) { | ||||
|     } | ||||
|      | ||||
|     template <typename X> | ||||
|     numeric_pair(const numeric_pair<X> & n) : x(n.x), y(n.y) {} | ||||
|      | ||||
|     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 { | ||||
|         // lean_unreachable();
 | ||||
|     } | ||||
|      | ||||
|      | ||||
|     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  { | ||||
|         // lean_unreachable();
 | ||||
|     } | ||||
| 
 | ||||
|     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 lean::numeric_traits<T>::precize();} | ||||
| 
 | ||||
|     bool is_zero() const { return x.is_zero() && y.is_zero(); } | ||||
| 
 | ||||
|     bool is_pos() const { return x.is_pos() || (x.is_zero() && y.is_pos());} | ||||
| 
 | ||||
|     bool is_neg() const { return x.is_neg() || (x.is_zero() && y.is_neg());} | ||||
|      | ||||
|     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 <typename T, typename X> | ||||
| numeric_pair<T> operator/(const numeric_pair<T> & r, const X & a) { | ||||
|     return numeric_pair<T>(r.x / a,  r.y / a); | ||||
| } | ||||
| 
 | ||||
| // template <numeric_pair, typename T>  bool precise() { return numeric_traits<T>::precise();}
 | ||||
| template <typename T> double get_double(const lean::numeric_pair<T> & ) { /* lean_unreachable(); */ return 0;} | ||||
| template <typename T> | ||||
| class numeric_traits<lean::numeric_pair<T>> { | ||||
|   public: | ||||
|     static bool precise() { return numeric_traits<T>::precise();} | ||||
|     static lean::numeric_pair<T> zero() { return lean::numeric_pair<T>(numeric_traits<T>::zero(), numeric_traits<T>::zero()); } | ||||
|     static bool is_zero(const lean::numeric_pair<T> & v) { return numeric_traits<T>::is_zero(v.x) && numeric_traits<T>::is_zero(v.y); } | ||||
|     static double get_double(const lean::numeric_pair<T> & v){ return numeric_traits<T>::get_double(v.x); } // just return the double of the first coordinate
 | ||||
|     static double one() { /*lean_unreachable();*/ return 0;} | ||||
|     static bool is_pos(const numeric_pair<T> &p) { | ||||
|         return numeric_traits<T>::is_pos(p.x) || | ||||
|             (numeric_traits<T>::is_zero(p.x) && numeric_traits<T>::is_pos(p.y)); | ||||
|     } | ||||
|     static bool is_neg(const numeric_pair<T> &p) { | ||||
|         return numeric_traits<T>::is_neg(p.x) || | ||||
|             (numeric_traits<T>::is_zero(p.x) && numeric_traits<T>::is_neg(p.y)); | ||||
|     } | ||||
|              | ||||
| }; | ||||
| 
 | ||||
| template <> | ||||
| struct convert_struct<double, numeric_pair<double>> { | ||||
|     static double convert(const numeric_pair<double> & q) {return q.x;} | ||||
| }; | ||||
| 
 | ||||
| typedef numeric_pair<mpq> impq; | ||||
| 
 | ||||
| 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), numeric_traits<T>::zero()); | ||||
|     } | ||||
|     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 &) { | ||||
|         // lean_unreachable();
 | ||||
|         return false; | ||||
|     } | ||||
|     static bool above_bound_numeric(const numeric_pair<T> &, const numeric_pair<T> &, const double &) { | ||||
|         // lean_unreachable();
 | ||||
|         return false; | ||||
|     } | ||||
| }; | ||||
| 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) { | ||||
|         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
 | ||||
|         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
 | ||||
|         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) { | ||||
|         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) { | ||||
|         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);} | ||||
| } | ||||
							
								
								
									
										173
									
								
								src/util/lp/permutation_matrix.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										173
									
								
								src/util/lp/permutation_matrix.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,173 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include <algorithm> | ||||
| #include "util/debug.h" | ||||
| #include <string> | ||||
| #include "util/lp/sparse_vector.h" | ||||
| #include "util/lp/indexed_vector.h" | ||||
| #include "util/lp/lp_settings.h" | ||||
| #include "util/lp/matrix.h" | ||||
| #include "util/lp/tail_matrix.h" | ||||
| namespace lean { | ||||
| #ifdef LEAN_DEBUG | ||||
|     inline bool is_even(int k) {  return (k/2)*2 == k; } | ||||
| #endif | ||||
| 
 | ||||
|     template <typename T, typename X> | ||||
| class permutation_matrix : public tail_matrix<T, X> { | ||||
|         vector<unsigned> m_permutation; | ||||
|         vector<unsigned> m_rev; | ||||
|         vector<unsigned> m_work_array; | ||||
|         vector<T> m_T_buffer; | ||||
|         vector<X> m_X_buffer; | ||||
| 
 | ||||
| 
 | ||||
|         class ref { | ||||
|             permutation_matrix & m_p; | ||||
|             unsigned m_i; | ||||
|         public: | ||||
|             ref(permutation_matrix & m, unsigned i):m_p(m), m_i(i) {} | ||||
| 
 | ||||
|             ref & operator=(unsigned  v) { m_p.set_val(m_i, v); return *this; } | ||||
| 
 | ||||
|             ref & operator=(ref const & v) { | ||||
|                 m_p.set_val(m_i, v.m_p.m_permutation[v.m_i]); | ||||
|                 return *this; | ||||
|             } | ||||
|             operator unsigned & () const { return m_p.m_permutation[m_i]; } | ||||
|         }; | ||||
| 
 | ||||
|     public: | ||||
|         permutation_matrix() {} | ||||
|         permutation_matrix(unsigned length); | ||||
| 
 | ||||
|         permutation_matrix(unsigned length, vector<unsigned> const & values); | ||||
|         // create a unit permutation of the given length
 | ||||
|         void init(unsigned length); | ||||
|         unsigned get_rev(unsigned i) { return m_rev[i]; } | ||||
|         bool is_dense() const { return false; } | ||||
| #ifdef LEAN_DEBUG | ||||
|         permutation_matrix get_inverse() const { | ||||
|             return permutation_matrix(size(), m_rev); | ||||
|         } | ||||
|         void print(std::ostream & out) const; | ||||
| #endif | ||||
| 
 | ||||
|         ref operator[](unsigned i) { return ref(*this, i); } | ||||
| 
 | ||||
|         unsigned operator[](unsigned i) const { return m_permutation[i]; } | ||||
| 
 | ||||
|         void apply_from_left(vector<X> & w, lp_settings &); | ||||
| 
 | ||||
|         void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings); | ||||
| 
 | ||||
|         void apply_from_right(vector<T> & w); | ||||
| 
 | ||||
|         void apply_from_right(indexed_vector<T> & w); | ||||
|          | ||||
|         template <typename L> | ||||
|         void copy_aside(vector<L> & t, vector<unsigned> & tmp_index, indexed_vector<L> & w); | ||||
| 
 | ||||
|         template <typename L> | ||||
|         void clear_data(indexed_vector<L> & w); | ||||
| 
 | ||||
|         template <typename L> | ||||
|         void apply_reverse_from_left(indexed_vector<L> & w); | ||||
| 
 | ||||
|         void apply_reverse_from_left_to_T(vector<T> & w); | ||||
|         void apply_reverse_from_left_to_X(vector<X> & w); | ||||
| 
 | ||||
|         void apply_reverse_from_right_to_T(vector<T> & w); | ||||
|         void apply_reverse_from_right_to_T(indexed_vector<T> & w); | ||||
|         void apply_reverse_from_right_to_X(vector<X> & w); | ||||
| 
 | ||||
|         void set_val(unsigned i, unsigned pi) { | ||||
|             lean_assert(i < size() && pi < size());  m_permutation[i] = pi;  m_rev[pi] = i;  } | ||||
| 
 | ||||
|         void transpose_from_left(unsigned i, unsigned j); | ||||
| 
 | ||||
|         unsigned apply_reverse(unsigned i) const { return m_rev[i];  } | ||||
| 
 | ||||
|         void transpose_from_right(unsigned i, unsigned j); | ||||
| #ifdef LEAN_DEBUG | ||||
|         T get_elem(unsigned i, unsigned j) const{ | ||||
|             return m_permutation[i] == j? numeric_traits<T>::one() : numeric_traits<T>::zero(); | ||||
|         } | ||||
|         unsigned row_count() const{ return size(); } | ||||
|         unsigned column_count() const { return size(); } | ||||
|         virtual void set_number_of_rows(unsigned /*m*/) { } | ||||
|         virtual void set_number_of_columns(unsigned /*n*/) { } | ||||
| #endif | ||||
|         void multiply_by_permutation_from_left(permutation_matrix<T, X> & p); | ||||
| 
 | ||||
|         // this is multiplication in the matrix sense
 | ||||
|         void multiply_by_permutation_from_right(permutation_matrix<T, X> & p); | ||||
| 
 | ||||
|         void multiply_by_reverse_from_right(permutation_matrix<T, X> & q); | ||||
| 
 | ||||
|         void multiply_by_permutation_reverse_from_left(permutation_matrix<T, X> & r); | ||||
| 
 | ||||
|         void shrink_by_one_identity(); | ||||
| 
 | ||||
|         bool is_identity() const; | ||||
| 
 | ||||
|         unsigned size() const { return static_cast<unsigned>(m_rev.size()); } | ||||
| 
 | ||||
|         unsigned * values() const { return m_permutation; } | ||||
| 
 | ||||
|         void resize(unsigned size) { | ||||
|             unsigned old_size = m_permutation.size(); | ||||
|             m_permutation.resize(size); | ||||
|             m_rev.resize(size); | ||||
|             m_T_buffer.resize(size); | ||||
|             m_X_buffer.resize(size); | ||||
|             for (unsigned i = old_size; i < size; i++) { | ||||
|                 m_permutation[i] = m_rev[i] = i; | ||||
|     } | ||||
|         } | ||||
|          | ||||
|     }; // end of the permutation class
 | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T, typename X> | ||||
| class permutation_generator { | ||||
|     unsigned m_n; | ||||
|     permutation_generator* m_lower; | ||||
|     bool m_done = false; | ||||
|     permutation_matrix<T, X> m_current; | ||||
|     unsigned m_last; | ||||
| public: | ||||
|     permutation_generator(unsigned n); | ||||
|     permutation_generator(const permutation_generator & o); | ||||
|     bool move_next(); | ||||
| 
 | ||||
|     ~permutation_generator() { | ||||
|         if (m_lower != nullptr) { | ||||
|             delete m_lower; | ||||
|         } | ||||
|     } | ||||
| 
 | ||||
|     permutation_matrix<T, X> *current() { | ||||
|         return &m_current; | ||||
|     } | ||||
| }; | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| inline unsigned number_of_inversions(permutation_matrix<T, X> & p); | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| int sign(permutation_matrix<T, X> & p) { | ||||
|     return is_even(number_of_inversions(p))? 1: -1; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| T det_val_on_perm(permutation_matrix<T, X>* u, const matrix<T, X>& m); | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| T determinant(const matrix<T, X>& m); | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										419
									
								
								src/util/lp/permutation_matrix.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										419
									
								
								src/util/lp/permutation_matrix.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,419 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/permutation_matrix.h" | ||||
| namespace lean { | ||||
| template <typename T, typename X> permutation_matrix<T, X>::permutation_matrix(unsigned length): m_permutation(length), m_rev(length), m_T_buffer(length), m_X_buffer(length)  { | ||||
|     for (unsigned i = 0; i < length; i++) { // do not change the direction of the loop because of the vectorization bug in clang3.3
 | ||||
|         m_permutation[i] = m_rev[i] = i; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> permutation_matrix<T, X>::permutation_matrix(unsigned length, vector<unsigned> const & values): m_permutation(length), m_rev(length) , m_T_buffer(length), m_X_buffer(length) { | ||||
|     for (unsigned i = 0; i < length; i++) { | ||||
|         set_val(i, values[i]); | ||||
|     } | ||||
| } | ||||
| // create a unit permutation of the given length
 | ||||
| template <typename T, typename X> void permutation_matrix<T, X>::init(unsigned length) { | ||||
|     m_permutation.resize(length); | ||||
|     m_rev.resize(length); | ||||
|     m_T_buffer.resize(length); | ||||
|     m_X_buffer.resize(length); | ||||
|     for (unsigned i = 0; i < length; i++) { | ||||
|         m_permutation[i] = m_rev[i] = i; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T, typename X> void permutation_matrix<T, X>::print(std::ostream & out) const { | ||||
|     out << "["; | ||||
|     for (unsigned i = 0; i < size(); i++) { | ||||
|         out << m_permutation[i]; | ||||
|         if (i < size() - 1) { | ||||
|             out << ","; | ||||
|         } else { | ||||
|             out << "]"; | ||||
|         } | ||||
|     } | ||||
|     out << std::endl; | ||||
| } | ||||
| #endif | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void permutation_matrix<T, X>::apply_from_left(vector<X> & w, lp_settings & ) { | ||||
| #ifdef LEAN_DEBUG | ||||
|     // dense_matrix<L, X> deb(*this);
 | ||||
|     // L * deb_w = clone_vector<L>(w, row_count());
 | ||||
|     // deb.apply_from_left(deb_w);
 | ||||
| #endif | ||||
|     // std::cout << " apply_from_left " << std::endl;
 | ||||
|     lean_assert(m_X_buffer.size() == w.size()); | ||||
|     unsigned i = size(); | ||||
|     while (i-- > 0) { | ||||
|         m_X_buffer[i] = w[m_permutation[i]]; | ||||
|     } | ||||
|     i = size(); | ||||
|     while (i-- > 0) { | ||||
|         w[i] = m_X_buffer[i]; | ||||
|     } | ||||
| #ifdef LEAN_DEBUG | ||||
|     // lean_assert(vectors_are_equal<L>(deb_w, w, row_count()));
 | ||||
|     // delete [] deb_w;
 | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void permutation_matrix<T, X>::apply_from_left_to_T(indexed_vector<T> & w, lp_settings & ) { | ||||
|     vector<T> t(w.m_index.size()); | ||||
|     vector<unsigned> tmp_index(w.m_index.size()); | ||||
|     copy_aside(t, tmp_index, w); // todo: is it too much copying
 | ||||
|     clear_data(w); | ||||
|     // set the new values
 | ||||
|     for (unsigned i = static_cast<unsigned>(t.size()); i > 0;) { | ||||
|         i--; | ||||
|         unsigned j = m_rev[tmp_index[i]]; | ||||
|         w[j] = t[i]; | ||||
|         w.m_index[i] = j; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void permutation_matrix<T, X>::apply_from_right(vector<T> & w) { | ||||
| #ifdef LEAN_DEBUG | ||||
|     // dense_matrix<T, X> deb(*this);
 | ||||
|     // T * deb_w = clone_vector<T>(w, row_count());
 | ||||
|     // deb.apply_from_right(deb_w);
 | ||||
| #endif | ||||
|     lean_assert(m_T_buffer.size() == w.size()); | ||||
|     for (unsigned i = 0; i < size(); i++) { | ||||
|         m_T_buffer[i] = w[m_rev[i]]; | ||||
|     } | ||||
| 
 | ||||
|     for (unsigned i = 0; i < size(); i++) { | ||||
|         w[i] = m_T_buffer[i]; | ||||
|     } | ||||
| #ifdef LEAN_DEBUG | ||||
|     // lean_assert(vectors_are_equal<T>(deb_w, w, row_count()));
 | ||||
|     // delete [] deb_w;
 | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void permutation_matrix<T, X>::apply_from_right(indexed_vector<T> & w) { | ||||
| #ifdef LEAN_DEBUG | ||||
|     vector<T> wcopy(w.m_data); | ||||
|     apply_from_right(wcopy); | ||||
| #endif | ||||
|     vector<T> buffer(w.m_index.size()); | ||||
|     vector<unsigned> index_copy(w.m_index); | ||||
|     for (unsigned i = 0; i < w.m_index.size(); i++) { | ||||
|         buffer[i] = w.m_data[w.m_index[i]]; | ||||
|     } | ||||
|     w.clear(); | ||||
| 
 | ||||
|     for (unsigned i = 0; i < index_copy.size(); i++) { | ||||
|         unsigned j = index_copy[i]; | ||||
|         unsigned pj = m_permutation[j]; | ||||
|         w.set_value(buffer[i], pj); | ||||
|     } | ||||
|     lean_assert(w.is_OK()); | ||||
| #ifdef LEAN_DEBUG | ||||
|     lean_assert(vectors_are_equal(wcopy, w.m_data)); | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> template <typename L> | ||||
| void permutation_matrix<T, X>::copy_aside(vector<L> & t, vector<unsigned> & tmp_index, indexed_vector<L> & w) { | ||||
|     for (unsigned i = static_cast<unsigned>(t.size()); i > 0;) { | ||||
|         i--; | ||||
|         unsigned j = w.m_index[i]; | ||||
|         t[i] = w[j]; // copy aside all non-zeroes
 | ||||
|         tmp_index[i] = j; // and the indices too
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> template <typename L> | ||||
| void permutation_matrix<T, X>::clear_data(indexed_vector<L> & w) { | ||||
|     // clear old non-zeroes
 | ||||
|     for (unsigned i = static_cast<unsigned>(w.m_index.size()); i > 0;) { | ||||
|         i--; | ||||
|         unsigned j = w.m_index[i]; | ||||
|         w[j] = zero_of_type<L>(); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X>template <typename L> | ||||
| void permutation_matrix<T, X>::apply_reverse_from_left(indexed_vector<L> & w) { | ||||
|     // the result will be w = p(-1) * w
 | ||||
| #ifdef LEAN_DEBUG | ||||
|     // dense_matrix<L, X> deb(get_reverse());
 | ||||
|     // L * deb_w = clone_vector<L>(w.m_data, row_count());
 | ||||
|     // deb.apply_from_left(deb_w);
 | ||||
| #endif | ||||
|     vector<L> t(w.m_index.size()); | ||||
|     vector<unsigned> tmp_index(w.m_index.size()); | ||||
| 
 | ||||
|     copy_aside(t, tmp_index, w); | ||||
|     clear_data(w); | ||||
| 
 | ||||
|     // set the new values
 | ||||
|     for (unsigned i = static_cast<unsigned>(t.size()); i > 0;) { | ||||
|         i--; | ||||
|         unsigned j = m_permutation[tmp_index[i]]; | ||||
|         w[j] = t[i]; | ||||
|         w.m_index[i] = j; | ||||
|     } | ||||
| #ifdef LEAN_DEBUG | ||||
|     // lean_assert(vectors_are_equal<L>(deb_w, w.m_data, row_count()));
 | ||||
|     // delete [] deb_w;
 | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void permutation_matrix<T, X>::apply_reverse_from_left_to_T(vector<T> & w) { | ||||
|     // the result will be w = p(-1) * w
 | ||||
|     lean_assert(m_T_buffer.size() == w.size()); | ||||
|     unsigned i = size(); | ||||
|     while (i-- > 0) { | ||||
|         m_T_buffer[m_permutation[i]] = w[i]; | ||||
|     } | ||||
|     i = size(); | ||||
|     while (i-- > 0) { | ||||
|         w[i] = m_T_buffer[i]; | ||||
|     } | ||||
| } | ||||
| template <typename T, typename X> | ||||
| void permutation_matrix<T, X>::apply_reverse_from_left_to_X(vector<X> & w) { | ||||
|     // the result will be w = p(-1) * w
 | ||||
|     lean_assert(m_X_buffer.size() == w.size()); | ||||
|     unsigned i = size(); | ||||
|     while (i-- > 0) { | ||||
|         m_X_buffer[m_permutation[i]] = w[i]; | ||||
|     } | ||||
|     i = size(); | ||||
|     while (i-- > 0) { | ||||
|         w[i] = m_X_buffer[i]; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void permutation_matrix<T, X>::apply_reverse_from_right_to_T(vector<T> & w) { | ||||
|     // the result will be w = w * p(-1)
 | ||||
|     lean_assert(m_T_buffer.size() == w.size()); | ||||
|     unsigned i = size(); | ||||
|     while (i-- > 0) { | ||||
|         m_T_buffer[i] = w[m_permutation[i]]; | ||||
|     } | ||||
|     i = size(); | ||||
|     while (i-- > 0) { | ||||
|         w[i] = m_T_buffer[i]; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void permutation_matrix<T, X>::apply_reverse_from_right_to_T(indexed_vector<T> & w) { | ||||
|     // the result will be w = w * p(-1)
 | ||||
| #ifdef LEAN_DEBUG | ||||
|     // vector<T> wcopy(w.m_data);
 | ||||
|     // apply_reverse_from_right_to_T(wcopy);
 | ||||
| #endif | ||||
|     lean_assert(w.is_OK()); | ||||
|     vector<T> tmp; | ||||
|     vector<unsigned> tmp_index(w.m_index); | ||||
|     for (auto i : w.m_index) { | ||||
|         tmp.push_back(w[i]); | ||||
|     } | ||||
|     w.clear(); | ||||
|      | ||||
|     for (unsigned k = 0; k < tmp_index.size(); k++) { | ||||
|         unsigned j = tmp_index[k]; | ||||
|         w.set_value(tmp[k], m_rev[j]); | ||||
|     } | ||||
| 
 | ||||
|     // lean_assert(w.is_OK());    
 | ||||
|     // lean_assert(vectors_are_equal(w.m_data, wcopy));
 | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void permutation_matrix<T, X>::apply_reverse_from_right_to_X(vector<X> & w) { | ||||
|     // the result will be w = w * p(-1)
 | ||||
|     lean_assert(m_X_buffer.size() == w.size()); | ||||
|     unsigned i = size(); | ||||
|     while (i-- > 0) { | ||||
|         m_X_buffer[i] = w[m_permutation[i]]; | ||||
|     } | ||||
|     i = size(); | ||||
|     while (i-- > 0) { | ||||
|         w[i] = m_X_buffer[i]; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void permutation_matrix<T, X>::transpose_from_left(unsigned i, unsigned j) { | ||||
|     // the result will be this = (i,j)*this
 | ||||
|     lean_assert(i < size() && j < size() && i != j); | ||||
|     auto pi = m_rev[i]; | ||||
|     auto pj = m_rev[j]; | ||||
|     set_val(pi, j); | ||||
|     set_val(pj, i); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void permutation_matrix<T, X>::transpose_from_right(unsigned i, unsigned j) { | ||||
|     // the result will be this = this * (i,j)
 | ||||
|     lean_assert(i < size() && j < size() && i != j); | ||||
|     auto pi = m_permutation[i]; | ||||
|     auto pj = m_permutation[j]; | ||||
|     set_val(i, pj); | ||||
|     set_val(j, pi); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_permutation_from_left(permutation_matrix<T, X> & p) { | ||||
|     m_work_array = m_permutation; | ||||
|     lean_assert(p.size() == size()); | ||||
|     unsigned i = size(); | ||||
|     while (i-- > 0) { | ||||
|         set_val(i, m_work_array[p[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| // this is multiplication in the matrix sense
 | ||||
| template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_permutation_from_right(permutation_matrix<T, X> & p) { | ||||
|     m_work_array = m_permutation; | ||||
|     lean_assert(p.size() == size()); | ||||
|     unsigned i = size(); | ||||
|     while (i-- > 0) | ||||
|         set_val(i, p[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
 | ||||
|      | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_reverse_from_right(permutation_matrix<T, X> & q){ // todo : condensed permutations ?
 | ||||
|     lean_assert(q.size() == size()); | ||||
|     m_work_array = m_permutation; | ||||
|     // the result is this = this*q(-1)
 | ||||
|     unsigned i = size(); | ||||
|     while (i-- > 0) { | ||||
|         set_val(i, q.m_rev[m_work_array[i]]); // we have m(P)*m(Q) = m(QP), where m is the matrix of the permutation
 | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> void permutation_matrix<T, X>::multiply_by_permutation_reverse_from_left(permutation_matrix<T, X> & r){ // todo : condensed permutations?
 | ||||
|     // the result is this = r(-1)*this
 | ||||
|     m_work_array = m_permutation; | ||||
|     // the result is this = this*q(-1)
 | ||||
|     unsigned i = size(); | ||||
|     while (i-- > 0) { | ||||
|         set_val(i, m_work_array[r.m_rev[i]]); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| template <typename T, typename X> bool permutation_matrix<T, X>::is_identity() const { | ||||
|     unsigned i = size(); | ||||
|     while (i-- > 0) { | ||||
|         if (m_permutation[i] != i) { | ||||
|             return false; | ||||
|         } | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T, typename X> | ||||
| permutation_generator<T, X>::permutation_generator(unsigned n): m_n(n), m_current(n) { | ||||
|     lean_assert(n > 0); | ||||
|     if (n > 1) { | ||||
|         m_lower = new permutation_generator(n - 1); | ||||
|     } else { | ||||
|         m_lower = nullptr; | ||||
|     } | ||||
| 
 | ||||
|     m_last = 0; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| permutation_generator<T, X>::permutation_generator(const permutation_generator & o): m_n(o.m_n), m_done(o.m_done), m_current(o.m_current), m_last(o.m_last) { | ||||
|     if (m_lower != nullptr) { | ||||
|         m_lower = new permutation_generator(o.m_lower); | ||||
|     } else { | ||||
|         m_lower = nullptr; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> bool | ||||
| permutation_generator<T, X>::move_next() { | ||||
|     if (m_done) { | ||||
|         return false; | ||||
|     } | ||||
| 
 | ||||
|     if (m_lower == nullptr) { | ||||
|         if (m_last == 0) { | ||||
|             m_last++; | ||||
|             return true; | ||||
|         } else { | ||||
|             m_done = true; | ||||
|             return false; | ||||
|         } | ||||
|     } else { | ||||
|         if (m_last < m_n && m_last > 0) { | ||||
|             m_current[m_last - 1] = m_current[m_last]; | ||||
|             m_current[m_last] = m_n - 1; | ||||
|             m_last++; | ||||
|             return true; | ||||
|         } else { | ||||
|             if (m_lower -> move_next()) { | ||||
|                 auto lower_curr = m_lower -> current(); | ||||
|                 for ( unsigned i = 1; i < m_n; i++ ){ | ||||
|                     m_current[i] = (*lower_curr)[i - 1]; | ||||
|                 } | ||||
|                 m_current[0] = m_n - 1; | ||||
|                 m_last = 1; | ||||
|                 return true; | ||||
|             } else { | ||||
|                 m_done = true; | ||||
|                 return false; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| inline unsigned number_of_inversions(permutation_matrix<T, X> & p) { | ||||
|     unsigned ret = 0; | ||||
|     unsigned n = p.size(); | ||||
|     for (unsigned i = 0; i < n; i++) { | ||||
|         for (unsigned j = i + 1; j < n; j++) { | ||||
|             if (p[i] > p[j]) { | ||||
|                 ret++; | ||||
|             } | ||||
|         } | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| T det_val_on_perm(permutation_matrix<T, X>* u, const matrix<T, X>& m) { | ||||
|     unsigned n = m.row_count(); | ||||
|     T ret = numeric_traits<T>::one(); | ||||
|     for (unsigned i = 0; i < n; i++) { | ||||
|         unsigned j = (*u)[i]; | ||||
|         ret *=  m(i, j); | ||||
|     } | ||||
|     return ret * sign(*u); | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| T determinant(const matrix<T, X>& m) { | ||||
|     lean_assert(m.column_count() == m.row_count()); | ||||
|     unsigned n = m.row_count(); | ||||
|     permutation_generator<T, X> allp(n); | ||||
|     T ret = numeric_traits<T>::zero(); | ||||
|     while (allp.move_next()){ | ||||
|         ret += det_val_on_perm(allp.current(), m); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| #endif | ||||
| } | ||||
							
								
								
									
										60
									
								
								src/util/lp/permutation_matrix_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								src/util/lp/permutation_matrix_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,60 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include <memory> | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/permutation_matrix.hpp" | ||||
| #include "util/lp/numeric_pair.h" | ||||
| template void lean::permutation_matrix<double, double>::apply_from_right(vector<double>&); | ||||
| template void lean::permutation_matrix<double, double>::init(unsigned int); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::mpq>::init(unsigned int); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq>>::init(unsigned int); | ||||
| template bool lean::permutation_matrix<double, double>::is_identity() const; | ||||
| template void lean::permutation_matrix<double, double>::multiply_by_permutation_from_left(lean::permutation_matrix<double, double>&); | ||||
| template void lean::permutation_matrix<double, double>::multiply_by_permutation_reverse_from_left(lean::permutation_matrix<double, double>&); | ||||
| template void lean::permutation_matrix<double, double>::multiply_by_reverse_from_right(lean::permutation_matrix<double, double>&); | ||||
| template lean::permutation_matrix<double, double>::permutation_matrix(unsigned int, vector<unsigned int> const&); | ||||
| template void lean::permutation_matrix<double, double>::transpose_from_left(unsigned int, unsigned int); | ||||
| 
 | ||||
| template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_from_right(vector<lean::mpq>&); | ||||
| template bool lean::permutation_matrix<lean::mpq, lean::mpq>::is_identity() const; | ||||
| template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_permutation_from_left(lean::permutation_matrix<lean::mpq, lean::mpq>&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_permutation_from_right(lean::permutation_matrix<lean::mpq, lean::mpq>&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_permutation_reverse_from_left(lean::permutation_matrix<lean::mpq, lean::mpq>&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::mpq>::multiply_by_reverse_from_right(lean::permutation_matrix<lean::mpq, lean::mpq>&); | ||||
| template lean::permutation_matrix<lean::mpq, lean::mpq>::permutation_matrix(unsigned int); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::mpq>::transpose_from_left(unsigned int, unsigned int); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::mpq>::transpose_from_right(unsigned int, unsigned int); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_from_right(vector<lean::mpq>&); | ||||
| template bool lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::is_identity() const; | ||||
| template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_permutation_from_left(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_permutation_from_right(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_permutation_reverse_from_left(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::multiply_by_reverse_from_right(lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >&); | ||||
| template lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::permutation_matrix(unsigned int); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::transpose_from_left(unsigned int, unsigned int); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::transpose_from_right(unsigned int, unsigned int); | ||||
| template void lean::permutation_matrix<double, double>::apply_reverse_from_left<double>(lean::indexed_vector<double>&); | ||||
| template void lean::permutation_matrix<double, double>::apply_reverse_from_left_to_T(vector<double>&); | ||||
| template void lean::permutation_matrix<double, double>::apply_reverse_from_right_to_T(vector<double>&); | ||||
| template void lean::permutation_matrix<double, double>::transpose_from_right(unsigned int, unsigned int); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_left<lean::mpq>(lean::indexed_vector<lean::mpq>&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_left_to_T(vector<lean::mpq>&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_right_to_T(vector<lean::mpq>&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_left<lean::mpq>(lean::indexed_vector<lean::mpq>&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_left_to_T(vector<lean::mpq>&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_right_to_T(vector<lean::mpq >&); | ||||
| template void lean::permutation_matrix<double, double>::multiply_by_permutation_from_right(lean::permutation_matrix<double, double>&); | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
| template bool lean::permutation_generator<double, double>::move_next(); | ||||
| template lean::permutation_generator<double, double>::permutation_generator(unsigned int); | ||||
| #endif | ||||
| template lean::permutation_matrix<double, double>::permutation_matrix(unsigned int); | ||||
| template void lean::permutation_matrix<double, double>::apply_reverse_from_left_to_X(vector<double> &); | ||||
| template void  lean::permutation_matrix< lean::mpq, lean::mpq>::apply_reverse_from_left_to_X(vector<lean::mpq> &); | ||||
| template void lean::permutation_matrix< lean::mpq, lean::numeric_pair< lean::mpq> >::apply_reverse_from_left_to_X(vector<lean::numeric_pair< lean::mpq>> &); | ||||
| template void lean::permutation_matrix<double, double>::apply_reverse_from_right_to_T(lean::indexed_vector<double>&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::mpq>::apply_reverse_from_right_to_T(lean::indexed_vector<lean::mpq>&); | ||||
| template void lean::permutation_matrix<lean::mpq, lean::numeric_pair<lean::mpq> >::apply_reverse_from_right_to_T(lean::indexed_vector<lean::mpq>&); | ||||
							
								
								
									
										138
									
								
								src/util/lp/quick_xplain.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										138
									
								
								src/util/lp/quick_xplain.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,138 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/lar_solver.h" | ||||
| namespace lean { | ||||
| quick_xplain::quick_xplain(vector<std::pair<mpq, constraint_index>> & explanation, const lar_solver & ls, lar_solver & qsol) : | ||||
|     m_explanation(explanation), | ||||
|     m_parent_solver(ls), | ||||
|     m_qsol(qsol) { | ||||
| } | ||||
| void quick_xplain::add_constraint_to_qsol(unsigned j) { | ||||
|     auto & lar_c = m_constraints_in_local_vars[j]; | ||||
|     auto ls = lar_c.get_left_side_coefficients(); | ||||
|     auto ci = m_qsol.add_constraint(ls, lar_c.m_kind, lar_c.m_right_side); | ||||
|     m_local_ci_to_constraint_offsets[ci] = j; | ||||
| } | ||||
|      | ||||
| void quick_xplain::copy_constraint_and_add_constraint_vars(const lar_constraint& lar_c) { | ||||
|     vector < std::pair<mpq, unsigned>> ls; | ||||
|     for (auto & p : lar_c.get_left_side_coefficients()) { | ||||
|         unsigned j = p.second; | ||||
|         unsigned lj = m_qsol.add_var(j); | ||||
|         ls.push_back(std::make_pair(p.first, lj)); | ||||
|     } | ||||
|     m_constraints_in_local_vars.push_back(lar_constraint(ls, lar_c.m_kind, lar_c.m_right_side)); | ||||
| 
 | ||||
| } | ||||
| 
 | ||||
| bool quick_xplain::infeasible() { | ||||
|     m_qsol.solve(); | ||||
|     return m_qsol.get_status() == INFEASIBLE; | ||||
| } | ||||
| 
 | ||||
| // u - unexplored constraints
 | ||||
| // c and x are assumed, in other words, all constrains of x and c are already added to m_qsol
 | ||||
| void quick_xplain::minimize(const vector<unsigned>& u) { | ||||
|     unsigned k = 0; | ||||
|     unsigned initial_stack_size = m_qsol.constraint_stack_size(); | ||||
|     for (; k < u.size();k++) { | ||||
|         m_qsol.push(); | ||||
|         add_constraint_to_qsol(u[k]); | ||||
|         if (infeasible()) | ||||
|             break; | ||||
|     } | ||||
|     m_x.insert(u[k]); | ||||
|     unsigned m = k / 2; // the split
 | ||||
|     if (m < k) { | ||||
|         m_qsol.pop(k + 1 - m); | ||||
|         add_constraint_to_qsol(u[k]); | ||||
|         if (!infeasible()) { | ||||
|             vector<unsigned> un; | ||||
|             for (unsigned j = m; j < k; j++) | ||||
|                 un.push_back(u[j]); | ||||
|             minimize(un); | ||||
|         } | ||||
|     } | ||||
|     if (m > 0) { | ||||
|         lean_assert(m_qsol.constraint_stack_size() >= initial_stack_size); | ||||
|         m_qsol.pop(m_qsol.constraint_stack_size() - initial_stack_size); | ||||
|         for (auto j : m_x)  | ||||
|             add_constraint_to_qsol(j); | ||||
|         if (!infeasible()) { | ||||
|             vector<unsigned> un; | ||||
|             for (unsigned j = 0; j < m; j++) | ||||
|                 un.push_back(u[j]); | ||||
|             minimize(un); | ||||
|         } | ||||
|     } | ||||
| } | ||||
| 
 | ||||
|      | ||||
| void quick_xplain::run(vector<std::pair<mpq, constraint_index>> & explanation, const lar_solver & ls){ | ||||
|     if (explanation.size() <= 2) return; | ||||
|     lar_solver qsol; | ||||
|     lean_assert(ls.explanation_is_correct(explanation)); | ||||
|     quick_xplain q(explanation, ls, qsol); | ||||
|     q.solve(); | ||||
| } | ||||
| 
 | ||||
| void quick_xplain::copy_constraints_to_local_constraints() { | ||||
|     for (auto & p : m_explanation) { | ||||
|         const auto & lar_c = m_parent_solver.get_constraint(p.second); | ||||
|         m_local_constraint_offset_to_external_ci.push_back(p.second); | ||||
|         copy_constraint_and_add_constraint_vars(lar_c); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| bool quick_xplain::is_feasible(const vector<unsigned> & x, unsigned k) const { | ||||
|     lar_solver l; | ||||
|     for (unsigned i : x) { | ||||
|         if (i == k) | ||||
|             continue; | ||||
|         vector < std::pair<mpq, unsigned>> ls; | ||||
|         const lar_constraint & c = m_constraints_in_local_vars[i]; | ||||
|         for (auto & p : c.get_left_side_coefficients()) { | ||||
|             unsigned lj = l.add_var(p.second); | ||||
|             ls.push_back(std::make_pair(p.first, lj)); | ||||
|         } | ||||
|         l.add_constraint(ls, c.m_kind, c.m_right_side); | ||||
|     } | ||||
|     l.solve(); | ||||
|     return l.get_status() != INFEASIBLE; | ||||
| } | ||||
| 
 | ||||
| bool quick_xplain::x_is_minimal() const { | ||||
|     vector<unsigned> x; | ||||
|     for (auto j : m_x) | ||||
|         x.push_back(j); | ||||
| 
 | ||||
|     for (unsigned k = 0; k < x.size(); k++) { | ||||
|         lean_assert(is_feasible(x, x[k])); | ||||
|     } | ||||
|     return true; | ||||
| } | ||||
| 
 | ||||
| void quick_xplain::solve() { | ||||
|     copy_constraints_to_local_constraints(); | ||||
|     m_qsol.push(); | ||||
|     lean_assert(m_qsol.constraint_count() == 0) | ||||
|         vector<unsigned> u; | ||||
|     for (unsigned k = 0; k < m_constraints_in_local_vars.size(); k++) | ||||
|         u.push_back(k); | ||||
|     minimize(u); | ||||
|     while (m_qsol.constraint_count() > 0) | ||||
|         m_qsol.pop(); | ||||
|     for (unsigned i : m_x) | ||||
|         add_constraint_to_qsol(i); | ||||
|     m_qsol.solve(); | ||||
|     lean_assert(m_qsol.get_status() == INFEASIBLE); | ||||
|     m_qsol.get_infeasibility_explanation(m_explanation); | ||||
|     lean_assert(m_qsol.explanation_is_correct(m_explanation)); | ||||
|     lean_assert(x_is_minimal()); | ||||
|     for (auto & p : m_explanation) { | ||||
|         p.second = this->m_local_constraint_offset_to_external_ci[m_local_ci_to_constraint_offsets[p.second]]; | ||||
|     } | ||||
| } | ||||
| } | ||||
							
								
								
									
										33
									
								
								src/util/lp/quick_xplain.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/util/lp/quick_xplain.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| /*
 | ||||
| Copyright (c) 2017 Microsoft Corporation | ||||
| Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include <unordered_set> | ||||
| 
 | ||||
| namespace lean { | ||||
|     class lar_solver; // forward definition
 | ||||
|     | ||||
|     class quick_xplain { | ||||
|         std::unordered_set<unsigned> m_x; // the minimal set of constraints, the core - it is empty at the begining
 | ||||
|         vector<lar_constraint> m_constraints_in_local_vars; | ||||
|         vector<std::pair<mpq, constraint_index>> & m_explanation; | ||||
|         const lar_solver& m_parent_solver; | ||||
|         lar_solver & m_qsol; | ||||
|         vector<constraint_index> m_local_constraint_offset_to_external_ci; | ||||
|         std::unordered_map<constraint_index, unsigned> m_local_ci_to_constraint_offsets; | ||||
|         quick_xplain(vector<std::pair<mpq, constraint_index>> & explanation, const lar_solver & parent_lar_solver, lar_solver & qsol); | ||||
|         void minimize(const vector<unsigned> & u); | ||||
|         void add_constraint_to_qsol(unsigned j); | ||||
|         void copy_constraint_and_add_constraint_vars(const lar_constraint& lar_c); | ||||
|         void copy_constraints_to_local_constraints(); | ||||
|         bool infeasible(); | ||||
|         bool is_feasible(const vector<unsigned> & x, unsigned k) const; | ||||
|         bool x_is_minimal() const; | ||||
|     public: | ||||
|         static void run(vector<std::pair<mpq, constraint_index>> & explanation,const lar_solver & ls); | ||||
|         void solve(); | ||||
|     }; | ||||
| } | ||||
							
								
								
									
										77
									
								
								src/util/lp/random_updater.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										77
									
								
								src/util/lp/random_updater.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,77 @@ | |||
| /*
 | ||||
| Copyright (c) 2017 Microsoft Corporation | ||||
| Author: Lev Nachmanson | ||||
| */ | ||||
| #pragma once | ||||
| #include <set> | ||||
| #include "util/vector.h" | ||||
| #include <unordered_map> | ||||
| #include <string> | ||||
| #include <algorithm> | ||||
| #include "util/lp/lp_settings.h" | ||||
| #include "util/lp/linear_combination_iterator.h" | ||||
| // see http://research.microsoft.com/projects/z3/smt07.pdf
 | ||||
| // The class searches for a feasible solution with as many different values of variables as it can find
 | ||||
| namespace lean { | ||||
| template <typename T> struct numeric_pair; // forward definition
 | ||||
| class lar_core_solver; // forward definition
 | ||||
| class random_updater { | ||||
|     unsigned range = 100000; | ||||
|     struct interval { | ||||
|         bool upper_bound_is_set = false; | ||||
|         numeric_pair<mpq> upper_bound; | ||||
|         bool low_bound_is_set = false; | ||||
|         numeric_pair<mpq> low_bound; | ||||
|         void set_low_bound(const numeric_pair<mpq> & v) { | ||||
|             if (low_bound_is_set) { | ||||
|                 low_bound = std::max(v, low_bound); | ||||
|             } else { | ||||
|                 low_bound = v; | ||||
|                 low_bound_is_set = true; | ||||
|             } | ||||
|         } | ||||
|         void set_upper_bound(const numeric_pair<mpq> & v) { | ||||
|             if (upper_bound_is_set) { | ||||
|                 upper_bound = std::min(v, upper_bound); | ||||
|             } else { | ||||
|                 upper_bound = v; | ||||
|                 upper_bound_is_set = true; | ||||
|             } | ||||
|         } | ||||
|         bool is_empty() const {return | ||||
|                 upper_bound_is_set && low_bound_is_set && low_bound >= upper_bound; | ||||
|         } | ||||
| 
 | ||||
|         bool low_bound_holds(const numeric_pair<mpq> & a) const { | ||||
|             return low_bound_is_set == false || a >= low_bound; | ||||
|         } | ||||
|         bool upper_bound_holds(const numeric_pair<mpq> & a) const { | ||||
|             return upper_bound_is_set == false || a <= upper_bound; | ||||
|         } | ||||
| 
 | ||||
|         bool contains(const numeric_pair<mpq> & a) const { | ||||
|             return low_bound_holds(a) && upper_bound_holds(a); | ||||
|         } | ||||
|         std::string lbs() { return low_bound_is_set ? T_to_string(low_bound):std::string("inf");} | ||||
|         std::string rbs() { return upper_bound_is_set? T_to_string(upper_bound):std::string("inf");} | ||||
|         std::string to_str() { return std::string("[")+ lbs() + ", " + rbs() + "]";} | ||||
|     }; | ||||
|     std::set<var_index> m_var_set; | ||||
|     lar_core_solver & m_core_solver; | ||||
|     linear_combination_iterator<mpq>* m_column_j; // the actual column
 | ||||
|     interval find_shift_interval(unsigned j); | ||||
|     interval get_interval_of_non_basic_var(unsigned j); | ||||
|     void add_column_to_sets(unsigned j); | ||||
|     void random_shift_var(unsigned j); | ||||
|     std::unordered_map<numeric_pair<mpq>, unsigned> m_values; // it maps a value to the number of time it occurs
 | ||||
|     void diminish_interval_to_leave_basic_vars_feasible(numeric_pair<mpq> &nb_x, interval & inter); | ||||
|     void shift_var(unsigned j, interval & r); | ||||
|     void diminish_interval_for_basic_var(numeric_pair<mpq> &nb_x, unsigned j, mpq & a, interval & r); | ||||
|     numeric_pair<mpq> get_random_from_interval(interval & r); | ||||
|     void add_value(numeric_pair<mpq>& v); | ||||
|     void remove_value(numeric_pair<mpq> & v); | ||||
|   public: | ||||
|     random_updater(lar_core_solver & core_solver, const vector<unsigned> & column_list); | ||||
|     void update(); | ||||
| }; | ||||
| } | ||||
							
								
								
									
										205
									
								
								src/util/lp/random_updater.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										205
									
								
								src/util/lp/random_updater.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,205 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/random_updater.h" | ||||
| #include "util/lp/static_matrix.h" | ||||
| #include "util/lp/lar_solver.h" | ||||
| #include "util/vector.h" | ||||
| namespace lean { | ||||
| 
 | ||||
| 
 | ||||
| 
 | ||||
| random_updater::random_updater( | ||||
|                                lar_core_solver & lar_core_solver, | ||||
|                                const vector<unsigned> & column_indices) : m_core_solver(lar_core_solver) { | ||||
|     for (unsigned j : column_indices) | ||||
|         add_column_to_sets(j); | ||||
| } | ||||
| 
 | ||||
| random_updater::interval random_updater::get_interval_of_non_basic_var(unsigned j) { | ||||
|     interval ret; | ||||
|     switch (m_core_solver.get_column_type(j)) { | ||||
|     case column_type::free_column: | ||||
|         break; | ||||
|     case column_type::low_bound: | ||||
|         ret.set_low_bound(m_core_solver.m_r_low_bounds[j]); | ||||
|         break; | ||||
|     case column_type::upper_bound: | ||||
|         ret.set_upper_bound(m_core_solver.m_r_upper_bounds[j]); | ||||
|         break; | ||||
|     case column_type::boxed: | ||||
|     case column_type::fixed: | ||||
|         ret.set_low_bound(m_core_solver.m_r_low_bounds[j]); | ||||
|         ret.set_upper_bound(m_core_solver.m_r_upper_bounds[j]); | ||||
|         break; | ||||
|     default: | ||||
|         lean_assert(false); | ||||
|     } | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void random_updater::diminish_interval_for_basic_var(numeric_pair<mpq>& nb_x, unsigned j, | ||||
|                                                      mpq & a, | ||||
|                                                      interval & r) { | ||||
|     lean_assert(m_core_solver.m_r_heading[j] >= 0); | ||||
|     numeric_pair<mpq> delta; | ||||
|     lean_assert(a != zero_of_type<mpq>()); | ||||
|     switch (m_core_solver.get_column_type(j)) { | ||||
|     case column_type::free_column: | ||||
|         break; | ||||
|     case column_type::low_bound: | ||||
|         delta = m_core_solver.m_r_x[j] - m_core_solver.m_r_low_bounds[j]; | ||||
|         lean_assert(delta >= zero_of_type<numeric_pair<mpq>>()); | ||||
|         if (a > 0) { | ||||
|             r.set_upper_bound(nb_x + delta / a); | ||||
|         } else { | ||||
|             r.set_low_bound(nb_x + delta / a); | ||||
|         } | ||||
|         break; | ||||
|     case column_type::upper_bound: | ||||
|         delta = m_core_solver.m_r_upper_bounds()[j] - m_core_solver.m_r_x[j]; | ||||
|         lean_assert(delta >= zero_of_type<numeric_pair<mpq>>()); | ||||
|         if (a > 0) { | ||||
|             r.set_low_bound(nb_x - delta / a); | ||||
|         } else { | ||||
|             r.set_upper_bound(nb_x - delta / a); | ||||
|         } | ||||
|         break; | ||||
|     case column_type::boxed: | ||||
|         if (a > 0) { | ||||
|             delta = m_core_solver.m_r_x[j] - m_core_solver.m_r_low_bounds[j]; | ||||
|             lean_assert(delta >= zero_of_type<numeric_pair<mpq>>()); | ||||
|             r.set_upper_bound(nb_x + delta / a); | ||||
|             delta = m_core_solver.m_r_upper_bounds()[j] - m_core_solver.m_r_x[j]; | ||||
|             lean_assert(delta >= zero_of_type<numeric_pair<mpq>>()); | ||||
|             r.set_low_bound(nb_x - delta / a); | ||||
|         } else { // a < 0
 | ||||
|             delta = m_core_solver.m_r_upper_bounds()[j] - m_core_solver.m_r_x[j]; | ||||
|             lean_assert(delta >= zero_of_type<numeric_pair<mpq>>()); | ||||
|             r.set_upper_bound(nb_x - delta / a); | ||||
|             delta = m_core_solver.m_r_x[j] - m_core_solver.m_r_low_bounds[j]; | ||||
|             lean_assert(delta >= zero_of_type<numeric_pair<mpq>>()); | ||||
|             r.set_low_bound(nb_x + delta / a); | ||||
|         } | ||||
|         break; | ||||
|     case column_type::fixed: | ||||
|           r.set_low_bound(nb_x); | ||||
|           r.set_upper_bound(nb_x); | ||||
|           break; | ||||
|     default: | ||||
|         lean_assert(false); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| 
 | ||||
| void random_updater::diminish_interval_to_leave_basic_vars_feasible(numeric_pair<mpq> &nb_x, interval & r) { | ||||
|     m_column_j->reset(); | ||||
|     unsigned i; | ||||
|     mpq a; | ||||
|     while (m_column_j->next(a, i)) { | ||||
|         diminish_interval_for_basic_var(nb_x, m_core_solver.m_r_basis[i], a, r); | ||||
|         if (r.is_empty()) | ||||
|             break; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| random_updater::interval random_updater::find_shift_interval(unsigned j) { | ||||
|     interval ret = get_interval_of_non_basic_var(j); | ||||
|     diminish_interval_to_leave_basic_vars_feasible(m_core_solver.m_r_x[j], ret); | ||||
|     return ret; | ||||
| } | ||||
| 
 | ||||
| void random_updater::shift_var(unsigned j, interval & r) { | ||||
|     lean_assert(r.contains(m_core_solver.m_r_x[j])); | ||||
|     lean_assert(m_core_solver.m_r_solver.column_is_feasible(j)); | ||||
|     auto old_x = m_core_solver.m_r_x[j]; | ||||
|     remove_value(old_x); | ||||
|     auto new_val = m_core_solver.m_r_x[j] = get_random_from_interval(r); | ||||
|     add_value(new_val); | ||||
| 
 | ||||
|     lean_assert(r.contains(m_core_solver.m_r_x[j])); | ||||
|     lean_assert(m_core_solver.m_r_solver.column_is_feasible(j)); | ||||
|     auto delta = m_core_solver.m_r_x[j] - old_x; | ||||
|     | ||||
|     unsigned i; | ||||
|     m_column_j->reset(); | ||||
|     mpq a; | ||||
|     while(m_column_j->next(a, i)) { | ||||
|         unsigned bj = m_core_solver.m_r_basis[i]; | ||||
|         m_core_solver.m_r_x[bj] -= a * delta; | ||||
|         lean_assert(m_core_solver.m_r_solver.column_is_feasible(bj)); | ||||
|     } | ||||
|     lean_assert(m_core_solver.m_r_solver.A_mult_x_is_off() == false); | ||||
| } | ||||
| 
 | ||||
| numeric_pair<mpq> random_updater::get_random_from_interval(interval & r) { | ||||
|     unsigned rand = my_random(); | ||||
|     if ((!r.low_bound_is_set)  && (!r.upper_bound_is_set)) | ||||
|         return numeric_pair<mpq>(rand % range, 0); | ||||
|     if (r.low_bound_is_set  && (!r.upper_bound_is_set)) | ||||
|         return r.low_bound + numeric_pair<mpq>(rand % range, 0); | ||||
|     if ((!r.low_bound_is_set) && r.upper_bound_is_set) | ||||
|         return r.upper_bound - numeric_pair<mpq>(rand % range, 0); | ||||
|     lean_assert(r.low_bound_is_set && r.upper_bound_is_set); | ||||
|     return r.low_bound + (rand % range) * (r.upper_bound - r.low_bound)/ range; | ||||
| } | ||||
| 
 | ||||
| void random_updater::random_shift_var(unsigned j) { | ||||
|     m_column_j = m_core_solver.get_column_iterator(j); | ||||
|     if (m_column_j->size() >= 50) { | ||||
|         delete m_column_j; | ||||
|         return; | ||||
|     } | ||||
|     interval interv = find_shift_interval(j); | ||||
|     if (interv.is_empty()) { | ||||
|         delete m_column_j; | ||||
|         return; | ||||
|     } | ||||
| 
 | ||||
|     shift_var(j, interv); | ||||
|     delete m_column_j; | ||||
| } | ||||
| 
 | ||||
| void random_updater::update() { | ||||
|     for (auto j : m_var_set) { | ||||
|         if (m_var_set.size() <= m_values.size()) { | ||||
|             break; // we are done
 | ||||
|         } | ||||
|         random_shift_var(j); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void random_updater::add_value(numeric_pair<mpq>& v) { | ||||
|     auto it = m_values.find(v); | ||||
|     if (it == m_values.end()) { | ||||
|         m_values[v] = 1; | ||||
|     } else { | ||||
|         it->second++; | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| void random_updater::remove_value(numeric_pair<mpq>& v) { | ||||
|     auto it = m_values.find(v); | ||||
|     lean_assert(it != m_values.end()); | ||||
|     it->second--; | ||||
|     if (it->second == 0) | ||||
|         m_values.erase(it); | ||||
| } | ||||
| 
 | ||||
| void random_updater::add_column_to_sets(unsigned j) { | ||||
|     if (m_core_solver.m_r_heading[j] < 0) { | ||||
|         m_var_set.insert(j); | ||||
|         add_value(m_core_solver.m_r_x[j]); | ||||
|     } else { | ||||
|         unsigned row = m_core_solver.m_r_heading[j]; | ||||
|         for (auto row_c : m_core_solver.m_r_A.m_rows[row]) { | ||||
|             unsigned cj = row_c.m_j; | ||||
|             if (m_core_solver.m_r_heading[cj] < 0) { | ||||
|                 m_var_set.insert(cj); | ||||
|                 add_value(m_core_solver.m_r_x[cj]); | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| } | ||||
| } | ||||
							
								
								
									
										5
									
								
								src/util/lp/random_updater_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										5
									
								
								src/util/lp/random_updater_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,5 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/lp/random_updater.hpp" | ||||
							
								
								
									
										74
									
								
								src/util/lp/row_eta_matrix.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										74
									
								
								src/util/lp/row_eta_matrix.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,74 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include "util/debug.h" | ||||
| #include <string> | ||||
| #include "util/lp/sparse_vector.h" | ||||
| #include "util/lp/indexed_vector.h" | ||||
| #include "util/lp/permutation_matrix.h" | ||||
| namespace lean { | ||||
|     // This is the sum of a unit matrix and a lower triangular matrix
 | ||||
|     // with non-zero elements only in one row
 | ||||
| template <typename T, typename X> | ||||
| class row_eta_matrix | ||||
|         : public tail_matrix<T, X> { | ||||
| #ifdef LEAN_DEBUG | ||||
|     unsigned m_dimension; | ||||
| #endif | ||||
|     unsigned m_row_start; | ||||
|     unsigned m_row; | ||||
|     sparse_vector<T> m_row_vector; | ||||
| public: | ||||
| #ifdef LEAN_DEBUG | ||||
|     row_eta_matrix(unsigned row_start, unsigned row, unsigned dim): | ||||
| #else | ||||
|     row_eta_matrix(unsigned row_start, unsigned row): | ||||
| #endif | ||||
| 
 | ||||
| #ifdef LEAN_DEBUG | ||||
|     m_dimension(dim), | ||||
| #endif | ||||
|     m_row_start(row_start), m_row(row) { | ||||
|     } | ||||
| 
 | ||||
|     bool is_dense() const { return false; } | ||||
| 
 | ||||
|     void print(std::ostream & out) { | ||||
|         print_matrix(*this, out); | ||||
|     } | ||||
| 
 | ||||
|     const T & get_diagonal_element() const { | ||||
|         return m_row_vector.m_data[m_row]; | ||||
|     } | ||||
| 
 | ||||
|     void apply_from_left(vector<X> & w, lp_settings &); | ||||
| 
 | ||||
|     void apply_from_left_local_to_T(indexed_vector<T> & w, lp_settings & settings); | ||||
|     void apply_from_left_local_to_X(indexed_vector<X> & w, lp_settings & settings); | ||||
| 
 | ||||
|     void apply_from_left_to_T(indexed_vector<T> & w, lp_settings & settings) { | ||||
|         apply_from_left_local_to_T(w, settings); | ||||
|     } | ||||
| 
 | ||||
|     void push_back(unsigned row_index, T val ) { | ||||
|         lean_assert(row_index != m_row); | ||||
|         m_row_vector.push_back(row_index, val); | ||||
|     } | ||||
| 
 | ||||
|     void apply_from_right(vector<T> & w); | ||||
|     void apply_from_right(indexed_vector<T> & w); | ||||
| 
 | ||||
|     void conjugate_by_permutation(permutation_matrix<T, X> & p); | ||||
| #ifdef LEAN_DEBUG | ||||
|     T get_elem(unsigned row, unsigned col) const; | ||||
|     unsigned row_count() const { return m_dimension; } | ||||
|     unsigned column_count() const { return m_dimension; } | ||||
|     void set_number_of_rows(unsigned m) { m_dimension = m; } | ||||
|     void set_number_of_columns(unsigned n) { m_dimension = n; } | ||||
| #endif | ||||
| }; // end of row_eta_matrix
 | ||||
| } | ||||
							
								
								
									
										171
									
								
								src/util/lp/row_eta_matrix.hpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										171
									
								
								src/util/lp/row_eta_matrix.hpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,171 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/vector.h" | ||||
| #include "util/lp/row_eta_matrix.h" | ||||
| namespace lean { | ||||
| template <typename T, typename X> | ||||
| void row_eta_matrix<T, X>::apply_from_left(vector<X> & w, lp_settings &) { | ||||
|     // #ifdef LEAN_DEBUG
 | ||||
|     //         dense_matrix<T> deb(*this);
 | ||||
|     //         auto clone_w = clone_vector<T>(w, m_dimension);
 | ||||
|     //         deb.apply_from_left(clone_w, settings);
 | ||||
|     // #endif
 | ||||
|      | ||||
|     auto & w_at_row = w[m_row]; | ||||
|     for (auto & it : m_row_vector.m_data) { | ||||
|         w_at_row += w[it.first] * it.second; | ||||
|     } | ||||
|     // w[m_row] = w_at_row;
 | ||||
|     // #ifdef LEAN_DEBUG
 | ||||
|     //         lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
 | ||||
|     //         delete [] clone_w;
 | ||||
|     // #endif
 | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void row_eta_matrix<T, X>::apply_from_left_local_to_T(indexed_vector<T> & w, lp_settings & settings) { | ||||
|     auto w_at_row = w[m_row]; | ||||
|     bool was_zero_at_m_row = is_zero(w_at_row); | ||||
| 
 | ||||
|     for (auto & it : m_row_vector.m_data) { | ||||
|         w_at_row += w[it.first] * it.second; | ||||
|     } | ||||
| 
 | ||||
|     if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){ | ||||
|         if (was_zero_at_m_row) { | ||||
|             w.m_index.push_back(m_row); | ||||
|         } | ||||
|         w[m_row] = w_at_row; | ||||
|     } else if (!was_zero_at_m_row){ | ||||
|         w[m_row] = zero_of_type<T>(); | ||||
|         auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); | ||||
|         w.m_index.erase(it); | ||||
|     } | ||||
|     // TBD: lean_assert(check_vector_for_small_values(w, settings));
 | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void row_eta_matrix<T, X>::apply_from_left_local_to_X(indexed_vector<X> & w, lp_settings & settings) { | ||||
|     auto w_at_row = w[m_row]; | ||||
|     bool was_zero_at_m_row = is_zero(w_at_row); | ||||
| 
 | ||||
|     for (auto & it : m_row_vector.m_data) { | ||||
|         w_at_row += w[it.first] * it.second; | ||||
|     } | ||||
| 
 | ||||
|     if (!settings.abs_val_is_smaller_than_drop_tolerance(w_at_row)){ | ||||
|         if (was_zero_at_m_row) { | ||||
|             w.m_index.push_back(m_row); | ||||
|         } | ||||
|         w[m_row] = w_at_row; | ||||
|     } else if (!was_zero_at_m_row){ | ||||
|         w[m_row] = zero_of_type<X>(); | ||||
|         auto it = std::find(w.m_index.begin(), w.m_index.end(), m_row); | ||||
|         w.m_index.erase(it); | ||||
|     } | ||||
|     // TBD: does not compile lean_assert(check_vector_for_small_values(w, settings));
 | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void row_eta_matrix<T, X>::apply_from_right(vector<T> & w) { | ||||
|     const T & w_row = w[m_row]; | ||||
|     if (numeric_traits<T>::is_zero(w_row)) return; | ||||
| #ifdef LEAN_DEBUG | ||||
|     // dense_matrix<T> deb(*this);
 | ||||
|     // auto clone_w = clone_vector<T>(w, m_dimension);
 | ||||
|     // deb.apply_from_right(clone_w);
 | ||||
| #endif | ||||
|     for (auto & it : m_row_vector.m_data) { | ||||
|         w[it.first] += w_row * it.second; | ||||
|     } | ||||
| #ifdef LEAN_DEBUG | ||||
|     // lean_assert(vectors_are_equal<T>(clone_w, w, m_dimension));
 | ||||
|     // delete clone_w;
 | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void row_eta_matrix<T, X>::apply_from_right(indexed_vector<T> & w) { | ||||
|     lean_assert(w.is_OK()); | ||||
|     const T & w_row = w[m_row]; | ||||
|     if (numeric_traits<T>::is_zero(w_row)) return; | ||||
| #ifdef LEAN_DEBUG | ||||
|     // vector<T> wcopy(w.m_data);
 | ||||
|     // apply_from_right(wcopy);
 | ||||
| #endif | ||||
|     if (numeric_traits<T>::precise()) { | ||||
|         for (auto & it : m_row_vector.m_data) { | ||||
|             unsigned j = it.first; | ||||
|             bool was_zero = numeric_traits<T>::is_zero(w[j]); | ||||
|             const T & v = w[j] += w_row * it.second;        | ||||
|              | ||||
|             if (was_zero) { | ||||
|                 if (!numeric_traits<T>::is_zero(v))                      | ||||
|                     w.m_index.push_back(j); | ||||
|             } else { | ||||
|                 if (numeric_traits<T>::is_zero(v)) | ||||
|                     w.erase_from_index(j); | ||||
|             } | ||||
|         } | ||||
|     } else { // the non precise version
 | ||||
|         const double drop_eps = 1e-14; | ||||
|         for (auto & it : m_row_vector.m_data) { | ||||
|             unsigned j = it.first; | ||||
|             bool was_zero = numeric_traits<T>::is_zero(w[j]); | ||||
|             T & v = w[j] += w_row * it.second;        | ||||
|              | ||||
|             if (was_zero) {  | ||||
|                 if (!lp_settings::is_eps_small_general(v, drop_eps)) | ||||
|                     w.m_index.push_back(j); | ||||
|                 else | ||||
|                     v = zero_of_type<T>(); | ||||
|             } else { | ||||
|                 if (lp_settings::is_eps_small_general(v, drop_eps)) { | ||||
|                     w.erase_from_index(j); | ||||
|                     v = zero_of_type<T>(); | ||||
|                 } | ||||
|             } | ||||
|         } | ||||
|     } | ||||
| #ifdef LEAN_DEBUG | ||||
|     // lean_assert(vectors_are_equal(wcopy, w.m_data));
 | ||||
| 
 | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
| template <typename T, typename X> | ||||
| void row_eta_matrix<T, X>::conjugate_by_permutation(permutation_matrix<T, X> & p) { | ||||
|     // this = p * this * p(-1)
 | ||||
| #ifdef LEAN_DEBUG | ||||
|     // auto rev = p.get_reverse();
 | ||||
|     // auto deb = ((*this) * rev);
 | ||||
|     // deb = p * deb;
 | ||||
| #endif | ||||
|     m_row = p.apply_reverse(m_row); | ||||
|     // copy aside the column indices
 | ||||
|     vector<unsigned> columns; | ||||
|     for (auto & it : m_row_vector.m_data) | ||||
|         columns.push_back(it.first); | ||||
|     for (unsigned i = static_cast<unsigned>(columns.size()); i-- > 0;) | ||||
|         m_row_vector.m_data[i].first = p.get_rev(columns[i]); | ||||
| #ifdef LEAN_DEBUG | ||||
|     // lean_assert(deb == *this);
 | ||||
| #endif | ||||
| } | ||||
| #ifdef LEAN_DEBUG | ||||
| template <typename T, typename X> | ||||
| T row_eta_matrix<T, X>::get_elem(unsigned row, unsigned col) const { | ||||
|     if (row == m_row){ | ||||
|         if (col == row) { | ||||
|             return numeric_traits<T>::one(); | ||||
|         } | ||||
|         return m_row_vector[col]; | ||||
|     } | ||||
| 
 | ||||
|     return col == row ? numeric_traits<T>::one() : numeric_traits<T>::zero(); | ||||
| } | ||||
| #endif | ||||
| } | ||||
| 
 | ||||
							
								
								
									
										33
									
								
								src/util/lp/row_eta_matrix_instances.cpp
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								src/util/lp/row_eta_matrix_instances.cpp
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,33 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| #include "util/vector.h" | ||||
| #include <memory> | ||||
| #include "util/lp/row_eta_matrix.hpp" | ||||
| #include "util/lp/lu.h" | ||||
| namespace lean { | ||||
| template void row_eta_matrix<double, double>::conjugate_by_permutation(permutation_matrix<double, double>&); | ||||
| template void row_eta_matrix<mpq, numeric_pair<mpq> >::conjugate_by_permutation(permutation_matrix<mpq, numeric_pair<mpq> >&); | ||||
| template void row_eta_matrix<mpq, mpq>::conjugate_by_permutation(permutation_matrix<mpq, mpq>&); | ||||
| #ifdef LEAN_DEBUG | ||||
| template mpq row_eta_matrix<mpq, mpq>::get_elem(unsigned int, unsigned int) const; | ||||
| template mpq row_eta_matrix<mpq, numeric_pair<mpq> >::get_elem(unsigned int, unsigned int) const; | ||||
| template double row_eta_matrix<double, double>::get_elem(unsigned int, unsigned int) const; | ||||
| #endif | ||||
| template void row_eta_matrix<mpq, mpq>::apply_from_left(vector<mpq>&, lp_settings&); | ||||
| template void row_eta_matrix<mpq, mpq>::apply_from_right(vector<mpq>&); | ||||
| template void row_eta_matrix<mpq, mpq>::apply_from_right(indexed_vector<mpq>&); | ||||
| template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_left(vector<numeric_pair<mpq>>&, lp_settings&); | ||||
| template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_right(vector<mpq>&); | ||||
| template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_right(indexed_vector<mpq>&); | ||||
| template void row_eta_matrix<double, double>::apply_from_left(vector<double>&, lp_settings&); | ||||
| template void row_eta_matrix<double, double>::apply_from_right(vector<double>&); | ||||
| template void row_eta_matrix<double, double>::apply_from_right(indexed_vector<double>&); | ||||
| template void row_eta_matrix<mpq, mpq>::apply_from_left_to_T(indexed_vector<mpq>&, lp_settings&); | ||||
| template void row_eta_matrix<mpq, mpq>::apply_from_left_local_to_T(indexed_vector<mpq>&, lp_settings&); | ||||
| template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_left_to_T(indexed_vector<mpq>&, lp_settings&); | ||||
| template void row_eta_matrix<mpq, numeric_pair<mpq> >::apply_from_left_local_to_T(indexed_vector<mpq>&, lp_settings&); | ||||
| template void row_eta_matrix<double, double>::apply_from_left_to_T(indexed_vector<double>&, lp_settings&); | ||||
| template void row_eta_matrix<double, double>::apply_from_left_local_to_T(indexed_vector<double>&, lp_settings&); | ||||
| } | ||||
							
								
								
									
										79
									
								
								src/util/lp/scaler.h
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										79
									
								
								src/util/lp/scaler.h
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,79 @@ | |||
| /*
 | ||||
|   Copyright (c) 2017 Microsoft Corporation | ||||
|   Author: Lev Nachmanson | ||||
| */ | ||||
| 
 | ||||
| #pragma once | ||||
| #include "util/vector.h" | ||||
| #include <math.h> | ||||
| #include <algorithm> | ||||
| #include <stdio.h>      /* printf, fopen */ | ||||
| #include <stdlib.h>     /* exit, EXIT_FAILURE */ | ||||
| #include "util/lp/lp_utils.h" | ||||
| #include "util/lp/static_matrix.h" | ||||
| 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 get_balance() { return m_A.get_balance(); } | ||||
| 
 | ||||
|     T A_min() const; | ||||
| 
 | ||||
|     T A_max() const; | ||||
| 
 | ||||
|     T get_A_ratio() const; | ||||
| 
 | ||||
|     T get_max_ratio_on_rows() const; | ||||
| 
 | ||||
|     T get_max_ratio_on_columns() const; | ||||
| 
 | ||||
|     void scale_rows_with_geometric_mean(); | ||||
| 
 | ||||
|     void scale_columns_with_geometric_mean(); | ||||
| 
 | ||||
|     void scale_once_for_ratio(); | ||||
| 
 | ||||
|     bool scale_with_ratio(); | ||||
| 
 | ||||
|     void bring_row_maximums_to_one(); | ||||
| 
 | ||||
|     void bring_column_maximums_to_one(); | ||||
| 
 | ||||
|     void bring_rows_and_columns_maximums_to_one(); | ||||
| 
 | ||||
|     bool scale_with_log_balance(); | ||||
|     // Returns true if and only if the scaling was successful.
 | ||||
|     // It is the caller responsibility to restore the matrix
 | ||||
|     bool scale(); | ||||
| 
 | ||||
|     void scale_rows(); | ||||
| 
 | ||||
|     void scale_row(unsigned i); | ||||
| 
 | ||||
|     void scale_column(unsigned i); | ||||
| 
 | ||||
|     void scale_columns(); | ||||
| }; | ||||
| } | ||||
Some files were not shown because too many files have changed in this diff Show more
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue