Search code examples
tuplesjuliaouter-product

How to Code Outer Product of a Set of Elements in Julia?


Permutations of a Set of Elements

I have a Julia snippet which generates all possible 4-tuples with 3 possible elements in each slot:

julia> collect(Iterators.product(ntuple(_ -> 1:3, 4)...))
3×3×3×3 Array{NTuple{4, Int64}, 4}:
[:, :, 1, 1] =
 (1, 1, 1, 1)  (1, 2, 1, 1)  (1, 3, 1, 1)
 (2, 1, 1, 1)  (2, 2, 1, 1)  (2, 3, 1, 1)
 (3, 1, 1, 1)  (3, 2, 1, 1)  (3, 3, 1, 1)

[:, :, 2, 1] =
 (1, 1, 2, 1)  (1, 2, 2, 1)  (1, 3, 2, 1)
 (2, 1, 2, 1)  (2, 2, 2, 1)  (2, 3, 2, 1)
 (3, 1, 2, 1)  (3, 2, 2, 1)  (3, 3, 2, 1)

[:, :, 3, 1] =
 (1, 1, 3, 1)  (1, 2, 3, 1)  (1, 3, 3, 1)
 (2, 1, 3, 1)  (2, 2, 3, 1)  (2, 3, 3, 1)
 (3, 1, 3, 1)  (3, 2, 3, 1)  (3, 3, 3, 1)

[:, :, 1, 2] =
 (1, 1, 1, 2)  (1, 2, 1, 2)  (1, 3, 1, 2)
 (2, 1, 1, 2)  (2, 2, 1, 2)  (2, 3, 1, 2)
 (3, 1, 1, 2)  (3, 2, 1, 2)  (3, 3, 1, 2)
...

Question

How do I modify this code so that instead of using a range 1:3 for the elements, I select elements out of an array, say [1,5], or a set Set([1,5]), with only 2 possibilities?


Solution

  • I think this is what you're looking for:

    julia> original = collect(Iterators.product(ntuple(_ -> [1,5], 4)...))
    2×2×2×2 Array{NTuple{4, Int64}, 4}:
    [:, :, 1, 1] =
     (1, 1, 1, 1)  (1, 5, 1, 1)
     (5, 1, 1, 1)  (5, 5, 1, 1)
    
    [:, :, 2, 1] =
     (1, 1, 5, 1)  (1, 5, 5, 1)
     (5, 1, 5, 1)  (5, 5, 5, 1)
    
    [:, :, 1, 2] =
     (1, 1, 1, 5)  (1, 5, 1, 5)
     (5, 1, 1, 5)  (5, 5, 1, 5)
    
    [:, :, 2, 2] =
     (1, 1, 5, 5)  (1, 5, 5, 5)
     (5, 1, 5, 5)  (5, 5, 5, 5)
    
    julia> flattened = [original...]
    16-element Vector{NTuple{4, Int64}}:
     (1, 1, 1, 1)
     (5, 1, 1, 1)
     (1, 5, 1, 1)
     (5, 5, 1, 1)
     (1, 1, 5, 1)
     (5, 1, 5, 1)
     (1, 5, 5, 1)
     (5, 5, 5, 1)
     (1, 1, 1, 5)
     (5, 1, 1, 5)
     (1, 5, 1, 5)
     (5, 5, 1, 5)
     (1, 1, 5, 5)
     (5, 1, 5, 5)
     (1, 5, 5, 5)
     (5, 5, 5, 5)
    

    So, it's possible to simply substitute an array or set for the iterator. Afterwards, you can flatten the resulting matrix with the splat operator.

    Additionally, here is how elements can be accessed in the form A[i,j] using list comprehension:

    julia> original  =  collect(Iterators.product(ntuple(_ -> 1:3, 4)...));
    julia> newMatrix = [original[:,:,a,b] for a in 1:3, b in 1:3];
    
    julia> newMatrix[2,3]
    3×3 Matrix{NTuple{4, Int64}}:
     (1, 1, 2, 3)  (1, 2, 2, 3)  (1, 3, 2, 3)
     (2, 1, 2, 3)  (2, 2, 2, 3)  (2, 3, 2, 3)
     (3, 1, 2, 3)  (3, 2, 2, 3)  (3, 3, 2, 3)
    
    julia> typeof(newMatrix)
    Matrix{Matrix{NTuple{4, Int64}}} (alias for Array{Array{NTuple{4, Int64}, 2}, 2})