user2231787
user2231787

Reputation: 41

MATLAB: Multiplying elements of a cell array with another matrix to create a new cell array consisting of the products

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

Answers (3)

Rody Oldenhuis
Rody Oldenhuis

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);

Cellfun

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.

Cell-expansion, num2cell, reshape

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.

For loop

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 :)

Comparison

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

fpe
fpe

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

wakjah
wakjah

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

Related Questions