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