Julia: create specific index variables in Base.Cartesian
-
23-12-2019 - |
Question
I am using Base.Cartesian for the first time and have found it to be very powerful. However, it seems to generate code which uses all available indeces, whereas I have a case where I would like to only use a few.
In short
If I am inside @nloops
but I only want to use a subset of the indexing variables, is it possible to generate expressions for those indexing variables using scalars corresponding to their subscript indeces?
a,b -> i_{a}, i_{b} where we have i_{1:N}
Long form
Specifically, I have N objects, each which can take on some number of values:
Obj1: m11, m12, m13
Obj2: m21, m22, m23, m24
Obj3: m31, m32
which corresponds to:
i_1 = 1 : 3
i_2 = 1 : 4
i_3 = 1 : 2
I am generating each combination using @nloops:
{m11,m21,m31}{m12,m21,m31}{m13,m21,m31}{m11,m22,m31}{m12,m22,m31}...
I then run through pairwise enumerations for each combination:
comb = {m11,m21,m31} -> pairs = {m11,m21}{m11,m31}{m21,m31}
I would like to use these pairs to index into a matrix. This is where I am getting tripped up, since I need to generate a varying expression only using a subset of the indexing variables.
a,b = pair[1],pair[2]
foo = max(foo, mat[i_{a}, i_{b}]) # need to figure out appropriate syntax here
Here is a cleaned up demonstrative example: (3 is used as N, I'll use @ngenerate
later)
@nloops 3 i d->1:lens[d] begin
foo = 0
for pair in combinations([1:3],2)
a, b = pair[1], pair[2]
M_ind = pair2ind(ia,ib)
mat = M[M_ind]
foo = max(foo, mat[i_{a}, i_{b}]) # need to figure this out
if foo==1 # early exit
break
end
end
# do something with foo...
end
Is it possible to do what I am trying to do?
I tried using @eval
, but I couldn't get it to compile:
@eval(parse(@sprintf("foo = max(foo, mat[i_%d, i_%d])", a, b)))
Error: a not defined
Thank you.
Solution
The problem was solved by avoiding it altogether. A separate version using recursion was used.
N = number of objects
M = array containing number of states
function next_combination!( comb )
i = findfirst(i->comb[i] < M[i], 1:N)
@assert(i != 0)
comb[i] += 1
comb[1:i-1] = 1
return comb
end
ncombs = prod(M)
comb = int([0, ones(N-1)])
for ci = 1 : ncombs
next_combination!(comb)
foo = 0
for pair in combinations([1:N],2)
a, b = pair[1], pair[2]
col_ind = pair2ind(a,b)
foo = max(foo, mymat[col_ind][comb[a], comb[b]])
if foo == 1 # early out
break
end
end
# do something with foo...
end
Modifying the Existing Method
As DSM pointed out, the following works:
inds = @tuple 3 i
foo = max(foo, mymat[col_ind][inds[a], inds[b]])