Nothing
#' Create a model component object for a generic random effects component in the linear predictor
#'
#' This function is intended to be used on the right hand side of the \code{formula} argument to
#' \code{\link{create_sampler}} or \code{\link{generate_data}}.
#'
#' @export
#' @param formula a model formula specifying the effects that vary over the levels of
#' the factor variable(s) specified by argument \code{factor}. Defaults to \code{~1},
#' corresponding to random intercepts. If \code{X} is specified \code{formula} is ignored.
#' Variable names are looked up in the data frame passed as \code{data} argument to
#' \code{\link{create_sampler}} or \code{\link{generate_data}}, or in \code{environment(formula)}.
#' @param factor a formula with factors by which the effects specified in the \code{formula}
#' argument vary. Often only one such factor is needed but multiple factors are allowed so that
#' interaction terms can be modeled conveniently. The formula must take the form
#' \code{ ~ f1(fac1, ...) * f2(fac2, ...) ...}, where
#' \code{fac1, fac2} are factor variables and \code{f1, f2} determine the
#' correlation structure assumed between levels of each factor, and the \code{...} indicate
#' that for some correlation types further arguments can be passed. Correlation structures
#' currently supported include \code{iid} for independent identically distributed effects,
#' \code{RW1} and \code{RW2} for random walks of first or second order over the factor levels,
#' \code{AR1} for first-order autoregressive effects, \code{season} for seasonal effects,
#' \code{spatial} for spatial (CAR) effects and \code{custom} for supplying a custom
#' precision matrix corresponding to the levels of the factor. For further details about
#' the correlation structures, and further arguments that can be passed, see
#' \code{\link{correlation}}. Argument \code{factor} is ignored if \code{X} is specified.
#' The factor variables are looked up in the data frame passed as \code{data} argument to
#' \code{\link{create_sampler}} or \code{\link{generate_data}}, or in \code{environment(formula)}.
#' @param remove.redundant whether redundant columns should be removed from the model matrix
#' associated with \code{formula}. Default is \code{FALSE}.
#' @param drop.empty.levels whether to remove factor levels without observations.
#' @param X A (possibly sparse) design matrix. This can be used instead of \code{formula} and \code{factor}.
#' @param var the (co)variance structure among the varying effects defined by \code{formula}
#' over the levels of the factors defined by \code{factor}.
#' The default is \code{"unstructured"}, meaning that a full covariance matrix
#' parameterization is used. For uncorrelated effects with unequal variances use
#' \code{var="diagonal"}. For uncorrelated effects with equal variances use \code{var="scalar"}.
#' In the case of a single varying effect there is no difference between these choices.
#' @param prior the prior specification for the variance parameters of the random effects.
#' These can currently be specified by a call to \code{\link{pr_invwishart}} in case
#' \code{var="unstructured"} or by a call to \code{\link{pr_invchisq}} otherwise.
#' See the documentation of those prior specification functions for more details.
#' @param Q0 precision matrix associated with \code{formula}. This can only be used in
#' combination with \code{var="scalar"}.
#' @param PX whether parameter expansion should be used. Default is \code{TRUE}, which
#' applies parameter expansion with default options. Alternative options can be specified
#' by supplying a list with one or more of the following components:
#' \describe{
#' \item{vector}{whether a redundant multiplicative expansion parameter is used for each varying effect
#' specified by \code{formula}. The default is \code{TRUE} except when \code{var="scalar"}.
#' If \code{FALSE} a single redundant multiplicative parameter is used.}
#' \item{data.scale}{whether the data level scale is used as a variance factor for the expansion
#' parameters. Default is \code{TRUE}.}
#' \item{mu0}{location (vector) parameter for parameter expansion. By default \code{0}.}
#' \item{Q0}{precision (matrix) parameter for parameter expansion. Default is the identity matrix.}
# \item{sparse}{UNDOCUMENTED}
#' }
#' @param GMRFmats list of incidence/precision/constraint matrices. This can be specified
#' as an alternative to \code{factor}. It should be a list such as that returned
#' by \code{\link{compute_GMRF_matrices}}. Can be used together with argument \code{X}
#' as a flexible alternative to \code{formula} and \code{factor}.
#' @param priorA prior distribution for scale factors at the variance scale associated with \code{QA}.
#' In case of IGMRF models the scale factors correspond to the innovations.
#' The default \code{NULL} means not to use any local scale factors.
#' A prior can currently be specified using \code{\link{pr_invchisq}} or \code{\link{pr_exp}}.
#' @param Leroux this option alters the precision matrix determined by \code{factor} by taking a
#' weighted average of it with the identity matrix. If \code{TRUE} the model gains an additional parameter,
#' the 'Leroux' parameter, being the weight of the original, structured, precision matrix in the weighted
#' average. By default a uniform prior for the weight and a uniform Metropolis-Hastings proposal density
#' are employed. This default can be changed by supplying a list with elements a, b, and a.star, b.star,
#' implying a beta(a, b) prior and a beta(a.star, b.star) independence proposal density. A third option is
#' to supply a single number between 0 and 1, which is then used as a fixed value for the Leroux parameter.
#' @param R0 an optional equality restriction matrix acting on the coefficients defined by \code{formula}, for each
#' level defined by \code{factor}. If c is the number of restrictions, \code{R0} is a
#' q0 x c matrix where q0 is the number of columns of the design matrix derived
#' from \code{formula}. Together with \code{RA} it defines the set of equality constraints
#' to be imposed on the vector of coefficients. Only allowed in combination with \code{var="scalar"}.
#' @param RA an optional equality restriction matrix acting on the coefficients defined by \code{factor},
#' for each effect defined by \code{formula}. If c is the number of restrictions, \code{RA} is a
#' l x c matrix where l is the number of levels defined by \code{factor}.
#' Together with \code{R0} this defines the set of equality constraints to be imposed on the vector
#' of coefficients.
#' If \code{constr=TRUE}, additional constraints are imposed, corresponding to the
#' null-vectors of the singular precision matrix in case of an intrinsic Gaussian Markov Random Field.
#' @param constr whether constraints corresponding to the null-vectors of the precision matrix
#' are to be imposed on the vector of coefficients. By default this is \code{TRUE} for
#' improper or intrinsic GMRF model components, i.e. components with a singular precision matrix
#' such as random walks or CAR spatial components.
#' @param S0 an optional inequality restriction matrix acting on the coefficients defined by \code{formula}, for each
#' level defined by \code{factor}. If c is the number of restrictions, \code{S0} is a
#' q0 x c matrix where q0 is the number of columns of the design matrix derived
#' from \code{formula}. Together with \code{SA} it defines the set of inequality constraints
#' to be imposed on the vector of coefficients.
# TODO does this work, also for var != "scalar"?
#' @param SA an optional inequality restriction matrix acting on the coefficients defined by \code{factor},
#' for each effect defined by \code{formula}. If c is the number of restrictions, \code{SA} is a
#' l x c matrix where l is the number of levels defined by \code{factor}.
#' Together with \code{S0} this defines the set of constraints to be imposed on the vector
#' of coefficients.
# TODO R,S instead of R0,RA and S0,SA, and rhs r,s
#' @param formula.gl a formula of the form \code{~ glreg(...)} for group-level predictors
#' around which the random effect component is hierarchically centered.
#' See \code{\link{glreg}} for details.
#' @param name the name of the model component. This name is used in the output of the MCMC simulation
#' function \code{\link{MCMCsim}}. By default the name will be 'gen' with the number of the model term attached.
#' @param sparse whether the model matrix associated with \code{formula} should be sparse.
#' The default is based on a simple heuristic based on storage size.
#' @param debug if \code{TRUE} a breakpoint is set at the beginning of the posterior
#' draw function associated with this model component. Mainly intended for developers.
#' @return An object with precomputed quantities and functions for sampling from
#' prior or conditional posterior distributions for this model component. Intended
#' for internal use by other package functions.
#' @references
#' J. Besag and C. Kooperberg (1995).
#' On Conditional and Intrinsic Autoregression.
#' Biometrika 82(4), 733-746.
#'
#' C.M. Carvalho, N.G. Polson and J.G. Scott (2010).
#' The horseshoe estimator for sparse signals.
#' Biometrika 97(2), 465-480.
#'
#' L. Fahrmeir, T. Kneib and S. Lang (2004).
#' Penalized Structured Additive Regression for Space-Time Data:
#' a Bayesian Perspective.
#' Statistica Sinica 14, 731-761.
#'
#' A. Gelman (2006).
#' Prior distributions for variance parameters in hierarchical models.
#' Bayesian Analysis 1(3), 515-533.
#'
#' A. Gelman, D.A. Van Dyk, Z. Huang and W.J. Boscardin (2008).
#' Using Redundant Parameterizations to Fit Hierarchical Models.
#' Journal of Computational and Graphical Statistics 17(1), 95-122.
#'
#' B. Leroux, X. Lei and N. Breslow (1999).
#' Estimation of Disease Rates in Small Areas: A New Mixed Model for Spatial Dependence.
#' In M. Halloran and D. Berry (Eds.), Statistical Models in Epidemiology,
#' the Environment and Clinical Trials, 135-178.
#'
#' T. Park and G. Casella (2008).
#' The Bayesian Lasso.
#' Journal of the American Statistical Association 103(482), 681-686.
#'
#' H. Rue and L. Held (2005).
#' Gaussian Markov Random Fields.
#' Chapman & Hall/CRC.
gen <- function(formula = ~ 1, factor=NULL,
remove.redundant=FALSE, drop.empty.levels=FALSE, X=NULL,
var=NULL, prior=NULL, Q0=NULL, PX=TRUE,
GMRFmats=NULL, priorA=NULL, Leroux=FALSE,
R0=NULL, RA=NULL, constr=NULL,
S0=NULL, SA=NULL,
formula.gl=NULL,
name="", sparse=NULL, debug=FALSE) {
e <- sys.frame(-2L)
type <- "gen" # for generic (random effects)
if (name == "") stop("missing model component name")
# check supplied var component; if NULL leave it to be filled in later
if (!is.null(var)) var <- match.arg(var, c("unstructured", "diagonal", "scalar"))
if (!is.null(factor) && !inherits(factor, "formula")) stop("element 'factor' of a model component must be a formula")
if (is.list(Leroux)) {
if (!all(names(Leroux) %in% c("a", "b", "a.star", "b.star"))) stop("invalid Leroux model component parameter list")
if (is.null(Leroux$a)) Leroux$a <- 1
if (is.null(Leroux$b)) Leroux$b <- 1
if (is.null(Leroux$a.star)) Leroux$a.star <- 1
if (is.null(Leroux$b.star)) Leroux$b.star <- 1
if (any(unlist(Leroux, use.names=FALSE) < 0)) stop("parameters of beta distribution cannot be negative")
Leroux_type <- "general"
} else {
if (is.numeric(Leroux)) {
if (length(Leroux) != 1L) stop("only a single number can be specified for a Leroux parameter")
if (Leroux < 0 || Leroux > 1) stop("Leroux parameter must be between 0 and 1")
Leroux_type <- "fixed"
} else {
if (!is.logical(Leroux) || (length(Leroux) != 1L)) stop("wrong input for 'Leroux'")
Leroux_type <- if (Leroux) "default" else "none"
}
}
Leroux_update <- any(Leroux_type == c("general", "default"))
varnames <- factor.cols.removed <- NULL
if (is.null(X)) {
if (e$family$family == "multinomial") {
edat <- new.env(parent = .GlobalEnv)
for (k in seq_len(e$Km1)) {
edat$cat_ <- factor(rep.int(e$cats[k], e$n0), levels=e$cats[-length(e$cats)])
if (k == 1L) {
X0 <- model_matrix(formula, e$data, sparse=sparse, enclos=edat)
XA <- compute_XA(factor, e$data, enclos=edat)
} else {
X0 <- rbind(X0, model_matrix(formula, e$data, sparse=sparse, enclos=edat))
if (!is.null(XA)) XA <- rbind(XA, compute_XA(factor, e$data, enclos=edat))
}
}
rm(edat)
} else {
X0 <- model_matrix(formula, e$data, sparse=sparse)
XA <- compute_XA(factor, e$data)
}
if (remove.redundant) X0 <- remove_redundancy(X0)
varnames <- colnames(X0)
if (!is.null(XA)) {
if (drop.empty.levels) {
factor.cols.removed <- which(zero_col(XA))
if (length(factor.cols.removed)) XA <- XA[, -factor.cols.removed, drop=FALSE]
}
X <- combine_X0_XA(X0, XA)
} else {
X <- X0
}
rm(X0, XA)
}
if (nrow(X) != e$n) stop("design matrix with incompatible number of rows")
e$coef.names[[name]] <- colnames(X)
X <- economizeMatrix(X, strip.names=TRUE, check=TRUE)
q <- ncol(X)
in_block <- any(name == unlist(e$block, use.names=FALSE))
self <- environment()
# fast GMRF prior currently only for IGMRF without user-defined constraints
fastGMRFprior <- is.null(priorA) && allow_fastGMRFprior(self) && is.null(RA) && is.null(R0) && is.null(SA) && is.null(S0)
# by default, IGMRF constraints are imposed, unless the GMRF is proper
# NB constr currently only refers to QA, not Q0
if (is.null(constr)) constr <- !is_proper_GMRF(self)
if (is.null(GMRFmats)) {
if (e$family$family == "multinomial") {
edat <- new.env(parent = .GlobalEnv)
edat$cat_ <- e$cats[-length(e$cats)] # K-1 categories; TODO for general GMRF prediction need all K categories?
} else {
edat <- .GlobalEnv
}
GMRFmats <- compute_GMRF_matrices(factor, e$data,
D=fastGMRFprior || !is.null(priorA),
Q=!(fastGMRFprior || !is.null(priorA)),
R=constr, sparse=if (in_block) TRUE else NULL,
cols2remove=factor.cols.removed, drop.zeros=TRUE, enclos=edat, n.parent=2L
)
rm(edat)
}
if (fastGMRFprior || !is.null(priorA)) { # compute lD x l matrix DA
DA <- GMRFmats$D
l <- ncol(DA)
if (is.null(priorA))
QA <- economizeMatrix(crossprod(DA), sparse=if (in_block) TRUE else NULL, symmetric=TRUE, check=TRUE)
} else { # compute precision matrix QA only
QA <- economizeMatrix(GMRFmats$Q, sparse=if (in_block) TRUE else NULL, symmetric=TRUE, check=TRUE)
l <- nrow(QA)
}
if (q %% l != 0L) stop("incompatible dimensions of design and precision matrices")
q0 <- q %/% l # --> q = q0 * l
if (!is.null(priorA)) { # scaled precision matrix QA = DA' QD DA with QD modeled
lD <- nrow(DA)
name_omega <- paste0(name, "_omega")
if (Leroux_type != "none") stop("not implemented: scaled Leroux type correlation")
switch(priorA$type,
invchisq = {
priorA$init(lD, !e$prior.only)
df.data.omega <- q0 # TODO is this always the correct value?
},
exp = priorA$init(lD, !e$prior.only)
)
}
if (q0 == 1L) {
var <- "scalar" # single varying effect
} else if (is.null(var)) {
if (l == 1L)
var <- "diagonal" # single group, default ARD prior
else if (q0 <= 500L)
var <- "unstructured"
else {
warn("model component '", name, "': default variance structure changed
to 'diagonal' because of the large number ", q0, " of varying effects.
If you instead intend to model a full covariance matrix among all varying effects,
use var='unstructured', though it may be very slow.")
var <- "diagonal"
}
}
PX.defaults <- list(mu0=0, Q0=1, data.scale=TRUE, vector=var != "scalar", sparse=NULL)
if (is.list(PX)) {
usePX <- TRUE
if (!all(names(PX) %in% names(PX.defaults))) stop("invalid 'PX' options list")
PX <- modifyList(PX.defaults, PX)
} else {
if (is.logical(PX) && length(PX) == 1L) {
usePX <- PX
PX <- PX.defaults
} else
stop("wrong input for 'PX'")
}
rm(PX.defaults)
if (usePX) {
if (is.null(PX$sparse)) PX$sparse <- (q0 > 10L)
if (q0 == 1L) PX$vector <- FALSE
if (!PX$vector) PX$sparse <- FALSE
PX$dim <- if (PX$vector) q0 else 1L
if (PX$vector) {
if (length(PX$mu0) == 1L) PX$mu0 <- rep.int(PX$mu0, q0)
if (is.numeric(PX$Q0)) {
if (length(PX$Q0) == 1L) PX$Q0 <- rep.int(PX$Q0, q0)
PX$Q0 <- Cdiag(PX$Q0)
}
PX$Q0 <- economizeMatrix(PX$Q0, sparse=PX$sparse, symmetric=TRUE, check=TRUE)
base_tcrossprod <- base::tcrossprod # faster access (Matrix promotes tcrossprod to S4 generic)
}
if (length(PX$mu0) != PX$dim) stop("wrong length for 'PX$mu0'")
PX$Qmu0 <- PX$Q0 %m*v% PX$mu0
name_xi <- paste0(name, "_xi_") # trailing '_' --> not stored by MCMCsim even if store.all=TRUE
}
if (Leroux_type != "none") {
if (isUnitDiag(QA)) warn("Leroux extension for iid random effects has no effect")
if (Leroux_type == "fixed") {
# compute the weighted average once and for all
QA <- economizeMatrix(Leroux * QA + (1 - Leroux) * CdiagU(l), symmetric=TRUE, sparse=if (in_block) TRUE else NULL)
} else {
# use a sparse (sum) template
idL <- CdiagU(l)
mat_sum_Leroux <- make_mat_sum(M1=QA, M2=idL)
# base the determinant template on 0.5*(QA + I) to ensure it is non-singular
det <- make_det(mat_sum_Leroux(QA, idL, 0.5, 0.5))
}
}
# construct equality restriction matrix
# TODO if R is supplied, it is assumed to be the full constraint matrix...
# in that case the derivation from R0 and RA can be skipped
if (!is.null(R0)) {
if (var != "scalar") stop("R0 constraint matrix only allowed if var='scalar'")
if (nrow(R0) != q0) stop("incompatible matrix R0")
}
# if a matrix 'RA' is provided by the user, use these constraints in addition to possible GMRF constraints
if (is.null(RA)) {
RA <- GMRFmats$R
} else {
if (nrow(RA) != l) stop("incompatible matrix RA")
if (!is.null(GMRFmats$R)) {
RA <- economizeMatrix(cbind(RA, GMRFmats$R), allow.tabMatrix=FALSE, check=TRUE)
RA <- remove_redundancy(RA) # combination of user-supplied and IGMRF constraints may contain redundant columns
}
}
R <- switch(paste0(is.null(RA), is.null(R0)),
TRUETRUE = NULL,
TRUEFALSE = kronecker(CdiagU(l), R0),
FALSETRUE = kronecker(RA, CdiagU(q0)),
FALSEFALSE = cbind(kronecker(CdiagU(l), R0), kronecker(RA, CdiagU(q0)))
)
if (!is.null(R)) {
# tabMatrix doesn't work yet because of lack of solve method
R <- economizeMatrix(R, allow.tabMatrix=FALSE, check=TRUE)
}
if (!is.null(R0) && !is.null(RA)) R <- remove_redundancy(R)
rm(R0, GMRFmats)
# construct inequality restriction matrix (TODO S instead of S0, SA)
if (!is.null(S0) && nrow(S0) != q0) stop("incompatible matrix S0")
if (!is.null(SA) && nrow(SA) != l) stop("incompatible matrix SA")
S <- switch(paste0(is.null(SA), is.null(S0)),
TRUETRUE = NULL,
TRUEFALSE = kronecker(CdiagU(l), S0),
FALSETRUE = kronecker(SA, CdiagU(q0)),
FALSEFALSE = cbind(kronecker(CdiagU(l), S0), kronecker(SA, CdiagU(q0)))
)
rm(S0, SA)
if (is.null(prior)) {
prior <- switch(var,
unstructured = pr_invwishart(df=q0+1, scale=diag(q0)),
diagonal=, scalar = pr_invchisq(if (usePX) 1 else 1e-3, 1)
)
}
if (all(prior$type != if (var == "unstructured") "invwishart" else c("invchisq", "exp"))) stop("unsupported prior")
if (is.list(prior$df)) stop("not supported: modeled df for random effect variance prior")
switch(prior$type,
invwishart = prior$init(q0),
invchisq = {
if (var == "scalar") {
prior$init(1L, !e$prior.only)
df.data <- if (is.null(R)) q else q - ncol(R)
} else { # var "diagonal"
prior$init(q0, !e$prior.only)
# df based on number of unconstrained coefficients
df.data <- if (is.null(RA)) l else l - ncol(RA)
}
},
exp = {
if (var == "scalar") {
prior$init(1L, !e$prior.only)
ppar <- if (is.null(R)) q else q - ncol(R)
} else { # var "diagonal"
prior$init(q0, !e$prior.only)
ppar <- if (is.null(RA)) l else l - ncol(RA)
}
ppar <- 1 - ppar/2
}
)
if (var == "scalar") {
# also check given precision matrix Q0, if any
if (!is.null(Q0)) {
Q0 <- economizeMatrix(Q0, symmetric=TRUE, vec.diag=TRUE, check=TRUE)
if (is.vector(Q0)) {
if (all(length(Q0) != c(1L, q0))) stop("incompatible 'Q0'")
} else {
if (!identical(dim(Q0), c(q0, q0))) stop("incompatible 'Q0'")
}
if (is.vector(Q0) && all(Q0 == 1)) Q0 <- NULL # identity Q0, can be ignored
}
} else {
if (var == "unstructured") {
# TODO add rprior and draw methods to pr_invwishart
df <- prior$df + l
if (!is.null(RA)) df <- df - ncol(RA)
}
if (!is.null(Q0)) warn("'Q0' ignored")
}
rm(RA)
gl <- !is.null(formula.gl)
if (gl) { # group-level covariates
if (!inherits(formula.gl, "formula")) stop("'formula.gl' must be a formula")
vars <- as.list(attr(terms(formula.gl), "variables"))[-1L]
if (length(vars) != 1L || as.character(vars[[1L]][[1L]]) != "glreg") stop("'formula.gl' should be a formula with a single 'glreg' component")
name_gl <- paste0(name, "_gl")
glp <- vars[[1L]]
glp$name <- name_gl
gl <- TRUE
rm(vars)
}
if (e$modeled.Q) {
if (gl) {
# ensure that XX is always sparse because we need a sparse block diagonal template in this case
X <- economizeMatrix(X, sparse=TRUE) # --> crossprod_sym(X, Q) also sparse
}
XX <- crossprod_sym(X, crossprod_sym(Diagonal(x=runif(e$n, 0.9, 1.1)), e$Q0))
} else {
XX <- economizeMatrix(crossprod_sym(X, e$Q0), symmetric=TRUE)
}
# for both memory and speed efficiency define unit_Q case (random intercept and random slope with scalar var components, no constraints)
unit_Q <- is.null(priorA) && isUnitDiag(QA) && (var == "scalar" && is.null(Q0)) && is.null(R)
name_sigma <- paste0(name, "_sigma")
if (var == "unstructured") name_rho <- paste0(name, "_rho")
if (q0 > 1L) { # add labels for sigma, rho, xi to e$coef.names
if (var != "scalar" || (usePX && PX$vector)) {
e$coef.names[[name_sigma]] <- varnames
if (var == "unstructured")
e$coef.names[[name_rho]] <- outer(varnames, varnames, FUN=paste, sep=":")[upper.tri(diag(q0))]
if (usePX && PX$vector)
e$coef.names[[name_xi]] <- varnames
}
if (gl) {
if (is.null(e$coef.names[[name_gl]]))
e$coef.names[[name_gl]] <- varnames
else
e$coef.names[[name_gl]] <- as.vector(outer(varnames, e$coef.names[[name_gl]], FUN=paste, sep=":"))
}
}
rm(varnames)
linpred <- function(p) X %m*v% p[[name]]
make_predict <- function(newdata=NULL, Xnew, verbose=TRUE) {
if (is.null(newdata)) {
Xnew <- economizeMatrix(Xnew, strip.names=TRUE, check=TRUE)
if (ncol(Xnew) != q) stop("wrong number of columns for Xnew matrix of component ", name)
linpred <- function(p) Xnew %m*v% p[[name]]
} else {
if (e$family$family == "multinomial") {
edat <- new.env(parent = .GlobalEnv)
for (k in seq_len(e$Km1)) {
edat$cat_ <- factor(rep.int(e$cats[k], nrow(newdata)), levels=e$cats[-length(e$cats)])
if (k == 1L) {
X0 <- model_matrix(formula, data=newdata, sparse=sparse, enclos=edat)
XA <- compute_XA(factor, newdata, enclos=edat)
} else {
X0 <- rbind(X0, model_matrix(formula, data=newdata, sparse=sparse, enclos=edat))
if (!is.null(XA)) XA <- rbind(XA, compute_XA(factor, newdata, enclos=edat))
}
}
rm(edat)
} else {
X0 <- model_matrix(formula, newdata, sparse=sparse)
XA <- compute_XA(factor, newdata)
}
if (!is.null(XA))
Xnew <- combine_X0_XA(X0, XA)
else
Xnew <- X0
rm(X0, XA)
if (unit_Q) {
# in this case we allow oos random effects and mixed cases by matching training and test levels
m <- match(colnames(Xnew), e$coef.names[[name]])
qnew <- ncol(Xnew)
Xnew <- economizeMatrix(Xnew, sparse=sparse, strip.names=TRUE, check=TRUE)
if (all(is.na(m))) {
# all random effects in Xnew are new
if (verbose) message("model component '", name, "': all categories in 'newdata' are out-of-sample categories")
linpred <- function(p) Xnew %m*v% Crnorm(qnew, sd = p[[name_sigma]])
} else if (anyNA(m)) {
# both existing and new levels in newdata
q_unmatched <- sum(is.na(m))
if (verbose) message("model component '", name, "': ", q_unmatched, " out of ", qnew, " categories in 'newdata' are out-of-sample categories")
I_matched <- which(!is.na(m))
I_unmatched <- setdiff(seq_len(qnew), I_matched)
I_coef <- m[!is.na(m)]
# TODO next function in C++ (no need to initialize)
linpred <- function(p) {
v <- vector("double", qnew)
v[I_matched] <- p[[name]][I_coef] # TODO distinguish case where I_coef = 1:q
v[I_unmatched] <- Crnorm(q_unmatched, sd=p[[name_sigma]])
Xnew %m*v% v
}
} else {
# all columns of Xnew correspond to existing levels
if (identical(m, seq_len(q))) {
linpred <- function(p) Xnew %m*v% p[[name]]
} else {
I_coef <- m
linpred <- function(p) Xnew %m*v% p[[name]][I_coef]
}
}
rm(m)
} else {
# generic case: here we expect the same levels in training and test sets
# for prediction do not (automatically) remove redundant columns!
if (remove.redundant || drop.empty.levels)
Xnew <- Xnew[, e$coef.names[[name]], drop=FALSE]
if (ncol(Xnew) != q) stop("'newdata' yields ", ncol(Xnew), " predictor column(s) for model term '", name, "' versus ", q, " originally")
Xnew <- economizeMatrix(Xnew, sparse=sparse, strip.names=TRUE, check=TRUE)
linpred <- function(p) Xnew %m*v% p[[name]]
}
}
rm(newdata, verbose)
linpred
}
if (gl) { # group-level covariates
glp <- eval(glp)
sparse_template(self, update.XX=e$modeled.Q, prior.only=e$prior.only)
if (!in_block && !e$prior.only) glp$R <- NULL
} else {
sparse_template(self, update.XX=e$modeled.Q, prior.only=e$prior.only)
}
# BEGIN rprior function
# draw from prior; draws are independent and stored in p
rprior <- function(p) {}
if (usePX) {
if (PX$data.scale && !e$sigma.fixed)
rprior <- add(rprior, quote(xi <- PX$mu0 + drawMVN_Q(PX$Q0, sd=p[["sigma_"]])))
else
rprior <- add(rprior, quote(xi <- PX$mu0 + drawMVN_Q(PX$Q0)))
rprior <- add(rprior, bquote(p[[.(name_xi)]] <- xi))
rprior <- add(rprior, quote(inv_xi <- 1/xi))
} else {
rprior <- add(rprior, quote(inv_xi <- 1))
}
if (var == "unstructured") {
if (is.list(prior$scale)) {
psi0 <- prior$scale$df / prior$scale$scale
# TODO C++ version of dense diagonal matrix creation
if (prior$scale$common)
rprior <- add(rprior, quote(psi0 <- diag(rep.int(rchisq_scaled(1L, prior$scale$df, psi=psi0), q0))))
else
rprior <- add(rprior, quote(psi0 <- diag(rchisq_scaled(q0, prior$scale$df, psi=psi0))))
} else {
psi0 <- prior$scale # matrix
}
rprior <- add(rprior, quote(Qraw <- rWishart(1L, df=prior$df, Sigma=inverseSPD(psi0))[,,1L]))
if (usePX && PX$vector)
rprior <- add(rprior, quote(inv_xi_qform <- base_tcrossprod(inv_xi)))
else
rprior <- add(rprior, quote(inv_xi_qform <- inv_xi^2))
rprior <- add(rprior, quote(Qv <- Qraw * inv_xi_qform)) # use Qv to distinguish from data-level precision Q
# convert precision matrix to standard errors and correlations
names_se_cor <- c(name_sigma, name_rho)
rprior <- add(rprior, quote(p[names_se_cor] <- prec2se_cor(Qv)))
} else {
rprior <- add(rprior, quote(Qraw <- 1 / prior$rprior()))
rprior <- add(rprior, quote(Qv <- Qraw * inv_xi^2))
rprior <- add(rprior, bquote(p[[.(name_sigma)]] <- sqrt(1/Qv)))
}
if (Leroux_update) {
name_Leroux <- paste0(name, "_Leroux")
if (Leroux_type == "default")
rprior <- add(rprior, bquote(p[[.(name_Leroux)]] <- runif(1L)))
else
rprior <- add(rprior, bquote(p[[.(name_Leroux)]] <- rbeta(1L, Leroux$a, Leroux$b)))
rprior <- add(rprior, bquote(QA <- mat_sum_Leroux(QA, idL, p[[.(name_Leroux)]], 1 - p[[.(name_Leroux)]])))
}
if (gl) { # draw group-level predictor coefficients
rprior <- add(rprior, bquote(p[[.(name_gl)]] <- glp$prior$rprior(p)))
}
if (!is.null(priorA)) {
switch(priorA$type,
invchisq = {
if (is.list(priorA$df)) {
name_df <- paste0(name, "_df")
rprior <- add(rprior, bquote(p[[.(name_df)]] <- priorA$rprior_df()))
rprior <- add(rprior, bquote(p[[.(name_omega)]] <- priorA$rprior(p[[.(name_df)]])))
} else {
rprior <- add(rprior, bquote(p[[.(name_omega)]] <- priorA$rprior()))
}
},
exp = {
rprior <- add(rprior, bquote(p[[.(name_omega)]] <- priorA$rprior()))
}
)
rprior <- add(rprior, bquote(QA <- crossprod_sym(DA, 1/p[[.(name_omega)]])))
}
# draw coefficient from prior
if (unit_Q) { # slightly faster alternative for random intercept models (or random slope with scalar variance)
rprior <- add(rprior, bquote(p[[.(name)]] <- Crnorm(.(q), sd=p[[.(name_sigma)]])))
} else {
if (fastGMRFprior) {
rGMRFprior <- NULL # to be created at the first occasion rprior is called
rprior <- add(rprior, quote(if (is.null(rGMRFprior)) setup_priorGMRFsampler(self, Qv)))
rprior <- add(rprior, bquote(p[[.(name)]] <- rGMRFprior(Qv)))
} else {
if (q0 == 1L)
rprior <- add(rprior, quote(Q <- Qv * QA))
else
rprior <- add(rprior, quote(Q <- kron_prod(QA, Qv)))
rMVNprior <- NULL # to be created at the first occasion rprior is called
rprior <- add(rprior, quote(if (is.null(rMVNprior)) setup_priorMVNsampler(self, Q)))
rprior <- add(rprior, bquote(p[[.(name)]] <- rMVNprior(p, Q)))
}
}
if (gl) {
# add U alpha, where U = glp$X x I_q0 and alpha are the gl effects
#rprior <- add(rprior, quote(browser()))
if (glp$p0 == 1L)
rprior <- add(rprior, bquote(p[[.(name)]] <- p[[.(name)]] + glp$X %m*m% matrix(p[[.(name_gl)]], 1L)))
else
rprior <- add(rprior, quote(stop("TBI: include multiple group-level effect centering in generation of random effects")))
}
rprior <- add(rprior, quote(p))
# END rprior function
if (e$prior.only) return(self) # no need for posterior draw function in this case
if (!is.null(priorA) || is.list(prior$scale) || Leroux_update) {
# store Qraw --> no need to recompute at next MCMC iteration
name_Qraw <- paste0(name, "_Qraw_")
}
# BEGIN draw function
draw <- function(p) {}
if (debug) draw <- add(draw, quote(browser()))
if (e$single.block && length(e$mod) == 1L) {
# optimalization in case of a single regression term, only in case of single mc (due to PX)
draw <- add(draw, quote(p$e_ <- e$y_eff()))
} else {
if (e$e.is.res)
draw <- add(draw, bquote(mv_update(p[["e_"]], plus=TRUE, X, p[[.(name)]])))
else
draw <- add(draw, bquote(mv_update(p[["e_"]], plus=FALSE, X, p[[.(name)]])))
}
if (!e$e.is.res && e$single.block && !e$use.offset && length(e$mod) != 1L) {
# need to correct here Q_e function; this should not be necessary if we draw all xi's in a single block too !!
if (e$family$link == "probit")
draw <- add(draw, quote(Xy <- crossprod_mv(X, e$Q_e(p) - p[["e_"]])))
else
draw <- add(draw, quote(Xy <- crossprod_mv(X, e$Q_e(p) - p[["Q_"]] * p[["e_"]])))
} else {
draw <- add(draw, quote(Xy <- crossprod_mv(X, e$Q_e(p))))
}
if (e$modeled.Q) {
if (e$Q0.type == "symm")
draw <- add(draw, quote(XX <- crossprod_sym(X, p[["QM_"]])))
else
draw <- add(draw, quote(XX <- crossprod_sym(X, p[["Q_"]])))
}
if (usePX)
draw <- add(draw, bquote(inv_xi <- 1 / p[[.(name_xi)]]))
else
draw <- add(draw, quote(inv_xi <- 1))
draw <- add(draw, bquote(coef_raw <- inv_xi * p[[.(name)]]))
if (q0 == 1L)
draw <- add(draw, quote(M_coef_raw <- coef_raw))
else
draw <- add(draw, bquote(M_coef_raw <- matrix(coef_raw, nrow=.(l), byrow=TRUE)))
draw <- add(draw, bquote(tau <- .(if (e$sigma.fixed) 1 else quote(1 / p[["sigma_"]]^2))))
if (usePX) {
if (PX$vector) {
if (PX$sparse) { # sparse M_ind, Dv
M_ind <- as(as(kronecker(rep.int(1, l), CdiagU(q0)), "CsparseMatrix"), "generalMatrix")
mat_sum_xi <- make_mat_sum(M0=PX$Q0, M1=crossprod_sym(M_ind, XX))
chol_xi <- build_chol(mat_sum_xi(crossprod_sym(M_ind, XX)))
draw <- add(draw, quote(Dv <- M_ind))
draw <- add(draw, quote(attr(Dv, "x") <- as.numeric(M_coef_raw)))
if (PX$data.scale)
draw <- add(draw, quote(chol_xi$update(mat_sum_xi(crossprod_sym(Dv, XX)))))
else
draw <- add(draw, quote(chol_xi$update(mat_sum_xi(crossprod_sym(Dv, XX), w1=tau))))
} else { # matrix M_ind, Dv
M_ind <- kronecker(rep.int(1, l), diag(q0))
chol_xi <- build_chol(crossprod_sym(M_ind, XX) + PX$Q0)
draw <- add(draw, quote(Dv <- coef_raw * M_ind))
if (PX$data.scale)
draw <- add(draw, quote(chol_xi$update(crossprod_sym(Dv, XX) + PX$Q0)))
else
draw <- add(draw, quote(chol_xi$update(crossprod_sym(Dv, XX) * tau + PX$Q0)))
}
if (PX$data.scale)
draw <- add(draw, bquote(xi <- drawMVN_cholQ(chol_xi, crossprod_mv(Dv, Xy) + PX$Qmu0, sd=.(if (e$sigma.fixed) 1 else quote(p[["sigma_"]])))))
else
draw <- add(draw, quote(xi <- drawMVN_cholQ(chol_xi, crossprod_mv(Dv, Xy) * tau + PX$Qmu0)))
} else { # scalar xi
if (PX$data.scale) {
draw <- add(draw, quote(V <- 1 / (dotprodC(coef_raw, XX %m*v% coef_raw) + PX$Q0)))
draw <- add(draw, quote(E <- V * (dotprodC(coef_raw, Xy) + PX$Qmu0)))
draw <- add(draw, bquote(xi <- rnorm(1L, mean=E, sd=.(if (e$sigma.fixed) quote(sqrt(V)) else quote(p[["sigma_"]] * sqrt(V))))))
} else {
draw <- add(draw, quote(V <- 1 / (dotprodC(coef_raw, XX %m*v% coef_raw) * tau + PX$Q0)))
draw <- add(draw, quote(E <- V * (dotprodC(coef_raw, Xy) * tau + PX$Qmu0)))
draw <- add(draw, quote(xi <- rnorm(1L, mean=E, sd=sqrt(V))))
}
}
if (!is.null(S)) {
stop("TBI: inequality constrained coefficients with parameter expansion") # --> f.c. of xi also TMVN(?)
draw <- add(draw, bquote(p[[.(name)]] <- xi * coef_raw)) # need updated coefficient as input for TMVN sampler
}
} else {
xi <- 1 # no parameter expansion
}
if (e$modeled.Q) rm(XX) # XX recomputed at each iteration
if (gl) { # group-level predictors
if (usePX) {
# MH step
if (PX$vector)
draw <- add(draw, bquote(logr <- .(glp$p0) * (sum(log(abs(xi))) - sum(log(abs(p[[.(name_xi)]]))))))
else
draw <- add(draw, bquote(logr <- .(q0 * glp$p0) * (log(abs(xi)) - log(abs(p[[.(name_xi)]])))))
draw <- add(draw, bquote(delta <- (xi * inv_xi) * p[[.(name_gl)]]))
# in case !sigma.fixed, we need sigma^-2 factor in 2nd term on next line(?)
draw <- add(draw, quote(logr <- logr - 0.5 * dotprodC(delta, glp$Q0 %m*v% delta)))
draw <- add(draw, bquote(delta <- p[[.(name_gl)]]))
# in case !sigma.fixed, we need sigma^-2 factor in 2nd term on next line(?)
draw <- add(draw, quote(logr <- logr + 0.5 * dotprodC(delta, glp$Q0 %m*v% delta)))
draw <- add(draw, bquote(if (logr < log(runif(1L))) xi <- p[[.(name_xi)]])) # NB reject, otherwise accept
}
# NB use old value of xi to get 'raw' group-level coefficients
if (q0 == 1L) {
draw <- add(draw, bquote(M_coef_raw <- M_coef_raw - glp$X %m*v% (inv_xi * p[[.(name_gl)]])))
} else {
draw <- add(draw, bquote(M_coef_raw <- M_coef_raw - glp$X %m*m% matrix(inv_xi * p[[.(name_gl)]], nrow=.(glp$p0), byrow=TRUE)))
}
}
if (usePX) {
draw <- add(draw, bquote(p[[.(name_xi)]] <- xi))
draw <- add(draw, quote(inv_xi <- 1 / xi))
}
if (!is.null(priorA)) {
if (q0 == 1L) # DAM is lD vector
draw <- add(draw, quote(DAM <- DA %m*v% M_coef_raw))
else # DAM is of type matrix (lD x q0) as M_coef_raw is
draw <- add(draw, quote(DAM <- DA %m*m% M_coef_raw))
switch(var,
unstructured = {
draw <- add(draw, bquote(SSR <- .rowSums((DAM %m*m% p[[.(name_Qraw)]]) * DAM, .(lD), .(q0))))
},
diagonal = {
#draw <- add(draw, bquote(SSR <- .rowSums((DAM %*% Cdiag(p[[.(name_temp)]]$Qraw)) * DAM, .(lD), .(q0))))
draw <- add(draw, bquote(SSR <- .rowSums(rep_each(p[[.(name_Qraw)]], .(lD)) * DAM^2, .(lD), .(q0))))
},
scalar = {
if (q0 == 1L) {
if (is.null(Q0)) {
draw <- add(draw, bquote(SSR <- p[[.(name_Qraw)]] * DAM^2))
} else {
draw <- add(draw, bquote(SSR <- Q0 * p[[.(name_Qraw)]] * DAM^2))
}
} else {
if (is.null(Q0)) {
draw <- add(draw, bquote(SSR <- p[[.(name_Qraw)]] * .rowSums(DAM * DAM, .(lD), .(q0))))
} else {
draw <- add(draw, bquote(SSR <- p[[.(name_Qraw)]] * .rowSums((DAM %m*m% Q0) * DAM, .(lD), .(q0))))
}
}
}
)
switch(priorA$type,
invchisq = {
if (is.list(priorA$df)) {
draw <- add(draw, bquote(p[[.(name_df)]] <- priorA$draw_df(p[[.(name_df)]], 1 / p[[.(name_omega)]])))
if (is.list(priorA$scale)) {
draw <- add(draw, bquote(p[[.(name_omega)]] <- priorA$draw(p[[.(name_df)]], df.data.omega, SSR, 1 / p[[.(name_omega)]])))
} else {
draw <- add(draw, bquote(p[[.(name_omega)]] <- priorA$draw(p[[.(name_df)]], df.data.omega, SSR)))
}
} else {
if (is.list(priorA$scale)) {
draw <- add(draw, bquote(p[[.(name_omega)]] <- priorA$draw(df.data.omega, SSR, 1 / p[[.(name_omega)]])))
} else {
draw <- add(draw, bquote(p[[.(name_omega)]] <- priorA$draw(df.data.omega, SSR)))
}
}
},
exp = {
aparA <- 2 / priorA$scale
draw <- add(draw, bquote(p[[.(name_omega)]] <- priorA$draw(1 - q0/2, aparA, SSR)))
}
)
# then update QA
draw <- add(draw, bquote(QA <- crossprod_sym(DA, 1 / p[[.(name_omega)]])))
}
if (Leroux_update) {
# draw Leroux parameter
draw <- add(draw, quote(p <- draw_Leroux(p, self, M_coef_raw)))
# compute QA using current value of Leroux parameter
# alternatively, store it in p[[name_temp]]$QA_L and recompute only if a new Leroux parameter has been accepted
draw <- add(draw, bquote(QA <- mat_sum_Leroux(QA, idL, p[[.(name_Leroux)]], 1 - p[[.(name_Leroux)]])))
}
# draw V
if (var == "unstructured") {
if (is.list(prior$scale)) {
# Qraw stored in p; NB Qv has changed because of updated xi (PX), but Qraw has not
if (prior$scale$common) {
draw <- add(draw, bquote(psi0 <- psi0 + sum(diagC(p[[.(name_Qraw)]]))))
draw <- add(draw, quote(psi0 <- rchisq_scaled(1L, q0 * prior$df + prior$scale$df, psi = psi0)))
} else {
draw <- add(draw, bquote(psi0 <- psi0 + diagC(p[[.(name_Qraw)]])))
draw <- add(draw, bquote(psi0 <- rchisq_scaled(.(q0), prior$df + prior$scale$df, psi=psi0)))
}
draw <- add(draw, quote(Qraw <- rWishart(1L, df=df, Sigma = inverseSPD(add_diagC(crossprod_sym(M_coef_raw, QA), psi0)))[,,1L]))
} else {
# TODO draw V_raw using invWishart and form Qv by solving --> 1 instead of 2 solves
draw <- add(draw, quote(Qraw <- rWishart(1L, df=df, Sigma = inverseSPD(psi0 + crossprod_sym(M_coef_raw, QA)))[,,1L]))
}
if (usePX && PX$vector)
draw <- add(draw, quote(inv_xi_qform <- base_tcrossprod(inv_xi)))
else
draw <- add(draw, quote(inv_xi_qform <- inv_xi^2))
draw <- add(draw, quote(Qv <- Qraw * inv_xi_qform))
# convert precision matrix to standard errors and correlations
draw <- add(draw, quote(p[names_se_cor] <- prec2se_cor(Qv)))
} else {
if (var == "diagonal") {
draw <- add(draw, bquote(SSR <- .colSums(M_coef_raw * (QA %m*m% M_coef_raw), .(l), .(q0))))
} else { # scalar variance
if (q0 == 1L) {
if (is.null(Q0))
draw <- add(draw, quote(SSR <- dotprodC(M_coef_raw, QA %m*v% M_coef_raw)))
else
draw <- add(draw, quote(SSR <- Q0 * dotprodC(M_coef_raw, QA %m*v% M_coef_raw)))
} else {
if (is.null(Q0))
draw <- add(draw, quote(SSR <- sum(M_coef_raw * (QA %m*m% M_coef_raw))))
else
draw <- add(draw, quote(SSR <- sum(M_coef_raw * (QA %m*m% (M_coef_raw %m*m% Q0)))))
}
}
switch(prior$type,
invchisq = {
if (is.list(prior$scale))
draw <- add(draw, bquote(Qraw <- 1 / prior$draw(df.data, SSR, p[[.(name_Qraw)]])))
else
draw <- add(draw, quote(Qraw <- 1 / prior$draw(df.data, SSR)))
},
exp = {
apar <- 2 / prior$scale
draw <- add(draw, quote(Qraw <- 1 / prior$draw(ppar, apar, SSR)))
}
)
# NB if xi is a vector so is Qv, even for scalar Qraw
draw <- add(draw, quote(Qv <- Qraw * inv_xi^2))
if (is.null(Q0)) {
draw <- add(draw, bquote(p[[.(name_sigma)]] <- sqrt(1/Qv)))
} else {
draw <- add(draw, quote(sqrtQv <- sqrt(Qv)))
draw <- add(draw, quote(Qv <- scale_mat(Q0, sqrtQv)))
draw <- add(draw, bquote(p[[.(name_sigma)]] <- 1/sqrtQv))
}
}
if (!is.null(priorA) || is.list(prior$scale) || Leroux_update) {
# store Qraw for next iteration -> saves reconstructing it from sigma, rho, xi
draw <- add(draw, bquote(p[[.(name_Qraw)]] <- Qraw))
}
if (!is.null(e$control$CG)) {
name_Qv <- paste0(name, "_Qv_")
draw <- add(draw, bquote(p[[.(name_Qv)]] <- Qv))
}
# draw coefficients
if (gl) {
i.v <- seq_len(q) # indices for random effect vector in u=(v, alpha)
i.alpha <- (q + 1L):(q + glp$p0 * q0) # indices for group-level effect vector in u=(v, alpha)
}
if (in_block) {
name_Q <- paste0(name, "_Q_") # trailing "_" --> only temporary storage
if (gl) {
draw <- add(draw, bquote(p[[.(name_Q)]] <- kron_prod(glp$QA.ext, Qv, values.only=TRUE)))
} else {
draw <- add(draw, bquote(p[[.(name_Q)]] <- kron_prod(QA, Qv, values.only=TRUE)))
}
draw <- add(draw, bquote(p[[.(name)]] <- xi * coef_raw))
} else {
if (gl) { # block sampling of (coef, glp)
if (e$modeled.Q)
draw <- add(draw, quote(attr(glp$XX.ext, "x") <- c(XX@x, glp$Q0@x)))
if (Leroux_update)
draw <- add(draw, quote(glp$QA.ext <- crossprod_sym(glp$IU0, QA)))
draw <- add(draw, quote(Qlist <- update(glp$XX.ext, glp$QA.ext, Qv, 1/tau)))
draw <- add(draw, bquote(coef <- MVNsampler$draw(p, .(if (e$sigma.fixed) 1 else quote(p[["sigma_"]])), Q=Qlist$Q, Imult=Qlist$Imult, Xy=c(Xy, glp$Q0b0))[[.(name)]]))
draw <- add(draw, bquote(p[[.(name)]] <- coef[i.v]))
draw <- add(draw, bquote(p[[.(name_gl)]] <- coef[i.alpha]))
} else { # no blocking, no group-level component
draw <- add(draw, quote(Qlist <- update(XX, QA, Qv, 1/tau)))
draw <- add(draw, bquote(p[[.(name)]] <- MVNsampler$draw(p, .(if (e$sigma.fixed) 1 else quote(p[["sigma_"]])), Q=Qlist$Q, Imult=Qlist$Imult, Xy=Xy)[[.(name)]]))
}
}
if (e$e.is.res)
draw <- add(draw, bquote(mv_update(p[["e_"]], plus=FALSE, X, p[[.(name)]])))
else
draw <- add(draw, bquote(mv_update(p[["e_"]], plus=TRUE, X, p[[.(name)]])))
draw <- add(draw, quote(p))
# END draw function
start <- function(p) {}
# TODO check formats of user-provided start values
if (!in_block) {
if (gl) {
# TODO conditional sampling if one of p[[.(name)]] and p[[.(name_gl)]] is provided
start <- add(start, bquote(coef <- MVNsampler$start(p, e$scale_sigma)[[.(name)]]))
#if (!is.null(glp$R))
# start <- add(start, quote(coef <- constrain_cholQ(coef, ops$ch, glp$R)))
start <- add(start, bquote(if (is.null(p[[.(name)]])) p[[.(name)]] <- coef[i.v]))
start <- add(start, bquote(if (is.null(p[[.(name_gl)]])) p[[.(name_gl)]] <- coef[i.alpha]))
} else {
start <- add(start, bquote(if (is.null(p[[.(name)]])) p[[.(name)]] <- MVNsampler$start(p, e$scale_sigma)[[.(name)]]))
}
}
if (usePX) {
if (PX$vector)
start <- add(start, bquote(if (is.null(p[[.(name_xi)]])) p[[.(name_xi)]] <- rep.int(1, .(q0))))
else
start <- add(start, bquote(if (is.null(p[[.(name_xi)]])) p[[.(name_xi)]] <- 1))
}
if (!is.null(priorA) || is.list(prior$scale) || Leroux_update) {
switch(var,
unstructured = {
start <- add(start, bquote(if (is.null(p[[.(name_Qraw)]])) p[[.(name_Qraw)]] <- rWishart(1L, prior$df, if (is.list(prior$scale)) (1/psi0)*diag(q0) else inverseSPD(psi0))[,,1L]))
},
diagonal=, scalar = {
if (prior$type == "exp")
start <- add(start, bquote(if (is.null(p[[.(name_Qraw)]])) p[[.(name_Qraw)]] <- rexp(.(if (var == "diagonal") q0 else 1L), rate=1/prior$scale)))
else
start <- add(start, bquote(if (is.null(p[[.(name_Qraw)]])) p[[.(name_Qraw)]] <- rchisq_scaled(.(if (var == "diagonal") q0 else 1L), prior$df, psi=prior$psi0)))
}
)
if (Leroux_update) {
start <- add(start, bquote(if (is.null(p[[.(name_Leroux)]])) p[[.(name_Leroux)]] <- runif(1L)))
name_detQA <- paste0(name, "_detQA_")
start <- add(start, bquote(if (is.null(p[[.(name_detQA)]])) p[[.(name_detQA)]] <- det(2*p[[.(name_Leroux)]], 1 - 2*p[[.(name_Leroux)]])))
}
if (!is.null(priorA)) {
if (is.list(priorA$df))
start <- add(start, bquote(if (is.null(p[[.(name_df)]])) p[[.(name_df)]] <- runif(1L, 1, 25)))
if (is.list(priorA$df) || is.list(priorA$scale))
start <- add(start, bquote(if (is.null(p[[.(name_omega)]])) p[[.(name_omega)]] <- runif(.(lD), 0.75, 1.25)))
}
}
start <- add(start, quote(p))
# TODO rm more components no longer needed (Leroux_type, Leroux, other switches such as gl, ...)
self
}
setup_priorGMRFsampler <- function(mc, Qv) {
# TODO
# - support local scale parameters DA --> diag(omega_i)^-1/2 DA
# - in some cases perm=TRUE may be more efficient
mc$cholDD <- build_chol(tcrossprod(mc$DA), control=chol_control(perm=FALSE))
mc$cholQv <- build_chol(Qv, control=chol_control(perm=FALSE))
rGMRF <- function(Qv) {}
rGMRF <- add(rGMRF, quote(cholQv$update(Qv)))
if (mc$q0 == 1L) {
rGMRF <- add(rGMRF, bquote(Z <- Crnorm(.(nrow(mc$DA)))))
rGMRF <- add(rGMRF, quote(Z <- cholQv$solve(Z, system="Lt", systemP=TRUE)))
rGMRF <- add(rGMRF, quote(crossprod_mv(DA, cholDD$solve(Z))))
} else {
rGMRF <- add(rGMRF, bquote(Z <- matrix(Crnorm(.(mc$q0 * nrow(mc$DA))), nrow=.(mc$q0))))
rGMRF <- add(rGMRF, quote(Z <- cholQv$solve(Z, system="Lt", systemP=TRUE)))
rGMRF <- add(rGMRF, quote(coef <- crossprod(DA, cholDD$solve(t.default(Z)))))
rGMRF <- add(rGMRF, quote(as.numeric(t.default(coef))))
}
mc$rGMRFprior <- rGMRF
environment(mc$rGMRFprior) <- mc
}
setup_priorMVNsampler <- function(mc, Q) {
rMVN <- function(p, Q) {}
if (is_proper_GMRF(mc)) {
mc$priorMVNsampler <- create_TMVN_sampler(Q, update.Q=TRUE, update.mu=FALSE, name="coef", R=mc$R)
} else {
if (is.null(mc$R)) stop("cannot sample from improper GMRF without constraints")
# for IGMRF add tcrossprod(mc$R) to Q to make it non-singular (--> inefficient)
# TODO maybe mc$R is removed and stored in (posterior) MVNsampler --> keep a reference(!) to R in mc
mc$mat_sum_prior <- make_mat_sum(M0=economizeMatrix(tcrossprod(mc$R), symmetric=TRUE), M1=Q)
mc$priorMVNsampler <- create_TMVN_sampler(mc$mat_sum_prior(Q), update.Q=TRUE, update.mu=FALSE, name="coef", R=mc$R)
rMVN <- add(rMVN, quote(Q <- mat_sum_prior(Q)))
}
rMVN <- add(rMVN, quote(priorMVNsampler$draw(p, Q=Q)[["coef"]]))
mc$rMVNprior <- rMVN
environment(mc$rMVNprior) <- mc
}
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.