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.DenseAxisArray
— Type.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
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
JuMP.Containers.SparseAxisArray
— Type.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
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.container
— Function.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
JuMP.Containers.default_container
— Function.default_container(indices)
If indices
is a NestedIterator
, return a SparseAxisArray
. Otherwise, indices
should be a VectorizedProductIterator
and the function returns Array
if all iterators of the product are Base.OneTo
and retunrs DenseAxisArray
otherwise.
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))
istuple()
whilesize(VectorizedProductIterator(1, 2))
is(1, 1)
.size(Iterators.product(ones(2, 3)))
is(2, 3)
whilesize(VectorizedProductIterator(ones(2, 3)))
is(1, 1)
.
JuMP.Containers.NestedIterator
— Type.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
JuMP.Containers.@container
— Macro.@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
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 ann
-dimensional container of namename
; or[index_set_1, index_set_2, ..., index_set_n]
creating an anonymousn
-dimensional container.
Each expression index_set_i
can either be
- of the form
index_set
specifying that thei
th index set of the container isindex_set
; or - of the form
index_name=index_set
specifying that thei
th index set of the container isindex_set
and allowing values used in the macro expression and keyword arguments to be expressions depending on theindex_name
.
The macro then creates the container using the JuMP.Containers.container
function with the following arguments:
- 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. - An iterator over the indices of the container.
- The value of the
container
keyword argument if given.