Defining variable with varying number of index sets

I want to create a JuMP variable with the syntax

@variable(model, [U])

such that it is equivalent to

@variable(model, [S,T])

How can I ‘pack’ S,T into U such that it exapnds to S,T in the @variable syntax?

What I would like is to have a function get_index_sets(A) that can dispatch on different types of A to give different sets of indexes

U = get_index_set(A)
# U could then be 1:N for one type of A and (say) 1:N, ["high", "low"] for another type
@variable(model, x[U])

I tried

U = [(s,t) for s in S for t in T]

But this gives 1-D array of indicies instead of a 2D array?

Or is this not possible so I would have to write the variable creation seperately for each case?

Why do you want to do this, and the “dispatch”.
Sounds a bit complicated.
Decision variables are typically of type Float64, which should be sufficient in practice.

In my opinion, you should decide which quantity should be modeled as a variable judiciously.
And for the rest you can use @expression, which is a very powerful macro.

e.g.
@variable(model, U[1:4])
@expression(model, S, U[1:2])
@expression(model, T, U[3:4])

or you can do
@variable(model, S[1:2])
@variable(model, T[1:2])
@expression(model, U, [S; T])

julia> [(i, j) for i in 1:3, j in 1:2]
3×2 Matrix{Tuple{Int64, Int64}}:
 (1, 1)  (1, 2)
 (2, 1)  (2, 2)
 (3, 1)  (3, 2)

julia> [(i, j) for i in 1:3 for j in 1:2]
6-element Vector{Tuple{Int64, Int64}}:
 (1, 1)
 (1, 2)
 (2, 1)
 (2, 2)
 (3, 1)
 (3, 2)

Hi @Magnus_Lilledahl,

You cannot do this via the built-in JuMP syntax.

Take a read of Variables · JuMP

You do not need to use the built-in JuMP containers. They’re intended to help common use-cases. If you want something else, you’ll need to implement it yourself.

Just my 2 cents, but I often find when I am struggling to think of how to use the existing JuMP syntax to set up a model, it means I’m thinking about it wrong and a change of perspective can make it clearer how to model it, for example seeing it as a graph with decision variables on edges and nodes, or some other data structure such as state-task networks, activity networks, etc. The possibilities really are quite endless.

1 Like

:+1:

This is the strength and limitation of JuMP at the same time. It is possible to write some very nice code that elegantly models the problem. But it’s also hard to provide generic advice for new problems because there isn’t a one-size-fits-all approach. The built-in containers (Array, DenseAxisArray and SparseAxisArray) are most commonly useful, but they aren’t always the best data structures.

1 Like

Yeah. The subtle shift in perspective is really important for modeling. It’s what I was trying to get across with my blog post over at AlgebraicJulia about integrating JuMP into C-Sets.

1 Like

@odow Thanks for the clear answer (that it is not possible) and suggestion about the custom containers, I had forgotten (I think it docs says something like “…a feature often overlook by beginners…” :grinning_face: ) about that possibility - and will check it out.

Just to be clear about my original plan (@WalterMadelim ): What I want is a piece of software where I can easily change between deterministic and stochastic modelling. To try to elaborate (in a minimal way), I have (something like) two different structs

struct Deterministic <: Indexstruct
   periods::Int
end

struct Stochastic <: Indexstruct
   periods::Int
   scenarios:Vector{Any}
end

Then somewhere in my code I would call a function to define the variables

function set_variables(ind::Indexstruct)
    index_sets = indices(ind)
    @variable(model, [index_sets])
end

# Users inputs one of these
d = Deterministic(10)
s = Stochastic(10,["high", "low"])

# and the system calls
set_variables(d)
# or set_variables(s)

and the indices function will dispatch on the type of index struct

function indices(d::Deterministic)
    return 1:d.periods
end

function inidices(s::Stochastic)
    return [1:s.periods, s.scenarios]
end
# The syntax in this last expression does not work because the @variable macro 
# will interpret this as two elements with the indices being a OneTo object and 
# a vector. As Odow points out, it seems not to be possible

A workaround could be to dispatch on the set_variables function

function set_variables(d::Deterministic)
   @variable(model, [1:d.periods])
end

function set_variables(s::Stochastic)
   @variable(model, [1:s.periods, s.scenarios]
end

This would lead to a lot of repeated code (there are many @variable calls), but maybe a worth it? Or maybe that I am struggling with the design means I should reconsider my data structure as @slwu89 points out…

I think it docs says something like “…a feature often overlook by beginners

See the link I shared above :smile:

You could do:

function indices(d::Deterministic)
    return [1:d.periods, 1:1]
end

a deterministic problem is also a stochastic problem with one scenario.

A workaround could be to dispatch on the set_variables function

See: Design patterns for larger models · JuMP

For other inspiration, see:

1 Like

The aforementioned blog post I wrote about embedding JuMP models in a C-Set (a type of data structure from applied category theory generalizing graphs and databases) is here, where I show how to formulate the simple multi-commodity flow problem in this way JuMP-ing with AlgebraicJulia II: A practical optimization model – AlgebraicJulia blog

If you’ve never seen a C-Set before, this is a nice place to start Graphs and C-sets I: What is a graph? – AlgebraicJulia blog