@nloops with lower triangular indices

I am trying to create N nested loops that create indices, up to and including the previous index. I am clearly bad at Symbols and macros and struggling to make up the index expression that @nloops. My attempt here clearly fails:

@nloops 3 i d -> (d < 3) ? range(1,Symbol("i_",d+1)) : (1:3) begin display(@ntuple 2 i) end

Any help is welcome. Thanks!

A slight improvement:

@nloops 3 i d -> (d < 3) ? range(1,eval(Symbol("i_",d+1))) : (1:3) begin display(@ntuple 3 i) end

Now the problem is that the generated inner loops do not have access to the indices in the outer loops. Strange.

The error:

ERROR: UndefVarError: `i_2` not defined
Stacktrace:
 [1] top-level scope
   @ :0
 [2] eval
   @ ./boot.jl:385 [inlined]
 [3] eval
   @ ./client.jl:491 [inlined]
 [4] macro expansion
   @ ./cartesian.jl:62 [inlined]
 [5] top-level scope
   @ ./REPL[158]:1

I’m not sure whether it’s possible to do what you want with @nloops. Those macros are somewhat limited in their capability. I’ve seldom found them actually useful.

I typically find CartesianIndices to be more powerful for nested loop ranges. For example,

julia> ci = CartesianIndices(ntuple(Returns(1:3),3))
CartesianIndices((1:3, 1:3, 1:3))

julia> for c in Iterators.filter(c -> issorted(Tuple(c);rev=true), ci); display(Tuple(c)); end
(1, 1, 1)
(2, 1, 1)
(3, 1, 1)
(2, 2, 1)
(3, 2, 1)
(3, 3, 1)
(2, 2, 2)
(3, 2, 2)
(3, 3, 2)
(3, 3, 3)

Equivalently, you could drop the filter and instead check the issorted condition (and continue if it’s violated).

However, (unless the compiler is quite clever, which I’m doubting here) this will still loop over every possible index in order to throw many out (not a big deal in 2-3 dimensions but becomes worse with more).

Another option is to construct a custom iterator that more efficiently visits the lower triangle using the iteration interface.

1 Like

Thanks, I was contemplating going through all indices with CartesianIndices and it might be compromise solution in the end. I’ll wait a bit more, there might be a direct solution…

The solution is quite simple. The trick is to use LaTeX style syntax as in the documentation of @nloops:

@nloops(3, i, d -> (d < 3) ? range(1,i_{d+1}) : (1:3), begin display(@ntuple 3 i) end)

1 Like