Skip to content

Commit b1f1acc

Browse files
committed
Add parallel lock. Fix cut pool sepa
1 parent b0b8374 commit b1f1acc

File tree

4 files changed

+48
-16
lines changed

4 files changed

+48
-16
lines changed

highs/mip/HighsCutPool.cpp

Lines changed: 16 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -148,18 +148,21 @@ double HighsCutPool::getParallelism(HighsInt row1, HighsInt row2,
148148
}
149149

150150
void HighsCutPool::lpCutRemoved(HighsInt cut, bool thread_safe) {
151-
numLps_[cut].fetch_add(-1, std::memory_order_relaxed);
151+
HighsInt numLps = numLps_[cut].fetch_add(-1, std::memory_order_relaxed);
152152
if (thread_safe) return;
153153
if (matrix_.columnsLinked(cut)) {
154-
propRows.erase(std::make_pair(-1, cut));
154+
propRows.erase(std::make_pair(ages_[cut], cut));
155155
propRows.emplace(1, cut);
156156
}
157157
ages_[cut] = 1;
158158
--numLpCuts;
159159
++ageDistribution[1];
160+
if (numLps == 0) {
161+
numLps_[cut].fetch_add(-1, std::memory_order_relaxed);
162+
}
160163
}
161164

162-
void HighsCutPool::performAging(const bool parallel_sepa) {
165+
void HighsCutPool::performAging() {
163166
HighsInt cutIndexEnd = matrix_.getNumRows();
164167

165168
HighsInt agelim = agelim_;
@@ -170,19 +173,17 @@ void HighsCutPool::performAging(const bool parallel_sepa) {
170173
}
171174

172175
for (HighsInt i = 0; i != cutIndexEnd; ++i) {
173-
if (numLps_[i] == 0 && ages_[i] >= 0 && parallel_sepa) {
174-
ageDistribution[ages_[i]] -= 1;
175-
lpCutRemoved(i);
176-
numLps_[i] = -1;
177-
} else if (numLps_[i] > 0 && ages_[i] >= 0 && parallel_sepa) {
178-
// Age and propRows were not updated in the multi-thread case
176+
if (numLps_[i] > 0 && ages_[i] >= 0) {
177+
--ageDistribution[ages_[i]];
179178
if (matrix_.columnsLinked(i)) {
180179
propRows.erase(std::make_pair(ages_[i], i));
181180
propRows.emplace(-1, i);
182181
}
183-
--ageDistribution[ages_[i]];
184182
ages_[i] = -1;
185183
}
184+
if (numLps_[i] == 0) {
185+
lpCutRemoved(i);
186+
}
186187
if (ages_[i] < 0) continue;
187188

188189
bool isPropagated = matrix_.columnsLinked(i);
@@ -369,7 +370,9 @@ void HighsCutPool::separate(const std::vector<double>& sol, HighsDomain& domain,
369370

370371
efficacious_cuts.resize(numefficacious);
371372

372-
HighsInt selectednnz = cutset.ARindex_.size();
373+
HighsInt orignumcuts = cutset.numCuts();
374+
HighsInt origselectednnz = cutset.ARindex_.size();
375+
HighsInt selectednnz = origselectednnz;
373376

374377
for (const std::pair<double, HighsInt>& p : efficacious_cuts) {
375378
bool discard = false;
@@ -415,8 +418,8 @@ void HighsCutPool::separate(const std::vector<double>& sol, HighsDomain& domain,
415418
assert(int(cutset.ARvalue_.size()) == selectednnz);
416419
assert(int(cutset.ARindex_.size()) == selectednnz);
417420

418-
HighsInt offset = 0;
419-
for (HighsInt i = 0; i != cutset.numCuts(); ++i) {
421+
HighsInt offset = origselectednnz;
422+
for (HighsInt i = orignumcuts; i != cutset.numCuts(); ++i) {
420423
cutset.ARstart_[i] = offset;
421424
HighsInt cut = cutset.cutindices[i];
422425
HighsInt start = matrix_.getRowStart(cut);

highs/mip/HighsCutPool.h

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -124,7 +124,7 @@ class HighsCutPool {
124124
double getParallelism(HighsInt row1, HighsInt row2,
125125
const HighsCutPool& pool2) const;
126126

127-
void performAging(bool parallel_sepa = false);
127+
void performAging();
128128

129129
void lpCutRemoved(HighsInt cut, bool thread_safe = false);
130130

highs/mip/HighsMipSolver.cpp

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,7 +143,6 @@ void HighsMipSolver::run() {
143143
HighsMipWorker& master_worker = mipdata_->workers.at(0);
144144

145145
restart:
146-
mipdata_->parallel_lock = false;
147146
if (modelstatus_ == HighsModelStatus::kNotset) {
148147
// Check limits have not been reached before evaluating root node
149148
if (mipdata_->checkLimits()) {
@@ -315,7 +314,6 @@ void HighsMipSolver::run() {
315314
// Initialize worker relaxations and mipworkers
316315
// todo lps and workers are still empty right now
317316

318-
mipdata_->parallel_lock = true;
319317
const HighsInt mip_search_concurrency = options_mip_->mip_search_concurrency;
320318
const HighsInt num_worker = mip_search_concurrency - 1;
321319
highs::parallel::TaskGroup tg;
@@ -412,6 +410,14 @@ void HighsMipSolver::run() {
412410
return search.performed_dive_;
413411
};
414412

413+
auto setParallelLock = [&](bool lock) -> void {
414+
if (mipdata_->workers.size() <= 1) return;
415+
mipdata_->parallel_lock = lock;
416+
for (HighsConflictPool& conflictpool : mipdata_->conflictpools) {
417+
conflictpool.setAgeLock(lock);
418+
}
419+
};
420+
415421
auto syncSolutions = [&]() -> void {
416422
for (HighsMipWorker& worker : mipdata_->workers) {
417423
for (auto& sol : worker.solutions_) {
@@ -427,6 +433,16 @@ void HighsMipSolver::run() {
427433
}
428434
};
429435

436+
auto syncPools = [&]() -> void {
437+
if (mipdata_->workers.size() <= 1 || mipdata_->parallelLockActive()) return;
438+
for (HighsInt i = 1; i < mipdata_->conflictpools.size(); ++i) {
439+
mipdata_->conflictpools[i].syncConflictPool(mipdata_->conflictPool);
440+
}
441+
for (HighsInt i = 1; i < mipdata_->cutpools.size(); ++i) {
442+
mipdata_->cutpools[i].syncCutPool(*this, mipdata_->cutpool);
443+
}
444+
};
445+
430446
auto syncGlobalDomain = [&]() -> void {
431447
if (mipdata_->workers.size() <= 1) return;
432448
for (HighsMipWorker& worker : mipdata_->workers) {
@@ -515,6 +531,7 @@ void HighsMipSolver::run() {
515531
auto evaluateNodes = [&](std::vector<HighsInt>& search_indices) -> void {
516532
std::vector<HighsSearch::NodeResult> search_results(search_indices.size());
517533
analysis_.mipTimerStart(kMipClockEvaluateNode1);
534+
setParallelLock(true);
518535
for (HighsInt i = 0; i != search_indices.size(); i++) {
519536
// TODO MT: Remove this dummy if statement
520537
if (i != 0) continue;
@@ -529,6 +546,7 @@ void HighsMipSolver::run() {
529546
}
530547
}
531548
if (mipdata_->parallelLockActive()) tg.taskWait();
549+
setParallelLock(false);
532550
analysis_.mipTimerStop(kMipClockEvaluateNode1);
533551
for (HighsInt i = 0; i != search_indices.size(); i++) {
534552
// TODO MT: Remove this dummy if statement
@@ -629,6 +647,7 @@ void HighsMipSolver::run() {
629647
std::deque<bool> infeasible;
630648
std::deque<bool> flush;
631649
std::vector<bool> prune(search_indices.size(), false);
650+
setParallelLock(true);
632651
for (HighsInt i = 0; i < search_indices.size(); i++) {
633652
// TODO MT: Remove this redundant if
634653
if (search_indices[i] != 0 || !mipdata_->workers[search_indices[i]]
@@ -658,6 +677,7 @@ void HighsMipSolver::run() {
658677
}
659678
}
660679
}
680+
setParallelLock(false);
661681

662682
// Remove search indices that need a new node
663683
HighsInt num_search_indices = static_cast<HighsInt>(search_indices.size());
@@ -700,6 +720,7 @@ void HighsMipSolver::run() {
700720
[&](std::vector<HighsInt>& search_indices) -> bool {
701721
// the node is still not fathomed, so perform separation
702722
analysis_.mipTimerStart(kMipClockNodeSearchSeparation);
723+
setParallelLock(true);
703724
for (HighsInt i : search_indices) {
704725
// TODO MT: Get rid of this line
705726
if (i != 0) continue;
@@ -715,6 +736,7 @@ void HighsMipSolver::run() {
715736
}
716737
analysis_.mipTimerStop(kMipClockNodeSearchSeparation);
717738
if (mipdata_->parallelLockActive()) tg.taskWait();
739+
setParallelLock(false);
718740

719741
for (HighsInt i : search_indices) {
720742
// TODO MT: Get rid of this line
@@ -765,6 +787,7 @@ void HighsMipSolver::run() {
765787
-analysis_.mipTimerRead(kMipClockTheDive));
766788
std::vector<HighsSearch::NodeResult> dive_results(
767789
mip_search_concurrency, HighsSearch::NodeResult::kBranched);
790+
setParallelLock(true);
768791
if (mip_search_concurrency > 1) {
769792
for (int i = 0; i < mip_search_concurrency; i++) {
770793
tg.spawn([&, i]() {
@@ -784,6 +807,7 @@ void HighsMipSolver::run() {
784807
dive_times[0] += analysis_.mipTimerRead(kMipClockNodeSearch);
785808
}
786809
}
810+
setParallelLock(false);
787811
bool suboptimal = false;
788812
for (int i = 0; i < mip_search_concurrency; i++) {
789813
if (dive_times[i] != -1) {

highs/mip/HighsSeparation.cpp

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,11 @@ HighsInt HighsSeparation::separationRound(HighsDomain& propdomain,
140140

141141
mipworker_.cutpool_->separate(sol.col_value, propdomain, cutset,
142142
mipdata.feastol, mipdata.cutpools);
143+
// Also separate the global cut pool
144+
if (mipworker_.cutpool_ != &mipdata.cutpool) {
145+
mipdata.cutpool.separate(sol.col_value, propdomain, cutset, mipdata.feastol,
146+
mipdata.cutpools, true);
147+
}
143148

144149
if (cutset.numCuts() > 0) {
145150
ncuts += cutset.numCuts();

0 commit comments

Comments
 (0)