Skip to content

Commit af6ae36

Browse files
committed
Update localmip. Run in parallel
1 parent 3a03e04 commit af6ae36

File tree

6 files changed

+77
-21
lines changed

6 files changed

+77
-21
lines changed

highs/lp_data/HighsOptions.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -480,6 +480,7 @@ struct HighsOptionsStruct {
480480
bool mip_heuristic_run_root_reduced_cost;
481481
bool mip_heuristic_run_zi_round;
482482
bool mip_heuristic_run_shifting;
483+
bool mip_heuristic_run_local_mip;
483484
double mip_min_logging_interval;
484485
std::string mip_lp_solver;
485486
std::string mip_ipm_solver;
@@ -638,6 +639,7 @@ struct HighsOptionsStruct {
638639
mip_heuristic_run_root_reduced_cost(false),
639640
mip_heuristic_run_zi_round(false),
640641
mip_heuristic_run_shifting(false),
642+
mip_heuristic_run_local_mip(false),
641643
mip_min_logging_interval(0.0),
642644
mip_lp_solver(""),
643645
mip_ipm_solver(""),
@@ -1217,6 +1219,11 @@ class HighsOptions : public HighsOptionsStruct {
12171219
&mip_heuristic_run_shifting, false);
12181220
records.push_back(record_bool);
12191221

1222+
record_bool = new OptionRecordBool("mip_heuristic_run_local_mip",
1223+
"Use the Local MIP heuristic", advanced,
1224+
&mip_heuristic_run_local_mip, true);
1225+
records.push_back(record_bool);
1226+
12201227
record_bool = new OptionRecordBool(
12211228
"mip_allow_cut_separation_at_nodes",
12221229
"Whether cut separation at nodes is permitted", advanced,

highs/mip/HighsMipSolverData.cpp

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -343,6 +343,27 @@ HighsModelStatus HighsMipSolverData::trivialHeuristics() {
343343
return HighsModelStatus::kNotset;
344344
}
345345

346+
void HighsMipSolverData::startLocalMipComputation(
347+
const highs::parallel::TaskGroup& taskGroup, const HighsDomain& globaldom,
348+
std::vector<double>& localMipSol) {
349+
localMipSuccess = false;
350+
taskGroup.spawn([&]() {
351+
HighsRandom randgen(mipsolver.options_mip_->random_seed + numRestarts);
352+
if (heuristics.localMip(globaldom, randgen, localMipSol)) {
353+
localMipSuccess = true;
354+
}
355+
});
356+
}
357+
358+
void HighsMipSolverData::finishLocalMipComputation(
359+
const highs::parallel::TaskGroup& taskGroup,
360+
const std::vector<double>& localMipSol) {
361+
taskGroup.sync();
362+
if (localMipSuccess) {
363+
trySolution(localMipSol, kSolutionSourceLocalMip);
364+
}
365+
}
366+
346367
void HighsMipSolverData::startAnalyticCenterComputation(
347368
const highs::parallel::TaskGroup& taskGroup) {
348369
taskGroup.spawn([&]() {
@@ -1927,6 +1948,14 @@ void HighsMipSolverData::evaluateRootNode() {
19271948
startSymmetryDetection(tg, symData);
19281949
analysis.mipTimerStop(kMipClockStartSymmetryDetection);
19291950
}
1951+
std::vector<double> localMipSol(mipsolver.numCol());
1952+
HighsDomain domainSnapshot = domain;
1953+
if (mipsolver.options_mip_->mip_heuristic_run_local_mip &&
1954+
!mipsolver.submip) {
1955+
analysis.mipTimerStart(kMipClockStartLocalMipComputation);
1956+
startLocalMipComputation(tg, domainSnapshot, localMipSol);
1957+
analysis.mipTimerStop(kMipClockStartLocalMipComputation);
1958+
}
19301959
if (compute_analytic_centre && !analyticCenterComputed) {
19311960
if (analysis.analyse_mip_time)
19321961
highsLogUser(
@@ -2038,8 +2067,6 @@ void HighsMipSolverData::evaluateRootNode() {
20382067
if (mipsolver.options_mip_->mip_heuristic_run_shifting)
20392068
heuristics.shifting(firstlpsol);
20402069

2041-
heuristics.localMip();
2042-
20432070
heuristics.flushStatistics();
20442071

20452072
analysis.mipTimerStart(kMipClockEvaluateRootLp);
@@ -2414,11 +2441,6 @@ void HighsMipSolverData::evaluateRootNode() {
24142441
if (lower_bound <= upper_limit) {
24152442
if (!mipsolver.submip && mipsolver.options_mip_->mip_allow_restart &&
24162443
mipsolver.options_mip_->presolve != kHighsOffString) {
2417-
if (!analyticCenterComputed && compute_analytic_centre) {
2418-
analysis.mipTimerStart(kMipClockFinishAnalyticCentreComputation);
2419-
finishAnalyticCenterComputation(tg);
2420-
analysis.mipTimerStop(kMipClockFinishAnalyticCentreComputation);
2421-
}
24222444
double fixingRate = percentageInactiveIntegers();
24232445
if (fixingRate >= 2.5 + 7.5 * mipsolver.submip ||
24242446
(!mipsolver.submip && fixingRate > 0 && numRestarts == 0)) {
@@ -2441,6 +2463,13 @@ void HighsMipSolverData::evaluateRootNode() {
24412463
}
24422464
}
24432465

2466+
if (!mipsolver.submip &&
2467+
mipsolver.options_mip_->mip_heuristic_run_local_mip) {
2468+
analysis.mipTimerStart(kMipClockFinishLocalMipComputation);
2469+
finishLocalMipComputation(tg, localMipSol);
2470+
analysis.mipTimerStop(kMipClockFinishLocalMipComputation);
2471+
}
2472+
24442473
if (detectSymmetries) {
24452474
finishSymmetryDetection(tg, symData);
24462475
analysis.mipTimerStart(kMipClockEvaluateRootLp);

highs/mip/HighsMipSolverData.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,7 @@ struct HighsMipSolverData {
8686
bool rowMatrixSet;
8787
bool analyticCenterComputed;
8888
HighsModelStatus analyticCenterStatus;
89+
bool localMipSuccess;
8990
bool detectSymmetries;
9091
HighsInt numRestarts;
9192
HighsInt numRestartsRoot;
@@ -173,6 +174,7 @@ struct HighsMipSolverData {
173174
rowMatrixSet(false),
174175
analyticCenterComputed(false),
175176
analyticCenterStatus(HighsModelStatus::kNotset),
177+
localMipSuccess(false),
176178
detectSymmetries(false),
177179
numRestarts(0),
178180
numRestartsRoot(0),
@@ -221,6 +223,12 @@ struct HighsMipSolverData {
221223
HighsModelStatus feasibilityJump();
222224
HighsModelStatus trivialHeuristics();
223225

226+
void startLocalMipComputation(const highs::parallel::TaskGroup& taskGroup,
227+
const HighsDomain& globaldom,
228+
std::vector<double>& localMipSol);
229+
void finishLocalMipComputation(const highs::parallel::TaskGroup& taskGroup,
230+
const std::vector<double>& localMipSol);
231+
224232
void startAnalyticCenterComputation(
225233
const highs::parallel::TaskGroup& taskGroup);
226234
void finishAnalyticCenterComputation(

highs/mip/HighsPrimalHeuristics.cpp

Lines changed: 14 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1590,9 +1590,9 @@ void HighsPrimalHeuristics::centralRounding() {
15901590
kSolutionSourceCentralRounding);
15911591
}
15921592

1593-
bool HighsPrimalHeuristics::localMip() {
1594-
const HighsDomain& globaldom = mipsolver.mipdata_->domain;
1595-
std::vector<double> intsol(mipsolver.numCol());
1593+
bool HighsPrimalHeuristics::localMip(const HighsDomain& globaldom,
1594+
HighsRandom& randgen,
1595+
std::vector<double>& intsol) {
15961596
bool found_feas_before = false;
15971597
double feastol = mipsolver.mipdata_->feastol;
15981598
const HighsSparseMatrix& a_matrix = mipsolver.model_->a_matrix_;
@@ -1602,6 +1602,8 @@ bool HighsPrimalHeuristics::localMip() {
16021602
// Initialise values and reserve vectors
16031603
HighsInt num_violated_rows_sample = 250;
16041604
HighsInt num_satisfied_rows_sample = 20;
1605+
HighsInt max_iters =
1606+
std::min(250000, std::max(5000, mipsolver.numNonzero() / 2));
16051607
HighsInt iters = 0;
16061608
HighsCDouble bestobj = kHighsInf;
16071609
HighsCDouble obj = 0;
@@ -1785,6 +1787,8 @@ bool HighsPrimalHeuristics::localMip() {
17851787
if (delta < feastol) return 0;
17861788
}
17871789
if (integral) delta = std::floor(delta + feastol);
1790+
assert(sol[c] + dir * delta > globaldom.col_lower_[c] - feastol);
1791+
assert(sol[c] + dir * delta < globaldom.col_upper_[c] + feastol);
17881792
return delta * dir;
17891793
};
17901794

@@ -1809,6 +1813,10 @@ bool HighsPrimalHeuristics::localMip() {
18091813
}
18101814
}
18111815
if (best_col == -1) return false;
1816+
assert(sol[best_col] + one_opt_deltas[best_col] >
1817+
globaldom.col_lower_[best_col] - feastol);
1818+
assert(sol[best_col] + one_opt_deltas[best_col] <
1819+
globaldom.col_upper_[best_col] + feastol);
18121820
apply_move(best_col, one_opt_deltas[best_col]);
18131821
one_opt_deltas[best_col] = one_opt_calc_col_delta(best_col);
18141822
HighsInt start = a_matrix.start_[best_col];
@@ -2091,7 +2099,7 @@ bool HighsPrimalHeuristics::localMip() {
20912099
bestobj = obj;
20922100
};
20932101

2094-
auto terminate = [&]() { return iters > 5000; };
2102+
auto terminate = [&]() { return iters > max_iters; };
20952103

20962104
calc_activities(false);
20972105
bool last_iter_feas = false;
@@ -2110,7 +2118,7 @@ bool HighsPrimalHeuristics::localMip() {
21102118
momentum = 1;
21112119
recalc_objective();
21122120
// If solution is improving then store it
2113-
// TODO: Undo the transformation here??
2121+
// TODO: Undo the transformation here?? (Need parallel branch changes)
21142122
if (obj + feastol < bestobj) {
21152123
found_feas_before = true;
21162124
save_sol();
@@ -2145,14 +2153,7 @@ bool HighsPrimalHeuristics::localMip() {
21452153
// viol.viol_index.size(), found_feas_before,
21462154
// static_cast<double>(bestobj));
21472155
}
2148-
// Try and add incumbent to storage
2149-
if (found_feas_before) {
2150-
recalc_objective();
2151-
mipsolver.mipdata_->addIncumbent(intsol, static_cast<double>(obj),
2152-
kSolutionSourceLocalMip);
2153-
return true;
2154-
}
2155-
return false;
2156+
return found_feas_before;
21562157
}
21572158

21582159
#if 0

highs/mip/HighsPrimalHeuristics.h

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,7 +57,8 @@ class HighsPrimalHeuristics {
5757

5858
void centralRounding();
5959

60-
bool localMip();
60+
bool localMip(const HighsDomain& globaldom, HighsRandom& randgen,
61+
std::vector<double>& intsol);
6162

6263
void flushStatistics();
6364

highs/mip/MipTimer.h

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,12 +40,14 @@ enum iClockMip {
4040
// Evaluate root node
4141
kMipClockStartSymmetryDetection,
4242
kMipClockStartAnalyticCentreComputation,
43+
kMipClockStartLocalMipComputation,
4344
kMipClockEvaluateRootLp,
4445
kMipClockSeparateLpCuts,
4546
kMipClockRandomizedRounding,
4647
kMipClockPerformRestart,
4748
kMipClockRootSeparation,
4849
kMipClockFinishAnalyticCentreComputation,
50+
kMipClockFinishLocalMipComputation,
4951
kMipClockRootCentralRounding,
5052
kMipClockRootSeparationRound0,
5153
kMipClockRootHeuristicsReducedCost,
@@ -176,6 +178,8 @@ class MipTimer {
176178
timer_pointer->clock_def("Start symmetry detection");
177179
clock[kMipClockStartAnalyticCentreComputation] =
178180
timer_pointer->clock_def("A-centre - start");
181+
clock[kMipClockStartLocalMipComputation] =
182+
timer_pointer->clock_def("Local Mip - start");
179183
clock[kMipClockEvaluateRootLp] =
180184
timer_pointer->clock_def("Evaluate root LP");
181185
clock[kMipClockSeparateLpCuts] =
@@ -188,6 +192,8 @@ class MipTimer {
188192
timer_pointer->clock_def("Root separation");
189193
clock[kMipClockFinishAnalyticCentreComputation] =
190194
timer_pointer->clock_def("A-centre - finish");
195+
clock[kMipClockFinishLocalMipComputation] =
196+
timer_pointer->clock_def("Local Mip - finish");
191197
clock[kMipClockRootCentralRounding] =
192198
timer_pointer->clock_def("Root central rounding");
193199
clock[kMipClockRootSeparationRound0] =
@@ -414,12 +420,14 @@ class MipTimer {
414420
const std::vector<HighsInt> mip_clock_list{
415421
kMipClockStartSymmetryDetection,
416422
kMipClockStartAnalyticCentreComputation,
423+
kMipClockStartLocalMipComputation,
417424
kMipClockEvaluateRootLp,
418425
kMipClockSeparateLpCuts,
419426
kMipClockRandomizedRounding,
420427
kMipClockPerformRestart,
421428
kMipClockRootSeparation,
422429
kMipClockFinishAnalyticCentreComputation,
430+
kMipClockFinishLocalMipComputation,
423431
kMipClockRootCentralRounding,
424432
kMipClockRootSeparationRound0,
425433
kMipClockRootHeuristicsReducedCost,
@@ -509,12 +517,14 @@ class MipTimer {
509517
const std::vector<HighsInt> mip_clock_list{
510518
kMipClockStartSymmetryDetection,
511519
kMipClockStartAnalyticCentreComputation,
520+
kMipClockStartLocalMipComputation,
512521
kMipClockEvaluateRootLp,
513522
kMipClockSeparateLpCuts,
514523
kMipClockRandomizedRounding,
515524
kMipClockPerformRestart,
516525
kMipClockRootSeparation,
517526
kMipClockFinishAnalyticCentreComputation,
527+
kMipClockFinishLocalMipComputation,
518528
kMipClockRootCentralRounding,
519529
kMipClockRootSeparationRound0,
520530
kMipClockRootHeuristicsReducedCost,

0 commit comments

Comments
 (0)