I am trying to optimize my code where I integrate a system of non-autonomous ODEs. The problem is the interpolations that I am doing in order to have the value of a parameter at each time step needed by the integrator.
This is the code:
# Code that integrates the Hanski model to obtain the solutions
# Load pkgs and remove data -----------------------------------------------------
import Pkg
using DifferentialEquations
using DataFrames
using CSV
using Plots
using Shapefile
import GeoDataFrames as GDF
using LinearAlgebra
using ODE
using Interpolations
using DiffEqParamEstim
using SparseArrays
#using RecursiveArrayTools
using Optimization
using Statistics
using Dates
# Constants
N = 5
m_c = 0.001 # probability of mosquito in a car
alp = 1/200 # average natural dispersal distance
# Create fake data
end_ind = 5
# Create fake mobility matrix
eta = [0.0 7.74415 5.24014 8.57707 5.21319;
6.9175 0.0 7.23771 45.2212 16.8025;
6.17319 7.426 0.0 17.7236 5.49988;
8.40241 44.21 20.1597 0.0 5309.46;
5.8309 17.5283 5.36841 5226.36 0.0]
# IC
pop_init = zeros(end_ind)
pop_init[1] = 1
# Fake time series
num_years = 1
num_days = 365*num_years
days = 1:num_days
days_per_year = 365
dates = collect(1:num_days)
# Fake seasonal
function fake_timeseries!(interpolated_functions, days ,days_per_year,num_days,dates)
R_Ms = Matrix{Float64}(undef, num_days,end_ind)
R_Ms[1:end,1] .= (30 .+ 10 * sin.(2 * π * days / days_per_year) + 2. * randn(num_days))./10
R_Ms[1:end,2] .= (30 .+ 10 * sin.(2 * π * days / days_per_year) + 2. * randn(num_days))./30
R_Ms[1:end,3] .= (30 .+ 10 * sin.(2 * π * days / days_per_year) + 2. * randn(num_days))./40
R_Ms[1:end,4] .= (30 .+ 10 * sin.(2 * π * days / days_per_year) + 2. * randn(num_days))./10
R_Ms[1:end,5] .= (30 .+ 10 * sin.(2 * π * days / days_per_year) + 2. * randn(num_days))./100
# Perform interpolation for each location
for i in 1:end_ind
# Extract temperature values for the current location
R_Ms_val = R_Ms[:, i]
dates_num = 1:size(dates, 1)
# Perform linear interpolation
itp = LinearInterpolation(dates_num, R_Ms_val,extrapolation_bc=Flat())
# Store the interpolated function
push!(interpolated_functions, itp)
end
end
# Create an array to store interpolated functions
interpolated_functions = []
fake_timeseries!(interpolated_functions, days ,days_per_year,num_days,dates)
interpolated_functions_mint = []
fake_timeseries!(interpolated_functions_mint, days ,days_per_year,num_days,dates)
# Parameters
R_M_vec = zeros(N)
min_temp = zeros(N)
p = [0.001,0.000000001,5, -0.5,-15]
# Non autonomous sigmoidal model ---------------------------------------------------------
# Withouth interpolation
function fun_na1!(du, u, p, t)
R_M_vec .= 1#[interpolated_functions[i](t) for i in 1:N] # Time series RM
min_temp .= 1#[interpolated_functions_mint[i](t) for i in 1:N] # Time series min temperature
du.= R_M_vec.*(p[1]./(1.0 .+ exp.(-p[2]*m_c.*eta.+p[3]))*u).* (1 .- u) - exp.(p[4].+(p[5].*min_temp)).* u
end
# With just one interpolation
function fun_na2!(du, u, p, t)
R_M_vec .= [interpolated_functions[i](t) for i in 1:N] # Time series RM
min_temp .= 1#[interpolated_functions_mint[i](t) for i in 1:N] # Time series min temperature
du.= R_M_vec.*(p[1]./(1.0 .+ exp.(-p[2]*m_c.*eta.+p[3]))*u).* (1 .- u) - exp.(p[4].+(p[5].*min_temp)).* u
end
# With both interpolations
function fun_na3!(du, u, p, t)
R_M_vec .= [interpolated_functions[i](t) for i in 1:N] # Time series RM
min_temp .= [interpolated_functions_mint[i](t) for i in 1:N] # Time series min temperature
du.= R_M_vec.*(p[1]./(1.0 .+ exp.(-p[2]*m_c.*eta.+p[3]))*u).* (1 .- u) - exp.(p[4].+(p[5].*min_temp)).* u
end
p1 = [0.001,0.000000001,5, -0.5,-15, R_M_vec, min_temp]
# Non autonomous sigmoidal model ---------------------------------------------------------
function fun_na1!(du, u, p1, t)
p1[6] .= [interpolated_functions[i](t) for i in 1:N] # Time series RM
p1[7] .= [interpolated_functions_mint[i](t) for i in 1:N] # Time series min temperature
du.= R_M_vec.*(p1[1]./(1.0 .+ exp.(-p1[2]*m_c.*eta.+p1[3]))*u).* (1 .- u) - exp.(p1[4].+(p1[5].*min_temp)).* u
end
# Set parameters
t0=0.0f0
tf=365.0f0
tspan = (t0, tf)
t_vect=1:tf
u0 = pop_init
# Create the ode model to solve it ---------------------------------------------------------
# Pkg.add("BenchmarkTools")
using BenchmarkTools
prob1 = DifferentialEquations.ODEProblem(fun_na1!, u0, tspan, p)
prob2 = DifferentialEquations.ODEProblem(fun_na2!, u0, tspan, p)
prob3 = DifferentialEquations.ODEProblem(fun_na3!, u0, tspan, p)
prob4 = DifferentialEquations.ODEProblem(fun_na3!, u0, tspan, p)
@benchmark sol = solve(prob1,alg_hints=[:stiff])
@benchmark sol = solve(prob2,alg_hints=[:stiff])
@benchmark sol = solve(prob3,alg_hints=[:stiff])
@benchmark sol = solve(prob4,alg_hints=[:stiff])
# plot(sol)
This is the output:
The memory allocated is multiply by an order of magnitude aprox. when introducing the interpolation inside the ode function. Is there any other way to do this which is more efficient? I have tried to used all the recommendations given in the differentialequations.jl youtube tutorial