[英]Calculate more time points from ode() of the “deSolve” R package without increasing runtime
I wrote a function: ODEsystem = function(t, states, parameters) , which contains an ODE-system and solved it with the well documented R packages "deSolve" written by Karline Soetaert, Thomas Petzoldt and R. Woodrow Setzer. 我编写了一个函数: ODEsystem = function(t,states,parameters) ,其中包含一个ODE系统,并使用Karline Soetaert,Thomas Petzoldt和R. Woodrow Setzer编写的有据可查的R包“ deSolve”来解决该问题。 The documentation of the package is comprehensive and with many examples. 该软件包的文档非常全面,并包含许多示例。 It gives me confidence in their programming and memory optimization skills. 这使我对他们的编程和内存优化技能充满信心。
However, when solving the ODE-system with daily intervals instead of monthly intervals the time it takes to calculate the state-values for the specified moments increases tenfold. 但是,以每日间隔而不是每月间隔求解ODE系统时,计算指定时刻的状态值所花费的时间增加了十倍。 There might be a bit additional calculations for reaching the exact required moments in time, but for both cases roughly the same internal dynamic time steps should have been made. 可能还需要进行一些额外的计算才能达到所需的准确时刻,但是对于两种情况,都应该进行大致相同的内部动态时间步长。 I did not expect such a large drop in runtime. 我没想到运行时会大幅下降。
The call to ode() in “desolve” looks like this: 在“ desolve”中对ode()的调用如下所示:
out <- as.data.frame(ode(states, t=times, func=ODEsystem), parms=parameters, method="ode45"))
I used two variants for times times = seq(0, 100*365, by=365/12) # 100 years, one time point per month times = seq(0, 100*365, by=1) # 100 years, one time point per day 我使用了两种变体来表示时间= seq(0,100 * 365,by = 365/12)#100年,每月一个时间点时间= seq(0,100 * 365,by = 1)#100年,一个每天的时间点
Calling with data points per month 每月致电数据点
user system elapsed
4.59 0.00 4.58
Calling with data points per month and cmpfun() on the function containing the ODEsystem 每月使用数据点进行调用,并在包含ODEsystem的函数上使用cmpfun()进行调用
user system elapsed
4.39 0.00 4.38
Calling with data points per day 每天致电数据点
user system elapsed
44.41 0.00 44.46
Calling with data points per day and cmpfun() on the function containing the ODEsystem 每天调用数据点,并在包含ODEsystem的函数上调用cmpfun()
user system elapsed
43.01 0.00 43.17
The runtime measured with system.time() increases with factor ten when switching from monthly intervals to daily intervals. 从每月间隔切换到每天间隔时,用system.time()度量的运行时间将增加十倍。 Matters do not improve much by using cmpfun() on the function containing the ODE-system. 通过在包含ODE系统的函数上使用cmpfun() ,事情不会有太大改善。
(The output "out" is only assigned when the function call to ode() is done. Thus pre-assigning "out" yields no performance gain.) (仅在对ode()的函数调用完成后才分配输出“ out”。因此,预先分配“ out”不会产生性能提升。)
Question 1: I am looking for the reason why there is this decrease in runtime/performance. 问题1:我正在寻找运行时间/性能下降的原因。
(I expect it to be in the internals of the deSolve package.) (我希望它在deSolve软件包的内部。)
Question 2: Given the answer to Question 1, how can I improve the runtime without resorting to dynamic link libraries? 问题2:有了问题1的答案,如何在不借助动态链接库的情况下改善运行时间?
Pre-assign some memory for what will become “out” might help (using knowledge on the time steps in “times”), but I do not known which internal variable in ode() to affect. 预先分配一些内存,以解决“出局”可能会有所帮助(使用有关“时间”的时间步长的知识),但是我不知道ode()中的哪个内部变量会受到影响。
#### Clear currrent lists from memory
rm(list=ls())
### Load libraries
# library(rootSolve);library(ggplot2);
library(base);library(deSolve);library(stringr);library(compiler);library(data.table);
#### constants
dpy=365;durX1 = 40*dpy;rH = 1/durX1;durX4 = 365/12;rX4 = 1/durX4;durX6 = 365/12;rX6 = 1/durX6;durX2 = 80;rX2 = 1/durX2;durX3 = 31;rX3 = 1/durX3;durX7 = 20*365/12;rX7 = 1/durX7;durX5 = 29;rX5 = 1/durX5;durX8 = 200;rX8 = 1/durX8;fS = 0.013;fR = 8/100;fL = .03;fP = .03;fF = .05;X1zero = 1000;UDdur = 365/12*5;rK = rX3*(1/UDdur);fD1 = .05;fD2 = .05;durbt = 4;bt = 1/durbt;LX11 = 14;rF = 1/LX11;durX11 = 5;rX11 = 1/durX11;iniX12 = 0;pH = 1;frac_Im = 0;durX9 = dpy*5;ini_X2 = 1;sp = .90;fpX1 = 5;NF = fpX1*X1zero;rT1 = fD1*rX4;rT2 = fD2*rX6;pX1 = 0*sp;pX2 = 1/80*sp;pX3 = .50*sp;pX4 = .5*sp;pX6 = .5*sp;pX7 = 1/100*sp;pX5 = pX3;pX9 = 0*sp;pX8 = 1*sp;rX9 = 1/durX9;
#### vector with parameters
parameters = c(rH, rX3, rX4, rX6, rX2, rX8, rX7, rX5, rK, rT1, rT2, bt, rF, NF, rX11, pX1, pX2, pX3, pX4, pX6, pX7, pX5, pX9, pX8, rX9, X1zero)
### States contains initial conditions
states = c( X1 =X1zero-1,X2=1,X3=0, X4=0, X5=0,X6=0, X7=0, X8=0, X9=0, X10=NF,X11=0,X12=0, X13 = 0)
### function with ODE system
ODEsystem = function(t,states,parameters){
with(as.list(c(states,parameters)),{
### functions
X1part = (pX2*X2 + pX3*X3 + pX4*X4 + pX6*X6 + pX7*X7 + pX5*X5 + pX9*X9 + pX8*X8); prob1 = bt * X12 / X1zero; lF = bt * X1part/X1zero; AD = rK*(X3+X5+X4+X6)+rT1*X4+rT2*X6;
### fluxes
J1 = prob1*X1; J2 = fS*rX2*X2; J3 = (1-fS)*rX2*X2; J4 = (1-fP)*rX3*X3 ; J5 = fP*rX3*X3; J6 = (1-fF)*rX4*X4; J7 = fF*rX4*X4; J8 = rX6*X6; J9 = fR*rX7*X7; J10 = rX5*X5; J11 = (1-fR)*(1-fL)*rX7*X7; J12 = (1-fR)*fL*rX7*X7; J13 = rX8*X8; J14 = rH*X3; J15 = rH*X1; J16 = rH*X2; J17 = rH*X4; J18 = rH*X6; J19 = rH*X5; J20 = rH*X8; J21 = rH*X7; J22 = rH*X9; J23 = rK*X3; J24 = rK*X4; J25 = rK*X6; J26 = rT2*X6; J27 = rH*X1zero; J28 = rT1*X4; J29 = AD; J30 = rK*X5; J31 = rF*X12; J32 = rF*X11; J33 = rF*X10; J34 = lF*X10; J35 = rX11*X11; J36 = rF*NF; J37 = rX9*X9; J38 = 0; J39 = 0; J40 = 0; J41 = 0; J42 = 0; J43 = 0; flux1=J4/X1zero*1e4*dpy; flux2=J12/X1zero*1e4*dpy;
# rate of change
dX1 = - J1 - J15 + J27 + J29 + J37
dX2 = + J1 - J2 - J3 - J16 - J40
dX3 = + J2 - J4 - J5 - J14 - J23 - J41
dX4 = + J4 - J6 - J7 - J17 - J24 - J28
dX5 = + J9 - J10 - J19 - J30 - J43
dX6 = + J7 - J8 + J10 - J18 - J25 - J26
dX7 = + J5 + J6 + J8 - J9 - J11 - J12 - J21
dX8 = + J12 - J13 - J20 - J42
dX9 = + J3 + J11 + J13 - J22 - J37 + J40 + J41 + J42 + J43
dX10 = - J33 - J34 + J36
dX11 = - J32 + J34 - J35
dX12 = - J31 + J35
dX13 = + J38 - J39
# return the rate of change
list(c(dX1,dX2,dX3,dX4,dX5,dX6,dX7,dX8,dX9,dX10,dX11,dX12,dX13),flux1,flux2,prob1)
})
}
## compiled version of ODE system function
cfODEsystem=cmpfun(ODEsystem)
#### time points to be calculated
times = seq(0, 100*365,by=365/12) # 100 year, time points per month
#times = seq(0, 100*365,by=1) # 100 year, time points per day
### calculations
system.time(out <- as.data.frame(ode(states, t=times, func=ODEsystem, parms=parameters, method="ode45")))
#system.time(out <- as.data.frame(ode(states, t=times, func=cfODEsystem, parms=parameters, method="ode45")))
### longitudinal plots of each variable, flux1 and 2 and prob1
for (i in seq(from=2, to=dim(out)[2], by=1) ) {
tempdata <- out[c("time",names(out)[i])]
tempdata$time= tempdata$time/365
templabel <-names(out)[i]
plot(tempdata,col = "black","l",xlab="time (years)",ylab=templabel,
xlim=c(0, max(tempdata$time)), ylim=c(0, signif(max(tempdata[2]),2)))
}
So thanks for writing this question, it prompted me to look into the deSolve internals and learn a bit (and also maybe speed my own code up). 因此,感谢您编写此问题,它促使我研究了deSolve内部原理并学到了一点(也许还可以加快我自己的代码的速度)。
Question 1 问题1
The ODE function is called a number of times to solve the function (maybe less than the number of timepoints), but then is also called once a timepoint to evaluate the additional algebraic equations. 多次调用ODE函数来求解该函数(可能少于时间点的数量),但随后又一次将其称为时间点,以评估其他代数方程式。 So if you add 30x the timepoints, you will always add to the runtime but by less than a factor of 30, due to things like setup and teardown. 因此,如果您将时间点增加30倍,则由于设置和拆卸等原因,总会增加运行时间,但增加的比例小于30。
Question 2 问题2
There are a few things you can do to speed things up without resorting to C code (though that is an excellent option) 您可以采取一些措施来加快速度,而无需借助C代码(尽管这是一个很好的选择)
with
, instead using array accesses (possibly to temporary named variables). 重写代码以避免使用with
,而是使用数组访问(可能是对临时命名变量的访问)。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.