ODE - Solving Parameter Dependent on Variable [Matlab] - matlab

Lets say I have a function file of the ODE that goes like this
function xprime = RabbitTemp(t,X)
% Model of Rabbit Population
% where,
% Xo = Initial Population of Rabbits
% X(1) = Population density of Rabbit
% X(2) = Temperature T (that varies with time)
% a = test parameter
%%% ODE
a = 10;
dx(1) = (X(1))*(1 - a*X(1) - 3*(X(2))));
dx(2) = sin(t);
%%%%%%%
xprime = [dx(1) dx(2)]';
end
But what if I would like parameter a to vary as temperature X(2) varies, as the ODE solver calculates.
I understand I would first have to create some data between a and X(2) and interpolate it. But after that I'm not very sure what's next. Could any one point me in the right direction?
Or is there any other way?

It really depends on the function of a, a=f(T). If you can take the derivative of f(T) I suggest you include a as another state, for instance X(3) and then you can access it from the X argument in xprime easily, plus it will help Matlab determine the proper step size during integration:
function xprime = RabbitTemp(t,X)
% Model of Rabbit Population
% where,
% Xo = Initial Population of Rabbits
% X(1) = Population density of Rabbit
% X(2) = Temperature T (that varies with time)
% X(3) = test parameter
%%% ODE
a = 10;
Yo = 0.4; %Just some Constant
dx(1) = (X(1))*(1 - X(1)^(a) - Yo*(X(2))));
dx(2) = sin(t);
dx(3) = ....
%%%%%%%
xprime = [dx(1) dx(2) dx(3)]';
end
Second method is to create a function(-handle) for a=f(T) and call it from within xprime:
function xprime = RabbitTemp(t,X)
% Model of Rabbit Population
% where,
% Xo = Initial Population of Rabbits
% X(1) = Population density of Rabbit
% X(2) = Temperature T (that varies with time)
% a = test parameter
%%% ODE
a = f_a(t,X);
Yo = 0.4; %Just some Constant
dx(1) = (X(1))*(1 - X(1)^(a) - Yo*(X(2))));
dx(2) = sin(t);
%%%%%%%
xprime = [dx(1) dx(2)]';
end
function a = f_a(t,X)
return 10; % You might want to change this.
end
If possible go with the first variant which is creating another state for a.

Related

MatLab: is there a way to adjust fmincon to return a 3x4 matrix of parameters?

I wrote the following code for MatLab but for now it clearly works only to return a row of parameters. The code is composed by the main file, the function file (MLE_GARCH) and the constraint file (nonlcon_GARCH) as follows:
clc
clear
%% Setup Data %%
% Input the price levels and dates.
Data = readtable('HSI.xlsx','Sheet','HSI Index');
Prices = Data.PX_LAST;
Dates = Data.Dates;
% Continuous risk free rate.
dt = 1/252; % Daily time steps
rfd = [0.01 0.02 0.03]*dt; % Duan risk-free rate
rfbs = rfd/dt; %BS risk-free rate
% Define the returns (pad first return with risk-free rate).
r = [rfd; repmat(diff(log(Prices)),1,3)];
residuals = r - mean(r);
size = length(r);
%% Duan (1995) Pricing Model Estimation %%
% Starting values for constrained optimization.
start = [0.1 0.1 0.1 0.1];
A = [0 1 1 0];
b = 1;
lb = [eps 0 0 -Inf]; % Lower bound for parameters
ub = [Inf Inf Inf Inf]; % Upper bound for parameters
[params,fval,exitflag,output,llambda,grad,hessian] = fmincon(#(p) ...
MLE_GARCH(p, Prices, rfd), start, A, b, [], [], lb, ub, #(c) Nonlcon_GARCH(c));
alpha0 = params(1);
alpha1 = params(2);
beta1 = params(3);
lambda = params(4);
This one above is the main file.
function y = MLE_GARCH(params,S,rf)
% Finds log-likelihood for the GARCH option pricing model.
alpha0 = params(1);
alpha1 = params(2);
beta1 = params(3);
lambda = params(4);
N = length(S);
% Define the returns (pad first return with risk-free rate)
r(1,:) = [rf; price2ret(S)];
% Infer the other conditional instantaneous variances
h(1) = alpha0/(1 - alpha1 - beta1);
% Initialize the conditional variance recursion
for i=2:N
h(i) = alpha0 + alpha1*(r(i-1) - rf - lambda*sqrt(h(i-1)) + ...
0.5*h(i-1))^2 + beta1*h(i-1);
end
% Initialize the log-likelihood recursion
for i=1:N
LL(i) = - 0.5*log(h(i)) - 0.5/h(i)*(r(i) - rf - lambda*sqrt(h(i)) + ...
0.5*h(i))^2;
end
% Return the negative log-likelihood function
y = -sum(LL);
This other one is the function file.
function [c, ceq] = Nonlcon_GARCH(params)
alpha0 = params(1);
alpha1 = params(2);
beta1 = params(3);
lambda = params(4);
% Set inequality constraints for parameters estimation.
c = alpha1*(1 + lambda^2) + beta1 - 1 - eps;
ceq = [];
And this last one is the constraint for fmincon.
I already tried to change some things in the main file and before the optimisation part everything is fine. However, when I started changing the function file, fmincon does not run and it tells me that "A must have 16 column(s)". The modified function file is as follows:
function y = MLE_GARCH_(params,S,rf)
% Finds log-likelihood for the GARCH option pricing model.
alpha0 = params(1,:);
alpha1 = params(2,:);
beta1 = params(3,:);
lambda = params(4,:);
N = length(S);
% Define the returns (pad first return with risk-free rate)
r = [rf; repmat(price2ret(S),1,3)];
% Infer the other conditional instantaneous variances
h(1,:) = alpha0./(1 - alpha1 - beta1);
% Initialize the conditional variance recursion
for i=2:N
h(i,:) = alpha0 + alpha1.*(r(i-1,:) - rf(:) - lambda.*sqrt(h(i-1,:)) + ...
0.5*h(i-1,:)).^2 + beta1.*h(i-1,:);
end
% Initialize the log-likelihood recursion
for i=1:N
LL(i,:) = - 0.5.*log(h(i,:)) - 0.5./h(i,:).*(r(i,:) - rf(:) - lambda.*sqrt(h(i,:)) + ...
0.5.*h(i,:))^2;
end
% Return the negative log-likelihood function
y = -sum(LL);
Given that my original code runs without problems, how can I change it to make it return a 3x4 matrix of parameters?
This is a quite long question with a lot of code that we cannot replicate because it uses a xls-file on your HDD. You may want to post a minimum viable example (MVE) for us?
Anyway, the short answer is: no.
The long anser is: Why should someone do this? Change your cost function that is builds the 3x4 matrix from a 12x1 vector and your are done.

MATLAB - passing a sinusoidal forcing function to ode45

I'm new to Matlab and am really struggling even to get to grips with the basics.
I've got a function, myspring, that solves position and velocity of a mass/spring system with damping and a driving force. I can specify values for the spring stiffness (k), damping coefficient (c), and mass (m), in the command window prior to running ode45. What I am unable to do is to define a forcing function (even something simple like g = sin(t)) and use that as the forcing function, rather than having it written into the myspring function.
Can anyone help? Here's my function:
function pdot = myspring(t,p,c,k,m)
w = sqrt(k/m);
g = sin(t); % This is the forcing function
pdot = zeros(size(p));
pdot(1) = p(2);
pdot(2) = g - c*p(2) - (w^2)*p(1);
end
and how I'm using it in the command window:
>> k = 2; c = 2; m = 4;
>> tspan = linspace(0,10,100);
>> x0 = [1 0];
>> [t,x] = ode45(#(t,p)myspring(t,p,c,k,m),tspan,x0);
That works, but what I want should look something like this (I imagine):
function pdot = myspring(t,p,c,k,m,g)
w = sqrt(k/m);
pdot = zeros(size(p));
pdot(1) = p(2);
pdot(2) = g - c*p(2) - (w^2)*p(1);
end
Then
g = sin(t);
[t,x] = ode45(#(t,p)myspring(t,p,c,k,m,g),tspan,x0);
But what I get is this
In an assignment A(:) = B, the number of elements in A and B must be the same.
Error in myspring (line 7)
pdot(2) = g - c*p(2) - (w^2)*p(1);
Error in #(t,p)myspring(t,p,c,k,m,g)
Error in odearguments (line 87)
f0 = feval(ode,t0,y0,args{:}); % ODE15I sets args{1} to yp0.
Error in ode45 (line 115)
odearguments(FcnHandlesUsed, solver_name, ode, tspan, y0, options, varargin);
Horchler, thank you for the reply. I can do as you suggested and it works. I am now faced with another problem that I hope you could advise me on.
I have a script that calculates the force on a structure due to wave interaction using Morison's equation. I gave it an arbitrary time span to begin with. I would like to use the force F that script calculates as the input driving force to myspring. Here is my Morison script:
H = 3.69; % Wave height (m)
A = H/2; % Wave amplitude (m)
Tw = 9.87; % Wave period (s)
omega = (2*pi)/Tw; % Angular frequency (rad/s)
lambda = 128.02; % Wavelength (m)
k = (2*pi)/lambda; % Wavenumber (1/m)
dw = 25; % Water depth (m)
Cm = 2; % Mass coefficient (-)
Cd = 0.7; % Drag coefficient (-)
rho = 1025; % Density of water (kg/m^3)
D = 3; % Diameter of structure (m)
x = 0; % Fix the position at x = 0 for simplicity
t = linspace(0,6*pi,n); % Define the vector t with n time steps
eta = A*cos(k*x - omega*t); % Create the surface elevation vector with size equal to t
F_M = zeros(1,n); % Initiate an inertia force vector with same dimension as t
F_D = zeros(1,n); % Initiate a drag force vector with same dimension as t
F = zeros(1,n); % Initiate a total force vector with same dimension as t
fun_inertia = #(z)cosh(k*(z+dw)); % Define the inertia function to be integrated
fun_drag = #(z)(cosh(k*(z+dw)).*abs(cosh(k*(z+dw)))); % Define the drag function to be integrated
for i = 1:n
F_D(i) = abs(((H*pi)/Tw) * (1/sinh(k*dw)) * cos(k*x - omega*t(i))) * ((H*pi)/Tw) * (1/sinh(k*dw)) * cos(k*x - omega*t(i)) * integral(fun_drag,-dw,eta(i));
F_M(i) = (Cm*rho*pi*(D^2)/4) * ((2*H*pi^2)/(Tw^2)) * (1/(sin(k*dw))) * sin(k*x - omega*t(i)) * integral(fun_inertia,-dw,eta(i));
F(i) = F_D(i) + F_M(i);
end
Any further advice would be much appreciated.
You can't pre-calculate your forcing function. It depends on time, which ode45 determines. You need to define g as a function and pass a handle to it into your integration function:
...
g = #(t)sin(t);
[t,x] = ode45(#(t,p)myspring(t,p,c,k,m,g),tspan,x0);
And then call it I n your integration function, passing in the current time:
...
pdot(2) = g(t) - c*p(2) - (w^2)*p(1);
...

Using ode45 in Matlab

I'm trying to simulate the time behavior for a physical process governed by a system of ODEs. When I switch the width of the input pulse from 20 to 19, there is no depletion of the y(1) state, which doesn't make sense physically. What am I doing wrong? Am I using ode45 incorrectly?
function test
width = 20;
center = 100;
tspan = 0:0.1:center+50*(width/2);
[t,y] = ode45(#ODEsystem,tspan,[1 0 0 0]);
plot(t,y(:,1),'k*',t,y(:,2),'k:',t,y(:,3),'k--',t,y(:,4),'k');
hold on;
axis([center-3*(width/2) center+50*(width/2) -0.1 1.1])
xlabel('Time')
ylabel('Relative values')
legend({'y1','y2','y3','y4'});
function dy = ODEsystem(t,y)
k1 = 0.1;
k2 = 0.000333;
k3 = 0.1;
dy = zeros(size(y));
% rectangular pulse
I = rectpuls(t-center,width);
% ODE system
dy(1) = -k1*I*y(1);
dy(2) = k1*I*y(1) - k2*y(2);
dy(3) = k2*y(2) - k3*I*y(3);
dy(4) = k3*I*y(3);
end
end
You are changing the parameters of your your ODEs discontinuously in time. This results in a very stiff system and less accurate, or even completely wrong, results. In this case, because the your ODE is so simple when I = 0, an adaptive solver like ode45 will take very large steps. Thus, there's a high probability that it will step right over the region where you inject the impulse and never see it. See my answer here if you're confused as to why the code in your question misses the pulse even though you've specified tspan to have (output) steps of just 0.1.
In general it is a bad idea to have any discontinuities (if statements, abs, min/max, functions like rectpuls, etc.) in your integration function. Instead, you need to break up the integration and calculate your results piecewise in time. Here's a modified version of your code that implements this:
function test_fixed
width = 19;
center = 100;
t = 0:0.1:center+50*(width/2);
I = rectpuls(t-center,width); % Removed from ODE function, kept if wanted for plotting
% Before pulse
tspan = t(t<=center-width/2);
y0 = [1 0 0 0];
[~,out] = ode45(#(t,y)ODEsystem(t,y,0),tspan,y0); % t pre-calculated, no need to return
y = out;
% Pulse
tspan = t(t>=center-width/2&t<=center+width/2);
y0 = out(end,:); % Initial conditions same as last stage from previous integration
[~,out] = ode45(#(t,y)ODEsystem(t,y,1),tspan,y0);
y = [y;out(2:end,:)]; % Append new data removing identical initial condition
% After pulse
tspan = t(t>=center+width/2);
y0 = out(end,:);
[~,out] = ode45(#(t,y)ODEsystem(t,y,0),tspan,y0);
y = [y;out(2:end,:)];
plot(t,y(:,1),'k*',t,y(:,2),'k:',t,y(:,3),'k--',t,y(:,4),'k');
hold on;
axis([center-3*(width/2) center+50*(width/2) -0.1 1.1])
xlabel('Time')
ylabel('Relative values')
legend({'y1','y2','y3','y4'});
function dy = ODEsystem(t,y,I)
k1 = 0.1;
k2 = 0.000333;
k3 = 0.1;
dy = zeros(size(y));
% ODE system
dy(1) = -k1*I*y(1);
dy(2) = k1*I*y(1) - k2*y(2);
dy(3) = k2*y(2) - k3*I*y(3);
dy(4) = k3*I*y(3);
end
end
See also my answer to a similar question.

Implement finite difference method in matlab

I am trying to implement the finite difference method in matlab. I did some calculations and I got that y(i) is a function of y(i-1) and y(i+1), when I know y(1) and y(n+1). However, I don't know how I can implement this so the values of y are updated the right way. I tried using 2 fors, but it's not going to work that way.
EDIT
This is the script and the result isn't right
n = 10;
m = n+1;
h = 1/m;
x = 0:h:1;
y = zeros(m+1,1);
y(1) = 4;
y(m+1) = 6;
s = y;
for i=2:m
y(i) = y(i-1)*(-1+(-2)*h)+h*h*x(i)*exp(2*x(i));
end
for i=m:-1:2
y(i) = (y(i) + (y(i+1)*(2*h-1)))/(3*h*h-2);
end
The equation is:
y''(x) - 4y'(x) + 3y(x) = x * e ^ (2x),
y(0) = 4,
y(1) = 6
Thanks.
Consider the following code. The central differential quotient is discretized.
% Second order diff. equ.
% y'' - 4*y' + 3*y = x*exp(2*x)
% (y(i+1)-2*y(i)+y(i-1))/h^2-4*(y(i+1)-y(i-1))/(2*h) + 3*y(i) = x(i)*exp(2*x(i));
The solution region is specified.
x = (0:0.01:1)'; % Solution region
h = min(diff(x)); % distance
As said in my comment, using this method, all points have to be solved simultaneously. Therefore, above numerical approximation of the equation is transformed in a linear system of euqations.
% System of equations
% Matrix of coefficients
A = zeros(length(x));
A(1,1) = 1; % known solu for first point
A(end,end) = 1; % known solu for last point
% y(i) y'' y
A(2:end-1,2:end-1) = A(2:end-1,2:end-1)+diag(repmat(-2/h^2+3,[length(x)-2 1]));
% y(i-1) y'' -4*y'
A(1:end-1,1:end-1) = A(1:end-1,1:end-1)+diag(repmat(1/h^2+4/(2*h),[length(x)-2 1]),-1);
% y(i+1) y'' -4*y'
A(2:end,2:end) = A(2:end,2:end)+diag(repmat(1/h^2-4/(2*h),[length(x)-2 1]),+1);
With the rhs of the differential equation. Note that the known values are calculated by 1 in the matrix and the actual value in the solution vector.
Y = x.*exp(2*x);
Y(1) = 4; % known solu for first point
Y(end) = 6; % known solu for last point
y = A\Y;
Having an equation to approximate the first order derivative (see above) you can verify the solution. (note, ddx2 is an own function)
f1 = ddx2(x,y); % first derivative (own function)
f2 = ddx2(x,f1); % second derivative (own function)
figure;
plot(x,y);
saveas(gcf,'solu1','png');
figure;
plot(x,f2-4*f1+3*y,x,x.*exp(2*x),'ko');
ylim([0 10]);
legend('lhs','rhs','Location','nw');
saveas(gcf,'solu2','png');
I hope the solution shown below is correct.

Solving ODEs with Matlab, with varying Parameters

Lets say I have a simple logistic equation
dx/dt = 2ax(1 - x/N)
where N is the carrying capacity, a is some growth rate, and both a and N are parameters I'd like to vary.
So what I want to do is to plot a 3D graph of my fixed point and the two parameters.
I understand how to find a fixed point of a single parameter.
Here is my sample code
function xprime = MyLogisticFunction(t,X) %% The ODE
% Parameters
N = 10 % Carrying Capacity
a = 0.5 % Growth Rate
x1prime = 2*a*X(1)*(1 - X(1)/N );
xprime = [x1prime ]';
end
Next my solver
% Initial Number
x0 = 0.4;
%Time Window
tspan=[0 100];
[t,x]=ode45(#MyLogisticFunction,tspan,x0);
clf
x(end,1) % This gives me the fixed point for the parameters above.
So my real question is, how do I put a for loop across two functions, that allows me to vary a and N, so that I can plot out a 3D graph of a and N and my fixed point x*.
I've tried combining both functions into one .m file but it does not seem to work
You need to pass the parameters to your function:
function xprime = MyLogisticFunction(t,X,a,N) %% The ODE
% Parameters (passed as function arguments)
% N = 10 % Carrying Capacity
% a = 0.5 % Growth Rate
x1prime = 2*a*X(1)*(1 - X(1)/N );
xprime = [x1prime ]';
end
and then when you call the ode solver:
% Initial Number
x0 = 0.4;
%Time Window
tspan=[0 100];
a = 0.1:0.1:1; % or whatever
N = 1:10; % or whatever
x_end = zeros(length(a),length(N));
for ii = 1:length(a)
for jj = 1:length(N)
[t,x]=ode45(#(t,X)MyLogisticFunction(t,X,a(ii),N(jj)),tspan,x0);
x_end(ii,jj) = x(end,1);
end
end