Containers

More information can be found in the Containers section of the manual.

JuMP.ContainersModule
Containers

Module defining the containers DenseAxisArray and SparseAxisArray that behaves as a regular AbstractArray but with custom indexes that are not necessarily integers.

source
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.

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
source
JuMP.Containers.containerFunction
container(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
source
JuMP.Containers.rowtableFunction
rowtable([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 NamedTuples, 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].

Info

A Vector of NamedTuples 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])
source
JuMP.Containers.@containerMacro
@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.

Note

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
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.

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
source

For advanced users, the following functions are provided to aid the writing of macros that use the container functionality.

JuMP.Containers.build_ref_setsFunction
build_ref_sets(_error::Function, expr)

Helper function for macros to construct container objects.

Warning

This function is for advanced users implementing JuMP extensions. See container_code for more details.

Arguments

  • _error: a function that takes a String and throws an error, potentially annotating the input string with extra information such as from which macro it was thrown from. Use error if you do not want a modified error message.
  • expr: an Expr that specifies the container, e.g., :(x[i = 1:3, [:red, :blue], k = S; i + k <= 6])

Returns

  1. index_vars: a Vector{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]).
  2. 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.

source
JuMP.Containers.container_codeFunction
container_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 named index_vars.
  • requested_container: passed to the third argument of container. 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.
Warning

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"
source