Skip to content

Commit 756d7cf

Browse files
committed
Rework how cutspools are handled in paralell case
1 parent fd13d2f commit 756d7cf

File tree

3 files changed

+44
-52
lines changed

3 files changed

+44
-52
lines changed

highs/mip/HighsConflictPool.cpp

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -270,8 +270,6 @@ void HighsConflictPool::syncConflictPool(HighsConflictPool& syncpool) {
270270
HighsInt end = conflictRanges_[i].second;
271271
assert(start >= 0 && end >= 0);
272272
syncpool.addConflictFromOtherPool(&conflictEntries_[start], end - start);
273-
ageDistribution_[ages_[i]] -= 1;
274-
ages_[i] = -1;
275273
removeConflict(i);
276274
}
277275
deletedConflicts_.clear();

highs/mip/HighsCutPool.cpp

Lines changed: 40 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -148,7 +148,7 @@ double HighsCutPool::getParallelism(HighsInt row1, HighsInt row2,
148148
}
149149

150150
void HighsCutPool::lpCutRemoved(HighsInt cut, bool thread_safe) {
151-
HighsInt numLps = numLps_[cut].fetch_add(-1, std::memory_order_relaxed);
151+
numLps_[cut].fetch_add(-1, std::memory_order_relaxed);
152152
if (thread_safe) return;
153153
if (matrix_.columnsLinked(cut)) {
154154
propRows.erase(std::make_pair(ages_[cut], cut));
@@ -157,9 +157,6 @@ void HighsCutPool::lpCutRemoved(HighsInt cut, bool thread_safe) {
157157
ages_[cut] = 1;
158158
--numLpCuts;
159159
++ageDistribution[1];
160-
if (numLps == 1) {
161-
numLps_[cut].fetch_add(-1, std::memory_order_relaxed);
162-
}
163160
}
164161

165162
void HighsCutPool::performAging() {
@@ -173,21 +170,31 @@ void HighsCutPool::performAging() {
173170
}
174171

175172
for (HighsInt i = 0; i != cutIndexEnd; ++i) {
173+
// Catch buffered changes (should only occur in parallel case)
174+
// TODO: This misses the case where a cut is added then deleted before aging
175+
// TODO: has been called once. We'd miss resetting the age in this case.
176176
if (numLps_[i] > 0 && ages_[i] >= 0) {
177+
// Cut has been added to the LP, but age changes haven't been made
177178
--ageDistribution[ages_[i]];
178179
if (matrix_.columnsLinked(i)) {
179180
propRows.erase(std::make_pair(ages_[i], i));
180181
propRows.emplace(-1, i);
181182
}
182183
ages_[i] = -1;
183184
++numLpCuts;
185+
} else if (numLps_[i] == 0 && ages_[i] == -1 && rhs_[i] != kHighsInf) {
186+
// Cut was removed from the LP, but age changes haven't been made
187+
if (matrix_.columnsLinked(i)) {
188+
propRows.erase(std::make_pair(ages_[i], i));
189+
propRows.emplace(1, i);
190+
}
191+
ages_[i] = 1;
192+
--numLpCuts;
193+
++ageDistribution[1];
194+
} else if (usedInRound_[i]) {
195+
resetAge(i);
184196
}
185-
if (numLps_[i] == 0) {
186-
// TODO MT: This doesn't work.... What happens if a cut was generated, and
187-
// then used in propagation, but never added. It has age 0 but never is in
188-
// an LP.....
189-
lpCutRemoved(i);
190-
}
197+
usedInRound_[i] = false;
191198
if (ages_[i] < 0) continue;
192199

193200
bool isPropagated = matrix_.columnsLinked(i);
@@ -208,6 +215,7 @@ void HighsCutPool::performAging() {
208215
matrix_.removeRow(i);
209216
ages_[i] = -1;
210217
rhs_[i] = kHighsInf;
218+
hasSynced_[i] = false;
211219
} else {
212220
if (isPropagated) propRows.emplace(ages_[i], i);
213221
ageDistribution[ages_[i]] += 1;
@@ -227,7 +235,7 @@ void HighsCutPool::separate(const std::vector<double>& sol, HighsDomain& domain,
227235

228236
std::vector<std::pair<double, HighsInt>> efficacious_cuts;
229237

230-
HighsInt agelim = thread_safe ? -1 : agelim_;
238+
HighsInt agelim = agelim_;
231239

232240
HighsInt numCuts = getNumCuts() - numLpCuts;
233241
while (agelim > 1 && numCuts > softlimit_) {
@@ -238,7 +246,7 @@ void HighsCutPool::separate(const std::vector<double>& sol, HighsDomain& domain,
238246
for (HighsInt i = 0; i < nrows; ++i) {
239247
// cuts with an age of -1 are already in the LP and are therefore skipped
240248
// TODO: Parallel case here loops over cuts potentially added in current LP
241-
if (ages_[i] < 0 && (!thread_safe || numLps_[i] < 0)) continue;
249+
if (ages_[i] < 0) continue;
242250

243251
HighsInt start = matrix_.getRowStart(i);
244252
HighsInt end = matrix_.getRowEnd(i);
@@ -280,6 +288,8 @@ void HighsCutPool::separate(const std::vector<double>& sol, HighsDomain& domain,
280288
matrix_.removeRow(i);
281289
ages_[i] = -1;
282290
rhs_[i] = 0;
291+
usedInRound_[i] = false;
292+
hasSynced_[i] = false;
283293
auto range = hashToCutMap.equal_range(h);
284294

285295
for (auto it = range.first; it != range.second; ++it) {
@@ -403,11 +413,7 @@ void HighsCutPool::separate(const std::vector<double>& sol, HighsDomain& domain,
403413

404414
if (discard) continue;
405415

406-
int16_t numLp = numLps_[p.second].fetch_add(1, std::memory_order_relaxed);
407-
if (numLp == -1) {
408-
numLps_[p.second].fetch_add(1, std::memory_order_relaxed);
409-
if (thread_safe) ++numLpCuts;
410-
}
416+
numLps_[p.second].fetch_add(1, std::memory_order_relaxed);
411417
if (!thread_safe) {
412418
--ageDistribution[ages_[p.second]];
413419
++numLpCuts;
@@ -587,6 +593,8 @@ HighsInt HighsCutPool::addCut(const HighsMipSolver& mipsolver, HighsInt* Rindex,
587593
rhs_.resize(rowindex + 1);
588594
ages_.resize(rowindex + 1);
589595
numLps_.resize(rowindex + 1);
596+
usedInRound_.resize(rowindex + 1);
597+
hasSynced_.resize(rowindex + 1);
590598
rownormalization_.resize(rowindex + 1);
591599
maxabscoef_.resize(rowindex + 1);
592600
rowintegral.resize(rowindex + 1);
@@ -597,7 +605,9 @@ HighsInt HighsCutPool::addCut(const HighsMipSolver& mipsolver, HighsInt* Rindex,
597605
ages_[rowindex] = std::max(HighsInt{0}, agelim_ - 5);
598606
++ageDistribution[ages_[rowindex]];
599607
rowintegral[rowindex] = integral;
600-
numLps_[rowindex] = -1;
608+
numLps_[rowindex] = 0;
609+
usedInRound_[rowindex] = false;
610+
hasSynced_[rowindex] = false;
601611
if (propagate) propRows.emplace(ages_[rowindex], rowindex);
602612
assert((HighsInt)propRows.size() == numPropRows);
603613

@@ -623,34 +633,19 @@ void HighsCutPool::syncCutPool(const HighsMipSolver& mipsolver,
623633
HighsInt cutIndexEnd = matrix_.getNumRows();
624634

625635
for (HighsInt i = 0; i != cutIndexEnd; ++i) {
626-
// cut is then in the LP or already deleted
627-
if (ages_[i] < 0) continue;
628-
629-
HighsInt Rlen;
630-
const HighsInt* Rindex;
631-
const double* Rvalue;
632-
getCut(i, Rlen, Rindex, Rvalue);
633-
// copy cut into something mutable (addCut reorders so can't take const)
634-
std::vector<HighsInt> idxs(Rindex, Rindex + Rlen);
635-
std::vector<double> vals(Rvalue, Rvalue + Rlen);
636-
syncpool.addCut(mipsolver, idxs.data(), vals.data(), Rlen, rhs_[i],
637-
rowintegral[i]);
638-
639-
bool isPropagated = matrix_.columnsLinked(i);
640-
if (isPropagated) propRows.erase(std::make_pair(ages_[i], i));
641-
ageDistribution[ages_[i]] -= 1;
642-
for (HighsDomain::CutpoolPropagation* propagationdomain :
643-
propagationDomains)
644-
propagationdomain->cutDeleted(i);
645-
646-
if (isPropagated) {
647-
--numPropRows;
648-
numPropNzs -= getRowLength(i);
636+
// Only sync cuts in the LP that are not already synced
637+
if (numLps_[i] > 0 && !hasSynced_[i]) {
638+
HighsInt Rlen;
639+
const HighsInt* Rindex;
640+
const double* Rvalue;
641+
getCut(i, Rlen, Rindex, Rvalue);
642+
// copy cut into something mutable (addCut reorders so can't take const)
643+
std::vector<HighsInt> idxs(Rindex, Rindex + Rlen);
644+
std::vector<double> vals(Rvalue, Rvalue + Rlen);
645+
syncpool.addCut(mipsolver, idxs.data(), vals.data(), Rlen, rhs_[i],
646+
rowintegral[i]);
647+
hasSynced_[i] = true;
649648
}
650-
651-
matrix_.removeRow(i);
652-
ages_[i] = -1;
653-
rhs_[i] = kHighsInf;
654649
}
655650

656651
assert((HighsInt)propRows.size() == numPropRows);

highs/mip/HighsCutPool.h

Lines changed: 4 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,9 +56,9 @@ class HighsCutPool {
5656
HighsDynamicRowMatrix matrix_;
5757
std::vector<double> rhs_;
5858
std::vector<int16_t> ages_;
59-
std::deque<std::atomic<int16_t>>
60-
numLps_; // -1 : never used, 0 : used but no longer in LP, 1+ : currently
61-
// in an LP
59+
std::deque<std::atomic<int16_t>> numLps_;
60+
std::vector<bool> usedInRound_; // Was the cut propagated?
61+
std::vector<bool> hasSynced_; // Has the cut been globally synced?
6262
std::vector<double> rownormalization_;
6363
std::vector<double> maxabscoef_;
6464
std::vector<uint8_t> rowintegral;
@@ -105,8 +105,7 @@ class HighsCutPool {
105105
void resetAge(HighsInt cut, bool thread_safe = false) {
106106
if (ages_[cut] > 0) {
107107
if (thread_safe) {
108-
int16_t numLp = numLps_[cut].fetch_add(1, std::memory_order_relaxed);
109-
if (numLp >= 0) numLps_[cut].fetch_add(-1, std::memory_order_relaxed);
108+
usedInRound_[cut] = true;
110109
return;
111110
}
112111
if (matrix_.columnsLinked(cut)) {

0 commit comments

Comments
 (0)