vectorizing 4 nested for-loops in Matlab - matlab

I'm writing a program for school and I have nested for-loops that create a 4-dimensional array (of the distances between two points with coordinates (x,y) and (x',y')) as below:
pos_x=1:20;
pos_y=1:20;
Lx = length(pos_x);
Ly = length(pos_y);
Lx2 = Lx/2;
Ly2 = Ly/2;
%Distance function, periodic boundary conditions
d_x=abs(repmat(1:Lx,Lx,1)-repmat((1:Lx)',1,Lx));
d_x(d_x>Lx2)=Lx-d_x(d_x>Lx2);
d_y=abs(repmat(1:Ly,Ly,1)-repmat((1:Ly)',1,Ly));
d_y(d_y>Ly2)=Ly-d_y(d_y>Ly2);
for l=1:Ly
for k=1:Lx
for j=1:Ly
for i=1:Lx
distance(l,k,j,i)=sqrt(d_x(k,i).^2+d_y(l,j).^2);
end
end
end
end
d_x and d_y are just 20x20 matrices and Lx=Ly for trial purposes. It's very slow and obviously not a very elegant way of doing it. I tried to vectorize the nested loops and succeeded in getting rid of the two inner loops as:
dx2=zeros(Ly,Lx,Ly,Lx);
dy2=zeros(Ly,Lx,Ly,Lx);
distance=zeros(Ly,Lx,Ly,Lx);
for l=1:Ly
for k=1:Lx
dy2(l,k,:,:)=repmat(d_y(l,:),Ly,1);
dx2(l,k,:,:)=repmat(d_x(k,:)',1,Lx);
end
end
distance=sqrt(dx2.^2+dy2.^2);
which basically replaces the 4 for-loops above. I've now been trying for 2 days but I couldn't find a way to vectorize all the loops. I wanted to ask:
whether it's possible to actually get rid of these 2 loops
if so, i'd appreciate any tips and tricks to do so.
I have so far tried using repmat again in 4 dimensions, but you can't transpose a 4 dimensional matrix so I tried using permute and repmat together in many different combinations to no avail.
Any advice will be greatly appreciated.
thanks for the replies. Sorry for the bad wording, what I basically want is to have a population of oscillators uniformly located on the x-y plane. I want to simulate their coupling and the coupling function is a function of the distance between every oscillator. And every oscillator has an x and a y coordinate, so i need to find the distance between osci(1,1) and osci(1,1),..osci(1,N),osci(2,1),..osci(N,N)... and then the same for osci(1,2) and osci(1,1)...osci(N,N) and so on.. (so basically the distance between all oscillators and all other oscillators plus the self-coupling) if there's an easier way to do it other than using a 4-D array, i'd also definitely like to know it..

If I understand you correctly, you have oscillators all over the place, like this:
Then you want to calculate the distance between oscillator 1 and oscillators 1 through 100, and then between oscillator 2 and oscillators 1 through 100 etc. I believe that this can be represented by a 2D distance matrix, were the first dimension goes from 1 to 100, and the second dimension goes from 1 to 100.
For example
%# create 100 evenly spaced oscillators
[xOscillator,yOscillator] = ndgrid(1:10,1:10);
oscillatorXY = [xOscillator(:),yOscillator(:)];
%# calculate the euclidean distance between the oscillators
xDistance = abs(bsxfun(#minus,oscillatorXY(:,1),oscillatorXY(:,1)')); %'# abs distance x
xDistance(xDistance>5) = 10-xDistance; %# add periodic boundary conditions
yDistance = abs(bsxfun(#minus,oscillatorXY(:,2),oscillatorXY(:,2)')); %'# abs distance y
yDistance(yDistance>5) = 10-yDistance; %# add periodic boundary conditions
%# and we get the Euclidean distance
euclideanDistance = sqrt(xDistance.^2 + yDistance.^2);

I find that imaginary numbers can sometimes help convey coupled information quite well while reducing clutter. My method will double the number of calculations necessary (i.e. I find the distance X and Y then Y and X), and I still need a single for loop
x = 1:20;
y = 1:20;
[X,Y] = meshgrid(x,y);
Z =X + Y*i;
z = Z(:);
leng = length(z);
store = zeros(leng);
for looper = 1:(leng-1)
dummyz = circshift(z,looper);
store(:,looper+1) = z - dummyz;
end
final = abs(store);

Related

How do I build a matrix using two vectors?

So I need to build a matrix of x and y coordinates. I have the x stored in one matrix called vx=0:6000; and y stored in Vy=repmat(300,1,6000);.
Values in x are 0,1,2,...,5999,6000.
Values in y are 300,300,...,300,300.
How do I build a "vector" with the x,y coordinates above?
It would look like this [(0,300);(1,300);...;(5999,300);(6000,300)].
After I finish doing this, I am going to want to find the distance between another fixed point x,y (that I will replicate 6000 times) and the vector above, in order to make a distance graph over time.
Thank you so much!
You can just use horizontal concatenation with []
X = [Vx(:), Vy(:)];
If you want to compute the distance between another point and every point in this 2D array, you could do the following:
point = [10, 100];
distances = sqrt(sum(bsxfun(#minus, X, point).^2, 2));
If you have R2016b or newer you can simply do
distances = sqrt(sum((X - point).^2, 2));
A slightly more elegant alternative (in my opinion) is the following:
Vx = (0:1:6000).';
C = [Vx 0*Vx+300]; % Just a trick to avoid the overly verbose `repmat`.
p = [10,100]; % Define some point of reference.
d = pdist2(C,p); % The default "distance type" is 'euclidian' - which is what you need.
This uses the pdist2 function, introduced in MATLAB 2010a, and requires the Statistics and Machine Learning Toolbox.

Computing a moving average

I need to compute a moving average over a data series, within a for loop. I have to get the moving average over N=9 days. The array I'm computing in is 4 series of 365 values (M), which itself are mean values of another set of data. I want to plot the mean values of my data with the moving average in one plot.
I googled a bit about moving averages and the "conv" command and found something which i tried implementing in my code.:
hold on
for ii=1:4;
M=mean(C{ii},2)
wts = [1/24;repmat(1/12,11,1);1/24];
Ms=conv(M,wts,'valid')
plot(M)
plot(Ms,'r')
end
hold off
So basically, I compute my mean and plot it with a (wrong) moving average. I picked the "wts" value right off the mathworks site, so that is incorrect. (source: http://www.mathworks.nl/help/econ/moving-average-trend-estimation.html) My problem though, is that I do not understand what this "wts" is. Could anyone explain? If it has something to do with the weights of the values: that is invalid in this case. All values are weighted the same.
And if I am doing this entirely wrong, could I get some help with it?
My sincerest thanks.
There are two more alternatives:
1) filter
From the doc:
You can use filter to find a running average without using a for loop.
This example finds the running average of a 16-element vector, using a
window size of 5.
data = [1:0.2:4]'; %'
windowSize = 5;
filter(ones(1,windowSize)/windowSize,1,data)
2) smooth as part of the Curve Fitting Toolbox (which is available in most cases)
From the doc:
yy = smooth(y) smooths the data in the column vector y using a moving
average filter. Results are returned in the column vector yy. The
default span for the moving average is 5.
%// Create noisy data with outliers:
x = 15*rand(150,1);
y = sin(x) + 0.5*(rand(size(x))-0.5);
y(ceil(length(x)*rand(2,1))) = 3;
%// Smooth the data using the loess and rloess methods with a span of 10%:
yy1 = smooth(x,y,0.1,'loess');
yy2 = smooth(x,y,0.1,'rloess');
In 2016 MATLAB added the movmean function that calculates a moving average:
N = 9;
M_moving_average = movmean(M,N)
Using conv is an excellent way to implement a moving average. In the code you are using, wts is how much you are weighing each value (as you guessed). the sum of that vector should always be equal to one. If you wish to weight each value evenly and do a size N moving filter then you would want to do
N = 7;
wts = ones(N,1)/N;
sum(wts) % result = 1
Using the 'valid' argument in conv will result in having fewer values in Ms than you have in M. Use 'same' if you don't mind the effects of zero padding. If you have the signal processing toolbox you can use cconv if you want to try a circular moving average. Something like
N = 7;
wts = ones(N,1)/N;
cconv(x,wts,N);
should work.
You should read the conv and cconv documentation for more information if you haven't already.
I would use this:
% does moving average on signal x, window size is w
function y = movingAverage(x, w)
k = ones(1, w) / w
y = conv(x, k, 'same');
end
ripped straight from here.
To comment on your current implementation. wts is the weighting vector, which from the Mathworks, is a 13 point average, with special attention on the first and last point of weightings half of the rest.

Vectorizing Dependent Nested Loops

I am looking for an efficient way to calculate the pairwise distances between all points in a coordinate matrix of size (nodeCount x 2) using MATLAB. I do not desire to calculate the pairwise distance twice (for example, between nodes 1-2 and between nodes 2-1). I have constructed an outer 'for' loop that increments through each node with an inner loop that evaluates only nodes of a higher index number. The result is an upper triangular matrix populated by the nodal separation distances. I would like to vectorize these computations, or at a minimum increase the efficiency of this operation. Any help would be appreciated.
gap = 10;
for s = 1:(nodeCount);
for ss = s+1:(nodeCount);
if abs(nodeCoord(s,1)-nodeCoord(ss,1)) < gap;
sep(s,ss) = sqrt((nodeCoord(s,1)-nodeCoord(ss,1))^2+(nodeCoord(s,2)-nodeCoord(ss,2))^2);
end
end
end
The loop is not really dependent that perspect. I guess you want to find the distance to all other coordinates try this:
xCoord = [1;2;3;4;5];
yCoord = [1;2;3;4;5]:
xSquare = bsxfun(#(x,y) power((x-y),2),xCoord,xCoord.');
ySquare = bsxfun(#(x,y) power((x-y),2),yCoord,yCoord.');
dist = sqrt(xSquare+ySquare);
xCoord = [1;2;3;4;5];
yCoord = [1;2;3;4;5];
dist = sqrt(pdist2(xCoord,yCoord,'euclidean'));
Can use the function pdist2
Rather than trying to use the fact that the lower triangular elements are not needed, as they are zeros in the output, I think you are better off using a technique that's based on fast matrix multiplication as discussed in this very smart solution to get the full matrix of euclidean distances. To get the desired output of upper triangular matrix, you can wrap the output with triu.
The code that follows next is a slightly modified version of it on the terms that we are calculating the distances among the same pair of coordinates from nodeCoord.
Code
numA = size(nodeCoord,1);
helpA = ones(numA,6);
helpB = ones(numA,6);
for idx = 1:2
sqA_idx = nodeCoord(:,idx).^2;
helpA(:,3*idx-1:3*idx) = [-2*nodeCoord(:,idx), sqA_idx ];
helpB(:,3*idx-2:3*idx-1) = [sqA_idx , nodeCoord(:,idx)];
end
sep = triu(sqrt(helpA(:,1:3) * helpB(:,1:3)')<gap).* sqrt(helpA * helpB');
pdist(nodeCoord) does it in a fast way, but returning the data in a vector. Mapping it back to a matrix costs about the same time as computing the distances:
sep3=zeros(nodeCount,nodeCount);
sep3(tril(true(nodeCount),-1))=pdist(nodeCoord);
sep3=sep3+sep3.';
If you are happy with a lower triangular matrix, you can leave out the last line.

MATLAB vectors, angles, plots

I apologize for the ambiguous title, but I am not entirely sure how to phrase this one. So bear with me.
I have a matrix of data. Each column and row represents a certain vector (column 1 = row 1, column 2 = row 2, etc.), and every cell value is the cosine similarity between the corresponding vectors. So every value in the matrix is a cosine.
There are a couple of things I want to do with this. First, I want to create a figure that shows all of the vectors on it. I know the cosine of the angle between every vector, and I know the magnitude of each vector, but that is the only information I have - is there some algorithm I can implement that will run through all of the various pair-wise angles and display it graphically? That is, I don't know where all the vectors are in relation to each other, and there are too many data points to do this by hand (e.g. if I only had three vectors, and the angles between them all were 45, 12, and 72 degrees it would be trivial). So how do I go about doing this? I don't even have the slightest idea what sort of mathematical function I would need to do this. (I have 83 vectors, so that's thousands of cosine values). So basically this figure (it could be either 2D or multidimensional, and to be honest I would like to do both) would show all of the vectors and how they relate to each other in space (so I could compare both angles and relative magnitudes).
The other thing I would like to do is simpler but I am having a hard time figuring it out. I can convert the cosine values into Cartesian coordinates and display them in a scatter plot. Is there a way to connect each of the points of a scatter plot to (0,0) on the plot?
Finally, in trying to figure out how to do some of the above on my own I have run into some inconsistencies. I calculated the mean angles and Cartesian coordinates for each of the 83 vectors. The math for this is easy, and I have checked and double-checked it. However, when I try to plot it, different plotting methods give me radically different things. So, if I plot the Cartesian coordinates as a scatter plot I get this:
If I plot the mean angles in a compass plot I get this:
And if I use a quiver plot I get something like this (I transformed this a little by shifting the origin up and to the right just so you can see it better):
Am I doing something wrong, or am I misunderstanding the plotting functions I am using? Because these results all seem pretty inconsistent. The mean angles on the compass plot are all <30 degrees or so, but on the quiver plot some seem to exceed 90 degrees, and on the scatter plot they extend above 30 as well. What's going on here?
(Here is my code:)
cosine = load('LSA.txt');
[rows,columns]=size(cosine);
p = cosine.^2;
pp = bsxfun(#minus, 1, p);
sine = sqrt(pp);
tangent = sine./cosine;
Xx = zeros(rows,1);
Yy = zeros(rows,1);
for i = 1:columns
x = cosine(:,i);
y = sine(:,i);
Xx(i,1) = sum(x) * (1/columns);
Yy(i,1) = sum(y) * (1/columns);
end
scatter(Xx,Yy);
Rr = zeros(rows,1);
Uu = zeros(rows,1);
for j = 1:rows
Rr(j,1) = sqrt(Xx(j,1).^2 + Yy(j,1).^2);
Uu(j,1) = atan2(Xx(j,1),Yy(j,2));
end
%COMPASS PLOT
[theta,rho] = pol2cart(Uu,1);
compass(theta,rho);
%QUIVER PLOT
r = 7;
sx = ones(size(cosine))*2; sy = ones(size(cosine))*2;
pu = r * cosine;
pv = r * sine;
h = quiver(sx,sy,pu,pv);
set(gca, 'XLim', [1 10], 'YLim', [1 10]);
You can exactly solve this problem. The dot product calculates the cosine. This means your matrix is actually M=V'*V
This should be solvable through eigenvalues. And you said you also have the length.
Your only problem - as your original matrix the vectors will be 83 dimensional. Not easy to plot in 2 or 3 dimensions. I think you are over simplifying by just using the average angle. There are some techniques called dimensionality reduction - here's a toolbox. I would suggest a sammon projection on 1-cosine (as this would be the distance of points on the unit ball) to calculate the vectors for such a plot.
In the quiver plot, you are plotting all of the data in the cosine and sine matrices. In the other plots, you are only plotting the means. The first two plots appear to match up, so no problem there.
A few other things. I notice that in
Uu(j,1) = atan2(Xx(j,1),Yy(j,2));
Yy(j,2) is not actually defined, so it seems like this code should fail.
Furthermore, you could define Yy and Xx as:
Xx = mean(cosine,2);
Yy = mean(sine,2);
And also get rid of the other for loop:
Rr = sqrt(Xx.^2 + Yy.^2)
Uu = atan2(Xx,Yy)
I still have to think about your first question, but I hope this was helpful.

How to generate random cartesian coordinates given distance constraint in Matlab

I need to generate N random coordinates for a 2D plane. The distance between any two points are given (number of distance is N(N - 1) / 2). For example, say I need to generate 3 points i.e. A, B, C. I have the distance between pair of them i.e. distAB, distAC and distBC.
Is there any built-in function in MATLAB that can do this? Basically, I'm looking for something that is the reverse of pdist() function.
My initial idea was to choose a point (say A is the origin). Then, I can randomly find B and C being on two different circles with radii distAB and distAC. But then the distance between B and C might not satisfy distBC and I'm not sure how to proceed if this happens. And I think this approach will get very complicated if N is a large number.
Elaborating on Ansaris answer I produced the following. It assumes a valid distance matrix provided, calculates positions in 2D based on cmdscale, does a random rotation (random translation could be added also), and visualizes the results:
%Distance matrix
D = [0 2 3; ...
2 0 4; ...
3 4 0];
%Generate point coordinates based on distance matrix
Y = cmdscale(D);
[nPoints dim] = size(Y);
%Add random rotation
randTheta = 2*pi*rand(1);
Rot = [cos(randTheta) -sin(randTheta); sin(randTheta) cos(randTheta) ];
Y = Y*Rot;
%Visualization
figure(1);clf;
plot(Y(:,1),Y(:,2),'.','markersize',20)
hold on;t=0:.01:2*pi;
for r = 1 : nPoints - 1
for c = r+1 : nPoints
plot(Y(r,1)+D(r,c)*sin(t),Y(r,2)+D(r,c)*cos(t));
plot(Y(c,1)+D(r,c)*sin(t),Y(c,2)+D(r,c)*cos(t));
end
end
You want to use a technique called classical multidimensional scaling. It will work fine and losslessly if the distances you have correspond to distances between valid points in 2-D. Luckily there is a function in MATLAB that does exactly this: cmdscale. Once you run this function on your distance matrix, you can treat the first two columns in the first output argument as the points you need.