简体   繁体   中英

solving compiled ODE in desolve package in R

I am solving ODE's in R using the deSolve package. In order to speed up the calculations I want to use compiled code, using the instructions here

I am showing below an example of ODE system - which I am coding using Rcpp below. The details of the ODE system are taken from an example MATLAB code (can be found here ). I wanted to simulate a non-trivial set of ODE in order to see a difference in only R and compile code. The following is my drive file, where I calculated the mass-balances in two different ways (only R and compiled code)

library(deSolve)
library(ggplot2)
library(microbenchmark)

source('parameters_gprotein.R')
p   <- parameters()

source('IC_gprotein.R')
IC  <- Initial_conditions()

TIME = seq(from = 0, to = 600)

source('odes_gprotein.R')
sim.data.df  <- as.data.frame(vode(IC,TIME,ODE_gprotein,p,
                                    mf = 22, rtol=1e-3,atol=1e-6,maxord = 5,
                                    verbose = F))

Rcpp::sourceCpp("odes_gprotein.cpp")
sim.data.df <- as.data.frame(vode(IC,TIME,odes_gprotein,p,
                                      mf = 22, rtol = 1e-3, atol = 1e-6, maxord = 5,
                                      verbose = F))

My question is since vode call is made in R . Does that mean the equations are solved in compiled code if the mass balances are formed in cpp and the speed gains are realized, or do I have to also make the vode call in cpp file.

Certainly, the microbenchmarking results show that there is a speed gain when using odes_gprotein.cpp

Unit: milliseconds
                                                                                                                               expr
  sim.data.df1 <- as.data.frame(vode(IC, TIME, ODE_gprotein, p,      mf = 22, rtol = 0.001, atol = 1e-06, maxord = 5, verbose = F))
 sim.data.df2 <- as.data.frame(vode(IC, TIME, odes_gprotein, p,      mf = 22, rtol = 0.001, atol = 1e-06, maxord = 5, verbose = F))


       min        lq      mean    median        uq      max neval
 27.801954 29.543624 31.213758 30.565434 31.399140 86.28537   100
  8.188846  8.577824  9.177491  8.817025  9.437214 18.94304   100

Thanks

Reproducible example

In the future, please link to / provide all written code that the question is based on.

Comparisons

When writing a comparison of R and Rcpp function code you should append to the end of the c++ function name _cpp so that the call to different implementations is clear. eg is ODE_gprotein() or odes_gprotein() the C++ implementation?

In the case of the benchmark, it appears as if odes_gprotein() is the cpp() call since the microbenchmark is lower. This means that the vode() function is calling the Rcpp wrapper introduced into the namespace via the Rcpp::sourceCpp .

Gaining Speed

If it is possible, all code should be embedded within cpp for the greatest gain. This means you may have to do an implementation from scratch if speed is a huge factor (eg working with very large data or performing computationally intensive simulations). The recommendation comes as a result of decreasing the relay between the R code front and the C++ code. In essence, when the C++ code is called from R the objects must sometimes be copied and reformatted into different objects behind the scenes before they can be worked upon. This is particularly the case if you are using objects outside of the default Rcpp types (eg RcppArmadillo , RcppGSL , et cetera) as a copy must happen from R's object structure to the importing structure.

What's Happening in Rcpp::sourceCpp (Behind the Scenes)

Let's dive a little bit deeper into what is actually happening behind Rcpp::sourceCpp() . Specifically, when the method is called the different attributes are extracted (eg the // [[Rcpp::]] tags) and their options are enabled.

The most common tag is // [[Rcpp::export] which creates a wrapper or an abstraction layer around the C++ code so that it can easily work with R objects. This behavior is important for three reasons: 1. You do not need to worry about SEXP types and memory protection, 2. Automatic creation of an R level function with a default .Call("fname") , and 3. code caches that enable compiled code to only be recompiled on change.

Thus, when you call odes_gprotein() you are actually calling a C++ function.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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