Julia JuMP - 如何将变量与 CartesianIndex 一起使用?

Julia JuMP - how to use variable with CartesianIndex?

为了减小我的变量的大小,我想用 CartesianIndex 定义一个 JuMP 模型。

我尝试了以下代码,但在使用变量时处理 CartesianIndex 时总是遇到问题:

using JuMP, Cbc, IterTools

Random.seed!(3)
N = 10
cost = rand(1:4, N, N)

# reducing size using an index
idx = findall(cost .< 2)
# size reduce from 100 to 22 variables

model = Model(with_optimizer(Cbc.Optimizer));
@variable(model, x[idx], Bin);

# constraint to have only sum(x) = 1 over each row
for i in 1:N
  # creating a second CartesianIndex to select row i, then intersect with the 22 indices of x
  cart_idx = CartesianIndex.(collect(product(i,1:N)))
  cart_idx = intersect(cart_idx, idx)
  
  @constraint(model, sum(x[cart_idx]) == 1)
  # ERROR: KeyError: key CartesianIndex{2}[CartesianIndex(1, 5), CartesianIndex(1, 6)] not found
  
end

循环中最后一行代码抛出错误:

ERROR: KeyError: key CartesianIndex{2}[CartesianIndex(1, 5), CartesianIndex(1, 6)] not found

是否可以将 CartesianIndex 与 JuMP 变量一起使用,以及如何解决上述问题?

我找到了一个解决方法,尽管我不确定这是最好的编写方法。 我没有使用带有 CartesianIndex 的变量,而是使用一维索引来引用 CartesianIndex 的元素。请参阅下面 x 的定义。

using JuMP, Cbc, IterTools

Random.seed!(3)
N = 10
cost = rand(1:4, N, N)

# reducing size using an index
idx = findall(cost .< 2)
# size reduce from 100 to 22 variables

K = length(idx)

model = Model(with_optimizer(Cbc.Optimizer));
@variable(model, x[1:K], Bin);



# constraint to have only sum(x) = 1 over each row and each column
for i in 1:N
  cart_idx = CartesianIndex.(collect(product(i,1:N)))
  cart_idx = intersect(cart_idx, idx)
  positions = findall([idx[i] in cart_idx for i in 1:K])
  
  @constraint(model, sum(x[positions]) == 1)
end

如果您尝试使用 CartesianIndices 构造 DenseAxisArray,下一版本的 JuMP 将抛出错误。它们并非用于该目的。

您应该使用替代方法:

选项 1

using JuMP, IterTools
Random.seed!(3)
N = 10
cost = rand(1:4, N, N)
model = Model()
@variable(model, x[i=1:N, j=1:N; cost[i, j] < 2], Bin)
@constraint(
    model, 
    [ii=1:N], 
    sum(x[i, j] for (i, j) in eachindex(x) if ii == i) == 1,
)

选项 2

using JuMP, IterTools
Random.seed!(3)
N = 10
cost = rand(1:4, N, N)
S = [(i, j) for i in 1:N, j in 1:N if cost[i, j] < 2]
model = Model()
@variable(model, x[S], Bin)
@constraint(model, [ii=1:N],  sum(x[i, j] for (i, j) in S if ii == i) == 1)

之前post:

我认为 JuMP 中存在一些与笛卡尔索引相关的错误:

julia> model = Model()
A JuMP Model
Feasibility problem with:
Variables: 0
Model mode: AUTOMATIC
CachingOptimizer state: NO_OPTIMIZER
Solver name: No optimizer attached.

julia> S = CartesianIndex.([2, 4])
2-element Vector{CartesianIndex{1}}:
 CartesianIndex(2,)
 CartesianIndex(4,)

julia> @variable(model, x[S])
1-dimensional DenseAxisArray{VariableRef,1,...} with index sets:
    Dimension 1, CartesianIndex{1}[CartesianIndex(2,), CartesianIndex(4,)]
And data, a 2-element Vector{VariableRef}:
 x[CartesianIndex(2,)]
 x[CartesianIndex(4,)]

julia> x[CartesianIndex(2,)]
x[CartesianIndex(4,)]

julia> x[CartesianIndex(1,)]
x[CartesianIndex(2,)]

julia> x[CartesianIndex(4,)]
ERROR: BoundsError: attempt to access 2-element Vector{VariableRef} at index [4]
Stacktrace:
 [1] getindex
   @ ./array.jl:801 [inlined]
 [2] getindex
   @ ./multidimensional.jl:637 [inlined]
 [3] getindex(A::JuMP.Containers.DenseAxisArray{VariableRef, 1, Tuple{Vector{CartesianIndex{1}}}, Tuple{JuMP.Containers._AxisLookup{Dict{CartesianIndex{1}, Int64}}}}, idx::CartesianIndex{1})
   @ JuMP.Containers ~/.julia/packages/JuMP/2IF9U/src/Containers/DenseAxisArray.jl:323
 [4] top-level scope
   @ REPL[42]:1

似乎如果我们有笛卡尔索引作为键,我们应该通过键查找它们,而不是通过典型的笛卡尔索引。

编辑:我打开了一个问题 https://github.com/jump-dev/JuMP.jl/issues/2825