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