Alex Azazel
Alex Azazel

Reputation: 342

How to multiply tensors in MATLAB without looping?

Suppose I have:

A = rand(1,10,3);
B = rand(10,16);

And I want to get:

C(:,1) = A(:,:,1)*B;
C(:,2) = A(:,:,2)*B;
C(:,3) = A(:,:,3)*B;

Can I somehow multiply this in a single line so that it is faster?

What if I create new tensor b like this

for i = 1:3
    b(:,:,i) = B;
end

Can I multiply A and b to get the same C but faster? Time taken in creation of b by the loop above doesn't matter since I will be needing C for many different A-s while B stays the same.

Upvotes: 8

Views: 10320

Answers (4)

Divakar
Divakar

Reputation: 221584

If A is a true 3D array, something like A = rand(4,10,3) and assuming that B stays as a 2D array, then each A(:,:,1)*B would yield a 2D array.

So, assuming that you want to store those 2D arrays as slices in the third dimension of output array, C like so -

C(:,:,1) = A(:,:,1)*B;
C(:,:,2) = A(:,:,2)*B;
C(:,:,3) = A(:,:,3)*B; and so on.

To solve this in a vectorized manner, one of the approaches would be to use reshape A into a 2D array merging the first and third dimensions and then performing matrix-muliplication. Finally, to bring the output size same as the earlier listed C, we need a final step of reshaping.

The implementation would look something like this -

%// Get size and then the final output C
[m,n,r] = size(A);
out = permute(reshape(reshape(permute(A,[1 3 2]),[],n)*B,m,r,[]),[1 3 2]);

Sample run -

>> A = rand(4,10,3);
B = rand(10,16);

C(:,:,1) = A(:,:,1)*B;
C(:,:,2) = A(:,:,2)*B;
C(:,:,3) = A(:,:,3)*B;
>> [m,n,r] = size(A);
out = permute(reshape(reshape(permute(A,[1 3 2]),[],n)*B,m,r,[]),[1 3 2]);
>> all(C(:)==out(:)) %// Verify results
ans =
     1

As per the comments, if A is a 3D array with always a singleton dimension at the start, you can just use squeeze and then matrix-multiplication like so -

C = B.'*squeeze(A)

Upvotes: 6

GJStein
GJStein

Reputation: 658

EDIT: @LuisMendo points out that this is indeed possible for this specific use case. However, it is not (in general) possible if the first dimension of A is not 1.

I've grappled with this for a while now, and I've never been able to come up with a solution. Performing element-wise calculations is made nice by bsxfun, but tensor multiplication is something which is woefully unsupported. Sorry, and good luck!

You can check out this mathworks file exchange file, which will make it easier for you and supports the behavior you're looking for, but I believe that it relies on loops as well. Edit: it relies on MEX/C++, so it isn't a pure MATLAB solution if that's what you're looking for.

Upvotes: 2

Luis Mendo
Luis Mendo

Reputation: 112699

Permute the dimensions of A and B and then apply matrix multiplication:

C = B.'*permute(A, [2 3 1]);

Upvotes: 7

user1543042
user1543042

Reputation: 3440

I have to agree with @GJSein, the for loop is really fast

time
    0.7050    0.3145

Here's the timer function

function time

    n = 1E7;
    A = rand(1,n,3);
    B = rand(n,16);
    t = [];
    C = {};

    tic
        C{length(C)+1} = squeeze(cell2mat(cellfun(@(x) x*B,num2cell(A,[1 2]),'UniformOutput',false)));
    t(length(t)+1) = toc;

    tic
        for i = 1:size(A,3)
            C{length(C)+1}(:,i) = A(:,:,i)*B;
        end
    t(length(t)+1) = toc;

    disp(t)
end

Upvotes: 0

Related Questions