“dumb” version of Euler’s method using Matlab - matlab

I tried to write the 'dumb' version of Euler's method using Matlab but I always came up with nothing. My code was trash :-(
See this pseudocode for this method :
‘set integration range
xi = 0
xf = 0
‘initialize variables
x = xi
y = 1
‘set step size and determine
‘number of calculation steps
dx = 0.5
nc = (xf – xi) / dx
‘ output initial condition
PRINT x, y
‘Loop to implement Euler’s method
‘and display results
DOFOR I = 1, nc
dydx = -(2X**3) + (12X**2) - (20X) + 8.5
y = y + dydx . dx
x = x + dx
PRINT x, y
END DO
I'm pretty sure that this pseudocode is what I need to implement, but I failed to convert it into Matlab code.
Any help please ?

I am not sure where you're failing, it would really help to know how you're failing or what's a complication. Otherwise this just seems like doing your homework.
Here's my attempt at the MATLAB code. (Note: I do not have MATLAB on this computer and have not tested it)
i = 0;
stepsize = .1; % Define as what you want it to be
y = 1; % Initial value condition given
t = 0; % Initial time value
yout = [zeros(1,20)]; % Assuming you want 20 outputs, can change
fvec = [zeros(1,20)];
for i = 1:20 % Time range, can change to correspond to what you want
fvec(i) = 2 - exp(-4*t) - 2*yout(i); % Just loops calculating based on Euler's method
yout(i+1) = yout(i) + stepsize*fvec(i)
t = t+stepsize % Time has passed, increment the time.
end
I'm not entirely sure this code, but it should give you an example of how to go about this. Please comment if something is wrong.

Related

Why am I getting the wrong sign on my cos(x) approximation?

I am writing a Matlab script that will approximate sin(x) and cos(x) using their Maclaurin polynomials.
When I input
arg = (5*pi)/4 I expect to get the correct approximations for
sin((5*pi)/4) = -0.7071067811865474617
cos((5*pi)/4) = -0.7071067811865476838.
Instead I get the following when running the script:
Approximation of sin(3.92699) >> -0.7071067811865474617
Actual sin(3.92699) = -0.7071067811865474617
Error approximately = 0.0000000000000000000 (0)
----------------------------------------------------------
Approximation of cos(3.92699) >> 0.7071067811865474617
Actual cos(3.92699) = -0.7071067811865476838
Error approximately = 0.0000000000000001110 (1.1102e-16)
I am getting the correct answers for sin but incorrect for cosine when the argument (angle) is in quadrant 3 or 4. The problem is that I am getting the wrong sign on the cos(arg) value. Where have I messed up?
CalculatorForSineCosine.m
% Argument for sine/cosine in radians.
arg = (5*pi)/4;
% Move the argument x so it's within [0, pi/2].
newArg = moveArgumentV2(arg);
% Calculate what degree we need for our Taylorpolynomial.
TOL = 0; % If 0, assume we want Machine Epsilon.
r = findDegreeV2(TOL);
% Plot nth degree Taylorpolynomial around x = 0 for sine.
% and calculate approximation of sin(x).
[approximatedSin, errorSin] = sin_taylorV2(r, newArg);
eS = num2str(errorSin); % errorSin in string format
% Plot nth degree Taylorpolynomial around x = 0 for cosine.
% and calculate approximation of cos(x).
[approximatedCos, errorCos] = cos_taylorV2(r, newArg);
eC = num2str(errorCos); % errorCos in string format
% Print out the result.
fprintf('\nApproximation of sin(%.5f)\t >> %.19f\n', arg, approximatedSin);
fprintf('Actual sin(%.5f)\t\t\t\t = %.19f\n', arg, sin(arg));
fprintf('Error approximately\t\t\t\t = %.19f (%s)\n', errorSin, eS);
disp("----------------------------------------------------------")
fprintf('Approximation of cos(%.5f)\t >> %.19f\n', arg, approximatedCos);
fprintf('Actual cos(%.5f)\t\t\t\t = %.19f\n', arg, cos(arg));
fprintf('Error approximately\t\t\t\t = %.19f (%s)\n\n', errorCos, eC);
sin_taylorV2.m
function [approximatedSin, errorSin] = sin_taylorV2(r, x)
%% sss
% Q_2n+1(x) where 2n+1 = degree of polynomial.
n = (r - 1)/2;
% Approximate sin(x) using its Taylorpolynomial.
approximatedSin = 0;
for k = 0:n
approximatedSin = approximatedSin + (((-1).^k) .* (x.^(2.*k+1)))./(factorial(2.*k+1));
end
% Calculate the error.
errorSin = abs(sin(x) - approximatedSin);
end
cos_taylorV2.m
function [approximatedCos, errorCos] = cos_taylorV2(r, x)
%% sss
% Q_2n+1(x) where 2n+1 = degree of polynomial and n = # terms.
n = (r - 1)/2;
% Approximate cos(x) using its Taylorpolynomial.
approximatedCos = 0;
for k = 0:n
approximatedCos = approximatedCos + (((-1).^k) .* (x.^(2.*k)))./(factorial(2.*k));
end
% Calculate the error.
errorCos = abs(cos(x) - approximatedCos);
end
moveArgumentV2.m
function newArg = moveArgumentV2(arg)
%% Moves the argument x to the interval [0, pi/2].
% Make use of sines periodocity and choose n as ceil( (x-pi)/2pi) )
n = ceil((arg-pi)/(2*pi));
x1 = arg - 2*pi*n; % New angle will be in [-pi, pi]
x2 = abs(x1); % Angle will be in [0, pi]
if (x2 < pi/2) && (x2 > 0)
x3 = x2;
else
x3 = pi - x2;
end
newArg = x3*sign(x1); % Angle will be in [0, pi/2]
end
I would like to notice two things in your code.
First, you don't need the moveArgumentV2(arg) function, as, if you remember, the radius of convergence for the Maclaurin/Taylor series of the sin(x)/cos(x) is the set of all real numbers. That means the series should converge for any real x, disregarding the round-off errors inherently to every arithmetic operations done in a computer.
As a matter of fact, following your code, we can write a function that approximates the cos as:
function y = mycos(x,n)
y = 0;
for k=0:n
term = (-1)^k*x.^(2*k)/factorial(2*k);
y = y + term;
end
end
Notice this function works for values outside the range [-pi,pi]:
x = -10*pi:0.1:10*pi;
ye = cos(x) % exact value
ya = mycos(x,100) % approximated value
plot(x,ye,x,ya,'o')
The values returned by the mycos function are close to the exact value given by the cos built-in function. This happens because I calculated the approximation with the first 100 terms. The error, however, for higher values of x, is extremely large if we use just a few terms.
ya = mycos(x,10) % approximated value with 10 terms only
plot(x,ye-ya); title('error')
The problem now is that we can't just increase the number of terms without running in another problem.
If we increase the number of points, the mycos function crumbles due to round-off errors, because of the factorial function that overflows. A good idea is to try to change your code in order to avoid the use of the factorial function. Notice the recurrence between sucessive terms in the Maclaurin expansion of the cos function, and you can create another function without the use of the factorial:
function y = mycos2(x,n)
term = 1;
y = 1;
for k=1:n
term = -term.*x.^2/(2*k-1)/(2*k);
y = y + term;
end
end
Here, we calculate each term in the series expansion from the previous calculated term. We avoid the calculation of the factorial and make use of what we already have. This speeds the code and avoids overflow. As a matter of fact, if we now calculate the cos approximation with 500 terms, we get:
x = -10*pi:0.5:10*pi;
ye = cos(x); % exact value
ya = mycos(x,500); % approximated value
ya2 = mycos2(x,500); % approximated value
plot(x,ye,x,ya,'x',x,ya2,'s')
legend('ye','ya','ya2')
Notice in this figure the x marks are the calculations done with the mycos function, while the o marks are done without using the factorial function. The first function crumbles for values outside the range [-2,2], but the second one runs just fine. It works even when I use 1e5 terms. Increasing the number of terms reduces the errors, so you can estimate how much terms you will use on an approximation, given a desired tolerance. If this number is greater than 170, the first function will not work properly.
factorial(170) returns 7.2574e+306, but factorial(171) returns Inf, so any value that should be calculated with more than 170 terms will have problems in the first function. Avoid the calculation of factorial at all costs.
This is what I tried:
x = -3*pi:0.01:3*pi;
y = x;
for ii=1:numel(y)
y(ii) = moveArgumentV2(y(ii)); % not vectorized
end
plot(sin(x))
hold on
plot(sin(y))
Both sin(x) and sin(y) produce the same plot. But:
plot(cos(x))
hold on
plot(cos(y))
Now we see that cos(x) and cos(y) are not the same! This is because moveArgumentV2 changes the angle to be in the first and fourth quadrant (in the range [-pi/2, pi/2]), which is what you need for the sin function, but is not adequate for the cos function.
I would modify sin_taylorV2 and cos_taylorV2 to call moveArgumentV2, so you don't rely on the caller to know what the valid input range is. In cos_taylorV2 you would need to call it this way:
x = moveArgumentV2(x+pi/2) - pi/2;
and in sin_taylorV2 you'd call it the same way you do now.
Or, better, write cos_taylorV2 in terms of sin_taylorV2, which we know to be correct. This avoids code duplication.

How to avoid tf() command using State-space models in Matlab

I'm trying to avoid the function tf() from Matlab since it requires specific toolboxes to run.
The transfer function that I'm using is quite simple. Is the model for a heatsink temperature.
H(s) = (Rth/Tau)/(s + 1/Tau)
In order to avoid the tf() function, I've tried to substitute the transfer function with a state space model coded in Matlab.
I've used the function ss() to get te values of A,B,C and D. And I've tried to compare the results from tf() and my function.
Here's the code that I've used:
Rth = 8.3220e-04; % ºC/W
Tau = 0.0025; % s
P = rand(1,10)*1000; % Losses = input
t = 0:1:length(P)-1; % Time array
%%%%%%%%%%%%%%%%%%%%%%%%%
%%% Transfer function %%%
%%%%%%%%%%%%%%%%%%%%%%%%%
H = tf([0 Rth/Tau],[1 1/Tau]);
Transfer_func = lsim(H,P,t);
figure, plot(Transfer_func),grid on,grid minor, title('Transfer func')
%%%%%%%%%%%%%%%%%%%%%%%%%
%%% My función ss %%%
%%%%%%%%%%%%%%%%%%%%%%%%%
% Preallocate for speed
x(1:length(P)) = 0;
y(1:length(P)) = 0;
u = P;
sys = ss(H);
A = sys.A;
B = sys.B;
C = sys.C;
D = sys.D;
for k = 1:length(u)
x(k+1) = A*x(k) + B*u(k);
y(k) = C*x(k) + D*u(k);
end
figure, plot(y), grid on,grid minor, title('With my función')
I know that the values from A,B,C and D are ok, since I've checked them using
H = tf([0 Rth/Tau],[1 1/Tau]);
sys = ss(H);
state_space_sys = ss(sys.A,sys.B,sys.C,sys.D);
state_space = lsim(state_space_sys,P,t);
figure, plot(state_space),grid on,grid minor, title('State space')
As you can see, the results obtained from my funtion and the function tf() are very different.
Is there any mistakes on the approach?
If it's not possible to avoid the tf() function in this way, is there any other way?
At the end, I found another solution. I'm posting this here, so if someone has the same problem, can use this approach.
If you take the transfer function, and develop it, we reach to the following expresion
H(s) = deltaT(s)/P(s) = (Rth/Tau)/(s + 1/Tau)
deltaT(s) * (s + 1/Tau) = (Rth/Tau) * P(s)
deltaT(s) * s = (Rth/Tau) * P(s) - deltaT(s)/Tau
Now, we know that 1/s is equal to integrate. So in the end, we have to integrate the right side of the equation. The code would be like this.
Cth = Tau/Rth;
deltaT = zeros(size(P));
for i = 2:length(P)
deltaT(i) = (1/Cth * (P(i)-deltaT(i-1)/Rth))*(time(i)-time(i-1)) + deltaT(i-1);
end
This integral has the same output as the function tf().

Creating a universal code

I have been working on creating a universal equation to calculate the theta function. The current equation I have is not universal. This works for maximum of 2 thetas. If I have to do the calculation for theta more than 2, I have to manually modify the numbers within the brackets of theta().
Is there any way to setup a universal function? something like thata(j) = equation. I have pasted two codes below, one that is working but not universal and the other that I tried playing with and still ended up with error.
Code 1:
for iter = 1:num_iters
theta(1) = theta(1) - (alpha/m)*((X*theta-y)')*X(:,1),
theta(2) = theta(2) - (alpha/m)*((X*theta-y)')*X(:,2);
theta_hist(iter,1) = theta(1);
theta_hist(iter,2) = theta(2);
end
Code 2:
num_cols = size(X,2)
for inter = 1:num_iters
for j = 1:num_cols
theta(j) = theta(j) - (alpha/m)*((X*theta-y)')*X(:,j),
end,
end,
The challenge with this code is that the when the code goes through the loop second time (i=1, j=2). The function is using the new value of the theta. This is screwing up the calculation.
Any feedbacks or suggestion is greatly appreciated.
More description of this issue is mentioned in the link below:
https://docs.google.com/document/d/1XwAVV1OBN9BhQ7n60F2oEUoT263t_2hcAAuCaroR-2g/edit?usp=sharing
After multiple iterations, I was able to answer my own question.
The code is posted on github in the link below:
https://github.com/Boniface316/Octave/tree/master/GradientDescent_ThetaCalculator
Thanks for all the help.
You should vectorize your code. You haven't provided matrix dimenstions and/or sample inputs, but if I'm guessing it correctly, the following should give you what you need:
% Some random inputs
alpha = 1;
m = 1;
num_iters = 15;
N = 5; k = 12;
X = randn(k,N);
y = randn(k,1);
theta = zeros(N,1);
theta_hist = zeros(N,num_iters);
for i = 1:num_iters
theta = theta - (alpha/m)*X'*(X*theta-y);
theta_hist(:,i) = theta;
end

Matlab: Repeat iteration until condition is met

I am trying to learn Matlab as someone with an R background. I have the following program written for an iteration that I would like to repeat until the specified condition is met. I believe that I have all of code written for Matlab, except for the command that the iteration should repeat infinitely times until the condition is met (denoted below).
Would someone be able to tell me how to translate this to Matlab syntax? I think that I should be using a while-loop, but I'm not sure since the iterations should continue until a condition is met rather than continuing while some condition is met. Is there an until equivalent? Thank you!
function xn = newton_v2(f,fd,x0,tol)
% newton iteration
xn = x0;
repeat{ %%% 'This is how I would begin the repeat command in R'
% evaluate function and derivative
fxn = feval(f,xn);
fdxn = feval(fd,xn);
% newton iteration step
xnp1 = xn - fxn/fdxn;
if(abs(xnp1 - xn)/abs(xnp1) < tol)
xn<-xnp1
end
% update
xn = xnp1;
} %%% 'This is how I would end the repeat command in R'
end
Also, please let me know if you see anything else wrong in my Matlab code.
I'm not great with R syntax, but you would have two options 1.pass in an array of values to a matlab for loop and iterate through them. Then graph it to find the best solution and try to get more precise from there 2. Set a condition on a while loop. I think you are looking more for a while loop, so you'll put your condition in there. It may be more computationally heavy, but depending on the funct. may be fine.
I do the Newton method with a while loop though the recursive version in the answer by hamaney is interesting. I do this by passing a function handle, initial guess and tolerance to a function.
function x1 = NewtonMethod(f,x,tol)
% dtol is calculated from eps based on the function handle...not shown
xi = f(x); % initial guess
dxi = (f(x+dtol) - f(x-dtol)) / (2 * dtol);
x1 = x - xi / dxi;
while (abs(x1 - x) < tol)
x = x1;
xi = f(x);
dxi = (f(x+dtol) - f(x-dtol)) / (2 * dtol);
x1 = x - xi / dxi;
end
end
There is a problem with this approach. If the initial guess is poorly conditioned or the function unbounded it is possible for this to be an infinite loop. Because of this I include a loop counter and normally break when that counter reaches a maxSteps value.
Adding the inifinite loop protection looks something like this.
xi = f(x); % initial guess
dxi = (f(x+dtol) - f(x-dtol)) / (2 * dtol);
x1 = x - xi / dxi;
loopCounter = 0;
while (abs(x1 - x) < tol)
x = x1;
xi = f(x);
dxi = (f(x+dtol) - f(x-dtol)) / (2 * dtol);
x1 = x - xi / dxi;
loopCounter = loopCounter + 1;
if loopCounter > maxSteps
% include warning message
break
end
end
Do you mean the syntax for the for loop?
If you are trying to create Newton's method, see my own implementation script
y is the function
x0 - first initial value
n - # of xns to approximate
function Xn = newtonsMethod (y,x0,n,startDomain,endDomain)
%the tangent line at any point on the curve
y1 = matlabFunction(diff(sym(y)));
Xn = x0;
x= linspace(startDomain,endDomain,1000);
%recursive call for the newton formula
for i = 1 : n
%find the tangent line
L =#(x)(y1(Xn).*(x-Xn)+y(Xn));
%find the root
Xn = Xn - (y(Xn)./y1(Xn));
end
end
This might be not the best way to do it. but it could help!

Need help plotting this function in Matlab

I recently started using Matlab and I am trying to plot Imaginary part of a function. I can see I am making a mistake somewhere because I have the graph showing what I need to get and I am getting something else. Here is the link of the graph that I need to get, that I am getting and a function that I am plotting:
I know that this function has a singularity at frequency around 270 Hz and I don't know if a 'quadgk' can solve integral then. You guys probably know but theta function inside the integral is a heaviside and I don't know if that is causing problem maybe? Is there something wrong I am doing here? Here is the matlab code I wrote:
clear all
clc
ff=1:10:1000;
K1=(2*3)*log(2*cosh(135/6))/pi;
theta1=ones(size(ff));
theta1(ff<270)=0;
I1=zeros(size(ff));
for n = 1:numel(ff)
f = ff(n);
I1(n)= K1/f + (f/pi)*quadgk(#(x)(sinh(x/3)/(cosh(135/3)+cosh(x/3))-theta1(n))./((f^2)-4*(x.^2)), 0, inf);
end
plot(ff,I1, 'b');
hold on
axis([0 1000 -0.3 1])
First, I altered the expression for your heaviside function to what I think is the correct form.
Second, I introduced the definitions of mu and T explicitly.
Third, I broke down the integral into 4 components, as follows, and evaluated them individually (along the lines of what Fraukje proposed)
Fourth, I use quadl, although this is prob. of minor importance.
(edit) Changed the range of ff
Here's the code with changes:
fstep=1;
ff=[1:fstep:265 275:fstep:1000];
T = 3;
mu = 135;
df = 0.01;
xmax = 10;
K1=(2*T/pi)*log(2*cosh(mu/(2*T)));
theta1=ones(size(ff));
theta1(ff-2*mu<0)=0;
I1=zeros(size(ff));
for n = 1:numel(ff)
f = ff(n);
sigm1 = #(x) sinh(x/T)./((f^2-4*x.^2).*(cosh(mu/T)+cosh(x/T)));
sigm2 = #(x) -theta1(n)./(f^2-4*x.^2);
I1(n) = K1/f + (f/pi)*quadl(sigm1,0,f/2-df); % term #1
% I1(n) = I1(n) + (f/pi)*quadl(sigm1,f/2+df,xmax); % term #2
% I1(n) = I1(n) + (f/pi)*quadl(sigm2,0,f/2-df); % term #3
% I1(n) = I1(n) + (f/pi)*quadl(sigm2,f/2+df,xmax); % term #4
end
I selected to split the integrals around x=f/2 since there is clearly a singularity there (division by 0). But additional problems occur for terms #2 and #4, that is when the integrals are evaluated for x>f/2, presumably due to all of the trigonometric terms.
If you keep only terms 1 and 3 you get something reasonably similar to the plot you show:
However you should probably inspect your function more closely and see what can be done to evaluate the integrals for x>f/2.
EDIT
I inspected the code again and redefined the auxiliary integrals:
I1=zeros(size(ff));
I2=zeros(size(ff));
I3=zeros(size(ff));
for n = 1:numel(ff)
f = ff(n);
sigm3 = #(x) sinh(x/T)./((f^2-4*x.^2).*(cosh(mu/T)+cosh(x/T))) -theta1(n)./(f^2-4*x.^2);
I1(n) = K1/f + (f/pi)*quadl(sigm3,0,f/2-df);
I2(n) = (f/pi)*quadl(sigm3,f/2+df,10);
end
I3=I2;
I3(isnan(I3)) = 0;
I3 = I3 + I1;
This is how the output looks like now:
The green line is the integral of the function over 0<x<f/2 and seems ok. The red line is the integral over Inf>x>f/2 and clearly fails around f=270. The blue curve is the sum (the total integral) excluding the NaN contribution when integrating over Inf>x>f/2.
My conclusion is that there might be something wrong with the curve as you expect it to look.
So far I'd proceed this way:
clc,clear
T = 3;
mu = 135;
f = 1E-04:.1:1000;
theta = ones(size(f));
theta(f < 270)= 0;
integrative = zeros(size(f));
for ii = 1:numel(f)
ff = #(x) int_y(x, f(ii), theta(ii));
integrative(ii) = quad(ff,0,2000);
end
Imm = ((2*T)./(pi*f)).*log(2*cosh(mu/(2*T))) + (f/pi).*integrative;
Imm1 = exp(interp1(log(f(1:2399)),log(Imm(1:2399)),log(f(2400):.001:f(2700)),'linear','extrap'));
Imm2 = exp(interp1(log(f(2985:end)),log(Imm(2985:end)),log(f(2701):.001:f(2984)),'linear','extrap'));
plot([(f(2400):.001:f(2700)) (f(2701):.001:f(2984))],[Imm1 Imm2])
hold on
axis([0 1000 -1.0 1])
plot(f,Imm,'g')
grid on
hold off
with
function rrr = int_y(x,f,theta)
T = 3;
mu = 135;
rrr = ( (sinh(x./T)./(cosh(mu/T) + cosh(x/T))) - theta ) ./ (f.^2 - 4.*(x.^2));
end
I've come up with this plot: