Solve systems of ODEs in Julia with initial condition as 2D array - arrays

http://diffeq.sciml.ai/latest/tutorials/ode_example.html
I am trying to use the ODE solver in Julia (DifferentialEquations.jl) to solve a system of n interacting particles. Let's say that the system is in 2D, and the equation of motion of each particle is described by a second-order ODE of its position with respect to time. Then four variables are needed for each particle, two for positions and two for velocities. Then 4n variables are needed to be declared. Is there a way to generalize, such that one does not need to list all 4n equations one by one?
For example:
http://diffeq.sciml.ai/latest/tutorials/ode_example.html#Example-2:-Solving-Systems-of-Equations-1
I try to modify the Lorenz equation in the link above a little bit to n particles (which is a very very rough attempt since I actually have no idea how to do it) by trying to extend u and du to 2D arrays.
using DifferentialEquations
using Plots
n = 4
function lorenz(du,u,p,t,i)
du[i,1] = 10.0*(u[i,2]-u[i,1])*sum(u[1:n,1])
du[i,2] = (u[i,1]*(28.0-u[i,3]) - u[i,2])*sum(u[1:n,1])
du[i,3] = (u[i,1]*u[i,2] - (8/3)*u[i,3])*sum(u[1:n,1])
end
u0 = hcat([1.0;0.0;0.0], [0.0;1.0;0.0], [0.0;0.0;1.0])
tspan = (0.0,100.0)
prob = ODEProblem(lorenz,u0,tspan)
sol = solve(prob)
This, without surprise, does not work, but I hope that you get the idea what I am trying to do. Is there anyway for the ODE solver to solve u as a 2D array (or other ways that can achieve similar purposes?)

The problem is not the 2D Array. For example replacing your lorenz definition with
function lorenz(du,u,p,t)
du[1,1] = 10.0*(u[1,2]-u[1,1])
du[1,2] = (u[1,1]*(28.0-u[1,3]) - u[1,2])
du[1,3] = (u[1,1]*u[1,2] - (8/3)*u[1,3])
end
will work.
The problem is the function signature, the additional i is not supported. If you want to solve a network of Lorenz oscillators you have to code it with a function with the same signature, e.g. lorenz_network!(du, u, p, t) for the inplace version. Put a loop over the individual oscillators in your function and you are almost there.

Related

MATLAB: Improving for-loop

I need to multiply parts of a column vector with a fixed row vector. I solved this problem using a for-loop. However, I am wondering if the performance can be improved as I have to perform this kind of computation around 50 million times. Here's my code so far:
multMat = 1:5;
mat = randi(5,10,1);
windowSize = 5;
vout = nan(10,1);
for r = windowSize : 10
vout(r) = multMat * mat( (r - windowSize + 1) : r);
end
I was thinking about uisng arrayfun. However, first I don't know how to adress the cell range (i.e. the previous five cells including the current cell), and second, I am not sure if arrayfun will be any faster than using the loop?
This sliding vector multiplication you're describing is an example of what is known as convolution. The following produces the same result as the loop in your example:
vout = [nan(windowSize-1,1);
conv(mat,flip(multMat),'valid')];
If your output doesn't really need the leading NaN values which aren't overwritten in your loop then the conv expression is sufficient without concatenating the NaN elements to it.
For sufficiently large vectors this is of course not guaranteed to be as fast as you'd like it to be, but MATLAB's built-in convolution implementation is likely to be pretty close to an optimal tool for the job.

removing second layer for loop when defining array

Working in MATLAB R2017a. I'm trying to optimise a piece of code I'm working on. It uses arrays to store field values on a grid.
In order to create a specific function in a field array I originally used the straight forward method of two for loops iterating over all the array elements. But i know for loops are slow so since then I came back and tried my best to remove them. However I could only manage to remove one of the loops; leaving me with this:
for n = 1:1:K
%%% define initial pertubation
t=n*dt;
% create array for source Ez field.
xtemps = (1:Ng)*dX;
for k = 1:Ng
ztemp = k*dX;
Ez0(k,:) = THzamp * (1/(1+exp(-(t-stepuppos)))) * exp(-((xtemps-...
THzstartx).^2)./(bx^2)) .* (t-((ztemp-THzstartz)/vg))*exp(-((t-((ztemp-...
THzstartz)/vg))^2)/(bt^2));
end
The important bit here is the last 5 lines, but I figured the stuff before might be important for context. I've removed the for loop looping over the x coordinates. I want to vectorize the z/k for loop but I can't figure out how to distinguish between the dimensions with the array oporators.
Edit: THzamp, stepuppos, bx, bt, THzstartz, THzstartx are all just scalars, they control the function (Ez0) I'm trying to create. dX and t are also just scalars. Ez0 is a square array of size Ng.
What I want to achieve is to remove the for loop that loops over k, so that that the values of ztemp are defined in a vector (like xtemps already is), rather than individually in the loop. However, I don't know how I'd write the definition of Ez0 in that case.
First time posting here, if I'm doing it wrong let me know. If you need more info just ask.
It isn't clear if n is used in the other headers and as stated in the comments your sizes aren't properly defined so you'll have to ensure the sizes are correct.
However, you can give this vectorize code a try.
n = 1:K
%%% define initial pertubation
t=n*dt;
% create array for source Ez field.
xtemps = (1:Ng)*dX;
for k = 1:Ng
ztemp = k*dX;
Ez0(k,:) = THzamp .* (1./(1+exp(-(t-stepuppos)))) .* exp(-((xtemps-...
THzstartx).^2)./(bx^2)) .* (t-((ztemp-THzstartz)/vg)).*exp(-((t-((ztemp-...
THzstartz)/vg)).^2)/(bt.^2));
end
So now t has the size K you'll need to ensure stepupposand (ztemp-THzstartz)/vg) have the same size K. Also you can take a look at vectors vs array operators here.

use five point stencil to evaluate function with vector inputs and converge to maximum output value

I am familiar with iterative methods on paper, but MATLAB coding is relatively new to me and I cannot seem to find a way to code this.
In code language...
This is essentially what I have:
A = { [1;1] [2;1] [3;1] ... [33;1]
[1;2] [2;2] [3;2] ... [33;2]
... ... ... ... ....
[1;29] [2;29] [3;29] ... [33;29] }
... a 29x33 cell array of 2x1 column vectors, which I got from:
[X,Y] = meshgrid([1:33],[1:29])
A = squeeze(num2cell(permute(cat(3,X,Y),[3,1,2]),1))
[ Thanks to members of stackOverflow who helped me do this ]
I have a function that calls each of these column vectors and returns a single value. I want to institute a 2-D 5-point stencil method that evaluates a column vector and its 4 neighbors and finds the maximum value attained through the function out of those 5 column vectors.
i.e. if I was starting from the middle, the points evaluated would be :
1.
A{15,17}(1)
A{15,17}(2)
2.
A{14,17}(1)
A{14,17}(2)
3.
A{15,16}(1)
A{15,16}(2)
4.
A{16,17}(1)
A{16,17}(2)
5.
A{15,18}(1)
A{15,18}(2)
Out of these 5 points, the method would choose the one with the largest returned value from the function, move to that point, and rerun the method. This would continue on until a global maximum is reached. It's basically an iterative optimization method (albeit a primitive one). Note: I don't have access to the optimization toolbox.
Thanks a lot guys.
EDIT: sorry I didn't read the iterative part of your Q properly. Maybe someone else wants to use this as a template for a real answer, I'm too busy to do so now.
One solution using for loops (there might be a more elegant one):
overallmax=0;
for v=2:size(A,1)-1
for w=2:size(A,2)-1
% temp is the horizontal part of the "plus" stencil
temp=A((v-1):(v+1),w);
tmpmax=max(cat(1,temp{:}));
temp2=A(v,(w-1):(w+1));
% temp2 is the vertical part of the "plus" stencil
tmpmax2=max(cat(1,temp2{:}));
mxmx=max(tmpmax,tmpmax2);
if mxmx>overallmax
overallmax=mxmx;
end
end
end
But if you're just looking for max value, this is equivalent to:
maxoverall=max(cat(1,A{:}));

Matlab: how to use an array in dsolve function?

I have an ODE system of two equations, but want to minimize it with using just one equation with the result of the other.
1)
t=linspace(0,2,3);
syms x(t) y(t);
inits='x(0)=2,y(0)=0';
[x,y]=dsolve('Dx=y','Dy=(y*2)-x', inits)
x = 2*exp(t) - 2*t*exp(t);
y = -2*t*exp(t)
xx=eval(vectorize(x));
xx = 2.0000; 0; -14.7781
yy=eval(vectorize(y));
yy = 0; -5.4366; -29.5562
After I had got the results, I tried to solve it just with one equation and use xx array in Dy equation.
2)
inits='y(0)=0';
[y]=dsolve('Dy=(y*2)-xx', inits);
y = xx/2 - (xx*exp(2*t))/2
yy=eval(vectorize(y));
yy = 0; 0; 396.0397
The values are not the same as it was in the first example. How to get the same result using array?
One problem seems to be that variable xx is not symbolic, so the symbolic solver appears to be considering it as a constant.
A bigger problem is that you really haven't identified how exactly you want matlab to treat the xx values as a continuous function, when it's merely a vector of three points! The fact that you are even expecting the output to be the same for the second case indicates some kind of misunderstanding to me.
But to make this definite, lets assume that you want it to treat xx as a ZOH (zero order held) continuous signal. To handle this symbolically I believe you would need to construct the ZOH signal explicitly using Heaviside functions.
Alternative you could solve it numerically using ode45 for example
t = [0,1,2];
xx = [2, 0, -14.7781];
dydy = #(t,y) 2*y - xx(1+trunc(t));
y = ode45(dydt, [0,2], 0);
This will return yy values of [0, -6.39, -47.21] at the t values of [0, 1, 2] respectively.
This corresponds well with the theoretical values (calculated by hand) of [0, 1-e^2, e^2-e^4] for the ZOH system.
As you can see the above answer is much more in line with your original solution of yy = [0, -5.4366, -29.5562]. Naturally however the two systems differ, as the first one was fed with a continuous time exponential signal whereas the second system was fed with a very coarsely sampled approximation!
You make the two more similar by sampling at a faster rate (finer time interval), and also by interpolating the intersample points with something better than a ZOH.
Update:
Thank you. Maybe can you help me with creating ZOH continuous signal? How to do that?
In the above example I created a ZOH in my derivative function (dydx) by using the three given points in the xx vector and accessing these using "xx(1+trunc(t))". This uses trunc (truncate) to explicitly hold the input constant during the inter-sample (non integer) times.
Seeing as your ODE is linear, you could also use the matlab function "lsim()" which allows you to directly specify the time vector and input vector, and also to directly specify the type of input interpolation (including ZOH, which is actually the default).
For example:
t=[0,1,2]
x=[2,0,-2*e^2]
num=-1
den=[1,-2]
mytf = tf(num,den)
y = lsim(mytf,x,t,0,'zoh');
As with my previous ode45 numerical solution, this gives the identical solution of,
y = [0.00000; -6.38906; -47.20909]
Update (again)
Thank you. Maybe can you help me with creating ZOH continuous signal? How to do that?
Re the symbolic solver. I don't have access to the Matlab symbolic library, but if you really want to use the symbolic solver, then as I explained previous, you can construct a continuous time ZOH signal using the heaviside (unit step) function. Something like the following should do it:
syms xzoh(t)
xzoh = xx(1)*heaviside(t) + (xx(2)-xx(1))*heaviside(t-1) + (xx(3)-xx(2))*heaviside(t-2)

Matlab array of struct : Fast assignment

Is there any way to "vector" assign an array of struct.
Currently I can
edges(1000000) = struct('weight',1.0); //This really does not assign the value, I checked on 2009A.
for i=1:1000000; edges(i).weight=1.0; end;
But that is slow, I want to do something more like
edges(:).weight=[rand(1000000,1)]; //with or without the square brackets.
Any ideas/suggestions to vectorize this assignment, so that it will be faster.
Thanks in advance.
This is much faster than deal or a loop (at least on my system):
N=10000;
edge(N) = struct('weight',1.0); % initialize the array
values = rand(1,N); % set the values as a vector
W = mat2cell(values, 1,ones(1,N)); % convert values to a cell
[edge(:).weight] = W{:};
Using curly braces on the right gives a comma separated value list of all the values in W (i.e. N outputs) and using square braces on the right assigns those N outputs to the N values in edge(:).weight.
You can try using the Matlab function deal, but I found it requires to tweak the input a little (using this question: In Matlab, for a multiple input function, how to use a single input as multiple inputs?), maybe there is something simpler.
n=100000;
edges(n)=struct('weight',1.0);
m=mat2cell(rand(n,1),ones(n,1),1);
[edges(:).weight]=deal(m{:});
Also I found that this is not nearly as fast as the for loop on my computer (~0.35s for deal versus ~0.05s for the loop) presumably because of the call to mat2cell. The difference in speed is reduced if you use this more than once but it stays in favor of the for loop.
You could simply write:
edges = struct('weight', num2cell(rand(1000000,1)));
Is there something requiring you to particularly use a struct in this way?
Consider replacing your array of structs with simply a separate array for each member of the struct.
weights = rand(1, 1000);
If you have a struct member which is an array, you can make an extra dimension:
matrices = rand(3, 3, 1000);
If you just want to keep things neat, you could put these arrays into a struct:
edges.weights = weights;
edges.matrices = matrices;
But if you need to keep an array of structs, I think you can do
[edges.weight] = rand(1, 1000);
The reason that the structs in your example don't get initialized properly is that the syntax you're using only addresses the very last element in the struct array. For a nonexistent array, the rest of them get implicitly filled in with structs that have the default value [] in all their fields.
To make this behavior clear, try doing a short array with clear edges; edges(1:3) = struct('weight',1.0) and looking at each of edges(1), edges(2), and edges(3). The edges(3) element has 1.0 in its weight like you want; the others have [].
The syntax for efficiently initializing an array of structs is one of these.
% Using repmat and full assignment
edges = repmat(struct('weight', 1.0), [1 1000]);
% Using indexing
% NOTE: Only correct if variable is uninitialized!!!
edges(1:1000) = struct('weight', 1.0); % QUESTIONABLE
Note the 1:1000 instead of just 1000 when indexing in to the uninitialized edges array.
There's a problem with the edges(1:1000) form: if edges is already initialized, this syntax will just update the values of selected elements. If edges has more than 1000 elements, the others will be left unchanged, and your code will be buggy. Or if edges is a different type, you could get an error or weird behavior depending on its existing datatype. To be safe, you need to do clear edges before initializing using the indexing syntax. So it's better to just do full assignment with the repmat form.
BUT: Regardless of how you initialize it, an array-of-structs like this is always going to be inherently slow to work with for larger data sets. You can't do real "vectorized" operations on it because your primitive arrays are all broken up in to separate mxArrays inside each struct element. That includes the field assignment in your question – it is not possible to vectorize that. Instead, you should switch a struct-of-arrays like Brian L's answer suggests.
You can use a reverse struct and then do all operations without any errors
like this
x.E(1)=1;
x.E(2)=3;
x.E(2)=8;
x.E(3)=5;
and then the operation like the following
x.E
ans =
3 8 5
or like this
x.E(1:2)=2
x =
E: [2 2 5]
or maybe this
x.E(1:3)=[2,3,4]*5
x =
E: [10 15 20]
It is really faster than for_loop and you do not need other big functions to slow your program.

Resources