Nothing
#' User-defined variables passed to Stan
#'
#' Prepare user-defined variables to be passed to one of Stan's
#' program blocks. This is primarily useful for defining more complex
#' priors, for refitting models without recompilation despite
#' changing priors, or for defining custom Stan functions.
#'
#' @aliases stanvars
#'
#' @param x An \R object containing data to be passed to Stan.
#' Only required if \code{block = 'data'} and ignored otherwise.
#' @param name Optional character string providing the desired variable
#' name of the object in \code{x}. If \code{NULL} (the default)
#' the variable name is directly inferred from \code{x}.
#' @param scode Line of Stan code to define the variable
#' in Stan language. If \code{block = 'data'}, the
#' Stan code is inferred based on the class of \code{x} by default.
#' @param block Name of one of Stan's program blocks in
#' which the variable should be defined. Can be \code{'data'},
#' \code{'tdata'} (transformed data), \code{'parameters'},
#' \code{'tparameters'} (transformed parameters), \code{'model'},
#' \code{'likelihood'} (part of the model block where the likelihood is given),
#' \code{'genquant'} (generated quantities) or \code{'functions'}.
#' @param position Name of the position within the block where the
#' Stan code should be placed. Currently allowed are \code{'start'}
#' (the default) and \code{'end'} of the block.
#' @param pll_args Optional Stan code to be put into the header
#' of \code{partial_log_lik} functions. This ensures that the variables
#' specified in \code{scode} can be used in the likelihood even when
#' within-chain parallelization is activated via \code{\link{threading}}.
#'
#' @return An object of class \code{stanvars}.
#'
#' @details
#' The \code{stanvar} function is not vectorized. Instead, multiple
#' \code{stanvars} objects can be added together via \code{+} (see Examples).
#'
#' Special attention is necessary when using \code{stanvars} to inject
#' code into the \code{'likelihood'} block while having \code{\link{threading}}
#' activated. In this case, your custom Stan code may need adjustments to ensure
#' correct observation indexing. Please investigate the generated Stan code via
#' \code{\link[brms:stancode.default]{stancode}} to see which adjustments are necessary in your case.
#'
#' @examples
#' bprior <- prior(normal(mean_intercept, 10), class = "Intercept")
#' stanvars <- stanvar(5, name = "mean_intercept")
#' stancode(count ~ Trt, epilepsy, prior = bprior,
#' stanvars = stanvars)
#'
#' # define a multi-normal prior with known covariance matrix
#' bprior <- prior(multi_normal(M, V), class = "b")
#' stanvars <- stanvar(rep(0, 2), "M", scode = " vector[K] M;") +
#' stanvar(diag(2), "V", scode = " matrix[K, K] V;")
#' stancode(count ~ Trt + zBase, epilepsy,
#' prior = bprior, stanvars = stanvars)
#'
#' # define a hierachical prior on the regression coefficients
#' bprior <- set_prior("normal(0, tau)", class = "b") +
#' set_prior("target += normal_lpdf(tau | 0, 10)", check = FALSE)
#' stanvars <- stanvar(scode = "real<lower=0> tau;",
#' block = "parameters")
#' stancode(count ~ Trt + zBase, epilepsy,
#' prior = bprior, stanvars = stanvars)
#'
#' # ensure that 'tau' is passed to the likelihood of a threaded model
#' # not necessary for this example but may be necessary in other cases
#' stanvars <- stanvar(scode = "real<lower=0> tau;",
#' block = "parameters", pll_args = "real tau")
#' stancode(count ~ Trt + zBase, epilepsy,
#' stanvars = stanvars, threads = threading(2))
#'
#' @export
stanvar <- function(x = NULL, name = NULL, scode = NULL,
block = "data", position = "start",
pll_args = NULL) {
vblocks <- c(
"data", "tdata", "parameters", "tparameters",
"model", "genquant", "functions", "likelihood"
)
block <- match.arg(block, vblocks)
vpositions <- c("start", "end")
position <- match.arg(position, vpositions)
if (block == "data") {
if (is.null(x)) {
stop2("Argument 'x' is required if block = 'data'.")
}
if (is.null(name)) {
name <- deparse0(substitute(x))
}
name <- as_one_character(name)
if (!is_equal(name, make.names(name)) || grepl("\\.", name)) {
stop2("'", limit_chars(name, 30), "' is not ",
"a valid variable name in Stan.")
}
if (is.null(scode)) {
# infer scode from x
if (is.integer(x)) {
if (length(x) == 1L) {
scode <- paste0("int ", name)
} else {
scode <- paste0("int ", name, "[", length(x), "]")
}
} else if (is.vector(x)) {
if (length(x) == 1L) {
scode <- paste0("real ", name)
} else {
scode <- paste0("vector[", length(x), "] ", name)
}
} else if (is.array(x)) {
if (length(dim(x)) == 1L) {
scode <- paste0("vector[", length(x), "] ", name)
} else if (is.matrix(x)) {
scode <- paste0("matrix[", nrow(x), ", ", ncol(x), "] ", name)
}
}
if (is.null(scode)) {
stop2(
"'stanvar' could not infer the Stan code for an object ",
"of class '", class(x), "'. Please specify the Stan code ",
"manually via argument 'scode'."
)
}
scode <- paste0(scode, ";")
}
if (is.null(pll_args)) {
# infer pll_args from x
pll_type <- str_if(block %in% c("data", "tdata"), "data ")
if (is.integer(x)) {
if (length(x) == 1L) {
pll_type <- paste0(pll_type, "int")
} else {
pll_type <- paste0(pll_type, "array[] int")
}
} else if (is.vector(x)) {
if (length(x) == 1L) {
pll_type <- paste0(pll_type, "real")
} else {
pll_type <- paste0(pll_type, "vector")
}
} else if (is.array(x)) {
if (length(dim(x)) == 1L) {
pll_type <- paste0(pll_type, "vector")
} else if (is.matrix(x)) {
pll_type <- paste0(pll_type, "matrix")
}
}
if (!is.null(pll_type)) {
pll_args <- paste0(pll_type, " ", name)
} else {
# don't throw an error because most people will not use threading
pll_args <- character(0)
}
}
} else {
x <- NULL
if (is.null(name)) {
name <- ""
}
name <- as_one_character(name)
if (is.null(scode)) {
stop2("Argument 'scode' is required if block is not 'data'.")
}
scode <- as.character(scode)
pll_args <- as.character(pll_args)
}
if (position == "end" && block %in% c("functions", "data")) {
stop2("Position '", position, "' is not sensible for block '", block, "'.")
}
out <- nlist(name, sdata = x, scode, block, position, pll_args)
structure(setNames(list(out), name), class = "stanvars")
}
# take a subset of a stanvars object
# @param x a stanvars object
# @param ... conditions defining the desired subset
subset_stanvars <- function(x, ...) {
x <- validate_stanvars(x)
structure_not_null(x[find_elements(x, ...)], class = "stanvars")
}
# collapse Stan code provided in a stanvars object
collapse_stanvars <- function(x, block = NULL, position = NULL) {
x <- validate_stanvars(x)
if (!length(x)) {
return(character(0))
}
if (!is.null(block)) {
x <- subset_stanvars(x, block = block)
}
if (!is.null(position)) {
x <- subset_stanvars(x, position = position)
}
if (!length(x)) {
return("")
}
collapse(wsp(nsp = 2), ufrom_list(x, "scode"), "\n")
}
# collapse partial log-lik code provided in a stanvars object
collapse_stanvars_pll_args <- function(x) {
x <- validate_stanvars(x)
if (!length(x)) {
return(character(0))
}
out <- ufrom_list(x, "pll_args")
if (!length(out)) {
return("")
}
collapse(", ", out)
}
# validate 'stanvars' objects
validate_stanvars <- function(x, stan_funs = NULL) {
if (is.null(x)) {
x <- empty_stanvars()
}
if (!is.stanvars(x)) {
stop2("Argument 'stanvars' is invalid. See ?stanvar for help.")
}
if (length(stan_funs) > 0) {
warning2("Argument 'stan_funs' is deprecated. Please use argument ",
"'stanvars' instead. See ?stanvar for more help.")
stan_funs <- as_one_character(stan_funs)
x <- x + stanvar(scode = stan_funs, block = "functions")
}
x
}
# add new data to stanvars
# @param x a 'stanvars' object
# @param newdata2 a list with new 'data2' objects
# @return a 'stanvars' object
add_newdata_stanvars <- function(x, newdata2) {
stopifnot(is.stanvars(x))
stanvars_data <- subset_stanvars(x, block = "data")
for (name in names(stanvars_data)) {
if (name %in% names(newdata2)) {
x[[name]]$sdata <- newdata2[[name]]
}
}
x
}
#' @export
c.stanvars <- function(x, ...) {
dots <- lapply(list(...), validate_stanvars)
class(x) <- "list"
out <- unlist(c(list(x), dots), recursive = FALSE)
svnames <- names(out)[nzchar(names(out))]
if (any(duplicated(svnames))) {
stop2("Duplicated names in 'stanvars' are not allowed.")
}
structure(out, class = "stanvars")
}
#' @export
"+.stanvars" <- function(e1, e2) {
c(e1, e2)
}
is.stanvars <- function(x) {
inherits(x, "stanvars")
}
empty_stanvars <- function() {
structure(list(), class = "stanvars")
}
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.