Separate solver_available and license_available#3730
Separate solver_available and license_available#3730mrmundt wants to merge 12 commits intoPyomo:mainfrom
solver_available and license_available#3730Conversation
| ) | ||
| return self._version_cache | ||
|
|
||
| def license_available( |
There was a problem hiding this comment.
Should we separate acquire_license and license_available?
There was a problem hiding this comment.
Probably. What is the right way to acquire a license in gurobi? Could you help with that?
There was a problem hiding this comment.
For anyone following along, we are discussing this offline.
available and license_availablesolver_available and license_available
michaelbynum
left a comment
There was a problem hiding this comment.
Overall, I think this looks good. I am happy with the design. I have a few questions/suggestions on the details/implementation.
| try: | ||
| env = self.acquire_license(timeout=timeout) | ||
| if env is None: | ||
| self._license_cache = LicenseAvailability.Timeout |
There was a problem hiding this comment.
It is hard to distinguish between a timeout and no license... It looks like it is possible to return Timeout when the result should actually be NotAvailable?
There was a problem hiding this comment.
I messed with this one a lot and am 1000% not sure what the right thing to do is. So basically, if acquire_license times out, it is guaranteed to return None. I think otherwise, it will error. But I was having a hard time testing to ensure that that did actually happen.
| if env is None: | ||
| self._license_cache = LicenseAvailability.Timeout | ||
| return self._license_cache | ||
| except gurobipy.GurobiError as acquire_error: |
There was a problem hiding this comment.
Is it possible to hit this exception? It looks like acquire_license is guarded with try statements in a way that this code cannot be reached?
There was a problem hiding this comment.
Ooo. That's a good point. What about this instead?
if not timeout:
try:
cls._gurobipy_env = gurobipy.Env()
except gurobipy.GurobiError:
# Re-raise so license_available can inspect further
# or so users can explicitly view the error
raise
return cls._gurobipy_envThere was a problem hiding this comment.
That looks good, but I'm not sure there is a point to the try statement at this point???
There was a problem hiding this comment.
The only thing I see that might be useful here is, "What if there is some other type of exception that isn't a GurobiError?" - In that case, we are just returning None. But do we want to do that? Is it better if we just let it explode?
There was a problem hiding this comment.
Sorry - I was not clear. I mean the try statement in acquire_license, not the try statement here.
| except gurobipy.GurobiError as large_error: | ||
| msg = str(large_error).lower() | ||
| status = getattr(large_error, "errno", None) | ||
| if "too large" in msg or status in (10010,): | ||
| self._license_cache = LicenseAvailability.LimitedLicense | ||
| elif "queue" in msg or "timeout" in msg: | ||
| self._license_cache = LicenseAvailability.Timeout | ||
| elif ( | ||
| "no gurobi license" in msg | ||
| or "not licensed" in msg | ||
| or status in (10009,) | ||
| ): | ||
| self._license_cache = LicenseAvailability.NotAvailable | ||
| else: | ||
| self._license_cache = LicenseAvailability.BadLicense |
There was a problem hiding this comment.
Once we have gotten here, don't we know that the result should be LimitedLicense? Is anything else possible?
There was a problem hiding this comment.
I think a timeout or "some other unknown thing" is possible. But not NotAvailable, you're right. What about this?
try:
# We try a 'big' model (more than 2000 vars).
# This should give us all the information we need
# about the license status.
large_model = gurobipy.Model(env=env)
large_model.addVars(range(2001))
large_model.optimize()
self._license_cache = LicenseAvailability.FullLicense
except gurobipy.GurobiError as large_error:
msg = str(large_error).lower()
status = getattr(large_error, "errno", None)
if "too large" in msg or status in (10010,):
self._license_cache = LicenseAvailability.LimitedLicense
elif "queue" in msg or "timeout" in msg:
self._license_cache = LicenseAvailability.Timeout
else:
self._license_cache = LicenseAvailability.Unknown
finally:
large_model.dispose()| self._vars_added_since_update = ComponentSet() | ||
| self._last_results_object: Optional[Results] = None | ||
|
|
||
| def close(self): |
There was a problem hiding this comment.
What motivated this new method?
There was a problem hiding this comment.
See the comment below on release_license - because I made a classmethod version, there was a naming clash, so I needed a bit of a hack to get around it.
| try: | ||
| cls._gurobipy_env = gurobipy.Env() | ||
| except gurobipy.GurobiError: | ||
| # Re-raise so license_available can inspect further | ||
| # or so users can explicitly view the error | ||
| raise |
There was a problem hiding this comment.
This is what I am talking about. This try statement does not seem to do anything except raise any exception that gets raised???
michaelbynum
left a comment
There was a problem hiding this comment.
My remaining comments don't really matter. I'm happy at this point.
|
I'm re-working this one, so closing |
Fixes None (but is part of #3688)
Summary/Motivation:
As part of the solver redesign, we decided to separate the checks for
solver_available(is the solver findable, e.g., through importing, on the path, etc.) andlicense_is_valid(does the user have a valid license to use the solver).This PR introduces a fundamental change in which each derived class is now expected to create both
solver_availableandlicense_available. The combination of these two results is then propagated toavailable(which overall returns a bool stating whether a solver is both findable and licensed).Changes proposed in this PR:
pyomo.contrib.solver.common.availabilityforSolverAvailabilityandLicenseAvailabilityenumslicense_availableandsolver_availabletoSolverBaselicense_availableandsolver_available(and properly do caching for version and availability)configas an argument in IPOPTLegal Acknowledgement
By contributing to this software project, I have read the contribution guide and agree to the following terms and conditions for my contribution: