Skip to content

Commit 9e4d8c4

Browse files
authored
Merge pull request #2491 from fwesselm/simpleDegree1Tests
Small addition to row presolve
2 parents 340f098 + 0152f99 commit 9e4d8c4

File tree

1 file changed

+55
-0
lines changed

1 file changed

+55
-0
lines changed

highs/presolve/HPresolve.cpp

Lines changed: 55 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)