Skip to content

Commit b8bf59f

Browse files
committed
Create lambda for handling pruned nodes
1 parent f79b79c commit b8bf59f

File tree

1 file changed

+160
-69
lines changed

1 file changed

+160
-69
lines changed

highs/mip/HighsMipSolver.cpp

Lines changed: 160 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)