# Efficient approach to modifying existing JuMP constraints

**URL:** <https://discourse.julialang.org/t/efficient-approach-to-modifying-existing-jump-constraints/113492>\
**Category:** Optimization (Mathematical)\
**Tags:** question, jump, gurobi\
**Created:** [April 25, 2024, 9:22am UTC](https://discourse.julialang.org/t/efficient-approach-to-modifying-existing-jump-constraints/113492 "2024-04-25T09:22:53Z")\
**Posts on this page:** 9\
**Page:** 1

<div class="post-metadata">

**Author:** ![aartinian](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aartinian/32/36023_2.png) [@aartinian](https://discourse.julialang.org/u/aartinian)\
**Post date:** [April 25, 2024, 9:22am UTC](https://discourse.julialang.org/t/efficient-approach-to-modifying-existing-jump-constraints/113492/1 "2024-04-25T09:22:53Z")

</div>

Hello everyone,

Long time lurker, first time poster. 😃 I’m trying to add a new expression to an existing JuMP constraint, but my approach seems pretty slow. Am I approaching this wrong? Thank you in advance.

```julia
# @expression(m, New_Expr[i in set[:key_1]], α * Another_Expr[i])

for i in set[:key_1], t in set[:time_24h]
    # ...store the original contraint function, (=> slow execution)
    constraint = constraint_object(m[:Existing_Cstr][i, t])
    # ...delete it from the model,
    delete(m, m[:Existing_Cstr][i, t])
    # ...and reinsert the modified constraint
    m[:Existing_Cstr][i, t] = @constraint(m, constraint.func - New_Expr[i] == 0.)
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:** [April 25, 2024, 10:12am UTC](https://discourse.julialang.org/t/efficient-approach-to-modifying-existing-jump-constraints/113492/2 "2024-04-25T10:12:49Z")

</div>

What are the constraint and expression types generally speaking? Affine/linear, quadratic and/or nonlinear?

---

<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:** [April 25, 2024, 10:26am UTC](https://discourse.julialang.org/t/efficient-approach-to-modifying-existing-jump-constraints/113492/3 "2024-04-25T10:26:59Z")

</div>

Hi @aartinian, welcome 😄

This depends one what, specifically, you want to modify. Some examples:

- [https://jump.dev/JuMP.jl/stable/manual/constraints/#Modify-a-constant-term](https://jump.dev/JuMP.jl/stable/manual/constraints/#Modify-a-constant-term)
- [https://jump.dev/JuMP.jl/stable/manual/constraints/#Modify-a-variable-coefficient](https://jump.dev/JuMP.jl/stable/manual/constraints/#Modify-a-variable-coefficient)

In general, it is possible to write something that is much more efficient than deleting and re-adding a constraint.

To give some specific advice, can you provide a reproducible example? It doesn’t have to be your exact code; something similar will do.

---

<div class="post-metadata">

**Author:** ![aartinian](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aartinian/32/36023_2.png) [@aartinian](https://discourse.julialang.org/u/aartinian)\
**Post date:** [April 25, 2024, 11:16am UTC](https://discourse.julialang.org/t/efficient-approach-to-modifying-existing-jump-constraints/113492/4 "2024-04-25T11:16:22Z")

</div>

Thank you both for your lightning-fast response to my question. I’ll prepare a reproducible example and post it later today.

---

<div class="post-metadata">

**Author:** ![ianfiske](https://avatars.discourse-cdn.com/v4/letter/i/58f4c7/32.png) [@ianfiske](https://discourse.julialang.org/u/ianfiske)\
**Post date:** [April 25, 2024, 1:05pm UTC](https://discourse.julialang.org/t/efficient-approach-to-modifying-existing-jump-constraints/113492/5 "2024-04-25T13:05:29Z")

</div>

Parameters are another good way to modify constraints [GitHub - jump-dev/ParametricOptInterface.jl: Extension for dealing with parameters](https://github.com/jump-dev/ParametricOptInterface.jl)

---

<div class="post-metadata">

**Author:** ![aartinian](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aartinian/32/36023_2.png) [@aartinian](https://discourse.julialang.org/u/aartinian)\
**Post date:** [April 26, 2024, 3:58pm UTC](https://discourse.julialang.org/t/efficient-approach-to-modifying-existing-jump-constraints/113492/6 "2024-04-26T15:58:11Z")

</div>

While preparing the example below, I realised that I was actually using [direct\_model](https://jump.dev/JuMP.jl/stable/api/JuMP/#direct_model) to initialise the model. This was necessary due to some additional adjustments of Gurobi. However, it is no longer needed. Switching to the [Model](https://jump.dev/JuMP.jl/stable/api/JuMP/#Model) constructor removes the bottleneck mentioned above. Still, I would be very interested in a more efficient solution here.

```julia
using JuMP, Gurobi

function main()

    sto = TimerOutput()

    var_1 = 1
    var_2 = 1:100
    var_3 = 1:10
    var_4 = 1:144
    var_5 = [:a]
    var_6 = [1, 2, 3]

    sets = Dict(
        :A => [var_1],
        :B => Dict(var_1 => collect(var_2)),
        :C => Dict((i => collect(var_3) for i in var_2)...),
        :D => collect(var_4),
        :E => var_5,
        :F => Dict(var_1 => [:b1, :b2, :b3]),
        :G => Dict((j => var_6 for j in var_3)...),
        :H => Dict((j => 100 + 10 * j) for j in var_3),
        :I => Dict(((j, k) => 0.1 * k + 0.05 * j) for j in var_3 for k in var_6)
    )

    # model = Model(() -> Gurobi.Optimizer(GRB_ENV))
    model = direct_model(Gurobi.Optimizer(GRB_ENV))

    # variables
    @variable(model, 0 <= X[sets[:A], i in sets[:B][var_1], j in sets[:C][i], t in sets[:D]] <= sets[:H][j])
    @variable(model, 0 <= Y[sets[:A], n in sets[:E], br in sets[:F][var_1]] <= 100)

    # expressions
    @expression(model, Z[a in sets[:A], i in sets[:B][var_1], j in sets[:C][i], b in sets[:G][j], t in sets[:D]], X[a, i, j, t] * sets[:I][(j, b)])
    @expression(model, W[a in sets[:A], n in sets[:E]], sum(Y[a, n, br] for br in sets[:F][var_1]))

    # constraints
    @constraint(model, U[a in sets[:A], b in var_6, t in sets[:D]], sum(Z[a, i, j, b, t] for i in sets[:B][var_1], j in sets[:C][i] if b in sets[:G][j]) == 0)

    # ... a few moments later ...

    calculated_parameter = 0.5
    @expression(model, V[a in sets[:A], n in sets[:E]], calculated_parameter * W[a, n])

    for a in sets[:A], t in sets[:D]
        @timeit sto "get" constraint = constraint_object(model[:U][a, 1, t])
        @timeit sto "del" delete(model, model[:U][a, 1, t])
        @timeit sto "set" model[:U][a, 1, t] = @constraint(
            model, constraint.func - V[a, :a] == 0.
        )
    end

    print_timer(sto)
end

main()

```

With `direct_model` :

```julia
 ────────────────────────────────────────────────────────────────────
                            Time Allocations      
                   ─────────────────────── ────────────────────────
 Tot / % measured: 42.1s / 93.1% 1.40GiB / 2.3%    

 Section ncalls time %tot avg alloc %tot avg
 ────────────────────────────────────────────────────────────────────
 get 144 39.1s 99.7% 272ms 16.2MiB 49.9% 115KiB
 set 144 105ms 0.3% 731μs 16.2MiB 50.0% 115KiB
 del 144 16.1ms 0.0% 112μs 6.75KiB 0.0% 48.0B
 ────────────────────────────────────────────────────────────────────

```

With the `Model` constructor:

```julia
  ────────────────────────────────────────────────────────────────────
                            Time Allocations      
                   ─────────────────────── ────────────────────────
 Tot / % measured: 2.97s / 0.8% 1.40GiB / 2.0%    

 Section ncalls time %tot avg alloc %tot avg
 ────────────────────────────────────────────────────────────────────
 get 144 11.9ms 52.2% 82.4μs 12.3MiB 42.2% 87.2KiB
 set 144 10.7ms 47.0% 74.3μs 16.7MiB 57.6% 119KiB
 del 144 182μs 0.8% 1.26μs 38.7KiB 0.1% 275B
 ────────────────────────────────────────────────────────────────────

```

---

<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:** [April 27, 2024, 3:44am UTC](https://discourse.julialang.org/t/efficient-approach-to-modifying-existing-jump-constraints/113492/7 "2024-04-27T03:44:41Z")

</div>

What version of Gurobi.jl do you have installed? Try updating to the latest version, we recently fixed a performance issue that is probably causing this.

I would probably do something like this:

```Julia
    # @expression(model, W[a in sets[:A], n in sets[:E]], sum(Y[a, n, br] for br in sets[:F][var_1]))
    @variable(model, W[a in sets[:A], n in sets[:E]])
    @constraint(
        model, 
        [a in sets[:A], n in sets[:E]],
        W[a, n] == sum(Y[a, n, br] for br in sets[:F][var_1]),
    )
    @constraint(
        model, 
        U[a in sets[:A], b in var_6, t in sets[:D]],
        sum(Z[a, i, j, b, t] for i in sets[:B][var_1], j in sets[:C][i] if b in sets[:G][j]) == 0,
    )
    calculated_parameter = 0.5
    for a in sets[:A], t in sets[:D]
        set_normalized_coefficient(model[:U][a, 1, t], W[a, :a], -calculated_parameter)
    end

```

where you introduce a new variable to represent the expression, and then you modify the coefficient of that variable in the relevant constraints.

---

<div class="post-metadata">

**Author:** ![aartinian](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/aartinian/32/36023_2.png) [@aartinian](https://discourse.julialang.org/u/aartinian)\
**Post date:** [April 29, 2024, 7:08am UTC](https://discourse.julialang.org/t/efficient-approach-to-modifying-existing-jump-constraints/113492/8 "2024-04-29T07:08:43Z")

</div>

Thank you, @odow . It works perfectly and even opens the door for further improvements. 😄

Currently using Gurobi.jl 1.0.0, but will definitely update soon as part of switching from Julia 1.8 to 1.11.

---

<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:** [April 29, 2024, 7:21am UTC](https://discourse.julialang.org/t/efficient-approach-to-modifying-existing-jump-constraints/113492/9 "2024-04-29T07:21:49Z")

</div>

You don’t have to switch versions of Julia to update a package. Just run:

```Julia
julia> import Pkg

julia> Pkg.update("Gurobi")
    Updating registry at `~/.julia/registries/General.toml`
    Updating `~/.julia/dev/Convex/grb/Project.toml`
  [2e9cd046] ↑ Gurobi v1.0.0 ⇒ v1.2.3
    Updating `~/.julia/dev/Convex/grb/Manifest.toml`
  [2e9cd046] ↑ Gurobi v1.0.0 ⇒ v1.2.3

```

There are no breaking changes, just performance and bug fixes:

> **[Comparing v1.0.0...v1.2.3 · jump-dev/Gurobi.jl](https://github.com/jump-dev/Gurobi.jl/compare/v1.0.0...v1.2.3)**
>
> Julia interface for Gurobi Optimizer. Contribute to jump-dev/Gurobi.jl development by creating an account on GitHub.
