Skip to content

Commit cfc2795

Browse files
committed
Add highdebugsol. Add simulate_concurrency option
1 parent 05c83ea commit cfc2795

File tree

5 files changed

+59
-27
lines changed

5 files changed

+59
-27
lines changed

highs/lp_data/HighsOptions.h

Lines changed: 9 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -484,6 +484,7 @@ struct HighsOptionsStruct {
484484
bool mip_root_presolve_only;
485485
HighsInt mip_lifting_for_probing;
486486
HighsInt mip_search_concurrency;
487+
bool mip_search_simulate_concurrency;
487488

488489
// Logging callback identifiers
489490
HighsLogOptions log_options;
@@ -639,7 +640,8 @@ struct HighsOptionsStruct {
639640
mip_root_presolve_only(false),
640641
mip_lifting_for_probing(-1),
641642
// clang-format off
642-
mip_search_concurrency(0) {};
643+
mip_search_concurrency(0),
644+
mip_search_simulate_concurrency(false) {};
643645
// clang-format on
644646
};
645647

@@ -927,7 +929,7 @@ class HighsOptions : public HighsOptionsStruct {
927929

928930
record_bool = new OptionRecordBool(
929931
"timeless_log", "Suppression of time-based data in logging", true,
930-
&timeless_log, true);//false);
932+
&timeless_log, true); // false);
931933
records.push_back(record_bool);
932934

933935
record_string = new OptionRecordString(kLogFileString, "Log file", advanced,
@@ -1249,6 +1251,11 @@ class HighsOptions : public HighsOptionsStruct {
12491251
&mip_search_concurrency, 1, 2, kMipSearchConcurrencyLimit);
12501252
records.push_back(record_int);
12511253

1254+
record_bool = new OptionRecordBool("mip_search_simulate_concurrency",
1255+
"Simulate concurrency on a single thread", advanced,
1256+
&mip_search_simulate_concurrency, false);
1257+
records.push_back(record_bool);
1258+
12521259
record_int = new OptionRecordInt(
12531260
"ipm_iteration_limit", "Iteration limit for IPM solver", advanced,
12541261
&ipm_iteration_limit, 0, kHighsIInf, kHighsIInf);

highs/mip/HighsMipSolver.cpp

Lines changed: 41 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -584,17 +584,24 @@ void HighsMipSolver::run() {
584584
setParallelLock(true);
585585
for (HighsInt i = 0; i != static_cast<HighsInt>(search_indices.size());
586586
i++) {
587-
if (mipdata_->parallelLockActive() && search_indices.size() > 1) {
587+
if (mipdata_->parallelLockActive() && search_indices.size() > 1 &&
588+
!options_mip_->mip_search_simulate_concurrency) {
588589
tg.spawn([&, i]() {
589590
search_results[i] =
590591
mipdata_->workers[search_indices[i]].search_ptr_->evaluateNode();
591592
});
592593
} else {
594+
mipdata_->debugSolution.resetDomain(
595+
mipdata_->workers[search_indices[i]].search_ptr_->getLocalDomain());
596+
mipdata_->debugSolution.registerDomain(
597+
mipdata_->workers[search_indices[i]].search_ptr_->getLocalDomain());
593598
search_results[i] =
594599
mipdata_->workers[search_indices[i]].search_ptr_->evaluateNode();
595600
}
596601
}
597-
if (mipdata_->parallelLockActive()) tg.taskWait();
602+
if (mipdata_->parallelLockActive() && search_indices.size() > 1 &&
603+
!options_mip_->mip_search_simulate_concurrency)
604+
tg.taskWait();
598605
setParallelLock(false);
599606
analysis_.mipTimerStop(kMipClockEvaluateNode1);
600607
for (HighsInt i = 0; i != static_cast<HighsInt>(search_indices.size());
@@ -687,19 +694,24 @@ void HighsMipSolver::run() {
687694
if (!mipdata_->workers[search_indices[i]]
688695
.search_ptr_->currentNodePruned())
689696
continue;
690-
if (mipdata_->parallelLockActive() && search_indices.size() > 1) {
697+
if (mipdata_->parallelLockActive() && search_indices.size() > 1 &&
698+
!options_mip_->mip_search_simulate_concurrency) {
691699
tg.spawn([&, i]() {
692700
doHandlePrunedNodes(search_indices[i], mipdata_->parallelLockActive(),
693701
flush[i], infeasible[i]);
694702
});
695703
} else {
704+
mipdata_->debugSolution.resetDomain(
705+
mipdata_->workers[search_indices[i]].search_ptr_->getLocalDomain());
706+
mipdata_->debugSolution.resetDomain(
707+
mipdata_->workers[search_indices[i]].search_ptr_->getLocalDomain());
696708
doHandlePrunedNodes(search_indices[i], mipdata_->parallelLockActive(),
697709
flush[i], infeasible[i]);
698710
}
699711
// This search object is "finished" and needs a new node
700712
prune[i] = true;
701713
}
702-
if (mipdata_->parallelLockActive()) {
714+
if (mipdata_->parallelLockActive() && search_indices.size() > 1) {
703715
tg.taskWait();
704716
for (HighsInt i = 0; i < static_cast<HighsInt>(search_indices.size()) &&
705717
i < static_cast<HighsInt>(flush.size());
@@ -769,18 +781,25 @@ void HighsMipSolver::run() {
769781
analysis_.mipTimerStart(kMipClockNodeSearchSeparation);
770782
setParallelLock(true);
771783
for (HighsInt i : search_indices) {
772-
if (mipdata_->parallelLockActive()) {
784+
if (mipdata_->parallelLockActive() &&
785+
!options_mip_->mip_search_simulate_concurrency) {
773786
tg.spawn([&, i]() {
774787
mipdata_->workers[i].sepa_ptr_->separate(
775788
mipdata_->workers[i].search_ptr_->getLocalDomain());
776789
});
777790
} else {
791+
mipdata_->debugSolution.resetDomain(
792+
mipdata_->workers[i].search_ptr_->getLocalDomain());
793+
mipdata_->debugSolution.resetDomain(
794+
mipdata_->workers[i].search_ptr_->getLocalDomain());
778795
mipdata_->workers[i].sepa_ptr_->separate(
779796
mipdata_->workers[i].search_ptr_->getLocalDomain());
780797
}
781798
}
782799
analysis_.mipTimerStop(kMipClockNodeSearchSeparation);
783-
if (mipdata_->parallelLockActive()) tg.taskWait();
800+
if (mipdata_->parallelLockActive() &&
801+
!options_mip_->mip_search_simulate_concurrency)
802+
tg.taskWait();
784803
setParallelLock(false);
785804

786805
for (HighsInt i : search_indices) {
@@ -877,14 +896,19 @@ void HighsMipSolver::run() {
877896
setParallelLock(true);
878897
std::vector<HighsInt> search_indices = getSearchIndicesWithNodes();
879898
for (HighsInt i : search_indices) {
880-
if (mipdata_->parallelLockActive()) {
899+
if (mipdata_->parallelLockActive() &&
900+
!options_mip_->mip_search_simulate_concurrency) {
881901
tg.spawn([&, i]() { doRunHeuristics(mipdata_->workers[i]); });
882902
} else {
903+
mipdata_->debugSolution.resetDomain(
904+
mipdata_->workers[i].search_ptr_->getLocalDomain());
905+
mipdata_->debugSolution.resetDomain(
906+
mipdata_->workers[i].search_ptr_->getLocalDomain());
883907
doRunHeuristics(mipdata_->workers[i]);
884908
}
885909
}
886910
if (mipdata_->parallelLockActive()) {
887-
tg.taskWait();
911+
if (!options_mip_->mip_search_simulate_concurrency) tg.taskWait();
888912
for (const HighsInt i : search_indices) {
889913
if (mipdata_->workers[i].search_ptr_->currentNodePruned()) {
890914
++mipdata_->num_leaves;
@@ -906,7 +930,8 @@ void HighsMipSolver::run() {
906930
setParallelLock(true);
907931
for (HighsInt i = 0; i != static_cast<HighsInt>(mipdata_->workers.size());
908932
++i) {
909-
if (mipdata_->hasMultipleWorkers()) {
933+
if (mipdata_->hasMultipleWorkers() &&
934+
!options_mip_->mip_search_simulate_concurrency) {
910935
tg.spawn([&, i]() {
911936
if (!mipdata_->workers[i].search_ptr_->hasNode() ||
912937
mipdata_->workers[i].search_ptr_->currentNodePruned()) {
@@ -921,12 +946,18 @@ void HighsMipSolver::run() {
921946
mipdata_->workers[i].search_ptr_->currentNodePruned()) {
922947
dive_times[i] = -1;
923948
} else {
949+
mipdata_->debugSolution.resetDomain(
950+
mipdata_->workers[i].search_ptr_->getLocalDomain());
951+
mipdata_->debugSolution.resetDomain(
952+
mipdata_->workers[i].search_ptr_->getLocalDomain());
924953
dive_results[i] = mipdata_->workers[i].search_ptr_->dive();
925954
dive_times[i] += analysis_.mipTimerRead(kMipClockNodeSearch);
926955
}
927956
}
928957
}
929-
if (mipdata_->hasMultipleWorkers()) tg.taskWait();
958+
if (mipdata_->hasMultipleWorkers() &&
959+
!options_mip_->mip_search_simulate_concurrency)
960+
tg.taskWait();
930961
analysis_.mipTimerStop(kMipClockTheDive);
931962
setParallelLock(false);
932963
bool suboptimal = false;

highs/mip/HighsMipWorker.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -59,11 +59,11 @@ bool HighsMipWorker::addIncumbent(const std::vector<double>& sol, double solobj,
5959
transformNewIntegerFeasibleSolution(sol);
6060
if (transformed_solobj.first && transformed_solobj.second < upper_bound) {
6161
upper_bound = transformed_solobj.second;
62-
double new_upper_limit = mipdata_.computeNewUpperLimit(solobj, 0.0, 0.0);
62+
double new_upper_limit = mipdata_.computeNewUpperLimit(upper_bound, 0.0, 0.0);
6363
if (new_upper_limit < upper_limit) {
6464
upper_limit = new_upper_limit;
6565
optimality_limit = mipdata_.computeNewUpperLimit(
66-
solobj, mipsolver_.options_mip_->mip_abs_gap,
66+
upper_bound, mipsolver_.options_mip_->mip_abs_gap,
6767
mipsolver_.options_mip_->mip_rel_gap);
6868
}
6969
}

highs/mip/HighsSearch.cpp

Lines changed: 7 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -195,7 +195,7 @@ void HighsSearch::addBoundExceedingConflict() {
195195
getConflictPool(), mipworker.getGlobalDomain());
196196

197197
HighsCutGeneration cutGen(*lp, getCutPool());
198-
getDebugSolution().checkCut(inds.data(), vals.data(), inds.size(), rhs);
198+
mipsolver.mipdata_->debugSolution.checkCut(inds.data(), vals.data(), inds.size(), rhs);
199199
cutGen.generateConflict(localdom, mipworker.getGlobalDomain(), inds, vals,
200200
rhs);
201201
}
@@ -225,7 +225,7 @@ void HighsSearch::addInfeasibleConflict() {
225225
getConflictPool(), mipworker.getGlobalDomain());
226226

227227
HighsCutGeneration cutGen(*lp, getCutPool());
228-
getDebugSolution().checkCut(inds.data(), vals.data(), inds.size(), rhs);
228+
mipsolver.mipdata_->debugSolution.checkCut(inds.data(), vals.data(), inds.size(), rhs);
229229
cutGen.generateConflict(localdom, mipworker.getGlobalDomain(), inds, vals,
230230
rhs);
231231

@@ -622,7 +622,7 @@ HighsInt HighsSearch::selectBranchingCandidate(int64_t maxSbIters,
622622
addBoundExceedingConflict();
623623

624624
bool pruned = solobj > getCutoffBound();
625-
if (pruned) getDebugSolution().nodePruned(localdom);
625+
if (pruned) mipsolver.mipdata_->debugSolution.nodePruned(localdom);
626626

627627
localdom.backtrack();
628628
lp->flushDomain(localdom);
@@ -660,7 +660,7 @@ HighsInt HighsSearch::selectBranchingCandidate(int64_t maxSbIters,
660660
}
661661
}
662662
} else if (status == HighsLpRelaxation::Status::kInfeasible) {
663-
getDebugSolution().nodePruned(localdom);
663+
mipsolver.mipdata_->debugSolution.nodePruned(localdom);
664664
addInfeasibleConflict();
665665
pseudocost.addCutoffObservation(col, upbranch);
666666
localdom.backtrack();
@@ -738,7 +738,7 @@ void HighsSearch::currentNodeToQueue(HighsNodeQueue& nodequeue) {
738738
nodestack.back().estimate, getCurrentDepth());
739739
if (countTreeWeight) treeweight += tmpTreeWeight;
740740
} else {
741-
getDebugSolution().nodePruned(localdom);
741+
mipsolver.mipdata_->debugSolution.nodePruned(localdom);
742742
if (countTreeWeight) treeweight += std::ldexp(1.0, 1 - getCurrentDepth());
743743
}
744744
nodestack.back().opensubtrees = 0;
@@ -779,7 +779,7 @@ void HighsSearch::openNodesToQueue(HighsNodeQueue& nodequeue) {
779779
nodestack.back().estimate, getCurrentDepth());
780780
if (countTreeWeight) treeweight += tmpTreeWeight;
781781
} else {
782-
getDebugSolution().nodePruned(localdom);
782+
mipsolver.mipdata_->debugSolution.nodePruned(localdom);
783783
if (countTreeWeight) treeweight += std::ldexp(1.0, 1 - getCurrentDepth());
784784
}
785785
nodestack.back().opensubtrees = 0;
@@ -1076,7 +1076,7 @@ HighsSearch::NodeResult HighsSearch::evaluateNode() {
10761076
}
10771077

10781078
if (result != NodeResult::kOpen) {
1079-
getDebugSolution().nodePruned(localdom);
1079+
mipsolver.mipdata_->debugSolution.nodePruned(localdom);
10801080
treeweight += std::ldexp(1.0, 1 - getCurrentDepth());
10811081
currnode.opensubtrees = 0;
10821082
} else if (!inheuristic) {
@@ -1927,10 +1927,6 @@ HighsConflictPool& HighsSearch::getConflictPool() const {
19271927

19281928
HighsCutPool& HighsSearch::getCutPool() const { return *mipworker.cutpool_; }
19291929

1930-
const HighsDebugSol& HighsSearch::getDebugSolution() const {
1931-
return mipsolver.mipdata_->debugSolution;
1932-
}
1933-
19341930
const HighsNodeQueue& HighsSearch::getNodeQueue() const {
19351931
return mipsolver.mipdata_->nodequeue;
19361932
}

highs/mip/HighsSearch.h

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -263,8 +263,6 @@ class HighsSearch {
263263
HighsConflictPool& getConflictPool() const;
264264
HighsCutPool& getCutPool() const;
265265

266-
const HighsDebugSol& getDebugSolution() const;
267-
268266
const HighsNodeQueue& getNodeQueue() const;
269267

270268
const bool checkLimits(int64_t nodeOffset = 0) const;

0 commit comments

Comments
 (0)