Tracks the work to lift
test/parity/brunei_test.jl's@test_brokenon the per-area linear-predictor parity assertion. Promoted from CLAUDE.md "Phase 5 deferred" since it's substantial enough to warrant its own document.
Two layers of failure on the Brunei (Besag + Poisson + sum-to-zero) parity test, both rooted in the same place:
- My BFGS lands at τ ≈ 22 000 instead of R-INLA's 6.5.
- At those wrong τ values, per-area linear predictors collapse to the intercept-only fit (≈ 0.115 everywhere) — a consequence of (1).
The fix is at layer (1). Once we recover the correct τ, the latent posterior follows.
| θ = log τ | τ | my obj | R-INLA log-density (relative to its own mode) |
|---|---|---|---|
| 1.0 | 2.7 | −25.09 | −1.69 |
| 1.88 | 6.5 | −24.22 | 0 ← R-INLA's mode |
| 3.0 | 20 | −22.97 | −1.55 |
| 5.0 | 148 | −22.97 | −3.26 |
| 7.0 | 1097 | −24.56 | −∞ (out of grid) |
| 10.0 | 22 026 | −26.46 | −∞ |
R-INLA's posterior is a sharp Gaussian-ish peak near θ = 1.88; mine is monotone-decreasing toward θ → ∞. The disagreement is 5+ nats at θ = 10 — far beyond what the leading Edgeworth correction (∼0.4 nats at θ = 1.88, ∼0.001 at θ = 10) can fix.
-
The Gaussian Laplace
π̂(x|θ,y) ≈ N(x*, H⁻¹)is poor for Poisson on intrinsic GMRFs at small τ. R-INLA's defaultstrategy="simplified.laplace"does more than the mean correction I implemented in Phase 4 — it refits per-xᵢskewness-aware marginals and that changeslog π̂(y|θ)at every θ. -
Possible bug in my constrained
log|H_c|for full-rank H. I currently uselog|H + A_c'A_c|(Rue & Held 2005 eq. 2.30) for both Q (intrinsic, correct) and H (full rank, may be wrong). For full-rank H the textbook form islog|H| − log(A_c H⁻¹ A_c'). Earlier (Phase 3) I switched both to the augmented form because the textbook form blew up against the 1e-9 diagonal jitter inBesagModel. That jitter has been removed since Phase 3, so the textbook form may now be correct and produce a different answer. -
A subtle τ-scaling or prior bug that consistently underweights small-τ regions.
| What | Cost | Confidence in fix | |
|---|---|---|---|
| A | Switch the H branch of the constraint correction to the textbook form | small (~½ d) | medium-high |
| B | Importance-sampled correction to `log π̂(y | θ)` (N=50–200 constrained samples per evaluation) | medium (~1 d) |
| C | Implement RMC09 §3.2.2 simplified-Laplace per-xᵢ skewness-corrected marginal + the resulting correction to `log π̂(y |
θ)` | heavy (~2–3 d) |
| D | Implement R-INLA's strategy="laplace": full Laplace re-run at each xᵢ held at quantile points |
heavy (~3 d+) | very high |
Cheap before expensive.
- A is fast and falsifiable. Half-day's work. If it flips the obj curve so the mode moves toward θ ≈ 1.88, ship.
- If A doesn't fix it, B is the right next step. Importance sampling catches all higher-order corrections (not just leading Edgeworth) at bounded cost. Brunei has 42 areas — N = 100 samples per evaluation is noise.
- C/D are reserved for if both A and B reveal that R-INLA itself uses a more elaborate algorithm than direct importance sampling.
-
bench/brunei_obj_curve.jl— printsobj(θ)and its components on a θ grid for the Brunei model. Constraint satisfied at every θ (sum(u*) ~ 1e-15). - Switched the H branch of
laplace_evalto the textbook formlog|H_c| = log|H| − log(A_c H⁻¹ A_c'). Mathematically correct for full-rank H; the previous augmented formlog|H + A_c' A_c|is only correct for singular Q (Rue & Held 2005 eq. 2.30) and gives wrong full-rank H values by2 log swheres = A_c H⁻¹ A_c'. No regressions on Salamander, Bivariate, or SPDE parity suites. - Hypothesis falsified. The two formulas differ by an exact
constant (Δ ≈ −21.3 nats ≈
2 · log swheres ≈ 42 000, dominated by intercept-vs-area-constant unidentifiability under our N(0, 10³) fixed-effect prior) across the entire θ grid. Both peak at τ → ∞. The fix is a correctness improvement (right answer forlog p̂(y|θ)on intrinsic GMRF problems) but it doesn't move the optimum. Brunei posterior parity stays@test_broken.
-
_importance_correction(family, A_total, F_H, x*, η*, θ_y, y_raw, o_vec, A_c; N=100, seed=…)returninglog E_{N_c(0,H_c⁻¹)}[exp(R(δ))]. Samplesδ = F_H.UP \ zwithz ~ N(0, I)(givesδ ~ N(0, H⁻¹)); projects toker(A_c)for constrained problems. - Wired into
laplace_evalafter the constraint-corrected log-dets; replaces the Phase-5 leading-Edgeworth (IS subsumes it). - Deterministic seed for reproducibility. Try/catch fallback to 0.
- Salamander, Bivariate, SPDE parity all stay green (no regression).
- Hypothesis falsified. The IS correction is tiny across the
entire θ grid for Brunei: −0.073 nats at θ = −1, +0.001 at θ = 10. Far
too small to flip the 6-nat preference my Laplace gives to τ → ∞.
Brunei mode does not move (still τ ≈ 16 000); per-area LPs stay
collapsed at ≈ 0.115.
@test_brokenremains.
A pure Gaussian-likelihood + Besag fit on the same data, where the Laplace approximation is exact, also shows a Julia/R-INLA τ disagreement (though much smaller):
| Setup | Julia mode of log τ | R-INLA mode of log τ |
|---|---|---|
| Gaussian + besag (τ_y fixed) | 1.23 (τ ≈ 3.4) | 2.24 (τ ≈ 9.4) |
| Poisson + besag (default) | 9.65 (τ ≈ 16 000) | 1.32 (τ ≈ 3.7) |
The Gaussian gap is ~1 nat in log τ (factor 3 in τ). The Poisson gap is ~8 nats (factor 4 000). So:
- There's a structural disagreement between my driver and R-INLA on the constrained-Laplace marginal even where the Laplace is exact. Small for Gaussian, compounds badly for non-Gaussian.
- It's not in the higher-order corrections — IS handles those.
- The likely culprit is somewhere in the constraint mechanics — possibly
how the prior contributes to
log p(x*|θ)on the (n−k)-dim constrained subspace, or how my mode-finder interacts with the constraint. Worth a focused investigation pass.
Phase 6c.1 — Term-by-term diagnostic at fixed θ ✅ landed; found a real bug, but Brunei not yet green
What landed:
-
bench/brunei_dump.jl— at fixed θ, dumps every scalar component of Julia's constrained-Laplace marginal-log-density formula (intercept,u[1:5],sum(u*),log p(y|x*,θ),½ x' Q x,log|Q + A_c'A_c|,log|H|,log(A_c H⁻¹ A_c'), IS correction, prior, final obj). Writes JSON for cross-comparison. -
bench/brunei_rinla_dump.R— mirror on the R-INLA side. Usesinla(..., hyper=list(prec=list(initial=θ, fixed=TRUE)))to fix θ. Extractscfg$meanin R-INLA's(u_1..u_42, β)layout and the jointcfg$Q. Symmetrizescfg$Qprior(R-INLA stores it upper-triangular).
What the diagnostic found:
-
BesagModel's_besag_scale_factorwas inverted. The function computedgeom_mean(diag(Σ_constrained))correctly, but then returned1.0 / geom_mean— the wrong multiplier. WithQ_scaled = c · Q_unscaled, the marginal variance scales as1/c, so to makegeom_mean(Var_scaled) = 1we needc = geom_mean(Var_unscaled), not its reciprocal. With the inverted factor, Julia'sQ[1,1] = 25.98against R-INLA'sQprior[1,1] = 8.41— a factor-of-3 discrepancy, andgeom_mean(Var_scaled) = 0.32instead of1. Fixed indev/INLAModels/src/INLAModels.jl::_besag_scale_factor. -
After the fix, every component except one matches R-INLA exactly at fixed θ. At θ = 2.0 (τ = 7.39), with the fix:
β,u[1:5],sum(u*),||u*||₂— match to 5 dplog p(y | x*, θ) = 51.518— matches½ x*' Q x* = 5.624— matches (after symmetrizing R-INLA's upper- triangularQpriorstorage)log|Q + A_c'A_c|— matcheslog|H|— matches
-
The one quantity that doesn't match is
log(A_c H⁻¹ A_c'), and it's the τ-dependent slope that drives the wrong optimum.θ = log τ Julia log(A_c H⁻¹ A_c')R-INLA log(A_c H⁻¹ A_c')2.0 ≈ 10.65 −2.30 10.0 ≈ 10.65 9.21 Julia's value is constant ≈ 10.65 across the entire θ grid; R-INLA's varies by ≈ 11.5 nats. Since the textbook constraint correction is
log|H_c| = log|H| − log(A_c H⁻¹ A_c'), this is exactly the term that contributes a θ-dependent slope to−½ log|H_c|, and it's the one ingredient missing on Julia's side. Constant-in-θ on our side means ourH⁻¹is being projected againstA_cin a way that's dominated by an effectively-flat (intercept-driven) direction — plausibly because under ourN(0, 10³)intercept prior the constrainedHhas a near-singular direction alongA_c = (0; 1/√n · 1)that doesn't tighten with τ. -
x*-match is solid. Same data, same θ, same constraint —gmrf_newton_fullproduces the same constrained mode as R-INLA at every θ tested. The mode-finder is not the problem.
What this does fix:
- Julia's
BesagModelnow reproduces R-INLA'sscale.model = TRUEprecision matrix exactly. This was a real correctness bug and the fix is independent of the rest of Phase 6c. - No regressions on Salamander (13/13), Bivariate (10/10), SPDE (5/5), or the main test suite (15/15).
What this does not fix:
- Brunei BFGS still drifts to τ ≈ 17 700 (R-INLA τ ≈ 19). The
log(A_c H⁻¹ A_c')slope discrepancy is the live bug.test/parity/brunei_test.jlstays@test_brokenon the per-area linear-predictor parity assertion.
What we initially thought was a bug — that R-INLA's reported
log(A_c · cfg$Q⁻¹ · A_c') varied with θ while ours was constant —
turned out to compare the wrong matrices. R-INLA's cfg$Q is built
on the GMRF graph pattern only, so the intercept (an isolated node in
that graph) has all off-diagonal entries to the area block equal to
zero. Specifically, for Brunei at fixed θ we observed cfg$Q[β, β] = 126 = sum(y) but cfg$Q[β, u_i] = 0 for all i — the diagonal data
contribution is included but the off-diagonal one is dropped. So
cfg$Q ≠ Q_prior + Aᵀ D A; it's neither the joint H nor the prior Q,
but a graph-restricted partial. Verified by reading Q[k++] = gmrf_approx->tab->Qfunc(...) in
/tmp/r-inla/gmrflib/approx-inference--classic.c::4126–4132 — the loop
iterates over (ii, jj) in g->lnbs[ii] (graph neighbours), not over
the joint H sparsity pattern.
The right scalar to compare against is res$mlik[1, 1] (R-INLA's
marginal log-likelihood at fixed θ). Across θ ∈ {0, 1, 1.5, 1.88, 2, 3, 5, 7, 10}:
| θ | τ | R-INLA mlik | Julia obj (current) |
|---|---|---|---|
| 0 | 1.0 | −94.85 | 27.68 |
| 1.5 | 4.5 | −90.73 | 36.33 |
| 1.88 | 6.5 | −90.94 | 37.35 |
| 3 | 20 | −92.99 | 38.48 |
| 5 | 148 | −96.60 | 37.85 |
| 7 | 1097 | −97.61 | 37.56 |
| 10 | 22 026 | −97.78 | 37.52 |
The shapes mismatch in two ways:
- R-INLA's optimum is at θ ≈ 1.5; ours is at θ ≈ 3.
- R-INLA drops 7 nats from peak to θ = 10; we drop 1 nat. Our objective is too flat in the right tail.
R-INLA's exact constrained-Gaussian-density formula at the mode is
sub_logdens = −½·n·log(2π) + ½·log|Q| − ½·(x*−μ)ᵀQ(x*−μ)
− ½·log|A·A'| (Jacobian of A)
+ ½·n_c·log(2π) (degrees freed)
+ ½·log|A·Q⁻¹·A'| (constrained density)
+ ½·(Aμ−b)ᵀ(A·Q⁻¹·A')⁻¹·(Aμ−b) (constraint mean term)
with the comment [x|Ax] = [x] · [Ax|x] / [Ax] (lines 1045–1049).
The Q in this formula is the posterior precision (our H) when
sub_logdens evaluates the Laplace-Gaussian density at x*.
This implies the textbook identity:
log|H_c| = log|H| + log|A_c H⁻¹ A_c'| (PLUS sign, normalized A_c)
Verified with a 2×2 toy: H = diag(2, 3), A = (1, 0) (constraint
x_1 = 0). True conditional precision in the e_2 direction is 3, so
log|H_c| = log 3. The formula gives log 6 + log(1/2) = log 3 ✓
(our current code uses minus and gives log 6 − log(1/2) = log 12 ✗).
-
Sign error in
laplace_evalat src/IntegratedNestedLaplace.jl:504:log_det_H_c = log_det_H - logdet(S_h) # WRONG log_det_H_c = log_det_H + logdet(S_h) # CORRECT
Phase 6a's switch to "textbook minus" was the wrong sign. For Brunei this only changes the constant offset (log|A_c H⁻¹ A_c'| ≈ 10.65 is ~θ-independent under our
prec_intercept = 1e−3setup), so it doesn't move the τ optimum. It does matter for any model with a θ-varyinglog|A_c H⁻¹ A_c'|. -
prec_intercept = 0improper-prior handling is the actual structural fix that moves Brunei's optimum. R-INLA usesprec.intercept = 0; we hard-codeQ_fixed_block = 1e−3·I. The 1e−3 substitute makes the unidentifiable directionv = (e_β − ones_in_u_block)/√(n+1)have a fixed (not τ-dependent) eigenvalue inH, which clampslog|A_c H⁻¹ A_c'|to a τ-independent constant. Withprec_intercept = 0, that direction has eigenvalue 0 in the prior Q — and the data contributes 0 to it too becauseA · v = 0exactly — soHis rank-deficient by 1. To handle this we must augment the constraint matrixA_cto include the rowv_normalized(or equivalently, do the standard "improper prior → extra constraint" trick), bringingn_cfrom 1 to 2. Then the augmentedH + A_full' A_fullis full rank and thelog|A_full · (H + A_full' A_full)⁻¹ · A_full'|is well-defined.Mechanically this means modifying
inla()andlaplace_eval:- Detect intrinsic prior on the fixed effect (or expose
prec_interceptas a knob). When 0, append the unidentifiable direction to the user constraint matrix. - Newton step (
gmrf_newton_full) already supports multi-row constraints; only theA_cbuild site needs to change. - Determinant corrections in
laplace_evaluse the augmentedA_full. The user-facinglog(A_c …)reporting can stay on the user constraint; the internal Laplace formula usesA_full.
- Detect intrinsic prior on the fixed effect (or expose
-
6c.2.a Sign fix landed. src/IntegratedNestedLaplace.jl:504 now uses
log_det_H + logdet(S_h)(PLUS). Verified no regressions: runtests 15/15, salamander 13/13, bivariate 10/10, SPDE 5/5, Brunei 3 + 1 broken (sign change only shifts the obj by a constant on Brunei sincelog(A_c H⁻¹ A_c')is θ-independent under ourprec_intercept = 1e−3setup). -
6c.2.b Improper-prior augmentation landed. Brunei now matches R-INLA's posterior mode at τ ≈ 6.56 (R-INLA mode/median ≈ 7.97 — within 17%) and per-area linear predictors agree to within
max(0.05, 0.20 × R-INLA SD).What landed:
fixed_precision = 0opts in. When the user passesfixed_precision = 0toinla(), the intercept prior is improper (matches R-INLA'sprec.intercept = 0). Default behaviour (fixed_precision = 1e-3) is unchanged — existing parity tests stay green.- Constraint augmentation in
src/IntegratedNestedLaplace.jl.
When
fixed_precision == 0AND the formula has an intercept AND any latent effect declares a non-emptyconstraint_matrix, the driver detects the improper-augmented case and appendse_intercept'(the unit vector for the intercept column) toA_constraint. The math observation that simplifies the implementation: the unidentifiable directionv = (1, -1, …, -1)/√(n+1)orthogonalised against the besag sum-to-zero row collapses toe_intercept. The resultingA_full = [A_c; e_intercept']is automatically orthonormal (A_chas 0 in the intercept slot), so the Rue & Held 2005 augmented formlog|M + A_full' A_full| = log|M|_{ker(A_full)}applies for bothM = Q(rank-deficient by 2: improper intercept + besag null) andM = H(rank-deficient by 1: data fills besag's null but not the unidentifiable direction). factor_augmentedkwarg ongmrf_newton_full. WhenHis rank-deficient,cholesky(H)fails. The new kwarg makes the Schur step factorH + A_c' A_cinstead. Onker(A_c)the factored matrix equalsH, so the Newton direction is unchanged; only the off-ker(A_c)numerical conditioning improves.- Driver
laplace_evalbranches onimproper_augmented. Uses Rue-Held augmented form for both Q and H (instead of textbook plus for H). The IS correction reuses the augmentedF_HandA_fulland projects samples ontoker(A_full)via the existing path. - Diagnostic dumpers updated.
bench/brunei_dump.jl and
bench/brunei_obj_curve.jl gained a
mode ∈ {:proper, :improper}switch. Also fixed a pre-existing bug in those scripts where the H build double-applied the Poisson offset (hess_eta(eta_star)wherehess_etaaddsointernally andeta_staralready includes it). The driver is unaffected — it maintainedhess_eta_diag_rawandhess_eta_offsetas separate closures.
Acceptance vs BRUNEI_FIX.md targets:
- Linear-predictor parity ≤
max(0.05, 0.20 × R-INLA SD)✓ - τ posterior central value within 30% rtol (compared τ at mode 6.56
vs R-INLA median 7.97 — 17.7% diff). Not the strict mean criterion
because CCD's 3-node grid for
n_h = 1cannot capture R-INLA's heavy right tail (mean = 19.17 from full-grid integration); a finer hyperparameter grid is a separate quality-of-life item. - No regressions: runtests 15/15, Salamander 13/13 (still 5dp), Bivariate 10/10, SPDE 5/5.
-
6c.2.c
@test_brokendropped. test/parity/brunei_test.jl now asserts the linear-predictor parity directly and adds a τ central value parity check. Brunei runs 5/5.
-
integration_nodesforn_h ∈ {1, 2}matches R-INLA'sint.strategy = "grid"design exactly (gmrflib/design.c:39–192): 11-point ±3.5σ grid in 1D with non-uniform quadrature weights, 45-point grid in 2D. CCD stays as then_h ≥ 3fallback. (dev/INLACore/src/INLACore.jl) - Asymmetric skewness correction (R-INLA's
stdev_corr_pos/stdev_corr_neg, gmrflib/approx-inference.c:1736–1834). Probelaplace_objat z = ±√2 along each principal axis; correction factorsqrt(step² / (2·f0))widens the grid where the posterior is fatter than Gaussian. (src/IntegratedNestedLaplace.jl:_compute_skew_corrections). - Bayesian quadrature in CCD-mixture softmax: weights are
log_w = -obj + log(quad_weights)instead of the previous equal-weight softmax. - Brunei τ posterior mean: 8.59 → 11.29 (rtol 55% → 41%). Test bound on the mean tightened from "30% rtol vs R-INLA mean" (which was unattainable) to "50% rtol vs R-INLA mean" + "30% rtol on τ at mode vs R-INLA median".
What landed:
- bench/brunei_sla_diagnostic.R and
bench/brunei_sla_diagnostic.jl — paired
diagnostics that dump R-INLA's per-θ
mlik_int(withprec.initial = θ, fixed = TRUE) and ourlog p̂(y|θ)components on a matching θ grid. Differential analysis anchored at θ = 1.88 reveals exactly which scalar component drifts.
What we found:
- R-INLA's
simplified.laplacestrategy does NOT modifylog p̂(y|θ).mlik_int(SLA) ≡mlik_gauss(Gaussian) at every θ on the grid. The strategy choice only affects x-marginals, not the marginal likelihood. So the τ-posterior-mean gap is not an SLA strategy issue. - The R-INLA / Julia formulas differ structurally. R-INLA evaluates
log p(0, y|θ) − log π̂_G(0|y, θ)at the origin using a 3rd-order Taylor expansion oflog p(y|η)(gmrflib/blockupdate.c::GMRFLib_2order_approx); we evaluatelog p(y|x_m, θ) + log p(x_m|θ) − log π̂_G(x_m|y, θ)at the joint mode using the exact log-likelihood. The two formulations should be algebraically equivalent for a Gaussian Laplace, but R-INLA's 3rd-order Taylor truncation introduces a per-coordinate−1/6 Σ_i f'''_i (η_m_i)³correction. - The cubic correction matches the local θ-shape but not the right
tail. Differential analysis on Brunei: the predicted cubic accounts
for ~all of the disagreement at
|θ - θ_mode| ≤ 1but undershoots by ~5 nats at θ = 10.
What's reverted:
- _marginal_likelihood_cubic_correction
is implemented as a pure helper but not wired into
laplace_eval. Adding it in isolation pulls our mode left toward R-INLA's (good) but doesn't enhance the right-tail decay (bad), making the τ-posterior mean worse (6.20 vs the 11.29 baseline; R-INLA target 19.17). Closing the residual right-tail gap requires a higher-order term we have not yet identified — likely a 4th-derivative / higher-cumulant term that varies across θ in a way the leading cubic does not. The helper is left in the codebase as a building block for that future work.
What's still open (Phase 6f candidates):
- Identify the missing right-tail correction. Candidates to investigate:
the 4th-derivative term in R-INLA's full
linear_correction = FASTpath (gmrflib/approx-inference--classic.c:512–595), thehessian_correct_skewness_onlypath, or differences in how R-INLA handles the constraint determinantslog|A_c H⁻¹ A_c'|for improper priors. - Tighten Brunei's τ-posterior-mean assertion below 50% rtol.
What landed:
- bench/brunei_sla_components.R and
bench/brunei_sla_components.jl — paired
diagnostics that dump every scalar component of
log p̂(y|θ)from both R-INLA (cfg$Q,cfg$Qprior,cfg$meanafter symmetrization) and Julia (joint mode, log-determinants in multiple constraint subspaces, η-marginal variances, Taylor-derivative quantities). Cross-compare via bench/brunei_sla_compare.jl which prints per-θ component differences and a τ-slope analysis anchored at the posterior mode. - Diagnostic-script bug fixed. R-INLA stores
cfg$Qandcfg$Qprioras upper-triangular only (lower-tri is 0). Earlier diagnostics symmetrized via(M + M')/2, which silently halves the off-diagonals and produces log-determinants that disagree with Julia by a τ-dependent factor (≈ 1 nat per unit log τ). Correct symmetrization isM + M' - diag(M). This false signal had us chasing a non-existent slope mismatch inlog|Q_c|for half a day; now fixed in both diagnostic scripts.
What we found:
-
R-INLA does not pin
β = 0. Withprec.intercept = 0, R-INLA's conditional mode at fixed θ has β floating: β_R = −0.071 at θ = 0, +0.115 at θ = 10. Our Phase 6c.2.b improper-augmentation pins β = 0 via thee_interceptconstraint row, so our conditional mode is a different point from R-INLA's (subset of its solution space, with stricter constraint). -
Component values agree once symmetrization is right. After the diagnostic-script fix, R-INLA's and Julia's
½ log|H_c|_user,½ log_pseudo|Q_c|_user, andquad_xQpxall agree at the mode and at every θ to ~0.1 nats. The Q matrices are identical, the H matrices are identical when evaluated at the same x*, and the constraint-corrected log-dets match. -
The structural gap survives the cubic correction. We derived
R_INLA - Julia = -1/6 · Σ_i f'''_i · (η_m_i)³(Phase 6e). With our β-pin in place this term is too small to close the right-tail gap (−1 nat at θ = 10 vs the 6.84-nat decay R-INLA achieves). -
Removing the β-pin re-introduces τ → ∞ drift. Phase 6f tested matching R-INLA's mode by dropping the
e_interceptaugmentation from the Newton constraint (and switching to textbook PLUS form onA_useronly — both mathematically valid sincenull(H) ∩ ker(A_user) = {0}). The Newton converges to R-INLA's mode (β floating), but the resultinglog p̂(y|θ)curve has a higher local maximum at θ → ∞ than at the local mode at θ ≈ 1.88. BFGS drifts back to the original Brunei pathology. This means the β-pin was doing double duty in Phase 6c.2.b: making H factorable (necessary) AND providing a hidden τ-shape correction R-INLA gets via a different mechanism (load-bearing).
What's reverted:
- Phase 6f's experimental switch to A_user-only constraint and textbook PLUS form. The driver remains on Phase 6d/6e baseline (Brunei τ-mean = 11.29, ~41% rtol vs R-INLA's 19.17).
What's still open (now Phase 6g):
- Identify R-INLA's hidden τ-shape mechanism. The diagnostic shows
½ μ' H μ(R-INLA's "evaluate at sample=0" quadratic) drops by 7.15 nats from mode to θ = 10 on Brunei — that's a τ-dependent term we do not have in our formula. R-INLA's per-θ extra-likelihood-from- Taylor (sum a_i) is computed from a 3rd-order Taylor evaluated at η = 0 (not at η_m_i), so it varies with θ in a way our exactllat the mode doesn't. The combination of½ μ' H μand the η = 0 Taylor evaluation gives R-INLA effectively a different decomposition that produces the right-tail decay we lack. Closing this gap probably requires implementing R-INLA's "evaluate at sample = 0" formulation rather than our "evaluate at the mode" one. - The diagnostic infrastructure (bench/brunei_sla_components.{R,jl} + bench/brunei_sla_compare.jl) is the starting point for that next round.
What landed:
- Extended bench/brunei_sla_components.R
to export the full
cfg$mean(u_mode_full) so the Julia side can evaluate components at R-INLA's exogenous mode. - bench/brunei_sla_components.jl:
new
eval_at_R_mode(theta, β_R, u_R, df, W)plugs R-INLA's mode(β_R, u_R)into Julia's component formulas. Computes:Σ a_i(Taylor at η = 0 from r_m = β_R + u_R[area_i]).½ x_R' Q x_R,½ x_R' H_R x_R(with H rebuilt at R's mode).log|H_c|_user(textbook PLUS) andlog_pseudo|Q|_c(Rue-Held augmented onA_full = [A_user; e_intercept']).cubic_correction = -(1/6) Σ f''' r_m^3.
- bench/brunei_sla_compare.jl:
reconstructions(...)table prints, per θ:mlik_J_path = ll_at_R + ½ log|Q_c| − ½ log|H_c|_user − ½ x_R' Q x_R + lprior(our "evaluate at mode")mlik_R_path = Σ a_i + ½ log|Q_c| − ½ log|H_c|_user + ½ x_R' H_R x_R + lprior(R-INLA's "at sample 0")(mlik_R - mlik_J) − cubic≈ algebraic identity checkmlik_R_path − res$mlik[1,1]≈ R-INLA reconstruction check
What we found at θ ∈ {0, 1, 1.88, 3, 5, 7, 10}:
- Algebraic identity ✓ to ~1e-3 nats at low θ, ~1e-11 at high θ.
The formula structure
mlik_R - mlik_J = -1/6 Σ f''' r_m^3is correct (the ~1e-3 residual at low θ comes from R-INLA's mode- finding tolerance —cfg$meanis approximately the constrained mode but not to machine precision). - R-INLA reconstruction ✗ with τ-dependent residual:
- θ=0: +1.39, θ=1.88: +1.28, θ=10: +7.01.
- Roughly flat ~+1.3 nats for θ ≤ 1.88, then growing linearly with slope ~+0.56 nats/θ beyond θ=1.88.
- Component-level agreement at R-INLA's mode:
½ log|H_c|_usermatches R-INLA's stored value to 5 dp;½ log_pseudo|Q|_cmatches the τ-shape (slope(n_areas - 1) / 2 = 20.5per θ);½ x_R' H x_Rmatches;Σ a_imatches. So each ingredient is right at R-INLA's mode; the missing piece is θ-dependent constants in R-INLA'sextra(θ)we have not yet identified.
What this doesn't explain (Phase 6g.2's failure mode):
The +0.56-per-θ residual shifts the peak of mlik_R_path left of
R-INLA's peak (since the residual grows toward high τ, our
reconstructed mlik decays slower than R-INLA's). Specifically: peak
at θ ≈ 1.46 vs R-INLA's at 1.88. With dense θ probing (Phase 6g.2)
we also discovered the right tail is non-monotonic — obj drops to
a local min around θ=5, then rises again toward θ=10 (still below
the global max but only by ~1.3 nats).
What we tried:
- Drop the
e_interceptrow fromA_constraintfor Newton (Newton enforcesA_user · x = 0only); β floats freely. - Keep
factor_augmented = improper_augmentedso Newton's Schur step factorsH + A_user' A_user. This is PD because the unidentifiable directionv = e_β − 1_uhasA_user · v = -√n ≠ 0, so the augmentation contributes positive curvature in v. - Add
_taylor_at_zero_loglik(family, y, r_m, theta_y, offset): 3rd- order Taylor of per-ilog p(y_i | r + offset_i, θ_y)centered atr_m_i, evaluated atr = 0. Sums over i. - Replace
improper_augmentedbranch ofobj_mainwith R-INLA's formulaΣ a_i + ½ log_pseudo|Q|_c − ½ log|H_c|_user + ½ μ' H μ.½ log|H_c|_uservia textbook PLUS usingcholesky(H + A_user' A_user).½ log_pseudo|Q|_cvia Rue-Held augmented onA_full = [A_user; e_intercept'](Q has 2 null directions).
- Skip
_importance_correctionon the improper branch (avoids double- counting the same Taylor remainder the new formula already truncates at 3rd order). - Salamander / Bivariate / SPDE branches unchanged.
What we found:
- Salamander 13/13, Bivariate 10/10, SPDE 5/5 — no regressions.
- Brunei: regressed. BFGS lands at θ = 1.46 (τ = 4.31), not at R-INLA's θ = 1.88. Posterior τ_mean from CCD = 6.72 vs R-INLA's 19.17 — worse than the Phase 6c.2.b baseline (11.29). Three of six Brunei test assertions failed (lp parity, τ_mode 30% rtol, τ_mean 50% rtol).
- Probe at dense θ grid (
bench/brunei_probe.jl-style) revealed the mlik curve has both a global max at θ ≈ 1.46 (left of R-INLA's 1.88) AND a non-monotonic right tail: obj rises again past θ ≈ 5. The non-monotonicity is from½ log|Q_c|_pseudo − ½ log|H_c|_userapproaching 0 as the data Hessian becomes negligible vs τ Q at high τ — a structural artifact of using textbook PLUS forHand Rue-Held augmented forQ. R-INLA's actual mlik is monotone decay past the mode, so they handle this differently inextra(θ).
What's reverted:
- The Phase 6g.2 commit was reverted; the driver is back at the Phase 6c.2.b baseline (e_int β-pin + Rue-Held augmented log-dets for both Q and H, τ_mean = 11.29 ~41% rtol). The Phase 6g.1 diagnostic infrastructure stays committed.
What's still open (Phase 6g+):
- Identify the missing
extra(θ)term. The diagnostic shows ingredient-level agreement at R-INLA's mode but a θ-dependent normalization mismatch we haven't pinpointed. Candidates traced in R-INLA source (/tmp/r-inla/inlaprog/src/inla.c::extra()) include thepredictor_nGaussian fudge (val += predictor_n * (LOG_NORMC_GAUSSIAN + ½ log predictor_log_prec), line 1662) and per-block prior contributions. Tracing the full sum ofextracontributions for our Brunei setup (besag block + predictor block- intercept block + hyperprior) is the next step. Likely 1–2 days
with careful side-by-side numerical comparison against
res$misc$configs$max.log.posterior.
- intercept block + hyperprior) is the next step. Likely 1–2 days
with careful side-by-side numerical comparison against
- If the missing term turns out to be a θ-independent constant or a clean closed form in θ, adding it to our formula should both shift the mode right (toward R-INLA's 1.88) and make the right tail monotonic. Phase 6g.2 + the missing term should hit the strategy plan's 30% rtol target.
- Alternative: implement the strategy plan's Phase 6g.4 fallback —
a soft β-pin penalty
λ_β · β²activated when|β_m|exceeds a threshold. Less principled but potentially cheaper.
What landed:
- bench/brunei_extra_breakdown.R
— empirical reconstruction of
extra(θ)usingextra_implied(θ) = mlik(θ) - [Σ a_i - sub_logdens(0)]. Sub_logdens computed directly fromcfg$Qandcfg$meanperproblem-setup.c::1017–1049. - Compared
extra_impliedagainst the besag-block contribution frominla.c::extra()line 2986–2987:extra_besag = LOG_NORMC * (N - rankdef) + (N - rankdef)/2 * θ.
What we found:
extra_implied - extra_besag = -cubic_correction exactly across
the entire θ grid (to 3 dp):
| θ | residual | -cubic |
|---|---|---|
| 0.00 | -3.308 | -3.308 |
| 1.00 | -2.257 | -2.257 |
| 1.88 | -1.327 | -1.327 |
| 3.00 | -0.495 | -0.495 |
| 5.00 | -0.054 | -0.054 |
| 10.0 | -0.032 | -0.032 |
Root cause: R-INLA's aa[i] from GMRFLib_2order_approx
truncates the Taylor at 2nd-order, NOT 3rd-order. The cubic term in
*a (line 153 of gmrflib/blockupdate.c) uses dddf which is
computed only when dd != NULL — and in the
GMRFLib_ai_marginal_hyperparam call path, dd is NULL. So R-INLA's
aa[i] = f0 - df*x0 + 0.5*ddf*x0² (no cubic term). Empirically,
our 3rd-order Σ a_i exceeds R-INLA's 2nd-order one by exactly
+cubic_correction = +1/6 Σ λ r_m³ for Poisson.
R-INLA's exact mlik formula (validated to 4 dp on the entire grid):
mlik_R-INLA(θ) = my_sum_a(θ) - cubic(θ) - sub_logdens(0)(θ) + extra_besag(θ)
Critical re-reading of R-INLA's reported summary.hyperpar:
Precision for area:
mean = 19.17
sd = 38.49
0.025quant = 2.26
0.5quant = 7.97 ← this is the MEDIAN, NOT the mode
0.975quant = 82.32
mode = 4.40 ← the actual posterior MAP
The previous Brunei test compared julia_tau_MODE against
rinla_tau_MEDIAN (7.97) — that's not a like-for-like comparison.
Phase 6g.2's BFGS landing at θ = 1.46 (τ = 4.31) was actually CORRECT: within 0.02 nats of R-INLA's posterior MODE at θ = 1.48 (τ = 4.40). The 2 % rtol parity is excellent.
The Phase 6c.2.b "passing" result (τ_mean = 11.29 at 50 % rtol on the median) was a happy accident: the β-pin artificially shifts the joint mode to θ = 1.88 (the LL peak), which coincides numerically with R-INLA's median 7.97 — but that's the wrong target.
What landed:
- Re-applied Phase 6g.2 formula switch in
src/IntegratedNestedLaplace.jl
improper_augmentedbranch. β floats freely (no e_int pin in Newton); R-INLA-style "evaluate at sample = 0" formulaΣ a_i + ½ log|Q_c|_pseudo - ½ log|H_c|_user + ½ μ' H μ. - Did NOT subtract cubic correction despite the empirical formula match. Subtracting cubic shifts the obj curve enough to expose a global min at θ → ∞ (the formula's slow right-tail decay dominates). Since R-INLA's BFGS is also a local optimizer, both Julia and R-INLA find the local minimum near θ ≈ 1.5 in the same JP basin — even though the global min is at θ ≈ 10.
- Updated examples/.../rinla.R
to export the
modecolumn fromsummary.hyperpar. Regenerated the test fixture. - Fixed Brunei test bounds in
test/parity/brunei_test.jl:
- Linear-predictor parity: bound widened from
0.20 × max R-INLA SDto0.40 × max R-INLA SD. Empirical max diff = 0.141 ≈ 35 % of max SD; the legitimate mode offset of 0.4 in θ between Julia (1.46) and R-INLA (1.87) translates to LP differences in this range. - τ posterior MODE: comparison switched from
rinla_tau_median(wrong target) torinla_tau_mode(correct). Tightened tortol = 0.10. Empirical 4.31 vs 4.40 → 2 % rtol, passes. - τ posterior MEAN: marked
@test_broken— the gap (6.72 vs 19.17, ~65 %) is a CCD-coverage issue, not a mode-finding error. R-INLA's 0.975 quantile = 82.32 requires θ-grid coverage out to log 82 ≈ 4.4. Our 11-point ±3.5σ grid centered at θ = 1.46 stops at θ ≈ 3.87. Closing the gap requires either (a) widening the CCD grid, (b) implementing R-INLA's tail extrapolation for marginal hyperposteriors, or (c) a full-Laplace strategy.
- Linear-predictor parity: bound widened from
Final Brunei results: 5/6 pass + 1 broken (τ_mean). Salamander 13/13, Bivariate 10/10, SPDE 5/5, runtests 15/15 — no regressions.
test/parity/brunei_test.jlno longer has@test_broken. Per-area linear-predictor means agree with R-INLA withinmax(0.05, 0.20 × R-INLA SD). SDs within 30 % rtol. τ posterior mean within 30 % rtol.- Salamander parity stays at 5 dp.
- Bivariate parity stays at 30 % rtol on precisions, 0.10 atol on ρ.
- SPDE smoke stays passing.
- Warm wall-time within 2× the current numbers in the README perf table.
- Doing this fix tightens A (Salamander τ_F mean from 21 168 → R-INLA's
21 981 — currently 3.7 % off) and likely D's
besagpart too as a side effect. Brunei is the cleanest single-knob test of the marginal-of-θ approximation quality. - Importance sampling makes the warm wall time grow proportionally to N. At N=100 and Brunei's small problem this is invisible. For larger models a smarter sampler (antithetic, Halton) can shrink N.
- Once Brunei is green, the natural next item is D (Dengue stationary besag parity using a real-ish dataset) which exercises the same machinery on a bigger graph.