Skip to content

GDPopt LBB time-limit path calls stale _get_final_results_object #3941

@bernalde

Description

@bernalde

Summary

GDP_LBB_Solver._solve_gdp() calls a stale private method, self._get_final_results_object(), when GDPopt LBB exits through the time-limit branch. The current GDPopt base class exposes _get_final_pyomo_results_object() instead, so LBB raises AttributeError instead of returning a normal SolverResults object with maxTimeLimit.

This appears to be distinct from, but related to, the broader open LBB correctness issue in #2483.

Minimal reproducer

This MWE forces the LBB time-limit branch to isolate the stale method call. It does not require external solvers or the downstream GDPlib model.

import pyomo
import pyomo.environ as pyo
from pyomo.gdp import Disjunct, Disjunction
from pyomo.contrib.gdpopt.branch_and_bound import GDP_LBB_Solver

print("pyomo", pyomo.__version__)
print(
    "has _get_final_results_object",
    hasattr(GDP_LBB_Solver, "_get_final_results_object"),
)
print(
    "has _get_final_pyomo_results_object",
    hasattr(GDP_LBB_Solver, "_get_final_pyomo_results_object"),
)

# Force the time-limit exit path without depending on solver runtime.
GDP_LBB_Solver.reached_time_limit = lambda self, config: True

m = pyo.ConcreteModel()
m.x = pyo.Var(bounds=(0, 2))
m.d1 = Disjunct()
m.d2 = Disjunct()
m.d1.c = pyo.Constraint(expr=m.x <= 0.5)
m.d2.c = pyo.Constraint(expr=m.x >= 1.5)
m.disj = Disjunction(expr=[m.d1, m.d2])
m.obj = pyo.Objective(expr=m.x)

pyo.SolverFactory("gdpopt.lbb").solve(m, time_limit=1, tee=False)

Actual output

pyomo 6.10.0
has _get_final_results_object False
has _get_final_pyomo_results_object True
WARNING: 09/06/22: The GDPopt LBB algorithm currently has known issues. Please
use the results with caution and report any bugs!
Traceback (most recent call last):
  File "<string>", line 1, in <module>
  File ".../pyomo/contrib/gdpopt/branch_and_bound.py", line 90, in solve
    return super().solve(model, **kwds)
  File ".../pyomo/contrib/gdpopt/algorithm_base_class.py", line 128, in solve
    self._solve_gdp(model, config)
  File ".../pyomo/contrib/gdpopt/branch_and_bound.py", line 239, in _solve_gdp
    return self._get_final_results_object()
AttributeError: 'GDP_LBB_Solver' object has no attribute '_get_final_results_object'. Did you mean: '_get_final_pyomo_results_object'?

Expected behavior

LBB should return a normal Pyomo SolverResults object from the time-limit branch, with the termination condition set by reached_time_limit(config).

Likely fix

In pyomo/contrib/gdpopt/branch_and_bound.py, the time-limit branch currently contains:

return self._get_final_results_object()

The leaf-node return path in the same method already uses the current method name:

return self._get_final_pyomo_results_object()

Changing the time-limit branch to call _get_final_pyomo_results_object() appears to resolve the AttributeError. A local monkeypatch aliasing the old name to the current method let the time-limit path return normally in the downstream reproducer.

Downstream context

This was encountered while benchmarking GDPlib's gdp_col model:

In the GDPlib run, both DICOPT-backed and BARON-backed gdpopt.lbb campaigns reached this same AttributeError after the LBB time-limit path. With a local method-name monkeypatch, the same gdp_col LBB path returned maxTimeLimit cleanly, although it still did not find a feasible incumbent. That suggests the stale method call is a reporting/control-flow bug independent of the model formulation.

Environment

Observed locally with:

  • Pyomo: 6.10.0
  • Python: 3.12.13
  • OS/platform: Linux WSL2, glibc 2.39

I also checked pyomo/contrib/gdpopt/branch_and_bound.py on upstream Pyomo/pyomo main via GitHub and saw the same stale call in the time-limit branch.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type
    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions