Loops for unknown numbers of inputs

Hello!
Now I am trying to make functions to form a matrix which can traverse all the values of input. Can you help with that?
Suppose that we have three arrays:

as = [1,2]
bs = [3,4, 5]
cs = [6]

Then I want to build a matrix that includes all the possible combinations

nloop = length(as) * length(bs) * length(cs)
num_set = zeros(nloop, 3)
loop_num = 1
 for (ia, va) in enumerate(as), (ib, vb) in enumerate(bs), (ic, vc) in enumerate(cs)
num_set[loop_num, :] .= [va,vb,vc]
global loop_num += 1
end

then output should be:

6×3 Matrix{Float64}:
 1.0  3.0  6.0
 2.0  3.0  6.0
 1.0  4.0  6.0
 2.0  4.0  6.0
 1.0  5.0  6.0
 2.0  5.0  6.0

What if I have few inputs of sets, for example:

as = [1,2]
bs = [3,4, 5]
cs = [6]
ds = [7, 8, 9]

How can I create a function to form a few loops according to the number of number sets? Many thanks in advance!

I am surprised that Combinatorics.jl doesn’t have this functionality already?

Maybe Iterators.product can help you out (also see zip)

Thank you! I would check Iterators.product .

Iterators.product can solve my problem! But how can I transform the Array to Matrix{Float64}?

This is most definitely not the most efficient way, but this matches your original output:

mapreduce(collect,hcat,Iterators.product(as,bs,cs))'
1 Like

It is more efficient and natural to turn it into a 3 x nloops matrix instead of an nloops x 3 matrix, since Julia arrays are column major and the tuples are adjacent in memory. In that case:

julia> reshape(reinterpret(Int, collect(Iterators.product(as, bs, cs))), 3, :)
3×6 reshape(reinterpret(Int64, ::Array{Tuple{Int64, Int64, Int64}, 3}), 3, 6) with eltype Int64:
 1  2  1  2  1  2
 3  3  4  4  5  5
 6  6  6  6  6  6

(On my laptop that’s >6x as fast as the mapreduce.)

1 Like

Thank you! really helpful!

Thank you!

just for fun

function hmprod(v...)
    prod=[]
    for a in v[1], b in v[2]
        push!(prod, [a...,b])
        end
        prod
    end

hcat(reduce(hmprod, ads)...)
1 Like

Thank you! I am wondering how for a in v[1], b in v[2] can work for unknown numbers of inputs. Seems I should read more about .... BTW, your code is more friendly for mixed types of input arrays. Thank you all!

I’m not sure I fully understand your doubt, but the script, which is certainly not as efficient as the other proposals, should work for a generic number of input vectors.

julia> as = [1,2]
2-element Vector{Int64}:
 1
 2

julia> bs = (3,4, 5)
(3, 4, 5)

julia> cs = [6]
1-element Vector{Int64}:
 6

julia> ds = [7, 8, 9]
3-element Vector{Int64}:
 7
 8
 9

julia> ads=[as,bs,cs,ds]
4-element Vector{Any}:
 [1, 2]
 (3, 4, 5)
 [6]
 [7, 8, 9]

julia> reduce(hmprod, ads)
18-element Vector{Any}:
 (1, 3, 6, 7)
 (1, 3, 6, 8)
 (1, 3, 6, 9)
 (1, 4, 6, 7)
 (1, 4, 6, 8)
 (1, 4, 6, 9)
 (1, 5, 6, 7)
 (1, 5, 6, 8)
 (1, 5, 6, 9)
 (2, 3, 6, 7)
 (2, 3, 6, 8)
 (2, 3, 6, 9)
 (2, 4, 6, 7)
 (2, 4, 6, 8)
 (2, 4, 6, 9)
 (2, 5, 6, 7)
 (2, 5, 6, 8)
 (2, 5, 6, 9)

julia> function hmprod(v...)
           p=[]
           i=1
           for a in v[1], b in v[2]
               push!(p, (a...,b))
               i+=1
           end
               p
           end
hmprod (generic function with 1 method)

I add a recursive version, which does not use functions of either Base or other packages.

julia> function rhmprod(v...)
           p=Tuple[]
           i=1
           if length(v)>=2
               for a in v[1], b in v[2]
                   push!(p, (a...,b))
                   i+=1
               end
               rhmprod(p,v[3:end]...)
           else
               v[1]
           end
       end
rhmprod (generic function with 1 method)

julia> rhmprod(ads...)
18-element Vector{Tuple}:
 (1, 3, 6, 7)
 (1, 3, 6, 8)
 (1, 3, 6, 9)
 (1, 4, 6, 7)
 (1, 4, 6, 8)
 (1, 4, 6, 9)
 (1, 5, 6, 7)
 (1, 5, 6, 8)
 (1, 5, 6, 9)
 (2, 3, 6, 7)
 (2, 3, 6, 8)
 (2, 3, 6, 9)
 (2, 4, 6, 7)
 (2, 4, 6, 8)
 (2, 4, 6, 9)
 (2, 5, 6, 7)
 (2, 5, 6, 8)
 (2, 5, 6, 9)
1 Like

Thank you! I can understand your code now. but why we need the i to count the loop?

It does not have anything to do with it.
It’s just the remnant of a “refactoring” :blush:.

PS
consider that the solution based on the use of the product function is much, much better performing.

1 Like