# ParametricOptInterface + Clarabel

**URL:** <https://discourse.julialang.org/t/parametricoptinterface-clarabel/129745>\
**Category:** Optimization (Mathematical)\
**Tags:** jump\
**Created:** [June 8, 2025, 8:35pm UTC](https://discourse.julialang.org/t/parametricoptinterface-clarabel/129745 "2025-06-08T20:35:25Z")\
**Posts on this page:** 6\
**Page:** 1

<div class="post-metadata">

**Author:** ![klamike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/klamike/32/214776_2.png) [@klamike](https://discourse.julialang.org/u/klamike)\
**Post date:** [June 8, 2025, 8:35pm UTC](https://discourse.julialang.org/t/parametricoptinterface-clarabel/129745/1 "2025-06-08T20:35:25Z")

</div>

I am trying to use POI with Clarabel. Based on the ParametricOptInterface.jl readme, the recommended usage is with `direct_model`. Here, I’ve just swapped HiGHS.jl for Clarabel.jl:

```julia
using JuMP, Clarabel
import ParametricOptInterface as POI

model = direct_model(POI.Optimizer(Clarabel.Optimizer()))
#Error showing value of type Model:
#
#SYSTEM (REPL): showing an error caused an error
#ERROR: MathOptInterface.GetAttributeNotAllowed{MathOptInterface.ObjectiveSense}:
#...

@variable(model, x)
# ERROR: MathOptInterface.AddVariableNotAllowed:

```

and with the other (old?) syntax, we get

```julia
using JuMP, Clarabel
import ParametricOptInterface as POI

model = Model(() -> POI.Optimizer(Clarabel.Optimizer()))
@variable(model, x)
@variable(model, p in Parameter(1.0))
@constraint(model, cons, x + p >= 3)
@objective(model, Min, 2x)
optimize!(model)
# ERROR: `copy_to` is not supported by the solver `ParametricOptInterface.Optimizer{Float64, Clarabel.MOIwrapper.Optimizer{Float64}}`. Did you mean to call `optimize!(dest::AbstractOptimizer, src::ModelLike)` instead?

```

So, (why) is Clarabel.jl not compatible with POI? Note both of those syntax work well for HiGHS.jl and Ipopt.jl.

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [June 8, 2025, 9:45pm UTC](https://discourse.julialang.org/t/parametricoptinterface-clarabel/129745/2 "2025-06-08T21:45:45Z")

</div>

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
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
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.

---

<div class="post-metadata">

**Author:** ![klamike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/klamike/32/214776_2.png) [@klamike](https://discourse.julialang.org/u/klamike)\
**Post date:** [June 8, 2025, 10:22pm UTC](https://discourse.julialang.org/t/parametricoptinterface-clarabel/129745/3 "2025-06-08T22:22:32Z")

</div>

Thanks for the quick response @odow!

I came across this since I’m [starting to implement](https://github.com/LearningToOptimize/L2ODLL.jl) the [DLL](https://arxiv.org/pdf/2402.03086) method using JuMP. In the most general case, the “Dual Completion” step is a (conic) solve, which has to be done many times during training. I implemented it for LP first where HiGHS and POI worked well. Then trying a conic problem I swapped Clarabel in and saw the error.

Actually, it turns out for the problem I’m looking at, those conic constraints are not even needed, so I am now deleting them from the model and using POI+HiGHS like before. But I appreciate the explanation 😃

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [June 8, 2025, 10:28pm UTC](https://discourse.julialang.org/t/parametricoptinterface-clarabel/129745/4 "2025-06-08T22:28:31Z")

</div>

I assume @mtanneau used JuMP for this. He can probably share more details.

It looks like he used Mosek (MosekTools.jl) which _does_ implement the incremental interface.

---

<div class="post-metadata">

**Author:** ![klamike](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/klamike/32/214776_2.png) [@klamike](https://discourse.julialang.org/u/klamike)\
**Post date:** [June 8, 2025, 10:55pm UTC](https://discourse.julialang.org/t/parametricoptinterface-clarabel/129745/5 "2025-06-08T22:55:20Z")

</div>

Yes, I am generalizing/automating [his implementation](https://github.com/AI4OPT/DualLagrangianLearning). The main idea is to automate the training of DLL models (for arbitrary primals) as much as possible, so (eventually) L2ODLL will detect the decomposition, generate the completion/projection functions, etc.

---

<div class="post-metadata">

**Author:** ![mtanneau](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/mtanneau/32/17787_2.png) [@mtanneau](https://discourse.julialang.org/u/mtanneau)\
**Post date:** [June 11, 2025, 2:25pm UTC](https://discourse.julialang.org/t/parametricoptinterface-clarabel/129745/6 "2025-06-11T14:25:46Z")

</div>

Hi `odow` and `klamike`!

My implementation was limited to a handful of problem types, and I did re-write several things manually mainly because I didn’t know how to use other tools.

I know that `klamike` is very familiar with the underlying work and code, so I’ll dive right in, hopefully making some sense and being somewhat useful 🤞

At the core of DLL is the ability to 1) project onto a cone and 2) differentiate through that projection. In theory, this can be done via JuMP + DiffOpt, as long as there’s a solver that supports the underlying cone. I hard-coded it because it was simple enough to do so and more efficient. Streamlining this particular step would go a long way in making the method more accessible.

The second step of DLL (called “dual completion” in the [paper](https://proceedings.neurips.cc/paper_files/paper/2024/file/646d2edf873df99d36aaeeaf058acdb8-Paper-Conference.pdf)) consists in taking a partial (i.e. only a subset of variables are known) dual solution and recovering the remaining variables. In my implementation, this step was also hard-coded, again because the problems were “easy” enough. I expect that streamlining this would be more complicated, as it requires detecting some structure. I’m also not sure whether this was the scope of `klamike`’s original question 🤔
