# MOI: Re-evaluate custom bridge & \`direct\_model(...)\` bridging

**URL:** <https://discourse.julialang.org/t/moi-re-evaluate-custom-bridge-direct-model-bridging/94219>\
**Category:** Optimization (Mathematical)\
**Created:** [February 7, 2023, 3:38pm UTC](https://discourse.julialang.org/t/moi-re-evaluate-custom-bridge-direct-model-bridging/94219 "2023-02-07T15:38:20Z")\
**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:** [February 7, 2023, 3:38pm UTC](https://discourse.julialang.org/t/moi-re-evaluate-custom-bridge-direct-model-bridging/94219/1 "2023-02-07T15:38:20Z")

</div>

As far as I understand, when I add a custom bridge (`struct MyCustomBridge{T} <: MOI.Bridges.Constraint.AbstractBridge end`) to my model using

```julia
model = JuMP.Model(HiGHS.Optimizer)

MOI.Bridges.add_bridge(JuMP.backend(model).optimizer, MyCustomBridge)

```

the respective `MOI.Bridges.Constraint.bridge_constraint(...)` function is called as soon as I call `optimize!(model)` for the first time. I noticed that it does not automatically re-evaluate the bridging if the overall model is flagged as `dirty` and then re-optimized. However, executing

```julia
JuMP.set_normalized_rhs(constr, JuMP.normalized_rhs(constr))

```

properly triggers a “re-bridging” of `constr`.

Is there a more “clean” (as in “intended”) way to trigger this, for a given constraint (but not for every constraint)?

* * *

Additionally, I tried applying that to a model created with `direct_model(...)` by utilizing the `LazyBridgeOptimizer`:

```julia
model = direct_model(HiGHS.Optimizer())
optimizer = MOI.Bridges.LazyBridgeOptimizer(backend(model))

MOI.Bridges.add_bridge(optimizer, MyCustomBridge)

```

However, on creating a constraint that is not directly supported by the solver, it triggers the `Constraints of type ... are not supported by the solver.` error (the bridge is the same as in the non-direct model, where it works).

---

<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:** [February 7, 2023, 7:56pm UTC](https://discourse.julialang.org/t/moi-re-evaluate-custom-bridge-direct-model-bridging/94219/2 "2023-02-07T19:56:07Z")

</div>

Can you provide a reproducible example? I don’t understand what is going on.

Your code is also a little off. It should probably be:

```julia
model = JuMP.Model(HiGHS.Optimizer)
JuMP.add_bridge(model, MyCustomBridge)

# or

model = direct_model(
    MOI.Bridges.full_bridge_optimizer(HiGHS.Optimizer(), Float64),
)
JuMP.add_bridge(model, MyCustomBridge)

```

---

<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:** [February 7, 2023, 9:46pm UTC](https://discourse.julialang.org/t/moi-re-evaluate-custom-bridge-direct-model-bridging/94219/3 "2023-02-07T21:46:13Z")

</div>

Thanks for the hint, besides all the digging into MOI, I somehow missed that JuMP has the `add_bridge` too… 🙂

MWE below, short preface: I’ve got a quadratic constraint, that I want to reformulate in a way that HiGHS can handle it; the MWE just deletes the constraint.

The bridge looks like ([based on your answer here](https://discourse.julialang.org/t/help-understanding-moi-bridges-even-proof-of-concept-doesnt-work/43152/2)):

```julia
struct MyCustomBridge{T} <: MOI.Bridges.Constraint.AbstractBridge end

function MOI.Bridges.Constraint.concrete_bridge_type(
    ::Type{<:MyCustomBridge}, 
    F::Type{<:MOI.ScalarQuadraticFunction},
    S::Type{<:MOI.AbstractScalarSet},
)
    return MyCustomBridge{Float64}
end

function MOI.supports_constraint(
    ::Type{<:MyCustomBridge},
	::Type{<:MOI.ScalarQuadraticFunction},
	::Type{<:MOI.AbstractScalarSet},
)
	return true
end

function MOI.Bridges.added_constrained_variable_types(::Type{<:MyCustomBridge}) 
	return Tuple{DataType}[]
end

function MOI.Bridges.added_constraint_types(::Type{<:MyCustomBridge})
	return []
end

function MOI.Bridges.Constraint.bridge_constraint(
    ::Type{<:MyCustomBridge}, 
    m::MOI.ModelLike, 
    f::MOI.ScalarQuadraticFunction, 
    s::MOI.AbstractScalarSet,
)
    @info "Bridging now"
    return MyCustomBridge{Float64}()
end

```

The model building looks like:

```julia
model = JuMP.Model(HiGHS.Optimizer)
JuMP.add_bridge(model, MyCustomBridge)

x = JuMP.@variable(model, [1:3], lower_bound=0)
c = JuMP.@constraint(model, x[1] + x[2]^2 - 3*x[3] >= 0)

JuMP.@objective(model, Min, x[1] + 0)

JuMP.optimize!(model) # calling the bridge here

JuMP.fix(x[2], 5; force=true) # fixing this variable would make the constraint viable for HiGHS

JuMP.optimize!(model) # not calling the bride here - constraint is still deleted

JuMP.set_normalized_rhs(c, JuMP.normalized_rhs(c))

JuMP.optimize!(model) # calling the bridge here

```

The important part is `JuMP.fix(x[2], 5; force=true)`, where the user may change the constraint (by fixing the variable that complicates the constraint into a quadratic one) - essentially reducing the constraint to an affine one. Now the next `optimize!(...)` does not call the bridge - as far as I understand because it actually does not “know of the change” (since `fix()` just creates an additional MOI Equal).

If however I force an “update” of the respective constraint, e.g. using `JuMP.set_normalized_rhs(c, JuMP.normalized_rhs(c))`, then the following `optimize!(...)` will invoke the bridge - allowing me to react to the change that the user made.

While it makes sense (at least to me 😅), that this happens, I’m looking for a “less hacky” way to trigger that update.

---

<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:** [February 7, 2023, 9:49pm UTC](https://discourse.julialang.org/t/moi-re-evaluate-custom-bridge-direct-model-bridging/94219/4 "2023-02-07T21:49:02Z")

</div>

> [@odow](#):
>
> ```julia
> model = direct_model(
> MOI.Bridges.full_bridge_optimizer(HiGHS.Optimizer(), Float64),
> )
> JuMP.add_bridge(model, MyCustomBridge)
> 
> ```

I tried that (using the same MWE as above), which results - upon calling the `@constraint(...)` in:

`Bridge of type `MyCustomBridge{Float64}` does not support accessing the attribute `MathOptInterface.ConstraintFunction()`. [...]`

---

<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:** [February 7, 2023, 10:05pm UTC](https://discourse.julialang.org/t/moi-re-evaluate-custom-bridge-direct-model-bridging/94219/5 "2023-02-07T22:05:16Z")

</div>

The bridges are assumed to be independent. So calling `fix` cannot modify another constraint.

The bridge updates when you call `set_normalized_rhs` because your bridge does not support `MOI.modify`, so JuMP deletes the constraint, modifies it, and then re-adds it, triggering the bridge again.

If you’re looking to write a bridge that depends on variable bounds, you’ll need `final_touch`.

For examples, see:

- [MathOptInterface.jl/bin\_packing.jl at master · jump-dev/MathOptInterface.jl · GitHub](https://github.com/jump-dev/MathOptInterface.jl/blob/master/src/Bridges/Constraint/bridges/bin_packing.jl)
- [MathOptInterface.jl/count\_belongs.jl at master · jump-dev/MathOptInterface.jl · GitHub](https://github.com/jump-dev/MathOptInterface.jl/blob/master/src/Bridges/Constraint/bridges/count_belongs.jl)
- [MathOptInterface.jl/count\_greater\_than.jl at master · jump-dev/MathOptInterface.jl · GitHub](https://github.com/jump-dev/MathOptInterface.jl/blob/master/src/Bridges/Constraint/bridges/count_greater_than.jl)
- [MathOptInterface.jl/count\_distinct.jl at master · jump-dev/MathOptInterface.jl · GitHub](https://github.com/jump-dev/MathOptInterface.jl/blob/master/src/Bridges/Constraint/bridges/count_distinct.jl)

This will ensure you get to see any updated bounds before each call to `optimize!`.

I still need to write documentation for this though: [Explain bridges in the documentation (better) · Issue #1504 · jump-dev/MathOptInterface.jl · GitHub](https://github.com/jump-dev/MathOptInterface.jl/issues/1504#issuecomment-1170635360)

And for the last part, using `direct_model` includes a bunch of additional assumptions on the methods you need to implement. You’re missing `MOI.get` for `ConstraintFunction` and `ConstraintSet`.

---

<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:** [February 7, 2023, 10:42pm UTC](https://discourse.julialang.org/t/moi-re-evaluate-custom-bridge-direct-model-bridging/94219/6 "2023-02-07T22:42:08Z")

</div>

Thanks, amazing input (as always!). And by the way: the bridges are a really fascinating piece to dig into as an outsider 😃

---

<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:** [February 7, 2023, 11:24pm UTC](https://discourse.julialang.org/t/moi-re-evaluate-custom-bridge-direct-model-bridging/94219/7 "2023-02-07T23:24:05Z")

</div>

> the bridges are a really fascinating piece to dig into as an outsider

Yes, they work surprisingly well. Although they’re pretty complicated to work on.

There’s some testing infrastructure that helps a lot to check you’re getting things correct:

> <https://github.com/jump-dev/MathOptInterface.jl/blob/fbf833c94d3b8c25b819025d81a590679b781a1b/test/Bridges/Constraint/count_belongs.jl#L25-L54>

---

<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:** [February 7, 2023, 11:26pm UTC](https://discourse.julialang.org/t/moi-re-evaluate-custom-bridge-direct-model-bridging/94219/8 "2023-02-07T23:26:29Z")

</div>

Ah. I’ve just put your two questions together: [JuMP: Multiplicative parameters for LP](https://discourse.julialang.org/t/jump-multiplicative-parameters-for-lp/94192)

Yes, that’s an elegant way to go about it: for solvers not supporting quadratics, add a bridge from quadratic to linear that checks for fixed variables in `final_touch` 😄

Open an issue or a PR at MathOptInterface.jl if you get stuck and I can point you in the right direction.

@blegat and @joaquimg will be interested/impressed.

---

<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:** [February 8, 2023, 6:41am UTC](https://discourse.julialang.org/t/moi-re-evaluate-custom-bridge-direct-model-bridging/94219/9 "2023-02-08T06:41:19Z")

</div>

> There’s some testing infrastructure that helps a lot to check you’re getting things correct […]

Thanks for pointing that out!

> [@odow](#):
>
> Ah. I’ve just put your two questions together: [JuMP: Multiplicative parameters for LP](https://discourse.julialang.org/t/jump-multiplicative-parameters-for-lp/94192)
> 
> Yes, that’s an elegant way to go about it: for solvers not supporting quadratics, add a bridge from quadratic to linear that checks for fixed variables in `final_touch` 😄
> 
> Open an issue or a PR at MathOptInterface.jl if you get stuck and I can point you in the right direction.
> 
> @blegat and @joaquimg will be interested/impressed.

Yeah, that was the initial thought - and after reading into it, I noticed that I could do so much more using bridges (e.g. automatic/built-in piecewise approximations that are right now… let’s say “non-elegant”… in my implementation) 😃

I just didn’t want to jump the gun before trying it out - could be way worse than any “modify the affine terms of some expression”-approach. If I can get a working prototype up and running, I’ll make sure to discuss it further on github!

---

<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:** [February 8, 2023, 7:36am UTC](https://discourse.julialang.org/t/moi-re-evaluate-custom-bridge-direct-model-bridging/94219/10 "2023-02-08T07:36:20Z")

</div>

Sounds good. Feel free to open a PR even if you have only half a working prototype.
