-
Notifications
You must be signed in to change notification settings - Fork 290
Add logging and time budget for probing in presolve, and increase visibility of presolve rule switch-off #2865
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 8 commits
9c097ca
e8a4198
350ce51
3a92b40
591fb8d
f6de02b
849b1df
9b202d2
1c2cb33
06309ae
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -1683,12 +1683,70 @@ HPresolve::Result HPresolve::runProbing(HighsPostsolveStack& postsolve_stack) { | |
| }; | ||
| } | ||
|
|
||
| assert(mipsolver); | ||
| // Don't log probing for presolve before restart | ||
| const bool silent = mipsolver->mipdata_->numRestarts > 0; | ||
| HighsInt iBin = -1; | ||
| HighsInt iBin_probed = -1; | ||
| HighsInt num_binary = binaries.size(); | ||
| double log_tt_interval = 5.0; | ||
| double tt0 = this->timer->read(); | ||
| double log_tt = tt0; | ||
| HighsInt log_iBin_probed = iBin_probed; | ||
| double time_remaining = options->time_limit - tt0; | ||
| double probing_time_limit = 0.1 * time_remaining; | ||
| bool logged_probing_time_limit = false; | ||
| for (const auto& binvar : binaries) { | ||
| iBin++; | ||
| HighsInt i = std::get<3>(binvar); | ||
|
|
||
| if (cliquetable.getSubstitution(i) != nullptr || !domain.isBinary(i)) | ||
| continue; | ||
|
|
||
| iBin_probed++; | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Would it be worth putting the new code that checks the time limit into a lambda? This could also be done later - it would make reading the probing code easier for me.
Member
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Will do |
||
| double tt = this->timer->read(); | ||
| if (tt > log_tt + log_tt_interval && iBin_probed > log_iBin_probed) { | ||
|
||
| if (!silent && !logged_probing_time_limit && | ||
| probing_time_limit < kHighsInf && !options->timeless_log) { | ||
| highsLogUser(options->log_options, HighsLogType::kInfo, | ||
| " Probing %d binaries with a time limit of %s\n", | ||
| int(num_binary), | ||
| highsTimeSecondToString(probing_time_limit).c_str()); | ||
| logged_probing_time_limit = true; | ||
| } | ||
| if (tt > probing_time_limit) { | ||
| if (!silent) | ||
| highsLogUser( | ||
| options->log_options, HighsLogType::kWarning, | ||
| " Probing time limit reached: solver behaviour may be " | ||
| "non-deterministic\n"); | ||
| return Result::kStopped; | ||
| } | ||
| if (!silent && !options->timeless_log) { | ||
| assert(iBin_probed > 0); | ||
| HighsInt dl_iBin_probed = iBin_probed - log_iBin_probed; | ||
| assert(dl_iBin_probed > 0); | ||
| double rate0 = (tt - tt0) / double(iBin_probed); | ||
| double rate1 = (tt - log_tt) / double(dl_iBin_probed); | ||
| double rate = std::max(rate0, rate1); | ||
| std::string rate_str = | ||
| " (rate " + highsTimeToString(1e3 * rate) + "/ms"; | ||
| double expected_probing_finish_time = | ||
| tt + rate * (num_binary - iBin_probed); | ||
| std::string expected_probing_finish_time_str = | ||
| " => expected probing finish time " + | ||
| highsTimeSecondToString(expected_probing_finish_time) + ")"; | ||
| std::string time_str = highsTimeSecondToString(tt); | ||
| highsLogUser( | ||
| options->log_options, HighsLogType::kInfo, | ||
| " Considered %d / %d binaries; %d probed %s%s %s\n", int(iBin), | ||
| int(num_binary), int(iBin_probed), rate_str.c_str(), | ||
| expected_probing_finish_time_str.c_str(), time_str.c_str()); | ||
| log_tt = tt; | ||
| log_iBin_probed = iBin_probed; | ||
| } | ||
| } | ||
|
|
||
| bool tightenLimits = (numProbed - oldNumProbed) >= 2500; | ||
|
|
||
| // when a large percentage of columns have been deleted, stop this round | ||
|
|
@@ -1761,9 +1819,6 @@ HPresolve::Result HPresolve::runProbing(HighsPostsolveStack& postsolve_stack) { | |
| if (numLiftOpps >= maxNumLiftOpps) | ||
| implications.storeLiftingOpportunity = nullptr; | ||
|
|
||
| // printf("nprobed: %" HIGHSINT_FORMAT ", numCliques: %" HIGHSINT_FORMAT | ||
| // "\n", nprobed, | ||
| // cliquetable.numCliques()); | ||
| if (domain.infeasible()) { | ||
| mipsolver->analysis_.mipTimerStop(kMipClockProbingPresolve); | ||
| return Result::kPrimalInfeasible; | ||
|
|
@@ -5468,35 +5523,33 @@ HPresolve::Result HPresolve::presolve(HighsPostsolveStack& postsolve_stack) { | |
| model->sense_ = ObjSense::kMinimize; | ||
| } | ||
|
|
||
| const bool silent = mipsolver && mipsolver->mipdata_->numRestarts > 0; | ||
|
||
| if (options->presolve != kHighsOffString) { | ||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
|
||
| if (!silent) | ||
| highsLogUser(options->log_options, HighsLogType::kInfo, | ||
| "Presolving model\n"); | ||
| } | ||
| // Set up the logic to allow presolve rules, and logging for their | ||
| // effectiveness | ||
| analysis_.setup(this->model, this->options, this->numDeletedRows, | ||
| this->numDeletedCols); | ||
| this->numDeletedCols, silent); | ||
|
|
||
| if (options->presolve != kHighsOffString) { | ||
| if (mipsolver) mipsolver->mipdata_->cliquetable.setPresolveFlag(true); | ||
| if (!mipsolver || mipsolver->mipdata_->numRestarts == 0) | ||
| highsLogUser(options->log_options, HighsLogType::kInfo, | ||
| "Presolving model\n"); | ||
|
|
||
| auto report = [&]() { | ||
| if (!mipsolver || mipsolver->mipdata_->numRestarts == 0) { | ||
| if (!silent) { | ||
| HighsInt numCol = model->num_col_ - numDeletedCols; | ||
| HighsInt numRow = model->num_row_ - numDeletedRows; | ||
| HighsInt numNonz = | ||
| static_cast<HighsInt>(Avalue.size() - freeslots.size()); | ||
| // Only read the run time if it's to be printed | ||
| const double run_time = options->output_flag ? this->timer->read() : 0; | ||
| #ifndef NDEBUG | ||
| std::string time_str = " " + std::to_string(run_time) + "s"; | ||
| #else | ||
| std::string time_str = | ||
| " " + std::to_string(static_cast<int>(run_time)) + "s"; | ||
| #endif | ||
| std::string time_str = highsTimeSecondToString(run_time); | ||
| if (options->timeless_log) time_str = ""; | ||
| highsLogUser(options->log_options, HighsLogType::kInfo, | ||
| "%" HIGHSINT_FORMAT " rows, %" HIGHSINT_FORMAT | ||
| " cols, %" HIGHSINT_FORMAT " nonzeros %s\n", | ||
| " cols, %" HIGHSINT_FORMAT " nonzeros %s\n", | ||
|
||
| numRow, numCol, numNonz, time_str.c_str()); | ||
| } | ||
| }; | ||
|
|
@@ -6171,28 +6224,31 @@ HPresolve::Result HPresolve::removeDependentEquations( | |
| const double time_limit = | ||
| std::max(1.0, std::min(0.01 * options->time_limit, 1000.0)); | ||
| factor.setTimeLimit(time_limit); | ||
| const bool silent = mipsolver && mipsolver->mipdata_->numRestarts > 0; | ||
| // Determine rank deficiency of the equations | ||
| highsLogUser(options->log_options, HighsLogType::kInfo, | ||
| "Dependent equations search running on %d equations with time " | ||
| "limit of %.2fs\n", | ||
| static_cast<int>(matrix.num_col_), time_limit); | ||
| if (!silent) | ||
| highsLogUser(options->log_options, HighsLogType::kInfo, | ||
| "Dependent equations search running on %d equations with time " | ||
| "limit of %.2fs\n", | ||
| static_cast<int>(matrix.num_col_), time_limit); | ||
| double time_taken = -this->timer->read(); | ||
| HighsInt build_return = factor.build(); | ||
| time_taken += this->timer->read(); | ||
| if (build_return == kBuildKernelReturnTimeout) { | ||
| // HFactor::build has timed out, so just return | ||
| highsLogUser(options->log_options, HighsLogType::kInfo, | ||
| "Dependent equations search terminated after %.3gs due to " | ||
| "expected time exceeding limit\n", | ||
| time_taken); | ||
| if (!silent) | ||
| highsLogUser(options->log_options, HighsLogType::kInfo, | ||
| "Dependent equations search terminated after %.3gs due to " | ||
| "expected time exceeding limit\n", | ||
| time_taken); | ||
| analysis_.logging_on_ = logging_on; | ||
| if (logging_on) | ||
| analysis_.stopPresolveRuleLog(kPresolveRuleDependentFreeCols); | ||
| return Result::kOk; | ||
| } else { | ||
| double pct_off_timeout = | ||
| 1e2 * std::fabs(time_taken - time_limit) / time_limit; | ||
| if (pct_off_timeout < 1.0) | ||
| if (!silent && pct_off_timeout < 1.0) | ||
| highsLogUser(options->log_options, HighsLogType::kWarning, | ||
| "Dependent equations search finished within %.2f%% of limit " | ||
| "of %.2fs: " | ||
|
|
@@ -6217,11 +6273,12 @@ HPresolve::Result HPresolve::removeDependentEquations( | |
| num_fictitious_rows_skipped++; | ||
| } | ||
| } | ||
| highsLogUser(options->log_options, HighsLogType::kInfo, | ||
| "Dependent equations search removed %d rows and %d nonzeros " | ||
| "in %.2fs (limit = %.2fs)\n", | ||
| static_cast<int>(num_removed_row), | ||
| static_cast<int>(num_removed_nz), time_taken, time_limit); | ||
| if (!silent) | ||
| highsLogUser(options->log_options, HighsLogType::kInfo, | ||
| "Dependent equations search removed %d rows and %d nonzeros " | ||
| "in %.2fs (limit = %.2fs)\n", | ||
| static_cast<int>(num_removed_row), | ||
| static_cast<int>(num_removed_nz), time_taken, time_limit); | ||
| if (num_fictitious_rows_skipped) | ||
| highsLogDev(options->log_options, HighsLogType::kInfo, | ||
| ", avoiding %d fictitious rows", | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I understand that a time limit may be needed, but I also do not really like hard-coding things. However, having an option may also be too much.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Agreed, but I felt that something was needed so that users for whom probing is very expensive could get some advantage from it.
Maybe it's better to let probing run to
options->time_limitand print a message suggesting that probing in presolve is switched off.