#' Wrapper to Parse Stan Model Blocks Based on the Family of the Response
#'
#' @param prefix \[`character(1)`]\cr Stan model block name, e.g., "model".
#' @param family \[`dynamitefamily`, `character(1)`]\cr A family object, a
#' supported family name, or `"default"`.
#' @param args Channel specific component of `channel_vars`.
#' @param idt An indenter function.
#' @param backend The Stan backend.
#' (see [create_blocks()])
#' @noRd
lines_wrap <- function(prefix, family, idt, backend, args) {
args$idt <- idt
args$backend <- backend
suffix <- ifelse_(
is.dynamitefamily(family),
family$name,
family
)
do.call(what = paste0(prefix, "_lines_", suffix), args = args)
}
# Block parameters --------------------------------------------------------
#' Parameters for Stan Code Generation
#'
#' Each of the following functions of the form `(stan_block)_lines_(family)`
#' uses a subset from a common set of arguments that are documented here.
#'
#' @param backend \[`character(1)`]\cr `"rstan"` or `"cmdstanr"`.
#' @param y \[`character(1)`]\cr The name of the channel or vector of
#' channel names for multivariate channels.
#' @param t \[`character(1)`]\cr The time index. Either `"t"` if there
#' are observations at every time point, otherwise a channel-specific
#' vector of indices with at least one observation.
#' @param obs \[`character(1)`]\cr A vector of indices indicating non-missing
#' values of the channel.
#' @param t_obs \[`character(1)`]\cr Either `"T"` if the are observations
#' at every time point, otherwise a channel specific integer that represents
#' the number of time points with at least one observation for the channel.
#' @param default \[`character(1)`]\cr Default channel specifications for the
#' block.
#' @param link \[`character(1)`]\cr Link function for the linear predictor
#' @param intercept \[`character(1)`]\cr Model block intercept definitions
#' @param priors \[`character(1)`]\cr Model block prior definitions
#' @param y_cg \[`character(1)`]\cr Name of the channel group for multivariate
#' channels.
#' @param idt \[`function`]\cr An indentation function, see [indenter_()].
#' @param has_missing \[`logical(1)`]\cr Does the channel contain missing
#' observations?
#' @param has_fully_missing \[`logical(1)`]\cr Does the channel have time
#' points with zero observations?
#' @param has_offset \[`logical(1)`]\cr Does the channel contain an offset term
#' @param has_fixed \[`logical(1)`]\cr Does the channel have time-invariant
#' predictors?
#' @param has_varying \[`logical(1)`]\cr Does the channel have time-varying
#' predictors?
#' @param has_random \[`logical(1)`]\cr Does the channel have random
#' (group-specific) effects?
#' @param has_fixed_intercept \[`logical(1)`]\cr Does the channel have a
#' time-invariant intercept?
#' @param has_varying_intercept \[`logical(1)`]\cr Does the channel have a
#' time-varying intercept?
#' @param has_random_intercept \[`logical(1)`]\cr Does the channel have a
#' random intercept?
#' @param noncentered \[`logical(1)`]\cr Should the noncentered parametrization
#' be used for splines?
#' @param shrinkage \[`logical(1)`]\cr Should the common global shrinkage
#' parameter be used?
#' @param lb \[`double(1)`]\cr Lower bound for the `tau` parameter.
#' @param J \[`integer()`]\cr Model matrix column indices of the predictors
#' of the channel
#' @param J_fixed \[`integer()`]\cr Model matrix column indices of the
#' time-invariant predictors of the channel
#' @param J_varying \[`integer()`]\cr Model matrix column indices of the
#' time-varying predictors of the channel
#' @param K \[`integer(1)`]\cr Total number of predictors of the channel
#' @param K_fixed \[`integer(1)`]\cr Number of time-invariant predictors of
#' the channel.
#' @param K_varying \[`integer(1)`]\cr Number of time-varying predictors of
#' the channel.
#' @param K_random \[`integer(1)`]\cr Number of random effect predictors of
#' the channel.
#' @param L_fixed \[`integer(1)`]\cr Indices of the time-invariant predictors
#' of the channel in the joint parameter vector `gamma`.
#' @param L_varying \[`integer(1)`]\cr Indices of the time-varying predictors
#' of the channel in the joint parameter vector `gamma`.
#' @param S \[`integer(1)`]\cr Number of categories for a categorical channel.
#' @param write_alpha \[`logical(1)`]\cr If `TRUE`, use vectorized prior for
#' `alpha` parameters of the model by hard-coding the parameters of the prior
#' distribution to the model code?
#' @param vectorized_beta \[`logical(1)`]\cr If `TRUE`, use a vectorized prior
#' for time-invariant predictors `beta`.
#' @param vectorized_delta \[`logical(1)`]\cr If `TRUE`, use a vectorized prior
#' for time-varying predictors `delta`.
#' @param vectorized_tau \[`logical(1)`]\cr If `TRUE`, use a vectorized prior
#' for `tau` parameters.
#' @param vectorized_sigma_nu \[`logical(1)`]\cr If `TRUE`, use a vectorized
#' prior for `sigma_nu` parameters.
#' @param sigma_prior_distr \[`character(1)`]\cr `sigma` parameter prior
#' specification.
#' @param sigma_nu_prior_distr \[`character(1)`]\cr `sigma_nu` parameter prior
#' specification.
#' @param alpha_prior_distr \[`character(1)`]\cr `alpha` parameter prior
#' specification.
#' @param tau_alpha_prior_distr \[`character(1)`]\cr `tau_alpha` parameter
#' prior specification.
#' @param beta_prior_distr \[`character(1)`]\cr `beta` parameter prior
#' specification.
#' @param beta_prior_npars \[`integer(1)`]\cr Number of parameters for the
#' prior of `beta`.
#' @param delta_prior_distr \[`character(1)`]\cr `delta` parameter prior
#' specification.
#' @param delta_prior_npars \[`integer(1)`]\cr Number of parameters for the
#' prior of `delta`.
#' @param tau_prior_distr \[`character(1)`]\cr `tau` parameter prior
#' specification.
#' @param tau_prior_npars \[`integer(1)`]\cr Number of parameters for the
#' prior of `tau`.
#' @param sigma_nu_prior_distr \[`character(1)`]\cr `sigma_nu` parameter prior
#' specification.
#' @param sigma_nu_prior_npars \[`integer(1)`]\cr Number of parameters for the
#' prior of `tau`.
#' @param phi_prior_distr \[`character(1)`]\cr `phi` parameter prior
#' specification.
#' @noRd
NULL
# log-likelihoods -----------------------------------------------------------
intercept_lines <- function(y, obs, family, has_varying, has_fixed, has_random,
has_fixed_intercept, has_varying_intercept,
has_random_intercept, has_lfactor, has_offset,
backend, ydim = y, ...) {
intercept_alpha <- ifelse_(
is_cumulative(family),
"0",
ifelse_(
has_fixed_intercept,
glue::glue("alpha_{y}"),
ifelse_(
has_varying_intercept,
glue::glue("alpha_{y}[t]"),
"0"
)
)
)
offset <- ifelse_(
has_offset,
glue::glue(" + offset_{y}[{obs}, t]"),
""
)
intercept_nu <- ifelse_(
has_random_intercept,
ifelse(
nzchar(obs),
glue::glue(" + nu_{y}[{obs}, 1]"),
glue::glue(" + nu_{y}[, 1]")
),
""
)
random <- ifelse_(
has_random,
ifelse_(
has_random_intercept,
paste0(
glue::glue(" + rows_dot_product(X[t][{obs}, J_random_{ydim}], "),
glue::glue("nu_{y}[{obs}, 2:K_random_{ydim}])")
),
paste0(
glue::glue(" + rows_dot_product(X[t][{obs}, J_random_{ydim}], "),
glue::glue("nu_{y}[{obs}, ])")
)
),
""
)
lfactor <- ifelse_(
has_lfactor,
ifelse(
nzchar(obs),
glue::glue(" + lambda_{y}[{obs}] * psi_{y}[t]"),
glue::glue(" + lambda_{y} * psi_{y}[t]")
),
""
)
fixed <- ifelse_(
has_fixed,
glue::glue(" + X[t][{obs}, J_fixed_{ydim}] * beta_{y}"),
""
)
varying <- ifelse_(
has_varying,
glue::glue(" + X[t][{obs}, J_varying_{ydim}] * delta_{y}[t]"),
""
)
common_intercept <- !has_random && !has_random_intercept && !has_lfactor
glm <- stan_supports_glm_likelihood(family, backend, common_intercept) &&
(has_fixed || has_varying)
intercept <- ifelse_(
glm,
glue::glue(
"{intercept_alpha}{offset}{intercept_nu}{random}{lfactor}"
),
glue::glue(
"{intercept_alpha}{offset}{intercept_nu}{random}{lfactor}{fixed}{varying}"
)
)
intercept <- sub("^0 \\+", "", intercept)
attr(intercept, "glm") <- glm
intercept
}
loglik_lines_default <- function(y, idt, obs, family, has_missing,
has_fully_missing,
has_varying, has_fixed, has_random,
has_fixed_intercept, has_varying_intercept,
has_random_intercept, has_lfactor,
has_offset, backend, threading, ydim = y,
K, ...) {
y_type <- ifelse_(
family$name %in% c("gaussian", "exponential", "gamma", "beta", "student"),
"matrix",
ifelse_(
stan_supports_array_keyword(backend),
"array[,] int",
"int[,]"
)
)
has_X <- has_fixed || has_varying || has_random
intercept <- intercept_lines(
y, obs, family, has_varying, has_fixed, has_random, has_fixed_intercept,
has_varying_intercept, has_random_intercept, has_lfactor, has_offset,
backend, ydim
)
glm <- attr(intercept, "glm")
scalar_intercept <- !has_offset && !has_random && !has_random_intercept &&
!has_lfactor && (glm || !has_X) && !is_cumulative(family)
n_obs <- ifelse_(
nchar(obs),
paste0("n_obs_", y, "[t]"),
"N"
)
if (is_cumulative(family) && intercept == "0") {
intercept <- glue::glue("rep_vector(0, {n_obs})")
}
intercept_line <- ifelse_(
scalar_intercept,
"real intercept_{y} = {intercept};",
"vector[{n_obs}] intercept_{y} = {intercept};"
)
fun_name <- paste0(family$name, "_loglik_", y, "_lpmf")
LJ <- ifelse_(glm, "L", "J")
fun_args <- onlyif(
threading,
glue_cs(
stan_array_arg(backend, "int", "t_obs_{y}", 0L, TRUE),
c("int start", "int end"),
ifelse_(
has_missing,
c(
stan_array_arg(backend, "int", "obs_{y}", 1L, TRUE),
stan_array_arg(backend, "int", "n_obs_{y}", 0L, TRUE)
),
"data int N"
),
paste("data", y_type, "y_{y}"),
onlyif(
has_fixed_intercept && !is_cumulative(family),
"real alpha_{y}"
),
onlyif(
has_varying_intercept && !is_cumulative(family),
stan_array_arg(backend, "real", "alpha_{y}", 0L)
),
onlyif(
has_random,
c(
stan_array_arg(backend, "int", "J_random_{y}", 0L, TRUE),
"int K_random_{y}"
)
),
onlyif(has_random || has_random_intercept, "matrix nu_{y}"),
onlyif(has_lfactor, c("vector lambda_{y}", "vector psi_{y}")),
onlyif(
has_fixed,
c(
stan_array_arg(backend, "int", "{LJ}_fixed_{y}", 0L, TRUE),
"vector beta_{y}"
)
),
onlyif(
has_varying,
c(
stan_array_arg(backend, "int", "{LJ}_varying_{y}", 0L, TRUE),
stan_array_arg(backend, "vector", "delta_{y}")
)
),
onlyif(
has_fixed || has_varying,
c(
stan_array_arg(backend, "int", "J_{y}", 0L, TRUE),
"data int K_{y}"
)
),
onlyif(has_X, "data array[] matrix X"),
onlyif(has_offset, "data matrix offset_{y}"),
onlyif(
is_binomial(family),
stan_array_arg(backend, "int", "trials_{y}", 1L, TRUE)
)
)
)
fun_call_args <- onlyif(
threading,
glue_cs(
ifelse_(
has_missing,
c("obs_{y}", "n_obs_{y}"),
"N"
),
"y_{y}",
onlyif(has_fixed_intercept || has_varying_intercept, "alpha_{y}"),
onlyif(has_random, c("J_random_{y}", "K_random_{y}")),
onlyif(has_random || has_random_intercept, "nu_{y}"),
onlyif(has_lfactor, c("lambda_{y}", "psi_{y}")),
onlyif(has_fixed, c("{LJ}_fixed_{y}", "beta_{y}")),
onlyif(has_varying, c("{LJ}_varying_{y}", "delta_{y}")),
onlyif(has_fixed || has_varying, c("J_{y}", "K_{y}")),
onlyif(has_X, "X"),
onlyif(has_offset, "offset_{y}"),
onlyif(is_binomial(family), "trials_{y}")
)
)
loop_index <- ifelse_(
threading,
paste0("t_obs_", y),
ifelse_(
has_fully_missing,
paste0("t_obs_", y),
"1:T"
)
)
fun_body <- paste_rows(
"real ll = 0.0;",
onlyif(glm, "vector[K_{y}] gamma__{y};"),
onlyif(glm && has_fixed, "gamma__{y}[L_fixed_{y}] = beta_{y};"),
"for (t in {loop_index}) {{",
intercept_line,
onlyif(glm && has_varying, "gamma__{y}[L_varying_{y}] = delta_{y}[t];"),
"__likelihood__",
"}}",
ifelse_(threading, "return ll;", "target += ll;"),
.indent = idt(c(2, 2, 2, 2, 3, 3, 3, 2, 2))
)
seq1T <- ifelse_(
has_fully_missing,
"t_obs_{y}",
"seq1T"
)
u <- ifelse_(stan_version(backend) >= "2.25", "u", "")
list(
fun_name = fun_name,
fun_args = fun_args,
fun_call_args = fun_call_args,
fun_body = fun_body,
use_glm = glm,
threading = threading,
seq1T = seq1T,
u = u
)
}
loglik_lines_finalize <- function(idt, default, likelihood, ...) {
fun_body <- default$fun_body
fun_body <- gsub("__likelihood__", likelihood, fun_body)
paste_rows(
ifelse_(
default$threading,
"real {default$fun_name}({default$fun_args}) {{",
"{{"
),
"{fun_body}",
"}}",
.indent = idt(c(1, 0, 1))
)
}
loglik_lines_bernoulli <- function(y, obs, idt, default, ...) {
u <- default$u
likelihood <- ifelse_(
default$use_glm,
glue::glue(
"ll += bernoulli_logit_glm_l{u}pmf(",
"y_{y}[t, {obs}] | X[t][{obs}, J_{y}], ",
"intercept_{y}, gamma__{y});"
),
glue::glue(
"ll += bernoulli_logit_l{u}pmf(y_{y}[t, {obs}] | intercept_{y});"
)
)
loglik_lines_finalize(idt, default, likelihood)
}
loglik_lines_beta <- function(y, obs, idt, default, ...) {
u <- default$u
likelihood <- glue::glue(
"ll += beta_proportion_l{u}pdf(y_{y}[{obs}, t] | ",
"inv_logit(intercept_{y}), phi_{y});"
)
if (default$threading) {
default$fun_args <- cs(default$fun_args, glue::glue("real phi_{y}"))
}
loglik_lines_finalize(idt, default, likelihood)
}
loglik_lines_binomial <- function(y, obs, idt, default, ...) {
u <- default$u
likelihood <- glue::glue(
"ll += binomial_logit_l{u}pmf(y_{y}[t, {obs}] | ",
"trials_{y}[t, {obs}], intercept_{y});"
)
loglik_lines_finalize(idt, default, likelihood)
}
loglik_lines_categorical <- function(y, idt, obs, family, has_missing,
has_fully_missing, has_offset,
has_varying, has_fixed, has_random,
has_fixed_intercept,
has_varying_intercept,
has_random_intercept, has_lfactor,
backend, threading,
ydim = y, K, categories,
multinomial = FALSE, ...) {
u <- ifelse_(stan_version(backend) >= "2.25", "u", "")
distr <- ifelse_(multinomial, "multinomial", "categorical")
S <- length(categories)
cats <- categories[seq.int(2L, S)]
fun_args <- character(length(y))
intercept <- vector("list", length(y))
for (i in seq_along(cats)) {
yi <- ifelse_(
multinomial,
cats[i],
paste0(y, "_", cats[i])
)
intercept[[i]] <- intercept_lines(
yi, obs, family, has_varying, has_fixed, has_random, has_fixed_intercept,
has_varying_intercept, has_random_intercept, has_lfactor, has_offset,
backend, ydim = y
)
fun_args[i] <- glue_cs(
onlyif(has_fixed_intercept, "real alpha_{yi}"),
onlyif(
has_varying_intercept,
stan_array_arg(backend, "real", "alpha_{yi}", 0L)
),
onlyif(has_random || has_random_intercept, "matrix nu_{yi}"),
onlyif(has_lfactor, c("vector lambda_{yi}", "vector psi_{yi}")),
onlyif(has_fixed, "vector beta_{yi}"),
onlyif(has_varying, stan_array_arg(backend, "vector", "delta_{yi}"))
)
}
has_X <- has_fixed || has_varying || has_random
glm <- attr(intercept[[1]], "glm")
LJ <- ifelse_(glm, "L", "J")
scalar_intercept <- !has_random && !has_random_intercept &&
!has_lfactor && (glm || !has_X)
fun_args <- onlyif(
threading,
glue_cs(
stan_array_arg(backend, "int", "t_obs_{y}", 0L, TRUE),
onlyif(threading, c("int start", "int end")),
ifelse_(
has_missing,
c(stan_array_arg(backend, "int", "obs_{y}", 1L, TRUE),
stan_array_arg(backend, "int", "n_obs_{y}", 0L, TRUE)
),
"data int N"
),
stan_array_arg(backend, "int", "y_{y}", 1 + multinomial, TRUE),
"data int S_{y}",
fun_args,
onlyif(
has_random,
c(
stan_array_arg(backend, "int", "J_random_{y}", 0L, TRUE),
"int K_random_{y}"
)
),
onlyif(
has_fixed,
stan_array_arg(backend, "int", "{LJ}_fixed_{y}", 0L, TRUE)
),
onlyif(
has_varying,
stan_array_arg(backend, "int", "{LJ}_varying_{y}", 0L, TRUE)
),
onlyif(
has_fixed || has_varying,
c(
stan_array_arg(backend, "int", "J_{y}", 0L, TRUE),
"data int K_{y}"
)
),
onlyif(has_X, "data array[] matrix X")
)
)
n_obs <- ifelse_(
has_missing,
glue::glue("n_obs_{y}[t]"),
"N"
)
loop_index <- ifelse_(
threading,
paste0("t_obs_", y),
ifelse_(
has_fully_missing,
paste0("t_obs_", y),
"1:T"
)
)
if (glm && !multinomial) {
# combine intercepts and gammas
icpt_y <- c("0", paste0("intercept_", cats))
icpt <- paste_rows(
"real intercept_{cats} = {unlist(intercept)};",
"vector[S_{y}] intercept_{y} = [{cs(icpt_y)}]';",
.indent = idt(3)
)
gamma <- onlyif(
has_fixed || has_varying,
paste_rows(
"matrix[K_{y}, S_{y}] gamma__{y};",
"gamma__{y}[, 1] = zeros_K_{y};",
.indent = idt(2)
)
)
beta <- onlyif(
has_fixed,
paste0(
"gamma__{y}[L_fixed_{y}, {seq.int(2L, S)}] = ",
"beta_{y}_{cats};"
)
)
delta <- onlyif(
has_varying,
paste0(
"gamma__{y}[L_varying_{y}, {seq.int(2L, S)}] = ",
"delta_{y}_{cats}[t];"
)
)
likelihood_term <- paste0(
"ll += categorical_logit_glm_l{u}pmf(y_{y}[t, {obs}] | X[t][{obs},",
" J_{y}], intercept_{y}, gamma__{y});"
)
likelihood <- paste_rows(
gamma,
beta,
"for (t in {loop_index}) {{",
icpt,
delta,
likelihood_term,
"}}",
.indent = idt(c(0, 2, 2, 0, 3, 3, 2))
)
} else {
category_dim <- ifelse_(multinomial, ", ", "")
intercept_line <- ifelse_(
scalar_intercept,
"real intercept_{y} = {intercept};",
"vector[{n_obs}] intercept_{y} = {intercept};"
)
if (scalar_intercept) {
icpt_y <- c("0", paste0("intercept_", cats))
icpt <- paste_rows(
"real intercept_{cats} = {unlist(intercept)};",
.indent = idt(3)
)
} else {
icpt_y <- c("0", paste0("intercept_", cats, "[i]"))
icpt <- paste_rows(
ifelse_(
nzchar(obs),
"vector[n_obs_{y}[t]] intercept_{cats} = {unlist(intercept)};",
"vector[N] intercept_{cats} = {unlist(intercept)};"
),
.indent = idt(3)
)
}
likelihood_term <- ifelse_(
nzchar(obs),
paste0(
"ll += {distr}_logit_l{u}pmf(y_{y}[t, obs_{y}[i, t]{category_dim}]",
"| intercept_{y});"
),
"ll += {distr}_logit_l{u}pmf(y_{y}[t, i{category_dim}] | intercept_{y});"
)
likelihood <- paste_rows(
"vector[S_{y}] intercept_{y};",
"for (t in {loop_index}) {{",
icpt,
ifelse_(
nzchar(obs),
"for (i in 1:n_obs_{y}[t]) {{",
"for (i in 1:N) {{"
),
"intercept_{y} = [{cs(icpt_y)}]';",
likelihood_term,
"}}",
"}}",
.indent = idt(c(2, 2, 0, 3, 4, 4, 3, 2))
)
}
fun_body <- paste_rows(
onlyif(
K > 0L,
glue::glue("vector[K_{y}] zeros_K_{y} = rep_vector(0, K_{y});")
),
"real ll = 0.0;",
likelihood,
ifelse_(threading, "return ll;", "target += ll;"),
.parse = FALSE,
.indent = idt(c(1, 2, 0, 2))
)
paste_rows(
ifelse_(threading, "real {distr}_loglik_{y}_lpmf({fun_args}) {{", "{{"),
"{fun_body}",
"}}",
.indent = idt(c(1, 1, 1))
)
}
loglik_lines_cumulative <- function(y, obs, idt, default, family,
has_fixed_intercept,
has_varying_intercept, backend, ...) {
u <- default$u
is_logit <- identical("logit", family$link)
link <- ifelse_(is_logit, "logistic", "probit")
cutpoint <- ifelse_(
has_varying_intercept,
glue::glue("cutpoint_{y}[t, ]"),
glue::glue("cutpoint_{y}")
)
likelihood <- ifelse_(
default$use_glm && is_logit,
glue::glue(
"ll += ordered_{link}_glm_l{u}pmf(y_{y}[t, {obs}] | X[t][{obs}, J_{y}], ",
"gamma__{y}, {cutpoint});"
),
glue::glue(
"ll += ordered_{link}_l{u}pmf(y_{y}[t, {obs}] | ",
"intercept_{y}, {cutpoint});"
)
)
if (default$threading) {
default$fun_args <- cs(
default$fun_args,
onlyif(
has_fixed_intercept,
glue::glue("vector cutpoint_{y}")
),
onlyif(
has_varying_intercept,
glue::glue("array[] vector cutpoint_{y}")
)
)
}
loglik_lines_finalize(idt, default, likelihood)
}
loglik_lines_exponential <- function(y, obs, idt, default, ...) {
u <- default$u
likelihood <- glue::glue(
"ll += exponential_l{u}pdf(y_{y}[{obs}, t] | inv(exp(intercept_{y})));"
)
loglik_lines_finalize(idt, default, likelihood)
}
loglik_lines_gamma <- function(y, obs, idt, default, ...) {
u <- default$u
likelihood <- glue::glue(
"ll += gamma_l{u}pdf(y_{y}[{obs}, t] | phi_{y}, phi_{y} * ",
"inv(exp(intercept_{y})));"
)
if (default$threading) {
default$fun_args <- cs(default$fun_args, glue::glue("real phi_{y}"))
}
loglik_lines_finalize(idt, default, likelihood)
}
loglik_lines_gaussian <- function(y, obs, idt, default, ...) {
u <- default$u
likelihood <- ifelse_(
default$use_glm,
glue::glue(
"ll += normal_id_glm_l{u}pdf(y_{y}[{obs}, t] | X[t][{obs}, J_{y}], ",
"intercept_{y}, gamma__{y}, sigma_{y});"
),
glue::glue(
"ll += normal_l{u}pdf(y_{y}[{obs}, t] | intercept_{y}, sigma_{y});"
)
)
if (default$threading) {
default$fun_args <- cs(default$fun_args, glue::glue("real sigma_{y}"))
}
loglik_lines_finalize(idt, default, likelihood)
}
loglik_lines_multinomial <- function(idt, cvars, cgvars, backend,
threading, ...) {
cgvars$categories <- cgvars$y
cgvars$y <- cgvars$y_cg
cgvars$multinomial <- TRUE
cgvars$threading <- threading
cgvars$backend <- backend
do.call(loglik_lines_categorical, args = c(cgvars, idt = idt))
}
loglik_lines_mvgaussian <- function(idt, cvars, cgvars, backend,
threading, ...) {
u <- ifelse_(stan_version(backend) >= "2.25", "u", "")
y <- cgvars$y
y_cg <- cgvars$y_cg
obs <- cgvars$obs
mu <- fun_args <- character(length(y))
has_X <- FALSE
for (i in seq_along(y)) {
yi <- y[i]
args <- cvars[[i]]
args$obs <- obs
args$backend <- backend
mu[i] <- do.call(intercept_lines, args = args)
fun_args[i] <- glue_cs(
onlyif(cvars[[i]]$has_fixed_intercept, "real alpha_{yi}"),
onlyif(
cvars[[i]]$has_varying_intercept,
stan_array_arg(backend, "real", "alpha_{yi}")
),
onlyif(
cvars[[i]]$has_random,
c(
stan_array_arg(backend, "int", "J_random_{yi}", 0L, TRUE),
"int K_random_{yi}"
)
),
onlyif(
cvars[[i]]$has_random || cvars[[i]]$has_random_intercept,
"matrix nu_{yi}"
),
onlyif(
cvars[[i]]$has_lfactor,
c("vector lambda_{yi}", "vector psi_{yi}")
),
onlyif(
cvars[[i]]$has_fixed,
c(
stan_array_arg(backend, "int", "J_fixed_{yi}", 0L, TRUE),
"vector beta_{yi}"
)
),
onlyif(
cvars[[i]]$has_varying,
c(
stan_array_arg(backend, "int", "J_varying_{yi}", 0L, TRUE),
stan_array_arg(backend, "vector", "delta_{yi}")
)
),
"real sigma_{yi}"
)
has_X <- has_X ||
cvars[[i]]$has_fixed || cvars[[i]]$has_varying || cvars[[i]]$has_random
}
fun_args <- onlyif(
threading,
glue_cs(
stan_array_arg(backend, "int", "t_obs_{y_cg}", 0L, TRUE),
onlyif(threading, c("int start", "int end")),
ifelse_(
cgvars$has_missing,
c(stan_array_arg(backend, "int", "obs_{y_cg}", 1L, TRUE),
stan_array_arg(backend, "int", "n_obs_{y_cg}", 0L, TRUE)
),
"data int N"
),
stan_array_arg("cmdstanr", "vector", "y_{y_cg}", 1, TRUE),
"int O_{y_cg}",
fun_args,
onlyif(has_X, "data array[] matrix X"),
"matrix L_{y_cg}"
)
)
n_obs <- ifelse_(
cgvars$has_missing,
glue::glue("n_obs_{y_cg}[t]"),
"N"
)
loop_index <- ifelse_(
threading,
paste0("t_obs_", y_cg),
ifelse_(
cgvars$has_fully_missing,
paste0("t_obs_", y_cg),
"1:T"
)
)
sd_y <- paste0("sigma_", y)
mu_y <- paste0("mu_", y, "[i]")
likelihood <- paste_rows(
"vector[O_{y_cg}] sigma_{y_cg} = [{cs(sd_y)}]';",
paste0(
"matrix[O_{y_cg}, O_{y_cg}] Lsigma = ",
"diag_pre_multiply(sigma_{y_cg}, L_{y_cg});"
),
"for (t in {loop_index}) {{",
stan_array(backend, "vector", "mu", "{n_obs}", dims = "O_{y_cg}"),
"vector[{n_obs}] mu_{y} = {mu};",
"for (i in 1:{n_obs}) {{",
"mu[i] = [{cs(mu_y)}]';",
"}}",
"ll += multi_normal_cholesky_l{u}pdf(y_{y_cg}[t, {obs}] | mu, Lsigma);",
"}}",
.indent = idt(c(1, 1, 1, 2, 2, 2, 3, 2, 2, 1))
)
fun_body <- paste_rows(
"real ll = 0.0;",
likelihood,
ifelse_(threading, "return ll;", "target += ll;"),
.parse = FALSE,
.indent = idt(c(1, 0, 1))
)
paste_rows(
ifelse_(
threading,
"real mvgaussian_loglik_{y_cg}_lpmf({fun_args}) {{",
"{{"
),
"{fun_body}",
"}}",
.indent = idt(c(1, 0, 1))
)
}
loglik_lines_negbin <- function(y, obs, idt, default, ...) {
u <- default$u
likelihood <- ifelse_(
default$use_glm,
glue::glue(
"ll += neg_binomial_2_log_glm_l{u}pmf(y_{y}[t, {obs}] | ",
"X[t][{obs}, J_{y}], intercept_{y}, gamma__{y}, phi_{y});"
),
glue::glue(
"ll += neg_binomial_2_log_l{u}pmf(y_{y}[t, {obs}] | ",
"intercept_{y}, phi_{y});"
)
)
if (default$threading) {
default$fun_args <- cs(default$fun_args, glue::glue("real phi_{y}"))
}
loglik_lines_finalize(idt, default, likelihood)
}
loglik_lines_poisson <- function(y, obs, idt, default, ...) {
u <- default$u
likelihood <- ifelse_(
default$use_glm,
glue::glue(
"ll += poisson_log_glm_l{u}pmf(y_{y}[t, {obs}] | X[t][{obs}, J_{y}], ",
"intercept_{y}, gamma__{y});"
),
glue::glue("ll += poisson_log_l{u}pmf(y_{y}[t, {obs}] | intercept_{y});")
)
loglik_lines_finalize(idt, default, likelihood)
}
loglik_lines_student <- function(y, obs, idt, default, ...) {
u <- default$u
likelihood <- glue::glue(
"ll += student_t_l{u}pdf(y_{y}[{obs}, t] | phi_{y}, intercept_{y}, ",
"sigma_{y});"
)
if (default$threading) {
default$fun_args <- cs(
default$fun_args, glue::glue("real sigma_{y}"), glue::glue("real phi_{y}")
)
}
loglik_lines_finalize(idt, default, likelihood)
}
# Functions block ---------------------------------------------------------
functions_lines_bernoulli <- function(...) {
loglik_lines_bernoulli(...)
}
functions_lines_beta <- function(...) {
loglik_lines_beta(...)
}
functions_lines_binomial <- function(...) {
loglik_lines_binomial(...)
}
functions_lines_categorical <- function(...) {
loglik_lines_categorical(...)
}
functions_lines_cumulative <- function(...) {
loglik_lines_cumulative(...)
}
functions_lines_exponential <- function(...) {
loglik_lines_exponential(...)
}
functions_lines_gamma <- function(...) {
loglik_lines_gamma(...)
}
functions_lines_gaussian <- function(...) {
loglik_lines_gaussian(...)
}
functions_lines_multinomial <- function(...) {
loglik_lines_multinomial(...)
}
functions_lines_mvgaussian <- function(...) {
loglik_lines_mvgaussian(...)
}
functions_lines_negbin <- function(...) {
loglik_lines_negbin(...)
}
functions_lines_poisson <- function(...) {
loglik_lines_poisson(...)
}
functions_lines_student <- function(...) {
loglik_lines_student(...)
}
# Data block --------------------------------------------------------------
missing_data_lines <- function(y, idt, has_missing,
has_fully_missing, backend) {
mis <- character(0L)
full_mis <- character(0L)
if (has_missing) {
mis <- paste_rows(
"// Missing data indicators",
stan_array(backend, "int", "obs_{y}", "N, T", "lower=0"),
stan_array(backend, "int", "n_obs_{y}", "T", "lower=0"),
.indent = idt(1),
.parse = TRUE
)
}
if (has_fully_missing) {
full_mis <- paste_rows(
"int<lower=0> T_obs_{y};",
stan_array(backend, "int", "t_obs_{y}", "T_obs_{y}", "lower=0"),
.indent = idt(1),
.parse = TRUE
)
}
paste_rows(
mis,
full_mis,
.indent = idt(0),
.parse = FALSE
)
}
prior_data_lines <- function(y, idt, prior_distr,
K_fixed, K_varying, K_random, category = "",
multinomial = FALSE, ...) {
ycat <- ifelse(
nzchar(category),
ifelse_(
multinomial,
category,
paste0(y, "_", category)
),
y
)
vectorize_beta <-
K_fixed > 0L &&
prior_distr$vectorized_beta &&
prior_distr$beta_prior_npars > 0L
vectorize_delta <-
K_varying > 0L &&
prior_distr$vectorized_delta &&
prior_distr$delta_prior_npars > 0L
vectorize_tau <-
K_varying > 0L &&
prior_distr$vectorized_tau &&
prior_distr$tau_prior_npars > 0L
vectorize_sigma_nu <-
K_random > 0L &&
prior_distr$vectorized_sigma_nu &&
prior_distr$sigma_nu_prior_npars > 0L
any_vectorized <-
vectorize_beta ||
vectorize_delta ||
vectorize_tau ||
vectorize_sigma_nu
paste_rows(
onlyif(
any_vectorized,
"// Parameters of vectorized priors"
),
onlyif(
vectorize_beta,
paste0(
"matrix[K_fixed_{y}, {prior_distr$beta_prior_npars}] ",
"beta_prior_pars_{ycat};"
)
),
onlyif(
vectorize_delta,
paste0(
"matrix[K_varying_{y}, {prior_distr$delta_prior_npars}] ",
"delta_prior_pars_{ycat};"
)
),
onlyif(
vectorize_tau,
paste0(
"matrix[K_varying_{y}, {prior_distr$tau_prior_npars}] ",
"tau_prior_pars_{ycat};"
)
),
onlyif(
vectorize_sigma_nu,
paste0(
"matrix[K_random_{y}, {prior_distr$sigma_nu_prior_npars}] ",
"sigma_nu_prior_pars_{ycat};"
)
),
.indent = idt(1)
)
}
data_lines_default <- function(y, idt, has_random_intercept,
K_fixed, K_varying, K_random, backend, ...) {
icpt <- ifelse_(has_random_intercept, " - 1", "")
re_icpt <- ifelse_(has_random_intercept, 1L, 0L)
paste_rows(
"// number of fixed, varying and random coefficients, and related indices",
onlyif(K_fixed > 0L, "int<lower=0> K_fixed_{y};"),
onlyif(K_varying > 0L, "int<lower=0> K_varying_{y};"),
onlyif(
K_random > 0L,
"int<lower=0> K_random_{y}; // includes the potential random intercept"
),
onlyif(
K_fixed + K_varying > 0L,
"int<lower=0> K_{y}; // K_fixed + K_varying"
),
onlyif(
K_fixed > 0L,
stan_array(backend, "int", "J_fixed_{y}", "K_fixed_{y}")
),
onlyif(
K_varying > 0L,
stan_array(backend, "int", "J_varying_{y}", "K_varying_{y}")
),
onlyif(
K_random > re_icpt,
stan_array(
backend,
"int",
"J_random_{y}",
"K_random_{y}{icpt}",
comment = "no intercept"
)
),
onlyif(
K_fixed + K_varying > 0L,
stan_array(
backend,
"int",
"J_{y}",
"K_{y}",
comment = "fixed and varying"
)
),
onlyif(
K_fixed > 0L,
stan_array(backend, "int", "L_fixed_{y}", "K_fixed_{y}")
),
onlyif(
K_varying > 0L,
stan_array(backend, "int", "L_varying_{y}", "K_varying_{y}")
),
.indent = idt(1)
)
}
data_lines_bernoulli <- function(y, idt, default, has_missing,
has_fully_missing, prior_distr,
K_fixed, K_varying, K_random, backend, ...) {
paste_rows(
missing_data_lines(y, idt, has_missing, has_fully_missing, backend),
default,
prior_data_lines(y, idt, prior_distr, K_fixed, K_varying, K_random),
stan_array(backend, "int", "y_{y}", "T, N", "lower=0,upper=1"),
.indent = idt(c(0, 0, 0, 1))
)
}
data_lines_beta <- function(y, idt, default, has_missing,
has_fully_missing, prior_distr,
K_fixed, K_varying, K_random, backend, ...) {
paste_rows(
missing_data_lines(y, idt, has_missing, has_fully_missing, backend),
default,
prior_data_lines(y, idt, prior_distr, K_fixed, K_varying, K_random),
"matrix<lower=0, upper=1>[N, T] y_{y};",
.indent = idt(c(0, 0, 0, 1))
)
}
data_lines_binomial <- function(y, idt, default, has_missing,
has_fully_missing, prior_distr,
K_fixed, K_varying, K_random, backend, ...) {
paste_rows(
missing_data_lines(y, idt, has_missing, has_fully_missing, backend),
default,
prior_data_lines(y, idt, prior_distr, K_fixed, K_varying, K_random),
stan_array(backend, "int", "y_{y}", "T, N", "lower=0"),
"// Trials for binomial response {y}",
stan_array(backend, "int", "trials_{y}", "T, N", "lower=1"),
.indent = idt(c(0, 0, 0, 1, 1, 1))
)
}
data_lines_categorical <- function(y, idt, default, has_missing,
has_fully_missing, prior_distr,
K_fixed, K_varying, K_random,
categories, backend, ...) {
prior_data <- lapply(
categories[-1L],
function(s) {
prior_data_lines(
y, idt, prior_distr[[s]], K_fixed, K_varying, K_random, category = s
)
}
)
paste_rows(
"int<lower=0> S_{y}; // number of categories",
missing_data_lines(y, idt, has_missing, has_fully_missing, backend),
default,
prior_data,
"// Response",
stan_array(backend, "int", "y_{y}", "T, N", "lower=0"),
.indent = idt(c(1, 0, 0, 0, 1, 1))
)
}
data_lines_cumulative <- function(y, idt, default, has_missing,
has_fully_missing, prior_distr,
K_fixed, K_varying, K_random, backend, ...) {
paste_rows(
"int<lower=2> S_{y}; // number of categories",
missing_data_lines(y, idt, has_missing, has_fully_missing, backend),
default,
prior_data_lines(y, idt, prior_distr, K_fixed, K_varying, K_random),
"// Response",
stan_array(backend, "int", "y_{y}", "T, N", "lower=0, upper=S_{y}"),
.indent = idt(c(1, 0, 0, 0, 1, 1))
)
}
data_lines_exponential <- function(y, idt, default, has_missing,
has_fully_missing, prior_distr,
K_fixed, K_varying, K_random,
backend, ...) {
paste_rows(
missing_data_lines(y, idt, has_missing, has_fully_missing, backend),
default,
prior_data_lines(y, idt, prior_distr, K_fixed, K_varying, K_random),
"matrix<lower=0>[N, T] y_{y};",
.indent = idt(c(0, 0, 0, 1))
)
}
data_lines_gamma <- function(y, idt, default, has_missing,
has_fully_missing, prior_distr,
K_fixed, K_varying, K_random, backend, ...) {
paste_rows(
missing_data_lines(y, idt, has_missing, has_fully_missing, backend),
default,
prior_data_lines(y, idt, prior_distr, K_fixed, K_varying, K_random),
"matrix<lower=0>[N, T] y_{y};",
.indent = idt(c(0, 0, 0, 1))
)
}
data_lines_gaussian <- function(y, idt, default, has_missing,
has_fully_missing, prior_distr,
K_fixed, K_varying, K_random, backend, ...) {
paste_rows(
missing_data_lines(y, idt, has_missing, has_fully_missing, backend),
default,
prior_data_lines(y, idt, prior_distr, K_fixed, K_varying, K_random),
"matrix[N, T] y_{y};",
.indent = idt(c(0, 0, 0, 1))
)
}
data_lines_multinomial <- function(y_cg, idt, default, has_missing,
has_fully_missing, prior_distr,
K_fixed, K_varying, K_random,
y, has_random_intercept, backend, ...) {
default <- data_lines_default(
y_cg, idt, has_random_intercept, K_fixed, K_varying, K_random, backend
)
prior_data <- ulapply(
y[-1L],
function(s) {
prior_data_lines(
y_cg, idt, prior_distr[[s]],
K_fixed, K_varying, K_random,
category = s, multinomial = TRUE
)
}
)
paste_rows(
"int<lower=0> S_{y_cg}; // number of categories",
missing_data_lines(y_cg, idt, has_missing, has_fully_missing, backend),
default,
prior_data,
"// Response",
stan_array(backend, "int", "y_{y_cg}", "T, N, S_{y_cg}", "lower=0"),
.indent = idt(c(1, 0, 0, 0, 1, 1))
)
}
data_lines_mvgaussian <- function(y_cg, idt, default, has_missing,
has_fully_missing, prior_distr,
K_fixed, K_varying, K_random, backend, ...) {
paste_rows(
missing_data_lines(y_cg, idt, has_missing, has_fully_missing, backend),
default,
"int<lower=0> O_{y_cg};",
stan_array(backend, "vector", "y_{y_cg}", "T, N", dims = "O_{y_cg}"),
.indent = idt(c(0, 0, 1, 1))
)
}
data_lines_negbin <- function(y, idt, default, has_missing,
has_fully_missing, has_offset, prior_distr,
K_fixed, K_varying, K_random, backend, ...) {
paste_rows(
missing_data_lines(y, idt, has_missing, has_fully_missing, backend),
default,
prior_data_lines(y, idt, prior_distr, K_fixed, K_varying, K_random),
stan_array(backend, "int", "y_{y}", "T, N", "lower=0"),
onlyif(
has_offset,
"// Offset term"
),
onlyif(
has_offset,
"matrix[N, T] offset_{y};"
),
.indent = idt(c(0, 0, 0, 1, 1, 1))
)
}
data_lines_poisson <- function(y, idt, default, has_missing,
has_fully_missing, has_offset, prior_distr,
K_fixed, K_varying, K_random, backend, ...) {
paste_rows(
missing_data_lines(y, idt, has_missing, has_fully_missing, backend),
default,
prior_data_lines(y, idt, prior_distr, K_fixed, K_varying, K_random),
stan_array(backend, "int", "y_{y}", "T, N", "lower=0"),
onlyif(
has_offset,
"// Offset term"
),
onlyif(
has_offset,
"matrix[N, T] offset_{y};"
),
.indent = idt(c(0, 0, 0, 1, 1, 1))
)
}
data_lines_student <- function(y, idt, default, has_missing,
has_fully_missing, prior_distr,
K_fixed, K_varying, K_random, backend, ...) {
paste_rows(
missing_data_lines(y, idt, has_missing, has_fully_missing, backend),
default,
prior_data_lines(y, idt, prior_distr, K_fixed, K_varying, K_random),
"matrix[N, T] y_{y};",
.indent = idt(c(0, 0, 0, 1))
)
}
# Transformed data block --------------------------------------------------
transformed_data_lines_default <- function(y, idt, ...) {
list(
declarations = "",
statements = ""
)
}
transformed_data_lines_bernoulli <- function(default, ...) {
default
}
transformed_data_lines_beta <- function(default, ...) {
default
}
transformed_data_lines_binomial <- function(default, ...) {
default
}
transformed_data_lines_categorical <- function(y, idt, K, S, ...) {
list(
declarations = "",
statements = ""
)
}
transformed_data_lines_cumulative <- function(default, ...) {
default
}
transformed_data_lines_exponential <- function(default, ...) {
default
}
transformed_data_lines_gamma <- function(default, ...) {
default
}
transformed_data_lines_gaussian <- function(default, ...) {
default
}
transformed_data_lines_multinomial <- function(y_cg, idt, K, S, ...) {
list(
declarations = "",
statements = ""
)
}
transformed_data_lines_mvgaussian <- function(default, ...) {
default[[1L]]
}
transformed_data_lines_negbin <- function(default, ...) {
default
}
transformed_data_lines_poisson <- function(default, ...) {
default
}
transformed_data_lines_student <- function(default, ...) {
default
}
# Parameters block --------------------------------------------------------
parameters_lines_default <- function(y, idt, noncentered, lb, has_fixed,
has_varying, has_fixed_intercept,
has_varying_intercept, has_lfactor,
noncentered_psi, flip_sign,
nonzero_lambda, ydim = y, ...) {
oname <- ifelse_(noncentered, "omega_raw_", "omega_")
paste_rows(
onlyif(
has_fixed,
"vector[K_fixed_{ydim}] beta_{y}; // Fixed coefficients"
),
onlyif(
has_varying,
"matrix[K_varying_{ydim}, D] {oname}{y}; // Spline coefficients"
),
onlyif(
has_varying,
paste0(
"vector<lower={lb}>[K_varying_{ydim}] tau_{y}; ",
"// SDs for the random walks"
)
),
ifelse_(
has_fixed_intercept || has_varying_intercept,
"real a_{y}; // Mean of the first time point",
""
),
onlyif(
has_varying_intercept,
"row_vector[D - 1] omega_raw_alpha_{y}; // Coefficients for alpha"
),
onlyif(
has_varying_intercept,
"real<lower={lb}> tau_alpha_{y}; // SD for the random walk"
),
onlyif(
has_lfactor && nonzero_lambda,
paste_rows(
"real<lower=0> zeta_{y}; // tau_psi * sigma_lambda",
"real<lower=0,upper=1> kappa_{y}; // sigma_lambda = kappa * zeta",
.parse = FALSE
)
),
onlyif(
has_lfactor && !nonzero_lambda,
"real<lower=0> sigma_lambda_{y};"
),
onlyif(
has_lfactor,
"vector[N - 1] lambda_raw_{y}; // raw factor loadings"
),
onlyif(
has_lfactor,
paste0(
"real omega_raw_psi_1_{y}; ",
"// factor spline coef for first time point"
)
),
.indent = idt(1)
)
}
parameters_lines_bernoulli <- function(default, ...) {
default
}
parameters_lines_beta <- function(y, idt, default, ...) {
paste_rows(
default,
"real<lower=0> phi_{y}; // Precision parameter of the Beta distribution",
.indent = idt(c(0, 1))
)
}
parameters_lines_binomial <- function(default, ...) {
default
}
parameters_lines_categorical <- function(y, idt, default, ...) {
paste_rows(
default,
.parse = FALSE,
.indent = idt(0)
)
}
parameters_lines_cumulative <- function(y, idt, default,
has_fixed_intercept, ...) {
paste_rows(
default,
onlyif(
has_fixed_intercept,
"ordered[S_{y} - 1] cutpoint_{y}; // Cutpoints"
),
.indent = idt(c(0, 1))
)
}
parameters_lines_exponential <- function(y, idt, default, ...) {
default
}
parameters_lines_gamma <- function(y, idt, default, ...) {
paste_rows(
default,
"real<lower=0> phi_{y}; // Shape parameter of the Gamma distribution",
.indent = idt(c(0, 1))
)
}
parameters_lines_gaussian <- function(y, idt, default, ...) {
paste_rows(
default,
"real<lower=0> sigma_{y}; // SD of the normal distribution",
.indent = idt(c(0, 1))
)
}
parameters_lines_multinomial <- function(y_cg, idt, univariate, ...) {
paste_rows(
univariate,
.indent = idt(0)
)
}
parameters_lines_mvgaussian <- function(y_cg, idt, univariate, ...) {
paste_rows(
univariate,
"cholesky_factor_corr[O_{y_cg}] L_{y_cg}; // Cholesky for gaussian",
.indent = idt(c(0, 1))
)
}
parameters_lines_negbin <- function(y, idt, default, ...) {
paste_rows(
default,
"real<lower=0> phi_{y}; // Dispersion parameter of the NB distribution",
.indent = idt(c(0, 1))
)
}
parameters_lines_poisson <- function(default, ...) {
default
}
parameters_lines_student <- function(y, idt, default, ...) {
paste_rows(
default,
"real<lower=0> sigma_{y}; // scale of the t-distribution",
"real<lower=1> phi_{y}; // Degrees of freedom of the t-distribution",
.indent = idt(c(0, 1, 1))
)
}
# Transformed parameters block --------------------------------------------
transformed_parameters_lines_default <- function(y, idt, noncentered,
shrinkage,
has_fixed, has_varying,
has_fixed_intercept,
has_varying_intercept,
has_random_intercept,
J, K, K_fixed, K_varying,
L_fixed, L_varying,
has_lfactor,
noncentered_psi, flip_sign,
nonzero_lambda,
backend, ydim = y, ...) {
if (noncentered) {
xi_term <- ifelse_(shrinkage, " * xi[i - 1];", ";")
declare_omega <- paste_rows(
"matrix[K_varying_{ydim}, D] omega_{y};",
.indent = idt(1),
.parse = FALSE
)
state_omega <- paste_rows(
"omega_{y}[, 1] = omega_raw_{y}[, 1];",
"for (i in 2:D) {{",
paste0(
"omega_{y}[, i] = omega_{y}[, i - 1] + ",
"omega_raw_{y}[, i] .* tau_{y}{xi_term}"
),
"}}",
.indent = idt(c(1, 1, 2, 1)),
.parse = FALSE
)
declare_omega_alpha <- paste_rows(
"row_vector[D] omega_alpha_{y};",
.indent = idt(1),
.parse = FALSE
)
state_omega_alpha <- paste_rows(
"omega_alpha_{y}[1] = omega_alpha_1_{y};",
"for (i in 2:D) {{",
paste0(
"omega_alpha_{y}[i] = omega_alpha_{y}[i - 1] + ",
"omega_raw_alpha_{y}[i - 1] * tau_alpha_{y}{xi_term}"
),
"}}",
.indent = idt(c(1, 1, 2, 1)),
.parse = FALSE
)
} else {
declare_omega_alpha <- paste_rows(
"row_vector[D] omega_alpha_{y};",
.indent = idt(1),
.parse = FALSE
)
state_omega_alpha <- paste_rows(
"omega_alpha_{y}[1] = omega_alpha_1_{y};",
"omega_alpha_{y}[2:D] = omega_raw_alpha_{y};",
.indent = idt(1),
.parse = FALSE
)
}
declare_delta <- paste_rows(
"// Time-varying coefficients",
stan_array(backend, "vector", "delta_{y}", "T", dims = "K_varying_{ydim}"),
.indent = idt(1),
.parse = FALSE
)
state_delta <- paste_rows(
"for (t in 1:T) {{",
"delta_{y}[t] = omega_{y} * Bs[, t];",
"}}",
.indent = idt(c(1, 2, 1)),
.parse = FALSE
)
psi <- ifelse_(
has_lfactor && nonzero_lambda,
glue::glue(" - psi_{y}[1]"),
""
)
declare_fixed_intercept <- paste_rows(
"// Time-invariant intercept",
"real alpha_{y};",
.indent = idt(1),
.parse = FALSE
)
if (has_fixed || has_varying) {
declare_omega_alpha_1 <- paste_rows(
"// Time-varying intercept",
stan_array(backend, "real", "alpha_{y}", "T"),
"// Spline coefficients",
"real omega_alpha_1_{y};",
.indent = idt(1),
.parse = FALSE
)
state_omega_alpha_1 <- paste_rows(
"// Define the first alpha using mean a_{y}",
"{{",
"vector[K_{ydim}] gamma__{y};",
onlyif(has_fixed, "gamma__{y}[L_fixed_{ydim}] = beta_{y};"),
onlyif(has_varying, "gamma__{y}[L_varying_{ydim}] = delta_{y}[1];"),
"omega_alpha_1_{y} = a_{y} - X_m[J_{ydim}] * gamma__{y};",
"}}",
.indent = idt(c(1, 1, 2, 2, 2, 2, 1)),
.parse = FALSE
)
state_fixed_intercept <- paste_rows(
"// Define the first alpha using mean a_{y}",
"{{",
"vector[K_{ydim}] gamma__{y};",
onlyif(has_fixed, "gamma__{y}[L_fixed_{ydim}] = beta_{y};"),
onlyif(has_varying, "gamma__{y}[L_varying_{ydim}] = delta_{y}[1];"),
"alpha_{y} = a_{y} - X_m[J_{ydim}] * gamma__{y}{psi};",
"}}",
.indent = idt(c(1, 1, 2, 2, 2, 2, 1)),
.parse = FALSE
)
} else {
declare_omega_alpha_1 <- paste_rows(
"// Time-varying intercept",
stan_array(backend, "real", "alpha_{y}", "T"),
"real omega_alpha_1_{y} = a_{y};",
.indent = idt(1),
.parse = FALSE
)
state_omega_alpha_1 <- character(0L)
state_fixed_intercept <- paste_rows(
"alpha_{y} = a_{y}{psi};",
.indent = idt(1),
.parse = FALSE
)
}
declare_varying_intercept <- paste_rows(
declare_omega_alpha_1,
declare_omega_alpha,
.indent = idt(0),
.parse = FALSE
)
state_varying_intercept <- paste_rows(
state_omega_alpha_1,
state_omega_alpha,
"for (t in 1:T) {{",
"alpha_{y}[t] = omega_alpha_{y} * Bs[, t];",
"}}",
.indent = idt(c(0, 0, 1, 2, 1)),
.parse = FALSE
)
m <- ifelse(has_lfactor && nonzero_lambda, "1 + ", "")
o <- ifelse(
has_random_intercept,
glue::glue("(lambda_{y} - dot_product(lambda_{y}, nu_{y}[, 1])",
"/ dot_self(nu_{y}[, 1]) * nu_{y}[, 1])"),
glue::glue("lambda_{y}")
)
declare_lambda <- paste_rows(
onlyif(
has_lfactor && nonzero_lambda,
"real sigma_lambda_{y} = kappa_{y} * zeta_{y};"
),
onlyif(
has_lfactor && nonzero_lambda,
"real tau_psi_{y} = (1 - kappa_{y}) * zeta_{y};"
),
"// hard sum constraint",
"vector[N] lambda_{y} = sum_to_zero(lambda_raw_{y}, QR_Q);",
"lambda_{y} = {m}sigma_lambda_{y} * {o};",
.indent = idt(1),
.parse = FALSE
)
if (noncentered_psi) {
state_omega_psi <- paste_rows(
"omega_psi_{y}[1] = omega_raw_psi_1_{y};",
"omega_psi_{y} = cumulative_sum(omega_psi_{y});",
.indent = idt(1),
.parse = FALSE
)
} else {
state_omega_psi <- paste_rows(
"omega_psi_{y}[1] = omega_raw_psi_1_{y};",
.indent = idt(1),
.parse = FALSE
)
}
declare_psi <- paste_rows(
"// Latent factor",
"vector[T] psi_{y};",
.indent = idt(1),
.parse = FALSE
)
if (has_lfactor && !nonzero_lambda && flip_sign) {
state_psi <- paste_rows(
"// try to avoid sign-switching by adjusting psi and lambda",
"real sign_omega_{y} = mean(omega_psi_{y}) < 0 ? -1 : 1;",
"psi_{y} = sign_omega_{y} * (omega_psi_{y} * Bs)';",
"lambda_{y} = -sign_omega_{y} * lambda_{y};",
.indent = idt(1),
.parse = FALSE
)
} else {
state_psi <- paste_rows(
"psi_{y} = (omega_psi_{y} * Bs)';",
.indent = idt(1),
.parse = FALSE
)
}
list(
declarations = paste_rows(
onlyif(has_lfactor, declare_psi),
onlyif(has_lfactor, declare_lambda),
onlyif(has_varying && noncentered, declare_omega),
onlyif(has_varying, declare_delta),
onlyif(has_fixed_intercept, declare_fixed_intercept),
onlyif(has_varying_intercept, declare_varying_intercept),
.indent = idt(0)
),
statements = paste_rows(
onlyif(has_lfactor, state_omega_psi),
onlyif(has_lfactor, state_psi),
onlyif(has_varying && noncentered, state_omega),
onlyif(has_varying, state_delta),
onlyif(has_fixed_intercept, state_fixed_intercept),
onlyif(has_varying_intercept, state_varying_intercept),
.indent = idt(0)
)
)
}
transformed_parameters_lines_bernoulli <- function(default, ...) {
default
}
transformed_parameters_lines_beta <- function(default, ...) {
default
}
transformed_parameters_lines_binomial <- function(default, ...) {
default
}
transformed_parameters_lines_categorical <- function(default, idt, ...) {
list(
declarations = paste_rows(
ulapply(default, "[[", "declarations"),
.parse = FALSE,
.indent = idt(0)
),
statements = paste_rows(
ulapply(default, "[[", "statements"),
.parse = FALSE,
.indent = idt(0)
)
)
}
transformed_parameters_lines_cumulative <- function(y, categories,
has_varying_intercept,
default, idt,
backend, ...) {
declare_cutpoints <- ""
state_cutpoints <- ""
if (has_varying_intercept) {
declare_cutpoints <- glue::glue(
stan_array(
backend, "vector", "cutpoint_{y}", "T", "", "S_{y} - 1"
)
)
assign_cutpoints <- vapply(
seq_along(categories),
function(s) {
glue::glue(
"cutpoint_{y}[, {s}] = alpha_{y}_{s};"
)
},
character(1L)
)
state_cutpoints <- paste_rows(
assign_cutpoints,
"for (t in 1:T) {{",
"vector[S_{y}] tmp = exp(append_row(0, cutpoint_{y}[t, ]));",
"for (s in 1:(S_{y} - 1)) {{",
"cutpoint_{y}[t, s] = log(sum(tmp[1:s]) / sum(tmp[(s + 1):S_{y}]));",
"}}",
"}}",
.indent = idt(c(1, 1, 2, 2, 3, 2, 1))
)
}
list(
declarations = paste_rows(
ulapply(default, "[[", "declarations"),
declare_cutpoints,
.parse = FALSE,
.indent = idt(c(0, 1))
),
statements = paste_rows(
ulapply(default, "[[", "statements"),
state_cutpoints,
.parse = FALSE,
.indent = idt(0)
)
)
}
transformed_parameters_lines_exponential <- function(default, ...) {
default
}
transformed_parameters_lines_gamma <- function(default, ...) {
default
}
transformed_parameters_lines_gaussian <- function(default, ...) {
default
}
transformed_parameters_lines_multinomial <- function(default, idt, ...) {
list(
declarations = paste_rows(
ulapply(default, "[[", "declarations"),
.parse = FALSE,
.indent = idt(0)
),
statements = paste_rows(
ulapply(default, "[[", "statements"),
.parse = FALSE,
.indent = idt(0)
)
)
}
transformed_parameters_lines_mvgaussian <- function(default, idt, ...) {
list(
declarations = paste_rows(
ulapply(default, "[[", "declarations"),
.parse = FALSE,
.indent = idt(0)
),
statements = paste_rows(
ulapply(default, "[[", "statements"),
.parse = FALSE,
.indent = idt(0)
)
)
}
transformed_parameters_lines_negbin <- function(default, ...) {
default
}
transformed_parameters_lines_poisson <- function(default, ...) {
default
}
transformed_parameters_lines_student <- function(default, ...) {
default
}
# Model block -------------------------------------------------------------
# priors which do not depend on family
prior_lines <- function(y, idt, noncentered, shrinkage,
has_varying, has_fixed, has_random,
has_fixed_intercept,
has_varying_intercept, has_random_intercept,
has_lfactor, noncentered_psi, flip_sign,
nonzero_lambda,
K_fixed, K_varying, K_random, prior_distr, ...) {
if (prior_distr$vectorized_sigma_nu) {
dpars_sigma_nu <- ifelse_(
prior_distr$sigma_nu_prior_npars > 0L,
paste0(
"sigma_nu_prior_pars_", y, "[, ",
seq_len(prior_distr$sigma_nu_prior_npars), "]",
collapse = ", "
),
""
)
mtext_sigma_nu <- paste0(
"sigma_nu_{y} ~ {prior_distr$sigma_nu_prior_distr}",
"({dpars_sigma_nu});"
)
} else {
mtext_sigma_nu <-
"sigma_nu_{y}[{1:K_random}] ~ {prior_distr$sigma_nu_prior_distr};"
}
if (has_lfactor) {
mtext_lambda <- paste_rows(
"lambda_raw_{y} ~ normal(0, inv(sqrt(1 - inv(N))));",
"omega_raw_psi_1_{y} ~ {prior_distr$psi_prior_distr};",
onlyif(!nonzero_lambda,
"sigma_lambda_{y} ~ {prior_distr$sigma_lambda_prior_distr};"),
onlyif(nonzero_lambda, "zeta_{y} ~ {prior_distr$zeta_prior_distr};"),
onlyif(nonzero_lambda, "kappa_{y} ~ {prior_distr$kappa_prior_distr};"),
.indent = idt(c(0, 1, 1, 1, 1)),
.parse = TRUE
)
}
if (noncentered) {
mtext_omega <- "omega_raw_alpha_{y} ~ std_normal();"
if (prior_distr$vectorized_delta) {
dpars_varying <- ifelse_(
prior_distr$delta_prior_npars > 0L,
paste0(
"delta_prior_pars_", y, "[, ",
seq_len(prior_distr$delta_prior_npars), "]",
collapse = ", "
),
""
)
mtext_varying <- paste0(
"omega_raw_{y}[, 1] ~ {prior_distr$delta_prior_distr}",
"({dpars_varying});"
)
} else {
mtext_varying <-
"omega_raw_{y}[{1:K_varying}, 1] ~ {prior_distr$delta_prior_distr};"
}
mtext_varying <- paste_rows(
mtext_varying,
"to_vector(omega_raw_{y}[, 2:D]) ~ std_normal();",
.indent = idt(c(0, 1)),
.parse = FALSE
)
} else {
xi_term1 <- ifelse_(shrinkage, " * xi[1]", "")
xi_term <- ifelse_(shrinkage, " * xi[i]", "")
mtext_omega <- paste_rows(
paste0(
"omega_raw_alpha_{y}[1] ~ normal(omega_alpha_1_{y}, ",
"tau_alpha_{y}{xi_term1});"
),
"for (i in 2:(D - 1)) {{",
paste0(
"omega_raw_alpha_{y}[i] ~ normal(omega_raw_alpha_{y}[i - 1], ",
"tau_alpha_{y}{xi_term});"
),
"}}",
.indent = idt(c(0, 1, 2, 1)),
.parse = FALSE
)
if (prior_distr$vectorized_delta) {
dpars_varying <- ifelse_(
prior_distr$delta_prior_npars > 0L,
paste0(
"delta_prior_pars_", y, "[, ",
seq_len(prior_distr$delta_prior_npars), "]",
collapse = ", "
),
""
)
mtext_varying <- paste0(
"omega_{y}[, 1] ~ {prior_distr$delta_prior_distr}",
"({dpars_varying});"
)
} else {
mtext_varying <-
"omega_{y}[{1:K_varying}, 1] ~ {prior_distr$delta_prior_distr};"
}
mtext_varying <- paste_rows(
mtext_varying,
"for (i in 2:D) {{",
ifelse_(
shrinkage,
paste0(
"omega_{y}[, i] ~ normal(omega_{y}[, i - 1], ",
"xi[i - 1] * tau_{y});"
),
"omega_{y}[, i] ~ normal(omega_{y}[, i - 1], tau_{y});"
),
"}}",
.indent = idt(c(0, 1, 2, 1)),
.parse = FALSE
)
}
mtext_alpha <- "a_{y} ~ {prior_distr$alpha_prior_distr};"
mtext_tau_alpha <- "tau_alpha_{y} ~ {prior_distr$tau_alpha_prior_distr};"
mtext_varying_intercept <- paste_rows(
mtext_alpha,
mtext_omega,
mtext_tau_alpha,
.indent = idt(c(0, 1, 1)),
.parse = FALSE
)
mtext_fixed_intercept <- mtext_alpha
if (prior_distr$vectorized_beta) {
dpars_fixed <- ifelse_(
prior_distr$beta_prior_npars > 0L,
paste0(
"beta_prior_pars_", y, "[, ",
seq_len(prior_distr$beta_prior_npars), "]",
collapse = ", "
),
""
)
mtext_fixed <- "beta_{y} ~ {prior_distr$beta_prior_distr}({dpars_fixed});"
} else {
mtext_fixed <- "beta_{y}[{1:K_fixed}] ~ {prior_distr$beta_prior_distr};"
}
if (prior_distr$vectorized_tau) {
dpars_tau <- ifelse_(
prior_distr$tau_prior_npars > 0L,
paste0(
"tau_prior_pars_", y, "[, ", seq_len(prior_distr$tau_prior_npars), "]",
collapse = ", "
),
""
)
mtext_tau <- "tau_{y} ~ {prior_distr$tau_prior_distr}({dpars_tau});"
} else {
mtext_tau <- "tau_{y}[{1:K_varying}] ~ {prior_distr$tau_prior_distr};"
}
paste_rows(
onlyif(has_lfactor, mtext_lambda),
onlyif(has_random_intercept || has_random, mtext_sigma_nu),
onlyif(has_fixed_intercept, mtext_fixed_intercept),
onlyif(has_varying_intercept, mtext_varying_intercept),
onlyif(has_fixed, mtext_fixed),
onlyif(has_varying, mtext_varying),
onlyif(has_varying, mtext_tau),
.indent = idt(c(1, 1, 1, 1, 1, 1, 1))
)
}
model_lines_default <- function(y, obs, idt, threading, default, family, ...) {
likelihood <- ifelse_(
threading,
paste_rows(
paste0(
"target += reduce_sum({family$name}_loglik_{y}_lpmf, {default$seq1T}, ",
"grainsize, {default$fun_call_args});"
),
.indent = idt(1)
),
do.call(
paste0("loglik_lines_", family$name),
args = c(
list(y = y, obs = obs, idt = idt, default = default, family = family),
list(...)
)
)
)
likelihood
}
model_lines_bernoulli <- function(y, obs, idt, priors,
threading, default, ...) {
paste_rows(
priors,
model_lines_default(y, obs, idt, threading, default, ...),
.parse = FALSE
)
}
model_lines_beta <- function(y, obs, idt, priors,
threading, prior_distr, default, ...) {
if (threading) {
default$fun_call_args <- cs(default$fun_call_args, glue::glue("phi_{y}"))
}
model_text <- paste_rows(
glue::glue("phi_{y} ~ {prior_distr$phi_prior_distr};"),
model_lines_default(y, obs, idt, threading, default, ...),
.indent = idt(c(1, 0)),
.parse = FALSE
)
paste_rows(priors, model_text, .parse = FALSE)
}
model_lines_binomial <- function(y, obs, idt, priors,
threading, default, ...) {
paste_rows(
priors,
model_lines_default(y, obs, idt, threading, default, ...),
.parse = FALSE
)
}
model_lines_categorical <- function(y, idt, obs, family, priors,
has_missing, has_fully_missing, has_offset,
has_fixed_intercept, has_varying_intercept,
has_random_intercept,
has_fixed, has_varying, has_random,
has_lfactor, threading,
categories, multinomial = FALSE,
backend, K, ...) {
distr <- ifelse_(multinomial, "multinomial", "categorical")
if (threading) {
seq1T <- ifelse_(
has_fully_missing,
"t_obs_{y}",
"seq1T"
)
S <- length(categories)
cats <- categories[seq.int(2L, S)]
fun_args <- character(length(y))
for (i in seq_along(cats)) {
yi <- ifelse_(
multinomial,
cats[i],
paste0(y, "_", cats[i])
)
fun_args[i] <- glue_cs(
onlyif(has_fixed_intercept || has_varying_intercept, "alpha_{yi}"),
onlyif(has_random || has_random_intercept, "nu_{yi}"),
onlyif(has_lfactor, c("lambda_{y}_{yi}", "psi_{yi}")),
onlyif(has_fixed, "beta_{yi}"),
onlyif(has_varying, "delta_{yi}")
)
}
common_intercept <- !has_random && !has_random_intercept && !has_lfactor
glm <- stan_supports_categorical_logit_glm(backend, common_intercept) &&
(has_fixed || has_varying) && !multinomial
LJ <- ifelse_(
glm,
"L",
"J"
)
has_X <- has_fixed || has_varying || has_random
fun_args <- glue_cs(
ifelse_(
has_missing,
c("obs_{y}", "n_obs_{y}"),
"N"
),
"y_{y}",
"S_{y}",
fun_args,
onlyif(has_random, c("J_random_{y}", "K_random_{y}")),
onlyif(has_fixed, "{LJ}_fixed_{y}"),
onlyif(has_varying, "{LJ}_varying_{y}"),
onlyif(has_fixed || has_varying, c("J_{y}", "K_{y}")),
onlyif(has_X, "X")
)
likelihood <- paste_rows(
paste0(
"target += reduce_sum({distr}_loglik_{y}_lpmf, {seq1T}, grainsize, ",
"{fun_args});"
),
.indent = idt(1)
)
} else {
likelihood <- loglik_lines_categorical(
y, idt, obs, family, has_missing, has_fully_missing, has_offset,
has_varying, has_fixed, has_random, has_fixed_intercept,
has_varying_intercept, has_random_intercept, has_lfactor,
backend, threading, y, K, categories, multinomial
)
}
paste_rows(priors, likelihood, .parse = FALSE)
}
model_lines_cumulative <- function(y, obs, idt, priors,
threading, prior_distr, default, ...) {
if (threading) {
default$fun_call_args <- cs(
default$fun_call_args,
glue::glue("cutpoint_{y}")
)
}
S <- length(prior_distr$cutpoint_prior_distr)
paste_rows(
glue::glue(
"cutpoint_{y}[{seq_len(S)}] ~ {prior_distr$cutpoint_prior_distr};"
),
priors,
model_lines_default(y, obs, idt, threading, default, ...),
.parse = FALSE
)
}
model_lines_exponential <- function(y, obs, idt, priors,
threading, default, ...) {
paste_rows(
priors,
model_lines_default(y, obs, idt, threading, default, ...),
.parse = FALSE
)
}
model_lines_gamma <- function(y, obs, idt, priors,
threading, prior_distr, default, ...) {
if (threading) {
default$fun_call_args <- cs(default$fun_call_args, glue::glue("phi_{y}"))
}
model_text <- paste_rows(
glue::glue("phi_{y} ~ {prior_distr$phi_prior_distr};"),
model_lines_default(y, obs, idt, threading, default, ...),
.indent = idt(c(1, 0)),
.parse = FALSE
)
paste_rows(priors, model_text, .parse = FALSE)
}
model_lines_gaussian <- function(y, obs, idt, priors,
threading, prior_distr, default, ...) {
if (threading) {
default$fun_call_args <- cs(default$fun_call_args, glue::glue("sigma_{y}"))
}
model_text <- paste_rows(
glue::glue("sigma_{y} ~ {prior_distr$sigma_prior_distr};"),
model_lines_default(y, obs, idt, threading, default, ...),
.indent = idt(c(1, 0)),
.parse = FALSE
)
paste_rows(priors, model_text, .parse = FALSE)
}
model_lines_multinomial <- function(cvars, cgvars, idt, backend,
threading, ...) {
stopifnot_(
stan_version(backend) >= "2.24",
c(
"Multinomial family is not supported for
this version of {.pkg {backend}}.",
`i` = "Please install a newer version of {.pkg {backend}}."
)
)
cgvars$priors <- lapply(
cgvars$y[-1L],
function(s) {
cvars[[s]]$y <- s
cvars[[s]]$prior_distr <- cgvars$prior_distr[[s]]
do.call(prior_lines, c(cvars[[s]], idt = idt))
}
)
cgvars$categories <- cgvars$y
cgvars$y <- cgvars$y_cg
cgvars$multinomial <- TRUE
do.call(
model_lines_categorical,
args = c(cgvars, idt = idt, threading = threading)
)
}
model_lines_mvgaussian <- function(cvars, cgvars, idt, backend, threading,
...) {
y <- cgvars$y
y_cg <- cgvars$y_cg
priors <- character(length(y))
for (i in seq_along(y)) {
yi <- y[i]
args <- c(cvars[[i]], idt = idt)
priors[i] <- do.call(prior_lines, args = args)
priors[i] <- paste_rows(
priors[i],
glue::glue("sigma_{y[i]} ~ {cvars[[i]]$sigma_prior_distr};"),
.indent = idt(c(0, 1)),
.parse = FALSE
)
}
if (threading) {
fun_args <- character(length(y))
has_X <- FALSE
for (i in seq_along(y)) {
yi <- y[i]
has_X <- has_X ||
cvars[[i]]$has_fixed || cvars[[i]]$has_varying || cvars[[i]]$has_random
fun_args[i] <- glue::glue(cs(c(
onlyif(
cvars[[i]]$has_fixed_intercept || cvars[[i]]$has_varying_intercept,
"alpha_{yi}"
),
onlyif(cvars[[i]]$has_random, c("J_random_{yi}", "K_random_{yi}")),
onlyif(
cvars[[i]]$has_random || cvars[[i]]$has_random_intercept,
"nu_{yi}"
),
onlyif(cvars[[i]]$has_lfactor, c("lambda_{yi}", "psi_{yi}")),
onlyif(cvars[[i]]$has_fixed, c("J_fixed_{yi}", "beta_{yi}")),
onlyif(cvars[[i]]$has_varying, c("J_varying_{yi}", "delta_{yi}")),
"sigma_{yi}"
)))
}
fun_args <- glue_cs(
ifelse_(
cgvars$has_missing,
c("obs_{y_cg}", "n_obs_{y_cg}"),
"N"
),
"y_{y_cg}",
"O_{y_cg}",
fun_args,
onlyif(has_X, "X"),
"L_{y_cg}"
)
seq1T <- ifelse_(
cgvars$has_fully_missing,
"t_obs_{y_cg}",
"seq1T"
)
likelihood <- glue::glue(
"target += reduce_sum(mvgaussian_loglik_{y_cg}_lpmf, {seq1T}, grainsize,",
" {fun_args});"
)
} else {
likelihood <- loglik_lines_mvgaussian(idt, cvars, cgvars, backend,
threading)
}
model_text <- paste_rows(
glue::glue("L_{y_cg} ~ {cgvars$prior_distr$L_prior_distr};"),
likelihood,
.indent = idt(c(1, 0)),
.parse = FALSE
)
paste_rows(priors, model_text, .parse = FALSE)
}
model_lines_negbin <- function(y, obs, idt, priors,
threading, prior_distr, default, ...) {
if (threading) {
default$fun_call_args <- cs(default$fun_call_args, glue::glue("phi_{y}"))
}
model_text <- paste_rows(
glue::glue("phi_{y} ~ {prior_distr$phi_prior_distr};"),
model_lines_default(y, obs, idt, threading, default, ...),
.indent = idt(c(1, 0)),
.parse = FALSE
)
paste_rows(priors, model_text, .parse = FALSE)
}
model_lines_poisson <- function(y, obs, idt, priors,
threading, default, ...) {
paste_rows(
priors,
model_lines_default(y, obs, idt, threading, default, ...),
.parse = FALSE
)
}
model_lines_student <- function(y, obs, idt, priors,
threading, prior_distr, default, ...) {
if (threading) {
default$fun_call_args <- cs(
default$fun_call_args, glue::glue("sigma_{y}"), glue::glue("phi_{y}")
)
}
model_text <- paste_rows(
glue::glue("sigma_{y} ~ {prior_distr$sigma_prior_distr};"),
glue::glue("phi_{y} ~ {prior_distr$phi_prior_distr};"),
model_lines_default(y, obs, idt, threading, default, ...),
.indent = idt(c(1, 1, 0)),
.parse = FALSE
)
paste_rows(priors, model_text, .parse = FALSE)
}
# Generated quantities block ----------------------------------------------
generated_quantities_lines_default <- function() {
""
}
generated_quantities_lines_bernoulli <- function(...) {
""
}
generated_quantities_lines_beta <- function(...) {
""
}
generated_quantities_lines_binomial <- function(...) {
""
}
generated_quantities_lines_categorical <- function(...) {
""
}
generated_quantities_lines_cumulative <- function(...) {
""
}
generated_quantities_lines_exponential <- function(...) {
""
}
generated_quantities_lines_gamma <- function(...) {
""
}
generated_quantities_lines_gaussian <- function(...) {
""
}
generated_quantities_lines_multinomial <- function(...) {
""
}
generated_quantities_lines_mvgaussian <- function(y, y_cg, idt, ...) {
O <- length(y)
paste_rows(
"matrix[O_{y_cg},O_{y_cg}] corr_matrix_{y_cg} = ",
"multiply_lower_tri_self_transpose(L_{y_cg});",
"vector[{(O * (O - 1L)) %/% 2L}] corr_{y_cg};",
"for (k in 1:O_{y_cg}) {{",
"for (j in 1:(k - 1)) {{",
"corr_{y_cg}[choose(k - 1, 2) + j] = corr_matrix_{y_cg}[j, k];",
"}}",
"}}",
.indent = idt(c(1, 2, 1, 1, 2, 3, 2, 1))
)
}
generated_quantities_lines_negbin <- function(...) {
""
}
generated_quantities_lines_poisson <- function(...) {
""
}
generated_quantities_lines_student <- function(...) {
""
}
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.