tags:

views:

569

answers:

6

Suppose I have an AxBxC matrix X and a BxD matrix Y.

Is there a non-loop method by which I can multiply each of the C AxB matrices with Y?

+1  A: 

I would think recursion, but that's the only other non- loop method you can do

Kevin
In MATLAB? I think there might be other options ..
Jacob
A: 

You could "unroll" the loop, ie write out all the multiplications sequentially that would occur in the loop

BioBuckyBall
Suppose C is variable .. among other things.
Jacob
+1  A: 

Nope. There are several ways, but it always comes out in a loop, direct or indirect.

Just to please my curiosity, why would you want that anyway ?

ldigas
Why would I want to do it without a loop? Just old habits. MATLAB is supposed to be loop-optimized now with JITA, but I try to avoid them whenever I can - and I have a strong feeling it is possible to solve this without loops.
Jacob
yes, ok, i can understand that. (on the opposite, i sometimes do stuff which can be done without a loop in a loop, since i find it easier to read <-- :( old habits, too :)
ldigas
+4  A: 

Here's a one-line solution (two if you want to split into 3rd dimension):

A = 2;
B = 3;
C = 4;
D = 5;

X = rand(A,B,C);
Y = rand(B,D);

%# calculate result in one big matrix
Z = reshape(reshape(permute(X, [2 1 3]), [A B*C]), [B A*C])' * Y;

%'# split into third dimension
Z = permute(reshape(Z',[D A C]),[2 1 3]);

Hence now: Z(:,:,i) contains the result of X(:,:,i) * Y


Explanation:

The above may look confusing, but the idea is simple. First I start by take the third dimension of X and do a vertical concatenation along the first dim:

XX = cat(1, X(:,:,1), X(:,:,2), ..., X(:,:,C))

... the difficulty was that C is a variable, hence you can't generalize that expression using cat or vertcat. Next we multiply this by Y:

ZZ = XX * Y;

Finally I split it back into the third dimension:

Z(:,:,1) = ZZ(1:2, :);
Z(:,:,2) = ZZ(3:4, :);
Z(:,:,3) = ZZ(5:6, :);
Z(:,:,4) = ZZ(7:8, :);

So you can see it only requires one matrix multiplication, but you have to reshape the matrix before and after.

Amro
Thanks! I was hoping for a solution along the lines of `bsxfun` but this looks interesting
Jacob
there was no need. As you can see from the explanation I added, it was only a matter of preparing the matrix by rearranging its shape, so that a simple multiplication would suffice.
Amro
+3  A: 

You can do this in one line using the functions NUM2CELL to break the matrix X into a cell array and CELLFUN to operate across the cells:

Z = cellfun(@(x) x*Y,num2cell(X,[1 2]),'UniformOutput',false);

The result Z is a 1-by-C cell array where each cell contains an A-by-D matrix. If you want Z to be an A-by-D-by-C matrix, you can use the CAT function:

Z = cat(3,Z{:});



NOTE: My old solution used MAT2CELL instead of NUM2CELL, which wasn't as succinct:

[A,B,C] = size(X);
Z = cellfun(@(x) x*Y,mat2cell(X,A,B,ones(1,C)),'UniformOutput',false);
gnovice
This is **exactly** what I was looking for.
Jacob
With this solution the loop is inside cellfun. But it is nevertheless 10% faster then solution provided by amro (on large matrces, shortly before MATLAB runs out of memory).
Mikhail
I'm curious as to the 2 downvotes I got. Whether or not you *like* the answer, it *does* answer the question by avoiding explicit use of a for loop.
gnovice
Man, who would've thought a simple question like this would be so controversial?
Jacob
@Jacob: Yeah, it appears to have spawned some debate. Since I had seen you answering MATLAB questions before, I figured you already knew how to do this using loops (the most straight-forward way). I just assumed you were asking the question out of curiosity for what other ways it could also be done.
gnovice
@gnovice: You figured correctly. I was looking for a `bsxfun`-ish way of doing this and your `cellfun` implementation fit the bill. I had assumed the "non-loop" criterion would've been enough to make my intentions clear: guess not!
Jacob
+3  A: 

As a personal preference, I like my code to be as succinct and readable as possible.

Here's what I would have done, though it doesn't meet your 'no-loops' requirement:

for m = 1:C

    Z(:,:,m) = X(:,:,m)*Y;

end

This results in an A x D x C matrix Z.

And of course, you can always pre-allocate Z to speed things up by using Z = zeros(A,D,C);.

Zaid
-1 : because this is not a real solution regardless of your disclaimer. If you have any opinions on succinctness or readability, please leave them as comments.
Jacob
+1 because it's also faster than gnovice and amro's fine solutions.
Ramashalanka

related questions