Skip to content

Commit 24c8177

Browse files
committed
Scale tolerance in forcing row reduction
1 parent 0876f9b commit 24c8177

File tree

1 file changed

+30
-24
lines changed

1 file changed

+30
-24
lines changed

highs/presolve/HPresolve.cpp

Lines changed: 30 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -3827,9 +3827,19 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack,
38273827
// baseiRUpper);
38283828

38293829
auto checkForcingRow = [&](HighsInt row, HighsInt direction, double rowSide,
3830+
double impliedRowBound, double minAbsCoef,
38303831
HighsPostsolveStack::RowType rowType) {
3831-
// store row
3832-
storeRow(row);
3832+
// 1. direction = 1 (>=): forcing row if upper bound on constraint activity
3833+
// is equal to row's lower bound
3834+
// 2. direction = -1 (<=): forcing row if lower bound on constraint activity
3835+
// is equal to row's upper bound
3836+
// scale tolerance (equivalent to scaling row to have minimum absolute
3837+
// coefficient of 1)
3838+
if (direction * impliedRowBound >
3839+
direction * rowSide + primal_feastol * std::min(1.0, minAbsCoef))
3840+
return Result::kOk;
3841+
3842+
// get stored row
38333843
auto rowVector = getStoredRow();
38343844

38353845
HighsInt nfixings = 0;
@@ -3905,29 +3915,25 @@ HPresolve::Result HPresolve::rowPresolve(HighsPostsolveStack& postsolve_stack,
39053915

39063916
if (analysis_.allow_rule_[kPresolveRuleForcingRow]) {
39073917
// Allow rule to consider forcing rows
3908-
if (impliedRowUpper <= // check for forcing row on the row lower bound
3909-
model->row_lower_[row] + primal_feastol) {
3910-
// the row upper bound that is implied by the column bounds is equal to
3911-
// the row lower bound there for we can fix all columns at their bound
3912-
// as this is the only feasible assignment for this row and then find a
3913-
// suitable dual multiplier in postsolve. First we store the row on the
3914-
// postsolve stack (forcingRow() call) afterwards we store each column
3915-
// fixing on the postsolve stack. As the postsolve goes over the stack
3916-
// in reverse, it will first restore the column primal and dual values
3917-
// as the dual values are required to find the proper dual multiplier for
3918-
// the row and the column that we put in the basis.
3919-
HPRESOLVE_CHECKED_CALL(
3920-
checkForcingRow(row, HighsInt{1}, model->row_lower_[row],
3921-
HighsPostsolveStack::RowType::kGeq));
3922-
if (rowDeleted[row]) return Result::kOk;
3923-
3924-
} else if (impliedRowLower >= model->row_upper_[row] - primal_feastol) {
3925-
// forcing row in the other direction
3926-
HPRESOLVE_CHECKED_CALL(
3927-
checkForcingRow(row, HighsInt{-1}, model->row_upper_[row],
3928-
HighsPostsolveStack::RowType::kLeq));
3929-
if (rowDeleted[row]) return Result::kOk;
3918+
3919+
// store row and compute minimum absolute coefficient
3920+
storeRow(row);
3921+
double minAbsCoef = kHighsInf;
3922+
for (const HighsSliceNonzero& nonzero : getStoredRow()) {
3923+
minAbsCoef = std::min(minAbsCoef, std::abs(nonzero.value()));
39303924
}
3925+
3926+
// >= inequality
3927+
HPRESOLVE_CHECKED_CALL(checkForcingRow(
3928+
row, HighsInt{1}, model->row_lower_[row], impliedRowUpper, minAbsCoef,
3929+
HighsPostsolveStack::RowType::kGeq));
3930+
if (rowDeleted[row]) return Result::kOk;
3931+
3932+
// <= inequality
3933+
HPRESOLVE_CHECKED_CALL(checkForcingRow(
3934+
row, HighsInt{-1}, model->row_upper_[row], impliedRowLower, minAbsCoef,
3935+
HighsPostsolveStack::RowType::kLeq));
3936+
if (rowDeleted[row]) return Result::kOk;
39313937
}
39323938

39333939
// implied bounds can only be computed when row bounds are available and

0 commit comments

Comments
 (0)