Variable not defined when working with 2 loops

question

#1

Hi all,

I am working on lagrangian relaxation of a particular problem, and I meet the following error.

The original problem consists of 10 almost independent subproblems, where 10 subproblems are only linked by 1 simple linking constraint.

Hence the linking constraint is penalized, moved to objective, and then 10 independent subproblems are solved with modified objective.

read problem input W

m = Vector{Model}( undef, 10 )

# add variables and constraints
# to define feasible region for each subproblem
for k in 1:10   
  m[ k ] = Model( solver = GurobiSolver() )
  @variable( m[ k ] , c )
end

# outter loop for update lagrangian dual variables
for it in 1:50
  set up lagrangian dual variable \lambda
  # inner loop for solving decomposed subproblem
  # with modified objective
  for k in 1:10
    @objective( m[ k ] , Min , c - \lambda )
    solve( m[ k ] )
  end
end

But julia says c is not defined when it encoutner the second loop. Can anyone help me on this?

ERROR: LoadError: UndefVarError: c not defined
Stacktrace:
 [1] top-level scope at C:\Users\tommyricardo\.julia\packages\JuMP\PbnIJ\src\parseExpr_staged.jl:508
 [2] top-level scope at C:\Users\tommyricardo\.julia\packages\JuMP\PbnIJ\src\macros.jl:859
 [3] top-level scope at F:\a__top_secret\lagrangian_relaxation_TypeExperiment_SingleUnitQIP.jl:173
in expression starting at F:\a__top_secret\lagrangian_relaxation_TypeExperiment_SingleUnitQIP.jl:171

#2

I think you can debug by querying the model variables before setting the objective


#3

There are a couple of problems you’re running into. The first has nothing to do with JuMP and is simply the fact that loops in Julia create their own scope:

julia> for k in 1:10
         c = 5
       end

julia> c
ERROR: UndefVarError: c not defined

But even if they didn’t, after your k in 1:10 loop completes, c would just refer to the variable in the last iteration (that is, the variable in model m[10]). That’s almost certainly not what you want.

You probably want to also maintain a collection of variables c like your collection of models m. That way you can refer to the variable c belonging to the correct model each time. For example:

cs = Vector{Variable}()
for k in 1:10 
  m[k] = Model()
  @variable(m[k], c)
  push!(cs, c)
end

...
for k in 1:10 
  @objective(m[k], Min, cs[k] - \lambda)
end

#4

Thanks very much! I saved those variables into Vector, and the code could be excuted.

But then I am a bit confused in this case… this is also related to reply from @nitu0317. From @rdeits’s comment I have the following understanding.

Since I have decleared a Vector of model m. When a variable c is added to m[ k ] in the way @variable( m[ k ] , c ), some variable is added to model m[ k ] but it is not particularly named by c. And we could only refer to that variable by the name c under current scope.

When I use @objective( m[ k ] , c ) at second loop, julia is looking for c as a variable at current scope , rather than a variable from the model m[ k ].

I did the following small experiment

m = Model( solver = GurobiSolver( ) )
@variable( m , b  )
@constraint( m , b >= 1 )

b = 4

@constraint( m , b >= 3 )
@objective( m , Min , b )

print( m )

The resulting model is

Min 4
Subject to
 b >= 1
 0 >= -1
 b

This is quite suprising to me… Is there any way to give a “global fixed” name to a variable in some models ? So that for the rest of the code we can tell julia we are refering to that particular variable.

Thanks a lot if anyone could help on this.


#5

If I understand your question correctly, then the answer is no. When you write @constraint(m, b >= 3), you are telling JuMP to create a constraint involving whatever value has the name b in your current scope. That has nothing to do with the fact that in your model, you happened to have a JuMp variable also named “b”.

However, you can ask the model to look up a variable by its name. For example:

for k in 1:10 
  b = m[:b] # get the JuMP variable named "b" and assign it to the Julia variable `b`
  @objective(m, Min, b)
end

in the latest version of JuMP, I believe you can use JuMP.variable_by_name(m, "b") instead of m[:b].


#6

Thanks for explaining everything!