Say I have the following matrix
B = [1 2 3;4 5 6;7 8 9;10 11 12]
and another matrix
A = [a b c;d e f;g h i]
How do I multiply each row of matrix B by the matrix A (without using a for loop), i.e.
for i = 1:4
c(i) = B(i,:)*A*B(i,:)'
end
many thanks in advance.
You can use:
c = diag(B*A*B.');
However, this computes a whole 4×4 matrix only to extract its diagonal, so it's not very efficient.
A more efficient way that only computes the desired values is:
c = sum(bsxfun(#times, permute(sum(bsxfun(#times, B, permute(A, [3 1 2])), 2), [1 3 2]), B), 2);
Here is a breakdown of the above code:
c1 = sum(bsxfun(#times, B, permute(A, [3 1 2])), 2); % B(i,:)*A
c = sum(bsxfun(#times, permute(c1, [1 3 2]), B), 2); % (B(i,:)*A)*B(i,:)'
The first permute is used so that the number of columns in B matches the number of columns in A. Following the element-wise multiplication in bsxfun() each row is summed up (remember, permute shifted the rows into the 2nd-dimension), reproducing the effect of the vector-matrix multiplication B(i,:) * A occurring in the for loop.
Following the first sum, the 2nd-dimension is a singleton dimension. So, we use the second permute to move the 2nd-dimension into the 3rd-dimension and produce a 2-D matrix. Now, both c1 and B are the same size. Following element-wise multiplication in the second bsxfun() each column is summed up (remember, permute shifted columns back into the 2nd-dimension), reproducing the effect of B(i,:) * A * B(i,:)'.
Take note of a hidden advantage in this approach. Since we are using element-wise multiplication to replicate the results of matrix multiplication, order of the arguments doesn't matter in the bsxfun() calls. One less thing to worry about!
Or, from Matlab R2016b onwards, you can replace bsxfun(#times,...) by .*, thanks to implicit expansion:
c = sum(permute(sum(B.*permute(A, [3 1 2]), 2), [1 3 2]).*B, 2);
Related
I have a data set that consists of two 1800 x 900 x 3 x 3 arrays, which should each be interpreted as a 1800x900 array of 3x3 matrices. As part of the analysis, at one point I need to create another such array by, at each point of the 1800x900 array, multiplying the corresponding 3x3 matrices together.
There seem to be two ways to do this that I can think of. The obvious way is
C = zeros(size(A))
for i = 1:900
for j = 1:1800
C(i,j,:,:) = A(i,j,:,:)*B(i,j,:,:)
end
end
But that's quite a long loop and doesn't really take advantage of MATLAB's vectorization. The other way is
C = zeros(size(A))
for i = 1:3
for j = 1:3
for k = 1:3
C(:,:,i,j) = C(:,:,i,j) + A(:,:,i,k).*B(:,:,k,j)
end
end
end
where the large dimensions are getting vectorized and I'm basically using the for loops to implement the Einstein summation convention. This seems really inelegant, though, which makes me think there should be a better way. Is there?
Perfect job for bsxfun with permute:
C = permute(sum(bsxfun(#times, A, permute(B, [1 2 5 3 4])), 4), [1 2 3 5 4]);
In R2016b onwards you can avoid bsxfun thanks to implicit singleton expansion:
C = permute(sum(A .* permute(B, [1 2 5 3 4]), 4), [1 2 3 5 4]);
Is there a quick way of normalizing each row of a 3 dimensional Matrix without resorting to slow for loops in Matlab?
Say my input data looks like this:
d(:,:,1) =
1 2 3
4 5 6
d(:,:,2) =
7 8 9
10 11 12
I know that I can get the norm of each row by using
norms = sqrt(sum(d.^2,2))
norms(:,:,1) =
3.7417
8.7750
norms(:,:,2) =
13.9284
19.1050
But how to divide now the second dimension with these norm values?
I know that in 2 dims I can use ./ however this seems not to work for 3 dimensional data.
bsxfun is your friend:
out = bsxfun(#rdivide, d, norms);
What this does is that it temporarily creates a 3D matrix that replicates each row of norms for as many columns as there are in d and it divides each element in an element-wise manner with d and norms.
We get:
>> d = cat(3, [1 2 3; 4 5 6], [7 8 9; 10 11 12]);
>> norms = sqrt(sum(d.^2,2));
>> out = bsxfun(#rdivide, d, norms)
out(:,:,1) =
0.2673 0.5345 0.8018
0.4558 0.5698 0.6838
out(:,:,2) =
0.5026 0.5744 0.6462
0.5234 0.5758 0.6281
We can also verify that each row is L2-normalized by determining the sum of squares along each row independently and ensuring that each result sums to 1:
>> sum(out.^2, 2)
ans(:,:,1) =
1.0000
1.0000
ans(:,:,2) =
1.0000
1.0000
If the approach with bsxfun doesn't quite make sense, an alternative you could use is to create a matrix that respects the same dimensions as d by using repmat... then you can perform the element-wise division you desire:
>> out = d ./ repmat(norms, [1 size(d,2) 1])
out(:,:,1) =
0.2673 0.5345 0.8018
0.4558 0.5698 0.6838
out(:,:,2) =
0.5026 0.5744 0.6462
0.5234 0.5758 0.6281
With repmat you specify how many times you want the matrix to be copied in each dimension. We only want the matrix to be replicated over the columns while the number of rows and slices are the same... hence the vector [1 size(d,2) 1] that specifies how many times you want the matrix copied in each dimension.
Actually, this is what bsxfun does under the hood without you having to deal with the headaches of creating this temporary matrix. This replication is done for you without having you think about it.
Suppose I have a matrix A [m x 1], where m is not necessarily even. I to create a matrix B also [m x 1] which tells me the decile of the elements in A (i.e. matrix B has numbers from 1 to 10).
I know I can use the function sort(A) to get the position of the elements in A and from there I can manually get deciles. Is there another way of doing it?
I think one possibility would be B = ceil(10 * tiedrank(A) / length(A) . What do you think? Are there any issues with this?
Also, more generally, if I have a matrix A [m x n] and I want to create a matrix B also [m x n], in which each column of B should have the decile of the corresponding column in A , is there a way of doing it without a for loop through the columns?
Hope the problem at hand is clear. So far I have been doing it using the sort function and then manually assigning the deciles, but it is very inefficient.
This is how I would do it:
N = 10;
B = ceil(sum(bsxfun(#le, A(:), A(:).'))*N/numel(A));
This counts, for each element, how many elements are less than or equal to it; and then rounds the results to 10 values.
Depending on how you define deciles, you may want to change #le to #lt, or ceil to floor. For numel(A) multiple of N, the above definition gives exactly numel(A)/N values in each of the N quantiles. For example,
>> A = rand(1,8)
A =
0.4387 0.3816 0.7655 0.7952 0.1869 0.4898 0.4456 0.6463
>> N = 4;
>> B = ceil(sum(bsxfun(#le, A(:), A(:).'))*N/numel(A))
B =
2 1 4 4 1 3 2 3
I have a 5 by 3 matrix, e.g the following:
A=[1 1 1; 2 2 2; 3 3 3; 4 4 4; 5 5 5]
I run a for loop:
for i = 1:5
AA = A(i)'*A(i);
end
My question is how to store each of the 5 (3 by 3) AA matrices?
Thanks.
You could pre-allocate enough memory to the AA matrix to hold all the results:
[r,c] = size(A); % get the rows and columns of A (r and c respectively)
AA = zeros(c,c,r); % pre-allocate memory to AA for all 5 products
% (so we have 5 3x3 arrays)
Now do almost the same loop as above BUT realize that A(i) in the above code only returns one element whereas you want the full row. So you want the data from row i but all columns which can be represented as 1:3 or just the colon :
for i=1:r
AA(:,:,i) = A(i,:)' * A(i,:);
end
In the above, A(i,:) is the ith row of A and we are setting all rows and columns in the third dimension (i) of AA to the result of the product.
Assuming, as in Geoff's answer, that you mean A(i,:)'*A(i,:) (to get 5 matrices of size 3x3 in your example), you can do it in one line with bsxfun and permute:
AA = bsxfun(#times, permute(A, [3 2 1]), permute(A, [2 3 1]));
(I'm also assuming that your matrices only contain real numbers, as in your example. If by ' you really mean conjugate transpose, you need to add a conj in the above).
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
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]);
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.
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)