Unified single-/two-level engine for the nested gravitation model (market price reverting to a latent production price, which has its own G'-driven OU). Work in progress toward 0.2.0.
nested-gravitation vignette (sections 16-17): recovery + 90% coverage
(n=3, pooled 0.91); adversarial negative controls (a unit-root DGP yields no
false fast gravitation; an m_v=0 DGP is covered at 0); multiple-imputation
coverage with a quantified, conservative disaggregation bias on kappa_m;
simulation-based calibration (Talts et al. 2018) showing uniform posterior ranks
for the linear core; and external comparisons (held-out forecast vs random walk /
AR(1) / kappa=0; PSIS-LOO across depth; wedge placebos) reported anti-overreach.
Section 17 develops the logical verdict: the "negative" external results are
deductively entailed by slow gravitation and corroborate, rather than refute, the
structural thesis. No package code changed (validation scripts are external).fit_ou_nested_mi).
Beyond the market price phi_draws, the anchors X (production price Phi),
V_value (value), CAPITAL_TOTAL (K) and COM may now each vary by
imputation: each accepts either a fixed matrix (the legacy path, used verbatim
for every imputation, byte-identical results) or a per-imputation set -- a list
of D matrices or a [T, S, D] array -- paired one-to-one with phi_draws.
This lets a single external generator propagate ALL the construction
uncertainty (market disaggregation, the imputed v/p split that feeds K and
V, and the reconstructed Phi) through the convergent K-DET / Variant-A
engine, pooled by Rubin -- so the uncertainty of V and K is carried by
EXTERNAL multiple imputation rather than an in-model latent (the in-model
Variant B / recon modes are not reopened). The observed aggregates TMG and
Gprime stay fixed. Per-imputation lengths are validated up-front (a mismatch
fails before any Stan fit). Additive, R-side only: the Stan model is untouched
and n_levels in {1,2} and the all-fixed path are byte-identical (equivalence
and the 7 goldens re-verified, 0 regressions). Decision D-IMPL-13.5 / D-IMPL-14.1.fit_ou_nested_mi, n_levels = 3).
The MI driver now accepts the value model: a named V_value argument (the
direct-price anchor c+v+p, fixed across imputations like X), the value
coupling m_v added to the default Rubin-pooled set, and the latent-Phi
pooling and the kappa^m > kappa^p separation evidence widened from the
2-level case to n_levels >= 2 (mirroring the Stan == 2 -> >= 2 refactor of
D-IMPL-10.1). n_levels in {1,2} is unchanged. At n_levels = 3 the joint
separation typically collapses (kappa^p is re-identified once the value term
absorbs the production mean). Additive, R-side only. Decision D-IMPL-11.1.evaluate_oos_nested). The
forecast recursion now adds the value term to the Level-2 attractor,
mu_{s,t} = m0_s + m1 G'_t + m_v V_{s,t}, via a new optional V_z argument and
an m_v component in meds. Both gated: the term is inert (byte-identical to
the Level-2 forecast) unless the standardized anchor V_z and a finite m_v
are both supplied, so n_levels <= 2 is unaffected. Decision D-IMPL-11.2.fit_ou_nested_mi now records, for each imputation, the chain-aware
rhat_max_pooled, ess_bulk_min_pooled and ess_tail_min_pooled over the
Rubin-pooled parameters -- the binding diagnostic for the reliability of the
pooling -- plus the global rhat_max, rhat_share, ess_bulk_min (over all
parameters, latent states included; reference only) and divergences. This
makes per-imputation convergence verifiable to an impeccable standard
(R-hat < 1.01, ESS-bulk and ESS-tail >= 1000, 0 divergences) on the
quantities that actually enter the pooled estimates, rather than on a global
extreme that a single weakly-identified latent cell can dominate. Additive
and numerically inert.n_levels = 3. The nested cascade extends to a third
level: the latent production price reverts toward a mean that also tracks the
VALUE anchor V (direct prices c+v+p = DirectPrices_Index, constructed
directly from k_cost + EBO), so prices of production gravitate around values
(Marx, Capital III ch. IX). New argument V_value (T x S, required for
n_levels = 3); the production reversion mean gains the term m_v * V with the
coupling m_v estimated under a neutral, falsifiable prior (the ch. IX
hypothesis is m_v > 0, adjudicated by the data). Values enter as a datum
(direct empirical construction), never solved simultaneously (no Leontief
inverse). The Level-2 gates were widened from == 2 to >= 2 so n_levels = 3
inherits all of the 2-level machinery; n_levels in {1,2} is byte-for-byte
unchanged (equivalence and all goldens re-certified, plus a new level3
golden). Decision D-IMPL-10.1.sigma_phi_meas_fixed).
In the deterministic limit the estimated anchor measurement SD piled up against
its lower bound 0 (a boundary funnel, G6; rhat 2.208 on the real panel). The
SD can now be held FIXED as a datum instead of estimated (sigma_phi_meas_fixed = 0.05), which is the sigma_meas -> 0 limit done well and removes the funnel.
On the real 37-sector panel this collapses rhat_max 2.208 -> 1.079 and lifts
E-BFMI from ~0.02 to ~0.9-1.1 (the boundary funnel was also dragging the energy
mixing). The estimated mode (looser prior) remains for the K-stochastic / recon
regime. Decision D-IMPL-9.4.Phi_lat). The latent production price is now
sampled directly (centered parametrization) instead of being reconstructed
from non-centered innovations. On the real 37-sector panel the former NCP
factorization induced a multiplicative non-identification ridge
(cor(sigma_p_tilde, log||Phi_innov||^2) ~ -0.98, E-BFMI ~0.03, rhat 1.5-2.0
in Phi_innov/sigma_p_tilde/m0_z); the centered form removes it (those
parameters now converge, the fit is ~13x faster and no longer saturates the
tree depth). The single-level mode is byte-for-byte unchanged (equivalence and
goldens re-certified). Decision D-IMPL-9.1.ou_geom_target(),
ou_geom_metric_euclidean(), ou_geom_metric_riemannian(), ou_geom_hmc(),
ou_geom_bridge()) — a self-contained R-native HMC with a pluggable metric
(dense Euclidean or position-dependent Riemannian SoftAbs / supplied-Fisher)
and an E-BFMI diagnostic, for probing and remedying hard posterior geometry.
Opt-in; the cmdstan fit path is untouched. Decision D-IMPL-9.2.fit_ou_nested() — single engine. n_levels = 1 reproduces the legacy
single-level model; n_levels = 2 fits the nested model with the latent
production price Phi, anchored by the constructed production-price index as a
noisy measurement. Dispatch over theta_separation ("soft"/"hard"
time-scale separation κ^m > κ^p), k_uncertainty ("meas" σ_Φ measurement
error; "recon" reconstruction with uncertain K, eq. 11), kappa_cap
(stability cap) and level_spec (per-level richness; all named configurations
realised).fit_ou_nested_mi() — multiple-imputation driver (Rubin's rule + mixture
posterior) coupling the disaggregation draws of the market price to the OU
model; default M = 25.Phi trajectory with credible bands, the
Level-2 parameters (kappa_p, kappa_m_base, mu_const, sigma_p, m1,
sigma_phi_meas) and the posterior evidence for κ^m > κ^p.evaluate_oos_nested() — out-of-sample
recursion in which the market price reverts to the latent production price
Phi propagated forward through its own Level-2 OU (never to a realized X);
diagnostics$oos is now populated for 2-level fits. extract_mu_trajectory()
reconstructs the G'-driven Level-2 mean mu_t = m0_s + m1 G'_t with bands
(fit$mu_path). The 2-level result now carries the S3 class
ou_nested_2level at the top level, with print/summary/plot methods
(plot types "phi", "mu", "separation"); ou_nested_mi gains
print/summary/plot ("phi", "fmi").level_spec). Each level
toggles cubic drift, sv (stochastic volatility), student_t tails and
hierarchy (cross-sector partial pooling); the switches act only in the
2-level mode (single-level is always the full legacy Level 1). The new
ou_level_spec() helper builds the named experiment configurations
"canonical" (N1-full/N2-lean), "both_full", "both_lean" and "n1_lean";
the single-level arm is n_levels = 1. Level-2 enrichment (cubic / SV /
Student-t on the latent production price) carries a Level-2 stability bound —
the cubic argument and the SV scale are clamped so the propagated latent path
cannot diverge (the Level-2 analogue of the 0.1.4 reversion-speed bound) — and
the 2-level fit uses a reduced default init radius so the latent recursion
starts in a well-behaved region. The canonical configuration reproduces the
exact linear Gaussian Level-2 OU (the richness blocks are length 0 when off).k_uncertainty = "recon" is now wired (eq. 11 / Hallazgo 2). The latent-
Phi anchor is reconstructed as Phi = k + K G' with the total capital
advanced K lognormally uncertain (non-centered prior f_Y around the point
estimate CAPITAL_TOTAL, scale priors$sigma_K_recon, default 0.10). The
reconstruction is standardized so that the sigma_K -> 0 limit reproduces the
"meas" anchor exactly (recon nests meas); a consistency guard warns when the
constructed index X departs from k + K G'. Requires the new k_cost
argument (cost price c + v, raw units) and n_levels = 2.summary/plot). When the
Level-2 switches are on, the latent production price's cubic coefficient
a3_p, Student-t degrees of freedom nu_p and time-varying SV scale path
sigma_p_t = exp(h_p/2) are added to factor_ou$level2, the Level-2 summary
table and a new plot(type = "sv_p"). They are derived from the posterior
draws, so the canonical configuration and the bit-exact single-level path are
unchanged (the fields are NULL when the switch is off).tests/testthat/test-recovery-by-config.R, gated by
BAYESOU_RUN_RECOVERY_CONFIG) checking 90% credible-interval coverage and LOO
discrimination, with the heavier script validacion/recovery_by_config.R; and
per-configuration bit-for-bit log_lik golden guards
(tests/testthat/test-golden-configs.R, fixtures generated by
validacion/make_golden_configs.R).inst/stan/ou_nested.stan (covers both
modes). fit_ou_nonlinear_tmg() is a thin wrapper of fit_ou_nested(n_levels = 1); its behaviour and result structure are unchanged. ou_nested_stan_code()
replaces ou_nonlinear_tmg_stan_code().(0, kappa_cap) via a logit link (TMG modulation inside the link). Unbounded
speeds let the latent path explore the explosive region (κ > 2 ⇒ |1−κ| > 1),
which broke convergence; the single-level path is untouched (exp link).inst/stan/ou_nonlinear_tmg.stan (the legacy single-level file) and
ou_nonlinear_tmg_stan_code(). The single-level mode of ou_nested.stan was
certified bit-for-bit equivalent to the retired file (recomputing log_lik
from the same draws via generate_quantities); the equivalence is frozen as a
golden fixture and guarded by test-equivalence.R.Robustness overhaul. Several changes alter results and are intentional; review the methodology section of the README and re-run with your data.
loo::loo() as a 3-D array
[draws, time, sector], which loo interprets as [iterations, chains, observations] — it silently treated time as chains and only the sectors as
observations. It is now reshaped to a proper [draws x observations] matrix
over the fitted window, with a chain_id so relative_eff() is correct.fit_window argument ("train" default / "full") keeps
the likelihood, log_lik, and OOS evaluation coherent.kappa0/kappa_COM/sigma_kappa, a3_0/sigma_a3,
beta00/beta0_COM/sigma_beta0 were declared but unused — only theta_s was
hierarchical. All four sector blocks now use a non-centered hierarchical
(partial-pooling) parameterization that actually uses the hyperparameters.evaluate_oos index bug: for horizons longer than the test window the
(T_train+1):(Tn-hh+1) sequence ran backwards and indexed out of range; it
now guards the window and returns NA/n_obs = 0 instead.use_train_loadings now defaults to TRUE (loadings from
training only), avoiding look-ahead in the orthogonalized TMG regressor.compare_models_loo: validates that both loo objects exist, are of
class loo, and have matching observation counts; deltaELPD is now computed
unambiguously as elpd_new - elpd_base.beta1 ~ Normal(0, 0.5) (was Normal(0.5, 0.25)): neutral on the TMG-effect
hypothesis (no sign baked into the prior).nu_tilde ~ Gamma(2, 0.1) (was Exponential(3)): weakly informative; the old
prior forced nu into (2,3) (extreme heavy tails).rho_s ~ Normal(0.7, 0.2) (was Normal(0.90, 0.05)): less rigid SV
persistence.beta1_mean, beta1_sd, nu_shape, nu_rate,
rho_mean, rho_sd.validate_ou_fit() is now a real validator: structural checks plus a Pareto-k
summary with a warning when PSIS-LOO is unreliable; separates MCMC convergence
from dynamic mean reversion.extract_convergence_evidence() no longer prints "CONVERGENCE GUARANTEED"; it
reports dynamic mean-reversion evidence and is aliased by the clearer
kappa_stability_evidence().count_divergences() / LOO are surfaced with loo_pareto_k in diagnostics.delta_z now has length 0 in the default hard case
(hard_sum_zero = TRUE). Previously it was sampled as T parameters pinned by
a normal(0, 1e-6) prior, whose near-zero scale throttled the NUTS step size
and made sampling crawl. Removing them is both faster and better-mixing.tools::R_user_dir), not the (possibly read-only) package directory, and the
compiled binary is no longer left in the source tree.inst/stan/ou_nonlinear_tmg.stan
is canonical and is read/compiled directly; the R string copy is gone.fit_ou_nonlinear_tmg() no longer calls setwd(); validates inputs
(finiteness, iter > warmup, train_frac); caps threads to avoid
oversubscription; exposes train_frac; thin default is now 1; no longer
duplicates the full draws array in the result (access via stan_fit).parallel in Imports; removed stray LazyData: true (no data/);
fixed repository URL capitalization.evaluate_oos index guard, PSIS-LOO reshape, and a Stan-based
parameter-recovery test (skipped when no backend is available).