# Workarounds for splatting in index sets?

**URL:** <https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212>\
**Category:** Optimization (Mathematical)\
**Created:** [June 5, 2024, 6:19am UTC](https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212 "2024-06-05T06:19:53Z")\
**Posts on this page:** 11\
**Page:** 1

<div class="post-metadata">

**Author:** ![solliolli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/solliolli/32/209733_2.png) [@solliolli](https://discourse.julialang.org/u/solliolli)\
**Post date:** [June 5, 2024, 6:19am UTC](https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212/1 "2024-06-05T06:19:53Z")

</div>

Hi! I ran into a small issue when writing a MILP model, namely the fact that the splatting operator cannot be used in the definition of an index set. What I’m trying to achieve is to define JuMP variables with dimensions specified by a vector. I understand this is probably not something that is going to be changed in the future versions of JuMP for technical reasons (I’m one of those people who knows just enough about macros to stay away from them), but I wonder if people here have come up with creative workarounds. As an example of what doesn’t work:

```julia
using JuMP
# I would like a JuMP variable array with dimensions 
# specified by the vector a, 2x3x4 in this case.
a=[1:2, 1:3, 1:4] 

model = Model()
@variable(model, x[a...] >= 0) # Throws an error

```

And my workaround, sufficient for my use case where the indices are simply of the form 1:k, does not work if you want more general index sets like `[1:2, ["red","blue"]]`:

```julia
using JuMP
a=[1:2, 1:3, 1:4]
model = Model()
x = Array{VariableRef}(undef, Tuple(length.(a)))
for index in CartesianIndices(x)
    x[index] = @variable(model, base_name="x[$(join(Tuple(index),','))]", lower_bound=0)
end

```

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [June 5, 2024, 1:31pm UTC](https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212/2 "2024-06-05T13:31:46Z")

</div>

Does this modification do what you’re looking for?

```julia
for (index,bn) in zip(CartesianIndices(x),Base.product(a...))
    x[index] = @variable(model, base_name="x[$(join(bn,','))]", lower_bound=0)
end

```

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [June 5, 2024, 8:19pm UTC](https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212/3 "2024-06-05T20:19:49Z")

</div>

Hi @solliolli, welcome to the forum!

Your work-around is on the right track. A key thing to remember is that you can write your own data structures to use with JuMP; you don’t need to be limited to the syntax that JuMP provides.

In your case, I would do something like:

```julia
julia> using JuMP

julia> a = [1:2, ["red", "blue", "green"], 1:4]
3-element Vector{AbstractVector}:
 1:2
 ["red", "blue", "green"]
 1:4

julia> model = Model();

julia> @variable(model, x[a...] >= 0)
ERROR: LoadError: At REPL[32]:1: `@variable(model, x[a...] >= 0)`: cannot use splatting operator `...` in the definition of an index set.
Stacktrace:
 [1] error(::String, ::String)
   @ Base ./error.jl:44
 [2] (::JuMP.Containers.var"#error_fn#98"{String})(str::String)
   @ JuMP.Containers ~/.julia/dev/JuMP/src/Containers/macro.jl:331
 [3] build_ref_sets(error_fn::JuMP.Containers.var"#error_fn#98"{String}, expr::Expr)
   @ JuMP.Containers ~/.julia/dev/JuMP/src/Containers/macro.jl:345
 [4] parse_ref_sets(error_fn::JuMP.Containers.var"#error_fn#98"{String}, expr::Expr; invalid_index_variables::Vector{Symbol})
   @ JuMP.Containers ~/.julia/dev/JuMP/src/Containers/macro.jl:299
 [5] var"@variable"( __source__ ::LineNumberNode, __module__ ::Module, input_args::Vararg{Any})
   @ JuMP ~/.julia/dev/JuMP/src/macros/@variable.jl:194
in expression starting at REPL[32]:1

julia> function my_add_variable(model::JuMP.Model, a::Vector)
           x = @variable(model, [1:prod(length.(a))], lower_bound = 0)
           for (xi, ai) in zip(x, Iterators.product(a...))
               set_name(xi, replace("x[$ai]", "(" => "", ")" => ""))
           end
           return Containers.DenseAxisArray(reshape(x, length.(a)...), a...)
       end
my_add_variable (generic function with 2 methods)

julia> x = my_add_variable(model, a)
3-dimensional DenseAxisArray{VariableRef,3,...} with index sets:
    Dimension 1, 1:2
    Dimension 2, ["red", "blue", "green"]
    Dimension 3, 1:4
And data, a 2×3×4 Array{VariableRef, 3}:
[:, :, 1] =
 x[1, "red", 1] x[1, "blue", 1] x[1, "green", 1]
 x[2, "red", 1] x[2, "blue", 1] x[2, "green", 1]

[:, :, 2] =
 x[1, "red", 2] x[1, "blue", 2] x[1, "green", 2]
 x[2, "red", 2] x[2, "blue", 2] x[2, "green", 2]

[:, :, 3] =
 x[1, "red", 3] x[1, "blue", 3] x[1, "green", 3]
 x[2, "red", 3] x[2, "blue", 3] x[2, "green", 3]

[:, :, 4] =
 x[1, "red", 4] x[1, "blue", 4] x[1, "green", 4]
 x[2, "red", 4] x[2, "blue", 4] x[2, "green", 4]

```

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [June 5, 2024, 9:23pm UTC](https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212/4 "2024-06-05T21:23:36Z")

</div>

If I define my function your\_add\_variable(), I get the following result, where the second index is not of type string.

```julia
julia> function your_add_variable(model::JuMP.Model, a::Vector)    
           x = Array{VariableRef}(undef, Tuple(length.(a)))        
           for (index,bn) in zip(CartesianIndices(x),Base.product(a...))
               x[index] = @variable(model, base_name="x[$(join(bn,','))]", lower_bound=0)
           end
       Containers.DenseAxisArray(x,a...)
       end
your_add_variable (generic function with 1 method)

julia> your_add_variable(model,a)
3-dimensional DenseAxisArray{VariableRef,3,...} with index sets:
    Dimension 1, 1:2
    Dimension 2, ["red", "blue", "green"]
    Dimension 3, 1:4
And data, a 2×3×4 Array{VariableRef, 3}:
[:, :, 1] =
 x[1,red,1] x[1,blue,1] x[1,green,1]
 x[2,red,1] x[2,blue,1] x[2,green,1]

[:, :, 2] =
 x[1,red,2] x[1,blue,2] x[1,green,2]
 x[2,red,2] x[2,blue,2] x[2,green,2]

[:, :, 3] =
 x[1,red,3] x[1,blue,3] x[1,green,3]
 x[2,red,3] x[2,blue,3] x[2,green,3]

[:, :, 4] =
 x[1,red,4] x[1,blue,4] x[1,green,4]
 x[2,red,4] x[2,blue,4] x[2,green,4]

```

What is the difference between my your\_add\_variable and your my\_add\_variable?

Are both fine?  
If so, is one preferable to the other, for some reason?

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [June 5, 2024, 9:32pm UTC](https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212/5 "2024-06-05T21:32:49Z")

</div>

> What is the difference between my your\_add\_variable and your my\_add\_variable?

Perhaps to state the obvious: `my_add_variable` creates a vector of variables and then reshapes into a matrix. `your_add_variable` creates an empty matrix and then fill one-by-one.

> Are both fine?

Yes.

> If so, is one preferable to the other, for some reason?

Nope. Choose whichever one you find the simplest to understand.

I like your `join` trick for the name. I didn’t think of that:

```julia
julia> function my_add_variable(model::JuMP.Model, a::Vector)
           x = @variable(model, [1:prod(length.(a))], lower_bound = 0)
           for (xi, ai) in zip(x, Iterators.product(a...))
               set_name(xi, "x[$(join(ai, ", "))]")
           end
           return Containers.DenseAxisArray(reshape(x, length.(a)...), a...)
       end

```

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [June 5, 2024, 9:37pm UTC](https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212/6 "2024-06-05T21:37:13Z")

</div>

> [@odow](#):
>
> I like your `join` trick for the name. I didn’t think of that:

This is @solliolli’s idea.  
I, on my own, added the use of the Base.product function to be used together with the Cartesian indices, to obtain the names

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [June 5, 2024, 9:40pm UTC](https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212/7 "2024-06-05T21:40:02Z")

</div>

Indeed 😄 I didn’t scroll up far enough

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [June 5, 2024, 10:00pm UTC](https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212/8 "2024-06-05T22:00:02Z")

</div>

If I try to use the ones provided by your\_add\_variable() it doesn’t work

```julia
julia> yourx=your_add_variable(model,a)
3-dimensional DenseAxisArray{VariableRef,3,...} with index sets:        
    Dimension 1, 1:2
    Dimension 2, ["red", "blue", "green"]
    Dimension 3, 1:4
And data, a 2×3×4 Array{VariableRef, 3}:
[:, :, 1] =
 x[1,red,1] x[1,blue,1] x[1,green,1]
 x[2,red,1] x[2,blue,1] x[2,green,1]

[:, :, 2] =
 x[1,red,2] x[1,blue,2] x[1,green,2]
 x[2,red,2] x[2,blue,2] x[2,green,2]

[:, :, 3] =
 x[1,red,3] x[1,blue,3] x[1,green,3]
 x[2,red,3] x[2,blue,3] x[2,green,3]

[:, :, 4] =
 x[1,red,4] x[1,blue,4] x[1,green,4]
 x[2,red,4] x[2,blue,4] x[2,green,4]

julia> fix(yourx[1,red,3],3;force=true)
ERROR: UndefVarError: `red` not defined
Stacktrace:

```

---

<div class="post-metadata">

**Author:** ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)\
**Post date:** [June 5, 2024, 10:33pm UTC](https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212/9 "2024-06-05T22:33:01Z")

</div>

It is `"red"` the string. `red` the variable doesn’t exist.

---

<div class="post-metadata">

**Author:** ![solliolli](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/solliolli/32/209733_2.png) [@solliolli](https://discourse.julialang.org/u/solliolli)\
**Post date:** [June 6, 2024, 5:58am UTC](https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212/10 "2024-06-06T05:58:37Z")

</div>

Thank you both, this is the nice and fruitful forum experience I expected!

> [@odow](#):
>
> ```julia
> julia> function my_add_variable(model::JuMP.Model, a::Vector)
> x = @variable(model, [1:prod(length.(a))], lower_bound = 0)
> for (xi, ai) in zip(x, Iterators.product(a...))
> set_name(xi, "x[$(join(ai, ", "))]")
> end
> return Containers.DenseAxisArray(reshape(x, length.(a)...), a...)
> end
> 
> ```

This is indeed prettier and more functional, the reshape at the end is a nice trick that did not cross my mind.

---

<div class="post-metadata">

**Author:** ![rocco\_sprmnt21](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/rocco_sprmnt21/32/20127_2.png) [@rocco\_sprmnt21](https://discourse.julialang.org/u/rocco_sprmnt21)\
**Post date:** [June 6, 2024, 7:19am UTC](https://discourse.julialang.org/t/workarounds-for-splatting-in-index-sets/115212/11 "2024-06-06T07:19:40Z")

</div>

> [@odow](#):
>
> It is `"red"` the string. `red` the variable doesn’t exist.

I’m not sure I fully understand what the situation is behind the scenes.  
I understand that what the name\_base function sets is only to facilitate “human reading”.

```julia
julia> function my_add_variable(model::JuMP.Model, a::Vector)
                   x = @variable(model, [1:prod(length.(a))], lower_bound = 0)
                   for (xi, ai) in zip(x, Iterators.product(a...))      
                       set_name(xi,randstring('a':'z', 6))
                   end
                   return Containers.DenseAxisArray(reshape(x, length.(a)...), a...)
               end
my_add_variable (generic function with 1 method)

julia> x = my_add_variable(model, a)
3-dimensional DenseAxisArray{VariableRef,3,...} with index sets:
    Dimension 1, 1:2
    Dimension 2, ["red", "blue", "green"]
    Dimension 3, 1:4
And data, a 2×3×4 Array{VariableRef, 3}:
[:, :, 1] =
 frwiol cduuyy iinatx
 ppzwzl qyyqbk rgucfd

[:, :, 2] =
 qepntc eumldd jfpmeg
 tkmxzi fxyavd wptzrn

[:, :, 3] =
 ewuiqn sfrkom xqzsrq
 jexcux wfmzjg kffpse

[:, :, 4] =
 rpvjdr hpdcgf igsgib
 qxiqld kcgyfc kztysx

```

```julia

Perhaps the road could be "shortened" in this way
model = Model();
y=@variables(model, begin
           x[Base.product(a...)].>=0
    end) ;

ourx=Containers.DenseAxisArray(reshape(y[1].data, length.(a)...), a...)

```

or is it inappropriate to use internal aspects like this?

Can I think of the Containers.DenseAxisArray structure as a sort of multidimensional NamedTuple?

So would it make sense to do something like this?

```julia
julia> our_add_variable(a)=Containers.DenseAxisArray(collect(Base.product(a...)), a...)
our_add_variable (generic function with 1 method)

julia> our_add_variable(a)
3-dimensional DenseAxisArray{Tuple{Int64, String, Int64},3,...} with index sets:
    Dimension 1, 1:2
    Dimension 2, ["red", "blue", "green"]
    Dimension 3, 1:4
And data, a 2×3×4 Array{Tuple{Int64, String, Int64}, 3}:
[:, :, 1] =
 (1, "red", 1) (1, "blue", 1) (1, "green", 1)
 (2, "red", 1) (2, "blue", 1) (2, "green", 1)

[:, :, 2] =
 (1, "red", 2) (1, "blue", 2) (1, "green", 2)
 (2, "red", 2) (2, "blue", 2) (2, "green", 2)

[:, :, 3] =
 (1, "red", 3) (1, "blue", 3) (1, "green", 3)
 (2, "red", 3) (2, "blue", 3) (2, "green", 3)

[:, :, 4] =
 (1, "red", 4) (1, "blue", 4) (1, "green", 4)
 (2, "red", 4) (2, "blue", 4) (2, "green", 4)

```
