MATLAB: Multiply 2D matrix with 3D matrix within cell arrays - matlab

I have a constant 2D double matrix mat1. I also have a 2D cell array mat2 where every cell contains a 2D or 3D double matrix. These double matrices have the same number of rows and columns as mat1. I need to dot multiply (.*) mat1 with every slice of each double matrix within mat2. The result needs to be another cell array results with the same size as mat2, whereby the contatining double matrices must equal the double matrices of mat2 in terms of size.
Here's my code to generate mat1 and mat2 for illustrating purposes. I am struggling at the point where the multiplication should take place.
rowCells = 5;
colCells = 3;
rowTimeSeries = 300;
colTimeSeries = 5;
slices = [1;10];
% Create 2D double matrix
mat1 = rand(rowTimeSeries, colTimeSeries);
% Create 2D cell matrix comprisiong 2D and/or 3D double matrices
mat2 = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
slice = randsample(slices, 1, true);
mat2{r,c} = rand(rowTimeSeries, colTimeSeries, slice);
end
end
% Multiply (.*) mat1 with mat2 (every slice)
results = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
results{r,c} = ... % I am struggling here!!!
end
end

You could use bsxfun to remove the need for your custom function multiply2D3D, it works in a similar way! Updated code:
results = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
results{r,c} = bsxfun(#times, mat1, mat2{r,c});
end
end
This will work for 2D and 3D matrices where the number of rows and cols is the same in each of your "slices", so it should work in your case.
You also don't need to loop over the rows and the columns of your cell array separately. This loop has the same number of iterations, but it is one loop not two, so the code is a little more streamlined:
results = cell(size(mat2));
for n = 1:numel(mat2) % Loop over every element of mat2. numel(mat2) = rowCells*colCells
results{n} = bsxfun(#times, mat1, mat2{n});
end

I had almost the exact same answer as Wolfie but he beat me to it.
Anyway, here is a one liner that I think is slightly nicer:
nR = rowCells; % Number of Rows
nC = colCells; % Number of Cols
results = arrayfun(#(I) bsxfun(#times, mat1, mat2{I}), reshape(1:nR*nC,[],nC), 'un',0);
This uses arrayfun to perform the loop indexing and bsxfun for the multiplications.
A few advantages
1) Specifying 'UniformOutput' ('un') in arrayfun returns a cell array so the results variable is also a cell array and doesn't need to be initialised (in contrast to using loops).
2) The dimensions of the indexes determine the dimensions of results at the output, so they can match what you like.
3) The single line can be used directly as an input argument to a function.
Disadvantage
1) Can run slower than using for loops as Wolfie pointed out in the comments.

One solution I came up with is to outsource the multiplication of a 2D with a 3D matrix into a function. However, I am curious to know whether this is the most efficient way to solve this problem?
rowCells = 5;
colCells = 3;
rowTimeSeries = 300;
colTimeSeries = 5;
slices = [1;10];
% Create 2D double matrix
mat1 = rand(rowTimeSeries, colTimeSeries);
% Create 2D cell matrix comprisiong 2D and/or 3D double matrices
mat2 = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
slice = randsample(slices, 1, true);
mat2{r,c} = rand(rowTimeSeries, colTimeSeries, slice);
end
end
% Multiply (.*) mat1 with mat2 (every slice)
results = cell(rowCells,colCells);
for c = 1:colCells
for r = 1:rowCells
results{r,c} = multiply2D3D(mat1, mat2{r,c});
end
end
function vout = multiply2D3D(mat2D, mat3D)
%MULTIPLY2D3D multiplies a 2D double matrix with every slice of a 3D
% double matrix.
%
% INPUTs:
% mat2D:
% 2D double matrix
%
% mat3D:
% 3D double matrix where the third dimension is equal or greater than 1.
%
% OUTPUT:
% vout:
% 3D double matrix with the same size as mat3D. Every slice in vout
% is the result of a multiplication of mat2D with every individual slice
% of mat3D.
[rows, cols, slices] = size(mat3D);
vout = zeros(rows, cols, slices);
for s = 1 : slices
vout(:,:,s) = mat2D .* mat3D(:,:,s);
end
end

Related

Generalise indexing of a multi-dimensional array in Matlab

I want to generalise to any n the Matlab code below.
Let A be an n-dimensional array:
clear
rng default
n=4;
A=randn(n,n,n,n);
n=5;
A=randn(n,n,n,n,n);
Note that A is composed of n^(n-2) 2-dimensional matrices, each of size nxn.
For example, when n=4 these matrices are A(:,:,1,1),...,A(:,:,4,1),A(:,:,1,2),...,A(:,:,4,4).
Suppose I'm interested in a code which:
1) deletes the last column and row in each of the n^(n-2) 2-dimensional matrices
%when n=4
A(n,:,:,:)=[];
A(:,n,:,:)=[];
%when n=5
A(n,:,:,:,:)=[];
A(:,n,:,:,:)=[];
2) deletes the 2-dimensional matrices with the 3-th,4-th,5-th,n-th index equal to n.
%when n=4
A(:,:,n,:)=[];
A(:,:,:,n)=[];
%when n=5
A(:,:,n,:,:)=[];
A(:,:,:,n,:)=[];
A(:,:,:,:,n)=[];
Question: could you help me to generalise the code above to any n? I cannot see how to proceed.
You can index your matrix with a cell containing multiple elements. Each element will be interpreted as a new index (more information here):
%Example 1: A(:,:,1:3,1:3,1:3}
%elements per dimension
n = 4;
%number of dimension
d = 5;
%random matrix
repdim = repmat({n},d,1)
A = rand(repdim{:});
%We want A(:,:,1:3,1:3,1:3}, so we create c = {1:3,1:3,1:3}
c = repmat({1:n-1},d-2,1);
%Get the new matrix
A = A(:,:,c{:});
%Example 2: A(1:3,1:3,:,:,:}
%elements per dimension
n = 4;
%number of dimension
d = 5;
%random matrix
repdim = repmat({n},d,1)
A = rand(repdim{:});
%We want A(1:3,1:3,:,:,:}, so we create c1 = {1:3,1:3} and c2 = {':',':',':'}
c1 = repmat({1:n-1},2,1);
c2 = repmat({':'},d-2,1); %thanks to #LuisMendo for the suggestion.
%Get the new matrix
A = A(c1{:},c2{:});

Multiplying a 4D matrix by a vector, and collapsing 1 dimension

I have a question regarding the multiplication of a 4-dimensional object by a 1 dimensional object.
Effectively, I have a 4D object of sizes (15,15,3,5).
I want to multiply out the 4th dimension by using a 5x1 vector, collapsing the last dimension to 1. Then I want to use squeeze to get a (15,15,3) sized object, again multiplying it by a 3x1 vector, leaving me with a 15x15 matrix.
I can do this in a loop, but that is quite costly. Can anyone give me suggestions how to do this without a loop?
For now the loop:
expectationCalc = reshape(mValueFunction(age+1, :, :, :, :), nGridAssets, nGridHumanCapital, nNetInterestRate, nShockstoHumanCapital);
for i = 1:nGridAssets
for j = 1:nGridHumanCapital
expectation(i,j) = mTransitionNetInterestRate(nNetIntRate, :)*(squeeze(expectationCalc(i,j,:,:))*mTransitionShockHumanCapital(ShockHcapital, :)');
end
end
If you reshape your 4D matrix to a 2D matrix, where the 2nd dimension is the one you want to reduce by dot product, and the 1st dimension contains all other dimensions, then you can apply a regular matrix multiplication. The result can then be reshaped to the original size (minus one dimension):
% Input data
M = randn(15,15,3,5);
v1 = randn(5,1);
v2 = randn(3,1);
% 1st multiplication
sz = size(M);
M = reshape(M,[],sz(end));
M = M * v1;
sz(end) = []; % We no longer have that last dimension
M = reshape(M,sz);
% 2nd multiplication
M = reshape(M,[],sz(end));
M = M * v2;
sz(end) = []; % We no longer have that last dimension
M = reshape(M,sz);

How can all dimensions left after the specified one be preserved, without explicitly listing them?

Or equivalently, "what is the equivalent of NumPy's ellipsis indexing in Matlab"
Say I have some high-dimensional array:
x = zeros(3, 4, 5, 6);
I want to write a function that takes an array of size (3, ...), and does some computation. In NumPy, I could write this:
def fun(x):
return x[0]*x[1] + x[2]
However, the equivalent in MATLAB doesn't work, because indexing with one integer flattens the array to 1d
function y = fun_bad(x)
y = x(1)*x(2) + x(3)
I can make this work for up to 3-dimensional arrays with
function y = fun_ok3d(x)
y = x(1,:,:)*x(2,:,:) + x(3,:,:)
If I want this to work for up to 10-dimensional arrays, I can write
function y = fun_ok10d(x)
y = x(1,:,:,:,:,:,:,:,:,:)*x(2,:,:,:,:,:,:,:,:,:) + x(3,:,:,:,:,:,:,:,:,:)
How can I avoid writing stupid numbers of colons here, and just make this work for any dimension? Is there some x(1,...) syntax that implies this?
NumPy can use the ... (Ellipsis) literal in an indexing expression to mean ": as many times as needed", which would solve this problem.
Approach 1: using a comma-separated list with ':'
I don't know a way to specify
: as many times as needed
while preserving shape. But you can specify
: an arbitrary number of times
where that number of times is defined at run-time. With this method you can preserve shape, provided that the number of indices coincides with the number of dimensions.
This is done using a comma-separated list generated from a cell array, and exploiting the fact that the string ':' can be used as an index instead of ::
function y = fun(x)
colons = repmat({':'}, 1, ndims(x)-1); % row cell array containing the string ':'
% repeated the required number of times
y = x(1,colons{:}).*x(2,colons{:}) + x(3,colons{:});
This approach can be easily generalized to indexing along any dimension, not just the first:
function y = fun(x, dim)
% Input argument dim is the dimension along which to index
colons_pre = repmat({':'}, 1, dim-1);
colons_post = repmat({':'}, 1, ndims(x)-dim);
y = x(colons_pre{:}, 1, colons_post{:}) ...
.*x(colons_pre{:}, 2, colons_post{:}) ...
+ x(colons_pre{:}, 3, colons_post{:});
Approach 2: splitting the array
You can split the array along the first dimension using num2cell, and then apply the operation to the resulting subarrays. Of course this uses more memory; and as noted by #Adriaan it is slower.
function y = fun(x)
xs = num2cell(x, [2:ndims(x)]); % x split along the first dimension
y = xs{1}.*xs{2} + xs{3};
Or, for indexing along any dimension:
function y = fun(x, dim)
xs = num2cell(x, [1:dim-1 dim+1:ndims(x)]); % x split along dimension dim
y = xs{1}.*xs{2} + xs{3};
MATLAB flattens all trailing dimensions when using a single colon, so you can use that to get from your N-D array to a 2D array, which you can reshape back into the original N dimensions after the calculation.
Along the first dimension
If you want to use the first dimension you can use a relatively simple and short piece of code:
function y = MyMultiDimensional(x)
x_size = size(x); % Get input size
yflat = x(1,:) .* x(2,:) + x(3,:); % Calculate "flattened" 2D function
y = reshape(yflat, [1 x_size(2:end)]); % Reshape output back to original size
end
Along an arbitrary dimension, now featuring N-D permute.
When you want your function to act along the n-th dimension out of a total of N, you can permute that dimension to the front first:
function y = MyMultiDimensional(x,n)
x_size = size(x); % Get input size
Order = 1:numel(x_size);
Order(n)=[]; % Remove n-th dimension
Order2 = [n, Order]; % Prepend n-th dimension
xPermuted = permute(x,Order2); % permute the n-th dimension to the front
yTmp = xPermuted (1,:) .* xPermuted (2,:) + xPermuted (3,:); % Calculate "flattened" 2D function
y = reshape(yTmp, x_size(Order)); % Reshape output back to original size
end
I timed the results of the two methods of Luis' and my methods:
function timeMultiDim()
x = rand(1e1,1e1,1e1,1e1,1e1,1e1,1e1,1e1);
function y = Luis1(x)
colons = repmat({':'}, 1, ndims(x)-1); % row cell array containing the string ':'
% repeated the required number of times
y = x(1,colons{:}).*x(2,colons{:}) + x(3,colons{:});
end
function y = Luis2(x)
xs = num2cell(x, [2:ndims(x)]); % x split along the first dimension
y = xs{1}.*xs{2} + xs{3};
end
function y = Adriaan(x)
x_size = size(x); % Get input size
yflat = x(1,:) .* x(2,:) + x(3,:); % Calculate "flattened" 2D function
y = reshape(yflat, [1 x_size(2:end)]); % Reshape output back to original size
end
n=1;
function y = Adriaan2(x,n)
x_size = size(x); % Get input size
Order = 1:numel(x_size);
Order(n)=[]; % Remove n-th dimension
Order2 = [n, Order]; % Prepend n-th dimension
xPermuted = permute(x,Order2); % permute the n-th dimension to the front
yTmp = xPermuted (1,:) .* xPermuted (2,:) + xPermuted (3,:); % Calculate "flattened" 2D function
y = reshape(yTmp, x_size(Order)); % Reshape output back to original size
end
t1 = timeit(#() Luis1(x));
t2 = timeit(#() Luis2(x));
t3 = timeit(#() Adriaan(x));
t4 = timeit(#() Adriaan2(x,n));
format long g;
fprintf('Luis 1: %f seconds\n', t1);
fprintf('Luis 2: %f seconds\n', t2);
fprintf('Adriaan 1: %f seconds\n', t3);
fprintf('Adriaan 2: %f seconds\n', t4);
end
Luis 1: 0.698139 seconds
Luis 2: 4.082378 seconds
Adriaan 1: 0.696034 seconds
Adriaan 2: 0.691597 seconds
So, going to a cell is bad, it takes more than 5 times as long, reshape and ':' are barely apart, so that'd come down to preference.

How to avoid nested for loops in matlab?

I am constructing an adjacency list based on intensity difference of the pixels in an image.
The code snippet in Matlab is as follows:
m=1;
len = size(cur_label, 1);
for j=1:len
for k=1:len
if(k~=j) % avoiding diagonal elements
intensity_diff = abs(indx_intensity(j)-indx_intensity(k)); %intensity defference of two pixels.
if intensity_diff<=10 % difference thresholded by 10
adj_list(m, 1) = j; % storing the vertices of the edge
adj_list(m, 2) = k;
m = m+1;
end
end
end
end
y = sparse(adj_list(:,1),adj_list(:,2),1); % creating a sparse matrix from the adjacency list
How can I avoid these nasty nested for loops? If the image size is big, then its working just as disaster. If anyone have any solution, it would be a great help for me.
Regards
Ratna
I am assuming the input indx_intensity as a 1D array here. With that assumption, here's a vectorized approach with broadcasting/bsxfun -
%// Threshold parameter
thresh = 10;
%// Get elementwise differentiation between elements in indx_intensity
diffs = abs(bsxfun(#minus,indx_intensity(:),indx_intensity(:).')) %//'
%// Threshold the differentiations against the threshold, thus giving us a
%// 2D square matrix. Then, set the diagonal elements to zero to avoid them.
mask = diffs <= thresh;
mask(1:len+1:end) = 0;
%// Get the indices of the TRUE elements in the valid mask as final output.
[R,C] = find(mask);
adj_list_out = [C R];

Generate every binary n x m matrix in matlab

I'd like to generate every boolean matrix in matlab as a 3-dimensional array.
For example:
mat(:,:,1) = [[1 0][0 1]]
mat(:,:,2) = [[1 1][0 1]]
...
My final goal is to generate every trinary matrix of a given size.
Keep in mind that I know that the number of matrices is exponential, but I'll use small numbers.
Not sure that the previous answer actually does what you want... With that method, I get multiple entries in array2D that are the same. Here is a vectorised and (I believe) correct solution:
clear all;
nRows = 2;
nCols = nRows; % Only works for square matrices
% Generate matrix of all binary numbers that fit in nCols
max2Pow = nCols;
maxNum = 2 ^ max2Pow - 1;
allBinCols = bsxfun(#bitand, (0:maxNum)', 2.^((max2Pow-1):-1:0)) > 0;
% Get the indices of the rows in this matrix required for each output
% binary matrix
N = size(allBinCols, 1);
A = repmat({1:N}, nCols, 1);
[B{1:nCols}] = ndgrid(A{:});
rowInds = reshape(cat(3, B{:}), [], nCols)';
% Get the appropriate rows and reshape to a 3D array of right size
nMats = size(rowInds, 2);
binMats = reshape(allBinCols(rowInds(:), :)', nRows, nCols, nMats)
Note that for anything other than small numbers of nRows you will run out of memory pretty quickly, because you're generating 2^(nRows*nRows) matrices of size nRows*nRows. ThatsAlottaNumbers.
Actually the answer is pretty straightforward. Each matrix being boolean, it can be indexed by the binary number obtained when reading all the values in any given order.
For a binary matrix: let n be the number of element in your matrix (n = rows * cols).
for d=0:(2^n-1)
%Convert binary to decimal string
str = dec2bin(d);
%Convert string to array
array1D = str - '0';
array1D = [array1D zeros(1, n-length(array1D))];
%Reshape
array2D(:,:,d+1) = reshape(array1D, rows, cols);
end
This can be very easily generalized to any base by changing dec2bin into dec2base and changing 2^n into (yourbase)^n.