How to use elements of a N*M elements Vector of Vector for a constraint

Hi,

We have two sets A = [1,2,...,N] and B = [1,2,...,M]. There are two other sets SA, SB associated with A and Bwhich are N-elements and M-elemets vector of vector.

A = [1:N]
B = [1:M]

SA =  [ [some stuff for 1] , [some stuff for 2] ,..., [[some stuff for N] ]  
SB =  [ [some stuff for 1] , [some stuff for 2] ,..., [[some stuff for M] ]  

I’m interested in the intersection of things between SA and SB to later use them for a constraint.
Therefore I did the following:

AB  =  [(a,b) for a in A for b in B]
SAB = [ intersect( SA[a], SB[b]) for (a,b) in AB]

This outputs N*M elements Vector of Vector. Until this point things are fine. Now I’d like to add a constrains to model that says:

sum( a in A , y[i, a, b]) <=  U*x[i,j,b]    for all b in B and i in SAB and j in SAB # where U is a constant

Therefore I translate it like below:

@constraint(model, [(a,b) in AB, i in SAB[a,b], j in SAB[a,b]], sum( y[i,a,b] for a in A) <= U * x[i,j,b]) 

But it return error like KeyError: key (this, this, this) not found. Next time I tried this

@constraint(model, [(b in B, i in SAB[a,b], j in SAB[a,b]], sum( y[i,a,b] for a in A) <= U * x[i,j,b]) 

Altough I knew it’s incorrect, but the only issue is that I cannot say i in SAB[a,b] when I only use [(b in B, i in SAB[a,b], j in SAB[a,b]] Right??

Basically, how we could do this? Sum over b while using elements of sahred vector like SAB?

You could instead try to collect the constraint indices into a separate vector first, and just work around this:

julia> constraint_index = Any[]
Any[]

julia> for (a,b) in AB, i in SAB[a,b], j in SAB[a,b]; push!(constraint_index, (;a,b,i,j)); end

julia> constraint_index
4-element Vector{Any}:
 (a = 1, b = 4, i = 1, j = 1)
 (a = 2, b = 1, i = 2, j = 2)
 (a = 2, b = 3, i = 2, j = 2)
 (a = 3, b = 4, i = 4, j = 4)

If you don’t use the macro to parse the constraint indices, then using such an array of tuples seems like it’s easier:

julia> @constraint(model, c[(;a,b,i,j) in constraint_index], sum(y[i,a,b] for a in A) <= x[i,j,b])
1-dimensional DenseAxisArray{ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.LessThan{Float64}}, ScalarShape},1,...} with index sets:
    Dimension 1, Any[(a = 1, b = 4, i = 1, j = 1), (a = 2, b = 1, i = 2, j = 2), (a = 2, b = 3, i = 2, j = 2), (a = 3, b = 4, i = 4, j = 4)]
And data, a 4-element Vector{ConstraintRef{Model, MathOptInterface.ConstraintIndex{MathOptInterface.ScalarAffineFunction{Float64}, MathOptInterface.LessThan{Float64}}, ScalarShape}}:
 c[(a = 1, b = 4, i = 1, j = 1)] : y[1,1,4] + y[1,2,4] + y[1,3,4] - x[1,1,4] ≤ 0.0
 c[(a = 2, b = 1, i = 2, j = 2)] : y[2,1,1] + y[2,2,1] + y[2,3,1] - x[2,2,1] ≤ 0.0
 c[(a = 2, b = 3, i = 2, j = 2)] : y[2,1,3] + y[2,2,3] + y[2,3,3] - x[2,2,3] ≤ 0.0
 c[(a = 3, b = 4, i = 4, j = 4)] : y[4,1,4] + y[4,2,4] + y[4,3,4] - x[4,4,4] ≤ 0.0

julia> c[(a=1,b=4,i=1,j=1)]
c[(a = 1, b = 4, i = 1, j = 1)] : y[1,1,4] + y[1,2,4] + y[1,3,4] - x[1,1,4] ≤ 0.0

You can index into the vector of constraints by arbitrary tuples this way.

(I used some arbitrary arrays A,B,etc., I hope I understood your example.)

I’m really not sure that the macro is even intended to parse complex for-loops like that in the index set, I think it’s just trying to figure out the dimensions of an array and failing.

2 Likes

It’s hard to tell what’s going wrong without a reproducible example. For example, it’s not obvious what SAB[a, b] is. Isn’t SAB a vector? Do you mean SAB = Dict((a, b) => intersect( SA[a], SB[b]) for (a,b) in AB) and then SAB[(a, b)]?

But @ikirill’s suggestion of collating the indices is a good one. Don’t feel you need to be constrained by the helper syntax that JuMP provides. It’s a helper, but there are other ways if things are more complicated.

1 Like

Thanks @ikirill this is very fresh to me. Nice to learn that. I did try it but I still have key error. There’s something wrong that is very uncelar why!! But thanks a lot. It is very inetersting.

I guess, the issue is realted to the sum part (sum(y[i,a,b] for a in A). When I ran the exact things as yours. It says: KeyError: key (661, 15, 14) not found. which is one of the combinations of (i,a,b). I mean it seems when summing over a in A there are some (i,a,b) which did not form by constraint_index

Thank @odow. Yes. They are like this

AB = [(a,b) for a in A for b in B]
sAB = [intersect(SA[a], SB[b]) for (a,b) in AB)]
SAB = Dict(  AB .=> sAB)

The question is harder to understand fully because you haven’t stated which indexing you’ve defined for x and y.

There are two scopes happening here, where the index a appears in two places: first, in (a,b) in AB and then inside the sum( ... for a in A). I would say that you probably want to write this as

@constraint(model, 
  [(a,b) in AB, i in SAB[a,b], j in SAB[a,b]], 
  sum( y[i,aa,b] for aa in A if aa == a) <= U * x[i,j,b]
)

A full minimal working example to test might look like this; you would help a lot by creating a similar one for future discussions:

using JuMP
model = JuMP.Model()

A = 1:4
B = 1:3
AB  =  [(a,b) for a in A for b in B]

SA = Dict([i => 10*i:10*(i+1) for i in A])
SB = Dict([i => [10*(i-1), 10*(i+2)] for i in B])
SAB = Dict((a, b) => intersect( SA[a], SB[b]) for (a,b) in AB)

SAB_vals = union(values(SAB)...)

@variable(model, x[i=SAB_vals, j=SAB_vals, b=B])
@variable(model, y[i=SAB_vals, a=A, b=B])

U = 7

@constraint(model, 
  [(a,b) in AB, i in SAB[a,b], j in SAB[a,b]], 
  sum( y[i,aa,b] for aa in A if aa == a) <= U * x[i,j,b]
)
2 Likes