Skip to content

Commit a179eb6

Browse files
authored
Merge pull request #2287 from fwesselm/ZIRoundAndShifting
ZI round and shifting
2 parents b8c4d68 + 136c472 commit a179eb6

File tree

7 files changed

+519
-20
lines changed

7 files changed

+519
-20
lines changed

check/TestMipSolver.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -833,3 +833,18 @@ TEST_CASE("issue-2204", "[highs_test_mip_solver]") {
833833
const double optimal_objective = 6.0;
834834
solve(highs, kHighsOnString, require_model_status, optimal_objective);
835835
}
836+
837+
TEST_CASE("ZI Round and Shifting Heuristics", "[highs_test_mip_solver]") {
838+
std::string model_file =
839+
std::string(HIGHS_DIR) + "/check/instances/rgn.mps";
840+
841+
Highs highs;
842+
highs.setOptionValue("output_flag", dev_run);
843+
// Enable both heuristics
844+
highs.setOptionValue("mip_heuristic_run_zi_round", true);
845+
highs.setOptionValue("mip_heuristic_run_shifting", true);
846+
highs.readModel(model_file);
847+
const HighsModelStatus require_model_status = HighsModelStatus::kOptimal;
848+
const double optimal_objective = 82.19999924;
849+
solve(highs, kHighsOnString, require_model_status, optimal_objective);
850+
}

highs/lp_data/HighsOptions.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -437,6 +437,12 @@ struct HighsOptionsStruct {
437437
double mip_abs_gap;
438438
double mip_heuristic_effort;
439439
double mip_min_logging_interval;
440+
bool mip_heuristic_run_rins;
441+
bool mip_heuristic_run_rens;
442+
bool mip_heuristic_run_root_reduced_cost;
443+
bool mip_heuristic_run_zi_round;
444+
bool mip_heuristic_run_shifting;
445+
440446
#ifdef HIGHS_DEBUGSOL
441447
std::string mip_debug_solution_file;
442448
#endif
@@ -1109,6 +1115,32 @@ class HighsOptions : public HighsOptionsStruct {
11091115
&mip_heuristic_effort, 0.0, 0.05, 1.0);
11101116
records.push_back(record_double);
11111117

1118+
record_bool = new OptionRecordBool("mip_heuristic_run_rins",
1119+
"Run RINS heuristic: Default = true",
1120+
advanced, &mip_heuristic_run_rins, true);
1121+
records.push_back(record_bool);
1122+
1123+
record_bool = new OptionRecordBool("mip_heuristic_run_rens",
1124+
"Run RENS heuristic: Default = true",
1125+
advanced, &mip_heuristic_run_rens, true);
1126+
records.push_back(record_bool);
1127+
1128+
record_bool = new OptionRecordBool(
1129+
"mip_heuristic_run_root_reduced_cost",
1130+
"Run rootReducedCost heuristic: Default = true", advanced,
1131+
&mip_heuristic_run_root_reduced_cost, true);
1132+
records.push_back(record_bool);
1133+
1134+
record_bool = new OptionRecordBool(
1135+
"mip_heuristic_run_zi_round", "Run ZI Round heuristic: Default = false",
1136+
advanced, &mip_heuristic_run_zi_round, false);
1137+
records.push_back(record_bool);
1138+
1139+
record_bool = new OptionRecordBool(
1140+
"mip_heuristic_run_shifting", "Run Shifting heuristic: Default = false",
1141+
advanced, &mip_heuristic_run_shifting, false);
1142+
records.push_back(record_bool);
1143+
11121144
record_double = new OptionRecordDouble(
11131145
"mip_rel_gap",
11141146
"Tolerance on relative gap, |ub-lb|/|ub|, to determine whether "

highs/mip/HighsMipSolver.cpp

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -257,15 +257,19 @@ void HighsMipSolver::run() {
257257
}
258258

259259
if (mipdata_->incumbent.empty()) {
260-
analysis_.mipTimerStart(kMipClockDiveRens);
261-
mipdata_->heuristics.RENS(
262-
mipdata_->lp.getLpSolver().getSolution().col_value);
263-
analysis_.mipTimerStop(kMipClockDiveRens);
260+
if (options_mip_->mip_heuristic_run_rens) {
261+
analysis_.mipTimerStart(kMipClockDiveRens);
262+
mipdata_->heuristics.RENS(
263+
mipdata_->lp.getLpSolver().getSolution().col_value);
264+
analysis_.mipTimerStop(kMipClockDiveRens);
265+
}
264266
} else {
265-
analysis_.mipTimerStart(kMipClockDiveRins);
266-
mipdata_->heuristics.RINS(
267-
mipdata_->lp.getLpSolver().getSolution().col_value);
268-
analysis_.mipTimerStop(kMipClockDiveRins);
267+
if (options_mip_->mip_heuristic_run_rins) {
268+
analysis_.mipTimerStart(kMipClockDiveRins);
269+
mipdata_->heuristics.RINS(
270+
mipdata_->lp.getLpSolver().getSolution().col_value);
271+
analysis_.mipTimerStop(kMipClockDiveRins);
272+
}
269273
}
270274

271275
mipdata_->heuristics.flushStatistics();

highs/mip/HighsMipSolverData.cpp

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,12 @@ std::string HighsMipSolverData::solutionSourceToString(
4848
} else if (solution_source == kSolutionSourceRandomizedRounding) {
4949
if (code) return "R";
5050
return "Randomized rounding";
51+
} else if (solution_source == kSolutionSourceZiRound) {
52+
if (code) return "Z";
53+
return "ZI Round";
54+
} else if (solution_source == kSolutionSourceShifting) {
55+
if (code) return "I";
56+
return "Shifting";
5157
} else if (solution_source == kSolutionSourceSolveLp) {
5258
if (code) return "S";
5359
return "Solve LP";
@@ -110,6 +116,32 @@ bool HighsMipSolverData::checkSolution(
110116
return true;
111117
}
112118

119+
std::vector<std::tuple<HighsInt, HighsInt, double>>
120+
HighsMipSolverData::getInfeasibleRows(
121+
const std::vector<double>& solution) const {
122+
std::vector<std::tuple<HighsInt, HighsInt, double>> infeasibleRows;
123+
for (HighsInt i = 0; i != mipsolver.model_->num_row_; ++i) {
124+
HighsInt start = ARstart_[i];
125+
HighsInt end = ARstart_[i + 1];
126+
127+
HighsCDouble row_activity_quad = 0.0;
128+
for (HighsInt j = start; j != end; ++j)
129+
row_activity_quad +=
130+
static_cast<HighsCDouble>(solution[ARindex_[j]]) * ARvalue_[j];
131+
132+
double row_activity = static_cast<double>(row_activity_quad);
133+
if (row_activity > mipsolver.rowUpper(i) + feastol) {
134+
double difference = std::abs(row_activity - mipsolver.rowUpper(i));
135+
infeasibleRows.push_back({i, +1, difference});
136+
}
137+
if (row_activity < mipsolver.rowLower(i) - feastol) {
138+
double difference = std::abs(mipsolver.rowLower(i) - row_activity);
139+
infeasibleRows.push_back({i, -1, difference});
140+
}
141+
}
142+
return infeasibleRows;
143+
}
144+
113145
bool HighsMipSolverData::trySolution(const std::vector<double>& solution,
114146
const int solution_source) {
115147
if (int(solution.size()) != mipsolver.model_->num_col_) return false;
@@ -1601,6 +1633,8 @@ bool HighsMipSolverData::rootSeparationRound(
16011633

16021634
if (mipsolver.submip || incumbent.empty()) {
16031635
heuristics.randomizedRounding(solvals);
1636+
if (mipsolver.options_mip_->mip_heuristic_run_shifting)
1637+
heuristics.shifting(solvals);
16041638
heuristics.flushStatistics();
16051639
status = evaluateRootLp();
16061640
if (status == HighsLpRelaxation::Status::kInfeasible) return true;
@@ -1680,6 +1714,11 @@ HighsLpRelaxation::Status HighsMipSolverData::evaluateRootLp() {
16801714
num_leaves += 1;
16811715
return HighsLpRelaxation::Status::kInfeasible;
16821716
}
1717+
1718+
if (status == HighsLpRelaxation::Status::kOptimal &&
1719+
mipsolver.options_mip_->mip_heuristic_run_zi_round)
1720+
heuristics.ziRound(lp.getLpSolver().getSolution().col_value);
1721+
16831722
} else
16841723
status = lp.getStatus();
16851724

@@ -1876,9 +1915,14 @@ void HighsMipSolverData::evaluateRootNode() {
18761915
last_disptime = -kHighsInf;
18771916
disptime = 0;
18781917

1918+
if (mipsolver.options_mip_->mip_heuristic_run_zi_round)
1919+
heuristics.ziRound(firstlpsol);
18791920
analysis.mipTimerStart(kMipClockRandomizedRounding);
18801921
heuristics.randomizedRounding(firstlpsol);
18811922
analysis.mipTimerStop(kMipClockRandomizedRounding);
1923+
if (mipsolver.options_mip_->mip_heuristic_run_shifting)
1924+
heuristics.shifting(firstlpsol);
1925+
18821926
heuristics.flushStatistics();
18831927

18841928
analysis.mipTimerStart(kMipClockEvaluateRootLp);
@@ -2068,6 +2112,15 @@ void HighsMipSolverData::evaluateRootNode() {
20682112
rootlpsolobj = lp.getObjective();
20692113
lp.setIterationLimit(std::max(10000, int(10 * avgrootlpiters)));
20702114

2115+
if (mipsolver.options_mip_->mip_heuristic_run_zi_round) {
2116+
heuristics.ziRound(firstlpsol);
2117+
heuristics.flushStatistics();
2118+
}
2119+
if (mipsolver.options_mip_->mip_heuristic_run_shifting) {
2120+
heuristics.shifting(rootlpsol);
2121+
heuristics.flushStatistics();
2122+
}
2123+
20712124
if (!analyticCenterComputed && compute_analytic_centre) {
20722125
if (checkLimits()) return clockOff(analysis);
20732126

@@ -2122,10 +2175,12 @@ void HighsMipSolverData::evaluateRootNode() {
21222175
if (rootlpsol.empty()) break;
21232176
if (upper_limit != kHighsInf && !moreHeuristicsAllowed()) break;
21242177

2125-
analysis.mipTimerStart(kMipClockRootHeuristicsReducedCost);
2126-
heuristics.rootReducedCost();
2127-
analysis.mipTimerStop(kMipClockRootHeuristicsReducedCost);
2128-
heuristics.flushStatistics();
2178+
if (mipsolver.options_mip_->mip_heuristic_run_root_reduced_cost) {
2179+
analysis.mipTimerStart(kMipClockRootHeuristicsReducedCost);
2180+
heuristics.rootReducedCost();
2181+
analysis.mipTimerStop(kMipClockRootHeuristicsReducedCost);
2182+
heuristics.flushStatistics();
2183+
}
21292184

21302185
if (checkLimits()) return clockOff(analysis);
21312186

@@ -2151,10 +2206,12 @@ void HighsMipSolverData::evaluateRootNode() {
21512206
if (upper_limit != kHighsInf && !moreHeuristicsAllowed()) break;
21522207

21532208
if (checkLimits()) return clockOff(analysis);
2154-
analysis.mipTimerStart(kMipClockRootHeuristicsRens);
2155-
heuristics.RENS(rootlpsol);
2156-
analysis.mipTimerStop(kMipClockRootHeuristicsRens);
2157-
heuristics.flushStatistics();
2209+
if (mipsolver.options_mip_->mip_heuristic_run_rens) {
2210+
analysis.mipTimerStart(kMipClockRootHeuristicsRens);
2211+
heuristics.RENS(rootlpsol);
2212+
analysis.mipTimerStop(kMipClockRootHeuristicsRens);
2213+
heuristics.flushStatistics();
2214+
}
21582215

21592216
if (checkLimits()) return clockOff(analysis);
21602217
// if there are new global bound changes we re-evaluate the LP and do one

highs/mip/HighsMipSolverData.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,8 @@ enum MipSolutionSource : int {
5050
kSolutionSourceSubMip,
5151
kSolutionSourceEmptyMip,
5252
kSolutionSourceRandomizedRounding,
53+
kSolutionSourceZiRound,
54+
kSolutionSourceShifting,
5355
kSolutionSourceSolveLp,
5456
kSolutionSourceEvaluateNode,
5557
kSolutionSourceUnbounded,
@@ -257,6 +259,8 @@ struct HighsMipSolverData {
257259
double percentageInactiveIntegers() const;
258260
void performRestart();
259261
bool checkSolution(const std::vector<double>& solution) const;
262+
std::vector<std::tuple<HighsInt, HighsInt, double>> getInfeasibleRows(
263+
const std::vector<double>& solution) const;
260264
bool trySolution(const std::vector<double>& solution,
261265
const int solution_source = kSolutionSourceNone);
262266
bool rootSeparationRound(HighsSeparation& sepa, HighsInt& ncuts,

0 commit comments

Comments
 (0)