Is it possible to improve the performance of an ODE system solving, if we have to evaluate the equation many times with different parameters (not in a Monte Carlo manner, but sequentially), and if we want to evaluate part of the solution at many time points? For instance, if we consider the following Lotka-Volterra system (just as an example), we’ll use only the first component of the solution (sol[1,:]), but we want to know its values at 10000 time points between [0,10]. Then we would do something with that and reassign the parameters for the system for further iterations.
In the example, we do 1000 iterations. If I run main() multiple times after the initial compilation, @time tells
1.549323 seconds (10.09 M allocations: 1.368 GiB, 11.56% gc time)
The number of allocations look big to me. Can we improve the performance, for instance by pre-allocating the solution variable beforehand, or specifying that we do not need to know the values of the second component? And indeed, we do not need to store the solution after we calculate new parameters for the system.
using LinearAlgebra;
using Plots;
using Random;
using DifferentialEquations;
using BenchmarkTools;
@inbounds function odefun!(du, u, p, t)
du[1] = u[1]*(p[1]-p[2]*u[2])
du[2] = -u[2]*(p[3]-p[4]*u[1])
return nothing
end
function main()
u0 = [0.7, 0.3]
p = [0.15,0.2,0.5,0.9]
tspan = (0.0, 10)
prob = ODEProblem{true}(odefun!, u0, tspan, p)
sol = solve(
prob,
DP5(),
saveat = 0.001,
reltol = 1e-3,
abstol = 1e-6,
alg_hints = [:nonstiff],
)
@time for i::Int32 = 1:1000
p = rand(4,) # Just to simulate new parameters.
prob = remake(prob; p = p)
sol = solve(
prob,
DP5(),
saveat = 0.001,
reltol = 1e-3,
abstol = 1e-6,
alg_hints = [:nonstiff],
)
# Do something with sol[1,:], and set new value for variable p:
# p = fancyfunc(sol[1,:])
end
plot(sol.t,sol[1,:])
end
main()