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 // this is a part of lp_primal_core_solver that deals with the tableau
21 #include "math/lp/lp_primal_core_solver.h"
22 namespace lp {
one_iteration_tableau()23 template <typename T, typename X> void lp_primal_core_solver<T, X>::one_iteration_tableau() {
24     int entering = choose_entering_column_tableau();
25     if (entering == -1) {
26         decide_on_status_when_cannot_find_entering();
27     }
28     else {
29         advance_on_entering_tableau(entering);
30     }
31     lp_assert(this->inf_set_is_correct());
32 }
33 
advance_on_entering_tableau(int entering)34 template <typename T, typename X> void lp_primal_core_solver<T, X>::advance_on_entering_tableau(int entering) {
35     X t;
36     int leaving = find_leaving_and_t_tableau(entering, t);
37     if (leaving == -1) {
38         TRACE("lar_solver", tout << "nothing leaving " << entering << "\n";);
39         this->set_status(lp_status::UNBOUNDED);
40         return;
41     }
42     advance_on_entering_and_leaving_tableau(entering, leaving, t);
43 }
44 /*
45 template <typename T, typename X> int lp_primal_core_solver<T, X>::choose_entering_column_tableau_rows() {
46     int i = find_inf_row();
47     if (i == -1)
48         return -1;
49     return find_shortest_beneficial_column_in_row(i);
50  }
51 */
choose_entering_column_tableau()52  template <typename T, typename X> int lp_primal_core_solver<T, X>::choose_entering_column_tableau() {
53     //this moment m_y = cB * B(-1)
54     unsigned number_of_benefitial_columns_to_go_over =  get_number_of_non_basic_column_to_try_for_enter();
55 
56     lp_assert(numeric_traits<T>::precise());
57     if (number_of_benefitial_columns_to_go_over == 0)
58         return -1;
59     if (this->m_basis_sort_counter == 0) {
60         sort_non_basis();
61         this->m_basis_sort_counter = 20;
62     }
63     else {
64         this->m_basis_sort_counter--;
65     }
66     unsigned j_nz = this->m_m() + 1; // this number is greater than the max column size
67     std::list<unsigned>::iterator entering_iter = m_non_basis_list.end();
68     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) {
69         unsigned j = *non_basis_iter;
70         if (!column_is_benefitial_for_entering_basis(j))
71             continue;
72 
73         // if we are here then j is a candidate to enter the basis
74         unsigned t = this->m_A.number_of_non_zeroes_in_column(j);
75         if (t < j_nz) {
76             j_nz = t;
77             entering_iter = non_basis_iter;
78             if (number_of_benefitial_columns_to_go_over)
79                 number_of_benefitial_columns_to_go_over--;
80         }
81         else if (t == j_nz && this->m_settings.random_next() % 2 == 0) {
82             entering_iter = non_basis_iter;
83         }
84     }// while (number_of_benefitial_columns_to_go_over && initial_offset_in_non_basis != offset_in_nb);
85     if (entering_iter == m_non_basis_list.end())
86         return -1;
87     unsigned entering = *entering_iter;
88     m_sign_of_entering_delta = this->m_d[entering] > 0 ? 1 : -1;
89     if (this->using_infeas_costs() && this->m_settings.use_breakpoints_in_feasibility_search)
90         m_sign_of_entering_delta = -m_sign_of_entering_delta;
91     m_non_basis_list.erase(entering_iter);
92     m_non_basis_list.push_back(entering);
93     return entering;
94 
95 }
96 
97 
98 
99 
100 template <typename T, typename X>
solve_with_tableau()101 unsigned lp_primal_core_solver<T, X>::solve_with_tableau() {
102     init_run_tableau();
103     if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only) {
104         this->set_status(lp_status::FEASIBLE);
105         return 0;
106     }
107 
108     if ((!numeric_traits<T>::precise()) && this->A_mult_x_is_off()) {
109         this->set_status(lp_status::FLOATING_POINT_ERROR);
110         return 0;
111     }
112     do {
113         if (this->print_statistics_with_iterations_and_nonzeroes_and_cost_and_check_that_the_time_is_over((this->using_infeas_costs()? "inf t" : "feas t"), * this->m_settings.get_message_ostream())) {
114             return this->total_iterations();
115         }
116         if (this->m_settings.use_tableau_rows()) {
117             one_iteration_tableau_rows();
118         } else {
119             one_iteration_tableau();
120         }
121         TRACE("lar_solver", tout << "one iteration tableau " << this->get_status() << "\n";);
122         switch (this->get_status()) {
123         case lp_status::OPTIMAL:  // double check that we are at optimum
124         case lp_status::INFEASIBLE:
125             if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
126                 break;
127             if (!numeric_traits<T>::precise()) {
128                 if(this->m_look_for_feasible_solution_only)
129                     break;
130                 this->init_lu();
131 
132                 if (this->m_factorization->get_status() != LU_status::OK) {
133                     this->set_status(lp_status::FLOATING_POINT_ERROR);
134                     break;
135                 }
136                 init_reduced_costs();
137                 if (choose_entering_column(1) == -1) {
138                     decide_on_status_when_cannot_find_entering();
139                     break;
140                 }
141                 this->set_status(lp_status::UNKNOWN);
142             } else { // precise case
143                 if ((!this->infeasibility_costs_are_correct())) {
144                     init_reduced_costs_tableau(); // forcing recalc
145                     if (choose_entering_column_tableau() == -1) {
146                         decide_on_status_when_cannot_find_entering();
147                         break;
148                     }
149                     this->set_status(lp_status::UNKNOWN);
150                 }
151             }
152             break;
153         case lp_status::TENTATIVE_UNBOUNDED:
154             this->init_lu();
155             if (this->m_factorization->get_status() != LU_status::OK) {
156                 this->set_status(lp_status::FLOATING_POINT_ERROR);
157                 break;
158             }
159 
160             init_reduced_costs();
161             break;
162         case lp_status::UNBOUNDED:
163             if (this->current_x_is_infeasible()) {
164                 init_reduced_costs_tableau();
165                 this->set_status(lp_status::UNKNOWN);
166             }
167             break;
168 
169         case lp_status::UNSTABLE:
170             lp_assert(! (numeric_traits<T>::precise()));
171             this->init_lu();
172             if (this->m_factorization->get_status() != LU_status::OK) {
173                 this->set_status(lp_status::FLOATING_POINT_ERROR);
174                 break;
175             }
176             init_reduced_costs();
177             break;
178 
179         default:
180             break; // do nothing
181         }
182         if (this->m_settings.get_cancel_flag()
183             ||
184             this->iters_with_no_cost_growing() > this->m_settings.max_number_of_iterations_with_no_improvements
185             ||
186             this->total_iterations() > this->m_settings.max_total_number_of_iterations
187             ) {
188             this->set_status(lp_status::CANCELLED);
189             break; // from the loop
190         }
191     } while (this->get_status() != lp_status::FLOATING_POINT_ERROR
192              &&
193              this->get_status() != lp_status::UNBOUNDED
194              &&
195              this->get_status() != lp_status::OPTIMAL
196              &&
197              this->get_status() != lp_status::INFEASIBLE
198              &&
199              !(this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)
200 	);
201 
202     lp_assert(this->get_status() == lp_status::FLOATING_POINT_ERROR
203               ||
204               this->get_status() == lp_status::CANCELLED
205               ||
206               this->current_x_is_feasible() == false
207               ||
208               this->calc_current_x_is_feasible_include_non_basis());
209     return this->total_iterations();
210 
211 }
advance_on_entering_and_leaving_tableau(int entering,int leaving,X & t)212 template <typename T, typename X>void lp_primal_core_solver<T, X>::advance_on_entering_and_leaving_tableau(int entering, int leaving, X & t) {
213     CASSERT("A_off", this->A_mult_x_is_off() == false);
214     lp_assert(leaving >= 0 && entering >= 0);
215     lp_assert((this->m_settings.simplex_strategy() ==
216                 simplex_strategy_enum::tableau_rows) ||
217                 m_non_basis_list.back() == static_cast<unsigned>(entering));
218     lp_assert(this->using_infeas_costs() || !is_neg(t));
219     lp_assert(entering != leaving || !is_zero(t)); // otherwise nothing changes
220     if (entering == leaving) {
221         advance_on_entering_equal_leaving_tableau(entering, t);
222         return;
223     }
224     if (!is_zero(t)) {
225         if (this->current_x_is_feasible() || !this->m_settings.use_breakpoints_in_feasibility_search ) {
226             if (m_sign_of_entering_delta == -1)
227                 t = -t;
228         }
229         this->update_basis_and_x_tableau(entering, leaving, t);
230         CASSERT("A_off", this->A_mult_x_is_off() == false);
231         this->iters_with_no_cost_growing() = 0;
232     } else {
233         this->pivot_column_tableau(entering, this->m_basis_heading[leaving]);
234         this->change_basis(entering, leaving);
235     }
236 
237     if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
238         return;
239 
240     if (this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows) {
241         if (need_to_switch_costs()) {
242             this->init_reduced_costs_tableau();
243         }
244 
245         lp_assert(!need_to_switch_costs());
246         std::list<unsigned>::iterator it = m_non_basis_list.end();
247         it--;
248         * it = static_cast<unsigned>(leaving);
249     }
250 }
251 
252 template <typename T, typename X>
advance_on_entering_equal_leaving_tableau(int entering,X & t)253 void lp_primal_core_solver<T, X>::advance_on_entering_equal_leaving_tableau(int entering, X & t) {
254     CASSERT("A_off", !this->A_mult_x_is_off() );
255     this->update_x_tableau(entering, t * m_sign_of_entering_delta);
256     if (this->m_look_for_feasible_solution_only && this->current_x_is_feasible())
257         return;
258 
259     if (need_to_switch_costs()) {
260         init_reduced_costs_tableau();
261     }
262     this->iters_with_no_cost_growing() = 0;
263 }
find_leaving_and_t_tableau(unsigned entering,X & t)264 template <typename T, typename X> int lp_primal_core_solver<T, X>::find_leaving_and_t_tableau(unsigned entering, X & t) {
265     unsigned k = 0;
266     bool unlimited = true;
267     unsigned row_min_nz = this->m_n() + 1;
268     m_leaving_candidates.clear();
269     auto & col = this->m_A.m_columns[entering];
270     unsigned col_size = col.size();
271     for (;k < col_size && unlimited; k++) {
272         const column_cell & c = col[k];
273         unsigned i = c.var();
274         const T & ed = this->m_A.get_val(c);
275         lp_assert(!numeric_traits<T>::is_zero(ed));
276         unsigned j = this->m_basis[i];
277         limit_theta_on_basis_column(j, - ed * m_sign_of_entering_delta, t, unlimited);
278         if (!unlimited) {
279             m_leaving_candidates.push_back(j);
280             row_min_nz = this->m_A.m_rows[i].size();
281         }
282     }
283     if (unlimited) {
284         if (try_jump_to_another_bound_on_entering_unlimited(entering, t))
285             return entering;
286         return -1;
287     }
288 
289     X ratio;
290     for (;k < col_size; k++) {
291         const column_cell & c = col[k];
292         unsigned i = c.var();
293         const T & ed = this->m_A.get_val(c);
294          lp_assert(!numeric_traits<T>::is_zero(ed));
295         unsigned j = this->m_basis[i];
296         unlimited = true;
297         limit_theta_on_basis_column(j, -ed * m_sign_of_entering_delta, ratio, unlimited);
298         if (unlimited) continue;
299         unsigned i_nz = this->m_A.m_rows[i].size();
300         if (ratio < t) {
301             t = ratio;
302             m_leaving_candidates.clear();
303             m_leaving_candidates.push_back(j);
304             row_min_nz = i_nz;
305         } else if (ratio == t && i_nz < row_min_nz) {
306             m_leaving_candidates.clear();
307             m_leaving_candidates.push_back(j);
308             row_min_nz = this->m_A.m_rows[i].size();
309         } else if (ratio == t && i_nz == row_min_nz) {
310             m_leaving_candidates.push_back(j);
311         }
312     }
313 
314     ratio = t;
315     unlimited = false;
316     if (try_jump_to_another_bound_on_entering(entering, t, ratio, unlimited)) {
317         t = ratio;
318         return entering;
319     }
320     if (m_leaving_candidates.size() == 1)
321         return m_leaving_candidates[0];
322     k = this->m_settings.random_next() % m_leaving_candidates.size();
323     return m_leaving_candidates[k];
324 }
init_run_tableau()325 template <typename T, typename X> void lp_primal_core_solver<T, X>::init_run_tableau() {
326     CASSERT("A_off", this->A_mult_x_is_off() == false);
327         lp_assert(basis_columns_are_set_correctly());
328         this->m_basis_sort_counter = 0; // to initiate the sort of the basis
329         //  this->set_total_iterations(0);
330         this->iters_with_no_cost_growing() = 0;
331 		lp_assert(this->inf_set_is_correct());
332         if (this->current_x_is_feasible() && this->m_look_for_feasible_solution_only)
333             return;
334         if (this->m_settings.backup_costs)
335             backup_and_normalize_costs();
336         m_epsilon_of_reduced_cost = numeric_traits<X>::precise() ? zero_of_type<T>() : T(1) / T(10000000);
337         if (this->m_settings.use_breakpoints_in_feasibility_search)
338             m_breakpoint_indices_queue.resize(this->m_n());
339         if (!numeric_traits<X>::precise()) {
340             this->m_column_norm_update_counter = 0;
341             init_column_norms();
342         }
343         if (this->m_settings.simplex_strategy() == simplex_strategy_enum::tableau_rows)
344             init_tableau_rows();
345         lp_assert(this->reduced_costs_are_correct_tableau());
346         lp_assert(!this->need_to_pivot_to_basis_tableau());
347 }
348 
349 template <typename T, typename X> bool lp_primal_core_solver<T, X>::
update_basis_and_x_tableau(int entering,int leaving,X const & tt)350 update_basis_and_x_tableau(int entering, int leaving, X const & tt) {
351     lp_assert(this->use_tableau());
352     lp_assert(entering != leaving);
353     update_x_tableau(entering, tt);
354     this->pivot_column_tableau(entering, this->m_basis_heading[leaving]);
355     this->change_basis(entering, leaving);
356     return true;
357 }
358 template <typename T, typename X> void lp_primal_core_solver<T, X>::
update_x_tableau(unsigned entering,const X & delta)359 update_x_tableau(unsigned entering, const X& delta) {
360     this->add_delta_to_x(entering, delta);
361     if (!this->using_infeas_costs()) {
362         for (const auto & c : this->m_A.m_columns[entering]) {
363             unsigned i = c.var();
364             this->add_delta_to_x_and_track_feasibility(this->m_basis[i], -  delta * this->m_A.get_val(c));
365         }
366     } else { // using_infeas_costs() == true
367         lp_assert(this->column_is_feasible(entering));
368         lp_assert(this->m_costs[entering] == zero_of_type<T>());
369         // m_d[entering] can change because of the cost change for basic columns.
370         for (const auto & c : this->m_A.m_columns[entering]) {
371             unsigned i = c.var();
372             unsigned j = this->m_basis[i];
373             this->add_delta_to_x(j, -delta * this->m_A.get_val(c));
374             update_inf_cost_for_column_tableau(j);
375             if (is_zero(this->m_costs[j]))
376                 this->remove_column_from_inf_set(j);
377             else
378                 this->insert_column_into_inf_set(j);
379         }
380     }
381     CASSERT("A_off", this->A_mult_x_is_off() == false);
382 }
383 
384 template <typename T, typename X> void lp_primal_core_solver<T, X>::
update_inf_cost_for_column_tableau(unsigned j)385 update_inf_cost_for_column_tableau(unsigned j) {
386     lp_assert(this->m_settings.simplex_strategy() != simplex_strategy_enum::tableau_rows);
387 
388     lp_assert(this->using_infeas_costs());
389 
390     T new_cost = get_infeasibility_cost_for_column(j);
391     T delta = this->m_costs[j] - new_cost;
392     if (is_zero(delta))
393         return;
394     this->m_costs[j] = new_cost;
395     update_reduced_cost_for_basic_column_cost_change(delta, j);
396 }
397 
init_reduced_costs_tableau()398 template <typename T, typename X> void lp_primal_core_solver<T, X>::init_reduced_costs_tableau() {
399     if (this->current_x_is_infeasible() && !this->using_infeas_costs()) {
400         init_infeasibility_costs();
401     } else if (this->current_x_is_feasible() && this->using_infeas_costs()) {
402         if (this->m_look_for_feasible_solution_only)
403             return;
404         this->m_costs = m_costs_backup;
405         this->set_using_infeas_costs(false);
406     }
407     unsigned size = this->m_basis_heading.size();
408     for (unsigned j = 0; j < size; j++) {
409         if (this->m_basis_heading[j] >= 0)
410             this->m_d[j] = zero_of_type<T>();
411         else {
412             T& d = this->m_d[j] = this->m_costs[j];
413             for (auto & cc : this->m_A.m_columns[j]) {
414                 d -= this->m_costs[this->m_basis[cc.var()]] * this->m_A.get_val(cc);
415             }
416         }
417     }
418 }
419 }
420