Fix GDPopt LBB time-limit results - #4010
Conversation
emma58
left a comment
There was a problem hiding this comment.
One question about the LBB test, but otherwise this look good!
| try: | ||
| results = SolverFactory('gdpopt.lbb').solve(m, time_limit=1, tee=False) | ||
| finally: | ||
| GDP_LBB_Solver.reached_time_limit = orig_reached_time_limit |
There was a problem hiding this comment.
I don't understand the try-finally here: It looks like if line 70 throws an exception results will be undefined, which will lead to an exception in the assertions below.
There was a problem hiding this comment.
Addressed in b63db6190. If solve() raises, the original try/finally restores the method and then propagates that exception, so the assertions are not reached and results is not read. I replaced the manual assignment/restoration with patch.object(...) to make that lifecycle explicit; the focused regression test and full GDPopt suite pass.
|
Addressed the LBB test review in
|
|
@emma58, the LBB test follow-up is addressed in |
Fixes #3941.
Summary/Motivation:
The GDPopt LBB time-limit path calls
self._get_final_results_object(), which no longer exists. The method was renamed to_get_final_pyomo_results_object()during an earlier GDPopt rewrite, and this one call site was left stale. Any LBB solve that terminates by hitting its time limit raisesAttributeErrorinstead of returning a results object.The same file already uses the current name at the other termination site (
branch_and_bound.py:288), so this is an isolated leftover rather than an intentional difference.This is a replacement for #3942, which GitHub would not allow me to reopen after it was closed. That PR was approved by @emma58 and was closed for a PR-template/CI-state reason rather than a code concern; the prior review discussion and history remain available there.
Changes proposed in this PR:
_get_final_pyomo_results_object()on the GDPopt LBB time-limit termination path inpyomo/contrib/gdpopt/branch_and_bound.py, matching the other termination site in the same file.TestGDPopt_LBB_TimeLimit.test_time_limit_returns_pyomo_results_object, a solver-independent regression that forces the time-limit branch and asserts a Pyomo results object is returned withmaxTimeLimitand unbounded problem bounds.TestMindtPyGOATimeLimit.test_goa_time_limit_sets_solver_results_condition, documenting that the corresponding MindtPy GOA time-limit path already sets its termination condition correctly.main:python -m pytest -q pyomo/contrib/gdpopt/tests/test_LBB.py pyomo/contrib/mindtpy/tests/test_mindtpy_no_discrete.py32 passed, 9 skipped, 2 deselected in 2.55spython -m pytest -q pyomo/contrib/gdpopt/tests/76 passed, 35 skipped, 5 deselected in 81.25spython -m black --check --diff pyomo/contrib/gdpopt/branch_and_bound.py pyomo/contrib/gdpopt/tests/test_LBB.py pyomo/contrib/mindtpy/tests/test_mindtpy_no_discrete.py3 files would be left unchangedtypos --config ./.github/workflows/typos.tomlon the three changed filesAI-Use Disclosure
or
AI tools contributed to the development of this PR
Review process (select ONE):
Notes for reviewers (optional): This replacement PR carries over the implementation and tests from #3942 unchanged, then refreshes the branch against current
main. The replacement PR description and branch-refresh workflow were prepared with AI assistance and reviewed before posting. The runtime change is a single call-site rename; reviewers may want to focus on whether the forced-time-limit regression test intest_LBB.pyis the right way to exercise that path without requiring a MINLP subsolver, since it temporarily patchesGDP_LBB_Solver.reached_time_limitand restores it in afinallyblock.Legal Acknowledgement
By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution: