Multidimensional Arrays Multiplication in Matlab - matlab

I have the following three arrays in Matlab:
A size: 2xMxN
B size: MxN
C size: 2xN
Is there any way to remove the following loop to speed things up?
D = zeros(2,N);
for i=1:N
D(:,i) = A(:,:,i) * ( B(:,i) - A(:,:,i)' * C(:,i) );
end
Thanks

Yes, it is possible to do without the for loop, but whether this leads to a speed-up depends on the values of M and N.
Your idea of a generalized matrix multiplication is interesting, but it is not exactly to the point here, because through the repeated use of the index i you effectively take a generalized diagonal of a generalized product, which means that most of the multiplication results are not needed.
The trick to implement the computation without a loop is to a) match matrix dimensions through reshape, b) obtain the matrix product through bsxfun(#times, …) and sum, and c) get rid of the resulting singleton dimensions through reshape:
par = B - reshape(sum(bsxfun(#times, A, reshape(C, 2, 1, N)), 1), M, N);
D = reshape(sum(bsxfun(#times, A, reshape(par, 1, M, N)), 2), 2, N);
par is the value of the inner expression in parentheses, D the final result.
As said, the timing depends on the exact values. For M = 100 and N = 1000000 I find a speed-up by about a factor of two, for M = 10000 and N = 10000 the loop-less implementation is actually a bit slower.

You may find that the following
D=tprod(A,[1 -3 2],B-tprod(A,[-3 1 2],C,[-3 2]),[-3 2]);
cuts the time taken. I did a few tests and found the time was cut in about half.
tprod is available at
http://www.mathworks.com/matlabcentral/fileexchange/16275
tprod requires that A, B and C are full (not sparse).

Related

Simplify nested loops with condition

I have a matlab program with 5 nested
for
loops and a
if
condition like this:
for x0=1:N
for y0=1:N
for k=1:N
for x1=1:N
for y1=1:N
if ~((y1-x1>N/2)||(x1-y1>N/2)) && ~((y0-x0>N/2)||(x0-y0>N/2))
A(x0,y0)=A(x0,y0)+2^(k*((x0-y0)+(x1-y1)))*B(x1,y1)
end
end
end
end
end
end
where A and B are two matrices. How can I make this program run faster?
I've tried to use meshgrid but it seems doesn't work because there's a
if
condition.
Lets be smart about loops and conditions first, as you are using the loop indices as condition variables.
We start with
~(y1-x1>N/2)||(x1-y1>N/2), or way clearer, abs(y1-x1)<N/2.
Instead of having an if condition, why not enforce y1 to be in range, always?
The last loop can be written as y1=max(x1-N/2,1):min(x1+N/2,N), and thus the entirety of the first part of the if condition is not needed. We can do the same for the other variables, of course:
for x0=1:N
for y0=max(x0-N/2,1):min(x0+N/2,N)
for k=1:N
for x1=1:N
for y1=max(x1-N/2,1):min(x1+N/2,N)
A(x0,y0)=A(x0,y0)+2^(k*((x0-y0)+(x1-y1)))*B(x1,y1)
end
end
end
end
end
Now, for clarity, lets reshuffle and vectorize that k. There is no need for it to be the middle loop, in fact, its only feature as the middle loop is to confuse the person reading the code. But aside from that, there is no need for it to be a loop either.
k=1:N;
for x0=1:N
for y0=max(x0-N/2,1):min(x0+N/2,N)
for x1=1:N
for y1=max(x1-N/2,1):min(x1+N/2,N)
A(x0,y0)=A(x0,y0)+sum(2.^(k*((x0-y0)+(x1-y1))))*B(x1,y1)
end
end
end
end
Now, is this faster?
No. MATLAB is really good at optimizing your code, so it is not faster. But at least its way way clearer, so I guess you got that going for you. But you need it faster! Well.... I am not sure you can. You have a 5 nested loops, that is just super slow. I don't think you can meshgrid this, even without the conditions, because you intermingle all loops. meshgrid is good when well, you do operations on a mesh grid, but in your case you use all x1,y1 for every x0,y0 and thus its not a mesh operation.
Here is a vectorized solution:
x0 = (1:N).';
y0 = 1:N;
x1 = (1:N).';
y1 = 1:N;
k = reshape(1:N, 1, 1, N);
conditiona = ~((y0-x0 > N/2) | (x0-y0 > N/2));
conditionb = ~((y1-x1 > N/2) | (x1-y1 > N/2));
a = 2 .^ (k .* (x0-y0)) .* conditiona;
b = 2 .^ (k .* (x1-y1)) .* B .* conditionb;
bsum = squeeze(sum(sum(b, 1) ,2));
A = A + reshape(reshape(a, [] , N) * bsum ,N ,N);
Note that two 3D arrays a and b are created that may/may not require a lot of memory. In such a case you need to loop over k. For example in the first iteration set k to 1:5. In the second iteration set it to 6:10 and so on. You need to addv the result of each iteration to the previous iteration to form the final A.
Explanation
This function can be vectorized by implicit expansion (that is more efficient than using meshgrid) and using element-wise operators like .^ and .* instead of ^ and * operators. As a result a 5D array is created (because we have 5 loop variables) that should be summed over 3-5th dimensions to produce the final 2D matrix. However that may require a lot of memory. Another point is that functions that contains the sum of products usually can be written as efficient matrix multiplication.
The expression:
2^(k*((x0-y0)+(x1-y1)))*B(x1,y1);
can be written as:
2 .^ (k .* (x0-y0)) .* 2 .^ (k .* (x1-y1)) .* B(x1, y1)
------- a -------- ------------- b ---------------
that is the multiplication of two sub-expressions that each has 3 dimensions, because each contains just 3 loop variables. So the 5D problem is reduced to 3D.
The if condition has also two sub-expressions that each can be multiplied by a and b sub-expressions:
conditiona = ~((y0-x0 > N/2) | (x0-y0 > N/2));
conditionb = ~((y1-x1 > N/2) | (x1-y1 > N/2));
a = 2 .^ (k .* (x0-y0)) .* conditiona;
b = 2 .^ (k .* (x1-y1)) .* B .* conditionb;
A for loop can be formed just by using two loop variables x0 and y0:
for x0=1:N
for y0=1:N
A(x0,y0)=A(x0,y0)+ sum(sum(sum(a(x0,x0, :) .* b, 3), 2), 1);
%or simply A(x0,y0)=A(x0,y0)+ sum(a(x0,x0, :) .* b, "all");
end
end
That can be simplified to the following loop by precomputing sum of b:
bsum = sum(sum(b, 1) ,2);
% bsum = sum(b ,[1, 2]);
for x0=1:N
for y0=1:N
A(x0,y0)=A(x0,y0)+ sum(a(x0,x0, :) .* bsum, 3);
% or as vector x vector multiplication
% A(x0,y0)=A(x0,y0)+ squeeze(a(x0,x0, :)).' * squeeze(bsum);
end
end
Here the loop can be prevented by using the matrix x vector multiplication:
A = A + reshape(reshape(a, [] , N) * bsum ,N ,N);
Update this solution may not be faster under Matlab, because the execution engine can optimise the loops in the original code. (It does provide a speedup under Octave.)
One trick to deal with if statements within loops is to turn the if statement (or part of it) into a logical matrix. You can then multiply the logical matrix elementwise by the matrix of values you are adding in each step. A false value will evaluate to zero and will not change the result.
This only works if each element can be computed independently of the others.
It will generally make the actual calculation line slower, but in Matlab this is often outweighed by the huge speed improvement from the the removal of the for loops.
In your example, we can use this idea with meshgrid to remove the loops over x0 and y0.
The calculation line needs to become an elementwise matrix caluclation, so elementwise operators .*, .^ and | replace *, ^ and |.
% Warning: Y0 and X0 are swapped in this example
[Y0, X0] = meshgrid(1:N,1:N);
% Create a logical matrix which represents part of the if statement
C = ~((Y0-X0>N/2) | (X0-Y0>N/2));
for k=1:N
for x1=1:N
for y1=1:N
if ~((y1-x1>N/2)||(x1-y1>N/2))
% Multiply by C elementwise
A = A + C.*2.^(k*((X0-Y0)+(x1-y1)))*B(x1,y1);
end
end
end
end
You could even take this concept further and remove more loops using multidemensional ndgrids, but it becomes more complex (you have to start summing over dimensions) and the multidimensional arrays become unwieldy if N is large.
Note: be careful with index order. meshgrid defines y as rows and x as columns, so matrix indexing is A(y,x) but you are using A(x,y). So to make your example work I've switched x and y in the output of meshgrid.

Tips to reduce the execution time of this Matlab loop containing several matrix operations

I am trying to evaluate the matrices Y(p,k) and Z(p,k) using the following simplified Matlab code.
They depend on some matrices A(j,k), B(j,p) and C(j,k) which I am able to precalculate, so I have just initialised them as random arrays for this MWE. (Note that B is a different size to A and C).
Nj = 5000;
Nk = 1000;
Np = 500; % max loop iterations
A = rand(Nj,Nk); % dummy precalculated matrices
B = rand(Nj,Np);
C = rand(Nj,Nk);
Y = zeros(Np,Nk); % allocate storage
Z = zeros(Np,Nk);
tic
for p = 1:Np
X = A .* B(:,p);
Y(p,:) = sum( X , 1 );
Z(p,:) = sum( C .* X , 1 );
end
toc % Evaluates to 11 seconds on my system
As can be seen above, I am repeating my calculation by looping over index p (because the matrix B depends on p).
I have managed to get this far by moving everything which can be precalculated outside the loop (contained in A, B and C), but on my system this code still takes around 11 seconds to execute. Can anyone see a way in Matlab to speed this up, or perhaps even remove the loop and process all at once?
Thank you
I think the following should be equivalent and much faster:
Y = B' * A;
Z = B' * (A.*C);
Notes:
If B is complex-valued then you should use .' for transposition instead.
You may also want to pre-compute B directly in transposed form (i.e. as a Np by Nj matrix) to avoid the transposition altogether.
If C is not needed anywhere else, then pre-compute it as A.*C instead in order to avoid the extra element-wise multiplication.

element by element matrix multiplication in Matlab

So I have the following matrices:
A = [1 2 3; 4 5 6];
B = [0.5 2 3];
I'm writing a function in MATLAB that will allow me to multiply a vector and a matrix by element as long as the number of elements in the vector matches the number of columns. In A there are 3 columns:
1 2 3
4 5 6
B also has 3 elements so this should work. I'm trying to produce the following output based on A and B:
0.5 4 9
2 10 18
My code is below. Does anyone know what I'm doing wrong?
function C = lab11(mat, vec)
C = zeros(2,3);
[a, b] = size(mat);
[c, d] = size(vec);
for i = 1:a
for k = 1:b
for j = 1
C(i,k) = C(i,k) + A(i,j) * B(j,k);
end
end
end
end
MATLAB already has functionality to do this in the bsxfun function. bsxfun will take two matrices and duplicate singleton dimensions until the matrices are the same size, then perform a binary operation on the two matrices. So, for your example, you would simply do the following:
C = bsxfun(#times,mat,vec);
Referencing MrAzzaman, bsxfun is the way to go with this. However, judging from your function name, this looks like it's homework, and so let's stick with what you have originally. As such, you need to only write two for loops. You would use the second for loop to index into both the vector and the columns of the matrix at the same time. The outer most for loop would access the rows of the matrix. In addition, you are referencing A and B, which are variables that don't exist in your code. You are also initializing the output matrix C to be 2 x 3 always. You want this to be the same size as mat. I also removed your checking of the length of the vector because you weren't doing anything with the result.
As such:
function C = lab11(mat, vec)
[a, b] = size(mat);
C = zeros(a,b);
for i = 1:a
for k = 1:b
C(i,k) = mat(i,k) * vec(k);
end
end
end
Take special note at what I did. The outer-most for loop accesses the rows of mat, while the inner-most loop accesses the columns of mat as well as the elements of vec. Bear in mind that the number of columns of mat need to be the same as the number of elements in vec. You should probably check for this in your code.
If you don't like using the bsxfun approach, one alternative is to take the vector vec and make a matrix out of this that is the same size as mat by stacking the vector vec on top of itself for as many times as we have rows in mat. After this, you can do element-by-element multiplication. You can do this stacking by using repmat which repeats a vector or matrices a given number of times in any dimension(s) you want. As such, your function would be simplified to:
function C = lab11(mat, vec)
rows = size(mat, 1);
vec_mat = repmat(vec, rows, 1);
C = mat .* vec_mat;
end
However, I would personally go with the bsxfun route. bsxfun basically does what the repmat paradigm does under the hood. Internally, it ensures that both of your inputs have the same size. If it doesn't, it replicates the smaller array / matrix until it is the same size as the larger array / matrix, then applies an element-by-element operation to the corresponding elements in both variables. bsxfun stands for Binary Singleton EXpansion FUNction, which is a fancy way of saying exactly what I just talked about.
Therefore, your function is further simplified to:
function C = lab11(mat, vec)
C = bsxfun(#times, mat, vec);
end
Good luck!

Matrix multiplication with different dimensions

I need to vectorize the following loop
a=rand(m,n,k)
b=rand(n,k)
c=zeros(m,k)
for ik=1:k
c(:,ik)=a(:,:,ik)*b(:,ik)
end
I couldn't find any matlab function for doing this and I think bsxfun #multiply does something different. Could you please help on doing this?
I think you can use bsxfun as follows (can't test this right now - let me know if this gives you trouble):
c = squeeze(sum(bsxfun(#times, a, b), 2));
The bsxfun will expand the matrix b and then do element-by-element multiplication. The sum operation on the second dimension takes care of the "matrix multiplication" aspect. It is possible that you need to expand b to have an explicit singleton first dimension:
c = squeeze(sum(bsxfun(#times, a, reshape(b, 1, n, k)), 2));

Multiplying a 3x3 matrix to 3nx1 array without using loops

In my code, I have to multiply a matrix A (dimensions 3x3) to a vector b1 (dimensions 3x1), resulting in C. So C = A*b1. Now, I need to repeat this process n times keeping A fixed and updating b to a different (3x1) vector each time. This can be done using loops but I want to avoid it to save computational cost. Instead I want to do it as matrix and vector product. Any ideas?
You need to build a matrix of b vectors, eg for n equal to 4:
bMat = [b1 b2 b3 b4];
Then:
C = A * bMat;
provides the solution of size 3x4 in this case. If you want the solution in the form of a vector of length 3n by 1, then do:
C = C(:);
Can we construct bMat for arbitrary n without a loop? That depends on what the form of all your b vectors is. If you let me know in a comment, I can update the answer.