@@ -1156,13 +1156,25 @@ HPresolve::Result HPresolve::dominatedColumns(
11561156 }
11571157
11581158 // count number of fixed columns and modified bounds
1159+ HighsInt numCols = 0 ;
11591160 HighsInt numFixedCols = 0 ;
1160- HighsInt numModifiedBounds = 0 ;
1161+ HighsInt numFixedColsPredBndAnalysis = 0 ;
1162+ HighsInt numModifiedBndsPredBndAnalysis = 0 ;
1163+
1164+ // parameters for predictive bound analysis
1165+ const size_t maxAverageNumDomChecksPredBndAnalysis = 10000 ;
1166+ const double minAverageNumRedsPredBndAnalysis = 1e-2 ;
1167+
1168+ // perform predictive bound analysis?
1169+ bool allowPredBndAnalysis = true ;
11611170
11621171 for (HighsInt j = 0 ; j < model->num_col_ ; ++j) {
11631172 // skip deleted columns
11641173 if (colDeleted[j]) continue ;
11651174
1175+ // increment counter for number of columns
1176+ numCols++;
1177+
11661178 // initialise
11671179 HighsInt bestRowPlus = -1 ;
11681180 HighsInt bestRowPlusLen = kHighsIInf ;
@@ -1254,20 +1266,22 @@ HPresolve::Result HPresolve::dominatedColumns(
12541266 if (lowerBound > model->col_lower_ [col] + primal_feastol) {
12551267 if (model->integrality_ [col] != HighsVarType::kContinuous )
12561268 lowerBound = std::ceil (lowerBound - primal_feastol);
1257- if (lowerBound == model->col_upper_ [col])
1269+ if (lowerBound == model->col_upper_ [col]) {
1270+ numFixedColsPredBndAnalysis++;
12581271 HPRESOLVE_CHECKED_CALL (fixCol (col, HighsInt{1 }));
1259- else if (model->integrality_ [col] != HighsVarType::kContinuous ) {
1260- numModifiedBounds ++;
1272+ } else if (model->integrality_ [col] != HighsVarType::kContinuous ) {
1273+ numModifiedBndsPredBndAnalysis ++;
12611274 changeColLower (col, lowerBound);
12621275 }
12631276 }
12641277 if (upperBound < model->col_upper_ [col] - primal_feastol) {
12651278 if (model->integrality_ [col] != HighsVarType::kContinuous )
12661279 upperBound = std::floor (upperBound + primal_feastol);
1267- if (upperBound == model->col_lower_ [col])
1280+ if (upperBound == model->col_lower_ [col]) {
1281+ numFixedColsPredBndAnalysis++;
12681282 HPRESOLVE_CHECKED_CALL (fixCol (col, HighsInt{-1 }));
1269- else if (model->integrality_ [col] != HighsVarType::kContinuous ) {
1270- numModifiedBounds ++;
1283+ } else if (model->integrality_ [col] != HighsVarType::kContinuous ) {
1284+ numModifiedBndsPredBndAnalysis ++;
12711285 changeColUpper (col, upperBound);
12721286 }
12731287 }
@@ -1346,6 +1360,7 @@ HPresolve::Result HPresolve::dominatedColumns(
13461360 auto checkRow = [&](HighsInt row, HighsInt col, HighsInt direction,
13471361 double bestVal, bool boundImplied, bool hasCliques) {
13481362 storeRow (row);
1363+ bool onlyPredBndAnalysis = !boundImplied && !hasCliques;
13491364 for (const HighsSliceNonzero& nonz : getStoredRow ()) {
13501365 // get column index
13511366 HighsInt k = nonz.index ();
@@ -1360,7 +1375,7 @@ HPresolve::Result HPresolve::dominatedColumns(
13601375 bool sameVarType = varsHaveSameType (col, k);
13611376
13621377 // skip checks if nothing to do
1363- if (!boundImplied && !hasCliques && !sameVarType) continue ;
1378+ if (onlyPredBndAnalysis && !sameVarType) continue ;
13641379
13651380 // try to fix variables or strengthen bounds
13661381 // check already known non-zeros in respective columns in advance to
@@ -1383,26 +1398,49 @@ HPresolve::Result HPresolve::dominatedColumns(
13831398 return Result::kOk ;
13841399 };
13851400
1401+ // check if bounds are implied or there are cliques
1402+ bool lowerImplied = isLowerImplied (j);
1403+ bool upperImplied = isUpperImplied (j);
1404+ bool hasNegCliques =
1405+ isBinary (j) && mipsolver->mipdata_ ->cliquetable .numCliques (j, 0 ) > 0 ;
1406+ bool hasPosCliques =
1407+ isBinary (j) && mipsolver->mipdata_ ->cliquetable .numCliques (j, 1 ) > 0 ;
1408+
13861409 // use row 'bestRowMinus'
1387- if (bestRowMinus != -1 )
1388- HPRESOLVE_CHECKED_CALL ( checkRow (
1389- bestRowMinus, j, HighsInt{-1 }, ajBestRowMinus, isLowerImplied (j) ,
1390- isBinary (j) &&
1391- mipsolver-> mipdata_ -> cliquetable . numCliques (j, 0 ) > 0 ));
1410+ if (bestRowMinus != -1 &&
1411+ (allowPredBndAnalysis || lowerImplied || hasNegCliques))
1412+ HPRESOLVE_CHECKED_CALL ( checkRow ( bestRowMinus, j, HighsInt{-1 },
1413+ ajBestRowMinus, lowerImplied,
1414+ hasNegCliques ));
13921415
13931416 // use row 'bestRowPlus'
1394- if (!colDeleted[j] && bestRowPlus != -1 )
1395- HPRESOLVE_CHECKED_CALL (checkRow (
1396- bestRowPlus, j, HighsInt{1 }, ajBestRowPlus, isUpperImplied (j),
1397- isBinary (j) &&
1398- mipsolver->mipdata_ ->cliquetable .numCliques (j, 1 ) > 0 ));
1399- }
1400-
1401- if (numFixedCols > 0 || numModifiedBounds > 0 )
1417+ if (!colDeleted[j] && bestRowPlus != -1 &&
1418+ (allowPredBndAnalysis || upperImplied || hasPosCliques))
1419+ HPRESOLVE_CHECKED_CALL (checkRow (bestRowPlus, j, HighsInt{1 },
1420+ ajBestRowPlus, upperImplied,
1421+ hasPosCliques));
1422+
1423+ // do not use predictive bound analysis if it requires many domination
1424+ // checks and only yields few fixings or improved bounds on average
1425+ size_t averageNumDomChecksPredBndAnalysis =
1426+ numDomChecksPredBndAnalysis / static_cast <size_t >(numCols);
1427+ double averageNumRedsPredBndAnalysis =
1428+ (numFixedColsPredBndAnalysis + numModifiedBndsPredBndAnalysis) /
1429+ static_cast <double >(numCols);
1430+ allowPredBndAnalysis =
1431+ allowPredBndAnalysis &&
1432+ (numDomChecksPredBndAnalysis <=
1433+ 30 * maxAverageNumDomChecksPredBndAnalysis ||
1434+ (averageNumDomChecksPredBndAnalysis <=
1435+ maxAverageNumDomChecksPredBndAnalysis &&
1436+ averageNumRedsPredBndAnalysis >= minAverageNumRedsPredBndAnalysis));
1437+ }
1438+
1439+ if (numFixedCols > 0 || numModifiedBndsPredBndAnalysis > 0 )
14021440 highsLogDev (options->log_options , HighsLogType::kInfo ,
14031441 " Fixed %d dominated columns and strengthened %d bounds\n " ,
14041442 static_cast <int >(numFixedCols),
1405- static_cast <int >(numModifiedBounds ));
1443+ static_cast <int >(numModifiedBndsPredBndAnalysis ));
14061444
14071445 return Result::kOk ;
14081446}
0 commit comments