Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion ARCHITECTURE.md

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ All notable changes to TEPP are documented here. The format follows Keep a Chang

## [Unreleased]

- `psychometric_core` recovers the Driver, Oud, and Voelkle (2017, p. 16 `discreteDIFFUSIONstd`; Eq. 3–4, pp. 4–5; footnote 4; §7.1, pp. 18–19; JSS PDF re-opened 2026-08-23T13:06Z from https://www.jstatsoft.org/index.php/jss/article/download/v077i05/1104) scalar standardised discrete `DIFFUSION`. Page 16 prints discrete-time transformations for a chosen event interval (`discreteDRIFT`, `discreteDIFFUSION`) and, when appropriate, standardised matrices with the suffix `std`. Footnote 4: standardisations use only the relevant variance, not the total. Process noise is within-subject stochastic input, so that relevant variance is within-subject `asymDIFFUSION` `-q / (2 a)`, the same footnote 4 variance used for `DRIFT`. Form strictly positive `asymDIFFUSION` first, then `Q_Δt` from Equation 4, then `Q_Δt / (−q / (2 a))`. In the scalar stationary case that ratio equals `1 − exp(2 a Δt)`. Unstandardised `Q_Δt` is defined for growing `a ≥ 0` and for zero diffusion; standardised `DIFFUSION` is not. Zero `asymDIFFUSION` has no positive SD and fails closed. The continuous standardisation `q / (−q / (2 a)) = −2 a` is not the discrete map. Section 7.1 warns that omitting trait variance confounds between- and within-person information. `Q_Δt / (trait + p + added)` uses the total, not `asymDIFFUSION`, and is not `discreteDIFFUSIONstd` when `TRAITVAR` is nonzero. `TRAITVAR` is not the standardisation variance. Still not a Kalman filter, not a matrix `expm`, not ESEM estimation, not DSEM, and not ctsem estimation. Meredith (1993) remains unread (Unpaywall 2026-08-23T13:06Z: `is_oa: false`; title *Measurement Invariance, Factor Analysis and Factorial Invariance*). Mislevy (1991, *Psychometrika, 56*, 177–196) remains unread (Unpaywall 2026-08-23T13:06Z: `is_oa: false`; title *Randomization-Based Inference about Latent Variables from Complex Samples*).
- `psychometric_core` recovers the Driver, Oud, and Voelkle (2017, p. 16 `discreteDRIFTstd`; Eq. 3, p. 5; footnote 4; §7.1, pp. 18–19; JSS PDF re-opened 2026-08-23T11:40Z from https://www.jstatsoft.org/index.php/jss/article/download/v077i05/1104) scalar standardised discrete `DRIFT`. Page 16 prints `discreteDRIFT` as `expm(DRIFT Δt)` and, when appropriate, `discreteDRIFTstd`. Footnote 4: standardisations use only the relevant variance, not the total. For `DRIFT` that relevant variance is within-subject `asymDIFFUSION` `-q / (2 a)`, because `DRIFT` is intended to represent individual, or average individual, temporal dynamics. Form strictly positive `asymDIFFUSION` first, then `φ = exp(a Δt)`. In the scalar stationary case the within-subject SD ratio is 1, so the standardised auto-effect equals the unstandardised discrete lag numerically; those remain distinct named quantities. Unstandardised `e^{a Δt}` is defined for growing `a ≥ 0` and for zero diffusion; standardised `DRIFT` is not. Zero `asymDIFFUSION` has no positive SD and fails closed. Section 7.1 warns that omitting trait variance confounds between- and within-person information. The trait-plus-state autocorrelation `(trait + e^{a Δt} p + added) / (trait + p + added)` uses the total, not `asymDIFFUSION`, and is not `discreteDRIFTstd` when `TRAITVAR` is nonzero. `TRAITVAR` is not the standardisation variance. Still not a Kalman filter, not a matrix `expm`, not ESEM estimation, not DSEM, and not ctsem estimation. Meredith (1993) remains unread (Unpaywall 2026-08-23T11:40Z: `is_oa: false`; title *Measurement Invariance, Factor Analysis and Factorial Invariance*). Mislevy (1991, *Psychometrika, 56*, 177–196) remains unread (Unpaywall 2026-08-23T11:40Z: `is_oa: false`; title *Randomization-Based Inference about Latent Variables from Complex Samples*).
- `psychometric_core` recovers the Driver, Oud, and Voelkle (2017, §4.3, pp. 9–10; Eq. 3–5, pp. 4–5; Table 2, p. 12; p. 16; §7.2, pp. 20–21; JSS PDF re-opened 2026-08-23T11:05Z from https://www.jstatsoft.org/index.php/jss/article/download/v077i05/1104) scalar later-start later-occasion variance of §4.3 predetermined `T0VAR`. Section 4.3 treats the first time point as predetermined when no assumptions are made about the process prior to the initial time point. Free `T0VAR` `p_0` is then estimated. Section 4.3 notes that the process gradually transitions from the initial variances toward the stationary variances, and that the initial time point need not reflect the first measurement occasion (`startoffset`). Equation 3 writes `η(t) = exp(A Δt) η(t0) + … +` the stochastic integral. Equation 4 writes that the integral exhibits covariance `Q_Δt`. After a later start `u` the within-subject state variance is `e^{2 a u} p_0 + Q_u`. Evolving that later start over `s` is `e^{2 a s}(e^{2 a u} p_0 + Q_u) + Q_s`. Chapman–Kolmogorov writes `Q_{u+s} = e^{2 a s} Q_u + Q_s`, so the later-occasion map over `u + s` is the same composition. Trait variance and `addedTIPREDVAR` are time-invariant between-subject and do not enter `Q_s`. The later-start later-occasion composition is `trait + e^{2 a s}(e^{2 a u} p_0 + Q_u) + Q_s + (B / a)² v`. Form the later-start within-subject variance first, then evolve that state, then include the trait, then include the TI extra variance, then add. Setting `p_0 = −q / (2 a)` recovers the stationary later-occasion map. Stationary later-occasion variance uses `−q / (2 a)` in place of `p_0` and is not this map when `p_0` is free. Later-occasion variance at `u` omits `Q_s` and is not this map when `s > 0`. Later-start lagged covariance is `e^{a s}` of the later state and omits `Q_s`; it is not this map. Evolving the later total as if it were all state (`e^{2 a s}` of `trait + e^{2 a u} p_0 + Q_u + (B / a)² v` plus `Q_s`) is not this map. Later-occasion variance over the lag interval alone ignores `startoffset` and omits `e^{2 a s} Q_u`; it is not this map when `u > 0`. As `u → 0+` the composition approaches later-occasion variance over `s`. As `s → 0+` the composition approaches later-occasion variance at `u`. As `s → ∞` with stable `a < 0` the carried later state vanishes and `Q_s` approaches `−q / (2 a)`, so the composition approaches contemporaneous stationary `T0VAR`. A zero trait, a zero initial variance, a zero diffusion, and a zero TI contribution is exactly zero. A zero initial variance, a zero diffusion, and a zero TI contribution is exactly the trait. Trait-only variance does not require a stable drift. `a ≥ 0` cannot hold a finite TI extra variance when that contribution is nonzero and fails closed. Nonzero diffusion with `a ≥ 0` is a growing process and is kept. Both intervals must be event time and strictly positive. Equation 5 of that later-start later-occasion variance is `λ²(trait + e^{2 a s}(e^{2 a u} p_0 + Q_u) + Q_s + (B / a)² v) + θ + ψ`. `MANIFESTVAR` is not that later-start later-occasion observed variance. The later-start later-occasion latent variance is not the later-start later-occasion observed variance. Predetermined later observed variance at `u` omits `Q_s` and is not that observed variance when `s > 0`. Later-start lagged observed covariance omits `Q_s` and `θ` and is not that observed variance. Stationary later-occasion observed variance is not that observed variance when `p_0` is free. Still not a Kalman filter, not a matrix `expm`, not ESEM estimation, not DSEM, and not ctsem estimation. Meredith (1993) remains unread (Unpaywall 2026-08-23T11:05Z: `is_oa: false`; title *Measurement Invariance, Factor Analysis and Factorial Invariance*). Mislevy (1991, *Psychometrika, 56*, 177–196) remains unread (Unpaywall 2026-08-23T11:05Z: `is_oa: false`; title *Randomization-Based Inference about Latent Variables from Complex Samples*).
- `psychometric_core` recovers the Driver, Oud, and Voelkle (2017, §4.3, pp. 9–10; Eq. 3–5, pp. 4–5; Table 2, p. 12; p. 16; §7.2, pp. 20–21; JSS PDF re-opened 2026-08-23T10:27Z from https://www.jstatsoft.org/index.php/jss/article/download/v077i05/1104) scalar later-start lagged covariance of §4.3 predetermined `T0VAR`. Section 4.3 treats the first time point as predetermined when no assumptions are made about the process prior to the initial time point. Free `T0VAR` `p_0` is then estimated. Section 4.3 notes that the process gradually transitions from the initial variances toward the stationary variances, and that the initial time point need not reflect the first measurement occasion (`startoffset`). Equation 3 writes `η(t) = exp(A Δt) η(t0) + … +` the stochastic integral. Equation 4 writes `cov(η_t, η_{t-1}) = A_Δt cov(η_{t-1})`. After a later start `u` the within-subject state variance is `e^{2 a u} p_0 + Q_u`. Lagging that later start over `s` is `e^{a s}(e^{2 a u} p_0 + Q_u)`. Trait variance and `addedTIPREDVAR` are time-invariant between-subject and do not decay with `e^{a s}`. The lagged composition is `trait + e^{a s}(e^{2 a u} p_0 + Q_u) + (B / a)² v`. Form the later-start within-subject variance first, then lag that state, then include the trait, then include the TI extra variance, then add. 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 this map when `p_0` is free. First-occasion lagged covariance `trait + e^{a s} p_0 + (B / a)² v` omits `e^{a s} Q_u` and is not this map when `u > 0`. Later-occasion variance includes `Q_u` without lagging that later state and is not this map. Evolving the later total as if it were all state (`e^{a s}` of `trait + e^{2 a u} p_0 + Q_u + (B / a)² v`) is not this map. As `u → 0+` the composition approaches first-occasion lagged covariance. As `s → 0+` the composition approaches later-occasion variance at `u`. As `s → ∞` with stable `a < 0` the state term vanishes. A zero trait, a zero initial variance, a zero diffusion, and a zero TI contribution is exactly zero. A zero initial variance, a zero diffusion, and a zero TI contribution is exactly the trait. Trait-only variance does not require a stable drift. `a ≥ 0` cannot hold a finite TI extra variance when that contribution is nonzero and fails closed. Nonzero diffusion with `a ≥ 0` is a growing process and is kept. Both intervals must be event time and strictly positive. Equation 5 of that later-start lagged covariance is `λ²(trait + e^{a s}(e^{2 a u} p_0 + Q_u) + (B / a)² v) + ψ`. Independent `ε_t` does not enter. `MANIFESTVAR` is not that later-start lagged observed covariance. The later-start lagged latent covariance is not the later-start lagged observed covariance. First-occasion lagged observed covariance omits `e^{a s} Q_u` and is not that observed covariance when `u > 0`. Predetermined later observed variance includes `Q_u` and `θ` and is not that later-start lagged observed covariance. Stationary lagged observed covariance is not that observed covariance when `p_0` is free. Still not a Kalman filter, not a matrix `expm`, not ESEM estimation, not DSEM, and not ctsem estimation. Meredith (1993) remains unread (Unpaywall 2026-08-23T10:27Z: `is_oa: false`; title *Measurement Invariance, Factor Analysis and Factorial Invariance*). Mislevy (1991, *Psychometrika, 56*, 177–196) remains unread (Unpaywall 2026-08-23T10:27Z: `is_oa: false`; title *Randomization-Based Inference about Latent Variables from Complex Samples*).
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md

Large diffs are not rendered by default.

54 changes: 54 additions & 0 deletions crates/psychometric_core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -684,6 +684,24 @@ pub enum PsychometricError {
/// footnote 4 standardisation variance. `TRAITVAR` is not
/// `asymDIFFUSION`.
TraitVarianceIsNotStandardisationVariance,
/// Driver p. 16 `discreteDIFFUSIONstd` was requested with a
/// non-positive within-subject variance. Footnote 4 standardises
/// process noise using only strictly positive `asymDIFFUSION`.
StandardisedDiscreteDiffusionRequiresPositiveWithinSubjectVariance,
/// Driver p. 16 unstandardised `discreteDIFFUSION` `Q_Δt` was
/// treated as `discreteDIFFUSIONstd`. Unstandardised `Q_Δt` is
/// defined for growing or zero-diffusion processes; standardised
/// `DIFFUSION` is not.
UnstandardisedDiscreteDiffusionIsNotStandardisedDiscreteDiffusion,
/// Driver continuous `DIFFUSION` standardisation `q / (−q / (2 a))`
/// was treated as p. 16 `discreteDIFFUSIONstd`. `−2 a` is not
/// `Q_Δt / (−q / (2 a))`.
StandardisedContinuousDiffusionIsNotStandardisedDiscreteDiffusion,
/// Driver §7.1 trait-contaminated process noise
/// `Q_Δt / (trait + p + added)` was treated as p. 16
/// `discreteDIFFUSIONstd`. Footnote 4 uses only `asymDIFFUSION`,
/// not `TRAITVAR`.
TraitContaminatedProcessNoiseIsNotStandardisedDiscreteDiffusion,
}

impl fmt::Display for PsychometricError {
Expand Down Expand Up @@ -1212,6 +1230,18 @@ impl fmt::Display for PsychometricError {
Self::TraitVarianceIsNotStandardisationVariance => {
"trait variance is not the standardisation variance"
}
Self::StandardisedDiscreteDiffusionRequiresPositiveWithinSubjectVariance => {
"standardised discrete DIFFUSION requires strictly positive within-subject variance"
}
Self::UnstandardisedDiscreteDiffusionIsNotStandardisedDiscreteDiffusion => {
"unstandardised discrete DIFFUSION is not standardised discrete DIFFUSION"
}
Self::StandardisedContinuousDiffusionIsNotStandardisedDiscreteDiffusion => {
"standardised continuous DIFFUSION is not standardised discrete DIFFUSION"
}
Self::TraitContaminatedProcessNoiseIsNotStandardisedDiscreteDiffusion => {
"trait-contaminated process noise is not standardised discrete DIFFUSION"
}
};
formatter.write_str(message)
}
Expand Down Expand Up @@ -2071,4 +2101,28 @@ mod tests {
"trait variance is not the standardisation variance"
);
}

#[test]
fn standardised_discrete_diffusion_boundary_messages_are_stable() {
assert_eq!(
PsychometricError::StandardisedDiscreteDiffusionRequiresPositiveWithinSubjectVariance
.to_string(),
"standardised discrete DIFFUSION requires strictly positive within-subject variance"
);
assert_eq!(
PsychometricError::UnstandardisedDiscreteDiffusionIsNotStandardisedDiscreteDiffusion
.to_string(),
"unstandardised discrete DIFFUSION is not standardised discrete DIFFUSION"
);
assert_eq!(
PsychometricError::StandardisedContinuousDiffusionIsNotStandardisedDiscreteDiffusion
.to_string(),
"standardised continuous DIFFUSION is not standardised discrete DIFFUSION"
);
assert_eq!(
PsychometricError::TraitContaminatedProcessNoiseIsNotStandardisedDiscreteDiffusion
.to_string(),
"trait-contaminated process noise is not standardised discrete DIFFUSION"
);
}
}
Loading
Loading