JuMP Multiple Models Design Question

What is the rationale behind throwing an error in this?

using JuMP

m1 = Model()
@variable(m1, x >= 0)

m2 = Model()
@variable(m2, x >= 0)

@objective(m1, Min, x) # this will error w/ variable not owned

gives

ERROR: LoadError: VariableNotOwned{VariableRef}(x)

I am telling @objective that the model I want it to operate on is m1. So shouldn’t it know that the x I am referring to is the one that is in m1?

I know the workaround:

using JuMP

m1 = Model()
@variable(m1, x >= 0)

m2 = Model()
@variable(m2, x >= 0)

# @objective(m1, Min, x) # this will error w/ variable not owned

x = m1[:x]
@objective(m1, Min, x)
print(m1)

gives

Min x
Subject to
 x ≥ 0.0

But I don’t get why the workaround is necessary. I am interested just so I have a better idea of how JuMP is working under the hood.

Here is the documentation which explains the difference: https://www.juliaopt.org/JuMP.jl/stable/variables/.

Essentially,

using JuMP

m1 = Model()
@variable(m1, x >= 0)  # Makes a Julia variable "x" which points to the JuMP variable "x" in m1.

m2 = Model()
@variable(m2, x >= 0) # Changes the Julia variable "x" to point to the JuMP variable "x" in m2.

m1[:x]  # The JuMP variable "x" in m1
m2[:x]  # The JuMP variable "x" in m2

m1[:x] == x # false
m2[:x] == x # true

My question is why it is like this
When I say

@objective(m1, Min, x)

I am already being clear about which x, I am talking about since I also passed m1.
I’m trying to understand where the ambiguity would arise.

x is not the name of the variable. It is a Julia binding. Consider

using JuMP
model = Model()
@variable(model, x)
y = x
@objective(model, Min, y)

julia> println(model)
Min x
Subject to

Or even more confusingly

using JuMP
model = Model()
@variable(model, x)
@variable(model, y)
y = x
@objective(model, Min, y)
@constraint(model, y <= model[:y])

julia> println(model)
Min x
Subject to
 x - y ≤ 0.0
3 Likes

Thanks, that makes sense as to why this is done the way it is.

My workaround for such cases is using Dictionaries to pack the models

m = Dict()  # models dictionary
x = Dict()  # x variables dictionary

# example with 2 models
for i = 1:2
    m[i] = Model()
    x[i] = @variable(m[i], base_name="x$i")
    @constraint(m[i], x[i] >= 0)
    # ... continue describing your model
end

The base_name="x$i" keyword argument is a nice addition, as you get to name the variables

If anyone has a better alternative I’m open to it :slightly_smiling_face: