@@ -148,7 +148,7 @@ double HighsCutPool::getParallelism(HighsInt row1, HighsInt row2,
148148}
149149
150150void 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
165162void 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);
0 commit comments