``` {r include = FALSE} path <- system.file("functions.json", package = "odin", mustWork = TRUE) functions <- jsonlite::fromJSON(path, FALSE) vcapply <- odin:::vcapply
render <- function(x) {
render1 <- function(el) {
if (!is.null(el$description)) {
ret <- sprintf("%s: %s", el$title, el$description)
} else {
ret <- sprintf("%s", el$title)
}
if (!is.null(el$examples)) {
examples <- vcapply(el$examples, function(ex)
sprintf("%s
→ %s
", ex[[1]], ex[[2]]))
ret <- sprintf("%s (e.g., %s)", ret, paste(examples, collapse = "; "))
}
ret
}
value <- vapply(x, render1, "", USE.NAMES = FALSE)
paste(sprintf("* %s
-- %s\n", names(x), value), collapse = "\n")
}
`odin` supports many functions that you'd expect to see for constructing differential equation models; primarily mathematical functions available through R's "Rmath" library. These include all mathematical operations, and many more obscure mathematical functions. Special support is provided for working with arrays. A further set of functions is available for working discrete time stochastic models. ## Basic operators ``` {r echo = FALSE, results = "asis"} writeLines(render(functions$basic))
Because general programming is not supported in odin
and because
every line must contain an assignment, instead of writing
if (mycondition) { a <- true_value } else { a <- false_value }
instead write
a <- if (mycondition) true_value else false_value
(this works in normal R too!)
There are a group of functions for interacting with arrays
``` {r echo = FALSE, results = "asis"} writeLines(render(functions$arrays))
When working with arrays, use generally implies a "for loop" in the generated C code. For example, in the example in [the main package vignette](odin.html) the derivatives are computed as ```r deriv(y[]) <- r[i] * y[i] * (1 - sum(ay[i, ]))
The indexes on the right hand side can be one of i
, j
, k
, l
i5
, i6
, i7
or i8
corresponding to the index on the left hand
side being iterated over (odin
supports arrays up to 8 dimensions).
The left-hand-side here contains no explicit entry (y[]
) which is
equivalent to y[1:length(y)]
, which expands (approximately) to the
"for loop"
for (i in 1:length(y)) { deriv(y[i]) <- r[i] * y[i] * (1 - sum(ay[i, ])) }
(except slightly different, and in C).
Similarly, the expression
ay[, ] <- a[i, j] * y[j]
involves loops over two dimensions (ay[, ]
becomes ay[1:dim(ay,
1), 1:dim(ay, 2)]
and so the loop becomes
for (i in 1:dim(ay, 1)) { for (j in 1:dim(ay, 2)) { ay[i, j] <- a[i, j] * y[j] } }
Due to constraints with using C, few things can be used as an index; in particular the following will not work:
idx <- if (t > 5) 2 else 1 x <- vec[idx]
(or where idx
is some general odin variable as the result of a different assignment). You must use as.integer
to cast this to integer immediately before indexing:
idx <- if (t > 5) 2 else 1 x <- vec[as.integer(idx)]
This will truncate the value (same behaviour as truncate
) so be warned if passing in things that may be approximately integer - you may want to use as.integer(round(x))
in that case.
The interpolation functions are described in more detail in the main package vignette
A number of logical-returning operators exist, primarily to support
the if
statement; all the usual comparison operators exist
(though not vectorised |
or &
).
``` {r echo = FALSE, results = "asis"} writeLines(render(functions$operators))
Be wary of strict equality with `==` or `!=` as numbers may be floating point numbers, which have some surprising properties for the uninitiated, for example ``` {r } sqrt(3)^2 == 3
``` {r echo = FALSE, results = "asis"} writeLines(render(functions$maths))
The exact for `%%` and `%/%` for floating point numbers and signed numbers are complicated - please see `?Arithmetic`. The rules for operators in `odin` are exactly those in R as the same underlying functions are used. Similarly, for the differences between `round`, `floor`, `ceiling` and `truncate`, see the help page `?round`. Note that R's behaviour for rounding away from 0.5 is exactly followed and that this slightly changed behaviour at version 4.0.0 All the usual trig functions are also available: ``` {r echo = FALSE, results = "asis"} writeLines(render(functions$trig))
For discrete time stochastic models, all of R's normal stochastic distribution functions are available:
``` {r echo = FALSE, results = "asis"} writeLines(render(functions$stochastic))
With random number functions we can write: ```r x <- runif(10, 20)
which will generate a random number from the uniform distribution. If you write:
x[] <- runif(10, 20)
then each element of x
will be filled with a different random number drawn from this distribution (which is generally what you want). Random numbers are considered to be time varying which means they will automatically generate each time step, so if you write
x <- rnorm(0, 10) update(y[]) <- y[i] + x
then at each time step, each element of y
will be updated by the same random number from a normal distribution with a mean of zero and a standard deviation of 10 - the number will change each time step but be the same for each element of y
in the example above.
In addition, two functions that are vector returning and require some care to use:
``` {r echo = FALSE, results = "asis"} writeLines(render(functions$inplace))
Both these functions require a vector input (of probabilities for `rmultinom` and of counts for `rmhyper`) and return a vector the same length. So the expression ```r y[] <- rmultinom(10, p)
will produce a vector y
of samples from the multinomial distribution with parameters size = 10
(so after wards sum(y)
is 10) and probabilities p
. It is very important that y
and p
have the same size.
At the moment it is not possible to use expressions like
y[1, ] <- rmultinom(10, p[i, ])
but this is planned for implementation in the future. A full example of using rmultinom
is given in the discrete models vignette.
Any scripts or data that you put into this service are public.
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.