# Using alternative array container types in JuMP

**URL:** https://discourse.julialang.org/t/using-alternative-array-container-types-in-jump/77707
**Category:** Optimization (Mathematical)
**Created:** [March 10, 2022, 5:40pm UTC](https://discourse.julialang.org/t/using-alternative-array-container-types-in-jump/77707 "2022-03-10T17:40:42Z")
**Posts on this page:** 6
**Page:** 1

<div class="post-metadata">

### Author: ![BSnelling](https://avatars.discourse-cdn.com/v4/letter/b/9d8465/32.png) [@BSnelling](https://discourse.julialang.org/u/BSnelling)
#### Post date: [March 10, 2022, 5:40pm UTC](https://discourse.julialang.org/t/using-alternative-array-container-types-in-jump/77707/1 "2022-03-10T17:40:42Z")

</div>

I am trying to force the container type used by a JuMP model. My example uses `KeyedArray` but I’ve seen a similar error when specifying other types from outside JuMP (e.g. `AxisArray`).

```julia
using JuMP, AxisKeys, GLPK
vals = ["A", "B", "C", "D"]
model = Model(GLPK.Optimizer)
@variable(model, x[v in vals], Bin, container=KeyedArray)

```

The error is:

```julia
ERROR: UndefVarError: KeyedArray not defined
Stacktrace:
 [1] macro expansion
   @ ~/.julia/packages/JuMP/zn6NT/src/macros.jl:142 [inlined]
 [2] top-level scope
   @ REPL[26]:1

```

I am wondering why `KeyedArray` is not recognised since `AxisKeys` has been loaded. I am using Julia 1.7 and JuMP 0.23.1.

---

<div class="post-metadata">

### Author: ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)
#### Post date: [March 10, 2022, 8:38pm UTC](https://discourse.julialang.org/t/using-alternative-array-container-types-in-jump/77707/2 "2022-03-10T20:38:13Z")

</div>

This exposed a bug in our container interface. Let me make a fix and I’ll get back to you with the syntax.

---

<div class="post-metadata">

### Author: ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)
#### Post date: [March 10, 2022, 8:56pm UTC](https://discourse.julialang.org/t/using-alternative-array-container-types-in-jump/77707/3 "2022-03-10T20:56:29Z")

</div>

Okay, this needs [https://github.com/jump-dev/JuMP.jl/pull/2916](https://github.com/jump-dev/JuMP.jl/pull/2916), which will be in the next JuMP release (but likely not today or in the next few days).

Once it lands, you’ll need to implement the `Containers.container` function to let JuMP know how to handle the new type. This can be a but tricky as there are a few different `indices` that can be passed. But a good first attempt is:

```julia
julia> using JuMP, AxisKeys

julia> function Containers.container(f::Function, indices, ::Type{AxisKeys.KeyedArray})
           set = collect(indices)
           data = [f(s...) for s in set]
           return AxisKeys.KeyedArray(data, set)
       end

julia> S = ["A", "B", "C", "D"]
4-element Vector{String}:
 "A"
 "B"
 "C"
 "D"

julia> model = Model();

julia> @variable(model, x[S], Bin, container = KeyedArray)
1-dimensional KeyedArray(...) with keys:
↓ 4-element Vector{Tuple{String}}
And data, 4-element Vector{VariableRef}:
  ("A",) x[A]
  ("B",) x[B]
  ("C",) x[C]
  ("D",) x[D]

```

Note that the keys are `x(("A",))`, but you could fix that in the `Containers.container` function.

In general though, this syntax isn’t intended for widespread usage or extension (which is why it had a bug and isn’t well documented.

A much better work-around is to go:

```julia
julia> model = Model();

julia> @variable(model, x[1:4], Bin)
4-element Vector{VariableRef}:
 x[1]
 x[2]
 x[3]
 x[4]

julia> X = KeyedArray(x, S)
1-dimensional KeyedArray(...) with keys:
↓ 4-element Vector{String}
And data, 4-element Vector{VariableRef}:
 ("A") x[1]
 ("B") x[2]
 ("C") x[3]
 ("D") x[4]

```

You should think of the built-in JuMP types as a good starting point. But there’s nothing stopping you from creating your own types outside JuMP without trying to interact with the complicated macro machinery.

---

<div class="post-metadata">

### Author: ![BSnelling](https://avatars.discourse-cdn.com/v4/letter/b/9d8465/32.png) [@BSnelling](https://discourse.julialang.org/u/BSnelling)
#### Post date: [March 11, 2022, 9:42am UTC](https://discourse.julialang.org/t/using-alternative-array-container-types-in-jump/77707/4 "2022-03-11T09:42:26Z")

</div>

I see, thank you for the explanation!

---

<div class="post-metadata">

### Author: ![raphaelsaavedra](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/raphaelsaavedra/32/27631_2.png) [@raphaelsaavedra](https://discourse.julialang.org/u/raphaelsaavedra)
#### Post date: [March 16, 2022, 3:28pm UTC](https://discourse.julialang.org/t/using-alternative-array-container-types-in-jump/77707/5 "2022-03-16T15:28:17Z")

</div>

Thanks for the explanation @odow, this is super helpful 🙂

Our use case involves a large model that is built via several functions which each add a part, something like

```julia
function build_model(data)
    m = Model(...)
    add_variables!(m, data)
    add_constraints!(m, data)
    add_objective!(m, data)
    return m
end

```

The downside of your suggested solution is that `X` (i.e., the `KeyedArray` wrapper for `x`) is not part of the model’s object dictionary, and therefore we can’t easily fetch `X` from `m` when analysing the results. One solution would be to create an extra variable/constraint for each one of them such as

```julia
X = model[:X] = KeyedArray(model[:x], indices=idx)

```

Which works fine, but requires us to have twice as many variables/constraints in the model and to keep track of which are the KeyedArray ones and which are not. Do you have any better suggestion for this case?

---

<div class="post-metadata">

### Author: ![odow](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/odow/32/28685_2.png) [@odow](https://discourse.julialang.org/u/odow)
#### Post date: [March 16, 2022, 8:03pm UTC](https://discourse.julialang.org/t/using-alternative-array-container-types-in-jump/77707/6 "2022-03-16T20:03:04Z")

</div>

This has been released in JuMP 0.23.2: [Extensions · JuMP](https://jump.dev/JuMP.jl/stable/developers/extensions/#Creating-new-container-types)  
I originally added this as a request from @oxinabox so I don’t think the “most users shouldn’t do this” applies to you.

If your keyed arrays are just vectors, then you can use the `Containers.container` function above. Otherwise, you’ll need to write something a bit more complicated.

> Which works fine, but requires us to have twice as many variables/constraints in the model and to keep track of which are the KeyedArray ones and which are not.

This is what I had in mind. Note that this isn’t creating twice as many decision variables. It’s just a new wrapper. You could also call `unregister(model, :x)` to get rid of the `:x` name, or you could just over-write it if you didn’t want to refer to it later.
