Pyomo / pyomo

An object-oriented algebraic modeling language in Python for structured optimization problems.
https://www.pyomo.org
Other
1.97k stars 504 forks source link

APPSI - RuntimeError when the problem is infeasible and config.load_solution is True #2363

Closed ZedongPeng closed 2 years ago

ZedongPeng commented 2 years ago

Summary

Currently in APPSI, if the problem is infeasible and config.load_solution=True, a RuntimeError will be raised.

For example, in appsi/solvers/cplex.py, there are several places that report a RuntimeError.

https://github.com/Pyomo/pyomo/blob/b82083bed71bffdb549fca41c18729e75b9b8b53/pyomo/contrib/appsi/solvers/cplex.py#L300-L305

https://github.com/Pyomo/pyomo/blob/b82083bed71bffdb549fca41c18729e75b9b8b53/pyomo/contrib/appsi/solvers/cplex.py#L317-L319

https://github.com/Pyomo/pyomo/blob/b82083bed71bffdb549fca41c18729e75b9b8b53/pyomo/contrib/appsi/solvers/cplex.py#L375-L377

Considering that infeasible problem happens a lot and config.load_solution=True by default, is it too serious to report a RuntimeError in this case? I think a warning or a debug log might be better. If we call _cplex_shell or cplex_direct, no error will be reported if the problem is infeasible.

michaelbynum commented 2 years ago

I have been trying to favor conservative and safe defaults. I don't want someone to miss a warning and proceed thinking they have a solution. However, this is definitely worth a design discussion. Do others have thoughts on this?

bernalde commented 2 years ago

I have an opinion regarding this. Although most people use Pyomo models to solve feasible problems, and a guarantee of infeasibility often means a modeling mistake; there is a considerable number of cases where infeasible solutions are valid solutions. One example is using a MIP solver to address a SAT problem, infeasible is a valid answer to this problem. It seems that having a runtime error for infeasibility is too conservative, and a warning might be enough.

michaelbynum commented 2 years ago

Even though there is a mechanism to say don't throw an error?

michaelbynum commented 2 years ago

What if we set all of the variable values to None by default when a feasible solution is not found? I think that would take care of my concerns, and I would be fine without an error.

bernalde commented 2 years ago

My point is that infeasible problems are not errors per se, most of the time they might mean modeling errors, but sometimes you are expecting your problem to be infeasible. Consider algorithms that take into account the infeasibility of problems to do stuff (e.g., Benders decomposition). Would then obtaining an infeasible problem (which is alright and even expected) lead to the same error as dividing by zero?

michaelbynum commented 2 years ago

The intent of the error is not to say that there is a problem with the model. The intent of the error is to say that a solution cannot be loaded into the model. By default, all solvers automatically load the solution into the model as part of the call to solve. If there is no feasible solution, how do we reconcile these two things? I'm liking the None idea for the moment.

ZedongPeng commented 2 years ago

The None idea also sounds good to me. By the way, I think it's always good to keep APPSI consistent with the base solver interface. https://github.com/Pyomo/pyomo/blob/a82c55771f38b6a5a689c4a40fe0b9040503c320/pyomo/opt/base/solvers.py#L604-L634 For the current RuntimeError implementation, I can use try and except to handle it.

michaelbynum commented 2 years ago

@ZedongPeng, can you elaborate on your last comment about keeping Apps consistent with the base solver interface? Are you referring to the point that the non-appsi solvers do not raise an error?

You do not need a try-except block. Just set load_solution to False. See this example: https://pyomo.readthedocs.io/en/stable/library_reference/appsi/appsi.base.html#pyomo.contrib.appsi.base.Results

michaelbynum commented 2 years ago

At the dev call today, there was consensus that we need to be very conservative here. The default behavior should be to raise an error if the solver did not terminate optimally. However, we did discuss a more flexible mechanism for configuring this behavior (rather than the single load_solution flag).

ZedongPeng commented 2 years ago

@ZedongPeng, can you elaborate on your last comment about keeping Apps consistent with the base solver interface? Are you referring to the point that the non-appsi solvers do not raise an error?

You do not need a try-except block. Just set load_solution to False. See this example: https://pyomo.readthedocs.io/en/stable/library_reference/appsi/appsi.base.html#pyomo.contrib.appsi.base.Results

Yes. I mean that the non-appsi solvers do not raise an error. Moreover, if I call CPLEX through APPSI appsi.solvers.Cplex(), the result returned is not in same format as non-appsi solvers, which has problem, solver and solution. (This is not a problem since SolverFactory('appsi_cplex') will return the same format result.) opt = appsi.solvers.Cplex() opt.solve() doesn't have the tee arguments. We need to set opt.config.stream_solver = True.