Документация Engee

API Containers

Страница в процессе перевода.

Containers

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

AutoContainerType

Pass AutoContainerType to container to let the container type be chosen based on the type of the indices using default_container.

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 = Containers.DenseAxisArray([1 2; 3 4], [:a, :b], 2:3)
2-dimensional DenseAxisArray{Int64,2,...} with index sets:
    Dimension 1, [:a, :b]
    Dimension 2, 2:3
And data, a 2×2 Matrix{Int64}:
 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 = Containers.DenseAxisArray{Float64}(undef, [:a, :b], 1:2);

julia> fill!(array, 1.0)
2-dimensional DenseAxisArray{Float64,2,...} with index sets:
    Dimension 1, [:a, :b]
    Dimension 2, 1:2
And data, a 2×2 Matrix{Float64}:
 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, [:a, :b]
    Dimension 2, 1:2
And data, a 2×2 Matrix{Float64}:
 1.0  5.0
 1.0  1.0
DenseAxisArrayKey

Structure to hold a DenseAxisArray key when it is viewed as key-value collection.

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

julia> iterators = (() -> 1:2, (i,) -> ["A", "B"]);

julia> condition = (i, j) -> isodd(i) || j == "B";

julia> x = Containers.NestedIterator(iterators, condition);

julia> for (i, j) in x
           println((i, j))
       end
(1, "A")
(1, "B")
(2, "B")

is the same as

julia> for i in iterators[1]()
           for j in iterators[2](i)
               if condition(i, j)
                   println((i, j))
               end
           end
       end
(1, "A")
(1, "B")
(2, "B")
struct NoDuplicateDict{K, V} <: AbstractDict{K, V}
    dict::OrderedCollections.OrderedDict{K, V}
end

Same as OrderedCollections.OrderedDict{K, V} but errors if constructed from an iterator with duplicate keys.

struct SparseAxisArray{T,N,K<:NTuple{N, Any}} <: AbstractArray{T,N}
    data::OrderedCollections.OrderedDict{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 as sa, even if f(zero(T)) is not zero.

Example

julia> using OrderedCollections: OrderedDict

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

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

julia> array[:b, 3]
3.0
struct 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.

_explicit_oneto(error_fn, index_set)

If the index_set matches the form of 1:N, then return Base.OneTo(index_set).

_expr_is_splat(expr)

Return true if expr is a ... expression (or an esc'd one).

_extract_kw_args(args)

This function is deprecated. Use parse_macro_arguments instead.

_get_arg(args::Tuple, index::Tuple)

Return a tuple corresponding to tuple([arg[index] for arg in args]...) in a type-stable way.

_parse_ref_sets(expr::Expr)

Helper function for macros to construct container objects.

Takes an Expr that specifies the container, for example, :(x[i=1:3,[:red,:blue],k=S; i+k <= 6]), and returns:

  1. index_vars: Names for the index variables, for example, [:i, gensym(), :k]. These may also be expressions, like :i, j from a call like :(x[(i, j) in S]).

  2. index_sets: Sets used for indexing, for example, [1:3, [:red,:blue], S]

  3. condition: Expr containing any conditional imposed on indexing, or :() if none is present

add_additional_args(
    call::Expr,
    args::Vector,
    kwargs::Dict{Symbol,Any};
    kwarg_exclude::Vector{Symbol} = Symbol[],
)

Add the positional arguments args to the function call expression call, escaping each argument expression.

This function is able to incorporate additional positional arguments to calls that already have keyword arguments.

build_error_fn(macro_name, args, source)

Return a function that can be used in place of Base.error, but which additionally prints the macro from which it was called.

build_name_expr(
    name::Union{Symbol,Nothing},
    index_vars::Vector,
    kwargs::Dict{Symbol,Any},
)

Returns an expression for the name of a container element, where name and index_vars are the values returned by parse_ref_sets and kwargs is the dictionary returned by parse_macro_arguments.

This assumes that the key in kwargs used to over-ride the name choice is :base_name.

Example

julia> Containers.build_name_expr(:x, [:i, :j], Dict{Symbol,Any}())
:(string("x", "[", string($(Expr(:escape, :i))), ",", string($(Expr(:escape, :j))), "]"))

julia> Containers.build_name_expr(nothing, [:i, :j], Dict{Symbol,Any}())
""

julia> Containers.build_name_expr(:y, [:i, :j], Dict{Symbol,Any}(:base_name => "y"))
:(string("y", "[", string($(Expr(:escape, :i))), ",", string($(Expr(:escape, :j))), "]"))
build_ref_sets(error_fn::Function, expr)

This function is deprecated. Use parse_ref_sets instead.

container(f::Function, indices[[, ::Type{C} = AutoContainerType], names])

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

If the method with names is not specialized on Type{C}, it falls back to calling container(f, indices, c) for backwards compatibility with containers not supporting index names.

Example

julia> Containers.container((i, j) -> i + j, Containers.vectorized_product(Base.OneTo(3), Base.OneTo(3)))
3×3 Matrix{Int64}:
 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 Matrix{Int64}:
 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 Matrix{Int64}:
 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, 1]  =  2
  [1, 2]  =  3
  [1, 3]  =  4
  [2, 3]  =  5
  [3, 3]  =  6
container_code(
    index_vars::Vector{Any},
    indices::Expr,
    code,
    requested_container::Union{Symbol,Expr,Dict{Symbol,Any}},
)

Used in macros to construct a call to container. This should be used in conjunction with parse_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. You may also pass the kwargs dictionary from parse_macro_arguments.

In most cases, you should esc(code) before passing it to container_code.

Example

julia> macro foo(ref_sets, code)
           name, index_vars, indices =
               Containers.parse_ref_sets(error, ref_sets)
           @assert name !== nothing  # Anonymous container not supported
           container =
               Containers.container_code(index_vars, indices, esc(code), :Auto)
           return quote
               $(esc(name)) = $container
           end
       end
@foo (macro with 1 method)

julia> @foo(x[i=1:2, j=["A", "B"]], j^i);

julia> x
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"
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 returns DenseAxisArray otherwise.

nested(iterators...; condition = (args...) -> true)

Create a NestedIterator.

Example

julia> iterator = Containers.nested(
           () -> 1:2,
           (i,) -> ["A", "B"];
           condition = (i, j) -> isodd(i) || j == "B",
       );

julia> collect(iterator)
3-element Vector{Tuple{Int64, String}}:
 (1, "A")
 (1, "B")
 (2, "B")
parse_macro_arguments(
    error_fn::Function,
    args;
    valid_kwargs::Union{Nothing,Vector{Symbol}} = nothing,
    num_positional_args::Union{Nothing,Int,UnitRange{Int}} = nothing,
)

Returns a Tuple{Vector{Any},Dict{Symbol,Any}} containing the ordered positional arguments and a dictionary mapping the keyword arguments.

This specially handles the distinction of @foo(key = value) and @foo(; key = value) in macros.

An error is thrown if multiple keyword arguments are passed with the same key.

If valid_kwargs is a Vector{Symbol}, an error is thrown if a keyword is not in valid_kwargs.

If num_positional_args is not nothing, an error is thrown if the number of positional arguments is not in num_positional_args.

parse_ref_sets(
    error_fn::Function,
    expr;
    invalid_index_variables::Vector{Symbol} = Symbol[],
)

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_fn: 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, for example, :(x[i = 1:3, [:red, :blue], k = S; i + k <= 6])

Returns

  1. name: the name of the container, if given, otherwise nothing

  2. index_vars: a Vector{Any} of names for the index variables, for example, [:i, gensym(), :k]. These may also be expressions, like :i, j from a call like :(x[(i, j) in S]).

  3. indices: an iterator over the indices, for example, Containers.NestedIterator

Example

See container_code for a worked example.

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

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::Int64, j::Int64, start::Float64}}:
 (i = 1, j = 1, start = 2.0)
 (i = 1, j = 2, start = 3.0)
 (i = 2, j = 2, start = 4.0)

julia> Containers.rowtable(x)
3-element Vector{@NamedTuple{x1::Int64, x2::Int64, y::VariableRef}}:
 (x1 = 1, x2 = 1, y = x[1,1])
 (x1 = 1, x2 = 2, y = x[1,2])
 (x1 = 2, x2 = 2, y = x[2,2])
vectorized_product(iterators...)

Example

julia> iterator = Containers.vectorized_product(1:2, ["A", "B"]);

julia> collect(iterator)
2×2 Matrix{Tuple{Int64, String}}:
 (1, "A")  (1, "B")
 (2, "A")  (2, "B")
@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.

Example

julia> Containers.@container([i = 1:3, j = 1:3], i + j)
3×3 Matrix{Int64}:
 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 Matrix{Int64}:
 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 Matrix{Int64}:
 3  4  5
 4  5  6

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