Can I create a null JuMP.VariableRef?

I can pre-allocate a JuMP.AffExpr(0.).

But can I create a dumb x = JuMP.VariableRef(undef)?

Then I can write something like

v = JuMP.Containers.@container([i=1:3], x)
for i = 1:3
    if i !== 3
       v[i] = JuMP.@variable(m) # this is real decision variable that has a Gurobi_c_index
    end
end

No.

This generally means you want to use a different data structure. For example, you could use a dictionary:

v = Dict(i => @variable(model) for i in 1:3 if i != 3)
1 Like
julia> c = JuMP.Containers.@container([g=1:3, t=1:4, s=1:2], NaN)
3×4×2 Array{Float64, 3}:
[:, :, 1] =
 NaN  NaN  NaN  NaN
 NaN  NaN  NaN  NaN
 NaN  NaN  NaN  NaN

[:, :, 2] =
 NaN  NaN  NaN  NaN
 NaN  NaN  NaN  NaN
 NaN  NaN  NaN  NaN

julia> a = JuMP.Containers.@container([g=1:3, t=2:4], NaN)
2-dimensional DenseAxisArray{Float64,2,...} with index sets:
    Dimension 1, Base.OneTo(3)
    Dimension 2, 2:4
And data, a 3×3 Matrix{Float64}:
 NaN  NaN  NaN
 NaN  NaN  NaN
 NaN  NaN  NaN

julia> c[1:3, 2:4, 2]
3×3 Matrix{Float64}:
 NaN  NaN  NaN
 NaN  NaN  NaN
 NaN  NaN  NaN

julia> c[1:3, 2:4, 2] .= a
ERROR: DimensionMismatch: array could not be broadcast to match destination
Stacktrace:
 [1] check_broadcast_shape
   @ ./broadcast.jl:559 [inlined]
 [2] check_broadcast_shape
   @ ./broadcast.jl:560 [inlined]
 [3] check_broadcast_axes
   @ ./broadcast.jl:562 [inlined]
 [4] instantiate
   @ ./broadcast.jl:316 [inlined]
 [5] materialize!(dest::SubArray{…}, x::JuMP.Containers.DenseAxisArray{…})
   @ Base.Broadcast ./broadcast.jl:898
 [6] top-level scope
   @ REPL[19]:1
Some type information was truncated. Use `show(err)` to see complete types.

julia> c[:,2,1] .= a[:,2]
ERROR: DenseAxisArray does not support this operation.

I think this broadcasting should be supported. why not?
This operation can be used in 2SSP. (g is the index of generators, t is the index of time, s is the index of scenarios. t=1 is first stage, t=2:4 is the second stage)

The real code I want to write is like

function Model!(m, t, T, F, GD, WD, DD, uvH;PfMax,KupRe,SuCostK,LsCost,WcCost)
    DDˈi, DDˈD, DDˈn, DDˈC = DD.i, DD.D, DD.n, DD.C
    times, Times, lines, loads = t+1:t+T, t:t+T, 1:size(F,1), eachindex(DDˈD)
    Common = JuMP.@variable(m)
    ζ = @container([loads, Times, 1:S], Common)
    let i=t
        ζ1 = JuMP.@variable(m, [d=loads], lower_bound=0., upper_bound=DDˈC[DDˈi[d]][i]DDˈD[d])
        for s=1:S ζ[:,i,s] .= ζ1 end
    end
    for s=1:S
        ζ2 = JuMP.@variable(m, [d=loads, i=times], lower_bound=0., upper_bound=DDˈC[DDˈi[d]][i]DDˈD[d])
        for i=times ζ[:,i,s] .= ζ2[:,i] end
    end
    #...
    ζ
end

But now the broadcasting is not working. Do I have to write a explicit for-loop to fill in?
Edit: It appears that this works

for s=1:S, i=times
    ζ[:,i,s] .= JuMP.@variable(m, [d=loads], lower_bound=0., upper_bound=DDˈC[DDˈi[d]][i]DDˈD[d])
end

So, an interesting question is

  • Can JuMP supports an in-place version of creating a vector of variables?

To do this line

ζ[:,i,s] .= JuMP.@variable(m, [d=loads], lower_bound=0., upper_bound=DDˈC[DDˈi[d]][i]DDˈD[d])

without allocation? where ζ is an already-existing container (whatever it is).

I have said this many times, but you are trying to write code that is too cryptic. Be more verbose. It is easier to read.

One option is:

function Model!(m, t, T, F, GD, WD, DD, uvH;PfMax,KupRe,SuCostK,LsCost,WcCost)
    DDˈi, DDˈD, DDˈn, DDˈC = DD.i, DD.D, DD.n, DD.C
    times, Times, lines, loads = t+1:t+T, t:t+T, 1:size(F,1), eachindex(DDˈD)
    Common = JuMP.@variable(m)
    
    JuMP.@variable(model, 0 <= ζ1[d in loads] <= DDˈC[DDˈi[d]][t]DDˈD[d])
    JuMP.@variable(
        model,
        0 <= ζ2[d in loads, i in times, s in 1:S] <= DDˈC[DDˈi[d]][i]DDˈD[d],

    )
    ζ = JuMP.Containers.@container(
        [d in loads, i in Times, s in 1:S],
        i == t ? ζ1[d] : ζ2[d, i, s]
    )
end

But otherwise, you can create your own data structures, you don’t need to use JuMP’s containers.

You could, for example, do:

struct Variables{T1,T2}
    t::Int
    ζ1::T1
    ζ2::T2
end

function Base.getindex(x::Variables, d, i, s)
    if i == x.t
        return x.ζ1[d]
    else
        return x.ζ2[d, i, s]
    end
end

ζ = Variables(t, ζ1, ζ2)
1 Like