@@ -3259,6 +3259,61 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack,
32593259 HPRESOLVE_CHECKED_CALL (checkRowRedundant (row));
32603260 if (rowDeleted[row]) return Result::kOk ;
32613261
3262+ if (rowsizeInteger[row] != 0 || rowsizeImplInt[row] != 0 ) {
3263+ // check if setting variable to opposite bound (with respect to bound used
3264+ // in calculation of bounds on row activity) makes row infeasible.
3265+ // this is a special case of bound tightening, but only performed once for
3266+ // binary variables here (see Suhl, Szymanski: Supernode processing of
3267+ // mixed-integer models. Comput. Optim. Appl. 3(4): 317-331 (1994)).
3268+
3269+ for (const HighsSliceNonzero& nonzero : getRowVector (row)) {
3270+ // get column index and coefficient
3271+ HighsInt col = nonzero.index ();
3272+ double val = nonzero.value ();
3273+
3274+ // skip continuous variables and non-binary variables
3275+ if (model->integrality_ [col] == HighsVarType::kContinuous ||
3276+ model->col_upper_ [col] != model->col_lower_ [col] + 1.0 )
3277+ continue ;
3278+
3279+ // lambda for computing offset
3280+ auto computeOffset = [&](HighsInt col, double val) {
3281+ return std::abs (val) *
3282+ (static_cast <HighsCDouble>(model->col_upper_ [col]) -
3283+ static_cast <HighsCDouble>(model->col_lower_ [col]));
3284+ };
3285+
3286+ auto degree1Tests = [&](HighsInt col, double val, HighsInt direction,
3287+ double rowActivityBound, double rowBound) {
3288+ // return if row is still feasible
3289+ if (direction * rowActivityBound >=
3290+ direction * rowBound - primal_feastol)
3291+ return false ;
3292+
3293+ // tighten bound
3294+ if (direction * val > 0 )
3295+ changeColLower (col, model->col_lower_ [col] + 1.0 );
3296+ else
3297+ changeColUpper (col, model->col_upper_ [col] - 1.0 );
3298+ return true ;
3299+ };
3300+
3301+ // perform tests
3302+ degree1Tests (
3303+ col, val, HighsInt{1 },
3304+ impliedRowBounds.getSumUpperOrig (row, -computeOffset (col, val)),
3305+ model->row_lower_ [row]);
3306+ degree1Tests (
3307+ col, val, HighsInt{-1 },
3308+ impliedRowBounds.getSumLowerOrig (row, computeOffset (col, val)),
3309+ model->row_upper_ [row]);
3310+ }
3311+ }
3312+
3313+ // check for redundancy again
3314+ HPRESOLVE_CHECKED_CALL (checkRowRedundant (row));
3315+ if (rowDeleted[row]) return Result::kOk ;
3316+
32623317 auto checkRedundantBounds = [&](HighsInt col, HighsInt row) {
32633318 // check if column singleton has redundant bounds
32643319 assert (model->col_cost_ [col] != 0.0 );
0 commit comments