Skip to content
Draft
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
3 changes: 1 addition & 2 deletions ARCHITECTURE.md

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion CLAUDE.md

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions crates/psychometric_core/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down Expand Up @@ -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)
}
Expand Down Expand Up @@ -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"
);
}
}
Loading