MatLab Optimize Matrix-wise Element Comparison - matlab

I am currently comparing elements of two matrices A and B in two nested for loops to create a new matrix F with the higher value at each position. All three matrices have equal dimensions.
My current code looks like this (MWE):
F = zeros(n, n)
Indeces_max_i = zeros(n, n)
for i=1 : max
%generate the matrix, mock example
B = randn(n, n)
for c=1 : size(F,1) %Loop over each element, possible to optimise?
for l=1 : size(F,2)
if B(c,l) > F(c,l)
F(c,l) = B(c,l);
Indeces_max_i(c,l) = i;
end
end
end
end
As this can get quite computational heavy, I was wondering if there is a possibility to improve performance of the for loop 1 - and potentially if this solution could also apply if B had size n x n x m (so it would take the highest value of each element along dimensions m)?

Related

How do you find an 'M' by 'M' submatrix in the center of an input 'N' by 'N' matrix? (In Matlab)

How do I write a function in Matlab to output the M x M submatrix at the center of an N x N input matrix? The function should have two input arguments—the N x N input matrix (2D array) and the size of the square submatrix, M, to be extracted from the input matrix. The sole output should be the M x M submatrix at the center of the input matrix. The function should use for loops to extract the submatrix and not use colon notation or any built-in functions for this part of the code. The function should work for any square input matrix where N ≥ 3. If N is even, M should be even. If N is odd, M should be odd.
Here is a picture of my flowchart so far.
Using For-Loops and Offsetting Indexing
Preface:
Here I like to visualize this question as trimming the matrix. The amount to trim I denote in this example is Trim_Amount. The Trim_Amount dictates the size of the sub-matrix and the start point to begin reading/saving the sub-matrix.
Since the trim amount is always taken from each side you can expect the sub-matrix to have dimensions in the form:
Sub-Matrix Width = M - (2 × Trim_Amount)
2 × Trim_Amount will always result in an even number therefore the following can be said:
if M is even → M - (Even Number) → Even Number
if M is odd → M - (Even Number) → Odd Number
Test Output Results:
I recommend going through the code to filter through any unexpected issues.
Full Script:
Dimension = 7;
Matrix = round(100*rand(Dimension));
Trim_Amount = 1;
[Sub_Matrix] = Grab_Sub_Matrix(Matrix,Trim_Amount);
Matrix
Sub_Matrix
%Function definition%
function [Sub_Matrix] = Grab_Sub_Matrix(Matrix,Trim_Amount)
%Minimum of M must be 5 since N >= 3%
[M,~] = size(Matrix);
%Ensuring the trimming factor does not go over possible range%
Max_Trimming_Factor = M - 3;
if(Trim_Amount > Max_Trimming_Factor)
Trim_Amount = Max_Trimming_Factor;
end
%Fill in the boundaries%
Row_Start_Limit = Trim_Amount + 1;
Column_Start_Limit = Trim_Amount + 1;
%Creating sub-matrix based on amount of trimming%
Sub_Matrix = zeros(M-(2*Trim_Amount),M-(2*Trim_Amount));
for Row = 1: length(Sub_Matrix)
for Column = 1: length(Sub_Matrix)
% fprintf("(%d,%d)\n",Row,Column);
Sub_Matrix(Row,Column) = Matrix(Row + Row_Start_Limit-1,Column + Column_Start_Limit-1);
end
end
end
Ran using MATLAB R2019b

Vectorization of double for loop including sine of two variables

I need to numerically evaluate some integrals which are all of the form shown in this image:
These integrals are the matrix elements of a N x N matrix, so I need to evaluate them for all possible combinations of n and m in the range of 1 to N. The integrals are symmetric in n and m which I have implemented in my current nested for loop approach:
function [V] = coulomb3(N, l, R, R0, c, x)
r1 = 0.01:x:R;
r2 = R:x:R0;
r = [r1 r2];
rl1 = r1.^(2*l);
rl2 = r2.^(2*l);
sines = zeros(N, length(r));
V = zeros(N, N);
for i = 1:N;
sines(i, :) = sin(i*pi*r/R0);
end
x1 = length(r1);
x2 = length(r);
for nn = 1:N
for mm = 1:nn
f1 = (1/6)*rl1.*r1.^2.*sines(nn, 1:x1).*sines(mm, 1:x1);
f2 = ((R^2/2)*rl2 - (R^3/3)*rl2.*r2.^(-1)).*sines(nn, x1+1:x2).*sines(mm, x1+1:x2);
value = 4*pi*c*x*trapz([f1 f2]);
V(nn, mm) = value;
V(mm, nn) = value;
end
end
I figured that calling sin(x) in the loop was a bad idea, so I calculate all the needed values and store them. To evaluate the integrals I used trapz, but as the first and the second/third integrals have different ranges the function values need to be calculated separately and then combined.
I've tried a couple different ways of vectorization but the only one that gives the correct results takes much longer than the above loop (used gmultiply but the arrays created are enourmous). I've also made an analytical solution (which is possible assuming m and n are integers and R0 > R > 0) but these solutions involve a cosine integral (cosint in MATLAB) function which is extremely slow for large N.
I'm not sure the entire thing can be vectorized without creating very large arrays, but the inner loop at least should be possible. Any ideas would be be greatly appreciated!
The inputs I use currently are:
R0 = 1000;
R = 8.4691;
c = 0.393*10^(-2);
x = 0.01;
l = 0 # Can reasonably be 0-6;
N = 20; # Increasing the value will give the same results,
# but I would like to be able to do at least N = 600;
Using these values
V(1, 1:3) = 873,379900963549 -5,80688363271849 -3,38139152472590
Although the diagonal values never converge with increasing R0 so they are less interesting.
You will lose the gain from the symmetricity of the problem with my approach, but this means a factor of 2 loss. Odds are that you'll still benefit in the end.
The idea is to use multidimensional arrays, making use of trapz supporting these inputs. I'll demonstrate the first term in your figure, as the two others should be done similarly, and the point is the technique:
r1 = 0.01:x:R;
r2 = R:x:R0;
r = [r1 r2].';
rl1 = r1.'.^(2*l);
rl2 = r2.'.^(2*l);
sines = zeros(length(r),N); %// CHANGED!!
%// V = zeros(N, N); not needed now, see later
%// you can define sines in a vectorized way as well:
sines = sin(r*(1:N)*pi/R0); %//' now size [Nr, N] !
%// note that implicitly r is of size [Nr, 1, 1]
%// and sines is of size [Nr, N, 1]
sines2mat = permute(sines,[1, 3, 2]); %// size [Nr, 1, N]
%// the first term in V: perform integral along first dimension
%//V1 = 1/6*squeeze(trapz(bsxfun(#times,bsxfun(#times,r.^(2*l+2),sines),sines2mat),1))*x; %// 4*pi*c prefactor might be physics, not math
V1 = 1/6*permute(trapz(bsxfun(#times,bsxfun(#times,r.^(2*l+2),sines),sines2mat),1),[2,3,1])*x; %// 4*pi*c prefactor might be physics, not math
The key point is that bsxfun(#times,r.^(2*l+2),sines) is a matrix of size [Nr,N,1], which is again multiplied by sines2mat using bsxfun, the result is of size [Nr,N,N] and an element (k1,k2,k3) corresponds to an integrand at radial point k1, n=k2 and m=k3. Using trapz() with explicitly the first dimension (which would be default) reduces this to an array of size [1,N,N], which is just what you need after a good squeeze(). Update: as per #Dev-iL's comment you should use permute instead of squeeze to get rid of the leading singleton dimension, as that might be more efficent.
The two other terms can be handled the same way, and of course it might still help if you restructure the integrals based on overlapping and non-overlapping parts.

How to sum up weights of equal rows of a matrix without using 'for loops'

I have a m x n matrix 'A' with a m x 1 vector of Weights corresponding to A. I have used 'unique' to find the unique matrix and to find IA and IC. How I can sum up the weights of equal rows in A in a faster way than using two 'for loops'? So far, I have
[Dis_Good_path,IA,IC]=unique(Good_path,'rows','stable');
for i=1:length(IA) % Summing up the weights corresponding to equal paths
Dis_R2(i)=0;
for j=1:length(IC)
if IA(i)==IC(j)
Dis_R2(i)= Dis_R2(i)+R2(j);
end
end
end
An answer with one loop less would look like this :)
[Dis_Good_path,ia,ic]=unique(Good_path,'rows','stable');
Dis_R2 = R2(ia);
rIA = setxor(ia,1:size(Good_path,1));
rIC = ic(rIA);
for i = 1:numel(rIC)
Dis_R2(rIC(i)) = Dis_R2(rIC(i)) + R2(rIA(i));
end

Vectorising 5 nested FOR loops

I am writing a program in MATLAB as a part of my project based on DFT.
Let the N x N data matrix be X and the corresponding DFT matrix be Y, then the DFT coefficients can be expressed as
Y(k1,k2) = ∑(n1=0:N-1)∑(n2=0:N-1)[X(n1,n2)*(WN^(n1k1+n2k2))] (1)
0≤k1,k2≤N-1
Where WN^k=e^((-j2πk)/N)
Since the twiddle factor WN is periodic, (1) can be expressed as
Y(k1,k2)=∑(n1=0:N-1)∑(n1=0:N-1)[X(n1,n2)*(WN^([(n1k1+n2k2)mod N) ] (2)
The exponent ((n1k1 +n2k2)) N = p is satisfied by a set of (n1,n2) for a given (k1,k2). Hence, by grouping such data and applying the property that WN^(p+N /2) = -(WN^P),
(2) can be expressed as
Y(k1,k2)= ∑(p=0:M-1)[Y(k1,k2,p)*(WN^p)] (3)
Where
Y(k1,k2,p)= ∑(∀(n1,n2)|z=p)X(n1,n2) - ∑(∀(n1,n2)|z=p+M)X(n1,n2) (4)
z=[(n1k1+n2k2)mod N] (5)
I am coding a program to find Y(k1,k2,p).ie I need to create slices of 2d matrices(ie a 3D matrix in which each slice is a 2D matrix )from a given 2D square matrix (which is the matrix X)..Dimensions of X can be upto 512.
Based on the above equations,I have written a code as follows.I need to vectorise it.
N=size(X,1);
M=N/2;
Y(1:N,1:N,1:M)=0;
for k1 = 1:N
for k2 = 1:N
for p= 1:M
for n1=1:N
for n2=1:N
N1=n1-1; N2=n2-1; P=p-1; K1=k1-1; K2=k2-1;
z=mod((N1*K1+N2*K2),N);
if (z==P)
Y(k1,k2,p)= Y(k1,k2,p)+ X(n1,n2);
elsif (z==(P+M))
Y(k1,k2,p)= Y(k1,k2,p)- X(n1,n2);
end
end
end
end
end
As there is 5 FOR loops, the execution time is very large for large dimensions of N. Hence please provide me a solution for eliminating the FOR loops and vectorising the code..I need to make the code execute in maximum speed...Thanks Again..
Here is a first hint to vectorize the most inner loop.
From your code, we can notice that n1, N1, P, K1 and K2 are constant in this loop.
So we can rewrite z as a mask vector as follows:
z = mod(N1*K1+K2*(0:N-1));
Then your if-statement is equivalent to adding the sum of all elements in X so that z==P minus the sum of all elements in X so that z==P+M. Rewriting this is straightforward:
Y(k1,k2,p)= Y(k1,k2,p)+sum(X(n1,z==P))-sum(X(n1,z==P+M));
So your program can be first written as follows:
N=size(X,1);
M=N/2;
Y(1:N,1:N,1:M)=0;
for k1 = 1:N
for k2 = 1:N
for p= 1:M
for n1=1:N
N1=n1-1; P=p-1; K1=k1-1; K2=k2-1;
z=mod(N1*K1+K2*(0:N-1),N);
Y(k1,k2,p) = sum(X(n1,z==P))-sum(X(n1,z==P+M));
end
end
end
end
Then you can do the same thing with n1; for that, you need to construct a 2D array for z, such as:
z = mod(K1*repmat(0:N-1,N,1)+K2*repmat((0:N-1).',1,N));
Notice that size(z)==size(X).Then the 2D sum for Y becomes:
Y(k1,k2,p) = Y(k1,k2,p)+sum(X(z==P))-sum(X(z==P+M));
The += operation is here no longer needed, since you access only once to each element of Y:
Y(k1,k2,p)= sum(X(n1,z==P))-sum(X(n1,z==P+M));
And so we discard one more loop:
N=size(X,1);
M=N/2;
Y(1:N,1:N,1:M)=0;
for k1 = 1:N
for k2 = 1:N
for p= 1:M
P=p-1; K1=k1-1; K2=k2-1;
z = mod(K1*repmat(0:N-1,N,1)+K2*repmat((0:N-1).',1,N));
Y(k1,k2,p) = sum(X(z==P))-sum(X(z==P+M));
end
end
end
Concerning the other loops, I don't think it worths it to vectorize them, as you have to build a 5D array, which could be very huge in memory. My advise is to keep z as a 2D array, as it is of the size of X. If it does not fit well in memory, just vectorize the most inner loop.

Incrementing values in a sparse matrix takes very long

i'm running the following code, where M is a ~200,000 by ~200,000 sparse matrix and points is ~200,000 by 2 matrix
inds=sub2ind(size(M),points(:,1),points(:,2));
M(inds)=M(inds)+1;
the problem is that the second line takes very long to run (15-90 seconds).
the operation takes longer depending on how many of the indices in inds are 'new' (i.e. that don't already have a value in the sparse matrix)
is there a more efficient way to do this?
Here's an idea:
M = M + sparse(points(:,1),points(:,2),1,size(M,1),size(M,2),size(points,1));
Just so you know,
S = sparse(i,j,s,m,n,nzmax) uses vectors i, j, and s to generate an
m-by-n sparse matrix such that S(i(k),j(k)) = s(k), with space
allocated for nzmax nonzeros. Vectors i, j, and s are all the same
length. Any elements of s that are zero are ignored, along with the
corresponding values of i and j. Any elements of s that have duplicate
values of i and j are added together.
For the curious:
M = sprand(200000,200000,1e-6);
points = [randperm(200000) ; randperm(200000)]'; %'//Initialization over
Mo = M;
tic;
inds=sub2ind(size(Mo),points(:,1),points(:,2));
Mo(inds) = Mo(inds)+1;
toc
tic;
M = M + sparse(points(:,1),points(:,2),1,size(M,1),size(M,2),size(points,1));
toc