Saving derivative values in ode45 in Matlab - matlab

I'm simulating equations of motion for a (somewhat odd) system with mass-springs and double pendulum, for which I have a mass matrix and function f(x), and call ode45 to solve
M*x' = f(x,t);
I have 5 state variables, q= [ QDot, phi, phiDot, r, rDot]'; (removed Q because nothing depends on it, QDot is current.)
Now, to calculate some forces, I would like to also save the calculated values of rDotDot, which ode45 calculates for each integration step, however ode45 doesn't give this back. I've searched around a bit, but the only two solutions I've found are to
a) turn this into a 3rd order problem and add phiDotDot and rDotDot to the state vector. I would like to avoid this as much as possible, as it's already non-linear and this really makes matters a lot worse and blows up computation time.
b) augment the state to directly calculate the function, as described here. However, in the example he says to make add a line of zeros in the mass matrix. It makes sense, since otherwise it will integrate the derivative and not just evaluate it at the one point, but on the other hand it makes the mass matrix singular. Doesn't seem to work for me...
This seems like such a basic thing (to want the derivative values of the state vector), is there something quite obvious that I'm not thinking of? (or something not so obvious would be ok too....)
Oh, and global variables are not so great because ode45 calls the f() function several time while refining it's step, so the sizes of the global variable and the returned state vector q don't match at all.
In case someone needs it, here's the code:
%(Initialization of parameters are above this line)
options = odeset('Mass',#massMatrix);
[T,q] = ode45(#f, tspan,q0,options);
function dqdt = f(t,q,p)
% q = [qDot phi phiDot r rDot]';
dqdt = zeros(size(q));
dqdt(1) = -R/L*q(1) - kb/L*q(3) +vs/L;
dqdt(2) = q(3);
dqdt(3) = kt*q(1) + mp*sin(q(2))*lp*g;
dqdt(4) = q(5);
dqdt(5) = mp*lp*cos(q(2))*q(3)^2 - ks*q(4) - (mb+mp)*g;
end
function M = massMatrix(~,q)
M = [
1 0 0 0 0;
0 1 0 0 0;
0 0 mp*lp^2 0 -mp*lp*sin(q(2));
0 0 0 1 0;
0 0 mp*lp*sin(q(2)) 0 (mb+mp)
];
end

The easiest solution is to just re-run your function on each of the values returned by ode45.
The hard solution is to try to log your DotDots to some other place (a pre-allocated matrix or even an external file). The problem is that you might end up with unwanted data points if ode45 secretly does evaluations in weird places.

Since you are using nested functions, you can use their scoping rules to mimic the behavior of global variables.
It's easiest to (ab)use an output function for this purpose:
function solveODE
% ....
%(Initialization of parameters are above this line)
% initialize "global" variable
rDotDot = [];
% Specify output function
options = odeset(...
'Mass', #massMatrix,...
'OutputFcn', #outputFcn);
% solve ODE
[T,q] = ode45(#f, tspan,q0,options);
% show the rDotDots
rDotDot
% derivative
function dqdt = f(~,q)
% q = [qDot phi phiDot r rDot]';
dqdt = [...
-R/L*q(1) - kb/L*q(3) + vs/L
q(3)
kt*q(1) + mp*sin(q(2))*lp*g
q(5)
mp*lp*cos(q(2))*q(3)^2 - ks*q(4) - (mb+mp)*g
];
end % q-dot function
% mass matrix
function M = massMatrix(~,q)
M = [
1 0 0 0 0;
0 1 0 0 0;
0 0 mp*lp^2 0 -mp*lp*sin(q(2));
0 0 0 1 0;
0 0 mp*lp*sin(q(2)) 0 (mb+mp)
];
end % mass matrix function
% the output function collects values for rDotDot at the initial step
% and each sucessful step
function status = outputFcn(t,q,flag)
status = 0;
% at initialization, and after each succesful step
if isempty(flag) || strcmp(flag, 'init')
deriv = f(t,q);
rDotDot(end+1) = deriv(end);
end
end % output function
end
The output function only computes the derivatives at the initial and all successful steps, so it's basically doing the same as what Adrian Ratnapala suggested; re-run the derivative at each of the outputs of ode45; I think that would even be more elegant (+1 for Adrian).
The output function approach has the advantage that you can plot the rDotDot values while the integration is being run (don't forget a drawnow!), which can be very useful for long-running integrations.

Related

How do I vectorize this code?

I have written a recursive function, however, it takes a lot of time. Hence I vectorized it, but it does not yield the same result as the recursive function. This is my non-vectorized code:
function visited = procedure_explore( u, adj_mat, visited )
visited(u) = 1;
neighbours = find(adj_mat(u,:));
for ii = 1:length(neighbours)
if (visited(neighbours(ii)) == 0)
visited = procedure_explore( neighbours(ii), adj_mat, visited );
end
end
end
This is my vectorized code:
function visited = procedure_explore_vec( u, adj_mat, visited )
visited(u) = 1;
neighbours = find(adj_mat(u,:));
len_neighbours=length(neighbours);
visited_neighbours_zero=visited(neighbours(1:len_neighbours)) == 0;
if(~isempty(visited_neighbours_zero))
visited = procedure_explore_vec( neighbours(visited_neighbours_zero), adj_mat, visited );
end
end
This is the test code
function main
adj_mat=[0 0 0 0;
1 0 1 1;
1 0 0 0;
1 0 0 1];
u=2;
visited=zeros(size(adj_mat,1));
tic
visited = procedure_explore( u, adj_mat, visited )
toc
visited=zeros(size(adj_mat,1));
tic
visited = procedure_explore_vec( u, adj_mat, visited )
toc
end
This is the algorithm I'm trying to implement:
If vectorization is impossible, a mex solution would also be good.
Update benchmark: This benchmark is based on MATLAB 2017a. It shows that the original code is faster than other methods
Speed up between original and logical methods is 0.39672
Speed up between original and nearest methods is 0.0042583
Full code
function main_recersive
adj_mat=[0 0 0 0;
1 0 1 1;
1 0 0 0;
1 0 0 1];
u=2;
visited=zeros(size(adj_mat,1));
f_original=#()(procedure_explore( u, adj_mat, visited ));
t_original=timeit(f_original);
f_logical=#()(procedure_explore_logical( u, adj_mat ));
t_logical=timeit(f_logical);
f_nearest=#()(procedure_explore_nearest( u, adj_mat,visited ));
t_nearest=timeit(f_nearest);
disp(['Speed up between original and logical methods is ',num2str(t_original/t_logical)])
disp(['Speed up between original and nearest methods is ',num2str(t_original/t_nearest)])
end
function visited = procedure_explore( u, adj_mat, visited )
visited(u) = 1;
neighbours = find(adj_mat(u,:));
for ii = 1:length(neighbours)
if (visited(neighbours(ii)) == 0)
visited = procedure_explore( neighbours(ii), adj_mat, visited );
end
end
end
function visited = procedure_explore_nearest( u, adj_mat, visited )
% add u since your function also includes it.
nodeIDs = [nearest(digraph(adj_mat),u,inf) ; u];
% transform to output format of your function
visited = zeros(size(adj_mat,1));
visited(nodeIDs) = 1;
end
function visited = procedure_explore_logical( u, adj_mat )
visited = false(1, size(adj_mat, 1));
visited(u) = true;
new_visited = visited;
while any(new_visited)
visited = any([visited; new_visited], 1);
new_visited = any(adj_mat(new_visited, :), 1);
new_visited = and(new_visited, ~visited);
end
end
Here's a fun little function that does a non-recursive breadth-first search on the graph.
function visited = procedure_explore_logical( u, adj_mat )
visited = false(1, size(adj_mat, 1));
visited(u) = true;
new_visited = visited;
while any(new_visited)
visited = any([visited; new_visited], 1);
new_visited = any(adj_mat(new_visited, :), 1);
new_visited = and(new_visited, ~visited);
end
end
In Octave, this runs about 50 times faster than your recursive version on a 100x100 adjacency matrix. You'll have to benchmark it on MATLAB to see what you get.
You can think of your adjacency matrix as a list of paths of length exactly one. You can generate paths of other lengths n by taking it to the n-th power up to the rank of your matrix. (adj_mat^0 is the identity matrix)
In a graph with n nodes, the longest path cannot be longer than n-1, therefore you can sum all the powers together for a reachability analysis:
adj_mat + adj_mat^2 + adj_mat^3
ans =
0 0 0 0
4 0 1 3
1 0 0 0
3 0 0 3
This is the number of (different) ways that you can use to go from one node to another. For simple reachablitity, check whether this value is greater than zero:
visited(v) = ans(v, :) > 0;
Depending on your definition, you may have to change columns and rows in the result (i.e. take ans(:, v)).
For performance, you can use the lower powers to make higher ones. For example something like A + A^2 + A^3 + A^4 + A^5 would be efficiently calculated:
A2 = A^2;
A3 = A2*A
A4 = A2^2;
A5 = A4*A;
allWalks= A + A2 + A3 + A4 + A5;
Note: If you want to include the original node as being reachable from itself, you should include the identity matrix in the sum.
This minimizes the number of matrix multiplications, also MATLAB will likely execute a matrix square faster than a regular multiplication.
In my experience, matrix multiplication is relatively fast in MATLAB and this will yield the result (reachability) vector for all the nodes in the graph at once. If you are only interested in a small subset of a large graph, this is probably not the best solution.
See also this answer: https://stackoverflow.com/a/7276595/1974021
I don't think you can properly vectorise your function: Your original function never reaches the same node multiple times. By vectorising it you would pass all directly connected nodes at the same time to the next function. Therefore it then would be possible that in the following instances the same node gets reached multiple times. E.g. in your example node 1 would be reached 3 times. So while you would no longer have a loop, the function might, depending on your network, be called more times recursively which would increase the computational time.
That being said, it is not generally impossible to find all reachable nodes without loops or recursive calls. For example you could check all (valid or invalid) paths. But that would work very different from your function and, depending on the number of nodes, might result in a performance loss due to the staggering amount of paths to be checked. Your current function isn't too bad and will scale well with large networks.
A bit offtopic, but since Matlab 2016a you can use nearest() to find all reachable nodes (without the starting node). It invokes a breadth-first algorithm in contrast to your depth-first algorithm:
% add u since your function also includes it.
nodeIDs = [nearest(digraph(adj_mat),u,inf) ; u];
% transform to output format of your function
visited = zeros(size(adj_mat,1));
visited(nodeIDs) = 1;
If this is for a students project, you could argue that while your function works you used the built-in function for performance reasons.
The problem with the recursive function is related to visited(u) = 1;. That is because MATLAB uses copy-on-write technique to pass/assign variables. If you don't change visited in the body of function no copy of it is made but when it is modified a copy of it is created and modification is applied on a copy of it. To prevent that you can use a handle object to be passed by reference to the function.
Define a handle class (save it to visited_class.m):
classdef visited_class < handle
properties
visited
end
methods
function obj = visited_class(adj_mat)
obj.visited = zeros(1, size(adj_mat,1));
end
end
end
The recursive function:
function procedure_explore_handle( u, adj_mat,visited_handle )
visited_handle.visited(u) = 1;
neighbours = find(adj_mat(u,:));
for n = neighbours
if (visited_handle.visited(n) == 0)
procedure_explore_handle( n, adj_mat , visited_handle );
end
end
end
Initialize variables:
adj_mat=[0 0 0 0;
1 0 1 1;
1 0 0 0;
1 0 0 1];
visited_handle = visited_class(adj_mat);
u = 2;
Call it as :
procedure_explore_handle( u, adj_mat,visited_handle );
The result is saved into visited_handle:
disp(visited_handle.visited)
If you want to go from one point in the graph to another, the most efficient way to find it in terms of resources is Dijkstra's algorithm. The Floyd-Warshall algorithm computes all the distances between all points and can be parallellised (by starting from multiple points).
Why the need to vectorise (or use mex programming)? If you just want to make the most use of Matlab's fast matrix multiplication routines then using products of A should get you there quickly:
adj_mat2=adj_mat^2; % allowed to use 2 steps
while (adj_mat2 ~= adj_mat) % check if new points were reached
adj_mat=adj_mat2; % current set of reachable points
adj_mat2=(adj_mat^2)>0; % allowed all steps again: power method
end
This answer just gives an explicit, vectorized implementation of the suggestion from DasKrümelmonster's answer, which I think is faster than the code in the question (at least if the dimensions of the matrix are not too big). It uses the polyvalm function to evaluate the sum of powers of the adjacency matrix.
function visited = procedure_explore_vec(u, adj_mat)
connectivity_matrix = polyvalm(ones(size(adj_mat,1),1),adj_mat)>0;
visited = connectivity_matrix(u,:);
end

How to generate the desired oscillation graph? [MATLAB]

I have a mathematical equation that describes a dynamical system as
The parameters are defined as follows
k1=1; S=1; Kd=1; p=2; tau=10; k2=1; ET=1; Km=1;
I coded the system as
y(1) = 1; % based on the y-axes starting point in the last figure
y(2) = y(1) + k1*S*Kd^p/(Kd^p + y(1)^p) - k2*ET*y(1)/(Km + y(1)); % to avoid errors
for t=1:100
y(t+1) = y(t+1) + (k1*S*Kd^p/(Kd^p + y(t)^p) - k2*ET*y(t+1)/(Km + y(t+1)));
end
plot(y);
Note that I did not use tau=10 for simplicity and instead used a delayed version by 1 instead of 10 (because I am not sure how to insert a delay of 10)
And obtained the following result
However, I need to obtain this
Can anyone help me rectify the mistake in my code?
Thanking you in advance.
If we assume that for Y(t) = 0 for t < 0 then you're code could be modified to produce a similar plot. However, it looks like the plot you are looking to generate uses different initial conditions. If you're just looking to measure Tc then it appears that the signal stabilizes with the period you're looking for.
k1=1; S=1; Kd=1; p=2; tau=10; k2=1; ET=1; Km=1;
% time step size (tau MUST be divisible by dt to ensure proper array indexing)
dt = 0.01;
% time series
t = -10:dt:100;
% initialize y to all zeros so that y(t)=0 for all t<0 (initial condition)
y = zeros(size(t));
% Find starting and ending indexes to iterate from t=0 to t=100-dt
idx0 = find(t == 0);
idx1 = numel(t)-1;
% initial condition y(0) = 1
y(idx0) = 1;
for n = idx0:idx1
% The indexing used here ensures the following equivalences.
% y(n+1) = y(t+dt)
% y(n) = y(t)
% y(n - round(tau/dt)) = y(t-tau)
%
% Note that (y(t+dt)-y(t))/dt is approximately y'(t)
% Solving for y(t+dt) we get the following formula
y(n+1) = y(n) + dt*((k1*S*Kd^p/(Kd^p + y(n - round(tau/dt))^p) - k2*ET*y(n)/(Km + y(n))));
end
% plot y(t) for t > 0
plot(t(t>0),y(t>0));
Result
Seeing as things stabilize we can take the values in one of the periods and use those for the initial conditions and we get.
Edit: To elaborate, the function contains a delay of 10 which means that instead of just a single initial condition at y(0), we also need to initialize all values from t=-10 to 0. In the code posted in this answer I arbitrarily assumed that y(t) = 0 for t < 0 and y(0) = 1 because I don't know otherwise. Once we run the code and see that the signal becomes periodic we can borrow the values from one of these periods to use those as the initial conditions.
From the diagram you posted we can use our intuition to guess that, before time 0, the signal probably looks something like the region highlighted in the figure below.
If, rather than using zero to initialize y at y < 0, we copy the values in the red highlighted region, then we get a plot that is more like what you desire.
To get the plot shown above I ran the script once, then found the indices in y for the part I wanted to use as initial conditions, then copied those into a new array.
init_cond = y(7004:8004);
Then I changed script to use this array as the initial condition and changed the initial y values to
y = zeros(size(t));
y(1:1001) = init_cond;
and ran the modified script again.
Edit 2: The built-in function dde23 appears to be applicable for your problem. To see an example run the command edit ddex1 in the command window.

Matlab Optimisation - Minimise objective function using genetic algorithm

I want to set up the generic algorithm for a function that includes roughly 400 lines of script. The script itself is an optimisation process and I want to use the genetic algorithm to find the best input parameters into the optimisation process (M and OPratio). M lies between 0 and 10^7 and OPratio between 0 and 1.
The function of the script is:
NPVtotal = cut_off_optimisation(M,OPratio)
set up for the genetic algorithm:
nvars = 2; % Number of variables
LB = [0 0]; % Lower bound
UB = [10000000 1]; % Upper bound
X0 = [6670000 0.45]; % Start point
options.InitialPopulationMatrix = X0;
[M,OPratio,fval] = ga(cut_off_optimisation(M,OPratio),nvars,[],[],[],[],LB,UB)
I get following error:
Undefined function or variable 'M'.
I am new to optimisation and the genetic algorithm so would appreciate any help, please let me know if more information is necessary.
First of all I am assuming that the objective is to minimize the Objective function cut_off_optimisation.
Now first update your function to look like this
function y = cut_off_optimisation(x)
M=x(1);
OPratio=x(2);
%
% paste body of your currently used function here
%
y=NPVtotal ;
Now use this code to minimize your objective function.
nvars = 2; % Number of variables
LB = [0 0]; % Lower bound
UB = [10000000 1]; % Upper bound
X0 = [6670000 0.45]; % Start point
options = gaoptimset('PlotFcns',{#gaplotbestf},'Display','iter','InitialPopulation',X0);
[x,fval] = ga(#cut_off_optimisation,nvars,[],[],[],[],...
LB,UB,[],options);
M=x(1);
OPratio=x(2);
Update: If you don't want to update your function. Just run this main code. Keep the function NPVtotal = cut_off_optimisation(M,OPratio) in the same folder as that of the main code.
objectiveFunction=#(x)cut_off_optimisation(x(1),x(2));
nvars = 2; % Number of variables
LB = [0 0]; % Lower bound
UB = [10000000 1]; % Upper bound
X0 = [6670000 0.45]; % Start point
options = gaoptimset('PlotFcns',{#gaplotbestf},'Display','iter','InitialPopulation',X0);
[x,fval] = ga(objectiveFunction,nvars,[],[],[],[],...
LB,UB,[],options);
M=x(1);
OPratio=x(2);
fval
M
OPratio
Update: For getting final population members and fitness values. Replace the above ga function call statement to below statement.
[x,fval,exitflag,output,population,score] = ga(objectiveFunction,nvars,[],[],[],[],LB,UB,[],options);
M=x(1);
OPratio=x(2);
In here population will have the members of the final population and score will have fitness values for the final population. Default population size is 20. So you will have 20 rows in both the matrix. Number of columns in population will be equivalent to number of variables in the problem and score will be a column matrix. You can change the population size by adding option PopulationSize to gaoptimset.
options = gaoptimset('PlotFcns',{#gaplotbestf},'Display','iter','InitialPopulation',X0,'PopulationSize',30);
To know more about the options available for gaoptimset and their expected values and their {default values}. Go to matlab help and search for gaoptimset. There you will find a table with all these details. Here is the link from matlab website http://in.mathworks.com/help/gads/gaoptimset.html .There may be changes according to your matlab version. So its better to use help in matlab.

Loop seems to go forever when plotting a graph

I have written the following piece of code:
M = [3 0 0; 0 2 0; 0 0 0.5] % mass matrix
i_vals = 1:1000:60e06; % values of k_12 from 1 to 600 million in steps of 1000
modes = zeros(3, length(i_vals));
for n=1:length(i_vals)
i = i_vals(n) % i is the value of k_12
K = [i+8e06 -i -2e06; -i i+2e06 -1e06; -2e06 -1e06 5e06]; % stiffness matrix
[V,L]=eig(K,M);
V(:,I)=V;
A = V(:, [1])
transpose(A)
modes(:, n) = A
end
loglog(i_vals, modes')
But the loop seems to go forever and I do now know what is wrong with it. The idea was to get the first column from matrix V, and see what happens to the 3 elements in this column when value of k_12 is changed.
I don't know how you make this run forever. To me it looks as if it won't run at all. This won't answer your question, but will hopefully help you on the way =)
What do you want to do with this line? V(:,I)=V; What is I? Was it supposed to be i? Btw, using i and j as variables in MATLAB is not recommended (however, if you don't use complex numbers in your field, you shouldn't care too much).
You have a loop that goes 60,000 times, with calculations of eigenvalues etc. That is bound to take time (although not forever, as you state it does). You should get the answer eventually (if only the rest of the code worked). The resolution of your plot would be more than accurate enough with 10,000 or even 100,000 steps at a time.
This part:
A = V(:, [1])
transpose(A)
modes(:, n) = A
could simply be written as:
modes(:,n) = V(:,1)';
assuming you want the transposed of A. transpose(A) does nothing in this context actually. You would have to do A = transpose(A) (or rather A = A') for it to work.
There are all kinds of problems with your code - some of which may contribute to your issue.
You are computing values of i on a linear scale, but ultimately will be plotting on a log scale. You are doing a huge amount of work towards the end, when there is nothing visible in the graph for your effort. Much better to use a log scale for i_vals:
i_vals = logspace(0, 7.778, 200); % to get 200 log spaced values from
% 1 to approx 60E6`
You are using a variable I that has not been defined (in the code snippet you provide). Depending on its size, you may find that V is growing...
You are using a variable name i - while that is legal, it overwrites a built in (sqrt(-1)) which I personally find troublesome.
Your transpose(A); line doesn't do anything (You would have to do A = transpose(A);).
You don't have ; after several lines - this is going to make Matlab want to print to the console. This will take a huge amount of resource. Suppress the output with ; after every statement.
EDIT the following program runs quickly:
M = [3 0 0.0;
0 2 0.0;
0 0 0.5]; % mass matrix
i_vals = logspace(0, 7.78, 200); % values of k_12 from 1 to 600 million in steps of 1000
modes = zeros(3, length(i_vals));
for n=1:length(i_vals)
i = i_vals(n); % i is the value of k_12
K = [i+8e06 -i -2e06; -i i+2e06 -1e06; -2e06 -1e06 5e06]; % stiffness matrix
[V,L]=eig(K,M);
modes(:, n) = V(:,1);
end
loglog(i_vals, modes')
Resulting graph:
If I didn't break anything (hard to know what you were doing with I), maybe this can be helpful.

Plotting time graph in MATLAB

I have a function of this form in MATLAB,
C=S*e^(L*t)*inv(S)*C_0
where my
S=[-2 -3;3 -2]
L=[0.5 0; 0 1.5]
C_0=[1; 1]
I need to plot this function with respect to time. My output C is a 2-by-1 matrix.
What I have done is computed e^L separately using b=expm(L) and then I inserted mpower(b,t) into the function. So my resulting function in the script looks like
b=expm(L);
C=S*mpower(b,t)*inv(S)*C_0;
Now, how should I go about plotting this w.r.t time. I tried defining the time vector and then using it, but quite obviously I get the error message which says matrix dimensions do not agree. Can someone give me a suggestion?
You can probably do this in a vectorised manner but if you're not worried about speed or succinct code, why not just write a for loop?
ts = 1 : 100;
Cs = zeros(2, length(ts) );
S = [-2 -3;3 -2];
L = [0.5 0; 0 1.5];
C_0 = [1; 1];
for ii = 1 : length(ts)
b = expm(L);
Cs(:,ii) = S*mpower(b,ts(ii))*inv(S)*C_0;
end
ts contains the time values, Cs contains the values of C at each time.