Vectorizing ODE in Octave / Matlab - matlab

If I have an ode and wrote it in two ways, like here:
function re=rabdab()
x=linspace(0,2000,2000)';
tic;
[T,Y] = ode45(#fun,[x],[0 1 1]);
[T,Y] = ode45(#fun,[x],[0 1 1]);
[T,Y] = ode45(#fun,[x],[0 1 1]);
toc;
tic;
[A,B] = ode45(#fun2,[x],[0 1 1]);
[A,B] = ode45(#fun2,[x],[0 1 1]);
[A,B] = ode45(#fun2,[x],[0 1 1]);
toc;
function dy = fun(t,y)
dy = zeros(3,1); % a column vector
dy = [y(2) * y(3);...
-y(1) * y(3);...
-0.51 * y(1) * y(2);];
function dy = fun2(t,y)
dy = zeros(3,1); % a column vector
dy(1) = y(2) * y(3);
dy(2) = -y(1) * y(3);
dy(3) = -0.51 * y(1) * y(2);
There is almost no difference in time. One takes just as long as the other. But I thought that fun is the vectorized version of fun2. Or am I mistaken here?
The purpose is to speed up my code a little. The example is taken from the matlab webpage.
I think I haven't really understood what "vectorized" means.
If this is already vectorized, what would a non-vectorized code look like?

Vectorization is a concept which is closely related to functional programming. In MATLAB it means performing operations on arrays (vectors or matrices) without implicitly writing a loop.
For example, if you were to compute the function f(x) = 2x for every integer x between 1 and 100, you could write:
for x = 1:100
f(x) = 2 * x;
end
which is not vectorized code. The vectorized version is:
x = 1:100; %// Declare a vector of integer values from 1 to 100
f = 2 * x; %// Vectorized operation "*"
or even shorter:
f = 2 * (1:100);
MATLAB is an interpreted language, so obviously the interpreter translates this into some kind of loop "under the hood", but it's optimized and is usually much faster than actually interpreting a loop (read this question for reference). Well, sort of -- it's been like that until the recent releases of MATLAB, where JIT acceleration has been integrated (read here).
Now getting back to your code: what you have here is two vectorized versions of your code: one that concatenates vertically three values and one that directly assigns these values into a column vector. That's basically the same thing. Should you have done it with an explicit for loop, this would not have been "vectorized". Regarding the actual performance gain from "vectorizing" a loop (that is, converting a for loop into a vectorized operation), this depends on the how fast the for loop actually was due to JIT acceleration in the first place.
It doesn't seem that there's much to be done to speed up your code. Your functions are pretty basic, so it boils down to the internal implementation of ode45, which you cannot modify.
If you're interested in further reading about vectorization and writing faster MATLAB code in general, here's an interesting article: Loren on the Art of MATLAB: "Speeding Up MATLAB Applications".
Happy coding!

While the previous answer is correct in general terms, vectorized in the context of ODEs means something more specific. In short, a function f(t,y) is vectorized iff f(t,[y1 y2 ...]) returns [f(t,y1) f(t,y2) ...], for y1,y2 column vectors. As per the documentation [1], "this allows the solver to reduce the number of function evaluations required to compute all the columns of the Jacobian matrix."
Functions fun3 and fun4 below are properly vectorized in the ODE sense:
function re=rabdab()
x=linspace(0,20000,20000)';
opts=odeset('Vectorized','on');
tic;
[T,Y] = ode45(#fun,[x],[0 1 1]);
[T,Y] = ode45(#fun,[x],[0 1 1]);
[T,Y] = ode45(#fun,[x],[0 1 1]);
toc;
tic;
[A,B] = ode45(#fun2,[x],[0 1 1]);
[A,B] = ode45(#fun2,[x],[0 1 1]);
[A,B] = ode45(#fun2,[x],[0 1 1]);
toc;
tic;
[A,B] = ode45(#fun3,[x],[0 1 1],opts);
[A,B] = ode45(#fun3,[x],[0 1 1],opts);
[A,B] = ode45(#fun3,[x],[0 1 1],opts);
toc;
tic;
[A,B] = ode45(#fun4,[x],[0 1 1],opts);
[A,B] = ode45(#fun4,[x],[0 1 1],opts);
[A,B] = ode45(#fun4,[x],[0 1 1],opts);
toc;
function dy = fun(t,y)
dy = zeros(3,1); % a column vector
dy = [y(2) * y(3);...
-y(1) * y(3);...
-0.51 * y(1) * y(2);];
function dy = fun2(t,y)
dy = zeros(3,1); % a column vector
dy(1) = y(2) * y(3);
dy(2) = -y(1) * y(3);
dy(3) = -0.51 * y(1) * y(2);
function dy = fun3(t,y)
dy = zeros(size(y)); % a matrix with arbitrarily many columns, rather than a column vector
dy = [y(2,:) .* y(3,:);...
-y(1,:) .* y(3,:);...
-0.51 .* y(1,:) .* y(2,:);];
function dy = fun4(t,y)
dy = [y(2,:) .* y(3,:);... % same as fun3()
-y(1,:) .* y(3,:);...
-0.51 .* y(1,:) .* y(2,:);];
(As a side remark: omitting the unnecessary memory allocation with zeros lets fun4 run slightly faster than fun3.)
Q: What about vectorizing with respect to the first argument?
A: For the ODE solvers, the ODE function is vectorized only with respect to the second argument. The boundary value problem solver bvp4c, however, does require vectorization with respect to the first and second arguments. [1]
The official documentation [1] provides further details on ODE-specific vectorization (see section "Description of Jacobian Properties").
[1] https://www.mathworks.com/help/releases/R2015b/matlab/ref/odeset.html

Related

Matlab solving ODEs can not understand this function description

I have stumbled upon this matlab code that solves this ODE
y'''(t) + a y(t) = -b y''(t) + u(t)
but I am confused by the ode_system function definition, specifically by the y(2) y(3) part. I would greatly appreciate if someone can shed some light
y(2) y(3) part in the ode_system function confuses me and how it contributes to overaal solution
% Define the parameters a and b
a = 1;
b = 2;
% Define the time horizon [0,1]
time_horizon = [0, 1];
% Define the initial conditions for y, y', and y''
initials = [0; 0; 0];
% Define the function handle for the input function u(t)
%sin(t) is a common example of a time-varying function.
% You can change the definition of u to any other function of time,
% such as a constant, a step function, or a more complex function, depending on your needs
u = #(t) sin(t);
% Define the function handle for the system of ODEs
odefunction = #(t, y) ode_system(t, y, a, b, u);
% Solve the ODEs using ode45
[t, y] = ode45(odefunction, time_horizon, initials);
% Plot the solution
plot(t, y(:,1), '-', 'LineWidth', 2);
xlabel('t');
ylabel('y');
function dydt = ode_system(t, y, a, b, u)
%Define the system of ODEs
dydt = [y(2); y(3); -b*y(3) + u(t) - a*y(1)];
end
This is more of a maths question than a Matlab one.
We would like to rewrite our ODE equation so that there is a single time derivative on the left-hand side and no derivatives on the right.
Currently we have:
y'''(t)+ay(t)=-by''(t)+u(t)
By letting z = y' and x = z' (= y''), we can rewrite this as:
x'(t)+a y(t)=-b x(t)+u(t)
So now we have 3 equations in the form:
y' = z
z' = x
x' = -b * x + u - a *y
We can also think of this as a vector equation where v = (y, z, x).
The right-hand side would then be,
v(1)' = v(2)
v(2)' = v(3)
v(3)' = -b * v(3) + u - a * v(1)
which is what you have in the question.

Numerical simulations of n-competing species

I would like to make numerical simulations of n-species sharing the common resource in Matlab. My system is of the form;
dx_n/dt=x_n(2 - x_{n-1} - x_n),
dx_{n-1}/dt=x_{n-1}(3 - x_{n-2} - x_{n-1} - x_n).
I have written a code for "n=5" as follows;
dx = #(t,x) [
x(1); x(1)*(2-x(1)-x(2));
x(2); x(2)*(3-x(2)-x(2)-x(3));
x(3); x(3)*(3-x(2)-x(3)-x(4));
x(4); x(4)*(3-x(3)-x(4)-x(5));
x(5); x(5)*(2-x(4)-x(5))];
tspan=[0 15];
x0=[0 2 0 3 0 4 0 6 0 8]
[t,x]=ode45(#(t,x) dx(t,x), tspan, x0);
figure(1)
plot(t,x)
My question is, "Is there an easier way in which I can write a general code for any 'n'?". Can someone please help me. I'm new to Matlab.
Seems very tricky, if not impossible, to code the differential in an anonymous function as you did for n=5.
So you have to put the math into an explicit (not anonymous) function
function dx = spdiff(t,x)
n = len(x);
dx(1) = x(1);
dx(2) = x(1) .* (2 - x(1) - x(2));
for k = 2:(n-1)
dx(2*k-1) = x(k);
dx(2*k) = x(k) .* (3 - x(k-1) - x(k) - x(k+1));
end
dx(2*n-1) = x(n);
dx(2*n) = x(n) .* (2 - x(n-1) - x(n));
And then to solve it
[t,x]=ode45(#spdiff, tspan, x0);
Addressing the comment...
Let's use the set of ODE in original question, I have written out the complete code above.
To get the derivative of the original system in one line use
dx = #(t,x) x.*conv(1-x, [1 1 1], 'same')
1-x produces the vector with entries 1-x(k), convolution with [1 1 1] produces the array
[ 1-x(1), 2-x(1)-x(2), 3-x(1)-x(2)-x(3), 3-x(2)-x(3)-x(4),...
..., 3-x(n-2)-x(n-1)-x(n), 2-x(n-1)-x(n), 1-x(n)]
The option shape='same' reduces that to the central part, that is, cuts away the first and last element. Then with elementwise multiplication you get exactly the right side of the formula.
One integration with the initial values from the question divided by 10 looks like
In gnu-octave, which is similar to matlab, the following code produces a similar graph
dx = #(x,t) x.*conv(1-x, [1 1 1], 'same')
tspan = 0:0.1:10;
x0=[0.2, 0.3, 0.4, 0.6, 0.8];
sol = lsode(dx, x0, tspan );
plot(tspan, sol)

How do I solve a third order differential equation using ode23 in MATLAB and plot the step response

I have based my solution off the example provided by Matlab - solving a third order differential equation.
My problem is that I have to solve the third order differential equation, y'''+3y''+2y'+y=4u, by using the ode23 solver and plot the step response.
Here is what I have so far.
function dy = diffuy( t, y )
%Split uy into variables in equation
%y'''+3y''+2y'+y=4u
%Have to take third order equation and convert to 1st order
%y0 = y
%y1 = y0'
%y2 = y1'
%y3 = y2'
%y0' = y1
%y1' = y2
%y2' = y3
%y3' = y''' = -3*y2-2*y1-y0+4*u
%Assume that y(0)= 0, y'(0)=0, y''(0)=0, no initial conditions
u = #(t) heaviside(t);
dy = zeros(4,1);
dy(1) = y(2);
dy(2) = y(3);
dy(3) = y(4);
dy(4) = -3*y(3)-2*y(2)-y(1)+4*u(t);
end
In my main file, I have the code:
[T, Y]=ode23(#diffuy,[0 20],[0 0 0 0]);
figure(1)
plot(T,Y(:,1))
A=[0 1 0;0 0 1; -1 -2 -3]
B=[0;0;4]
C=[1 0 0]
D=[0]
sys4=ss(A,B,C,D)
figure(2)
step(sys4)
The problem I am having is that the step response produced from using the state-space representation commands in MATLAB do not match the step response produced by the ode23, so I assumed that I solved the differential equation incorrectly. Any tips or comments would be very helpful.
Step Response from ss commands:
Step Response from using ode23:
I'm not sure how the linked question got the correct answer because you're actually solving a fourth-order equation using their methodology.
The right hand-side vector given to the ODE suite should only have n entries for an n-order problem.
In your case, the change of variables
results in the third order system
with the initial conditions
.
Changing diffuy to
function dy = diffuy( t, y )
dy = zeros(3,1);
dy(1) = y(2);
dy(2) = y(3);
dy(3) = -3*y(3)-2*y(2)-y(1)+4*u(t);
end
gives a solution that matches the state-space model.

Solving 2nd order differential equation with boundary condition z(inf) = 0

How can I solve a 2nd order differential equation with boundary condition like z(inf)?
2(x+0.1)·z'' + 2.355·z' - 0.71·z = 0
z(0) = 1
z(inf) = 0
z'(0) = -4.805
I can't understand where the boundary value z(inf) is to be used in ode45() function.
I used in the following condition [z(0) z'(0) z(inf)], but this does not give accurate output.
function [T, Y]=test()
% some random x function
x = #(t) t;
t=[0 :.01 :7];
% integrate numerically
[T, Y] = ode45(#linearized, t, [1 -4.805 0]);
% plot the result
plot(T, Y(:,1))
% linearized ode
function dy = linearized(t,y)
dy = zeros(3,1);
dy(1) = y(2);
dy(2) = y(3);
dy(3) = (-2.355*y(2)+0.71*y(1))/((2*x(t))+0.2);
end
end
please help me to solve this differential equation.
You seem to have a fairly advanced problem on your hands, but very limited knowledge of MATLAB and/or ODE theory. I'm happy to explain more if you want, but that should be in chat (I'll invite you) or via personal e-mail (my last name AT the most popular mail service from Google DOT com)
Now that you've clarified a few things and explained the whole problem, things are a bit more clear and I was able to come up with a reasonable solution. I think the following is at least in the general direction of what you'd need to do:
function [tSpan, Y2, Y3] = test
%%# Parameters
%# Time parameters
tMax = 1e3;
tSpan = 0 : 0.01 : 7;
%# Initial values
y02 = [1 -4.805]; %# second-order ODE
y03 = [0 0 4.8403]; %# third-order ODE
%# Optimization options
opts = optimset(...
'display', 'off',...
'TolFun' , 1e-5,...
'TolX' , 1e-5);
%%# Main procedure
%# Find X so that z2(t,X) -> 0 for t -> inf
sol2 = fminsearch(#obj2, 0.9879680932400429, opts);
%# Plug this solution into the original
%# NOTE: we need dense output, which is done via deval()
Z = ode45(#(t,y) linearized2(t,y,sol2), [0 tMax], y02);
%# plot the result
Y2 = deval(Z,tSpan,1);
plot(tSpan, Y2, 'b');
%# Find X so that z3(t,X) -> 1 for t -> inf
sol3 = fminsearch(#obj3, 1.215435887288112, opts);
%# Plug this solution into the original
[~, Y3] = ode45(#(t,y) linearized3(t,y,sol3), tSpan, y03);
%# plot the result
hold on, plot(tSpan, Y3(:,1), 'r');
%# Finish plots
legend('Second order ODE', 'Third order ODE')
xlabel('T [s]')
ylabel('Function value [-]');
%%# Helper functions
%# Function to optimize X for the second-order ODE
function val = obj2(X)
[~, y] = ode45(#(t,y) linearized2(t,y,X), [0 tMax], y02);
val = abs(y(end,1));
end
%# linearized second-order ODE with parameter X
function dy = linearized2(t,y,X)
dy = [
y(2)
(-2.355*y(2) + 0.71*y(1))/2/(X*t + 0.1)
];
end
%# Function to optimize X for the third-order ODE
function val = obj3(X3)
[~, y] = ode45(#(t,y) linearized3(t,y,X3), [0 tMax], y03);
val = abs(y(end,2) - 1);
end
%# linearized third-order ODE with parameters X and Z
function dy = linearized3(t,y,X)
zt = deval(Z, t, 1);
dy = [
y(2)
y(3)
(-1 -0.1*zt + y(2) -2.5*y(3))/2/(X*t + 0.1)
];
end
end
As in my comment above, I think you're confusing a couple of things. I suspect this is what is requested:
function [T,Y] = test
tMax = 1e3;
function val = obj(X)
[~, y] = ode45(#(t,y) linearized(t,y,X), [0 tMax], [1 -4.805]);
val = abs(y(end,1));
end
% linearized ode with parameter X
function dy = linearized(t,y,X)
dy = [
y(2)
(-2.355*y(2) + 0.71*y(1))/2/(X*t + 0.1)
];
end
% Find X so that z(t,X) -> 0 for t -> inf
sol = fminsearch(#obj, 0.9879);
% Plug this ssolution into the original
[T, Y] = ode45(#(t,y) linearized(t,y,sol), [0 tMax], [1 -4.805]);
% plot the result
plot(T, Y(:,1));
end
but I can't get it to converge anymore for tMax beyond 1000 seconds. You may be running into the limits of ode45 capabilities w.r.t. accuracy (switch to ode113), or your initial value is not accurate enough, etc.

Using MATLAB to write a function that implements Newton's method in two dimensions

I am trying to write a function that implements Newton's method in two dimensions and whilst I have done this, I have to now adjust my script so that the input parameters of my function must be f(x) in a column vector, the Jacobian matrix of f(x), the initial guess x0 and the tolerance where the function f(x) and its Jacobian matrix are in separate .m files.
As an example of a script I wrote that implements Newton's method, I have:
n=0; %initialize iteration counter
eps=1; %initialize error
x=[1;1]; %set starting value
%Computation loop
while eps>1e-10&n<100
g=[x(1)^2+x(2)^3-1;x(1)^4-x(2)^4+x(1)*x(2)]; %g(x)
eps=abs(g(1))+abs(g(2)); %error
Jg=[2*x(1),3*x(2)^2;4*x(1)^3+x(2),-4*x(2)^3+x(1)]; %Jacobian
y=x-Jg\g; %iterate
x=y; %update x
n=n+1; %counter+1
end
n,x,eps %display end values
So with this script, I had implemented the function and the Jacobian matrix into the actual script and I am struggling to work out how I can actually create a script with the input parameters required.
Thanks!
If you don't mind, I'd like to restructure your code so that it is more dynamic and more user friendly to read.
Let's start with some preliminaries. If you want to make your script truly dynamic, then I would recommend that you use the Symbolic Math Toolbox. This way, you can use MATLAB to tackle derivatives of functions for you. You first need to use the syms command, followed by any variable you want. This tells MATLAB that you are now going to treat this variable as "symbolic" (i.e. not a constant). Let's start with some basics:
syms x;
y = 2*x^2 + 6*x + 3;
dy = diff(y); % Derivative with respect to x. Should give 4*x + 6;
out = subs(y, 3); % The subs command will substitute all x's in y with the value 3
% This should give 2*(3^2) + 6*3 + 3 = 39
Because this is 2D, we're going to need 2D functions... so let's define x and y as variables. The way you call the subs command will be slightly different:
syms x, y; % Two variables now
z = 2*x*y^2 + 6*y + x;
dzx = diff(z, 'x'); % Differentiate with respect to x - Should give 2*y^2 + 1
dzy = diff(z, 'y'); % Differentiate with respect to y - Should give 4*x*y + 6
out = subs(z, {x, y}, [2, 3]); % For z, with variables x,y, substitute x = 2, y = 3
% Should give 56
One more thing... we can place equations into vectors or matrices and use subs to simultaneously substitute all values of x and y into each equation.
syms x, y;
z1 = 3*x + 6*y + 3;
z2 = 3*y + 4*y + 4;
f = [z1; z2];
out = subs(f, {x,y}, [2, 3]); % Produces a 2 x 1 vector with [27; 25]
We can do the same thing for matrices, but for brevity I won't show you how to do that. I will defer to the code and you can see it then.
Now that we have that established, let's tackle your code one piece at a time to truly make this dynamic. Your function requires the initial guess x0, the function f(x) as a column vector, the Jacobian matrix as a 2 x 2 matrix and the tolerance tol.
Before you run your script, you will need to generate your parameters:
syms x y; % Make x,y symbolic
f1 = x^2 + y^3 - 1; % Make your two equations (from your example)
f2 = x^4 - y^4 + x*y;
f = [f1; f2]; % f(x) vector
% Jacobian matrix
J = [diff(f1, 'x') diff(f1, 'y'); diff(f2, 'x') diff(f2, 'y')];
% Initial vector
x0 = [1; 1];
% Tolerance:
tol = 1e-10;
Now, make your script into a function:
% To run in MATLAB, do:
% [n, xout, tol] = Jacobian2D(f, J, x0, tol);
% disp('n = '); disp(n); disp('x = '); disp(xout); disp('tol = '); disp(tol);
function [n, xout, tol] = Jacobian2D(f, J, x0, tol)
% Just to be sure...
syms x, y;
% Initialize error
ep = 1; % Note: eps is a reserved keyword in MATLAB
% Initialize counter
n = 0;
% For the beginning of the loop
% Must transpose into a row vector as this is required by subs
xout = x0';
% Computation loop
while ep > tol && n < 100
g = subs(f, {x,y}, xout); %g(x)
ep = abs(g(1)) + abs(g(2)); %error
Jg = subs(J, {x,y}, xout); %Jacobian
yout = xout - Jg\g; %iterate
xout = yout; %update x
n = n + 1; %counter+1
end
% Transpose and convert back to number representation
xout = double(xout');
I should probably tell you that when you're doing computation using the Symbolic Math Toolbox, the data type of the numbers as you're calculating them are a sym object. You probably want to convert these back into real numbers and so you can use double to cast them back. However, if you leave them in the sym format, it displays your numbers as neat fractions if that's what you're looking for. Cast to double if you want the decimal point representation.
Now when you run this function, it should give you what you're looking for. I have not tested this code, but I'm pretty sure this will work.
Happy to answer any more questions you may have. Hope this helps.
Cheers!