2

I have 2 matrices A (nxm) and B (nxd) and want to multiply element-wise each column of A with a row of B. There are m columns in A and n 1xd vectors in B so the results are m nxd matrices. Then I want to sum(result_i, 1) to get m 1xd vectors, which I want to apply vertcat to get a mxd matrix. I'm doing this operations using for loop and it is slow because n and d are big. How can I vectorize this in matlab to make it faster? Thank you.

EDIT:
You're all right: I was confused by my own question. What I meant by "multiply element-wise each column of A with a row of B" is to multiply n elements of a column in A with the corresponding n rows of B. What I want to do with one column of A is as followed (and I repeat this for m columns of A, then vertcat the C's vector together to get an mxd matrix):

column_of_A =

     3
     3
     1


B =

     3     1     3     3
     2     2     1     2
     1     3     3     3


C = sum(diag(column_of_A)*B, 1)

     16    12    15    18
Luis Mendo
  • 110,752
  • 13
  • 76
  • 147
Martin08
  • 20,990
  • 22
  • 84
  • 93
  • 2
    If you multiply each col of `A` with each row of `B`, then you get m*n matrices, not m. If not, then how do you choose a row from `B` for a given col of `A`? Is it given in advance? – Itamar Katz Apr 07 '11 at 12:13
  • @Itamar Katz: I should say multiply each element of the m columns of A _elementwise_ with each row of B, i.e. [1 2 3]' * [1 1]=[1 1; 2 2; 3 3]. – Martin08 Apr 07 '11 at 12:36
  • this is not element-wise multiplication, but instead, it is a matrix multiplication. – Jonas Apr 07 '11 at 13:06
  • Just to re-state what @Itamar Katz said: Say you multiply the first col of A with the first row of B to get a mxd array. Then you multiply the first col of A with the second row of B. Then the first col of A with the third row of B, and so on. Then you start multiplying the second col of A with the first row of B, etc. Thus, you'll end up with m*n arrays of size mxd. – Jonas Apr 07 '11 at 13:17

3 Answers3

6

You can vectorize your operation the following way. Note, however, that vectorizing comes at the cost of higher memory usage, so the solution may end up not working for you.

%# multiply nxm A with nx1xd B to create a nxmxd array
tmp = bsxfun(@times,A,permute(B,[1 3 2]));

%# sum and turn into mxd
out = squeeze(sum(tmp,1));

You may want to do everything in one line, which may help the Matlab JIT compiler to save on memory.

EDIT

Here's a way to replace the first line if you don't have bsxfun

[n,m] = size(A);
[n,d] = size(B);
tmp = repmat(A,[1 1 d]) .* repmat(permute(B,[1 3 2]),[1,m,1]);
Jonas
  • 74,690
  • 10
  • 137
  • 177
  • @Jonas This doesn't seem to generate an `mxd` matrix. It gives me an `nxd` (same size as `B`). That or I'm doing something horribly stupid (quite possible). – AVH Apr 07 '11 at 14:53
  • @Darhuuk: Oh, I summed along the wrong dimension. Thanks for the heads-up! – Jonas Apr 07 '11 at 15:00
  • @Jonas No problem :). Nice method, didn't know about `bsxfun`, seems to be about 3-10x faster than what I proposed on the small matrices I'm testing with. – AVH Apr 07 '11 at 15:02
  • @Jonas: Thank you. Can you explain a bit about how you chose the permute order [1 3 2]? Thanks. – Martin08 Apr 07 '11 at 15:16
  • @Martin08: I want to turn a nxd array into a nx1xd array. Thus, I need to turn dimension #2 into dimension #3. Permute allows me to reorder dimensions of a matrix, so I want to write `permute(B,[1,X,2])`. For X, I can choose any of the dimensions of B that have length 1. One of them is the current dimension 3 (since B is a nxdx1 array), but I could also have set `X` to 4 (since B is a nxdx1x1 array). – Jonas Apr 07 '11 at 15:49
  • @Darhuuk: `bsxfun` is making code-writing a lot more convenient - one of the better innovations in the last few years. – Jonas Apr 07 '11 at 15:50
  • @Jonas: Many thanks. One last question: what I can substitute if the machines where I run my code do not have bsxfun? After knowing bsxfun, I don't want to go back to my loopy version :( – Martin08 Apr 07 '11 at 17:45
1

It's ugly, but as far as I can see, it works. I'm not sure it will be faster than your loop though, plus, it has a large memory overhead. Anyway, here goes:

A_3D = repmat(reshape(A, size(A, 1), 1, size(A, 2)), 1, size(B, 2));
B_3D = repmat(B, [ 1 1 size(A, 2)]);
result_3D = sum(A_3D .* B_3D, 1);
result = reshape(result_3D, size(A, 2), size(B, 2))

What it does is: make A into a 3D matrix of size n x 1 x m, so one column in each index of the 3rd dimension. Then we repeat the matrix so we get an n x d x m matrix. We repeat B in the 3rd dimension as well. We then do a piecewise multiplication of all the elements and sum them. The resulting matrix is a 1 x d x m matrix. We reshape this into a m x d matrix.

I'm pretty sure I switched around the size of the dimensions a few times in my explanation, but I hope you get the general gist.

Multiplying with a diagonal matrix seems at least twice as fast, but I couldn't find a way to use diag, since it wants a vector or 2D matrix as input. I might try again later tonight, I feel there must be a faster way :).

[Edit] Split up the command in parts to at least make it a little bit readable.

AVH
  • 11,349
  • 4
  • 34
  • 43
0

This is the way I would do this:

sum(repmat(A,1,4).*B)

If you don't know the number of columns of B:

sum(repmat(A,1,size(B,2)).*B)

antman
  • 1