Multiplying a 3d array by a scalar?

I have an array A of size M,N,P, and a vector v of size P. And I want to multiply each “plane” A[:,:,i] of the array by v[i]. I can do this in a loop, or with two calls to reshape. But is there any way of doing it directly?

If the “P” dimension was first; that is, if the array had size P,M,N, then I could:

v' .* A

I’m just wondering if there’s something similar for when the “P” dimension is last; that is, a scalar multiplication in which you can choose the dimension of the array to be used, Thanks!

I don’t know if you can, but an explicit loop is probably (IMHO) the best choice because it should be both efficient and readable.

1 Like

Otherwise I guess you could try

A loop is the best way. You can use Einsum.jl or Tullio.jl for syntax (and performance in many cases)

using Tullio

@tullio A[i, j, k] = v[k] * A[i, j, k]

There’s also the un-exported Base.PermutedDimsArray

julia> A = collect(reshape(1:12, 2, 2, 3));

julia> B = Base.PermutedDimsArray(A, (3,1,2));

julia> v = ones(3) .* 2;

julia> Base.PermutedDimsArray(v .* B, (2,3,1))
2×2×3 PermutedDimsArray(::Array{Float64,3}, (2, 3, 1)) with eltype Float64:
[:, :, 1] =
 2.0  6.0
 4.0  8.0

[:, :, 2] =
 10.0  14.0
 12.0  16.0

[:, :, 3] =
 18.0  22.0
 20.0  24.0
1 Like

Broadcasting is good for this, and you only need one reshape to put v along the 3rd dimension. I made a package which keeps track of these reshapes & permutations for me, instead of writing comments explaining which dimension was 3rd, or 4th, and wondering if it should be (2,3,1) or (3,1,2):

using TensorCast
@cast B[m,n,i] := A[m,n,i] * v[i]  # A .* reshape(v, 1,1,:)

@cast B[i,m,n] := A[i,m,n] * v[i]  # A .* v 
@cast B[i,m,n] := A[m,n,i] * v[i]  # PermutedDimsArray(A, (3,1,2)) .* v

Thanks, everyone. It seems that once I get into multidimensional arrays I fall into the world of tensors, in which as you say something like einsum.jl may do the job. Or write a loop myself… or re-write my code so that my “dimension of interest” (as it were) is first. This is what I’m currently doing as it is simple, requires no extra packages or macros, and seems to work fine. But it’s also somewhat inflexible, so I will indeed explore the other options! Again, many thanks.