1 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2 /* */
3 /* This file is part of the HiGHS linear optimization suite */
4 /* */
5 /* Written and engineered 2008-2021 at the University of Edinburgh */
6 /* */
7 /* Available as open-source under the MIT License */
8 /* */
9 /* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
10 /**@file presolve/Presolve.cpp
11 * @brief
12 * @author Julian Hall, Ivet Galabova, Qi Huangfu and Michael Feldmeier
13 */
14 #include "presolve/Presolve.h"
15
16 //#include "simplex/HFactor.h"
17 #include <algorithm>
18 #include <cassert>
19 #include <cmath>
20 #include <cstdio>
21 #include <iomanip>
22 #include <iostream>
23 #include <iterator>
24 #include <queue>
25 #include <sstream>
26
27 #include "io/HighsIO.h"
28 #include "lp_data/HConst.h"
29 #include "presolve/HighsLpPropagator.h"
30 #include "presolve/PresolveUtils.h"
31
32 namespace presolve {
33
34 using std::cout;
35 using std::endl;
36 using std::flush;
37 using std::get;
38 using std::ios;
39 using std::list;
40 using std::make_pair;
41 using std::max;
42 using std::min;
43 using std::ofstream;
44 using std::setprecision;
45 using std::setw;
46 using std::stringstream;
47
load(const HighsLp & lp,bool mip)48 void Presolve::load(const HighsLp& lp, bool mip) {
49 timer.recordStart(MATRIX_COPY);
50 numCol = lp.numCol_;
51 numRow = lp.numRow_;
52 numTot = numTot;
53 Astart = lp.Astart_;
54 Aindex = lp.Aindex_;
55 Avalue = lp.Avalue_;
56 this->mip = mip;
57
58 colCost = lp.colCost_;
59 objShift = lp.offset_;
60 if (lp.sense_ == ObjSense::MAXIMIZE) {
61 objShift = -objShift;
62 for (unsigned int col = 0; col < lp.colCost_.size(); col++)
63 colCost[col] = -colCost[col];
64 }
65
66 integrality = lp.integrality_;
67 colLower = lp.colLower_;
68 colUpper = lp.colUpper_;
69 rowLower = lp.rowLower_;
70 rowUpper = lp.rowUpper_;
71
72 modelName = lp.model_name_;
73 timer.recordFinish(MATRIX_COPY);
74 }
75
setNumericalTolerances()76 void Presolve::setNumericalTolerances() {
77 const bool use_original_tol = false;
78 const double zero_tolerance = 1e-16;
79 if (use_original_tol) {
80 inconsistent_bounds_tolerance = tol;
81 fixed_column_tolerance =
82 zero_tolerance; // Since exact equality is currently used
83 doubleton_equation_bound_tolerance = tol;
84 doubleton_inequality_bound_tolerance = tol;
85 presolve_small_matrix_value = tol;
86 empty_row_bound_tolerance = tol;
87 dominated_column_tolerance = tol;
88 weakly_dominated_column_tolerance = tol;
89 } else {
90 // Tolerance on bounds being inconsistent: should be twice
91 // primal_feasibility_tolerance since bounds inconsistent by this
92 // value can be satisfied to within the primal feasibility tolerance
93 // by a primal vlaue at their midpoint. The following is twice the
94 // default primal_feasibility_tolerance.
95 inconsistent_bounds_tolerance = 2 * default_primal_feasiblility_tolerance;
96 // Tolerance on column bounds differences being considered to be
97 // zero, allowing a column to be fixed
98 fixed_column_tolerance =
99 zero_tolerance; // Since exact equality is currently used
100 // 2 * default_primal_feasiblility_tolerance;
101 // Tolerance on bound differences being considered to be zero,
102 // allowing a doubleton to be treated as an equation. What value
103 // this should have is unclear. It could depend on the coefficients
104 // of the two variables and the values of the bounds, as there's an
105 // implicit infeasibility created when the optimal value for one
106 // variable is substituted to deduce the optimal value of the other.
107 doubleton_equation_bound_tolerance =
108 2 * default_primal_feasiblility_tolerance;
109 doubleton_inequality_bound_tolerance = doubleton_equation_bound_tolerance;
110 // Need to decide when a matrix coefficient changed by substitution
111 // is zeroed: should be the small_matrix_value, for which the
112 // following is the default value
113 presolve_small_matrix_value = default_small_matrix_value;
114 // Tolerance on the lower and upper bound being sufficiently close
115 // to zero to allowing an empty row to be removed, rather than have
116 // the LP deduced as infeasible. This should be t =
117 // primal_feasibility_tolerance since the row activity of zero
118 // satisfies a lower bound of at most t, and an upper bound of at
119 // least -t. The following is the default
120 // primal_feasibility_tolerance.
121 empty_row_bound_tolerance = default_primal_feasiblility_tolerance;
122 dominated_column_tolerance = default_dual_feasiblility_tolerance;
123 weakly_dominated_column_tolerance = default_dual_feasiblility_tolerance;
124 }
125 timer.model_name = modelName;
126 // Initialise the numerics records. JAJH thinks that this has to be
127 // done here, as the tolerances are only known in Presolve.h/cpp so
128 // have to be passed in
129 timer.presolve_numerics.resize(PRESOLVE_NUMERICS_COUNT);
130 timer.initialiseNumericsRecord(INCONSISTENT_BOUNDS, "Inconsistent bounds",
131 inconsistent_bounds_tolerance);
132 timer.initialiseNumericsRecord(FIXED_COLUMN, "Fixed column",
133 fixed_column_tolerance);
134 timer.initialiseNumericsRecord(DOUBLETON_EQUATION_BOUND,
135 "Doubleton equation bound",
136 doubleton_equation_bound_tolerance);
137 timer.initialiseNumericsRecord(DOUBLETON_INEQUALITY_BOUND,
138 "Doubleton inequality bound",
139 doubleton_inequality_bound_tolerance);
140 timer.initialiseNumericsRecord(SMALL_MATRIX_VALUE, "Small matrix value",
141 presolve_small_matrix_value);
142 timer.initialiseNumericsRecord(EMPTY_ROW_BOUND, "Empty row bounds",
143 empty_row_bound_tolerance);
144 timer.initialiseNumericsRecord(DOMINATED_COLUMN, "Dominated column",
145 dominated_column_tolerance);
146 timer.initialiseNumericsRecord(WEAKLY_DOMINATED_COLUMN,
147 "Weakly dominated column",
148 weakly_dominated_column_tolerance);
149 }
150
151 // printing with cout goes here.
reportDev(const string & message)152 void reportDev(const string& message) {
153 std::cout << message << std::flush;
154 return;
155 }
156
printMainLoop(const MainLoop & l)157 void printMainLoop(const MainLoop& l) {
158 std::cout << " loop : " << l.rows << "," << l.cols << "," << l.nnz << " "
159 << std::endl;
160 }
161
printDevStats(const DevStats & stats)162 void printDevStats(const DevStats& stats) {
163 assert(stats.n_loops == (int)stats.loops.size());
164
165 std::cout << "dev-presolve-stats::" << std::endl;
166 std::cout << " n_loops = " << stats.n_loops << std::endl;
167 std::cout << " loop : rows, cols, nnz " << std::endl;
168 for (const MainLoop l : stats.loops) printMainLoop(l);
169 return;
170 }
171
getRowsColsNnz(const std::vector<int> & flagRow,const std::vector<int> & flagCol,const std::vector<int> & nzRow,const std::vector<int> & nzCol,int & _rows,int & _cols,int & _nnz)172 void getRowsColsNnz(const std::vector<int>& flagRow,
173 const std::vector<int>& flagCol,
174 const std::vector<int>& nzRow,
175 const std::vector<int>& nzCol, int& _rows, int& _cols,
176 int& _nnz) {
177 int numCol = flagCol.size();
178 int numRow = flagRow.size();
179 int rows = 0;
180 int cols = 0;
181
182 std::vector<int> nnz_rows(numRow, 0);
183 std::vector<int> nnz_cols(numCol, 0);
184
185 int total_rows = 0;
186 int total_cols = 0;
187
188 for (int i = 0; i < numRow; i++)
189 if (flagRow.at(i)) {
190 rows++;
191 nnz_rows[i] += nzRow[i];
192 total_rows += nzRow[i];
193 }
194
195 for (int j = 0; j < numCol; j++)
196 if (flagCol.at(j)) {
197 cols++;
198 nnz_cols[j] += nzCol[j];
199 total_cols += nzCol[j];
200 }
201
202 // Nonzeros.
203 assert(total_cols == total_rows);
204
205 _rows = rows;
206 _cols = cols;
207 _nnz = total_cols;
208 }
209
reportDevMidMainLoop()210 void Presolve::reportDevMidMainLoop() {
211 if (iPrint == 0) return;
212
213 int rows = 0;
214 int cols = 0;
215 int nnz = 0;
216 getRowsColsNnz(flagRow, flagCol, nzRow, nzCol, rows, cols, nnz);
217
218 std::cout << " counts " << rows
219 << ", " << cols << ", " << nnz << std::endl;
220 }
221
reportDevMainLoop()222 void Presolve::reportDevMainLoop() {
223 if (iPrint == 0) {
224 if (timer.getTime() > 10)
225 HighsPrintMessage(output, message_level, ML_VERBOSE,
226 "Presolve finished main loop %d... ",
227 stats.dev.n_loops + 1);
228 } else {
229 int rows = 0;
230 int cols = 0;
231 int nnz = 0;
232
233 getRowsColsNnz(flagRow, flagCol, nzRow, nzCol, rows, cols, nnz);
234
235 stats.dev.n_loops++;
236 stats.dev.loops.push_back(MainLoop{rows, cols, nnz});
237
238 std::cout << "Starting loop " << stats.dev.n_loops;
239
240 printMainLoop(stats.dev.loops[stats.dev.n_loops - 1]);
241 }
242 return;
243 }
244
removeEmpty()245 void Presolve::removeEmpty() {
246 // cols
247 for (int col = 0; col < numCol; col++) {
248 if (flagCol[col])
249 if (nzCol[col] == 0) {
250 removeEmptyColumn(col);
251 }
252 }
253
254 // rows
255 for (int row = 0; row < numRow; row++) {
256 if (flagRow[row])
257 if (nzRow[row] == 0) {
258 removeEmptyRow(row);
259 }
260 }
261 }
262
runPresolvers(const std::vector<Presolver> & order)263 int Presolve::runPresolvers(const std::vector<Presolver>& order) {
264 //***************** main loop ******************
265
266 checkBoundsAreConsistent();
267 if (status) return status;
268
269 if (iPrint) std::cout << "----> fixed cols" << std::endl;
270
271 for (Presolver main_loop_presolver : order) {
272 double time_start = timer.timer_.readRunHighsClock();
273 if (iPrint) std::cout << "----> ";
274 auto it = kPresolverNames.find(main_loop_presolver);
275 assert(it != kPresolverNames.end());
276 if (iPrint) std::cout << (*it).second << std::endl;
277
278 switch (main_loop_presolver) {
279 case Presolver::kMainEmpty:
280 removeEmpty();
281 removeFixed();
282 break;
283 case Presolver::kMainRowSingletons:
284 timer.recordStart(REMOVE_ROW_SINGLETONS);
285 removeRowSingletons();
286 timer.recordFinish(REMOVE_ROW_SINGLETONS);
287 break;
288 case Presolver::kMainForcing:
289 timer.recordStart(REMOVE_FORCING_CONSTRAINTS);
290 removeForcingConstraints();
291 timer.recordFinish(REMOVE_FORCING_CONSTRAINTS);
292 break;
293 case Presolver::kMainColSingletons:
294 timer.recordStart(REMOVE_COLUMN_SINGLETONS);
295 removeColumnSingletons();
296 timer.recordFinish(REMOVE_COLUMN_SINGLETONS);
297 break;
298 case Presolver::kMainDoubletonEq:
299 timer.recordStart(REMOVE_DOUBLETON_EQUATIONS);
300 removeDoubletonEquations();
301 timer.recordFinish(REMOVE_DOUBLETON_EQUATIONS);
302 break;
303 case Presolver::kMainDominatedCols:
304 timer.recordStart(REMOVE_DOMINATED_COLUMNS);
305 removeDominatedColumns();
306 timer.recordFinish(REMOVE_DOMINATED_COLUMNS);
307 break;
308 case Presolver::kMainSingletonsOnly:
309 // To implement
310 // timer.recordStart(SING_ONLY);
311 removeSingletonsOnly();
312 // timer.recordFinish(SING_ONLY);
313 break;
314 case Presolver::kMainMipDualFixing:
315 timer.recordStart(MIP_DUAL_FIXING);
316 applyMipDualFixing();
317 timer.recordFinish(MIP_DUAL_FIXING);
318 break;
319 }
320
321 double time_end = timer.timer_.readRunHighsClock();
322 if (iPrint)
323 std::cout << (*it).second << " time: " << time_end - time_start
324 << std::endl;
325 reportDevMidMainLoop();
326 if (status) return status;
327 }
328
329 //***************** main loop ******************
330 return status;
331 }
332
333 // void Presolve::removeSingletonsOnly() {
334 // for (int row = 0; row < numRow; row++) {
335 // if (!flagRow[row]) continue;
336 // bool valid = true;
337 // int nz_col = 0;
338 // for (int k = ARstart[row]; k < ARstart[row + 1]; k++) {
339 // const int col = ARindex[k];
340 // if (!flagCol[col]) continue;
341 // if (nzCol[col] != 1) {
342 // valid = false;
343 // break;
344 // }
345 // nz_col++;
346 // }
347 // if (!valid) continue;
348 // if (nz_col == 0) {
349 // flagRow[row] = false;
350 // continue;
351 // }
352
353 // std::cout << "Singletons only row found! nzcol = " << nz_col << " L = "
354 // << rowLower[row] << " U = " << rowUpper[row] << std::endl;
355 // }
356 // }
357
removeFixed()358 void Presolve::removeFixed() {
359 timer.recordStart(FIXED_COL);
360 for (int j = 0; j < numCol; ++j)
361 if (flagCol.at(j)) {
362 // Analyse dependency on numerical tolerance
363 timer.updateNumericsRecord(FIXED_COLUMN,
364 fabs(colUpper.at(j) - colLower.at(j)));
365 roundIntegerBounds(j);
366 if (fabs(colUpper.at(j) - colLower.at(j)) > fixed_column_tolerance)
367 continue;
368 removeFixedCol(j);
369 if (status) {
370 timer.recordFinish(FIXED_COL);
371 return;
372 }
373 }
374 timer.recordFinish(FIXED_COL);
375 }
376
presolve(int print)377 int Presolve::presolve(int print) {
378 timer.start_time = timer.getTime();
379 bool aggregatorCalled = false;
380
381 if (iPrint > 0) {
382 cout << "Presolve started ..." << endl;
383 cout << "Original problem ... N=" << numCol << " M=" << numRow << endl;
384 }
385
386 if (iPrint < 0) {
387 stringstream ss;
388 ss << "dev-presolve: model: rows, colx, nnz , " << modelName << ": "
389 << numRow << ", " << numCol << ", " << (int)Avalue.size() << std::endl;
390 reportDev(ss.str());
391 }
392
393 initializeVectors();
394 if (status) return status;
395
396 // removeFixed();
397 // if (status) return status;
398
399 int iter = 1;
400 if (order.size() == 0) {
401 // pre_release_order:
402 order.push_back(Presolver::kMainEmpty);
403 order.push_back(Presolver::kMainRowSingletons);
404 order.push_back(Presolver::kMainForcing);
405 order.push_back(Presolver::kMainRowSingletons);
406 order.push_back(Presolver::kMainDoubletonEq);
407 order.push_back(Presolver::kMainRowSingletons);
408 order.push_back(Presolver::kMainColSingletons);
409 order.push_back(Presolver::kMainDominatedCols);
410 if (mip) order.push_back(Presolver::kMainMipDualFixing);
411 // wip
412 // order.push_back(Presolver::kMainSingletonsOnly);
413 }
414
415 const double reduction_pct_for_further_presolve_iteration = 0.05;
416 int model_cols_rows = numCol + numRow;
417 int prev_cols_rows;
418 int current_cols_rows = model_cols_rows;
419 // double prev_diff = 0;
420 // Else: The order has been modified for experiments
421 while (hasChange == 1) {
422 if (max_iterations > 0 && iter > max_iterations) break;
423 hasChange = false;
424 // printf("presolve iteration %d (max=%d)\n", iter, max_iterations);
425 reportDevMainLoop();
426 timer.recordStart(RUN_PRESOLVERS);
427 int run_status = runPresolvers(order);
428 timer.recordFinish(RUN_PRESOLVERS);
429 assert(run_status == status);
430 if (run_status) return status;
431
432 // Exit check
433 prev_cols_rows = current_cols_rows;
434 current_cols_rows = 0;
435 int current_num_col = 0;
436 int current_num_row = 0;
437 for (int i = 0; i < numRow; i++)
438 if (flagRow[i]) current_cols_rows++;
439 current_num_row = current_cols_rows;
440 for (int i = 0; i < numCol; i++)
441 if (flagCol[i]) current_cols_rows++;
442 current_num_col = current_cols_rows - current_num_row;
443 int diff = prev_cols_rows - current_cols_rows;
444 double iteration_reduction_pct =
445 100 * (1.0 * diff) / (1.0 * model_cols_rows);
446 HighsPrintMessage(
447 output, message_level, ML_VERBOSE,
448 // printf(
449 "Iteration %2d (Presolve) Current number rows = %9d; cols = %9d: "
450 "Reduction this iteration (%9d) is %5.2f%%\n",
451 iter, current_num_row, current_num_col, diff, iteration_reduction_pct);
452 if (current_cols_rows == 0) break;
453 iter++;
454
455 if (hasChange) {
456 if (iteration_reduction_pct <
457 reduction_pct_for_further_presolve_iteration)
458 hasChange = false;
459 }
460
461 if (!hasChange && !aggregatorCalled) {
462 aggregatorCalled = true;
463 runAggregator();
464 if (mip) detectImpliedIntegers();
465 runPropagator();
466 prev_cols_rows = current_cols_rows;
467 current_cols_rows = 0;
468 int current_num_col = 0;
469 int current_num_row = 0;
470 for (int i = 0; i < numRow; i++)
471 if (flagRow[i]) current_num_row++;
472 for (int i = 0; i < numCol; i++)
473 if (flagCol[i]) current_num_col++;
474 current_cols_rows = current_num_col + current_num_row;
475 int diff = prev_cols_rows - current_cols_rows;
476 double iteration_reduction_pct =
477 100 * (1.0 * diff) / (1.0 * model_cols_rows);
478 HighsPrintMessage(
479 output, message_level, ML_VERBOSE,
480 // printf(
481 "Iteration %2d (Aggregator) Current number rows = %9d; cols = %9d: "
482 "Reduction this iteration (%9d) is %5.2f%%\n",
483 iter, current_num_row, current_num_col, diff,
484 iteration_reduction_pct);
485 iter++;
486 }
487 }
488
489 // std::cout << " MAIN LOOP ITER = " << iter << std::endl;
490
491 reportDevMainLoop();
492
493 timer.recordStart(RESIZE_MATRIX);
494 checkForChanges(iter);
495 timer.recordFinish(RESIZE_MATRIX);
496
497 timer.updateInfo();
498
499 if (iPrint != 0) printDevStats(stats.dev);
500
501 return status;
502 }
503
presolve()504 HighsPresolveStatus Presolve::presolve() {
505 timer.recordStart(TOTAL_PRESOLVE_TIME);
506 HighsPresolveStatus presolve_status = HighsPresolveStatus::NotReduced;
507 int result = presolve(0);
508 switch (result) {
509 case stat::Unbounded:
510 presolve_status = HighsPresolveStatus::Unbounded;
511 break;
512 case stat::Infeasible:
513 presolve_status = HighsPresolveStatus::Infeasible;
514 break;
515 case stat::Reduced:
516 if (numCol > 0 || numRow > 0)
517 presolve_status = HighsPresolveStatus::Reduced;
518 else
519 presolve_status = HighsPresolveStatus::ReducedToEmpty;
520 break;
521 case stat::Empty:
522 presolve_status = HighsPresolveStatus::Empty;
523 break;
524 case stat::Optimal:
525 // reduced problem solution indicated as optimal by
526 // the solver.
527 break;
528 case stat::Timeout:
529 presolve_status = HighsPresolveStatus::Timeout;
530 }
531 timer.recordFinish(TOTAL_PRESOLVE_TIME);
532 if (iPrint > 0) {
533 timer.reportClocks();
534 timer.reportNumericsRecords();
535 }
536 return presolve_status;
537 }
538
checkBoundsAreConsistent()539 void Presolve::checkBoundsAreConsistent() {
540 for (int col = 0; col < numCol; col++) {
541 if (flagCol[col]) {
542 // Analyse dependency on numerical tolerance
543 timer.updateNumericsRecord(INCONSISTENT_BOUNDS,
544 colLower[col] - colUpper[col]);
545 roundIntegerBounds(col);
546 if (colLower[col] - colUpper[col] > inconsistent_bounds_tolerance) {
547 status = Infeasible;
548 return;
549 }
550 }
551 }
552
553 for (int row = 0; row < numRow; row++) {
554 if (flagRow[row]) {
555 // Analyse dependency on numerical tolerance
556 timer.updateNumericsRecord(INCONSISTENT_BOUNDS,
557 rowLower[row] - rowUpper[row]);
558 if (rowLower[row] - rowUpper[row] > inconsistent_bounds_tolerance) {
559 status = Infeasible;
560 return;
561 }
562 }
563 }
564 }
565
566 /**
567 * returns <x, y>
568 * <x, -1> if we need to skip row
569 *
570 * row is of form akx_x + aky_y = b,
571 */
getXYDoubletonEquations(const int row)572 pair<int, int> Presolve::getXYDoubletonEquations(const int row) {
573 // todo, for mip presolve also check integrality of right hand side value to
574 // detect integer feasibility
575 pair<int, int> colIndex;
576 // row is of form akx_x + aky_y = b, where k=row and y is present in fewer
577 // constraints
578
579 int col1 = -1;
580 int col2 = -1;
581 double val1 = 0.0;
582 double val2 = 0.0;
583 int kk = ARstart.at(row);
584 while (kk < ARstart.at(row + 1)) {
585 if (flagCol.at(ARindex.at(kk))) {
586 if (col1 == -1) {
587 col1 = ARindex.at(kk);
588 val1 = std::abs(ARvalue[kk]);
589 } else if (col2 == -1) {
590 col2 = ARindex.at(kk);
591 val2 = std::abs(ARvalue[kk]);
592 } else {
593 cout << "ERROR: doubleton eq row" << row
594 << " has more than two variables. \n";
595 col2 = -2;
596 break;
597 }
598 ++kk;
599 } else
600 ++kk;
601 }
602 if (col2 == -1)
603 cout << "ERROR: doubleton eq row" << row
604 << " has less than two variables. \n";
605 if (col2 < 0) {
606 colIndex.second = -1;
607 return colIndex;
608 }
609
610 int x, y;
611 if (mip && (integrality[col1] == HighsVarType::INTEGER ||
612 integrality[col2] == HighsVarType::INTEGER)) {
613 if (integrality[col1] != integrality[col2]) {
614 // only one of the columns is integral, select the non-integral column to
615 // be substituted out
616 if (integrality[col1] == HighsVarType::INTEGER) {
617 // printf("column %d integral, column %d is not\n", col1, col2);
618 assert(integrality[col2] != HighsVarType::INTEGER);
619 y = col2;
620 x = col1;
621 } else {
622 // printf("column %d integral, column %d is not\n", col2, col1);
623 assert(integrality[col2] == HighsVarType::INTEGER);
624 assert(integrality[col1] != HighsVarType::INTEGER);
625 y = col1;
626 x = col2;
627 }
628 } else {
629 // printf(
630 // "column %d with coefficient %f is integral and column %d with "
631 // "coefficient %f too\n",
632 // col1, val1, col2, val2);
633 // both columns are integral, need to choose column with smaller
634 // coefficient value, if coefficients are the same choose the column with
635 // fewer nonzeros
636 if (val1 < val2) {
637 y = col1;
638 x = col2;
639 } else if (val2 < val1) {
640 y = col2;
641 x = col1;
642 } else if (nzCol.at(col1) <= nzCol.at(col2)) {
643 y = col1;
644 x = col2;
645 } else {
646 x = col1;
647 y = col2;
648 }
649 }
650 } else if (nzCol.at(col1) <= nzCol.at(col2)) {
651 y = col1;
652 x = col2;
653 } else {
654 x = col1;
655 y = col2;
656 }
657
658 colIndex.first = x;
659 colIndex.second = y;
660 return colIndex;
661 }
662
processRowDoubletonEquation(const int row,const int x,const int y,const double akx,const double aky,const double b)663 void Presolve::processRowDoubletonEquation(const int row, const int x,
664 const int y, const double akx,
665 const double aky, const double b) {
666 // std::cout << "col 2... c = " << colCost.at(2)<< std::endl;
667 // presolve::printCol(2, numRow, numCol, flagRow, flagCol, colLower,
668 // colUpper, valueRowDual, Astart, Aend, Aindex, Avalue);
669
670 postValue.push(akx);
671 postValue.push(aky);
672 postValue.push(b);
673
674 // modify bounds on variable x (j), variable y (col,k) is substituted out
675 // double aik = Avalue.at(k);
676 // double aij = Avalue.at(kk);
677 pair<double, double> p = getNewBoundsDoubletonConstraint(row, y, x, aky, akx);
678 double low = p.first;
679 double upp = p.second;
680
681 // add old bounds of x to checker and for postsolve
682 if (iKKTcheck == 1) {
683 vector<pair<int, double>> bndsL, bndsU, costS;
684 bndsL.push_back(make_pair(x, colLower.at(x)));
685 bndsU.push_back(make_pair(x, colUpper.at(x)));
686 costS.push_back(make_pair(x, colCost.at(x)));
687
688 chk2.cLowers.push(bndsL);
689 chk2.cUppers.push(bndsU);
690 chk2.costs.push(costS);
691 }
692
693 vector<double> bnds({colLower.at(y), colUpper.at(y), colCost.at(y)});
694 vector<double> bnds2({colLower.at(x), colUpper.at(x), colCost.at(x)});
695 oldBounds.push(make_pair(y, bnds));
696 oldBounds.push(make_pair(x, bnds2));
697
698 if (low > colLower.at(x)) colLower.at(x) = low;
699 if (upp < colUpper.at(x)) colUpper.at(x) = upp;
700
701 roundIntegerBounds(x);
702
703 // modify cost of xj
704 colCost.at(x) = colCost.at(x) - colCost.at(y) * akx / aky;
705 objShift += colCost.at(y) * b / aky;
706
707 // for postsolve: need the new bounds too
708 assert(x >= 0 && x < numCol);
709 vector<double> bnds3({colLower.at(x), colUpper.at(x), colCost.at(x)});
710 oldBounds.push(make_pair(x, bnds3));
711
712 addChange(DOUBLETON_EQUATION, row, y);
713
714 // remove y (col) and the row
715 if (iPrint > 0)
716 cout << "PR: Doubleton equation removed. Row " << row << ", column " << y
717 << ", column left is " << x << " nzy=" << nzCol.at(y) << endl;
718
719 flagRow.at(row) = 0;
720 nzCol.at(x)--;
721
722 countRemovedRows(DOUBLETON_EQUATION);
723 countRemovedCols(DOUBLETON_EQUATION);
724
725 //----------------------------
726 flagCol.at(y) = 0;
727 if (!hasChange) hasChange = true;
728 }
729
caseTwoSingletonsDoubletonInequality(const int row,const int x,const int y)730 void Presolve::caseTwoSingletonsDoubletonInequality(const int row, const int x,
731 const int y) {
732 // std::cout << "Call caseTwoSing..." << std::endl;
733
734 // std::cout << "Two column singletons: row " << row << ", x = " << x << ", y
735 // = " << y << std::endl; std::cout << " cx = " <<
736 // colCost[x] << " cy = " << colCost[y] << std::endl; std::cout << " ax = "
737 // << getaij(row, x) << " ay = " << getaij(row, y) << std::endl; std::cout <<
738 // " L = " << rowLower[row] << " U = " << rowUpper[row] << std::endl;
739 // std::cout << " lx = " << colLower[x] << " ux = " << colUpper[x] <<
740 // std::endl; std::cout << " ly = " << colLower[y] << " uy = " <<
741 // colUpper[y] << std::endl;
742
743 assert(nzRow[row] = 2);
744 assert(nzCol[x] = 1);
745 assert(nzCol[y] = 1);
746
747 assert(flagCol[x]);
748 assert(flagCol[y]);
749 assert(flagRow[row]);
750
751 // trivial case
752 // if(rowLower[row] == 0 && rowUpper[row] == 0) {
753 // if ((colLower[x] <= 0 && colUpper[x] >= 0) &&
754 // (colLower[y] <= 0 && colUpper[y] >= 0)) {
755
756 // // primal and dual values set to 0 already. just flagRow
757 // flagRow[row] = false;
758 // flagCol[x] = false;
759 // flagCol[y] = false;
760 // postValue.push((double)y);
761 // addChange(PresolveRule::TWO_COL_SING_TRIVIAL, row, x);
762 // std::cout << "Trivial case row " << row << std::endl;
763 // }
764 // }
765 }
766
removeDoubletonEquations()767 void Presolve::removeDoubletonEquations() {
768 if (timer.reachLimit()) {
769 status = stat::Timeout;
770 return;
771 }
772 timer.recordStart(DOUBLETON_EQUATION);
773 // flagCol should have one more element at end which is zero
774 // needed for AR matrix manipulation
775 if ((int)flagCol.size() == numCol) flagCol.push_back(0);
776
777 int iter = 0;
778
779 for (int row = 0; row < numRow; row++) {
780 if (flagRow.at(row)) {
781 // Analyse dependency on numerical tolerance
782 if (nzRow.at(row) == 2 && rowLower[row] > -HIGHS_CONST_INF &&
783 rowUpper[row] < HIGHS_CONST_INF) {
784 // Possible doubleton equation
785 timer.updateNumericsRecord(DOUBLETON_EQUATION_BOUND,
786 fabs(rowLower[row] - rowUpper[row]));
787 }
788 if (nzRow.at(row) == 2 && rowLower[row] > -HIGHS_CONST_INF &&
789 rowUpper[row] < HIGHS_CONST_INF &&
790 fabs(rowLower[row] - rowUpper[row]) <=
791 doubleton_equation_bound_tolerance) {
792 // row is of form akx_x + aky_y = b, where k=row and y is present in
793 // fewer constraints
794 const double b = rowLower.at(row);
795 pair<int, int> colIndex = getXYDoubletonEquations(row);
796 const int x = colIndex.first;
797 const int y = colIndex.second;
798
799 if (x >= 0 && y == -1) {
800 // no second variable
801 nzRow[row]--;
802 continue;
803 }
804
805 // two singletons case handled elsewhere
806 if (y < 0 || ((nzCol.at(y) == 1 && nzCol.at(x) == 1))) {
807 caseTwoSingletonsDoubletonInequality(row, x, y);
808 continue;
809 }
810
811 // singleton rows only in y column which is present in fewer constraints
812 // and eliminated. bool rs_only = true; for (int k = Astart.at(y); k <
813 // Aend.at(y); ++k)
814 // if (flagRow.at(Aindex.at(k)) && Aindex.at(k) != row) {
815 // if (nzRow[row] > 1) {
816 // rs_only = false;
817 // break;
818 // }
819 // }
820 // if (rs_only) continue;
821
822 const double akx = getaij(row, x);
823 const double aky = getaij(row, y);
824 processRowDoubletonEquation(row, x, y, akx, aky, b);
825 if (status) {
826 timer.recordFinish(DOUBLETON_EQUATION);
827 return;
828 }
829
830 // printRow(row, numRow, numCol, flagRow, flagCol, rowLower, rowUpper,
831 // valuePrimal, ARstart, ARindex, ARvalue);
832
833 for (int k = Astart.at(y); k < Aend.at(y); ++k)
834 if (flagRow.at(Aindex.at(k)) && Aindex.at(k) != row) {
835 const int i = Aindex.at(k);
836 const double aiy = Avalue.at(k);
837
838 // update row bounds
839 if (iKKTcheck == 1) {
840 vector<pair<int, double>> bndsL, bndsU;
841 bndsL.push_back(make_pair(i, rowLower.at(i)));
842 bndsU.push_back(make_pair(i, rowUpper.at(i)));
843 chk2.rLowers.push(bndsL);
844 chk2.rUppers.push(bndsU);
845 addChange(DOUBLETON_EQUATION_ROW_BOUNDS_UPDATE, i, y);
846 }
847
848 if (rowLower.at(i) > -HIGHS_CONST_INF)
849 rowLower.at(i) -= b * aiy / aky;
850 if (rowUpper.at(i) < HIGHS_CONST_INF)
851 rowUpper.at(i) -= b * aiy / aky;
852
853 if (implRowValueLower.at(i) > -HIGHS_CONST_INF)
854 implRowValueLower.at(i) -= b * aiy / aky;
855 if (implRowValueUpper.at(i) < HIGHS_CONST_INF)
856 implRowValueUpper.at(i) -= b * aiy / aky;
857
858 // update matrix coefficients
859 if (isZeroA(i, x)) {
860 UpdateMatrixCoeffDoubletonEquationXzero(i, x, y, aiy, akx, aky);
861 // std::cout << " . row " << i << " zero " << std::endl;
862 } else {
863 UpdateMatrixCoeffDoubletonEquationXnonZero(i, x, y, aiy, akx,
864 aky);
865 // std::cout << " . row " << i << " zero " << std::endl;
866 }
867 }
868 if (Avalue.size() > 40000000) {
869 trimA();
870 }
871
872 iter++;
873 }
874 }
875 }
876 timer.recordFinish(DOUBLETON_EQUATION);
877 }
878
UpdateMatrixCoeffDoubletonEquationXzero(const int i,const int x,const int y,const double aiy,const double akx,const double aky)879 void Presolve::UpdateMatrixCoeffDoubletonEquationXzero(const int i, const int x,
880 const int y,
881 const double aiy,
882 const double akx,
883 const double aky) {
884 // case x is zero initially
885 // row nonzero count doesn't change here
886 // cout<<"case: x not present "<<i<<" "<<endl;
887
888 // update AR
889 int ind;
890 for (ind = ARstart.at(i); ind < ARstart.at(i + 1); ++ind)
891 if (ARindex.at(ind) == y) {
892 break;
893 }
894
895 assert(ARvalue.at(ind) == aiy);
896
897 postValue.push(aiy);
898 postValue.push(y);
899 addChange(DOUBLETON_EQUATION_X_ZERO_INITIALLY, i, x);
900
901 ARindex.at(ind) = x;
902 ARvalue.at(ind) = -aiy * akx / aky;
903
904 // update A: append X column to end of array
905 const int st = Avalue.size();
906 for (int ind = Astart.at(x); ind < Aend.at(x); ++ind) {
907 Avalue.push_back(Avalue.at(ind));
908 Aindex.push_back(Aindex.at(ind));
909 }
910 Avalue.push_back(-aiy * akx / aky);
911 Aindex.push_back(i);
912 Astart.at(x) = st;
913 Aend.at(x) = Avalue.size();
914
915 nzCol.at(x)++;
916 // nzRow does not change here.
917 }
918
UpdateMatrixCoeffDoubletonEquationXnonZero(const int i,const int x,const int y,const double aiy,const double akx,const double aky)919 void Presolve::UpdateMatrixCoeffDoubletonEquationXnonZero(
920 const int i, const int x, const int y, const double aiy, const double akx,
921 const double aky) {
922 int ind;
923
924 // update nonzeros: for removal of
925 nzRow.at(i)--;
926 if (nzRow.at(i) == 1) singRow.push_back(i);
927
928 if (nzRow.at(i) == 0) {
929 // singRow.remove(i);
930 removeEmptyRow(i);
931 countRemovedRows(DOUBLETON_EQUATION);
932 }
933
934 double xNew;
935 for (ind = ARstart.at(i); ind < ARstart.at(i + 1); ++ind)
936 if (ARindex.at(ind) == x) break;
937
938 xNew = ARvalue.at(ind) - (aiy * akx) / aky;
939 // Analyse dependency on numerical tolerance
940 timer.updateNumericsRecord(SMALL_MATRIX_VALUE, fabs(xNew));
941 if (fabs(xNew) > presolve_small_matrix_value) {
942 // case new x != 0
943 // cout<<"case: x still there row "<<i<<" "<<endl;
944
945 postValue.push(ARvalue.at(ind));
946 addChange(DOUBLETON_EQUATION_NEW_X_NONZERO, i, x);
947 ARvalue.at(ind) = xNew;
948
949 // update A:
950 for (ind = Astart.at(x); ind < Aend.at(x); ++ind)
951 if (Aindex.at(ind) == i) {
952 break;
953 }
954 Avalue.at(ind) = xNew;
955 } else {
956 // case new x == 0
957 // cout<<"case: x also disappears from row "<<i<<" "<<endl;
958 // update nz row
959 nzRow.at(i)--;
960 // update singleton row list
961 if (nzRow.at(i) == 1) singRow.push_back(i);
962
963 if (nzRow.at(i) == 0) {
964 removeEmptyRow(i);
965 countRemovedRows(DOUBLETON_EQUATION);
966 }
967
968 if (nzRow.at(i) > 0) {
969 // AR update
970 // set ARindex of element for x to numCol
971 // flagCol[numCol] = false
972 // mind when resizing: should be OK
973 postValue.push(ARvalue.at(ind));
974
975 ARindex.at(ind) = numCol;
976
977 addChange(DOUBLETON_EQUATION_NEW_X_ZERO_AR_UPDATE, i, x);
978 }
979
980 if (nzCol.at(x) > 0) {
981 // A update for case when x is zero: move x entry to end and set
982 // Aend to be Aend - 1;
983 int indi;
984 for (indi = Astart.at(x); indi < Aend.at(x); ++indi)
985 if (Aindex.at(indi) == i) break;
986
987 postValue.push(Avalue.at(indi));
988
989 // if indi is not Aend-1 swap elements indi and Aend-1
990 if (indi != Aend.at(x) - 1) {
991 double tmp = Avalue.at(Aend.at(x) - 1);
992 int tmpi = Aindex.at(Aend.at(x) - 1);
993 Avalue.at(Aend.at(x) - 1) = Avalue.at(indi);
994 Aindex.at(Aend.at(x) - 1) = Aindex.at(indi);
995 Avalue.at(indi) = tmp;
996 Aindex.at(indi) = tmpi;
997 }
998 Aend.at(x)--;
999 addChange(DOUBLETON_EQUATION_NEW_X_ZERO_A_UPDATE, i, x);
1000 }
1001
1002 // update nz col
1003 nzCol.at(x)--;
1004 // update singleton col list
1005 if (nzCol.at(x) == 1) singCol.push_back(x);
1006 if (nzCol.at(x) == 0) {
1007 removeEmptyColumn(x);
1008 }
1009 }
1010 }
1011
trimA()1012 void Presolve::trimA() {
1013 int cntEl = 0;
1014 for (int j = 0; j < numCol; ++j)
1015 if (flagCol.at(j)) cntEl += nzCol.at(j);
1016
1017 vector<pair<int, size_t>> vp;
1018 vp.reserve(numCol);
1019
1020 for (int i = 0; i != numCol; ++i) {
1021 vp.push_back(make_pair(Astart.at(i), i));
1022 }
1023
1024 // Sorting will put lower values ahead of larger ones,
1025 // resolving ties using the original index
1026 sort(vp.begin(), vp.end());
1027
1028 vector<int> Aendtmp;
1029 Aendtmp = Aend;
1030
1031 int iPut = 0;
1032 for (size_t i = 0; i != vp.size(); ++i) {
1033 int col = vp.at(i).second;
1034 if (flagCol.at(col)) {
1035 int k = vp.at(i).first;
1036 Astart.at(col) = iPut;
1037 while (k < Aendtmp.at(col)) {
1038 if (flagRow.at(Aindex.at(k))) {
1039 Avalue[iPut] = Avalue.at(k);
1040 Aindex[iPut] = Aindex.at(k);
1041 iPut++;
1042 }
1043 k++;
1044 }
1045 Aend.at(col) = iPut;
1046 }
1047 }
1048 Avalue.resize(iPut);
1049 Aindex.resize(iPut);
1050 }
1051
resizeProblem()1052 void Presolve::resizeProblem() {
1053 int nz = 0;
1054 int nR = 0;
1055 int nC = 0;
1056
1057 // arrays to keep track of indices
1058 rIndex.assign(numRow, -1);
1059 cIndex.assign(numCol, -1);
1060
1061 for (int i = 0; i < numRow; ++i)
1062 if (flagRow.at(i)) {
1063 nz += nzRow.at(i);
1064 rIndex.at(i) = nR;
1065 nR++;
1066 }
1067
1068 for (int i = 0; i < numCol; ++i)
1069 if (flagCol.at(i)) {
1070 cIndex.at(i) = nC;
1071 nC++;
1072 }
1073
1074 // counts
1075 numRowOriginal = numRow;
1076 numColOriginal = numCol;
1077 numRow = nR;
1078 numCol = nC;
1079 numTot = nR + nC;
1080
1081 if (iPrint < 0) {
1082 stringstream ss;
1083 ss << ", Reduced : " << numRow << ", " << numCol << ", ";
1084 reportDev(ss.str());
1085 }
1086
1087 chk2.setBoundsCostRHS(colUpper, colLower, colCost, rowLower, rowUpper);
1088
1089 if (nR + nC == 0) {
1090 status = Empty;
1091 return;
1092 }
1093
1094 // matrix
1095 vector<int> iwork(numCol, 0);
1096 Astart.assign(numCol + 1, 0);
1097 Aend.assign(numCol + 1, 0);
1098 Aindex.resize(nz);
1099 Avalue.resize(nz);
1100
1101 for (int i = 0; i < numRowOriginal; ++i)
1102 if (flagRow.at(i))
1103 for (int k = ARstart.at(i); k < ARstart.at(i + 1); ++k) {
1104 const int j = ARindex.at(k);
1105 if (flagCol.at(j)) iwork.at(cIndex.at(j))++;
1106 }
1107
1108 for (int i = 1; i <= numCol; ++i)
1109 Astart.at(i) = Astart.at(i - 1) + iwork.at(i - 1);
1110 for (int i = 0; i < numCol; ++i) iwork.at(i) = Aend.at(i) = Astart.at(i);
1111 for (int i = 0; i < numRowOriginal; ++i) {
1112 if (flagRow.at(i)) {
1113 int iRow = rIndex.at(i);
1114 for (int k = ARstart.at(i); k < ARstart.at(i + 1); ++k) {
1115 const int j = ARindex.at(k);
1116 if (flagCol.at(j)) {
1117 int iCol = cIndex.at(j);
1118 int iPut = iwork.at(iCol)++;
1119 Aindex.at(iPut) = iRow;
1120 Avalue.at(iPut) = ARvalue.at(k);
1121 }
1122 }
1123 }
1124 }
1125
1126 if (iPrint < 0) {
1127 stringstream ss;
1128 ss << Avalue.size() << ", ";
1129 reportDev(ss.str());
1130 }
1131
1132 // also call before trimming
1133 resizeImpliedBounds();
1134
1135 // cost, bounds
1136 colCostAtEl = colCost;
1137 vector<double> tempCost = colCost;
1138 vector<double> temp = colLower;
1139 vector<double> teup = colUpper;
1140 vector<HighsVarType> tempintegrality = integrality;
1141
1142 colCost.resize(numCol);
1143 colLower.resize(numCol);
1144 colUpper.resize(numCol);
1145 integrality.resize(numCol);
1146
1147 int k = 0;
1148 for (int i = 0; i < numColOriginal; ++i)
1149 if (flagCol.at(i)) {
1150 colCost.at(k) = tempCost.at(i);
1151 colLower.at(k) = temp.at(i);
1152 colUpper.at(k) = teup.at(i);
1153 integrality.at(k) = tempintegrality.at(i);
1154 k++;
1155 }
1156
1157 // RHS and bounds
1158 temp = rowLower;
1159 teup = rowUpper;
1160 rowLower.resize(numRow);
1161 rowUpper.resize(numRow);
1162 k = 0;
1163 for (int i = 0; i < numRowOriginal; ++i)
1164 if (flagRow.at(i)) {
1165 rowLower.at(k) = temp.at(i);
1166 rowUpper.at(k) = teup.at(i);
1167 k++;
1168 }
1169 }
1170
initializeVectors()1171 void Presolve::initializeVectors() {
1172 // copy original bounds
1173 colCostOriginal = colCost;
1174 rowUpperOriginal = rowUpper;
1175 rowLowerOriginal = rowLower;
1176 colUpperOriginal = colUpper;
1177 colLowerOriginal = colLower;
1178
1179 makeARCopy();
1180
1181 valueRowDual.resize(numRow);
1182 valuePrimal.resize(numCol);
1183 valueColDual.resize(numCol);
1184
1185 flagCol.assign(numCol, 1);
1186 flagRow.assign(numRow, 1);
1187
1188 if (iKKTcheck) setKKTcheckerData();
1189
1190 nzCol.assign(numCol, 0);
1191 nzRow.assign(numRow, 0);
1192
1193 for (int i = 0; i < numRow; ++i) {
1194 nzRow.at(i) = ARstart.at(i + 1) - ARstart.at(i);
1195 if (nzRow.at(i) == 1) singRow.push_back(i);
1196 if (nzRow.at(i) == 0) {
1197 timer.recordStart(EMPTY_ROW);
1198 removeEmptyRow(i);
1199 countRemovedRows(EMPTY_ROW);
1200 timer.recordFinish(EMPTY_ROW);
1201 }
1202 }
1203
1204 Aend.resize(numCol + 1);
1205 for (int i = 0; i < numCol; ++i) {
1206 Aend.at(i) = Astart.at(i + 1);
1207 nzCol.at(i) = Aend.at(i) - Astart.at(i);
1208 if (nzCol.at(i) == 1) singCol.push_back(i);
1209 }
1210
1211 implColUpper = colUpper; // working copies of primal variable bounds
1212 implColLower = colLower;
1213 implColLowerRowIndex.assign(numCol, -1);
1214 implColUpperRowIndex.assign(numCol, -1);
1215
1216 implRowDualLowerSingColRowIndex.assign(numRow, -1);
1217 implRowDualUpperSingColRowIndex.assign(numRow, -1);
1218 implRowDualLower.assign(numRow, -HIGHS_CONST_INF);
1219 implRowDualUpper.assign(numRow, HIGHS_CONST_INF);
1220
1221 implColDualLower.assign(numCol, -HIGHS_CONST_INF);
1222 implColDualUpper.assign(numCol, HIGHS_CONST_INF);
1223 implRowValueLower = rowLower;
1224 implRowValueUpper = rowUpper;
1225
1226 for (int i = 0; i < numRow; ++i) {
1227 if (rowLower.at(i) <= -HIGHS_CONST_INF) implRowDualUpper.at(i) = 0;
1228 if (rowUpper.at(i) >= HIGHS_CONST_INF) implRowDualLower.at(i) = 0;
1229 }
1230
1231 for (int i = 0; i < numCol; ++i) {
1232 if (colLower.at(i) <= -HIGHS_CONST_INF) implColDualUpper.at(i) = 0;
1233 if (colUpper.at(i) >= HIGHS_CONST_INF) implColDualLower.at(i) = 0;
1234 }
1235
1236 colCostAtEl = colCost;
1237
1238 // initialize integrality information
1239 if (!mip) integrality.assign(numCol, HighsVarType::CONTINUOUS);
1240 }
1241
runAggregator()1242 void Presolve::runAggregator() {
1243 // run the aggregator and store back the modified matrix
1244 timer.recordStart(AGGREGATOR);
1245 aggregatorStack.emplace_back();
1246
1247 aggregatorStack.back().colCostAtCall = colCost;
1248
1249 {
1250 HAggregator aggregator(rowLower, rowUpper, colCost, objShift, integrality,
1251 colLower, colUpper);
1252
1253 aggregator.fromDynamicCSC(Avalue, Aindex, Astart, Aend, flagRow, flagCol);
1254 aggregatorStack.back().postsolveStack = aggregator.run();
1255
1256 if (aggregatorStack.back().postsolveStack.empty()) {
1257 if (mip) {
1258 // todo, we need to store the state of the matrix before
1259 // doing mip presolve steps that alter the coefficients,
1260 // currently only coefficient tightening.
1261 // this is a bit hacky but works for now as those steps
1262 // run after the first call of the aggregator
1263 aggregatorStack.back().ARvalueAtCall = std::move(ARvalue);
1264 aggregatorStack.back().ARindexAtCall = std::move(ARindex);
1265 aggregatorStack.back().ARstartAtCall = std::move(ARstart);
1266 aggregator.toCSC(Avalue, Aindex, Astart);
1267 makeARCopy();
1268 Aend.resize(numCol + 1);
1269 std::copy(Astart.begin() + 1, Astart.end(), Aend.begin());
1270 chng.emplace(change{AGGREGATOR, 0, 0});
1271 } else
1272 aggregatorStack.pop_back();
1273 timer.recordFinish(AGGREGATOR);
1274 return;
1275 }
1276 aggregator.toCSC(Avalue, Aindex, Astart);
1277 }
1278
1279 hasChange = true;
1280 chng.emplace(change{AGGREGATOR, 0, 0});
1281
1282 aggregatorStack.back().postsolveStack.unsetFlags(flagRow, flagCol);
1283
1284 // store old AR and create updated AR matrix
1285 aggregatorStack.back().ARvalueAtCall = std::move(ARvalue);
1286 aggregatorStack.back().ARindexAtCall = std::move(ARindex);
1287 aggregatorStack.back().ARstartAtCall = std::move(ARstart);
1288 makeARCopy();
1289
1290 // set nzRow and nzCol, remove empty rows
1291 nzCol.assign(numCol, 0);
1292 nzRow.assign(numRow, 0);
1293
1294 for (int i = 0; i < numRow; ++i) {
1295 if (!flagRow[i]) continue;
1296 nzRow.at(i) = ARstart.at(i + 1) - ARstart.at(i);
1297 if (nzRow.at(i) == 1) singRow.push_back(i);
1298 if (nzRow.at(i) == 0) {
1299 timer.recordStart(EMPTY_ROW);
1300 removeEmptyRow(i);
1301 countRemovedRows(EMPTY_ROW);
1302 timer.recordFinish(EMPTY_ROW);
1303 }
1304 }
1305
1306 Aend.resize(numCol + 1);
1307 for (int i = 0; i < numCol; ++i) {
1308 if (!flagCol[i]) continue;
1309 Aend.at(i) = Astart.at(i + 1);
1310 nzCol.at(i) = Aend.at(i) - Astart.at(i);
1311 if (nzCol.at(i) == 1) singCol.push_back(i);
1312 }
1313
1314 implColUpper = colUpper; // working copies of primal variable bounds
1315 implColLower = colLower;
1316 implColLowerRowIndex.assign(numCol, -1);
1317 implColUpperRowIndex.assign(numCol, -1);
1318
1319 implRowDualLowerSingColRowIndex.assign(numRow, -1);
1320 implRowDualUpperSingColRowIndex.assign(numRow, -1);
1321 implRowDualLower.assign(numRow, -HIGHS_CONST_INF);
1322 implRowDualUpper.assign(numRow, HIGHS_CONST_INF);
1323
1324 implColDualLower.assign(numCol, -HIGHS_CONST_INF);
1325 implColDualUpper.assign(numCol, HIGHS_CONST_INF);
1326 implRowValueLower = rowLower;
1327 implRowValueUpper = rowUpper;
1328
1329 for (int i = 0; i < numRow; ++i) {
1330 if (rowLower.at(i) <= -HIGHS_CONST_INF) implRowDualUpper.at(i) = 0;
1331 if (rowUpper.at(i) >= HIGHS_CONST_INF) implRowDualLower.at(i) = 0;
1332 }
1333
1334 for (int i = 0; i < numCol; ++i) {
1335 if (colLower.at(i) <= -HIGHS_CONST_INF) implColDualUpper.at(i) = 0;
1336 if (colUpper.at(i) >= HIGHS_CONST_INF) implColDualLower.at(i) = 0;
1337 }
1338
1339 timer.recordFinish(AGGREGATOR);
1340 }
1341
runPropagator()1342 void Presolve::runPropagator() {
1343 // run the propagator to compute tightened bounds
1344 // which can help with other reductions like redundant rows or forcing
1345 // constraints and also decrease the number of total iterations required due
1346 // to their exploitation with the long-step rule for the dual simplex ratio
1347 // test
1348 HighsLpPropagator propagator(colLower, colUpper, integrality, Avalue, Aindex,
1349 Astart, Aend, ARvalue, ARindex, ARstart, flagRow,
1350 flagCol, rowLower, rowUpper);
1351 propagator.computeRowActivities();
1352 int nboundchgs = propagator.propagate();
1353 HighsPrintMessage(output, message_level, ML_VERBOSE,
1354 "propagation found %d bound changes\n", nboundchgs);
1355 // propagation found nothing, so we can stop here. Only for mip we also try
1356 // coefficient tightening
1357 if (!mip && nboundchgs == 0) return;
1358
1359 if (mip) {
1360 int ntotalcoeffchgs = 0;
1361 while (true) {
1362 int ncoeffchgs = propagator.tightenCoefficients();
1363 HighsPrintMessage(output, message_level, ML_VERBOSE,
1364 "tightened %d coefficients\n", ncoeffchgs);
1365 // if no coefficients where tightened we can stop
1366 if (ncoeffchgs == 0) break;
1367 ntotalcoeffchgs += ncoeffchgs;
1368
1369 hasChange = true;
1370 nboundchgs = propagator.propagate();
1371
1372 if (propagator.infeasible()) {
1373 status = Infeasible;
1374 return;
1375 }
1376 // if no further bounds where changed we can stop
1377 if (nboundchgs == 0) break;
1378 }
1379
1380 if (ntotalcoeffchgs != 0) {
1381 // row sides might have changed during coefficient tightening
1382 implRowValueLower = rowLower;
1383 implRowValueUpper = rowUpper;
1384 }
1385
1386 // no bounds where tightened, so we can return here
1387 if (propagator.getNumChangedBounds() == 0) return;
1388 }
1389
1390 int ntightened = 0;
1391 // we cannot use the tightest bounds that we obtained by propagation
1392 // as then the dual postsolve step might not work anymore. Instead
1393 // we relax the bounds by a wide enough margin so that they cannot
1394 // be used in a basic feasible solution
1395 for (int i = 0; i != numCol; ++i) {
1396 if (!flagCol[i]) continue;
1397
1398 if (colLower[i] >= propagator.colLower_[i] &&
1399 colUpper[i] <= propagator.colUpper_[i])
1400 continue;
1401
1402 if (mip) {
1403 if (colLower[i] < propagator.colLower_[i]) {
1404 colLower[i] = propagator.colLower_[i];
1405 ++ntightened;
1406 }
1407
1408 if (colUpper[i] > propagator.colUpper_[i]) {
1409 colUpper[i] = propagator.colUpper_[i];
1410 ++ntightened;
1411 }
1412
1413 roundIntegerBounds(i);
1414 if (std::abs(colUpper[i] - colLower[i]) <= fixed_column_tolerance)
1415 removeFixedCol(i);
1416
1417 continue;
1418 }
1419
1420 int start = Astart[i];
1421 int end = Aend[i];
1422 double minabs = 1.0;
1423
1424 for (int j = start; j != end; ++j) {
1425 int row = Aindex[j];
1426 if (!flagRow[row]) continue;
1427
1428 minabs = std::min(minabs, std::abs(Avalue[j]));
1429 }
1430
1431 // use a big enough factor of the feasibility tolerance and further increase
1432 // it if the column has coefficient values below 1.0 so that it is ensured
1433 // that no primal feasible solution can have this variable sitting at this
1434 // artifical bound
1435 double margin = 128.0 * default_primal_feasiblility_tolerance / minabs;
1436
1437 // now widen the bounds and check if they are tighter than the previous
1438 // bounds
1439 double abslb = std::abs(propagator.colLower_[i]);
1440
1441 if (abslb <= 1e8) {
1442 // use a relative minimal margin to ensure large bounds are relaxed enough
1443 double minlowermargin = default_primal_feasiblility_tolerance * abslb;
1444 propagator.colLower_[i] -= std::max(margin, minlowermargin);
1445 if (propagator.colLower_[i] > colLower[i]) {
1446 colLower[i] = propagator.colLower_[i];
1447 ++ntightened;
1448 }
1449 }
1450
1451 double absub = std::abs(propagator.colUpper_[i]);
1452
1453 if (absub <= 1e8) {
1454 // use a relative minimal margin to ensure large bounds are relaxed enough
1455 double minuppermargin = default_primal_feasiblility_tolerance * absub;
1456 propagator.colUpper_[i] += std::max(margin, minuppermargin);
1457 if (propagator.colUpper_[i] < colUpper[i]) {
1458 colUpper[i] = propagator.colUpper_[i];
1459 ++ntightened;
1460 }
1461 }
1462 }
1463
1464 implColLower = colLower;
1465 implColUpper = colUpper;
1466
1467 HighsPrintMessage(output, message_level, ML_VERBOSE, "tightened %d bounds\n ",
1468 ntightened);
1469 if (ntightened != 0) hasChange = true;
1470 }
1471
removeFixedCol(int j)1472 void Presolve::removeFixedCol(int j) {
1473 assert(std::isfinite(colUpper[j]));
1474 setPrimalValue(j, colUpper.at(j));
1475 addChange(FIXED_COL, 0, j);
1476 if (iPrint > 0)
1477 cout << "PR: Fixed variable " << j << " = " << colUpper.at(j)
1478 << ". Column eliminated." << endl;
1479
1480 countRemovedCols(FIXED_COL);
1481
1482 for (int k = Astart.at(j); k < Aend.at(j); ++k) {
1483 if (flagRow.at(Aindex.at(k))) {
1484 int i = Aindex.at(k);
1485
1486 if (nzRow.at(i) == 0) {
1487 removeEmptyRow(i);
1488 if (status == stat::Infeasible) return;
1489 countRemovedRows(FIXED_COL);
1490 }
1491 }
1492 }
1493 }
1494
detectImpliedIntegers()1495 void Presolve::detectImpliedIntegers() {
1496 std::vector<int> numcont(numRow);
1497 std::vector<int> equations;
1498 equations.reserve(numRow);
1499
1500 for (int i = 0; i != numRow; ++i) {
1501 if (!flagRow[i]) continue;
1502 if (rowLower[i] != rowUpper[i]) continue;
1503
1504 const int start = ARstart[i];
1505 const int end = ARstart[i + 1];
1506
1507 for (int j = start; j != end; ++j) {
1508 if (!flagCol[ARindex[j]]) continue;
1509 if (integrality[ARindex[j]] == HighsVarType::CONTINUOUS) ++numcont[i];
1510 }
1511
1512 if (numcont[i] == 1) equations.push_back(i);
1513 }
1514
1515 int numimplint = 0;
1516 int primalimplint;
1517
1518 for (size_t k = 0; k != equations.size(); ++k) {
1519 int i = equations[k];
1520 if (numcont[i] != 1) continue;
1521
1522 const int start = ARstart[i];
1523 const int end = ARstart[i + 1];
1524
1525 int cont = -1;
1526 for (int j = start; j != end; ++j) {
1527 if (!flagCol[ARindex[j]]) continue;
1528 if (integrality[ARindex[j]] == HighsVarType::CONTINUOUS) {
1529 cont = j;
1530 break;
1531 }
1532 }
1533
1534 assert(cont != -1);
1535 double b = rowUpper[i] / ARvalue[cont];
1536 if (std::abs(b - std::floor(b + 0.5)) > 1e-9) continue;
1537
1538 bool impliedint = true;
1539 for (int j = start; j != end; ++j) {
1540 if (j == cont) continue;
1541 if (!flagCol[ARindex[j]]) continue;
1542
1543 double val = ARvalue[j] / ARvalue[cont];
1544 if (std::abs(val - std::floor(val + 0.5)) > 1e-9) {
1545 impliedint = false;
1546 break;
1547 }
1548 }
1549
1550 if (!impliedint) continue;
1551
1552 int col = ARindex[cont];
1553 const int colstart = Astart[col];
1554 const int colend = Aend[col];
1555 integrality[col] = HighsVarType::IMPLICIT_INTEGER;
1556 roundIntegerBounds(col);
1557 ++numimplint;
1558
1559 for (int j = colstart; j != colend; ++j) {
1560 if (--numcont[Aindex[j]] == 1) {
1561 assert(rowLower[Aindex[j]] == rowUpper[Aindex[j]]);
1562 equations.push_back(Aindex[j]);
1563 }
1564 }
1565 }
1566
1567 HighsPrintMessage(output, message_level, ML_VERBOSE,
1568 "found %d implied integers with primal detection method\n",
1569 numimplint);
1570
1571 primalimplint = numimplint;
1572
1573 for (int i = 0; i != numCol; ++i) {
1574 if (!flagCol[i]) continue;
1575 if (integrality[i] != HighsVarType::CONTINUOUS) continue;
1576
1577 const int colstart = Astart[i];
1578 const int colend = Aend[i];
1579 bool haseq = false;
1580 for (int j = colstart; j != colend; ++j) {
1581 int row = Aindex[j];
1582 if (!flagRow[row]) continue;
1583 if (rowLower[row] == rowUpper[row]) {
1584 haseq = true;
1585 break;
1586 }
1587 }
1588
1589 if (haseq) continue;
1590
1591 bool impliedinteger = true;
1592 for (int j = colstart; j != colend; ++j) {
1593 int row = Aindex[j];
1594 if (!flagRow[row]) continue;
1595
1596 if (rowUpper[row] != HIGHS_CONST_INF) {
1597 double val = rowUpper[row] / Avalue[j];
1598
1599 if (std::abs(val - std::floor(val + 0.5)) > 1e-9) {
1600 impliedinteger = false;
1601 break;
1602 }
1603 }
1604
1605 if (rowLower[row] != -HIGHS_CONST_INF) {
1606 double val = rowLower[row] / Avalue[j];
1607
1608 if (std::abs(val - std::floor(val + 0.5)) > 1e-9) {
1609 impliedinteger = false;
1610 break;
1611 }
1612 }
1613
1614 const int start = ARstart[row];
1615 const int end = ARstart[row + 1];
1616 for (int k = start; k != end; ++k) {
1617 if (ARindex[k] == i) continue;
1618 if (!flagCol[ARindex[k]]) continue;
1619
1620 if (integrality[ARindex[k]] == HighsVarType::CONTINUOUS) {
1621 impliedinteger = false;
1622 break;
1623 }
1624
1625 double val = ARvalue[k] / Avalue[j];
1626 if (std::abs(val - std::floor(val + 0.5)) > 1e-9) {
1627 impliedinteger = false;
1628 break;
1629 }
1630 }
1631
1632 if (!impliedinteger) break;
1633 }
1634
1635 if (!impliedinteger) continue;
1636 integrality[i] = HighsVarType::IMPLICIT_INTEGER;
1637 roundIntegerBounds(i);
1638 ++numimplint;
1639 }
1640
1641 HighsPrintMessage(output, message_level, ML_VERBOSE,
1642 "found %d implied integers with dual detection method\n",
1643 numimplint - primalimplint);
1644
1645 HighsPrintMessage(output, message_level, ML_VERBOSE,
1646 "implint detection found %d implied integers\n",
1647 numimplint);
1648 }
1649
applyMipDualFixing()1650 void Presolve::applyMipDualFixing() {
1651 for (int i = 0; i != numCol; ++i) {
1652 if (!flagCol[i] || integrality[i] != HighsVarType::INTEGER) continue;
1653
1654 int start = Astart[i];
1655 int end = Aend[i];
1656 int nuplocks = 0;
1657 int ndownlocks = 0;
1658
1659 if (colCost[i] > 0 || colUpper[i] == HIGHS_CONST_INF) ++nuplocks;
1660
1661 if (colCost[i] < 0 || colLower[i] == -HIGHS_CONST_INF) ++ndownlocks;
1662
1663 if (ndownlocks != 0 && nuplocks != 0) continue;
1664
1665 for (int j = start; j != end; ++j) {
1666 int row = Aindex[j];
1667 if (!flagRow[row]) continue;
1668 double lower;
1669 double upper;
1670
1671 if (Avalue[j] < 0) {
1672 lower = -rowUpper[row];
1673 upper = -rowLower[row];
1674 } else {
1675 lower = rowLower[row];
1676 upper = rowUpper[row];
1677 }
1678
1679 if (lower != -HIGHS_CONST_INF) ++ndownlocks;
1680 if (upper != HIGHS_CONST_INF) ++nuplocks;
1681
1682 if (ndownlocks != 0 && nuplocks != 0) break;
1683 }
1684
1685 if (ndownlocks == 0) {
1686 colUpper[i] = colLower[i];
1687 removeFixedCol(i);
1688 timer.increaseCount(false, MIP_DUAL_FIXING);
1689 } else if (nuplocks == 0) {
1690 colLower[i] = colUpper[i];
1691 removeFixedCol(i);
1692 timer.increaseCount(false, MIP_DUAL_FIXING);
1693 }
1694 }
1695 }
1696
removeEmptyRow(int i)1697 void Presolve::removeEmptyRow(int i) {
1698 // Analyse dependency on numerical tolerance
1699 double value = min(rowLower.at(i), -rowUpper.at(i));
1700 timer.updateNumericsRecord(EMPTY_ROW_BOUND, value);
1701 if (rowLower.at(i) <= empty_row_bound_tolerance &&
1702 rowUpper.at(i) >= -empty_row_bound_tolerance) {
1703 if (iPrint > 0) cout << "PR: Empty row " << i << " removed. " << endl;
1704 flagRow.at(i) = 0;
1705 valueRowDual.at(i) = 0;
1706 addChange(EMPTY_ROW, i, 0);
1707 } else {
1708 if (iPrint > 0) cout << "PR: Problem infeasible." << endl;
1709 status = Infeasible;
1710 return;
1711 }
1712 }
1713
removeEmptyColumn(int j)1714 void Presolve::removeEmptyColumn(int j) {
1715 flagCol.at(j) = 0;
1716 // singCol.remove(j);
1717 double value;
1718 if ((colCost.at(j) < 0 && colUpper.at(j) >= HIGHS_CONST_INF) ||
1719 (colCost.at(j) > 0 && colLower.at(j) <= -HIGHS_CONST_INF)) {
1720 if (iPrint > 0) cout << "PR: Problem unbounded." << endl;
1721 status = Unbounded;
1722 return;
1723 }
1724
1725 if (colCost.at(j) > 0)
1726 value = colLower.at(j);
1727 else if (colCost.at(j) < 0)
1728 value = colUpper.at(j);
1729 else if (colUpper.at(j) >= 0 && colLower.at(j) <= 0)
1730 value = 0;
1731 else if (colUpper.at(j) < 0)
1732 value = colUpper.at(j);
1733 else
1734 value = colLower.at(j);
1735
1736 setPrimalValue(j, value);
1737 valueColDual.at(j) = colCost.at(j);
1738
1739 addChange(EMPTY_COL, 0, j);
1740
1741 if (iPrint > 0)
1742 cout << "PR: Column: " << j
1743 << " eliminated: all nonzero rows have been removed. Cost = "
1744 << colCost.at(j) << ", value = " << value << endl;
1745
1746 countRemovedCols(EMPTY_COL);
1747 }
1748
rowDualBoundsDominatedColumns()1749 void Presolve::rowDualBoundsDominatedColumns() {
1750 int col, i, k;
1751
1752 // todo, do not use integer variables to derive bounds on the row duals
1753 // using continous and implied integer variables is valid as the complementary
1754 // slackness condition holds when all integer columns are fixed to any integer
1755 // value
1756
1757 // for each row calc yihat and yibar and store in implRowDualLower and
1758 // implRowDualUpper
1759 for (list<int>::iterator it = singCol.begin(); it != singCol.end(); ++it)
1760 if (flagCol.at(*it)) {
1761 col = *it;
1762 if (mip && integrality[col] == HighsVarType::INTEGER) continue;
1763 k = getSingColElementIndexInA(col);
1764 if (k < 0) continue;
1765 assert(k < (int)Aindex.size());
1766 i = Aindex.at(k);
1767
1768 if (!flagRow.at(i)) {
1769 cout << "ERROR: column singleton " << col << " is in row " << i
1770 << " which is already mapped off\n";
1771 exit(-1);
1772 }
1773
1774 if (colLower.at(col) <= -HIGHS_CONST_INF ||
1775 colUpper.at(col) >= HIGHS_CONST_INF) {
1776 if (colLower.at(col) > -HIGHS_CONST_INF &&
1777 colUpper.at(col) >= HIGHS_CONST_INF) {
1778 if (Avalue.at(k) > 0)
1779 if ((colCost.at(col) / Avalue.at(k)) < implRowDualUpper.at(i))
1780 implRowDualUpper.at(i) = colCost.at(col) / Avalue.at(k);
1781 if (Avalue.at(k) < 0)
1782 if ((colCost.at(col) / Avalue.at(k)) > implRowDualLower.at(i))
1783 implRowDualLower.at(i) = colCost.at(col) / Avalue.at(k);
1784 } else if (colLower.at(col) <= -HIGHS_CONST_INF &&
1785 colUpper.at(col) < HIGHS_CONST_INF) {
1786 if (Avalue.at(k) > 0)
1787 if ((colCost.at(col) / Avalue.at(k)) > implRowDualLower.at(i))
1788 implRowDualUpper.at(i) = -colCost.at(col) / Avalue.at(k);
1789 if (Avalue.at(k) < 0)
1790 if ((colCost.at(col) / Avalue.at(k)) < implRowDualUpper.at(i))
1791 implRowDualUpper.at(i) = colCost.at(col) / Avalue.at(k);
1792 } else if (colLower.at(col) <= -HIGHS_CONST_INF &&
1793 colUpper.at(col) >= HIGHS_CONST_INF) {
1794 // all should be removed earlier but use them
1795 if ((colCost.at(col) / Avalue.at(k)) > implRowDualLower.at(i))
1796 implRowDualLower.at(i) = colCost.at(col) / Avalue.at(k);
1797 if ((colCost.at(col) / Avalue.at(k)) < implRowDualUpper.at(i))
1798 implRowDualUpper.at(i) = colCost.at(col) / Avalue.at(k);
1799 }
1800
1801 if (implRowDualLower.at(i) > implRowDualUpper.at(i)) {
1802 cout << "Error: inconstistent bounds for Lagrange multiplier for row "
1803 << i << " detected after column singleton " << col
1804 << ". In presolve::dominatedColumns" << endl;
1805 exit(0);
1806 }
1807 }
1808 }
1809 }
1810
getImpliedColumnBounds(int j)1811 pair<double, double> Presolve::getImpliedColumnBounds(int j) {
1812 pair<double, double> out;
1813 double e = 0;
1814 double d = 0;
1815
1816 int i;
1817 for (int k = Astart.at(j); k < Aend.at(j); ++k) {
1818 i = Aindex.at(k);
1819 if (flagRow.at(i)) {
1820 if (Avalue.at(k) < 0) {
1821 if (implRowDualUpper.at(i) < HIGHS_CONST_INF)
1822 e += Avalue.at(k) * implRowDualUpper.at(i);
1823 else {
1824 e = -HIGHS_CONST_INF;
1825 break;
1826 }
1827 } else {
1828 if (implRowDualLower.at(i) > -HIGHS_CONST_INF)
1829 e += Avalue.at(k) * implRowDualLower.at(i);
1830 else {
1831 e = -HIGHS_CONST_INF;
1832 break;
1833 }
1834 }
1835 }
1836 }
1837
1838 for (int k = Astart.at(j); k < Aend.at(j); ++k) {
1839 i = Aindex.at(k);
1840 if (flagRow.at(i)) {
1841 if (Avalue.at(k) < 0) {
1842 if (implRowDualLower.at(i) > -HIGHS_CONST_INF)
1843 d += Avalue.at(k) * implRowDualLower.at(i);
1844 else {
1845 d = HIGHS_CONST_INF;
1846 break;
1847 }
1848 } else {
1849 if (implRowDualUpper.at(i) < HIGHS_CONST_INF)
1850 d += Avalue.at(k) * implRowDualUpper.at(i);
1851 else {
1852 d = HIGHS_CONST_INF;
1853 break;
1854 }
1855 }
1856 }
1857 }
1858
1859 if (e > d) {
1860 cout << "Error: inconstistent bounds for Lagrange multipliers for column "
1861 << j << ": e>d. In presolve::dominatedColumns" << endl;
1862 exit(-1);
1863 }
1864 out.first = d;
1865 out.second = e;
1866 return out;
1867 }
1868
removeDominatedColumns()1869 void Presolve::removeDominatedColumns() {
1870 // todo, use only continuous and implied integer columns for calculating row
1871 // dual upper and lower bounds then the domination (and also weak domination
1872 // check) should be correct for integers
1873
1874 // for each column j calculate e and d and check:
1875 double e, d;
1876 pair<double, double> p;
1877 if (timer.reachLimit()) {
1878 status = stat::Timeout;
1879 return;
1880 }
1881 for (int j = 0; j < numCol; ++j)
1882 if (flagCol.at(j)) {
1883 p = getImpliedColumnBounds(j);
1884 d = p.first;
1885 e = p.second;
1886
1887 // Analyse dependency on numerical tolerance
1888 bool dominated = colCost.at(j) - d > tol;
1889 timer.updateNumericsRecord(DOMINATED_COLUMN, colCost.at(j) - d);
1890 if (!dominated) {
1891 timer.updateNumericsRecord(DOMINATED_COLUMN, e - colCost.at(j));
1892 }
1893
1894 // check if it is dominated
1895 if (colCost.at(j) - d > tol) {
1896 if (colLower.at(j) <= -HIGHS_CONST_INF) {
1897 if (iPrint > 0) cout << "PR: Problem unbounded." << endl;
1898 status = Unbounded;
1899 return;
1900 }
1901 setPrimalValue(j, colLower.at(j));
1902 addChange(DOMINATED_COLS, 0, j);
1903 if (iPrint > 0)
1904 cout << "PR: Dominated column " << j
1905 << " removed. Value := " << valuePrimal.at(j) << endl;
1906 countRemovedCols(DOMINATED_COLS);
1907 } else if (colCost.at(j) - e < -tol) {
1908 if (colUpper.at(j) >= HIGHS_CONST_INF) {
1909 if (iPrint > 0) cout << "PR: Problem unbounded." << endl;
1910 status = Unbounded;
1911 return;
1912 }
1913 setPrimalValue(j, colUpper.at(j));
1914 addChange(DOMINATED_COLS, 0, j);
1915 if (iPrint > 0)
1916 cout << "PR: Dominated column " << j
1917 << " removed. Value := " << valuePrimal.at(j) << endl;
1918 countRemovedCols(DOMINATED_COLS);
1919 } else {
1920 // update implied bounds
1921 if (implColDualLower.at(j) < (colCost.at(j) - d))
1922 implColDualLower.at(j) = colCost.at(j) - d;
1923 if (implColDualUpper.at(j) > (colCost.at(j) - e))
1924 implColDualUpper.at(j) = colCost.at(j) - e;
1925 if (implColDualLower.at(j) > implColDualUpper.at(j))
1926 cout << "INCONSISTENT\n";
1927
1928 removeIfWeaklyDominated(j, d, e);
1929 continue;
1930 }
1931 if (status) return;
1932 }
1933 }
1934
removeIfWeaklyDominated(const int j,const double d,const double e)1935 void Presolve::removeIfWeaklyDominated(const int j, const double d,
1936 const double e) {
1937 int i;
1938 // check if it is weakly dominated: Excluding singletons!
1939 if (nzCol.at(j) > 1) {
1940 // Analyse dependency on numerical tolerance
1941 bool possible = d < HIGHS_CONST_INF && colLower.at(j) > -HIGHS_CONST_INF;
1942 timer.updateNumericsRecord(WEAKLY_DOMINATED_COLUMN,
1943 fabs(colCost.at(j) - d));
1944 if (possible &&
1945 fabs(colCost.at(j) - d) < weakly_dominated_column_tolerance) {
1946 if (e > -HIGHS_CONST_INF && colUpper.at(j) < HIGHS_CONST_INF)
1947 timer.updateNumericsRecord(WEAKLY_DOMINATED_COLUMN,
1948 fabs(colCost.at(j) - e));
1949 }
1950
1951 if (d < HIGHS_CONST_INF &&
1952 fabs(colCost.at(j) - d) < weakly_dominated_column_tolerance &&
1953 colLower.at(j) > -HIGHS_CONST_INF) {
1954 setPrimalValue(j, colLower.at(j));
1955 addChange(WEAKLY_DOMINATED_COLS, 0, j);
1956 if (iPrint > 0)
1957 cout << "PR: Weakly Dominated column " << j
1958 << " removed. Value := " << valuePrimal.at(j) << endl;
1959
1960 countRemovedCols(WEAKLY_DOMINATED_COLS);
1961 } else if (e > -HIGHS_CONST_INF &&
1962 fabs(colCost.at(j) - e) < weakly_dominated_column_tolerance &&
1963 colUpper.at(j) < HIGHS_CONST_INF) {
1964 setPrimalValue(j, colUpper.at(j));
1965 addChange(WEAKLY_DOMINATED_COLS, 0, j);
1966 if (iPrint > 0)
1967 cout << "PR: Weakly Dominated column " << j
1968 << " removed. Value := " << valuePrimal.at(j) << endl;
1969
1970 countRemovedCols(WEAKLY_DOMINATED_COLS);
1971 } else {
1972 double bnd;
1973
1974 // calculate new bounds
1975 if (!mip || integrality[j] != HighsVarType::INTEGER) {
1976 if (colLower.at(j) > -HIGHS_CONST_INF ||
1977 colUpper.at(j) >= HIGHS_CONST_INF)
1978 for (int kk = Astart.at(j); kk < Aend.at(j); ++kk)
1979 if (flagRow.at(Aindex.at(kk)) && d < HIGHS_CONST_INF) {
1980 i = Aindex.at(kk);
1981 if (Avalue.at(kk) > 0 &&
1982 implRowDualLower.at(i) > -HIGHS_CONST_INF) {
1983 bnd = -(colCost.at(j) + d) / Avalue.at(kk) +
1984 implRowDualLower.at(i);
1985 if (bnd < implRowDualUpper.at(i) &&
1986 !(bnd < implRowDualLower.at(i)))
1987 implRowDualUpper.at(i) = bnd;
1988 } else if (Avalue.at(kk) < 0 &&
1989 implRowDualUpper.at(i) < HIGHS_CONST_INF) {
1990 bnd = -(colCost.at(j) + d) / Avalue.at(kk) +
1991 implRowDualUpper.at(i);
1992 if (bnd > implRowDualLower.at(i) &&
1993 !(bnd > implRowDualUpper.at(i)))
1994 implRowDualLower.at(i) = bnd;
1995 }
1996 }
1997
1998 if (colLower.at(j) <= -HIGHS_CONST_INF ||
1999 colUpper.at(j) < HIGHS_CONST_INF)
2000 for (int kk = Astart.at(j); kk < Aend.at(j); ++kk)
2001 if (flagRow.at(Aindex.at(kk)) && e > -HIGHS_CONST_INF) {
2002 i = Aindex.at(kk);
2003 if (Avalue.at(kk) > 0 &&
2004 implRowDualUpper.at(i) < HIGHS_CONST_INF) {
2005 bnd = -(colCost.at(j) + e) / Avalue.at(kk) +
2006 implRowDualUpper.at(i);
2007 if (bnd > implRowDualLower.at(i) &&
2008 !(bnd > implRowDualUpper.at(i)))
2009 implRowDualLower.at(i) = bnd;
2010 } else if (Avalue.at(kk) < 0 &&
2011 implRowDualLower.at(i) > -HIGHS_CONST_INF) {
2012 bnd = -(colCost.at(j) + e) / Avalue.at(kk) +
2013 implRowDualLower.at(i);
2014 if (bnd < implRowDualUpper.at(i) &&
2015 !(bnd < implRowDualLower.at(i)))
2016 implRowDualUpper.at(i) = bnd;
2017 }
2018 }
2019 }
2020 }
2021 }
2022 }
2023
setProblemStatus(const int s)2024 void Presolve::setProblemStatus(const int s) {
2025 if (s == Infeasible)
2026 cout << "NOT-OPT status = 1, returned from solver after presolve: Problem "
2027 "infeasible.\n";
2028 else if (s == Unbounded)
2029 cout << "NOT-OPT status = 2, returned from solver after presolve: Problem "
2030 "unbounded.\n";
2031 else if (s == 0) {
2032 status = Optimal;
2033 return;
2034 } else
2035 cout << "unknown problem status returned from solver after presolve: " << s
2036 << endl;
2037 status = s;
2038 }
2039
setKKTcheckerData()2040 void Presolve::setKKTcheckerData() {
2041 // after initializing equations.
2042 chk2.setBoundsCostRHS(colUpper, colLower, colCost, rowLower, rowUpper);
2043 }
2044
getNewBoundsDoubletonConstraint(const int row,const int col,const int j,const double aik,const double aij)2045 pair<double, double> Presolve::getNewBoundsDoubletonConstraint(
2046 const int row, const int col, const int j, const double aik,
2047 const double aij) {
2048 int i = row;
2049
2050 double upp = HIGHS_CONST_INF;
2051 double low = -HIGHS_CONST_INF;
2052
2053 roundIntegerBounds(col);
2054
2055 if (aij > 0 && aik > 0) {
2056 if (colLower.at(col) > -HIGHS_CONST_INF && rowUpper.at(i) < HIGHS_CONST_INF)
2057 upp = (rowUpper.at(i) - aik * colLower.at(col)) / aij;
2058 if (colUpper.at(col) < HIGHS_CONST_INF && rowLower.at(i) > -HIGHS_CONST_INF)
2059 low = (rowLower.at(i) - aik * colUpper.at(col)) / aij;
2060 } else if (aij > 0 && aik < 0) {
2061 if (colLower.at(col) > -HIGHS_CONST_INF &&
2062 rowLower.at(i) > -HIGHS_CONST_INF)
2063 low = (rowLower.at(i) - aik * colLower.at(col)) / aij;
2064 if (colUpper.at(col) < HIGHS_CONST_INF && rowUpper.at(i) < HIGHS_CONST_INF)
2065 upp = (rowUpper.at(i) - aik * colUpper.at(col)) / aij;
2066 } else if (aij < 0 && aik > 0) {
2067 if (colLower.at(col) > -HIGHS_CONST_INF && rowUpper.at(i) < HIGHS_CONST_INF)
2068 low = (rowUpper.at(i) - aik * colLower.at(col)) / aij;
2069 if (colUpper.at(col) < HIGHS_CONST_INF && rowLower.at(i) > -HIGHS_CONST_INF)
2070 upp = (rowLower.at(i) - aik * colUpper.at(col)) / aij;
2071 } else {
2072 if (colLower.at(col) > -HIGHS_CONST_INF &&
2073 rowLower.at(i) > -HIGHS_CONST_INF)
2074 upp = (rowLower.at(i) - aik * colLower.at(col)) / aij;
2075 if (colUpper.at(col) < HIGHS_CONST_INF && rowUpper.at(i) < HIGHS_CONST_INF)
2076 low = (rowUpper.at(i) - aik * colUpper.at(col)) / aij;
2077 }
2078
2079 if (upp - low < -inconsistent_bounds_tolerance) {
2080 if (iPrint > 0)
2081 std::cout << "Presolve warning: inconsistent bounds in doubleton "
2082 "constraint row "
2083 << row << std::endl;
2084 }
2085
2086 return make_pair(low, upp);
2087 }
2088
roundIntegerBounds(const int col)2089 void Presolve::roundIntegerBounds(const int col) {
2090 // for mip we check if the bounds can be rounded
2091 if (mip && integrality[col] != HighsVarType::CONTINUOUS) {
2092 if (colLower[col] != -HIGHS_CONST_INF)
2093 colLower[col] =
2094 ceil(colLower[col] - default_primal_feasiblility_tolerance);
2095
2096 if (colUpper[col] != HIGHS_CONST_INF)
2097 colUpper[col] =
2098 floor(colUpper[col] + default_primal_feasiblility_tolerance);
2099 }
2100 }
2101
removeFreeColumnSingleton(const int col,const int row,const int k)2102 void Presolve::removeFreeColumnSingleton(const int col, const int row,
2103 const int k) {
2104 if (iPrint > 0)
2105 cout << "PR: Free column singleton " << col << " removed. Row " << row
2106 << " removed." << endl;
2107
2108 // modify costs
2109 vector<pair<int, double>> newCosts;
2110 int j;
2111 for (int kk = ARstart.at(row); kk < ARstart.at(row + 1); ++kk) {
2112 j = ARindex.at(kk);
2113 if (flagCol.at(j) && j != col) {
2114 newCosts.push_back(make_pair(j, colCost.at(j)));
2115 colCost.at(j) =
2116 colCost.at(j) - colCost.at(col) * ARvalue.at(kk) / Avalue.at(k);
2117 }
2118 }
2119 if (iKKTcheck == 1) chk2.costs.push(newCosts);
2120
2121 flagCol.at(col) = 0;
2122 postValue.push(colCost.at(col));
2123 fillStackRowBounds(row);
2124
2125 valueColDual.at(col) = 0;
2126 valueRowDual.at(row) = -colCost.at(col) / Avalue.at(k);
2127
2128 double b = valueRowDual[row] < 0 ? rowLower[row] : rowUpper[row];
2129 objShift += colCost.at(col) * b / Avalue.at(k);
2130
2131 addChange(FREE_SING_COL, row, col);
2132 removeRow(row);
2133
2134 countRemovedCols(FREE_SING_COL);
2135 countRemovedRows(FREE_SING_COL);
2136 }
2137
removeColumnSingletonInDoubletonInequality(const int col,const int i,const int k)2138 bool Presolve::removeColumnSingletonInDoubletonInequality(const int col,
2139 const int i,
2140 const int k) {
2141 // second column index j
2142 // second column row array index kk
2143 int j = -1;
2144
2145 // count
2146 int kk = ARstart.at(i);
2147 while (kk < ARstart.at(i + 1)) {
2148 j = ARindex.at(kk);
2149 if (flagCol.at(j) && j != col)
2150 break;
2151 else
2152 ++kk;
2153 }
2154 if (kk == ARstart.at(i + 1))
2155 cout << "ERROR: nzRow[" << i << "]=2, but no second variable in row. \n";
2156
2157 // only inequality case and case two singletons here,
2158 // others handled in doubleton equation
2159 // Analyse dependency on numerical tolerance
2160 if (nzCol.at(j) > 1)
2161 timer.updateNumericsRecord(DOUBLETON_INEQUALITY_BOUND,
2162 fabs(rowLower.at(i) - rowUpper.at(i)));
2163 if ((fabs(rowLower.at(i) - rowUpper.at(i)) <
2164 doubleton_inequality_bound_tolerance) &&
2165 (nzCol.at(j) > 1))
2166 return false;
2167
2168 // additional check if it is indeed implied free
2169 // needed since we handle inequalities and it may not be true
2170 // low and upp to be tighter than original bounds for variable col
2171 // so it is indeed implied free and we can remove it
2172 pair<double, double> p =
2173 getNewBoundsDoubletonConstraint(i, j, col, ARvalue.at(kk), Avalue.at(k));
2174 if (!(colLower.at(col) <= p.first && colUpper.at(col) >= p.second)) {
2175 return false;
2176 }
2177
2178 postValue.push(ARvalue.at(kk));
2179 postValue.push(Avalue.at(k));
2180
2181 // modify bounds on variable j, variable col (k) is substituted out
2182 // double aik = Avalue.at(k);
2183 // double aij = Avalue.at(kk);
2184 p = getNewBoundsDoubletonConstraint(i, col, j, Avalue.at(k), ARvalue.at(kk));
2185 double low = p.first;
2186 double upp = p.second;
2187
2188 // add old bounds of xj to checker and for postsolve
2189 if (iKKTcheck == 1) {
2190 vector<pair<int, double>> bndsL, bndsU, costS;
2191 bndsL.push_back(make_pair(j, colLower.at(j)));
2192 bndsU.push_back(make_pair(j, colUpper.at(j)));
2193 costS.push_back(make_pair(j, colCost.at(j)));
2194 chk2.cLowers.push(bndsL);
2195 chk2.cUppers.push(bndsU);
2196 chk2.costs.push(costS);
2197 }
2198
2199 vector<double> bndsCol({colLower.at(col), colUpper.at(col), colCost.at(col)});
2200 vector<double> bndsJ({colLower.at(j), colUpper.at(j), colCost.at(j)});
2201 oldBounds.push(make_pair(col, bndsCol));
2202 oldBounds.push(make_pair(j, bndsJ));
2203
2204 // modify bounds of xj
2205 if (low > colLower.at(j)) colLower.at(j) = low;
2206 if (upp < colUpper.at(j)) colUpper.at(j) = upp;
2207
2208 // modify cost of xj
2209 colCost.at(j) =
2210 colCost.at(j) - colCost.at(col) * ARvalue.at(kk) / Avalue.at(k);
2211
2212 // for postsolve: need the new bounds too
2213 // oldBounds.push_back(colLower.at(j)); oldBounds.push_back(colUpper.at(j));
2214 bndsJ.at(0) = (colLower.at(j));
2215 bndsJ.at(1) = (colUpper.at(j));
2216 bndsJ.at(2) = (colCost.at(j));
2217 oldBounds.push(make_pair(j, bndsJ));
2218
2219 // remove col as free column singleton
2220 if (iPrint > 0)
2221 cout << "PR: Column singleton " << col
2222 << " in a doubleton inequality constraint removed. Row " << i
2223 << " removed. variable left is " << j << endl;
2224
2225 flagCol.at(col) = 0;
2226 fillStackRowBounds(i);
2227 countRemovedCols(SING_COL_DOUBLETON_INEQ);
2228 countRemovedRows(SING_COL_DOUBLETON_INEQ);
2229
2230 valueColDual.at(col) = 0;
2231 valueRowDual.at(i) =
2232 -colCost.at(col) /
2233 Avalue.at(k); // may be changed later, depending on bounds.
2234 addChange(SING_COL_DOUBLETON_INEQ, i, col);
2235
2236 // if not special case two column singletons
2237 if (nzCol.at(j) > 1)
2238 removeRow(i);
2239 else if (nzCol.at(j) == 1)
2240 removeSecondColumnSingletonInDoubletonRow(j, i);
2241
2242 return true;
2243 }
2244
removeSecondColumnSingletonInDoubletonRow(const int j,const int i)2245 void Presolve::removeSecondColumnSingletonInDoubletonRow(const int j,
2246 const int i) {
2247 // case two singleton columns
2248 // when we get here bounds on xj are updated so we can choose low/upper one
2249 // depending on the cost of xj
2250 // throw; // does not get triggered by ctest or small.
2251 flagRow.at(i) = 0;
2252 double value;
2253 if (colCost.at(j) > 0) {
2254 if (colLower.at(j) <= -HIGHS_CONST_INF) {
2255 if (iPrint > 0) cout << "PR: Problem unbounded." << endl;
2256 status = Unbounded;
2257 return;
2258 }
2259 value = colLower.at(j);
2260 } else if (colCost.at(j) < 0) {
2261 if (colUpper.at(j) >= HIGHS_CONST_INF) {
2262 if (iPrint > 0) cout << "PR: Problem unbounded." << endl;
2263 status = Unbounded;
2264 return;
2265 }
2266 value = colUpper.at(j);
2267 } else { //(colCost.at(j) == 0)
2268 if (colUpper.at(j) >= 0 && colLower.at(j) <= 0)
2269 value = 0;
2270 else if (fabs(colUpper.at(j)) < fabs(colLower.at(j)))
2271 value = colUpper.at(j);
2272 else
2273 value = colLower.at(j);
2274 }
2275 setPrimalValue(j, value);
2276 addChange(SING_COL_DOUBLETON_INEQ_SECOND_SING_COL, 0, j);
2277 if (iPrint > 0)
2278 cout << "PR: Second singleton column " << j << " in doubleton row " << i
2279 << " removed.\n";
2280 countRemovedCols(SING_COL_DOUBLETON_INEQ);
2281 // singCol.remove(j);
2282 }
2283
removeZeroCostColumnSingleton(const int col,const int row,const int k)2284 void Presolve::removeZeroCostColumnSingleton(const int col, const int row,
2285 const int k) {
2286 assert(Aindex[k] == row);
2287 assert(fabs(colCost[col]) < tol);
2288 std::cout << "Zero cost column singleton: col = " << col << ", row " << row
2289 << ", coeff = " << Avalue[k] << ", cost = " << colCost[col]
2290 << std::endl;
2291 std::cout << " L = " << rowLower[row] << " U = " << rowUpper[row]
2292 << std::endl;
2293 std::cout << " l = " << colLower[col] << " u = " << colUpper[col]
2294 << std::endl;
2295 }
2296
removeColumnSingletons()2297 void Presolve::removeColumnSingletons() {
2298 list<int>::iterator it = singCol.begin();
2299
2300 if (timer.reachLimit()) {
2301 status = stat::Timeout;
2302 return;
2303 }
2304
2305 while (it != singCol.end()) {
2306 if (flagCol[*it]) {
2307 const int col = *it;
2308 assert(0 <= col && col <= numCol);
2309
2310 const int k = getSingColElementIndexInA(col);
2311 if (k < 0) {
2312 it = singCol.erase(it);
2313 if (k == -2) flagCol[col] = 0;
2314 continue;
2315 }
2316 assert(k < (int)Aindex.size());
2317 const int i = Aindex.at(k);
2318 const double ai = Avalue[k];
2319
2320 // todo if the variable type of column is HighsVarType::INTEGRAL check the
2321 // integrality of all coefficients divided by the coefficient of the
2322 // integral singleton variable coefficients before doing a substitution,
2323 // for now we skip the reductions
2324 if (mip && integrality[col] == HighsVarType::INTEGER) {
2325 // for integral columns only handle equality rows
2326 if (rowLower[i] != rowUpper[i]) {
2327 ++it;
2328 continue;
2329 }
2330
2331 bool suitable = false;
2332 for (int kk = ARstart[i]; kk < ARstart[i + 1]; ++kk) {
2333 int j = ARindex[kk];
2334 if (flagCol[j] && j != col) {
2335 if (integrality[col] != HighsVarType::INTEGER) {
2336 suitable = false;
2337 break;
2338 }
2339
2340 double substval = ARvalue[kk] / ai;
2341 double intval = std::floor(substval + 0.5);
2342 if (std::abs(substval - intval) > tol) {
2343 suitable = false;
2344 break;
2345 }
2346 }
2347 }
2348
2349 if (!suitable) {
2350 ++it;
2351 continue;
2352 }
2353
2354 printf("integer singleton can be subsituted\n");
2355 }
2356
2357 // zero cost
2358 bool on_zero_cost = false && !mip;
2359 if (on_zero_cost && fabs(colCost.at(col)) < tol) {
2360 removeZeroCostColumnSingleton(col, i, k);
2361 it = singCol.erase(it);
2362 continue;
2363 }
2364
2365 if (colLower.at(col) <= -HIGHS_CONST_INF &&
2366 colUpper.at(col) >= HIGHS_CONST_INF) {
2367 removeFreeColumnSingleton(col, i, k);
2368 it = singCol.erase(it);
2369 continue;
2370 }
2371
2372 // implied free
2373 const bool result = removeIfImpliedFree(col, i, k);
2374 if (result) {
2375 it = singCol.erase(it);
2376 continue;
2377 }
2378
2379 // todo, I think this case might not work for integral variables
2380 // singleton column in a doubleton inequality
2381 // case two column singletons
2382 if (!mip || integrality[col] != HighsVarType::INTEGER)
2383 if (nzRow.at(i) == 2) {
2384 const bool result_di =
2385 removeColumnSingletonInDoubletonInequality(col, i, k);
2386 if (result_di) {
2387 if (status) return;
2388 it = singCol.erase(it);
2389 continue;
2390 }
2391 }
2392 it++;
2393
2394 if (status) return;
2395 } else
2396 it = singCol.erase(it);
2397 }
2398 }
2399
removeSingletonsOnly()2400 void Presolve::removeSingletonsOnly() {
2401 for (int row = 0; row < numRow; row++) {
2402 if (!flagRow[row]) continue;
2403 bool valid = true;
2404 int nz_col = 0;
2405 for (int k = ARstart[row]; k < ARstart[row + 1]; k++) {
2406 const int col = ARindex[k];
2407 if (!flagCol[col]) continue;
2408 if (nzCol[col] != 1) {
2409 valid = false;
2410 break;
2411 }
2412 nz_col++;
2413 }
2414 if (!valid) continue;
2415 if (nz_col == 0) {
2416 flagRow[row] = false;
2417 continue;
2418 }
2419
2420 std::cout << "Singletons only row found! nzcol = " << nz_col
2421 << " L = " << rowLower[row] << " U = " << rowUpper[row]
2422 << std::endl;
2423 }
2424 // timer.recordStart(KNAPSACK);
2425
2426 list<int>::iterator it = singCol.begin();
2427 while (it != singCol.end()) {
2428 const int col = *it;
2429 if (!flagCol[col]) {
2430 it = singCol.erase(it);
2431 continue;
2432 }
2433
2434 bool remove = isKnapsack(col);
2435 if (remove) {
2436 removeKnapsack(col);
2437 it = singCol.erase(it);
2438 continue;
2439 }
2440 it++;
2441 }
2442
2443 // timer.recordFinish(KNAPSACK);
2444 }
2445
removeKnapsack(const int col)2446 void Presolve::removeKnapsack(const int col) {
2447 for (int k = Astart[col]; k < Aend[col]; k++) {
2448 assert(Aindex[k] >= 0 && Aindex[k] <= numRow);
2449 // todo:
2450 }
2451
2452 return;
2453 }
2454
isKnapsack(const int col) const2455 bool Presolve::isKnapsack(const int col) const {
2456 for (int k = Astart[col]; k < Aend[col]; k++) {
2457 assert(Aindex[k] >= 0 && Aindex[k] <= numRow);
2458 if (flagRow[Aindex[k]]) {
2459 if (nzCol[Aindex[k]] != 1) return false;
2460 }
2461 }
2462 return true;
2463 }
2464
getBoundsImpliedFree(double lowInit,double uppInit,const int col,const int i,const int k)2465 pair<double, double> Presolve::getBoundsImpliedFree(double lowInit,
2466 double uppInit,
2467 const int col, const int i,
2468 const int k) {
2469 double low = lowInit;
2470 double upp = uppInit;
2471
2472 // use implied bounds with original bounds
2473 int j;
2474 double l, u;
2475 // if at any stage low becomes or upp becomes inf break loop
2476 // can't use bounds for variables generated by the same row.
2477 // low
2478 for (int kk = ARstart.at(i); kk < ARstart.at(i + 1); ++kk) {
2479 j = ARindex.at(kk);
2480 if (flagCol.at(j) && j != col) {
2481 // check if new bounds are precisely implied bounds from same row
2482 if (i != implColLowerRowIndex.at(j))
2483 l = max(colLower.at(j), implColLower.at(j));
2484 else
2485 l = colLower.at(j);
2486 if (i != implColUpperRowIndex.at(j))
2487 u = min(colUpper.at(j), implColUpper.at(j));
2488 else
2489 u = colUpper.at(j);
2490
2491 if ((Avalue.at(k) < 0 && ARvalue.at(kk) > 0) ||
2492 (Avalue.at(k) > 0 && ARvalue.at(kk) < 0))
2493 if (l <= -HIGHS_CONST_INF) {
2494 low = -HIGHS_CONST_INF;
2495 break;
2496 } else
2497 low -= ARvalue.at(kk) * l;
2498 else if (u >= HIGHS_CONST_INF) {
2499 low = -HIGHS_CONST_INF;
2500 break;
2501 } else
2502 low -= ARvalue.at(kk) * u;
2503 }
2504 }
2505 // upp
2506 for (int kk = ARstart.at(i); kk < ARstart.at(i + 1); ++kk) {
2507 j = ARindex.at(kk);
2508 if (flagCol.at(j) && j != col) {
2509 // check if new bounds are precisely implied bounds from same row
2510 if (i != implColLowerRowIndex.at(j))
2511 l = max(colLower.at(j), implColLower.at(j));
2512 else
2513 l = colLower.at(j);
2514 if (i != implColUpperRowIndex.at(j))
2515 u = min(colUpper.at(j), implColUpper.at(j));
2516 else
2517 u = colUpper.at(j);
2518 // if at any stage low becomes or upp becomes inf it's not implied free
2519 // low::
2520 if ((Avalue.at(k) < 0 && ARvalue.at(kk) > 0) ||
2521 (Avalue.at(k) > 0 && ARvalue.at(kk) < 0))
2522 if (u >= HIGHS_CONST_INF) {
2523 upp = HIGHS_CONST_INF;
2524 break;
2525 } else
2526 upp -= ARvalue.at(kk) * u;
2527 else if (l <= -HIGHS_CONST_INF) {
2528 upp = HIGHS_CONST_INF;
2529 break;
2530 } else
2531 upp -= ARvalue.at(kk) * l;
2532 }
2533 }
2534 return make_pair(low, upp);
2535 }
2536
removeImpliedFreeColumn(const int col,const int i,const int k)2537 void Presolve::removeImpliedFreeColumn(const int col, const int i,
2538 const int k) {
2539 if (iPrint > 0)
2540 cout << "PR: Implied free column singleton " << col << " removed. Row "
2541 << i << " removed." << endl;
2542
2543 countRemovedCols(IMPLIED_FREE_SING_COL);
2544 countRemovedRows(IMPLIED_FREE_SING_COL);
2545
2546 // modify costs
2547 int j;
2548 vector<pair<int, double>> newCosts;
2549 for (int kk = ARstart.at(i); kk < ARstart.at(i + 1); ++kk) {
2550 j = ARindex.at(kk);
2551 if (flagCol.at(j) && j != col) {
2552 newCosts.push_back(make_pair(j, colCost.at(j)));
2553 colCost.at(j) =
2554 colCost.at(j) - colCost.at(col) * ARvalue.at(kk) / Avalue.at(k);
2555 }
2556 }
2557 if (iKKTcheck == 1) chk2.costs.push(newCosts);
2558
2559 flagCol.at(col) = 0;
2560 postValue.push(colCost.at(col));
2561 fillStackRowBounds(i);
2562
2563 valueColDual.at(col) = 0;
2564 valueRowDual.at(i) = -colCost.at(col) / Avalue.at(k);
2565 double b = valueRowDual[i] < 0 || rowUpper[i] == HIGHS_CONST_INF
2566 ? rowLower[i]
2567 : rowUpper[i];
2568 assert(std::isfinite(b));
2569 objShift += colCost.at(col) * b / Avalue.at(k);
2570 addChange(IMPLIED_FREE_SING_COL, i, col);
2571 removeRow(i);
2572 }
2573
removeIfImpliedFree(int col,int i,int k)2574 bool Presolve::removeIfImpliedFree(int col, int i, int k) {
2575 // first find which bound is active for row i
2576 // A'y + c = z so yi = -ci/aij
2577 double aij = getaij(i, col);
2578 if (aij != Avalue.at(k)) cout << "ERROR during implied free";
2579 double yi = -colCost.at(col) / aij;
2580 double low, upp;
2581
2582 if (yi > 0) {
2583 if (rowUpper.at(i) >= HIGHS_CONST_INF) return false;
2584 low = rowUpper.at(i);
2585 upp = rowUpper.at(i);
2586 } else if (yi < 0) {
2587 if (rowLower.at(i) <= -HIGHS_CONST_INF) return false;
2588 low = rowLower.at(i);
2589 upp = rowLower.at(i);
2590 } else {
2591 low = rowLower.at(i);
2592 upp = rowUpper.at(i);
2593 }
2594
2595 pair<double, double> p = getBoundsImpliedFree(low, upp, col, i, k);
2596 low = p.first;
2597 upp = p.second;
2598
2599 if (low > -HIGHS_CONST_INF) low = low / Avalue.at(k);
2600 if (upp < HIGHS_CONST_INF) upp = upp / Avalue.at(k);
2601
2602 // if implied free
2603 if (colLower.at(col) <= low && low <= upp && upp <= colUpper.at(col)) {
2604 removeImpliedFreeColumn(col, i, k);
2605 return true;
2606 }
2607 // else calculate implied bounds
2608 else if (colLower.at(col) <= low && low <= upp) {
2609 if (implColLower.at(col) < low) {
2610 implColLower.at(col) = low;
2611 implColUpperRowIndex.at(col) = i;
2612 }
2613 implColDualUpper.at(col) = 0;
2614 } else if (low <= upp && upp <= colUpper.at(col)) {
2615 if (implColUpper.at(col) > upp) {
2616 implColUpper.at(col) = upp;
2617 implColUpperRowIndex.at(col) = i;
2618 }
2619 implColDualLower.at(col) = 0;
2620 }
2621
2622 return false;
2623 }
2624
2625 // used to remove column too, now possible to just modify bounds
removeRow(int i)2626 void Presolve::removeRow(int i) {
2627 hasChange = true;
2628 flagRow.at(i) = 0;
2629 for (int k = ARstart.at(i); k < ARstart.at(i + 1); ++k) {
2630 int j = ARindex.at(k);
2631 if (flagCol.at(j)) {
2632 nzCol.at(j)--;
2633 // if now singleton add to list
2634 if (nzCol.at(j) == 1) {
2635 int index = getSingColElementIndexInA(j);
2636 if (index >= 0)
2637 singCol.push_back(j);
2638 else
2639 cout << "Warning: Column " << j
2640 << " with 1 nz but not in singCol or? Row removing of " << i
2641 << ". Ignored.\n";
2642 }
2643 // if it was a singleton column remove from list and problem
2644 if (nzCol.at(j) == 0) removeEmptyColumn(j);
2645 }
2646 }
2647 }
2648
fillStackRowBounds(int row)2649 void Presolve::fillStackRowBounds(int row) {
2650 postValue.push(rowUpper.at(row));
2651 postValue.push(rowLower.at(row));
2652 }
2653
getImpliedRowBounds(int row)2654 pair<double, double> Presolve::getImpliedRowBounds(int row) {
2655 double g = 0;
2656 double h = 0;
2657
2658 int col;
2659 for (int k = ARstart.at(row); k < ARstart.at(row + 1); ++k) {
2660 col = ARindex.at(k);
2661 if (flagCol.at(col)) {
2662 if (ARvalue.at(k) < 0) {
2663 if (colUpper.at(col) < HIGHS_CONST_INF)
2664 g += ARvalue.at(k) * colUpper.at(col);
2665 else {
2666 g = -HIGHS_CONST_INF;
2667 break;
2668 }
2669 } else {
2670 if (colLower.at(col) > -HIGHS_CONST_INF)
2671 g += ARvalue.at(k) * colLower.at(col);
2672 else {
2673 g = -HIGHS_CONST_INF;
2674 break;
2675 }
2676 }
2677 }
2678 }
2679
2680 for (int k = ARstart.at(row); k < ARstart.at(row + 1); ++k) {
2681 col = ARindex.at(k);
2682 if (flagCol.at(col)) {
2683 if (ARvalue.at(k) < 0) {
2684 if (colLower.at(col) > -HIGHS_CONST_INF)
2685 h += ARvalue.at(k) * colLower.at(col);
2686 else {
2687 h = HIGHS_CONST_INF;
2688 break;
2689 }
2690 } else {
2691 if (colUpper.at(col) < HIGHS_CONST_INF)
2692 h += ARvalue.at(k) * colUpper.at(col);
2693 else {
2694 h = HIGHS_CONST_INF;
2695 break;
2696 }
2697 }
2698 }
2699 }
2700 return make_pair(g, h);
2701 }
2702
setVariablesToBoundForForcingRow(const int row,const bool isLower)2703 void Presolve::setVariablesToBoundForForcingRow(const int row,
2704 const bool isLower) {
2705 int k, col;
2706 if (iPrint > 0)
2707 cout << "PR: Forcing row " << row
2708 << " removed. Following variables too: nzRow=" << nzRow.at(row)
2709 << endl;
2710
2711 flagRow.at(row) = 0;
2712 addChange(FORCING_ROW, row, 0);
2713 k = ARstart.at(row);
2714 while (k < ARstart.at(row + 1)) {
2715 col = ARindex.at(k);
2716 if (flagCol.at(col)) {
2717 double value;
2718 if ((ARvalue.at(k) < 0 && isLower) || (ARvalue.at(k) > 0 && !isLower))
2719 value = colUpper.at(col);
2720 else
2721 value = colLower.at(col);
2722
2723 setPrimalValue(col, value);
2724 valueColDual.at(col) = colCost.at(col);
2725 vector<double> bnds({colLower.at(col), colUpper.at(col)});
2726 oldBounds.push(make_pair(col, bnds));
2727 addChange(FORCING_ROW_VARIABLE, 0, col);
2728
2729 if (iPrint > 0)
2730 cout << "PR: Variable " << col << " := " << value << endl;
2731 countRemovedCols(FORCING_ROW);
2732 }
2733 ++k;
2734 }
2735
2736 countRemovedRows(FORCING_ROW);
2737 }
2738
dominatedConstraintProcedure(const int i,const double g,const double h)2739 void Presolve::dominatedConstraintProcedure(const int i, const double g,
2740 const double h) {
2741 int j;
2742 double val;
2743 if (h < HIGHS_CONST_INF) {
2744 // fill in implied bounds arrays
2745 if (h < implRowValueUpper.at(i)) {
2746 implRowValueUpper.at(i) = h;
2747 }
2748 if (h <= rowUpper.at(i)) implRowDualLower.at(i) = 0;
2749
2750 // calculate implied bounds for discovering free column singletons
2751 for (int k = ARstart.at(i); k < ARstart.at(i + 1); ++k) {
2752 j = ARindex.at(k);
2753 if (flagCol.at(j)) {
2754 if (ARvalue.at(k) < 0 && colLower.at(j) > -HIGHS_CONST_INF) {
2755 val = (rowLower.at(i) - h) / ARvalue.at(k) + colLower.at(j);
2756 if (val < implColUpper.at(j)) {
2757 implColUpper.at(j) = val;
2758 implColUpperRowIndex.at(j) = i;
2759 }
2760 } else if (ARvalue.at(k) > 0 && colUpper.at(j) < HIGHS_CONST_INF) {
2761 val = (rowLower.at(i) - h) / ARvalue.at(k) + colUpper.at(j);
2762 if (val > implColLower.at(j)) {
2763 implColLower.at(j) = val;
2764 implColLowerRowIndex.at(j) = i;
2765 }
2766 }
2767 }
2768 }
2769 }
2770 if (g > -HIGHS_CONST_INF) {
2771 // fill in implied bounds arrays
2772 if (g > implRowValueLower.at(i)) {
2773 implRowValueLower.at(i) = g;
2774 }
2775 if (g >= rowLower.at(i)) implRowDualUpper.at(i) = 0;
2776
2777 // calculate implied bounds for discovering free column singletons
2778 for (int k = ARstart.at(i); k < ARstart.at(i + 1); ++k) {
2779 int j = ARindex.at(k);
2780 if (flagCol.at(j)) {
2781 if (ARvalue.at(k) < 0 && colUpper.at(j) < HIGHS_CONST_INF) {
2782 val = (rowUpper.at(i) - g) / ARvalue.at(k) + colUpper.at(j);
2783 if (val > implColLower.at(j)) {
2784 implColLower.at(j) = val;
2785 implColLowerRowIndex.at(j) = i;
2786 }
2787 } else if (ARvalue.at(k) > 0 && colLower.at(j) > -HIGHS_CONST_INF) {
2788 val = (rowUpper.at(i) - g) / ARvalue.at(k) + colLower.at(j);
2789 if (val < implColUpper.at(j)) {
2790 implColUpper.at(j) = val;
2791 implColUpperRowIndex.at(j) = i;
2792 }
2793 }
2794 }
2795 }
2796 }
2797 }
2798
removeForcingConstraints()2799 void Presolve::removeForcingConstraints() {
2800 double g, h;
2801 pair<double, double> implBounds;
2802
2803 if (timer.reachLimit()) {
2804 status = stat::Timeout;
2805 return;
2806 }
2807 for (int i = 0; i < numRow; ++i)
2808 if (flagRow.at(i)) {
2809 if (status) return;
2810 if (nzRow.at(i) == 0) {
2811 removeEmptyRow(i);
2812 countRemovedRows(EMPTY_ROW);
2813 continue;
2814 }
2815
2816 // removeRowSingletons will handle just after removeForcingConstraints
2817 if (nzRow.at(i) == 1) continue;
2818
2819 implBounds = getImpliedRowBounds(i);
2820
2821 g = implBounds.first;
2822 h = implBounds.second;
2823
2824 // Infeasible row
2825 if (g > rowUpper.at(i) || h < rowLower.at(i)) {
2826 if (iPrint > 0) cout << "PR: Problem infeasible." << endl;
2827 status = Infeasible;
2828 return;
2829 }
2830 // Forcing row
2831 else if (g == rowUpper.at(i)) {
2832 setVariablesToBoundForForcingRow(i, true);
2833 } else if (h == rowLower.at(i)) {
2834 setVariablesToBoundForForcingRow(i, false);
2835 }
2836 // Redundant row
2837 else if (g >= rowLower.at(i) && h <= rowUpper.at(i)) {
2838 removeRow(i);
2839 addChange(REDUNDANT_ROW, i, 0);
2840 if (iPrint > 0)
2841 cout << "PR: Redundant row " << i << " removed." << endl;
2842 countRemovedRows(REDUNDANT_ROW);
2843 }
2844 // Dominated constraints
2845 else {
2846 dominatedConstraintProcedure(i, g, h);
2847 continue;
2848 }
2849 }
2850 }
2851
removeRowSingletons()2852 void Presolve::removeRowSingletons() {
2853 if (timer.reachLimit()) {
2854 status = stat::Timeout;
2855 return;
2856 }
2857 timer.recordStart(SING_ROW);
2858
2859 list<int>::iterator it = singRow.begin();
2860 while (it != singRow.end()) {
2861 if (flagRow[*it]) {
2862 const int i = *it;
2863 assert(i >= 0 && i < numRow);
2864
2865 const int k = getSingRowElementIndexInAR(i);
2866 if (k < 0) {
2867 it = singRow.erase(it);
2868 // kxx
2869 continue;
2870 }
2871
2872 const int j = ARindex.at(k);
2873
2874 // add old bounds OF X to checker and for postsolve
2875 if (iKKTcheck == 1) {
2876 vector<pair<int, double>> bndsL, bndsU, costS;
2877 bndsL.push_back(make_pair(j, colLower.at(j)));
2878 bndsU.push_back(make_pair(j, colUpper.at(j)));
2879 costS.push_back(make_pair(j, colCost.at(j)));
2880
2881 chk2.cLowers.push(bndsL);
2882 chk2.cUppers.push(bndsU);
2883 chk2.costs.push(costS);
2884 }
2885
2886 vector<double> bnds(
2887 {colLower.at(j), colUpper.at(j), rowLower.at(i), rowUpper.at(i)});
2888 oldBounds.push(make_pair(j, bnds));
2889
2890 double aij = ARvalue.at(k);
2891 /* //before update bounds of x take it out of rows with
2892 implied row bounds for (int r = Astart.at(j); r<Aend.at(j); r++) { if
2893 (flagRow[Aindex[r]]) { int rr = Aindex[r]; if (implRowValueLower[rr] >
2894 -HIGHS_CONST_INF) { if (aij > 0) implRowValueLower[rr] =
2895 implRowValueLower[rr] - aij*colLower.at(j); else implRowValueLower[rr] =
2896 implRowValueLower[rr] - aij*colUpper.at(j);
2897 }
2898 if (implRowValueUpper[rr] < HIGHS_CONST_INF) {
2899 if (aij > 0)
2900 implRowValueUpper[rr] =
2901 implRowValueUpper[rr] - aij*colUpper.at(j); else implRowValueUpper[rr] =
2902 implRowValueUpper[rr] - aij*colLower.at(j);
2903 }
2904 }
2905 }*/
2906
2907 // todo, round bounds for integer and implied integer variables, be
2908 // careful with tolerances, i.e. use floor(ub + eps) for rounding down an
2909 // upper bound update bounds of X
2910 if (aij > 0) {
2911 if (rowLower.at(i) != -HIGHS_CONST_INF)
2912 colLower.at(j) =
2913 max(max(rowLower.at(i) / aij, -HIGHS_CONST_INF), colLower.at(j));
2914 if (rowUpper.at(i) != HIGHS_CONST_INF)
2915 colUpper.at(j) =
2916 min(min(rowUpper.at(i) / aij, HIGHS_CONST_INF), colUpper.at(j));
2917 } else if (aij < 0) {
2918 if (rowLower.at(i) != -HIGHS_CONST_INF)
2919 colUpper.at(j) =
2920 min(min(rowLower.at(i) / aij, HIGHS_CONST_INF), colUpper.at(j));
2921 if (rowUpper.at(i) != HIGHS_CONST_INF)
2922 colLower.at(j) =
2923 max(max(rowUpper.at(i) / aij, -HIGHS_CONST_INF), colLower.at(j));
2924 }
2925
2926 /* //after update bounds of x add to rows with implied row
2927 bounds for (int r = Astart.at(j); r<Aend.at(j); r++) { if (flagRow[r]) {
2928 int rr = Aindex[r];
2929 if (implRowValueLower[rr] > -HIGHS_CONST_INF) {
2930 if (aij > 0)
2931 implRowValueLower[rr] =
2932 implRowValueLower[rr] + aij*colLower.at(j); else implRowValueLower[rr] =
2933 implRowValueLower[rr] + aij*colUpper.at(j);
2934 }
2935 if (implRowValueUpper[rr] < HIGHS_CONST_INF) {
2936 if (aij > 0)
2937 implRowValueUpper[rr] =
2938 implRowValueUpper[rr] + aij*colUpper.at(j); else implRowValueUpper[rr] =
2939 implRowValueUpper[rr] + aij*colLower.at(j);
2940 }
2941 }
2942 }*/
2943
2944 roundIntegerBounds(j);
2945
2946 // check for feasibility
2947 // Analyse dependency on numerical tolerance
2948 timer.updateNumericsRecord(INCONSISTENT_BOUNDS,
2949 colLower.at(j) - colUpper.at(j));
2950 if (colLower.at(j) - colUpper.at(j) > inconsistent_bounds_tolerance) {
2951 status = Infeasible;
2952 timer.recordFinish(SING_ROW);
2953 return;
2954 }
2955
2956 if (iPrint > 0)
2957 cout << "PR: Singleton row " << i << " removed. Bounds of variable "
2958 << j << " modified: l= " << colLower.at(j)
2959 << " u=" << colUpper.at(j) << ", aij = " << aij << endl;
2960
2961 addChange(SING_ROW, i, j);
2962 postValue.push(colCost.at(j));
2963 removeRow(i);
2964
2965 if (flagCol.at(j)) {
2966 // Analyse dependency on numerical tolerance
2967 timer.updateNumericsRecord(FIXED_COLUMN,
2968 fabs(colUpper.at(j) - colLower.at(j)));
2969 if (fabs(colUpper.at(j) - colLower.at(j)) <= fixed_column_tolerance)
2970 removeFixedCol(j);
2971 }
2972 countRemovedRows(SING_ROW);
2973
2974 if (status) {
2975 timer.recordFinish(SING_ROW);
2976 return;
2977 }
2978 it = singRow.erase(it);
2979 } else {
2980 it++;
2981 }
2982 }
2983 timer.recordFinish(SING_ROW);
2984 }
2985
addChange(PresolveRule type,int row,int col)2986 void Presolve::addChange(PresolveRule type, int row, int col) {
2987 change ch;
2988 ch.type = type;
2989 ch.row = row;
2990 ch.col = col;
2991 chng.push(ch);
2992
2993 if (type < PRESOLVE_RULES_COUNT) timer.addChange(type);
2994 }
2995
2996 // when setting a value to a primal variable and eliminating row update b,
2997 // singleton Rows linked list, number of nonzeros in rows
setPrimalValue(const int j,const double value)2998 void Presolve::setPrimalValue(const int j, const double value) {
2999 flagCol.at(j) = 0;
3000 if (!hasChange) hasChange = true;
3001 valuePrimal.at(j) = value;
3002
3003 // update nonzeros
3004 for (int k = Astart.at(j); k < Aend.at(j); ++k) {
3005 int row = Aindex.at(k);
3006 if (flagRow.at(row)) {
3007 nzRow.at(row)--;
3008
3009 // update singleton row list
3010 if (nzRow.at(row) == 1) singRow.push_back(row);
3011 }
3012 }
3013
3014 // update values if necessary
3015 if (fabs(value) > 0) {
3016 // RHS
3017 vector<pair<int, double>> bndsL, bndsU;
3018
3019 for (int k = Astart.at(j); k < Aend.at(j); ++k)
3020 if (flagRow.at(Aindex.at(k))) {
3021 const int row = Aindex[k];
3022 // std::cout << row << " " << rowLower[row] << " " << rowUpper[row] <<
3023 // std::endl;
3024
3025 if (iKKTcheck == 1) {
3026 bndsL.push_back(make_pair(row, rowLower.at(row)));
3027 bndsU.push_back(make_pair(row, rowUpper.at(row)));
3028 }
3029 if (rowLower.at(row) > -HIGHS_CONST_INF)
3030 rowLower.at(row) -= Avalue.at(k) * value;
3031 if (rowUpper.at(row) < HIGHS_CONST_INF)
3032 rowUpper.at(row) -= Avalue.at(k) * value;
3033
3034 if (implRowValueLower.at(row) > -HIGHS_CONST_INF)
3035 implRowValueLower.at(row) -= Avalue.at(k) * value;
3036 if (implRowValueUpper.at(row) < HIGHS_CONST_INF)
3037 implRowValueUpper.at(row) -= Avalue.at(k) * value;
3038
3039 if (nzRow.at(row) == 0) {
3040 if (rowLower[row] - rowUpper[row] > tol) {
3041 status = Infeasible;
3042 return;
3043 }
3044 if (rowLower[row] > tol || rowUpper[row] < -tol) {
3045 status = Infeasible;
3046 return;
3047 }
3048
3049 flagRow[row] = 0;
3050 addChange(PresolveRule::EMPTY_ROW, row, j);
3051 }
3052 }
3053
3054 if (iKKTcheck == 1) {
3055 chk2.rLowers.push(bndsL);
3056 chk2.rUppers.push(bndsU);
3057 }
3058
3059 // shift objective
3060 if (colCost.at(j) != 0) objShift += colCost.at(j) * value;
3061 }
3062 }
3063
checkForChanges(int iteration)3064 void Presolve::checkForChanges(int iteration) {
3065 if (iteration <= 2) {
3066 // flagCol has one more element at end which is zero
3067 // from removeDoubletonEquatoins, needed for AR matrix manipulation
3068 if (none_of(flagCol.begin(), flagCol.begin() + numCol,
3069 [](int i) { return i == 0; }) &&
3070 none_of(flagRow.begin(), flagRow.begin() + numRow,
3071 [](int i) { return i == 0; })) {
3072 if (iPrint > 0)
3073 cout << "PR: No variables were eliminated at presolve." << endl;
3074 noPostSolve = true;
3075 return;
3076 }
3077 }
3078 resizeProblem();
3079 status = stat::Reduced;
3080 }
3081
3082 // void Presolve::reportTimes() {
3083 // int reportList[] = {EMPTY_ROW,
3084 // FIXED_COL,
3085 // SING_ROW,
3086 // DOUBLETON_EQUATION,
3087 // FORCING_ROW,
3088 // REDUNDANT_ROW,
3089 // FREE_SING_COL,
3090 // SING_COL_DOUBLETON_INEQ,
3091 // IMPLIED_FREE_SING_COL,
3092 // DOMINATED_COLS,
3093 // WEAKLY_DOMINATED_COLS};
3094 // int reportCount = sizeof(reportList) / sizeof(int);
3095
3096 // printf("Presolve rules ");
3097 // for (int i = 0; i < reportCount; ++i) {
3098 // printf(" %s", timer.itemNames[reportList[i]].c_str());
3099 // cout << flush;
3100 // }
3101
3102 // printf("\n");
3103 // cout << "Time spent " << flush;
3104 // for (int i = 0; i < reportCount; ++i) {
3105 // float f = (float)timer.itemTicks[reportList[i]];
3106 // if (f < 0.01)
3107 // cout << setw(4) << " <.01 ";
3108 // else
3109 // printf(" %3.2f ", f);
3110 // }
3111 // printf("\n");
3112 // }
3113
3114 // void Presolve::recordCounts(const string fileName) {
3115 // ofstream myfile;
3116 // myfile.open(fileName.c_str(), ios::app);
3117 // int reportList[] = {EMPTY_ROW,
3118 // FIXED_COL,
3119 // SING_ROW,
3120 // DOUBLETON_EQUATION,
3121 // FORCING_ROW,
3122 // REDUNDANT_ROW,
3123 // FREE_SING_COL,
3124 // SING_COL_DOUBLETON_INEQ,
3125 // IMPLIED_FREE_SING_COL,
3126 // DOMINATED_COLS,
3127 // WEAKLY_DOMINATED_COLS,
3128 // EMPTY_COL};
3129 // int reportCount = sizeof(reportList) / sizeof(int);
3130
3131 // myfile << "Problem " << modelName << ":\n";
3132 // myfile << "Rule , removed rows , removed cols , time \n";
3133
3134 // int cRows = 0, cCols = 0;
3135 // for (int i = 0; i < reportCount; ++i) {
3136 // float f = (float)timer.itemTicks[reportList[i]];
3137
3138 // myfile << setw(7) << timer.itemNames[reportList[i]].c_str() << ", "
3139 // << setw(7) << countRemovedRows[reportList[i]] << ", " << setw(7)
3140 // << countRemovedCols[reportList[i]] << ", ";
3141 // if (f < 0.001)
3142 // myfile << setw(7) << " <.001 ";
3143 // else
3144 // myfile << setw(7) << setprecision(3) << f;
3145 // myfile << endl;
3146
3147 // cRows += countRemovedRows[reportList[i]];
3148 // cCols += countRemovedCols[reportList[i]];
3149 // }
3150
3151 // if (!noPostSolve) {
3152 // if (cRows != numRowOriginal - numRow) cout << "Wrong row reduction
3153 // count\n"; if (cCols != numColOriginal - numCol) cout << "Wrong col
3154 // reduction count\n";
3155
3156 // myfile << setw(7) << "Total "
3157 // << ", " << setw(7) << numRowOriginal - numRow << ", " << setw(7)
3158 // << numColOriginal - numCol;
3159 // } else {
3160 // myfile << setw(7) << "Total "
3161 // << ", " << setw(7) << 0 << ", " << setw(7) << 0;
3162 // }
3163 // myfile << endl << " \\\\ " << endl;
3164 // myfile.close();
3165 // }
3166
resizeImpliedBounds()3167 void Presolve::resizeImpliedBounds() {
3168 // implied bounds for crashes
3169 // row duals
3170 vector<double> temp = implRowDualLower;
3171 vector<double> teup = implRowDualUpper;
3172 implRowDualLower.resize(numRow);
3173 implRowDualUpper.resize(numRow);
3174
3175 int k = 0;
3176 for (int i = 0; i < numRowOriginal; ++i)
3177 if (flagRow.at(i)) {
3178 implRowDualLower.at(k) = temp.at(i);
3179 implRowDualUpper.at(k) = teup.at(i);
3180 k++;
3181 }
3182
3183 // row value
3184 temp = implRowValueLower;
3185 teup = implRowValueUpper;
3186 implRowValueLower.resize(numRow);
3187 implRowValueUpper.resize(numRow);
3188 k = 0;
3189 for (int i = 0; i < numRowOriginal; ++i)
3190 if (flagRow.at(i)) {
3191 if (temp.at(i) < rowLower.at(i)) temp.at(i) = rowLower.at(i);
3192 implRowValueLower.at(k) = temp.at(i);
3193 if (teup.at(i) > rowUpper.at(i)) teup.at(i) = rowUpper.at(i);
3194 implRowValueUpper.at(k) = teup.at(i);
3195 k++;
3196 }
3197
3198 // column dual
3199 temp = implColDualLower;
3200 teup = implColDualUpper;
3201 implColDualLower.resize(numCol);
3202 implColDualUpper.resize(numCol);
3203
3204 k = 0;
3205 for (int i = 0; i < numColOriginal; ++i)
3206 if (flagCol.at(i)) {
3207 implColDualLower.at(k) = temp.at(i);
3208 implColDualUpper.at(k) = teup.at(i);
3209 k++;
3210 }
3211
3212 // column value
3213 temp = implColLower;
3214 teup = implColUpper;
3215 implColLower.resize(numCol);
3216 implColUpper.resize(numCol);
3217
3218 k = 0;
3219 for (int i = 0; i < numColOriginal; ++i)
3220 if (flagCol.at(i)) {
3221 if (temp.at(i) < colLower.at(i)) temp.at(i) = colLower.at(i);
3222 implColLower.at(k) = temp.at(i);
3223 if (teup.at(i) > colUpper.at(i)) teup.at(i) = colUpper.at(i);
3224 implColUpper.at(k) = teup.at(i);
3225 k++;
3226 }
3227 }
3228
getSingRowElementIndexInAR(int i)3229 int Presolve::getSingRowElementIndexInAR(int i) {
3230 assert(i >= 0 && i < numRow);
3231 int k = ARstart.at(i);
3232 while (k < ARstart[i + 1] && !flagCol.at(ARindex.at(k))) k++;
3233 if (k >= ARstart.at(i + 1)) {
3234 return -1;
3235 }
3236 int rest = k + 1;
3237 while (rest < ARstart.at(i + 1) && !flagCol.at(ARindex.at(rest))) ++rest;
3238 if (rest < ARstart.at(i + 1)) {
3239 return -1;
3240 }
3241 return k;
3242 }
3243
getSingColElementIndexInA(int j)3244 int Presolve::getSingColElementIndexInA(int j) {
3245 int k = Astart.at(j);
3246 assert(k >= 0 && k < (int)Aindex.size());
3247 assert(Aindex[k] >= 0 && Aindex[k] < numRow);
3248 assert(flagRow.size() == (unsigned int)numRow);
3249
3250 while (!flagRow.at(Aindex.at(k))) ++k;
3251 if (k >= Aend.at(j)) {
3252 assert(nzCol[j] == 0);
3253 return -2;
3254 }
3255 int rest = k + 1;
3256 while (rest < Aend.at(j) && !flagRow.at(Aindex.at(rest))) ++rest;
3257 if (rest < Aend.at(j)) {
3258 // Occurs if a singleton column is no longer singleton.
3259 return -1;
3260 }
3261 return k;
3262 }
3263
testAnAR(int post)3264 void Presolve::testAnAR(int post) {
3265 int rows = numRow;
3266 int cols = numCol;
3267
3268 double valueA = 0;
3269 double valueAR = 0;
3270 bool hasValueA, hasValueAR;
3271
3272 if (post) {
3273 rows = numRowOriginal;
3274 cols = numColOriginal;
3275 }
3276
3277 // check that A = AR
3278 for (int i = 0; i < rows; ++i) {
3279 for (int j = 0; j < cols; ++j) {
3280 if (post == 0)
3281 if (!flagRow.at(i) || !flagCol.at(j)) continue;
3282 hasValueA = false;
3283 for (int k = Astart.at(j); k < Aend.at(j); ++k)
3284 if (Aindex.at(k) == i) {
3285 hasValueA = true;
3286 valueA = Avalue.at(k);
3287 }
3288
3289 hasValueAR = false;
3290 for (int k = ARstart.at(i); k < ARstart.at(i + 1); ++k)
3291 if (ARindex.at(k) == j) {
3292 hasValueAR = true;
3293 valueAR = ARvalue.at(k);
3294 }
3295
3296 if (hasValueA != hasValueAR)
3297 cout << " MATRIX is0 DIFF row=" << i << " col=" << j
3298 << " ------------A: " << hasValueA
3299 << " AR: " << hasValueAR << endl;
3300 else if (hasValueA && valueA != valueAR)
3301 cout << " MATRIX VAL DIFF row=" << i << " col=" << j
3302 << " ------------A: " << valueA << " AR: " << valueAR
3303 << endl;
3304 }
3305 }
3306
3307 if (post == 0) {
3308 // check nz
3309 int nz = 0;
3310 for (int i = 0; i < rows; ++i) {
3311 if (!flagRow.at(i)) continue;
3312 nz = 0;
3313 for (int k = ARstart.at(i); k < ARstart.at(i + 1); ++k)
3314 if (flagCol.at(ARindex.at(k))) nz++;
3315 if (nz != nzRow.at(i))
3316 cout << " NZ ROW DIFF row=" << i << " nzRow=" << nzRow.at(i)
3317 << " actually " << nz << "------------" << endl;
3318 }
3319
3320 for (int j = 0; j < cols; ++j) {
3321 if (!flagCol.at(j)) continue;
3322 nz = 0;
3323 for (int k = Astart.at(j); k < Aend.at(j); ++k)
3324 if (flagRow.at(Aindex.at(k))) nz++;
3325 if (nz != nzCol.at(j))
3326 cout << " NZ COL DIFF col=" << j << " nzCol=" << nzCol.at(j)
3327 << " actually " << nz << "------------" << endl;
3328 }
3329 }
3330 }
3331
primalPostsolve(const std::vector<double> & reduced_solution,HighsSolution & recovered_solution)3332 HighsPostsolveStatus Presolve::primalPostsolve(
3333 const std::vector<double>& reduced_solution,
3334 HighsSolution& recovered_solution) {
3335 colValue = reduced_solution;
3336
3337 makeACopy(); // so we can efficiently calculate primal and dual
3338 // values
3339
3340 // iKKTcheck = false;
3341 // set corresponding parts of solution vectors:
3342 int j_index = 0;
3343 vector<int> eqIndexOfReduced(numCol, -1);
3344 vector<int> eqIndexOfReduROW(numRow, -1);
3345 for (int i = 0; i < numColOriginal; ++i)
3346 if (cIndex.at(i) > -1) {
3347 eqIndexOfReduced.at(j_index) = i;
3348 ++j_index;
3349 }
3350 j_index = 0;
3351 for (int i = 0; i < numRowOriginal; ++i)
3352 if (rIndex.at(i) > -1) {
3353 eqIndexOfReduROW.at(j_index) = i;
3354 ++j_index;
3355 }
3356
3357 for (int i = 0; i < numCol; ++i) {
3358 int iCol = eqIndexOfReduced.at(i);
3359 assert(iCol < (int)valuePrimal.size());
3360 assert(iCol >= 0);
3361 valuePrimal[iCol] = colValue.at(i);
3362 // valueColDual[iCol] = colDual.at(i);
3363 // col_status.at(iCol) = temp_col_status.at(i);
3364 }
3365
3366 // cmpNBF(-1, -1);
3367 // testBasisMatrixSingularity();
3368
3369 if (iKKTcheck) {
3370 cout << std::endl << "~~~~~ KKT check on HiGHS solution ~~~~~\n";
3371 checkKkt();
3372 }
3373
3374 vector<int> fRjs;
3375 while (!chng.empty()) {
3376 change c = chng.top();
3377 chng.pop();
3378 // cout<<"chng.pop: "<<c.col<<" "<<c.row << endl;
3379
3380 switch (c.type) {
3381 case AGGREGATOR: {
3382 // restore solution, basis, flags, and colCostAtEl
3383 aggregatorStack.back().postsolveStack.undo(flagCol, flagRow,
3384 valuePrimal);
3385
3386 // restore AR to state before the aggregator got called
3387 ARstart = std::move(aggregatorStack.back().ARstartAtCall);
3388 ARindex = std::move(aggregatorStack.back().ARindexAtCall);
3389 ARvalue = std::move(aggregatorStack.back().ARvalueAtCall);
3390 colCostAtEl = std::move(aggregatorStack.back().colCostAtCall);
3391 aggregatorStack.pop_back();
3392
3393 // restore A from AR
3394 makeACopy();
3395 break;
3396 }
3397 case TWO_COL_SING_TRIVIAL: {
3398 // WIP
3399 int y = (int)postValue.top();
3400 postValue.pop();
3401 int x = (int)postValue.top();
3402 postValue.pop();
3403 assert(x == c.col);
3404 flagRow[c.row] = true;
3405 flagCol[x] = true;
3406 flagCol[y] = true;
3407 break;
3408 }
3409 case DOUBLETON_EQUATION: { // Doubleton equation row
3410 getDualsDoubletonEquation(c.row, c.col);
3411
3412 // exit(2);
3413 break;
3414 }
3415 case DOUBLETON_EQUATION_ROW_BOUNDS_UPDATE: {
3416 // new bounds from doubleton equation, retrieve old ones
3417 // just for KKT check, not called otherwise
3418 // chk2.addChange(171, c.row, c.col, 0, 0, 0);
3419 break;
3420 }
3421 case DOUBLETON_EQUATION_NEW_X_NONZERO: {
3422 // matrix transformation from doubleton equation, case x still there
3423 // case new x is not 0
3424 // just change value of entry in row for x
3425 int indi;
3426 for (indi = ARstart[c.row]; indi < ARstart[c.row + 1]; ++indi)
3427 if (ARindex.at(indi) == c.col) break;
3428 ARvalue.at(indi) = postValue.top();
3429 for (indi = Astart[c.col]; indi < Aend[c.col]; ++indi)
3430 if (Aindex.at(indi) == c.row) break;
3431 Avalue.at(indi) = postValue.top();
3432
3433 postValue.pop();
3434
3435 break;
3436 }
3437 case DOUBLETON_EQUATION_X_ZERO_INITIALLY: {
3438 // matrix transformation from doubleton equation, retrieve old value
3439 // case when row does not have x initially: entries for row i swap x and
3440 // y cols
3441
3442 const int yindex = (int)postValue.top();
3443 postValue.pop();
3444
3445 // reverse AR for case when x is zero and y entry has moved
3446 int indi;
3447 for (indi = ARstart[c.row]; indi < ARstart[c.row + 1]; ++indi)
3448 if (ARindex.at(indi) == c.col) break;
3449 ARvalue.at(indi) = postValue.top();
3450 ARindex.at(indi) = yindex;
3451
3452 // reverse A for case when x is zero and y entry has moved
3453 for (indi = Astart[c.col]; indi < Aend[c.col]; ++indi)
3454 if (Aindex.at(indi) == c.row) break;
3455
3456 // recover x: column decreases by 1
3457 // if indi is not Aend-1 swap elements indi and Aend-1
3458 if (indi != Aend[c.col] - 1) {
3459 double tmp = Avalue[Aend[c.col] - 1];
3460 int tmpi = Aindex[Aend[c.col] - 1];
3461 Avalue[Aend[c.col] - 1] = Avalue.at(indi);
3462 Aindex[Aend[c.col] - 1] = Aindex.at(indi);
3463 Avalue.at(indi) = tmp;
3464 Aindex.at(indi) = tmpi;
3465 }
3466 Aend[c.col]--;
3467
3468 // recover y: column increases by 1
3469 // update A: append X column to end of array
3470 int st = Avalue.size();
3471 for (int ind = Astart[yindex]; ind < Aend[yindex]; ++ind) {
3472 Avalue.push_back(Avalue.at(ind));
3473 Aindex.push_back(Aindex.at(ind));
3474 }
3475 Avalue.push_back(postValue.top());
3476 Aindex.push_back(c.row);
3477 Astart[yindex] = st;
3478 Aend[yindex] = Avalue.size();
3479
3480 postValue.pop();
3481
3482 break;
3483 }
3484 case DOUBLETON_EQUATION_NEW_X_ZERO_AR_UPDATE: {
3485 // sp case x disappears row representation change
3486 int indi;
3487 for (indi = ARstart[c.row]; indi < ARstart[c.row + 1]; ++indi)
3488 if (ARindex.at(indi) == numColOriginal) break;
3489 ARindex.at(indi) = c.col;
3490 ARvalue.at(indi) = postValue.top();
3491
3492 postValue.pop();
3493
3494 break;
3495 }
3496 case DOUBLETON_EQUATION_NEW_X_ZERO_A_UPDATE: {
3497 // sp case x disappears column representation change
3498 // here A is copied from AR array at end of presolve so need to expand x
3499 // column Aend[c.col]++; wouldn't do because old value is overriden
3500 double oldXvalue = postValue.top();
3501 postValue.pop();
3502 int x = c.col;
3503
3504 // update A: append X column to end of array
3505 int st = Avalue.size();
3506 for (int ind = Astart.at(x); ind < Aend.at(x); ++ind) {
3507 Avalue.push_back(Avalue.at(ind));
3508 Aindex.push_back(Aindex.at(ind));
3509 }
3510 Avalue.push_back(oldXvalue);
3511 Aindex.push_back(c.row);
3512 Astart.at(x) = st;
3513 Aend.at(x) = Avalue.size();
3514
3515 break;
3516 }
3517 case EMPTY_ROW: {
3518 flagRow[c.row] = 1;
3519 break;
3520 }
3521 case SING_ROW: {
3522 // valuePrimal is already set for this one, colDual also, we need
3523 // rowDual. AR copy keeps full matrix. col dual maybe infeasible, we
3524 // need to check. recover old bounds and see
3525 // getDualsSingletonRow(c.row, c.col);
3526 oldBounds.pop();
3527 postValue.pop();
3528
3529 break;
3530 }
3531 case FORCING_ROW_VARIABLE:
3532 oldBounds.pop();
3533 flagCol[c.col] = 1;
3534 break;
3535 case FORCING_ROW: {
3536 flagRow[c.row] = 1;
3537 break;
3538 }
3539 case REDUNDANT_ROW: {
3540 flagRow[c.row] = 1;
3541 break;
3542 }
3543 case FREE_SING_COL:
3544 case IMPLIED_FREE_SING_COL: {
3545 // colDual rowDual already set.
3546 // calculate row value without xj
3547 double aij = getaij(c.row, c.col);
3548 double sum = 0;
3549 for (int k = ARstart[c.row]; k < ARstart[c.row + 1]; ++k)
3550 if (flagCol.at(ARindex.at(k)))
3551 sum += valuePrimal.at(ARindex.at(k)) * ARvalue.at(k);
3552
3553 double rowlb = postValue.top();
3554 postValue.pop();
3555 double rowub = postValue.top();
3556 postValue.pop();
3557
3558 // calculate xj
3559 if (rowlb == rowub)
3560 valuePrimal[c.col] = (rowlb - sum) / aij;
3561 else if (colCostAtEl[c.col] > 0) {
3562 // we are interested in the lowest possible value of x:
3563 // max { l_j, bound implied by row i }
3564 double bndL;
3565 if (aij > 0)
3566 bndL = (rowlb - sum) / aij;
3567 else
3568 bndL = (rowub - sum) / aij;
3569 valuePrimal[c.col] = max(colLowerOriginal[c.col], bndL);
3570 } else if (colCostAtEl[c.col] < 0) {
3571 // we are interested in the highest possible value of x:
3572 // min { u_j, bound implied by row i }
3573 double bndU;
3574 if (aij < 0)
3575 bndU = (rowlb - sum) / aij;
3576 else
3577 bndU = (rowub - sum) / aij;
3578 valuePrimal[c.col] = min(colUpperOriginal[c.col], bndU);
3579 } else { // cost is zero
3580 double bndL, bndU;
3581 if (aij > 0) {
3582 bndL = (rowlb - sum) / aij;
3583 bndU = (rowub - sum) / aij;
3584 } else {
3585 bndL = (rowub - sum) / aij;
3586 bndU = (rowlb - sum) / aij;
3587 }
3588 double valuePrimalUB = min(colUpperOriginal[c.col], bndU);
3589 double valuePrimalLB = max(colLowerOriginal[c.col], bndL);
3590 if (valuePrimalUB < valuePrimalLB - tol) {
3591 cout << "Postsolve error: inconsistent bounds for implied free "
3592 "column singleton "
3593 << c.col << endl;
3594 }
3595
3596 if (fabs(valuePrimalLB) < fabs(valuePrimalUB))
3597 valuePrimal[c.col] = valuePrimalLB;
3598 else
3599 valuePrimal[c.col] = valuePrimalUB;
3600 }
3601 sum = sum + valuePrimal[c.col] * aij;
3602
3603 double costAtTimeOfElimination = postValue.top();
3604 postValue.pop();
3605 objShift += (costAtTimeOfElimination * sum) / aij;
3606
3607 flagRow[c.row] = 1;
3608 flagCol[c.col] = 1;
3609 // valueRowDual[c.row] = 0;
3610 break;
3611 }
3612 case SING_COL_DOUBLETON_INEQ: {
3613 assert(false);
3614 // column singleton in a doubleton equation.
3615 // colDual already set. need valuePrimal from stack. maybe change
3616 // rowDual depending on bounds. old bounds kept in oldBounds. variables
3617 // j,k : we eliminated j and are left with changed bounds on k and no
3618 // row. c.col is column COL (K) - eliminated, j is with new bounds
3619 pair<int, vector<double>> p = oldBounds.top();
3620 oldBounds.pop();
3621 const int j = p.first;
3622 vector<double> v = p.second;
3623 // double lbNew = v[0];
3624 // double ubNew = v[1];
3625 double cjNew = v[2];
3626
3627 p = oldBounds.top();
3628 oldBounds.pop();
3629 v = p.second;
3630 double ubOld = v[1];
3631 double lbOld = v[0];
3632 double cjOld = v[2];
3633
3634 p = oldBounds.top();
3635 oldBounds.pop();
3636 v = p.second;
3637 double ubCOL = v[1];
3638 double lbCOL = v[0];
3639 double ck = v[2];
3640
3641 double rowlb = postValue.top();
3642 postValue.pop();
3643 double rowub = postValue.top();
3644 postValue.pop();
3645 double aik = postValue.top();
3646 postValue.pop();
3647 double aij = postValue.top();
3648 postValue.pop();
3649 double xj = valuePrimal.at(j);
3650
3651 // calculate xk, depending on signs of coeff and cost
3652 double upp = HIGHS_CONST_INF;
3653 double low = -HIGHS_CONST_INF;
3654
3655 if ((aij > 0 && aik > 0) || (aij < 0 && aik < 0)) {
3656 if (rowub < HIGHS_CONST_INF) upp = (rowub - aij * xj) / aik;
3657 if (rowlb > -HIGHS_CONST_INF) low = (rowlb - aij * xj) / aik;
3658 } else {
3659 if (rowub < HIGHS_CONST_INF) upp = (rowub - aij * xj) / aik;
3660 if (rowlb > -HIGHS_CONST_INF) low = (rowlb - aij * xj) / aik;
3661 }
3662
3663 double xkValue = 0;
3664 if (ck == 0) {
3665 if (low < 0 && upp > 0)
3666 xkValue = 0;
3667 else if (fabs(low) < fabs(upp))
3668 xkValue = low;
3669 else
3670 xkValue = upp;
3671 }
3672
3673 else if ((ck > 0 && aik > 0) || (ck < 0 && aik < 0)) {
3674 assert(low > -HIGHS_CONST_INF);
3675 xkValue = low;
3676 } else if ((ck > 0 && aik < 0) || (ck < 0 && aik > 0)) {
3677 assert(low < HIGHS_CONST_INF);
3678 xkValue = upp;
3679 }
3680
3681 // primal value and objective shift
3682 valuePrimal[c.col] = xkValue;
3683 objShift += -cjNew * xj + cjOld * xj + ck * xkValue;
3684
3685 // fix duals
3686 double rowVal = aij * xj + aik * xkValue;
3687
3688 // If row is strictly between bounds:
3689 // Row is basic and column is non basic.
3690 if ((rowub == HIGHS_CONST_INF || (rowub - rowVal > tol)) &&
3691 (rowlb == -HIGHS_CONST_INF || (rowVal - rowlb > tol))) {
3692 row_status.at(c.row) = HighsBasisStatus::BASIC;
3693 col_status.at(c.col) = HighsBasisStatus::NONBASIC;
3694 valueRowDual[c.row] = 0;
3695 flagRow[c.row] = 1;
3696 valueColDual[c.col] = getColumnDualPost(c.col);
3697 } else {
3698 // row is at a bound
3699 // case fabs(rowlb - rowub) < tol
3700 double lo = -HIGHS_CONST_INF;
3701 double up = HIGHS_CONST_INF;
3702
3703 if (fabs(rowub - rowVal) <= tol) {
3704 lo = 0;
3705 up = HIGHS_CONST_INF;
3706 } else if (fabs(rowlb - rowVal) <= tol) {
3707 lo = -HIGHS_CONST_INF;
3708 up = 0;
3709 }
3710
3711 colCostAtEl.at(j) = cjOld; // revert cost before calculating duals
3712 getBoundOnLByZj(c.row, j, &lo, &up, lbOld, ubOld);
3713 getBoundOnLByZj(c.row, c.col, &lo, &up, lbCOL, ubCOL);
3714
3715 // calculate yi
3716 if (lo - up > tol)
3717 cout << "PR: Error in postsolving doubleton inequality " << c.row
3718 << " : inconsistent bounds for its dual value." << std::endl;
3719
3720 // WARNING: bound_row_dual not used. commented out to surpress warning
3721 // but maybe this causes trouble. Look into when you do dual postsolve
3722 // again (todo)
3723 //
3724 //
3725 // double bound_row_dual = 0;
3726 // if (lo > 0) {
3727 // bound_row_dual = lo;
3728 // } else if (up < 0) {
3729 // bound_row_dual = up;
3730 // }
3731
3732 // kxx
3733 // if (lo > 0 || up < 0)
3734 if (lo > 0 || up < 0 || ck != 0) {
3735 // row is nonbasic
3736 // since either dual value zero for it is infeasible
3737 // or the column cost has changed for col j hence the row dual has
3738 // to be nonzero to balance out the Stationarity of Lagrangian.
3739 row_status.at(c.row) = HighsBasisStatus::NONBASIC;
3740 col_status.at(c.col) = HighsBasisStatus::BASIC;
3741 valueColDual[c.col] = 0;
3742 flagRow[c.row] = 1;
3743 valueRowDual[c.row] = getRowDualPost(c.row, c.col);
3744 valueColDual[j] = getColumnDualPost(j);
3745 } else {
3746 // zero row dual is feasible, set row to basic and column to
3747 // nonbasic.
3748 row_status.at(c.row) = HighsBasisStatus::BASIC;
3749 col_status.at(c.col) = HighsBasisStatus::NONBASIC;
3750 valueRowDual[c.row] = 0;
3751 flagRow[c.row] = 1;
3752 valueColDual[c.col] = getColumnDualPost(c.col);
3753 }
3754 }
3755
3756 flagCol[c.col] = 1;
3757
3758 // exit(2);
3759 break;
3760 }
3761 case EMPTY_COL:
3762 case DOMINATED_COLS:
3763 case WEAKLY_DOMINATED_COLS: {
3764 // got valuePrimal, need colDual
3765 flagCol[c.col] = 1;
3766 break;
3767 }
3768
3769 case FIXED_COL: {
3770 // got valuePrimal, need colDual
3771 flagCol[c.col] = 1;
3772 break;
3773 }
3774 }
3775 // cmpNBF(c.row, c.col);
3776 }
3777
3778 // cmpNBF();
3779
3780 // now recover original model data to pass back to HiGHS
3781 // A is already recovered!
3782 // however, A is expressed in terms of Astart, Aend and columns are in
3783 // different order so
3784 makeACopy();
3785
3786 numRow = numRowOriginal;
3787 numCol = numColOriginal;
3788 numTot = numRow + numCol;
3789
3790 rowUpper = rowUpperOriginal;
3791 rowLower = rowLowerOriginal;
3792
3793 colUpper = colUpperOriginal;
3794 colLower = colLowerOriginal;
3795
3796 colCost = colCostOriginal;
3797
3798 colValue = valuePrimal;
3799
3800 rowValue.assign(numRow, 0);
3801 for (int i = 0; i < numRowOriginal; ++i) {
3802 for (int k = ARstart.at(i); k < ARstart.at(i + 1); ++k)
3803 rowValue.at(i) += valuePrimal.at(ARindex.at(k)) * ARvalue.at(k);
3804 }
3805
3806 // cout<<"Singularity check at end of postsolve: ";
3807 // testBasisMatrixSingularity();
3808
3809 // Save solution to PresolveComponentData.
3810 recovered_solution.col_value = colValue;
3811 recovered_solution.row_value = rowValue;
3812
3813 return HighsPostsolveStatus::SolutionRecovered;
3814 }
3815 // todo: error reporting.
postsolve(const HighsSolution & reduced_solution,const HighsBasis & reduced_basis,HighsSolution & recovered_solution,HighsBasis & recovered_basis)3816 HighsPostsolveStatus Presolve::postsolve(const HighsSolution& reduced_solution,
3817 const HighsBasis& reduced_basis,
3818 HighsSolution& recovered_solution,
3819 HighsBasis& recovered_basis) {
3820 colValue = reduced_solution.col_value;
3821 colDual = reduced_solution.col_dual;
3822 rowDual = reduced_solution.row_dual;
3823
3824 col_status = reduced_basis.col_status;
3825 row_status = reduced_basis.row_status;
3826
3827 makeACopy(); // so we can efficiently calculate primal and dual values
3828
3829 // iKKTcheck = false;
3830 // set corresponding parts of solution vectors:
3831 int j_index = 0;
3832 vector<int> eqIndexOfReduced(numCol, -1);
3833 vector<int> eqIndexOfReduROW(numRow, -1);
3834 for (int i = 0; i < numColOriginal; ++i)
3835 if (cIndex.at(i) > -1) {
3836 eqIndexOfReduced.at(j_index) = i;
3837 ++j_index;
3838 }
3839 j_index = 0;
3840 for (int i = 0; i < numRowOriginal; ++i)
3841 if (rIndex.at(i) > -1) {
3842 eqIndexOfReduROW.at(j_index) = i;
3843 ++j_index;
3844 }
3845
3846 vector<HighsBasisStatus> temp_col_status = col_status;
3847 vector<HighsBasisStatus> temp_row_status = row_status;
3848
3849 nonbasicFlag.assign(numColOriginal + numRowOriginal, 1);
3850 col_status.assign(numColOriginal, HighsBasisStatus::NONBASIC); // Was LOWER
3851 row_status.assign(numRowOriginal, HighsBasisStatus::NONBASIC); // Was LOWER
3852
3853 for (int i = 0; i < numCol; ++i) {
3854 int iCol = eqIndexOfReduced.at(i);
3855 assert(iCol < (int)valuePrimal.size());
3856 assert(iCol < (int)valueColDual.size());
3857 assert(iCol >= 0);
3858 valuePrimal[iCol] = colValue.at(i);
3859 valueColDual[iCol] = colDual.at(i);
3860 col_status.at(iCol) = temp_col_status.at(i);
3861 }
3862
3863 for (int i = 0; i < numRow; ++i) {
3864 int iRow = eqIndexOfReduROW.at(i);
3865 valueRowDual[iRow] = rowDual.at(i);
3866 row_status.at(iRow) = temp_row_status.at(i);
3867 }
3868
3869 // cmpNBF(-1, -1);
3870 // testBasisMatrixSingularity();
3871
3872 if (iKKTcheck) {
3873 cout << std::endl << "~~~~~ KKT check on HiGHS solution ~~~~~\n";
3874 checkKkt();
3875 }
3876
3877 vector<int> fRjs;
3878 while (!chng.empty()) {
3879 change c = chng.top();
3880 chng.pop();
3881 // cout<<"chng.pop: "<<c.col<<" "<<c.row << endl;
3882
3883 setBasisElement(c);
3884 switch (c.type) {
3885 case AGGREGATOR: {
3886 // restore solution, basis, flags, and colCostAtEl
3887 aggregatorStack.back().postsolveStack.undo(
3888 flagCol, flagRow, valuePrimal, valueColDual, valueRowDual,
3889 col_status, row_status);
3890
3891 // restore AR to state before the aggregator got called
3892 ARstart = std::move(aggregatorStack.back().ARstartAtCall);
3893 ARindex = std::move(aggregatorStack.back().ARindexAtCall);
3894 ARvalue = std::move(aggregatorStack.back().ARvalueAtCall);
3895 colCostAtEl = std::move(aggregatorStack.back().colCostAtCall);
3896 aggregatorStack.pop_back();
3897
3898 // restore A from AR
3899 makeACopy();
3900 break;
3901 }
3902 case TWO_COL_SING_TRIVIAL: {
3903 // WIP
3904 int y = (int)postValue.top();
3905 postValue.pop();
3906 int x = (int)postValue.top();
3907 postValue.pop();
3908 assert(x == c.col);
3909 flagRow[c.row] = true;
3910 flagCol[x] = true;
3911 flagCol[y] = true;
3912 row_status.at(c.row) = HighsBasisStatus::BASIC;
3913 break;
3914 }
3915 case DOUBLETON_EQUATION: { // Doubleton equation row
3916 getDualsDoubletonEquation(c.row, c.col);
3917
3918 if (iKKTcheck == 1) {
3919 if (chk2.print == 1)
3920 cout
3921 << "----KKT check after doubleton equation re-introduced. Row: "
3922 << c.row << ", column " << c.col << " -----\n";
3923 chk2.addChange(17, c.row, c.col, valuePrimal[c.col],
3924 valueColDual[c.col], valueRowDual[c.row]);
3925 checkKkt();
3926 }
3927 // exit(2);
3928 break;
3929 }
3930 case DOUBLETON_EQUATION_ROW_BOUNDS_UPDATE: {
3931 // new bounds from doubleton equation, retrieve old ones
3932 // just for KKT check, not called otherwise
3933 chk2.addChange(171, c.row, c.col, 0, 0, 0);
3934 break;
3935 }
3936 case DOUBLETON_EQUATION_NEW_X_NONZERO: {
3937 // matrix transformation from doubleton equation, case x still there
3938 // case new x is not 0
3939 // just change value of entry in row for x
3940 int indi;
3941 for (indi = ARstart[c.row]; indi < ARstart[c.row + 1]; ++indi)
3942 if (ARindex.at(indi) == c.col) break;
3943 ARvalue.at(indi) = postValue.top();
3944 for (indi = Astart[c.col]; indi < Aend[c.col]; ++indi)
3945 if (Aindex.at(indi) == c.row) break;
3946 Avalue.at(indi) = postValue.top();
3947
3948 if (iKKTcheck == 1)
3949 chk2.addChange(172, c.row, c.col, postValue.top(), 0, 0);
3950 postValue.pop();
3951
3952 break;
3953 }
3954 case DOUBLETON_EQUATION_X_ZERO_INITIALLY: {
3955 // matrix transformation from doubleton equation, retrieve old value
3956 // case when row does not have x initially: entries for row i swap x and
3957 // y cols
3958
3959 const int yindex = (int)postValue.top();
3960 postValue.pop();
3961
3962 // reverse AR for case when x is zero and y entry has moved
3963 int indi;
3964 for (indi = ARstart[c.row]; indi < ARstart[c.row + 1]; ++indi)
3965 if (ARindex.at(indi) == c.col) break;
3966 ARvalue.at(indi) = postValue.top();
3967 ARindex.at(indi) = yindex;
3968
3969 // reverse A for case when x is zero and y entry has moved
3970 for (indi = Astart[c.col]; indi < Aend[c.col]; ++indi)
3971 if (Aindex.at(indi) == c.row) break;
3972
3973 // recover x: column decreases by 1
3974 // if indi is not Aend-1 swap elements indi and Aend-1
3975 if (indi != Aend[c.col] - 1) {
3976 double tmp = Avalue[Aend[c.col] - 1];
3977 int tmpi = Aindex[Aend[c.col] - 1];
3978 Avalue[Aend[c.col] - 1] = Avalue.at(indi);
3979 Aindex[Aend[c.col] - 1] = Aindex.at(indi);
3980 Avalue.at(indi) = tmp;
3981 Aindex.at(indi) = tmpi;
3982 }
3983 Aend[c.col]--;
3984
3985 // recover y: column increases by 1
3986 // update A: append X column to end of array
3987 int st = Avalue.size();
3988 for (int ind = Astart[yindex]; ind < Aend[yindex]; ++ind) {
3989 Avalue.push_back(Avalue.at(ind));
3990 Aindex.push_back(Aindex.at(ind));
3991 }
3992 Avalue.push_back(postValue.top());
3993 Aindex.push_back(c.row);
3994 Astart[yindex] = st;
3995 Aend[yindex] = Avalue.size();
3996
3997 double topp = postValue.top();
3998 postValue.pop();
3999 if (iKKTcheck == 1) {
4000 chk2.addChange(173, c.row, c.col, topp, (double)yindex, 0);
4001 }
4002
4003 break;
4004 }
4005 case DOUBLETON_EQUATION_NEW_X_ZERO_AR_UPDATE: {
4006 // sp case x disappears row representation change
4007 int indi;
4008 for (indi = ARstart[c.row]; indi < ARstart[c.row + 1]; ++indi)
4009 if (ARindex.at(indi) == numColOriginal) break;
4010 ARindex.at(indi) = c.col;
4011 ARvalue.at(indi) = postValue.top();
4012
4013 postValue.pop();
4014
4015 break;
4016 }
4017 case DOUBLETON_EQUATION_NEW_X_ZERO_A_UPDATE: {
4018 // sp case x disappears column representation change
4019 // here A is copied from AR array at end of presolve so need to expand x
4020 // column Aend[c.col]++; wouldn't do because old value is overriden
4021 double oldXvalue = postValue.top();
4022 postValue.pop();
4023 int x = c.col;
4024
4025 // update A: append X column to end of array
4026 int st = Avalue.size();
4027 for (int ind = Astart.at(x); ind < Aend.at(x); ++ind) {
4028 Avalue.push_back(Avalue.at(ind));
4029 Aindex.push_back(Aindex.at(ind));
4030 }
4031 Avalue.push_back(oldXvalue);
4032 Aindex.push_back(c.row);
4033 Astart.at(x) = st;
4034 Aend.at(x) = Avalue.size();
4035
4036 break;
4037 }
4038 case EMPTY_ROW: {
4039 valueRowDual[c.row] = 0;
4040 flagRow[c.row] = 1;
4041 if (iKKTcheck == 1) {
4042 if (chk2.print == 1)
4043 cout << "----KKT check after empty row " << c.row
4044 << " re-introduced-----\n";
4045 chk2.addChange(0, c.row, 0, 0, 0, 0);
4046 checkKkt();
4047 }
4048 break;
4049 }
4050 case SING_ROW: {
4051 // valuePrimal is already set for this one, colDual also, we need
4052 // rowDual. AR copy keeps full matrix. col dual maybe infeasible, we
4053 // need to check. recover old bounds and see
4054 getDualsSingletonRow(c.row, c.col);
4055
4056 if (iKKTcheck == 1) {
4057 if (chk2.print == 1)
4058 cout << "----KKT check after singleton row " << c.row
4059 << " re-introduced. Variable: " << c.col << " -----\n";
4060 chk2.addChange(1, c.row, c.col, valuePrimal[c.col],
4061 valueColDual[c.col], valueRowDual[c.row]);
4062 checkKkt();
4063 }
4064 break;
4065 }
4066 case FORCING_ROW_VARIABLE:
4067 fRjs.push_back(c.col);
4068 flagCol[c.col] = 1;
4069 if (iKKTcheck == 1 && valuePrimal[c.col] != 0)
4070 chk2.addChange(22, c.row, c.col, 0, 0, 0);
4071 break;
4072 case FORCING_ROW: {
4073 string str = getDualsForcingRow(c.row, fRjs);
4074
4075 if (iKKTcheck == 1) {
4076 if (chk2.print == 1)
4077 cout << "----KKT check after forcing row " << c.row
4078 << " re-introduced. Variable(s): " << str << " -----\n";
4079 chk2.addChange(3, c.row, 0, 0, 0, valueRowDual[c.row]);
4080 checkKkt();
4081 }
4082 fRjs.clear();
4083 break;
4084 }
4085 case REDUNDANT_ROW: {
4086 // this is not zero if the row bounds got relaxed and transferred to a
4087 // column which then had a nonzero dual.
4088 valueRowDual[c.row] = 0;
4089
4090 flagRow[c.row] = 1;
4091
4092 if (iKKTcheck == 1) {
4093 if (chk2.print == 1)
4094 cout << "----KKT check after redundant row " << c.row
4095 << " re-introduced.----------------\n";
4096 checkKkt();
4097 }
4098 break;
4099 }
4100 case FREE_SING_COL:
4101 case IMPLIED_FREE_SING_COL: {
4102 // colDual rowDual already set.
4103 // calculate row value without xj
4104 double aij = getaij(c.row, c.col);
4105 double sum = 0;
4106 for (int k = ARstart[c.row]; k < ARstart[c.row + 1]; ++k)
4107 if (flagCol.at(ARindex.at(k)))
4108 sum += valuePrimal.at(ARindex.at(k)) * ARvalue.at(k);
4109
4110 double rowlb = postValue.top();
4111 postValue.pop();
4112 double rowub = postValue.top();
4113 postValue.pop();
4114
4115 // calculate xj
4116 if (valueRowDual[c.row] < 0) {
4117 // row is at lower bound
4118 valuePrimal[c.col] = (rowlb - sum) / aij;
4119 } else if (valueRowDual[c.row] > 0) {
4120 // row is at upper bound
4121 valuePrimal[c.col] = (rowub - sum) / aij;
4122 } else if (rowlb == rowub)
4123 valuePrimal[c.col] = (rowlb - sum) / aij;
4124 else if (colCostAtEl[c.col] > 0) {
4125 // we are interested in the lowest possible value of x:
4126 // max { l_j, bound implied by row i }
4127 double bndL;
4128 if (aij > 0)
4129 bndL = (rowlb - sum) / aij;
4130 else
4131 bndL = (rowub - sum) / aij;
4132 valuePrimal[c.col] = max(colLowerOriginal[c.col], bndL);
4133 } else if (colCostAtEl[c.col] < 0) {
4134 // we are interested in the highest possible value of x:
4135 // min { u_j, bound implied by row i }
4136 double bndU;
4137 if (aij < 0)
4138 bndU = (rowlb - sum) / aij;
4139 else
4140 bndU = (rowub - sum) / aij;
4141 valuePrimal[c.col] = min(colUpperOriginal[c.col], bndU);
4142 } else { // cost is zero
4143 double bndL, bndU;
4144 if (aij > 0) {
4145 bndL = (rowlb - sum) / aij;
4146 bndU = (rowub - sum) / aij;
4147 } else {
4148 bndL = (rowub - sum) / aij;
4149 bndU = (rowlb - sum) / aij;
4150 }
4151 double valuePrimalUB = min(colUpperOriginal[c.col], bndU);
4152 double valuePrimalLB = max(colLowerOriginal[c.col], bndL);
4153 if (valuePrimalUB < valuePrimalLB - tol) {
4154 cout << "Postsolve error: inconsistent bounds for implied free "
4155 "column singleton "
4156 << c.col << endl;
4157 }
4158
4159 if (fabs(valuePrimalLB) < fabs(valuePrimalUB))
4160 valuePrimal[c.col] = valuePrimalLB;
4161 else
4162 valuePrimal[c.col] = valuePrimalUB;
4163 }
4164 sum = sum + valuePrimal[c.col] * aij;
4165
4166 double costAtTimeOfElimination = postValue.top();
4167 postValue.pop();
4168 objShift += (costAtTimeOfElimination * sum) / aij;
4169
4170 flagRow[c.row] = 1;
4171 flagCol[c.col] = 1;
4172 // valueRowDual[c.row] = 0;
4173
4174 if (iKKTcheck == 1) {
4175 chk2.addCost(c.col, costAtTimeOfElimination);
4176 if (c.type == FREE_SING_COL && chk2.print == 1)
4177 cout << "----KKT check after free col singleton " << c.col
4178 << " re-introduced. Row: " << c.row << " -----\n";
4179 else if (c.type == IMPLIED_FREE_SING_COL && chk2.print == 1)
4180 cout << "----KKT check after implied free col singleton " << c.col
4181 << " re-introduced. Row: " << c.row << " -----\n";
4182 chk2.addChange(4, c.row, c.col, valuePrimal[c.col],
4183 valueColDual[c.col], valueRowDual[c.row]);
4184 checkKkt();
4185 }
4186 break;
4187 }
4188 case SING_COL_DOUBLETON_INEQ: {
4189 // column singleton in a doubleton equation.
4190 // colDual already set. need valuePrimal from stack. maybe change
4191 // rowDual depending on bounds. old bounds kept in oldBounds. variables
4192 // j,k : we eliminated j and are left with changed bounds on k and no
4193 // row. c.col is column COL (K) - eliminated, j is with new bounds
4194 pair<int, vector<double>> p = oldBounds.top();
4195 oldBounds.pop();
4196 const int j = p.first;
4197 vector<double> v = p.second;
4198 // double lbNew = v[0];
4199 // double ubNew = v[1];
4200 double cjNew = v[2];
4201
4202 p = oldBounds.top();
4203 oldBounds.pop();
4204 v = p.second;
4205 double ubOld = v[1];
4206 double lbOld = v[0];
4207 double cjOld = v[2];
4208
4209 p = oldBounds.top();
4210 oldBounds.pop();
4211 v = p.second;
4212 double ubCOL = v[1];
4213 double lbCOL = v[0];
4214 double ck = v[2];
4215
4216 double rowlb = postValue.top();
4217 postValue.pop();
4218 double rowub = postValue.top();
4219 postValue.pop();
4220 double aik = postValue.top();
4221 postValue.pop();
4222 double aij = postValue.top();
4223 postValue.pop();
4224 double xj = valuePrimal.at(j);
4225
4226 // calculate xk, depending on signs of coeff and cost
4227 double upp = HIGHS_CONST_INF;
4228 double low = -HIGHS_CONST_INF;
4229
4230 if ((aij > 0 && aik > 0) || (aij < 0 && aik < 0)) {
4231 if (rowub < HIGHS_CONST_INF) upp = (rowub - aij * xj) / aik;
4232 if (rowlb > -HIGHS_CONST_INF) low = (rowlb - aij * xj) / aik;
4233 } else {
4234 if (rowub < HIGHS_CONST_INF) upp = (rowub - aij * xj) / aik;
4235 if (rowlb > -HIGHS_CONST_INF) low = (rowlb - aij * xj) / aik;
4236 }
4237
4238 double xkValue = 0;
4239 if (ck == 0) {
4240 if (low < 0 && upp > 0)
4241 xkValue = 0;
4242 else if (fabs(low) < fabs(upp))
4243 xkValue = low;
4244 else
4245 xkValue = upp;
4246 }
4247
4248 else if ((ck > 0 && aik > 0) || (ck < 0 && aik < 0)) {
4249 assert(low > -HIGHS_CONST_INF);
4250 xkValue = low;
4251 } else if ((ck > 0 && aik < 0) || (ck < 0 && aik > 0)) {
4252 assert(low < HIGHS_CONST_INF);
4253 xkValue = upp;
4254 }
4255
4256 // primal value and objective shift
4257 valuePrimal[c.col] = xkValue;
4258 objShift += -cjNew * xj + cjOld * xj + ck * xkValue;
4259
4260 // fix duals
4261 double rowVal = aij * xj + aik * xkValue;
4262
4263 // If row is strictly between bounds:
4264 // Row is basic and column is non basic.
4265 if ((rowub == HIGHS_CONST_INF || (rowub - rowVal > tol)) &&
4266 (rowlb == -HIGHS_CONST_INF || (rowVal - rowlb > tol))) {
4267 row_status.at(c.row) = HighsBasisStatus::BASIC;
4268 col_status.at(c.col) = HighsBasisStatus::NONBASIC;
4269 valueRowDual[c.row] = 0;
4270 flagRow[c.row] = 1;
4271 valueColDual[c.col] = getColumnDualPost(c.col);
4272 } else {
4273 // row is at a bound
4274 // case fabs(rowlb - rowub) < tol
4275 double lo = -HIGHS_CONST_INF;
4276 double up = HIGHS_CONST_INF;
4277
4278 if (fabs(rowub - rowVal) <= tol) {
4279 lo = 0;
4280 up = HIGHS_CONST_INF;
4281 } else if (fabs(rowlb - rowVal) <= tol) {
4282 lo = -HIGHS_CONST_INF;
4283 up = 0;
4284 }
4285
4286 colCostAtEl.at(j) = cjOld; // revert cost before calculating duals
4287 getBoundOnLByZj(c.row, j, &lo, &up, lbOld, ubOld);
4288 getBoundOnLByZj(c.row, c.col, &lo, &up, lbCOL, ubCOL);
4289
4290 // calculate yi
4291 if (lo - up > tol)
4292 cout << "PR: Error in postsolving doubleton inequality " << c.row
4293 << " : inconsistent bounds for its dual value." << std::endl;
4294
4295 // WARNING: bound_row_dual not used. commented out to surpress warning
4296 // but maybe this causes trouble. Look into when you do dual postsolve
4297 // again (todo)
4298 //
4299 //
4300 // double bound_row_dual = 0;
4301 // if (lo > 0) {
4302 // bound_row_dual = lo;
4303 // } else if (up < 0) {
4304 // bound_row_dual = up;
4305 // }
4306
4307 // kxx
4308 // if (lo > 0 || up < 0)
4309 if (lo > 0 || up < 0 || ck != 0) {
4310 // row is nonbasic
4311 // since either dual value zero for it is infeasible
4312 // or the column cost has changed for col j hence the row dual has
4313 // to be nonzero to balance out the Stationarity of Lagrangian.
4314 row_status.at(c.row) = HighsBasisStatus::NONBASIC;
4315 col_status.at(c.col) = HighsBasisStatus::BASIC;
4316 valueColDual[c.col] = 0;
4317 flagRow[c.row] = 1;
4318 valueRowDual[c.row] = getRowDualPost(c.row, c.col);
4319 valueColDual[j] = getColumnDualPost(j);
4320 } else {
4321 // zero row dual is feasible, set row to basic and column to
4322 // nonbasic.
4323 row_status.at(c.row) = HighsBasisStatus::BASIC;
4324 col_status.at(c.col) = HighsBasisStatus::NONBASIC;
4325 valueRowDual[c.row] = 0;
4326 flagRow[c.row] = 1;
4327 valueColDual[c.col] = getColumnDualPost(c.col);
4328 }
4329 }
4330
4331 flagCol[c.col] = 1;
4332
4333 if (iKKTcheck == 1) {
4334 if (chk2.print == 1)
4335 cout << "----KKT check after col singleton " << c.col
4336 << " in doubleton ineq re-introduced. Row: " << c.row
4337 << " -----\n";
4338
4339 chk2.addChange(5, c.row, c.col, valuePrimal[c.col],
4340 valueColDual[c.col], valueRowDual[c.row]);
4341 checkKkt();
4342 }
4343 // exit(2);
4344 break;
4345 }
4346 case EMPTY_COL:
4347 case DOMINATED_COLS:
4348 case WEAKLY_DOMINATED_COLS: {
4349 // got valuePrimal, need colDual
4350 if (c.type != EMPTY_COL) {
4351 double z = colCostAtEl[c.col];
4352 for (int k = Astart[c.col]; k < Astart[c.col + 1]; ++k)
4353 if (flagRow.at(Aindex.at(k)))
4354 z = z + valueRowDual.at(Aindex.at(k)) * Avalue.at(k);
4355 valueColDual[c.col] = z;
4356 }
4357
4358 flagCol[c.col] = 1;
4359 if (iKKTcheck == 1) {
4360 if (c.type == EMPTY_COL && chk2.print == 1)
4361 cout << "----KKT check after empty column " << c.col
4362 << " re-introduced.-----------\n";
4363 else if (c.type == DOMINATED_COLS && chk2.print == 1)
4364 cout << "----KKT check after dominated column " << c.col
4365 << " re-introduced.-----------\n";
4366 else if (c.type == WEAKLY_DOMINATED_COLS && chk2.print == 1)
4367 cout << "----KKT check after weakly dominated column " << c.col
4368 << " re-introduced.-----------\n";
4369
4370 chk2.addChange(6, 0, c.col, valuePrimal[c.col], valueColDual[c.col],
4371 0);
4372 checkKkt();
4373 }
4374 break;
4375 }
4376
4377 case FIXED_COL: {
4378 // got valuePrimal, need colDual
4379 valueColDual[c.col] = getColumnDualPost(c.col);
4380
4381 flagCol[c.col] = 1;
4382 if (iKKTcheck == 1) {
4383 if (chk2.print == 1)
4384 cout << "----KKT check after fixed variable " << c.col
4385 << " re-introduced.-----------\n";
4386 chk2.addChange(7, 0, c.col, valuePrimal[c.col], valueColDual[c.col],
4387 0);
4388 checkKkt();
4389 }
4390 break;
4391 }
4392 }
4393 // cmpNBF(c.row, c.col);
4394 }
4395
4396 // cmpNBF();
4397
4398 // Check number of basic variables
4399 int num_basic_var = 0;
4400 for (int iCol = 0; iCol < numColOriginal; iCol++) {
4401 if (col_status[iCol] == HighsBasisStatus::BASIC) {
4402 assert(num_basic_var < numRowOriginal);
4403 if (num_basic_var == numRowOriginal) {
4404 printf("Error in postsolve: more basic variables than rows\n");
4405 break;
4406 }
4407 num_basic_var++;
4408 }
4409 }
4410 for (int iRow = 0; iRow < numRowOriginal; iRow++) {
4411 // int iVar = numColOriginal + iRow;
4412 if (row_status[iRow] == HighsBasisStatus::BASIC) {
4413 assert(num_basic_var < numRowOriginal);
4414 if (num_basic_var == numRowOriginal) {
4415 printf("Error from postsolve: more basic variables than rows\n");
4416 break;
4417 }
4418 num_basic_var++;
4419 }
4420 }
4421 // Return error if the number of basic variables does not equal the
4422 // number of rows in the original LP
4423 assert(num_basic_var == numRowOriginal);
4424 if (num_basic_var != numRowOriginal) {
4425 printf(
4426 "Error from postsolve: number of basic variables = %d != %d = number "
4427 "of rows\n",
4428 num_basic_var, numRowOriginal);
4429 return HighsPostsolveStatus::BasisError;
4430 }
4431
4432 // now recover original model data to pass back to HiGHS
4433 // A is already recovered!
4434 // however, A is expressed in terms of Astart, Aend and columns are in
4435 // different order so
4436 makeACopy();
4437
4438 numRow = numRowOriginal;
4439 numCol = numColOriginal;
4440 numTot = numRow + numCol;
4441
4442 rowUpper = rowUpperOriginal;
4443 rowLower = rowLowerOriginal;
4444
4445 colUpper = colUpperOriginal;
4446 colLower = colLowerOriginal;
4447
4448 colCost = colCostOriginal;
4449
4450 colValue = valuePrimal;
4451 colDual = valueColDual;
4452 rowDual = valueRowDual;
4453
4454 rowValue.assign(numRow, 0);
4455 for (int i = 0; i < numRowOriginal; ++i) {
4456 for (int k = ARstart.at(i); k < ARstart.at(i + 1); ++k)
4457 rowValue.at(i) += valuePrimal.at(ARindex.at(k)) * ARvalue.at(k);
4458 }
4459
4460 // cout<<"Singularity check at end of postsolve: ";
4461 // testBasisMatrixSingularity();
4462
4463 if (iKKTcheck != 0) {
4464 cout << "~~~~~ KKT check of postsolved solution with DevKkt checker ~~~~~"
4465 << std::endl;
4466
4467 checkKkt(true);
4468 }
4469
4470 // Save solution to PresolveComponentData.
4471 recovered_solution.col_value = colValue;
4472 recovered_solution.col_dual = colDual;
4473 recovered_solution.row_value = rowValue;
4474 recovered_solution.row_dual = rowDual;
4475
4476 recovered_basis.col_status = col_status;
4477 recovered_basis.row_status = row_status;
4478
4479 return HighsPostsolveStatus::SolutionRecovered;
4480 }
4481
checkKkt(bool final)4482 void Presolve::checkKkt(bool final) {
4483 // final = true or intermediate = true
4484 if (!iKKTcheck) return;
4485
4486 // update row value done in initState below.
4487
4488 std::cout << "~~~~~~~~ " << std::endl;
4489 bool intermediate = !final;
4490 dev_kkt_check::State state = initState(intermediate);
4491
4492 dev_kkt_check::KktInfo info = dev_kkt_check::initInfo();
4493
4494 bool pass = dev_kkt_check::checkKkt(state, info);
4495 if (final) {
4496 if (pass)
4497 std::cout << "KKT PASS" << std::endl;
4498 else
4499 std::cout << "KKT FAIL" << std::endl;
4500 }
4501 std::cout << "~~~~~~~~ " << std::endl;
4502 }
4503
setBasisElement(change c)4504 void Presolve::setBasisElement(change c) {
4505 // col_status starts off as [numCol] and has already been increased to
4506 // [numColOriginal] and row_status starts off as [numRow] and has already been
4507 // increased to [numRowOriginal] so fill fill in gaps in both
4508
4509 switch (c.type) {
4510 case EMPTY_ROW: {
4511 if (report_postsolve) {
4512 printf("2.1 : Recover row %3d as %3d (basic): empty row\n", c.row,
4513 numColOriginal + c.row);
4514 }
4515 row_status.at(c.row) = HighsBasisStatus::BASIC;
4516 break;
4517 }
4518 case REDUNDANT_ROW: {
4519 if (report_postsolve) {
4520 printf("2.3 : Recover row %3d as %3d (basic): redundant\n", c.row,
4521 numColOriginal + c.row);
4522 }
4523 row_status.at(c.row) = HighsBasisStatus::BASIC;
4524 break;
4525 }
4526 case FREE_SING_COL:
4527 case IMPLIED_FREE_SING_COL: {
4528 if (report_postsolve) {
4529 printf(
4530 "2.4a: Recover col %3d as %3d (basic): implied free singleton "
4531 "column\n",
4532 c.col, numColOriginal + c.row);
4533 }
4534 col_status.at(c.col) = HighsBasisStatus::BASIC;
4535
4536 if (report_postsolve) {
4537 printf(
4538 "2.5b: Recover row %3d as %3d (nonbasic): implied free singleton "
4539 "column\n",
4540 c.row, numColOriginal + c.row);
4541 }
4542 row_status.at(c.row) = HighsBasisStatus::NONBASIC; // Was LOWER
4543 break;
4544 }
4545 case EMPTY_COL:
4546 case DOMINATED_COLS:
4547 case WEAKLY_DOMINATED_COLS: {
4548 if (report_postsolve) {
4549 printf("2.7 : Recover column %3d (nonbasic): weakly dominated column\n",
4550 c.col);
4551 }
4552 col_status.at(c.col) = HighsBasisStatus::NONBASIC; // Was LOWER
4553 break;
4554 }
4555 case FIXED_COL: { // fixed variable:
4556 // check if it was NOT after singRow
4557 if (chng.size() > 0)
4558 if (chng.top().type != SING_ROW) {
4559 if (report_postsolve) {
4560 printf(
4561 "2.8 : Recover column %3d (nonbasic): weakly dominated "
4562 "column\n",
4563 c.col);
4564 }
4565 col_status.at(c.col) = HighsBasisStatus::NONBASIC; // Was LOWER
4566 }
4567 break;
4568 }
4569 default:
4570 break;
4571 }
4572 }
4573
4574 /* testing and dev
4575 int Presolve::testBasisMatrixSingularity() {
4576
4577 HFactor factor;
4578
4579 //resize matrix in M so we can pass to factor
4580 int i, j, k;
4581 int nz = 0;
4582 int nR = 0;
4583 int nC = 0;
4584
4585 numRowOriginal = rowLowerOriginal.size();
4586 numColOriginal = colLowerOriginal.size();
4587 //arrays to keep track of indices
4588 vector<int> rIndex_(numRowOriginal, -1);
4589 vector<int> cIndex_(numColOriginal, -1);
4590
4591 for (i=0;i<numRowOriginal;++i)
4592 if (flagRow.at(i)) {
4593 for (j = ARstart.at(i); j<ARstart.at(i+1); ++j)
4594 if (flagCol[ARindex.at(j)])
4595 nz ++;
4596 rIndex_.at(i) = nR;
4597 nR++;
4598 }
4599
4600 for (i=0;i<numColOriginal;++i)
4601 if (flagCol.at(i)) {
4602 cIndex_.at(i) = nC;
4603 nC++;
4604 }
4605
4606
4607 //matrix
4608 vector<int> Mstart(nC + 1, 0);
4609 vector<int> Mindex(nz);
4610 vector<double> Mvalue(nz);
4611
4612 vector<int> iwork(nC, 0);
4613
4614 for (i = 0;i<numRowOriginal; ++i)
4615 if (flagRow.at(i))
4616 for (int k = ARstart.at(i); k < ARstart.at(i+1);++k ) {
4617 j = ARindex.at(k);
4618 if (flagCol.at(j))
4619 iwork[cIndex_.at(j)]++;
4620 }
4621 for (i = 1; i <= nC; ++i)
4622 Mstart.at(i) = Mstart[i - 1] + iwork[i - 1];
4623 for (i = 0; i < numColOriginal; ++i)
4624 iwork.at(i) = Mstart.at(i);
4625
4626 for (i = 0; i < numRowOriginal; ++i) {
4627 if (flagRow.at(i)) {
4628 int iRow = rIndex_.at(i);
4629 for (k = ARstart.at(i); k < ARstart[i + 1];++k ) {
4630 j = ARindex.at(k);
4631 if (flagCol.at(j)) {
4632 int iCol = cIndex_.at(j);
4633 int iPut = iwork[iCol]++;
4634 Mindex[iPut] = iRow;
4635 Mvalue[iPut] = ARvalue.at(k);
4636 }
4637 }
4638 }
4639 }
4640
4641 vector<int> bindex(nR);
4642 int countBasic=0;
4643
4644 printf("To recover this test need to use col/row_status\n");
4645 for (int i=0; i< nonbasicFlag.size();++i) {
4646 if (nonbasicFlag.at(i) == 0)
4647 countBasic++;
4648 }
4649
4650 if (countBasic != nR)
4651 cout<<" Wrong count of basic variables: != numRow"<<endl;
4652
4653 int c=0;
4654 for (int i=0; i< nonbasicFlag.size();++i) {
4655 if (nonbasicFlag.at(i) == 0) {
4656 if (i < numColOriginal)
4657 bindex[c] = cIndex_.at(i);
4658 else
4659 bindex[c] = nC + rIndex_[i - numColOriginal];
4660 c++;
4661 }
4662 }
4663
4664 factor.setup(nC, nR, &Mstart[0], &Mindex[0], &Mvalue[0], &bindex[0]);
4665 / * if (1) // for this check both A and M are the full matrix again
4666 {
4667 if (nC - numColOriginal != 0)
4668 cout<<"columns\n";
4669 if (nR - numRowOriginal != 0)
4670 cout<<"rows\n";
4671 for (int i=0; i< Mstart.size();++i)
4672 if (Mstart.at(i) - Astart.at(i) != 0)
4673 cout<<"Mstart "<<i<<"\n";
4674 for (int i=0; i< Mindex.size();++i)
4675 if (Mindex.at(i) - Aindex.at(i) != 0)
4676 cout<<"Mindex "<<i<<"\n";
4677 for (int i=0; i< Mvalue.size();++i)
4678 if (Mvalue.at(i) - Avalue.at(i) != 0)
4679 cout<<"Mvalue "<<i<<"\n";
4680 for (int i=0; i< bindex.size();++i)
4681 if (nonbasicFlag.at(i) - nbffull.at(i) != 0)
4682 cout<<"nbf "<<i<<"\n";
4683 } * /
4684
4685 try {
4686 factor.build();
4687 } catch (runtime_error& error) {
4688 cout << error.what() << endl;
4689 cout << "Postsolve: could not factorize basis matrix." << endl;
4690 return 0;
4691 }
4692 cout << "Postsolve: basis matrix successfully factorized." << endl;
4693
4694 return 1;
4695 }*/
4696
4697 /***
4698 * lo and up refer to the place storing the current bounds on y_row
4699 *
4700 */
getBoundOnLByZj(int row,int j,double * lo,double * up,double colLow,double colUpp)4701 void Presolve::getBoundOnLByZj(int row, int j, double* lo, double* up,
4702 double colLow, double colUpp) {
4703 double cost = colCostAtEl.at(j); // valueColDual.at(j);
4704 double x = -cost;
4705
4706 double sum = 0;
4707 for (int kk = Astart.at(j); kk < Aend.at(j); ++kk)
4708 if (flagRow.at(Aindex.at(kk))) {
4709 sum = sum + Avalue.at(kk) * valueRowDual.at(Aindex.at(kk));
4710 }
4711 x = x - sum;
4712
4713 double aij = getaij(row, j);
4714 x = x / aij;
4715
4716 if (fabs(colLow - colUpp) < tol)
4717 return; // here there is no restriction on zj so no bound on y
4718
4719 if ((valuePrimal.at(j) - colLow) > tol &&
4720 (colUpp - valuePrimal.at(j)) > tol) {
4721 // set both bounds
4722 if (x < *up) *up = x;
4723 if (x > *lo) *lo = x;
4724 }
4725
4726 else if ((valuePrimal.at(j) == colLow && aij < 0) ||
4727 (valuePrimal.at(j) == colUpp && aij > 0)) {
4728 if (x < *up) *up = x;
4729 } else if ((valuePrimal.at(j) == colLow && aij > 0) ||
4730 (valuePrimal.at(j) == colUpp && aij < 0)) {
4731 if (x > *lo) *lo = x;
4732 }
4733 }
4734
4735 /**
4736 * returns z_col
4737 * z = A'y + c
4738 */
getColumnDualPost(int col)4739 double Presolve::getColumnDualPost(int col) {
4740 int row;
4741 double z;
4742 double sum = 0;
4743 for (int cnt = Astart.at(col); cnt < Aend.at(col); cnt++)
4744 if (flagRow.at(Aindex.at(cnt))) {
4745 row = Aindex.at(cnt);
4746 sum = sum + valueRowDual.at(row) * Avalue.at(cnt);
4747 }
4748 z = sum + colCostAtEl.at(col);
4749 return z;
4750 }
4751
4752 /***
4753 * A'y + c = z
4754 *
4755 * returns y_row = -(A'y + c - z )/a_rowcol
4756 * (except row) (at el)
4757 */
getRowDualPost(int row,int col)4758 double Presolve::getRowDualPost(int row, int col) {
4759 double x = 0;
4760
4761 for (int kk = Astart.at(col); kk < Aend.at(col); ++kk)
4762 if (flagRow.at(Aindex.at(kk)) && Aindex.at(kk) != row)
4763 x = x + Avalue.at(kk) * valueRowDual.at(Aindex.at(kk));
4764
4765 x = x + colCostAtEl.at(col) - valueColDual.at(col);
4766
4767 double y = getaij(row, col);
4768 return -x / y;
4769 }
4770
getDualsForcingRow(int row,vector<int> & fRjs)4771 string Presolve::getDualsForcingRow(int row, vector<int>& fRjs) {
4772 double z;
4773 stringstream ss;
4774 int j;
4775
4776 double lo = -HIGHS_CONST_INF;
4777 double up = HIGHS_CONST_INF;
4778 int lo_col = -1;
4779 int up_col = -1;
4780
4781 double cost, sum;
4782
4783 for (size_t jj = 0; jj < fRjs.size(); ++jj) {
4784 j = fRjs[jj];
4785
4786 pair<int, vector<double>> p = oldBounds.top();
4787 vector<double> v = get<1>(p);
4788 oldBounds.pop();
4789 double colLow = v[0];
4790 double colUpp = v[1];
4791
4792 // calculate bound x imposed by zj
4793 double save_lo = lo;
4794 double save_up = up;
4795 getBoundOnLByZj(row, j, &lo, &up, colLow, colUpp);
4796 if (lo > save_lo) lo_col = j;
4797 if (up < save_up) up_col = j;
4798 }
4799
4800 // calculate yi
4801 if (lo > up)
4802 cout << "PR: Error in postsolving forcing row " << row
4803 << " : inconsistent bounds for its dual value.\n";
4804
4805 if (lo <= 0 && up >= 0) {
4806 valueRowDual.at(row) = 0;
4807 row_status[row] = HighsBasisStatus::BASIC;
4808 } else if (lo > 0) {
4809 // row is set to basic and column to non-basic but that should change
4810 row_status[row] = HighsBasisStatus::NONBASIC;
4811 col_status.at(lo_col) = HighsBasisStatus::BASIC;
4812 valueRowDual.at(row) = lo;
4813 valueColDual.at(lo_col) = 0;
4814 // valueColDual[lo_col] should be zero since it imposed the lower bound.
4815 } else if (up < 0) {
4816 // row is set to basic and column to non-basic but that should change
4817 row_status[row] = HighsBasisStatus::NONBASIC;
4818 col_status.at(up_col) = HighsBasisStatus::BASIC;
4819 valueRowDual.at(row) = up;
4820 valueColDual.at(up_col) = 0;
4821 }
4822
4823 flagRow.at(row) = 1;
4824
4825 for (size_t jj = 0; jj < fRjs.size(); ++jj) {
4826 j = fRjs[jj];
4827 if (lo > 0 && j == lo_col) continue;
4828 if (up < 0 && j == up_col) continue;
4829
4830 col_status[j] = HighsBasisStatus::NONBASIC;
4831
4832 cost = valueColDual.at(j);
4833 sum = 0;
4834 for (int k = Astart.at(j); k < Aend.at(j); ++k)
4835 if (flagRow.at(Aindex.at(k))) {
4836 sum = sum + valueRowDual.at(Aindex.at(k)) * Avalue.at(k);
4837 // cout<<" row "<<Aindex.at(k)<<" dual
4838 // "<<valueRowDual.at(Aindex.at(k))<<" a_"<<Aindex.at(k)<<"_"<<j<<"\n";
4839 }
4840 z = cost + sum;
4841
4842 valueColDual.at(j) = z;
4843
4844 if (iKKTcheck == 1) {
4845 ss << j;
4846 ss << " ";
4847 chk2.addChange(2, 0, j, valuePrimal.at(j), valueColDual.at(j), cost);
4848 }
4849 }
4850
4851 return ss.str();
4852 }
4853
getDualsSingletonRow(const int row,const int col)4854 void Presolve::getDualsSingletonRow(const int row, const int col) {
4855 pair<int, vector<double>> bnd = oldBounds.top();
4856 oldBounds.pop();
4857
4858 valueRowDual.at(row) = 0;
4859
4860 const double cost = postValue.top();
4861 postValue.pop();
4862 colCostAtEl[col] = cost;
4863
4864 const double aij = getaij(row, col);
4865 const double l = (get<1>(bnd))[0];
4866 const double u = (get<1>(bnd))[1];
4867 const double lrow = (get<1>(bnd))[2];
4868 const double urow = (get<1>(bnd))[3];
4869
4870 flagRow.at(row) = 1;
4871
4872 HighsBasisStatus local_status = col_status.at(col);
4873 if (local_status != HighsBasisStatus::BASIC) {
4874 // x was not basic but is now
4875 // if x is strictly between original bounds or a_ij*x_j is at a bound.
4876 if (fabs(valuePrimal.at(col) - l) > tol &&
4877 fabs(valuePrimal.at(col) - u) > tol) {
4878 if (report_postsolve) {
4879 printf("3.1 : Make column %3d basic and row %3d nonbasic\n", col, row);
4880 }
4881 col_status.at(col) = HighsBasisStatus::BASIC;
4882 row_status.at(row) = HighsBasisStatus::NONBASIC; // Was LOWER
4883 valueColDual[col] = 0;
4884 valueRowDual[row] = getRowDualPost(row, col);
4885 } else {
4886 // column is at bound
4887 const bool isRowAtLB = fabs(aij * valuePrimal[col] - lrow) < tol;
4888 const bool isRowAtUB = fabs(aij * valuePrimal[col] - urow) < tol;
4889
4890 const double save_dual = valueColDual[col];
4891 valueColDual[col] = 0;
4892 const double row_dual = getRowDualPost(row, col);
4893
4894 if ((isRowAtLB && !isRowAtUB && row_dual > 0) ||
4895 (!isRowAtLB && isRowAtUB && row_dual < 0) ||
4896 (!isRowAtLB && !isRowAtUB)) {
4897 // make row basic
4898 row_status.at(row) = HighsBasisStatus::BASIC;
4899 valueRowDual[row] = 0;
4900 valueColDual[col] = save_dual;
4901 } else {
4902 // column is basic
4903 col_status.at(col) = HighsBasisStatus::BASIC;
4904 row_status.at(row) = HighsBasisStatus::NONBASIC;
4905 valueColDual[col] = 0;
4906 valueRowDual[row] = getRowDualPost(row, col);
4907 }
4908 }
4909 } else {
4910 // x is basic
4911 if (report_postsolve) {
4912 printf("3.3 : Make row %3d basic\n", row);
4913 }
4914 row_status.at(row) = HighsBasisStatus::BASIC;
4915 valueRowDual[row] = 0;
4916 // if the row dual is zero it does not contribute to the column dual.
4917 }
4918 }
4919
getDualsDoubletonEquation(const int row,const int col)4920 void Presolve::getDualsDoubletonEquation(const int row, const int col) {
4921 // colDual already set. need valuePrimal from stack. maybe change rowDual
4922 // depending on bounds. old bounds kept in oldBounds. variables j,k : we
4923 // eliminated col(k)(c.col) and are left with changed bounds on j and no row.
4924 // y x
4925
4926 constexpr bool report = false;
4927
4928 pair<int, vector<double>> p = oldBounds.top();
4929 oldBounds.pop();
4930 vector<double> v = get<1>(p);
4931 const int x = get<0>(p);
4932 assert(x >= 0 && x <= numColOriginal);
4933 const double ubxNew = v[1];
4934 const double lbxNew = v[0];
4935 const double cxNew = v[2];
4936
4937 p = oldBounds.top();
4938 oldBounds.pop();
4939 v = get<1>(p);
4940 const double ubxOld = v[1];
4941 const double lbxOld = v[0];
4942 const double cxOld = v[2];
4943
4944 p = oldBounds.top();
4945 oldBounds.pop();
4946 v = get<1>(p);
4947 const double uby = v[1];
4948 const double lby = v[0];
4949 const double cy = v[2];
4950
4951 const int y = col;
4952 assert(y >= 0 && y <= numColOriginal);
4953
4954 const double b = postValue.top();
4955 postValue.pop();
4956 const double aky = postValue.top();
4957 postValue.pop();
4958 const double akx = postValue.top();
4959 postValue.pop();
4960 const double valueX = valuePrimal.at(x);
4961
4962 // primal value and objective shift
4963 valuePrimal.at(y) = (b - akx * valueX) / aky;
4964 objShift += -cxNew * valueX + cxOld * valueX + cy * valuePrimal.at(y);
4965
4966 // column cost of x
4967 colCostAtEl.at(x) = cxOld;
4968
4969 flagRow.at(row) = 1;
4970 flagCol.at(y) = 1;
4971
4972 if (mip) return;
4973
4974 const HighsBasisStatus x_status_reduced = col_status.at(x);
4975 bool x_make_basic = false;
4976 bool y_make_basic = false;
4977 bool row_basic = false;
4978 // x stayed, y was removed
4979 if (valuePrimal.at(y) - lby > tol && uby - valuePrimal.at(y) > tol) {
4980 // If column y has value between bounds set it to basic.
4981 col_status.at(y) = HighsBasisStatus::BASIC;
4982 row_status.at(row) = HighsBasisStatus::NONBASIC;
4983
4984 // makeYBasic();
4985 valueColDual.at(y) = 0;
4986 valueRowDual.at(row) = getRowDualPost(row, y);
4987 if (report) printf("4.2 : Make column %3d basic\n", y);
4988 return;
4989 }
4990
4991 if (((x_status_reduced == HighsBasisStatus::NONBASIC ||
4992 x_status_reduced == HighsBasisStatus::UPPER) &&
4993 fabs(valueX - ubxNew) < tol && ubxNew < ubxOld) ||
4994 ((x_status_reduced == HighsBasisStatus::NONBASIC ||
4995 x_status_reduced == HighsBasisStatus::LOWER) &&
4996 fabs(valueX - lbxNew) < tol && lbxNew > lbxOld) ||
4997 (fabs(valueX - lbxNew) < tol && fabs(lbxOld - lbxNew) < tol &&
4998 (x_status_reduced == HighsBasisStatus::UPPER ||
4999 x_status_reduced == HighsBasisStatus::LOWER))) {
5000 if (ubxNew > lbxNew) {
5001 // Column x is nonbasic at reduced solution at a reduced bound but needs
5002 // to be changed to basic since this bound is expanding.
5003 assert(col_status.at(y) == HighsBasisStatus::NONBASIC);
5004
5005 x_make_basic = true;
5006 // makeXBasic()
5007 }
5008
5009 if (ubxNew == lbxNew) {
5010 if (report) {
5011 printf(
5012 "4.5 : Maybe dual restriction on column x : no longer on feas "
5013 "side.\n");
5014 if (ubxNew == lbxOld && ubxNew < ubxOld)
5015 std::cout << " u. " << valueColDual[x] << std::endl;
5016 if (lbxNew == ubxOld && lbxNew > lbxOld)
5017 std::cout << " l. " << valueColDual[x] << std::endl;
5018
5019 if ((ubxNew == lbxOld && ubxNew < ubxOld && valueColDual[x] < tol) ||
5020 (lbxNew == ubxOld && lbxNew > lbxOld && valueColDual[x] > tol)) {
5021 if (report) printf("4.6 : Change dual of x to zero.\n");
5022 }
5023
5024 // make x basic.
5025 valueColDual.at(x) = 0;
5026 valueRowDual.at(row) = getRowDualPost(row, x);
5027 valueColDual.at(y) = getColumnDualPost(y);
5028 col_status.at(x) = HighsBasisStatus::BASIC;
5029 row_status.at(row) = HighsBasisStatus::NONBASIC;
5030 if (report) printf("4.77 : Make column %3d basic\n", x);
5031 return;
5032 }
5033 }
5034
5035 if (x_make_basic) {
5036 // transfer dual of x to dual of row
5037 valueColDual.at(x) = 0;
5038 valueRowDual.at(row) = getRowDualPost(row, x);
5039 valueColDual.at(y) = getColumnDualPost(y);
5040
5041 if (lby == -HIGHS_CONST_INF || uby == HIGHS_CONST_INF ||
5042 fabs(lby - uby) > tol) {
5043 // Make sure y is at a bound
5044 assert(fabs(valuePrimal[y] - lby) < tol ||
5045 fabs(valuePrimal[y] - uby) < tol);
5046
5047 // Check that y is dual feasible
5048 bool feasible = true;
5049 if (fabs(valuePrimal[y] - lby) < tol && valueColDual[y] < -tol)
5050 feasible = false;
5051 if (fabs(valuePrimal[y] - uby) < tol && valueColDual[y] > tol)
5052 feasible = false;
5053
5054 if (feasible) {
5055 col_status.at(x) = HighsBasisStatus::BASIC;
5056 row_status.at(row) = HighsBasisStatus::NONBASIC;
5057 if (report) printf("4.1 : Make column %3d basic\n", x);
5058 return;
5059 }
5060 // Y not dual feasible
5061 y_make_basic = true;
5062 }
5063 // If not feasble X will remail nonbasic and we will make the
5064 }
5065 }
5066
5067 if (!y_make_basic) {
5068 if (lby != uby) {
5069 assert(fabs(lby - valuePrimal[y]) < tol ||
5070 fabs(uby - valuePrimal[y]) < tol);
5071 // If postsolved column y is at a bound. If lby != uby we have a
5072 // restriction on the dual sign of y.
5073 // col_status.at(y) = HighsBasisStatus::BASIC;
5074 // row_status.at(row) = HighsBasisStatus::NONBASIC;
5075
5076 // valueColDual.at(y) = 0;
5077 // valueRowDual.at(row) = getRowDualPost(row, y);
5078
5079 // if (report) printf("4.2 : Make column %3d basic\n", y);
5080 } else {
5081 // Column y is at a bound.
5082 assert(fabs(uby - valuePrimal[y]) < tol ||
5083 fabs(lby - valuePrimal[y]) < tol);
5084
5085 // Check if tight.
5086 if (fabs(lby - uby) < tol) {
5087 // assert(fabs(lby - valuePrimal[y]) < tol);
5088 // no restriction so can make row basic but we don't have to always
5089 // since it can make the dual of X infeasible (check below)
5090 row_basic = true;
5091 } // Else Will need to check dual feasibility of y dual.
5092
5093 if (x_status_reduced != HighsBasisStatus::BASIC) {
5094 // make x basic.
5095 valueColDual.at(x) = 0;
5096 valueRowDual.at(row) = getRowDualPost(row, x);
5097 valueColDual.at(y) = getColumnDualPost(y);
5098 col_status.at(x) = HighsBasisStatus::BASIC;
5099 row_status.at(row) = HighsBasisStatus::NONBASIC;
5100 if (report) printf("4.778 : Make column %3d basic\n", x);
5101 return;
5102 }
5103 }
5104 }
5105
5106 // Print & check some info.
5107 // if (x_status_reduced == HighsBasisStatus::BASIC)
5108 // std::cout << "BASIC" << std::endl;
5109 // else
5110 // std::cout << "NOT BASIC" << std::endl;
5111
5112 // std::cout << "valueXDual " << valueColDual[x] << std::endl;
5113
5114 // see if X at a bound
5115 if (fabs(valueX - ubxNew) < tol || fabs(valueX - lbxNew) < tol) {
5116 if ((fabs(valueX - ubxNew) < tol && ubxNew < ubxOld) ||
5117 (fabs(valueX - lbxNew) < tol && lbxNew > lbxOld)) {
5118 // std::cout << " 4.122" << std::endl;
5119 // std::cout << lbxOld << " lbxOld " << std::endl;
5120 // std::cout << ubxOld << " ubxOld " << std::endl;
5121 // std::cout << lbxNew << " lbxNew " << std::endl;
5122 // std::cout << ubxNew << " ubxNew " << std::endl;
5123 // std::cout << valueX << " val " << std::endl;
5124 // if X was non basic make it basic
5125 if (x_status_reduced != HighsBasisStatus::BASIC) {
5126 valueColDual.at(x) = 0;
5127 valueRowDual.at(row) = getRowDualPost(row, x);
5128 valueColDual.at(y) = getColumnDualPost(y);
5129
5130 // Check dual feasibility of y.
5131 bool feasible = true;
5132 if (lby > -HIGHS_CONST_INF && lby < uby &&
5133 fabs(lby - valuePrimal[y]) < tol)
5134 if (valueColDual[y] < 0) feasible = false;
5135 if (uby < HIGHS_CONST_INF && lby < uby &&
5136 fabs(uby - valuePrimal[y]) < tol)
5137 if (valueColDual[y] > 0) feasible = false;
5138
5139 if (feasible) {
5140 col_status.at(x) = HighsBasisStatus::BASIC;
5141 row_status.at(row) = HighsBasisStatus::NONBASIC;
5142 if (report) printf("4.122778 : Make column %3d basic\n", x);
5143 return;
5144 } else {
5145 if (report) printf("4.1227785 : Make column %3d basic\n", y);
5146 // dual of y needs to change. make y basic by proceeding below.
5147 }
5148 }
5149
5150 // if X was basic make y basic.
5151 valueColDual.at(y) = 0;
5152 valueRowDual.at(row) = getRowDualPost(row, y);
5153 valueColDual.at(x) = getColumnDualPost(x);
5154 col_status.at(y) = HighsBasisStatus::BASIC;
5155 row_status.at(row) = HighsBasisStatus::NONBASIC;
5156 if (report) printf("4.122779 : Make column %3d basic\n", y);
5157 return;
5158 } else {
5159 // std::cout << " 4.002" << std::endl;
5160 row_basic = false;
5161 }
5162 } else {
5163 // X strictly between bounds
5164 assert(x_status_reduced == HighsBasisStatus::BASIC);
5165 assert(valuePrimal[x] - lbxNew > tol && ubxNew - valuePrimal[x] > tol);
5166 }
5167
5168 if (row_basic) {
5169 assert(col_status.at(y) == HighsBasisStatus::NONBASIC);
5170 row_status.at(row) = HighsBasisStatus::BASIC;
5171
5172 valueRowDual.at(row) = 0;
5173 valueColDual.at(y) = getColumnDualPost(y);
5174
5175 if (report) printf("4.1 : Make row %3d basic\n", row);
5176 } else {
5177 // Try Y Basic.
5178
5179 col_status.at(y) = HighsBasisStatus::BASIC;
5180 row_status.at(row) = HighsBasisStatus::NONBASIC;
5181
5182 valueColDual.at(y) = 0;
5183 valueRowDual.at(row) = getRowDualPost(row, y);
5184
5185 if (report) printf("4.4 : Make column %3d basic\n", y);
5186
5187 // Check complementary slackness on x.
5188 if ((valueColDual[x] < -tol && fabs(valuePrimal[x] - lbxOld) > tol) ||
5189 (valueColDual[x] > tol && fabs(ubxOld - valuePrimal[x]) > tol)) {
5190 if (x_status_reduced != HighsBasisStatus::BASIC) {
5191 // make X basic.
5192 valueColDual.at(x) = 0;
5193 valueRowDual.at(row) = getRowDualPost(row, x);
5194 valueColDual.at(y) = getColumnDualPost(y);
5195 col_status.at(x) = HighsBasisStatus::BASIC;
5196 col_status.at(y) = HighsBasisStatus::NONBASIC;
5197 row_status.at(row) = HighsBasisStatus::NONBASIC;
5198 if (report) printf("4.779 : Make column %3d basic\n", x);
5199 return;
5200 }
5201 // If X already basic and y can not be feasibly made basic then the row
5202 // remains as the only option. X not working out
5203
5204 // row_status.at(row) = HighsBasisStatus::BASIC;
5205 // col_status.at(y) = HighsBasisStatus::NONBASIC;
5206
5207 // valueRowDual.at(row) = 0;
5208 // valueColDual.at(y) = getColumnDualPost(y);
5209
5210 // if (report) printf("4.7791 : Make row %3d basic\n", row);
5211 if (report) printf("??? 4.7791 : Make row %3d basic\n", row);
5212 }
5213
5214 // Check dual feasibility of y.
5215 bool feasible = true;
5216 if (lby > -HIGHS_CONST_INF && lby < uby && fabs(lby - valuePrimal[y]) < tol)
5217 if (valueColDual[y] < 0) feasible = false;
5218 if (uby < HIGHS_CONST_INF && lby < uby && fabs(uby - valuePrimal[y]) < tol)
5219 if (valueColDual[y] > 0) feasible = false;
5220
5221 if (!feasible) {
5222 // make X basic.
5223 valueColDual.at(x) = 0;
5224 valueRowDual.at(row) = getRowDualPost(row, x);
5225 valueColDual.at(y) = getColumnDualPost(y);
5226 col_status.at(x) = HighsBasisStatus::BASIC;
5227 col_status.at(y) = HighsBasisStatus::NONBASIC;
5228 row_status.at(row) = HighsBasisStatus::NONBASIC;
5229 if (report) printf("4.879 : Make column %3d basic\n", x);
5230 }
5231
5232 // y is at a bound with infeasible dual
5233 // : attempt to make basic
5234 }
5235 }
5236
countRemovedRows(PresolveRule rule)5237 void Presolve::countRemovedRows(PresolveRule rule) {
5238 timer.increaseCount(true, rule);
5239 }
5240
countRemovedCols(PresolveRule rule)5241 void Presolve::countRemovedCols(PresolveRule rule) {
5242 timer.increaseCount(false, rule);
5243 if (timer.time_limit > 0 &&
5244 timer.timer_.readRunHighsClock() > timer.time_limit)
5245 status = stat::Timeout;
5246 }
5247
initState(const bool intermediate)5248 dev_kkt_check::State Presolve::initState(const bool intermediate) {
5249 // update row value
5250 rowValue.assign(numRowOriginal, 0);
5251 for (int i = 0; i < numRowOriginal; ++i) {
5252 if (flagRow[i])
5253 for (int k = ARstart.at(i); k < ARstart.at(i + 1); ++k) {
5254 const int col = ARindex[k];
5255 if (flagCol[col]) rowValue.at(i) += valuePrimal.at(col) * ARvalue.at(k);
5256 }
5257 }
5258
5259 if (!intermediate)
5260 return dev_kkt_check::State(
5261 numCol, numRow, Astart, Aend, Aindex, Avalue, ARstart, ARindex, ARvalue,
5262 colCost, colLower, colUpper, rowLower, rowUpper, flagCol, flagRow,
5263 colValue, colDual, rowValue, rowDual, col_status, row_status);
5264
5265 // if intermediate step use checker's row and col bounds and cost
5266 return chk2.initState(numColOriginal, numRowOriginal, Astart, Aend, Aindex,
5267 Avalue, ARstart, ARindex, ARvalue, flagCol, flagRow,
5268 valuePrimal, valueColDual, rowValue, valueRowDual,
5269 col_status, row_status);
5270 }
5271
5272 } // namespace presolve
5273