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
16 changes: 9 additions & 7 deletions crates/mlsirm-core/src/inference.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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]
Expand Down
18 changes: 14 additions & 4 deletions docs/doctoring/inference_nonfinite_uncertainty.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
8 changes: 6 additions & 2 deletions tests/test_inference_nonfinite_uncertainty.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,25 +11,29 @@

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)

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(
Expand Down
Loading