I understand that when plotting an equation for x iterations that when you use a pause with a decimal number you can speed up the time it takes to go from one iteration to the next. My question is there a way to speed it up even more? Basically I am running a upwind 1D advection equation and my plot is doing pretty slow even when I put a pause of say 0.0001. Any advice on making the speed of this program increase for plotting or would I just need to let it run its course.
Here is the code I am using:
clear;
clc;
%Set initial values
xmin=0;
xmax=1;
N=101; %Amount of segments
dt= 0.0001; % Time step
t=0; % t initial
tmax=2; % Run this test until t=2
u=1; %Velocity
dx = (xmax - xmin)/100 %finding delta x from the given information
x =xmin-dx : dx : xmax+dx; %setting the x values that will be plugged in
h0= exp(-(x- 0.5).^2/0.01); %Initial gaussian profile for t=0
h = h0;
hp1=h0;
nsteps =tmax/dt; % total number of steps taken
for n=1 : nsteps
h(1)=h(end-2); %Periodic B.C
h(end)=h(2);
for i =2 : N+1
if u>0
hp1(i) = h(i) - u*dt/dx *( h(i)-h(i-1)); %Loop to solve the FOU
elseif u<0
hp1(i) = h(i) - u*dt/dx*(h(i+1)-h(i)); %downwind
end
end
t=t+dt; %Increase t after each iteration
h= hp1; %have the new hp1 equal to h for the next step
initial= exp(-(x- 0.5).^2/0.01); % The initial plot when t =0
%hold on
plot(x,initial,'*') %plot initial vs moving
plot(x,h,'o-')
pause(0.0001);
%hold off
figure(2)
plot(x,initial) %plot end value
end
Isn't this "speedup" due to pause() flushing the graphic event buffer like drawnow, but apparently doing it faster? So it is not the length of the pause doing any work (in fact, I don't think the resolution is in the millisecond range on many machines) but just the command itself.
The thing really slowing down your code is those for loops. You should try to change the code to calculate the segments in parallel instead.
Related
I'm writing a Matlab program to compute pi through the summation of series
A = Sum of a_i from i=1 to N
where
pi/4 = 1 - 1/3 + 1/5 - 1/7 + 1/9 - 1/11 + 1/13 ...
To compute pi through the series summation, the suggested approach is to set
a_i = (-1)^(i+1)/(2i-1)
In order to do this, I wrote the program below
n=100;
f=[];
for jj=1:n
ii=1:jj;
f=[f 4*sum( ((-1).^(ii+1))./(2.*ii-1) )];
end;
hold on
plot(f)
title('Computing of \pi using a finite sum')
xlabel('Number of summations')
ylabel('Estimated value of \pi')
plot(1:size(f,2),ones(size(f))*pi)
This program shows that the series approximation is somewhat accurate near N=80.
I am now attempting to adjust my program so that the y-axis displays total calculation time T_N and the x-axis displays N (the number of summations). The total calculation time T_N should increase as N increases. Ideally, I am looking to have the graph display something close to a linear relationship between T(N) and N
To do this, I have adjusted my original program as follows
n=100;
f=[];
tic
for jj=1:n
ii=1:jj;
f=[f 4*sum( ((-1).^(ii+1))./(2.*ii-1) )];
end;
hold on
plot(f)
title('Time it takes to sum \pi using N summations')
xlabel('Number of summations (N)')
ylabel('Total Time (T_N)')
plot(1:size(f,2),toc)
slope = polyfit(1:size(f,2),toc,1);
This looks wrong. I must have incorrectly applied the built-in timing functions in Matlab (tic and toc). So, I am going to analyze my code and ask two questions -
How could I adjust my code above so that the y-axis correctly displays the total calculation time per the summation N? It looks like I did something wrong in plot(1:size(f,2),toc).
After I get the y-axis to display the correct total calculation time (T_N), I should be able to use the polyfit command to find the slope of T(N)/N. This will give me a linear relationship between T(N) and N. I could then use the value of slope = polyfit(1:size(f,2),toc,1) to compute
t_N = a + b*N
where t_N is computed for every value of N and b is the slope calculated through the polyfit command.
I think that I should be able to find the values of a and b after I correctly display the y-axis and correctly reference the polyfit command.
There are several things that can be improved in your code:
f should be preallocated, so as not to waste time in repeatedly assigning memory.
tic should be called within the loop, in order to restart the stopwatch timer.
When you call toc you get the current time from the last tic. The time spent should be stored in a vector (also preallocated).
Since the computations you want to time are very fast, measuring the time they take is very unrealiable. The computations should be repeated many times, so the measured time is larger and you get better accuracy. Even better would be to use timeit (see below).
You cannot plot the time and the results in the same figure, because the scales are too different.
The code incorporating these changes is:
n = 100;
f = NaN(1,n); % preallocate
times = NaN(1,n); % preallocate
repeat_factor = 1e4; % repeat computations for better time accuracy
for jj=1:n
tic % initiallize time count
for repeat = 1:repeat_factor % repeat many times for better time accuracy
ii=1:jj;
f(jj) = 4*sum( ((-1).^(ii+1))./(2.*ii-1) ); % store value
end
times(jj) = toc; % store time
end
times = times / repeat_factor; % divide by repeat factor
plot(f)
title('Time it takes to sum \pi using N summations')
xlabel('Number of summations (N)')
ylabel('Total Time (T_N)')
figure % new figure for time
plot(1:size(f,2), times)
p = polyfit(1:size(f,2),times,1);
slope = p(1);
Using timeit for measuring the time will probably give improved accuracy (but not very good because, as mentioned above, the computations you want to time are very fast). To use timeit you need to define a function with the code to be timed. The simplest way is to use an anonymous function without input arguments. See code below.
n = 100;
f = NaN(1,n); % preallocate
times = NaN(1,n); % preallocate
for jj=1:n
ii=1:jj;
fun = #() 4*sum( ((-1).^(ii+1))./(2.*ii-1) );
f(jj) = fun(); % store value
times(jj) = timeit(fun); % measure and store time
end
plot(f)
title('Time it takes to sum \pi using N summations')
xlabel('Number of summations (N)')
ylabel('Total Time (T_N)')
figure % new figure for time
plot(1:size(f,2), times)
p = polyfit(1:size(f,2),times,1);
slope = p(1);
If I understand your problem correctly, I think there are two different issues here. First, you plot your result function then the elapsed time which is several orders of magnitude smaller than pi:
hold on
plot(f) % <---- Comment me out!
...stuff...
plot(1:size(f,2),toc)
Secondly, you need to store the execution time of each pass of the loop:
n=100;
f=[];
telapsed = zeros(1,n);
tic
for jj=1:n
ii=1:jj;
f=[f 4*sum( ((-1).^(ii+1))./(2.*ii-1) )];
telapsed(jj) = toc;
end
hold on
% plot(f)
title('Time it takes to sum \pi using N summations')
xlabel('Number of summations (N)')
ylabel('Total Time (T_N)')
plot(1:n,telapsed)
slope = polyfit(1:n,telapsed,1);
Note the new polyfit expression for slope of the execution time. Does that help?
I am currently working on a problem in Matlab where I am given a distributed loaded beam and then numerically integrate (using the composite trapezoidal rule) to find the shear force and bending moment. From there I find the maximum moment value and the position at which this occurs. Then I differentiate twice to again find the shear force and distributed load.
I am using the analytically integrated functions in order to check and make sure that each of my numerical integrations and differentiations are calculated correctly. My issue is that when I use only 13 points to perform the calculations (x = 0:12;) my values are as close as I have gotten them to the results from the analytical values. (This number comes from the beam being 12 ft and the divisions are 1 ft each). When I increase the number of divisions in order to increase the accuracy of my numerical integration the values get further away from the analytical values. I would like to figure out what could have caused this and thus far have been unsuccessful.
My code:
clear; clc;
% n=100;
% x = linspace(0,12,n+1); %dx (100 divisions)
x=0:12; %dx (12 divisions)
w = 12.5.*x; %distributed load
%//Integrated Shear
V(1)=300;
for i = 2: length(w)
weight = [.5 ones(1,i-2) .5];
V(i)=300-sum(weight.*w(1:i));
end
figure(1); clf;
plot(x,V,'--');
legend('Numerical Shear');
xlabel('Position (ft)'); ylabel('Shear Force (lb)');
%//Integrated Moment
Mactual = 300.*x - ((25/12).*(x.^3)); %analytically integrated M
M(1)=0;
for i = 2: length(V)
weight = [.5 ones(1,i-2) .5];
M(i)=sum(weight.*V(1:i));
end
figure(2); clf;
plot(x,M,'--'); hold on;
plot(x,Mactual); hold off;
legend('Numerical Moment' , 'Analytical Moment');
xlabel('Position (ft)'); ylabel('Bending Moment (lbf)');
%//Max Moment and Position at Max Moment
[maxMValue, indexAtMaxM] = max(M);
xValueAtMaxMValue = x(indexAtMaxM(1));
dispM = [ 'Maximum bending moment: ' , num2str(maxMValue) , ' lbf'];
disp(dispM);
dispX = [ 'Position at maximum bending moment: ' , num2str(xValueAtMaxMValue) , ' ft' ];
disp(dispX)
%//Derived Shear
dM(1)=300;
for i = 1:length(x)-1
dM(i+1) = (M(i+1)-M(i));
end
figure(3); clf;
plot(x,dM,'--'); hold on;
Vactual = 300 - 6.25.*(x.^2); %analytically integrated shear
plot(x,Vactual); hold off;
legend('Numerical Shear' , 'Analytical Shear');
xlabel('Position (ft)'); ylabel('Shear Force (lb)');
%//Derived Load
dV(1)=0;
for i = 1:length(x)-1
dV(i+1) = -(V(i+1)-V(i));
end
figure(4); clf;
plot(x,dV,'--'); hold on;
plot (x,w); hold off;
legend('Numerical Load' , 'Analytical Load');
xlabel('Position (ft)'); ylabel('Distributed Load (lb/ft)');
In the code above the 2nd and 3rd lines are commented out to show the output with only 12 divisions. If you uncomment those two lines and comment out the 4th line the output shows values with 100 divisions.
Quick Note: The analytical values are plotted against the integrated moment, derived shear, and derived distributed load as a comparison. Consider the analytical values (Mactual and Vactual) as the correct output values.
Any input which could help lead me to a way to solve this issue would be greatly appreciated.
To get the correct integral value, you will need to multiply the function value sums with dx=12/n or identically dx=x(2)-x(1) if n is different from 12.
(11/28) You can also simplify the trapezoidal integration loops as
V(1)=300
M(1)=0
for i=2:n
V(i) = V(i-1) - 0.5*dx*(w(i-1)+w(i))
M(i) = M(i-1) + 0.5*dx*(V(i-1)+V(i))
end
thus avoiding reconstruction of the weights and computation of the sums in each step. This reduces this part of the algorithm from O(n^2) to O(n).
I am currently trying to simulate a random walk. The idea is to choose a random number between 0 and 2*pi and let the random walker go in that direction. Here is what I tried to do to simulate such a random walk:
before=[0 0]; %start in (0,0)
while 1
x=rand;
x=x*2*pi; %// choose random angle
increment=[cos(x),sin(x)] %// increments using the sine and cosine function
now=before+increment;
plot(before, now)
hold on
before=now;
pause(1);
end
I expect this program to plot lines and each new line starts at the ending point of the previous line, but this does not happen. I have no clue why it is not working.
You got the syntax for plot wrong, which is plot(X,Y). Change the call to
plot([before(1), now(1)], [before(2), now(2)])
and your program should work as expected.
Here is an improved version that does all the calculation vectorized and gives you two choices of output. The first one displays all at once and is very fast. The second one takes a lot of time depending on the amount of samples.
pts = [0,0]; % starting point
N = 10000; % sample count
x = rand(N,1) * 2*pi; % random angle
% calculate increments and points
inc = [cos(x),sin(x)];
pts = [pts;cumsum(inc,1)];
% plot result at once
figure;
plot(pts(:,1),pts(:,2));
% plot results in time steps
figure; hold on;
for i = 1:size(pts,1)
plot(pts(i:i+1,1),pts(i:i+1,2))
pause(1)
end
Here is an example of the output:
I've been solving a pretty simple set of ODEs which, for a particular case, produce results as I'd expect. The required output is a graph of the 'population' which is just a manipulation of the results output by ODE.
The issue is that I have some variable del which must be slowly incremented as time goes on (slowing being on the order of del_increment/time_increment << Omega^2, where Omega is set to 1). I can happily produce what I've been calling the 'static' case where I have a for loop incrementing del outside of ode45, however this is really just the solution for a static del value over a time period.
I'm struggling to see how I can have ODE45 increment del values [between a max and min] within the function as I have the restriction of small changes of del with time. This would show one plot, displaying small oscillations at zero population and a gradual increase to population =1, as opposed to my current multiple plots for each particular del value.
I've given what I've written below, and I hope this explanation makes sense.
del=-10:0.1:10; %start at large minus, with some small increment
Omega=1;
tmax=10; %integration time
for ret=1:length(del)
y=[0 0 1]; % starting conditions
[t,y]=ode45(#(t,y) psfour(t,y,del(ret)),[0 tmax],y);
plot(t,y(:,1)) % plot of one of the solutions
axis([0 max(t) -1 1])
p_11=0.5*(1+y(:,3)); %calculation of the population of an energy level
figure(2)
plot(t,p_11)
axis([0 max(t) 0 1])
pause(0.05) % asthetic pause so I can see results
end
and my function
function dydt = react(t,y,del)
dydt = zeros(size(y));
Omega=1;
A = y(1);
B = y(2);
C = y(3);
dydt(1) = del*B;
dydt(2) = -del*A + Omega*C;
dydt(3) = -Omega*B;
% del=del + Omega*t;
end
i have variable x that undergoes a random walk according to the following rules:
x(t+1)=x(t)-1; probability p=0.3
x(t+1)=x(t)-2; probability q=0.2
x(t+1)=x(t)+1; probability p=0.5
a) i have to create this variable initialized at zero and write a for loop for 100 steps and that runs 10000 times storing each final value in xfinal
b) i have to plot a probability distribution of xfinal (a histogram) choosing a bin size and normalization!!* i have to report the mean and variance of xfinal
c) i have to recreate the distribution by application of the central limit theorem and plot the probability distribution on the same plot!
help would be appreciated in telling me how to choose the bin size and normalize the histogram and how to attempt part c)
your help is much appreciated!!
p=0.3;
q=0.2;
s=0.5;
numberOfSteps = 100;
maxCount = 10000;
for count=1:maxCount
x=0;
for i = 1:numberOfSteps
random = rand(1, 1);
if random <=p
x=x-1;
elseif random<=(p+q)
x=x-2;
else
x=x+1;
end
end
xfinal(count) = x;
end
[f,x]=hist(xfinal,30);
figure(1)
bar(x,f/sum(f));
xlabel('xfinal')
ylabel('frequency')
mean = mean(xfinal)
variance = var(xfinal)
For the first question, check the help for hist on mathworks homepage
[nelements,centers] = hist(data,nbins);
You do not select the bin size, but the number of bins. nelements gives the elements per bin and center is all the bin centers. So to say, it would be the same to call
hist(data,nbins);
as
[nelements,centers] = hist(data,nbins);
plot(centers,nelements);
except that the representation is different (line or pile). To normalize, simply divide nelements with sum(nelements)
For c, here i.i.d. variables it actually is a difference if the variables are real or complex. However for real variables the central limit theorem in short tells you that for a large number of samples the distribution will limit the normal distribution. So if the samples are real, you simply asssumes a normal distribution, calculates the mean and variance and plots this as a normal distribution. If the variables are complex, then each of the variables will be normally distributed which means that you will have a rayleigh distribution instead.
Mathworks is deprecating hist that is being replaced with histogram.
more details in this link
You are not applying the PDF function as expected, the expression Y doesn't work
For instance Y does not have the right X-axis start stop points. And you are using x as input to Y while x already used as pivot inside the double for loop.
When I ran your code Y generates a single value, it is not a vector but just a scalar.
This
bar(x,f/sum(f));
bringing down all input values with sum(f) division? no need.
On attempting to overlap the ideal probability density function, often one has to do additional scaling, to have both real and ideal visually overlapped.
MATLAB can do the scaling for us, and no need to modify input data /sum(f).
With a dual plot using yyaxis
You also mixed variance and standard deviation.
Instead try something like this
y2=1 / sqrt(2*pi*var1)*exp(-(x2-m1).^2 / (2*var1))
ok, the following solves your question(s)
codehere
clear all;
close all;
clc
p=0.3; % thresholds
q=0.2;
s=0.5;
n_step=100;
max_cnt=10000;
n_bin=30; % histogram amount bins
xf=zeros(1,max_cnt);
for cnt=1:max_cnt % runs loop
x=0;
for i = 1:n_step % steps loop
t_rand1 = rand(1, 1);
if t_rand1 <=p
x=x-1;
elseif t_rand1<=(p+q)
x=x-2;
else
x=x+1;
end
end
xf(cnt) = x;
end
% [f,x]=hist(xf,n_bin);
hf1=figure(1)
ax1=gca
yyaxis left
hp1=histogram(xf,n_bin);
% bar(x,f/sum(f));
grid on
xlabel('xf')
ylabel('frequency')
m1 = mean(xf)
var1 = var(xf)
s1=var1^.5 % sigma
%applying central limit theorem %finding the mean
n_x2=1e3 % just enough points
min_x2=min(hp1.BinEdges)
max_x2=max(hp1.BinEdges)
% quite same as
min_x2=hp1.BinLimits(1)
max_x2=hp1.BinLimits(2)
x2=linspace(min_x2,max_x2,n_x2)
y2=1/sqrt(2*pi*var1)*exp(-(x2-m1).^2/(2*var1));
% hold(ax1,'on')
yyaxis right
plot(ax1,x2,y2,'r','LineWidth',2)
.
.
.
note I have not used these lines
% Xp=-1; Xq=-2; Xs=1; mu=Xp.*p+Xq.*q+Xs.*s;
% muN=n_step.*mu;
%
% sigma=(Xp).^2.*p+(Xq).^2.*q+(Xs).^2.s; % variance
% sigmaN=n_step.(sigma-(mu).^2);
People ususally call sigma to variance^.5
This supplied script is a good start point to now take it to wherever you need it to go.