Reputation: 41
I have a cell array A consisting of a number of (2x1) matrices. I have another matrix B (2x2) which I intend to multiply with each element of the cell A. In other words, every matrix within the array A has to be multiplied with B to generate another cell array C (same size as A) consisting of 2x1 matrices.
Visually,
B*|A{1,1} A{1,2} ... A{1,n}| = |C{1,1} C{1,2} ... C{1,n}|
|::::::::::::::::::::::::| |::::::::::::::::::::::::|
|A{m,1} A{m,2} ... A{m,n}| |C{m,1} C{m,2} ... C{m,n}|
Here, C{i,j}=B*A{i,j}
Doing this using cellfun is very slow. Is there a way to vectorize this without using explicit loops and speed up the operation irrespective of the size of A and B (as long as they are multiply-able)?
Upvotes: 1
Views: 3352
Reputation: 38032
I can think of a few ways. First, I generate some bogus data through
A = cellfun(@(~)rand(2,1), cell(1000), 'uni', false);
B = rand(2);
As you said, this is what you want to avoid. In any case I'll include it here for comparison:
C = cellfun(@(x)B*x, A, 'UniformOutput',false);
It is slow because of the anonymous function involved. Cellfun with a built-in string function (see help cellfun
) is raging fast, however, anonymous functions force the execution to pass by the Matlab interpreter on each and every iteration. Although this can somewhat be made more efficient by JIT, it's far from optimal.
The idea: expand the cell into a 2x1000 matrix, carry out the multiplication, and cast the results back into a cell array of the right size. Although elegant in principle, when put in practice it becomes a bit of a mess:
C = reshape( num2cell(B*[A{:}],1), size(A) );
Note that the intermediate 2x1000 temporary is a bit of a waste from a memory-footprint point of view...also, the pass through num2cell
(which is non-builtin) and 'useless' reshape
slow down the execution quite a bit.
A loop is simplest to read, simplest to implement. Small memory footprint, good for acceleration by JIT, etc.
C = cell(size(A));
for ii = 1:numel(A)
C{ii} = B*A{ii};
end
It's only drawback really is its bad reputation :)
And now for the kicker: which one is fastest?
tic
C = cellfun(@(x)B*x, A, 'uni',false);
toc
tic
C = reshape( num2cell(B*[A{:}],1), size(A) );
toc
tic
C = cell(size(A));
for ii = 1:numel(A)
C{ii} = B*A{ii};
end
toc
Results:
Elapsed time is 4.738791 seconds. % cellfun
Elapsed time is 4.161515 seconds. % cell expansion, num2cell, reshape
Elapsed time is 3.808822 seconds. % loop
Conclusion: loops are not evil anymore since the introduction of JIT compilation in R2008; don't try to avoid them by default.
Upvotes: 3
Reputation: 2750
I just tried this and I hope it's what you look for:
clc,clear all,close all
clc,clear all,close all
A = cell(10,2);
for jj = 1:numel(A)
A{jj} = rand(2,1);
end
B = rand(2,2);
C = cat(3,A{:});
D = cell(size(A));
for ii = 1:size(C,3)
D{ii} = B*C(:,:,ii);
end
It contains an explicit for
loop (I know), but in latest MATLAB releases performance should not be that badly impacted by calling for
loops.
Or you can use this answer just as hint. I hope this helps.
Upvotes: 0
Reputation: 4551
You can do this using mat2cell
/cell2mat
A = rand(2, 10);
B = rand(2, 2);
Ac = mat2cell(A, 2, repmat(1, size(A, 2), 1)); % Convert to cell to generate your initial data
Amat = cell2mat(Ac); % Now it's a matrix
Cmat = B * Amat;
C = mat2cell(Cmat, 2, repmat(1, size(A, 2), 1)); % Now it's a cell array again
Upvotes: 2