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