# "Switching" out the attached \`Optimizer\` of an (empty) model

**URL:** <https://discourse.julialang.org/t/switching-out-the-attached-optimizer-of-an-empty-model/104633>\
**Category:** Optimization (Mathematical)\
**Created:** [October 5, 2023, 9:29pm UTC](https://discourse.julialang.org/t/switching-out-the-attached-optimizer-of-an-empty-model/104633 "2023-10-05T21:29:59Z")\
**Posts on this page:** 10\
**Page:** 1

<div class="post-metadata">

**Author:** ![sstroemer](https://avatars.discourse-cdn.com/v4/letter/s/a88e4f/32.png) [@sstroemer](https://discourse.julialang.org/u/sstroemer)\
**Post date:** [October 5, 2023, 9:29pm UTC](https://discourse.julialang.org/t/switching-out-the-attached-optimizer-of-an-empty-model/104633/1 "2023-10-05T21:29:59Z")

</div>

I’m trying to do the following, in order to “parameterize” a model

```julia
function make_parametric!(model::JuMP.Model)
    if !isempty(model)
        @error "[core] Model must be empty when trying to switch to parametric mode"
    end

    # TODO: Remember/copy all optimizer attributes.
    
    if JuMP.mode(model) == JuMP.DIRECT
        # this does not work
        JuMP.set_optimizer(model, () -> POI.Optimizer(eval(Symbol(JuMP.solver_name(model))).Optimizer()))
    else
        JuMP.set_optimizer(model, () -> POI.Optimizer(eval(Symbol(JuMP.solver_name(model))).Optimizer()))
    end
end

m = Model(() -> HiGHS.Optimizer())
dm = direct_model(HiGHS.Optimizer())

make_parametric!(m) # works
make_parametric!(dm) # does not work

```

which fails, since we can’t switch Optimizers on models in direct mode. Is there a work around? Or is there a completely different way to do this?

---

<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:** [October 6, 2023, 5:49am UTC](https://discourse.julialang.org/t/switching-out-the-attached-optimizer-of-an-empty-model/104633/2 "2023-10-06T05:49:14Z")

</div>

You cannot switch the solver in direct-mode, and there is no work-around.

Instead of trying to parameterize an existing model, you should ask the user for an optimizer contractor, and wrap that.

Something like:

```julia
function make_parametric(optimizer)
    Model(() -> MOI.Optimizer(MOI.instantiate(optimizer; with_bridge_type = Float64)))
end

```

---

<div class="post-metadata">

**Author:** ![jd-foster](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/jd-foster/32/35824_2.png) [@jd-foster](https://discourse.julialang.org/u/jd-foster)\
**Post date:** [October 6, 2023, 5:55am UTC](https://discourse.julialang.org/t/switching-out-the-attached-optimizer-of-an-empty-model/104633/3 "2023-10-06T05:55:14Z")

</div>

This might save you from doing an `eval(...)`, but I’m not sure what you’re aiming for overall.

```julia
using JuMP
import ParametricOptInterface
const POI = ParametricOptInterface
import HiGHS

function make_parametric_copy(model::JuMP.Model)
    m = Model(() -> backend(model))
    set_optimizer(m, () -> POI.Optimizer(backend(model)))
    return m
end

dm = direct_model(HiGHS.Optimizer())
pm = make_parametric_copy(dm)

```

---

<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:** [October 6, 2023, 6:28am UTC](https://discourse.julialang.org/t/switching-out-the-attached-optimizer-of-an-empty-model/104633/4 "2023-10-06T06:28:46Z")

</div>

Hmm. I guess this works, if the model is empty. @blegat might want to comment. It looks fine, but the only issue is that your `model` and `m` now share the same memory, so you might have to be a bit careful.

---

<div class="post-metadata">

**Author:** ![sstroemer](https://avatars.discourse-cdn.com/v4/letter/s/a88e4f/32.png) [@sstroemer](https://discourse.julialang.org/u/sstroemer)\
**Post date:** [October 6, 2023, 8:02am UTC](https://discourse.julialang.org/t/switching-out-the-attached-optimizer-of-an-empty-model/104633/5 "2023-10-06T08:02:35Z")

</div>

> [@jd-foster](#):
>
> but I’m not sure what you’re aiming for overall.

Currently our workflow looks like this:

1. User creates a JuMP model, using a model mode, as well as optimizer of their choice
2. User sets some optimizer attributes, or overall flags (like `silent`, `string_names`, …)
3. Our tool builds “into” that model, solves it, and properly extracts results
4. Potential re-solves, or other shenanigans

I assume this ((1.) and (2.)) does not work with

> [@odow](#):
>
> Instead of trying to parameterize an existing model, you should ask the user for an optimizer contractor, and wrap that.

which is why I was trying to parameterize it “behind the scenes” (therefore also my open _copy optimizer attributes_ TODO).

Furthermore, the main problem is that our exposed functionality looks something like `build_and_run!(model, ...)`, where the proposed `make_parametric_copy(...)` needs to return the “new” `m` so one would have to change every code using it to `model = build_and_run(model)` (which in theory is fine, we return the model anyways for convenience, but most people using the tool actually only use the in-place formulation, so for them it would be a breaking change).

* * *

**Essentially I’m aiming for:**

1. take a `model` as function argument
2. depending on some setting either  
a. run with it  
b. switch to a `POI` wrapped optimizer, and then run with it
3. make sure that whatever happens in that function actually modifies the passed `model` since that is what the “outside” is using

* * *

> [@odow](#):
>
> I guess this works, if the model is empty.

That is fine (I think). We didn’t previously expect a completely empty model, just something that does not clash with our internals, but changing that and checking `isempty(model)` is a reasonable “loss”.

> [@odow](#):
>
> he only issue is that your `model` and `m` now share the same memory

Is there anything specific that comes to your mind where this could be dangerous?

---

<div class="post-metadata">

**Author:** ![sstroemer](https://avatars.discourse-cdn.com/v4/letter/s/a88e4f/32.png) [@sstroemer](https://discourse.julialang.org/u/sstroemer)\
**Post date:** [October 6, 2023, 8:27am UTC](https://discourse.julialang.org/t/switching-out-the-attached-optimizer-of-an-empty-model/104633/6 "2023-10-06T08:27:27Z")

</div>

Stupid question…

Assuming a completely empty model (except “settings”), can I do

```julia
_dm = direct_model(POI.Optimizer(backend(dm)))
dm.moi_backend = _dm.moi_backend

```

for a given direct model `dm`, or those this furiously break something in the background?

---

<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:** [October 6, 2023, 9:25am UTC](https://discourse.julialang.org/t/switching-out-the-attached-optimizer-of-an-empty-model/104633/7 "2023-10-06T09:25:03Z")

</div>

I think the answer to all of this is yes, it probably works and you can do it. But we’ve never tested or documented this at the JuMP level so it might break in future.

---

<div class="post-metadata">

**Author:** ![sstroemer](https://avatars.discourse-cdn.com/v4/letter/s/a88e4f/32.png) [@sstroemer](https://discourse.julialang.org/u/sstroemer)\
**Post date:** [October 6, 2023, 9:34am UTC](https://discourse.julialang.org/t/switching-out-the-attached-optimizer-of-an-empty-model/104633/8 "2023-10-06T09:34:25Z")

</div>

Then I’ll try that and see if something somewhere clashes with that. And if it’s changed in some future version, it’s a single point of failure that our tests can easily catch on a new JuMP version; at which point I can still come back and see if there is another work around before upping our dependency.

Thanks @all (as always)!

---

<div class="post-metadata">

**Author:** ![blegat](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/blegat/32/217090_2.png) [@blegat](https://discourse.julialang.org/u/blegat)\
**Post date:** [October 6, 2023, 1:39pm UTC](https://discourse.julialang.org/t/switching-out-the-attached-optimizer-of-an-empty-model/104633/9 "2023-10-06T13:39:18Z")

</div>

Can’t you even directly do `dm.moi_backend = POI.Optimizer(backend(dm))` ?  
Note `dm.shapes` is based on constraint indices of `dm.moi_backend`. If you add a layer, the indices might change in general so it might cause issues. But `shapes` are mostly used for PSD constraints and POI maybe does not change indices so it might not be an issue. You could add some asserts for that to be on the safe side.

---

<div class="post-metadata">

**Author:** ![sstroemer](https://avatars.discourse-cdn.com/v4/letter/s/a88e4f/32.png) [@sstroemer](https://discourse.julialang.org/u/sstroemer)\
**Post date:** [October 6, 2023, 5:28pm UTC](https://discourse.julialang.org/t/switching-out-the-attached-optimizer-of-an-empty-model/104633/10 "2023-10-06T17:28:04Z")

</div>

Tried it, and yes that works perfectly!
