Preferably there would be one way of performing this indexing operation, that works for both sparse and dense arrays
In this case there isn’t really, because you are doing the additional operation that , m
. The keys of X
are not compatible with the keys of Xdata
.
Note that SparseAxisArrays
are backed by a Dict
, and DenseAxisArray
s are backed by an Array
. We’ve tried to paper over the differences, but they are fundamentally different objects.
This shows up in a bunch of places, for example:
julia> axes(dense_data)
(["a", "b", "c"], ["a", "b", "c"], Base.OneTo(12))
julia> axes(sparse_data)
ERROR: `Base.size` is not implemented for `SparseAxisArray` because although it is a subtype of `AbstractArray`, it is conceptually closer to a dictionary with `N`-dimensional keys. If you encounter this error and you didn't call `size` explicitly, it is because you called a method that is unsupported for `SparseAxisArray`s. Consult the JuMP documentation for a list of supported operations.
Stacktrace:
[...]
If the arrays have compatible dimensions, one approach is to use broadcasting and let JuMP figure out the details:
julia> using JuMP
julia> begin
abc = ["a", "b", "c"]
sparse_model = Model()
@expression(sparse_model, sparse_data[i in abc, j in abc, m in 1:12; i == j], 1.0)
@variable(sparse_model, sparse_x[i in abc, j in abc; i == j], start = 0.0)
dense_model = Model()
@expression(dense_model, dense_data[i in abc, j in abc, m in 1:12], 1.0)
@variable(dense_model, dense_x[i in abc, j in abc], start = 0.0)
end
2-dimensional DenseAxisArray{VariableRef,2,...} with index sets:
Dimension 1, ["a", "b", "c"]
Dimension 2, ["a", "b", "c"]
And data, a 3×3 Matrix{VariableRef}:
dense_x[a,a] dense_x[a,b] dense_x[a,c]
dense_x[b,a] dense_x[b,b] dense_x[b,c]
dense_x[c,a] dense_x[c,b] dense_x[c,c]
julia> m = 1
1
julia> fix.(sparse_x, sparse_data[:, :, m])
JuMP.Containers.SparseAxisArray{Nothing, 2, Tuple{String, String}} with 3 entries:
[a, a] = nothing
[b, b] = nothing
[c, c] = nothing
julia> fix.(dense_x, dense_data[:, :, m])
2-dimensional DenseAxisArray{Nothing,2,...} with index sets:
Dimension 1, ["a", "b", "c"]
Dimension 2, ["a", "b", "c"]
And data, a 3×3 Matrix{Nothing}:
nothing nothing nothing
nothing nothing nothing
nothing nothing nothing
julia> print(sparse_model)
Feasibility
Subject to
sparse_x[a,a] = 1
sparse_x[b,b] = 1
sparse_x[c,c] = 1
julia> print(dense_model)
Feasibility
Subject to
dense_x[a,a] = 1
dense_x[b,a] = 1
dense_x[c,a] = 1
dense_x[a,b] = 1
dense_x[b,b] = 1
dense_x[c,b] = 1
dense_x[a,c] = 1
dense_x[b,c] = 1
dense_x[c,c] = 1
Another approach would be to use Julia’s multiple dispatch:
function my_fix(
x::JuMP.Containers.SparseAxisArray,
data::JuMP.Containers.SparseAxisArray,
m,
)
for key in eachindex(x)
fix(x[key], data[key..., m])
end
return
end
function my_fix(
x::JuMP.Containers.DenseAxisArray,
data::JuMP.Containers.DenseAxisArray,
m,
)
for i in axes(x, 1), j in axes(x, 2)
fix(x[i, j], data[i, j, m])
end
return
end
m = 1
my_fix(sparse_x, sparse_data, m)
my_fix(dense_x, dense_data, m)
But I would encourage you to think about different representations of the data that do not involve SparseAxisArray
julia> using JuMP
julia> begin
abc = ["a", "b", "c"]
pairs = [(i, j) for i in abc, j in abc if i == j]
sparse_model = Model()
@expression(sparse_model, sparse_data[pairs, 1:12], 1.0)
@variable(sparse_model, sparse_x[pairs], start = 0.0)
for ij in pairs
fix(sparse_x[ij], sparse_data[ij, m])
end
print(sparse_model)
end
Feasibility
Subject to
sparse_x[("a", "a")] = 1
sparse_x[("b", "b")] = 1
sparse_x[("c", "c")] = 1
julia> begin
pairs = [(i, j) for i in abc, j in abc]
dense_model = Model()
@expression(dense_model, dense_data[pairs, m in 1:12], 1.0)
@variable(dense_model, dense_x[pairs], start = 0.0)
for ij in pairs
fix(dense_x[ij], dense_data[ij, m])
end
print(dense_model)
end
Feasibility
Subject to
dense_x[("a", "a")] = 1
dense_x[("b", "a")] = 1
dense_x[("c", "a")] = 1
dense_x[("a", "b")] = 1
dense_x[("b", "b")] = 1
dense_x[("c", "b")] = 1
dense_x[("a", "c")] = 1
dense_x[("b", "c")] = 1
dense_x[("c", "c")] = 1
Since you’re likely coming from GAMS, I’ll point you to:
The biggest lesson is that you do not need to use JuMP’s built-in data structures. They’re made to be convenient, but they may not be the best choice for your problem.