GAMS solver sets incorrect termination condition on timeout for MIP model solved with CPLEX
Summary
When I try to solve a MIP model using the Pyomo GAMS solver and CPLEX and it terminates due to the time limit, GAMS returns (extracted from the .lst file):
**** SOLVER STATUS 3 Resource Interrupt
**** MODEL STATUS 8 Integer Solution
As I understand these codes, the solver has found an integer solution, but was interrupted by the time limit, which means that the solution is not necessarily optimal.
However, Pyomo interprets this as an optimal solution and returns
results.solver.status == SolverStatus.ok
results.solver.termination_condition == TerminationCondition.optimal
Steps to reproduce the issue
I cannot reproduce my entire model here, but this issue will occur whenever modelstat 8 and solvestat 3 are returned from a GAMS model. I can trigger that by applying a low value for the time limit (with the GAMS option reslim) to a MIP model that would otherwise take longer to solve.
opt = SolverFactory("gams")
res = opt.solve(
model,
solver="cplex",
mtype="mip",
add_options=["option optcr = 0.0002;", "option reslim = 10;"],
keepfiles=True
)
This is the statuses given in resultsstat.dat:
MODELSTAT 8.000000000000000
SOLVESTAT 3.000000000000000
But the Pyomo result shows:
res.solver.status
>>> <SolverStatus.ok: 'ok'>
res.solver.termination_condition
>>> <TerminationCondition.optimal: 'optimal'>
Error Message
Expected: res.solver.termination_condition == TerminationCondition.maxTimeLimit
Actual: res.solver.termination_condition == TerminationCondition.optimal.
Information on your system
Pyomo version: 6.4.1 Python version: 3.9.1 Operating system: Linux How Pyomo was installed (PyPI, conda, source): PyPI Solver (if applicable): GAMS 24.7.4
Additional information
In GAMS.py, the correct termination condition is first set based on solvestat == 3:
https://github.com/Pyomo/pyomo/blob/85382448f469e60397cefbdb1c26cb685362ea13/pyomo/solvers/plugins/solvers/GAMS.py#L405-L407
However, later in the file, the termination condition is overwritten based on modelstat == 8 (meaning there is an integer solution, but it is not proven optimal):
https://github.com/Pyomo/pyomo/blob/85382448f469e60397cefbdb1c26cb685362ea13/pyomo/solvers/plugins/solvers/GAMS.py#L457-L460
(These references point to the GAMSDirect solver class, but the same checks happen in GAMSShell.)