# Containers

JuMP provides a specialized container similar to AxisArrays that enables indexing with non-integer indices. Normally these are created automatically by JuMP's macros. The following constructors can be used to create them manually.

JuMP.Containers.DenseAxisArrayType
DenseAxisArray(data::Array{T, N}, axes...) where {T, N}

Construct a JuMP array with the underlying data specified by the data array and the given axes. Exactly N axes must be provided, and their lengths must match size(data) in the corresponding dimensions.

Example

julia> array = JuMP.Containers.DenseAxisArray([1 2; 3 4], [:a, :b], 2:3)
2-dimensional DenseAxisArray{Int64,2,...} with index sets:
Dimension 1, Symbol[:a, :b]
Dimension 2, 2:3
And data, a 2×2 Array{Int64,2}:
1  2
3  4

julia> array[:b, 3]
4
source
DenseAxisArray{T}(undef, axes...) where T

Construct an uninitialized DenseAxisArray with element-type T indexed over the given axes.

Example

julia> array = JuMP.Containers.DenseAxisArray{Float64}(undef, [:a, :b], 1:2);

julia> fill!(array, 1.0)
2-dimensional DenseAxisArray{Float64,2,...} with index sets:
Dimension 1, Symbol[:a, :b]
Dimension 2, 1:2
And data, a 2×2 Array{Float64,2}:
1.0  1.0
1.0  1.0

julia> array[:a, 2] = 5.0
5.0

julia> array[:a, 2]
5.0

julia> array
2-dimensional DenseAxisArray{Float64,2,...} with index sets:
Dimension 1, Symbol[:a, :b]
Dimension 2, 1:2
And data, a 2×2 Array{Float64,2}:
1.0  5.0
1.0  1.0
source
JuMP.Containers.SparseAxisArrayType
struct SparseAxisArray{T,N,K<:NTuple{N, Any}} <: AbstractArray{T,N}
data::Dict{K,T}
end

N-dimensional array with elements of type T where only a subset of the entries are defined. The entries with indices idx = (i1, i2, ..., iN) in keys(data) has value data[idx]. Note that as opposed to SparseArrays.AbstractSparseArray, the missing entries are not assumed to be zero(T), they are simply not part of the array. This means that the result of map(f, sa::SparseAxisArray) or f.(sa::SparseAxisArray) has the same sparsity structure than sa even if f(zero(T)) is not zero.

Example

julia> dict = Dict((:a, 2) => 1.0, (:a, 3) => 2.0, (:b, 3) => 3.0)
Dict{Tuple{Symbol,Int64},Float64} with 3 entries:
(:b, 3) => 3.0
(:a, 2) => 1.0
(:a, 3) => 2.0

julia> array = JuMP.Containers.SparseAxisArray(dict)
JuMP.Containers.SparseAxisArray{Float64,2,Tuple{Symbol,Int64}} with 3 entries:
[b, 3]  =  3.0
[a, 2]  =  1.0
[a, 3]  =  2.0

julia> array[:b, 3]
3.0
source

## Containers in macros

The container function encodes the logic for how containers are constructed in JuMP's macros. The @container macro is available to create containers independently of any JuMP model.

JuMP.Containers.containerFunction
container(f::Function, indices, ::Type{C})

Create a container of type C with indices indices and values at given indices given by f.

container(f::Function, indices)

Create a container with indices indices and values at given indices given by f. The type of container used is determined by default_container.

Examples

julia> Containers.container((i, j) -> i + j, Containers.vectorized_product(Base.OneTo(3), Base.OneTo(3)))
3×3 Array{Int64,2}:
2  3  4
3  4  5
4  5  6

julia> Containers.container((i, j) -> i + j, Containers.vectorized_product(1:3, 1:3))
2-dimensional DenseAxisArray{Int64,2,...} with index sets:
Dimension 1, 1:3
Dimension 2, 1:3
And data, a 3×3 Array{Int64,2}:
2  3  4
3  4  5
4  5  6

julia> Containers.container((i, j) -> i + j, Containers.vectorized_product(2:3, Base.OneTo(3)))
2-dimensional DenseAxisArray{Int64,2,...} with index sets:
Dimension 1, 2:3
Dimension 2, Base.OneTo(3)
And data, a 2×3 Array{Int64,2}:
3  4  5
4  5  6

julia> Containers.container((i, j) -> i + j, Containers.nested(() -> 1:3, i -> i:3, condition = (i, j) -> isodd(i) || isodd(j)))
SparseAxisArray{Int64,2,Tuple{Int64,Int64}} with 5 entries:
[1, 2]  =  3
[2, 3]  =  5
[3, 3]  =  6
[1, 1]  =  2
[1, 3]  =  4
source
JuMP.Containers.VectorizedProductIteratorType
struct VectorizedProductIterator{T}
prod::Iterators.ProductIterator{T}
end

Cartesian product of the iterators prod.iterators. It is the same iterator as Base.Iterators.ProductIterator except that it is independent of the IteratorSize of the elements of prod.iterators. For instance:

• size(Iterators.product(1, 2)) is tuple() while size(VectorizedProductIterator(1, 2)) is (1, 1).
• size(Iterators.product(ones(2, 3))) is (2, 3) while size(VectorizedProductIterator(ones(2, 3))) is (1, 1).
source
JuMP.Containers.NestedIteratorType
struct NestedIterator{T}
iterators::T # Tuple of functions
condition::Function
end

Iterators over the tuples that are produced by a nested for loop. For instance, if length(iterators) == 3 , this corresponds to the tuples (i1, i2, i3) produced by:

for i1 in iterators[1]()
for i2 in iterator[2](i1)
for i3 in iterator[3](i1, i2)
if condition(i1, i2, i3)
# produces (i1, i2, i3)
end
end
end
end
source
JuMP.Containers.@containerMacro
@container([i=..., j=..., ...], expr)

Create a container with indices i, j, ... and values given by expr that may depend on the value of the indices.

@container(ref[i=..., j=..., ...], expr)

Same as above but the container is assigned to the variable of name ref.

The type of container can be controlled by the container keyword. See Containers in macros. Note that when the index set is explicitly given as 1:n for any expression n, it is transformed to Base.OneTo(n) before being given to container.

Examples

julia> Containers.@container([i = 1:3, j = 1:3], i + j)
3×3 Array{Int64,2}:
2  3  4
3  4  5
4  5  6

julia> I = 1:3
1:3

julia> Containers.@container([i = I, j = I], i + j)
2-dimensional DenseAxisArray{Int64,2,...} with index sets:
Dimension 1, 1:3
Dimension 2, 1:3
And data, a 3×3 Array{Int64,2}:
2  3  4
3  4  5
4  5  6

julia> Containers.@container([i = 2:3, j = 1:3], i + j)
2-dimensional DenseAxisArray{Int64,2,...} with index sets:
Dimension 1, 2:3
Dimension 2, Base.OneTo(3)
And data, a 2×3 Array{Int64,2}:
3  4  5
4  5  6

julia> Containers.@container([i = 1:3, j = 1:3; i <= j], i + j)
JuMP.Containers.SparseAxisArray{Int64,2,Tuple{Int64,Int64}} with 6 entries:
[1, 2]  =  3
[2, 3]  =  5
[3, 3]  =  6
[2, 2]  =  4
[1, 1]  =  2
[1, 3]  =  4
source

In the @variable (resp. @constraint) macro, containers of variables (resp. constraints) can be created with the following syntax:

• name[index_set_1, index_set_2, ..., index_set_n] creating an n-dimensional container of name name; or
• [index_set_1, index_set_2, ..., index_set_n] creating an anonymous n-dimensional container.

Each expression index_set_i can either be

• of the form index_set specifying that the ith index set of the container is index_set; or
• of the form index_name=index_set specifying that the ith index set of the container is index_set and allowing values used in the macro expression and keyword arguments to be expressions depending on the index_name.

The macro then creates the container using the JuMP.Containers.container function with the following arguments:

1. A function taking as argument the value of the indices and returning the value to be stored in the container, e.g. a variable for the @variable macro and a constraint for the @constraint macro.
2. An iterator over the indices of the container.
3. The value of the container keyword argument if given.