Could someone please explain what am I doing wrong when setting up an ode45 event this way?
When I tried running the code it would reach line 6, however it would not stop computing the ode regardless that I set isTerminal=[1,1] on line 7, instead it continues until the end: when the second event z(3) reaches 0 at which point it reaches line 10 and depending on whether I set isTerminal=[1,1] or isTerminal=[0,1] then it stops the computation with isTerminal(1) as the triggered event and isTerminal(2) in the later case.
function [value, isTerminal, direction] = myEvent(t, z)
posObst=100;
dist = z(1)-posObst;
if dist == 0
value = [dist; z(3)];
isTerminal = [1; 1];
direction = [0; -1];
else
value = [dist; z(3)];
isTerminal = [0; 1];
direction = [-1 ; -1];
end
end
My question is why doesn't the ode stop the computations once it reaches line 7? This is the most minimalistic example I could come up with. If there is anything else I should add please let me know.
I found what the problem was. It arises from the line
if dist==0
Should have defined a tolerance and then made the floating point comparison like so:
tol = 1e-4
if dist <= tol
Related
I have a ode system that I solve with Matlab. I want to find the steady state of the system and to do so, I use the event function as described here.
But some times, the solver doesn't stop even if the criterium is achieved.
For example, if you solve the following system with x0 = 10 the solver will stop before 2000 but with x0 = 0.0001 it won't.
The event function (eventfun_t.m)
function [x,isterm,dir] = eventfun_t(t,y)
dy = test_systeme(t,y);
x = norm(dy) - 1e-3;
isterm = 1;
dir = 0; %or -1, doesn't matter
end
The system (test_systeme.m)
function dx = test_systeme(t,x)
v = x./(x+1);
dx = -v;
end
Solve the system
x0 = 10;
eventfonction = #(t,y) eventfun_t(t,y);
optionsode=odeset('Events',eventfonction);
[t x]=ode15s(#(t,x) test_systeme(t,x),[0 2000],x0,optionsode);
I suppose it's because with x0 = 0.0001 norm(dy) is already lower than 1e-3 but in that case, how can I stop the solver without checking by myself ?
The events function checks for sign changes in value. So if value(t=0)<0 and value(0 < t < t_end)<0, then it will never trigger.
A way I've gotten around this is to use conditional statements:
value = 1;
if norm(dy)<1e-3&&t~=0
value = -1;
end
The t~=0 statement allows value to change sign after the first step if it's already less than the theshold.
The function below, bisection, is supposed to find a root given three inputs: a function f, and an interval defined using the two parameters a and b. The intention is that the value of a and b are changed within the function to approach a common point, as long as their signs are different.
When I call my function like this:
bisection( #(x)x-1 ,-2,3)
no output is returned. What am I doing wrong?
function X = bisection(f,a,b)
if ge((f(a)*f(b)),0)
disp('Wrong')
return;
end
X = (a+b)/2;
while abs(X)>0.01
if f(X)*f(a)>0
X=a;
else
X=b;
end
end
Enter the Infinite!
Well done! You've written your first (and not last, believe me) infinite loop. The problem is threefold. Firstly your stop condition was abs(X) and should have been abs(f(X)) - you don't care for X to be zero, you want f(X) to be zeros. Secondly you don't update your your X correctly so your break condition is never hit (unless you are lucky to give this function symmetrical a,b bounds around the zero of the function). You could see this easily by adding a line like disp(f(X)); pause(0.5); somewhere in the while-loop.
In general try to avoid infinite loops with some explicit stop condition. In my code below I've put in the interaction limit past which the algorithm will just stop (it would be more elegant to catch that condition and warn the user of hitting the iteration limit...).
function x0 = bisection(f,a,b)
assert(f(a)*f(b)<0,'Invalid f(x) range. f(a)*f(b) >= 0');
tol = 0.00001; % Tolerance
iter_limit = 10000; % Limit of number of iterations
iter = 0;
x0 = (a+b)/2; % Midpoint
while abs(f(x0)) > tol && iter < iter_limit
if f(x0)*f(a) > 0
a = x0; % Zero to the right of the midpoint
else
b = x0; % Zero to the left
end
x0 = (a+b)/2; % Recalculate midpoint
iter = iter + 1;
end
end
This should work no problem with
f = #(x)x-1;
bisection(f,-2,3);
I get something like 0.999992370... which is within specified tolerance from the actual answer (1).
I want to solve following DDE using a for loop in MATLAB:
xdot(t) = Ax(t) + BKx(t-h)
in which:
A = [0 1 ; -1 0.1];
B = [0 ; 1];
h = 0.2;
K = [-0.0469 -1.7663];
t = [0 5]
Solving this with conventional procedure is simple and the results are acceptable.
sol = dde23(ddefun,lags,history,tspan,options,varargin)
However, when I try to solve it using for loop, the results are wrong. Here is a simple code for my for loop.
time = 0:0.001:5;
for i = 2:5001
x(:,1) = [1 -1];
history(:,1) = [1 -1];
[t h] = ode23(#(t,h)histExam1(t,h,A,B,K),[time(i-1) time(i)],history(:,i-1));
history(:,i)= h(end,:);
sol = dde23(((#(t,y,z)ddefun(t,y,z,A,B,K))),0.2,history(:,i),[time(i-1) time(i)]);
x(:,i)=sol.y(:,end);
end
I think, the only problem in this code is my time steps and delay input. I use same dde function for both codes so It cannot be a problem.The reason I want to solve DDE in a for loop is "BK" value which is state dependent (not in this simple example) and in each time step I need to update "BK".
The correct answer plotted above with conventional method.
And the wrong answer I get using "for loop" is plotted above.
It is interesting to mention that correct answer is sensitive to delay. But delay doesn’t affect the answer from 2nd method.
Ok. After weeks of thinking, finally found a solution.
Simply:
sol = dde23(((#(t,y,z)ddefun(t,y,z,A,B,K))),0.2,[1;-1],[0 time(i)]);
and let the magics happen.
This code helps you with updating states at each time step.
I hope it will help you in the future.
All the Best,
Sina
The issue I have is having to compute the derivative (in real time) of the solution produced by ode45 within the events function.
Some pseudo-code to explain what I'm mean is,
function dx = myfunc(t,x,xevent,ie)
persistent xs
persistent dx2
% xevent is the solution at the event
if isempty(dx2) == 1
dx2 = 0;
end
k = sign(dx2*x(1));
if ie(end) == 1
xs = xevent
elseif ie(end) == 2
xs = xevent
end
dx(1) = x(2);
dx(2) = function of k,x(1),x(2), and xs;
dx2 = dx(2);
end
function [value,isterminal,direction] = myeventfcn(t,x)
% dx2 = some function of x
if dx2*x(1)>=0
position = [dx2*x(1); dx2*x(1)];
isterminal = [1; 1];
direction = [1 ; -1 ];
elseif dx2*x(1)<0
position = [dx2*x(1); dx2*x(1)];
isterminal = [1; 1];
direction = [-1 ; 1 ];
end
I know that if I didn't need to use the solution at the event within myfunc I could just compute dx=myfunc(t,x) within my event function and use dx(2), yet since xevent is used within myfunc I can't input xevent.
I know there is a way of inputting constant parameters within the event function, but since it's the solution at the event location, which also changes, I'm not sure how to go about doing that.
My work around to this is to approximate dx(2) using the solution x. What I wanted to know is if it will be satisfactory to just use a finite difference approximation here, using a small fixed step size relative to the step size od45 takes, which is a variable step size.
As a note, the reason I have myeventfcn split by the if statement is to know what the direction the event is crossed, since it will update within myfunc.
Also, I need to use dx(2) of the previous successful time step and so that's why I have dx2 defined as a persistent variable. I believe having k=sign(dx(2)*x(1)) is okay to do, since my event depends on dx(2)*x(1), and so I won't be introducing any new discontinuities with the sign function.
Thanks for any help!
function [y]=AmericanPutClassic (St,t)
% S0 = 100;
K = 100;
r = 0.05;
sigma = 0.3;
T = 2;
nsteps = 5;
% St
dt = T / nsteps;
u=exp(sigma*sqrt(dt));
d=1/u;
Pu=(exp(r*dt)-d)/(u-d);
Pd=1-Pu;
if t==T
y=max(K-St,0);
return
elseif t<T
upPrice=AmericanPutClassic(St*u,t+dt);
downPrice=AmericanPutClassic(St*d,t+dt);
PrevPrice=(Pu*upPrice+Pd*downPrice)*exp(-r*dt);
if max(K-St,0) > PrevPrice
y=max(K-St,0);
else
y=PrevPrice;
end
return
end
end
I think my code does the job, but when I make 'nsteps' higher than 5, it crushes... I keep getting different errors... now it just says there is problem with my code when its higher than 5... before it would say:
"??? Maximum recursion limit of 500 reached. Use set(0,'RecursionLimit',N)
to change the limit. Be aware that exceeding your available stack space can
crash MATLAB and/or your computer."
Can anybody spot the problem?
I start by calling AmericanPutClassic(100,0)...
I don't know what you're trying to do but this problem has been described so many time on SO it's not funny anymore.
Let's paint a picture:
you start off with
dt = T / nsteps;
which is my first red flag.
then you do:
if t==T
where t = t + dt
Why is that wrong? Because of this wonderful thing called quantization. In other words there will be a time when because of super small micro difference, the result will not be correct. The bigger nsteps is, the worse this will be.
And then to add insult to injury, you put this line in
elseif t<T ... end
which means your code will skip everything, and you'll return nothing, causing your code to crash.
How does one solve this? If you can move integers around instead of floating values, you'll be in a better position. So instead of dt, you can have currentStep, such that:
function [y]=AmericanPutClassic (St, currentStep)
if nargin < 2
currentStep = 0;
end
...
if currentStep>=nsteps % if t==T
...
else
...
upPrice=AmericanPutClassic(St*u,currentStep+1);
...
end