`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.

`+`

–**Plus**: Both infix (`a + b`

) and prefix (`+a`

) versions supported (e.g.,`1 + 2`

→`3`

)`-`

–**Minus**: Both infix (`a - b`

) and prefix (`-a`

) versions supported (e.g.,`10 - 1`

→`9`

)`*`

–**Multiply**: Multiply two numbers together (e.g.,`2 * 6`

→`12`

)`/`

–**Divide**: Divide two numbers (e.g.,`12 / 6`

→`2`

)`^`

–**Power**: Raise the first number to the power of the second. Either number may be a floating point number (e.g.,`2.3 ^ 1.2`

→`2.716898`

)`(`

–**Parenthesis**: Group expressions together (e.g.,`(1 + 5) * 2`

→`12`

)`if`

–**Conditional**: Inline conditional statement. This takes a form slightly different to typically seen in R with the result of the statement directly assigned (e.g.,`if (9 > 10) 1 else 2`

→`2`

)

Because general programming is not supported in `odin`

and because every line must contain an assignment, instead of writing

instead write

(this works in normal R too!)

There are a group of functions for interacting with arrays

`sum`

–**Sum**: Compute sum over an array, or over some dimension of an array`length`

–**Length**: Total length of an array`dim`

–**Length of one of an array’s dimensions**: If an array`x`

has 10 rows and 20 columns, then`dim(x, 1)`

is`10`

and`dim(x, 2)`

is`20`

. Note that this differs from`dim`

in R and`dim(x, i)`

is closer to`dim(x)[[i]]`

`[`

–**Subset an array**: See below`interpolate`

–**Interpolate an array over time**: See below

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 the derivatives are computed as

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”

(except slightly different, and in C).

Similarly, the expression

involves loops over two dimensions (`ay[, ]`

becomes `ay[1:dim(ay, 1), 1:dim(ay, 2)]`

and so the loop becomes

Due to constraints with using C, few things can be used as an index; in particular the following will not work:

(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:

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 `&`

).

`>`

–**Greater than**(e.g.,`1 > 2`

→`FALSE`

)`<`

–**Less than**(e.g.,`1 < 2`

→`TRUE`

)`>=`

–**Greater than or equal to**(e.g.,`1 >= 2`

→`FALSE`

)`<=`

–**Less than or equal to**(e.g.,`1 <= 2`

→`TRUE`

)`==`

–**Is exactly equal to**(e.g.,`1 == 1`

→`TRUE`

)`!=`

–**Is not exactly equal to**(e.g.,`1 != 2`

→`TRUE`

)`&&`

–**Boolean AND**(e.g.,`(1 == 1) && (2 > 1)`

→`TRUE`

)`||`

–**Boolean OR**(e.g.,`(1 == 1) && (2 > 1)`

→`TRUE`

)

Be wary of strict equality with `==`

or `!=`

as numbers may be floating point numbers, which have some surprising properties for the uninitiated, for example

`## [1] FALSE`

`%%`

–**Modulo**: Finds the remainder after division of one number by another (e.g.,`123 %% 100`

→`23`

)`%/%`

–**Integer divide**: Different to floating point division, effectively the full number of times one number divides into another (e.g.,`20 %/% 7`

→`2`

)`abs`

–**Absolute value**(e.g.,`abs(-1)`

→`1`

)`sign`

–**Sign function**: Returns the sign of its argument as either -1, 0 or 1, which may be useful for multiplying by another argument (e.g.,`sign(-100)`

→`-1`

)`round`

–**Round a number**(e.g.,`round(1.23)`

→`1`

;`round(1.23, 1)`

→`1.2`

)`floor`

–**Floor of a number**: Largest integer not greater than the provided number (e.g.,`floor(6.5)`

→`6`

)`ceiling`

–**Ceiling of a number**: Smallest integer not less than the provided number (e.g.,`ceiling(6.5)`

→`7`

)`trunc`

–**Truncate a number**: Round a number towards zero`max`

–**Maximum**: Returns maximum of all arguments given (e.g.,`max(2, 6, 1)`

→`6`

)`min`

–**Minimum**(e.g.,`min(2, 6, 1)`

→`1`

)`exp`

–**Exponential function**(e.g.,`exp(1)`

→`2.718282`

)`expm1`

–**Computes exp(x) - 1 accurately for small |x|**(e.g.,`exp(1)`

→`1.718282`

)`log`

–**Logarithmic function**(e.g.,`log(1)`

→`0`

)`log2`

–**Logarithmic function in base 2**(e.g.,`log2(1024)`

→`10`

)`log10`

–**Logarithmic function in base 10**(e.g.,`log10(1000)`

→`3`

)`log1p`

–**Computes log(x + 1) accurately for small |x|**(e.g.,`log1p(1)`

→`0.6931472`

)`sqrt`

–**Square root function**(e.g.,`sqrt(4)`

→`2`

)`beta`

–**Beta function**(e.g.,`beta(3, 5)`

→`0.00952381`

)`lbeta`

–**Log beta function**(e.g.,`lbeta(3, 5)`

→`-4.65396`

)`choose`

–**Binomial coefficients**(e.g.,`choose(60, 3)`

→`34220`

)`lchoose`

–**Log binomial coefficients**(e.g.,`choose(60, 3)`

→`10.44057`

)`gamma`

–**Gamma function**(e.g.,`gamma(10)`

→`362880`

)`lgamma`

–**Log gamma function**(e.g.,`lgamma(10)`

→`12.80183`

)

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:

`cos`

–**Cosine function**`sin`

–**Sine function**`tan`

–**Tangent function**`acos`

–**Arc-cosine function**`asin`

–**Arc-sin function**`atan`

–**Arc-tangent function**`atan2`

–**Two-arg arc-tangent function**`cosh`

–**Hyperbolic cosine function**`sinh`

–**Hyperbolic sine function**`tanh`

–**Hyperbolic tangent function**`acosh`

–**Hyperbolic arc-cosine function**`asinh`

–**Hyperbolic arc-sine function**`atanh`

–**Hyperbolic arc-tangent function**

For discrete time stochastic models, all of R’s normal stochastic distribution functions are available:

`unif_rand`

–**Standard uniform distribution**: Sample from the uniform distribution on [0, 1] - more efficient than but equivalent to runif(0, 1)`norm_rand`

–**Standard normal distribution**: Sample from the standard normal distribution - more efficient than but equivalent to rnorm(0, 1)`exp_rand`

–**Standard exponential distribution**: Sample from the exponential distribution with rate 1 - more efficient than but equivalent to rexp(1)`rbeta`

–**Beta distribution**: With parameters shape1 and shape2 (see`?rbeta`

for details)`rbinom`

–**Binomial distribution**: With parameters`size`

(number of trials) and`prob`

(probability of success)`rcauchy`

–**Cauchy distribution**: With parameters`location`

and`scale`

`rchisq`

–**Chi-Squared distribution**: With parameter`df`

`rexp`

–**Exponential distribution**: With parameter`rate`

`rf`

–**F-distribution**: With parameter`df1`

and `df2`rgamma`

–**Gamma distribution**: With parameters`shape`

and`rate`

`rgeom`

–**Geometric distribution**: Distribution with parameters`prob`

`rhyper`

–**Hypergeometric distribution**: With parameters`m`

(the number of white balls in the urn),`n`

(the number of black balls in the urn) and`k`

(the number of balls drawn from the urn)`rlogis`

–**Logistic distribution**: With parameters`location`

and`scale`

`rlnorm`

–**Log-normal distribution**: With parameters`meanlog`

and`sdlog`

`rnbinom`

–**Negative binomial distribution**: With parameters`size`

,`prob`

and`mu`

`rnorm`

–**Normal distribution**: With parameters`mean`

and`sd`

`rpois`

–**Poisson distribution**: With parameter`lambda`

`rt`

–**Student’s t distribution**: With parameter`df`

`runif`

–**uniform distribution**: With parameters`min`

and`max`

`rweibull`

–**Weibull distribution**: With parameters`shape`

and`scale`

`rwilcox`

–**Wilcoxon rank sum statistic distribution**: With parameters`n`

and`m`

`rsignrank`

–**Wilcoxon signed rank statistic distribution**: With parameter`n`

With random number functions we can write:

which will generate a random number from the uniform distribution. If you write:

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

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:

`rmultinom`

–**multinomial distribution**: The first parameter is the number of samples and the second is the per-class probability and must be a vector`rmhyper`

–**Multivariate hypergeometric distribution**: The first parameter is the number of samples and the second is the per-class count and must be a vector

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

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

but this is planned for implementation in the future. A full example of using `rmultinom`

is given in the discrete models vignette.