diff --git a/ARCHITECTURE.md b/ARCHITECTURE.md index 33fbd2417..6178873fe 100644 --- a/ARCHITECTURE.md +++ b/ARCHITECTURE.md @@ -116,8 +116,7 @@ boundaries above remain the target modular MSA architecture. | `analysis_engine` | bounded cutoff-safe temporal evidence readiness execution and digest-bound terminal artifacts | | `validation_core` | RMSE, bias, coverage, graph, and Monte Carlo metrics | | `tepp_api` | versioned DTO, schema, and export contracts | -| `psychometric_core` | posterior-aware structural input gates, CWC within/between OLS plus the contextual effect, event-time log-rate, unequal-interval discrete-lag remapping, constant-predictor discrete effect, time-varying-predictor discrete effect (Eq. 14), exact scalar discrete process noise (Driver et al., 2017, Eq. 3), lagged latent covariance and unconditional latent variance (Driver et al., 2017, Eq. 3–4), stationary within-subject variance (Driver et al., 2017, Eq. 4 as `Δt → ∞`; `asymDIFFUSION`), trait-plus-state variance (Driver et al., 2017, §4.3 `TRAITVAR`; not process noise), observed-indicator variance and lagged observed covariance (Driver et al., 2017, Eq. 5; Table 2 `MANIFESTVAR` is `Θ`, not `Var(y)`; `MANIFESTTRAITVAR` is not `MANIFESTVAR`; `Θ` does not enter lagged observed covariance; observed-indicator mean is `τ + λ μ`; `MANIFESTMEANS` is not `E(y)`; `CINT` is not `MANIFESTMEANS`; discrete latent mean is `exp(a Δt) μ_0 + (exp(a Δt) − 1)/a κ`; `T0MEANS` is not `μ_t`; `CINT` is not the discrete increment; evolved observed mean is `τ + λ μ_t`; `τ + λ μ_0` is not `E(y_t)`; contemporaneous `TDPREDEFFECT` impulse is `m x`, not `CINT`, not `TIPREDEFFECT`, and not Voelkle Eq. 14; Eq. 5 of that contemporaneous impulse is `τ + λ(μ_t + m x)`, and `τ + λ μ_t` is not that observed mean; time-independent `TIPREDEFFECT` increment is `A^{-1}[e^{A Δt} − I] B z`, not `CINT`, not `M x`, not Voelkle Eq. 14, and not the coefficient `B`; Eq. 5 of that increment is `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)`, and `τ + λ μ_t` is not that observed mean; `τ + λ(μ_t + m x)` is not that observed mean; `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t`; within-interval `TDPREDEFFECT` carry is `e^{A(t−u)} M x` for `t0 < u < t`, not the contemporaneous Dirac, not `CINT`, not `TIPREDEFFECT`, and not Voelkle Eq. 14; Eq. 5 of that carry is `τ + λ(μ_t + e^{a(t−u)} m x)`, and `τ + λ μ_t` is not that observed mean; `τ + λ(μ_t + m x)` is not that carried observed mean when `u ≠ t`; first-occasion `T0TIPREDEFFECT` shift is `t0_b z` and Eq. 3 first-summand carry is `e^{A Δt} t0_b z` (`T0TIPREDEFFECT` is not `TIPREDEFFECT` `B`; `t0_b z` is not `A^{-1}[e^{A Δt} − I] B z`; `e^{A Δt} t0_b z` is not `t0_b z`; Eq. 5 of that carry is `τ + λ(μ_t + e^{a Δt} t0_b z)`, and `τ + λ μ_t` is not that observed mean; `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` is not that observed mean), first-occasion `T0TDPREDEFFECT` shift is `t0_m x0` and Eq. 3 first-summand carry is `e^{A Δt} t0_m x0` (`T0TDPREDEFFECT` is not `TDPREDEFFECT` `M`; `t0_m x0` is not `M x`; `e^{A Δt} t0_m x0` is not `t0_m x0`; `e^{A Δt} t0_m x0` is not `e^{A(t−u)} M x` for `t0 < u < t`; `t0_m x0` is not `t0_b z`; an impulse at `u ≤ t0` that used `M` is already in `η(t0)` as `TDPREDEFFECT`, not as `T0TDPREDEFFECT`; Eq. 5 of that carry is `τ + λ(μ_t + e^{a Δt} t0_m x0)`, and `τ + λ μ_t` is not that observed mean; `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` is not that observed mean; `τ + λ(μ_t + e^{a Δt} t0_b z)` is not that observed mean; §7.2 level-change `CINT` is `κ = −a m x` with `a < 0` so `−κ / a = m x` (`−a m x` is not the dissipating Dirac, not a free `CINT`, not `TIPREDEFFECT`, and not the extra near-zero-drift latent process also named in §7.2; Eq. 3 of that setting is `(1 − e^{a Δt}) m x`, which is not `m x`, not `κ`, and not `TIPREDEFFECT`; §7.2 extra-process contribution is `a_{ηξ} x (e^{ε Δt} − e^{a Δt}) / (ε − a)` (`ε = a` is `a_{ηξ} x Δt e^{a Δt}`; identification `TDPREDEFFECT` on the extra process is 1; printed extra `DRIFT` is `−0.000001`; not `κ = −a m x`, not `(1 − e^{a Δt}) m x`, and not the dissipating Dirac `m x`; `ε ≥ 0` fails closed; Eq. 5 of that contribution is `τ + λ(μ_t + a_{ηξ} x (e^{ε Δt} − e^{a Δt}) / (ε − a)`; the extra process has `LAMBDA` 0 and is not an observed indicator; `τ + λ μ_t` is not that observed mean; `τ + λ(μ_t + m x)` is not that observed mean; the contribution is not `E(y_t)`; the evolved-plus-contribution latent mean is not `E(y_t)`; after-t0 extra-process `TDPREDEFFECT` is `a_{ηξ} x (e^{ε(t−u)} − e^{a(t−u)}) / (ε − a)` for `t0 < u < t` while `μ_t` uses `Δt`; Eq. 5 of that after-t0 contribution is `τ + λ(μ_t + a_{ηξ} x (e^{ε(t−u)} − e^{a(t−u)}) / (ε − a)`; the first-occasion extra-process observed mean is not that observed mean when `u ≠ t0`; `e^{a(t−u)} m x` is a Dirac on the original process, not this `DRIFT` drive; §7.2 `asymTIPREDEFFECT` is `-B z / a` for `a < 0` (`-B z / a` is not the coefficient `B`, not `A^{-1}[e^{A Δt} − I] B z`, not `CINT`, and not `M x`; §7.2 `addedTIPREDVAR` is `(B / a)² v`, not `TRAITVAR`, not `asymDIFFUSION`, and not `-B z / a`; Table 2 `asymCINT` is `-κ / a` for `a < 0` and is not `κ`, not `A^{-1}[e^{A Δt} − I] κ`, not `T0MEANS`, and not `-B z / a`; p. 16 stationary `T0MEANS` is `-κ / a + −B z / a` and is not free `T0MEANS`, not `asymCINT` alone, not `asymTIPREDEFFECT` alone, and not the finite-interval discrete latent mean; Eq. 5 of that constrained mean is `τ + λ(−κ / a + −B z / a)`; `τ + λ μ_0` is not that observed mean; `τ + λ(−κ / a)` is not that observed mean when `B z ≠ 0`; `τ + λ μ_t` is not that observed mean; `MANIFESTMEANS` is not `E(y_0)`; the constrained latent mean is not `E(y_0)`; stationary `T0VAR` is `trait + −q / (2 a) + (B / a)² v` (not free `T0VAR`, not `asymDIFFUSION` alone, not `TRAITVAR` alone, not `addedTIPREDVAR` alone, and not the finite-interval discrete latent variance. Eq. 5 of that constrained variance is `λ²(trait + −q / (2 a) + (B / a)² v) + θ + ψ` (JSS PDF re-opened 2026-08-22T03:20Z; form the stationary latent variance first, then `λ² p + θ + ψ`; `λ² p_0` is not that observed variance; `λ²(−q / (2 a)) + θ` is not that observed variance when `TRAITVAR` or `addedTIPREDVAR` is nonzero; `MANIFESTVAR` is not `Var(y_0)`; the constrained latent variance is not `Var(y_0)`); lagged stationary `T0VAR` is `trait + e^{a Δt}(−q / (2 a)) + (B / a)² v` (trait and `addedTIPREDVAR` do not decay; contemporaneous `T0VAR` is not that lagged map; decaying the constrained total as if it were all state is not that lagged map; Eq. 5 of that lagged covariance is `λ²(trait + e^{a Δt}(−q / (2 a)) + (B / a)² v) + ψ`; `Θ` does not enter; contemporaneous `Var(y_0)` is not that lagged observed covariance; the lagged latent covariance is not that observed covariance); later-occasion stationary `T0VAR` is `trait + e^{2 a Δt}(−q / (2 a)) + Q_Δt + (B / a)² v` (trait and `addedTIPREDVAR` do not enter `Q_Δt`; under stationarity that composition equals contemporaneous `T0VAR`; evolving the constrained total as if it were all state is not that later map; the lagged covariance omits `Q_Δt`; `Q_Δt` is not that later map; Eq. 5 of that later-occasion variance is `λ²(trait + e^{2 a Δt}(−q / (2 a)) + Q_Δt + (B / a)² v) + θ + ψ`; lagged observed covariance omits `Q_Δt` and `θ`; `MANIFESTVAR` is not `Var(y_t)`; the later-occasion latent variance is not `Var(y_t)`); predetermined later-occasion `T0VAR` is `trait + e^{2 a Δt} p_0 + Q_Δt + (B / a)² v` (free `T0VAR` `p_0` is not that later map; setting `p_0 = −q / (2 a)` recovers the stationary later-occasion map; stationary later variance uses `−q / (2 a)` in place of `p_0` and is not that later map when `p_0` is free; evolving `trait + p_0 + (B / a)² v` as if it were all state is not that later map; Eq. 5 of that predetermined later-occasion variance is `λ²(trait + e^{2 a Δt} p_0 + Q_Δt + (B / a)² v) + θ + ψ`; `MANIFESTVAR` is not `Var(y_t)`; the predetermined later-occasion latent variance is not `Var(y_t)`; stationary later observed variance is not that observed variance when `p_0` is free); predetermined lagged `T0VAR` is `trait + e^{a Δt} p_0 + (B / a)² v` (free `T0VAR` `p_0` is not that lagged map; setting `p_0 = −q / (2 a)` recovers the stationary lagged map; stationary lagged covariance uses `−q / (2 a)` in place of `p_0` and is not that lagged map when `p_0` is free; evolving `trait + p_0 + (B / a)² v` as if it were all state is not that lagged map; later-occasion variance includes `Q_Δt` and is not that lagged map; Eq. 5 of that predetermined lagged covariance is `λ²(trait + e^{a Δt} p_0 + (B / a)² v) + ψ`; `MANIFESTVAR` does not enter; the predetermined lagged latent covariance is not that observed covariance; predetermined later observed variance includes `Q_Δt` and `θ` and is not that lagged observed covariance; stationary lagged observed covariance is not that observed covariance when `p_0` is free; the predetermined first-occasion variance of §4.3 predetermined `T0VAR` is `trait + p_0 + (B / a)² v`; free `p_0` is not that map; stationary first-occasion variance uses `−q / (2 a)` in place of `p_0` and is not that map when `p_0` is free; lagged covariance decays the state and is not that map; later-occasion variance includes `Q_Δt` and is not that map; Eq. 5 of that predetermined first-occasion variance is `λ²(trait + p_0 + (B / a)² v) + θ + ψ`; `MANIFESTVAR` is not that first-occasion observed variance; the predetermined first-occasion latent variance is not that observed variance; stationary first-occasion observed variance is not that observed variance when `p_0` is free; predetermined later observed variance includes `Q_Δt` and is not that first-occasion observed variance; later-start lagged covariance of predetermined `T0VAR` is `trait + e^{a s}(e^{2 a u} p_0 + Q_u) + (B / a)² v` (Driver et al., 2017, §4.3 `startoffset`; Eq. 4; JSS PDF re-opened 2026-08-23T10:27Z; first-occasion lagged omits `e^{a s} Q_u`; later-occasion variance does not lag; stationary lagged uses `−q / (2 a)`; decaying the later total is not that map; Eq. 5 of that later-start lagged covariance is `λ²` of it plus `ψ`; `Θ` does not enter; first-occasion lagged observed omits `e^{a s} Q_u`; later observed variance includes `Q_u` and `θ`; later-start later-occasion variance of predetermined `T0VAR` is `trait + e^{2 a s}(e^{2 a u} p_0 + Q_u) + Q_s + (B / a)² v` (Driver et al., 2017, §4.3 `startoffset`; Eq. 3–4 Chapman–Kolmogorov `Q_{u+s} = e^{2 a s} Q_u + Q_s`; JSS PDF re-opened 2026-08-23T11:05Z; later-occasion variance at `u` omits `Q_s`; later-start lagged covariance omits `Q_s`; stationary later uses `−q / (2 a)`; evolving the later total as if it were all state is not that map; ignoring `startoffset` omits `e^{2 a s} Q_u`; Eq. 5 of that later-start later-occasion variance is `λ²` of it plus `θ + ψ`; `MANIFESTVAR` is not that observed variance; p. 16 `discreteDRIFTstd` is `e^{a Δt}` after strictly positive `asymDIFFUSION` `-q / (2 a)` (footnote 4; unstandardised `e^{a Δt}` is defined for growing `a ≥ 0` and for zero diffusion and is not `discreteDRIFTstd`; the §7.1 trait-plus-state autocorrelation uses `TRAITVAR` and is not `discreteDRIFTstd`; `TRAITVAR` is not the standardisation variance; p. 16 `discreteDIFFUSIONstd` is `Q_Δt / (−q / (2 a))` after strictly positive `asymDIFFUSION` `-q / (2 a)` (footnote 4; unstandardised `Q_Δt` is defined for growing `a ≥ 0` and for zero diffusion and is not `discreteDIFFUSIONstd`; the continuous standardisation `−2 a` is not `discreteDIFFUSIONstd`; `Q_Δt / (trait + p + added)` uses `TRAITVAR` and is not `discreteDIFFUSIONstd`; `TRAITVAR` is not the standardisation variance; p. 16 `DIFFUSIONstd` is `q / (−q / (2 a)) = −2 a` after strictly positive `asymDIFFUSION` `-q / (2 a)` (Driver et al., 2017, p. 16; Eq. 4; footnote 4; JSS PDF re-opened 2026-08-23T13:20Z; unstandardised `q` is defined for growing `a ≥ 0` and for zero diffusion and is not `DIFFUSIONstd`; the discrete standardisation `Q_Δt / (−q / (2 a))` depends on `Δt` and is not `DIFFUSIONstd`; `q / (trait + p + added)` uses `TRAITVAR` and is not `DIFFUSIONstd`; `TRAITVAR` is not the standardisation variance; p. 16 `DRIFTstd` is the continuous auto-effect after strictly positive `asymDIFFUSION` `-q / (2 a)` (Driver et al., 2017, p. 16; Eq. 1; footnote 4; JSS PDF re-opened 2026-08-23T13:28Z); unstandardised `a` is defined for growing `a ≥ 0` and for zero diffusion and is not `DRIFTstd`; the discrete standardisation `e^{a Δt}` depends on the event interval and is not `DRIFTstd`; `a p / (trait + p + added)` uses `TRAITVAR` and is not `DRIFTstd`; `TRAITVAR` is not the standardisation variance); p. 16 `asymTIPREDEFFECTstd` is `(-B / a) · √v / √(-q / (2 a))` after strictly positive `asymDIFFUSION` `-q / (2 a)` and strictly positive predictor variance `v` (Driver et al., 2017, p. 16; §7.2; footnote 4; JSS PDF re-opened 2026-08-23T14:25Z; unstandardised `-B / a` is defined for a zero coefficient and for zero predictor variance and is not `asymTIPREDEFFECTstd`; the finite-interval standardisation `A^{-1}[e^{A Δt} − I] B · √v / √p` depends on the event interval and is not `asymTIPREDEFFECTstd`; `(-B / a) · √v / √(trait + p + added)` uses `TRAITVAR` and is not `asymTIPREDEFFECTstd`; `TRAITVAR` is not the standardisation variance); p. 16 `TIPREDEFFECTstd` is `B · √v / √(-q / (2 a))` after strictly positive `asymDIFFUSION` `-q / (2 a)` and strictly positive predictor variance `v` (Driver et al., 2017, p. 16; §7.2; footnote 4; JSS PDF re-opened 2026-08-23T16:21Z; unstandardised `B` is defined for a zero coefficient and for zero predictor variance and is not `TIPREDEFFECTstd`; the asymptotic standardisation `(-B / a) · √v / √p` is the total change and is not `TIPREDEFFECTstd`; the finite-interval standardisation `A^{-1}[e^{A Δt} − I] B · √v / √p` depends on the event interval and is not `TIPREDEFFECTstd`; `B · √v / √(trait + p + added)` uses `TRAITVAR` and is not `TIPREDEFFECTstd`; `TRAITVAR` is not the standardisation variance); Table 3 `T0TIPREDEFFECTstd` is `t0_b · √v / √p_0` after strictly positive free `T0VAR` `p_0` and strictly positive predictor variance `v` (Driver et al., 2017, Table 3, p. 13; p. 16; footnote 4; 2017-era ctsem `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-23T17:20Z; the affected variance is free `T0VAR`, not `asymDIFFUSION`; unstandardised `t0_b` is defined for a zero coefficient and for zero predictor variance and is not `T0TIPREDEFFECTstd`; `TIPREDEFFECTstd` `B · √v / √(-q / (2 a))` is the continuous coefficient and is not `T0TIPREDEFFECTstd`; `asymTIPREDEFFECTstd` `(-B / a) · √v / √p` is the total change and is not `T0TIPREDEFFECTstd`; `t0_b · √v / √(trait + p_0 + added)` uses `TRAITVAR` and is not `T0TIPREDEFFECTstd`; `TRAITVAR` is not the standardisation variance); 2017-era `addedT0TIPREDVAR` is `t0_b² v` (Driver et al., 2017, Table 3, p. 13; p. 16; §7.2; 2017-era ctsem `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-23T18:20Z; `T0TIPREDEFFECT %*% TIPREDVAR %*% t(T0TIPREDEFFECT)` immediately after `T0TIPREDEFFECTstd`; form `t0_b` first, then square, then multiply by `v`; a zero coefficient or zero predictor variance is exactly zero; free `T0TIPREDEFFECT` does not require `a < 0`; `(B / a)² v` is `addedTIPREDVAR` and is not this first-occasion map; `t0_b · √v / √p_0` is `T0TIPREDEFFECTstd` and is not this variance; free `T0VAR` is not this extra TI variance; `TRAITVAR` is not this extra TI variance; Equation 5 of 2017-era `addedT0TIPREDVAR` is `λ² t0_b² v` (Driver et al., 2017, Eq. 5, p. 5; Table 3, p. 13; Table 2, p. 12; 2017-era ctsem `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-23T19:10Z; form `t0_b² v` first, then `(λ extra) λ` with `θ = 0`; a zero loading or zero extra is exactly zero; `t0_b² v` is the latent extra, not the observed extra; `λ² p_0 + θ` is first-occasion observed variance, not this extra; `λ² (B / a)² v` is Eq. 5 of `addedTIPREDVAR`, not this first-occasion observed extra; `MANIFESTVAR` `θ` is not this extra; Equation 5 of §7.2 `addedTIPREDVAR` is `λ² (B / a)² v`; form `(B / a)² v` first, then `(λ extra) λ` with `θ = 0`; a zero loading or zero extra is exactly zero; lasting asymptotic extra requires `a < 0`; `(B / a)² v` is the latent extra, not the observed extra; `λ² t0_b² v` is first-occasion extra observed TI variance, not this extra; `λ² p + θ` is stationary observed variance, not this extra; `MANIFESTVAR` `θ` is not this extra; p. 16 `TDPREDEFFECTstd` is `m · √v / √(-q / (2 a))` after strictly positive `asymDIFFUSION` and strictly positive time-dependent predictor variance; unstandardised `M` is not `TDPREDEFFECTstd`; `TIPREDEFFECTstd` is not `TDPREDEFFECTstd` even when `M = B`; intercept-style `A^{-1}[e^{A Δt} − I] M · √v / √p` is not `TDPREDEFFECTstd`; `m · √v / √(trait + p + added)` uses `TRAITVAR` and is not `TDPREDEFFECTstd`; Table 3 / p. 16 `T0TDPREDEFFECTstd` is `t0_m · √v / √p_0` after strictly positive free `T0VAR` and strictly positive TD predictor variance; unstandardised `t0_m` is not `T0TDPREDEFFECTstd`; `TDPREDEFFECTstd` uses `asymDIFFUSION` and is not `T0TDPREDEFFECTstd`; `T0TIPREDEFFECTstd` is not `T0TDPREDEFFECTstd` even when `t0_m = t0_b`; `t0_m · √v / √(trait + p_0 + added)` uses `TRAITVAR` and is not `T0TDPREDEFFECTstd`; free `T0VAR` does not require `a < 0`; p. 16 `T0VARstd` is `p_0 / p_0 = 1` after strictly positive free `T0VAR` (`solve(sqrt(diag(T0VAR))) %&% T0VAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; default ridge is 0); unstandardised `T0VAR` is not `T0VARstd`; `T0TDPREDEFFECTstd` is not `T0VARstd`; `addedT0TIPREDVAR` is not `T0VARstd`; p. 16 `TRAITVARstd` is `trait / trait = 1` after strictly positive `TRAITVAR` (`solve(sqrt(diag(TRAITVAR))) %&% TRAITVAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; no ridge addend); unstandardised `TRAITVAR` is not `TRAITVARstd`; `T0VARstd` is not `TRAITVARstd` even when both equal 1; `addedT0TIPREDVAR` is not `TRAITVARstd`; p. 16 `MANIFESTTRAITVARstd` is `ψ / ψ = 1` after strictly positive `MANIFESTTRAITVAR` (`solve(sqrt(diag(MANIFESTTRAITVAR))) %&% MANIFESTTRAITVAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; 2017-era source adds ridging; default ridge is 0); unstandardised `MANIFESTTRAITVAR` is not `MANIFESTTRAITVARstd`; `TRAITVARstd` is not `MANIFESTTRAITVARstd` even when both equal 1; `MANIFESTVAR` is not `MANIFESTTRAITVARstd`; p. 16 `MANIFESTVARstd` is `θ / θ = 1` after strictly positive `MANIFESTVAR` (`solve(sqrt(diag(MANIFESTVAR))) %&% MANIFESTVAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; 2017-era source adds ridging; default ridge is 0; 2017-era `dimnames` assignment to `latentNames` is a source bug); unstandardised `MANIFESTVAR` is not `MANIFESTVARstd`; `MANIFESTTRAITVARstd` is not `MANIFESTVARstd` even when both equal 1; Equation 5 `Var(y)` is not `MANIFESTVARstd`; p. 16 `TIPREDVARstd` is `v / v = 1` after strictly positive `TIPREDVAR` (`solve(sqrt(diag(TIPREDVAR))) %&% TIPREDVAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; 2017-era source adds ridging; default ridge is 0; `dimnames` are `TIpredNames`); unstandardised `TIPREDVAR` is not `TIPREDVARstd`; `MANIFESTVARstd` is not `TIPREDVARstd` even when both equal 1; §7.2 `addedTIPREDVAR` is not `TIPREDVARstd`; p. 16 `asymDIFFUSIONstd` is `p / p = 1` after strictly positive `asymDIFFUSION` (`solve(sqrt(diag(asymDIFFUSION))) %&% asymDIFFUSION`; OpenMx `%&%` is `t(A) %*% B %*% A`; 2017-era source adds ridging; default ridge is 0; `dimnames` are `latentNames`); unstandardised `asymDIFFUSION` is not `asymDIFFUSIONstd`; `TIPREDVARstd` is not `asymDIFFUSIONstd` even when both equal 1; `DIFFUSIONstd` `−2 a` is not `asymDIFFUSIONstd`; p. 16 `discreteCINTstd` is `A^{-1}[e^{A Δt} − I] κ / √p` after strictly positive `asymDIFFUSION`; unstandardised `discreteCINT` is not `discreteCINTstd`; `κ / √p` is not `discreteCINTstd`; `(-κ / a) / √p` is not `discreteCINTstd`; `asymCINTstd` is `(-κ / a) / √p` after strictly positive `asymDIFFUSION`; unstandardised `asymCINT` is not `asymCINTstd`; `κ / √p` is not `asymCINTstd`; `discreteCINTstd` is not `asymCINTstd`; `T0MEANSstd` is `μ_0 / √p_0` after strictly positive free `T0VAR`; unstandardised `T0MEANS` is not `T0MEANSstd`; `T0VARstd` is not `T0MEANSstd`; `μ_0 / √asymDIFFUSION` is not `T0MEANSstd`; `MANIFESTMEANSstd` is `τ / √θ` after strictly positive `MANIFESTVAR`; unstandardised `MANIFESTMEANS` is not `MANIFESTMEANSstd`; `MANIFESTVARstd` is not `MANIFESTMEANSstd`; `τ / √(λ² Var(η) + θ)` is not `MANIFESTMEANSstd`; p. 16 `CINTstd` is `κ / √p` after strictly positive `asymDIFFUSION`; unstandardised `CINT` is not `CINTstd`; `asymCINTstd` is not `CINTstd`; `discreteCINTstd` is not `CINTstd`; `κ / √(trait + p + added)` is not `CINTstd`;))))), irregular already-centered residual lag, public CWC lagged-within-residual pipeline and pairwise-mean log-rate after CWC (not Newton LS, not raw-process drift), Rubin `T` on OLS loadings, and strong-gated latent means (two-observation residual variance is identically `0` and caps at strong/scalar; Putnick & Bornstein, 2016) | - +| `psychometric_core` | posterior-aware structural input gates, CWC within/between OLS plus the contextual effect, event-time log-rate, unequal-interval discrete-lag remapping, constant-predictor discrete effect, time-varying-predictor discrete effect (Eq. 14), exact scalar discrete process noise (Driver et al., 2017, Eq. 3), lagged latent covariance and unconditional latent variance (Driver et al., 2017, Eq. 3–4), stationary within-subject variance (Driver et al., 2017, Eq. 4 as `Δt → ∞`; `asymDIFFUSION`), trait-plus-state variance (Driver et al., 2017, §4.3 `TRAITVAR`; not process noise), observed-indicator variance and lagged observed covariance (Driver et al., 2017, Eq. 5; Table 2 `MANIFESTVAR` is `Θ`, not `Var(y)`; `MANIFESTTRAITVAR` is not `MANIFESTVAR`; `Θ` does not enter lagged observed covariance; observed-indicator mean is `τ + λ μ`; `MANIFESTMEANS` is not `E(y)`; `CINT` is not `MANIFESTMEANS`; discrete latent mean is `exp(a Δt) μ_0 + (exp(a Δt) − 1)/a κ`; `T0MEANS` is not `μ_t`; `CINT` is not the discrete increment; evolved observed mean is `τ + λ μ_t`; `τ + λ μ_0` is not `E(y_t)`; contemporaneous `TDPREDEFFECT` impulse is `m x`, not `CINT`, not `TIPREDEFFECT`, and not Voelkle Eq. 14; Eq. 5 of that contemporaneous impulse is `τ + λ(μ_t + m x)`, and `τ + λ μ_t` is not that observed mean; time-independent `TIPREDEFFECT` increment is `A^{-1}[e^{A Δt} − I] B z`, not `CINT`, not `M x`, not Voelkle Eq. 14, and not the coefficient `B`; Eq. 5 of that increment is `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)`, and `τ + λ μ_t` is not that observed mean; `τ + λ(μ_t + m x)` is not that observed mean; `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t`; within-interval `TDPREDEFFECT` carry is `e^{A(t−u)} M x` for `t0 < u < t`, not the contemporaneous Dirac, not `CINT`, not `TIPREDEFFECT`, and not Voelkle Eq. 14; Eq. 5 of that carry is `τ + λ(μ_t + e^{a(t−u)} m x)`, and `τ + λ μ_t` is not that observed mean; `τ + λ(μ_t + m x)` is not that carried observed mean when `u ≠ t`; first-occasion `T0TIPREDEFFECT` shift is `t0_b z` and Eq. 3 first-summand carry is `e^{A Δt} t0_b z` (`T0TIPREDEFFECT` is not `TIPREDEFFECT` `B`; `t0_b z` is not `A^{-1}[e^{A Δt} − I] B z`; `e^{A Δt} t0_b z` is not `t0_b z`; Eq. 5 of that carry is `τ + λ(μ_t + e^{a Δt} t0_b z)`, and `τ + λ μ_t` is not that observed mean; `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` is not that observed mean), first-occasion `T0TDPREDEFFECT` shift is `t0_m x0` and Eq. 3 first-summand carry is `e^{A Δt} t0_m x0` (`T0TDPREDEFFECT` is not `TDPREDEFFECT` `M`; `t0_m x0` is not `M x`; `e^{A Δt} t0_m x0` is not `t0_m x0`; `e^{A Δt} t0_m x0` is not `e^{A(t−u)} M x` for `t0 < u < t`; `t0_m x0` is not `t0_b z`; an impulse at `u ≤ t0` that used `M` is already in `η(t0)` as `TDPREDEFFECT`, not as `T0TDPREDEFFECT`; Eq. 5 of that carry is `τ + λ(μ_t + e^{a Δt} t0_m x0)`, and `τ + λ μ_t` is not that observed mean; `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` is not that observed mean; `τ + λ(μ_t + e^{a Δt} t0_b z)` is not that observed mean; §7.2 level-change `CINT` is `κ = −a m x` with `a < 0` so `−κ / a = m x` (`−a m x` is not the dissipating Dirac, not a free `CINT`, not `TIPREDEFFECT`, and not the extra near-zero-drift latent process also named in §7.2; Eq. 3 of that setting is `(1 − e^{a Δt}) m x`, which is not `m x`, not `κ`, and not `TIPREDEFFECT`; §7.2 extra-process contribution is `a_{ηξ} x (e^{ε Δt} − e^{a Δt}) / (ε − a)` (`ε = a` is `a_{ηξ} x Δt e^{a Δt}`; identification `TDPREDEFFECT` on the extra process is 1; printed extra `DRIFT` is `−0.000001`; not `κ = −a m x`, not `(1 − e^{a Δt}) m x`, and not the dissipating Dirac `m x`; `ε ≥ 0` fails closed; Eq. 5 of that contribution is `τ + λ(μ_t + a_{ηξ} x (e^{ε Δt} − e^{a Δt}) / (ε − a)`; the extra process has `LAMBDA` 0 and is not an observed indicator; `τ + λ μ_t` is not that observed mean; `τ + λ(μ_t + m x)` is not that observed mean; the contribution is not `E(y_t)`; the evolved-plus-contribution latent mean is not `E(y_t)`; after-t0 extra-process `TDPREDEFFECT` is `a_{ηξ} x (e^{ε(t−u)} − e^{a(t−u)}) / (ε − a)` for `t0 < u < t` while `μ_t` uses `Δt`; Eq. 5 of that after-t0 contribution is `τ + λ(μ_t + a_{ηξ} x (e^{ε(t−u)} − e^{a(t−u)}) / (ε − a)`; the first-occasion extra-process observed mean is not that observed mean when `u ≠ t0`; `e^{a(t−u)} m x` is a Dirac on the original process, not this `DRIFT` drive; §7.2 `asymTIPREDEFFECT` is `-B z / a` for `a < 0` (`-B z / a` is not the coefficient `B`, not `A^{-1}[e^{A Δt} − I] B z`, not `CINT`, and not `M x`; §7.2 `addedTIPREDVAR` is `(B / a)² v`, not `TRAITVAR`, not `asymDIFFUSION`, and not `-B z / a`; Table 2 `asymCINT` is `-κ / a` for `a < 0` and is not `κ`, not `A^{-1}[e^{A Δt} − I] κ`, not `T0MEANS`, and not `-B z / a`; p. 16 stationary `T0MEANS` is `-κ / a + −B z / a` and is not free `T0MEANS`, not `asymCINT` alone, not `asymTIPREDEFFECT` alone, and not the finite-interval discrete latent mean; Eq. 5 of that constrained mean is `τ + λ(−κ / a + −B z / a)`; `τ + λ μ_0` is not that observed mean; `τ + λ(−κ / a)` is not that observed mean when `B z ≠ 0`; `τ + λ μ_t` is not that observed mean; `MANIFESTMEANS` is not `E(y_0)`; the constrained latent mean is not `E(y_0)`; stationary `T0VAR` is `trait + −q / (2 a) + (B / a)² v` (not free `T0VAR`, not `asymDIFFUSION` alone, not `TRAITVAR` alone, not `addedTIPREDVAR` alone, and not the finite-interval discrete latent variance. Eq. 5 of that constrained variance is `λ²(trait + −q / (2 a) + (B / a)² v) + θ + ψ` (JSS PDF re-opened 2026-08-22T03:20Z; form the stationary latent variance first, then `λ² p + θ + ψ`; `λ² p_0` is not that observed variance; `λ²(−q / (2 a)) + θ` is not that observed variance when `TRAITVAR` or `addedTIPREDVAR` is nonzero; `MANIFESTVAR` is not `Var(y_0)`; the constrained latent variance is not `Var(y_0)`); lagged stationary `T0VAR` is `trait + e^{a Δt}(−q / (2 a)) + (B / a)² v` (trait and `addedTIPREDVAR` do not decay; contemporaneous `T0VAR` is not that lagged map; decaying the constrained total as if it were all state is not that lagged map; Eq. 5 of that lagged covariance is `λ²(trait + e^{a Δt}(−q / (2 a)) + (B / a)² v) + ψ`; `Θ` does not enter; contemporaneous `Var(y_0)` is not that lagged observed covariance; the lagged latent covariance is not that observed covariance); later-occasion stationary `T0VAR` is `trait + e^{2 a Δt}(−q / (2 a)) + Q_Δt + (B / a)² v` (trait and `addedTIPREDVAR` do not enter `Q_Δt`; under stationarity that composition equals contemporaneous `T0VAR`; evolving the constrained total as if it were all state is not that later map; the lagged covariance omits `Q_Δt`; `Q_Δt` is not that later map; Eq. 5 of that later-occasion variance is `λ²(trait + e^{2 a Δt}(−q / (2 a)) + Q_Δt + (B / a)² v) + θ + ψ`; lagged observed covariance omits `Q_Δt` and `θ`; `MANIFESTVAR` is not `Var(y_t)`; the later-occasion latent variance is not `Var(y_t)`); predetermined later-occasion `T0VAR` is `trait + e^{2 a Δt} p_0 + Q_Δt + (B / a)² v` (free `T0VAR` `p_0` is not that later map; setting `p_0 = −q / (2 a)` recovers the stationary later-occasion map; stationary later variance uses `−q / (2 a)` in place of `p_0` and is not that later map when `p_0` is free; evolving `trait + p_0 + (B / a)² v` as if it were all state is not that later map; Eq. 5 of that predetermined later-occasion variance is `λ²(trait + e^{2 a Δt} p_0 + Q_Δt + (B / a)² v) + θ + ψ`; `MANIFESTVAR` is not `Var(y_t)`; the predetermined later-occasion latent variance is not `Var(y_t)`; stationary later observed variance is not that observed variance when `p_0` is free); predetermined lagged `T0VAR` is `trait + e^{a Δt} p_0 + (B / a)² v` (free `T0VAR` `p_0` is not that lagged map; setting `p_0 = −q / (2 a)` recovers the stationary lagged map; stationary lagged covariance uses `−q / (2 a)` in place of `p_0` and is not that lagged map when `p_0` is free; evolving `trait + p_0 + (B / a)² v` as if it were all state is not that lagged map; later-occasion variance includes `Q_Δt` and is not that lagged map; Eq. 5 of that predetermined lagged covariance is `λ²(trait + e^{a Δt} p_0 + (B / a)² v) + ψ`; `MANIFESTVAR` does not enter; the predetermined lagged latent covariance is not that observed covariance; predetermined later observed variance includes `Q_Δt` and `θ` and is not that lagged observed covariance; stationary lagged observed covariance is not that observed covariance when `p_0` is free; the predetermined first-occasion variance of §4.3 predetermined `T0VAR` is `trait + p_0 + (B / a)² v`; free `p_0` is not that map; stationary first-occasion variance uses `−q / (2 a)` in place of `p_0` and is not that map when `p_0` is free; lagged covariance decays the state and is not that map; later-occasion variance includes `Q_Δt` and is not that map; Eq. 5 of that predetermined first-occasion variance is `λ²(trait + p_0 + (B / a)² v) + θ + ψ`; `MANIFESTVAR` is not that first-occasion observed variance; the predetermined first-occasion latent variance is not that observed variance; stationary first-occasion observed variance is not that observed variance when `p_0` is free; predetermined later observed variance includes `Q_Δt` and is not that first-occasion observed variance; later-start lagged covariance of predetermined `T0VAR` is `trait + e^{a s}(e^{2 a u} p_0 + Q_u) + (B / a)² v` (Driver et al., 2017, §4.3 `startoffset`; Eq. 4; JSS PDF re-opened 2026-08-23T10:27Z; first-occasion lagged omits `e^{a s} Q_u`; later-occasion variance does not lag; stationary lagged uses `−q / (2 a)`; decaying the later total is not that map; Eq. 5 of that later-start lagged covariance is `λ²` of it plus `ψ`; `Θ` does not enter; first-occasion lagged observed omits `e^{a s} Q_u`; later observed variance includes `Q_u` and `θ`; later-start later-occasion variance of predetermined `T0VAR` is `trait + e^{2 a s}(e^{2 a u} p_0 + Q_u) + Q_s + (B / a)² v` (Driver et al., 2017, §4.3 `startoffset`; Eq. 3–4 Chapman–Kolmogorov `Q_{u+s} = e^{2 a s} Q_u + Q_s`; JSS PDF re-opened 2026-08-23T11:05Z; later-occasion variance at `u` omits `Q_s`; later-start lagged covariance omits `Q_s`; stationary later uses `−q / (2 a)`; evolving the later total as if it were all state is not that map; ignoring `startoffset` omits `e^{2 a s} Q_u`; Eq. 5 of that later-start later-occasion variance is `λ²` of it plus `θ + ψ`; `MANIFESTVAR` is not that observed variance; p. 16 `discreteDRIFTstd` is `e^{a Δt}` after strictly positive `asymDIFFUSION` `-q / (2 a)` (footnote 4; unstandardised `e^{a Δt}` is defined for growing `a ≥ 0` and for zero diffusion and is not `discreteDRIFTstd`; the §7.1 trait-plus-state autocorrelation uses `TRAITVAR` and is not `discreteDRIFTstd`; `TRAITVAR` is not the standardisation variance; p. 16 `discreteDIFFUSIONstd` is `Q_Δt / (−q / (2 a))` after strictly positive `asymDIFFUSION` `-q / (2 a)` (footnote 4; unstandardised `Q_Δt` is defined for growing `a ≥ 0` and for zero diffusion and is not `discreteDIFFUSIONstd`; the continuous standardisation `−2 a` is not `discreteDIFFUSIONstd`; `Q_Δt / (trait + p + added)` uses `TRAITVAR` and is not `discreteDIFFUSIONstd`; `TRAITVAR` is not the standardisation variance; p. 16 `DIFFUSIONstd` is `q / (−q / (2 a)) = −2 a` after strictly positive `asymDIFFUSION` `-q / (2 a)` (Driver et al., 2017, p. 16; Eq. 4; footnote 4; JSS PDF re-opened 2026-08-23T13:20Z; unstandardised `q` is defined for growing `a ≥ 0` and for zero diffusion and is not `DIFFUSIONstd`; the discrete standardisation `Q_Δt / (−q / (2 a))` depends on `Δt` and is not `DIFFUSIONstd`; `q / (trait + p + added)` uses `TRAITVAR` and is not `DIFFUSIONstd`; `TRAITVAR` is not the standardisation variance; p. 16 `DRIFTstd` is the continuous auto-effect after strictly positive `asymDIFFUSION` `-q / (2 a)` (Driver et al., 2017, p. 16; Eq. 1; footnote 4; JSS PDF re-opened 2026-08-23T13:28Z); unstandardised `a` is defined for growing `a ≥ 0` and for zero diffusion and is not `DRIFTstd`; the discrete standardisation `e^{a Δt}` depends on the event interval and is not `DRIFTstd`; `a p / (trait + p + added)` uses `TRAITVAR` and is not `DRIFTstd`; `TRAITVAR` is not the standardisation variance); p. 16 `asymTIPREDEFFECTstd` is `(-B / a) · √v / √(-q / (2 a))` after strictly positive `asymDIFFUSION` `-q / (2 a)` and strictly positive predictor variance `v` (Driver et al., 2017, p. 16; §7.2; footnote 4; JSS PDF re-opened 2026-08-23T14:25Z; unstandardised `-B / a` is defined for a zero coefficient and for zero predictor variance and is not `asymTIPREDEFFECTstd`; the finite-interval standardisation `A^{-1}[e^{A Δt} − I] B · √v / √p` depends on the event interval and is not `asymTIPREDEFFECTstd`; `(-B / a) · √v / √(trait + p + added)` uses `TRAITVAR` and is not `asymTIPREDEFFECTstd`; `TRAITVAR` is not the standardisation variance); p. 16 `TIPREDEFFECTstd` is `B · √v / √(-q / (2 a))` after strictly positive `asymDIFFUSION` `-q / (2 a)` and strictly positive predictor variance `v` (Driver et al., 2017, p. 16; §7.2; footnote 4; JSS PDF re-opened 2026-08-23T16:21Z; unstandardised `B` is defined for a zero coefficient and for zero predictor variance and is not `TIPREDEFFECTstd`; the asymptotic standardisation `(-B / a) · √v / √p` is the total change and is not `TIPREDEFFECTstd`; the finite-interval standardisation `A^{-1}[e^{A Δt} − I] B · √v / √p` depends on the event interval and is not `TIPREDEFFECTstd`; `B · √v / √(trait + p + added)` uses `TRAITVAR` and is not `TIPREDEFFECTstd`; `TRAITVAR` is not the standardisation variance); Table 3 `T0TIPREDEFFECTstd` is `t0_b · √v / √p_0` after strictly positive free `T0VAR` `p_0` and strictly positive predictor variance `v` (Driver et al., 2017, Table 3, p. 13; p. 16; footnote 4; 2017-era ctsem `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-23T17:20Z; the affected variance is free `T0VAR`, not `asymDIFFUSION`; unstandardised `t0_b` is defined for a zero coefficient and for zero predictor variance and is not `T0TIPREDEFFECTstd`; `TIPREDEFFECTstd` `B · √v / √(-q / (2 a))` is the continuous coefficient and is not `T0TIPREDEFFECTstd`; `asymTIPREDEFFECTstd` `(-B / a) · √v / √p` is the total change and is not `T0TIPREDEFFECTstd`; `t0_b · √v / √(trait + p_0 + added)` uses `TRAITVAR` and is not `T0TIPREDEFFECTstd`; `TRAITVAR` is not the standardisation variance); 2017-era `addedT0TIPREDVAR` is `t0_b² v` (Driver et al., 2017, Table 3, p. 13; p. 16; §7.2; 2017-era ctsem `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-23T18:20Z; `T0TIPREDEFFECT %*% TIPREDVAR %*% t(T0TIPREDEFFECT)` immediately after `T0TIPREDEFFECTstd`; form `t0_b` first, then square, then multiply by `v`; a zero coefficient or zero predictor variance is exactly zero; free `T0TIPREDEFFECT` does not require `a < 0`; `(B / a)² v` is `addedTIPREDVAR` and is not this first-occasion map; `t0_b · √v / √p_0` is `T0TIPREDEFFECTstd` and is not this variance; free `T0VAR` is not this extra TI variance; `TRAITVAR` is not this extra TI variance; Equation 5 of 2017-era `addedT0TIPREDVAR` is `λ² t0_b² v` (Driver et al., 2017, Eq. 5, p. 5; Table 3, p. 13; Table 2, p. 12; 2017-era ctsem `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-23T19:10Z; form `t0_b² v` first, then `(λ extra) λ` with `θ = 0`; a zero loading or zero extra is exactly zero; `t0_b² v` is the latent extra, not the observed extra; `λ² p_0 + θ` is first-occasion observed variance, not this extra; `λ² (B / a)² v` is Eq. 5 of `addedTIPREDVAR`, not this first-occasion observed extra; `MANIFESTVAR` `θ` is not this extra; Equation 5 of §7.2 `addedTIPREDVAR` is `λ² (B / a)² v`; form `(B / a)² v` first, then `(λ extra) λ` with `θ = 0`; a zero loading or zero extra is exactly zero; lasting asymptotic extra requires `a < 0`; `(B / a)² v` is the latent extra, not the observed extra; `λ² t0_b² v` is first-occasion extra observed TI variance, not this extra; `λ² p + θ` is stationary observed variance, not this extra; `MANIFESTVAR` `θ` is not this extra; p. 16 `TDPREDEFFECTstd` is `m · √v / √(-q / (2 a))` after strictly positive `asymDIFFUSION` and strictly positive time-dependent predictor variance; unstandardised `M` is not `TDPREDEFFECTstd`; `TIPREDEFFECTstd` is not `TDPREDEFFECTstd` even when `M = B`; intercept-style `A^{-1}[e^{A Δt} − I] M · √v / √p` is not `TDPREDEFFECTstd`; `m · √v / √(trait + p + added)` uses `TRAITVAR` and is not `TDPREDEFFECTstd`; Table 3 / p. 16 `T0TDPREDEFFECTstd` is `t0_m · √v / √p_0` after strictly positive free `T0VAR` and strictly positive TD predictor variance; unstandardised `t0_m` is not `T0TDPREDEFFECTstd`; `TDPREDEFFECTstd` uses `asymDIFFUSION` and is not `T0TDPREDEFFECTstd`; `T0TIPREDEFFECTstd` is not `T0TDPREDEFFECTstd` even when `t0_m = t0_b`; `t0_m · √v / √(trait + p_0 + added)` uses `TRAITVAR` and is not `T0TDPREDEFFECTstd`; free `T0VAR` does not require `a < 0`; p. 16 `T0VARstd` is `p_0 / p_0 = 1` after strictly positive free `T0VAR` (`solve(sqrt(diag(T0VAR))) %&% T0VAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; default ridge is 0); unstandardised `T0VAR` is not `T0VARstd`; `T0TDPREDEFFECTstd` is not `T0VARstd`; `addedT0TIPREDVAR` is not `T0VARstd`; p. 16 `TRAITVARstd` is `trait / trait = 1` after strictly positive `TRAITVAR` (`solve(sqrt(diag(TRAITVAR))) %&% TRAITVAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; no ridge addend); unstandardised `TRAITVAR` is not `TRAITVARstd`; `T0VARstd` is not `TRAITVARstd` even when both equal 1; `addedT0TIPREDVAR` is not `TRAITVARstd`; p. 16 `MANIFESTTRAITVARstd` is `ψ / ψ = 1` after strictly positive `MANIFESTTRAITVAR` (`solve(sqrt(diag(MANIFESTTRAITVAR))) %&% MANIFESTTRAITVAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; 2017-era source adds ridging; default ridge is 0); unstandardised `MANIFESTTRAITVAR` is not `MANIFESTTRAITVARstd`; `TRAITVARstd` is not `MANIFESTTRAITVARstd` even when both equal 1; `MANIFESTVAR` is not `MANIFESTTRAITVARstd`; p. 16 `MANIFESTVARstd` is `θ / θ = 1` after strictly positive `MANIFESTVAR` (`solve(sqrt(diag(MANIFESTVAR))) %&% MANIFESTVAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; 2017-era source adds ridging; default ridge is 0; 2017-era `dimnames` assignment to `latentNames` is a source bug); unstandardised `MANIFESTVAR` is not `MANIFESTVARstd`; `MANIFESTTRAITVARstd` is not `MANIFESTVARstd` even when both equal 1; Equation 5 `Var(y)` is not `MANIFESTVARstd`; p. 16 `TIPREDVARstd` is `v / v = 1` after strictly positive `TIPREDVAR` (`solve(sqrt(diag(TIPREDVAR))) %&% TIPREDVAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; 2017-era source adds ridging; default ridge is 0; `dimnames` are `TIpredNames`); unstandardised `TIPREDVAR` is not `TIPREDVARstd`; `MANIFESTVARstd` is not `TIPREDVARstd` even when both equal 1; §7.2 `addedTIPREDVAR` is not `TIPREDVARstd`; p. 16 `asymDIFFUSIONstd` is `p / p = 1` after strictly positive `asymDIFFUSION` (`solve(sqrt(diag(asymDIFFUSION))) %&% asymDIFFUSION`; OpenMx `%&%` is `t(A) %*% B %*% A`; 2017-era source adds ridging; default ridge is 0; `dimnames` are `latentNames`); unstandardised `asymDIFFUSION` is not `asymDIFFUSIONstd`; `TIPREDVARstd` is not `asymDIFFUSIONstd` even when both equal 1; `DIFFUSIONstd` `−2 a` is not `asymDIFFUSIONstd`; p. 16 `discreteCINTstd` is `A^{-1}[e^{A Δt} − I] κ / √p` after strictly positive `asymDIFFUSION`; unstandardised `discreteCINT` is not `discreteCINTstd`; `κ / √p` is not `discreteCINTstd`; `(-κ / a) / √p` is not `discreteCINTstd`; `asymCINTstd` is `(-κ / a) / √p` after strictly positive `asymDIFFUSION`; unstandardised `asymCINT` is not `asymCINTstd`; `κ / √p` is not `asymCINTstd`; `discreteCINTstd` is not `asymCINTstd`; `T0MEANSstd` is `μ_0 / √p_0` after strictly positive free `T0VAR`; unstandardised `T0MEANS` is not `T0MEANSstd`; `T0VARstd` is not `T0MEANSstd`; `μ_0 / √asymDIFFUSION` is not `T0MEANSstd`; `MANIFESTMEANSstd` is `τ / √θ` after strictly positive `MANIFESTVAR`; unstandardised `MANIFESTMEANS` is not `MANIFESTMEANSstd`; `MANIFESTVARstd` is not `MANIFESTMEANSstd`; `τ / √(λ² Var(η) + θ)` is not `MANIFESTMEANSstd`; p. 16 `CINTstd` is `κ / √p` after strictly positive `asymDIFFUSION`; unstandardised `CINT` is not `CINTstd`; `asymCINTstd` is not `CINTstd`; `discreteCINTstd` is not `CINTstd`; `κ / √(trait + p + added)` is not `CINTstd`;))))), irregular already-centered residual lag, public CWC lagged-within-residual pipeline and pairwise-mean log-rate after CWC (not Newton LS, not raw-process drift), public grand-mean-centered event-time lag (Hamaker et al., 2015, p. 104: not a within-person lag, not RI-CLPM), Rubin `T` on OLS loadings, and strong-gated latent means (two-observation residual variance is identically `0` and caps at strong/scalar; Putnick & Bornstein, 2016) | Foundation crates expose only tested contracts. Empty façades are not public APIs. diff --git a/CHANGELOG.md b/CHANGELOG.md index df89de0a6..f29440b23 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -38,6 +38,8 @@ All notable changes to TEPP are documented here. The format follows Keep a Chang ## [Unreleased] +- `psychometric_core` exposes the public grand-mean-centered event-time lag as a distinct estimand from CWC: `center_grand_mean_event_lags` subtracts the sample grand mean (`sum/n` when that sum is finite; otherwise positives and negatives averaged separately with the overflow-safe incremental mean and combined by count) then emits consecutive `LaggedGrandMeanResidual` pairs ordered with `f64::total_cmp`. That type is not `LaggedWithinResidual` and cannot enter `recover_irregular_centered_residual_log_rate` (Hamaker, Kuiper, & Grasman, 2015, p. 104; UvA PDF re-opened 2026-08-30T23:35Z: lagged relations from grand-mean deviations "only represent actual within-person relationships if there are no trait-like, time-invariant, between-person differences"). `recover_grand_mean_centered_irregular_residual_log_rate` is the pairwise mean of Voelkle et al. (2012, Eq. 7) on nonzero same-sign residuals using `ln(|later| / |earlier|) / Δt` when that ratio is finite and `(ln|later| − ln|earlier|) / Δt` when it overflows or underflows; the pairwise mean is incremental so two finite rates whose raw sum overflows stay representable; `refuse_grand_mean_centered_log_rate_as_within_person_lag` always fails closed. T=2 CWC is always `r, −r` (empty admissible); T=2 CGM can keep same-sign pairs. When cluster means coincide, CGM equals CWC numerically and remains a distinct type. An all-`MAX` series has a finite grand mean of `MAX`. Cancelling `±MAX` scores keep a representable zero grand mean in either row order. A finite grand mean whose residual overflows fails closed. A non-finite event time fails closed at centering. Still not CWC, not RI-CLPM, not DSEM. + - `psychometric_core` exposes the public CWC-then-irregular residual pipeline: `center_within_cluster_event_lags` extracts `LaggedWithinResidual` pairs after cluster-mean centering; consecutive times are ordered with `f64::total_cmp` because finite event times never take `partial_cmp`'s `None` arm; `recover_within_cluster_irregular_residual_log_rate` is the pairwise mean of Voelkle et al. (2012, Eq. 7) on nonzero same-sign residuals (sign without dividing). When `|later| / |earlier|` is finite and positive the rate is `ln(|later| / |earlier|) / Δt` so near-equal large residuals do not collapse to zero; when that ratio overflows or underflows the rate is `(ln|later| − ln|earlier|) / Δt`. The near-equal unit case always asserts a nonzero rate and does not hide that assertion behind a libm-dependent subtracted-log zero branch. Nightly branch coverage on `79262df` left five `&&` short-circuit arms in those unit cases; the underflow reconstruction now classifies IEEE underflow to `+0`, and the overflow/underflow unit case no longer uses a short-circuit `&&`. The pairwise mean is formed incrementally as `mean · (n − 1) / n + rate / n` so two finite rates whose raw sum overflows still keep a representable mean; mixed signs scale each term before adding. This is **not** the Newton least-squares fit used by `recover_within_residual_event_time_log_rate`. The already-centered helper still uses `later / earlier` and fails closed on a non-finite ratio. Finite strictly positive residual-ratio pairs are retained; an empty admissible set fails closed. CWC residuals of a connected series sum to zero (Curran & Bauer, 2011, pp. 607–608); consecutive pairs need not all have opposite signs. `refuse_cwc_residual_log_rate_as_raw_process_drift` always fails closed. Already-centered irregular pairs still recover the known drift; CWC of a raw AR path does not. Still not DSEM, not a Kalman filter, not a matrix `expm`, and not ctsem estimation. Meredith (1993) remains unread (Unpaywall/OpenAlex/Semantic Scholar 2026-08-30T21:36Z: `is_oa: false`; Internet Archive 0 hits; Springer `content/pdf` redirected to the identity provider, not a PDF). Mislevy (1991) remains unread on the same terms (ETS article page is a stub; 1988 ETS RR-88-45 / DTIC ADA200179 is not the 1991 journal article). - `event_core` adds bounded Allen interval-consistency classification, atomic path-consistency closure, contradiction/resource refusals, and an explicit dependency-error fallback without claiming unrestricted global satisfiability. diff --git a/CLAUDE.md b/CLAUDE.md index a71f5d145..f037994a7 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -16,7 +16,7 @@ Read and follow `AGENTS.md` before changing this repository. The repository-wide - Do not treat raw topic proportions as ordinary Euclidean indicators. Use logistic-normal coordinates or valid log-ratio coordinates and propagate posterior uncertainty into ESEM/DSEM. - Do not treat metric/weak invariance as a latent-mean license. Strong (equal loading and intercept) or strict is required; `#84` `metric` licenses shared metric meaning only. Putnick and Bornstein (2016, PMC5145197 opened 2026-08-19T22:15Z) require scalar invariance before latent-mean comparison; residual invariance is not a prerequisite. Two-observation series have no residual degrees of freedom (`ordinary_least_squares_fit` returns residual variance `0`) and cap at strong/scalar; they still license means. This is two-group OLS, not MGCFA. Meredith (1993) names remain unread labels (Unpaywall 2026-08-30T20:58Z: `is_oa: false`). - Do not use the difference quotient as a continuous-time rate. The scalar map is `a = ln(φ) / Δt` on event time. Discrete lags from unequal event intervals are not one coefficient; remap them through that log-rate. Binary64 `exp(a Δt) = 0` is not a discrete lag. A constant predictor's discrete effect is Voelkle et al. (2012, Eq. 12), evaluated as `a_yx (expm1(z) / a_xx)` with `z = a_xx Δt` so a finite result is not lost when `z` overflows to `-∞` or when `a_yx Δt` overflows. When `expm1(z)` overflows at a finite `z`, rewrite in log space; a zero continuous effect is exactly zero; an overflowing `a_yx/a_xx` rewrite term fails closed. The first-order product is the underflow limit of that equation, not the general constant-predictor discrete effect. A time-varying predictor whose sampling interval equals its constancy interval uses Voelkle et al. (2012, Eq. 14): `b* = a_yx Δt`. Unmatched intervals fail closed (Oud & Jansen, 2000, unread). Discrete process noise is Driver et al. (2017, Eq. 3): `Q_Δt = 0.5 q (expm1(z) / a)` with `z = 2 (a Δt)` and `q = G G⊤ ≥ 0`; do not form `2 a` first; `a = 0` and `z → 0` recover `q Δt`; a zero diffusion is exactly zero; an overflowing rewrite scale `0.5 q / a` fails closed; this is not a Kalman filter. `Q_Δt` is `cov(η_t | η_{t-1})`, not `Var(η_t)`. The lagged covariance is `exp(a Δt) p` and the unconditional variance is `exp(2 a Δt) p + Q_Δt` (Driver et al., 2017, Eq. 3–4, pp. 4–5; JSS has no numbered §2.2). A zero diffusion whose `2 (a Δt)` overflows to `+∞` is not a finite `Var(η_t)`. The stationary within-subject variance is the `Δt → ∞` limit of Eq. 4: `-q / (2 a)` for stable `a < 0` (JSS p. 16 `asymDIFFUSION`; §4.3). When `2 a` is finite, form `q / -(2 a)` so `q / a` overflow does not lose a finite result (`q = MAX`, `a = -0.75` → `MAX / 1.5`). When `2 a` overflows, form `(q / a) * -0.5`. Do not form `0.5 q` first (`q = from_bits(1)` underflows). `a ≥ 0` has no finite stationary variance. Finite-interval `Q_Δt` is not that limit. Trait-plus-state variance is `trait + state` and lagged covariance is `trait + exp(a Δt) p` (Driver et al., 2017, §4.3, p. 9). Trait variance is not process noise and not `asymDIFFUSION`. Evolving the summed variance as if it were all state is not that map. This is not RI-CLPM. Observed-indicator variance is `λ² Var(η) + θ` when `MANIFESTTRAITVAR` is zero and `λ² Var(η) + θ + ψ` otherwise (Driver et al., 2017, Eq. 5, p. 5; Table 2, p. 12). Lagged observed covariance is `λ² cov(η_t, η_{t-1}) + ψ`; `MANIFESTVAR` does not enter. Observed-indicator mean is `τ + λ μ` (Driver et al., 2017, Eq. 5; Table 2, p. 12). `MANIFESTMEANS` is `τ`, not `E(y)`. `E(η)` is not `E(y)`. `CINT` is not `MANIFESTMEANS`. `T0MEANS` is not `E(y)`. The discrete latent mean is `μ_t = exp(a Δt) μ_0 + (exp(a Δt) − 1)/a κ` (Driver et al., 2017, Eq. 3, p. 4; Table 2, p. 12). `T0MEANS` is not `μ_t`. `CINT` is not that discrete increment. A zero drift is `κ Δt`. Underflow of `exp(a Δt)` to `+0` drops the carried `T0MEANS` and keeps `−κ / a`. The evolved observed mean is `τ + λ μ_t` (Driver et al., 2017, Eq. 5 of that Eq. 3 map). The first-occasion map `τ + λ μ_0` is not `E(y_t)`. `μ_t` is not `E(y_t)`. The contemporaneous time-dependent predictor impulse is `m x` (Driver et al., 2017, Eq. 3 fourth summand; Table 2 `TDPREDEFFECT` is `M`). Form `μ_t` first, then add `m x`. `TDPREDEFFECT` is not `CINT`. `M x` is not `A^{-1}[e^{A Δt} − I] B z` and is not Voelkle et al. (2012, Eq. 14). The §7.2 level-change form is not that impulse. The observed mean of that contemporaneous impulse is `τ + λ(μ_t + m x)` (Driver et al., 2017, Eq. 5 of the Eq. 3 fourth-summand composition). The evolved map `τ + λ μ_t` is not that observed mean. The carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t`. The evolved-plus-impulse latent mean is not `E(y_t)`. The time-independent predictor increment is `A^{-1}[e^{A Δt} − I] B z` (Driver et al., 2017, Eq. 3 second summand; Table 2 `TIPREDEFFECT` is `B`). Form `B z` first, then the discrete intercept map. A zero drift is `B z Δt`. `TIPREDEFFECT` is `B`, not that discrete increment. `A^{-1}[e^{A Δt} − I] B z` is not `CINT`, not `M x`, and not Voelkle et al. (2012, Eq. 14). The observed mean of that increment is `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` (Driver et al., 2017, Eq. 5 of the Eq. 3 printed addend after the `T0MEANS` carry and the `CINT` increment). The evolved map `τ + λ μ_t` is not that observed mean. The contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean. The carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t`. The evolved-plus-increment latent mean is not `E(y_t)`. The within-interval time-dependent impulse carry is `e^{A(t−u)} M x` for `t0 < u < t` (Driver et al., 2017, Eq. 1–2 Green-function integral of Eq. 2; §7.2 dissipation). Form `m x` first, then `e^{a(t−u)} m x`. A zero drift is `m x` with no dissipation. Underflow of `e^{a(t−u)}` to `+0` is vanishing dissipation and is kept. `e^{A(t−u)} M x` is not the contemporaneous Dirac, not `CINT`, not `TIPREDEFFECT`, and not Voelkle et al. (2012, Eq. 14). An impulse at `u = t` is the contemporaneous map. An impulse at `u ≤ t0` is already in `η(t0)`. The observed mean of that carry is `τ + λ(μ_t + e^{a(t−u)} m x)` (Driver et al., 2017, Eq. 5 of the Eq. 1–2 carried latent mean). The evolved map `τ + λ μ_t` is not that observed mean. The contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean when `u ≠ t`. `MANIFESTMEANS` is not `E(y_t)`. The carried latent mean is not `E(y_t)`. The first-occasion time-independent predictor shift is `t0_b z` (Driver et al., 2017, Table 3 `T0TIPREDEFFECT`; Eq. 3 first summand). Form `t0_b z` first, then `e^{a Δt} t0_b z`. Form `μ_t` first, then add that carry. A zero drift is `t0_b z`. Underflow of `e^{a Δt}` to `+0` is a vanishing carry of the first-occasion shift and is kept. `t0_b z` is not `A^{-1}[e^{A Δt} − I] B z`, not `CINT`, and not `M x`. `e^{A Δt} t0_b z` is not `t0_b z`. `T0TIPREDEFFECT` is the coefficient, not the shift. The observed mean of that first-occasion carry is `τ + λ(μ_t + e^{a Δt} t0_b z)` (Driver et al., 2017, Eq. 5 of the Table 3 / Eq. 3 first-summand composition). The evolved map `τ + λ μ_t` is not that observed mean. The process-increment map `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` is not that observed mean. The contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean. The impulse-carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t0`. The evolved-plus-carry latent mean is not `E(y_t)`. The first-occasion time-dependent predictor shift is `t0_m x0` (Driver et al., 2017, Table 3 `T0TDPREDEFFECT`; Eq. 3 first summand; JSS PDF re-opened 2026-08-20T19:10Z). Form `t0_m x0` first, then `e^{a Δt} t0_m x0`. Form `μ_t` first, then add that carry. A zero drift is `t0_m x0`. Underflow of `e^{a Δt}` to `+0` is a vanishing carry of the first-occasion shift and is kept. `t0_m x0` is not `M x`, not `e^{A(t−u)} M x` for `t0 < u < t`, not `t0_b z`, not `A^{-1}[e^{A Δt} − I] B z`, and not `CINT`. `e^{A Δt} t0_m x0` is not `t0_m x0`. `T0TDPREDEFFECT` is the coefficient, not the shift. An impulse at `u ≤ t0` that used `M` is already in `η(t0)` as `TDPREDEFFECT`, not as `T0TDPREDEFFECT`. The observed mean of that first-occasion TD carry is `τ + λ(μ_t + e^{a Δt} t0_m x0)` (Driver et al., 2017, Eq. 5 of the Table 3 / Eq. 3 first-summand TD composition; JSS PDF re-opened 2026-08-20T19:07Z). The evolved map `τ + λ μ_t` is not that observed mean. The process-increment map `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` is not that observed mean. The contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean. The impulse-carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t0`. The first-occasion TI map `τ + λ(μ_t + e^{a Δt} t0_b z)` is not that observed mean. The evolved-plus-carry latent mean is not `E(y_t)`. The lasting level-change `CINT` is `κ = −a m x` (Driver et al., 2017, §7.2, pp. 20–21; JSS PDF re-opened 2026-08-20T19:45Z). Form `m x` first, then multiply by `−a`. Stable `a < 0` is required so `−κ / a = m x` is an equilibrium offset. `a ≥ 0` cannot hold a new process mean. `−a m x` is not the dissipating Dirac `m x`, not a free `CINT`, and not `A^{-1}[e^{A Δt} − I] B z`. The extra near-zero-drift latent process also named in §7.2 is a different specification and is not this `CINT` setting. Equation 3 maps that intercept as `(1 − e^{a Δt}) m x` (JSS PDF re-opened 2026-08-20T19:50Z). Form the level-change `CINT` first, then the discrete intercept map. Underflow of `e^{a Δt}` to `+0` keeps `m x`. `(1 − e^{a Δt}) m x` is not `m x`, not `κ`, and not `A^{-1}[e^{A Δt} − I] B z`. The printed §7.2 lasting level change is an extra near-zero-drift latent process (Driver et al., 2017, §7.2, pp. 22–23; JSS PDF re-opened 2026-08-20T23:10Z). `T0MEANS`, `CINT`, `T0VAR`, `DIFFUSION`, and `TRAITVAR` of that process are fixed to 0; `TDPREDEFFECT` on it is fixed to 1; its `DRIFT` diagonal is very close to 0 (printed example `−0.000001`; precisely 0 causes computational problems); the original process is driven by the `DRIFT` coupling `a_{ηξ}`. After a unit identification impulse the scalar contribution is `a_{ηξ} x (e^{ε Δt} − e^{a Δt}) / (ε − a)` (`ε = a` is `a_{ηξ} x Δt e^{a Δt}`). Form `a_{ηξ} x` first. A zero coupling or zero predictor is exactly zero. `ε ≥ 0` fails closed. That contribution is not `κ = −a m x`, not `(1 − e^{a Δt}) m x`, and not the dissipating Dirac `m x`. The observed mean of that extra-process contribution is `τ + λ(μ_t + a_{ηξ} x (e^{ε Δt} − e^{a Δt}) / (ε − a))` (Driver et al., 2017, Eq. 5 of that §7.2 contribution; JSS PDF re-opened 2026-08-21T06:12Z). The extra process has `LAMBDA` 0 and is not an observed indicator. Original indicators load on the original process after the `DRIFT` coupling. The evolved map `τ + λ μ_t` is not that observed mean. The contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean. The contribution is not `E(y_t)`. The evolved-plus-contribution latent mean is not `E(y_t)`. `T0TDPREDEFFECT` on the extra process begins at `t = 0` and uses `Δt = t − t0` for both the original-process evolution and the extra drive. `TDPREDEFFECT` after `t0` uses `t − u` with `t0 < u < t` for the extra drive while `μ_t` still uses `Δt`. The observed mean of that after-t0 extra-process contribution is `τ + λ(μ_t + a_{ηξ} x (e^{ε(t−u)} − e^{a(t−u)}) / (ε − a))` (Driver et al., 2017, Eq. 5 of that §7.2 after-t0 contribution; JSS PDF re-opened 2026-08-21T06:32Z). The first-occasion extra-process observed mean is not that observed mean when `u ≠ t0`. The impulse-carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is a Dirac on the original process and is not that `DRIFT` drive. An impulse at `u = t0` or `u = t` is not interior. The asymptotic time-independent predictor effect is `-B z / a` (Driver et al., 2017, §7.2, pp. 20–21; JSS PDF opened 2026-08-21T13:08Z). Form `B z` first, then divide by `-a`. Stable `a < 0` is required. `a ≥ 0` cannot hold a finite process-mean change. `-B z / a` is not the coefficient `B`, not `A^{-1}[e^{A Δt} − I] B z`, not `CINT`, and not `M x`. The asymptotic time-independent predictor variance is `(B / a)² v` (Driver et al., 2017, §7.2, pp. 20–21 `addedTIPREDVAR`). Form the unit asymptotic effect first, then square, then multiply by `v`. `(B / a)² v` is not `TRAITVAR`, not `asymDIFFUSION`, and not `-B z / a`. The asymptotic continuous intercept is `-κ / a` (Driver et al., 2017, Table 2, p. 12 `asymCINT`; Eq. 3 as `Δt → ∞`; JSS PDF opened 2026-08-21T16:13Z). Form `κ` first, then divide by `-a`. Stable `a < 0` is required. `-κ / a` is not `κ`, not `A^{-1}[e^{A Δt} − I] κ`, not `T0MEANS`, and not `-B z / a`. The p. 16 stationary `T0MEANS` constraint is `-κ / a + −B z / a`. Form the intercept contribution first, then include the TI extra effect, then add. That constrained first-occasion mean is not free `T0MEANS`, not `asymCINT` alone, not `asymTIPREDEFFECT` alone, and not the finite-interval discrete latent mean. Equation 5 of that constrained mean is `τ + λ(−κ / a + −B z / a)` (Driver et al., 2017, §4.3, pp. 9–10; Eq. 5, p. 5; JSS PDF re-opened 2026-08-21T20:07Z). Form the stationary latent mean first, then `τ + λ` of that mean. `τ + λ μ_0` for free `T0MEANS` is not that composition. `τ + λ(−κ / a)` is not that composition when `B z ≠ 0`. `τ + λ μ_t` is not that composition. `MANIFESTMEANS` is not `E(y_0)`. The constrained latent mean is not `E(y_0)`. The p. 16 constrained first-occasion variance `trait + −q / (2 a) + (B / a)² v` is not free `T0VAR`, not `asymDIFFUSION` alone, not `TRAITVAR` alone, not `addedTIPREDVAR` alone, and not the finite-interval discrete latent variance. Eq. 5 of that constrained variance is `λ²(trait + −q / (2 a) + (B / a)² v) + θ + ψ` (JSS PDF re-opened 2026-08-22T03:20Z; form the stationary latent variance first, then `λ² p + θ + ψ`; `λ² p_0` is not that observed variance; `λ²(−q / (2 a)) + θ` is not that observed variance when `TRAITVAR` or `addedTIPREDVAR` is nonzero; `MANIFESTVAR` is not `Var(y_0)`; the constrained latent variance is not `Var(y_0)`). The lagged covariance of that constrained process is `trait + e^{a Δt}(−q / (2 a)) + (B / a)² v` (Driver et al., 2017, Eq. 3–4 of §4.3 / p. 16 `T0VAR`; JSS PDF re-opened 2026-08-22T19:13Z). Trait and `addedTIPREDVAR` do not decay with `e^{a Δt}`. Contemporaneous `T0VAR` is not that lagged map. Decaying the constrained total as if it were all state is not that lagged map. Equation 5 of that lagged covariance is `λ²(trait + e^{a Δt}(−q / (2 a)) + (B / a)² v) + ψ`. `Θ` does not enter. Contemporaneous `Var(y_0)` is not that lagged observed covariance. The lagged latent covariance is not that observed covariance. The later-occasion variance of that constrained process is `trait + e^{2 a Δt}(−q / (2 a)) + Q_Δt + (B / a)² v` (Driver et al., 2017, Eq. 3–4 of §4.3 / p. 16 `T0VAR`; JSS PDF re-opened 2026-08-22T23:12Z). Trait and `addedTIPREDVAR` do not enter `Q_Δt`. Under stationarity that composition equals contemporaneous `T0VAR`. Evolving the constrained total as if it were all state is not that later map. The lagged covariance omits `Q_Δt` and is not that later map. `Q_Δt` is not that later map. Equation 5 of that later-occasion variance is `λ²(trait + e^{2 a Δt}(−q / (2 a)) + Q_Δt + (B / a)² v) + θ + ψ`. The lagged observed covariance omits `Q_Δt` and `θ`. `MANIFESTVAR` is not `Var(y_t)`. The later-occasion latent variance is not `Var(y_t)`. The later-occasion variance of §4.3 predetermined `T0VAR` is `trait + e^{2 a Δt} p_0 + Q_Δt + (B / a)² v` (Driver et al., 2017, Eq. 3–4 of §4.3 predetermined first occasion; JSS PDF re-opened 2026-08-23T05:12Z). Trait and `addedTIPREDVAR` do not enter `Q_Δt`. Free `T0VAR` `p_0` is not that later map. Setting `p_0 = −q / (2 a)` recovers the stationary later-occasion map. Stationary later variance uses `−q / (2 a)` in place of `p_0` and is not that later map when `p_0` is free. Evolving `trait + p_0 + (B / a)² v` as if it were all state is not that later map. As `Δt → ∞` with stable `a < 0` the composition approaches contemporaneous stationary `T0VAR`. As `Δt → 0+` the composition approaches `trait + p_0 + (B / a)² v`. Nonzero diffusion with `a ≥ 0` is a growing process and is kept. Equation 5 of that predetermined later-occasion variance is `λ²(trait + e^{2 a Δt} p_0 + Q_Δt + (B / a)² v) + θ + ψ`. `MANIFESTVAR` is not `Var(y_t)`. The predetermined later-occasion latent variance is not `Var(y_t)`. Stationary later observed variance is not that observed variance when `p_0` is free. The lagged covariance of §4.3 predetermined `T0VAR` is `trait + e^{a Δt} p_0 + (B / a)² v` (Driver et al., 2017, Eq. 3–4 of §4.3 predetermined first occasion; JSS PDF re-opened 2026-08-23T09:04Z). Trait and `addedTIPREDVAR` do not decay with `e^{a Δt}`. Free `T0VAR` `p_0` is not that lagged map. Setting `p_0 = −q / (2 a)` recovers the stationary lagged map. Stationary lagged covariance uses `−q / (2 a)` in place of `p_0` and is not that lagged map when `p_0` is free. Evolving `trait + p_0 + (B / a)² v` as if it were all state is not that lagged map. Later-occasion variance includes `Q_Δt` and is not that lagged map. As `Δt → ∞` with stable `a < 0` the state term vanishes. As `Δt → 0+` the composition approaches `trait + p_0 + (B / a)² v`. Equation 5 of that predetermined lagged covariance is `λ²(trait + e^{a Δt} p_0 + (B / a)² v) + ψ`. `MANIFESTVAR` does not enter. The predetermined lagged latent covariance is not that observed covariance. Predetermined later observed variance includes `Q_Δt` and `θ` and is not that lagged observed covariance. Stationary lagged observed covariance is not that observed covariance when `p_0` is free. The predetermined first-occasion variance of §4.3 predetermined `T0VAR` is `trait + p_0 + (B / a)² v`. Free `p_0` is not that map. Stationary first-occasion variance uses `−q / (2 a)` in place of `p_0` and is not that map when `p_0` is free. Lagged covariance decays the state and is not that map. Later-occasion variance includes `Q_Δt` and is not that map. Equation 5 of that predetermined first-occasion variance is `λ²(trait + p_0 + (B / a)² v) + θ + ψ`. `MANIFESTVAR` is not that first-occasion observed variance. The predetermined first-occasion latent variance is not that observed variance. Stationary first-occasion observed variance is not that observed variance when `p_0` is free. Predetermined later observed variance includes `Q_Δt` and is not that first-occasion observed variance. Later-start lagged covariance of predetermined `T0VAR` is `trait + e^{a s}(e^{2 a u} p_0 + Q_u) + (B / a)² v` (Driver et al., 2017, §4.3 `startoffset`; Eq. 4; JSS PDF re-opened 2026-08-23T10:27Z). First-occasion lagged omits `e^{a s} Q_u`. Later-occasion variance does not lag. Stationary lagged uses `−q / (2 a)`. Decaying the later total is not that map. Equation 5 of that later-start lagged covariance is `λ²` of it plus `ψ`. Independent `ε_t` does not enter. First-occasion lagged observed omits `e^{a s} Q_u`. Predetermined later observed variance includes `Q_u` and `θ` and is not that later-start lagged observed covariance. Later-start later-occasion variance of predetermined `T0VAR` is `trait + e^{2 a s}(e^{2 a u} p_0 + Q_u) + Q_s + (B / a)² v` (Driver et al., 2017, §4.3 `startoffset`; Eq. 3–4 Chapman–Kolmogorov `Q_{u+s} = e^{2 a s} Q_u + Q_s`; JSS PDF re-opened 2026-08-23T11:05Z). Later-occasion variance at `u` omits `Q_s`. Later-start lagged covariance omits `Q_s`. Stationary later uses `−q / (2 a)`. Evolving the later total as if it were all state is not that map. Ignoring `startoffset` omits `e^{2 a s} Q_u`. Equation 5 of that later-start later-occasion variance is `λ²` of it plus `θ + ψ`. `MANIFESTVAR` is not that observed variance. Page 16 `discreteDRIFTstd` is `e^{a Δt}` after strictly positive `asymDIFFUSION` `-q / (2 a)` (Driver et al., 2017, p. 16; footnote 4; §7.1; JSS PDF re-opened 2026-08-23T11:40Z). Unstandardised `e^{a Δt}` is defined for growing `a ≥ 0` and for zero diffusion and is not `discreteDRIFTstd`. The §7.1 trait-plus-state autocorrelation `(trait + e^{a Δt} p + added) / (trait + p + added)` uses `TRAITVAR` and is not `discreteDRIFTstd`. `TRAITVAR` is not the standardisation variance. Page 16 `discreteDIFFUSIONstd` is `Q_Δt / (−q / (2 a))` after strictly positive `asymDIFFUSION` `-q / (2 a)` (Driver et al., 2017, p. 16; Eq. 4; footnote 4; JSS PDF re-opened 2026-08-23T13:06Z). Unstandardised `Q_Δt` is defined for growing `a ≥ 0` and for zero diffusion and is not `discreteDIFFUSIONstd`. The continuous standardisation `−2 a` is not `discreteDIFFUSIONstd`. `Q_Δt / (trait + p + added)` uses `TRAITVAR` and is not `discreteDIFFUSIONstd`. `TRAITVAR` is not the standardisation variance. Page 16 `DIFFUSIONstd` is `q / (−q / (2 a)) = −2 a` after strictly positive `asymDIFFUSION` `-q / (2 a)` (Driver et al., 2017, p. 16; Eq. 4; footnote 4; JSS PDF re-opened 2026-08-23T13:20Z). Unstandardised `q` is defined for growing `a ≥ 0` and for zero diffusion and is not `DIFFUSIONstd`. The discrete standardisation `Q_Δt / (−q / (2 a))` depends on `Δt` and is not `DIFFUSIONstd`. `q / (trait + p + added)` uses `TRAITVAR` and is not `DIFFUSIONstd`. `TRAITVAR` is not the standardisation variance. Page 16 `DRIFTstd` is the continuous auto-effect after strictly positive `asymDIFFUSION` `-q / (2 a)` (Driver et al., 2017, p. 16; Eq. 1; footnote 4; JSS PDF re-opened 2026-08-23T13:28Z). Unstandardised `a` is defined for growing `a ≥ 0` and for zero diffusion and is not `DRIFTstd`. The discrete standardisation `e^{a Δt}` depends on the event interval and is not `DRIFTstd`. `a p / (trait + p + added)` uses `TRAITVAR` and is not `DRIFTstd`. `TRAITVAR` is not the standardisation variance. Page 16 `asymTIPREDEFFECTstd` is `(-B / a) · √v / √(-q / (2 a))` after strictly positive `asymDIFFUSION` `-q / (2 a)` and strictly positive predictor variance `v` (Driver et al., 2017, p. 16; §7.2; footnote 4; JSS PDF re-opened 2026-08-23T14:25Z). Unstandardised `-B / a` is defined for a zero coefficient and for zero predictor variance and is not `asymTIPREDEFFECTstd`. The finite-interval standardisation `A^{-1}[e^{A Δt} − I] B · √v / √p` depends on the event interval and is not `asymTIPREDEFFECTstd`. `(-B / a) · √v / √(trait + p + added)` uses `TRAITVAR` and is not `asymTIPREDEFFECTstd`. `TRAITVAR` is not the standardisation variance. Page 16 `TIPREDEFFECTstd` is `B · √v / √(-q / (2 a))` after strictly positive `asymDIFFUSION` `-q / (2 a)` and strictly positive predictor variance `v` (Driver et al., 2017, p. 16; §7.2; footnote 4; JSS PDF re-opened 2026-08-23T16:21Z). Unstandardised `B` is defined for a zero coefficient and for zero predictor variance and is not `TIPREDEFFECTstd`. The asymptotic standardisation `(-B / a) · √v / √p` is the total change and is not `TIPREDEFFECTstd`. The finite-interval standardisation `A^{-1}[e^{A Δt} − I] B · √v / √p` depends on the event interval and is not `TIPREDEFFECTstd`. `B · √v / √(trait + p + added)` uses `TRAITVAR` and is not `TIPREDEFFECTstd`. `TRAITVAR` is not the standardisation variance. Page 16 / Table 3 `T0TIPREDEFFECTstd` is `t0_b · √v / √p_0` after strictly positive free `T0VAR` `p_0` and strictly positive predictor variance `v` (Driver et al., 2017, Table 3, p. 13; p. 16; footnote 4; 2017-era ctsem `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-23T17:20Z). The affected variance is free first-occasion `T0VAR`, not `asymDIFFUSION`. Unstandardised `t0_b` is defined for a zero coefficient and for zero predictor variance and is not `T0TIPREDEFFECTstd`. `TIPREDEFFECTstd` `B · √v / √(-q / (2 a))` is the continuous coefficient and is not `T0TIPREDEFFECTstd`. `asymTIPREDEFFECTstd` `(-B / a) · √v / √p` is the total change and is not `T0TIPREDEFFECTstd`. `t0_b · √v / √(trait + p_0 + added)` uses `TRAITVAR` and is not `T0TIPREDEFFECTstd`. `TRAITVAR` is not the standardisation variance. 2017-era `addedT0TIPREDVAR` is `t0_b² v` after a first-occasion time-independent predictor (Driver et al., 2017, Table 3, p. 13; p. 16; §7.2; 2017-era ctsem `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-23T18:20Z). Form `t0_b` first, then square, then multiply by `v`. A zero coefficient or zero predictor variance is exactly zero. Free `T0TIPREDEFFECT` does not require `a < 0`. `(B / a)² v` is `addedTIPREDVAR` and is not this first-occasion map. `t0_b · √v / √p_0` is `T0TIPREDEFFECTstd` and is not this variance. Free `T0VAR` is not this extra TI variance. `TRAITVAR` is not this extra TI variance. Equation 5 of 2017-era `addedT0TIPREDVAR` is `λ² t0_b² v` (Driver et al., 2017, Eq. 5, p. 5; Table 3, p. 13; Table 2, p. 12; 2017-era ctsem `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-23T19:10Z). Form `t0_b² v` first, then `(λ extra) λ` with `θ = 0`. A zero loading or zero extra is exactly zero. `t0_b² v` is the latent extra, not the observed extra. `λ² p_0 + θ` is first-occasion observed variance, not this extra. `λ² (B / a)² v` is Eq. 5 of `addedTIPREDVAR`, not this first-occasion observed extra. `MANIFESTVAR` `θ` is not this extra. Equation 5 of §7.2 `addedTIPREDVAR` is `λ² (B / a)² v` (Driver et al., 2017, Eq. 5, p. 5; Table 2, p. 12; §7.2, pp. 20–21; 2017-era ctsem `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-23T19:23Z). Form `(B / a)² v` first, then `(λ extra) λ` with `θ = 0`. A zero loading or zero extra is exactly zero. Lasting asymptotic extra requires `a < 0`. `(B / a)² v` is the latent extra, not the observed extra. `λ² t0_b² v` is first-occasion extra observed TI variance, not this extra. `λ² p + θ` is stationary observed variance, not this extra. `MANIFESTVAR` `θ` is not this extra. Page 16 `TDPREDEFFECTstd` is `m · √v / √(-q / (2 a))` after strictly positive `asymDIFFUSION` and strictly positive time-dependent predictor variance. Unstandardised `M` is not `TDPREDEFFECTstd`. `TIPREDEFFECTstd` is not `TDPREDEFFECTstd` even when `M = B`. intercept-style `A^{-1}[e^{A Δt} − I] M · √v / √p` is not `TDPREDEFFECTstd`. `m · √v / √(trait + p + added)` uses `TRAITVAR` and is not `TDPREDEFFECTstd`. Table 3 / p. 16 `T0TDPREDEFFECTstd` is `t0_m · √v / √p_0` after strictly positive free `T0VAR` and strictly positive TD predictor variance. Unstandardised `t0_m` is not `T0TDPREDEFFECTstd`. `TDPREDEFFECTstd` uses `asymDIFFUSION` and is not `T0TDPREDEFFECTstd`. `T0TIPREDEFFECTstd` is not `T0TDPREDEFFECTstd` even when `t0_m = t0_b`. `t0_m · √v / √(trait + p_0 + added)` uses `TRAITVAR` and is not `T0TDPREDEFFECTstd`. Free `T0VAR` does not require `a < 0`. Page 16 `T0VARstd` is `p_0 / p_0 = 1` after strictly positive free `T0VAR` (`solve(sqrt(diag(T0VAR))) %&% T0VAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; the default ridge is 0). Unstandardised `T0VAR` is not `T0VARstd`. `T0TDPREDEFFECTstd` is not `T0VARstd`. `addedT0TIPREDVAR` is not `T0VARstd`. Page 16 `TRAITVARstd` is `trait / trait = 1` after strictly positive `TRAITVAR` (`solve(sqrt(diag(TRAITVAR))) %&% TRAITVAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; no ridge addend). Unstandardised `TRAITVAR` is not `TRAITVARstd`. `T0VARstd` is not `TRAITVARstd` even when both equal 1. `addedT0TIPREDVAR` is not `TRAITVARstd`. Page 16 `MANIFESTTRAITVARstd` is `ψ / ψ = 1` after strictly positive `MANIFESTTRAITVAR` (`solve(sqrt(diag(MANIFESTTRAITVAR))) %&% MANIFESTTRAITVAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; 2017-era source adds ridging; default ridge is 0). Unstandardised `MANIFESTTRAITVAR` is not `MANIFESTTRAITVARstd`. `TRAITVARstd` is not `MANIFESTTRAITVARstd` even when both equal 1. `MANIFESTVAR` is not `MANIFESTTRAITVARstd`. Page 16 `MANIFESTVARstd` is `θ / θ = 1` after strictly positive `MANIFESTVAR` (`solve(sqrt(diag(MANIFESTVAR))) %&% MANIFESTVAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; 2017-era source adds ridging; default ridge is 0; 2017-era `dimnames` assignment to `latentNames` is a source bug). Unstandardised `MANIFESTVAR` is not `MANIFESTVARstd`. `MANIFESTTRAITVARstd` is not `MANIFESTVARstd` even when both equal 1. Equation 5 `Var(y)` is not `MANIFESTVARstd`. Page 16 `TIPREDVARstd` is `v / v = 1` after strictly positive `TIPREDVAR` (`solve(sqrt(diag(TIPREDVAR))) %&% TIPREDVAR`; OpenMx `%&%` is `t(A) %*% B %*% A`; 2017-era source adds ridging; default ridge is 0; `dimnames` are `TIpredNames`). Unstandardised `TIPREDVAR` is not `TIPREDVARstd`. `MANIFESTVARstd` is not `TIPREDVARstd` even when both equal 1. Section 7.2 `addedTIPREDVAR` is not `TIPREDVARstd`. Page 16 `asymDIFFUSIONstd` is `p / p = 1` after strictly positive `asymDIFFUSION` (`solve(sqrt(diag(asymDIFFUSION))) %&% asymDIFFUSION`; OpenMx `%&%` is `t(A) %*% B %*% A`; 2017-era source adds ridging; default ridge is 0; `dimnames` are `latentNames`). Unstandardised `asymDIFFUSION` is not `asymDIFFUSIONstd`. `TIPREDVARstd` is not `asymDIFFUSIONstd` even when both equal 1. `DIFFUSIONstd` `−2 a` is not `asymDIFFUSIONstd`. Page 16 `discreteCINTstd` is `A^{-1}[e^{A Δt} − I] κ / √p` after strictly positive `asymDIFFUSION`. Unstandardised `discreteCINT` is not `discreteCINTstd`. `κ / √p` is not `discreteCINTstd`. `(-κ / a) / √p` is not `discreteCINTstd`. `asymCINTstd` is `(-κ / a) / √p` after strictly positive `asymDIFFUSION`. Unstandardised `asymCINT` is not `asymCINTstd`. `κ / √p` is not `asymCINTstd`. `discreteCINTstd` is not `asymCINTstd`. `T0MEANSstd` is `μ_0 / √p_0` after strictly positive free `T0VAR`. Unstandardised `T0MEANS` is not `T0MEANSstd`. `T0VARstd` is not `T0MEANSstd`. `μ_0 / √asymDIFFUSION` is not `T0MEANSstd`. Page 16 `MANIFESTMEANSstd` is `τ / √θ` after strictly positive `MANIFESTVAR`. Unstandardised `MANIFESTMEANS` is not `MANIFESTMEANSstd`. `MANIFESTVARstd` is not `MANIFESTMEANSstd`. `τ / √(λ² Var(η) + θ)` is not `MANIFESTMEANSstd`. Page 16 `CINTstd` is `κ / √p` after strictly positive `asymDIFFUSION`. Unstandardised `CINT` is not `CINTstd`. `asymCINTstd` is not `CINTstd`. `discreteCINTstd` is not `CINTstd`. `κ / √(trait + p + added)` is not `CINTstd`. Evolving from that stationary start with `CINT` and `TIPREDEFFECT` stays at the stationary mean. Equation 1 is the latent SDE, not the measurement model. Form `(λ p) λ` then add `θ`, then add `ψ`. `MANIFESTVAR` is `Θ`, not `Var(y)`. `MANIFESTTRAITVAR` is `Ψ_τ`, not `Θ`. `TRAITVAR` is latent and scaled by `λ²`. `Var(η)` is not `Var(y)`. -- Separate cluster means before within-unit lag. CWC plus an event-time lag is not DSEM. Subtracting the person-specific mean from a raw autoregressive series does not isolate the lagged within-person effect (Curran & Bauer, 2011, pp. 607–608); already-centered residuals with irregular event intervals use the exact scalar map. The public CWC pipeline emits `LaggedWithinResidual` pairs; the pairwise-mean log-rate after that CWC uses same-sign residuals without dividing. When `|later| / |earlier|` is finite the rate is `ln(|later| / |earlier|) / Δt`; overflowed or underflowed ratios fall back to `ln|later| − ln|earlier|`. The pairwise mean is `mean · (n − 1) / n + rate / n` so two finite rates whose raw sum overflows stay representable; mixed signs scale before adding. That pairwise mean is not the Newton least-squares log-rate and is not raw-process drift. Hamaker, Kuiper, and Grasman (2015, UvA PDF opened 2026-08-30T20:58Z) show that lagged relations from grand-mean deviations confound stable between-person differences; that is not this CWC residual map and is not RI-CLPM. +- Separate cluster means before within-unit lag. CWC plus an event-time lag is not DSEM. Subtracting the person-specific mean from a raw autoregressive series does not isolate the lagged within-person effect (Curran & Bauer, 2011, pp. 607–608); already-centered residuals with irregular event intervals use the exact scalar map. The public CWC pipeline emits `LaggedWithinResidual` pairs; the pairwise-mean log-rate after that CWC uses same-sign residuals without dividing. When `|later| / |earlier|` is finite the rate is `ln(|later| / |earlier|) / Δt`; overflowed or underflowed ratios fall back to `ln|later| − ln|earlier|`. The pairwise mean is `mean · (n − 1) / n + rate / n` so two finite rates whose raw sum overflows stay representable; mixed signs scale before adding. That pairwise mean is not the Newton least-squares log-rate and is not raw-process drift. Hamaker, Kuiper, and Grasman (2015, UvA PDF re-opened 2026-08-30T23:35Z, p. 104) show that lagged relations from grand-mean deviations confound stable between-person differences; that is not this CWC residual map and is not RI-CLPM. The public grand-mean-centered pipeline is a distinct estimand and a distinct type (`LaggedGrandMeanResidual`): lagged relations from grand-mean deviations "only represent actual within-person relationships if there are no trait-like, time-invariant, between-person differences" (Hamaker, Kuiper, & Grasman, 2015, p. 104). Those pairs cannot enter `recover_irregular_centered_residual_log_rate`. When the raw score sum is finite the grand mean is that sum divided by n so coinciding cluster means match CWC. When the raw sum overflows, positives and negatives are averaged separately with the overflow-safe incremental mean and combined by count, so cancelling ±MAX scores keep a representable zero mean in either row order. T=2 CWC is always `r, −r`; T=2 CGM can keep same-sign pairs. Treating that CGM log-rate as a within-person lag fails closed. That is not RI-CLPM. - Do not treat the CWC cluster-mean coefficient as the between-cluster effect. It is the contextual effect `between − within` (Enders & Tofighi, 2007, Table 2, pp. 124–127). - Never use future-available evidence in historical model fits. - Do not blanket-mask PII when identity/role/linkage is scientifically required. Follow the purpose-bound separation, opaque-ID, encryption, retention, and audit contract in `docs/PRIVACY_DATA_GOVERNANCE.md`. diff --git a/crates/psychometric_core/src/error.rs b/crates/psychometric_core/src/error.rs index 3804944a6..af16b214d 100644 --- a/crates/psychometric_core/src/error.rs +++ b/crates/psychometric_core/src/error.rs @@ -715,6 +715,12 @@ pub enum PsychometricError { /// pp. 607–608) show that person-mean subtraction on a raw AR /// series does not isolate the lagged within-person residual. CwcResidualLogRateIsNotRawProcessDrift, + /// Grand-mean-centered residual log-rate was treated as the + /// within-person lagged effect. Hamaker, Kuiper, and Grasman + /// (2015) show that lagged relations from grand-mean deviations + /// confound stable between-person differences with within-person + /// change. + GrandMeanCenteredLogRateIsNotWithinPersonLag, } impl fmt::Display for PsychometricError { @@ -1243,6 +1249,9 @@ impl fmt::Display for PsychometricError { Self::CwcResidualLogRateIsNotRawProcessDrift => { "cluster-mean-centered residual log-rate is not the raw-process autoregressive drift" } + Self::GrandMeanCenteredLogRateIsNotWithinPersonLag => { + "grand-mean-centered residual log-rate is not the within-person lagged effect" + } }; formatter.write_str(message) } @@ -2112,4 +2121,12 @@ mod tests { "cluster-mean-centered residual log-rate is not the raw-process autoregressive drift" ); } + + #[test] + fn grand_mean_centered_log_rate_boundary_message_is_stable() { + assert_eq!( + PsychometricError::GrandMeanCenteredLogRateIsNotWithinPersonLag.to_string(), + "grand-mean-centered residual log-rate is not the within-person lagged effect" + ); + } } diff --git a/crates/psychometric_core/src/event_time.rs b/crates/psychometric_core/src/event_time.rs index 3b4d82e00..bc39db9b5 100644 --- a/crates/psychometric_core/src/event_time.rs +++ b/crates/psychometric_core/src/event_time.rs @@ -261,6 +261,8 @@ pub struct ClusteredEventScore { /// /// `earlier_residual` and `later_residual` are within residuals the caller /// already formed. This type is not a raw score and is not re-centered. +/// Grand-mean deviations are [`LaggedGrandMeanResidual`], not this type +/// (Hamaker, Kuiper, & Grasman, 2015, p. 104). #[derive(Clone, Copy, Debug, PartialEq)] pub struct LaggedWithinResidual { /// Earlier within residual. @@ -271,6 +273,24 @@ pub struct LaggedWithinResidual { pub event_delta: f64, } +/// One grand-mean-centered lagged residual pair on event time. +/// +/// Hamaker, Kuiper, and Grasman (2015, p. 104) show that lagged relations +/// from deviations around shared (grand) means mix between-person +/// differences with within-person change, and represent actual +/// within-person relationships only if there are no trait-like +/// between-person differences. This type is not [`LaggedWithinResidual`] +/// and cannot enter [`recover_irregular_centered_residual_log_rate`]. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct LaggedGrandMeanResidual { + /// Earlier grand-mean residual. + pub earlier_residual: f64, + /// Later grand-mean residual. + pub later_residual: f64, + /// Strictly positive event-time interval. Intervals may be irregular. + pub event_delta: f64, +} + /// Discrete lag-1 coefficient and its exact local log-rate. #[derive(Clone, Copy, Debug, PartialEq)] pub struct DiscreteLagAndLogRate { @@ -6802,6 +6822,48 @@ pub(crate) fn overflow_safe_running_mean(mean: f64, count: f64, value: f64) -> ( } } +/// Fold [`overflow_safe_running_mean`] over finite scores in the given order. +fn fold_overflow_safe_mean(values: &[f64]) -> f64 { + let mut mean = 0.0_f64; + let mut count = 0.0_f64; + for &value in values { + (mean, count) = overflow_safe_running_mean(mean, count, value); + } + mean +} + +/// Sample mean that stays finite when a raw sum of finite scores overflows. +/// +/// Positives and negatives are averaged separately with +/// [`overflow_safe_running_mean`] and combined by count, so `MAX` and +/// `-MAX` cancel as `0.5 MAX + 0.5 (−MAX)` in either row order. Zeros +/// contribute only to the count. Caller supplies finite values. +pub(crate) fn overflow_safe_signed_mean(scores: I) -> Result +where + I: IntoIterator, +{ + let mut positives = Vec::new(); + let mut negatives = Vec::new(); + let mut zero_count = 0.0_f64; + for score in scores { + if score > 0.0 { + positives.push(score); + } else if score < 0.0 { + negatives.push(score); + } else { + zero_count += 1.0; + } + } + positives.sort_unstable_by(f64::total_cmp); + negatives.sort_unstable_by(f64::total_cmp); + let pos_n = positives.len() as f64; + let neg_n = negatives.len() as f64; + let n = pos_n + neg_n + zero_count; + let pos_mean = fold_overflow_safe_mean(&positives); + let neg_mean = fold_overflow_safe_mean(&negatives); + require_finite((pos_n / n).mul_add(pos_mean, (neg_n / n) * neg_mean)) +} + /// Pairwise-mean exact log-rate after CWC on irregular event intervals. /// /// This is [`center_within_cluster_event_lags`] then the pairwise mean of @@ -6900,12 +6962,157 @@ pub fn refuse_cwc_residual_log_rate_as_raw_process_drift( Err(PsychometricError::CwcResidualLogRateIsNotRawProcessDrift) } +/// Grand-mean-center consecutive event-time lags inside each cluster. +/// +/// The sample grand mean is removed first (CGM). Consecutive residuals +/// then become [`LaggedGrandMeanResidual`] pairs on possibly irregular +/// event intervals. Singleton clusters are skipped. When the raw score +/// sum is finite the grand mean is that sum divided by the row count so +/// coinciding cluster means match CWC. When the raw sum overflows, +/// positives and negatives are averaged separately with the overflow-safe +/// incremental mean and combined by count, so cancelling +/// `MAX` and `-MAX` scores keep a representable zero mean in either row +/// order. Hamaker, Kuiper, and Grasman (2015, p. 104) show that lagged +/// relations from grand-mean deviations confound stable between-person +/// differences with within-person change. The returned pairs are +/// therefore not [`LaggedWithinResidual`] and are not a license to +/// recover a within-person lag. This is not CWC, not RI-CLPM, and not +/// DSEM. +/// +/// # Errors +/// +/// Returns [`PsychometricError::EventTimeRequired`] for a non-event clock, +/// [`PsychometricError::InvalidNumericInput`] for empty, singleton-only, or +/// non-finite rows, [`PsychometricError::InsufficientClusters`] when fewer +/// than two clusters appear, and [`PsychometricError::NonPositiveInterval`] +/// when any consecutive event interval is not strictly positive. A finite +/// grand mean whose residual overflows also fails closed. +pub fn center_grand_mean_event_lags( + rows: &[ClusteredEventScore], + clock: LagClock, +) -> Result, PsychometricError> { + if !clock.admits_structural_lag() { + return Err(PsychometricError::EventTimeRequired); + } + if rows.len() < 2 { + return Err(PsychometricError::InvalidNumericInput); + } + let mut groups: BTreeMap> = BTreeMap::new(); + let mut score_sum = 0.0_f64; + for &row in rows { + if !row.event_time.is_finite() || !row.score.is_finite() { + return Err(PsychometricError::InvalidNumericInput); + } + score_sum += row.score; + groups.entry(row.cluster_key).or_default().push(row); + } + if groups.len() < 2 { + return Err(PsychometricError::InsufficientClusters); + } + let grand_mean = if score_sum.is_finite() { + score_sum / (rows.len() as f64) + } else { + overflow_safe_signed_mean(groups.values().flatten().map(|row| row.score))? + }; + let mut pairs = Vec::new(); + for occasions in groups.values_mut() { + if occasions.len() < 2 { + continue; + } + occasions.sort_by(|left, right| left.event_time.total_cmp(&right.event_time)); + for window in occasions.windows(2) { + let earlier_residual = window[0].score - grand_mean; + let later_residual = window[1].score - grand_mean; + let event_delta = window[1].event_time - window[0].event_time; + if !event_delta.is_finite() || event_delta <= 0.0 { + return Err(PsychometricError::NonPositiveInterval); + } + if !(earlier_residual.is_finite() & later_residual.is_finite()) { + return Err(PsychometricError::InvalidNumericInput); + } + pairs.push(LaggedGrandMeanResidual { + earlier_residual, + later_residual, + event_delta, + }); + } + } + if pairs.is_empty() { + return Err(PsychometricError::InvalidNumericInput); + } + Ok(pairs) +} + +/// Pairwise-mean exact log-rate after grand-mean centering. +/// +/// This is [`center_grand_mean_event_lags`] then the pairwise mean of +/// Voelkle et al. (2012, Eq. 7) on nonzero same-sign residuals. When +/// `|later| / |earlier|` is finite and positive the rate is +/// `ln(|later| / |earlier|) / Δt`; overflowed or underflowed ratios use +/// `(ln|later| − ln|earlier|) / Δt`. The pairwise mean is formed +/// incrementally so two finite rates whose raw sum overflows stay +/// representable. It is **not** CWC and is **not** a within-person lag +/// (Hamaker, Kuiper, & Grasman, 2015, p. 104). The pairs are +/// [`LaggedGrandMeanResidual`], so they cannot enter +/// [`recover_irregular_centered_residual_log_rate`]. It is not DSEM. +/// +/// # Errors +/// +/// Propagates centering errors from [`center_grand_mean_event_lags`]. A +/// non-finite log-rate after the stable logarithm is +/// [`PsychometricError::InvalidNumericInput`]. An empty admissible list +/// after skipping zero and opposite-sign pairs is +/// [`PsychometricError::InvalidNumericInput`]. +pub fn recover_grand_mean_centered_irregular_residual_log_rate( + rows: &[ClusteredEventScore], + clock: LagClock, +) -> Result { + let lagged = center_grand_mean_event_lags(rows, clock)?; + let mut mean = 0.0_f64; + let mut count = 0.0_f64; + for pair in lagged { + if !same_sign_nonzero(pair.earlier_residual, pair.later_residual) { + continue; + } + let rate = voelkle_same_sign_log_rate( + pair.earlier_residual, + pair.later_residual, + pair.event_delta, + )?; + (mean, count) = overflow_safe_running_mean(mean, count, rate); + } + if count <= 0.0 { + return Err(PsychometricError::InvalidNumericInput); + } + require_finite(mean) +} + +/// Refuse treating a grand-mean-centered log-rate as a within-person lag. +/// +/// Always fails closed. Hamaker, Kuiper, and Grasman (2015) show that +/// lagged relations from grand-mean deviations confound stable +/// between-person differences with within-person change. +/// +/// # Errors +/// +/// Always returns +/// [`PsychometricError::GrandMeanCenteredLogRateIsNotWithinPersonLag`]. +pub fn refuse_grand_mean_centered_log_rate_as_within_person_lag( + grand_mean_log_rate: f64, + within_person_lag: f64, +) -> Result { + let _ = (grand_mean_log_rate, within_person_lag); + Err(PsychometricError::GrandMeanCenteredLogRateIsNotWithinPersonLag) +} + /// Mean exact scalar log-rate on already-centered residuals with irregular intervals. /// /// Each pair is `a = ln(later / earlier) / Δt` (Voelkle et al., 2012, Eq. 7). /// The function does **not** center again. Curran and Bauer (2011, pp. 607–608) /// reject person-mean subtraction on a raw autoregressive series as the /// lagged within-person residual. Intervals may be irregular. This is not DSEM. +/// Grand-mean pairs are [`LaggedGrandMeanResidual`] and cannot enter this +/// helper (Hamaker, Kuiper, & Grasman, 2015, p. 104). /// /// # Errors /// @@ -7003,9 +7210,9 @@ pub(crate) fn fit_scalar_log_rate(pairs: &[(f64, f64, f64)]) -> Result [ClusteredEventScore; 3] { let delta = 1e-305_f64; let (first, second) = if growing { @@ -8972,6 +9209,487 @@ mod tests { assert!((pairs[0].later_residual - (0.4 - cluster_one_mean)).abs() < 1e-15); } + fn two_wave_level_separated() -> [ClusteredEventScore; 4] { + [ + clustered(1, 0.0, 10.0), + clustered(1, 1.0, 12.0), + clustered(2, 0.0, 0.0), + clustered(2, 1.0, 1.0), + ] + } + + #[test] + fn t2_cwc_is_empty_while_t2_cgm_keeps_same_sign() { + let rows = two_wave_level_separated(); + assert_eq!( + recover_within_cluster_irregular_residual_log_rate(&rows, LagClock::EventTime), + Err(PsychometricError::InvalidNumericInput), + "T=2 CWC is always r, −r" + ); + let cgm = + recover_grand_mean_centered_irregular_residual_log_rate(&rows, LagClock::EventTime) + .expect("T=2 CGM same-sign"); + assert!(cgm.is_finite()); + let pairs = center_grand_mean_event_lags(&rows, LagClock::EventTime).expect("cgm pairs"); + assert_eq!(pairs.len(), 2); + assert!( + pairs + .iter() + .all(|pair| same_sign_nonzero(pair.earlier_residual, pair.later_residual)) + ); + let cwc_pairs = + center_within_cluster_event_lags(&rows, LagClock::EventTime).expect("cwc pairs"); + assert!( + cwc_pairs + .iter() + .all(|pair| !same_sign_nonzero(pair.earlier_residual, pair.later_residual)) + ); + } + + #[test] + fn grand_mean_centered_log_rate_is_not_cwc_or_within_person_lag() { + let drift = -0.3_f64; + let rows = decaying_clustered_scores(drift); + let cgm = + recover_grand_mean_centered_irregular_residual_log_rate(&rows, LagClock::EventTime) + .expect("cgm"); + let cwc = recover_within_cluster_irregular_residual_log_rate(&rows, LagClock::EventTime) + .expect("cwc"); + assert!( + (cgm - cwc).abs() > 1e-6, + "CGM {cgm} must not equal CWC {cwc}" + ); + assert!( + (cgm - drift).abs() > 1e-6, + "Hamaker (2015, p. 104): CGM {cgm} must not equal within-person drift {drift}" + ); + let already = recover_irregular_centered_residual_log_rate( + &[ + lagged(1.0, 1.0 * drift.exp(), 1.0), + lagged(-0.8, -0.8 * (drift * 1.5).exp(), 1.5), + ], + LagClock::EventTime, + ) + .expect("already centered"); + assert!((already - drift).abs() < 1e-12); + assert!((cgm - already).abs() > 1e-6); + assert_eq!( + refuse_grand_mean_centered_log_rate_as_within_person_lag(cgm, already), + Err(PsychometricError::GrandMeanCenteredLogRateIsNotWithinPersonLag) + ); + assert_eq!( + refuse_grand_mean_centered_log_rate_as_within_person_lag(f64::NAN, f64::INFINITY), + Err(PsychometricError::GrandMeanCenteredLogRateIsNotWithinPersonLag) + ); + } + + #[test] + fn equal_cluster_means_make_cgm_equal_cwc() { + let rows = [ + clustered(1, 0.0, 1.0), + clustered(1, 1.0, 0.5), + clustered(1, 2.0, 0.25), + clustered(1, 3.0, 0.125), + clustered(2, 0.0, 1.0), + clustered(2, 1.0, 0.5), + clustered(2, 2.0, 0.25), + clustered(2, 3.0, 0.125), + ]; + let cgm = + recover_grand_mean_centered_irregular_residual_log_rate(&rows, LagClock::EventTime) + .expect("cgm"); + let cwc = recover_within_cluster_irregular_residual_log_rate(&rows, LagClock::EventTime) + .expect("cwc"); + assert!((cgm - cwc).abs() < 1e-15); + let extracted = center_grand_mean_event_lags(&rows, LagClock::EventTime).expect("extract"); + let cwc_extracted = + center_within_cluster_event_lags(&rows, LagClock::EventTime).expect("cwc extract"); + let admissible: Vec = cwc_extracted + .iter() + .copied() + .filter(|pair| same_sign_nonzero(pair.earlier_residual, pair.later_residual)) + .collect(); + let from_cwc_pairs = + recover_irregular_centered_residual_log_rate(&admissible, LagClock::EventTime) + .expect("cwc pairs"); + assert!((cwc - from_cwc_pairs).abs() < 1e-15); + assert_eq!(extracted.len(), cwc_extracted.len()); + } + + #[test] + fn grand_mean_pairwise_keeps_overflowed_same_sign_ratio_via_stable_log() { + let rows = [ + clustered(1, 0.0, 1e-160), + clustered(1, 1.0, 1e160), + clustered(2, 0.0, -1e-160), + clustered(2, 1.0, -1e160), + ]; + let recovered = + recover_grand_mean_centered_irregular_residual_log_rate(&rows, LagClock::EventTime) + .expect("stable log overflow"); + let extracted = center_grand_mean_event_lags(&rows, LagClock::EventTime).expect("extract"); + assert_eq!(extracted.len(), 2); + let mut expected = 0.0_f64; + let mut count = 0.0_f64; + for pair in extracted { + let ratio = pair.later_residual / pair.earlier_residual; + assert!( + !ratio.is_finite(), + "fixture pairs overflow; ordinary-ratio reconstruction is a different test" + ); + let rate = voelkle_same_sign_log_rate( + pair.earlier_residual, + pair.later_residual, + pair.event_delta, + ) + .expect("pair rate"); + (expected, count) = overflow_safe_running_mean(expected, count, rate); + } + assert_eq!(count.to_bits(), 2.0_f64.to_bits()); + assert!((recovered - expected).abs() < 1e-12); + } + + #[test] + fn grand_mean_pairwise_keeps_underflowed_same_sign_ratio_via_stable_log() { + let rows = [ + clustered(1, 0.0, 1e30), + clustered(1, 1.0, 1e-300), + clustered(2, 0.0, -1e30), + clustered(2, 1.0, -1e-300), + ]; + let recovered = + recover_grand_mean_centered_irregular_residual_log_rate(&rows, LagClock::EventTime) + .expect("stable log underflow"); + let extracted = center_grand_mean_event_lags(&rows, LagClock::EventTime).expect("extract"); + assert_eq!(extracted.len(), 2); + let mut expected = 0.0_f64; + let mut count = 0.0_f64; + for pair in extracted { + let ratio = pair.later_residual / pair.earlier_residual; + assert_eq!( + ratio.to_bits(), + 0.0_f64.to_bits(), + "fixture pairs underflow to +0" + ); + let rate = voelkle_same_sign_log_rate( + pair.earlier_residual, + pair.later_residual, + pair.event_delta, + ) + .expect("pair rate"); + (expected, count) = overflow_safe_running_mean(expected, count, rate); + } + assert_eq!(count.to_bits(), 2.0_f64.to_bits()); + assert!((recovered - expected).abs() < 1e-12); + } + + #[test] + fn grand_mean_pairwise_tiny_interval_with_huge_log_ratio_fails_closed() { + assert_eq!( + recover_grand_mean_centered_irregular_residual_log_rate( + &[ + clustered(1, 0.0, 1e-160), + clustered(1, f64::from_bits(1), 1e160), + clustered(2, 0.0, -1e-160), + clustered(2, f64::from_bits(1), -1e160), + ], + LagClock::EventTime + ), + Err(PsychometricError::InvalidNumericInput) + ); + } + + #[test] + fn grand_mean_irregular_residual_paths_fail_closed() { + let rows = decaying_clustered_scores(-0.25); + assert_eq!( + center_grand_mean_event_lags(&rows, LagClock::SystemTime), + Err(PsychometricError::EventTimeRequired) + ); + assert_eq!( + recover_grand_mean_centered_irregular_residual_log_rate(&rows, LagClock::SystemTime), + Err(PsychometricError::EventTimeRequired) + ); + assert_eq!( + center_grand_mean_event_lags(&[], LagClock::EventTime), + Err(PsychometricError::InvalidNumericInput) + ); + assert_eq!( + recover_grand_mean_centered_irregular_residual_log_rate(&[], LagClock::EventTime), + Err(PsychometricError::InvalidNumericInput) + ); + assert_eq!( + center_grand_mean_event_lags( + &[clustered(1, 0.0, 1.0), clustered(1, 1.0, 0.5)], + LagClock::EventTime + ), + Err(PsychometricError::InsufficientClusters) + ); + assert_eq!( + recover_grand_mean_centered_irregular_residual_log_rate( + &[clustered(1, 0.0, 1.0), clustered(1, 1.0, 0.5)], + LagClock::EventTime + ), + Err(PsychometricError::InsufficientClusters) + ); + assert_eq!( + center_grand_mean_event_lags( + &[clustered(1, 0.0, 1.0), clustered(2, 1.0, 0.5)], + LagClock::EventTime + ), + Err(PsychometricError::InvalidNumericInput) + ); + assert_eq!( + center_grand_mean_event_lags( + &[ + clustered(1, 0.0, 1.0), + clustered(1, 0.0, 1.2), + clustered(2, 0.0, 2.0), + clustered(2, 1.0, 1.5), + ], + LagClock::EventTime + ), + Err(PsychometricError::NonPositiveInterval) + ); + assert_eq!( + recover_grand_mean_centered_irregular_residual_log_rate( + &[ + clustered(1, 0.0, 1.0), + clustered(1, 0.0, 1.2), + clustered(2, 0.0, 2.0), + clustered(2, 1.0, 1.5), + ], + LagClock::EventTime + ), + Err(PsychometricError::NonPositiveInterval) + ); + assert_eq!( + recover_grand_mean_centered_irregular_residual_log_rate( + &[ + clustered(1, 0.0, 1.0), + clustered(1, 1.0, 2.0), + clustered(1, 2.0, 3.0), + clustered(2, 0.0, 1.0), + clustered(2, 1.0, 2.0), + clustered(2, 2.0, 3.0), + ], + LagClock::EventTime + ), + Err(PsychometricError::InvalidNumericInput), + "T=3 arithmetic progression around a shared grand mean has a zero residual" + ); + } + + #[test] + fn grand_mean_irregular_residual_numeric_inputs_fail_closed() { + assert_eq!( + center_grand_mean_event_lags( + &[ + clustered(1, f64::NAN, 1.0), + clustered(1, 1.0, 0.5), + clustered(2, 0.0, 1.0), + clustered(2, 1.0, 0.5), + ], + LagClock::EventTime + ), + Err(PsychometricError::InvalidNumericInput) + ); + assert_eq!( + center_grand_mean_event_lags( + &[ + clustered(1, 0.0, f64::NAN), + clustered(1, 1.0, 1.0), + clustered(2, 0.0, 1.0), + clustered(2, 1.0, 0.5), + ], + LagClock::EventTime + ), + Err(PsychometricError::InvalidNumericInput) + ); + assert_eq!( + recover_grand_mean_centered_irregular_residual_log_rate( + &[ + clustered(1, 0.0, f64::INFINITY), + clustered(1, 1.0, 1.0), + clustered(2, 0.0, 1.0), + clustered(2, 1.0, 0.5), + ], + LagClock::EventTime + ), + Err(PsychometricError::InvalidNumericInput) + ); + assert_eq!( + center_grand_mean_event_lags( + &[ + clustered(2, 0.0, -f64::MAX), + clustered(1, 0.0, f64::MAX), + clustered(2, 1.0, -f64::MAX), + clustered(1, 1.0, 0.0), + ], + LagClock::EventTime + ), + Err(PsychometricError::InvalidNumericInput), + "finite grand mean with overflowing residual fails closed" + ); + assert_eq!( + center_grand_mean_event_lags(&[clustered(1, 0.0, 1.0)], LagClock::EventTime), + Err(PsychometricError::InvalidNumericInput) + ); + assert_eq!( + center_grand_mean_event_lags( + &[ + clustered(1, f64::MAX, 1.0), + clustered(1, -f64::MAX, 0.5), + clustered(2, 0.0, 1.0), + clustered(2, 1.0, 0.5), + ], + LagClock::EventTime + ), + Err(PsychometricError::NonPositiveInterval) + ); + } + + #[test] + fn grand_mean_overflowing_sum_keeps_representable_mean() { + let pairs = center_grand_mean_event_lags( + &[ + clustered(1, 0.0, f64::MAX), + clustered(1, 1.0, f64::MAX), + clustered(2, 0.0, f64::MAX), + clustered(2, 1.0, f64::MAX), + ], + LagClock::EventTime, + ) + .expect("finite all-MAX grand mean"); + assert_eq!(pairs.len(), 2); + assert_eq!(pairs[0].earlier_residual.to_bits(), 0.0_f64.to_bits()); + assert_eq!(pairs[0].later_residual.to_bits(), 0.0_f64.to_bits()); + assert_eq!( + recover_grand_mean_centered_irregular_residual_log_rate( + &[ + clustered(1, 0.0, f64::MAX), + clustered(1, 1.0, f64::MAX), + clustered(2, 0.0, f64::MAX), + clustered(2, 1.0, f64::MAX), + ], + LagClock::EventTime + ), + Err(PsychometricError::InvalidNumericInput), + "zero residuals after a finite all-MAX grand mean are not admissible" + ); + let neg_pairs = center_grand_mean_event_lags( + &[ + clustered(1, 0.0, -f64::MAX), + clustered(1, 1.0, -f64::MAX), + clustered(2, 0.0, -f64::MAX), + clustered(2, 1.0, -f64::MAX), + ], + LagClock::EventTime, + ) + .expect("finite all-neg-MAX grand mean"); + assert_eq!(neg_pairs.len(), 2); + assert_eq!(neg_pairs[0].earlier_residual.to_bits(), 0.0_f64.to_bits()); + assert_eq!(neg_pairs[0].later_residual.to_bits(), 0.0_f64.to_bits()); + let zero_pairs = center_grand_mean_event_lags( + &[ + clustered(1, 0.0, f64::MAX), + clustered(1, 1.0, f64::MAX), + clustered(2, 0.0, 0.0), + clustered(2, 1.0, 0.0), + ], + LagClock::EventTime, + ) + .expect("zeros keep overflowing positives"); + assert_eq!(zero_pairs.len(), 2); + assert_eq!( + zero_pairs[0].earlier_residual.to_bits(), + (f64::MAX / 2.0).to_bits() + ); + assert_eq!( + zero_pairs[1].earlier_residual.to_bits(), + (-(f64::MAX / 2.0)).to_bits() + ); + } + + #[test] + fn grand_mean_centering_is_stable_across_score_order() { + let positives_first = [ + clustered(1, 0.0, f64::MAX), + clustered(1, 1.0, f64::MAX), + clustered(2, 0.0, -f64::MAX), + clustered(2, 1.0, -f64::MAX), + ]; + let negatives_first = [ + clustered(2, 0.0, -f64::MAX), + clustered(2, 1.0, -f64::MAX), + clustered(1, 0.0, f64::MAX), + clustered(1, 1.0, f64::MAX), + ]; + let positives = + center_grand_mean_event_lags(&positives_first, LagClock::EventTime).expect("pos"); + let negatives = + center_grand_mean_event_lags(&negatives_first, LagClock::EventTime).expect("neg"); + assert_eq!(positives, negatives); + assert_eq!(positives.len(), 2); + assert_eq!(positives[0].earlier_residual.to_bits(), f64::MAX.to_bits()); + assert_eq!(positives[0].later_residual.to_bits(), f64::MAX.to_bits()); + assert_eq!( + positives[1].earlier_residual.to_bits(), + (-f64::MAX).to_bits() + ); + assert_eq!(positives[1].later_residual.to_bits(), (-f64::MAX).to_bits()); + let recovered = recover_grand_mean_centered_irregular_residual_log_rate( + &positives_first, + LagClock::EventTime, + ) + .expect("same-sign MAX pairs"); + let recovered_neg = recover_grand_mean_centered_irregular_residual_log_rate( + &negatives_first, + LagClock::EventTime, + ) + .expect("same-sign MAX pairs reversed"); + assert_eq!(recovered.to_bits(), 0.0_f64.to_bits()); + assert_eq!(recovered_neg.to_bits(), 0.0_f64.to_bits()); + } + + #[test] + fn grand_mean_orders_unsorted_event_times_before_lag_pairs() { + let later = clustered(1, 2.0, 12.0); + let earlier = clustered(1, 0.5, 10.0); + let other_later = clustered(2, 3.0, 1.0); + let other_earlier = clustered(2, 1.0, 0.0); + let pairs = center_grand_mean_event_lags( + &[later, other_later, earlier, other_earlier], + LagClock::EventTime, + ) + .expect("unsorted"); + assert_eq!(pairs.len(), 2); + assert!((pairs[0].event_delta - 1.5).abs() < 1e-15); + assert!((pairs[1].event_delta - 2.0).abs() < 1e-15); + let grand_mean = 5.75_f64; + assert!((pairs[0].earlier_residual - (10.0 - grand_mean)).abs() < 1e-15); + assert!((pairs[0].later_residual - (12.0 - grand_mean)).abs() < 1e-15); + } + + #[test] + fn grand_mean_skips_singleton_cluster() { + let mixed = [ + clustered(1, 0.0, 10.0), + clustered(1, 1.0, 12.0), + clustered(1, 2.0, 11.0), + clustered(2, 0.0, 4.0), + ]; + let pairs = center_grand_mean_event_lags(&mixed, LagClock::EventTime).expect("skip"); + assert_eq!(pairs.len(), 2); + assert_eq!( + center_grand_mean_event_lags( + &[clustered(1, 0.0, 1.0), clustered(2, 1.0, 0.5)], + LagClock::EventTime + ), + Err(PsychometricError::InvalidNumericInput) + ); + } + #[test] fn singleton_cluster_is_skipped_and_all_singletons_fail_closed() { let drift = -0.2_f64; diff --git a/crates/psychometric_core/src/lib.rs b/crates/psychometric_core/src/lib.rs index 2712256ed..c25e0ec31 100644 --- a/crates/psychometric_core/src/lib.rs +++ b/crates/psychometric_core/src/lib.rs @@ -16,7 +16,10 @@ //! `ln(|later| / |earlier|) / Δt` when that ratio is finite, else //! `(ln|later| − ln|earlier|) / Δt`; the pairwise mean is incremental so //! two finite rates whose raw sum overflows stay representable; -//! not Newton LS, not raw-process AR drift), remaps discrete +//! not Newton LS, not raw-process AR drift), extracts grand-mean-centered +//! `LaggedGrandMeanResidual` pairs and a pairwise-mean log-rate after that +//! CGM (Hamaker et al., 2015, p. 104: not a within-person lag; not +//! `LaggedWithinResidual`), remaps discrete //! lags across unequal event intervals through that log-rate, recovers the //! exact scalar discrete effect of a constant predictor, recovers the //! first-order discrete effect of a time-varying predictor with matched @@ -313,8 +316,12 @@ pub use event_time::DiscreteLagAndLogRate; pub use event_time::EventOccasion; /// Clock on which a structural lag may be computed. pub use event_time::LagClock; +/// Grand-mean-centered lagged residual pair (not a within-person residual). +pub use event_time::LaggedGrandMeanResidual; /// Already-centered lagged residual pair with an irregular event interval. pub use event_time::LaggedWithinResidual; +/// Grand-mean-center consecutive event-time lags (not a within-person lag). +pub use event_time::center_grand_mean_event_lags; /// Cluster-mean-center consecutive event-time lags (not raw-process AR drift). pub use event_time::center_within_cluster_event_lags; /// Map a discrete lag onto another event interval through the exact log-rate. @@ -379,6 +386,8 @@ pub use event_time::recover_discrete_time_varying_predictor_effect; pub use event_time::recover_event_series_mean_log_rate; /// Exact scalar pair `(φ, a)` on event time. pub use event_time::recover_event_time_discrete_lag_and_log_rate; +/// Pairwise-mean exact log-rate after grand-mean centering (not a within-person lag; not DSEM). +pub use event_time::recover_grand_mean_centered_irregular_residual_log_rate; /// Exact scalar carried first-occasion `T0TDPREDEFFECT` `e^{A Δt} t0_m x0`. pub use event_time::recover_initial_time_dependent_predictor_carry; /// Exact scalar first-occasion `T0TDPREDEFFECT` shift `t0_m x0`. @@ -530,6 +539,8 @@ pub use event_time::refuse_extra_process_latent_mean_as_observed_mean; pub use event_time::refuse_extra_process_observed_mean_as_after_extra_process_observed_mean; /// Refuse treating finite-interval `Q_Δt` as `asymDIFFUSION`. pub use event_time::refuse_finite_interval_process_noise_as_stationary_variance; +/// Refuse treating a grand-mean-centered log-rate as a within-person lag. +pub use event_time::refuse_grand_mean_centered_log_rate_as_within_person_lag; /// Refuse treating impulse-carry `τ + λ(μ_t + e^{a(t−u)} m x)` as the after-t0 extra-process observed mean. pub use event_time::refuse_impulse_carry_observed_mean_as_after_extra_process_observed_mean; /// Refuse treating impulse-carry `τ + λ(μ_t + e^{a(t−u)} m x)` as the first-occasion TD-predictor observed mean. diff --git a/crates/psychometric_core/tests/multilevel_event_time_recovery_contract.rs b/crates/psychometric_core/tests/multilevel_event_time_recovery_contract.rs index 0e84d854d..5b151d15b 100644 --- a/crates/psychometric_core/tests/multilevel_event_time_recovery_contract.rs +++ b/crates/psychometric_core/tests/multilevel_event_time_recovery_contract.rs @@ -3,9 +3,10 @@ use psychometric_core::{ ClusteredEventScore, ClusteredScore, EventOccasion, IndicatorKind, LagClock, - LaggedWithinResidual, PsychometricError, center_within_cluster_event_lags, - map_discrete_lag_across_event_intervals, ordinary_least_squares_slope, - recover_asymptotic_continuous_intercept, recover_asymptotic_time_independent_predictor_effect, + LaggedWithinResidual, PsychometricError, center_grand_mean_event_lags, + center_within_cluster_event_lags, map_discrete_lag_across_event_intervals, + ordinary_least_squares_slope, recover_asymptotic_continuous_intercept, + recover_asymptotic_time_independent_predictor_effect, recover_asymptotic_time_independent_predictor_variance, recover_cluster_mean_within_between_slopes, recover_discrete_constant_predictor_effect, recover_discrete_continuous_intercept_effect, recover_discrete_lag_from_log_rate, @@ -24,7 +25,9 @@ use psychometric_core::{ recover_discrete_observed_mean_with_time_independent_predictor, recover_discrete_process_noise, recover_discrete_time_independent_predictor_effect, recover_discrete_time_varying_predictor_effect, recover_event_series_mean_log_rate, - recover_event_time_discrete_lag_and_log_rate, recover_initial_time_dependent_predictor_carry, + recover_event_time_discrete_lag_and_log_rate, + recover_grand_mean_centered_irregular_residual_log_rate, + recover_initial_time_dependent_predictor_carry, recover_initial_time_dependent_predictor_effect, recover_initial_time_independent_predictor_carry, recover_initial_time_independent_predictor_effect, @@ -79,6 +82,7 @@ use psychometric_core::{ refuse_extra_process_latent_mean_as_observed_mean, refuse_extra_process_observed_mean_as_after_extra_process_observed_mean, refuse_finite_interval_process_noise_as_stationary_variance, + refuse_grand_mean_centered_log_rate_as_within_person_lag, refuse_impulse_carry_observed_mean_as_after_extra_process_observed_mean, refuse_impulse_carry_observed_mean_as_initial_time_dependent_observed_mean, refuse_impulse_carry_observed_mean_as_initial_time_independent_observed_mean, @@ -810,6 +814,69 @@ fn cwc_pairwise_mean_keeps_overflowed_finite_rate_sum() { assert!(mixed_mean.abs() < recovered.abs() * 1e-12); } +#[test] +fn grand_mean_centered_irregular_residuals_do_not_recover_within_person_drift() { + let true_drift = -0.35_f64; + let pairs = [ + LaggedWithinResidual { + earlier_residual: 1.4, + later_residual: 1.4 * (true_drift * 0.4).exp(), + event_delta: 0.4, + }, + LaggedWithinResidual { + earlier_residual: 0.9, + later_residual: 0.9 * (true_drift * 1.6).exp(), + event_delta: 1.6, + }, + LaggedWithinResidual { + earlier_residual: -0.7, + later_residual: -0.7 * (true_drift * 2.2).exp(), + event_delta: 2.2, + }, + ]; + let centered = recover_irregular_centered_residual_log_rate(&pairs, LagClock::EventTime) + .expect("centered residual"); + let centered_error = rmse(&[true_drift], &[centered]); + assert!( + centered_error < 1e-12, + "already-centered irregular RMSE {centered_error}" + ); + + let mut raw_ar = Vec::new(); + for (cluster, person_mean, start) in [(1_u64, 7.5_f64, 1.1_f64), (2, -4.0, 0.8)] { + for step in 0..6 { + let time = f64::from(step); + raw_ar.push(ClusteredEventScore { + cluster_key: cluster, + event_time: time, + score: person_mean + start * (true_drift * time).exp(), + }); + } + } + let cgm = recover_grand_mean_centered_irregular_residual_log_rate(&raw_ar, LagClock::EventTime) + .expect("cgm"); + let cwc = recover_within_cluster_irregular_residual_log_rate(&raw_ar, LagClock::EventTime) + .expect("cwc pairwise"); + let cgm_error = rmse(&[true_drift], &[cgm]); + assert!( + cgm_error > centered_error, + "Hamaker (2015, p. 104): CGM RMSE {cgm_error} must exceed already-centered {centered_error}" + ); + assert!( + (cgm - cwc).abs() > 1e-6, + "CGM {cgm} is not CWC {cwc} when between-person levels differ" + ); + let extracted = center_grand_mean_event_lags(&raw_ar, LagClock::EventTime).expect("extract"); + assert!( + extracted.iter().any(|pair| pair.earlier_residual != 0.0), + "level-separated CGM residuals are not all zero" + ); + assert_eq!( + refuse_grand_mean_centered_log_rate_as_within_person_lag(cgm, true_drift), + Err(PsychometricError::GrandMeanCenteredLogRateIsNotWithinPersonLag) + ); +} + #[test] fn discrete_latent_variance_recovers_driver_equations_three_and_four() { let prior = 2.0_f64; diff --git a/crates/psychometric_core/tests/scientific_claim_boundary_contract.rs b/crates/psychometric_core/tests/scientific_claim_boundary_contract.rs index 8d12b27de..ff9a7795e 100644 --- a/crates/psychometric_core/tests/scientific_claim_boundary_contract.rs +++ b/crates/psychometric_core/tests/scientific_claim_boundary_contract.rs @@ -2,7 +2,7 @@ use psychometric_core::{ ClusteredEventScore, ClusteredScore, IndicatorKind, LagClock, LaggedWithinResidual, - center_within_cluster_event_lags, ordinary_least_squares_slope, + center_grand_mean_event_lags, center_within_cluster_event_lags, ordinary_least_squares_slope, posterior_draw_point_estimate_mean, recover_asymptotic_continuous_intercept, recover_asymptotic_time_independent_predictor_effect, recover_asymptotic_time_independent_predictor_variance, @@ -21,7 +21,9 @@ use psychometric_core::{ recover_discrete_observed_mean_with_initial_time_independent_predictor, recover_discrete_observed_mean_with_time_independent_predictor, recover_discrete_process_noise, recover_discrete_time_independent_predictor_effect, - recover_discrete_time_varying_predictor_effect, recover_initial_time_dependent_predictor_carry, + recover_discrete_time_varying_predictor_effect, + recover_grand_mean_centered_irregular_residual_log_rate, + recover_initial_time_dependent_predictor_carry, recover_initial_time_dependent_predictor_effect, recover_initial_time_independent_predictor_carry, recover_initial_time_independent_predictor_effect, @@ -79,6 +81,7 @@ use psychometric_core::{ refuse_extra_process_latent_mean_as_observed_mean, refuse_extra_process_observed_mean_as_after_extra_process_observed_mean, refuse_finite_interval_process_noise_as_stationary_variance, + refuse_grand_mean_centered_log_rate_as_within_person_lag, refuse_impulse_carry_observed_mean_as_after_extra_process_observed_mean, refuse_impulse_carry_observed_mean_as_initial_time_dependent_observed_mean, refuse_impulse_carry_observed_mean_as_initial_time_independent_observed_mean, @@ -301,6 +304,104 @@ fn person_mean_subtraction_on_raw_ar_is_not_the_lagged_within_effect() { ); } +#[test] +fn grand_mean_centered_lag_is_not_the_within_person_effect() { + let drift = -0.28_f64; + let centered = recover_irregular_centered_residual_log_rate( + &[LaggedWithinResidual { + earlier_residual: 1.0, + later_residual: (drift * 1.3).exp(), + event_delta: 1.3, + }], + LagClock::EventTime, + ) + .expect("already centered"); + assert!((centered - drift).abs() < 1e-12); + + let raw = [ + ClusteredEventScore { + cluster_key: 1, + event_time: 0.0, + score: 6.0 + 1.0, + }, + ClusteredEventScore { + cluster_key: 1, + event_time: 1.0, + score: 6.0 + drift.exp(), + }, + ClusteredEventScore { + cluster_key: 1, + event_time: 2.0, + score: 6.0 + (drift * 2.0).exp(), + }, + ClusteredEventScore { + cluster_key: 2, + event_time: 0.0, + score: -3.0 + 1.2, + }, + ClusteredEventScore { + cluster_key: 2, + event_time: 1.0, + score: -3.0 + 1.2 * drift.exp(), + }, + ClusteredEventScore { + cluster_key: 2, + event_time: 2.0, + score: -3.0 + 1.2 * (drift * 2.0).exp(), + }, + ]; + let cgm = recover_grand_mean_centered_irregular_residual_log_rate(&raw, LagClock::EventTime) + .expect("cgm"); + let cwc = + recover_within_cluster_irregular_residual_log_rate(&raw, LagClock::EventTime).expect("cwc"); + assert!( + (cgm - drift).abs() > 1e-6, + "Hamaker, Kuiper, & Grasman (2015, p. 104): CGM {cgm} must not equal within-person drift {drift}" + ); + assert!( + (cgm - cwc).abs() > 1e-6, + "CGM {cgm} is not CWC {cwc} when between-person levels differ" + ); + let t2 = [ + ClusteredEventScore { + cluster_key: 1, + event_time: 0.0, + score: 10.0, + }, + ClusteredEventScore { + cluster_key: 1, + event_time: 1.0, + score: 12.0, + }, + ClusteredEventScore { + cluster_key: 2, + event_time: 0.0, + score: 0.0, + }, + ClusteredEventScore { + cluster_key: 2, + event_time: 1.0, + score: 1.0, + }, + ]; + assert_eq!( + recover_within_cluster_irregular_residual_log_rate(&t2, LagClock::EventTime), + Err(psychometric_core::PsychometricError::InvalidNumericInput), + "T=2 CWC is always r, −r" + ); + let t2_cgm = recover_grand_mean_centered_irregular_residual_log_rate(&t2, LagClock::EventTime) + .expect("T=2 CGM same-sign"); + assert!(t2_cgm.is_finite()); + let extracted = center_grand_mean_event_lags(&t2, LagClock::EventTime).expect("extract"); + assert!(extracted.iter().all(|pair| pair.earlier_residual != 0.0 + && pair.later_residual != 0.0 + && pair.earlier_residual.is_sign_positive() == pair.later_residual.is_sign_positive())); + assert_eq!( + refuse_grand_mean_centered_log_rate_as_within_person_lag(cgm, drift), + Err(psychometric_core::PsychometricError::GrandMeanCenteredLogRateIsNotWithinPersonLag) + ); +} + #[test] fn cwc_cluster_mean_coefficient_is_not_the_between_cluster_effect() { let rows = [ diff --git a/docs/adr/0005-posterior-esem-dsem.md b/docs/adr/0005-posterior-esem-dsem.md index e7c247c42..e7328408b 100644 --- a/docs/adr/0005-posterior-esem-dsem.md +++ b/docs/adr/0005-posterior-esem-dsem.md @@ -27,8 +27,7 @@ Longitudinal analysis evaluates measurement invariance at the level needed for t This ADR remains **accepted-target**. Naming ESEM/DSEM and compositional coordinates as the model-family contract is not a protected-main implementation claim. -The executable multilevel slice is cluster-mean centering (CWC) plus within/between OLS, the CWC contextual effect (`between − within`), and Kish-weighted slopes. Enders and Tofighi (2007, Table 2, pp. 124–127) show that the CWC cluster-mean coefficient is the contextual effect, not the between-cluster effect. It is not DSEM and not RI-CLPM. The executable temporal slice maps a discrete lag through the exact scalar exponential `a = ln(φ) / Δt` on event time only (Voelkle, Oud, Davidov, & Schmidt, 2012, Eq. 7; Driver, Oud, & Voelkle, 2017, Eq. 3), recovers the forward map `φ(Δt) = exp(a Δt)`, remaps a discrete lag onto another event interval through that log-rate, recovers the exact scalar discrete effect of a constant predictor (Voelkle et al., 2012, Eq. 12) as `a_yx (expm1(z) / a_xx)` with `z = a_xx Δt` so a finite result is not lost when `z` overflows to `-∞` or when `a_yx Δt` overflows, and in log space when `expm1(z)` overflows at a finite `z` (`a_yx = 0` is exactly zero; an overflowing `a_yx/a_xx` rewrite term fails closed), recovers the first-order discrete effect of a time-varying predictor with matched sampling and constancy intervals as `a_yx Δt` (Voelkle et al., 2012, Eq. 14; ZORA accepted manuscript p. 21; not Eq. 12; unmatched intervals fail closed because Oud & Jansen, 2000, is unread), recovers the exact scalar discrete process noise `Q_Δt = 0.5 q (expm1(z) / a)` with `z = 2 (a Δt)` and `q = G G⊤ ≥ 0` (Driver et al., 2017, Eq. 3; JSS PDF re-opened 2026-08-18T07:06Z, p. 4; scalar `L = 1`; do not form `2 a` first; `a = 0` and `z → 0` recover `q Δt`; `z → −∞` keeps `−0.5 q / a`; a zero diffusion is exactly zero; `z → +∞` and an overflowing rewrite scale `0.5 q / a` fail closed), recovers the exact scalar lagged latent covariance `exp(a Δt) p` and the law-of-total-variance map `Var(η_t) = exp(2 a Δt) p + Q_Δt` (Driver et al., 2017, Eq. 3–4, pp. 4–5; JSS PDF re-opened 2026-08-18T18:03Z; JSS has no numbered §2.2; `Q_Δt` is `cov(η_t | η_{t-1})` and is refused as the unconditional variance; a zero diffusion whose `2 (a Δt)` overflows to `+∞` fails closed), recovers the exact scalar stationary within-subject variance `-q / (2 a)` as the `Δt → ∞` limit of Eq. 4 for stable `a < 0` (JSS p. 16 `asymDIFFUSION`; §4.3 T0VAR stationarity; when `2 a` is finite, form `q / -(2 a)` so `q / a` overflow does not lose a finite result (`q = MAX`, `a = -0.75` → `MAX / 1.5`; CodeRabbit on `75ecdd3`); when `2 a` overflows, form `(q / a) * -0.5`; do not form `0.5 q` first (`q = from_bits(1)`, `a = -from_bits(1)` → `0.5`); `a ≥ 0` and finite-interval `Q_Δt` fail closed as that limit), recovers the exact scalar trait-plus-state variance `trait + state` and lagged covariance `trait + exp(a Δt) p` (Driver et al., 2017, §4.3, p. 9; JSS PDF re-opened 2026-08-18T21:07Z; a stable trait has `DRIFT` and `DIFFUSION` fixed to zero; `TRAITVAR` is not process noise and not `asymDIFFUSION`; evolving the summed variance as if it were all state fails the claim boundary; this is not RI-CLPM), recovers the exact scalar observed-indicator variance `λ² Var(η) + θ` when `MANIFESTTRAITVAR` is zero and `λ² Var(η) + θ + ψ` otherwise, and the lagged observed covariance `λ² cov(η_t, η_{t-1}) + ψ` (Driver et al., 2017, Eq. 5, p. 5; Table 2, p. 12; JSS PDF re-opened 2026-08-19T04:18Z; Equation 1 is the latent SDE; form `(λ p) λ` then add `θ`, then add `ψ`; do not form `λ²` first; `MANIFESTVAR` is `Θ`, not `Var(y)` and does not enter lagged observed covariance; `MANIFESTTRAITVAR` is `Ψ_τ`, not `Θ`; `TRAITVAR` is latent and scaled by `λ²`; `Var(η)` is not `Var(y)`), recovers the exact scalar observed-indicator mean `τ + λ μ` (Driver et al., 2017, Eq. 5, p. 5; Table 2, p. 12; JSS PDF re-opened 2026-08-19T14:08Z; `MANIFESTMEANS` is `τ`, not `E(y)`; `CINT` is not `MANIFESTMEANS`; `T0MEANS` is not `E(y)`), recovers the exact scalar discrete latent mean `exp(a Δt) μ_0 + (exp(a Δt) − 1)/a κ` (Driver et al., 2017, Eq. 3, p. 4; Table 2, p. 12; JSS PDF re-opened 2026-08-19T18:10Z; `T0MEANS` is not `μ_t`; `CINT` is not the discrete increment; a zero drift is `κ Δt`; underflow of `exp(a Δt)` to `+0` drops the carried `T0MEANS` and keeps `−κ / a`), recovers the exact scalar evolved observed-indicator mean `τ + λ μ_t` (Driver et al., 2017, Eq. 5 of the Eq. 3 expected-value map; JSS PDF re-opened 2026-08-19T22:10Z; the first-occasion map `τ + λ μ_0` is not `E(y_t)`; `MANIFESTMEANS` is not `E(y_t)`; `μ_t` is not `E(y_t)`), recovers the exact scalar contemporaneous time-dependent predictor impulse `m x` (Driver et al., 2017, Eq. 1–3, pp. 4–5; Table 2, p. 12; §7.2, pp. 20–21; JSS PDF re-opened 2026-08-20T07:10Z; `TDPREDEFFECT` is `M`, not `CINT`; `M x` is not `A^{-1}[e^{A Δt} − I] B z`; `M x` is not Voelkle et al., 2012, Eq. 14; the level-change form is not that impulse), recovers the exact scalar observed mean of that contemporaneous impulse `τ + λ(μ_t + m x)` (Driver et al., 2017, Eq. 5 of the Eq. 3 fourth-summand composition; JSS PDF re-opened 2026-08-20T09:01Z; the evolved map `τ + λ μ_t` is not that observed mean; the carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t`; `MANIFESTMEANS` is not `E(y_t)`; the evolved-plus-impulse latent mean is not `E(y_t)`), recovers the exact scalar time-independent predictor increment `A^{-1}[e^{A Δt} − I] B z` (Driver et al., 2017, Eq. 1–3, pp. 4–5; Table 2, p. 12; JSS PDF re-opened 2026-08-20T10:13Z; `TIPREDEFFECT` is `B`, not `κ`; form `B z` first, then the discrete intercept map; a zero drift is `B z Δt`; `B` is not the discrete increment; `A^{-1}[e^{A Δt} − I] B z` is not `CINT`, not `M x`, and not Voelkle Eq. 14), recovers the exact scalar observed mean of that increment `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` (Driver et al., 2017, Eq. 5 of the Eq. 3 printed addend after the `T0MEANS` carry and the `CINT` increment; JSS PDF re-opened 2026-08-20T12:12Z; the evolved map `τ + λ μ_t` is not that observed mean; the contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean; the carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t`; `MANIFESTMEANS` is not `E(y_t)`; the evolved-plus-increment latent mean is not `E(y_t)`), recovers the exact scalar within-interval time-dependent impulse carry `e^{A(t−u)} M x` for `t0 < u < t` (Driver et al., 2017, Eq. 1–2, pp. 4–5; Eq. 3 exponential map; §7.2, pp. 20–21; JSS PDF re-opened 2026-08-20T10:33Z; form `m x` first, then `e^{a(t−u)} m x`; a zero drift is `m x`; underflow of `e^{a(t−u)}` to `+0` is vanishing dissipation and is kept; `e^{A(t−u)} M x` is not the contemporaneous Dirac, not `CINT`, not `TIPREDEFFECT`, and not Voelkle Eq. 14; an impulse at `u = t` is the contemporaneous map; an impulse at `u ≤ t0` is already in `η(t0)`), recovers the exact scalar observed mean of that carry `τ + λ(μ_t + e^{a(t−u)} m x)` (Driver et al., 2017, Eq. 5 of the Eq. 1–2 carried latent mean; JSS PDF re-opened 2026-08-20T05:12Z; the evolved map `τ + λ μ_t` is not that observed mean; the contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean when `u ≠ t`; `MANIFESTMEANS` is not `E(y_t)`; the carried latent mean is not `E(y_t)`), recovers the exact scalar first-occasion `T0TIPREDEFFECT` shift `t0_b z` and its Eq. 3 first-summand carry `e^{A Δt} t0_b z` (Driver et al., 2017, Table 3, p. 13; Eq. 3, p. 5; JSS PDF opened 2026-08-20T15:14Z; form `t0_b z` first, then `e^{a Δt} t0_b z`; a zero drift is `t0_b z`; underflow of `e^{a Δt}` to `+0` is a vanishing carry and is kept; `t0_b z` is not `A^{-1}[e^{A Δt} − I] B z`, not `CINT`, and not `M x`; `e^{A Δt} t0_b z` is not `t0_b z`; `T0TIPREDEFFECT` is the coefficient, not the shift), recovers the exact scalar observed mean of that first-occasion carry `τ + λ(μ_t + e^{a Δt} t0_b z)` (Driver et al., 2017, Eq. 5 of the Table 3 / Eq. 3 first-summand composition; JSS PDF re-opened 2026-08-20T15:28Z; the evolved map `τ + λ μ_t` is not that observed mean; the process-increment map `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` is not that observed mean; the contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean; the impulse-carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t0`; `MANIFESTMEANS` is not `E(y_t)`; the evolved-plus-carry latent mean is not `E(y_t)`), recovers the exact scalar first-occasion `T0TDPREDEFFECT` shift `t0_m x0` and its Eq. 3 first-summand carry `e^{A Δt} t0_m x0` (Driver et al., 2017, Table 3, p. 13; Eq. 3, p. 5; JSS PDF re-opened 2026-08-20T19:10Z; form `t0_m x0` first, then `e^{a Δt} t0_m x0`; a zero drift is `t0_m x0`; underflow of `e^{a Δt}` to `+0` is a vanishing carry and is kept; `t0_m x0` is not `M x`, not `e^{A(t−u)} M x` for `t0 < u < t`, not `t0_b z`, not `A^{-1}[e^{A Δt} − I] B z`, and not `CINT`; `e^{A Δt} t0_m x0` is not `t0_m x0`; `T0TDPREDEFFECT` is the coefficient, not the shift; an impulse at `u ≤ t0` that used `M` is already in `η(t0)` as `TDPREDEFFECT`, not as `T0TDPREDEFFECT`), recovers the exact scalar observed mean of that first-occasion TD carry `τ + λ(μ_t + e^{a Δt} t0_m x0)` (Driver et al., 2017, Eq. 5 of the Table 3 / Eq. 3 first-summand TD composition; JSS PDF re-opened 2026-08-20T19:07Z; the evolved map `τ + λ μ_t` is not that observed mean; the process-increment map `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` is not that observed mean; the contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean; the impulse-carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t0`; the first-occasion TI map `τ + λ(μ_t + e^{a Δt} t0_b z)` is not that observed mean; `MANIFESTMEANS` is not `E(y_t)`; the evolved-plus-carry latent mean is not `E(y_t)`), recovers the exact scalar §7.2 level-change `CINT` setting `κ = −a m x` (Driver et al., 2017, §7.2, pp. 20–21; JSS PDF re-opened 2026-08-20T19:45Z; form `m x` first, then multiply by `−a`; `a < 0` so `−κ / a = m x`; `a ≥ 0` cannot hold a new process mean; `−a m x` is not the dissipating Dirac, not a free `CINT`, and not `A^{-1}[e^{A Δt} − I] B z`; the extra near-zero-drift latent process also named in §7.2 is a different specification), recovers the exact scalar Eq. 3 increment of that setting `(1 − e^{a Δt}) m x` (JSS PDF re-opened 2026-08-20T19:50Z; form the level-change `CINT` first, then the discrete intercept map; underflow of `e^{a Δt}` to `+0` keeps `m x`; `(1 − e^{a Δt}) m x` is not `m x`, not `κ`, and not `A^{-1}[e^{A Δt} − I] B z`), recovers the exact scalar §7.2 extra near-zero-drift latent process contribution `a_{ηξ} x (e^{ε Δt} − e^{a Δt}) / (ε − a)` (Driver et al., 2017, §7.2, pp. 22–23; JSS PDF re-opened 2026-08-20T23:10Z; form `a_{ηξ} x` first; `ε = a` is `a_{ηξ} x Δt e^{a Δt}`; a zero coupling or zero predictor is exactly zero; `ε ≥ 0` cannot hold a lasting extra state; that contribution is not `κ = −a m x`, not `(1 − e^{a Δt}) m x`, and not the dissipating Dirac `m x`), recovers the exact scalar observed mean of that extra-process contribution `τ + λ(μ_t + a_{ηξ} x (e^{ε Δt} − e^{a Δt}) / (ε − a))` (Driver et al., 2017, Eq. 5, p. 5; §7.2, pp. 22–23; JSS PDF re-opened 2026-08-21T06:12Z; the extra process has `LAMBDA` 0 and is not an observed indicator; form the evolved-plus-contribution latent mean first, then `τ + λ` of that mean; the evolved map `τ + λ μ_t` is not that observed mean; the contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean; the contribution is not `E(y_t)`; the evolved-plus-contribution latent mean is not `E(y_t)`), recovers the exact scalar after-t0 extra-process contribution `a_{ηξ} x (e^{ε(t−u)} − e^{a(t−u)}) / (ε − a)` for `t0 < u < t` and its Eq. 5 observed mean `τ + λ(μ_t + a_{ηξ} x (e^{ε(t−u)} − e^{a(t−u)}) / (ε − a))` (Driver et al., 2017, §7.2, pp. 22–23; JSS PDF re-opened 2026-08-21T06:32Z; `T0TDPREDEFFECT` uses `Δt` for both the evolution and the extra drive; `TDPREDEFFECT` after `t0` uses `t − u` while `μ_t` still uses `Δt`; an impulse at `u = t0` or `u = t` is not interior; `e^{a(t−u)} m x` is a Dirac on the original process, not this `DRIFT` drive), recovers the exact scalar §7.2 `asymTIPREDEFFECT` `-B z / a` (Driver et al., 2017, §7.2, pp. 20–21; JSS PDF opened 2026-08-21T13:08Z; form `B z` first, then divide by `-a`; `a < 0`; a zero coefficient or zero predictor is exactly zero; `a ≥ 0` cannot hold a finite process-mean change; `-B z / a` is not the coefficient `B`, not `A^{-1}[e^{A Δt} − I] B z`, not `CINT`, and not `M x`), recovers the exact scalar §7.2 `addedTIPREDVAR` `(B / a)² v` (Driver et al., 2017, §7.2, pp. 20–21; form the unit asymptotic effect first, then square, then multiply by `v`; `v ≥ 0`; a zero coefficient or zero predictor variance is exactly zero; `(B / a)² v` is not `TRAITVAR`, not `asymDIFFUSION`, and not `-B z / a`), recovers the exact scalar Table 2 `asymCINT` `-κ / a` (Driver et al., 2017, Table 2, p. 12; Eq. 3 as `Δt → ∞`; JSS PDF opened 2026-08-21T16:13Z; form `κ` first, then divide by `-a`; `a < 0`; a zero intercept is exactly zero; `-κ / a` is not `κ`, not `A^{-1}[e^{A Δt} − I] κ`, not `T0MEANS`, and not `-B z / a`), recovers the exact scalar p. 16 stationary `T0MEANS` `-κ / a + −B z / a` (Driver et al., 2017, p. 16; form the intercept contribution first, then include the TI extra effect, then add; not free `T0MEANS`, not `asymCINT` alone, not `asymTIPREDEFFECT` alone, and not the finite-interval discrete latent mean), recovers the exact scalar Eq. 5 of that constrained mean `τ + λ(−κ / a + −B z / a)` (Driver et al., 2017, §4.3, pp. 9–10; Eq. 5, p. 5; JSS PDF re-opened 2026-08-21T20:07Z; form the stationary latent mean first, then `τ + λ` of that mean; `τ + λ μ_0` is not that observed mean; `τ + λ(−κ / a)` is not that observed mean when `B z ≠ 0`; `τ + λ μ_t` is not that observed mean; `MANIFESTMEANS` is not `E(y_0)`; the constrained latent mean is not `E(y_0)`), recovers the exact scalar §4.3 / p. 16 stationary `T0VAR` `trait + −q / (2 a) + (B / a)² v` (Driver et al., 2017, §4.3, pp. 9–10; p. 16; JSS PDF re-opened 2026-08-22T03:07Z; form the within-subject contribution first, then include the trait, then include the TI extra variance, then add; not free `T0VAR`, not `asymDIFFUSION` alone, not `TRAITVAR` alone, not `addedTIPREDVAR` alone, and not the finite-interval discrete latent variance), recovers the exact scalar Eq. 5 of that constrained variance `λ²(trait + −q / (2 a) + (B / a)² v) + θ + ψ` (Driver et al., 2017, §4.3, pp. 9–10; Eq. 5, p. 5; Table 2, p. 12; JSS PDF re-opened 2026-08-22T03:20Z; form the stationary latent variance first, then `λ² p + θ + ψ`; `λ² p_0` is not that observed variance; `λ²(−q / (2 a)) + θ` is not that observed variance when `TRAITVAR` or `addedTIPREDVAR` is nonzero; `MANIFESTVAR` is not `Var(y_0)`; the constrained latent variance is not `Var(y_0)`), recovers the exact scalar lagged covariance of that constrained process `trait + e^{a Δt}(−q / (2 a)) + (B / a)² v` (Driver et al., 2017, Eq. 3–4 of §4.3 / p. 16 `T0VAR`; JSS PDF re-opened 2026-08-22T19:13Z; trait and `addedTIPREDVAR` do not decay with `e^{a Δt}`; contemporaneous `T0VAR` is not that lagged map; decaying the constrained total as if it were all state is not that lagged map), recovers the exact scalar Eq. 5 of that lagged covariance `λ²(trait + e^{a Δt}(−q / (2 a)) + (B / a)² v) + ψ` (`Θ` does not enter; contemporaneous `Var(y_0)` is not that lagged observed covariance; the lagged latent covariance is not that observed covariance), recovers the exact scalar later-occasion variance of that constrained process `trait + e^{2 a Δt}(−q / (2 a)) + Q_Δt + (B / a)² v` (Driver et al., 2017, Eq. 3–4 of §4.3 / p. 16 `T0VAR`; JSS PDF re-opened 2026-08-22T23:12Z; form the evolved within-subject variance first, then include the trait, then include the TI extra variance, then add; trait and `addedTIPREDVAR` do not enter `Q_Δt`; under stationarity that composition equals contemporaneous `T0VAR`; evolving the constrained total as if it were all state is not that later map; the lagged covariance omits `Q_Δt` and is not that later map; `Q_Δt` is not that later map), recovers the exact scalar Eq. 5 of that later-occasion variance `λ²(trait + e^{2 a Δt}(−q / (2 a)) + Q_Δt + (B / a)² v) + θ + ψ` (the lagged observed covariance omits `Q_Δt` and `θ`; `MANIFESTVAR` is not that later observed variance; the later-occasion latent variance is not that observed variance), and refuses the difference quotient, pooling discrete lags from unequal intervals, and a binary64 underflow of that exponential to `+0` (not a discrete lag). The first-order product `a_yx Δt` is Eq. 14 and the underflow limit of Eq. 12, not the general constant-predictor discrete effect. Already-centered residuals may have irregular event intervals. Subtracting the person-specific mean from a raw autoregressive series is not the lagged within-person residual (Curran & Bauer, 2011, pp. 607–608). The public CWC pipeline emits `LaggedWithinResidual` pairs; the pairwise-mean log-rate after that CWC uses `ln(|later| / |earlier|) / Δt` when that ratio is finite and `(ln|later| − ln|earlier|) / Δt` when it overflows or underflows, forms the pairwise mean incrementally so two finite rates whose raw sum overflows stay representable, is not the Newton least-squares log-rate, and is not raw-process drift. Metric/weak invariance licenses shared metric meaning only. Latent-mean comparison requires strong (equal loading and intercept) or strict invariance (Putnick & Bornstein, 2016, PMC5145197 opened 2026-08-19T22:15Z: scalar is required for latent means; residual invariance is not). Two-observation series have no residual degrees of freedom and cap at strong/scalar; identically-zero OLS residual variance is not strict. This two-group OLS gate is not MGCFA. - +The executable multilevel slice is cluster-mean centering (CWC) plus within/between OLS, the CWC contextual effect (`between − within`), and Kish-weighted slopes. Enders and Tofighi (2007, Table 2, pp. 124–127) show that the CWC cluster-mean coefficient is the contextual effect, not the between-cluster effect. It is not DSEM and not RI-CLPM. Grand-mean-centered event-time lag is a distinct public estimand from CWC: lagged relations from grand-mean deviations confound stable between-person differences with within-person change (Hamaker, Kuiper, & Grasman, 2015, p. 104; UvA PDF re-opened 2026-08-30T23:35Z) and are refused as a within-person lag. The pairs are `LaggedGrandMeanResidual`, not `LaggedWithinResidual`, so they cannot enter the already-centered within-person recovery path. When the raw score sum is finite the grand mean is that sum divided by n; when the raw sum overflows, positives and negatives are averaged separately with the overflow-safe incremental mean and combined by count. That helper is not RI-CLPM. The executable temporal slice maps a discrete lag through the exact scalar exponential `a = ln(φ) / Δt` on event time only (Voelkle, Oud, Davidov, & Schmidt, 2012, Eq. 7; Driver, Oud, & Voelkle, 2017, Eq. 3), recovers the forward map `φ(Δt) = exp(a Δt)`, remaps a discrete lag onto another event interval through that log-rate, recovers the exact scalar discrete effect of a constant predictor (Voelkle et al., 2012, Eq. 12) as `a_yx (expm1(z) / a_xx)` with `z = a_xx Δt` so a finite result is not lost when `z` overflows to `-∞` or when `a_yx Δt` overflows, and in log space when `expm1(z)` overflows at a finite `z` (`a_yx = 0` is exactly zero; an overflowing `a_yx/a_xx` rewrite term fails closed), recovers the first-order discrete effect of a time-varying predictor with matched sampling and constancy intervals as `a_yx Δt` (Voelkle et al., 2012, Eq. 14; ZORA accepted manuscript p. 21; not Eq. 12; unmatched intervals fail closed because Oud & Jansen, 2000, is unread), recovers the exact scalar discrete process noise `Q_Δt = 0.5 q (expm1(z) / a)` with `z = 2 (a Δt)` and `q = G G⊤ ≥ 0` (Driver et al., 2017, Eq. 3; JSS PDF re-opened 2026-08-18T07:06Z, p. 4; scalar `L = 1`; do not form `2 a` first; `a = 0` and `z → 0` recover `q Δt`; `z → −∞` keeps `−0.5 q / a`; a zero diffusion is exactly zero; `z → +∞` and an overflowing rewrite scale `0.5 q / a` fail closed), recovers the exact scalar lagged latent covariance `exp(a Δt) p` and the law-of-total-variance map `Var(η_t) = exp(2 a Δt) p + Q_Δt` (Driver et al., 2017, Eq. 3–4, pp. 4–5; JSS PDF re-opened 2026-08-18T18:03Z; JSS has no numbered §2.2; `Q_Δt` is `cov(η_t | η_{t-1})` and is refused as the unconditional variance; a zero diffusion whose `2 (a Δt)` overflows to `+∞` fails closed), recovers the exact scalar stationary within-subject variance `-q / (2 a)` as the `Δt → ∞` limit of Eq. 4 for stable `a < 0` (JSS p. 16 `asymDIFFUSION`; §4.3 T0VAR stationarity; when `2 a` is finite, form `q / -(2 a)` so `q / a` overflow does not lose a finite result (`q = MAX`, `a = -0.75` → `MAX / 1.5`; CodeRabbit on `75ecdd3`); when `2 a` overflows, form `(q / a) * -0.5`; do not form `0.5 q` first (`q = from_bits(1)`, `a = -from_bits(1)` → `0.5`); `a ≥ 0` and finite-interval `Q_Δt` fail closed as that limit), recovers the exact scalar trait-plus-state variance `trait + state` and lagged covariance `trait + exp(a Δt) p` (Driver et al., 2017, §4.3, p. 9; JSS PDF re-opened 2026-08-18T21:07Z; a stable trait has `DRIFT` and `DIFFUSION` fixed to zero; `TRAITVAR` is not process noise and not `asymDIFFUSION`; evolving the summed variance as if it were all state fails the claim boundary; this is not RI-CLPM), recovers the exact scalar observed-indicator variance `λ² Var(η) + θ` when `MANIFESTTRAITVAR` is zero and `λ² Var(η) + θ + ψ` otherwise, and the lagged observed covariance `λ² cov(η_t, η_{t-1}) + ψ` (Driver et al., 2017, Eq. 5, p. 5; Table 2, p. 12; JSS PDF re-opened 2026-08-19T04:18Z; Equation 1 is the latent SDE; form `(λ p) λ` then add `θ`, then add `ψ`; do not form `λ²` first; `MANIFESTVAR` is `Θ`, not `Var(y)` and does not enter lagged observed covariance; `MANIFESTTRAITVAR` is `Ψ_τ`, not `Θ`; `TRAITVAR` is latent and scaled by `λ²`; `Var(η)` is not `Var(y)`), recovers the exact scalar observed-indicator mean `τ + λ μ` (Driver et al., 2017, Eq. 5, p. 5; Table 2, p. 12; JSS PDF re-opened 2026-08-19T14:08Z; `MANIFESTMEANS` is `τ`, not `E(y)`; `CINT` is not `MANIFESTMEANS`; `T0MEANS` is not `E(y)`), recovers the exact scalar discrete latent mean `exp(a Δt) μ_0 + (exp(a Δt) − 1)/a κ` (Driver et al., 2017, Eq. 3, p. 4; Table 2, p. 12; JSS PDF re-opened 2026-08-19T18:10Z; `T0MEANS` is not `μ_t`; `CINT` is not the discrete increment; a zero drift is `κ Δt`; underflow of `exp(a Δt)` to `+0` drops the carried `T0MEANS` and keeps `−κ / a`), recovers the exact scalar evolved observed-indicator mean `τ + λ μ_t` (Driver et al., 2017, Eq. 5 of the Eq. 3 expected-value map; JSS PDF re-opened 2026-08-19T22:10Z; the first-occasion map `τ + λ μ_0` is not `E(y_t)`; `MANIFESTMEANS` is not `E(y_t)`; `μ_t` is not `E(y_t)`), recovers the exact scalar contemporaneous time-dependent predictor impulse `m x` (Driver et al., 2017, Eq. 1–3, pp. 4–5; Table 2, p. 12; §7.2, pp. 20–21; JSS PDF re-opened 2026-08-20T07:10Z; `TDPREDEFFECT` is `M`, not `CINT`; `M x` is not `A^{-1}[e^{A Δt} − I] B z`; `M x` is not Voelkle et al., 2012, Eq. 14; the level-change form is not that impulse), recovers the exact scalar observed mean of that contemporaneous impulse `τ + λ(μ_t + m x)` (Driver et al., 2017, Eq. 5 of the Eq. 3 fourth-summand composition; JSS PDF re-opened 2026-08-20T09:01Z; the evolved map `τ + λ μ_t` is not that observed mean; the carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t`; `MANIFESTMEANS` is not `E(y_t)`; the evolved-plus-impulse latent mean is not `E(y_t)`), recovers the exact scalar time-independent predictor increment `A^{-1}[e^{A Δt} − I] B z` (Driver et al., 2017, Eq. 1–3, pp. 4–5; Table 2, p. 12; JSS PDF re-opened 2026-08-20T10:13Z; `TIPREDEFFECT` is `B`, not `κ`; form `B z` first, then the discrete intercept map; a zero drift is `B z Δt`; `B` is not the discrete increment; `A^{-1}[e^{A Δt} − I] B z` is not `CINT`, not `M x`, and not Voelkle Eq. 14), recovers the exact scalar observed mean of that increment `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` (Driver et al., 2017, Eq. 5 of the Eq. 3 printed addend after the `T0MEANS` carry and the `CINT` increment; JSS PDF re-opened 2026-08-20T12:12Z; the evolved map `τ + λ μ_t` is not that observed mean; the contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean; the carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t`; `MANIFESTMEANS` is not `E(y_t)`; the evolved-plus-increment latent mean is not `E(y_t)`), recovers the exact scalar within-interval time-dependent impulse carry `e^{A(t−u)} M x` for `t0 < u < t` (Driver et al., 2017, Eq. 1–2, pp. 4–5; Eq. 3 exponential map; §7.2, pp. 20–21; JSS PDF re-opened 2026-08-20T10:33Z; form `m x` first, then `e^{a(t−u)} m x`; a zero drift is `m x`; underflow of `e^{a(t−u)}` to `+0` is vanishing dissipation and is kept; `e^{A(t−u)} M x` is not the contemporaneous Dirac, not `CINT`, not `TIPREDEFFECT`, and not Voelkle Eq. 14; an impulse at `u = t` is the contemporaneous map; an impulse at `u ≤ t0` is already in `η(t0)`), recovers the exact scalar observed mean of that carry `τ + λ(μ_t + e^{a(t−u)} m x)` (Driver et al., 2017, Eq. 5 of the Eq. 1–2 carried latent mean; JSS PDF re-opened 2026-08-20T05:12Z; the evolved map `τ + λ μ_t` is not that observed mean; the contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean when `u ≠ t`; `MANIFESTMEANS` is not `E(y_t)`; the carried latent mean is not `E(y_t)`), recovers the exact scalar first-occasion `T0TIPREDEFFECT` shift `t0_b z` and its Eq. 3 first-summand carry `e^{A Δt} t0_b z` (Driver et al., 2017, Table 3, p. 13; Eq. 3, p. 5; JSS PDF opened 2026-08-20T15:14Z; form `t0_b z` first, then `e^{a Δt} t0_b z`; a zero drift is `t0_b z`; underflow of `e^{a Δt}` to `+0` is a vanishing carry and is kept; `t0_b z` is not `A^{-1}[e^{A Δt} − I] B z`, not `CINT`, and not `M x`; `e^{A Δt} t0_b z` is not `t0_b z`; `T0TIPREDEFFECT` is the coefficient, not the shift), recovers the exact scalar observed mean of that first-occasion carry `τ + λ(μ_t + e^{a Δt} t0_b z)` (Driver et al., 2017, Eq. 5 of the Table 3 / Eq. 3 first-summand composition; JSS PDF re-opened 2026-08-20T15:28Z; the evolved map `τ + λ μ_t` is not that observed mean; the process-increment map `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` is not that observed mean; the contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean; the impulse-carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t0`; `MANIFESTMEANS` is not `E(y_t)`; the evolved-plus-carry latent mean is not `E(y_t)`), recovers the exact scalar first-occasion `T0TDPREDEFFECT` shift `t0_m x0` and its Eq. 3 first-summand carry `e^{A Δt} t0_m x0` (Driver et al., 2017, Table 3, p. 13; Eq. 3, p. 5; JSS PDF re-opened 2026-08-20T19:10Z; form `t0_m x0` first, then `e^{a Δt} t0_m x0`; a zero drift is `t0_m x0`; underflow of `e^{a Δt}` to `+0` is a vanishing carry and is kept; `t0_m x0` is not `M x`, not `e^{A(t−u)} M x` for `t0 < u < t`, not `t0_b z`, not `A^{-1}[e^{A Δt} − I] B z`, and not `CINT`; `e^{A Δt} t0_m x0` is not `t0_m x0`; `T0TDPREDEFFECT` is the coefficient, not the shift; an impulse at `u ≤ t0` that used `M` is already in `η(t0)` as `TDPREDEFFECT`, not as `T0TDPREDEFFECT`), recovers the exact scalar observed mean of that first-occasion TD carry `τ + λ(μ_t + e^{a Δt} t0_m x0)` (Driver et al., 2017, Eq. 5 of the Table 3 / Eq. 3 first-summand TD composition; JSS PDF re-opened 2026-08-20T19:07Z; the evolved map `τ + λ μ_t` is not that observed mean; the process-increment map `τ + λ(μ_t + A^{-1}[e^{A Δt} − I] B z)` is not that observed mean; the contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean; the impulse-carry map `τ + λ(μ_t + e^{a(t−u)} m x)` is not that observed mean when `u ≠ t0`; the first-occasion TI map `τ + λ(μ_t + e^{a Δt} t0_b z)` is not that observed mean; `MANIFESTMEANS` is not `E(y_t)`; the evolved-plus-carry latent mean is not `E(y_t)`), recovers the exact scalar §7.2 level-change `CINT` setting `κ = −a m x` (Driver et al., 2017, §7.2, pp. 20–21; JSS PDF re-opened 2026-08-20T19:45Z; form `m x` first, then multiply by `−a`; `a < 0` so `−κ / a = m x`; `a ≥ 0` cannot hold a new process mean; `−a m x` is not the dissipating Dirac, not a free `CINT`, and not `A^{-1}[e^{A Δt} − I] B z`; the extra near-zero-drift latent process also named in §7.2 is a different specification), recovers the exact scalar Eq. 3 increment of that setting `(1 − e^{a Δt}) m x` (JSS PDF re-opened 2026-08-20T19:50Z; form the level-change `CINT` first, then the discrete intercept map; underflow of `e^{a Δt}` to `+0` keeps `m x`; `(1 − e^{a Δt}) m x` is not `m x`, not `κ`, and not `A^{-1}[e^{A Δt} − I] B z`), recovers the exact scalar §7.2 extra near-zero-drift latent process contribution `a_{ηξ} x (e^{ε Δt} − e^{a Δt}) / (ε − a)` (Driver et al., 2017, §7.2, pp. 22–23; JSS PDF re-opened 2026-08-20T23:10Z; form `a_{ηξ} x` first; `ε = a` is `a_{ηξ} x Δt e^{a Δt}`; a zero coupling or zero predictor is exactly zero; `ε ≥ 0` cannot hold a lasting extra state; that contribution is not `κ = −a m x`, not `(1 − e^{a Δt}) m x`, and not the dissipating Dirac `m x`), recovers the exact scalar observed mean of that extra-process contribution `τ + λ(μ_t + a_{ηξ} x (e^{ε Δt} − e^{a Δt}) / (ε − a))` (Driver et al., 2017, Eq. 5, p. 5; §7.2, pp. 22–23; JSS PDF re-opened 2026-08-21T06:12Z; the extra process has `LAMBDA` 0 and is not an observed indicator; form the evolved-plus-contribution latent mean first, then `τ + λ` of that mean; the evolved map `τ + λ μ_t` is not that observed mean; the contemporaneous map `τ + λ(μ_t + m x)` is not that observed mean; the contribution is not `E(y_t)`; the evolved-plus-contribution latent mean is not `E(y_t)`), recovers the exact scalar after-t0 extra-process contribution `a_{ηξ} x (e^{ε(t−u)} − e^{a(t−u)}) / (ε − a)` for `t0 < u < t` and its Eq. 5 observed mean `τ + λ(μ_t + a_{ηξ} x (e^{ε(t−u)} − e^{a(t−u)}) / (ε − a))` (Driver et al., 2017, §7.2, pp. 22–23; JSS PDF re-opened 2026-08-21T06:32Z; `T0TDPREDEFFECT` uses `Δt` for both the evolution and the extra drive; `TDPREDEFFECT` after `t0` uses `t − u` while `μ_t` still uses `Δt`; an impulse at `u = t0` or `u = t` is not interior; `e^{a(t−u)} m x` is a Dirac on the original process, not this `DRIFT` drive), recovers the exact scalar §7.2 `asymTIPREDEFFECT` `-B z / a` (Driver et al., 2017, §7.2, pp. 20–21; JSS PDF opened 2026-08-21T13:08Z; form `B z` first, then divide by `-a`; `a < 0`; a zero coefficient or zero predictor is exactly zero; `a ≥ 0` cannot hold a finite process-mean change; `-B z / a` is not the coefficient `B`, not `A^{-1}[e^{A Δt} − I] B z`, not `CINT`, and not `M x`), recovers the exact scalar §7.2 `addedTIPREDVAR` `(B / a)² v` (Driver et al., 2017, §7.2, pp. 20–21; form the unit asymptotic effect first, then square, then multiply by `v`; `v ≥ 0`; a zero coefficient or zero predictor variance is exactly zero; `(B / a)² v` is not `TRAITVAR`, not `asymDIFFUSION`, and not `-B z / a`), recovers the exact scalar Table 2 `asymCINT` `-κ / a` (Driver et al., 2017, Table 2, p. 12; Eq. 3 as `Δt → ∞`; JSS PDF opened 2026-08-21T16:13Z; form `κ` first, then divide by `-a`; `a < 0`; a zero intercept is exactly zero; `-κ / a` is not `κ`, not `A^{-1}[e^{A Δt} − I] κ`, not `T0MEANS`, and not `-B z / a`), recovers the exact scalar p. 16 stationary `T0MEANS` `-κ / a + −B z / a` (Driver et al., 2017, p. 16; form the intercept contribution first, then include the TI extra effect, then add; not free `T0MEANS`, not `asymCINT` alone, not `asymTIPREDEFFECT` alone, and not the finite-interval discrete latent mean), recovers the exact scalar Eq. 5 of that constrained mean `τ + λ(−κ / a + −B z / a)` (Driver et al., 2017, §4.3, pp. 9–10; Eq. 5, p. 5; JSS PDF re-opened 2026-08-21T20:07Z; form the stationary latent mean first, then `τ + λ` of that mean; `τ + λ μ_0` is not that observed mean; `τ + λ(−κ / a)` is not that observed mean when `B z ≠ 0`; `τ + λ μ_t` is not that observed mean; `MANIFESTMEANS` is not `E(y_0)`; the constrained latent mean is not `E(y_0)`), recovers the exact scalar §4.3 / p. 16 stationary `T0VAR` `trait + −q / (2 a) + (B / a)² v` (Driver et al., 2017, §4.3, pp. 9–10; p. 16; JSS PDF re-opened 2026-08-22T03:07Z; form the within-subject contribution first, then include the trait, then include the TI extra variance, then add; not free `T0VAR`, not `asymDIFFUSION` alone, not `TRAITVAR` alone, not `addedTIPREDVAR` alone, and not the finite-interval discrete latent variance), recovers the exact scalar Eq. 5 of that constrained variance `λ²(trait + −q / (2 a) + (B / a)² v) + θ + ψ` (Driver et al., 2017, §4.3, pp. 9–10; Eq. 5, p. 5; Table 2, p. 12; JSS PDF re-opened 2026-08-22T03:20Z; form the stationary latent variance first, then `λ² p + θ + ψ`; `λ² p_0` is not that observed variance; `λ²(−q / (2 a)) + θ` is not that observed variance when `TRAITVAR` or `addedTIPREDVAR` is nonzero; `MANIFESTVAR` is not `Var(y_0)`; the constrained latent variance is not `Var(y_0)`), recovers the exact scalar lagged covariance of that constrained process `trait + e^{a Δt}(−q / (2 a)) + (B / a)² v` (Driver et al., 2017, Eq. 3–4 of §4.3 / p. 16 `T0VAR`; JSS PDF re-opened 2026-08-22T19:13Z; trait and `addedTIPREDVAR` do not decay with `e^{a Δt}`; contemporaneous `T0VAR` is not that lagged map; decaying the constrained total as if it were all state is not that lagged map), recovers the exact scalar Eq. 5 of that lagged covariance `λ²(trait + e^{a Δt}(−q / (2 a)) + (B / a)² v) + ψ` (`Θ` does not enter; contemporaneous `Var(y_0)` is not that lagged observed covariance; the lagged latent covariance is not that observed covariance), recovers the exact scalar later-occasion variance of that constrained process `trait + e^{2 a Δt}(−q / (2 a)) + Q_Δt + (B / a)² v` (Driver et al., 2017, Eq. 3–4 of §4.3 / p. 16 `T0VAR`; JSS PDF re-opened 2026-08-22T23:12Z; form the evolved within-subject variance first, then include the trait, then include the TI extra variance, then add; trait and `addedTIPREDVAR` do not enter `Q_Δt`; under stationarity that composition equals contemporaneous `T0VAR`; evolving the constrained total as if it were all state is not that later map; the lagged covariance omits `Q_Δt` and is not that later map; `Q_Δt` is not that later map), recovers the exact scalar Eq. 5 of that later-occasion variance `λ²(trait + e^{2 a Δt}(−q / (2 a)) + Q_Δt + (B / a)² v) + θ + ψ` (the lagged observed covariance omits `Q_Δt` and `θ`; `MANIFESTVAR` is not that later observed variance; the later-occasion latent variance is not that observed variance), and refuses the difference quotient, pooling discrete lags from unequal intervals, and a binary64 underflow of that exponential to `+0` (not a discrete lag). The first-order product `a_yx Δt` is Eq. 14 and the underflow limit of Eq. 12, not the general constant-predictor discrete effect. Already-centered residuals may have irregular event intervals. Subtracting the person-specific mean from a raw autoregressive series is not the lagged within-person residual (Curran & Bauer, 2011, pp. 607–608). The public CWC pipeline emits `LaggedWithinResidual` pairs; the pairwise-mean log-rate after that CWC uses `ln(|later| / |earlier|) / Δt` when that ratio is finite and `(ln|later| − ln|earlier|) / Δt` when it overflows or underflows, forms the pairwise mean incrementally so two finite rates whose raw sum overflows stay representable, is not the Newton least-squares log-rate, and is not raw-process drift. Metric/weak invariance licenses shared metric meaning only. Latent-mean comparison requires strong (equal loading and intercept) or strict invariance (Putnick & Bornstein, 2016, PMC5145197 opened 2026-08-19T22:15Z: scalar is required for latent means; residual invariance is not). Two-observation series have no residual degrees of freedom and cap at strong/scalar; identically-zero OLS residual variance is not strict. This two-group OLS gate is not MGCFA. The executable standardised-intercept slice recovers Driver et al. (2017, p. 16 `CINTstd`) as `κ / √p` after strictly positive `asymDIFFUSION` `p = −q / (2 a)` (footnote 4; JSS PDF re-opened 2026-08-25T11:43Z). Unstandardised `κ` is defined for growing `a ≥ 0` and for zero diffusion and is not that map. `(-κ / a) / √p` is `asymCINTstd` and is not `CINTstd`. `A^{-1}[e^{A Δt} − I] κ / √p` is `discreteCINTstd` and is not `CINTstd`. `κ / √(trait + p + added)` uses total variance and is not the residual map. This is not ctsem estimation. The executable standardised-measurement slice recovers Driver et al. (2017, p. 16 `MANIFESTMEANSstd`) as `τ / √θ` after strictly positive residual `MANIFESTVAR` (footnote 4; JSS PDF re-opened 2026-08-25T11:32Z). Unstandardised `τ` is defined for a zero residual and is not that map. `θ / θ = 1` is the named `MANIFESTVARstd` correlation form and is not `MANIFESTMEANSstd` even when `τ = √θ`. `τ / √(λ² Var(η) + θ)` uses total observed variance and is not the residual map. This is not ctsem estimation. The executable standardised-asymptotic-intercept slice recovers Driver et al. (2017, p. 16 `asymCINTstd`) as `(-κ / a) / √p` after strictly positive `asymDIFFUSION` `p = −q / (2 a)` (footnote 4; Eq. 3; Table 2; 2017-era `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-26T00:20Z). Unstandardised `-κ / a` is defined for a zero process and is not that map. `κ / √p` is `CINTstd` and is not this total-change map. `A^{-1}[e^{A Δt} − I] κ / √p` is `discreteCINTstd` and is not this `Δt → ∞` map. This is not ctsem estimation. diff --git a/docs/research/multilevel-event-time-recovery.md b/docs/research/multilevel-event-time-recovery.md index d36664b71..aa466894e 100644 --- a/docs/research/multilevel-event-time-recovery.md +++ b/docs/research/multilevel-event-time-recovery.md @@ -110,7 +110,9 @@ This slice stays inside `psychometric_core`. It does not add a second invariance 104. apply the same event-time map to CWC residuals (still not DSEM); 105. map already-centered lagged residuals with irregular event intervals without re-centering (Curran & Bauer, 2011, pp. 607–608); 106. emit public CWC `LaggedWithinResidual` pairs and recover the pairwise-mean exact log-rate after that CWC on nonzero same-sign pairs using `ln(|later| / |earlier|) / Δt` when that ratio is finite and `(ln|later| − ln|earlier|) / Δt` when it overflows or underflows, forming the pairwise mean incrementally so two finite rates whose raw sum overflows stay representable (Voelkle et al., 2012, Eq. 7; not the Newton least-squares CWC log-rate; the already-centered helper still fails closed on a non-finite `later / earlier`; still not DSEM); -107. refuse treating a CWC residual log-rate as the raw-process autoregressive drift (Curran & Bauer, 2011, pp. 607–608). +107. refuse treating a CWC residual log-rate as the raw-process autoregressive drift (Curran & Bauer, 2011, pp. 607–608); +108. emit public grand-mean-centered `LaggedGrandMeanResidual` pairs (not `LaggedWithinResidual`) and recover the pairwise-mean exact log-rate after that CGM on nonzero same-sign pairs using `ln(|later| / |earlier|) / Δt` when that ratio is finite and `(ln|later| − ln|earlier|) / Δt` when it overflows or underflows, forming the pairwise mean incrementally from a grand mean that uses `sum/n` when the raw sum is finite and a sign-split overflow-safe mean when it is not (Voelkle et al., 2012, Eq. 7; Hamaker, Kuiper, & Grasman, 2015, p. 104: not a within-person lag; not CWC; not RI-CLPM; not DSEM); +109. refuse treating a grand-mean-centered residual log-rate as the within-person lagged effect (Hamaker et al., 2015, p. 104). ## Claim boundary @@ -184,6 +186,7 @@ The Voelkle et al. (2012) ZORA accepted manuscript was re-opened 2026-08-18T21:0 - **CWC-then-lag.** Sample cluster means are removed first. Consecutive within residuals are then fitted by least squares to \(r_{t+\Delta t}\approx\exp(a\Delta t)\,r_{t}\) on event time. Same-sign pair-wise logs initialize the scalar Newton step. Sign-flipping \(T=2\) CWC pairs have no real logarithm and fail closed. Curran and Bauer (2011, pp. 607–608) show that this person-mean subtraction on a raw autoregressive series does **not** isolate the lagged within-person effect; the helper therefore does not claim to recover the raw-process drift. - **Public CWC lag pairs.** `center_within_cluster_event_lags` returns those consecutive CWC residuals as `LaggedWithinResidual` values. Singleton clusters are skipped. This is not DSEM. - **CWC-then-pairwise-mean irregular residual.** The pairwise mean of \(a=\ln(|r_{t+\Delta t}|/|r_t|)/\Delta t\) after that CWC is a distinct estimand from the Newton least-squares CWC log-rate. Same-sign admissibility does not divide. When the residual ratio is finite and positive the rate uses that ratio's logarithm so near-equal large residuals do not collapse to zero. When the ratio overflows or underflows the rate is \((\ln|r_{t+\Delta t}|-\ln|r_t|)/\Delta t\). The pairwise mean is formed incrementally as \(\mathrm{mean}\cdot(n-1)/n+\mathrm{rate}/n\) so two finite rates whose raw sum overflows still keep a representable mean; mixed signs scale each term before adding. CWC residuals of a connected series sum to zero; consecutive pairs need not all have opposite signs. Finite strictly positive residual-ratio pairs are retained; \(T=2\) CWC is always \(r,-r\) and yields an empty admissible set. The already-centered helper still uses \(r_{t+\Delta t}/r_t\) and fails closed on a non-finite ratio. It is not raw-process drift. +- **Grand-mean-centered event-time lag.** `center_grand_mean_event_lags` subtracts the sample grand mean (`sum/n` when that sum is finite; otherwise positives and negatives averaged separately with the overflow-safe incremental mean and combined by count), then emits consecutive residuals as `LaggedGrandMeanResidual` pairs. That type is not `LaggedWithinResidual` and cannot enter `recover_irregular_centered_residual_log_rate`. Hamaker, Kuiper, and Grasman (2015, p. 104; UvA PDF re-opened 2026-08-30T23:35Z from https://pure.uva.nl/ws/files/2688454/168970_Hamaker_Kuiper_Grasman_2015_A_Critique_of_Cross_Lagged_Panel_Model.pdf, journal pp. 102–105) write that CLPM lagged relations from deviations around shared (grand) means "only represent actual within-person relationships if there are no trait-like, time-invariant, between-person differences." \(T=2\) CGM can keep same-sign pairs (unlike \(T=2\) CWC). When cluster means coincide, CGM equals CWC numerically and remains a distinct type. The pairwise-mean log-rate after CGM is not a within-person lag, not CWC, not RI-CLPM, and not DSEM. Treating it as a within-person lag fails closed. An all-`MAX` series has a finite grand mean of `MAX` and zero residuals. Cancelling `±MAX` scores keep a representable zero grand mean in either row order. - **Already-centered irregular residual.** The caller supplies lagged within residuals. The mean of \(a=\ln(r_{t+\Delta t}/r_t)/\Delta t\) is the exact scalar map. Intervals may be irregular. The helper does not center again. This is not DSEM. - **Standardised initial latent mean.** Driver et al. (2017, Table 2, p. 12; p. 16; footnote 4; 2017-era ctsem `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-26T04:09Z): Table 2 names `T0MEANS` the latent process means at the first time point `T0`. Footnote 4 standardises using only the relevant variance, not the total. The first-occasion relevant variance is free `T0VAR` `p_0`, not `asymDIFFUSION`. The 2017-era source forms unstandardised `T0MEANS` and does not form `T0MEANSstd`. The scalar map is `μ_0/√p_0`. Form strictly positive `p_0` first, then divide. A zero mean is exactly zero. Zero `p_0` has no positive SD and fails closed. `T0` is an event-time occasion. Free `T0MEANS` does not require `a<0`. `T0VARstd` is not this map even when both equal 1. `μ_0/√asymDIFFUSION` is not this map. An overflowing quotient fails closed. This is not a Kalman filter and not ctsem estimation. - **Standardised initial latent variance.** Driver et al. (2017, Table 2, p. 12; p. 16; footnote 4; 2017-era ctsem `summary.ctsemFit.R`; JSS PDF re-opened 2026-08-26T07:17Z): Table 2 names `T0VAR` the latent process initial variance/covariance. Footnote 4 standardises using only the relevant variance, not the total. The first-occasion relevant variance is free `T0VAR` `p_0`, not `asymDIFFUSION`. The 2017-era source forms `T0VARstd` as `solve(sqrt(diag(T0VAR))) %&% T0VAR`. OpenMx `%&%` is `t(A) %*% B %*% A`. The default ridge is 0. The scalar correlation is `p_0/p_0=1`. Form strictly positive `p_0` first, then `1/√p_0`, then `(1/√p_0) p_0 (1/√p_0)`. Zero `p_0` has no positive SD and fails closed. `T0` is an event-time occasion. Free `T0VAR` does not require `a<0`. Distinct positive `p_0` recover the same 1. `T0MEANSstd` is not this map even when both equal 1. `asymDIFFUSIONstd` is not this map even when both equal 1. An overflowing quadratic form fails closed. This is not a Kalman filter and not ctsem estimation. @@ -243,6 +246,7 @@ The Voelkle et al. (2012) ZORA accepted manuscript was re-opened 2026-08-18T21:0 - CWC-then-lag on a two-cluster decaying series has smaller computed RMSE than a level-pooled series when the latter is identified; - already-centered irregular residuals recover a known drift at machine-scale RMSE, and that RMSE is smaller than CWC of the corresponding raw autoregressive series (Curran & Bauer, 2011, pp. 607–608); - public CWC lag-pair extraction composed with the pairwise-mean log-rate on nonzero same-sign pairs equals that pairwise-mean helper when every admitted ratio is finite; unfiltered CWC pairs fail closed on the already-centered irregular helper when no pair has a finite strictly positive residual ratio; a finite overflowed or underflowed same-sign ratio still enters the CWC pairwise mean via \(\ln|later|-\ln|earlier|\) and is not silently dropped; near-equal large residuals use \(\ln(|later|/|earlier|)\) so the rate does not collapse to zero; two finite CWC rates whose raw sum overflows still recover the representable incremental pairwise mean, including mixed signs; \(T=2\) sign-flip and \(T=3\) zero-residual CWC yield an empty admissible set; CWC of a raw AR path still does not recover the raw-process drift, and treating that CWC log-rate as raw-process drift fails closed; +- public grand-mean-centered lag-pair extraction uses `LaggedGrandMeanResidual` and cannot enter the already-centered within-person helper; \(T=2\) CGM can keep same-sign pairs while \(T=2\) CWC is empty; CGM of a two-level series with distinct person means does not recover the known within-person drift (Hamaker et al., 2015, p. 104) and is not equal to CWC; when cluster means coincide, CGM equals CWC numerically and remains a distinct type; treating the CGM log-rate as a within-person lag fails closed; an all-`MAX` series has a finite grand mean of `MAX`; cancelling `±MAX` scores keep a representable zero grand mean in either row order; a finite grand mean whose residual overflows fails closed; - a singleton cluster is skipped; two singleton clusters yield an empty pair list and fail closed; - overflowing CWC residuals, overflowing contextual subtraction, later-only residual overflow, non-finite intervals, Newton overflow / start-skip / deriv-INF, and Pearson empty/mismatch paths fail closed. - Driver et al. (2017, p. 16 `DIFFUSIONstd`; Eq. 4; footnote 4; JSS PDF re-opened 2026-08-23T13:20Z) recovers a known continuous standardisation \(q/(-q/(2a))=-2a\) at machine-scale RMSE, and that RMSE is smaller than treating unstandardised \(q\), discrete \(Q_{\Delta t}/p\), or \(q/(\mathrm{trait}+p+\mathrm{added})\) as `DIFFUSIONstd`; distinct positive \(q\) recover the same \(-2a\); \(q=0\) and \(a\ge 0\) fail closed; a non-event clock and an overflowing ratio fail closed. diff --git a/docs/research/standards-and-literature.md b/docs/research/standards-and-literature.md index 27e0b91d2..598d7cfb0 100644 --- a/docs/research/standards-and-literature.md +++ b/docs/research/standards-and-literature.md @@ -49,7 +49,7 @@ Jones, K. (1991). Specifying and estimating multi-level models for geographical TEPP applies these sources to construct definition, score interpretation, reliability, validity evidence, uncertainty, consequences, longitudinal invariance, ESEM cross-loadings, and DSEM. Topic outputs are treated as fallible indicators or components only after their construct role is evaluated. Reflective, formative, and network classes remain distinct (Bollen & Lennox, 1991). Complete-data OLS loadings across posterior indicator draws are combined with Rubin (1996) \(T_m\); the arithmetic-mean helper remains a point estimate. Mislevy (1991, *Psychometrika, 56*, 177–196, DOI 10.1007/bf02294457) remains unread (Unpaywall 2026-08-30T20:52Z: `is_oa: false`, `oa_status: closed`, 0 locations). The 1988 ETS RR-88-45 / DTIC ADA200179 technical report of the same title was opened 2026-08-17T12:04Z from archive.org; it is not the 1991 journal article and is not used as Mislevy plausible-value authority. Temporal precedence is not causal identification (Holland, 1986). Within/between OLS follows Enders and Tofighi (2007), Curran and Bauer (2011), and Hamaker et al. (2015). Enders and Tofighi (2007, Table 2, pp. 124–127; PDF opened 2026-08-17) show that the CWC cluster-mean coefficient is the contextual effect (`between − within`), not the between-cluster effect. Curran and Bauer (2011, pp. 607–608) reject person-mean subtraction on a raw autoregressive series as the lagged within-person residual; already-centered irregular residuals use the Voelkle et al. (2012, Eq. 7) / Driver et al. (2017, Eq. 3) scalar map. Discrete lags from unequal event intervals are remapped through that log-rate (Voelkle et al., 2012, ZORA accepted manuscript re-opened 2026-08-17T13:13Z) and are not pooled. Driver, Oud, and Voelkle (2017, Eq. 3 and p. 4) write \(A_{\Delta t}=\operatorname{expm}(A\Delta t)\) and restate the discrete intercept as a function of \(A\) and \(\Delta t\). A binary64 underflow of \(\exp(a\Delta t)\) to `+0` is refused because discrete auto-effects are strictly positive. The discrete effect of a constant predictor is Voelkle et al. (2012, Eq. 12; ZORA accepted manuscript re-opened 2026-08-17T14:20Z, Introducing Intercepts, manuscript p. 20), evaluated as \(a_{yx}(\operatorname{expm1}(z)/a_{xx})\) with \(z=a_{xx}\Delta t\) so a finite result is not lost when \(z\) overflows to \(-\infty\) or when \(a_{yx}\Delta t\) overflows, and in log space when `expm1(z)` overflows at a finite \(z\); a zero continuous effect is exactly zero; an overflowing \(a_{yx}/a_{xx}\) rewrite term fails closed; the first-order product is the underflow limit of that equation, not the general constant-predictor discrete effect. The discrete effect of a time-varying predictor with matched sampling and constancy intervals is Voelkle et al. (2012, Eq. 14; manuscript p. 21): \(b^{*}_{y.x}(\Delta t)=a_{yx}\Delta t\). That product is not Eq. 12. Unmatched intervals fail closed (Oud & Jansen, 2000, unread). The exact scalar discrete process noise is Driver, Oud, and Voelkle (2017, Eq. 3; JSS PDF re-opened 2026-08-18T14:04Z, p. 4): \(Q_{\Delta t}=0.5 q(\operatorname{expm1}(z)/a)\) with \(z=2(a\Delta t)\) for \(a\neq 0\) and \(q=GG^{\top}\ge 0\); do not form \(2a\) first; \(a=0\) recovers \(q\Delta t\); an overflowing rewrite scale \(0.5 q/a\) fails closed. This is not a Kalman filter. Driver, Oud, and Voelkle (2017, Eq. 3; JSS PDF re-opened 2026-08-18T14:04Z) write the same discrete intercept as \(A^{-1}[e^{A\Delta t}-I]\xi\). The lagged covariance is \(\mathrm{e}^{a\Delta t}p\) and the unconditional variance is \(\mathrm{e}^{2a\Delta t}p+Q_{\Delta t}\) (Driver et al., 2017, Eq. 3–4, pp. 4–5); a zero diffusion whose \(2(a\Delta t)\) overflows to \(+\infty\) fails closed. The stationary within-subject variance is the \(\Delta t\to\infty\) limit of Eq. 4: \(-q/(2a)\) for stable \(a<0\) (JSS p. 16 `asymDIFFUSION`; §4.3; PDF re-opened 2026-08-18T18:03Z). Finite-interval \(Q_{\Delta t}\) is not that limit. Trait-plus-state variance is \(\mathrm{trait}+\mathrm{state}\) and lagged covariance is \(\mathrm{trait}+\mathrm{e}^{a\Delta t}p\) (Driver et al., 2017, §4.3, p. 9; JSS PDF re-opened 2026-08-18T21:07Z). Trait variance is not process noise and not `asymDIFFUSION`. The first-occasion map `τ + λ μ_0` is not `E(y_t)`. The contemporaneous `TDPREDEFFECT` impulse is `m x` (Driver et al., 2017, Eq. 3 fourth summand; Table 2; §7.2; JSS PDF re-opened 2026-08-20T07:10Z). `TDPREDEFFECT` is not `CINT`. `M x` is not `A^{-1}[e^{A Δt} − I] B z` and is not Voelkle et al. (2012, Eq. 14). Metric/weak invariance does not license latent-mean comparison. Putnick and Bornstein (2016, PMC author manuscript PMC5145197 opened 2026-08-19T22:15Z) require scalar invariance before latent-mean comparison and state that residual invariance is not a prerequisite. Two-observation OLS residual variance is identically `0` and is not strict. Meredith (1993) remains unread (Unpaywall/OpenAlex/Semantic Scholar 2026-08-30T21:20Z: `is_oa: false`, `oa_status: closed`, `isOpenAccess: false`, 0 locations; Internet Archive title+creator search returned 0 hits; Springer `content/pdf` redirected to the identity provider and did not yield a PDF; Cambridge Core DOI 10.1007/BF02294825 remains a closed product page). Vandenberg and Lance (2000) remains unread. Mislevy (1991, *Psychometrika, 56*, 177–196, DOI 10.1007/bf02294457) remains unread (Unpaywall/OpenAlex/Semantic Scholar 2026-08-30T21:20Z: `is_oa: false`, `oa_status: closed`, 0 locations; Springer `content/pdf` redirected to the identity provider; ETS article page `hqig` is a stub without a PDF). ERIC ED334221 is Singer and Willett (1991), not the 1991 journal article. ERIC ED333032 is Mislevy, Sheehan, and Wingersky (1990), ETS RR-90-17-ONR, not the 1991 journal article. The 1988 ETS RR-88-45 / DTIC ADA200179 technical report of the same title is not the 1991 journal article. Oud and Jansen (2000) remains unread (Unpaywall/OpenAlex 2026-08-18T21:07Z: closed). -For Meredith (1993), the Cambridge Core original-paper page and abstract were opened on 2026-08-21; Unpaywall/OpenAlex/Semantic Scholar were re-tried 2026-08-30T21:20Z (`is_oa: false`, `oa_status: closed`, 0 locations) and Springer `content/pdf` redirected to the identity provider rather than a PDF. No publicly accessible full-text copy was found among those checked sources. The earlier `remains unread` note means that the full text was not available, not that the authoritative record was unverified. Hamaker, Kuiper, and Grasman (2015) was re-opened 2026-08-30T20:58Z from the University of Amsterdam repository PDF (`https://pure.uva.nl/ws/files/2688454/168970_Hamaker_Kuiper_Grasman_2015_A_Critique_of_Cross_Lagged_Panel_Model.pdf`): lagged relations formed from grand-mean deviations confound stable between-person differences with within-person change; the RI-CLPM lagged relations use within-person deviations from stable means. That paper does not license treating a CWC residual log-rate as raw-process drift. +For Meredith (1993), the Cambridge Core original-paper page and abstract were opened on 2026-08-21; Unpaywall/OpenAlex/Semantic Scholar were re-tried 2026-08-30T21:20Z (`is_oa: false`, `oa_status: closed`, 0 locations) and Springer `content/pdf` redirected to the identity provider rather than a PDF. No publicly accessible full-text copy was found among those checked sources. The earlier `remains unread` note means that the full text was not available, not that the authoritative record was unverified. Hamaker, Kuiper, and Grasman (2015) was re-opened 2026-08-30T23:35Z from the University of Amsterdam repository PDF (`https://pure.uva.nl/ws/files/2688454/168970_Hamaker_Kuiper_Grasman_2015_A_Critique_of_Cross_Lagged_Panel_Model.pdf`, journal p. 104): lagged relations formed from grand-mean deviations "only represent actual within-person relationships if there are no trait-like, time-invariant, between-person differences"; the RI-CLPM lagged relations use within-person deviations from stable means. `psychometric_core` therefore exposes grand-mean-centered event-time lag as `LaggedGrandMeanResidual`, a distinct type from `LaggedWithinResidual`, and refuses treating that CGM log-rate as a within-person lag. When the raw score sum is finite the grand mean is that sum divided by n; when the raw sum overflows, positives and negatives are averaged separately with the overflow-safe incremental mean and combined by count. That paper does not license treating a CWC residual log-rate as raw-process drift, and the CGM helper is not RI-CLPM. ## Numerical precision, memory-aware computation, and causal identification