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

时间:2014-11-04 11:50:17

标签: r performance ode

我写了一个函数: ODEsystem = function(t,states,parameters),它包含一个ODE系统,并用记录良好的R软件包解决它" deSolve"由Karline Soetaert,Thomas Petzoldt和R. Woodrow Setzer撰写。该软件包的文档是全面的,并有许多示例。它让我对编程和内存优化技能充满信心。

然而,当以每日间隔而不是每月间隔求解ODE系统时,计算指定时刻的状态值所花费的时间增加十倍。可能会有一些额外的计算来达到确切的所需时刻,但对于这两种情况,应该大致采用相同的内部动态时间步长。我没想到运行时会有这么大的下降。

“desolve”中对 ode()的调用如下所示:

out <- as.data.frame(ode(states, t=times, func=ODEsystem), parms=parameters, method="ode45"))

我经常使用两种变体     times = seq(0,100 * 365,by = 365/12)#100年,每月一个时间点     times = seq(0,100 * 365,by = 1)#100年,每天一个时间点

每月调用数据点

user  system elapsed 
4.59    0.00    4.58 

每月调用数据点,并在包含ODEsystem的函数上调用 cmpfun()

user  system elapsed 
4.39    0.00    4.38

每天使用数据点调用

user  system elapsed 
44.41    0.00   44.46

每天调用数据点,并在包含ODEsystem的函数上调用 cmpfun()

user  system elapsed 
43.01    0.00   43.17

使用 system.time()测量的运行时间从每月间隔切换到每日间隔时会增加10倍。在包含ODE系统的函数上使用 cmpfun(),事情并没有太大改善。

(输出&#34; out&#34;仅在完成对 ode()的函数调用时分配。因此预分配&#34; out&#34;不会产生任何性能增益。)

问题1:我正在寻找运行/性能下降的原因。

(我希望它在deSolve包的内部。)

问题2:给出问题1的答案,如何在不诉诸动态链接库的情况下改进运行时?

为“out”可能有所帮助预先分配一些内存(使用“时间”中的时间步骤的知识),但我不知道 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)))
    }

1 个答案:

答案 0 :(得分:0)

所以感谢写这个问题,它促使我调查deSolve内部并学习一点(也可能加快我自己的代码)。

问题1

ODE函数被多次调用以求解函数(可能小于时间点的数量),但是也会在一个时间点调用一次以评估其他代数方程。因此,如果您添加30倍的时间点,由于设置和拆卸等原因,您将始终添加到运行时但不到30倍。

问题2

你可以采取一些措施来加快速度而不诉诸C代码(尽管这是一个很好的选择)

  1. 使用不同的解算器(例如lsoda,在我的系统上比ode45快5倍)
  2. 使用lsoda时,增加hmax以允许自适应时间步进更自由地集成到前方(另一个加速)
  3. 重写代码以避免使用with,而是使用数组访问(可能是临时命名变量)。