# Copy\_model() equivalent for BilevelJuMP.jl

**URL:** <https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709>\
**Category:** Optimization (Mathematical)\
**Tags:** question, package\
**Created:** [April 19, 2022, 9:32pm UTC](https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709 "2022-04-19T21:32:30Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![math\_opt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/math_opt/32/25317_2.png) [@math\_opt](https://discourse.julialang.org/u/math_opt)\
**Post date:** [April 19, 2022, 9:32pm UTC](https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709/1 "2022-04-19T21:32:30Z")

</div>

Is there a way to copy a `JuMP model` and use it as a building block for the lower level problem in [BilevelJuMP.jl](https://github.com/joaquimg/BilevelJuMP.jl). For example:

```julia
using JuMP, BilevelJuMP
model = Model()
@variable(model, ...)
@constraint(model, ...)

#Defining bilevel model
new_model = BilevelModel(GLPK.Optimizer, mode = BilevelJuMP.SOS1Mode())

@variable(Lower(new_model ), ...)
@variable(Upper(new_model ), ...)

#Defining the upper model using usual BilevelJuMP syntax
@objective(Upper(new_model ), Min, ...)
@constraints(Upper(new_model ), begin
...
end)

#Made up function (copies the variables and constraints from model to new_model)
copy_model(model, lower(new_model))

#Adding more constraints to the lower level problem (probably using reference_map)
@constraints(Lower(new_model ), begin
...
end)

```

I see that we have [`copy_model`](https://jump.dev/JuMP.jl/stable/reference/models/#JuMP.copy_model) function in JuMP which returns a copy and reference map of a JuMP model. Is there a way I can use it or create a modified version of it to copy a model to the lower level problem of a bilevel model?

Thank you!

---

<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 19, 2022, 10:02pm UTC](https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709/2 "2022-04-19T22:02:20Z")

</div>

What’s the use-case for this? In general, JuMP extensions don’t compose well because they maintain extra state.

---

<div class="post-metadata">

**Author:** ![math\_opt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/math_opt/32/25317_2.png) [@math\_opt](https://discourse.julialang.org/u/math_opt)\
**Post date:** [April 19, 2022, 10:32pm UTC](https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709/3 "2022-04-19T22:32:11Z")

</div>

I don’t have a concrete use-case, but I was just thinking if we have a `JuMP model` already defined (probably using it in some other part of a project), then it might save us some effort to redefine everything using the BilevelJuMP.jl syntax if we have such a copy function.  
However, I suppose it would be easier to simply use [dualization.jl](https://github.com/jump-dev/Dualization.jl) to create a dual of the problem (which if I am not wrong is a `JuMP model` again) and maybe create a copy of it using [copy\_model](https://jump.dev/JuMP.jl/stable/reference/models/#JuMP.copy_model) (if needed) and then directly formulate a single-level problem.

---

<div class="post-metadata">

**Author:** ![joaquimg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joaquimg/32/223_2.png) [@joaquimg](https://discourse.julialang.org/u/joaquimg)\
**Post date:** [April 20, 2022, 6:11am UTC](https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709/4 "2022-04-20T06:11:47Z")

</div>

If you have a JuMP model, you probably had a function (or many) that built that.  
For instance:

```julia
function model_builder()
    model = Model()
    # add variables and constraints
    return model
end
model = model_builder()

```

You could change to:

```julia
model = Model()
function model_builder(model)
    # add variables and constraints
    return
end
model_builder(model)
# at this point `model` would be equivalent to the first version

```

you can re-use the builder for the lower level of a bilvel problem like this:

```julia
model = BilevelModel(...)
function model_builder(model)
    # add variables and constraints
    return
end
model_builder(Lower(model))
# this would load the lower level

```

In this case you don’t even need to pre-build a model an use a copy. Just re-use a builder function.

---

<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 20, 2022, 6:39am UTC](https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709/5 "2022-04-20T06:39:59Z")

</div>

Related tutorial: [Design patterns for larger models · JuMP](https://jump.dev/JuMP.jl/stable/tutorials/getting_started/design_patterns_for_larger_models/)

---

<div class="post-metadata">

**Author:** ![math\_opt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/math_opt/32/25317_2.png) [@math\_opt](https://discourse.julialang.org/u/math_opt)\
**Post date:** [April 20, 2022, 8:12pm UTC](https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709/6 "2022-04-20T20:12:13Z")

</div>

@joaquimg This works perfectly as I would want it to. Thank you. I have one additional question – when I try to print out the variables for each level, it counts the inner-level variables as variables in the upper-level problem and vice-versa. To show what I mean:

```julia
using JuMP, BilevelJuMP, Gurobi

model = BilevelModel(Gurobi.Optimizer, mode = BilevelJuMP.SOS1Mode())

@variable(Lower(model), x)
@variable(Upper(model), y)

@objective(Upper(model), Min, 3x + y)
@constraints(Upper(model), begin
    x <= 5
    y <= 8
    y >= 0
end)

@objective(Lower(model), Min, -x)
@constraints(Lower(model), begin
     x + y <= 8
    4x + y >= 8
    2x + y <= 13
    2x - 7y <= 0
end)

```

Now that we have the bilevel model defined, let’s check the variables:

```julia
all_variables(model.lower)
#returns
2-element Vector{VariableRef}:
 x
 y #Should not return this

all_variables(model.upper)
#returns
2-element Vector{VariableRef}:
 x #Should not return this
 y 

```

Is this the correct way to access the variables or am I missing something? Just for completeness, I also checked the `all_constraints` function and it worked as intended.

---

<div class="post-metadata">

**Author:** ![math\_opt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/math_opt/32/25317_2.png) [@math\_opt](https://discourse.julialang.org/u/math_opt)\
**Post date:** [April 20, 2022, 8:14pm UTC](https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709/7 "2022-04-20T20:14:39Z")

</div>

Very helpful reference! I will spend some time going through this. Thank you.

---

<div class="post-metadata">

**Author:** ![joaquimg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joaquimg/32/223_2.png) [@joaquimg](https://discourse.julialang.org/u/joaquimg)\
**Post date:** [April 22, 2022, 4:08am UTC](https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709/8 "2022-04-22T04:08:46Z")

</div>

This is intended behavior. Linking variables (variables that might appear both in upper and lower models) are considered variables for both as they might appear in constraints in both levels.  
If you have variables that surely wont be in both levels, you can create them with `UpperOnly(model)`/`LowerOnly(model)` (these are considered advanced features though),

---

<div class="post-metadata">

**Author:** ![math\_opt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/math_opt/32/25317_2.png) [@math\_opt](https://discourse.julialang.org/u/math_opt)\
**Post date:** [April 22, 2022, 3:19pm UTC](https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709/9 "2022-04-22T15:19:45Z")

</div>

> [@joaquimg](#):
>
> If you have variables that surely wont be in both levels, you can create them with `UpperOnly(model)` / `LowerOnly(model)` (these are considered advanced features though),

That sounds good; however, if I create these exclusive variables using just `Lower(model)`/`Upper(model)` does the package figure out itself that it belongs to only one level. So I guess my question is, for exclusive variables should I create them only using `LowerOnly(model)/UpperOnly(model)`? Is it incorrect if I define them using the usual `Lower(model)/Upper(model)`? For example, I tried:

```julia
using JuMP, Gurobi

model = BilevelModel(Gurobi.Optimizer, mode = BilevelJuMP.SOS1Mode())

@variable(Lower(model), x)
@variable(Lower(model), z) #this variable is exclusive to the lower level
@variable(Upper(model), y)

@objective(Upper(model), Min, 3x + y)
@constraints(Upper(model), begin
    x <= 5
    y <= 8
    y >= 0
end)

@objective(Lower(model), Min, -x + z)
@constraints(Lower(model), begin
     x + y <= 8z
    4x + y >= 8
    2x + y <= 13
    2x - 7y <= 0
end)

```

On enquiring the variables in each level it returns that `z` belongs to both levels:

```julia
all_variables(model.lower) 
#returns
2-element Vector{VariableRef}:
 x
 y  
 z

all_variables(model.upper)
#returns
2-element Vector{VariableRef}:
 x   
 y 
 z

```

When I use `LowerOnly(model)`, I don’t see the above behaviour.

---

<div class="post-metadata">

**Author:** ![joaquimg](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/joaquimg/32/223_2.png) [@joaquimg](https://discourse.julialang.org/u/joaquimg)\
**Post date:** [April 22, 2022, 3:43pm UTC](https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709/10 "2022-04-22T15:43:06Z")

</div>

> [@math\_opt](#):
>
> if I create these exclusive variables using just `Lower(model)` / `Upper(model)` does the package figure out itself that it belongs to only one level.

No.

> [@math\_opt](#):
>
> for exclusive variables should I create them only using `LowerOnly(model)/UpperOnly(model)` ?

Yes, but this is not necessary. There is no problem assuming that all variables might appear in both levels. I do not recommend this feature for beginers.

---

<div class="post-metadata">

**Author:** ![math\_opt](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/math_opt/32/25317_2.png) [@math\_opt](https://discourse.julialang.org/u/math_opt)\
**Post date:** [April 22, 2022, 3:46pm UTC](https://discourse.julialang.org/t/copy-model-equivalent-for-bileveljump-jl/79709/11 "2022-04-22T15:46:55Z")

</div>

> [@joaquimg](#):
>
> There is no problem assuming that all variables might appear in both levels.

Sounds good. Thanks for the clarifications!
