-
Notifications
You must be signed in to change notification settings - Fork 94
Add MOI.submit to add attribute #775
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.
I like this idea. A few comments:
I think overloading the existing Abstract*Attribute
for this is confusing. For example, LazyConstraint
could be both a constraint flag (http://www.gurobi.com/documentation/8.1/refman/lazy.html) describing how a regular constraint should be handled by a solver or a constraint added in a callback. I'd rather have an entirely separate abstraction for submitting "things" to the solver.
Lazy constraints never receive indices that you can later refer to them with (AFAIK), so we could drop AttributeIndex
.
The assumption that you only need to set variable-wise values is a bit off. What if you want to add a second-order cone constraint as a lazy constraint?
I'd propose something like:
submit(model::ModelLike, ::AbstractSubmittableThing, ::Any)
Each AbstractSubmittableThing
decides what the SubmittablePayload
should be. For LazyConstraint
it could be function-set tuple. For heuristic solutions it could be a dictionary from VariableIndex to value. The naming needs some work.
I can only imagine this working in direct mode, because it would be hard to translate indices between the cache and the underlying solver given arbitrary payloads.
Also it wasn't clear to me if LazyConstraint
in the proposal means a callback object or the actual constraint that would be added inside a callback.
Codecov Report
@@ Coverage Diff @@
## master #775 +/- ##
==========================================
- Coverage 94.26% 94.14% -0.12%
==========================================
Files 59 59
Lines 6481 6489 +8
==========================================
Hits 6109 6109
- Misses 372 380 +8
Continue to review full report at Codecov.
|
You pass it as a tuple
We could have a
The constraint object as
IMO |
They don't usually return indices.
Then what is the use case for the variable-wise and constraint-wise versions? Submitting a feasible solution? add(model::ModelLike, attr::AbstractVariableAttribute, v::VariableIndex,
value)::AttributeIndex{typeof(attr)}
add(model::ModelLike, attr::AbstractConstraintAttribute, c::ConstraintIndex,
value) |
Yes, submitting a heuristic solution. |
I'm only familiar with MILP callbacks so what follows is biased towards this use case; I have no experience (nor knowledge thereof) of callbacks for non-linear optimization solvers. Some general background. MILP solvers allow you to do two types of things during with a callback:
|
Now the specifics: Heuristic solutionThis allows you to suggest a heuristic solution to the solver. This does not modify the model. It does not create a new variable. Your heuristic solution may not even be considered by the solver. Therefore, the IMO, an intuitive syntax could look like MOI.submit(model, ::MOI.CbHeuristicSolution, ::Any) Lazy constraint and user cutProvide an additional constraint to the solver, which were not part of the original formulation.
Similar to heuristic solution, user cuts and lazy constraints may never enter the formulation (if the solver decides so).
The syntax proposed by Miles also fits this nicely, e.g.: MOI.submit(model, ::MOI.CbUserCut, ::Any)
MOI.submit(model, ::MOI.CbLazyConstraint, ::Any) the third argument will most likely encapsulate the constraint itself ( Note: as mentioned by Miles, some of the initial constraints may be marked as lazy: http://www.gurobi.com/documentation/8.1/refman/lazy.html. This has nothing to do with callbacks. Adding a new variable (from a callback)AFAIK, no MILP solver supports it. The only exception may be SCIP, but I'm not familiar enough. |
Now that I think about it, maybe we should be returning an index when adding constraints (and possibly variables) through a callback. Just because existing MILP solvers don't do it doesn't mean MOI should not, and it would restrict the future capabilities, if someone wants to implement lazy constraints a bit differently. My idea would look like the following. The same thing could be done with variables. It would actually be quite useful in a branch-and-price context: new variables are added at each node, but one may want to remove some when taking certain branching decisions. |
@odow thoughts? |
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.
I like submit
. I also support returning indices from MOI.submit
. It demonstrates that things are different from the standard get
/set
, and as @mtanneau says, just because current MILP solvers don't support callbacks, doesn't mean that new solvers won't (e.g., Tulip.jl?).
One question is whether to return an InvalidConstraintIndex
, or to return nothing
if the solver doesn't support indexing submit
ted attributes.
src/attributes.jl
Outdated
attr::AttrType | ||
message::String # Human-friendly explanation why the attribute cannot be set | ||
end | ||
AddAttributeNotAllowed(attr::AnyAttribute) = AddAttributeNotAllowed(attr, "") |
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.
Maybe a more descriptive default message?
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.
There is already a more descriptive one printed by NotAllowedError
, the message field is for custom messages that is added at the end of it.
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.
I like submit. It would be nice to see some examples.
I'm skeptical of the need for It would be a different story if we had a solver developer who said, "I want to use this for X." |
It might also not be too hard to add later when it's needed so I am ok to not have it for now. |
Miles has a strong point. In the mean time, it indeed seems preferable not to carry |
Looks good. I think what we need now are a couple of examples to check whether something's missing/odd. I'd be happy to port the examples that were in JuMP v18 (heuristic, lazy & user) to MOI, but I don't have time to commit to this until the end of the month. |
This PR doesn't have anything specific for callbacks, so I don't think it needs to be blocked until we have a full callback example. |
Some operations do not fall into the category of
set
such as:So when a solver want to expose a solver specific feature like this, we need to create a new
add_...
function in MOI and implement it in every layer + adding a function to JuMP, ...This PR adds a
MOI.add
function which is similar toMOI.set
except it returns anAttributeIndex
which is consistent withadd_variable(s)
andadd_constraint(s)
.With this PR, if a solver want to expose, say lazy constraints, it just have to define
LazyConstraint <: MOI.AbstractOptimizerAttribute
, implementMOI.add(::Optimizer, ::LazyConstraint, ...)
and no change is needed either from JuMP or MOI.This might be the only breaking change needed to add support for callbacks so it might be worth it to include it into v0.9