@@ -501,6 +501,16 @@ void HighsMipSolver::run() {
501501 return search_indices;
502502 };
503503
504+ auto getSearchIndicesWithNodes = [&]() -> std::vector<HighsInt> {
505+ std::vector<HighsInt> search_indices;
506+ for (HighsInt i = 0 ; i < mip_search_concurrency; i++) {
507+ if (mipdata_->workers [i].search_ptr_ ->hasNode ()) {
508+ search_indices.emplace_back (i);
509+ }
510+ }
511+ return search_indices;
512+ };
513+
504514 auto installNodes = [&](std::vector<HighsInt>& search_indices,
505515 bool & limit_reached) -> void {
506516 for (HighsInt index : search_indices) {
@@ -791,9 +801,81 @@ void HighsMipSolver::run() {
791801 return false ;
792802 };
793803
804+ auto doRunHeuristics = [&](HighsMipWorker& worker) -> void {
805+ bool clocks = !mipdata_->parallelLockActive ();
806+ if (clocks) analysis_.mipTimerStart (kMipClockDiveEvaluateNode );
807+ const HighsSearch::NodeResult evaluate_node_result =
808+ worker.search_ptr_ ->evaluateNode ();
809+ if (clocks) analysis_.mipTimerStop (kMipClockDiveEvaluateNode );
810+
811+ if (evaluate_node_result == HighsSearch::NodeResult::kSubOptimal ) return ;
812+
813+ if (worker.search_ptr_ ->currentNodePruned ()) {
814+ if (clocks) {
815+ ++mipdata_->num_leaves ;
816+ search.flushStatistics ();
817+ }
818+ } else {
819+ if (clocks) analysis_.mipTimerStart (kMipClockDivePrimalHeuristics );
820+ // TODO MT: Make trivial heuristics work locally
821+ if (mipdata_->incumbent .empty () && clocks) {
822+ analysis_.mipTimerStart (kMipClockDiveRandomizedRounding );
823+ mipdata_->heuristics .randomizedRounding (
824+ worker,
825+ worker.lprelaxation_ ->getLpSolver ().getSolution ().col_value );
826+ analysis_.mipTimerStop (kMipClockDiveRandomizedRounding );
827+ }
828+
829+ if (mipdata_->incumbent .empty ()) {
830+ if (options_mip_->mip_heuristic_run_rens ) {
831+ if (clocks) analysis_.mipTimerStart (kMipClockDiveRens );
832+ mipdata_->heuristics .RENS (
833+ worker,
834+ worker.lprelaxation_ ->getLpSolver ().getSolution ().col_value );
835+ if (clocks) analysis_.mipTimerStop (kMipClockDiveRens );
836+ }
837+ } else {
838+ if (options_mip_->mip_heuristic_run_rins ) {
839+ if (clocks) analysis_.mipTimerStart (kMipClockDiveRins );
840+ mipdata_->heuristics .RINS (
841+ worker,
842+ worker.lprelaxation_ ->getLpSolver ().getSolution ().col_value );
843+ if (clocks) analysis_.mipTimerStop (kMipClockDiveRins );
844+ }
845+ }
846+
847+ if (clocks) mipdata_->heuristics .flushStatistics (master_worker);
848+ if (clocks) analysis_.mipTimerStop (kMipClockDivePrimalHeuristics );
849+ }
850+ };
851+
852+ auto runHeuristics = [&]() -> void {
853+ setParallelLock (true );
854+ std::vector<HighsInt> search_indices = getSearchIndicesWithNodes ();
855+ for (HighsInt i : search_indices) {
856+ if (mipdata_->parallelLockActive ()) {
857+ tg.spawn ([&, i]() { doRunHeuristics (mipdata_->workers [i]); });
858+ } else {
859+ doRunHeuristics (mipdata_->workers [i]);
860+ }
861+ }
862+ if (mipdata_->parallelLockActive ()) {
863+ tg.taskWait ();
864+ for (const HighsInt i : search_indices) {
865+ if (mipdata_->workers [i].search_ptr_ ->currentNodePruned ()) {
866+ ++mipdata_->num_leaves ;
867+ search.flushStatistics ();
868+ } else {
869+ mipdata_->heuristics .flushStatistics (mipdata_->workers [i]);
870+ }
871+ }
872+ }
873+ };
874+
794875 auto diveAllSearches = [&]() -> bool {
795876 std::vector<double > dive_times (mip_search_concurrency,
796877 -analysis_.mipTimerRead (kMipClockTheDive ));
878+ analysis_.mipTimerStart (kMipClockTheDive );
797879 std::vector<HighsSearch::NodeResult> dive_results (
798880 mip_search_concurrency, HighsSearch::NodeResult::kBranched );
799881 setParallelLock (true );
@@ -816,6 +898,7 @@ void HighsMipSolver::run() {
816898 dive_times[0 ] += analysis_.mipTimerRead (kMipClockNodeSearch );
817899 }
818900 }
901+ analysis_.mipTimerStop (kMipClockTheDive );
819902 setParallelLock (false );
820903 bool suboptimal = false ;
821904 for (int i = 0 ; i < mip_search_concurrency; i++) {
@@ -869,48 +952,7 @@ void HighsMipSolver::run() {
869952 while (true ) {
870953 // Possibly apply primal heuristics
871954 if (considerHeuristics && mipdata_->moreHeuristicsAllowed ()) {
872- analysis_.mipTimerStart (kMipClockDiveEvaluateNode );
873- const HighsSearch::NodeResult evaluate_node_result =
874- search.evaluateNode ();
875- analysis_.mipTimerStop (kMipClockDiveEvaluateNode );
876-
877- if (evaluate_node_result == HighsSearch::NodeResult::kSubOptimal ) break ;
878-
879- if (search.currentNodePruned ()) {
880- // ig: do we update num_leaves here?
881- ++mipdata_->num_leaves ;
882- search.flushStatistics ();
883- } else {
884- analysis_.mipTimerStart (kMipClockDivePrimalHeuristics );
885- if (mipdata_->incumbent .empty ()) {
886- analysis_.mipTimerStart (kMipClockDiveRandomizedRounding );
887- mipdata_->heuristics .randomizedRounding (
888- master_worker,
889- mipdata_->lp .getLpSolver ().getSolution ().col_value );
890- analysis_.mipTimerStop (kMipClockDiveRandomizedRounding );
891- }
892-
893- if (mipdata_->incumbent .empty ()) {
894- if (options_mip_->mip_heuristic_run_rens ) {
895- analysis_.mipTimerStart (kMipClockDiveRens );
896- mipdata_->heuristics .RENS (
897- master_worker,
898- mipdata_->lp .getLpSolver ().getSolution ().col_value );
899- analysis_.mipTimerStop (kMipClockDiveRens );
900- }
901- } else {
902- if (options_mip_->mip_heuristic_run_rins ) {
903- analysis_.mipTimerStart (kMipClockDiveRins );
904- mipdata_->heuristics .RINS (
905- master_worker,
906- mipdata_->lp .getLpSolver ().getSolution ().col_value );
907- analysis_.mipTimerStop (kMipClockDiveRins );
908- }
909- }
910-
911- mipdata_->heuristics .flushStatistics (master_worker);
912- analysis_.mipTimerStop (kMipClockDivePrimalHeuristics );
913- }
955+ runHeuristics ();
914956 }
915957
916958 considerHeuristics = false ;
0 commit comments