I agree that the current strategy of JuMP is useful but I don't like the idea of cache being only in JuMP... I understand and accept the argument of having MOI a pure API but It would be nice to have the MOIU instance shared by JuMP and the solver wrapper. Example: if a solver wrapper wants to answer the query about MOI.ConstraintSet it will need to store it's own copy or build the set on the fly which would be a waste since the set exists and is stored in JuMP.
The problem with sharing is that I don't know if it is possible with julia to deny the solvers from modifying the instance once it is shared.
One possible solution: given a valid MOIU instance there are queries that do not need a solver to be answered. so wrappers do not implement those methods and we move them to MOIU. Everything using MOIU instance goes to MOIU to keep it separated from what solvers should implement. Then I see methods in MOIU like
MOIU.addconstraint!(m::MOI.AbstractSolverInstance, func::F, set::S, <:MOIU.AbstractInstance{T}, updateform::Bool)
#MOIU update its instance and if updateform == false
#MOIU keeps track of what is not yet passed through vectors for each combination F,S as discussed
#here https://github.com/JuliaOpt/MathOptInterface.jl/issues/63#issuecomment-317526848
MOIU.update_solver_instance()
#calls MOI.addconstraints! and deletes the vectors but keeps the instance.
MOIU.getattribute(m::MOIU.AbstractInstance{T}, ::MOI.ConstraintSet}, ref)
#replacing what was before in MOI and implemented by the solvers
#MOI.getattribute(m::MOI.SolverInstance{T}, ::MOI.ConstraintSet}, ref)
As @blegat mentioned, this will not work in the following case:
if the solver wrapper wants to have a mode on the fly where every info required for a single constraint is added and deleted right afterwards (being only kept inside the solver).
Such solver wrappers will be denied to have an answer to the following query since now it s the responsability of MOIU instance.
MOI.getattribute(m::MOI.SolverInstance{T}, ::MOI.ConstraintSet}, ref)
To solve this issue, solvers need to have access to the instance (bad thing). and all those methods will not be replaced but only get a default implementation that uses MOIU instance (when available):
So Instead of this
MOIU.getattribute(m::MOIU.AbstractInstance{T}, ::MOI.ConstraintSet, ref)
# replacing what was before in MOI and implemented by the solvers
# MOI.getattribute(m::MOI.SolverInstance{T}, ::MOI.ConstraintSet, ref)
We can have this
MOIU.getattribute(m::MOIU.AbstractInstance{T}, ::MOI.ConstraintSet, ref)
# helps providing default implementation to
# MOI.getattribute(m::MOI.SolverInstance{T}, ::MOI.ConstraintSet}, ref)
function MOI.getattribute(m::MOI.AbstractSolverInstance{T}, s::MOI.ConstraintSet, ref)
if has_instance(m)
MOIU.getattribute(instance(m), s, ref)
else
throw MethodError()
end
end
With JuMP being now only one among other possible users of MOI / MOIU doing instead of this :
functions1 = Vector{FunctionSetA}()
sets1 = Vector{ConstraintSetA}()
# fill the vectors
functions2 = Vector{FunctionSetA}()
sets2 = Vector{ConstraintSetB}()
# fill the vectors
# ...continue for each F,S type combination
MOI.addconstraints!(m,functions1, sets1)
MOI.addconstraints!(m,functions2, sets2)
#...continue for each F,S type combination
the follwing:
MOIU.addconstraint!(m, f, s, false) # assuming instance(m) returns an MOIU instance
#...continue adding the other constraints
MOIU.update_solver_instance(m)