Containers
More information can be found in the Containers section of the manual.
JuMP.Containers
— ModuleContainers
Module defining the containers DenseAxisArray
and SparseAxisArray
that behaves as a regular AbstractArray
but with custom indexes that are not necessarily integers.
JuMP.Containers.DenseAxisArray
— TypeDenseAxisArray(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
— Typestruct 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.
Examples
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
JuMP.Containers.container
— Functioncontainer(f::Function, indices, c::Type{C}, names)
Create a container of type C
with index names names
, indices indices
and values at given indices given by f
. If this method is not specialized on Type{C}
, it falls back to calling container(f, indices, c)
for backwards compatibility with containers not supporting index names.
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.rowtable
— Functionrowtable([f::Function=identity,] x; [header::Vector{Symbol} = Symbol[]])
Applies the function f
to all elements of the variable container x
, returning the result as a Vector
of NamedTuple
s, where header
is a vector containing the corresponding axis names.
If x
is an N
-dimensional array, there must be N+1
names, so that the last name corresponds to the result of f(x[i])
.
If header
is left empty, then the default header is [:x1, :x2, ..., :xN, :y]
.
A Vector
of NamedTuple
s implements the Tables.jl interface, and so the result can be used as input for any function that consumes a 'Tables.jl' compatible source.
Example
julia> model = Model();
julia> @variable(model, x[i=1:2, j=i:2] >= 0, start = i+j);
julia> Containers.rowtable(start_value, x; header = [:i, :j, :start])
3-element Vector{NamedTuple{(:i, :j, :start), Tuple{Int64, Int64, Float64}}}:
(i = 1, j = 2, start = 3.0)
(i = 1, j = 1, start = 2.0)
(i = 2, j = 2, start = 4.0)
julia> Containers.rowtable(x)
3-element Vector{NamedTuple{(:x1, :x2, :y), Tuple{Int64, Int64, VariableRef}}}:
(x1 = 1, x2 = 2, y = x[1,2])
(x1 = 1, x2 = 1, y = x[1,1])
(x1 = 2, x2 = 2, y = x[2,2])
JuMP.Containers.default_container
— Functiondefault_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.
JuMP.Containers.@container
— Macro@container([i=..., j=..., ...], expr[, container = :Auto])
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[, container = :Auto])
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.
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(x[i = I, j = I], i + j);
julia> x
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
JuMP.Containers.VectorizedProductIterator
— Typestruct VectorizedProductIterator{T}
prod::Iterators.ProductIterator{T}
end
A wrapper type for Iterators.ProuctIterator
that discards shape information and returns a Vector
.
Construct a VectorizedProductIterator
using vectorized_product
.
JuMP.Containers.vectorized_product
— Functionvectorized_product(iterators...)
Created a VectorizedProductIterator
.
Examples
vectorized_product(1:2, ["A", "B"])
JuMP.Containers.NestedIterator
— Typestruct NestedIterator{T}
iterators::T # Tuple of functions
condition::Function
end
Iterators over the tuples that are produced by a nested for loop.
Construct a NestedIterator
using nested
.
Example
If length(iterators) == 3
:
x = NestedIterator(iterators, condition)
for (i1, i2, i3) in x
# produces (i1, i2, i3)
end
is the same as
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.nested
— Functionnested(iterators...; condition = (args...) -> true)
Create a NestedIterator
.
Example
nested(1:2, ["A", "B"]; condition = (i, j) -> isodd(i) || j == "B")
For advanced users, the following functions are provided to aid the writing of macros that use the container functionality.
JuMP.Containers.build_ref_sets
— Functionbuild_ref_sets(_error::Function, expr)
Helper function for macros to construct container objects.
This function is for advanced users implementing JuMP extensions. See container_code
for more details.
Arguments
_error
: a function that takes aString
and throws an error, potentially annotating the input string with extra information such as from which macro it was thrown from. Useerror
if you do not want a modified error message.expr
: anExpr
that specifies the container, e.g.,:(x[i = 1:3, [:red, :blue], k = S; i + k <= 6])
Returns
index_vars
: aVector{Any}
of names for the index variables, e.g.,[:i, gensym(), :k]
. These may also be expressions, like:((i, j))
from a call like:(x[(i, j) in S])
.indices
: an iterator over the indices, e.g.,Containers.NestedIterators( (1:3, [:red, :blue], S), (i, _, k) -> i + k <= 6, )
Examples
See container_code
for a worked example.
JuMP.Containers.container_code
— Functioncontainer_code(
index_vars::Vector{Any},
indices::Expr,
code,
requested_container::Union{Symbol,Expr},
)
Used in macros to construct a call to container
. This should be used in conjunction with build_ref_sets
.
Arguments
index_vars::Vector{Any}
: a vector of names for the indices of the container. These may also be expressions, like:((i, j))
from a call like:(x[(i, j) in S])
.indices::Expr
: an expression that evaluates to an iterator of the indices.code
: an expression or literal constant for the value to be stored in the container as a function of the namedindex_vars
.requested_container
: passed to the third argument ofcontainer
. For built-in JuMP types, choose one of:Array
,:DenseAxisArray
,:SparseAxisArray
, or:Auto
. For a user-defined container, this expression must evaluate to the correct type.
In most cases, you should esc(code)
before passing it to container_code
.
Examples
julia> macro foo(ref_sets, code)
index_vars, indices = Containers.build_ref_sets(error, ref_sets)
return Containers.container_code(
index_vars,
indices,
esc(code),
:Auto,
)
end
@foo (macro with 1 method)
julia> @foo(x[i=1:2, j=["A", "B"]], j^i)
2-dimensional DenseAxisArray{String,2,...} with index sets:
Dimension 1, Base.OneTo(2)
Dimension 2, ["A", "B"]
And data, a 2×2 Matrix{String}:
"A" "B"
"AA" "BB"