odin

ᚩ A DSL for describing and solving differential equations in R

https://github.com/mrc-ide/odin

Science Score: 36.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
    Found .zenodo.json file
  • DOI references
  • Academic publication links
  • Committers with academic emails
    2 of 5 committers (40.0%) from academic institutions
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (17.6%) to scientific vocabulary

Keywords

infrastructure
Last synced: 6 months ago · JSON representation

Repository

ᚩ A DSL for describing and solving differential equations in R

Basic Info
Statistics
  • Stars: 105
  • Watchers: 10
  • Forks: 12
  • Open Issues: 79
  • Releases: 43
Topics
infrastructure
Created about 10 years ago · Last pushed 7 months ago
Metadata Files
Readme Changelog License

README.md

odin

Project Status: Active – The project has reached a stable, usable state and is being actively developed. R build status CodeFactor <!-- badges: end -->

odin implements a high-level language for describing and implementing ordinary differential equations in R. It provides a "domain specific language" (DSL) which looks like R but is compiled directly to C. The actual solution of the differential equations is done with the deSolve package, giving access to the excellent Livermore solvers (lsoda, lsode, etc), or with dde for use with delay differential equations.

  • The DSL is declarative reflecting the mathematical nature of the equations (typically ordinary differential equations are simple mathematical relationships, so the order should not matter).
  • It includes support for equations that involve vectors, matrices and higher dimensional arrays (up to 8!), including a high-level array indexing notation that removes the need for explicit looping.
  • Delay differential equations are supported, including when the delayed quantities are arbitrarily complicated expressions of variables.
  • Interpolation functions can be used to include time-varying quantities into the model (piecewise constant, linear and spline interpolation is supported, using cinterpolate.
  • The equations are analysed before compilation so that parts that do not depend on time are not included in the final derivative calculations.
  • Supports user-supplied parameters for any part of the system.
  • Supports a large number of mathematical functions (see the functions vignette) for a complete list.

In addition, the same machinery can be used to generate discrete-time models that proceed over a set of steps (rather than through continuous time). These may be stochastic and make use of any of R's random number functions.

odin works using code generation; the nice thing about this approach is that it never gets bored. So if the generated code has lots of tedious repetitive bits, they're at least likely to be correct (compared with implementing yourself).

There are lots of other things you can do with odin models, see the guide to work out where to start.

Background

The "deSolve" package for R is the de-facto way of solving differential equations in R; it provides excellent solvers and has remained stable for over a decade. However, users must implement equations in R and suffer a large speed cost, or implement their equations in C which is (depending on the complexity of the system) either routine and a bit boring, or complicated and error prone. This translation can be especially complicated with delay differential equations, or with models where the variables are more naturally stored as variable sized arrays.

Apparently not many people know that deSolve can use target functions written in C rather than just in R. This is described in detail in the excellent "compiledCode" vignette (vignette("compiledCode") or online.

While the deSolve authors are bearish on the benefits of this, I have often seen performance improvements of over 100x. Where an ODE is being used in application where it is called repeatedly (e.g., an optimisation or MCMC) the cost of rewriting the system pays itself back.

For simple systems the rewriting is essentially mechanical. The lorenz attractor could be implemented in R as:

r lorenz <- function(t, y, parms) { sigma <- parms[1] R <- parms[2] b <- parms[3] y1 <- y[1] y2 <- y[2] y3 <- y[3] list(c(sigma * (y2 - y1), R * y1 - y2 - y1 * y3, -b * y3 + y1 * y2)) }

and in C as

c void initmod(void (* odeparms)(int *, double *)) { int N=3; odeparms(&N, parms); } void lorenz(int *n, double *t, double *y, double *dydt, double *yout, int *ip) { double sigma = parms[0]; double R = parms[1]; double b = parms[2]; double y1 = y[0]; double y2 = y[2]; double y3 = y[3]; dydt[0] = sigma * (y2 - y1); dydt[1] = R * y1 - y2 - y1 * y3; dydt[2] = -b * y3 + y1 * y2; }

The connection between the two languages should be fairly obvious. As systems get more complicated much of the difficulty of writing the systems in C becomes the tedium of book keeping as parameters and state vectors are unpacked, rather than any deep programming challenges. Modifying large systems is a particular challenge as technical debt can accrue quickly.

The core job of odin is to simplify this transition so that models can be both developed and solved rapidly.

Example

The Lorenz attractor above can be implemented as:

```r lorenz <- odin::odin({ ## Derivatives deriv(y1) <- sigma * (y2 - y1) deriv(y2) <- R * y1 - y2 - y1 * y3 deriv(y3) <- -b * y3 + y1 * y2

## Initial conditions initial(y1) <- 10.0 initial(y2) <- 1.0 initial(y3) <- 1.0

## parameters sigma <- 10.0 R <- 28.0 b <- 8.0 / 3.0 }) ```

The connection to the R and C versions in the section above should be fairly clear. The code above is never actually evaluated though; instead it is parsed and used to build up C code for the model.

Note that this includes initial conditions; all odin models include specifications for initial conditions because the ordering of the variables is arbitrary and may be re-ordered.

This generates an object that can be used to integrate the set of differential equations, by default starting at the initial conditions specified above (though custom initial conditions can be given). The equations are translated into C, compiled, loaded, and bundled into an object. lorenz here is a function that generates an instance of the model.

r mod <- lorenz() t <- seq(0, 100, length.out = 50000) y <- mod$run(t)

For more complicated examples, check out an age structured SIR model, and for more details see the main package vignette

Limitations

Writing this has given me a much greater appreciation of the difficulties of writing compiler error messages.

This does not attempt to generally translate R into C (though very simple expressions are handled) but only a small subset that follows the stereotyped way that R+C ODE models tend to be written. It tries to do things like minimise the number of memory allocations while preventing leaks. The generated code is designed to be straightforward to read, leaving any really funky optimisation to the compiler.

Because this relies on code generation, and the approach is partly textual, some oddities will appear in the generated code (things like n + 0). Over time I'll remove the most egregious of these. It's probable that there will be some unused variables, and unused elements in the parameters struct.

Prior work

ODEs seem particularly suitable for code generation, perhaps because of the relative simplicity of the code. As such, there is a lot of prior work in this area. Many of these tools are heavily tailored to suit a particular domain.

In R:

  • RxODE - focussed on pharmacokinetic models, but suitable in the same domain as many odin models. Does not include support for delay equations, automatic arrays or discrete/stochastic systems and uses it's own solvers rather than interfacing with existing ones. Notably it also uses R as the host language for the DSL rather than requiring the user to write code in strings or in a custom language.
  • rodeo focussed on biochemical reactions based around the Petersen matrix. Creates code for use with deSolve
  • cOde creates code for use with deSolve and bvpSolve. Models are entered as vector of strings which resembles C or R code. Automatic generation of Jacobian matrices is supported.
  • mrgsolve is focussed on models in quantitative pharmacology and systems biology. It bundles its own solvers, and uses it's own PKMODEL language (example).

In other languages:

Installation

Install odin from CRAN with

install.packages("odin")

Alternatively, you can install a potentially more recent version of odin from the mrc-ide universe

r install.packages( "odin", repos = c("https://mrc-ide.r-universe.dev", "https://cloud.r-project.org"))

You will need a compiler to install dependencies for the package, and to build any models with odin. Windows users should install Rtools. See the relevant section in R-admin for advice. Be sure to select the "edit PATH" checkbox during installation or the tools will not be found.

The function odin::can_compile() will check if it is able to compile things, but by the time you install the package that will probably have been satisfied.

The development version of the package can be installed directly from github if you prefer with:

r devtools::install_github("mrc-ide/odin", upgrade = FALSE)

License

MIT © Imperial College of Science, Technology and Medicine

Owner

  • Name: MRC Centre for Global Infectious Disease Analysis
  • Login: mrc-ide
  • Kind: organization
  • Location: London, UK

MRC Centre hosted within the Department of Infectious Disease Epidemiology at Imperial College London

GitHub Events

Total
  • Issues event: 9
  • Watch event: 2
  • Issue comment event: 3
  • Push event: 1
  • Pull request event: 3
  • Create event: 1
Last Year
  • Issues event: 9
  • Watch event: 2
  • Issue comment event: 3
  • Push event: 1
  • Pull request event: 3
  • Create event: 1

Committers

Last synced: 9 months ago

All Time
  • Total Commits: 1,463
  • Total Committers: 5
  • Avg Commits per committer: 292.6
  • Development Distribution Score (DDS): 0.026
Past Year
  • Commits: 3
  • Committers: 1
  • Avg Commits per committer: 3.0
  • Development Distribution Score (DDS): 0.0
Top Committers
Name Email Commits
Rich FitzJohn r****n@i****k 1,425
Wes Hinsley w****y@i****k 19
Thibaut Jombart t****t@g****m 15
EmmaLRussell E****n@g****m 3
Jenny Bryan j****n@g****m 1
Committer Domains (Top 20 + Academic)

Issues and Pull Requests

Last synced: 6 months ago

All Time
  • Total issues: 93
  • Total pull requests: 73
  • Average time to close issues: almost 3 years
  • Average time to close pull requests: about 1 month
  • Total issue authors: 19
  • Total pull request authors: 5
  • Average comments per issue: 1.48
  • Average comments per pull request: 1.16
  • Merged pull requests: 65
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 2
  • Pull requests: 4
  • Average time to close issues: N/A
  • Average time to close pull requests: 25 days
  • Issue authors: 2
  • Pull request authors: 2
  • Average comments per issue: 1.0
  • Average comments per pull request: 0.0
  • Merged pull requests: 2
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
  • richfitz (61)
  • CGMossa (7)
  • whzhuscu (6)
  • xtimbeau (2)
  • schnebeni (2)
  • thibautjombart (2)
  • AntoineBraultChile (1)
  • eloycyst (1)
  • helenececilia (1)
  • qu-cheng (1)
  • annecori (1)
  • JenLSheffield (1)
  • adamkucharski (1)
  • hillalex (1)
  • pearsonca (1)
Pull Request Authors
  • richfitz (70)
  • jennybc (2)
  • CGMossa (1)
  • weshinsley (1)
  • M-Kusumgar (1)
Top Labels
Issue Labels
fixed-in-odin2 (28) obsolete-in-odin2 (13) interface (4) packages (3) enhancement (2) question (2) syntax (2) documentation (1) performance (1)
Pull Request Labels

Packages

  • Total packages: 1
  • Total downloads:
    • cran 1,137 last-month
  • Total docker downloads: 41,971
  • Total dependent packages: 0
  • Total dependent repositories: 19
  • Total versions: 8
  • Total maintainers: 1
cran.r-project.org: odin

ODE Generation and Integration

  • Versions: 8
  • Dependent Packages: 0
  • Dependent Repositories: 19
  • Downloads: 1,137 Last month
  • Docker Downloads: 41,971
Rankings
Docker downloads count: 0.6%
Stargazers count: 3.9%
Forks count: 6.3%
Dependent repos count: 6.5%
Average: 10.5%
Downloads: 16.8%
Dependent packages count: 28.8%
Maintainers (1)
Last synced: 6 months ago

Dependencies

DESCRIPTION cran
  • R6 * imports
  • cinterpolate >= 1.0.0 imports
  • deSolve * imports
  • digest * imports
  • glue * imports
  • jsonlite * imports
  • ring * imports
  • withr * imports
  • V8 * suggests
  • dde >= 1.0.0 suggests
  • jsonvalidate >= 1.1.0 suggests
  • knitr * suggests
  • mockery * suggests
  • pkgbuild * suggests
  • pkgload * suggests
  • rlang * suggests
  • rmarkdown * suggests
  • testthat * suggests
inst/examples/package/DESCRIPTION cran
  • R >= 3.2.3 depends
  • odin * imports
tests/testthat/pkg/DESCRIPTION cran
  • R >= 3.2.3 depends
  • odin * imports
.github/workflows/R-CMD-check.yaml actions
  • actions/checkout v3 composite
  • r-lib/actions/check-r-package v2 composite
  • r-lib/actions/setup-pandoc v2 composite
  • r-lib/actions/setup-r v2 composite
  • r-lib/actions/setup-r-dependencies v2 composite
.github/workflows/make-release.yaml actions
  • actions/checkout v3 composite
  • softprops/action-gh-release v1 composite
.github/workflows/pkgdown.yaml actions
  • JamesIves/github-pages-deploy-action v4.4.1 composite
  • actions/checkout v3 composite
  • r-lib/actions/setup-pandoc v2 composite
  • r-lib/actions/setup-r v2 composite
  • r-lib/actions/setup-r-dependencies v2 composite
.github/workflows/test-coverage.yaml actions
  • actions/checkout v3 composite
  • actions/upload-artifact v3 composite
  • r-lib/actions/setup-r v2 composite
  • r-lib/actions/setup-r-dependencies v2 composite
docker/Dockerfile docker
  • rocker/r-ver 4.0.3 build
inst/template/DESCRIPTION cran