@@ -73,12 +73,11 @@ HighsMipSolver::~HighsMipSolver() = default;
7373
7474template <class F >
7575void HighsMipSolver::runTask (F&& f, highs::parallel::TaskGroup& tg,
76- bool parallel_lock,
76+ bool parallel_lock, bool force_serial,
7777 const std::vector<HighsInt>& indices) {
7878 if (indices.empty ()) return ;
7979 setParallelLock (parallel_lock);
80- const bool spawn_tasks = mipdata_->parallelLockActive () &&
81- indices.size () > 1 &&
80+ const bool spawn_tasks = !force_serial && indices.size () > 1 &&
8281 !options_mip_->mip_search_simulate_concurrency ;
8382 for (HighsInt i : indices) {
8483 if (spawn_tasks) {
@@ -396,6 +395,8 @@ void HighsMipSolver::run() {
396395#endif
397396 worker.getGlobalDomain ().setDomainChangeStack (
398397 std::vector<HighsDomainChange>());
398+ // Warning: Resetting local domain cannot be done in parallel (changes
399+ // propagationDomains of main pool)
399400 worker.search_ptr_ ->resetLocalDomain ();
400401 worker.getGlobalDomain ().clearChangedCols ();
401402 };
@@ -412,7 +413,7 @@ void HighsMipSolver::run() {
412413 // Sync worker domains here. cleanupFixed might have found extra changes
413414 std::vector<HighsInt> indices (num_workers);
414415 std::iota (indices.begin (), indices.end (), 0 );
415- runTask (doResetWorkerDomain, tg, false , indices);
416+ runTask (doResetWorkerDomain, tg, false , true , indices);
416417 }
417418 for (const HighsInt col : mipdata_->domain .getChangedCols ())
418419 mipdata_->implications .cleanupVarbounds (col);
@@ -448,7 +449,7 @@ void HighsMipSolver::run() {
448449 auto doResetWorkerPseudoCost = [&](HighsInt i) -> void {
449450 mipdata_->pseudocost .syncPseudoCost (mipdata_->workers [i].getPseudocost ());
450451 };
451- runTask (doResetWorkerPseudoCost, tg, false , indices);
452+ runTask (doResetWorkerPseudoCost, tg, false , false , indices);
452453 };
453454
454455 destroyOldWorkers ();
@@ -566,7 +567,7 @@ void HighsMipSolver::run() {
566567 search_results[i] = mipdata_->workers [i].search_ptr_ ->evaluateNode ();
567568 };
568569 analysis_.mipTimerStart (kMipClockEvaluateNode1 );
569- runTask (doEvaluateNode, tg, true , indices);
570+ runTask (doEvaluateNode, tg, true , false , indices);
570571 analysis_.mipTimerStop (kMipClockEvaluateNode1 );
571572 for (size_t i = 0 ; i != indices.size (); i++) {
572573 HighsInt worker_id = indices[i];
@@ -633,7 +634,7 @@ void HighsMipSolver::run() {
633634 mipdata_->upper_bound );
634635 };
635636 analysis_.mipTimerStart (kMipClockNodePrunedLoop );
636- runTask (doHandlePrunedNodes, tg, true , indices);
637+ runTask (doHandlePrunedNodes, tg, true , false , indices);
637638 // Flush pruned nodes statistics that haven't yet been flushed
638639 for (HighsInt i : indices) {
639640 if (flush[i] == 1 ) {
@@ -697,7 +698,7 @@ void HighsMipSolver::run() {
697698 mipdata_->workers [i].search_ptr_ ->getLocalDomain ());
698699 };
699700 analysis_.mipTimerStart (kMipClockNodeSearchSeparation );
700- runTask (doSeparate, tg, true , indices);
701+ runTask (doSeparate, tg, true , false , indices);
701702 analysis_.mipTimerStop (kMipClockNodeSearchSeparation );
702703
703704 auto syncSepaStats = [&](HighsMipWorker& worker) {
@@ -755,7 +756,7 @@ void HighsMipSolver::run() {
755756 }
756757 };
757758
758- runTask (doStoreBasis, tg, false , indices);
759+ runTask (doStoreBasis, tg, false , false , indices);
759760 return false ;
760761 };
761762
@@ -775,7 +776,7 @@ void HighsMipSolver::run() {
775776 };
776777
777778 analysis_.mipTimerStart (kMipClockBacktrackPlunge );
778- runTask (doBacktrackPlunge, tg, true , indices);
779+ runTask (doBacktrackPlunge, tg, true , false , indices);
779780 analysis_.mipTimerStop (kMipClockBacktrackPlunge );
780781
781782 // Remove search indices that were not backtracked
@@ -819,6 +820,27 @@ void HighsMipSolver::run() {
819820 return ;
820821 }
821822
823+ // TODO MT: ERORRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRRR
824+ // When a domain is created (based on another existing domain)
825+ // , e.g., in randomizedRounding (as an object
826+ // that we can propagate and play around with) or in RINS (where one
827+ // is created as part of the HighsSearch object), it is going to notify
828+ // all cutpools / conflictpools that the original was propagating.
829+ // This "notify" is going to append the domain to the vector of
830+ // pools, which is going to be non-deterministic and error-prone.
831+ // We therefore need to either make the vector robust to multiple changes,
832+ // or consider not propagating the main pool if parallel lock is active?
833+ // The first would be complicated, and the second would make
834+ // heuristics take longer (potentially less effective?)
835+ // For the second: iN THE HighsDomain constructor there is the line,
836+ // cutpoolpropagation(other.cutpoolpropagation),
837+ // This will call the line:
838+ // HighsDomain::CutpoolPropagation::CutpoolPropagation()
839+ // which will call this cutpool->addPropagationDomain(this);
840+ // Consider checking at that stage and simply not notifying the cut pool!
841+ // Information would be one way -> cutpool update wouldn't change
842+ // the local domain, but that's not a problem in this case.
843+
822844 // analysis_.mipTimerStart(kMipClockDivePrimalHeuristics);
823845 if (mipdata_->incumbent .empty ()) {
824846 // analysis_.mipTimerStart(kMipClockDiveRandomizedRounding);
@@ -844,7 +866,7 @@ void HighsMipSolver::run() {
844866
845867 // analysis_.mipTimerStop(kMipClockDivePrimalHeuristics);
846868 };
847- runTask (doRunHeuristics, tg, true , indices);
869+ runTask (doRunHeuristics, tg, true , false , indices);
848870 for (const HighsInt i : indices) {
849871 if (suboptimal[i] == 0 ) {
850872 if (mipdata_->workers [i].search_ptr_ ->currentNodePruned ()) {
@@ -886,7 +908,7 @@ void HighsMipSolver::run() {
886908 return ;
887909 dive_results[i] = worker.search_ptr_ ->dive ();
888910 };
889- runTask (doDiveSearch, tg, true , non_pruned_indices);
911+ runTask (doDiveSearch, tg, true , false , non_pruned_indices);
890912 analysis_.mipTimerStop (kMipClockTheDive );
891913
892914 for (const HighsInt i : non_pruned_indices) {
0 commit comments