Title: | ODE Generation and Integration |
---|---|
Description: | Generate systems of ordinary differential equations (ODE) and integrate them, using a domain specific language (DSL). The DSL uses R's syntax, but compiles to C in order to efficiently solve the system. A solver is not provided, but instead interfaces to the packages 'deSolve' and 'dde' are generated. With these, while solving the differential equations, no allocations are done and the calculations remain entirely in compiled code. Alternatively, a model can be transpiled to R for use in contexts where a C compiler is not present. After compilation, models can be inspected to return information about parameters and outputs, or intermediate values after calculations. 'odin' is not targeted at any particular domain and is suitable for any system that can be expressed primarily as mathematical expressions. Additional support is provided for working with delays (delay differential equations, DDE), using interpolated functions during interpolation, and for integrating quantities that represent arrays. |
Authors: | Rich FitzJohn [aut, cre], Thibaut Jombart [ctb], Imperial College of Science, Technology and Medicine [cph] |
Maintainer: | Rich FitzJohn <[email protected]> |
License: | MIT + file LICENSE |
Version: | 1.5.11 |
Built: | 2024-08-30 05:38:37 UTC |
Source: | https://github.com/mrc-ide/odin |
Test if compilation appears possible. This is used in some
examples, and tries compiling a trivial C program with
pkgbuild
. Results are cached between runs within a session
so this should be fast to rely on.
can_compile(verbose = FALSE, refresh = FALSE)
can_compile(verbose = FALSE, refresh = FALSE)
verbose |
Be verbose when running commands? |
refresh |
Try again to compile, skipping the cached value? |
We use pkgbuild
in order to build packages, and it includes a
set of heuristics to locate and organise your C compiler. The most
likely people affected here are Windows users; if you get this
ensure that you have rtools installed. Using
pkgbuild::find_rtools()
with debug = TRUE
may be helpful for
diagnosing compiler issues.
A logical scalar
can_compile() # will take ~0.1s the first time can_compile() # should be basically instantaneous
can_compile() # will take ~0.1s the first time can_compile() # should be basically instantaneous
Create an odin model from a file, text string(s) or expression.
The odin_
version is a "standard evaluation" escape hatch.
odin(x, verbose = NULL, target = NULL, workdir = NULL, validate = NULL, pretty = NULL, skip_cache = NULL, compiler_warnings = NULL, debug_enable = NULL, no_check_unused_equations = NULL, options = NULL) odin_(x, verbose = NULL, target = NULL, workdir = NULL, validate = NULL, pretty = NULL, skip_cache = NULL, compiler_warnings = NULL, debug_enable = NULL, no_check_unused_equations = NULL, options = NULL)
odin(x, verbose = NULL, target = NULL, workdir = NULL, validate = NULL, pretty = NULL, skip_cache = NULL, compiler_warnings = NULL, debug_enable = NULL, no_check_unused_equations = NULL, options = NULL) odin_(x, verbose = NULL, target = NULL, workdir = NULL, validate = NULL, pretty = NULL, skip_cache = NULL, compiler_warnings = NULL, debug_enable = NULL, no_check_unused_equations = NULL, options = NULL)
x |
Either the name of a file to read, a text string (if length is greater than 1 elements will be joined with newlines) or an expression. |
verbose |
Logical scalar indicating if the compilation should
be verbose. Defaults to the value of the option
|
target |
Compilation target. Options are "c", "r" or "js",
defaulting to the option |
workdir |
Directory to use for any generated files. This is
only relevant for the "c" target. Defaults to the value of the
option |
validate |
Validate the model's intermediate representation
against the included schema. Normally this is not needed and is
intended primarily for development use. Defaults to the value
of the option |
pretty |
Pretty-print the model's intermediate
representation. Normally this is not needed and is intended
primarily for development use. Defaults to the value of the
option |
skip_cache |
Skip odin's cache. This might be useful if the
model appears not to compile when you would expect it to.
Hopefully this will not be needed often. Defaults to the option
|
compiler_warnings |
Previously this attempted detection of compiler warnings (with some degree of success), but is currently ignored. This may become supported again in a future version depending on underlying support in pkgbuild. |
debug_enable |
Enable debugging commands in generated code
(currently |
no_check_unused_equations |
If |
options |
Named list of options. If provided, then all other options are ignored. |
Do not use odin::odin
in a package; you almost certainly want
to use odin_package instead.
A generated model can return information about itself; odin_ir
An odin_generator
object (an R6 class) which can be used
to create model instances.
If the model accepts user parameters, then the parameter to the
constructor or the $set_user()
method can be used to control
the behaviour when unknown user actions are passed into the
model. Possible values are the strings stop
(throw an error),
warning
(issue a warning but keep going), message
(print a
message and keep going) or ignore
(do nothing). Defaults to
the option odin.unused_user_action
, or warning
otherwise.
When generating a model one must chose between using the
dde
package to solve the system or the default
deSolve
. Future versions may allow this to switch when
using run
, but for now this requires tweaking the generated
code to a point where one must decide at generation. dde
implements only the Dormand-Prince 5th order dense output solver,
with a delay equation solver that may perform better than the
solvers in deSolve. For non-delay equations, deSolve
is
very likely to outperform the simple solver implemented.
Rich FitzJohn
## Compile the model; exp_decay here is an R6ClassGenerator and will ## generate instances of a model of exponential decay: exp_decay <- odin::odin({ deriv(y) <- -0.5 * y initial(y) <- 1 }, target = "r") ## Generate an instance; there are no parameters here so all instances ## are the same and this looks a bit pointless. But this step is ## required because in general you don't want to have to compile the ## model every time it is used (so the generator will go in a ## package). mod <- exp_decay$new() ## Run the model for a series of times from 0 to 10: t <- seq(0, 10, length.out = 101) y <- mod$run(t) plot(y, xlab = "Time", ylab = "y", main = "", las = 1)
## Compile the model; exp_decay here is an R6ClassGenerator and will ## generate instances of a model of exponential decay: exp_decay <- odin::odin({ deriv(y) <- -0.5 * y initial(y) <- 1 }, target = "r") ## Generate an instance; there are no parameters here so all instances ## are the same and this looks a bit pointless. But this step is ## required because in general you don't want to have to compile the ## model every time it is used (so the generator will go in a ## package). mod <- exp_decay$new() ## Run the model for a series of times from 0 to 10: t <- seq(0, 10, length.out = 101) y <- mod$run(t) plot(y, xlab = "Time", ylab = "y", main = "", las = 1)
Build an odin model generator from its intermediate representation, as generated by odin_parse. This function is for advanced use.
odin_build(x, options = NULL)
odin_build(x, options = NULL)
x |
An odin ir (json) object or output from odin_validate. |
options |
Options to pass to the build stage (see odin_options |
In applications that want to inspect the intermediate
representation rather before compiling, rather than directly using
odin, use either odin_parse or
odin_validate and then pass the result to
odin::odin_build
.
The return value of this function includes information about how long the compilation took, if it was successful, etc, in the same style as odin_validate:
Logical, indicating if compilation was successful
Time taken to compile the model, as a
proc_time
object, as returned by proc.time.
Any output produced when compiling the model (only present if compiling to C, and if the cache was not hit.
The model itself, as an odin_generator
object,
as returned by odin.
The intermediate representation.
Any error thrown during compilation
odin_parse, which creates intermediate representations used by this function.
# Parse a model of exponential decay ir <- odin::odin_parse({ deriv(y) <- -0.5 * y initial(y) <- 1 }) # Compile the model: options <- odin::odin_options(target = "r") res <- odin::odin_build(ir, options) # All results: res # The model: mod <- res$model$new() mod$run(0:10)
# Parse a model of exponential decay ir <- odin::odin_parse({ deriv(y) <- -0.5 * y initial(y) <- 1 }) # Compile the model: options <- odin::odin_options(target = "r") res <- odin::odin_build(ir, options) # All results: res # The model: mod <- res$model$new() mod$run(0:10)
Return detailed information about an odin model. This is the mechanism through which coef works with odin.
odin_ir(x, parsed = FALSE)
odin_ir(x, parsed = FALSE)
x |
An |
parsed |
Logical, indicating if the representation should be
parsed and converted into an R object. If |
The returned data is subject to change for a few versions while I work out how we'll use it.
exp_decay <- odin::odin({ deriv(y) <- -0.5 * y initial(y) <- 1 }, target = "r") odin::odin_ir(exp_decay) coef(exp_decay)
exp_decay <- odin::odin({ deriv(y) <- -0.5 * y initial(y) <- 1 }, target = "r") odin::odin_ir(exp_decay) coef(exp_decay)
Deserialise odin's intermediate model representation from a json string into an R object. Unlike the json, there is no schema for this representation. This function provides access to the same deserialisation that odin uses internally so may be useful in applications.
odin_ir_deserialise(x)
odin_ir_deserialise(x)
x |
An intermediate representation as a json string |
A named list
# Parse a model of exponential decay ir <- odin::odin_parse({ deriv(y) <- -0.5 * y initial(y) <- 1 }) # Convert the representation to an R object odin::odin_ir_deserialise(ir)
# Parse a model of exponential decay ir <- odin::odin_parse({ deriv(y) <- -0.5 * y initial(y) <- 1 }) # Convert the representation to an R object odin::odin_ir_deserialise(ir)
Create a JavaScript bundle of an odin model
odin_js_bundle(code, include_support = TRUE)
odin_js_bundle(code, include_support = TRUE)
code |
An expression, string or path to a file containing
odin code (as for odin_parse_). If |
include_support |
Logical, indicating if the support code should be included. Without this you need to manually copy over odin.js or dust.js depending on what model type you have. |
A list, with contents subject to change.
The interface and generated code here are subject to change. As it stands, it does what is needed for our work in odin.api and does not actually produce a useful bundle!
js <- odin::odin_js_bundle(quote({ deriv(x) <- 1 initial(x) <- 1 }), include_support = FALSE) head(js$model$code, 20)
js <- odin::odin_js_bundle(quote({ deriv(x) <- 1 initial(x) <- 1 }), include_support = FALSE) head(js$model$code, 20)
Report versions of JavaScript packages used to run odin models.
odin_js_versions()
odin_js_versions()
A named list of package_version versions, for odinjs
and other components used in the JavaScript support.
odin::odin_js_versions()
odin::odin_js_versions()
For lower-level odin functions odin_parse, odin_validate we only accept a list of options rather than individually named options.
odin_options(verbose = NULL, target = NULL, workdir = NULL, validate = NULL, pretty = NULL, skip_cache = NULL, compiler_warnings = NULL, no_check_unused_equations = NULL, rewrite_dims = NULL, rewrite_constants = NULL, debug_enable = NULL, substitutions = NULL, options = NULL)
odin_options(verbose = NULL, target = NULL, workdir = NULL, validate = NULL, pretty = NULL, skip_cache = NULL, compiler_warnings = NULL, no_check_unused_equations = NULL, rewrite_dims = NULL, rewrite_constants = NULL, debug_enable = NULL, substitutions = NULL, options = NULL)
verbose |
Logical scalar indicating if the compilation should
be verbose. Defaults to the value of the option
|
target |
Compilation target. Options are "c", "r" or "js",
defaulting to the option |
workdir |
Directory to use for any generated files. This is
only relevant for the "c" target. Defaults to the value of the
option |
validate |
Validate the model's intermediate representation
against the included schema. Normally this is not needed and is
intended primarily for development use. Defaults to the value
of the option |
pretty |
Pretty-print the model's intermediate
representation. Normally this is not needed and is intended
primarily for development use. Defaults to the value of the
option |
skip_cache |
Skip odin's cache. This might be useful if the
model appears not to compile when you would expect it to.
Hopefully this will not be needed often. Defaults to the option
|
compiler_warnings |
Previously this attempted detection of compiler warnings (with some degree of success), but is currently ignored. This may become supported again in a future version depending on underlying support in pkgbuild. |
no_check_unused_equations |
If |
rewrite_dims |
Logical, indicating if odin should try and
rewrite your model dimensions (if using arrays). If |
rewrite_constants |
Logical, indicating if odin should try
and rewrite all constant scalars. This is a superset of
|
debug_enable |
Enable debugging commands in generated code
(currently |
substitutions |
Optionally, a list of values to substitute
into model specification as constants, even though they are
declared as |
options |
Named list of options. If provided, then all other options are ignored. |
A list of parameters, of class odin_options
odin_options()
odin_options()
Create an odin model within an existing package.
odin_package(path_package)
odin_package(path_package)
path_package |
Path to the package root (the directory that
contains |
I am resisting the urge to actually create the package here.
There are better options than I can come up with; for example
devtools::create
, pkgkitten::kitten
, mason::mason
, or
creating DESCRIPTION
files using desc
. What is required here
is that your package:
Lists odin
in Imports:
Includes useDynLib(<your package name>)
in
NAMESPACE
(possibly via a roxygen comment @useDynLib <your package name>
To avoid a NOTE in R CMD check
, import something from
odin
in your namespace (e.g., importFrom("odin", "odin")
s
or roxygen @importFrom(odin, odin)
Point this function at the package root (the directory containing
DESCRIPTION
and it will write out files src/odin.c
and odin.R
. These files will be overwritten without
warning by running this again.
path <- tempfile() dir.create(path) src <- system.file("examples/package", package = "odin", mustWork = TRUE) file.copy(src, path, recursive = TRUE) pkg <- file.path(path, "package") # The package is minimal: dir(pkg) # But contains odin files in inst/odin dir(file.path(pkg, "inst/odin")) # Compile the odin code in the package odin::odin_package(pkg) # Which creates the rest of the package structure dir(pkg) dir(file.path(pkg, "R")) dir(file.path(pkg, "src"))
path <- tempfile() dir.create(path) src <- system.file("examples/package", package = "odin", mustWork = TRUE) file.copy(src, path, recursive = TRUE) pkg <- file.path(path, "package") # The package is minimal: dir(pkg) # But contains odin files in inst/odin dir(file.path(pkg, "inst/odin")) # Compile the odin code in the package odin::odin_package(pkg) # Which creates the rest of the package structure dir(pkg) dir(file.path(pkg, "R")) dir(file.path(pkg, "src"))
Parse an odin model, returning an intermediate representation.
The odin_parse_
version is a "standard evaluation" escape
hatch.
odin_parse(x, type = NULL, options = NULL) odin_parse_(x, options = NULL, type = NULL)
odin_parse(x, type = NULL, options = NULL) odin_parse_(x, options = NULL, type = NULL)
x |
An expression, character vector or filename with the odin code |
type |
An optional string indicating the the type of input -
must be one of |
options |
odin options; see odin_options. The
primary options that affect the parse stage are |
A schema for the intermediate representation is available in the
package as schema.json
. It is subject to change at this
point.
odin_validate, which wraps this function where parsing might fail, and odin_build for building odin models from an intermediate representation.
# Parse a model of exponential decay ir <- odin::odin_parse({ deriv(y) <- -0.5 * y initial(y) <- 1 }) # This is odin's intermediate representation of the model ir # If parsing odin models programmatically, it is better to use # odin_parse_; construct the model as a string, from a file, or as a # quoted expression: code <- quote({ deriv(y) <- -0.5 * y initial(y) <- 1 }) odin::odin_parse_(code)
# Parse a model of exponential decay ir <- odin::odin_parse({ deriv(y) <- -0.5 * y initial(y) <- 1 }) # This is odin's intermediate representation of the model ir # If parsing odin models programmatically, it is better to use # odin_parse_; construct the model as a string, from a file, or as a # quoted expression: code <- quote({ deriv(y) <- -0.5 * y initial(y) <- 1 }) odin::odin_parse_(code)
Validate an odin model. This function is closer to odin_parse_ than odin_parse because it does not do any quoting of the code. It is primarily intended for use within other applications.
odin_validate(x, type = NULL, options = NULL)
odin_validate(x, type = NULL, options = NULL)
x |
An expression, character vector or filename with the odin code |
type |
An optional string indicating the the type of input -
must be one of |
options |
odin options; see odin_options. The
primary options that affect the parse stage are |
odin_validate
will always return a list with the same
elements:
A boolean, TRUE
if validation was successful
The intermediate representation, as returned by
odin_parse_, if the validation was successful,
otherwise NULL
An error object if the validation was unsuccessful,
otherwise NULL
. This may be a classed odin error, in which
case it will contain source location information - see the
examples for details.
A list of messages, if the validation returned any. At present this is only non-fatal information about unused variables.
Rich FitzJohn
# A successful validation: odin::odin_validate(c("deriv(x) <- 1", "initial(x) <- 1")) # A complete failure: odin::odin_validate("") # A more interesting failure code <- c("deriv(x) <- a", "initial(x) <- 1") res <- odin::odin_validate(code) res # The object 'res$error' is an 'odin_error' object: res$error # It contains information that might be used to display to a # user information about the error: unclass(res$error) # Notes are raised in a similar way: code <- c("deriv(x) <- 1", "initial(x) <- 1", "a <- 1") res <- odin::odin_validate(code) res$messages[[1]]
# A successful validation: odin::odin_validate(c("deriv(x) <- 1", "initial(x) <- 1")) # A complete failure: odin::odin_validate("") # A more interesting failure code <- c("deriv(x) <- a", "initial(x) <- 1") res <- odin::odin_validate(code) res # The object 'res$error' is an 'odin_error' object: res$error # It contains information that might be used to display to a # user information about the error: unclass(res$error) # Notes are raised in a similar way: code <- c("deriv(x) <- 1", "initial(x) <- 1", "a <- 1") res <- odin::odin_validate(code) res$messages[[1]]