Hi,
I had a question regarding scaling iCAR (car_normal) effects in nimble. This is something I'd like to implement in a couple of different settings: a BYM model where it would be helpful to parameterize the the marginal variance of spatial and non-spatial RE to be roughly equal/equivalent to one divided by car_normal's tau argument (or at least have consistent priors for each of these), and a latent factor model where it would be preferred for the marginal variance of the car_normal effects to be roughly 1.
Inla approaches this by rescaling the precision matrix--there are function (at bottom) that work to scale things by the geometric mean of the matrix diagonal following a sum to zero constraint so that the iCAR values are approximately ~N(mu [or 0], 1/tau). My understanding is that it would require custom functions to use a scaled precision matrix like this directly in nimble, and that nimble's iCAR sum to zero is a little more deterministic (subtracting the mean after the fact). What I''ve seen done in other software applications (
https://doi.org/10.1016/j.sste.2019.100301) is to derive the scaling factor in inla, pass it as a value or constant to the software, and rescale centered iCAR effects after the fact. I'm curious whether something like the stuff at the bottom here would work? (I think, at best, it might work only if there are not multiple disconnected regions).
I recognize this is maybe a little outside of the scope of the list, so particular thanks for any thoughts on this.
Thanks,
John
###Let Q be a precision matrix, N be number of areas,
##adj.matrix be full adjacency matrix, or sparseMatrix
###in R
###precision matrix for iCAR
Q= INLA::Diagonal(N, rowSums(adj.matrix)) - adj.matrix
###function to scale precision matrix
inla.scale.model.new = function(Q, constr = NULL, eps = sqrt(.Machine$double.eps))
{
## returns scaled Q, the scaled marginal variances
## and the scaling factor--only modification is returning 'fac'
marg.var = rep(0, nrow(Q))
Q = inla.as.sparse(Q)
g = inla.read.graph(Q)
for(k in seq_len(g$cc$n)) {
i = g$cc$nodes[[k]]
n = length(i)
QQ = Q[i, i, drop=FALSE]
if (n == 1) {
QQ[1, 1] = 1
marg.var[i] = 1
} else {
cconstr = constr
if (!is.null(constr)) {
## the GMRFLib will automatically drop duplicated constraints; how convenient...
cconstr$A = constr$A[, i, drop = FALSE]
eeps = eps
} else {
eeps = 0
}
res = inla.qinv(QQ + Diagonal(n) * max(diag(QQ)) * eeps, constr = cconstr)
fac = exp(mean(log(diag(res)))) ###this is the scaling factor--geometric mean...
QQ = fac * QQ
marg.var[i] = diag(res)/fac
}
Q[i, i] = QQ
}
return (list(Q=Q, var = marg.var, fac=fac))
}
###in nimbleCode
rho[1:X]~dcar_normal(....tau=1...., zero_mean=1)
rhostar[1:X] <- rho[1:X]/sqrt(scale_factor)
## in this case, want marginal variance of rhostar to be about 1