1 /*++
2 Copyright (c) 2017 Microsoft Corporation
3 
4 Module Name:
5 
6     <name>
7 
8 Abstract:
9 
10     <abstract>
11 
12 Author:
13 
14     Lev Nachmanson (levnach)
15 
16 Revision History:
17 
18 
19 --*/
20 #include <string>
21 #include <algorithm>
22 #include "util/vector.h"
23 #include "math/lp/lp_solver.h"
24 namespace lp {
get_or_create_column_info(unsigned column)25 template <typename T, typename X> column_info<T> * lp_solver<T, X>::get_or_create_column_info(unsigned column) {
26     auto it = m_map_from_var_index_to_column_info.find(column);
27     return (it == m_map_from_var_index_to_column_info.end())? (m_map_from_var_index_to_column_info[column] = new column_info<T>()) : it->second;
28 }
29 
30 template <typename T, typename X>
get_variable_name(unsigned j)31 std::string lp_solver<T, X>::get_variable_name(unsigned j) const { // j here is the core solver index
32     if (!m_settings.print_external_var_name())
33         return std::string("j")+T_to_string(j);
34     auto it = this->m_core_solver_columns_to_external_columns.find(j);
35     if (it == this->m_core_solver_columns_to_external_columns.end())
36         return std::string("x")+T_to_string(j);
37     unsigned external_j = it->second;
38     auto t = this->m_map_from_var_index_to_column_info.find(external_j);
39     if (t == this->m_map_from_var_index_to_column_info.end()) {
40         return std::string("x") +T_to_string(external_j);
41     }
42     return t->second->get_name();
43 }
44 
get_column_cost_value(unsigned j,column_info<T> * ci)45 template <typename T, typename X> T lp_solver<T, X>::get_column_cost_value(unsigned j, column_info<T> * ci) const {
46     if (ci->is_fixed()) {
47         return ci->get_cost() * ci->get_fixed_value();
48     }
49     return ci->get_cost() * get_column_value(j);
50 }
add_constraint(lp_relation relation,T right_side,unsigned row_index)51 template <typename T, typename X> void lp_solver<T, X>::add_constraint(lp_relation relation, T  right_side, unsigned row_index) {
52     lp_assert(m_constraints.find(row_index) == m_constraints.end());
53     lp_constraint<T, X> cs(right_side, relation);
54     m_constraints[row_index] = cs;
55 }
56 
give_symbolic_name_to_column(std::string name,unsigned column)57 template <typename T, typename X> void lp_solver<T, X>::give_symbolic_name_to_column(std::string name, unsigned column) {
58     auto it = m_map_from_var_index_to_column_info.find(column);
59     column_info<T> *ci;
60     if (it == m_map_from_var_index_to_column_info.end()){
61         m_map_from_var_index_to_column_info[column] = ci = new column_info<T>;
62     } else {
63         ci = it->second;
64     }
65     ci->set_name(name);
66     m_names_to_columns[name] = column;
67 }
68 
69 
get_column_value_by_name(std::string name)70 template <typename T, typename X>  T lp_solver<T, X>::get_column_value_by_name(std::string name) const {
71     auto it = m_names_to_columns.find(name);
72     if (it == m_names_to_columns.end()) {
73         std::stringstream s;
74         s << "get_column_value_by_name " << name;
75         throw_exception(s.str());
76     }
77     return get_column_value(it -> second);
78 }
79 
80 // returns -1 if not found
get_column_index_by_name(std::string name)81 template <typename T, typename X> int lp_solver<T, X>::get_column_index_by_name(std::string name) const {
82     auto t = m_names_to_columns.find(name);
83     if (t == m_names_to_columns.end()) {
84         return -1;
85     }
86     return t->second;
87 }
88 
89 
~lp_solver()90 template <typename T, typename X>  lp_solver<T, X>::~lp_solver(){
91     delete m_A;
92     for (auto t : m_map_from_var_index_to_column_info) {
93         delete t.second;
94     }
95 }
96 
flip_costs()97 template <typename T, typename X> void lp_solver<T, X>::flip_costs() {
98     for (auto t : m_map_from_var_index_to_column_info) {
99         column_info<T> *ci = t.second;
100         ci->set_cost(-ci->get_cost());
101     }
102 }
103 
problem_is_empty()104 template <typename T, typename X>    bool lp_solver<T, X>::problem_is_empty() {
105     for (auto & c : m_A_values)
106         if (!c.second.empty())
107             return false;
108     return true;
109 }
110 
scale()111 template <typename T, typename X> void lp_solver<T, X>::scale() {
112     if (numeric_traits<T>::precise() || m_settings.use_scaling == false) {
113         m_column_scale.clear();
114         m_column_scale.resize(m_A->column_count(), one_of_type<T>());
115         return;
116     }
117 
118     T smin = T(m_settings.scaling_minimum);
119     T smax = T(m_settings.scaling_maximum);
120 
121     scaler<T, X> scaler(m_b, *m_A, smin, smax, m_column_scale, this->m_settings);
122     if (!scaler.scale()) {
123         unscale();
124     }
125 }
126 
127 
print_rows_scale_stats(std::ostream & out)128 template <typename T, typename X> void lp_solver<T, X>::print_rows_scale_stats(std::ostream & out) {
129     out << "rows max" << std::endl;
130     for (unsigned i = 0; i < m_A->row_count(); i++) {
131         print_row_scale_stats(i, out);
132     }
133     out << std::endl;
134 }
135 
print_columns_scale_stats(std::ostream & out)136 template <typename T, typename X> void lp_solver<T, X>::print_columns_scale_stats(std::ostream & out) {
137     out << "columns max" << std::endl;
138     for (unsigned i = 0; i < m_A->column_count(); i++) {
139         print_column_scale_stats(i, out);
140     }
141     out << std::endl;
142 }
143 
print_row_scale_stats(unsigned i,std::ostream & out)144 template <typename T, typename X> void lp_solver<T, X>::print_row_scale_stats(unsigned i, std::ostream & out) {
145     out << "(" << std::min(m_A->get_min_abs_in_row(i), abs(m_b[i])) << " ";
146     out << std::max(m_A->get_max_abs_in_row(i), abs(m_b[i])) << ")";
147 }
148 
print_column_scale_stats(unsigned j,std::ostream & out)149 template <typename T, typename X> void lp_solver<T, X>::print_column_scale_stats(unsigned j, std::ostream & out) {
150     out << "(" << m_A->get_min_abs_in_row(j) << " ";
151     out <<  m_A->get_max_abs_in_column(j) << ")";
152 }
153 
print_scale_stats(std::ostream & out)154 template <typename T, typename X> void lp_solver<T, X>::print_scale_stats(std::ostream & out) {
155     print_rows_scale_stats(out);
156     print_columns_scale_stats(out);
157 }
158 
get_max_abs_in_row(std::unordered_map<unsigned,T> & row_map)159 template <typename T, typename X> void lp_solver<T, X>::get_max_abs_in_row(std::unordered_map<unsigned, T> & row_map) {
160     T ret = numeric_traits<T>::zero();
161     for (auto jp : row_map) {
162         T ac = numeric_traits<T>::abs(jp->second);
163         if (ac > ret) {
164             ret = ac;
165         }
166     }
167     return ret;
168 }
169 
pin_vars_on_row_with_sign(std::unordered_map<unsigned,T> & row,T sign)170 template <typename T, typename X> void lp_solver<T, X>::pin_vars_on_row_with_sign(std::unordered_map<unsigned, T> & row, T sign ) {
171     for (auto t : row) {
172         unsigned j = t.first;
173         column_info<T> * ci = m_map_from_var_index_to_column_info[j];
174         T a = t.second;
175         if (a * sign > numeric_traits<T>::zero()) {
176             lp_assert(ci->upper_bound_is_set());
177             ci->set_fixed_value(ci->get_upper_bound());
178         } else {
179             lp_assert(ci->lower_bound_is_set());
180             ci->set_fixed_value(ci->get_lower_bound());
181         }
182     }
183 }
184 
get_minimal_row_value(std::unordered_map<unsigned,T> & row,T & lower_bound)185 template <typename T, typename X>    bool lp_solver<T, X>::get_minimal_row_value(std::unordered_map<unsigned, T> & row, T & lower_bound) {
186     lower_bound = numeric_traits<T>::zero();
187     for (auto & t : row) {
188         T a = t.second;
189         column_info<T> * ci = m_map_from_var_index_to_column_info[t.first];
190         if (a > numeric_traits<T>::zero()) {
191             if (ci->lower_bound_is_set()) {
192                 lower_bound += ci->get_lower_bound() * a;
193             } else {
194                 return false;
195             }
196         } else {
197             if (ci->upper_bound_is_set()) {
198                 lower_bound += ci->get_upper_bound() * a;
199             } else {
200                 return false;
201             }
202         }
203     }
204     return true;
205 }
206 
get_maximal_row_value(std::unordered_map<unsigned,T> & row,T & lower_bound)207 template <typename T, typename X>    bool lp_solver<T, X>::get_maximal_row_value(std::unordered_map<unsigned, T> & row, T & lower_bound) {
208     lower_bound = numeric_traits<T>::zero();
209     for (auto & t : row) {
210         T a = t.second;
211         column_info<T> * ci = m_map_from_var_index_to_column_info[t.first];
212         if (a < numeric_traits<T>::zero()) {
213             if (ci->lower_bound_is_set()) {
214                 lower_bound += ci->get_lower_bound() * a;
215             } else {
216                 return false;
217             }
218         } else {
219             if (ci->upper_bound_is_set()) {
220                 lower_bound += ci->get_upper_bound() * a;
221             } else {
222                 return false;
223             }
224         }
225     }
226     return true;
227 }
228 
row_is_zero(std::unordered_map<unsigned,T> & row)229 template <typename T, typename X>    bool lp_solver<T, X>::row_is_zero(std::unordered_map<unsigned, T> & row) {
230     for (auto & t : row) {
231         if (!is_zero(t.second))
232             return false;
233     }
234     return true;
235 }
236 
row_e_is_obsolete(std::unordered_map<unsigned,T> & row,unsigned row_index)237 template <typename T, typename X>    bool lp_solver<T, X>::row_e_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
238     T rs = m_constraints[row_index].m_rs;
239     if (row_is_zero(row)) {
240         if (!is_zero(rs))
241             m_status = lp_status::INFEASIBLE;
242         return true;
243     }
244 
245     T lower_bound;
246     bool lb = get_minimal_row_value(row, lower_bound);
247     if (lb) {
248         T diff = lower_bound - rs;
249         if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
250             // lower_bound > rs + m_settings.refactor_epsilon
251             m_status = lp_status::INFEASIBLE;
252             return true;
253         }
254         if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
255             pin_vars_down_on_row(row);
256             return true;
257         }
258     }
259 
260     T upper_bound;
261     bool ub = get_maximal_row_value(row, upper_bound);
262     if (ub) {
263         T diff = rs - upper_bound;
264         if (!val_is_smaller_than_eps(diff,  m_settings.refactor_tolerance)) {
265             // upper_bound < rs - m_settings.refactor_tolerance
266             m_status = lp_status::INFEASIBLE;
267             return true;
268         }
269         if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
270             pin_vars_up_on_row(row);
271             return true;
272         }
273     }
274 
275     return false;
276 }
277 
row_ge_is_obsolete(std::unordered_map<unsigned,T> & row,unsigned row_index)278 template <typename T, typename X>  bool lp_solver<T, X>::row_ge_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
279     T rs = m_constraints[row_index].m_rs;
280     if (row_is_zero(row)) {
281         if (rs > zero_of_type<X>())
282             m_status = lp_status::INFEASIBLE;
283         return true;
284     }
285 
286     T upper_bound;
287     if (get_maximal_row_value(row, upper_bound)) {
288         T diff = rs - upper_bound;
289         if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)) {
290             // upper_bound < rs - m_settings.refactor_tolerance
291             m_status = lp_status::INFEASIBLE;
292             return true;
293         }
294         if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
295             pin_vars_up_on_row(row);
296             return true;
297         }
298     }
299 
300     return false;
301 }
302 
row_le_is_obsolete(std::unordered_map<unsigned,T> & row,unsigned row_index)303 template <typename T, typename X> bool lp_solver<T, X>::row_le_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index) {
304     T lower_bound;
305     T rs = m_constraints[row_index].m_rs;
306     if (row_is_zero(row)) {
307         if (rs < zero_of_type<X>())
308             m_status = lp_status::INFEASIBLE;
309         return true;
310     }
311 
312     if (get_minimal_row_value(row, lower_bound)) {
313         T diff = lower_bound - rs;
314         if (!val_is_smaller_than_eps(diff, m_settings.refactor_tolerance)){
315             // lower_bound > rs + m_settings.refactor_tolerance
316             m_status = lp_status::INFEASIBLE;
317             return true;
318         }
319         if (val_is_smaller_than_eps(-diff, m_settings.refactor_tolerance)){
320             pin_vars_down_on_row(row);
321             return true;
322         }
323     }
324 
325     return false;
326 }
327 
328 // analyse possible max and min values that are derived from var boundaries
329 // Let us say that the we have a "ge" constraint, and the min value is equal to the rs.
330 // Then we know what values of the variables are. For each positive coeff of the row it has to be
331 // the low boundary of the var and for a negative - the upper.
332 
333 // this routing also pins the variables to the boundaries
row_is_obsolete(std::unordered_map<unsigned,T> & row,unsigned row_index)334 template <typename T, typename X>    bool lp_solver<T, X>::row_is_obsolete(std::unordered_map<unsigned, T> & row, unsigned row_index ) {
335     auto & constraint = m_constraints[row_index];
336     switch (constraint.m_relation) {
337     case lp_relation::Equal:
338         return row_e_is_obsolete(row, row_index);
339 
340     case lp_relation::Greater_or_equal:
341         return row_ge_is_obsolete(row, row_index);
342 
343     case lp_relation::Less_or_equal:
344         return row_le_is_obsolete(row, row_index);
345     }
346     lp_unreachable();
347     return false; // it is unreachable
348 }
349 
remove_fixed_or_zero_columns()350 template <typename T, typename X> void lp_solver<T, X>::remove_fixed_or_zero_columns() {
351     for (auto & i_row : m_A_values) {
352         remove_fixed_or_zero_columns_from_row(i_row.first, i_row.second);
353     }
354 }
355 
remove_fixed_or_zero_columns_from_row(unsigned i,std::unordered_map<unsigned,T> & row)356 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) {
357     auto & constraint = m_constraints[i];
358     vector<unsigned> removed;
359     for (auto & col : row) {
360         unsigned j = col.first;
361         lp_assert(m_map_from_var_index_to_column_info.find(j) != m_map_from_var_index_to_column_info.end());
362         column_info<T> * ci = m_map_from_var_index_to_column_info[j];
363         if (ci->is_fixed()) {
364             removed.push_back(j);
365             T aj = col.second;
366             constraint.m_rs -= aj * ci->get_fixed_value();
367         } else {
368             if (numeric_traits<T>::is_zero(col.second)){
369                 removed.push_back(j);
370             }
371         }
372     }
373 
374     for (auto j : removed) {
375         row.erase(j);
376     }
377 }
378 
try_to_remove_some_rows()379 template <typename T, typename X> unsigned lp_solver<T, X>::try_to_remove_some_rows() {
380     vector<unsigned> rows_to_delete;
381     for (auto & t : m_A_values) {
382         if (row_is_obsolete(t.second, t.first)) {
383             rows_to_delete.push_back(t.first);
384         }
385 
386         if (m_status == lp_status::INFEASIBLE) {
387             return 0;
388         }
389     }
390     if (!rows_to_delete.empty()) {
391         for (unsigned k : rows_to_delete) {
392             m_A_values.erase(k);
393         }
394     }
395     remove_fixed_or_zero_columns();
396     return static_cast<unsigned>(rows_to_delete.size());
397 }
398 
cleanup()399 template <typename T, typename X> void lp_solver<T, X>::cleanup() {
400     int n = 0; // number of deleted rows
401     int d;
402     while ((d = try_to_remove_some_rows()) > 0)
403         n += d;
404 
405      if (n == 1) {
406          LP_OUT(m_settings, "deleted one row" << std::endl);
407      } else if (n) {
408          LP_OUT(m_settings, "deleted " << n << " rows" << std::endl);
409      }
410 }
411 
map_external_rows_to_core_solver_rows()412 template <typename T, typename X> void lp_solver<T, X>::map_external_rows_to_core_solver_rows() {
413     unsigned size = 0;
414     for (auto & row : m_A_values) {
415         m_external_rows_to_core_solver_rows[row.first] = size;
416         m_core_solver_rows_to_external_rows[size] = row.first;
417         size++;
418     }
419 }
420 
map_external_columns_to_core_solver_columns()421 template <typename T, typename X> void lp_solver<T, X>::map_external_columns_to_core_solver_columns() {
422     unsigned size = 0;
423     for (auto & row : m_A_values) {
424         for (auto & col : row.second) {
425             if (col.second == numeric_traits<T>::zero() || m_map_from_var_index_to_column_info[col.first]->is_fixed()) {
426                 throw_exception("found fixed column");
427             }
428             unsigned j = col.first;
429             auto column_info_it = m_map_from_var_index_to_column_info.find(j);
430             lp_assert(column_info_it != m_map_from_var_index_to_column_info.end());
431 
432             auto j_column = column_info_it->second->get_column_index();
433             if (!is_valid(j_column)) { // j is a newcomer
434                 m_map_from_var_index_to_column_info[j]->set_column_index(size);
435                 m_core_solver_columns_to_external_columns[size++] = j;
436             }
437         }
438     }
439 }
440 
unscale()441 template <typename T, typename X> void lp_solver<T, X>::unscale() {
442     delete m_A;
443     m_A = nullptr;
444     fill_A_from_A_values();
445     restore_column_scales_to_one();
446     fill_m_b();
447 }
448 
fill_A_from_A_values()449 template <typename T, typename X> void lp_solver<T, X>::fill_A_from_A_values() {
450     m_A = new static_matrix<T, X>(static_cast<unsigned>(m_A_values.size()), number_of_core_structurals());
451     for (auto & t : m_A_values) {
452         auto row_it = m_external_rows_to_core_solver_rows.find(t.first);
453         lp_assert(row_it != m_external_rows_to_core_solver_rows.end());
454         unsigned row =  row_it->second;
455         for (auto k : t.second) {
456             auto column_info_it = m_map_from_var_index_to_column_info.find(k.first);
457             lp_assert(column_info_it != m_map_from_var_index_to_column_info.end());
458             column_info<T> *ci = column_info_it->second;
459             unsigned col = ci->get_column_index();
460             lp_assert(is_valid(col));
461             bool col_is_flipped = m_map_from_var_index_to_column_info[k.first]->is_flipped();
462             if (!col_is_flipped) {
463                 (*m_A)(row, col) = k.second;
464             } else {
465                 (*m_A)(row, col) = - k.second;
466             }
467         }
468     }
469 }
470 
fill_matrix_A_and_init_right_side()471 template <typename T, typename X> void lp_solver<T, X>::fill_matrix_A_and_init_right_side() {
472     map_external_rows_to_core_solver_rows();
473     map_external_columns_to_core_solver_columns();
474     lp_assert(m_A == nullptr);
475     fill_A_from_A_values();
476     m_b.resize(m_A->row_count());
477 }
478 
count_slacks_and_artificials()479 template <typename T, typename X> void lp_solver<T, X>::count_slacks_and_artificials() {
480     for (int i = row_count() - 1; i >= 0; i--) {
481         count_slacks_and_artificials_for_row(i);
482     }
483 }
484 
count_slacks_and_artificials_for_row(unsigned i)485 template <typename T, typename X> void lp_solver<T, X>::count_slacks_and_artificials_for_row(unsigned i) {
486     lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end());
487     auto & constraint = this->m_constraints[this->m_core_solver_rows_to_external_rows[i]];
488     switch (constraint.m_relation) {
489     case Equal:
490         m_artificials++;
491         break;
492     case Greater_or_equal:
493         m_slacks++;
494         if (this->m_b[i] > 0) {
495             m_artificials++;
496         }
497         break;
498     case Less_or_equal:
499         m_slacks++;
500         if (this->m_b[i] < 0) {
501             m_artificials++;
502         }
503         break;
504     }
505 }
506 
lower_bound_shift_for_row(unsigned i)507 template <typename T, typename X>    T lp_solver<T, X>::lower_bound_shift_for_row(unsigned i) {
508     T ret = numeric_traits<T>::zero();
509 
510     auto row = this->m_A_values.find(i);
511     if (row == this->m_A_values.end()) {
512         throw_exception("cannot find row");
513     }
514     for (auto col : row->second) {
515         ret += col.second * this->m_map_from_var_index_to_column_info[col.first]->get_shift();
516     }
517     return ret;
518 }
519 
fill_m_b()520 template <typename T, typename X> void lp_solver<T, X>::fill_m_b() {
521     for (int i = this->row_count() - 1; i >= 0; i--) {
522         lp_assert(this->m_constraints.find(this->m_core_solver_rows_to_external_rows[i]) != this->m_constraints.end());
523         unsigned external_i = this->m_core_solver_rows_to_external_rows[i];
524         auto & constraint = this->m_constraints[external_i];
525         this->m_b[i] = constraint.m_rs - lower_bound_shift_for_row(external_i);
526     }
527 }
528 
get_column_value_with_core_solver(unsigned column,lp_core_solver_base<T,X> * core_solver)529 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 {
530     auto cit = this->m_map_from_var_index_to_column_info.find(column);
531     if (cit == this->m_map_from_var_index_to_column_info.end()) {
532         return numeric_traits<T>::zero();
533     }
534 
535     column_info<T> * ci = cit->second;
536 
537     if (ci->is_fixed()) {
538         return ci->get_fixed_value();
539     }
540 
541     unsigned cj = ci->get_column_index();
542     if (cj != static_cast<unsigned>(-1)) {
543         T v = core_solver->get_var_value(cj) * this->m_column_scale[cj];
544         if (ci->is_free()) {
545             return v;
546         }
547         if (!ci->is_flipped()) {
548             return v + ci->get_lower_bound();
549         }
550 
551         // the flipped case when there is only upper bound
552         return -v + ci->get_upper_bound(); //
553     }
554 
555     return numeric_traits<T>::zero(); // returns zero for out of boundary columns
556 }
557 
set_scaled_cost(unsigned j)558 template <typename T, typename X> void lp_solver<T, X>::set_scaled_cost(unsigned j) {
559     // grab original costs but modify it with the column scales
560     lp_assert(j < this->m_column_scale.size());
561     column_info<T> * ci = this->m_map_from_var_index_to_column_info[this->m_core_solver_columns_to_external_columns[j]];
562     T cost = ci->get_cost();
563     if (ci->is_flipped()){
564         cost *= T(-1);
565     }
566     lp_assert(ci->is_fixed() == false);
567     this->m_costs[j] = cost * this->m_column_scale[j];
568 }
569 }
570