@@ -222,6 +222,26 @@ void HPresolve::dualImpliedFreeGetRhsAndRowType(
222222 }
223223}
224224
225+ bool HPresolve::isImpliedEquationAtLower (HighsInt row) const {
226+ // if the implied lower bound on a row dual is strictly positive then the row
227+ // is an implied equation (using its lower bound) due to complementary
228+ // slackness
229+ bool isLbndPositive =
230+ implRowDualLower[row] > options->dual_feasibility_tolerance ;
231+ assert (!isLbndPositive || model->row_lower_ [row] != -kHighsInf );
232+ return isLbndPositive;
233+ }
234+
235+ bool HPresolve::isImpliedEquationAtUpper (HighsInt row) const {
236+ // if the implied upper bound on a row dual is strictly negative then the row
237+ // is an implied equation (using its upper bound) due to complementary
238+ // slackness
239+ bool isUbndNegative =
240+ implRowDualUpper[row] < -options->dual_feasibility_tolerance ;
241+ assert (!isUbndNegative || model->row_upper_ [row] != kHighsInf );
242+ return isUbndNegative;
243+ }
244+
225245bool HPresolve::isImpliedIntegral (HighsInt col) {
226246 bool runDualDetection = true ;
227247
@@ -236,15 +256,13 @@ bool HPresolve::isImpliedIntegral(HighsInt col) {
236256 continue ;
237257 }
238258
239- double rowLower =
240- implRowDualUpper[nz.index ()] < -options->dual_feasibility_tolerance
241- ? model->row_upper_ [nz.index ()]
242- : model->row_lower_ [nz.index ()];
259+ double rowLower = isImpliedEquationAtUpper (nz.index ())
260+ ? model->row_upper_ [nz.index ()]
261+ : model->row_lower_ [nz.index ()];
243262
244- double rowUpper =
245- implRowDualLower[nz.index ()] > options->dual_feasibility_tolerance
246- ? model->row_lower_ [nz.index ()]
247- : model->row_upper_ [nz.index ()];
263+ double rowUpper = isImpliedEquationAtLower (nz.index ())
264+ ? model->row_lower_ [nz.index ()]
265+ : model->row_upper_ [nz.index ()];
248266
249267 if (rowUpper == rowLower) {
250268 // if there is an equation the dual detection does not need to be tried
@@ -308,15 +326,13 @@ bool HPresolve::isImpliedInteger(HighsInt col) {
308326 continue ;
309327 }
310328
311- double rowLower =
312- implRowDualUpper[nz.index ()] < -options->dual_feasibility_tolerance
313- ? model->row_upper_ [nz.index ()]
314- : model->row_lower_ [nz.index ()];
329+ double rowLower = isImpliedEquationAtUpper (nz.index ())
330+ ? model->row_upper_ [nz.index ()]
331+ : model->row_lower_ [nz.index ()];
315332
316- double rowUpper =
317- implRowDualLower[nz.index ()] > options->dual_feasibility_tolerance
318- ? model->row_lower_ [nz.index ()]
319- : model->row_upper_ [nz.index ()];
333+ double rowUpper = isImpliedEquationAtLower (nz.index ())
334+ ? model->row_lower_ [nz.index ()]
335+ : model->row_upper_ [nz.index ()];
320336
321337 if (rowUpper == rowLower) {
322338 // if there is an equation the dual detection does not need to be tried
@@ -564,12 +580,10 @@ void HPresolve::updateRowDualImpliedBounds(HighsInt row, HighsInt col,
564580
565581void HPresolve::updateColImpliedBounds (HighsInt row, HighsInt col, double val) {
566582 // propagate implied column bound upper bound if row has an upper bound
567- double rowUpper = implRowDualLower[row] > options->dual_feasibility_tolerance
568- ? model->row_lower_ [row]
569- : model->row_upper_ [row];
570- double rowLower = implRowDualUpper[row] < -options->dual_feasibility_tolerance
571- ? model->row_upper_ [row]
572- : model->row_lower_ [row];
583+ double rowUpper = isImpliedEquationAtLower (row) ? model->row_lower_ [row]
584+ : model->row_upper_ [row];
585+ double rowLower = isImpliedEquationAtUpper (row) ? model->row_upper_ [row]
586+ : model->row_lower_ [row];
573587
574588 assert (rowLower != kHighsInf );
575589 assert (rowUpper != -kHighsInf );
@@ -3067,7 +3081,7 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack,
30673081 double origRowLower = model->row_lower_ [row];
30683082
30693083 if (model->row_lower_ [row] != model->row_upper_ [row]) {
3070- if (implRowDualLower[ row] > options-> dual_feasibility_tolerance ) {
3084+ if (isImpliedEquationAtLower ( row) ) {
30713085 // Convert to equality constraint (note that currently postsolve will not
30723086 // know about this conversion)
30733087 model->row_upper_ [row] = model->row_lower_ [row];
@@ -3076,7 +3090,7 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack,
30763090 changeRowDualLower (row, -kHighsInf );
30773091 if (mipsolver == nullptr )
30783092 checkRedundantBounds (rowDualLowerSource[row], row);
3079- } else if (implRowDualUpper[ row] < -options-> dual_feasibility_tolerance ) {
3093+ } else if (isImpliedEquationAtUpper ( row) ) {
30803094 // Convert to equality constraint (note that currently postsolve will not
30813095 // know about this conversion)
30823096 model->row_lower_ [row] = model->row_upper_ [row];
@@ -3769,27 +3783,13 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack,
37693783 }
37703784 }
37713785
3772- bool hasRowUpper =
3773- model->row_upper_ [row] != kHighsInf ||
3774- implRowDualUpper[row] < -options->dual_feasibility_tolerance ;
3775- // #1711: This looks very dodgy: surely model->row_lower_[row] !=
3776- // -kHighsInf: dates from before 25/02/21
3777- //
3778- // bool hasRowLower =
3779- // model->row_lower_[row] != kHighsInf ||
3780- // implRowDualLower[row] > options->dual_feasibility_tolerance;
3781- //
3782- // Using this corrected line will reduce the number of calls to
3783- // updateColImpliedBounds, as model->row_lower_[row] != kHighsInf is
3784- // never false
3785- bool hasRowLower =
3786- model->row_lower_ [row] != -kHighsInf ||
3787- implRowDualLower[row] > options->dual_feasibility_tolerance ;
3788- // #1711: Unsurprisingly, the assert is triggered very frequently
3789- // assert(true_hasRowLower == hasRowLower);
3790-
3791- if ((hasRowUpper && impliedRowBounds.getNumInfSumLowerOrig (row) <= 1 ) ||
3792- (hasRowLower && impliedRowBounds.getNumInfSumUpperOrig (row) <= 1 )) {
3786+ // implied bounds can only be computed when row bounds are available and
3787+ // bounds on activity contain at most one infinite bound
3788+ if (((model->row_upper_ [row] != kHighsInf || isImpliedEquationAtLower (row)) &&
3789+ impliedRowBounds.getNumInfSumLowerOrig (row) <= 1 ) ||
3790+ ((model->row_lower_ [row] != -kHighsInf ||
3791+ isImpliedEquationAtUpper (row)) &&
3792+ impliedRowBounds.getNumInfSumUpperOrig (row) <= 1 )) {
37933793 for (const HighsSliceNonzero& nonzero : getRowVector (row))
37943794 updateColImpliedBounds (row, nonzero.index (), nonzero.value ());
37953795 }
0 commit comments