Dynamic dimensions of optimization variable in JuMP


Although my question itself is JuMP related, the underlying problem relates to metaprogramming in general:

As the title suggests, I want to create a variable, which dimensions change depending on the numeric input provided. Preferably, this should be archived within the @variable macro. So far, I just write a uniform value into a dimension that was found to be non-existent. The performance gains from completely omitting these dimensions are not significant, but it would allow to greatly facilitate further parts of my code.

I have managed to introduce a new keyword argument “set_in” to the @variable macro that is expression of all relevant dimensions and filters the first input expression at the beginning of my new macro. Accordingly, the macro below will omit dimension i from the created variable.

using JuMP

m = Model()

VarDim_dic = Dict(:a => 1:8, :b => [])

@variable_new(m, x[a= VarDim_dic[:a], b= VarDim_dic[:b]], set_in = :( (a ) ) )

macro variable_new(args…)

extra, kw_args, requestedcontainer = _extract_kw_args(args[2:end])

extra = filterUsedSets(extra,set_in)

However, this obviously won’t work, if set_in is not a hard-coded, but generated dynamically as desired:

RelSets = Meta.parse(join(filter(x -> !isempty(VarDim_dic[x]),collect(keys(VarDim_dic))),", "))

@variable_new(m, x[a= VarDim_dic[:a],b= VarDim_dic][:b], set_in = RelSets )

I tried to add the filtering to the code being actually written by the macro as an expression, but did not succeed. So, first of all I would like to know, if what I’m trying to achieve is even possible and second, if I’ve there is a smarter way to this (For exampe, I thought about generating nested if clauses as well). I think, only if this is the case, it makes sense to have a closer look at my current code in detail. Thanks!

Most probably, you want something simpler like

function create_indices(i)
    return [(1, i), (2, i), (:a, "c")]

indices = create_indices(1)
model = Model()
@variable(model, x[indices])

# output

1-dimensional DenseAxisArray{VariableRef,1,...} with index sets:
    Dimension 1, Tuple{Any,Any}[(1, 1), (2, 1), (:a, "c")]
And data, a 3-element Array{VariableRef,1}:
 x[(1, 1)]   
 x[(2, 1)]   
 x[(:a, "c")]

The create_indices function could produce indices of the right dimension based on an argument.