@@ -537,6 +537,161 @@ void HighsMipSolver::run() {
537537 }
538538 };
539539
540+ auto doHandlePrunedNodes = [&](HighsInt index, bool thread_safe, bool & flush,
541+ bool & infeasible) {
542+ HighsDomain& globaldom = mipdata_->workers [index].globaldom_ ;
543+ mipdata_->workers [index].search_ptr_ ->backtrack ();
544+ if (!thread_safe) {
545+ ++mipdata_->num_leaves ;
546+ ++mipdata_->num_nodes ;
547+ mipdata_->workers [index].search_ptr_ ->flushStatistics ();
548+ } else {
549+ flush = true ;
550+ }
551+
552+ globaldom.propagate ();
553+ if (!thread_safe) {
554+ mipdata_->pruned_treeweight += mipdata_->nodequeue .pruneInfeasibleNodes (
555+ mipdata_->domain , mipdata_->feastol );
556+ }
557+
558+ if (globaldom.infeasible ()) {
559+ infeasible = true ;
560+ if (!thread_safe) {
561+ mipdata_->nodequeue .clear ();
562+ mipdata_->pruned_treeweight = 1.0 ;
563+
564+ double prev_lower_bound = mipdata_->lower_bound ;
565+
566+ mipdata_->lower_bound = std::min (kHighsInf , mipdata_->upper_bound );
567+
568+ bool bound_change = mipdata_->lower_bound != prev_lower_bound;
569+ if (!submip && bound_change)
570+ mipdata_->updatePrimalDualIntegral (
571+ prev_lower_bound, mipdata_->lower_bound , mipdata_->upper_bound ,
572+ mipdata_->upper_bound );
573+ }
574+ return ;
575+ }
576+
577+ if (!thread_safe && mipdata_->checkLimits ()) {
578+ return ;
579+ }
580+
581+ double prev_lower_bound = mipdata_->lower_bound ;
582+
583+ if (!thread_safe) {
584+ mipdata_->lower_bound = std::min (mipdata_->upper_bound ,
585+ mipdata_->nodequeue .getBestLowerBound ());
586+ }
587+
588+ bool bound_change = mipdata_->lower_bound != prev_lower_bound;
589+ if (!submip && bound_change)
590+ mipdata_->updatePrimalDualIntegral (
591+ prev_lower_bound, mipdata_->lower_bound , mipdata_->upper_bound ,
592+ mipdata_->upper_bound );
593+
594+ if (!globaldom.getChangedCols ().empty ()) {
595+ if (!thread_safe) {
596+ highsLogDev (options_mip_->log_options , HighsLogType::kInfo ,
597+ " added %" HIGHSINT_FORMAT " global bound changes\n " ,
598+ (HighsInt)globaldom.getChangedCols ().size ());
599+ mipdata_->cliquetable .cleanupFixed (globaldom);
600+ for (HighsInt col : globaldom.getChangedCols ())
601+ mipdata_->implications .cleanupVarbounds (col);
602+
603+ globaldom.setDomainChangeStack (std::vector<HighsDomainChange>());
604+ mipdata_->workers [index].search_ptr_ ->resetLocalDomain ();
605+
606+ globaldom.clearChangedCols ();
607+ mipdata_->removeFixedIndices ();
608+ } else {
609+ mipdata_->workers [index].search_ptr_ ->resetLocalDomain ();
610+ }
611+ }
612+
613+ analysis_.mipTimerStop (kMipClockNodePrunedLoop );
614+ };
615+
616+ auto handlePrunedNodes =
617+ [&](std::vector<HighsInt>& search_indices) -> std::pair<bool , bool > {
618+ analysis_.mipTimerStart (kMipClockNodePrunedLoop );
619+ // If flush then change statistics for all searches where this was the case
620+ // If infeasible then global domain is infeasible and stop the solve
621+ // If limit_reached then return something appropriate
622+ // In multi-thread case now check limits again after everything has been
623+ // flushed
624+ std::deque<bool > infeasible;
625+ std::deque<bool > flush;
626+ std::vector<bool > prune (search_indices.size (), false );
627+ for (HighsInt i = 0 ; i < search_indices.size (); i++) {
628+ // TODO MT: Remove this redundant if
629+ if (search_indices[i] != 0 ||
630+ !mipdata_->workers [search_indices[i]].search_ptr_ ->currentNodePruned ())
631+ continue ;
632+ infeasible.emplace_back (false );
633+ flush.emplace_back (false );
634+ if (mipdata_->parallelLockActive ()) {
635+ tg.spawn ([&, i]() {
636+ doHandlePrunedNodes (search_indices[i], mipdata_->parallelLockActive (),
637+ flush[i], infeasible[i]);
638+ });
639+ } else {
640+ doHandlePrunedNodes (search_indices[i], mipdata_->parallelLockActive (),
641+ flush[i], infeasible[i]);
642+ }
643+ // This search object is "finished" and needs a new node
644+ prune[i] = true ;
645+ }
646+ if (mipdata_->parallelLockActive ()) {
647+ tg.taskWait ();
648+ for (HighsInt i = 0 ; i < search_indices.size () && i < flush.size (); i++) {
649+ if (flush[i]) {
650+ ++mipdata_->num_leaves ;
651+ ++mipdata_->num_nodes ;
652+ mipdata_->workers [search_indices[i]].search_ptr_ ->flushStatistics ();
653+ }
654+ }
655+ }
656+
657+ // Remove search indices that need a new node
658+ HighsInt num_search_indices =
659+ static_cast <HighsInt>(search_indices.size ());
660+ for (HighsInt i = num_search_indices - 1 ; i >= 0 ; i--) {
661+ if (prune[i]) {
662+ num_search_indices--;
663+ std::swap (search_indices[i], search_indices[num_search_indices]);
664+ }
665+ }
666+ search_indices.resize (num_search_indices);
667+
668+ for (bool status : infeasible) {
669+ if (status) {
670+ mipdata_->nodequeue .clear ();
671+ mipdata_->pruned_treeweight = 1.0 ;
672+
673+ double prev_lower_bound = mipdata_->lower_bound ;
674+
675+ mipdata_->lower_bound = std::min (kHighsInf , mipdata_->upper_bound );
676+
677+ bool bound_change = mipdata_->lower_bound != prev_lower_bound;
678+ if (!submip && bound_change)
679+ mipdata_->updatePrimalDualIntegral (
680+ prev_lower_bound, mipdata_->lower_bound , mipdata_->upper_bound ,
681+ mipdata_->upper_bound );
682+ analysis_.mipTimerStop (kMipClockNodePrunedLoop );
683+ return std::make_pair (false , true );
684+ }
685+ }
686+
687+ if (mipdata_->checkLimits ()) {
688+ analysis_.mipTimerStop (kMipClockNodePrunedLoop );
689+ return std::make_pair (true , false );
690+ }
691+ analysis_.mipTimerStop (kMipClockNodePrunedLoop );
692+ return std::make_pair (false , false );
693+ };
694+
540695 auto diveAllSearches = [&]() -> bool {
541696 std::vector<double > dive_times (mip_search_concurrency,
542697 -analysis_.mipTimerRead (kMipClockTheDive ));
@@ -894,75 +1049,11 @@ void HighsMipSolver::run() {
8941049
8951050 // if the node was pruned we remove it from the search and install the
8961051 // next node from the queue
897- analysis_.mipTimerStart (kMipClockNodePrunedLoop );
898- if (search.currentNodePruned ()) {
899- // analysis_.mipTimerStart(kMipClockSearchBacktrack);
900- search.backtrack ();
901- // analysis_.mipTimerStop(kMipClockSearchBacktrack);
902- ++mipdata_->num_leaves ;
903- ++mipdata_->num_nodes ;
904- search.flushStatistics ();
905-
906- mipdata_->domain .propagate ();
907- mipdata_->pruned_treeweight += mipdata_->nodequeue .pruneInfeasibleNodes (
908- mipdata_->domain , mipdata_->feastol );
909-
910- if (mipdata_->domain .infeasible ()) {
911- mipdata_->nodequeue .clear ();
912- mipdata_->pruned_treeweight = 1.0 ;
913-
914- double prev_lower_bound = mipdata_->lower_bound ;
915-
916- mipdata_->lower_bound = std::min (kHighsInf , mipdata_->upper_bound );
917-
918- bool bound_change = mipdata_->lower_bound != prev_lower_bound;
919- if (!submip && bound_change)
920- mipdata_->updatePrimalDualIntegral (
921- prev_lower_bound, mipdata_->lower_bound , mipdata_->upper_bound ,
922- mipdata_->upper_bound );
923- analysis_.mipTimerStop (kMipClockNodePrunedLoop );
924- break ;
925- }
926-
927- if (mipdata_->checkLimits ()) {
928- limit_reached = true ;
929- break ;
930- }
931-
932- // analysis_.mipTimerStart(kMipClockStoreBasis);
933- double prev_lower_bound = mipdata_->lower_bound ;
934-
935- mipdata_->lower_bound = std::min (
936- mipdata_->upper_bound , mipdata_->nodequeue .getBestLowerBound ());
937-
938- bool bound_change = mipdata_->lower_bound != prev_lower_bound;
939- if (!submip && bound_change)
940- mipdata_->updatePrimalDualIntegral (
941- prev_lower_bound, mipdata_->lower_bound , mipdata_->upper_bound ,
942- mipdata_->upper_bound );
943- mipdata_->printDisplayLine ();
944-
945- if (!mipdata_->domain .getChangedCols ().empty ()) {
946- highsLogDev (options_mip_->log_options , HighsLogType::kInfo ,
947- " added %" HIGHSINT_FORMAT " global bound changes\n " ,
948- (HighsInt)mipdata_->domain .getChangedCols ().size ());
949- mipdata_->cliquetable .cleanupFixed (mipdata_->domain );
950- for (HighsInt col : mipdata_->domain .getChangedCols ())
951- mipdata_->implications .cleanupVarbounds (col);
952-
953- mipdata_->domain .setDomainChangeStack (
954- std::vector<HighsDomainChange>());
955- search.resetLocalDomain ();
956-
957- mipdata_->domain .clearChangedCols ();
958- mipdata_->removeFixedIndices ();
959- }
960- // analysis_.mipTimerStop(kMipClockStoreBasis);
961-
962- analysis_.mipTimerStop (kMipClockNodePrunedLoop );
963- continue ;
964- }
965- analysis_.mipTimerStop (kMipClockNodePrunedLoop );
1052+ std::pair<bool , bool > limit_or_infeas = handlePrunedNodes (search_indices);
1053+ if (limit_or_infeas.first ) limit_reached = true ;
1054+ if (limit_or_infeas.first || limit_or_infeas.second ) break ;
1055+ // TODO MT: Change this line
1056+ if (search_indices.empty () || search_indices[0 ] != 0 ) continue ;
9661057
9671058 // the node is still not fathomed, so perform separation
9681059 analysis_.mipTimerStart (kMipClockNodeSearchSeparation );
0 commit comments