-
Notifications
You must be signed in to change notification settings - Fork 525
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
NL writer: Add custom exception for no free variables #3445
base: main
Are you sure you want to change the base?
NL writer: Add custom exception for no free variables #3445
Conversation
Some feedback may be useful as I'm not sure if there are any implications on presolved values or other things I'm unaware of. My use case is "everything is defined", so solving can be skipped. |
pyomo/repn/plugins/nl_writer.py
Outdated
class NLWriterEmptyModelError(ValueError): | ||
""" | ||
A custom exception to allow handling of a | ||
model with no free variables. | ||
""" | ||
|
||
pass |
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.
Suggestion here: We have a "custom" exception from pyomo/common/errors
called PyomoException
that you might want to inherit from instead. We do this fairly frequently for custom error types so it is also clear when an exception comes from Pyomo as opposed to something else (e.g., gurobipy
or idaes
).
Example:
from pyomo.common.errors import PyomoException
class MyCustomError(PyomoException):
"""
My custom error
"""
Also note that you don't need to put pass
as long as you have a docstring.
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.
Fixed, I initially had it inheriting from ValueError
since that was raised previously, but I don't suppose there is much need for backwards compatibility in this case!
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.
Nevermind, there were a couple of tests designed to catch this - I have replaced the asserted exception in those tests
|
I have spent the last week debating (with myself [and losing]) what the "expected" behavior here should be. The current implementation dates back to the original NL writer, and followed the logic that since some mix of AMPL / the solver will die a horrible death of you give it an NL file with no variables, then we should consider a model with no free variables an "error" (and raise an exception). That seemed reasonable, but now with the introduction of the presolver, it is possible that a "valid" model could be square and completely solvable in the presolve (leaving us with an empty NL file). In this case, we can't raise an exception because we need the NL writer to return the presolve information so that we (the solver / caller) can record / load the computed values into the variables. That means that we need to move the check for an "empty" NL file from the writer and into the solver, and raises the question as to if this exception should be removed entirely. The argument against that is that an "empty" model can be a sign of a modeling error (something like forgetting to correctly load data into indexing sets, so everything is empty), and the exception was meant to prevent the user from assuming that everything was fine (when in fact there was nothing to do). I am wondering if the following is a reasonable path forward:
Thoughts? |
This sounds good to me. To summarise:
|
If I understand correctly, the new solver interface already has support for skipping a solve if it gets presolved? Something like this works: from pyomo.environ import ConcreteModel, Var, Constraint
from pyomo.contrib.solver.factory import SolverFactory
m = ConcreteModel()
m.x = Var()
m.c = Constraint(expr=m.x == 1)
opt = SolverFactory("ipopt")
result = opt.solve(m, tee=True)
print(m.x.value) but interestingly, this doesn't (is this any different from the "truly empty" case?) from pyomo.environ import ConcreteModel, Var
from pyomo.contrib.solver.factory import SolverFactory
m = ConcreteModel()
m.x = Var()
m.x.fix(1)
opt = SolverFactory("ipopt")
result = opt.solve(m, tee=True)
print(m.x.value) |
Also, I think it doesn't necessarily make sense to limit the scope of this PR to just fixing the exception raised by the old nl writer. It is probably a good idea to fix things properly (and on my end, we should look at moving to the new solver factory interface). |
@alma-walmsley - preemptive warning that I have a PR that is going to be up sometime in the next few weeks that restructures the new solver interfaces a bit, so the import path won't be exactly the same (it'll most likely be something like |
Fixes #3411.
Summary/Motivation:
nl_writer.py
raises aValueError
for when no free variables are found when writing thenl
file. However, this is potentially a valid case in my situation (ie. everything is defined; all expressions can just be evaluated). I don't want to try...catch for aValueError
because it is a pretty generic exception, and I do want to error if there are other problems.See the linked issue for a full description.
Changes proposed in this PR:
NLWriterEmptyModelError
, which inherits fromValueError
. This means a try...catch around thesolve
statement (with this specific exception) can be used to handle this case gracefully.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: