-
Notifications
You must be signed in to change notification settings - Fork 92
DNMY: add solver-independent(-ish) callbacks #670
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
Conversation
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.
Who are the intended users of this callback API? If it's meant to help port Pajarito and Pavito to MOI, that makes sense. Let's call it legacy_callbacks.jl
and put a big disclaimer on it. If it's meant for new users, I'm not a fan. It's a half-baked API (not to discredit the author, there just isn't a good way to do this). Do we really want to be responsible for supporting this API and answering questions about how to query X or Y solver-specific property from the callbacks?
If this is meant to be the easy API then shouldn't it also work with the caching optimizer and the bridges? Who volunteers to do that? Why is this a good use of developer time when we could make nice solver-specific callback APIs instead?
### The heuristic callback | ||
|
||
The `heuristic` callback can be used to provide the solver with heuristically | ||
obtained integer-feasible solutions at fractional nodes in the branch and bound |
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.
What attributes can be queried in the heuristic callback?
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.
Only VariablePrimal
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.
This is a regression from MPB and isn't sufficient to cover Pajarito: https://github.com/JuliaOpt/Pajarito.jl/blob/027cfbf882b4a1ae78f45fb11a6e51c58468a20f/src/conic_algorithm.jl#L1446 (this is from the lazy callback)
- The heuristic callback *may* be called when the solver has a fractional (i.e., | ||
non-integer) solution | ||
- The solver may silently reject the provided solution | ||
- Some solvers require a complete solution, others only partial solutions. It's |
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.
Does the solution have to satisfy all constraints in the model?
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.
No, see above
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.
If a solution does not satisfy constraints, is it guaranteed to be rejected?
primal solution. | ||
- You can only access the primal solution through `VariablePrimal`. For example, | ||
`ConstraintDual` etc will not work. | ||
- The optimal solution will satisfy all lazy constraints that could *possibly* |
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.
What does this mean? What are the constraints that could possibly have been added and how do they differ from the constraints actually returned?
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.
Poor wording on my part. It should just say all constraints that have been added. I'll hold off changing until we have a consensus on the larger issue of whether we want these in the first place.
Codecov Report
@@ Coverage Diff @@
## master #670 +/- ##
==========================================
- Coverage 94.96% 94.91% -0.06%
==========================================
Files 50 51 +1
Lines 5347 5350 +3
==========================================
Hits 5078 5078
- Misses 269 272 +3
Continue to review full report at Codecov.
|
1 similar comment
Codecov Report
@@ Coverage Diff @@
## master #670 +/- ##
==========================================
- Coverage 94.96% 94.91% -0.06%
==========================================
Files 50 51 +1
Lines 5347 5350 +3
==========================================
Hits 5078 5078
- Misses 269 272 +3
Continue to review full report at Codecov.
|
Yes, mainly the MINLP solvers.
Another option is to put it in a
@juan-pablo-vielma and I discussed this a little. There isn't a good way to do generic callbacks. We agree. But the concept of a lazy constraint is pretty solver-independent, and is useful for new users. To make it practical though, it has to be restrictive.
Answer: the functionality of JuMP-provided callbacks is limited by design. For solver-specific functions, use the solver-specific callbacks.
Nope. See limited functionality. Direct mode only.
Pajarito and Pavito. |
If you're happy calling them legacy callbacks and explaining why they exist, I don't see an issue documenting them and having JuMP bindings.
I wouldn't call these the JuMP-provided callbacks. JuMP can also provide solver-specific callbacks that aren't limited by design.
I question how useful the solver-independent aspect is for new users. I think users would be just fine given an example of how to do lazy callbacks in the solver of their choice. Especially when it would lead them to the right API instead of being a dead end. |
In think the advantage of solver independent lazy cut callbacks can be seen when thinking about modeling with a large number of constraints that can be efficiently separated. This is a concept that beginners often are not aware of (I have had referees in OR tell me that they don't believe I solved a MIP with an exponential number of constraints) and it is an important part of the power of MIP (and JuMP). If we consider lazy cuts as a modeling tool it makes sense that they are not dependent on the solver: you do not need to know anything about the solver to model a MIP without a large number of constraints. This modeling tool goal also keeps the interface simple and bounded: questions like how do I access this solver specific feature become irrelevant as you should only be using this feature it you don't want to think of the solver. So I would say this version would not actually have MINLP solvers as the intended users (they should use the solver specific callbacks). The issue with the caching optimizer and the bridges is worth considering though. Maybe the way to think about it is considering how SOS2 constraints relate to these two features (maybe also PolyJuMP relates and/or JuMPer would relate). |
Is a lazy constraint just a constraint that is adding during |
If we're considering these lazy constraints as a modeling tool and not as a direct way for users to interact with solvers, then I like the suggestion of coming up with an MOI function object to represent them. The MOI function would store the callback that generates the constraints given a point to separate. However, this doesn't address the Pajarito/Pavito use case. It also doesn't cover the heuristic callback that's proposed in this PR. |
Hi, I like to bring some arguments in favour of developing optimiser independent callbacks in general instead of solver-specific ones (or on top of solver-specific ones), and in particular lazy_cut_callbacks that are special useful to implement a generic Benders decomposition framework as we plan to do in Coluna. Beyond the fact that lazy_cut_callbacks can be seen as being part of the modelling JuMP language, we should see the great feature that offers MOI by being solver independent. In our development of the Coluna Platform, as in many other and futur project that are building over MOI, we wish to hide the MIP optimiser complexity to the user by providing a generic cut-callback. If it is not done in MOI, all the platforms above MOI will have to develop their own implementation with a SWITCH system to act differently depending on the solver being used. Wasn't it the goal of MOI to provide such service to all, rather than each platform having this maintenance duty. With Issam, we though of what could be a good compromise between the need for generic callbacks and the reality of the different functionalities that are offered by each solver. The JuMP team could select the "best" (most useful) callback options that are offered by the different solvers (not being restricted to those that are offered by all), and to implement a generic callback that supports those best options/parametrisations. At run time, MOI can check whether the demanded generic feature is offered by the selected solver; it returns an explicit error message if the feature is not supported. This is just a suggestion to contribute to the debate. |
following up on the brief gitter discussion yesterday, and the discussion at JuMP-dev 3 in mid-march. upgrading Pajarito and Pavito for MOI is going to be a big job and I'm not prepared to do it until callbacks are resolved. |
@@ -1,6 +1,8 @@ | |||
VERSION < v"0.7.0-beta2.199" && __precompile__() | |||
module MathOptInterface | |||
|
|||
using Compat |
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.
Not needed anymore
tl;dr: One change we should consider is replacing the There was a concern with the current approach which is: SandboxWe then discussed if it was possible to create a kind of sandbox for calls inside a callbacks so that getting attributes would either throw an helpful error if it cannot be called inside the callback or give the corresponding solution. This sandbox could be achieved by wrapping the Gurobi MOI model inside an MOI layer tailored for the callback. If the user tries to call the original model, it would get error since this model still has status New attributesThe advantage of the sandbox is that the user can use the classical attributes At the end of the discussion at JuMP-dev, we prefered the "New attributes" approach for its simplicity and genericity. It is not specific to MIP callbacks used today and would work for any kind of callbacks (maybe even NLP callbacks asking for gradients, hessian, ... ?) that would be defined as "just a function being called during where the user can access custom attributes". model = Model(...)
@variable(model, x)
MOI.set(model, MyCallback(), (cb_data) -> begin
v = MOI.get(model, MySolutionValue(cb_data), x)
...
end) Then a solver independent callback would simply be : |
Another thing to consider is that if users want the user-friendliness of the Sandbox approach, the sandbox approach can be implemented on top of the "new attributes" approach and this can even be done outside of JuMP/MOI, e.g. like a Training Wheels Protocol |
|
PR in JuMP exposing this:
jump-dev/JuMP.jl#1849
Solver-specific implementations:
GLPK: jump-dev/GLPK.jl#91
Gurobi: jump-dev/Gurobi.jl#194