I have a 3D matrix sized (x,y,N) and a 2D matrix sized (N,N).
I would like to manipulate the two in a way that each column in the 2D matrix has the coefficients for a linear combination of the 2D sized- (x, y) slices in the 3D matrix. And I would like to do this for all N columns in the 2D matrix.
Schematically,
Currently the code looks like:
A = zeros(numel(x_axis), numel(y_axis), N);
B = zeros(numel(x_axis), numel(y_axis), N);
C = zeros(N, N)
for i = 1 : N
for j = 1 : N
A(:,:,i) = A(:,:,i) + B(:,:,j) * C(j,i);
end
end
But it is quite slow. Is there any way to speed up the MATLAB code by vectorizing?
If I understand your problem well, then this should work:
[p,q,N] = size(B);
A = reshape( reshape(B, [p*q, N]) * C, [p, q, N]);
edit: Cleaner version suggested by Suever:
A = reshape(reshape(B, [], size(B, 3)) * C, size(B))
Generalization to the R-D case:
A = reshape(reshape(B, [], size(B, ndims(B))) * C, size(B))
You can use bsxfun which will calculate this very quickly for you. We have to use permute to re-arrange C a little bit to ensure that it has conformant dimensions for using bsxfun and then we perform the summation along the third dimension of the resulting output and apply squeeze to remove the singleton third dimension.
A = squeeze(sum(bsxfun(#times, B, permute(C, [3 4 1 2])), 3))
Related
I'm wondering if it is possible to perform a multidimensional matrix multiplication without resorting to a for-loop. Given the N-by-P matrix A and the N-by-M-by-P matrix B, I want to compute the M-dimensional vector y, defined element-wise as
y(j) = sum_(i = 1,...,N) sum_(k = 1,...,P) A(i,k)*B(i,j,k)
You can linearize A into a row vector, then reshape and permute the array B as a matrix, so that the desired result is just matrix multiplication:
M = 5;
N = 6;
P = 8;
A = rand(N,P);
B = rand(N,M,P);
result = A(:).'*reshape(permute(B, [1 3 2]), [], M);
Or reshape matrix A so that its dimensions are aligned with those of B, use bsxfun to multiply with singleton-expansion, and sum over the two desired dimensions:
result = sum(sum(bsxfun(#times, reshape(A, N, 1, P), B), 1), 3);
I was trying to vectorize a certain weighted sum but couldn't figure out how to do it. I have created a simple minimal working example below. I guess the solution involves either bsxfun or reshape and kronecker products but I still have not managed to get it working.
rng(1);
N = 200;
T1 = 5;
T2 = 7;
A = rand(N,T1,T2);
w1 = rand(T1,1);
w2 = rand(T2,1);
B = zeros(N,1);
for i = 1:N
for j1=1:T1
for j2=1:T2
B(i) = B(i) + w1(j1) * w2(j2) * A(i,j1,j2);
end
end
end
A = B;
You could use a combination of bsxfun, reshape and permute to accomplish this.
We first use permute to move the N dimension to the 3rd dimension of A. We then multiply w1 and the transpose of w2 to create a grid of weights. We can then use bsxfun to perform element-wise multiplication (#times) between this grid and each "slice" of A. We can then reshape the 3D result into M x N and sum across the first dimension.
B = sum(reshape(bsxfun(#times, w1 * w2.', permute(A, [2 3 1])), [], N)).';
Update
There's actually a simpler approach which would use matrix multiplication to perform the summation for you. It unfortunately has to be broken into
% Create the grid of weights
W = w1 * w2.';
% Perform matrix multiplication between a 2D version of A and the weights
B = reshape(A, N, []) * W(:);
Or you could use kron to create the flattened grid of weights:
B = reshape(A, N, []) * kron(w2, w1);
I have two matrices A and B for which I want to do a multiplication for each of their columns to produce a new matrix. The first thing cross my mind is
A = rand(4,3);
B = rand(4,3);
for J=1:SIZE(A,2)
for jj=1:size(B,2)
C(:,:,m) = A(:,j)*B(:,jj)' ;
m = m+1 ;
end
end
But I don't want to use for loops which makes it slow. Is there any way?
I am going to use the matrices of third dimension of C, the ones which are built by multiplication of columns of A and B, Is it better to first build the C and then use its 3rd dimension matrices in each loop or just do the multiplication in each loop?
One approach with bsxfun -
N1 = size(A,1);
N2 = size(B,1);
C = reshape(bsxfun(#times,permute(A,[1 4 3 2]),permute(B,[4 1 2 3])),N1,N2,[])
You could avoid going to the 4th dimension as listed next, but it's still marginally slower than the earlier 4D approach -
C = reshape(bsxfun(#times,permute(A,[1 3 2]),B(:).'),N1,N2,[])
As an alternative to Divakar's answer, you can generate all 4-fold combinations of row and column indices of the two matrices (with ndgrid) and then compute the products:
[m, p] = size(A);
[n, q] = size(B);
[mm, nn, qq, pp] = ndgrid(1:m, 1:n, 1:q, 1:p);
C = reshape(A(mm+(pp-1)*m).*B(nn+(qq-1)*n), m, n, p*q);
I have a Matlab function z = foo(x, y) that takes two column vector as inputs and output a scalar. Now I would like to apply this function to two matrices A(dimension n * d1) and B (dimension n * d2) and generate a d1 * d2 matrix, such that output(i, j) = foo( A(:, i), B(:, j) ). It should basically resemble the behavior of applying the corr function to two matrices.
I tried the solutions in this link, but the encounter the same problem in the first answer, and the meshgrid step in second solution is way too slow.
Any suggestions? Thanks very much in advance!
If foo accepts a matrix then:
%Find dimensions
dA = size(A,2);
dB = size(B,2);
%Generate a list of all possible column pairs for the two matrices
indA = ceil((1:dA*dB)/dA);
indB = mod(0:dA*dB, dB)+1;
X = A(:, indA);
Y = B(:, indB);
z = foo(X,Y)
then you'll probably be able to reshape z to what you want
I'm trying to vectorize the following operation in MATLAB, but it's got me stumped. I've learned from experience that there usually is a way, so I'm not giving up just yet. Any help would be appreciated.
I have a collection of m row-vectors each of size n, arranged in an m x n matrix; call it X.
I also have an m-sized vector of weights, w.
I want to compute a weighted sum of the matrices formed by the self outer products of the vectors in X.
Here is a MWE using a for loop:
m = 100;
n = 5;
X = rand(m, n);
w = rand(1, m);
S = zeros(n, n);
for i = 1 : m
S = S + (w(i) * X(i, :)' * X(i, :));
end
S
This is probably the fastest approach:
S = X' * bsxfun(#times, X, w(:));
You could also do
S = squeeze(sum(bsxfun(#times, ...
bsxfun(#times, conj(X), permute(X, [1 3 2])), w(:)), 1));
(or remove the complex conjugate if not needed).
You can employ two approaches here that use one bsxfun call and few permutes and reshapes. The reshaping trick basically allows us to use the efficient matrix multiplication and thus avoid any extra bsxfun call we might have required otherwise.
Approach #1
[m1,n1] = size(X);
XXmult = bsxfun(#times,X,permute(X,[1 3 2])); %// For X(i, :)' * X(i, :) step
S = reshape(reshape(permute(XXmult,[2 3 1]),[],m1)*w(:),n1,[]) %// multiply weights w
Approach #2
[m1,n1] = size(X);
XXmult = bsxfun(#times,permute(X,[2 3 1]),permute(X,[3 2 1]));
S = reshape(reshape(XXmult,[],m1)*w(:),n1,[])
Shortest answer, and probably fastest:
S = X'*diag(W)*X
Been using it for an unscented Kalman filter, works great.