View source: R/nested_laplace_joint.R
| print.tulpa_nested_laplace_joint | R Documentation |
Outer-grid nested Laplace driver for joint models – multiple response arms sharing one latent prior block, parameterized as a per-arm field amplitude (sigma) on a unit-precision latent.
Supported priors:
"bym2" – outer grid over (sigma, rho [, alpha]).
Latent: phi (n_s) | theta (n_s) with unit-precision
ICAR + iid components.
"icar" – outer grid over (sigma [, alpha]).
Latent: phi (n_s) with unit-precision ICAR.
"car_proper" – outer grid over (sigma, rho_car [, alpha]).
Latent: phi (n_s) with Q = D - rho_car * W.
Other backends (NNGP, HSGP, RW1/2, AR1) follow the same interface and land under Phase 3.
## S3 method for class 'tulpa_nested_laplace_joint'
print(x, ...)
## S3 method for class 'tulpa_nested_laplace_joint_multi'
print(x, ...)
tulpa_nested_laplace_joint(
responses,
prior,
copy = NULL,
phi_grid = NULL,
prior_sigma = NULL,
prior_alpha = NULL,
prior_phi = NULL,
hyperprior = c("proper", "flat"),
cell_coupling = "separable",
control = list()
)
x |
A joint nested-Laplace fit. |
... |
Ignored. |
responses |
A named list of arm specs (length >= 1). Each arm:
|
prior |
A list describing the shared latent prior block. Required
field
On the list-of-blocks path an An empty |
copy |
Multi-block copy specification (multi-block The |
phi_grid |
Optional list specifying per-arm dispersion axes on the
outer grid. Accepts either a named list (keys = arm names) or a
positional list of length
Family-specific interpretation of
A dispersion axis is PLACED like a prior block's scale axis when the caller
declares it a default with Each |
prior_sigma, prior_alpha |
Optional regularizing hyperpriors on the
donor field amplitude
WHICH AXES ONE SPEC REACHES. A list-of-blocks prior_alpha = list(
list(block = 1, prior = list("pc.prec", c(4.0, 0.01))),
list(block = 2, prior = list("half_normal", 2.0))
)
A block the list does not name carries no prior on that axis. An entry
naming a block that carries no such axis is an error, and the message
names the blocks that do. The per-block shape is a multi-block one: on a
single-block
|
prior_phi |
Optional regularizing hyperprior on the per-arm
dispersion axes declared through |
hyperprior |
The prior an outer hyperparameter axis carries when the
call states no density for it, |
cell_coupling |
Character scalar naming a per-cell coupled likelihood
registered against tulpa's process-global registry (see
|
control |
Optional list of perf/numerical tuning knobs (statistical
arguments stay top-level), following the
|
A list of class c("tulpa_nested_laplace_joint", "tulpa_nested_laplace", "list") with:
theta_grid, theta_names – outer-grid hyperparameter values
(includes the alpha axis when copy is set).
log_marginal, weights – per-grid-point log-marginal and integration
weights (sum to 1).
theta_mean, theta_sd – posterior moments per hyperparameter,
including alpha when copy is set.
theta_median, theta_ci_lo, theta_ci_hi – weighted-quantile
median and 2.5/97.5 empirical CI per hyperparameter axis (same
names as theta_mean). Recommended summary for right-skewed
scale-like axes (alpha at small n_pos, sigma/range/phi near
a boundary), where the posterior mean is pulled by the right
tail away from the bulk and mean +/- 1.96 sd mis-states the
uncertainty.
theta_sd_source – per axis, what the reported SD was read off:
"weighted" (the spread of the weights, on a resolved axis),
"stencil" (a parabola at the modal node) or "within_cell" (the
modal node's mass spread over its cell, the read the interval of an
unresolved axis comes from). theta_sd_stencil_declined says why a
parabola was not formed.
modes – [n_grid x n_x] matrix of inner modes.
fitted_eta – [n_grid x N] matrix of the linear predictor at each
cell's own mode, offset and latent field included. Present on a
single-arm fit, which is the one that has a single linear predictor to
report; a cell that was pruned or whose block preparation failed reads
NA. fitted_eta_var is its per-cell within-cell variance, present
under control$fitted_var (the default). Together these are the
per-cell Gaussian posterior_predict() draws a replicate from.
n_iter – inner Newton iterations per grid point.
arm_layout – list with per-arm beta_start, re_start,
spatial offset(s) and n_x for decoding modes.
prior, responses, copy – echoed inputs.
timing – named numeric of wall-clock seconds: total plus the
setup (validation / encoding / grid construction), grid (inner
Laplace solves, including adaptive-refinement and consistency passes),
postproc (weight / moment / marginal assembly) and diagnostics
(outer Pareto-\hat{k}) phases. The grid phase is the one that
scales with grid size and core count. Surfaced one-line in print.
pareto_k, pareto_k_is_ess, pareto_k_scope – outer
Pareto-\hat{k} accuracy diagnostic and its importance-sampling
ESS (both NA when control$diagnose_k = FALSE or the fit declines;
see the diagnose_k control knob). pareto_k < 0.7 indicates the
nested integration is reliable; >= 0.7 that the (skewed / heavy-
tailed) hyperparameter posterior is misfit by the Gaussian grid and
the fit should escalate to an exact debias.
pareto_k_declined – when pareto_k is NA, WHY:
"not_requested" (control$diagnose_k = FALSE; nothing is wrong),
"unguessable_axis: <axis>" (a support the engine will not guess, e.g.
car_proper's rho_car – a PERMANENT limitation of that family, so read
the quadrature ESS instead), "draws_too_few", "grid_too_small",
"no_varying_axis", "degenerate_proposal" (the outer mode curvature
came back non-finite – a signal about the fit), "not_applicable", or
"internal_inconsistency" (an engine bug worth reporting). NA on a
fit whose \hat{k} WAS computed.
pareto_k_proposal_source – how the outer importance proposal the
\hat{k} scores was built: "mode_hessian" from the Laplace
curvature at the hyperparameter mode (the CCD design's, or a
finite-difference Hessian when a sharp posterior collapses the grid),
"grid_moment" from the grid-weighted covariance, "moment_matched"
when the moment-matching refinement improved on either, "grid_mixture"
when the faithful mixture-over-grid-cells proposal won, or
"skew_normal" when the skew-normal rescue did. NA when the
diagnostic is off or declines. The mode-Hessian source keeps the
\hat{k} meaningful when the grid concentrates on ~1 cell.
pareto_k_regime – whether the outer grid actually integrated
hyperparameter uncertainty: "spread", or "collapsed_interior" /
"collapsed_edge" when the quadrature effective sample size falls below
two cells so the integration has degenerated to a point evaluation at
the modal hyperparameter. An interior collapse is benign (the grid
bracketed the mode; the fit is empirical Bayes at that mode and only the
integrated hyperparameter uncertainty is missing); an edge collapse means
the mode sits at an extreme node, so the grid may be too narrow.
Attached from the stored weights, so it is present even with
control$diagnose_k = FALSE.
pareto_k_grid_edge_axes, pareto_k_grid_edge_sides – for an edge
collapse, which axes the dominant cell sits against and on which side
("lower" / "upper"); widen those axes and refit to confirm the mode
is bracketed. Axes the grid pins to one value are excluded (pinned, not
at a boundary).
axis_span – per outer axis, the span the fit worked over and the two
terms that separate it from the nodes it was given: nodes (the range
of the axis's initial continuum nodes), declared (their support, i.e.
those nodes plus the half node step the outermost cells own – for k
equally spaced nodes that is k / (k - 1) times the node range on the
axis's integration coordinate, so 2x at two nodes and 1.125x at nine),
integrated (the region the final grid's cell measure integrates, the
same interval axis_support reports), refine (the mode refinement
ran under: "none" / "densify" / "extend") and n_nodes (initial
and final continuum node counts).
outer_grid_placement – "fixed" (the default sigma_grid axis was
used as-is) or "auto_recentered" when a railed sigma axis
triggered the mode-Hessian recenter-and-refit (see the prior
argument above). outer_grid_recenter_attempts (integer)
and outer_grid_prior_added (logical: whether the light default
PC(U=3, alpha=0.01) sigma prior was applied) are set alongside it, plus
outer_grid_prior_declined = "prior_pinned" when a second attempt ran
but the caller's pinned prior_sigma held that prior back.
outer_grid_recenter_declined – on a "fixed" placement, why the
recenter did not run: "grid_not_collapsed" (the grid already brackets
the mode – the common case, no refit needed), "axis_pinned" (the
caller pinned sigma_grid; mark it with auto_grid() if it is a
default rather than a choice), "default_axis_pinned" (a wrapper
package declared the nodes with auto_grid(place = FALSE) and asked
for them as written – nothing you pinned), or "no_usable_curvature"
(the outer mode-find the recenter lays the axis from did not reach a
mode with a usable curvature, e.g. a car_proper grid whose rho_car
axis has unguessable support).
Absent when the fit WAS recentred. A joint fit's field SD and its
per-arm dispersion are placed by different passes over the same grid,
and this slot reduces over them rather than holding the last to speak:
an axis held because it was declared does not stand as the fit's answer
while a pass with a movable axis has one.
outer_grid_axis_declined – the same question PER AXIS, as a named
character vector. The slot above holds one reason for the whole fit and
is written only while the fit is unplaced, so on a fit where one axis
moved and another did not it says auto_recentered and nothing about
the axis that stayed – which is the axis grid_coarsest_axis then
names. Written by the field-SD passes for sigma (b<k>.sigma on a
copy block) and by the per-arm dispersion pass for phi_<arm>, and
carried across a later pass placing a different axis; absent on a fit
no pass declined an axis of. An axis a pass DID move that still rails
reads "attempts_exhausted" (or the reason its last attempt could not be
laid), and is the axis the railed-mode warning names.
outer_grid_pilot – present only when control$recenter_pilot ran: the
pilot's resolution (n_pilot), its cell count (cells), the axes it
thinned (axes) and those it could not (axes_kept), and what the
placement was DECIDED from – the pilot's own regime, ess_grid and
edge_axes. The reported grid is never the grid the trigger was read
on, so a two-pass fit is legible from the object rather than only from
the progress log. outer_grid_pilot_declined = "pilot_placement_declined"
records a pilot whose detection placed nothing and which was therefore
followed by the full fit and the full fit's own detection.
outer_grid_recenter_sd_clamp, outer_grid_recenter_sd_raw,
outer_grid_recenter_sd_used – on a recentred fit, one entry per moved
axis: which mode-SD bound the placement hit ("none" / "floor" /
"ceiling"), the SD the finite-difference stencil MEASURED, and the SD
the axis was actually laid from. A clamped axis was
laid from a substituted spread rather than a measured one, and the
reported interval is read off that span; the two used to be
indistinguishable on the fit.
pareto_k_outer_skew – per-axis skewness of the hyperparameter marginal
in the proposal's whitened coordinate, present only when a
\hat{k} above the good band triggered the skew-normal rescue pass.
It is the EXPLANATION for an inflated \hat{k}: a symmetric Gaussian
proposal against a right-skewed variance-component marginal has a heavy
importance-ratio tail whatever the integration's quality. NULL when the
Gaussian proposal already fit.
pareto_k_se_boot, pareto_k_ci_low, pareto_k_ci_high,
pareto_k_se_formula, pareto_k_tail_points,
pareto_k_tail_points_requested, pareto_k_band_confident – the outer
Pareto-\hat{k} uncertainty, present whenever the
diagnostic ran. pareto_k_se_boot is the bootstrap SE of the k-hat and
pareto_k_ci_low / pareto_k_ci_high its 2.5\
quantiles – the estimator's sampling spread GIVEN the proposal, not a
posterior credible interval; pareto_k_se_formula is the closed-form
GPD-shape MLE asymptotic SE cross-check. pareto_k_tail_points is the GPD
tail size used and pareto_k_tail_points_requested the k_tail_points
request (NA when automatic). pareto_k_band_confident is TRUE iff the
bootstrap CI lies within one reliability band, NA when the bootstrap was
off or could not fit. diagnose_draws and diagnose_cost_ratio (the
diagnostic's draw budget and its wall-clock cost relative to the fit) are
attached at the top level.
pareto_k_by_arm, pareto_k_by_arm_is_ess, pareto_k_by_arm_scope –
present only with control$diagnose_k = "by_arm".
Named (by arm) outer Pareto-\hat{k} restricted to each arm's
hyperparameter axes, the other arms held at their posterior mean, so a
tail-heavy joint k can be localised to one arm. A per-arm entry is NA
when that arm carries no varying axis. Each arm carries its own bootstrap
uncertainty: pareto_k_by_arm_se_boot, pareto_k_by_arm_ci_low /
pareto_k_by_arm_ci_high, pareto_k_by_arm_se_formula,
pareto_k_by_arm_tail_points and pareto_k_by_arm_band_confident.
k_quality_requested, k_quality_reached, k_quality_best,
k_quality_miss, k_quality_reason, k_quality_rounds,
k_quality_k_trace – the reliability verdict for the
control$k_quality intent. k_quality_requested
echoes the intent; k_quality_best is the band THIS fit reached
("good" / "ok" / "unreliable", or "uncertain" when the bootstrap CI
crosses a boundary) – the band of the returned fit, not the best band
seen while escalating, which k_quality_k_trace carries;
k_quality_reached is TRUE/FALSE for an "ok" /
"good" target (NA for "report" / "none"), never silently
downgraded; k_quality_miss classifies a miss as "resolution" (the
\hat{k} confidently outside the band – a grid deficiency) or
"precision" (the bootstrap CI straddling a boundary – the estimator's
variance), NA when there is no miss to classify, and is what the
escalation reads once refinement is exhausted; k_quality_reason
records why it stopped;
k_quality_rounds is the number of escalation re-fits performed (0 when
the first fit sufficed or escalation was off); and k_quality_k_trace is
the outer \hat{k} round by round, starting at the first fit's, so a
chase that did not descend monotonically is visible. The refinement rungs
rebuild the proposal from the grid they just changed, so a rising trace is
a re-founded proposal on a strictly larger grid rather than a worse fit,
which is why the LAST round is returned rather than the lowest-\hat{k}
one.
adaptive_grid_info – when adaptive_grid = TRUE, a list with
triggered_axes (character) and n_points_added (integer)
describing the refinement passes. NULL otherwise.
local_ccd_info – when local_ccd engaged (or k_refine = "ccd"), a
list with n_cells_refined, n_nodes_added, the refined cells, the
n_cells_before / n_cells_after grid sizes, n_design_nodes (the cells
that carry an in-cell design weight rather than their own mass),
design_mass (the share of the integration weight sitting on them) and
cell_share (the share each refined cell held on the BASE grid, before
any node was placed). The two shares differ: the replacement nodes sit
nearer the peak than the cell's own coordinate did, so refining raises
the share the refined region holds, and reading both separates how
concentrated the base grid already was from how much the refinement
concentrated it. Three per-cell readings come with it, on three
orthogonal axes, none of them gating anything. Shape: misfit (the
standardized cubic magnitude of the part of the cell the design cannot
represent, the one reading skew_max gates on, with misfit_declined /
cells_declined for the cells put back as their own mass atoms).
Centring: offset (the norm of the whitened gradient, i.e. how far the
cell's own peak sat from the cell's coordinate in units of its marginal
spread) and mode_gain (the same displacement in the cell's own
curvature units, 0.5 * g' (-H)^-1 g in nats, read off the same fit's
quadratic coefficients and comparable across cells whose curvature
differs; NA where -H is not positive definite, a cell with no
interior peak to be displaced from). Mass: log_mass_ratio (the cell's
refined mass over the coarse atom it replaced, log_mass_refined - log_mass_coarse, both carried on the log scale with the cell's outer
design weight in them) and max_node_weight (the share the single
largest node takes of its own cell's refined mass). Each carries a
_declined twin for the cells the gate put back, whose nodes were
evaluated before the score was read. misfit certifies that the design
can represent the cell's shape and nothing more: a cell can be exactly
quadratic and still be read at a point that is not representative of it
(offset / mode_gain), and refining it can still move how much mass it
competes for against its unrefined neighbours (log_mass_ratio, the
embedded-rule local error indicator of adaptive cubature). NULL when
local CCD was off or declined (single-block, < 4 axes, an active
phi_grid, or no peaked interior cell).
integration_requested, integration_declined – what integration
asked for, and why the requested CCD or adaptive lattice did not run. $integration names the
integrator that RAN, and .nl_node_support() keys the interval
construction off it, so a caller who asked for a moment rule and
received a density grid reads the reason here rather than inferring it.
integration_declined is NA_character_ when nothing was declined, and
otherwise one of "axis_count" (fewer transformable latent axes than the
requested mode's threshold), "unguessable_axis" (a CAR_proper
rho_car or a non-BYM2 rho), "degenerate_axis" (a single-valued
axis), "modefind_ridge" / "modefind_boundary" /
"modefind_degenerate" / "modefind_failed", "hessian_singular",
"hessian_not_pd", "copy_atom_components" (more copy atoms than the
design splits into, see copy_atom_mass), "copy_atom_mass" (a
declared atom mass outside [0, 1)) or "placement_budget" (placing
the design would cost more inner solves than integrating the tensor grid
it replaces; see the ccd_budget control knob). Under
integration = "grid_adaptive" it is one of
"adaptive_small_grid" (fewer outer cells than
adaptive_grid_min_cells), "adaptive_degenerate_lattice",
"adaptive_seed_failed" (no finite marginal in the coarse seed),
"adaptive_diffuse_seed" / "adaptive_flood_cap" /
"adaptive_kept_cap" (the posterior mass spreads over more than
adaptive_grid_max_frac of the dense grid, read at the seed, during the
flood, or on the closed kept region), or "adaptive_error" (the flood
raised an error, reported as a warning carrying its message); the fit
then integrates the dense tensor. Multi-block fits only.
ccd_modefind_evals, ccd_modefind_budget, ccd_modefind_rounds,
ccd_modefind_seconds – what PLACING the CCD cost: inner Newton solves
spent by the mode-find (seed, step calibration and every round of every
mixture component), the ceiling they were spent against, the rounds
taken and the wall-clock seconds. Present on any multi-block fit that
attempted a placement, including one that declined – a decline reports
what it had already paid – and absent when no placement was attempted,
which is not the same as a placement that cost nothing. Read alongside
$integration to judge whether the CCD earned its design on this model
rather than arguing it from the round caps.
weight_kind – one entry per outer-grid cell, "mass" or "design".
A tensor cell holds the mass of its own cell and a CCD node holds a
design weight, so a fit integrated by one rule reports one value
throughout; a locally refined grid carries both, and this says which per
cell instead of leaving it to be read off integration.
theta_interval_read, theta_interval_design_mass – what the reported
per-axis theta_median / theta_ci_lo / theta_ci_hi were read off, and
how much of the integration weight sits on nodes whose cumulative sum is
not a CDF. "density" is a weighted quantile over cells that discretize
the posterior (design_mass 0), "moment_rule" an interval from the
moments a central-composite design delivers (design_mass 1), and
"mixed" the locally CCD-refined grid, which carries both kinds at once.
A mixed support still reports the weighted quantile, which measured best
against a converged reference in both design_mass regimes; the share is
the regime variable, since on that part of the support the quantile is
bounded by the refined cells' own grid neighbourhoods rather than by the
posterior. Every nested path stamps the pair, not only
the multi-block driver.
within_cell_requested, theta_within_cell,
theta_within_cell_declined – the WITHIN-CELL construction the same
intervals were read with. The kind above says what the
integrator left; this says how each cell's mass was spread inside its own
box when the grid was read back. "log_quadratic" is the default and
interpolates each row's node log densities; "box_uniform"
(control$within_cell) puts the cumulative full mass at each cell edge;
"chord" puts the cumulative mid-mass at each cell coordinate, the same
masses over the same boxes with the knots moved half a cell. The
construction is recorded per axis, and an axis whose cell partition could
not be built falls back to "chord" on its own with the reason in
theta_within_cell_declined ("support_<kind>", "single_node",
"boxes_do_not_tile", "no_usable_node"). A "log_quadratic" request
falls back to "box_uniform" first, with "unresolved" (no row
resolves its own conditional), "not_a_grid_axis" or
"no_node_density" as the reason.
theta_cell_edge_coord, theta_cell_edge_declined – per axis, the
COORDINATE that axis's outer cell edges were mirrored in, and why the
support the axis declares did not produce them. A
declared support is authoritative: its mirrored edge is used when finite
and inside it, and otherwise the axis reports its extreme grid
coordinate – which the containment test has already placed inside the
support – rather than falling through to a coordinate guessed from the
node values, which is what put a lower bound of 0 on a positive axis
and a bound above 1 on a unit one. An axis that declares no support at
all keeps the guess by design, but its mirrored edge is checked for
being a representable double that brackets the coordinates, and falls
back to the extreme grid coordinate with
"mirrored_edge_not_representable" when it is not.
theta_cell_edge_declined is NA where nothing was declined.
outer_grid_cell_width, outer_grid_axis_sd, outer_grid_h_over_sd –
per axis, the cell width and the posterior SD in that axis's own
coordinate, and their ratio. A within-cell reconstruction resolves an
interval endpoint to within one cell, so how much of the reported width
and of its realized coverage is a property of where the grid fell rather
than of the posterior is governed by this ratio; below
.nl_diag("grid_resolved") the cells are narrower than the posterior
they discretize and the two constructions converge to each other. NA on
an axis whose own marginal is maximal at an endpoint, which is the
placement question outer_grid_railed_axes reports.
outer_grid_resolution_declined – per axis, why that axis carries no
ratio, from the closed vocabulary too_few_nodes, mode_at_edge,
coord_not_finite, spacing_not_finite, stencil_degenerate,
curvature_not_negative; NA on an axis that scored. The reasons are
not interchangeable: mode_at_edge says the grid does not contain that
axis's own posterior mode, which is a stronger statement about the fit
than any ratio, and the whole-grid resolved verdict is withheld while
one is present rather than being read off the axes that did score.
prune_cheap_log_marginal, prune_mask, prune_n_pruned,
prune_tol, prune_screen_iters – present only when prune = TRUE
and the safety gate did not fall back. Cheap-pass log-marginals at every
cell – the truncated solve's value plus half its Newton decrement, the
second-order estimate of the converged value the screen ranks by, so a
cell whose inner mode had far to move from its warm start is not ranked
on where the truncation left it (gcol33/tulpa#919); that decrement is
prune_screen_decrement –
a logical mask of pruned cells, the pruned-cell count, the
threshold actually applied, and the screening depth the driver actually
ran at. Pruned cells have log_marginal = -Inf so they get zero
weight under .nl_normalise_weights_safe.
prune_log_gap_cut, prune_cheap_lm_spread, prune_min_keep,
prune_n_floor_restored – the screen read on the scale it operates on:
how many nats below the best screened cell the tolerance cut at, how
many nats the screened surface spans, the kept-cell floor applied, and
how many cells that floor put back. A cut that is a sliver of the spread
is a tolerance with no resolution on this grid.
prune_dropped_mass_bound, prune_n_repaired, prune_gate_mass – the
posterior mass the dropped cells may carry, with each lifted by the
worst screening error measured on a kept cell; how many dropped cells
the driver solved in full to bring that under the gate; and the gate.
outer_mode_carried – TRUE on a placement refit that read the outer
mode it was laid from rather than finding it again, which it does unless
the placed grid rails.
prune_fallback_triggered, prune_fallback_reason – present only when
a fallback to the full grid fired. The returned fit is the full-grid
(unpruned) result; the reason string records whether the cheap-pass
safety gate tripped or the screened grid left the placement pass no
curvature.
n_threads_outer_requested, n_threads_outer_realised – the outer-grid
width asked for and the width the grid ran at. The driver clamps the
request to the team the OpenMP environment hands out, so a fit launched
under OMP_NUM_THREADS=1 with n_threads_outer = 10 runs serial; the
pair is what a timing has to be recorded against.
converged, n_iter, score_max – per cell, whether the inner Newton
reached a mode within control$max_iter, the iterations it took, and
the largest absolute component of the joint penalized score at the
mode it reported (the residual a cell that stopped short stopped at). A cell that
stopped without a mode is solved again from the mode of its nearest
converged cell (nonconverged_resolve counts the cells that first
stalled and those the re-solve recovered); one that still has no mode
stays in the integration at the value it stopped at, is listed in
nonconverged_cells with the posterior mass such cells carry in
nonconverged_mass, and is named by diagnostics(). Past the screen's
mass gate the fit warns.
Each arm's linear predictor reads
\eta_{arm} = X_{arm} \beta_{arm} + \sigma_{arm} \cdot z_s,
where z_s is a unit-precision latent (ICAR(tau=1) for ICAR/BYM2,
or the BYM2 mix; CAR_proper uses the structure of
D - \rho_{car} W). All arms share the donor amplitude
\sigma from the outer-grid sigma_grid axis. The copy arm's
amplitude is \sigma_{arm} = \alpha \cdot \sigma, where \alpha
is a direct outer-grid axis taken from copy$alpha_grid. The Cartesian
product is over (sigma, [rho/rho_car,] alpha[, phi]). Direct \alpha
as an outer-grid axis (rather than a post-hoc ratio
\alpha = \sigma_{pos} / \sigma_{occ}) avoids plug-in bias on the
weakly-identified ratio at small n_pos and lets a regularizing
hyperprior land on \alpha directly.
Rue, Martino & Chopin (2009). Approximate Bayesian inference for latent Gaussian models by using integrated nested Laplace approximations. JRSS-B 71(2):319-392.
tulpa_nested_laplace() for the single-arm engine.
set.seed(1)
S <- 25L # shared spatial units (chain graph)
nb <- lapply(seq_len(S), function(s) setdiff(c(s - 1L, s + 1L), c(0L, S + 1L)))
nn <- lengths(nb)
field <- as.numeric(scale(cumsum(rnorm(S, 0, 0.4))))
mk_arm <- function(m, fam) {
si <- sample(S, m, replace = TRUE); x <- rnorm(m)
lin <- 0.2 + 0.5 * x + 0.8 * field[si]
y <- if (fam == "binomial") rbinom(m, 1L, plogis(lin)) else lin + rnorm(m, 0, 0.5)
list(y = as.numeric(y), n_trials = rep(1L, m), X = cbind(1, x),
spatial_idx = si, family = fam, phi = if (fam == "gaussian") 0.5 else 1)
}
prior <- list(type = "icar", n_spatial_units = S,
adj_row_ptr = c(0L, cumsum(nn)), adj_col_idx = unlist(nb) - 1L,
n_neighbors = nn, sigma_grid = c(0.3, 0.7, 1.4))
fit <- tulpa_nested_laplace_joint(
responses = list(occ = mk_arm(200L, "binomial"), pos = mk_arm(200L, "gaussian")),
prior = prior)
fit$theta_mean # shared field amplitude, integrated across both arms
Add the following code to your website.
For more information on customizing the embed code, read Embedding Snippets.