Multidimensional Matrix Multiplication - matlab

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

Related

Linear combination of the Slices in a 3D

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

Vectorize weighted sum matlab

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

Creating array from raising matrix to power in matlab

I need to create a 3-dimensional array from raising all elements of the matrix to different power given by a vector. Is there a way to avoid a loop over the power?
For example, if A is a scalar, I could do
A = 2;
b = 1:10;
C = A.^b;
If A is a vector, I could do
A = [1, 2, 3];
b = 1:10;
C = bsxfun(#power, A, (0:5)');
What can I do if A is a matrix?
Use bsxfun again, but arrange the exponents (b) in a third dimension:
A = [1, 2 3; 4 5 6];
b = 1:10;
C = bsxfun(#power, A, permute(b(:), [2 3 1]));
This gives you a 3D array as result (2x3x10 in this case).
If exponents are consecutive values, the following code may be faster:
n = 10; %// compute powers with exponents 1, 2, ..., n
C = cumprod(repmat(A, [1 1 n]) ,3);
Try like this,
% m & n being the dimensions of matrix A
A = randi(9,[m n]);
P = cat(3,1*ones(m,n),2*ones(m,n),3*ones(m,n));
C = bsxfun(#power, A, P);

Efficient computation of Euclidean distance between cell arrays

I have an a-by-b cell array, C. In each element, there is a float array.
I now want to create a new symmetric matrix M. Each element (i, j) in M is to be set to the sum of the Euclidean distances of all the respective float arrays in C.
For example, to find M(i,j), I would take the set of b float arrays in C along row i, and the set of b float arrays in C along row j, find the Euclidean distance between each array across the two sets, and then sum up the b x b values. C{i,j} is a column vector. All columns are the same length.
Below is my "brute force" implementation of this:
for i=1:a
for j=1:a
dist_sum = 0;
for k=1:b
for l=1:b
dist = sqrt(sum((C{i, k} - C{j, l}) .^ 2));
dist_sum = dist_sum + dist;
end
end
M(j, i) = dist_sum;
M(i, j) = dist_sum;
end
end
My question: Is there a more efficient way of doing this using matrix operations, without having to explicitly compute each Euclidean distance in turn?
It would be better to use a 3D array, instead of a 2D cell array of equal-size column vectors.
If you have a cell array: first convert into a 3D array (D in my code); then it's easy to compute distances with bsxfun; and finally apply sum:
D = permute(C, [3 1 2]);
D = reshape(cat(2, D{:}), [], size(C,1), size(C,2)); %// 3D array
dist = sqrt(sum(bsxfun(#minus, D, permute(D, [1 4 5 2 3])).^2)); %// distances
M = squeeze(sum(sum(dist, 3), 5)); %// sum of distances
Example: with
>> C = {[1; 2], [30; 40], [0; 1]; [5; 7] [19; 17] [4; 5]}; %// a is 2, b is 3
the result of both your code and mine is
M =
196.8391 182.8791
182.8791 77.3002
Before Calculating Euclidean Distance:
Can convert the cell array to matrix by using cell2mat... then u can use following methods..
Method 1:
G = rand(1, 72);
G2 = rand(1, 72);
D = sqrt(sum((G - G2) .^ 2));
Method 2:
V = G - G2;
D = sqrt(V * V');
Method 3:
D = norm(G - G2);
Method 4:
D = pdist2(G,G2);
I would suggest converting the matrix elements to a vector using (:) and then using distance2curve.m function file from Matlab File Exchange to find the minimum/Euclidean distance between the two arrays.
Let's say the two cell arrays are A and B with matrices containing the row and column indices for each cell array denoted as 'indA' and 'indB', where each row in 'indA' and 'indB' contains the row element and column elements of 'A' and 'B', respectively. Now use the above function as:
[M, distance, t] = distance2curve(indA(:, :), indB(:, :))
The output variable M should contain the minimum/Euclidean distance between the two arrays that you are looking for.

Matlab - Multiplying a matrix with every matrix of a 3d matrix

I have two matlab questions that seem closely related.
I want to find the most efficient way (no loop?) to multiply a (A x A) matrix with every single matrix of a 3d matrix (A x A x N). Also, I would like to take the trace of each of those products.
http://en.wikipedia.org/wiki/Matrix_multiplication#Frobenius_product
This is the inner frobenius product. On the crappy code I have below I'm using its secondary definition which is more efficient.
I want to multiply each element of a vector (N x 1) with its "corresponding" matrix of a 3d matrix (A x A x N).
function Y_returned = problem_1(X_matrix, weight_matrix)
% X_matrix is the randn(50, 50, 2000) matrix
% weight_matrix is the randn(50, 50) matrix
[~, ~, number_of_matries] = size(X_matrix);
Y_returned = zeros(number_of_matries, 1);
for i = 1:number_of_matries
% Y_returned(i) = trace(X_matrix(:,:,i) * weight_matrix');
temp1 = X_matrix(:,:,i)';
temp2 = weight_matrix';
Y_returned(i) = temp1(:)' * temp2(:);
end
end
function output = problem_2(vector, matrix)
% matrix is the randn(50, 50, 2000) matrix
% vector is the randn(2000, 1) vector
[n1, n2, number_of_matries] = size(matrix);
output = zeros(n1, n2, number_of_matries);
for i = 1:number_of_matries
output(:, :, i) = vector(i) .* matrix(:, :, i);
end
output = sum(output, 3);
end
I assume you mean element-wise multiplication:
Use bsxfun:
A = 10;
N = 4;
mat1 = randn(A,A);
mat2 = randn(A,A,N);
result = bsxfun(#times, mat1, mat2);
Use bsxfun with permute to align dimensions:
A = 10;
N = 4;
vec1 = rand(N,1);
mat2 = randn(A,A,N);
result = bsxfun(#times, permute(vec1,[2 3 1]), mat2);