Skip to content

Add ten Berge & Zegers mu reliability lower-bound series (mu0-mu3) - #226

Merged
seonghobae merged 1 commit into
seonghobae-guttman-lambdasfrom
seonghobae-tenberge-mu
Jul 24, 2026
Merged

seonghobae merged 1 commit into
seonghobae-guttman-lambdasfrom
seonghobae-tenberge-mu

Conversation

@seonghobae

Copy link
Copy Markdown
Contributor

Implements the ten Berge & Zegers (1978) mu reliability lower-bound series (mu0-mu3) in mlsirm_core::reliability, with PyO3 binding and thin Python wrapper (fast_mlsirm.tenberge_mu -> TenBergeResult). Stacked on #225 (Guttman lambdas).

Sources actually read

  • CRAN psych 2.6.5 source tenberge.R (14 lines) — read in full (oracle).
  • ten Berge & Zegers (1978), Psychometrika, 43(4), 575-579, doi:10.1007/BF02293811 — paywalled, NOT read; attribution is "as cited in / as implemented by Revelle (2025)" throughout.

Contract (on the Pearson correlation matrix; Vt = sum(R), S_k = sum of off-diagonal R^k, c = p/(p-1) innermost only)

  • mu0 = c*S1/Vt (= coefficient alpha = Guttman lambda3)
  • mu1 = (S1 + sqrt(c*S2))/Vt (= Guttman lambda2)
  • mu2 = (S1 + sqrt(S2 + sqrt(c*S4)))/Vt
  • mu3 = (S1 + sqrt(S2 + sqrt(S4 + sqrt(c*S8))))/Vt
  • Ordering mu0 <= mu1 <= mu2 <= mu3 proven via Cauchy-Schwarz over the p*(p-1) off-diagonal cells (documented in module docs; asserted on crate outputs).

Adversarial spec-verify (before implementation): GO-WITH-FIXES, all applied

  • Confirmed c placement innermost-only in mu2/mu3 vs the R lines; confirmed mu0==lambda3, mu1==lambda2 algebraically.
  • Ruled monotonicity assert SAFE with the Cauchy-Schwarz proof; MC target justified as consistency (asymptotic) only; S1 by direct off-diagonal summation (cancellation); fixture tolerance 1e-9 required to kill the ~5e-4 innermost-c-drop mutation.

Declared divergences from psych (documented in module docs)

Raw-data input only (no correlation-matrix passthrough via the fragile dim[1] > n heuristic, no use="pairwise"); hard errors on degenerate input instead of NA propagation.

Evidence

Check Result
Fixture A (n=30, p=6) vs independent NumPy replication, all four mu pinned at 1e-9 PASS
Fixture B (n=24, p=5) PASS
Identity cross-checks mu0==lambda3, mu1==lambda2 (both sides crate outputs, independent code paths) at 1e-12 PASS
Ordering mu0<=mu1<=mu2<=mu3 on crate outputs (fixtures + all 500 MC reps) PASS
Rejections (n<3, p<3, wrong length, inf, zero variance) PASS
500-rep tau-equivalent MC (#[ignore]): mean mu0 vs analytic 0.7967 within 0.01 PASS
Mutation kills (hand-verified FAIL then restored): innermost-c drop; S2-for-S4 swap; flattened mu3 nesting 3/3 killed
cargo test -p mlsirm-core 486 passed
Python tests (fixture parity + rejections) 4 passed (incl. 2 Guttman from #225)
Adversarial impl-review (transcription, x4*x4, identities, monotonicity proof, wrapper) NO CONFIRMED DEFECTS

Known identity limits disclosed in the test header (mu0/mu1 equal existing Guttman outputs by exact algebra — anchored by independent NumPy literals; all-cells vs 2x-upper-triangle S_2 unkillable for symmetric R).

Extends mlsirm_core::reliability with tenberge_mu, transcribed from CRAN
psych 2.6.5 tenberge.R (read line by line; ten Berge & Zegers, 1978,
cited as-cited-in Revelle, 2025). On the Pearson correlation matrix with
Vt = sum(R), off-diagonal power sums S_k, and c = p/(p-1) on the
innermost radical only: mu0 = c*S1/Vt (= alpha = Guttman lambda3),
mu1 = (S1 + sqrt(c*S2))/Vt (= Guttman lambda2), mu2 and mu3 nest one and
two further radicals over S4 and S8. Series ordering mu0 <= mu1 <= mu2
<= mu3 follows from Cauchy-Schwarz over the p*(p-1) off-diagonal cells.
Divergences documented in the module: raw-data input only, hard errors
on degenerate input, S1 by direct off-diagonal summation.

Exposes tenberge_mu through the PyO3 binding and the
fast_mlsirm.reliability wrapper returning TenBergeResult.

Tests: two fixtures pinned at 1e-9 against an independent NumPy
replication, exact-identity cross-checks vs guttman_lambdas (both sides
crate outputs from independent code paths), rejection paths, ordering
asserts on crate outputs, and a 500-rep tau-equivalent Monte Carlo
(#[ignore]). Mutation kills verified by hand (innermost-c drop,
S2-for-S4 swap, flattened mu3 nesting).

Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds the ten Berge & Zegers (1978) mu reliability lower-bound series (mu0–mu3) to the reliability toolkit, extending the existing Rust-backed reliability metrics (e.g., Guttman lambdas) with a documented, tested implementation and a Python-facing API.

Changes:

  • Implement tenberge_mu and TenBergeResult in mlsirm_core::reliability using off-diagonal power sums over the Pearson correlation matrix.
  • Expose the new statistic through the PyO3 _core module and a thin Python wrapper (fast_mlsirm.tenberge_mu -> TenBergeResult).
  • Add Rust + Python fixture tests (including ordering assertions and degenerate-input rejections) and document the feature in the changelog.

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated no comments.

Show a summary per file
File Description
crates/mlsirm-core/src/reliability.rs Implements tenberge_mu (mu0–mu3) with documented contract and input validation.
crates/fast-mlsirm-py/src/lib.rs Adds PyO3 binding tenberge_mu returning a dict of mu0–mu3.
python/fast_mlsirm/reliability.py Introduces TenBergeResult dataclass and the tenberge_mu Python wrapper.
python/fast_mlsirm/__init__.py Re-exports tenberge_mu / TenBergeResult and adds them to the public API list.
tests/unit/reliability_tests.rs Adds Rust fixture tests, identity cross-checks vs Guttman outputs, and rejection tests.
tests/test_paper_features.py Adds Python fixture parity test and degenerate-input rejection coverage.
CHANGELOG.md Documents the new ten Berge mu series feature and its contract/verification notes.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@seonghobae
seonghobae merged commit c2c0c48 into seonghobae-guttman-lambdas Jul 24, 2026
6 checks passed
@seonghobae
seonghobae deleted the seonghobae-tenberge-mu branch July 24, 2026 11:27
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants