# Unexpected JuMP variable order when indexed by tuple

**URL:** <https://discourse.julialang.org/t/unexpected-jump-variable-order-when-indexed-by-tuple/110262>\
**Category:** Optimization (Mathematical)\
**Tags:** jump\
**Created:** [February 15, 2024, 4:05pm UTC](https://discourse.julialang.org/t/unexpected-jump-variable-order-when-indexed-by-tuple/110262 "2024-02-15T16:05:34Z")\
**Posts on this page:** 8\
**Page:** 1

<div class="post-metadata">

**Author:** ![brunompacheco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brunompacheco/32/209375_2.png) [@brunompacheco](https://discourse.julialang.org/u/brunompacheco)\
**Post date:** [February 15, 2024, 4:05pm UTC](https://discourse.julialang.org/t/unexpected-jump-variable-order-when-indexed-by-tuple/110262/1 "2024-02-15T16:05:34Z")

</div>

When creating a variable indexed by Tuple{Int64, Float64}, I get a different order (apparently, sorted as string) than that provided when creating or accessing the variable.

Steps to reproduce:

```julia
using JuMP

model = Model()

N = 1:2
M = [[1.0, 11.0, 100.0], [0.0, 17.0, 104.0]]

@variable(model, foo[n in N, M[n]])

foo[1, M[1]]

```

Current outcome:

```julia
JuMP.Containers.SparseAxisArray{VariableRef, 2, Tuple{Int64, Float64}} with 6 entries:
  [1, 1.0] = foo[1,1.0]
  [1, 100.0] = foo[1,100.0]
  [1, 11.0] = foo[1,11.0]
  [2, 0.0] = foo[2,0.0]
  [2, 104.0] = foo[2,104.0]
  [2, 17.0] = foo[2,17.0]

```

Expected outcome:

```julia
JuMP.Containers.SparseAxisArray{VariableRef, 2, Tuple{Int64, Float64}} with 6 entries:
  [1, 1.0] = foo[1,1.0]
  [1, 11.0] = foo[1,11.0]
  [1, 100.0] = foo[1,100.0]
  [2, 0.0] = foo[2,0.0]
  [2, 17.0] = foo[2,17.0]
  [2, 104.0] = foo[2,104.0]

```

I’m having a huge problem with SOS2 constraints, as something like

```julia
@constraint(model, C[n in N], foo[n, M[n]] in SOS2(M[n]))

```

enforces the constraint on a different order than that established by `M`, resulting in

```julia
 C[1] : [foo[1,11.0], foo[1,1.0], foo[1,100.0]] ∈ MathOptInterface.SOS2{Float64}([1.0, 11.0, 100.0])
 C[2] : [foo[2,0.0], foo[2,104.0], foo[2,17.0]] ∈ MathOptInterface.SOS2{Float64}([0.0, 17.0, 104.0])

```

Is that something expected? It seems very counter-intuitive for me.

EDIT: I’m using Julia 1.9.3 and JuMP v1.19.0.  
EDIT2: fix constraint, extend example wrt the SOS2 constraint.

---

<div class="post-metadata">

**Author:** ![abelsiqueira](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abelsiqueira/32/47269_2.png) [@abelsiqueira](https://discourse.julialang.org/u/abelsiqueira)\
**Post date:** [February 15, 2024, 4:39pm UTC](https://discourse.julialang.org/t/unexpected-jump-variable-order-when-indexed-by-tuple/110262/2 "2024-02-15T16:39:19Z")

</div>

Maybe you can use OrderedDict as the container for the variable?

---

<div class="post-metadata">

**Author:** ![abelsiqueira](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/abelsiqueira/32/47269_2.png) [@abelsiqueira](https://discourse.julialang.org/u/abelsiqueira)\
**Post date:** [February 15, 2024, 4:41pm UTC](https://discourse.julialang.org/t/unexpected-jump-variable-order-when-indexed-by-tuple/110262/3 "2024-02-15T16:41:38Z")

</div>

[Containers · JuMP](https://jump.dev/JuMP.jl/stable/manual/containers/#Forcing-the-container-type) is the link, I think (on the phone).

---

<div class="post-metadata">

**Author:** ![brunompacheco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brunompacheco/32/209375_2.png) [@brunompacheco](https://discourse.julialang.org/u/brunompacheco)\
**Post date:** [February 15, 2024, 4:48pm UTC](https://discourse.julialang.org/t/unexpected-jump-variable-order-when-indexed-by-tuple/110262/4 "2024-02-15T16:48:39Z")

</div>

Using a different container seems a good workaround, but OrderedDicts are not sliceable.

---

<div class="post-metadata">

**Author:** ![brunompacheco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brunompacheco/32/209375_2.png) [@brunompacheco](https://discourse.julialang.org/u/brunompacheco)\
**Post date:** [February 15, 2024, 4:57pm UTC](https://discourse.julialang.org/t/unexpected-jump-variable-order-when-indexed-by-tuple/110262/5 "2024-02-15T16:57:44Z")

</div>

My current workaround for the SOS2 problem is

```julia
@constraint(model, C[n in N], [foo[n, m] for m in M[n]] in SOS2(M[n]))

```

---

<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:** [February 15, 2024, 7:12pm UTC](https://discourse.julialang.org/t/unexpected-jump-variable-order-when-indexed-by-tuple/110262/6 "2024-02-15T19:12:13Z")

</div>

First, hi @brunompacheco, welcome to the forum 😄

> [@brunompacheco](#):
>
> `@constraint(model, foo[n in N, M[n]] in SOS2(M[n]))`

This isn’t valid JuMP syntax?

But anyway, your suggested workaround is the recommended syntax.

A `SparseAxisArray` is more a wrapper around a `Dict` than an `Array`. A better name might have been `SliceableNDimensionalDict`.

```julia
julia> foo
JuMP.Containers.SparseAxisArray{VariableRef, 2, Tuple{Int64, Float64}} with 4 entries:
  [1, 100.0] = foo[1,100.0]
  [1, 11.0] = foo[1,11.0]
  [2, 100.0] = foo[2,100.0]
  [2, 11.0] = foo[2,11.0]

julia> foo.data
Dict{Tuple{Int64, Float64}, VariableRef} with 4 entries:
  (1, 11.0) => foo[1,11.0]
  (1, 100.0) => foo[1,100.0]
  (2, 11.0) => foo[2,11.0]
  (2, 100.0) => foo[2,100.0]

```

This is a useful reminder that you do not need to be restricted to the build-in JuMP types: [Variables · JuMP](https://jump.dev/JuMP.jl/stable/manual/variables/#User-defined-containers)

Sometimes (often) other data structures will be more useful and appropriate.

---

<div class="post-metadata">

**Author:** ![brunompacheco](https://sea2.discourse-cdn.com/julialang/user_avatar/discourse.julialang.org/brunompacheco/32/209375_2.png) [@brunompacheco](https://discourse.julialang.org/u/brunompacheco)\
**Post date:** [February 15, 2024, 7:29pm UTC](https://discourse.julialang.org/t/unexpected-jump-variable-order-when-indexed-by-tuple/110262/7 "2024-02-15T19:29:26Z")

</div>

> [@odow](#):
>
> > [@brunompacheco](#):
> >
> > `@constraint(model, foo[n in N, M[n]] in SOS2(M[n]))`
> 
> This isn’t valid JuMP syntax?

Thanks! I have just fixed the constraint expression and extended the example, just in case others face the same problem.

I still find it quite counter-intuitive that my workaround is necessary, as the [example in JuMP docs](https://jump.dev/JuMP.jl/stable/manual/constraints/#Special-Ordered-Sets-of-Type-2) made it seem to me that the variable’s index order would be preserved.

> [@odow](#):
>
> Sometimes (often) other data structures will be more useful and appropriate.

Do you suggest other container structure that would be more appropriate in 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:** [February 15, 2024, 7:39pm UTC](https://discourse.julialang.org/t/unexpected-jump-variable-order-when-indexed-by-tuple/110262/8 "2024-02-15T19:39:34Z")

</div>

> it seem to me that the variable’s index order would be preserved.

In that case, the `x` is a `Vector`. Perhaps we could throw an error (or a warning) if we try to convert a `SparseAxisArray` into a `Vector` as part of the constraints.

> Do you suggest other container structure that would be more appropriate in this case?

The `SparseAxisArray` doesn’t really model what you need. Would you ever slice in the other dimension `foo[:, 11.0]`?

You really have a vector of vectors:

```julia
using JuMP
model = Model()
N = 1:2
M = [[1.0, 11.0, 100.0], [0.0, 17.0, 104.0]]
foo = [@variable(model, [M[n]], base_name = "foo[$n]") for n in 1:2]
@constraint(model, [n in N], foo[n] in SOS2(M[n]))

```

p.s. I opened a bug report: [Slicing SparseAxisArray does not preserve order · Issue #3678 · jump-dev/JuMP.jl · GitHub](https://github.com/jump-dev/JuMP.jl/issues/3678)
