Create a 3-dim matrix from two 2-dim matrices - matlab

I already have a N_1 x N_2 matrix A, and a N_2 x N_3 matrix B.
I want to create a N_1 x N_2 x N_3 matrix C, such that C(i,j,k) = A(i,j)*B(j,k).
I was wondering if it is possible to create C using some Matlab operation, instead of doing it element by element?

You can do the same thing as the OP's answer using bsxfun (which actually works internally using a similar method, but is a little bit cleaner):
C = bsxfun(#times, A, permute(B, [3 1 2]));
This is also quite a bit faster (bsxfun must do some magic internally - probably takes advantage of MATLAB's internal ability to do certain operations using multiple threads, or it might just be that permuting the smaller matrix is a lot faster, or some combination of similar factors):
>> N1 = 100; N2 = 20; N3 = 4; A = rand(N1, N2); B = rand(N2, N3);
>> tic; for n = 1:10000; C = repmat(A, [1, 1, size(B, 2)]) .* permute(repmat(B, [1, 1, size(A, 1)]), [3, 1, 2]); end; toc
Elapsed time is 2.827492 seconds.
>> tic; for n = 1:10000; C2 = bsxfun(#times, A, permute(B, [3 1 2])); end; toc
Elapsed time is 0.287665 seconds.
Edit: moving the permute inside the repmat shaves a little bit of time off, but it's still nowhere near as fast as bsxfun:
>> tic; for n = 1:10000; C = (repmat(A, [1 1 size(B, 2)]) .* repmat(permute(B, [3 1 2]), [size(A, 1) 1 1])); end; toc
Elapsed time is 2.563069 seconds.

Rather clumsy, but it seems to work:
C = repmat(A, [1, 1, size(B, 2)]) .* permute(repmat(B, [1, 1, size(A, 1)]), [3, 1, 2]);

Related

Matlab: slower execution although less operation

I'm new to Matlab. This is my playground script:
function speedtest()
a = reshape(1:1:30000, 10000, 3);
tic;
for i = 1:100
a(:, [1, 2]) = bsxfun(#minus, a(:, [1, 2]), [1, 1]);
end
toc
tic;
for i = 1:100
a = bsxfun(#minus, a, [1, 1, 0]);
end
toc
end
And the execution time:
Elapsed time is 0.007709 seconds.
Elapsed time is 0.001803 seconds.
The first method has less operation, but it runs much slower. Is this a vectorization issue? If so, why can't Matlab "vectorize" my a(:, [1, 2]) selection?
Update:
As per #thewaywewalk, I put the code to individual function, remove the loop and use timeit. Here's the result:
# a(:, [1, 2]) = bsxfun(#minus, a(:, [1, 2]), [1, 1]);
1.0064e-04
# a = bsxfun(#minus, a, [1, 1, 0]);
6.4187e-05
the overhead of the first approach came from sub-matrix slicing. changing it to
tic;
b=a(:,[1,2]);
for i = 1:100
b = bsxfun(#minus, b, [1, 1]);
end
a(:,[1,2])=b;
toc
makes it significantly faster

Multiply n vectors of length p by n matrices of size pxp

I have n complex vectors of length p that I want to multiply by n complex matrices of size p-by-p. I am looking for the most efficient way to do this in MATLAB. If it matters, I am imagining that n is large and p is small.
An example using a loop (which I would like to avoid) is shown below.
N = 1e4;
p = 5;
A = randn(p, N); % N vectors of length p
B = randn(p, p, N); % N matrices of size pxp
C = zeros(p, N);
for k = 1:N
C(:, k) = B(:, :, k) * A(:, k);
end
It's been suggested that I might be able to achieve this efficiently using tensor functions, but I haven't been able to figure that out.
Here's a way using implicit expansion:
C = permute(sum(B.*permute(A, [3 1 2]), 2), [1 3 2]);
For old Matlab versions (before R2016b) you need to rewrite it with bsxfun:
C = permute(sum(bsxfun(#times, B, permute(A, [3 1 2])), 2), [1 3 2]);
You can accomplish that in various ways:
A = rand(3, 3, 1e6);
B = rand(3, 1);
tic, C = zeros(3, size(A, 3));
for i = 1:size(A, 3)
C(:,i) = A(:,:,i)*B ;
end, toc
tic; C = reshape(reshape(permute(A,[2,1,3]),3,[]).'*B,3,[]); toc
tic; C = squeeze(sum(bsxfun(#times, A, reshape(B, 1, 3)), 2)); toc
In my system:
Elapsed time is 2.067629 seconds. % Loop
Elapsed time is 0.064164 seconds. % permute
Elapsed time is 0.145738 seconds % sum(times())

Fast multipliction of multiple matrices by multiple vectors

In matlab, I would like to multiply M vectors using L matrices, resulting with M x L new vectors. Specifically, say I have a matrix A of size N x M and a matrix B of size N x N x L matrix, I would like to calculate a matrix C of size N x M x L where the result is exactly like the following slow code:
for m=1:M
for l=1:L
C(:,m,l)=B(:,:,l)*A(:,m)
end
end
but do achieve this efficiently (using native code rather than matlab looping).
We could ab-use fast matrix-multiplication here, just need to rearrange dimensions. So, push back the second dimension of B to the end and reshape to 2D such that the first two dims are merged. Perform matrix-multiplication with A to give us a 2D array. Let's call it C. Now, C's first dim were the merged dims from B. So, split it back into their original two dim lengths with reshaping resulting in a 3D array. Finally push back the second dim to the back with one more permute. This is the desired 3D output.
Hence, the implementation would be -
permute(reshape(reshape(permute(B,[1,3,2]),[],N)*A,N,L,[]),[1,3,2])
Benchmarking
Benchmarking code :
% Setup inputs
M = 150;
L = 150;
N = 150;
A = randn(N,M);
B = randn(N,N,L);
disp('----------------------- ORIGINAL LOOPY -------------------')
tic
C_loop = NaN(N,M,L);
for m=1:M
for l=1:L
C_loop(:,m,l)=B(:,:,l)*A(:,m);
end
end
toc
disp('----------------------- BSXFUN + PERMUTE -----------------')
% #Luis's soln
tic
C = permute(sum(bsxfun(#times, permute(B, [1 2 4 3]), ...
permute(A, [3 1 2])), 2), [1 3 4 2]);
toc
disp('----------------------- BSXFUN + MATRIX-MULT -------------')
% Propose in this post
tic
out = permute(reshape(reshape(permute(B,[1,3,2]),[],N)*A,N,L,[]),[1,3,2]);
toc
Timings :
----------------------- ORIGINAL LOOPY -------------------
Elapsed time is 0.905811 seconds.
----------------------- BSXFUN + PERMUTE -----------------
Elapsed time is 0.883616 seconds.
----------------------- BSXFUN + MATRIX-MULT -------------
Elapsed time is 0.045331 seconds.
You can do it with some permuting of dimensions and singleton expansion:
C = permute(sum(bsxfun(#times, permute(B, [1 2 4 3]), permute(A, [3 1 2])), 2), [1 3 4 2]);
Check:
% Example inputs:
M = 5;
L = 6;
N = 7;
A = randn(N,M);
B = randn(N,N,L);
% Output with bsxfun and permute:
C = permute(sum(bsxfun(#times, permute(B, [1 2 4 3]), permute(A, [3 1 2])), 2), [1 3 4 2]);
% Output with loops:
C_loop = NaN(N,M,L);
for m=1:M
for l=1:L
C_loop(:,m,l)=B(:,:,l)*A(:,m);
end
end
% Maximum relative error. Should be 0, or of the order of eps:
max_error = max(reshape(abs(C./C_loop),[],1)-1)

outer product of matrix rows (e.g. A(i,:)^T * A(i,:) for all rows i) + mean of same element in all submatrices created

I'm trying to calculate the outer product of all rows in a matrix. So far I'm doing the following:
A = rand(10,8);
[N J] = size(A);
for i = 1:N,
B(((i-1)*J+1):J+((i-1)*J),:) = A(i,:)'*A(i,:)
end
I then take the mean of the same elements of the same row in each submatrix created above.
for j=1:J,
C(1:J,j) = accumarray(repmat((1:J)',N,1),B(:,j)).*(1/N);
end
Now this works, but my input matrix will eventually be a number of magnitudes larger, so a vectorized version would be nice. I assume there is some way to do this with permute and bsxfun but the solutions for outer product I have seen so far don't seem to apply here.
Sure. You can compute C directly as
C = mean(bsxfun(#times, permute(A, [2 3 1]), permute(A, [3 2 1])), 3);
Or, if you really need the B variable, you can do it this way:
B_3D = bsxfun(#times, A.', permute(A,[3 1 2])); %'// 3D array
B = reshape(B_3D, numel(A), []);
C = mean(permute(B_3D, [1 3 2]), 3);

binary matrix of all combination with equal probability of 1 and 0 in matlab

I want to generate a binary matrix, let say (8,1). with equal probability means four 1's and four 0s in a matrix. with different permutation of these elements total 70 combination are possible (e.g 8C4). I want these all possible combination one by one.
please help.
The straighforward answer is:
unique(perms([true(1, N / 2), false(1, N / 2)]), 'rows')
or in a fancier form:
unique(perms(sparse(1, 1:N / 2, true, 1, N)), 'rows')
where N is the length of your vector (N = 8 in your example). However, expect this solution to be very slow for large arrays.
Surprisingly, in this case a faster method is to generate all possible permutations (see here) and eliminate those that do not satisfy the desired criterion:
C = cell(N, 1); %// Preallocate memory
[C{:}] = ndgrid([true, false]); %// Generate N grids of binary values
p = cellfun(#(x){x(:)}, C); %// Convert grids to column vectors
p = [p{:}]; %// Obtain all combinations
p = p(sum(p, 2) == N / 2, :); %// Keep only desired combinations
Benchmark
N = 8;
%// Method #1 (one-liner)
tic
for k = 1:1e3
p = unique(perms(sparse(1, 1:N / 2, true, 1, N)), 'rows');
end
toc
%// Method #2
tic
for k = 1:1e3
C = cell(N, 1);
[C{:}] = ndgrid([true, false]);
p = cellfun(#(x){x(:)}, C);
p = [p{:}];
p = p(sum(p, 2) == N / 2, :);
end
toc
The results I got were:
Elapsed time is 0.858539 seconds. %// Method #1
Elapsed time is 0.803826 seconds. %// Method #2
... and for N = 10:
Elapsed time is 55.3068 seconds. %// Method #1
Elapsed time is 1.03664 seconds. %// Method #2
Not only that nchoosek fails for large values of N, it's also slower.
Here is an even faster way to do it, using the fact that you are looking for a subset of binary number representations:
b = dec2bin(1:2^N-1);
x = b-'0';
x = x(sum(x,2)==N/2,:);
The performance comparison:
N = 8;
% Dennis solution
tic
b = dec2bin(1:2^N-1);
x = b-'0';
x=x(sum(x,2)==N/2,:);
toc
% Eitan Method 2
tic
for k = 1:1e3
C = cell(N, 1);
[C{:}] = ndgrid([true, false]);
p = cellfun(#(x){x(:)}, C);
p = [p{:}];
p = p(sum(p, 2) == N / 2, :);
end
toc
Gives these timings:
Elapsed time is 0.002200 seconds.
Elapsed time is 0.594309 seconds.
Note that the resulting lines will be in different orders for both solutions.