Hi @klamike,
Another good question that gets at something much deeper.
The answer is that there are two ways to write an MOI interface:
- incremental mode: where the solver supports building the model one variable and constraint at a time, supports deletion, and supports querying the various attributes of the model
- copy-to mode: where the solver supports building (and perhaps solving) the model in a single function call, where it gets given the entire model as input and it returns a solution.
POI assumes that the solver supports the incremental interface. Clarabel implements the copy-to interface, which you can see with:
julia> using JuMP, Clarabel
julia> import ParametricOptInterface as POI
julia> MOI.supports_incremental_interface(Clarabel.Optimizer())
false
The solution is to wrap Clarabel in a caching optimizer, which implements the incremental interface:
julia> inner = MOI.instantiate(Clarabel.Optimizer; with_cache_type = Float64)
MOIU.CachingOptimizer
├ state: EMPTY_OPTIMIZER
├ mode: AUTOMATIC
├ model_cache: MOIU.UniversalFallback{MOIU.Model{Float64}}
│ ├ ObjectiveSense: FEASIBILITY_SENSE
│ ├ ObjectiveFunctionType: MOI.ScalarAffineFunction{Float64}
│ ├ NumberOfVariables: 0
│ └ NumberOfConstraints: 0
└ optimizer: Empty Clarabel - Optimizer
julia> model = direct_model(POI.Optimizer(inner))
A JuMP Model
├ mode: DIRECT
├ solver: Parametric Optimizer with Clarabel attached
├ objective_sense: FEASIBILITY_SENSE
├ num_variables: 0
├ num_constraints: 0
└ Names registered in the model: none
julia> @variable(model, x)
x
But then you have a cache, two copies of the problem, etc.
The real answer is that you probably shouldn’t use POI with Clarabel because Clarabel won’t benefit from efficiently modifying the problem inn-place. POI is really intended to be used with MIP solvers that can efficiently warm start after small parts of the problem have changed.
What problem are you trying to solve? Why Clarabel and why POI?
In the mean time: we should improve the documentation of POI to make it clear when it should be used.