Search code examples
arraysjuliavectorizationtensorarray-broadcasting

How to broadcast the outer sum of elements of two vector of vectors?


I have two Matrix{Vector{}}, say A and B, of the same sizes. I want to get a Matrix{Matrix{}}, M, such that each element is an outer sum of each element of A and B, i.e.

M[i, j][k, l] = A[i, j][k] + B[i, j][l].

How could I vectorize the above operation?

Example case:

A=[[[1, 2]] [[3, 4]] ; [[5, 6]] [[7, 8]]]
2×2 Matrix{Vector{Int64}}:
 [1, 2]  [3, 4]
 [5, 6]  [7, 8]
B=[[[11, 21]] [[13, 44]] ; [[25, 61]] [[27, 28]]]
2×2 Matrix{Vector{Int64}}:
 [11, 21]  [13, 44]
 [25, 61]  [27, 28]
size(A)==size(B)
true
M=[A[i, j] .+ B[i, j]' for i in 1:size(A)[1], j in 1:size(A)[2]]
2×2 Matrix{Matrix{Int64}}:
 [12 22; 13 23]  [16 47; 17 48]
 [30 66; 31 67]  [34 35; 35 36]

I would love to find a way for avoiding the for loop, since in my use case, A and B are huge matrices! I also tried A .+ adjoint.(B) but this throws an error message:

ERROR: DimensionMismatch: dimensions must match: a has dims (Base.OneTo(1), Base.OneTo(2)), b has dims (Base.OneTo(2),), mismatch at 1.

I found a workaround using the TensorCast package which looks something like

@cast MCast[i, j][k, l] := A[i, j][k] + B[i, j][l];

julia> MCast = collect(MCast);

julia> MCast
2×2 Matrix{SubArray{Int64, 2, Array{Int64, 4}, Tuple{Base.Slice{Base.OneTo{Int64}}, Base.Slice{Base.OneTo{Int64}}, Int64, Int64}, true}}:
 [12 22; 13 23]  [16 47; 17 48]
 [30 66; 31 67]  [34 35; 35 36]

This does work, but I was wondering if there exists a more elegant way of doing this, preferably in native Julia.


Solution

  • julia> map(.+, A, adjoint.(B))
    2×2 Matrix{Matrix{Int64}}:
     [12 22; 13 23]  [16 47; 17 48]
     [30 66; 31 67]  [34 35; 35 36]
    

    is what I got so far.

    Slight improvement:

    using MappedArrays
    
    map(.+, A, mappedarray(adjoint,B))
    

    (does not materialize adjoint.(B))