Vectorize 3d operation in matlab - arrays

Can you vectorize a operation where the columns of U are added to cwx and the result is stored in a 3d array?
A non-vectorized solution:
cwx =rand([500,100]);
U = rand([500 10]);
F = zeros([500 100 10]);
for y = 1:10
F(:,:,y) = bsxfun(#plus,U(:,y),cwx);
end

It can be done with a single call to bsxfun, provided you first permute the second and third dimensions of U:
F = bsxfun(#plus, permute(U, [1 3 2]), cwx);

Related

Multiplying arrays of matrices in MATLAB

I have a data set that consists of two 1800 x 900 x 3 x 3 arrays, which should each be interpreted as a 1800x900 array of 3x3 matrices. As part of the analysis, at one point I need to create another such array by, at each point of the 1800x900 array, multiplying the corresponding 3x3 matrices together.
There seem to be two ways to do this that I can think of. The obvious way is
C = zeros(size(A))
for i = 1:900
for j = 1:1800
C(i,j,:,:) = A(i,j,:,:)*B(i,j,:,:)
end
end
But that's quite a long loop and doesn't really take advantage of MATLAB's vectorization. The other way is
C = zeros(size(A))
for i = 1:3
for j = 1:3
for k = 1:3
C(:,:,i,j) = C(:,:,i,j) + A(:,:,i,k).*B(:,:,k,j)
end
end
end
where the large dimensions are getting vectorized and I'm basically using the for loops to implement the Einstein summation convention. This seems really inelegant, though, which makes me think there should be a better way. Is there?
Perfect job for bsxfun with permute:
C = permute(sum(bsxfun(#times, A, permute(B, [1 2 5 3 4])), 4), [1 2 3 5 4]);
In R2016b onwards you can avoid bsxfun thanks to implicit singleton expansion:
C = permute(sum(A .* permute(B, [1 2 5 3 4]), 4), [1 2 3 5 4]);

Multiply each row of a matrix by another matrix

Say I have the following matrix
B = [1 2 3;4 5 6;7 8 9;10 11 12]
and another matrix
A = [a b c;d e f;g h i]
How do I multiply each row of matrix B by the matrix A (without using a for loop), i.e.
for i = 1:4
c(i) = B(i,:)*A*B(i,:)'
end
many thanks in advance.
You can use:
c = diag(B*A*B.');
However, this computes a whole 4×4 matrix only to extract its diagonal, so it's not very efficient.
A more efficient way that only computes the desired values is:
c = sum(bsxfun(#times, permute(sum(bsxfun(#times, B, permute(A, [3 1 2])), 2), [1 3 2]), B), 2);
Here is a breakdown of the above code:
c1 = sum(bsxfun(#times, B, permute(A, [3 1 2])), 2); % B(i,:)*A
c = sum(bsxfun(#times, permute(c1, [1 3 2]), B), 2); % (B(i,:)*A)*B(i,:)'
The first permute is used so that the number of columns in B matches the number of columns in A. Following the element-wise multiplication in bsxfun() each row is summed up (remember, permute shifted the rows into the 2nd-dimension), reproducing the effect of the vector-matrix multiplication B(i,:) * A occurring in the for loop.
Following the first sum, the 2nd-dimension is a singleton dimension. So, we use the second permute to move the 2nd-dimension into the 3rd-dimension and produce a 2-D matrix. Now, both c1 and B are the same size. Following element-wise multiplication in the second bsxfun() each column is summed up (remember, permute shifted columns back into the 2nd-dimension), reproducing the effect of B(i,:) * A * B(i,:)'.
Take note of a hidden advantage in this approach. Since we are using element-wise multiplication to replicate the results of matrix multiplication, order of the arguments doesn't matter in the bsxfun() calls. One less thing to worry about!
Or, from Matlab R2016b onwards, you can replace bsxfun(#times,...) by .*, thanks to implicit expansion:
c = sum(permute(sum(B.*permute(A, [3 1 2]), 2), [1 3 2]).*B, 2);

Finding the best way to define a function of dot product for a 3D grid

I am trying to build the following function in a three dimensional domain.
where is a constant vector, is the position vector, is a constant, and is time.
is a vector of size [1 3], is the array of size [NX*NY*NZ 3] that represents the points in the three-dimensional domain, is constant, and is array of size [1 NT].
The following is the setup of the problem.
dx = 0.1;
dy = 0.5;
dz = 0.1;
[x, y, z] = meshgrid( (1:100)*dx, (1:100)*dy, (1:100)*dz );
X = [x(:) y(:) z(:)];
k = [1 2 3];
c = 0.5;
t = 0:0.1:1;
In fact, the following loop works but it is very slow (~200 seconds).
f = zeros(numel(X)/3, numel(t));
for n = 1:numel(t)
for i = 1:numel(X)/3
f(i, n) = tan(dot(k, X(i,:)+c*t(n)));
end
end
I thought about using arrayfun and repeating the vector k using repmat and dot it with X in the second dimension but I don't know what I should do for the multiplication of c and t.
What would be an efficient way of defining the function for all the points and all the times? The output of this function, for example, looks like an array of size [NX*NY*NZ NT].
bsxfun approach to speedup f calculation -
t1 = bsxfun(#plus,X,permute(c*t,[1 3 2]));
t2 = permute(k(ones(numel(t),1),:),[3 2 1]);
t3 = bsxfun(#times,t1,t2);
f = tan(squeeze(sum(t3,2)));
Or a slight variant -
t1 = bsxfun(#plus,X,permute(c*t,[1 3 2]));
t3 = bsxfun(#times,t1,k);
f = tan(squeeze(sum(t3,2)));
f = tan(sum(bsxfun(#times, permute(k, [1 3 2]), bsxfun(#plus, permute(X, [1 3 2]), c*t)), 3));
Or replace the outer bsxfun by matrix multiplication. This seems to be slightly faster:
f = tan(reshape(reshape(bsxfun(#plus, permute(X, [1 3 2]), c*t), [], 3) * k(:), [size(X,1) numel(t)]));

how to assemble vectors into matrix using for loop in MATLAB

I want to assemble a matrix from 5 vectors using a for loop in Matlab.
How can I do that? Or is there a simple alternative to do the same?
Here's an example: I have 5 vectors:
A = [1 2 3]
B = [1 2 3]
C = [1 2 3]
D = [1 2 3]
E = [1 2 3]
I want to create a matrix Z of size 3 x 5 from these vectors.
for loops are generally a bad idea in Matlab. Use vectorized syntax:
Z = [A;B;C;D;E]'; %'// vertical concatenation (5x3), then transpose to get 3x5
>> size(Z)
ans =
3 5
You can also transpose the vectors first and then concatenate them:
Z = [A' B' C' D' E']; %'// this comment is a syntax highlighting hotfix
>> size(Z)
ans =
3 5
If you really insist on using a for loop, here's how you would do that:
Z(3,5) = 0; %// preallocate a 3x5 numerical matrix
for i=1:3
Z(i,1) = A(i);
Z(i,2) = B(i);
Z(i,3) = C(i);
Z(i,4) = D(i);
Z(i,5) = E(i);
end
But it really makes no sense at all to do it this way...

Selecting elements from an array in MATLAB

I know that in MATLAB, in the 1D case, you can select elements with indexing such as a([1 5 3]), to return the 1st, 5th, and 3rd elements of a. I have a 2D array, and would like to select out individual elements according to a set of tuples I have. So I may want to get a(1,3), a(1,4), a(2,5) and so on. Currently the best I have is diag(a(tuples(:,1), tuples(:,2)), but this requires a prohibitive amount of memory for larger a and/or tuples. Do I have to convert these tuples into linear indices, or is there a cleaner way of accomplishing what I want without taking so much memory?
Converting to linear indices seems like a legitimate way to go:
indices = tuples(:, 1) + size(a,1)*(tuples(:,2)-1);
selection = a(indices);
Note that this is also implement in the Matlab built-in solution sub2ind, as in nate'2 answer:
a(sub2ind(size(a), tuples(:,1),tuples(:,2)))
however,
a = rand(50);
tuples = [1,1; 1,4; 2,5];
start = tic;
for ii = 1:1e4
indices = tuples(:,1) + size(a,1)*(tuples(:,2)-1); end
time1 = toc(start);
start = tic;
for ii = 1:1e4
sub2ind(size(a),tuples(:,1),tuples(:,2)); end
time2 = toc(start);
round(time2/time1)
which gives
ans =
38
so although sub2ind is easier on the eyes, it's also ~40 times slower. If you have to do this operation often, choose the method above. Otherwise, use sub2ind to improve readability.
if x and y are vectors of the x y values of matrix a, then sub2und should solve your problem:
a(sub2ind(size(a),x,y))
For example
a=magic(3)
a =
8 1 6
3 5 7
4 9 2
x = [3 1];
y = [1 2];
a(sub2ind(size(a),x,y))
ans =
4 1
you can reference the 2D matlab position with a 1D number as in:
a = [3 4 5;
6 7 8;
9 10 11;];
a(1) = 3;
a(2) = 6;
a(6) = 10;
So if you can get the positions in a matrix like this:
a([(col1-1)*(rowMax)+row1, (col2-1)*(rowMax)+row2, (col3-1)*(rowMax)+row3])
note: rowmax is 3 in this case
will give you a list of the elements at col1/row1 col2/row2 and col3/row3.
so if
row1 = col1 = 1
row2 = col2 = 2
row3 = col3 = 3
you will get:
[3, 7, 11]
back.

Resources