From 77216bf3ce61747fda55fece059093d3e5972a0a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 19:43:38 +0900 Subject: [PATCH 1/3] test(inference): cover negative infinite uncertainty --- tests/test_inference_nonfinite_uncertainty.py | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/tests/test_inference_nonfinite_uncertainty.py b/tests/test_inference_nonfinite_uncertainty.py index af5d25d0c..c3060961e 100644 --- a/tests/test_inference_nonfinite_uncertainty.py +++ b/tests/test_inference_nonfinite_uncertainty.py @@ -11,18 +11,21 @@ def test_standard_errors_preserve_invalid_and_infinite_uncertainty() -> None: """Undefined/infinite covariance must never be converted into zero uncertainty.""" - vcov = np.diag(np.array([4.0, 0.0, -1.0, np.nan, np.inf], dtype=np.float64)) + vcov = np.diag( + np.array([4.0, 0.0, -1.0, np.nan, np.inf, -np.inf], dtype=np.float64) + ) result = standard_errors_from_vcov(vcov) assert np.array_equal(result[:3], np.array([2.0, 0.0, 0.0])) assert np.isnan(result[3]) assert np.isposinf(result[4]) + assert np.isneginf(result[5]) def test_direct_rust_standard_errors_match_public_nonfinite_semantics() -> None: """Rust and public wrappers must agree on non-finite uncertainty semantics.""" - vcov = np.diag(np.array([1.0, np.nan, np.inf], dtype=np.float64)) + vcov = np.diag(np.array([1.0, np.nan, np.inf, -np.inf], dtype=np.float64)) rust = np.asarray(core.standard_errors_from_vcov(vcov), dtype=np.float64) public = standard_errors_from_vcov(vcov) @@ -30,6 +33,7 @@ def test_direct_rust_standard_errors_match_public_nonfinite_semantics() -> None: assert np.array_equal(rust[:1], public[:1]) assert np.isnan(rust[1]) and np.isnan(public[1]) assert np.isposinf(rust[2]) and np.isposinf(public[2]) + assert np.isneginf(rust[3]) and np.isneginf(public[3]) @pytest.mark.parametrize( From 1a25135754a688d0f8ba80aab2a8190956f9ff95 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 19:44:15 +0900 Subject: [PATCH 2/3] docs(inference): ground nonfinite uncertainty semantics --- .../inference_nonfinite_uncertainty.md | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/docs/doctoring/inference_nonfinite_uncertainty.md b/docs/doctoring/inference_nonfinite_uncertainty.md index c0d02af67..4d00c93bc 100644 --- a/docs/doctoring/inference_nonfinite_uncertainty.md +++ b/docs/doctoring/inference_nonfinite_uncertainty.md @@ -5,13 +5,23 @@ After observed-information inversion, diagonal elements of a covariance matrix may be non-finite when the numeric path encounters undefined or unbounded curvature. Mapping those diagonals to zero would report perfect certainty and is scientifically false. The Rust-owned `standard_errors_from_vcov` therefore: 1. returns `sqrt(d)` for finite positive `d`; -2. clamps finite non-positive `d` to `0.0` (numerical noise / negative curvature residue); and +2. clamps finite non-positive `d` to `0.0` (negative roundoff/curvature residue on a path that cannot yield a real-valued standard error); and 3. preserves `NaN` and signed infinities unchanged. -Non-finite Hessian entries fail closed before inversion. +Non-finite Hessian entries fail closed before inversion. This representation rule is an explicit repository safety contract, not a claim that the cited measurement/statistical sources prescribe a particular IEEE-754 sentinel policy. + +## Source-specific rationale + +- **AERA/APA/NCME Standards.** The Standards require uncertainty, precision, and score-interpretation evidence to be represented in ways that support defensible interpretations. They motivate the no-false-precision invariant: an undefined or unbounded uncertainty state must not be silently reported as exact zero uncertainty. The official joint-publisher site provides the 2014 edition as open access. +- **NIST/SEMATECH uncertainty guidance.** NIST defines standard uncertainty through root-sum-of-squares combination of standard-deviation components and ties expanded uncertainty to interval coverage. This grounds the ordinary finite-positive `sqrt(variance)` interpretation and the need to preserve the distinction between a meaningful zero and a non-finite uncertainty state. +- **Rust `f64` primary documentation.** Rust exposes distinct `NaN`, `INFINITY`, and `NEG_INFINITY` values and explicit `is_nan`/`is_infinite` classification. This is the implementation-level basis for preserving both infinity signs and testing them symmetrically rather than collapsing them to a finite sentinel. + +Finite negative covariance diagonals do not define real standard errors. The existing compatibility rule clamps those finite non-positive values to zero; changing that interpretation to a hard failure would be a separate scientific/API decision requiring its own test-first review. ## References -American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. https://www.testingstandards.net/open-access-files.html + +National Institute of Standards and Technology. (n.d.). *Standard and expanded uncertainties*. In *NIST/SEMATECH e-Handbook of statistical methods*. https://www.itl.nist.gov/div898/handbook/mpc/section5/mpc57.htm -Casella, G., & Berger, R. L. (2002). *Statistical inference* (2nd ed.). Duxbury. +The Rust Project Developers. (2026). *Primitive type f64*. Rust documentation. https://doc.rust-lang.org/stable/core/primitive.f64.html From 3aa5af708364563a5d46724fdbd8c82cb40bd3e0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Tue, 11 Aug 2026 19:45:12 +0900 Subject: [PATCH 3/3] test(rust): preserve negative infinite SE sentinel --- crates/mlsirm-core/src/inference.rs | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/crates/mlsirm-core/src/inference.rs b/crates/mlsirm-core/src/inference.rs index 54b8a68f7..187f4a1a4 100644 --- a/crates/mlsirm-core/src/inference.rs +++ b/crates/mlsirm-core/src/inference.rs @@ -215,19 +215,21 @@ mod tests { #[test] fn standard_errors_preserve_nonfinite_diagonals() { - // row-major 5x5 with targeted diagonal entries - let mut v = vec![0.0_f64; 25]; + // row-major 6x6 with targeted diagonal entries + let mut v = vec![0.0_f64; 36]; v[0] = 4.0; // SE=2 - v[6] = 0.0; // SE=0 - v[12] = -1.0; // clamp to 0 - v[18] = f64::NAN; // preserve NaN - v[24] = f64::INFINITY; // preserve +inf - let se = standard_errors_from_vcov(&v, 5).unwrap(); + v[7] = 0.0; // SE=0 + v[14] = -1.0; // clamp to 0 + v[21] = f64::NAN; // preserve NaN + v[28] = f64::INFINITY; // preserve +inf + v[35] = f64::NEG_INFINITY; // preserve -inf + let se = standard_errors_from_vcov(&v, 6).unwrap(); assert!((se[0] - 2.0).abs() < 1e-15); assert_eq!(se[1], 0.0); assert_eq!(se[2], 0.0); assert!(se[3].is_nan()); assert!(se[4].is_infinite() && se[4].is_sign_positive()); + assert!(se[5].is_infinite() && se[5].is_sign_negative()); } #[test]