Skip to content
Merged
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
82 changes: 60 additions & 22 deletions highs/presolve/HPresolve.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1156,13 +1156,25 @@ HPresolve::Result HPresolve::dominatedColumns(
}

// count number of fixed columns and modified bounds
HighsInt numCols = 0;
HighsInt numFixedCols = 0;
HighsInt numModifiedBounds = 0;
HighsInt numFixedColsPredBndAnalysis = 0;
HighsInt numModifiedBndsPredBndAnalysis = 0;

// parameters for predictive bound analysis
const size_t maxAverageNumDomChecksPredBndAnalysis = 10000;
const double minAverageNumRedsPredBndAnalysis = 1e-2;

// perform predictive bound analysis?
bool allowPredBndAnalysis = true;

for (HighsInt j = 0; j < model->num_col_; ++j) {
// skip deleted columns
if (colDeleted[j]) continue;

// increment counter for number of columns
numCols++;

// initialise
HighsInt bestRowPlus = -1;
HighsInt bestRowPlusLen = kHighsIInf;
Expand Down Expand Up @@ -1254,20 +1266,22 @@ HPresolve::Result HPresolve::dominatedColumns(
if (lowerBound > model->col_lower_[col] + primal_feastol) {
if (model->integrality_[col] != HighsVarType::kContinuous)
lowerBound = std::ceil(lowerBound - primal_feastol);
if (lowerBound == model->col_upper_[col])
if (lowerBound == model->col_upper_[col]) {
numFixedColsPredBndAnalysis++;
HPRESOLVE_CHECKED_CALL(fixCol(col, HighsInt{1}));
else if (model->integrality_[col] != HighsVarType::kContinuous) {
numModifiedBounds++;
} else if (model->integrality_[col] != HighsVarType::kContinuous) {
numModifiedBndsPredBndAnalysis++;
changeColLower(col, lowerBound);
}
}
if (upperBound < model->col_upper_[col] - primal_feastol) {
if (model->integrality_[col] != HighsVarType::kContinuous)
upperBound = std::floor(upperBound + primal_feastol);
if (upperBound == model->col_lower_[col])
if (upperBound == model->col_lower_[col]) {
numFixedColsPredBndAnalysis++;
HPRESOLVE_CHECKED_CALL(fixCol(col, HighsInt{-1}));
else if (model->integrality_[col] != HighsVarType::kContinuous) {
numModifiedBounds++;
} else if (model->integrality_[col] != HighsVarType::kContinuous) {
numModifiedBndsPredBndAnalysis++;
changeColUpper(col, upperBound);
}
}
Expand Down Expand Up @@ -1346,6 +1360,7 @@ HPresolve::Result HPresolve::dominatedColumns(
auto checkRow = [&](HighsInt row, HighsInt col, HighsInt direction,
double bestVal, bool boundImplied, bool hasCliques) {
storeRow(row);
bool onlyPredBndAnalysis = !boundImplied && !hasCliques;
for (const HighsSliceNonzero& nonz : getStoredRow()) {
// get column index
HighsInt k = nonz.index();
Expand All @@ -1360,7 +1375,7 @@ HPresolve::Result HPresolve::dominatedColumns(
bool sameVarType = varsHaveSameType(col, k);

// skip checks if nothing to do
if (!boundImplied && !hasCliques && !sameVarType) continue;
if (onlyPredBndAnalysis && !sameVarType) continue;

// try to fix variables or strengthen bounds
// check already known non-zeros in respective columns in advance to
Expand All @@ -1383,26 +1398,49 @@ HPresolve::Result HPresolve::dominatedColumns(
return Result::kOk;
};

// check if bounds are implied or there are cliques
bool lowerImplied = isLowerImplied(j);
bool upperImplied = isUpperImplied(j);
bool hasNegCliques =
isBinary(j) && mipsolver->mipdata_->cliquetable.numCliques(j, 0) > 0;
bool hasPosCliques =
isBinary(j) && mipsolver->mipdata_->cliquetable.numCliques(j, 1) > 0;

// use row 'bestRowMinus'
if (bestRowMinus != -1)
HPRESOLVE_CHECKED_CALL(checkRow(
bestRowMinus, j, HighsInt{-1}, ajBestRowMinus, isLowerImplied(j),
isBinary(j) &&
mipsolver->mipdata_->cliquetable.numCliques(j, 0) > 0));
if (bestRowMinus != -1 &&
(allowPredBndAnalysis || lowerImplied || hasNegCliques))
HPRESOLVE_CHECKED_CALL(checkRow(bestRowMinus, j, HighsInt{-1},
ajBestRowMinus, lowerImplied,
hasNegCliques));

// use row 'bestRowPlus'
if (!colDeleted[j] && bestRowPlus != -1)
HPRESOLVE_CHECKED_CALL(checkRow(
bestRowPlus, j, HighsInt{1}, ajBestRowPlus, isUpperImplied(j),
isBinary(j) &&
mipsolver->mipdata_->cliquetable.numCliques(j, 1) > 0));
}

if (numFixedCols > 0 || numModifiedBounds > 0)
if (!colDeleted[j] && bestRowPlus != -1 &&
(allowPredBndAnalysis || upperImplied || hasPosCliques))
HPRESOLVE_CHECKED_CALL(checkRow(bestRowPlus, j, HighsInt{1},
ajBestRowPlus, upperImplied,
hasPosCliques));

// do not use predictive bound analysis if it requires many domination
// checks and only yields few fixings or improved bounds on average
size_t averageNumDomChecksPredBndAnalysis =
numDomChecksPredBndAnalysis / static_cast<size_t>(numCols);
double averageNumRedsPredBndAnalysis =
(numFixedColsPredBndAnalysis + numModifiedBndsPredBndAnalysis) /
static_cast<double>(numCols);
allowPredBndAnalysis =
allowPredBndAnalysis &&
(numDomChecksPredBndAnalysis <=
30 * maxAverageNumDomChecksPredBndAnalysis ||
(averageNumDomChecksPredBndAnalysis <=
maxAverageNumDomChecksPredBndAnalysis &&
averageNumRedsPredBndAnalysis >= minAverageNumRedsPredBndAnalysis));
}

if (numFixedCols > 0 || numModifiedBndsPredBndAnalysis > 0)
highsLogDev(options->log_options, HighsLogType::kInfo,
"Fixed %d dominated columns and strengthened %d bounds\n",
static_cast<int>(numFixedCols),
static_cast<int>(numModifiedBounds));
static_cast<int>(numModifiedBndsPredBndAnalysis));

return Result::kOk;
}
Expand Down
Loading