model_macro_builder | R Documentation |
A model macro expands one line of code in a nimbleModel into one or
more new lines. This supports compact programming by defining
re-usable modules. model_macro_builder
takes as input a
function that constructs new lines of model code from the original
line of code. It returns a function suitable for internal use by
nimbleModel
that arranges arguments for input function. Macros
are an experimental feature and are available only after setting
nimbleOptions(enableModelMacros = TRUE)
.
model_macro_builder(fun, use3pieces = TRUE, unpackArgs = TRUE)
fun |
A function written to construct new lines of model code (see below). |
use3pieces |
logical indicating whether the arguments from the input
line be split into pieces for the LHS (left-hand side), RHS
(right-hand side, possibly further split depending on
|
unpackArgs |
logical indicating whether arguments be passed as a list
( |
The arguments use3pieces
and unpackArgs
indicate how fun
expects to have arguments arranged from an
input line of code (processed by nimbleModel
).
Consider the defaults use3pieces = TRUE
and unpackArgs =
TRUE
, for a macro called macro1
. In this case, the line of
model code x ~ macro1(arg1 = z[1:10], arg2 = "hello")
will be
passed to fun
as fun(stoch = TRUE, LHS = x, arg1 =
z[1:10], arg2 = "hello")
.
If use3pieces = TRUE
but unpackArgs = FALSE
, then the
RHS will be passed as is, without unpacking its arguments into
separate arguments to fun
. In this case, x ~ macro1(arg1
= z[1:10], arg2 = "hello")
will be passed to fun
as
fun(stoch = TRUE, LHS = x, RHS = macro1(arg1 = z[1:10], arg2 =
"hello"))
.
If use3pieces = FALSE
and unpackArgs = FALSE
, the entire
line of code is passed as a single object. In this case, x ~
macro1(arg1 = z[1:10], arg2 = "hello")
will be passed to fun
as fun(x ~ macro1(arg1 = z[1:10], arg2 = "hello"))
. It is also
possible in this case to pass a macro without using a ~
or
<-
. For example, the line macro1(arg1 = z[1:10], arg2 =
"hello")
will be passed to fun
as fun(macro1(arg1 =
z[1:10], arg2 = "hello"))
.
If use3pieces = FALSE
and unpackArgs = TRUE
, it
won't make sense to anticipate a declaration using ~
or <-
.
Instead, arguments from an arbitrary call will be passed as separate arguments.
For example, the line macro1(arg1 = z[1:10], arg2 = "hello")
will be
passed to fun
as fun(arg1 = z[1:10], arg2 = "hello")
.
In addition, the final two arguments of fun
must be called modelInfo
and .env
respectively.
During macro processing, nimbleModel
passes a named list to the modelInfo
argument of fun
containing, among other things, elements called
constants
and dimensions
. Macro developers can modify these
two elements (for example, to add a new constant needed for a macro) and
these changes will be reflected in the final model object. Note that currently
it is not possible for a macro to modify the data. Furthermore, if your macro add a new element to the
constants that nimbleModel
then moves to the data, this new data will not be retained
in the final model object and thus will not be usable.
nimbleModel
passes the R environment from which nimbleModel
was
called to the .env
argument.
The fun
function must return a named list with two elements:
code
, the replacement code, and modelInfo
, the modelInfo
list described above. modelInfo
must be in the output even if the macro
does not modify it.
It is extremely useful to be familiar with processing R code as an
object to write fun
correctly. Functions such as
substitute
and as.name
(e.g. as.name('~')
), quote
, parse
and deparse
are particularly handy.
Multiple lines of new code should be contained in {}
. Extra
curly braces are not a problem. See example 2.
Macro expansion is done recursively: One macro can return code that invokes another macro.
A list of class model_macro
with one element called process
,
which contains the macro function suitable for use by nimbleModel
.
nimbleOptions(enableModelMacros = TRUE)
nimbleOptions(enableMacroComments = FALSE)
nimbleOptions(verbose = FALSE)
## Example 1: Say one is tired of writing "for" loops.
## This macro will generate a "for" loop with dnorm declarations
all_dnorm <- model_macro_builder(
function(stoch, LHS, RHSvar, start, end, sd = 1, modelInfo, .env) {
newCode <- substitute(
for(i in START:END) {
LHS[i] ~ dnorm(RHSvar[i], SD)
},
list(START = start,
END = end,
LHS = LHS,
RHSvar = RHSvar,
SD = sd))
list(code = newCode)
},
use3pieces = TRUE,
unpackArgs = TRUE
)
model1 <- nimbleModel(
nimbleCode(
{
## Create a "for" loop of dnorm declarations by invoking the macro
x ~ all_dnorm(mu, start = 1, end = 10)
}
))
## show code from expansion of macro
model1$getCode()
## The result should be:
## {
## for (i in 1:10) {
## x[i] ~ dnorm(mu[i], 1)
## }
## }
## Example 2: Say one is tired of writing priors.
## This macro will generate a set of priors in one statement
flat_normal_priors <- model_macro_builder(
function(..., modelInfo, .env) {
allVars <- list(...)
priorDeclarations <- lapply(allVars,
function(x)
substitute(VAR ~ dnorm(0, sd = 1000),
list(VAR = x)))
newCode <- quote({})
newCode[2:(length(allVars)+1)] <- priorDeclarations
list(code = newCode)
},
use3pieces = FALSE,
unpackArgs = TRUE
)
model2 <- nimbleModel(
nimbleCode(
{
flat_normal_priors(mu, beta, gamma)
}
))
## show code from expansion of macro
model2$getCode()
## The result should be:
## {
## mu ~ dnorm(0, sd = 1000)
## beta ~ dnorm(0, sd = 1000)
## gamma ~ dnorm(0, sd = 1000)
## }
## Example 3: Macro that modifies constants
new_constant <- model_macro_builder(
function(stoch, LHS, RHS, modelInfo, .env) {
# number of elements
n <- as.numeric(length(modelInfo$constants[[deparse(LHS)]]))
code <- substitute({
for (i in 1:N){
L[i] ~ dnorm(mu[i], 1)
}
}, list(L = LHS, N = n))
# Add a new constant mu
modelInfo$constants$mu <- rnorm(n, 0, 1)
list(code = code, modelInfo = modelInfo)
},
use3pieces = TRUE,
unpackArgs = TRUE
)
const <- list(y = rnorm(10))
code <- nimbleCode({
y ~ new_constant()
})
mod <- nimbleModel(code = code, constants=const)
mod$getCode()
mod$getConstants() # new constant is here
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.