简体   繁体   English

从“ deSolve” R包的​​ode()计算更多的时间点,而不增加运行时间

[英]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代码(尽管这是一个很好的选择)

  1. Use a different solver (eg lsoda, which on my system is around 5x faster than ode45) 使用其他求解器(例如lsoda,它在我的系统上比ode45快5倍)
  2. When using lsoda, increase the hmax to allow the adaptive timestepping more freedom to integrate ahead (another speedup) 使用lsoda时,请增加hmax以允许自适应时间步长更大的自由度来提前集成(另一种提速)
  3. rewrite the code to avoid the use of with , instead using array accesses (possibly to temporary named variables). 重写代码以避免使用with ,而是使用数组访问(可能是对临时命名变量的访问)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM