| tulpa_re_aghq | R Documentation |
Refines a generalized linear mixed model's fixed effects and random-effect
covariance by replacing the per-group Laplace integral with n_quad-point
adaptive Gauss-Hermite quadrature (AGHQ). At n_quad = 1 this is the joint
Laplace (glmer nAGQ = 1); higher n_quad reduces the small-cluster
attenuation of the variance components for binary / count data. Unlike
agq_fit() (intercept-only RE, built-in binomial/poisson/gaussian
likelihoods), this engine is callback-driven: the caller supplies the
per-group conditional likelihood, so a custom marginal (e.g. a
latent-state-integrated occupancy / detection likelihood, or the
latent-abundance-integrated N-mixture marginal) refines through the same
quadrature.
This is an engine block, not a front door: model packages call it
programmatically, so its tuning knobs (max_iter, n_quad, keep)
sit in the signature rather than in a control list.
The engine is structure-agnostic. It integrates the per-group marginal
M_g = \int \exp\{\ell_g(b_g)\}\, N(b_g; 0, \Sigma)\, db_g,
where b_g is the group's random-effect vector (dimension
\sum_m c_m over the RE terms) and \ell_g(b_g) is the group's
conditional log-likelihood when its linear predictors are perturbed by
b_g. How b_g enters the likelihood – through one linear
predictor, or through several coupled arms at different observation
granularities (e.g. a per-site abundance arm and a per-visit detection arm
sharing a species grouping) – lives entirely in the callback. The engine
only needs, per group, the value / gradient / Hessian of \ell_g in
b_g (for the mode) and \ell_g at the quadrature nodes (for the
sum). The fixed parameters theta and the log-Cholesky coordinates of
\Sigma are optimized jointly on \sum_g \log M_g; standard errors
come from the exact-marginal Hessian.
Two callback forms select the structure (supply exactly one):
make_site – the common single-arm, per-row-separable case
(\ell_g(b_g) = \sum_{i \in g} \log f_i(\eta_i + Z_i b_g) for one
linear predictor \eta). The engine builds the oracle from the
per-row marginal and the RE design Z itself.
make_group – the general / multi-arm case. The caller supplies
the per-group b-space oracle directly, so non-separable units (e.g. the
visits of an N-mixture site coupled through the shared latent count) and
random effects on several arms at once are handled with no engine change.
Scope: one shared grouping factor across all RE terms (the per-group integral
factorizes). The total RE dimension per group should be small (the quadrature
grid is n_quad^dim).
tulpa_re_aghq(
theta0,
re_terms,
Sigma0,
make_site = NULL,
make_group = NULL,
oracle = NULL,
n_obs = NULL,
keep = NULL,
n_quad = 9L,
lkj_eta = 1,
theta_prior_sd = Inf,
sigma_prior = NULL,
gradient = c("fd", "analytic"),
max_iter = 200L
)
theta0 |
Initial fixed-parameter vector. The engine optimizes these jointly with the RE covariance; the callback interprets them. |
re_terms |
A list of RE term specs (or one spec), each defining a
covariance block: |
Sigma0 |
List of initial per-term covariance matrices (the EM estimate). |
make_site |
|
make_group |
|
oracle |
Optional prebuilt native (compiled) oracle, an external pointer
to a |
n_obs |
Number of observations (length of each term's |
keep |
Optional logical/integer mask of observations to include
(default all; |
n_quad |
Quadrature nodes per RE dimension. Either a single integer
(default 9; |
lkj_eta |
LKJ shape for an optional correlation penalty on each
correlated block (log-density |
theta_prior_sd |
Optional Gaussian ridge SD on the fixed parameters
|
sigma_prior |
Optional Penalized-Complexity prior on the marginal
standard deviations of one or more RE covariance blocks, added to the
objective (and hence the marginal Hessian). |
gradient |
How |
max_iter |
Optimizer iteration cap (default 200). |
A list with: theta (refined fixed parameters), Sigma_list
(refined per-term covariance), blup / blup_var (per-term n_groups x n_coefs posterior mean / variance of the RE), group_ok (logical, length
n_groups: FALSE where that group's mode search or precision
factorization failed, which is what the NA rows of blup, blup_var,
blup_cov_g and blup_cross_g mean – a caller conditions its per-group
reads on this rather than on trapping the accompanying warning),
blup_cross (per-term
n_groups x n_theta x n_coefs array: the mode/theta cross-Hessian block
Bf, -d^2 ell_g / d theta db at each group's mode, in the same
negative-Hessian sign convention as the posterior precision underlying
blup_var – so a joint draw of theta and a group's RE b_g uses
b_g | theta ~ N(blup_g - Cinv_g %*% t(Bf_g) %*% (theta_draw - theta), Cinv_g) with Cinv_g the group's n_coefs x n_coefs posterior
covariance block. NA throughout when blup_cross_available is FALSE:
the cross-Hessian needs the oracle's analytic theta_score, which the
R-closure bridge (make_site / make_group) does not supply – only a
prebuilt native oracle carries it), blup_cov_g / blup_cross_g
(per-group lists, length n_groups, of the FULL joint posterior
covariance (d x d, d = every RE term's width combined) and mode/theta
cross-Hessian (n_theta x d) across ALL RE terms sharing that group –
the superset blup_var/blup_cross reduce to a per-term diagonal block
of when a group carries more than one term, since a group's terms are
found jointly and can carry real posterior covariance BETWEEN terms
(e.g. an abundance-arm and a detection-arm term sharing one grouping
factor); same NA-when-unavailable rule as blup_cross), theta_cov /
theta_se
(fixed-parameter covariance / SE from the marginal Hessian), re_par /
re_par_cov / re_par_se (the RE-covariance coordinates the optimizer
carried – log-Cholesky for a full block, log-SD for a diagonal one – with
their block of the same inverse Hessian, so SE(log sigma) is available
for a boundary test on a weakly-identified variance component),
re_par_layout (per block: label, nc, full, the index range into
re_par and the coord names, so a caller does not reconstruct the
packing), joint_cov (the whole (n_theta + n_chol) inverse Hessian),
log_marginal
(the AGHQ marginal log-likelihood at the optimum, excluding any ridge),
n_quad, lkj_eta, converged, and counts (stats::optim's own
function / gradient evaluation counts, so a caller reporting how much
work the fit took has a number to report rather than NA). RE terms that
do not share one
grouping factor are an input error and stop. Three conditions warn and
return NULL (caller keeps its prior fit): a singular / non-finite
optimum, an objective that is already undefined at the starting parameters
(some group's solve fails there, so there is nothing to descend), and an
optimum whose objective is the failure sentinel rather than an attained
marginal likelihood – the last two report which groups failed.
Pinheiro & Bates (1995). Approximations to the log-likelihood function in the nonlinear mixed-effects model. Journal of Computational and Graphical Statistics 4(1):12-35. Lewandowski, Kurowicka & Joe (2009). Generating random correlation matrices based on vines and extended onion method. Journal of Multivariate Analysis 100(9):1989-2001.
# A per-row-separable binomial GLMM marginal supplied through `make_site`.
l1pe <- function(x) ifelse(x > 0, x + log1p(exp(-x)), log1p(exp(x)))
make_binom_site <- function(X, y, nt) function(theta) {
eta_fixed <- as.numeric(X %*% theta)
list(eta_re = eta_fixed,
deriv = function(rows, eta) {
p <- plogis(eta)
list(logL = y[rows] * eta - nt[rows] * l1pe(eta),
d1 = y[rows] - nt[rows] * p, d2 = -nt[rows] * p * (1 - p))
},
lmat = function(rows, ETA) y[rows] * ETA - nt[rows] * l1pe(ETA))
}
set.seed(1)
ng <- 30L; npg <- 8L; n <- ng * npg
g <- rep(seq_len(ng), each = npg); x <- rnorm(n)
X <- cbind(1, x); nt <- rep(3L, n); u <- rnorm(ng, 0, 0.9)
y <- rbinom(n, nt, plogis(0.3 + 0.7 * x + u[g]))
fit <- tulpa_re_aghq(theta0 = c(0, 0),
re_terms = list(list(idx = g, n_groups = ng, n_coefs = 1L)),
Sigma0 = list(matrix(0.25, 1, 1)),
make_site = make_binom_site(X, y, nt), n_obs = n, n_quad = 5L)
sqrt(fit$Sigma_list[[1]][1, 1]) # adaptive-GHQ RE standard deviation
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.