From c5ec42e40307f3645c18b0d73114b73e01745a20 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:02:18 +0900 Subject: [PATCH 001/576] test(validation): expose representable bias sum overflow --- .../tests/bias_overflow_safe_mean_contract.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 crates/validation_core/tests/bias_overflow_safe_mean_contract.rs diff --git a/crates/validation_core/tests/bias_overflow_safe_mean_contract.rs b/crates/validation_core/tests/bias_overflow_safe_mean_contract.rs new file mode 100644 index 000000000..62101588d --- /dev/null +++ b/crates/validation_core/tests/bias_overflow_safe_mean_contract.rs @@ -0,0 +1,10 @@ +use validation_core::{bias_standard_error, mean_bias}; + +#[test] +fn representable_extreme_constant_bias_survives_raw_sum_overflow() { + let truth = [0.0, 0.0]; + let recovered = [f64::MAX, f64::MAX]; + + assert_eq!(mean_bias(&truth, &recovered), Ok(f64::MAX)); + assert_eq!(bias_standard_error(&truth, &recovered), Ok(0.0)); +} From 7499042f7451b2e3d5e9f83843aeea82c4f5ff06 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:03:55 +0900 Subject: [PATCH 002/576] fix(validation): preserve representable extreme bias --- crates/validation_core/src/bias.rs | 127 ++++++++++++++++++++++------- 1 file changed, 96 insertions(+), 31 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index d7ca438b9..52929180d 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -1,50 +1,90 @@ //! Signed mean bias recovery metric. use crate::ValidationError; -use crate::input::{require_finite, require_paired_finite}; +use crate::input::require_paired_finite; + +fn signed_residuals(truth: &[f64], recovered: &[f64]) -> Result, ValidationError> { + require_paired_finite(truth, recovered)?; + truth + .iter() + .zip(recovered) + .map(|(truth_value, recovered_value)| { + let residual = recovered_value - truth_value; + if residual.is_finite() { + Ok(residual) + } else { + Err(ValidationError::InvalidInput) + } + }) + .collect() +} + +fn scaled_compensated_mean(values: &[f64]) -> Result { + let scale = values.iter().map(|value| value.abs()).fold(0.0, f64::max); + if scale == 0.0 { + return Ok(0.0); + } + + let mut normalized: Vec<_> = values.iter().map(|value| *value / scale).collect(); + normalized.sort_by(f64::total_cmp); + + let mut sum = 0.0_f64; + let mut correction = 0.0_f64; + for value in normalized { + let next = sum + value; + if sum.abs() >= value.abs() { + correction += (sum - next) + value; + } else { + correction += (value - next) + sum; + } + sum = next; + } + + let normalized_mean = (sum + correction) / values.len() as f64; + let mean = scale * normalized_mean; + if !mean.is_finite() || (mean == 0.0 && normalized_mean != 0.0) { + Err(ValidationError::InvalidInput) + } else if mean == 0.0 { + Ok(0.0) + } else { + Ok(mean) + } +} /// Mean signed bias `mean(recovered − truth)`. /// +/// Signed residuals are normalized by their largest magnitude and summed with +/// deterministic compensated arithmetic before the final scale is restored. +/// This keeps a representable bias from failing only because the raw residual +/// sum overflows, while a mathematically non-zero mean that falls below the +/// binary64 range fails closed rather than masquerading as zero bias. +/// /// # Errors /// /// Returns [`ValidationError::InvalidInput`] for empty, unequal-length, -/// non-finite inputs, or arithmetic overflow to a non-finite mean. +/// non-finite inputs, an unrepresentable signed residual, or an unrepresentable +/// final mean bias. pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result { - require_paired_finite(truth, recovered)?; - let mut sum = 0.0_f64; - for (t, r) in truth.iter().zip(recovered) { - let diff = r - t; - if !diff.is_finite() { - return Err(ValidationError::InvalidInput); - } - sum += diff; - if !sum.is_finite() { - return Err(ValidationError::InvalidInput); - } - } - require_finite(sum / truth.len() as f64) + let residuals = signed_residuals(truth, recovered)?; + scaled_compensated_mean(&residuals) } /// Standard error of the mean signed bias under independent observations. /// +/// The signed-difference mean uses the same overflow-safe deterministic +/// reference as [`mean_bias`]. The sample variance remains fail-closed when a +/// squared deviation itself is not representable. +/// /// # Errors /// -/// Returns [`ValidationError::InvalidInput`] for invalid pairs, `n < 2`, or -/// non-finite intermediate bias arithmetic. +/// Returns [`ValidationError::InvalidInput`] for invalid pairs, `n < 2`, an +/// unrepresentable signed residual or mean, or non-finite variance arithmetic. pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result { if truth.len() < 2 { return Err(ValidationError::InvalidInput); } - require_paired_finite(truth, recovered)?; - let mut diffs = Vec::with_capacity(truth.len()); - for (t, r) in truth.iter().zip(recovered) { - let diff = r - t; - if !diff.is_finite() { - return Err(ValidationError::InvalidInput); - } - diffs.push(diff); - } - let mean = require_finite(diffs.iter().sum::() / diffs.len() as f64)?; + let diffs = signed_residuals(truth, recovered)?; + let mean = scaled_compensated_mean(&diffs)?; let mut variance_sum = 0.0_f64; for diff in &diffs { let delta = diff - mean; @@ -53,9 +93,17 @@ pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result Date: Thu, 3 Sep 2026 22:04:54 +0900 Subject: [PATCH 003/576] docs(validation): trace stable bias arithmetic --- .../task-11-recovery-metrics-foundations.md | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/docs/research/task-11-recovery-metrics-foundations.md b/docs/research/task-11-recovery-metrics-foundations.md index 33a946105..ce8ac820a 100644 --- a/docs/research/task-11-recovery-metrics-foundations.md +++ b/docs/research/task-11-recovery-metrics-foundations.md @@ -37,8 +37,22 @@ Manning, C. D., Raghavan, P., & Schütze, H. (2008). *Introduction to informatio - **Temporal-order accuracy** scores pairwise sign agreement, treating exact ties as a distinct class. - **Monte Carlo** percentiles use inclusive nearest-rank on sorted finite replications. +## 2026-09-03 bias arithmetic hardening + +Fresh Validation Evidence review found that the protected-main implementation formed mean signed bias by adding raw finite residuals before dividing by `n`. That makes the numerical procedure stricter than the estimand: two finite residuals equal to `f64::MAX` have a representable mean of `f64::MAX`, but their raw sum overflows. The same raw-sum dependency also made the standard error of a constant extreme bias fail even though its sampling variance is exactly zero. + +RED `c5ec42e40307f3645c18b0d73114b73e01745a20` fixes this contract through the public `mean_bias` and `bias_standard_error` APIs. Causal repair `7499042f7451b2e3d5e9f83843aeea82c4f5ff06` validates each signed residual, normalizes by the largest residual magnitude, uses deterministic compensated summation, and restores scale only after dividing by the replication count. Exact cancellation is canonical `+0.0`; a represented non-zero normalized mean that becomes `0.0` only when scaled back fails closed rather than being reported as zero bias. The bias-SE sample variance remains fail-closed when a squared deviation is itself outside binary64 range; that is a separate numerical contract rather than part of this repair. + +This is a Validation Evidence implementation repair, not a change to the bias estimand, estimator target, or longitudinal domain semantics, so it does not require a new PRD target or ADR. Morris, White, and Crowther's ADEMP guidance treats bias as an explicitly defined simulation performance measure against known truth; implementation overflow must not silently redefine when that measure exists. IEEE/ISO/IEC 60559-2020, the active international adoption of IEEE 754-2019 as of 2026-09-03, supplies the floating-point execution model. IEEE has an active P754 revision project approved in 2024, but that project is not substituted for the published active standard. + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +IEEE Computer Society. (2020). *IEEE/ISO/IEC 60559-2020: ISO/IEC/IEEE international standard—Floating-point arithmetic*. IEEE Standards Association. https://standards.ieee.org/ieee/60559/10226/ + ## Verification - unit oracle tests for every metric, including empty/unequal/non-finite inputs, inverted intervals, overflow RMSE, single-replication MC, and SE-aware accept/reject; - foundation recovery study unit test with known loadings, intervals, temporal order, edges, and report serialization; +- `crates/validation_core/tests/bias_overflow_safe_mean_contract.rs` exercises the representable `f64::MAX` constant-bias case through the public API; +- exact cancellation and non-zero-bias underflow are distinct contracts: cancellation remains zero, while an unrepresentable positive mean fails closed; - workspace line and branch coverage gates must remain complete for production modules. From e379d164c66abb2efa8918422b4e6cf7fe8e0cf0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:06:26 +0900 Subject: [PATCH 004/576] test(validation): cover bias numerical refusal branches --- crates/validation_core/src/bias.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 52929180d..d646c5909 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -116,6 +116,7 @@ mod tests { let truth = [1.0, 2.0, 3.0]; let recovered = [2.0, 3.0, 4.0]; assert!((mean_bias(&truth, &recovered).expect("bias") - 1.0).abs() < 1e-12); + assert_eq!(mean_bias(&[1.0], &[1.0]), Ok(0.0)); let se = bias_standard_error(&truth, &recovered).expect("se"); assert_eq!(se, 0.0); assert_eq!(mean_bias(&[], &[]), Err(ValidationError::InvalidInput)); @@ -180,5 +181,13 @@ mod tests { bias_standard_error(&[0.0, 0.0], &[huge, -huge]), Err(ValidationError::InvalidInput) ); + let finite_square_overflowing_sum = 1e154; + assert_eq!( + bias_standard_error( + &[0.0, 0.0], + &[finite_square_overflowing_sum, -finite_square_overflowing_sum], + ), + Err(ValidationError::InvalidInput) + ); } } From 859e66b4ab5e61613d9c62f51d1e27430475616a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:06:40 +0900 Subject: [PATCH 005/576] docs(changelog): record stable bias recovery --- CHANGELOG.d/validation-bias-overflow-safe-mean.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-overflow-safe-mean.md diff --git a/CHANGELOG.d/validation-bias-overflow-safe-mean.md b/CHANGELOG.d/validation-bias-overflow-safe-mean.md new file mode 100644 index 000000000..410ad4fd3 --- /dev/null +++ b/CHANGELOG.d/validation-bias-overflow-safe-mean.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core::mean_bias` now computes finite signed residual means with deterministic scale-normalized compensated summation, so a representable extreme bias such as two `f64::MAX` residuals is no longer rejected solely because the raw sum overflows. +- `validation_core::bias_standard_error` reuses the same stable bias mean, allowing constant extreme finite bias to retain its exact zero standard error. Exact cancellation remains zero; a mathematically non-zero bias that falls below binary64 range fails closed rather than being reported as zero. From 7de0ef90944925ae7b232a8280f5bf9096df6502 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:13:37 +0900 Subject: [PATCH 006/576] test(validation): expose representable bias SE overflow --- .../tests/bias_overflow_safe_mean_contract.rs | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/crates/validation_core/tests/bias_overflow_safe_mean_contract.rs b/crates/validation_core/tests/bias_overflow_safe_mean_contract.rs index 62101588d..14198996e 100644 --- a/crates/validation_core/tests/bias_overflow_safe_mean_contract.rs +++ b/crates/validation_core/tests/bias_overflow_safe_mean_contract.rs @@ -8,3 +8,12 @@ fn representable_extreme_constant_bias_survives_raw_sum_overflow() { assert_eq!(mean_bias(&truth, &recovered), Ok(f64::MAX)); assert_eq!(bias_standard_error(&truth, &recovered), Ok(0.0)); } + +#[test] +fn representable_bias_standard_error_survives_raw_square_sum_overflow() { + let truth = [0.0, 0.0, 0.0]; + let recovered = [1.0e154, -1.0e154, 0.0]; + + let expected = 1.0e154 / 3.0_f64.sqrt(); + assert_eq!(bias_standard_error(&truth, &recovered), Ok(expected)); +} From cad231620679d8f912bded36c654446032b45e57 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:15:17 +0900 Subject: [PATCH 007/576] fix(validation): preserve representable bias standard error --- crates/validation_core/src/bias.rs | 156 ++++++++++++++++++++--------- 1 file changed, 109 insertions(+), 47 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index d646c5909..9e3f57022 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -19,18 +19,11 @@ fn signed_residuals(truth: &[f64], recovered: &[f64]) -> Result, Valida .collect() } -fn scaled_compensated_mean(values: &[f64]) -> Result { - let scale = values.iter().map(|value| value.abs()).fold(0.0, f64::max); - if scale == 0.0 { - return Ok(0.0); - } - - let mut normalized: Vec<_> = values.iter().map(|value| *value / scale).collect(); - normalized.sort_by(f64::total_cmp); - +fn deterministic_compensated_sum(mut values: Vec) -> f64 { + values.sort_by(f64::total_cmp); let mut sum = 0.0_f64; let mut correction = 0.0_f64; - for value in normalized { + for value in values { let next = sum + value; if sum.abs() >= value.abs() { correction += (sum - next) + value; @@ -39,8 +32,17 @@ fn scaled_compensated_mean(values: &[f64]) -> Result { } sum = next; } + sum + correction +} - let normalized_mean = (sum + correction) / values.len() as f64; +fn scaled_compensated_mean(values: &[f64]) -> Result { + let scale = values.iter().map(|value| value.abs()).fold(0.0, f64::max); + if scale == 0.0 { + return Ok(0.0); + } + + let normalized: Vec<_> = values.iter().map(|value| *value / scale).collect(); + let normalized_mean = deterministic_compensated_sum(normalized) / values.len() as f64; let mean = scale * normalized_mean; if !mean.is_finite() || (mean == 0.0 && normalized_mean != 0.0) { Err(ValidationError::InvalidInput) @@ -51,6 +53,72 @@ fn scaled_compensated_mean(values: &[f64]) -> Result { } } +fn standard_error_from_deviations(deviations: &[f64]) -> Result { + let scale = deviations + .iter() + .map(|deviation| deviation.abs()) + .fold(0.0, f64::max); + if scale == 0.0 { + return Ok(0.0); + } + + let normalized_squares: Vec<_> = deviations + .iter() + .map(|deviation| { + let normalized = *deviation / scale; + normalized * normalized + }) + .collect(); + let square_sum = deterministic_compensated_sum(normalized_squares); + let sample_variance_scale = square_sum / (deviations.len() as f64 - 1.0); + let normalized_standard_error = sample_variance_scale.sqrt() / (deviations.len() as f64).sqrt(); + let standard_error = scale * normalized_standard_error; + if !standard_error.is_finite() + || (standard_error == 0.0 && normalized_standard_error != 0.0) + { + Err(ValidationError::InvalidInput) + } else if standard_error == 0.0 { + Ok(0.0) + } else { + Ok(standard_error) + } +} + +fn scaled_standard_error(values: &[f64], mean: f64) -> Result { + let direct_deviations: Option> = values + .iter() + .map(|value| { + let deviation = *value - mean; + deviation.is_finite().then_some(deviation) + }) + .collect(); + if let Some(deviations) = direct_deviations { + return standard_error_from_deviations(&deviations); + } + + let outer_scale = values.iter().map(|value| value.abs()).fold(0.0, f64::max); + if outer_scale == 0.0 { + return Ok(0.0); + } + let normalized_values: Vec<_> = values.iter().map(|value| *value / outer_scale).collect(); + let normalized_mean = scaled_compensated_mean(&normalized_values)?; + let normalized_deviations: Vec<_> = normalized_values + .iter() + .map(|value| *value - normalized_mean) + .collect(); + let normalized_standard_error = standard_error_from_deviations(&normalized_deviations)?; + let standard_error = outer_scale * normalized_standard_error; + if !standard_error.is_finite() + || (standard_error == 0.0 && normalized_standard_error != 0.0) + { + Err(ValidationError::InvalidInput) + } else if standard_error == 0.0 { + Ok(0.0) + } else { + Ok(standard_error) + } +} + /// Mean signed bias `mean(recovered − truth)`. /// /// Signed residuals are normalized by their largest magnitude and summed with @@ -72,38 +140,22 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Result { if truth.len() < 2 { return Err(ValidationError::InvalidInput); } let diffs = signed_residuals(truth, recovered)?; let mean = scaled_compensated_mean(&diffs)?; - let mut variance_sum = 0.0_f64; - for diff in &diffs { - let delta = diff - mean; - let square = delta * delta; - if !square.is_finite() { - return Err(ValidationError::InvalidInput); - } - variance_sum += square; - if !variance_sum.is_finite() { - return Err(ValidationError::InvalidInput); - } - } - let variance = variance_sum / (diffs.len() as f64 - 1.0); - let standard_error = variance.sqrt() / (diffs.len() as f64).sqrt(); - if standard_error.is_finite() { - Ok(standard_error) - } else { - Err(ValidationError::InvalidInput) - } + scaled_standard_error(&diffs, mean) } #[cfg(test)] @@ -133,7 +185,7 @@ mod tests { Err(ValidationError::InvalidInput) ); let se_var = bias_standard_error(&[0.0, 0.0], &[1.0, -1.0]).expect("se"); - assert!(se_var > 0.0); + assert_eq!(se_var, 1.0); assert_eq!( mean_bias(&[f64::MAX], &[-f64::MAX]), Err(ValidationError::InvalidInput) @@ -167,27 +219,37 @@ mod tests { } #[test] - fn overflow_and_nonfinite_variance_intermediates_fail_closed() { - assert_eq!( - mean_bias(&[-f64::MAX], &[f64::MAX]), - Err(ValidationError::InvalidInput) - ); + fn representable_standard_error_avoids_square_and_variance_overflow() { assert_eq!( bias_standard_error(&[0.0, 0.0], &[f64::MAX, -f64::MAX]), - Err(ValidationError::InvalidInput) + Ok(f64::MAX) ); let huge = 1e200; assert_eq!( bias_standard_error(&[0.0, 0.0], &[huge, -huge]), - Err(ValidationError::InvalidInput) + Ok(huge) ); - let finite_square_overflowing_sum = 1e154; + let square_sum_overflows = 1e154; assert_eq!( - bias_standard_error( - &[0.0, 0.0], - &[finite_square_overflowing_sum, -finite_square_overflowing_sum], - ), - Err(ValidationError::InvalidInput) + bias_standard_error(&[0.0, 0.0], &[square_sum_overflows, -square_sum_overflows]), + Ok(square_sum_overflows) ); + let three_point = bias_standard_error( + &[0.0, 0.0, 0.0], + &[square_sum_overflows, -square_sum_overflows, 0.0], + ) + .expect("representable three-point standard error"); + assert_eq!(three_point, square_sum_overflows / 3.0_f64.sqrt()); + } + + #[test] + fn overflowing_direct_deviation_uses_scaled_reference() { + let standard_error = bias_standard_error( + &[0.0, 0.0, 0.0], + &[f64::MAX, -f64::MAX, -f64::MAX], + ) + .expect("scaled deviation path"); + assert!(standard_error.is_finite()); + assert!(standard_error > 0.0); } } From 8a6cc346d0b058340285c4172bc14c42c0cdbfa5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:15:38 +0900 Subject: [PATCH 008/576] test(validation): keep bias SE oracle rounding-tolerant --- .../validation_core/tests/bias_overflow_safe_mean_contract.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/tests/bias_overflow_safe_mean_contract.rs b/crates/validation_core/tests/bias_overflow_safe_mean_contract.rs index 14198996e..322644a74 100644 --- a/crates/validation_core/tests/bias_overflow_safe_mean_contract.rs +++ b/crates/validation_core/tests/bias_overflow_safe_mean_contract.rs @@ -15,5 +15,6 @@ fn representable_bias_standard_error_survives_raw_square_sum_overflow() { let recovered = [1.0e154, -1.0e154, 0.0]; let expected = 1.0e154 / 3.0_f64.sqrt(); - assert_eq!(bias_standard_error(&truth, &recovered), Ok(expected)); + let actual = bias_standard_error(&truth, &recovered).expect("representable standard error"); + assert!(((actual - expected) / expected).abs() <= f64::EPSILON); } From 28d96c2315c58db5336292e000c9f6132cff2621 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:16:10 +0900 Subject: [PATCH 009/576] test(validation): keep bias SE unit oracle rounding-tolerant --- crates/validation_core/src/bias.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 9e3f57022..c97f80bf1 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -239,7 +239,8 @@ mod tests { &[square_sum_overflows, -square_sum_overflows, 0.0], ) .expect("representable three-point standard error"); - assert_eq!(three_point, square_sum_overflows / 3.0_f64.sqrt()); + let expected = square_sum_overflows / 3.0_f64.sqrt(); + assert!(((three_point - expected) / expected).abs() <= f64::EPSILON); } #[test] From 1f22ef675d632b15378da982ace2182de3fdbab0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:16:41 +0900 Subject: [PATCH 010/576] docs(validation): trace stable bias SE arithmetic --- docs/research/task-11-recovery-metrics-foundations.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/research/task-11-recovery-metrics-foundations.md b/docs/research/task-11-recovery-metrics-foundations.md index ce8ac820a..20e61bddf 100644 --- a/docs/research/task-11-recovery-metrics-foundations.md +++ b/docs/research/task-11-recovery-metrics-foundations.md @@ -41,9 +41,11 @@ Manning, C. D., Raghavan, P., & Schütze, H. (2008). *Introduction to informatio Fresh Validation Evidence review found that the protected-main implementation formed mean signed bias by adding raw finite residuals before dividing by `n`. That makes the numerical procedure stricter than the estimand: two finite residuals equal to `f64::MAX` have a representable mean of `f64::MAX`, but their raw sum overflows. The same raw-sum dependency also made the standard error of a constant extreme bias fail even though its sampling variance is exactly zero. -RED `c5ec42e40307f3645c18b0d73114b73e01745a20` fixes this contract through the public `mean_bias` and `bias_standard_error` APIs. Causal repair `7499042f7451b2e3d5e9f83843aeea82c4f5ff06` validates each signed residual, normalizes by the largest residual magnitude, uses deterministic compensated summation, and restores scale only after dividing by the replication count. Exact cancellation is canonical `+0.0`; a represented non-zero normalized mean that becomes `0.0` only when scaled back fails closed rather than being reported as zero bias. The bias-SE sample variance remains fail-closed when a squared deviation is itself outside binary64 range; that is a separate numerical contract rather than part of this repair. +RED `c5ec42e40307f3645c18b0d73114b73e01745a20` fixes this contract through the public `mean_bias` and `bias_standard_error` APIs. Causal repair `7499042f7451b2e3d5e9f83843aeea82c4f5ff06` validates each signed residual, normalizes by the largest residual magnitude, uses deterministic compensated summation, and restores scale only after dividing by the replication count. Exact cancellation is canonical `+0.0`; a represented non-zero normalized mean that becomes `0.0` only when scaled back fails closed rather than being reported as zero bias. -This is a Validation Evidence implementation repair, not a change to the bias estimand, estimator target, or longitudinal domain semantics, so it does not require a new PRD target or ADR. Morris, White, and Crowther's ADEMP guidance treats bias as an explicitly defined simulation performance measure against known truth; implementation overflow must not silently redefine when that measure exists. IEEE/ISO/IEC 60559-2020, the active international adoption of IEEE 754-2019 as of 2026-09-03, supplies the floating-point execution model. IEEE has an active P754 revision project approved in 2024, but that project is not substituted for the published active standard. +Review of that repair exposed a second avoidable intermediate: bias SE still squared unscaled deviations and accumulated those raw squares. A final SEM can be representable even when the raw sum of squared deviations or the intermediate sample variance is not. Public RED `7de0ef90944925ae7b232a8280f5bf9096df6502` uses signed residuals `[1e154, -1e154, 0]`: the raw square sum overflows, while the intended sample SEM is finite at approximately `1e154 / sqrt(3)`. Causal repair `cad231620679d8f912bded36c654446032b45e57` scales finite deviations before squaring and forms the SEM directly, avoiding unnecessary materialization of an overflowing variance. If subtraction from the finite bias mean itself overflows, the reference falls back to a scale-normalized deviation calculation. Oracle/edge refinements `8a6cc346d0b058340285c4172bc14c42c0cdbfa5` and `28d96c2315c58db5336292e000c9f6132cff2621` retain a one-ULP-tolerant public oracle while covering constant extreme bias, `f64::MAX` opposite residuals, direct-deviation overflow, exact cancellation, and non-zero mean underflow. + +These are Validation Evidence implementation repairs, not changes to the bias estimand, estimator target, or longitudinal domain semantics, so they do not require a new PRD target or ADR. Morris, White, and Crowther's ADEMP guidance treats bias and uncertainty as explicitly defined simulation performance measures against known truth; avoidable intermediate overflow must not silently redefine whether those measures exist. IEEE/ISO/IEC 60559-2020, the active international adoption of IEEE 754-2019 as of 2026-09-03, supplies the floating-point execution model. IEEE has an active P754 revision project approved in 2024, but that project is not substituted for the published active standard. Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 @@ -53,6 +55,6 @@ IEEE Computer Society. (2020). *IEEE/ISO/IEC 60559-2020: ISO/IEC/IEEE internatio - unit oracle tests for every metric, including empty/unequal/non-finite inputs, inverted intervals, overflow RMSE, single-replication MC, and SE-aware accept/reject; - foundation recovery study unit test with known loadings, intervals, temporal order, edges, and report serialization; -- `crates/validation_core/tests/bias_overflow_safe_mean_contract.rs` exercises the representable `f64::MAX` constant-bias case through the public API; +- `crates/validation_core/tests/bias_overflow_safe_mean_contract.rs` exercises both the representable `f64::MAX` constant-bias case and a representable SEM whose predecessor raw square sum overflows through public APIs; - exact cancellation and non-zero-bias underflow are distinct contracts: cancellation remains zero, while an unrepresentable positive mean fails closed; -- workspace line and branch coverage gates must remain complete for production modules. +- exact-head hosted workspace line and branch coverage gates remain required before the Draft repair can be promoted. From 956c9c98931cb22a445f22ec674eb48d598c7d5c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:16:59 +0900 Subject: [PATCH 011/576] docs(changelog): record stable bias standard error --- CHANGELOG.d/validation-bias-overflow-safe-mean.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-bias-overflow-safe-mean.md b/CHANGELOG.d/validation-bias-overflow-safe-mean.md index 410ad4fd3..f25a5ed07 100644 --- a/CHANGELOG.d/validation-bias-overflow-safe-mean.md +++ b/CHANGELOG.d/validation-bias-overflow-safe-mean.md @@ -1,4 +1,5 @@ ### Fixed - `validation_core::mean_bias` now computes finite signed residual means with deterministic scale-normalized compensated summation, so a representable extreme bias such as two `f64::MAX` residuals is no longer rejected solely because the raw sum overflows. -- `validation_core::bias_standard_error` reuses the same stable bias mean, allowing constant extreme finite bias to retain its exact zero standard error. Exact cancellation remains zero; a mathematically non-zero bias that falls below binary64 range fails closed rather than being reported as zero. +- `validation_core::bias_standard_error` reuses the same stable bias mean and scales deviations before squaring, forming the SEM directly rather than materializing an avoidably overflowing raw square sum or sample variance. Constant extreme finite bias retains its exact zero standard error, and representable non-constant extreme cases remain measurable. +- Exact cancellation remains zero; a mathematically non-zero mean or standard error that would become false zero only because it falls below binary64 range fails closed rather than being reported as perfect recovery. From dd41ff5323cbc6aa3f2da8a0fb6af540e38c582e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:28:27 +0900 Subject: [PATCH 012/576] test(validation): expose representable RMSE square overflow --- .../tests/rmse_overflow_safe_contract.rs | 10 ++++++++++ 1 file changed, 10 insertions(+) create mode 100644 crates/validation_core/tests/rmse_overflow_safe_contract.rs diff --git a/crates/validation_core/tests/rmse_overflow_safe_contract.rs b/crates/validation_core/tests/rmse_overflow_safe_contract.rs new file mode 100644 index 000000000..9a6aaaf5d --- /dev/null +++ b/crates/validation_core/tests/rmse_overflow_safe_contract.rs @@ -0,0 +1,10 @@ +use validation_core::{rmse_standard_error, root_mean_square_error}; + +#[test] +fn representable_extreme_rmse_does_not_fail_on_squared_residual_overflow() { + let truth = [0.0, 0.0]; + let recovered = [f64::MAX, f64::MAX]; + + assert_eq!(root_mean_square_error(&truth, &recovered), Ok(f64::MAX)); + assert_eq!(rmse_standard_error(&truth, &recovered), Ok(0.0)); +} From f4e19991bfe7b83cdce767a7214193c9e53e0b2b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:29:41 +0900 Subject: [PATCH 013/576] test(validation): pin subnormal RMSE recovery boundary --- .../tests/rmse_overflow_safe_contract.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/validation_core/tests/rmse_overflow_safe_contract.rs b/crates/validation_core/tests/rmse_overflow_safe_contract.rs index 9a6aaaf5d..0b0d8a623 100644 --- a/crates/validation_core/tests/rmse_overflow_safe_contract.rs +++ b/crates/validation_core/tests/rmse_overflow_safe_contract.rs @@ -8,3 +8,14 @@ fn representable_extreme_rmse_does_not_fail_on_squared_residual_overflow() { assert_eq!(root_mean_square_error(&truth, &recovered), Ok(f64::MAX)); assert_eq!(rmse_standard_error(&truth, &recovered), Ok(0.0)); } + +#[test] +fn subnormal_rmse_preserves_representable_error_and_refuses_false_zero() { + let ulp = f64::from_bits(1); + + assert_eq!(root_mean_square_error(&[0.0, 0.0], &[ulp, 0.0]), Ok(ulp)); + assert_eq!( + root_mean_square_error(&[0.0, 0.0, 0.0, 0.0], &[ulp, 0.0, 0.0, 0.0]), + Err(validation_core::ValidationError::InvalidInput) + ); +} From 6b182107376b9b7dec66570d21a1ea6b002266f3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:30:29 +0900 Subject: [PATCH 014/576] fix(validation): scale RMSE before squaring residuals --- crates/validation_core/src/rmse.rs | 192 +++++++++++++++++++++-------- 1 file changed, 138 insertions(+), 54 deletions(-) diff --git a/crates/validation_core/src/rmse.rs b/crates/validation_core/src/rmse.rs index 52b2a8323..4bdff264e 100644 --- a/crates/validation_core/src/rmse.rs +++ b/crates/validation_core/src/rmse.rs @@ -1,37 +1,106 @@ //! Root-mean-square error recovery metric. use crate::ValidationError; -use crate::input::require_finite; use crate::matching::absolute_residuals; +fn deterministic_compensated_sum(mut values: Vec) -> f64 { + values.sort_by(f64::total_cmp); + let mut sum = 0.0_f64; + let mut correction = 0.0_f64; + for value in values { + let next = sum + value; + if sum.abs() >= value.abs() { + correction += (sum - next) + value; + } else { + correction += (value - next) + sum; + } + sum = next; + } + sum + correction +} + +struct ScaledRmse { + scale: f64, + rmse: f64, + normalized_rmse: f64, + normalized_mean_square: f64, + normalized_squares: Vec, +} + +fn scaled_rmse(residuals: &[f64]) -> Result { + if residuals.is_empty() || residuals.iter().any(|residual| !residual.is_finite()) { + return Err(ValidationError::InvalidInput); + } + + let scale = residuals + .iter() + .map(|residual| residual.abs()) + .fold(0.0, f64::max); + if scale == 0.0 { + return Ok(ScaledRmse { + scale: 0.0, + rmse: 0.0, + normalized_rmse: 0.0, + normalized_mean_square: 0.0, + normalized_squares: vec![0.0; residuals.len()], + }); + } + + let normalized_squares: Vec<_> = residuals + .iter() + .map(|residual| { + let normalized = *residual / scale; + normalized * normalized + }) + .collect(); + let normalized_mean_square = + deterministic_compensated_sum(normalized_squares.clone()) / residuals.len() as f64; + let normalized_rmse = normalized_mean_square.sqrt(); + let rmse = scale * normalized_rmse; + if !rmse.is_finite() || (rmse == 0.0 && normalized_rmse != 0.0) { + Err(ValidationError::InvalidInput) + } else { + Ok(ScaledRmse { + scale, + rmse: if rmse == 0.0 { 0.0 } else { rmse }, + normalized_rmse, + normalized_mean_square, + normalized_squares, + }) + } +} + /// Compute RMSE between truth and recovered parameter vectors. /// +/// Residuals are normalized by their largest magnitude before squaring. This +/// preserves a representable RMSE when raw residual squares would overflow or +/// underflow, while a mathematically non-zero RMSE that is itself below the +/// binary64 range fails closed rather than becoming false perfect recovery. +/// /// # Errors /// /// Returns [`ValidationError::InvalidInput`] for empty, unequal-length, -/// non-finite inputs, or squared-residual overflow. +/// non-finite inputs, an unrepresentable residual, or an unrepresentable final +/// RMSE. pub fn root_mean_square_error(truth: &[f64], recovered: &[f64]) -> Result { let residuals = absolute_residuals(truth, recovered)?; - let mut square_sum = 0.0_f64; - for residual in &residuals { - let square = residual * residual; - if !square.is_finite() { - return Err(ValidationError::InvalidInput); - } - square_sum += square; - } - require_finite((square_sum / residuals.len() as f64).sqrt()) + Ok(scaled_rmse(&residuals)?.rmse) } /// Approximate standard error of the RMSE under independent squared residuals. /// /// Uses the delta-method form `se ≈ sd(r²) / (2 · RMSE · √n)` with sample SD of -/// squared residuals. Returns `0.0` when RMSE is zero. +/// squared residuals. Squared residuals stay normalized by the largest residual +/// magnitude, so representable RMSE standard errors do not fail because an +/// avoidable raw square or squared-deviation intermediate overflows. Returns +/// `0.0` when every residual is exactly zero or the squared residuals are +/// exactly constant. /// /// # Errors /// -/// Returns [`ValidationError::InvalidInput`] for invalid pairs or when fewer -/// than two observations are present for a non-zero RMSE. +/// Returns [`ValidationError::InvalidInput`] for invalid pairs, when fewer than +/// two observations are present for a non-zero RMSE, or when a non-zero RMSE or +/// standard error is outside the representable binary64 range. pub fn rmse_standard_error(truth: &[f64], recovered: &[f64]) -> Result { let residuals = absolute_residuals(truth, recovered)?; rmse_standard_error_from_residuals(&residuals) @@ -39,36 +108,37 @@ pub fn rmse_standard_error(truth: &[f64], recovered: &[f64]) -> Result Result { - let n = residuals.len() as f64; - let mut square_sum = 0.0_f64; - let mut squares = Vec::with_capacity(residuals.len()); - for residual in residuals { - let square = residual * residual; - if !square.is_finite() { - return Err(ValidationError::InvalidInput); - } - squares.push(square); - square_sum += square; - } - let rmse = require_finite((square_sum / n).sqrt())?; - if rmse <= 0.0 { + let scaled = scaled_rmse(residuals)?; + if scaled.rmse == 0.0 { return Ok(0.0); } if residuals.len() < 2 { return Err(ValidationError::InvalidInput); } - let mean = require_finite(squares.iter().sum::() / n)?; - let mut variance_sum = 0.0_f64; - for value in &squares { - let delta = value - mean; - let square = delta * delta; - if !square.is_finite() { - return Err(ValidationError::InvalidInput); - } - variance_sum += square; + + let n = residuals.len() as f64; + let normalized_deviation_squares: Vec<_> = scaled + .normalized_squares + .iter() + .map(|value| { + let deviation = *value - scaled.normalized_mean_square; + deviation * deviation + }) + .collect(); + let normalized_sample_variance = + deterministic_compensated_sum(normalized_deviation_squares) / (n - 1.0); + let denominator = 2.0 * scaled.normalized_rmse * n.sqrt(); + let normalized_standard_error = normalized_sample_variance.sqrt() / denominator; + let standard_error = scaled.scale * normalized_standard_error; + if !standard_error.is_finite() + || (standard_error == 0.0 && normalized_standard_error != 0.0) + { + Err(ValidationError::InvalidInput) + } else if standard_error == 0.0 { + Ok(0.0) + } else { + Ok(standard_error) } - let variance = variance_sum / (n - 1.0); - require_finite(require_finite(variance.sqrt())? / (2.0 * rmse * n.sqrt())) } #[cfg(test)] @@ -82,8 +152,8 @@ mod tests { let recovered = [3.0, 4.0, 0.0]; let rmse = root_mean_square_error(&truth, &recovered).expect("ok"); assert!((rmse - (25.0_f64 / 3.0).sqrt()).abs() < 1e-12); - assert!((root_mean_square_error(&[1.0], &[1.0]).expect("zero") - 0.0).abs() < 1e-12); - assert!((rmse_standard_error(&[1.0], &[1.0]).expect("se0") - 0.0).abs() < 1e-12); + assert_eq!(root_mean_square_error(&[1.0], &[1.0]), Ok(0.0)); + assert_eq!(rmse_standard_error(&[1.0], &[1.0]), Ok(0.0)); let se = rmse_standard_error(&truth, &recovered).expect("se"); assert!(se.is_finite()); assert!(se > 0.0); @@ -100,35 +170,49 @@ mod tests { Err(ValidationError::InvalidInput) ); assert_eq!(rmse_standard_error_from_residuals(&[0.0]), Ok(0.0)); - // Squared residual overflow yields non-finite RMSE. + } + + #[test] + fn representable_extremes_avoid_raw_square_overflow() { + assert_eq!( + root_mean_square_error(&[0.0, 0.0], &[f64::MAX, f64::MAX]), + Ok(f64::MAX) + ); assert_eq!( rmse_standard_error_from_residuals(&[f64::MAX, f64::MAX]), - Err(ValidationError::InvalidInput) + Ok(0.0) ); + + let huge = 1e200; assert_eq!( - root_mean_square_error(&[0.0], &[f64::MAX]), - Err(ValidationError::InvalidInput) + rmse_standard_error_from_residuals(&[huge, huge, huge]), + Ok(0.0) ); + + let finite_se = rmse_standard_error_from_residuals(&[1e154, 0.0]) + .expect("normalized squared-residual deviations remain representable"); + assert!(finite_se.is_finite()); + assert!(finite_se > 0.0); } #[test] - fn overflow_and_nonfinite_intermediates_fail_closed() { - assert_eq!( - root_mean_square_error(&[0.0, 0.0], &[f64::MAX, f64::MAX]), - Err(ValidationError::InvalidInput) - ); + fn subnormal_rmse_distinguishes_representable_error_from_false_zero() { + let ulp = f64::from_bits(1); + assert_eq!(root_mean_square_error(&[0.0, 0.0], &[ulp, 0.0]), Ok(ulp)); assert_eq!( - rmse_standard_error_from_residuals(&[f64::MAX, f64::MAX]), + root_mean_square_error(&[0.0, 0.0, 0.0, 0.0], &[ulp, 0.0, 0.0, 0.0]), Err(ValidationError::InvalidInput) ); - let huge = 1e200; + } + + #[test] + fn unrepresentable_residual_still_fails_closed() { assert_eq!( - rmse_standard_error_from_residuals(&[huge, -huge, huge]), + root_mean_square_error(&[f64::MAX], &[-f64::MAX]), Err(ValidationError::InvalidInput) ); - // Finite residual squares whose variance deviations overflow. assert_eq!( - rmse_standard_error_from_residuals(&[1e154, 0.0]), + rmse_standard_error(&[f64::MAX, 0.0], &[-f64::MAX, 0.0]), Err(ValidationError::InvalidInput) ); } From 5e341eec1e150ca8bb670259de7b9f6eaeb61dd3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:31:41 +0900 Subject: [PATCH 015/576] docs(research): trace overflow-safe RMSE recovery --- .../task-11-recovery-metrics-foundations.md | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/docs/research/task-11-recovery-metrics-foundations.md b/docs/research/task-11-recovery-metrics-foundations.md index 20e61bddf..f723d276a 100644 --- a/docs/research/task-11-recovery-metrics-foundations.md +++ b/docs/research/task-11-recovery-metrics-foundations.md @@ -45,7 +45,17 @@ RED `c5ec42e40307f3645c18b0d73114b73e01745a20` fixes this contract through the p Review of that repair exposed a second avoidable intermediate: bias SE still squared unscaled deviations and accumulated those raw squares. A final SEM can be representable even when the raw sum of squared deviations or the intermediate sample variance is not. Public RED `7de0ef90944925ae7b232a8280f5bf9096df6502` uses signed residuals `[1e154, -1e154, 0]`: the raw square sum overflows, while the intended sample SEM is finite at approximately `1e154 / sqrt(3)`. Causal repair `cad231620679d8f912bded36c654446032b45e57` scales finite deviations before squaring and forms the SEM directly, avoiding unnecessary materialization of an overflowing variance. If subtraction from the finite bias mean itself overflows, the reference falls back to a scale-normalized deviation calculation. Oracle/edge refinements `8a6cc346d0b058340285c4172bc14c42c0cdbfa5` and `28d96c2315c58db5336292e000c9f6132cff2621` retain a one-ULP-tolerant public oracle while covering constant extreme bias, `f64::MAX` opposite residuals, direct-deviation overflow, exact cancellation, and non-zero mean underflow. -These are Validation Evidence implementation repairs, not changes to the bias estimand, estimator target, or longitudinal domain semantics, so they do not require a new PRD target or ADR. Morris, White, and Crowther's ADEMP guidance treats bias and uncertainty as explicitly defined simulation performance measures against known truth; avoidable intermediate overflow must not silently redefine whether those measures exist. IEEE/ISO/IEC 60559-2020, the active international adoption of IEEE 754-2019 as of 2026-09-03, supplies the floating-point execution model. IEEE has an active P754 revision project approved in 2024, but that project is not substituted for the published active standard. +These are Validation Evidence implementation repairs, not changes to the bias estimand, estimator target, or longitudinal domain semantics, so they do not require a new PRD target or ADR. Morris, White, and Crowther's ADEMP guidance treats bias and uncertainty as explicitly defined simulation performance measures against known truth; avoidable intermediate overflow must not silently redefine whether those measures exist. + +## 2026-09-03 RMSE arithmetic hardening + +The same review was applied independently to generic RMSE rather than copying the bias estimator. Protected-main `root_mean_square_error` squared each finite absolute residual before normalization. A residual of `f64::MAX` is itself representable, and two identical such residuals have a representable RMSE of exactly `f64::MAX`, but the predecessor rejected the result because `f64::MAX²` overflowed. The inverse boundary was also unsound: a minimum-subnormal residual can square to zero before averaging, allowing a representable non-zero RMSE to be reported as exact perfect recovery. + +Public RED `dd41ff5323cbc6aa3f2da8a0fb6af540e38c582e` requires constant `f64::MAX` residuals to produce RMSE `f64::MAX` and exact-zero RMSE SE. Boundary RED refinement `f4e19991bfe7b83cdce767a7214193c9e53e0b2b` requires one minimum-subnormal residual among two observations to preserve the representable minimum-subnormal RMSE, while one such residual among four observations fails closed because the positive real RMSE lies at the binary64 half-ULP boundary and would otherwise become false zero. + +Causal repair `6b182107376b9b7dec66570d21a1ea6b002266f3` normalizes absolute residuals by their largest finite magnitude before squaring, deterministically accumulates normalized squares, and restores scale only after taking the square root. RMSE SE uses the same normalized squared-residual domain for its sample variance and applies the residual scale once at the end. This preserves representable extreme RMSE and RMSE SE without materializing avoidably overflowing raw squares or squared deviations. Exact all-zero residuals remain exact zero; a non-zero normalized RMSE or RMSE SE that scales below binary64 range fails closed instead of becoming perfect recovery. + +This is Validation Evidence numerical execution policy for TEPP's existing recovery metric, not a new psychometric estimator or a Longitudinal Modeling primitive. IEEE/ISO/IEC 60559-2020 remains the published active international adoption of IEEE 754-2019 as of 2026-09-03; IEEE P754 is an active revision project, not a replacement published standard. Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 @@ -53,8 +63,10 @@ IEEE Computer Society. (2020). *IEEE/ISO/IEC 60559-2020: ISO/IEC/IEEE internatio ## Verification -- unit oracle tests for every metric, including empty/unequal/non-finite inputs, inverted intervals, overflow RMSE, single-replication MC, and SE-aware accept/reject; +- unit oracle tests for every metric, including empty/unequal/non-finite inputs, inverted intervals, extreme/subnormal RMSE, single-replication MC, and SE-aware accept/reject; - foundation recovery study unit test with known loadings, intervals, temporal order, edges, and report serialization; - `crates/validation_core/tests/bias_overflow_safe_mean_contract.rs` exercises both the representable `f64::MAX` constant-bias case and a representable SEM whose predecessor raw square sum overflows through public APIs; +- `crates/validation_core/tests/rmse_overflow_safe_contract.rs` exercises representable `f64::MAX` RMSE, exact-zero RMSE SE for constant extreme residuals, representable minimum-subnormal RMSE, and false-perfect underflow refusal through public APIs; - exact cancellation and non-zero-bias underflow are distinct contracts: cancellation remains zero, while an unrepresentable positive mean fails closed; +- exact all-zero recovery and non-zero RMSE underflow are distinct contracts: exact zero remains zero, while a positive real RMSE that would round to false zero fails closed; - exact-head hosted workspace line and branch coverage gates remain required before the Draft repair can be promoted. From 7058b9c3714ed08b32a74b3c366b439043ea3d1c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:31:52 +0900 Subject: [PATCH 016/576] docs(changelog): record overflow-safe RMSE recovery --- CHANGELOG.d/validation-rmse-overflow-safe.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CHANGELOG.d/validation-rmse-overflow-safe.md diff --git a/CHANGELOG.d/validation-rmse-overflow-safe.md b/CHANGELOG.d/validation-rmse-overflow-safe.md new file mode 100644 index 000000000..9ea06bdc8 --- /dev/null +++ b/CHANGELOG.d/validation-rmse-overflow-safe.md @@ -0,0 +1,5 @@ +### Fixed + +- `validation_core::root_mean_square_error` now normalizes finite absolute residuals before squaring, so representable extreme RMSE values such as constant `f64::MAX` residuals are no longer rejected only because an intermediate square overflows. +- Minimum-subnormal recovery error is preserved when the final RMSE remains representable. A mathematically non-zero RMSE that would round to exact zero at the final binary64 boundary fails closed rather than being reported as perfect recovery. +- `validation_core::rmse_standard_error` computes squared-residual variation in the same normalized domain and restores the residual scale only once, preserving exact zero uncertainty for constant extreme residuals and finite uncertainty when raw squared deviations would overflow. From 856393c0eea291ce958776cd275e3b34837119b7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:32:30 +0900 Subject: [PATCH 017/576] refactor(validation): centralize deterministic accumulation --- crates/validation_core/src/numeric.rs | 34 +++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 crates/validation_core/src/numeric.rs diff --git a/crates/validation_core/src/numeric.rs b/crates/validation_core/src/numeric.rs new file mode 100644 index 000000000..2b096c0ed --- /dev/null +++ b/crates/validation_core/src/numeric.rs @@ -0,0 +1,34 @@ +//! Deterministic binary64 accumulation shared by validation metrics. + +/// Sum finite values in a canonical order with Neumaier compensation. +/// +/// Callers own domain validation and any scale normalization needed to keep the +/// final sum representable. Canonical ordering keeps equivalent metric inputs +/// from changing only because transport order changed. +pub(crate) fn deterministic_compensated_sum(mut values: Vec) -> f64 { + values.sort_by(f64::total_cmp); + let mut sum = 0.0_f64; + let mut correction = 0.0_f64; + for value in values { + let next = sum + value; + if sum.abs() >= value.abs() { + correction += (sum - next) + value; + } else { + correction += (value - next) + sum; + } + sum = next; + } + sum + correction +} + +#[cfg(test)] +mod tests { + use super::deterministic_compensated_sum; + + #[test] + fn canonical_compensated_sum_is_order_stable() { + let left = deterministic_compensated_sum(vec![1.0, 1e-100, -1.0]); + let right = deterministic_compensated_sum(vec![-1.0, 1.0, 1e-100]); + assert_eq!(left.to_bits(), right.to_bits()); + } +} From cbf505018263b7e6956a2bd12a6658114e1800b0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:32:48 +0900 Subject: [PATCH 018/576] refactor(validation): register numerical support module --- crates/validation_core/src/lib.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/validation_core/src/lib.rs b/crates/validation_core/src/lib.rs index 8c4637fb7..d610c7d9c 100644 --- a/crates/validation_core/src/lib.rs +++ b/crates/validation_core/src/lib.rs @@ -20,6 +20,7 @@ mod graph_metrics; mod input; mod matching; mod monte_carlo; +mod numeric; mod report; mod rmse; mod temporal_order; From c4d1234b0f7275063f99e65008d733bc66f51ae5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:33:19 +0900 Subject: [PATCH 019/576] refactor(validation): reuse canonical accumulator for bias --- crates/validation_core/src/bias.rs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index c97f80bf1..245a625fd 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -2,6 +2,7 @@ use crate::ValidationError; use crate::input::require_paired_finite; +use crate::numeric::deterministic_compensated_sum; fn signed_residuals(truth: &[f64], recovered: &[f64]) -> Result, ValidationError> { require_paired_finite(truth, recovered)?; @@ -19,22 +20,6 @@ fn signed_residuals(truth: &[f64], recovered: &[f64]) -> Result, Valida .collect() } -fn deterministic_compensated_sum(mut values: Vec) -> f64 { - values.sort_by(f64::total_cmp); - let mut sum = 0.0_f64; - let mut correction = 0.0_f64; - for value in values { - let next = sum + value; - if sum.abs() >= value.abs() { - correction += (sum - next) + value; - } else { - correction += (value - next) + sum; - } - sum = next; - } - sum + correction -} - fn scaled_compensated_mean(values: &[f64]) -> Result { let scale = values.iter().map(|value| value.abs()).fold(0.0, f64::max); if scale == 0.0 { From 36e7b71f8c50c38fc7e34931be170bf8b73d2deb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 22:34:02 +0900 Subject: [PATCH 020/576] refactor(validation): reuse canonical accumulator for RMSE --- crates/validation_core/src/rmse.rs | 17 +---------------- 1 file changed, 1 insertion(+), 16 deletions(-) diff --git a/crates/validation_core/src/rmse.rs b/crates/validation_core/src/rmse.rs index 4bdff264e..c1a30c769 100644 --- a/crates/validation_core/src/rmse.rs +++ b/crates/validation_core/src/rmse.rs @@ -2,22 +2,7 @@ use crate::ValidationError; use crate::matching::absolute_residuals; - -fn deterministic_compensated_sum(mut values: Vec) -> f64 { - values.sort_by(f64::total_cmp); - let mut sum = 0.0_f64; - let mut correction = 0.0_f64; - for value in values { - let next = sum + value; - if sum.abs() >= value.abs() { - correction += (sum - next) + value; - } else { - correction += (value - next) + sum; - } - sum = next; - } - sum + correction -} +use crate::numeric::deterministic_compensated_sum; struct ScaledRmse { scale: f64, From b60847500e9e7f3866f446302964627de009961c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 23:01:17 +0900 Subject: [PATCH 021/576] test(validation): preserve subnormal bias after extreme cancellation --- .../bias_cancellation_subnormal_contract.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 crates/validation_core/tests/bias_cancellation_subnormal_contract.rs diff --git a/crates/validation_core/tests/bias_cancellation_subnormal_contract.rs b/crates/validation_core/tests/bias_cancellation_subnormal_contract.rs new file mode 100644 index 000000000..56def8a1a --- /dev/null +++ b/crates/validation_core/tests/bias_cancellation_subnormal_contract.rs @@ -0,0 +1,29 @@ +use validation_core::mean_bias; + +#[test] +fn extreme_cancellation_preserves_representable_subnormal_bias() { + let minimum_subnormal = f64::from_bits(1); + let twice_minimum_subnormal = f64::from_bits(2); + let truth = [0.0; 4]; + + let positive_bias = [ + f64::MAX, + twice_minimum_subnormal, + twice_minimum_subnormal, + -f64::MAX, + ]; + let recovered_positive = mean_bias(&truth, &positive_bias).expect("representable positive bias"); + assert_eq!(recovered_positive.to_bits(), minimum_subnormal.to_bits()); + + let negative_bias = [ + -f64::MAX, + -twice_minimum_subnormal, + -twice_minimum_subnormal, + f64::MAX, + ]; + let recovered_negative = mean_bias(&truth, &negative_bias).expect("representable negative bias"); + assert_eq!( + recovered_negative.to_bits(), + (-minimum_subnormal).to_bits() + ); +} From 227921d99676a420c94df402fc29153718ae8d2c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 23:03:09 +0900 Subject: [PATCH 022/576] fix(validation): preserve mixed-sign cancellation in bias mean --- crates/validation_core/src/bias.rs | 113 +++++++++++++++++++++++++---- 1 file changed, 99 insertions(+), 14 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 245a625fd..92f0137fb 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -20,24 +20,108 @@ fn signed_residuals(truth: &[f64], recovered: &[f64]) -> Result, Valida .collect() } -fn scaled_compensated_mean(values: &[f64]) -> Result { - let scale = values.iter().map(|value| value.abs()).fold(0.0, f64::max); - if scale == 0.0 { +fn exact_power_of_two_scale(max_magnitude: f64) -> f64 { + let bits = max_magnitude.to_bits(); + let exponent = (bits >> 52) & 0x7ff; + if exponent == 0 { + let significand = bits & 0x000f_ffff_ffff_ffff; + let highest_bit = 63 - significand.leading_zeros(); + f64::from_bits(1_u64 << highest_bit) + } else { + f64::from_bits(exponent << 52) + } +} + +fn same_sign_mean_over_total(values: &[f64], total_count: usize) -> Result { + let max_magnitude = values + .iter() + .map(|value| value.abs()) + .max_by(f64::total_cmp) + .ok_or(ValidationError::InvalidInput)?; + if max_magnitude == 0.0 { return Ok(0.0); } - let normalized: Vec<_> = values.iter().map(|value| *value / scale).collect(); - let normalized_mean = deterministic_compensated_sum(normalized) / values.len() as f64; - let mean = scale * normalized_mean; - if !mean.is_finite() || (mean == 0.0 && normalized_mean != 0.0) { + let scale = exact_power_of_two_scale(max_magnitude); + let normalized = values.iter().map(|value| *value / scale).collect(); + let normalized_mean = deterministic_compensated_sum(normalized) / total_count as f64; + let mean = normalized_mean * scale; + if !mean.is_finite() || mean == 0.0 { Err(ValidationError::InvalidInput) - } else if mean == 0.0 { - Ok(0.0) } else { Ok(mean) } } +fn scaled_compensated_mean(values: &[f64]) -> Result { + let mut positives = Vec::new(); + let mut negatives = Vec::new(); + for &value in values { + if !value.is_finite() { + return Err(ValidationError::InvalidInput); + } + if value > 0.0 { + positives.push(value); + } else if value < 0.0 { + negatives.push(value); + } + } + + if positives.is_empty() && negatives.is_empty() { + return Ok(0.0); + } + if positives.is_empty() || negatives.is_empty() { + return same_sign_mean_over_total(values, values.len()); + } + + positives.sort_by(|left, right| right.total_cmp(left)); + negatives.sort_by(|left, right| left.total_cmp(right)); + + let mut positive_index = 0_usize; + let mut negative_index = 0_usize; + let mut positive = positives[0]; + let mut negative = negatives[0]; + let mut residuals = Vec::with_capacity(values.len()); + + loop { + let residual = positive + negative; + if residual > 0.0 { + positive = residual; + negative_index += 1; + if negative_index == negatives.len() { + residuals.push(positive); + residuals.extend_from_slice(&positives[positive_index + 1..]); + break; + } + negative = negatives[negative_index]; + } else if residual < 0.0 { + negative = residual; + positive_index += 1; + if positive_index == positives.len() { + residuals.push(negative); + residuals.extend_from_slice(&negatives[negative_index + 1..]); + break; + } + positive = positives[positive_index]; + } else { + positive_index += 1; + negative_index += 1; + if positive_index == positives.len() || negative_index == negatives.len() { + residuals.extend_from_slice(&positives[positive_index..]); + residuals.extend_from_slice(&negatives[negative_index..]); + break; + } + positive = positives[positive_index]; + negative = negatives[negative_index]; + } + } + + if residuals.is_empty() { + return Ok(0.0); + } + same_sign_mean_over_total(&residuals, values.len()) +} + fn standard_error_from_deviations(deviations: &[f64]) -> Result { let scale = deviations .iter() @@ -106,11 +190,12 @@ fn scaled_standard_error(values: &[f64], mean: f64) -> Result Date: Thu, 3 Sep 2026 23:05:46 +0900 Subject: [PATCH 023/576] test(validation): harden mixed-sign bias cancellation paths --- .../bias_cancellation_subnormal_contract.rs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) diff --git a/crates/validation_core/tests/bias_cancellation_subnormal_contract.rs b/crates/validation_core/tests/bias_cancellation_subnormal_contract.rs index 56def8a1a..4559e023b 100644 --- a/crates/validation_core/tests/bias_cancellation_subnormal_contract.rs +++ b/crates/validation_core/tests/bias_cancellation_subnormal_contract.rs @@ -27,3 +27,34 @@ fn extreme_cancellation_preserves_representable_subnormal_bias() { (-minimum_subnormal).to_bits() ); } + +#[test] +fn mixed_sign_bias_is_canonical_under_transport_permutation() { + let truth = [0.0; 3]; + let first = [3.0, -1.0, -1.0]; + let permuted = [-1.0, 3.0, -1.0]; + let expected = 1.0 / 3.0; + + let first_bias = mean_bias(&truth, &first).expect("first bias"); + let permuted_bias = mean_bias(&truth, &permuted).expect("permuted bias"); + assert_eq!(first_bias.to_bits(), expected.to_bits()); + assert_eq!(permuted_bias.to_bits(), first_bias.to_bits()); + + let negative = [-3.0, 1.0, 1.0]; + let negative_bias = mean_bias(&truth, &negative).expect("negative bias"); + assert_eq!(negative_bias.to_bits(), (-expected).to_bits()); +} + +#[test] +fn full_range_exact_cancellation_remains_exact_zero() { + let minimum_subnormal = f64::from_bits(1); + let truth = [0.0; 4]; + let recovered = [ + f64::MAX, + minimum_subnormal, + -f64::MAX, + -minimum_subnormal, + ]; + + assert_eq!(mean_bias(&truth, &recovered), Ok(0.0)); +} From da0953d96a4cbfd328c2b03b7da8516417725357 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 23:06:06 +0900 Subject: [PATCH 024/576] docs(validation): record cancellation-safe bias recovery --- CHANGELOG.d/validation-bias-overflow-safe-mean.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-bias-overflow-safe-mean.md b/CHANGELOG.d/validation-bias-overflow-safe-mean.md index f25a5ed07..6aea47d11 100644 --- a/CHANGELOG.d/validation-bias-overflow-safe-mean.md +++ b/CHANGELOG.d/validation-bias-overflow-safe-mean.md @@ -1,5 +1,5 @@ ### Fixed -- `validation_core::mean_bias` now computes finite signed residual means with deterministic scale-normalized compensated summation, so a representable extreme bias such as two `f64::MAX` residuals is no longer rejected solely because the raw sum overflows. +- `validation_core::mean_bias` now computes finite signed residual means without allowing either raw-sum overflow or largest-scale normalization to erase a representable low-order bias after extreme mixed-sign cancellation. Opposite signs cancel at represented magnitude first; the remaining one-sign mass is power-of-two normalized and divided by the original recovery denominator before scale restoration. - `validation_core::bias_standard_error` reuses the same stable bias mean and scales deviations before squaring, forming the SEM directly rather than materializing an avoidably overflowing raw square sum or sample variance. Constant extreme finite bias retains its exact zero standard error, and representable non-constant extreme cases remain measurable. - Exact cancellation remains zero; a mathematically non-zero mean or standard error that would become false zero only because it falls below binary64 range fails closed rather than being reported as perfect recovery. From b5cef5eb72311f11b9d9ed16b2e2a1f9957370b7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 23:06:42 +0900 Subject: [PATCH 025/576] docs(research): trace full-range bias cancellation repair --- docs/research/task-11-recovery-metrics-foundations.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/research/task-11-recovery-metrics-foundations.md b/docs/research/task-11-recovery-metrics-foundations.md index f723d276a..b7391b1c3 100644 --- a/docs/research/task-11-recovery-metrics-foundations.md +++ b/docs/research/task-11-recovery-metrics-foundations.md @@ -43,9 +43,11 @@ Fresh Validation Evidence review found that the protected-main implementation fo RED `c5ec42e40307f3645c18b0d73114b73e01745a20` fixes this contract through the public `mean_bias` and `bias_standard_error` APIs. Causal repair `7499042f7451b2e3d5e9f83843aeea82c4f5ff06` validates each signed residual, normalizes by the largest residual magnitude, uses deterministic compensated summation, and restores scale only after dividing by the replication count. Exact cancellation is canonical `+0.0`; a represented non-zero normalized mean that becomes `0.0` only when scaled back fails closed rather than being reported as zero bias. -Review of that repair exposed a second avoidable intermediate: bias SE still squared unscaled deviations and accumulated those raw squares. A final SEM can be representable even when the raw sum of squared deviations or the intermediate sample variance is not. Public RED `7de0ef90944925ae7b232a8280f5bf9096df6502` uses signed residuals `[1e154, -1e154, 0]`: the raw square sum overflows, while the intended sample SEM is finite at approximately `1e154 / sqrt(3)`. Causal repair `cad231620679d8f912bded36c654446032b45e57` scales finite deviations before squaring and forms the SEM directly, avoiding unnecessary materialization of an overflowing variance. If subtraction from the finite bias mean itself overflows, the reference falls back to a scale-normalized deviation calculation. Oracle/edge refinements `8a6cc346d0b058340285c4172bc14c42c0cdbfa5` and `28d96c2315c58db5336292e000c9f6132cff2621` retain a one-ULP-tolerant public oracle while covering constant extreme bias, `f64::MAX` opposite residuals, direct-deviation overflow, exact cancellation, and non-zero mean underflow. +A subsequent full-range cancellation review found that largest-magnitude normalization itself was not sufficient for mixed signs. With minimum-subnormal unit `u = f64::from_bits(1)`, residuals `[f64::MAX, 2u, 2u, -f64::MAX]` have exact sum `4u` and exact mean `u`, which is representable. The predecessor divided both `2u` residuals by `f64::MAX` before the opposing extremes cancelled, turning those terms into zero and returning false zero bias. Public RED `b60847500e9e7f3866f446302964627de009961c` fixes this represented-endpoint contract. Causal repair `227921d99676a420c94df402fc29153718ae8d2c` partitions finite residuals by sign, cancels opposite signs at their represented magnitudes before any scale reduction, and then applies an exact power-of-two normalization to the remaining one-sign mass while retaining the original recovery count as denominator. Contract hardening `77dcf38c9d356b0c72454b335944f5bdb2be52e3` adds the negative mirror, mixed-sign transport permutation, and full-range exact-cancellation cases. This distinguishes a representable non-zero bias after extreme cancellation from both genuine exact cancellation and a positive real mean below binary64 range. -These are Validation Evidence implementation repairs, not changes to the bias estimand, estimator target, or longitudinal domain semantics, so they do not require a new PRD target or ADR. Morris, White, and Crowther's ADEMP guidance treats bias and uncertainty as explicitly defined simulation performance measures against known truth; avoidable intermediate overflow must not silently redefine whether those measures exist. +Review of the initial overflow repair also exposed a second avoidable intermediate: bias SE still squared unscaled deviations and accumulated those raw squares. A final SEM can be representable even when the raw sum of squared deviations or the intermediate sample variance is not. Public RED `7de0ef90944925ae7b232a8280f5bf9096df6502` uses signed residuals `[1e154, -1e154, 0]`: the raw square sum overflows, while the intended sample SEM is finite at approximately `1e154 / sqrt(3)`. Causal repair `cad231620679d8f912bded36c654446032b45e57` scales finite deviations before squaring and forms the SEM directly, avoiding unnecessary materialization of an overflowing variance. If subtraction from the finite bias mean itself overflows, the reference falls back to a scale-normalized deviation calculation. Oracle/edge refinements `8a6cc346d0b058340285c4172bc14c42c0cdbfa5` and `28d96c2315c58db5336292e000c9f6132cff2621` retain a one-ULP-tolerant public oracle while covering constant extreme bias, `f64::MAX` opposite residuals, direct-deviation overflow, exact cancellation, and non-zero mean underflow. + +These are Validation Evidence implementation repairs, not changes to the bias estimand, estimator target, or longitudinal domain semantics, so they do not require a new PRD target or ADR. Morris, White, and Crowther's ADEMP guidance treats bias and uncertainty as explicitly defined simulation performance measures against known truth; avoidable intermediate overflow or scale erasure must not silently redefine whether those measures exist. ## 2026-09-03 RMSE arithmetic hardening @@ -66,6 +68,7 @@ IEEE Computer Society. (2020). *IEEE/ISO/IEC 60559-2020: ISO/IEC/IEEE internatio - unit oracle tests for every metric, including empty/unequal/non-finite inputs, inverted intervals, extreme/subnormal RMSE, single-replication MC, and SE-aware accept/reject; - foundation recovery study unit test with known loadings, intervals, temporal order, edges, and report serialization; - `crates/validation_core/tests/bias_overflow_safe_mean_contract.rs` exercises both the representable `f64::MAX` constant-bias case and a representable SEM whose predecessor raw square sum overflows through public APIs; +- `crates/validation_core/tests/bias_cancellation_subnormal_contract.rs` exercises positive and negative minimum-subnormal mean bias after `f64::MAX` cancellation, mixed-sign input permutation, and full-range exact cancellation through the public `mean_bias` API; - `crates/validation_core/tests/rmse_overflow_safe_contract.rs` exercises representable `f64::MAX` RMSE, exact-zero RMSE SE for constant extreme residuals, representable minimum-subnormal RMSE, and false-perfect underflow refusal through public APIs; - exact cancellation and non-zero-bias underflow are distinct contracts: cancellation remains zero, while an unrepresentable positive mean fails closed; - exact all-zero recovery and non-zero RMSE underflow are distinct contracts: exact zero remains zero, while a positive real RMSE that would round to false zero fails closed; From 41faba0d184e9f0a3067f6df74c74f74798fa839 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 23:14:16 +0900 Subject: [PATCH 026/576] test(validation): preserve representable Monte Carlo moments --- .../monte_carlo_extreme_moments_contract.rs | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 crates/validation_core/tests/monte_carlo_extreme_moments_contract.rs diff --git a/crates/validation_core/tests/monte_carlo_extreme_moments_contract.rs b/crates/validation_core/tests/monte_carlo_extreme_moments_contract.rs new file mode 100644 index 000000000..8c24caac5 --- /dev/null +++ b/crates/validation_core/tests/monte_carlo_extreme_moments_contract.rs @@ -0,0 +1,52 @@ +use validation_core::{ValidationError, summarize_replications}; + +#[test] +fn representable_extreme_symmetric_moments_survive_intermediate_overflow() { + let samples = [f64::MAX, 0.0, 0.0, -f64::MAX]; + let summary = summarize_replications(&samples, 0.0, 1.0) + .expect("representable extreme Monte Carlo moments"); + + let expected_standard_deviation = f64::MAX * (2.0_f64 / 3.0).sqrt(); + let expected_standard_error = expected_standard_deviation / 2.0; + assert_eq!(summary.mean.to_bits(), 0.0_f64.to_bits()); + assert!( + ((summary.standard_deviation - expected_standard_deviation) + / expected_standard_deviation) + .abs() + <= 2.0 * f64::EPSILON + ); + assert!( + ((summary.standard_error - expected_standard_error) / expected_standard_error).abs() + <= 2.0 * f64::EPSILON + ); + assert_eq!(summary.percentile_lower, -f64::MAX); + assert_eq!(summary.percentile_upper, f64::MAX); +} + +#[test] +fn nonzero_monte_carlo_uncertainty_cannot_collapse_to_exact_zero() { + let minimum_subnormal = f64::from_bits(1); + let samples = [ + minimum_subnormal, + -minimum_subnormal, + minimum_subnormal, + -minimum_subnormal, + minimum_subnormal, + -minimum_subnormal, + minimum_subnormal, + -minimum_subnormal, + minimum_subnormal, + -minimum_subnormal, + minimum_subnormal, + -minimum_subnormal, + minimum_subnormal, + -minimum_subnormal, + minimum_subnormal, + -minimum_subnormal, + ]; + + assert_eq!( + summarize_replications(&samples, 0.0, 1.0), + Err(ValidationError::InvalidInput) + ); +} From edaafaca68b2d85fb88f0f2fb7f1ec22618d720c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 23:16:01 +0900 Subject: [PATCH 027/576] refactor(validation): share cancellation-safe deterministic mean --- crates/validation_core/src/numeric.rs | 181 +++++++++++++++++++++++++- 1 file changed, 180 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/src/numeric.rs b/crates/validation_core/src/numeric.rs index 2b096c0ed..798bc94e5 100644 --- a/crates/validation_core/src/numeric.rs +++ b/crates/validation_core/src/numeric.rs @@ -1,5 +1,7 @@ //! Deterministic binary64 accumulation shared by validation metrics. +use crate::ValidationError; + /// Sum finite values in a canonical order with Neumaier compensation. /// /// Callers own domain validation and any scale normalization needed to keep the @@ -21,9 +23,120 @@ pub(crate) fn deterministic_compensated_sum(mut values: Vec) -> f64 { sum + correction } +fn exact_power_of_two_scale(max_magnitude: f64) -> f64 { + let bits = max_magnitude.to_bits(); + let exponent = (bits >> 52) & 0x7ff; + if exponent == 0 { + let significand = bits & 0x000f_ffff_ffff_ffff; + let highest_bit = 63 - significand.leading_zeros(); + f64::from_bits(1_u64 << highest_bit) + } else { + f64::from_bits(exponent << 52) + } +} + +fn same_sign_mean_over_total(values: &[f64], total_count: usize) -> Result { + let max_magnitude = values + .iter() + .map(|value| value.abs()) + .fold(0.0, f64::max); + let scale = exact_power_of_two_scale(max_magnitude); + let normalized = values.iter().map(|value| *value / scale).collect(); + let normalized_mean = deterministic_compensated_sum(normalized) / total_count as f64; + let mean = normalized_mean * scale; + if mean == 0.0 { + Err(ValidationError::InvalidInput) + } else { + Ok(mean) + } +} + +/// Deterministic mean of finite binary64 values with cancellation before scale reduction. +/// +/// Opposite signs cancel at represented magnitude before the remaining one-sign +/// mass is normalized by an exact power of two. The original sample count stays +/// in the denominator after cancellation. Exact all-zero input and exact mixed- +/// sign cancellation return canonical zero; a mathematically nonzero one-sign +/// mean that falls below binary64 range fails closed. +/// +/// # Errors +/// +/// Returns [`ValidationError::InvalidInput`] for empty or non-finite input, or +/// when a nonzero represented mass has no nonzero binary64 mean. +pub(crate) fn deterministic_representable_mean(values: &[f64]) -> Result { + if values.is_empty() || values.iter().any(|value| !value.is_finite()) { + return Err(ValidationError::InvalidInput); + } + + let mut positives = Vec::new(); + let mut negatives = Vec::new(); + for &value in values { + if value > 0.0 { + positives.push(value); + } else if value < 0.0 { + negatives.push(value); + } + } + + if positives.is_empty() && negatives.is_empty() { + return Ok(0.0); + } + if positives.is_empty() || negatives.is_empty() { + return same_sign_mean_over_total(values, values.len()); + } + + positives.sort_by(|left, right| right.total_cmp(left)); + negatives.sort_by(|left, right| left.total_cmp(right)); + + let mut positive_index = 0_usize; + let mut negative_index = 0_usize; + let mut positive = positives[0]; + let mut negative = negatives[0]; + let mut residuals = Vec::with_capacity(values.len()); + + loop { + let residual = positive + negative; + if residual > 0.0 { + positive = residual; + negative_index += 1; + if negative_index == negatives.len() { + residuals.push(positive); + residuals.extend_from_slice(&positives[positive_index + 1..]); + break; + } + negative = negatives[negative_index]; + } else if residual < 0.0 { + negative = residual; + positive_index += 1; + if positive_index == positives.len() { + residuals.push(negative); + residuals.extend_from_slice(&negatives[negative_index + 1..]); + break; + } + positive = positives[positive_index]; + } else { + positive_index += 1; + negative_index += 1; + if positive_index == positives.len() || negative_index == negatives.len() { + residuals.extend_from_slice(&positives[positive_index..]); + residuals.extend_from_slice(&negatives[negative_index..]); + break; + } + positive = positives[positive_index]; + negative = negatives[negative_index]; + } + } + + if residuals.is_empty() { + return Ok(0.0); + } + same_sign_mean_over_total(&residuals, values.len()) +} + #[cfg(test)] mod tests { - use super::deterministic_compensated_sum; + use super::{deterministic_compensated_sum, deterministic_representable_mean}; + use crate::ValidationError; #[test] fn canonical_compensated_sum_is_order_stable() { @@ -31,4 +144,70 @@ mod tests { let right = deterministic_compensated_sum(vec![-1.0, 1.0, 1e-100]); assert_eq!(left.to_bits(), right.to_bits()); } + + #[test] + fn representable_mean_preserves_full_range_cancellation() { + let minimum_subnormal = f64::from_bits(1); + let twice_minimum_subnormal = f64::from_bits(2); + let positive = [ + f64::MAX, + twice_minimum_subnormal, + twice_minimum_subnormal, + -f64::MAX, + ]; + let negative = [ + -f64::MAX, + -twice_minimum_subnormal, + -twice_minimum_subnormal, + f64::MAX, + ]; + assert_eq!( + deterministic_representable_mean(&positive) + .expect("positive") + .to_bits(), + minimum_subnormal.to_bits() + ); + assert_eq!( + deterministic_representable_mean(&negative) + .expect("negative") + .to_bits(), + (-minimum_subnormal).to_bits() + ); + assert_eq!( + deterministic_representable_mean(&[f64::MAX, -f64::MAX]), + Ok(0.0) + ); + } + + #[test] + fn representable_mean_covers_admission_and_residual_paths() { + let minimum_subnormal = f64::from_bits(1); + assert_eq!( + deterministic_representable_mean(&[]), + Err(ValidationError::InvalidInput) + ); + assert_eq!( + deterministic_representable_mean(&[f64::NAN]), + Err(ValidationError::InvalidInput) + ); + assert_eq!(deterministic_representable_mean(&[0.0, -0.0]), Ok(0.0)); + assert_eq!(deterministic_representable_mean(&[1.0, 1.0]), Ok(1.0)); + assert_eq!(deterministic_representable_mean(&[-1.0, -1.0]), Ok(-1.0)); + assert_eq!( + deterministic_representable_mean(&[minimum_subnormal, 0.0]), + Err(ValidationError::InvalidInput) + ); + assert_eq!( + deterministic_representable_mean(&[3.0, -1.0, -1.0]), + Ok(1.0 / 3.0) + ); + assert_eq!( + deterministic_representable_mean(&[-3.0, 1.0, 1.0]), + Ok(-1.0 / 3.0) + ); + assert_eq!( + deterministic_representable_mean(&[3.0, -1.0, -1.0, -1.0]), + Ok(0.0) + ); + } } From f22315c796123c75749a9de2fd6bcb744b410a97 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 23:18:17 +0900 Subject: [PATCH 028/576] refactor(validation): consume shared representable mean in bias --- crates/validation_core/src/bias.rs | 112 ++--------------------------- 1 file changed, 5 insertions(+), 107 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 92f0137fb..be579684c 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -2,7 +2,7 @@ use crate::ValidationError; use crate::input::require_paired_finite; -use crate::numeric::deterministic_compensated_sum; +use crate::numeric::{deterministic_compensated_sum, deterministic_representable_mean}; fn signed_residuals(truth: &[f64], recovered: &[f64]) -> Result, ValidationError> { require_paired_finite(truth, recovered)?; @@ -20,108 +20,6 @@ fn signed_residuals(truth: &[f64], recovered: &[f64]) -> Result, Valida .collect() } -fn exact_power_of_two_scale(max_magnitude: f64) -> f64 { - let bits = max_magnitude.to_bits(); - let exponent = (bits >> 52) & 0x7ff; - if exponent == 0 { - let significand = bits & 0x000f_ffff_ffff_ffff; - let highest_bit = 63 - significand.leading_zeros(); - f64::from_bits(1_u64 << highest_bit) - } else { - f64::from_bits(exponent << 52) - } -} - -fn same_sign_mean_over_total(values: &[f64], total_count: usize) -> Result { - let max_magnitude = values - .iter() - .map(|value| value.abs()) - .max_by(f64::total_cmp) - .ok_or(ValidationError::InvalidInput)?; - if max_magnitude == 0.0 { - return Ok(0.0); - } - - let scale = exact_power_of_two_scale(max_magnitude); - let normalized = values.iter().map(|value| *value / scale).collect(); - let normalized_mean = deterministic_compensated_sum(normalized) / total_count as f64; - let mean = normalized_mean * scale; - if !mean.is_finite() || mean == 0.0 { - Err(ValidationError::InvalidInput) - } else { - Ok(mean) - } -} - -fn scaled_compensated_mean(values: &[f64]) -> Result { - let mut positives = Vec::new(); - let mut negatives = Vec::new(); - for &value in values { - if !value.is_finite() { - return Err(ValidationError::InvalidInput); - } - if value > 0.0 { - positives.push(value); - } else if value < 0.0 { - negatives.push(value); - } - } - - if positives.is_empty() && negatives.is_empty() { - return Ok(0.0); - } - if positives.is_empty() || negatives.is_empty() { - return same_sign_mean_over_total(values, values.len()); - } - - positives.sort_by(|left, right| right.total_cmp(left)); - negatives.sort_by(|left, right| left.total_cmp(right)); - - let mut positive_index = 0_usize; - let mut negative_index = 0_usize; - let mut positive = positives[0]; - let mut negative = negatives[0]; - let mut residuals = Vec::with_capacity(values.len()); - - loop { - let residual = positive + negative; - if residual > 0.0 { - positive = residual; - negative_index += 1; - if negative_index == negatives.len() { - residuals.push(positive); - residuals.extend_from_slice(&positives[positive_index + 1..]); - break; - } - negative = negatives[negative_index]; - } else if residual < 0.0 { - negative = residual; - positive_index += 1; - if positive_index == positives.len() { - residuals.push(negative); - residuals.extend_from_slice(&negatives[negative_index + 1..]); - break; - } - positive = positives[positive_index]; - } else { - positive_index += 1; - negative_index += 1; - if positive_index == positives.len() || negative_index == negatives.len() { - residuals.extend_from_slice(&positives[positive_index..]); - residuals.extend_from_slice(&negatives[negative_index..]); - break; - } - positive = positives[positive_index]; - negative = negatives[negative_index]; - } - } - - if residuals.is_empty() { - return Ok(0.0); - } - same_sign_mean_over_total(&residuals, values.len()) -} - fn standard_error_from_deviations(deviations: &[f64]) -> Result { let scale = deviations .iter() @@ -170,7 +68,7 @@ fn scaled_standard_error(values: &[f64], mean: f64) -> Result = values.iter().map(|value| *value / outer_scale).collect(); - let normalized_mean = scaled_compensated_mean(&normalized_values)?; + let normalized_mean = deterministic_representable_mean(&normalized_values)?; let normalized_deviations: Vec<_> = normalized_values .iter() .map(|value| *value - normalized_mean) @@ -204,12 +102,12 @@ fn scaled_standard_error(values: &[f64], mean: f64) -> Result Result { let residuals = signed_residuals(truth, recovered)?; - scaled_compensated_mean(&residuals) + deterministic_representable_mean(&residuals) } /// Standard error of the mean signed bias under independent observations. /// -/// The signed-difference mean uses the same overflow-safe deterministic +/// The signed-difference mean uses the same cancellation-safe deterministic /// reference as [`mean_bias`]. Squared deviations are accumulated only after /// scaling by their largest magnitude, and the standard error is formed /// directly without materializing an avoidably overflowing raw variance. @@ -224,7 +122,7 @@ pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result Date: Thu, 3 Sep 2026 23:19:08 +0900 Subject: [PATCH 029/576] fix(validation): preserve representable Monte Carlo moments --- crates/validation_core/src/monte_carlo.rs | 113 ++++++++++++++++------ 1 file changed, 85 insertions(+), 28 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 1a4f2f345..379a6e974 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -2,6 +2,7 @@ use crate::ValidationError; use crate::input::require_finite; +use crate::numeric::{deterministic_compensated_sum, deterministic_representable_mean}; /// Summary of Monte Carlo replications for a scalar metric. #[derive(Clone, Copy, Debug, PartialEq)] @@ -52,16 +53,87 @@ impl MonteCarloSummary { } } +fn standard_deviation_from_deviations(deviations: &[f64]) -> Result { + let scale = deviations + .iter() + .map(|deviation| deviation.abs()) + .fold(0.0, f64::max); + if scale == 0.0 { + return Ok(0.0); + } + + let normalized_squares = deviations + .iter() + .map(|deviation| { + let normalized = *deviation / scale; + normalized * normalized + }) + .collect(); + let square_sum = deterministic_compensated_sum(normalized_squares); + let normalized_standard_deviation = + (square_sum / (deviations.len() as f64 - 1.0)).sqrt(); + let standard_deviation = scale * normalized_standard_deviation; + if !standard_deviation.is_finite() + || (standard_deviation == 0.0 && normalized_standard_deviation != 0.0) + { + Err(ValidationError::InvalidInput) + } else if standard_deviation == 0.0 { + Ok(0.0) + } else { + Ok(standard_deviation) + } +} + +fn scaled_sample_standard_deviation(samples: &[f64], mean: f64) -> Result { + let direct_deviations: Option> = samples + .iter() + .map(|value| { + let deviation = *value - mean; + deviation.is_finite().then_some(deviation) + }) + .collect(); + if let Some(deviations) = direct_deviations { + return standard_deviation_from_deviations(&deviations); + } + + let outer_scale = samples.iter().map(|value| value.abs()).fold(0.0, f64::max); + if outer_scale == 0.0 { + return Ok(0.0); + } + let normalized_mean = mean / outer_scale; + let normalized_deviations: Vec<_> = samples + .iter() + .map(|value| (*value / outer_scale) - normalized_mean) + .collect(); + let normalized_standard_deviation = + standard_deviation_from_deviations(&normalized_deviations)?; + let standard_deviation = outer_scale * normalized_standard_deviation; + if !standard_deviation.is_finite() + || (standard_deviation == 0.0 && normalized_standard_deviation != 0.0) + { + Err(ValidationError::InvalidInput) + } else if standard_deviation == 0.0 { + Ok(0.0) + } else { + Ok(standard_deviation) + } +} + /// Aggregate Monte Carlo metric replications with percentile bounds. /// /// Percentiles use the inclusive nearest-rank method on sorted finite samples. -/// Mean and variance use Welford accumulation so large finite samples do not -/// overflow intermediate sums. +/// Mean and sampling uncertainty use deterministic cancellation-safe/scaled +/// binary64 references so an avoidable raw sum, Welford delta product, or raw +/// square cannot reject a representable summary. A mathematically nonzero +/// standard deviation or standard error that becomes exact zero only at the +/// binary64 projection boundary fails closed rather than reporting no Monte +/// Carlo uncertainty. /// /// # Errors /// -/// Returns input errors for empty/non-finite samples or non-finite summaries, -/// and configuration errors for invalid percentile bounds. +/// Returns input errors for empty/non-finite samples, an unrepresentable mean, +/// standard deviation, standard error, or summary; and configuration errors for +/// invalid percentile bounds. /// /// # Panics /// @@ -81,18 +153,21 @@ pub fn summarize_replications( return Err(ValidationError::InvalidConfiguration); } let mut sorted = samples.to_vec(); - sorted.sort_by(|a, b| a.partial_cmp(b).unwrap_or(std::cmp::Ordering::Equal)); - let (mean, m2, count) = welford_moments(&sorted)?; - let n = count as f64; - let standard_deviation = if count == 1 { + sorted.sort_by(f64::total_cmp); + let mean = deterministic_representable_mean(&sorted)?; + let n = sorted.len() as f64; + let standard_deviation = if sorted.len() == 1 { 0.0 } else { - require_finite((m2 / (n - 1.0)).sqrt())? + scaled_sample_standard_deviation(&sorted, mean)? }; let standard_error = require_finite(standard_deviation / n.sqrt())?; + if standard_error == 0.0 && standard_deviation != 0.0 { + return Err(ValidationError::InvalidInput); + } let summary = MonteCarloSummary { replication_count: sorted.len(), - mean: require_finite(mean)?, + mean, standard_deviation, standard_error, percentile_lower: nearest_rank(&sorted, lower_percentile), @@ -140,24 +215,6 @@ pub fn accept_within_standard_errors( Ok(scaled_error.abs() <= scaled_bound) } -/// Welford one-pass mean and sum of squared deviations. -fn welford_moments(samples: &[f64]) -> Result<(f64, f64, usize), ValidationError> { - let mut mean = 0.0_f64; - let mut m2 = 0.0_f64; - let mut count = 0_usize; - for value in samples { - count += 1; - let delta = value - mean; - mean += delta / count as f64; - if !mean.is_finite() { - return Err(ValidationError::InvalidInput); - } - let delta2 = value - mean; - m2 += delta * delta2; - } - Ok((mean, m2, count)) -} - #[allow(clippy::cast_possible_truncation)] fn nearest_rank(sorted: &[f64], percentile: f64) -> f64 { if sorted.len() == 1 { From bd0343d924f6f54c3784bef4b5490de4b23e2bef Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 23:20:29 +0900 Subject: [PATCH 030/576] docs(research): trace Monte Carlo moment hardening --- .../task-11-recovery-metrics-foundations.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/docs/research/task-11-recovery-metrics-foundations.md b/docs/research/task-11-recovery-metrics-foundations.md index b7391b1c3..a1f049da4 100644 --- a/docs/research/task-11-recovery-metrics-foundations.md +++ b/docs/research/task-11-recovery-metrics-foundations.md @@ -35,7 +35,7 @@ Manning, C. D., Raghavan, P., & Schütze, H. (2008). *Introduction to informatio - **Coverage** is the closed-interval hit rate; Wilson bounds use the normal critical value `z` (for example 1.96). - **Edge precision/recall** operate on normalized undirected edge identities. - **Temporal-order accuracy** scores pairwise sign agreement, treating exact ties as a distinct class. -- **Monte Carlo** percentiles use inclusive nearest-rank on sorted finite replications. +- **Monte Carlo** percentiles use inclusive nearest-rank on sorted finite replications; mean, sample standard deviation, and standard error are deterministic CPU `f64` evidence and must remain representable whenever the final metric is representable. ## 2026-09-03 bias arithmetic hardening @@ -43,7 +43,7 @@ Fresh Validation Evidence review found that the protected-main implementation fo RED `c5ec42e40307f3645c18b0d73114b73e01745a20` fixes this contract through the public `mean_bias` and `bias_standard_error` APIs. Causal repair `7499042f7451b2e3d5e9f83843aeea82c4f5ff06` validates each signed residual, normalizes by the largest residual magnitude, uses deterministic compensated summation, and restores scale only after dividing by the replication count. Exact cancellation is canonical `+0.0`; a represented non-zero normalized mean that becomes `0.0` only when scaled back fails closed rather than being reported as zero bias. -A subsequent full-range cancellation review found that largest-magnitude normalization itself was not sufficient for mixed signs. With minimum-subnormal unit `u = f64::from_bits(1)`, residuals `[f64::MAX, 2u, 2u, -f64::MAX]` have exact sum `4u` and exact mean `u`, which is representable. The predecessor divided both `2u` residuals by `f64::MAX` before the opposing extremes cancelled, turning those terms into zero and returning false zero bias. Public RED `b60847500e9e7f3866f446302964627de009961c` fixes this represented-endpoint contract. Causal repair `227921d99676a420c94df402fc29153718ae8d2c` partitions finite residuals by sign, cancels opposite signs at their represented magnitudes before any scale reduction, and then applies an exact power-of-two normalization to the remaining one-sign mass while retaining the original recovery count as denominator. Contract hardening `77dcf38c9d356b0c72454b335944f5bdb2be52e3` adds the negative mirror, mixed-sign transport permutation, and full-range exact-cancellation cases. This distinguishes a representable non-zero bias after extreme cancellation from both genuine exact cancellation and a positive real mean below binary64 range. +A subsequent full-range cancellation review found that largest-magnitude normalization itself was not sufficient for mixed signs. With minimum-subnormal unit `u = f64::from_bits(1)`, residuals `[f64::MAX, 2u, 2u, -f64::MAX]` have exact sum `4u` and exact mean `u`, which is representable. The predecessor divided both `2u` residuals by `f64::MAX` before the opposing extremes cancelled, turning those terms into zero and returning false zero bias. Public RED `b60847500e9e7f3866f446302964627de009961c` fixes this represented-endpoint contract. Causal repair `227921d99676a420c94df402fc29153718ae8d2c` partitions finite residuals by sign, cancels opposite signs at their represented magnitudes before any scale reduction, and then applies an exact power-of-two normalization to the remaining one-sign mass while retaining the original recovery count as denominator. Contract hardening `77dcf38c9d356b0c72454b335944f5bdb2be52e3` adds the negative mirror, mixed-sign transport permutation, and full-range exact-cancellation cases. Shared-support refactor `edaafaca68b2d85fb88f0f2fb7f1ec22618d720c` moves only this deterministic representable-mean primitive into private `validation_core::numeric`, and `f22315c796123c75749a9de2fd6bcb744b410a97` makes bias consume it without moving bias-specific SEM or recovery semantics. This distinguishes a representable non-zero bias after extreme cancellation from both genuine exact cancellation and a positive real mean below binary64 range. Review of the initial overflow repair also exposed a second avoidable intermediate: bias SE still squared unscaled deviations and accumulated those raw squares. A final SEM can be representable even when the raw sum of squared deviations or the intermediate sample variance is not. Public RED `7de0ef90944925ae7b232a8280f5bf9096df6502` uses signed residuals `[1e154, -1e154, 0]`: the raw square sum overflows, while the intended sample SEM is finite at approximately `1e154 / sqrt(3)`. Causal repair `cad231620679d8f912bded36c654446032b45e57` scales finite deviations before squaring and forms the SEM directly, avoiding unnecessary materialization of an overflowing variance. If subtraction from the finite bias mean itself overflows, the reference falls back to a scale-normalized deviation calculation. Oracle/edge refinements `8a6cc346d0b058340285c4172bc14c42c0cdbfa5` and `28d96c2315c58db5336292e000c9f6132cff2621` retain a one-ULP-tolerant public oracle while covering constant extreme bias, `f64::MAX` opposite residuals, direct-deviation overflow, exact cancellation, and non-zero mean underflow. @@ -57,7 +57,15 @@ Public RED `dd41ff5323cbc6aa3f2da8a0fb6af540e38c582e` requires constant `f64::MA Causal repair `6b182107376b9b7dec66570d21a1ea6b002266f3` normalizes absolute residuals by their largest finite magnitude before squaring, deterministically accumulates normalized squares, and restores scale only after taking the square root. RMSE SE uses the same normalized squared-residual domain for its sample variance and applies the residual scale once at the end. This preserves representable extreme RMSE and RMSE SE without materializing avoidably overflowing raw squares or squared deviations. Exact all-zero residuals remain exact zero; a non-zero normalized RMSE or RMSE SE that scales below binary64 range fails closed instead of becoming perfect recovery. -This is Validation Evidence numerical execution policy for TEPP's existing recovery metric, not a new psychometric estimator or a Longitudinal Modeling primitive. IEEE/ISO/IEC 60559-2020 remains the published active international adoption of IEEE 754-2019 as of 2026-09-03; IEEE P754 is an active revision project, not a replacement published standard. +## 2026-09-03 Monte Carlo moment hardening + +Fresh review of `summarize_replications` found that sorting the replication vector did not make Welford's raw `delta * delta2` update safe across the full binary64 range. For samples `[f64::MAX, 0, 0, -f64::MAX]`, the exact mean is zero, the sample standard deviation is `f64::MAX * sqrt(2/3)`, and the standard error is half of that value; all three final moments are representable. The predecessor nevertheless overflowed a Welford squared-deviation intermediate and rejected the summary. The same raw-product path can underflow minimum-subnormal variation to an exact zero uncertainty estimate. + +Public RED `41faba0d184e9f0a3067f6df74c74f74798fa839` fixes both boundaries through `summarize_replications`: it requires the representable symmetric extreme summary to survive, and requires sixteen alternating minimum-subnormal replications to fail closed because their real sampling uncertainty is nonzero while the final standard error lies below binary64 range. Private support refactor `edaafaca68b2d85fb88f0f2fb7f1ec22618d720c` provides the same deterministic cancellation-safe represented mean used by bias. Causal Monte Carlo repair `91b4a93d160efa68942fcb8a38fddab10fb97caf` removes Welford accumulation from the public recovery path, computes deviations from the deterministic represented mean, normalizes before squaring, restores the sample-standard-deviation scale once, and rejects a nonzero standard deviation whose standard error becomes exact zero. If a finite mean makes a direct represented deviation overflow, the same deviation geometry is evaluated after outer-scale normalization. + +The shared helper is numerical support inside the Validation Evidence bounded context, not a psychometric estimator. Mean signed bias, bias SEM, Monte Carlo sample variance/SE, RMSE, and their respective denominators and refusal rules remain separately named scientific contracts. Morris, White, and Crowther's simulation-study guidance requires performance measures and their Monte Carlo uncertainty to be defined and reported explicitly; numerical intermediate behavior must not silently change whether that uncertainty is zero or unavailable. + +This is Validation Evidence numerical execution policy for TEPP's existing recovery metrics, not a new psychometric estimator or a Longitudinal Modeling primitive. IEEE/ISO/IEC 60559-2020 remains the published international adoption of IEEE 754-2019 referenced by this implementation; a later revision must not be treated as published authority until an actual replacement standard is issued. Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 @@ -70,6 +78,8 @@ IEEE Computer Society. (2020). *IEEE/ISO/IEC 60559-2020: ISO/IEC/IEEE internatio - `crates/validation_core/tests/bias_overflow_safe_mean_contract.rs` exercises both the representable `f64::MAX` constant-bias case and a representable SEM whose predecessor raw square sum overflows through public APIs; - `crates/validation_core/tests/bias_cancellation_subnormal_contract.rs` exercises positive and negative minimum-subnormal mean bias after `f64::MAX` cancellation, mixed-sign input permutation, and full-range exact cancellation through the public `mean_bias` API; - `crates/validation_core/tests/rmse_overflow_safe_contract.rs` exercises representable `f64::MAX` RMSE, exact-zero RMSE SE for constant extreme residuals, representable minimum-subnormal RMSE, and false-perfect underflow refusal through public APIs; +- `crates/validation_core/tests/monte_carlo_extreme_moments_contract.rs` exercises a representable full-range symmetric Monte Carlo mean/SD/SE and the false-zero uncertainty boundary through public `summarize_replications`; - exact cancellation and non-zero-bias underflow are distinct contracts: cancellation remains zero, while an unrepresentable positive mean fails closed; - exact all-zero recovery and non-zero RMSE underflow are distinct contracts: exact zero remains zero, while a positive real RMSE that would round to false zero fails closed; +- exact zero Monte Carlo uncertainty is admitted only for a degenerate replicated metric; nonzero replication variability whose final SE is below binary64 range is unavailable evidence, not zero uncertainty; - exact-head hosted workspace line and branch coverage gates remain required before the Draft repair can be promoted. From 0cb415cfbe38a45dacac7b829905561e8a8acd68 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Thu, 3 Sep 2026 23:20:40 +0900 Subject: [PATCH 031/576] docs(validation): record Monte Carlo moment hardening --- CHANGELOG.d/validation-monte-carlo-extreme-moments.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-monte-carlo-extreme-moments.md diff --git a/CHANGELOG.d/validation-monte-carlo-extreme-moments.md b/CHANGELOG.d/validation-monte-carlo-extreme-moments.md new file mode 100644 index 000000000..3e6c590d3 --- /dev/null +++ b/CHANGELOG.d/validation-monte-carlo-extreme-moments.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core::summarize_replications` now preserves representable Monte Carlo mean, sample standard deviation, and standard error across full-range finite replication values without relying on an overflowing Welford squared-deviation intermediate. +- Monte Carlo sampling uncertainty is scaled before squaring; a mathematically nonzero standard deviation or standard error that would become exact zero only because it falls below binary64 range fails closed instead of being reported as no simulation uncertainty. From bd8a7c8a6d93ec262f5634cec199265521308dfd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 00:32:57 +0900 Subject: [PATCH 032/576] test(validation): expose zero-multiplier scale collapse --- ...ror_acceptance_zero_multiplier_contract.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 crates/validation_core/tests/standard_error_acceptance_zero_multiplier_contract.rs diff --git a/crates/validation_core/tests/standard_error_acceptance_zero_multiplier_contract.rs b/crates/validation_core/tests/standard_error_acceptance_zero_multiplier_contract.rs new file mode 100644 index 000000000..b474715ca --- /dev/null +++ b/crates/validation_core/tests/standard_error_acceptance_zero_multiplier_contract.rs @@ -0,0 +1,22 @@ +use validation_core::accept_within_standard_errors; + +#[test] +fn zero_multiplier_requires_exact_recovery_before_scale_reduction() { + let minimum_subnormal = f64::from_bits(1); + + assert_eq!( + accept_within_standard_errors(minimum_subnormal, 0.0, f64::MAX, 0.0), + Ok(false), + "k = 0 is an exact-recovery gate; scaling by a huge SE must not erase a nonzero residual" + ); + assert_eq!( + accept_within_standard_errors(-minimum_subnormal, 0.0, f64::MAX, 0.0), + Ok(false), + "the negative mirror must remain a nonzero residual" + ); + assert_eq!( + accept_within_standard_errors(0.0, 0.0, f64::MAX, 0.0), + Ok(true), + "exact equality remains accepted when k = 0" + ); +} From 00ef2d90580e01494370e48ef68afbe4d0819ba8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 00:33:59 +0900 Subject: [PATCH 033/576] fix(validation): preserve exact zero-multiplier acceptance --- crates/validation_core/src/monte_carlo.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 379a6e974..dcc2391bd 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -179,7 +179,9 @@ pub fn summarize_replications( /// SE-aware acceptance: accept when `|estimate − target| ≤ k · se`. /// /// Comparison scales all terms by a shared finite magnitude so opposite-sign -/// extremes do not overflow both sides of the inequality to infinity. +/// extremes do not overflow both sides of the inequality to infinity. A zero +/// standard error or zero multiplier is an exact-recovery gate and is compared +/// before scale reduction so a huge SE cannot erase a nonzero residual. /// /// # Errors /// @@ -200,8 +202,8 @@ pub fn accept_within_standard_errors( if k < 0.0 || standard_error < 0.0 { return Err(ValidationError::InvalidConfiguration); } - if standard_error == 0.0 { - // Exact recovery only: zero SE admits no estimation residual. + if standard_error == 0.0 || k == 0.0 { + // Exact recovery only: a zero SE or zero multiplier admits no residual. return Ok(estimate.total_cmp(&target).is_eq()); } let scale = estimate From 27f5a6ed3b15928f2c313e115ddaf346dcf24fc3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 00:35:00 +0900 Subject: [PATCH 034/576] docs(validation): trace zero-multiplier acceptance repair --- ...andard-error-acceptance-zero-multiplier.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 docs/research/standard-error-acceptance-zero-multiplier.md diff --git a/docs/research/standard-error-acceptance-zero-multiplier.md b/docs/research/standard-error-acceptance-zero-multiplier.md new file mode 100644 index 000000000..c5b48bfe0 --- /dev/null +++ b/docs/research/standard-error-acceptance-zero-multiplier.md @@ -0,0 +1,50 @@ +# Zero-multiplier standard-error acceptance + +## Scope + +`validation_core::accept_within_standard_errors` evaluates the existing Validation Evidence rule + +`|estimate - target| <= k * standard_error`. + +This repair does not change the estimand, introduce a new acceptance rule, or move arithmetic into Longitudinal Modeling. It fixes the binary64 execution of the already-public rule when `k = 0`. + +## Finding + +For any finite nonnegative standard error, `k = 0` makes the mathematical acceptance bound exactly zero. The gate is therefore an exact-recovery comparison: a nonzero estimate-target residual must be rejected regardless of the magnitude of the supplied standard error. + +The predecessor implementation scaled `estimate`, `target`, and `standard_error` by one shared magnitude before comparing them. That protects the ordinary positive-`k` path from opposite-sign overflow, but it is not sound for the zero-multiplier boundary. With + +- `estimate = f64::from_bits(1)` (the minimum positive binary64 subnormal), +- `target = 0.0`, +- `standard_error = f64::MAX`, and +- `k = 0.0`, + +the scientific residual is nonzero and the acceptance bound is exactly zero. After predecessor scale reduction, however, `estimate / f64::MAX` rounds to `0.0`; the scaled residual and scaled bound both become zero and the gate incorrectly accepts. + +Public RED `bd8a7c8a6d93ec262f5634cec199265521308dfd` fixes this contract through `crates/validation_core/tests/standard_error_acceptance_zero_multiplier_contract.rs`, including positive and negative minimum-subnormal residuals and an exact-equality control. + +Causal repair `00ef2d90580e01494370e48ef68afbe4d0819ba8` handles `k == 0.0` in the same exact-recovery branch already used for `standard_error == 0.0`, before any scale reduction. Positive-`k` scaling, non-finite/configuration refusal, and the existing exact-comparison semantics remain unchanged. + +## Scientific and DDD boundary + +The repair belongs to Validation Evidence because it changes only execution fidelity of an acceptance predicate over already-computed estimate, target, and uncertainty values. It is not a psychometric estimator, longitudinal composition rule, or reusable static psychometric primitive, so no fast-mlsirm source or mutable sibling dependency is introduced. + +Morris, White, and Crowther treat simulation performance measures and Monte Carlo uncertainty as quantities whose definitions and evaluation rules must be explicit. Here the rule itself already defines the zero-multiplier endpoint; binary64 normalization must not silently widen that endpoint into a nonzero tolerance. + +IEEE/ISO/IEC 60559-2020 remains the floating-point authority for the binary64 projection behavior exercised by the regression. The failure is not subnormal input invalidity: it is avoidable loss of a representable nonzero input during an unnecessary scale operation after the mathematical bound is already known to be zero. + +## Traceability + +- Public API: `validation_core::accept_within_standard_errors`. +- RED: `bd8a7c8a6d93ec262f5634cec199265521308dfd`. +- Test: `crates/validation_core/tests/standard_error_acceptance_zero_multiplier_contract.rs`. +- Causal source repair: `00ef2d90580e01494370e48ef68afbe4d0819ba8`. +- Production module: `crates/validation_core/src/monte_carlo.rs`. +- Landing vehicle: PR #488. +- Required delivery evidence: exact-head Rust, documentation, security/supply-chain, owned line/branch coverage, and qualifying independent review; predecessor-head evidence does not transfer. + +## References + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +IEEE Computer Society. (2020). *IEEE/ISO/IEC 60559-2020: ISO/IEC/IEEE international standard—Floating-point arithmetic*. IEEE Standards Association. https://standards.ieee.org/ieee/60559/10226/ From 7de6847149c1928e0232f095023f8446fc2d4e70 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 00:35:15 +0900 Subject: [PATCH 035/576] docs(validation): record zero-multiplier acceptance fix --- CHANGELOG.d/validation-zero-multiplier-acceptance.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-zero-multiplier-acceptance.md diff --git a/CHANGELOG.d/validation-zero-multiplier-acceptance.md b/CHANGELOG.d/validation-zero-multiplier-acceptance.md new file mode 100644 index 000000000..abcbaba53 --- /dev/null +++ b/CHANGELOG.d/validation-zero-multiplier-acceptance.md @@ -0,0 +1,3 @@ +## Fixed + +- Validation Evidence now treats `k = 0` in the standard-error acceptance gate as exact recovery before any binary64 scale reduction, preventing a huge standard error from erasing a nonzero residual into a false acceptance. From 379e65258c5675ae9fee6d84d369803f1e8a1ae3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 00:45:51 +0900 Subject: [PATCH 036/576] test(validation): expose signed-zero exact-recovery split --- ...rd_error_acceptance_zero_multiplier_contract.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/validation_core/tests/standard_error_acceptance_zero_multiplier_contract.rs b/crates/validation_core/tests/standard_error_acceptance_zero_multiplier_contract.rs index b474715ca..682f548a8 100644 --- a/crates/validation_core/tests/standard_error_acceptance_zero_multiplier_contract.rs +++ b/crates/validation_core/tests/standard_error_acceptance_zero_multiplier_contract.rs @@ -20,3 +20,17 @@ fn zero_multiplier_requires_exact_recovery_before_scale_reduction() { "exact equality remains accepted when k = 0" ); } + +#[test] +fn exact_recovery_treats_signed_zero_as_one_numeric_value() { + assert_eq!( + accept_within_standard_errors(-0.0, 0.0, f64::MAX, 0.0), + Ok(true), + "zero multiplier must use numeric equality, not signed-zero bit identity" + ); + assert_eq!( + accept_within_standard_errors(0.0, -0.0, 0.0, 1.0), + Ok(true), + "zero-SE exact recovery must not split +0.0 and -0.0 into different scientific values" + ); +} From 55876e60b5ae553bcc4c1c41a793861b3d7e9cc8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 00:47:33 +0900 Subject: [PATCH 037/576] fix(validation): unify signed-zero exact recovery --- crates/validation_core/src/monte_carlo.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index dcc2391bd..88a432676 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -181,7 +181,9 @@ pub fn summarize_replications( /// Comparison scales all terms by a shared finite magnitude so opposite-sign /// extremes do not overflow both sides of the inequality to infinity. A zero /// standard error or zero multiplier is an exact-recovery gate and is compared -/// before scale reduction so a huge SE cannot erase a nonzero residual. +/// before scale reduction so a huge SE cannot erase a nonzero residual. Exact +/// recovery uses numeric equality, for which IEEE `-0.0` and `+0.0` denote the +/// same zero-valued scientific result. /// /// # Errors /// @@ -203,8 +205,8 @@ pub fn accept_within_standard_errors( return Err(ValidationError::InvalidConfiguration); } if standard_error == 0.0 || k == 0.0 { - // Exact recovery only: a zero SE or zero multiplier admits no residual. - return Ok(estimate.total_cmp(&target).is_eq()); + // Exact recovery is numerical equality; signed zero is one zero value. + return Ok(estimate == target); } let scale = estimate .abs() From c45a76cfe78f3b6eb6b9854648c769d86d371c0f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 00:48:21 +0900 Subject: [PATCH 038/576] docs(validation): trace signed-zero exact recovery --- ...andard-error-acceptance-zero-multiplier.md | 26 +++++++++++++------ 1 file changed, 18 insertions(+), 8 deletions(-) diff --git a/docs/research/standard-error-acceptance-zero-multiplier.md b/docs/research/standard-error-acceptance-zero-multiplier.md index c5b48bfe0..ae0b777c1 100644 --- a/docs/research/standard-error-acceptance-zero-multiplier.md +++ b/docs/research/standard-error-acceptance-zero-multiplier.md @@ -1,4 +1,4 @@ -# Zero-multiplier standard-error acceptance +# Standard-error exact-recovery boundaries ## Scope @@ -6,9 +6,9 @@ `|estimate - target| <= k * standard_error`. -This repair does not change the estimand, introduce a new acceptance rule, or move arithmetic into Longitudinal Modeling. It fixes the binary64 execution of the already-public rule when `k = 0`. +These repairs do not change the estimand or introduce a new acceptance rule. They fix binary64 execution only at the exact-recovery boundary reached when `k = 0` or `standard_error = 0`. -## Finding +## Zero-multiplier scale-collapse finding For any finite nonnegative standard error, `k = 0` makes the mathematical acceptance bound exactly zero. The gate is therefore an exact-recovery comparison: a nonzero estimate-target residual must be rejected regardless of the magnitude of the supplied standard error. @@ -23,22 +23,32 @@ the scientific residual is nonzero and the acceptance bound is exactly zero. Aft Public RED `bd8a7c8a6d93ec262f5634cec199265521308dfd` fixes this contract through `crates/validation_core/tests/standard_error_acceptance_zero_multiplier_contract.rs`, including positive and negative minimum-subnormal residuals and an exact-equality control. -Causal repair `00ef2d90580e01494370e48ef68afbe4d0819ba8` handles `k == 0.0` in the same exact-recovery branch already used for `standard_error == 0.0`, before any scale reduction. Positive-`k` scaling, non-finite/configuration refusal, and the existing exact-comparison semantics remain unchanged. +Causal repair `00ef2d90580e01494370e48ef68afbe4d0819ba8` handles `k == 0.0` in the same exact-recovery branch already used for `standard_error == 0.0`, before any scale reduction. Positive-`k` scaling and non-finite/configuration refusal remain unchanged. + +## Signed-zero identity finding + +Fresh review of the exact-recovery branch found a separate semantic defect. The predecessor used `f64::total_cmp` for equality. `total_cmp` intentionally distinguishes IEEE `-0.0` from `+0.0`, but the acceptance rule is numerical: both zeros satisfy `estimate - target = 0` exactly and neither sign bit denotes a distinct recovery error. + +Public RED `379e65258c5675ae9fee6d84d369803f1e8a1ae3` requires `-0.0` versus `+0.0` to be accepted in both exact-recovery entry paths: zero multiplier with finite positive SE and zero SE with positive multiplier. Causal repair `55876e60b5ae553bcc4c1c41a793861b3d7e9cc8` replaces bit-order equality with finite numeric equality (`estimate == target`) only in the exact-recovery branch. NaN and infinities remain rejected before that comparison, and every nonzero finite residual remains non-equal. + +This is not blanket signed-zero canonicalization. The API returns a Boolean acceptance decision, so the relevant invariant is that mathematically equal finite zero-valued estimates and targets produce one decision. Private numerical paths remain free to retain signed zero where it carries diagnostic information. ## Scientific and DDD boundary The repair belongs to Validation Evidence because it changes only execution fidelity of an acceptance predicate over already-computed estimate, target, and uncertainty values. It is not a psychometric estimator, longitudinal composition rule, or reusable static psychometric primitive, so no fast-mlsirm source or mutable sibling dependency is introduced. -Morris, White, and Crowther treat simulation performance measures and Monte Carlo uncertainty as quantities whose definitions and evaluation rules must be explicit. Here the rule itself already defines the zero-multiplier endpoint; binary64 normalization must not silently widen that endpoint into a nonzero tolerance. +Morris, White, and Crowther treat simulation performance measures and Monte Carlo uncertainty as quantities whose definitions and evaluation rules must be explicit. Here the rule already defines the exact-recovery endpoint; binary64 normalization or representational ordering must not silently change that endpoint. -IEEE/ISO/IEC 60559-2020 remains the floating-point authority for the binary64 projection behavior exercised by the regression. The failure is not subnormal input invalidity: it is avoidable loss of a representable nonzero input during an unnecessary scale operation after the mathematical bound is already known to be zero. +IEEE/ISO/IEC 60559-2020 remains the floating-point authority for binary64 behavior exercised by the regressions. Minimum subnormal values are valid finite inputs, and IEEE signed zeros compare numerically equal even though total ordering distinguishes their encodings. ## Traceability - Public API: `validation_core::accept_within_standard_errors`. -- RED: `bd8a7c8a6d93ec262f5634cec199265521308dfd`. +- Scale-collapse RED: `bd8a7c8a6d93ec262f5634cec199265521308dfd`. +- Scale-collapse repair: `00ef2d90580e01494370e48ef68afbe4d0819ba8`. +- Signed-zero RED: `379e65258c5675ae9fee6d84d369803f1e8a1ae3`. +- Signed-zero causal repair: `55876e60b5ae553bcc4c1c41a793861b3d7e9cc8`. - Test: `crates/validation_core/tests/standard_error_acceptance_zero_multiplier_contract.rs`. -- Causal source repair: `00ef2d90580e01494370e48ef68afbe4d0819ba8`. - Production module: `crates/validation_core/src/monte_carlo.rs`. - Landing vehicle: PR #488. - Required delivery evidence: exact-head Rust, documentation, security/supply-chain, owned line/branch coverage, and qualifying independent review; predecessor-head evidence does not transfer. From 71380b22409c60d21d9f296dcc588bc40a272600 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 00:48:33 +0900 Subject: [PATCH 039/576] docs(validation): record signed-zero exact recovery --- CHANGELOG.d/validation-zero-multiplier-acceptance.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.d/validation-zero-multiplier-acceptance.md b/CHANGELOG.d/validation-zero-multiplier-acceptance.md index abcbaba53..14d008bfb 100644 --- a/CHANGELOG.d/validation-zero-multiplier-acceptance.md +++ b/CHANGELOG.d/validation-zero-multiplier-acceptance.md @@ -1,3 +1,4 @@ ## Fixed - Validation Evidence now treats `k = 0` in the standard-error acceptance gate as exact recovery before any binary64 scale reduction, preventing a huge standard error from erasing a nonzero residual into a false acceptance. +- Exact-recovery acceptance now uses finite numeric equality rather than IEEE total-order identity, so `-0.0` and `+0.0` are one zero-valued recovery result while every nonzero finite residual remains distinct. From f84e5918acc81ca8bf3708f3cce2004c67675b78 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 01:01:56 +0900 Subject: [PATCH 040/576] test(validation): reproduce Wilson lower-bound cancellation --- .../wilson_all_covered_extreme_z_contract.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs diff --git a/crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs b/crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs new file mode 100644 index 000000000..63f120009 --- /dev/null +++ b/crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs @@ -0,0 +1,21 @@ +use validation_core::wilson_coverage_interval; + +#[test] +fn all_covered_wilson_lower_bound_survives_finite_extreme_z() { + let truth = [0.0]; + let lower = [-1.0]; + let upper = [1.0]; + let z = 1.0e154_f64; + + let z_squared = z * z; + assert!(z_squared.is_finite()); + let expected_lower = 1.0 / (1.0 + z_squared); + assert!(expected_lower.is_finite()); + assert!(expected_lower > 0.0); + + let (actual_lower, actual_upper) = + wilson_coverage_interval(&truth, &lower, &upper, z).expect("finite Wilson interval"); + + assert_eq!(actual_lower.to_bits(), expected_lower.to_bits()); + assert_eq!(actual_upper, 1.0); +} From fe9b9c8a5b94a01cd8416efd613503569b98ac1a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 01:02:43 +0900 Subject: [PATCH 041/576] fix(validation): stabilize all-covered Wilson lower bound --- crates/validation_core/src/coverage.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index 67b42e381..bbbf2d8f5 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -39,7 +39,10 @@ pub fn interval_coverage( /// Wilson score lower/upper bounds for a binomial coverage proportion. /// /// Returns `(lower, upper)` for the empirical coverage rate at the stated -/// normal critical value `z` (for example `1.96` for nominal 95%). +/// normal critical value `z` (for example `1.96` for nominal 95%). For an +/// all-covered sample, the exact Wilson lower endpoint is evaluated as +/// `n / (n + z²)` rather than subtracting two nearly equal `O(z²)` terms. This +/// preserves a representable positive lower endpoint at large finite `z`. /// /// # Errors /// @@ -60,6 +63,9 @@ pub fn wilson_coverage_interval( if !z2.is_finite() { return Err(ValidationError::InvalidConfiguration); } + if p == 1.0 { + return Ok((n / (n + z2), 1.0)); + } let denominator = 1.0 + z2 / n; let center = p + z2 / (2.0 * n); let radical = (p * (1.0 - p) / n) + z2 / (4.0 * n * n); From ff64f9a36fe9e7c926b84bd00ee3fa29b8662784 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 01:03:24 +0900 Subject: [PATCH 042/576] docs(validation): trace Wilson endpoint stability repair --- .../wilson-coverage-extreme-endpoint.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 docs/research/wilson-coverage-extreme-endpoint.md diff --git a/docs/research/wilson-coverage-extreme-endpoint.md b/docs/research/wilson-coverage-extreme-endpoint.md new file mode 100644 index 000000000..dd486e675 --- /dev/null +++ b/docs/research/wilson-coverage-extreme-endpoint.md @@ -0,0 +1,29 @@ +# Wilson coverage endpoint stability + +## Decision + +`validation_core::wilson_coverage_interval` remains the Validation Evidence authority for a Wilson score interval around empirical interval-coverage proportions. The public estimand is unchanged. The numerical implementation must not turn a mathematically positive, binary64-representable Wilson endpoint into exact zero through cancellation in an avoidable intermediate expression. + +For an all-covered sample (`p̂ = 1`), the ordinary Wilson lower endpoint simplifies algebraically to + +`n / (n + z²)`. + +Evaluating the generic center-minus-margin form first can subtract two nearly equal `O(z²)` quantities. With one covered replication and finite `z = 1e154`, `z² = 1e308` is still finite and the exact simplified lower endpoint is approximately `1e-308`, which is representable in binary64. The predecessor generic expression rounded the numerator cancellation to exact zero and therefore reported a stronger boundary statement than the represented inputs justify. + +## RED → repair trace + +- Public RED: `f84e5918acc81ca8bf3708f3cce2004c67675b78`, `crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs`. +- Causal repair: `fe9b9c8a5b94a01cd8416efd613503569b98ac1a`, `crates/validation_core/src/coverage.rs`. +- API: `validation_core::wilson_coverage_interval`. + +The repair evaluates the exact all-covered endpoint directly as `n / (n + z²)` and returns the exact upper endpoint `1.0`. It does not change the Wilson estimand, the ordinary mixed-coverage path, interval-admission rules, or the configuration rejection for non-finite/non-positive `z` and overflowing `z²`. + +## Scientific boundary + +This is Validation Evidence execution arithmetic, not a psychometric estimator and not Longitudinal Modeling composition. A value of `z` is caller-supplied configuration; finite positive values remain admitted under the existing API contract. If future product policy restricts supported confidence levels, that is a separate configuration/PRD decision and must not be smuggled in as a numerical workaround. + +Wilson's score construction is the methodological authority for the interval form. IEEE/ISO/IEC 60559 binary floating-point semantics explain why algebraically equivalent expressions can have different endpoint behavior in finite precision; TEPP therefore uses the algebraically reduced endpoint when it preserves a representable result. + +## Reference + +Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 From 0299dda58cff0ceea2ac5d9ce0e9b5066cb092bc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 01:03:32 +0900 Subject: [PATCH 043/576] docs(validation): record Wilson endpoint repair --- CHANGELOG.d/validation-wilson-extreme-endpoint.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-wilson-extreme-endpoint.md diff --git a/CHANGELOG.d/validation-wilson-extreme-endpoint.md b/CHANGELOG.d/validation-wilson-extreme-endpoint.md new file mode 100644 index 000000000..7cc644b67 --- /dev/null +++ b/CHANGELOG.d/validation-wilson-extreme-endpoint.md @@ -0,0 +1,3 @@ +### Fixed + +- `validation_core::wilson_coverage_interval` now evaluates the all-covered Wilson lower endpoint as `n / (n + z²)` instead of subtracting nearly equal `O(z²)` center and margin terms, preserving a positive binary64-representable lower bound for large finite critical values rather than collapsing it to exact zero. From 9d45f482854037d96d5dff38964fd3844335a39b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 01:59:02 +0900 Subject: [PATCH 044/576] test(validation): expose interior Wilson cancellation --- .../wilson_interior_extreme_z_contract.rs | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 crates/validation_core/tests/wilson_interior_extreme_z_contract.rs diff --git a/crates/validation_core/tests/wilson_interior_extreme_z_contract.rs b/crates/validation_core/tests/wilson_interior_extreme_z_contract.rs new file mode 100644 index 000000000..d2240f77e --- /dev/null +++ b/crates/validation_core/tests/wilson_interior_extreme_z_contract.rs @@ -0,0 +1,30 @@ +use validation_core::wilson_coverage_interval; + +#[test] +fn interior_wilson_lower_bound_survives_finite_extreme_z() { + let truth = [0.0, 1.0]; + let lower = [-1.0, 2.0]; + let upper = [1.0, 3.0]; + let z = 1.0e154_f64; + + let n = 2.0_f64; + let p = 0.5_f64; + let z_squared = z * z; + assert!(z_squared.is_finite()); + + // Algebraically rationalized Wilson lower endpoint. This form avoids the + // nearly-equal center-minus-margin subtraction used by the predecessor. + let expected_lower = (2.0 * n * p * p / z_squared) + / (1.0 + + 2.0 * n * p / z_squared + + (1.0 + 4.0 * n * p * (1.0 - p) / z_squared).sqrt()); + assert!(expected_lower.is_finite()); + assert!(expected_lower > 0.0); + + let (actual_lower, actual_upper) = + wilson_coverage_interval(&truth, &lower, &upper, z).expect("finite Wilson interval"); + + assert_eq!(actual_lower.to_bits(), expected_lower.to_bits()); + assert!(actual_upper >= p); + assert!(actual_upper <= 1.0); +} From 4f259f6e5c98ade2e4a34125430de872f32c1589 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 02:00:05 +0900 Subject: [PATCH 045/576] fix(validation): stabilize interior Wilson lower endpoint --- crates/validation_core/src/coverage.rs | 21 ++++++++++++++++++--- 1 file changed, 18 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index bbbf2d8f5..47adeb699 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -41,8 +41,9 @@ pub fn interval_coverage( /// Returns `(lower, upper)` for the empirical coverage rate at the stated /// normal critical value `z` (for example `1.96` for nominal 95%). For an /// all-covered sample, the exact Wilson lower endpoint is evaluated as -/// `n / (n + z²)` rather than subtracting two nearly equal `O(z²)` terms. This -/// preserves a representable positive lower endpoint at large finite `z`. +/// `n / (n + z²)`. For strict-interior coverage, a rationalized equivalent is +/// used when the generic `center - margin` subtraction collapses a positive, +/// representable lower endpoint to exact zero at extreme finite `z`. /// /// # Errors /// @@ -72,7 +73,21 @@ pub fn wilson_coverage_interval( // With finite z² and coverage p in [0,1], Wilson terms remain finite. let margin = z * radical.sqrt(); // radical and z are finite and non-negative; margin/bounds stay finite in [0,1]. - let low = ((center - margin) / denominator).clamp(0.0, 1.0); + let direct_low = ((center - margin) / denominator).clamp(0.0, 1.0); + let low = if direct_low == 0.0 && p > 0.0 && z2 > 0.0 { + // Rationalize the lower root and divide through by z²: + // 2 n p² / (z² + 2 n p + z sqrt(z² + 4 n p (1-p))). + // This is algebraically the same Wilson endpoint but avoids subtracting + // nearly equal O(z²) terms. It also avoids forming the O(z²) denominator + // sum directly, which can overflow even while the lower bound is finite. + let normalized_numerator = 2.0 * n * p * p / z2; + let normalized_denominator = 1.0 + + 2.0 * n * p / z2 + + (1.0 + 4.0 * n * p * (1.0 - p) / z2).sqrt(); + (normalized_numerator / normalized_denominator).clamp(0.0, 1.0) + } else { + direct_low + }; let high = ((center + margin) / denominator).clamp(0.0, 1.0); Ok((low, high)) } From 2875ac5fe28cccbe8aab65baf0ace0d247cc52d3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 02:00:19 +0900 Subject: [PATCH 046/576] docs(validation): record interior Wilson stability repair --- CHANGELOG.d/validation-wilson-extreme-endpoint.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.d/validation-wilson-extreme-endpoint.md b/CHANGELOG.d/validation-wilson-extreme-endpoint.md index 7cc644b67..7e1c7701b 100644 --- a/CHANGELOG.d/validation-wilson-extreme-endpoint.md +++ b/CHANGELOG.d/validation-wilson-extreme-endpoint.md @@ -1,3 +1,4 @@ ### Fixed - `validation_core::wilson_coverage_interval` now evaluates the all-covered Wilson lower endpoint as `n / (n + z²)` instead of subtracting nearly equal `O(z²)` center and margin terms, preserving a positive binary64-representable lower bound for large finite critical values rather than collapsing it to exact zero. +- Strict-interior coverage now falls back to an algebraically rationalized, `z²`-normalized lower-root expression when the generic `center - margin` evaluation collapses a positive representable Wilson lower endpoint to exact zero at extreme finite critical values. The ordinary mixed-coverage path and upper endpoint remain unchanged when cancellation is not present. From 5db4888366ae4d61661c54b70fd0ab55a608dba4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 02:00:45 +0900 Subject: [PATCH 047/576] docs(research): trace interior Wilson cancellation repair --- .../wilson-coverage-extreme-endpoint.md | 25 ++++++++++++++++--- 1 file changed, 21 insertions(+), 4 deletions(-) diff --git a/docs/research/wilson-coverage-extreme-endpoint.md b/docs/research/wilson-coverage-extreme-endpoint.md index dd486e675..25a074cb8 100644 --- a/docs/research/wilson-coverage-extreme-endpoint.md +++ b/docs/research/wilson-coverage-extreme-endpoint.md @@ -10,19 +10,36 @@ For an all-covered sample (`p̂ = 1`), the ordinary Wilson lower endpoint simpli Evaluating the generic center-minus-margin form first can subtract two nearly equal `O(z²)` quantities. With one covered replication and finite `z = 1e154`, `z² = 1e308` is still finite and the exact simplified lower endpoint is approximately `1e-308`, which is representable in binary64. The predecessor generic expression rounded the numerator cancellation to exact zero and therefore reported a stronger boundary statement than the represented inputs justify. +The same defect exists away from the endpoint. For strict-interior empirical coverage `0 < p̂ < 1`, write the Wilson lower root as + +`(A - B) / (2(n + z²))`, + +where `A = z² + 2np̂` and `B = z sqrt(z² + 4np̂(1-p̂))`. Rationalizing the numerator gives the exactly equivalent form + +`2np̂² / (A + B)`. + +Dividing numerator and denominator through by `z²` avoids both the `A - B` cancellation and an avoidable `A + B` overflow: + +`(2np̂² / z²) / (1 + 2np̂ / z² + sqrt(1 + 4np̂(1-p̂) / z²))`. + +With two replications, one covered (`p̂ = 0.5`), and finite `z = 1e154`, the generic predecessor path produces an exact-zero lower endpoint because its center and margin both round to `2.5e307`. The rationalized Wilson lower endpoint is approximately `5e-309`, still representable in binary64. + ## RED → repair trace -- Public RED: `f84e5918acc81ca8bf3708f3cce2004c67675b78`, `crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs`. -- Causal repair: `fe9b9c8a5b94a01cd8416efd613503569b98ac1a`, `crates/validation_core/src/coverage.rs`. +- All-covered public RED: `f84e5918acc81ca8bf3708f3cce2004c67675b78`, `crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs`. +- All-covered causal repair: `fe9b9c8a5b94a01cd8416efd613503569b98ac1a`, `crates/validation_core/src/coverage.rs`. +- Strict-interior public RED: `9d45f482854037d96d5dff38964fd3844335a39b`, `crates/validation_core/tests/wilson_interior_extreme_z_contract.rs`. +- Strict-interior causal repair: `4f259f6e5c98ade2e4a34125430de872f32c1589`, `crates/validation_core/src/coverage.rs`. +- Release trace: `2875ac5fe28cccbe8aab65baf0ace0d247cc52d3`, `CHANGELOG.d/validation-wilson-extreme-endpoint.md`. - API: `validation_core::wilson_coverage_interval`. -The repair evaluates the exact all-covered endpoint directly as `n / (n + z²)` and returns the exact upper endpoint `1.0`. It does not change the Wilson estimand, the ordinary mixed-coverage path, interval-admission rules, or the configuration rejection for non-finite/non-positive `z` and overflowing `z²`. +The implementation keeps the ordinary generic Wilson calculation for cases where the lower endpoint remains nonzero. It uses the rationalized strict-interior lower root only when the generic path has collapsed to exact zero, so ordinary mixed-coverage results and the upper endpoint are not needlessly perturbed. Interval-admission rules and rejection of non-finite/non-positive `z` or overflowing `z²` remain unchanged. ## Scientific boundary This is Validation Evidence execution arithmetic, not a psychometric estimator and not Longitudinal Modeling composition. A value of `z` is caller-supplied configuration; finite positive values remain admitted under the existing API contract. If future product policy restricts supported confidence levels, that is a separate configuration/PRD decision and must not be smuggled in as a numerical workaround. -Wilson's score construction is the methodological authority for the interval form. IEEE/ISO/IEC 60559 binary floating-point semantics explain why algebraically equivalent expressions can have different endpoint behavior in finite precision; TEPP therefore uses the algebraically reduced endpoint when it preserves a representable result. +Wilson's score construction is the methodological authority for the interval form. IEEE/ISO/IEC 60559 binary floating-point semantics explain why algebraically equivalent expressions can have different endpoint behavior in finite precision; TEPP therefore uses algebraically equivalent forms that preserve representable scientific evidence across the admitted binary64 domain. ## Reference From c070da269aa257fc9c9fa9eae17231a51ec63b74 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 03:02:26 +0900 Subject: [PATCH 048/576] test(validation): expose false-one Wilson upper endpoint --- ...n_all_uncovered_upper_endpoint_contract.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 crates/validation_core/tests/wilson_all_uncovered_upper_endpoint_contract.rs diff --git a/crates/validation_core/tests/wilson_all_uncovered_upper_endpoint_contract.rs b/crates/validation_core/tests/wilson_all_uncovered_upper_endpoint_contract.rs new file mode 100644 index 000000000..b8b9fdecb --- /dev/null +++ b/crates/validation_core/tests/wilson_all_uncovered_upper_endpoint_contract.rs @@ -0,0 +1,23 @@ +use validation_core::wilson_coverage_interval; + +#[test] +fn all_uncovered_wilson_upper_bound_does_not_round_to_false_one() { + let truth = [0.0, 1.0]; + let lower = [1.0, 2.0]; + let upper = [2.0, 3.0]; + let z = 134_217_728.0_f64; // 2^27, so z^2 / n = 2^53 exactly for n = 2. + + let z_squared = z * z; + assert_eq!(z_squared, 18_014_398_509_481_984.0_f64); // 2^54. + + // For p-hat = 0, the Wilson upper endpoint is z^2 / (n + z^2). + // At this represented input its correctly rounded binary64 value is the + // immediate predecessor of 1.0, not the exact endpoint 1.0. + let expected_upper = f64::from_bits(1.0_f64.to_bits() - 1); + + let (actual_lower, actual_upper) = + wilson_coverage_interval(&truth, &lower, &upper, z).expect("finite Wilson interval"); + + assert_eq!(actual_lower, 0.0); + assert_eq!(actual_upper.to_bits(), expected_upper.to_bits()); +} From 344081bfd98ee9bc70a3bf8fdebc795a9090e692 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 03:03:31 +0900 Subject: [PATCH 049/576] test(validation): cover interior false-one Wilson endpoint --- ...n_all_uncovered_upper_endpoint_contract.rs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/crates/validation_core/tests/wilson_all_uncovered_upper_endpoint_contract.rs b/crates/validation_core/tests/wilson_all_uncovered_upper_endpoint_contract.rs index b8b9fdecb..2f57be054 100644 --- a/crates/validation_core/tests/wilson_all_uncovered_upper_endpoint_contract.rs +++ b/crates/validation_core/tests/wilson_all_uncovered_upper_endpoint_contract.rs @@ -21,3 +21,23 @@ fn all_uncovered_wilson_upper_bound_does_not_round_to_false_one() { assert_eq!(actual_lower, 0.0); assert_eq!(actual_upper.to_bits(), expected_upper.to_bits()); } + +#[test] +fn strict_interior_wilson_upper_bound_does_not_round_to_false_one() { + let truth = [0.0, 1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0]; + let lower = [-1.0, 2.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0]; + let upper = [1.0, 3.0, 4.0, 5.0, 6.0, 7.0, 8.0, 9.0]; + let z = 268_435_456.0_f64; // 2^28. + + // Exactly one of eight replications is covered. At this represented z, + // the exact Wilson upper endpoint still rounds to next_down(1.0), while + // the direct center-plus-margin evaluation rounds the numerator and + // denominator to the same binary64 value and produces false exact 1.0. + let expected_upper = f64::from_bits(1.0_f64.to_bits() - 1); + + let (actual_lower, actual_upper) = + wilson_coverage_interval(&truth, &lower, &upper, z).expect("finite Wilson interval"); + + assert!(actual_lower > 0.0); + assert_eq!(actual_upper.to_bits(), expected_upper.to_bits()); +} From 9a2fdd05c2994f51f6c72030fe39e695ba5a876d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 03:04:04 +0900 Subject: [PATCH 050/576] fix(validation): preserve nonunit Wilson upper endpoints --- crates/validation_core/src/coverage.rs | 36 +++++++++++++++++--------- 1 file changed, 24 insertions(+), 12 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index 47adeb699..9178e1974 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -36,6 +36,18 @@ pub fn interval_coverage( Ok(covered as f64 / truth.len() as f64) } +fn rationalized_wilson_positive_lower(n: f64, p: f64, z2: f64) -> f64 { + // Rationalize the lower root and divide through by z²: + // 2 n p² / (z² + 2 n p + z sqrt(z² + 4 n p (1-p))). + // This form avoids subtracting nearly equal O(z²) terms and avoids + // materializing the O(z²) denominator sum directly. + let normalized_numerator = 2.0 * n * p * p / z2; + let normalized_denominator = 1.0 + + 2.0 * n * p / z2 + + (1.0 + 4.0 * n * p * (1.0 - p) / z2).sqrt(); + (normalized_numerator / normalized_denominator).clamp(0.0, 1.0) +} + /// Wilson score lower/upper bounds for a binomial coverage proportion. /// /// Returns `(lower, upper)` for the empirical coverage rate at the stated @@ -43,7 +55,10 @@ pub fn interval_coverage( /// all-covered sample, the exact Wilson lower endpoint is evaluated as /// `n / (n + z²)`. For strict-interior coverage, a rationalized equivalent is /// used when the generic `center - margin` subtraction collapses a positive, -/// representable lower endpoint to exact zero at extreme finite `z`. +/// representable lower endpoint to exact zero at extreme finite `z`. The same +/// positive-lower representation is applied to the complementary uncovered +/// proportion when `center + margin` falsely rounds an upper endpoint to exact +/// one even though the represented Wilson endpoint remains below one. /// /// # Errors /// @@ -75,20 +90,17 @@ pub fn wilson_coverage_interval( // radical and z are finite and non-negative; margin/bounds stay finite in [0,1]. let direct_low = ((center - margin) / denominator).clamp(0.0, 1.0); let low = if direct_low == 0.0 && p > 0.0 && z2 > 0.0 { - // Rationalize the lower root and divide through by z²: - // 2 n p² / (z² + 2 n p + z sqrt(z² + 4 n p (1-p))). - // This is algebraically the same Wilson endpoint but avoids subtracting - // nearly equal O(z²) terms. It also avoids forming the O(z²) denominator - // sum directly, which can overflow even while the lower bound is finite. - let normalized_numerator = 2.0 * n * p * p / z2; - let normalized_denominator = 1.0 - + 2.0 * n * p / z2 - + (1.0 + 4.0 * n * p * (1.0 - p) / z2).sqrt(); - (normalized_numerator / normalized_denominator).clamp(0.0, 1.0) + rationalized_wilson_positive_lower(n, p, z2) } else { direct_low }; - let high = ((center + margin) / denominator).clamp(0.0, 1.0); + let direct_high = ((center + margin) / denominator).clamp(0.0, 1.0); + let high = if direct_high == 1.0 && p < 1.0 && z2 > 0.0 { + let uncovered_lower = rationalized_wilson_positive_lower(n, 1.0 - p, z2); + (1.0 - uncovered_lower).clamp(0.0, 1.0) + } else { + direct_high + }; Ok((low, high)) } From c68076f565822ef9dd1e7c540d966cc29bf54191 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 03:04:41 +0900 Subject: [PATCH 051/576] docs(changelog): record Wilson false-one upper repair --- CHANGELOG.d/validation-wilson-extreme-endpoint.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-wilson-extreme-endpoint.md b/CHANGELOG.d/validation-wilson-extreme-endpoint.md index 7e1c7701b..6356d41df 100644 --- a/CHANGELOG.d/validation-wilson-extreme-endpoint.md +++ b/CHANGELOG.d/validation-wilson-extreme-endpoint.md @@ -1,4 +1,5 @@ ### Fixed - `validation_core::wilson_coverage_interval` now evaluates the all-covered Wilson lower endpoint as `n / (n + z²)` instead of subtracting nearly equal `O(z²)` center and margin terms, preserving a positive binary64-representable lower bound for large finite critical values rather than collapsing it to exact zero. -- Strict-interior coverage now falls back to an algebraically rationalized, `z²`-normalized lower-root expression when the generic `center - margin` evaluation collapses a positive representable Wilson lower endpoint to exact zero at extreme finite critical values. The ordinary mixed-coverage path and upper endpoint remain unchanged when cancellation is not present. +- Strict-interior coverage now falls back to an algebraically rationalized, `z²`-normalized lower-root expression when the generic `center - margin` evaluation collapses a positive representable Wilson lower endpoint to exact zero at extreme finite critical values. +- All-uncovered and strict-interior coverage now use the same rationalized positive-lower calculation on the complementary uncovered proportion when the generic `center + margin` path falsely rounds a Wilson upper endpoint to exact `1.0`. Ordinary upper endpoints remain on the direct path when no false-one collapse occurs. From 5189b7c2bad59eee4e36f1e5ef0d40957b1d411b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 03:05:03 +0900 Subject: [PATCH 052/576] docs(research): trace Wilson false-one upper endpoint --- .../wilson-coverage-extreme-endpoint.md | 31 +++++++++++++------ 1 file changed, 22 insertions(+), 9 deletions(-) diff --git a/docs/research/wilson-coverage-extreme-endpoint.md b/docs/research/wilson-coverage-extreme-endpoint.md index 25a074cb8..fe3a4885c 100644 --- a/docs/research/wilson-coverage-extreme-endpoint.md +++ b/docs/research/wilson-coverage-extreme-endpoint.md @@ -2,7 +2,7 @@ ## Decision -`validation_core::wilson_coverage_interval` remains the Validation Evidence authority for a Wilson score interval around empirical interval-coverage proportions. The public estimand is unchanged. The numerical implementation must not turn a mathematically positive, binary64-representable Wilson endpoint into exact zero through cancellation in an avoidable intermediate expression. +`validation_core::wilson_coverage_interval` remains the Validation Evidence authority for a Wilson score interval around empirical interval-coverage proportions. The public estimand is unchanged. The numerical implementation must not turn a mathematically interior, binary64-representable Wilson endpoint into exact `0.0` or `1.0` through avoidable cancellation or rounded equality in intermediate expressions. For an all-covered sample (`p̂ = 1`), the ordinary Wilson lower endpoint simplifies algebraically to @@ -24,23 +24,36 @@ Dividing numerator and denominator through by `z²` avoids both the `A - B` canc With two replications, one covered (`p̂ = 0.5`), and finite `z = 1e154`, the generic predecessor path produces an exact-zero lower endpoint because its center and margin both round to `2.5e307`. The rationalized Wilson lower endpoint is approximately `5e-309`, still representable in binary64. +The upper endpoint has the complementary identity + +`U(p̂) = 1 - L(1 - p̂)`. + +That identity matters when the generic `center + margin` numerator and denominator round to the same binary64 value. With two uncovered replications and `z = 2^27`, `z² / n = 2^53` exactly. The predecessor evaluates the all-uncovered upper endpoint as exact `1.0`, although the represented Wilson value correctly rounds to `next_down(1.0)`. The same false-one collapse occurs for strict-interior coverage; with one covered replication out of eight and `z = 2^28`, the represented upper endpoint also rounds to `next_down(1.0)`, not `1.0`. + +The repair therefore keeps the ordinary upper calculation when it remains below one. Only when that direct path reaches exact `1.0` while uncovered mass is nonzero does TEPP evaluate the positive lower endpoint of the complementary uncovered proportion with the same rationalized form and subtract it from one. This preserves a representable nonunit endpoint without perturbing ordinary cases or redefining true boundary coverage. + ## RED → repair trace -- All-covered public RED: `f84e5918acc81ca8bf3708f3cce2004c67675b78`, `crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs`. -- All-covered causal repair: `fe9b9c8a5b94a01cd8416efd613503569b98ac1a`, `crates/validation_core/src/coverage.rs`. -- Strict-interior public RED: `9d45f482854037d96d5dff38964fd3844335a39b`, `crates/validation_core/tests/wilson_interior_extreme_z_contract.rs`. -- Strict-interior causal repair: `4f259f6e5c98ade2e4a34125430de872f32c1589`, `crates/validation_core/src/coverage.rs`. -- Release trace: `2875ac5fe28cccbe8aab65baf0ace0d247cc52d3`, `CHANGELOG.d/validation-wilson-extreme-endpoint.md`. +- All-covered lower public RED: `f84e5918acc81ca8bf3708f3cce2004c67675b78`, `crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs`. +- All-covered lower causal repair: `fe9b9c8a5b94a01cd8416efd613503569b98ac1a`, `crates/validation_core/src/coverage.rs`. +- Strict-interior lower public RED: `9d45f482854037d96d5dff38964fd3844335a39b`, `crates/validation_core/tests/wilson_interior_extreme_z_contract.rs`. +- Strict-interior lower causal repair: `4f259f6e5c98ade2e4a34125430de872f32c1589`, `crates/validation_core/src/coverage.rs`. +- All-uncovered upper public RED: `c070da269aa257fc9c9fa9eae17231a51ec63b74`, `crates/validation_core/tests/wilson_all_uncovered_upper_endpoint_contract.rs`. +- Strict-interior upper RED expansion: `344081bfd98ee9bc70a3bf8fdebc795a9090e692`, same public contract file. +- Complementary rationalized upper repair: `9a2fdd05c2994f51f6c72030fe39e695ba5a876d`, `crates/validation_core/src/coverage.rs`. +- Release trace: `c68076f565822ef9dd1e7c540d966cc29bf54191`, `CHANGELOG.d/validation-wilson-extreme-endpoint.md`. - API: `validation_core::wilson_coverage_interval`. -The implementation keeps the ordinary generic Wilson calculation for cases where the lower endpoint remains nonzero. It uses the rationalized strict-interior lower root only when the generic path has collapsed to exact zero, so ordinary mixed-coverage results and the upper endpoint are not needlessly perturbed. Interval-admission rules and rejection of non-finite/non-positive `z` or overflowing `z²` remain unchanged. +Interval-admission rules and rejection of non-finite/non-positive `z` or overflowing `z²` remain unchanged. The new path is endpoint-representation repair, not a change to nominal coverage policy. ## Scientific boundary This is Validation Evidence execution arithmetic, not a psychometric estimator and not Longitudinal Modeling composition. A value of `z` is caller-supplied configuration; finite positive values remain admitted under the existing API contract. If future product policy restricts supported confidence levels, that is a separate configuration/PRD decision and must not be smuggled in as a numerical workaround. -Wilson's score construction is the methodological authority for the interval form. IEEE/ISO/IEC 60559 binary floating-point semantics explain why algebraically equivalent expressions can have different endpoint behavior in finite precision; TEPP therefore uses algebraically equivalent forms that preserve representable scientific evidence across the admitted binary64 domain. +Wilson's score construction is the methodological authority for the interval form. IEEE/ISO/IEC 60559 binary floating-point semantics explain why algebraically equivalent expressions can have different endpoint behavior in finite precision; TEPP therefore uses algebraically equivalent forms that preserve representable scientific evidence across the admitted binary64 domain. As of 2026-09-04, IEEE/ISO/IEC 60559-2020 remains an active published floating-point standard, while IEEE P754 is an active revision project superseding IEEE 754-2019; an unpublished revision is not treated as current normative text. + +## References -## Reference +IEEE. (2020). *IEEE/ISO/IEC 60559-2020: ISO/IEC/IEEE International Standard—Floating-point arithmetic*. https://standards.ieee.org/ieee/60559/10226/ Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 From 1a24fac71569334b0c0185d013574ec5ccdcc58d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 03:57:43 +0900 Subject: [PATCH 053/576] test(validation): expose Wilson nonzero cancellation residue --- ...interior_cancellation_residual_contract.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 crates/validation_core/tests/wilson_interior_cancellation_residual_contract.rs diff --git a/crates/validation_core/tests/wilson_interior_cancellation_residual_contract.rs b/crates/validation_core/tests/wilson_interior_cancellation_residual_contract.rs new file mode 100644 index 000000000..a9da5d0a2 --- /dev/null +++ b/crates/validation_core/tests/wilson_interior_cancellation_residual_contract.rs @@ -0,0 +1,32 @@ +use validation_core::wilson_coverage_interval; + +#[test] +fn interior_wilson_lower_bound_does_not_accept_a_roundoff_residual_as_signal() { + let truth = [0.0, 1.0, 2.0]; + let lower = [-1.0, 2.0, 3.0]; + let upper = [1.0, 3.0, 4.0]; + let z = 1.0e11_f64; + + let n = 3.0_f64; + let p = 1.0 / 3.0; + let z_squared = z * z; + assert!(z_squared.is_finite()); + + // The rationalized Wilson root is the same estimand without subtracting + // nearly equal O(z²) terms. At this finite z the predecessor subtraction + // leaves a nonzero floating-point residue, so an exact-zero fallback alone + // cannot detect the cancellation error. + let expected_lower = (2.0 * n * p * p / z_squared) + / (1.0 + + 2.0 * n * p / z_squared + + (1.0 + 4.0 * n * p * (1.0 - p) / z_squared).sqrt()); + assert!(expected_lower.is_finite()); + assert!(expected_lower > 0.0); + + let (actual_lower, actual_upper) = + wilson_coverage_interval(&truth, &lower, &upper, z).expect("finite Wilson interval"); + + assert_eq!(actual_lower.to_bits(), expected_lower.to_bits()); + assert!(actual_upper >= p); + assert!(actual_upper <= 1.0); +} From f7e20ddc59399f2e014a3ac91809bb57ba73460f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 03:59:41 +0900 Subject: [PATCH 054/576] fix(validation): rationalize Wilson positive lower root --- crates/validation_core/src/coverage.rs | 60 +++++++++++++++----------- 1 file changed, 36 insertions(+), 24 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index 9178e1974..d34e1f365 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -36,16 +36,26 @@ pub fn interval_coverage( Ok(covered as f64 / truth.len() as f64) } -fn rationalized_wilson_positive_lower(n: f64, p: f64, z2: f64) -> f64 { - // Rationalize the lower root and divide through by z²: - // 2 n p² / (z² + 2 n p + z sqrt(z² + 4 n p (1-p))). - // This form avoids subtracting nearly equal O(z²) terms and avoids - // materializing the O(z²) denominator sum directly. - let normalized_numerator = 2.0 * n * p * p / z2; - let normalized_denominator = 1.0 - + 2.0 * n * p / z2 - + (1.0 + 4.0 * n * p * (1.0 - p) / z2).sqrt(); - (normalized_numerator / normalized_denominator).clamp(0.0, 1.0) +fn rationalized_wilson_positive_lower(n: f64, p: f64, z: f64, z2: f64) -> f64 { + if z2 >= 1.0 { + // Rationalize the lower root and divide through by z²: + // 2 n p² / (z² + 2 n p + z sqrt(z² + 4 n p (1-p))). + // This form avoids subtracting nearly equal O(z²) terms and avoids + // materializing the O(z²) denominator sum directly. + let normalized_numerator = 2.0 * n * p * p / z2; + let normalized_denominator = 1.0 + + 2.0 * n * p / z2 + + (1.0 + 4.0 * n * p * (1.0 - p) / z2).sqrt(); + return (normalized_numerator / normalized_denominator).clamp(0.0, 1.0); + } + + // For z² < 1, dividing through by z² can overflow even though the Wilson + // endpoint is ordinary and representable. Evaluate the same rationalized + // root on its natural scale instead. + let numerator = 2.0 * n * p * p; + let denominator = + z2 + 2.0 * n * p + z * (z2 + 4.0 * n * p * (1.0 - p)).sqrt(); + (numerator / denominator).clamp(0.0, 1.0) } /// Wilson score lower/upper bounds for a binomial coverage proportion. @@ -53,12 +63,14 @@ fn rationalized_wilson_positive_lower(n: f64, p: f64, z2: f64) -> f64 { /// Returns `(lower, upper)` for the empirical coverage rate at the stated /// normal critical value `z` (for example `1.96` for nominal 95%). For an /// all-covered sample, the exact Wilson lower endpoint is evaluated as -/// `n / (n + z²)`. For strict-interior coverage, a rationalized equivalent is -/// used when the generic `center - margin` subtraction collapses a positive, -/// representable lower endpoint to exact zero at extreme finite `z`. The same -/// positive-lower representation is applied to the complementary uncovered -/// proportion when `center + margin` falsely rounds an upper endpoint to exact -/// one even though the represented Wilson endpoint remains below one. +/// `n / (n + z²)`. For nonzero strict-interior coverage, the lower endpoint is +/// evaluated through the algebraically rationalized positive root rather than +/// `center - margin`; the implementation switches scale at `z² = 1` so the +/// stable form neither suffers large-z cancellation nor small-z division +/// overflow. The same positive-lower representation is applied to the +/// complementary uncovered proportion when `center + margin` falsely rounds an +/// upper endpoint to exact one even though the represented Wilson endpoint +/// remains below one. /// /// # Errors /// @@ -82,21 +94,21 @@ pub fn wilson_coverage_interval( if p == 1.0 { return Ok((n / (n + z2), 1.0)); } + + let low = if p > 0.0 { + rationalized_wilson_positive_lower(n, p, z, z2) + } else { + 0.0 + }; + let denominator = 1.0 + z2 / n; let center = p + z2 / (2.0 * n); let radical = (p * (1.0 - p) / n) + z2 / (4.0 * n * n); // With finite z² and coverage p in [0,1], Wilson terms remain finite. let margin = z * radical.sqrt(); - // radical and z are finite and non-negative; margin/bounds stay finite in [0,1]. - let direct_low = ((center - margin) / denominator).clamp(0.0, 1.0); - let low = if direct_low == 0.0 && p > 0.0 && z2 > 0.0 { - rationalized_wilson_positive_lower(n, p, z2) - } else { - direct_low - }; let direct_high = ((center + margin) / denominator).clamp(0.0, 1.0); let high = if direct_high == 1.0 && p < 1.0 && z2 > 0.0 { - let uncovered_lower = rationalized_wilson_positive_lower(n, 1.0 - p, z2); + let uncovered_lower = rationalized_wilson_positive_lower(n, 1.0 - p, z, z2); (1.0 - uncovered_lower).clamp(0.0, 1.0) } else { direct_high From 39e5e087cba22d6df85b9ad5901cda0194e3da93 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 04:00:01 +0900 Subject: [PATCH 055/576] test(validation): cover Wilson small-z rationalized scale --- ...interior_cancellation_residual_contract.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/crates/validation_core/tests/wilson_interior_cancellation_residual_contract.rs b/crates/validation_core/tests/wilson_interior_cancellation_residual_contract.rs index a9da5d0a2..bb687a74a 100644 --- a/crates/validation_core/tests/wilson_interior_cancellation_residual_contract.rs +++ b/crates/validation_core/tests/wilson_interior_cancellation_residual_contract.rs @@ -30,3 +30,28 @@ fn interior_wilson_lower_bound_does_not_accept_a_roundoff_residual_as_signal() { assert!(actual_upper >= p); assert!(actual_upper <= 1.0); } + +#[test] +fn rationalized_lower_root_preserves_small_z_without_dividing_by_tiny_z_squared() { + let truth = [0.0, 1.0, 2.0]; + let lower = [-1.0, 2.0, 3.0]; + let upper = [1.0, 3.0, 4.0]; + let z = 0.5_f64; + + let n = 3.0_f64; + let p = 1.0 / 3.0; + let z_squared = z * z; + assert!(z_squared < 1.0); + + let expected_lower = (2.0 * n * p * p) + / (z_squared + + 2.0 * n * p + + z * (z_squared + 4.0 * n * p * (1.0 - p)).sqrt()); + + let (actual_lower, actual_upper) = + wilson_coverage_interval(&truth, &lower, &upper, z).expect("finite Wilson interval"); + + assert_eq!(actual_lower.to_bits(), expected_lower.to_bits()); + assert!(actual_upper >= p); + assert!(actual_upper <= 1.0); +} From 2dbdf330af21ea6263fd5d03eb6cb8defa867254 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 04:00:28 +0900 Subject: [PATCH 056/576] docs(changelog): record Wilson cancellation-residue repair --- CHANGELOG.d/validation-wilson-extreme-endpoint.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.d/validation-wilson-extreme-endpoint.md b/CHANGELOG.d/validation-wilson-extreme-endpoint.md index 6356d41df..35f8a6170 100644 --- a/CHANGELOG.d/validation-wilson-extreme-endpoint.md +++ b/CHANGELOG.d/validation-wilson-extreme-endpoint.md @@ -1,5 +1,5 @@ ### Fixed - `validation_core::wilson_coverage_interval` now evaluates the all-covered Wilson lower endpoint as `n / (n + z²)` instead of subtracting nearly equal `O(z²)` center and margin terms, preserving a positive binary64-representable lower bound for large finite critical values rather than collapsing it to exact zero. -- Strict-interior coverage now falls back to an algebraically rationalized, `z²`-normalized lower-root expression when the generic `center - margin` evaluation collapses a positive representable Wilson lower endpoint to exact zero at extreme finite critical values. -- All-uncovered and strict-interior coverage now use the same rationalized positive-lower calculation on the complementary uncovered proportion when the generic `center + margin` path falsely rounds a Wilson upper endpoint to exact `1.0`. Ordinary upper endpoints remain on the direct path when no false-one collapse occurs. +- Nonzero strict-interior coverage now evaluates the Wilson lower endpoint through the algebraically rationalized positive root instead of trusting `center - margin`. This also rejects nonzero floating-point cancellation residues that can be many orders of magnitude larger than the represented Wilson endpoint even when the subtraction does not collapse to exact zero. The rationalized implementation switches between natural-scale and `z²`-normalized forms at `z² = 1` so small finite critical values do not introduce division overflow. +- All-uncovered and strict-interior coverage use the same rationalized positive-lower calculation on the complementary uncovered proportion when the generic `center + margin` path falsely rounds a Wilson upper endpoint to exact `1.0`. Ordinary upper endpoints remain on the direct path when no false-one collapse occurs. From 51558008cf611eab6280c3b8e1793c0bd9bd4553 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 04:01:02 +0900 Subject: [PATCH 057/576] docs(research): trace Wilson nonzero cancellation residue --- .../wilson-coverage-extreme-endpoint.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/research/wilson-coverage-extreme-endpoint.md b/docs/research/wilson-coverage-extreme-endpoint.md index fe3a4885c..832db0ffa 100644 --- a/docs/research/wilson-coverage-extreme-endpoint.md +++ b/docs/research/wilson-coverage-extreme-endpoint.md @@ -2,7 +2,7 @@ ## Decision -`validation_core::wilson_coverage_interval` remains the Validation Evidence authority for a Wilson score interval around empirical interval-coverage proportions. The public estimand is unchanged. The numerical implementation must not turn a mathematically interior, binary64-representable Wilson endpoint into exact `0.0` or `1.0` through avoidable cancellation or rounded equality in intermediate expressions. +`validation_core::wilson_coverage_interval` remains the Validation Evidence authority for a Wilson score interval around empirical interval-coverage proportions. The public estimand is unchanged. The numerical implementation must not turn a mathematically interior, binary64-representable Wilson endpoint into exact `0.0` or `1.0`, or substitute a floating-point cancellation residue for that endpoint, through avoidable intermediate rounding. For an all-covered sample (`p̂ = 1`), the ordinary Wilson lower endpoint simplifies algebraically to @@ -24,6 +24,14 @@ Dividing numerator and denominator through by `z²` avoids both the `A - B` canc With two replications, one covered (`p̂ = 0.5`), and finite `z = 1e154`, the generic predecessor path produces an exact-zero lower endpoint because its center and margin both round to `2.5e307`. The rationalized Wilson lower endpoint is approximately `5e-309`, still representable in binary64. +An exact-zero fallback is not sufficient. With three replications, one covered (`p̂ = 1/3`), and finite `z = 1e11`, binary64 rounds the predecessor center and margin to adjacent values whose subtraction leaves the nonzero residue `262144`. Dividing that residue by the Wilson denominator produces approximately `7.86432e-17`, while the algebraically equivalent rationalized endpoint is approximately `3.3333333333333333e-23`. The predecessor result is therefore exactly 2,359,296 times larger even though it never reaches zero and would bypass an exact-zero-only fallback. + +The repair evaluates every nonzero strict-interior lower endpoint through the rationalized positive root. For `z² >= 1`, it uses the `z²`-normalized expression above. For `z² < 1`, dividing through by `z²` can itself create avoidable overflow, so the same rationalized root is evaluated on its natural scale as + +`2np̂² / (z² + 2np̂ + z sqrt(z² + 4np̂(1-p̂)))`. + +This is one algebraic estimand with scale-aware evaluation, not a confidence-policy change. + The upper endpoint has the complementary identity `U(p̂) = 1 - L(1 - p̂)`. @@ -36,12 +44,15 @@ The repair therefore keeps the ordinary upper calculation when it remains below - All-covered lower public RED: `f84e5918acc81ca8bf3708f3cce2004c67675b78`, `crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs`. - All-covered lower causal repair: `fe9b9c8a5b94a01cd8416efd613503569b98ac1a`, `crates/validation_core/src/coverage.rs`. -- Strict-interior lower public RED: `9d45f482854037d96d5dff38964fd3844335a39b`, `crates/validation_core/tests/wilson_interior_extreme_z_contract.rs`. -- Strict-interior lower causal repair: `4f259f6e5c98ade2e4a34125430de872f32c1589`, `crates/validation_core/src/coverage.rs`. +- Strict-interior exact-zero lower public RED: `9d45f482854037d96d5dff38964fd3844335a39b`, `crates/validation_core/tests/wilson_interior_extreme_z_contract.rs`. +- Strict-interior exact-zero causal repair: `4f259f6e5c98ade2e4a34125430de872f32c1589`, `crates/validation_core/src/coverage.rs`. +- Strict-interior nonzero cancellation-residue RED: `1a24fac71569334b0c0185d013574ec5ccdcc58d`, `crates/validation_core/tests/wilson_interior_cancellation_residual_contract.rs`. +- Scale-aware rationalized-lower causal repair: `f7e20ddc59399f2e014a3ac91809bb57ba73460f`, `crates/validation_core/src/coverage.rs`. +- Small-`z` branch coverage and oracle reinforcement: `39e5e087cba22d6df85b9ad5901cda0194e3da93`, same public contract file. - All-uncovered upper public RED: `c070da269aa257fc9c9fa9eae17231a51ec63b74`, `crates/validation_core/tests/wilson_all_uncovered_upper_endpoint_contract.rs`. - Strict-interior upper RED expansion: `344081bfd98ee9bc70a3bf8fdebc795a9090e692`, same public contract file. - Complementary rationalized upper repair: `9a2fdd05c2994f51f6c72030fe39e695ba5a876d`, `crates/validation_core/src/coverage.rs`. -- Release trace: `c68076f565822ef9dd1e7c540d966cc29bf54191`, `CHANGELOG.d/validation-wilson-extreme-endpoint.md`. +- Release trace: `2dbdf330af21ea6263fd5d03eb6cb8defa867254`, `CHANGELOG.d/validation-wilson-extreme-endpoint.md`. - API: `validation_core::wilson_coverage_interval`. Interval-admission rules and rejection of non-finite/non-positive `z` or overflowing `z²` remain unchanged. The new path is endpoint-representation repair, not a change to nominal coverage policy. From e9414e6f7b824c2dc508f335d355b97b7e399b9c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:01:29 +0900 Subject: [PATCH 058/576] test(validation): reject impossible report evidence --- ...n_report_scientific_invariants_contract.rs | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 crates/validation_core/tests/validation_report_scientific_invariants_contract.rs diff --git a/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs b/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs new file mode 100644 index 000000000..aa7bf31de --- /dev/null +++ b/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs @@ -0,0 +1,74 @@ +use validation_core::{ValidationError, ValidationReport}; + +fn valid_report() -> ValidationReport { + ValidationReport { + study_label: "validation-report-contract".into(), + rmse: 0.1, + rmse_standard_error: 0.01, + mean_bias: -0.02, + bias_standard_error: 0.02, + interval_coverage: 0.8, + coverage_wilson_lower: 0.6, + coverage_wilson_upper: 0.9, + temporal_order_accuracy: 0.75, + monte_carlo_rmse: None, + } +} + +#[test] +fn validation_report_rejects_impossible_metric_domains() { + let mut report = valid_report(); + report.rmse = -0.1; + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + + let mut report = valid_report(); + report.rmse_standard_error = -0.01; + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + + let mut report = valid_report(); + report.bias_standard_error = -0.01; + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + + for invalid_coverage in [-0.01, 1.01] { + let mut report = valid_report(); + report.interval_coverage = invalid_coverage; + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + } + + for invalid_accuracy in [-0.01, 1.01] { + let mut report = valid_report(); + report.temporal_order_accuracy = invalid_accuracy; + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + } +} + +#[test] +fn validation_report_rejects_incoherent_wilson_evidence() { + let mut report = valid_report(); + report.coverage_wilson_lower = -0.01; + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + + let mut report = valid_report(); + report.coverage_wilson_upper = 1.01; + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + + let mut report = valid_report(); + report.coverage_wilson_lower = 0.95; + report.coverage_wilson_upper = 0.90; + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + + let mut report = valid_report(); + report.coverage_wilson_lower = 0.81; + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + + let mut report = valid_report(); + report.coverage_wilson_upper = 0.79; + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); +} + +#[test] +fn serialization_cannot_bypass_report_validation() { + let mut report = valid_report(); + report.interval_coverage = 1.5; + assert_eq!(report.to_json(), Err(ValidationError::InvalidInput)); +} From 28924b0d82bc2d4663f5ba1317cc0c3d94a4833b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:02:06 +0900 Subject: [PATCH 059/576] fix(validation): enforce report scientific domains --- crates/validation_core/src/report.rs | 36 ++++++++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index e458f142f..95243cbc4 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -30,12 +30,20 @@ pub struct ValidationReport { } impl ValidationReport { - /// Reject non-finite numeric fields before serialization or export. + /// Validate numeric and scientific invariants before serialization or export. + /// + /// RMSE and standard errors are nonnegative; empirical coverage, Wilson + /// endpoints, and temporal-order accuracy are probabilities in `[0, 1]`; + /// the Wilson interval is ordered and must contain the empirical coverage + /// recorded in the same report. Mean signed bias remains unrestricted in + /// sign. These checks prevent a finite but scientifically impossible payload + /// from becoming durable Validation Evidence. /// /// # Errors /// - /// Returns [`ValidationError::InvalidInput`] when any `f64` field or the - /// optional Monte Carlo summary violates finiteness / summary invariants. + /// Returns [`ValidationError::InvalidInput`] when any `f64` field is + /// non-finite, violates its metric domain, Wilson evidence is incoherent, or + /// the optional Monte Carlo summary violates its own invariants. pub fn validate(&self) -> Result<(), ValidationError> { for value in [ self.rmse, @@ -51,6 +59,24 @@ impl ValidationReport { return Err(ValidationError::InvalidInput); } } + + if self.rmse < 0.0 || self.rmse_standard_error < 0.0 || self.bias_standard_error < 0.0 { + return Err(ValidationError::InvalidInput); + } + if !(0.0..=1.0).contains(&self.interval_coverage) + || !(0.0..=1.0).contains(&self.coverage_wilson_lower) + || !(0.0..=1.0).contains(&self.coverage_wilson_upper) + || !(0.0..=1.0).contains(&self.temporal_order_accuracy) + { + return Err(ValidationError::InvalidInput); + } + if self.coverage_wilson_lower > self.coverage_wilson_upper + || self.interval_coverage < self.coverage_wilson_lower + || self.interval_coverage > self.coverage_wilson_upper + { + return Err(ValidationError::InvalidInput); + } + if let Some(summary) = self.monte_carlo_rmse { summary.validate()?; } @@ -61,8 +87,8 @@ impl ValidationReport { /// /// # Errors /// - /// Returns [`ValidationError::InvalidInput`] when fields are non-finite or - /// serialization fails. + /// Returns [`ValidationError::InvalidInput`] when fields violate report + /// invariants or serialization fails. pub fn to_json(&self) -> Result { self.validate()?; serde_json::to_string(self).map_err(|_| ValidationError::InvalidInput) From 6d190cd783a00f8ce917e37b63ae87053a929ae1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:02:41 +0900 Subject: [PATCH 060/576] test(validation): fail closed on invalid report ingress --- ...tion_report_scientific_invariants_contract.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs b/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs index aa7bf31de..fc9da6da7 100644 --- a/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs +++ b/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs @@ -67,8 +67,22 @@ fn validation_report_rejects_incoherent_wilson_evidence() { } #[test] -fn serialization_cannot_bypass_report_validation() { +fn serialization_and_deserialization_cannot_bypass_report_validation() { let mut report = valid_report(); report.interval_coverage = 1.5; assert_eq!(report.to_json(), Err(ValidationError::InvalidInput)); + + let impossible = r#"{ + "study_label":"invalid-wire-report", + "rmse":0.1, + "rmse_standard_error":0.01, + "mean_bias":0.0, + "bias_standard_error":0.01, + "interval_coverage":1.5, + "coverage_wilson_lower":0.6, + "coverage_wilson_upper":0.9, + "temporal_order_accuracy":0.75, + "monte_carlo_rmse":null + }"#; + assert!(serde_json::from_str::(impossible).is_err()); } From f70a6fc0e58c2ae419c3b3bac322db5f35efe538 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:03:39 +0900 Subject: [PATCH 061/576] fix(validation): validate report ingress and egress --- crates/validation_core/src/report.rs | 39 +++++++++++++++++++++++++++- 1 file changed, 38 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index 95243cbc4..324791a97 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -5,7 +5,7 @@ use crate::ValidationError; use serde::{Deserialize, Serialize}; /// Machine-readable recovery report for a single study. -#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)] +#[derive(Clone, Debug, PartialEq, Serialize)] pub struct ValidationReport { /// Study label (not free-form PII). pub study_label: String, @@ -110,6 +110,43 @@ impl ValidationReport { } } +impl<'de> Deserialize<'de> for ValidationReport { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + struct Raw { + study_label: String, + rmse: f64, + rmse_standard_error: f64, + mean_bias: f64, + bias_standard_error: f64, + interval_coverage: f64, + coverage_wilson_lower: f64, + coverage_wilson_upper: f64, + temporal_order_accuracy: f64, + monte_carlo_rmse: Option, + } + + let raw = Raw::deserialize(deserializer)?; + let report = Self { + study_label: raw.study_label, + rmse: raw.rmse, + rmse_standard_error: raw.rmse_standard_error, + mean_bias: raw.mean_bias, + bias_standard_error: raw.bias_standard_error, + interval_coverage: raw.interval_coverage, + coverage_wilson_lower: raw.coverage_wilson_lower, + coverage_wilson_upper: raw.coverage_wilson_upper, + temporal_order_accuracy: raw.temporal_order_accuracy, + monte_carlo_rmse: raw.monte_carlo_rmse, + }; + report.validate().map_err(serde::de::Error::custom)?; + Ok(report) + } +} + // Serde for MonteCarloSummary impl Serialize for MonteCarloSummary { fn serialize(&self, serializer: S) -> Result From b60a49ebed0a270fa51b7e8c4b25db3cd914061f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:03:58 +0900 Subject: [PATCH 062/576] docs(changelog): record validation report invariants --- CHANGELOG.d/validation-report-scientific-invariants.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-report-scientific-invariants.md diff --git a/CHANGELOG.d/validation-report-scientific-invariants.md b/CHANGELOG.d/validation-report-scientific-invariants.md new file mode 100644 index 000000000..31235fe45 --- /dev/null +++ b/CHANGELOG.d/validation-report-scientific-invariants.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core::ValidationReport` now rejects finite but scientifically impossible metric payloads: negative RMSE/standard errors, coverage or temporal-order accuracy outside `[0, 1]`, invalid Wilson endpoints, and Wilson intervals that do not contain the empirical coverage recorded in the same report. +- JSON deserialization now applies the same report invariants as explicit validation and serialization, so invalid Validation Evidence cannot enter through the wire path while egress remains fail closed. From 7faaf5e6ebc7a64bf9cdd5e4b0d8ffb7dcec07dc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:04:17 +0900 Subject: [PATCH 063/576] docs(research): trace report scientific invariants --- ...validation-report-scientific-invariants.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 docs/research/validation-report-scientific-invariants.md diff --git a/docs/research/validation-report-scientific-invariants.md b/docs/research/validation-report-scientific-invariants.md new file mode 100644 index 000000000..d85e503af --- /dev/null +++ b/docs/research/validation-report-scientific-invariants.md @@ -0,0 +1,52 @@ +# Validation report scientific-invariant boundary + +## Finding + +`validation_core::ValidationReport` previously treated finiteness as sufficient payload validity. That allowed finite but impossible Validation Evidence to be serialized or deserialized: negative RMSE or standard errors, empirical coverage and temporal-order accuracy outside `[0, 1]`, Wilson endpoints outside the probability domain, inverted Wilson intervals, or an empirical coverage value outside the Wilson interval stored beside it. + +The defect is an evidence-admission problem rather than a new estimator. `ValidationReport` is a durable boundary over already-computed recovery metrics, so its responsibility is to preserve the metric domains and cross-field relationships established by the producing functions. Mean signed bias is intentionally not sign-restricted. + +## Decision + +The report boundary now enforces the following invariants on explicit validation, canonical JSON egress, and JSON ingress: + +- `rmse >= 0`, `rmse_standard_error >= 0`, and `bias_standard_error >= 0`; +- empirical coverage, Wilson endpoints, and temporal-order accuracy are each in `[0, 1]`; +- `coverage_wilson_lower <= coverage_wilson_upper`; +- `coverage_wilson_lower <= interval_coverage <= coverage_wilson_upper`; +- all numeric fields remain finite and an embedded Monte Carlo summary must satisfy its existing validation contract. + +Custom `Deserialize` is used for `ValidationReport`, matching the existing fail-closed pattern already used by `MonteCarloSummary`. This prevents a caller from bypassing the durable evidence contract merely by entering through serde rather than calling `validate()` or `to_json()`. + +## RED -> repair trace + +- Public RED `e9414e6f7b824c2dc508f335d355b97b7e399b9`: impossible finite metric domains and incoherent Wilson evidence must fail `ValidationReport::validate()` / `to_json()`. +- First causal repair `28924b0d82bc2d4663f5ba1317cc0c3d94a4833b`: enforce metric-domain and Wilson coherence invariants on the report object. +- Ingress RED `6d190cd783a00f8ce917e37b63ae87053a929ae1`: serde deserialization must not bypass the same scientific contract. +- Ingress/egress repair `f70a6fc0e58c2ae419c3b3bac322db5f35efe538`: custom `Deserialize` constructs then validates the report before admission. +- Changelog `b60a49ebed0a270fa51b7e8c4b25db3cd914061f`. + +Owned module/API/tests: + +- `crates/validation_core/src/report.rs` +- `validation_core::ValidationReport::validate` +- `validation_core::ValidationReport::to_json` +- `crates/validation_core/tests/validation_report_scientific_invariants_contract.rs` + +## Methodological basis + +The 2014 *Standards for Educational and Psychological Testing* remain the current published AERA/APA/NCME edition as of 2026-09-04; the sponsoring organizations announced a revision process in 2024 rather than a replacement published edition. The Standards' validity framework makes interpretation and use of reported scores/evidence contingent on appropriate evidence and coherent reporting, which supports fail-closed handling of impossible metric artifacts rather than treating mere machine representability as scientific validity. + +Morris, White, and Crowther (2019) frame simulation performance measures as quantities tied to explicit estimands/targets, emphasize unambiguous definitions, coverage, bias, and Monte Carlo uncertainty, and recommend checks during coding and execution. TEPP therefore treats metric-domain constraints as part of the Validation Evidence contract, not cosmetic post-processing. + +ISO/IEC 25012:2008 defines a data-quality model for structured data and was last reviewed and confirmed in 2025, so it remains current as of 2026-09-04. TEPP uses that standard only as a data-quality support for enforcing validity/consistency requirements at the artifact boundary; it does not replace psychometric validity theory. + +## 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. https://www.testingstandards.net/open-access-files.html + +American Educational Research Association. (2024, June 12). *Members of the Joint Committee for the Revision of the Standards for Educational and Psychological Testing named*. https://www.aera.net/Newsroom/Members-of-the-Joint-Committee-for-the-Revision-of-the-Standards-for-Educational-and-Psychological-Testing-Named + +International Organization for Standardization. (2008). *ISO/IEC 25012:2008 Software engineering—Software product Quality Requirements and Evaluation (SQuaRE)—Data quality model*. https://www.iso.org/standard/35736.html + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From fb28f959297a50cf50e26ea14dc9bfb5ee10ea89 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:06:00 +0900 Subject: [PATCH 064/576] test(validation): prevent direct serde egress bypass --- .../tests/validation_report_scientific_invariants_contract.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs b/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs index fc9da6da7..a0813278d 100644 --- a/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs +++ b/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs @@ -71,6 +71,7 @@ fn serialization_and_deserialization_cannot_bypass_report_validation() { let mut report = valid_report(); report.interval_coverage = 1.5; assert_eq!(report.to_json(), Err(ValidationError::InvalidInput)); + assert!(serde_json::to_string(&report).is_err()); let impossible = r#"{ "study_label":"invalid-wire-report", From f7e58ccdb1864ce775ef6797f7fd88596dff1269 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:06:51 +0900 Subject: [PATCH 065/576] fix(validation): validate direct report serialization --- crates/validation_core/src/report.rs | 25 ++++++++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index 324791a97..426e162da 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -5,7 +5,7 @@ use crate::ValidationError; use serde::{Deserialize, Serialize}; /// Machine-readable recovery report for a single study. -#[derive(Clone, Debug, PartialEq, Serialize)] +#[derive(Clone, Debug, PartialEq)] pub struct ValidationReport { /// Study label (not free-form PII). pub study_label: String, @@ -110,6 +110,29 @@ impl ValidationReport { } } +impl Serialize for ValidationReport { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + + self.validate().map_err(serde::ser::Error::custom)?; + let mut state = serializer.serialize_struct("ValidationReport", 10)?; + state.serialize_field("study_label", &self.study_label)?; + state.serialize_field("rmse", &self.rmse)?; + state.serialize_field("rmse_standard_error", &self.rmse_standard_error)?; + state.serialize_field("mean_bias", &self.mean_bias)?; + state.serialize_field("bias_standard_error", &self.bias_standard_error)?; + state.serialize_field("interval_coverage", &self.interval_coverage)?; + state.serialize_field("coverage_wilson_lower", &self.coverage_wilson_lower)?; + state.serialize_field("coverage_wilson_upper", &self.coverage_wilson_upper)?; + state.serialize_field("temporal_order_accuracy", &self.temporal_order_accuracy)?; + state.serialize_field("monte_carlo_rmse", &self.monte_carlo_rmse)?; + state.end() + } +} + impl<'de> Deserialize<'de> for ValidationReport { fn deserialize(deserializer: D) -> Result where From 5339d53974d747854ba6cdd6ee05b2c1093bad20 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:07:09 +0900 Subject: [PATCH 066/576] docs(changelog): close report serde bypasses --- CHANGELOG.d/validation-report-scientific-invariants.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-report-scientific-invariants.md b/CHANGELOG.d/validation-report-scientific-invariants.md index 31235fe45..4c7f39d97 100644 --- a/CHANGELOG.d/validation-report-scientific-invariants.md +++ b/CHANGELOG.d/validation-report-scientific-invariants.md @@ -1,4 +1,4 @@ ### Fixed - `validation_core::ValidationReport` now rejects finite but scientifically impossible metric payloads: negative RMSE/standard errors, coverage or temporal-order accuracy outside `[0, 1]`, invalid Wilson endpoints, and Wilson intervals that do not contain the empirical coverage recorded in the same report. -- JSON deserialization now applies the same report invariants as explicit validation and serialization, so invalid Validation Evidence cannot enter through the wire path while egress remains fail closed. +- Explicit validation, canonical JSON helpers, direct serde serialization, and serde deserialization now enforce the same report invariants, so neither wire ingress nor an alternate serialization call can bypass the durable Validation Evidence contract. From 91712f7521fded895d50d97080a307ceb96cf6b8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:07:27 +0900 Subject: [PATCH 067/576] docs(research): trace report serde boundary --- .../validation-report-scientific-invariants.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/research/validation-report-scientific-invariants.md b/docs/research/validation-report-scientific-invariants.md index d85e503af..3f3162fb9 100644 --- a/docs/research/validation-report-scientific-invariants.md +++ b/docs/research/validation-report-scientific-invariants.md @@ -8,7 +8,7 @@ The defect is an evidence-admission problem rather than a new estimator. `Valida ## Decision -The report boundary now enforces the following invariants on explicit validation, canonical JSON egress, and JSON ingress: +The report boundary now enforces the following invariants on explicit validation, canonical JSON egress, direct serde serialization, and JSON ingress: - `rmse >= 0`, `rmse_standard_error >= 0`, and `bias_standard_error >= 0`; - empirical coverage, Wilson endpoints, and temporal-order accuracy are each in `[0, 1]`; @@ -16,21 +16,24 @@ The report boundary now enforces the following invariants on explicit validation - `coverage_wilson_lower <= interval_coverage <= coverage_wilson_upper`; - all numeric fields remain finite and an embedded Monte Carlo summary must satisfy its existing validation contract. -Custom `Deserialize` is used for `ValidationReport`, matching the existing fail-closed pattern already used by `MonteCarloSummary`. This prevents a caller from bypassing the durable evidence contract merely by entering through serde rather than calling `validate()` or `to_json()`. +Custom `Serialize` and `Deserialize` implementations validate `ValidationReport` at both wire boundaries. This prevents callers from bypassing the durable evidence contract by calling serde directly instead of `validate()` or `to_json()`. ## RED -> repair trace - Public RED `e9414e6f7b824c2dc508f335d355b97b7e399b9`: impossible finite metric domains and incoherent Wilson evidence must fail `ValidationReport::validate()` / `to_json()`. - First causal repair `28924b0d82bc2d4663f5ba1317cc0c3d94a4833b`: enforce metric-domain and Wilson coherence invariants on the report object. - Ingress RED `6d190cd783a00f8ce917e37b63ae87053a929ae1`: serde deserialization must not bypass the same scientific contract. -- Ingress/egress repair `f70a6fc0e58c2ae419c3b3bac322db5f35efe538`: custom `Deserialize` constructs then validates the report before admission. -- Changelog `b60a49ebed0a270fa51b7e8c4b25db3cd914061f`. +- Ingress repair `f70a6fc0e58c2ae419c3b3bac322db5f35efe538`: custom `Deserialize` constructs then validates the report before admission. +- Direct-egress RED `fb28f959297a50cf50e26ea14dc9bfb5ee10ea89`: `serde_json::to_string(&report)` must not bypass report validation. +- Direct-egress repair `f7e58ccdb1864ce775ef6797f7fd88596dff1269`: custom `Serialize` validates before writing any report field. +- Changelog trace `5339d53974d747854ba6cdd6ee05b2c1093bad20`. Owned module/API/tests: - `crates/validation_core/src/report.rs` - `validation_core::ValidationReport::validate` - `validation_core::ValidationReport::to_json` +- `serde::Serialize` / `serde::Deserialize` for `ValidationReport` - `crates/validation_core/tests/validation_report_scientific_invariants_contract.rs` ## Methodological basis From b5a8ae1fbd1344b09a9fbb3c65a3c6d0cdc38c28 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:11:47 +0900 Subject: [PATCH 068/576] test(validation): reject invalid Monte Carlo serde egress --- ...tion_report_scientific_invariants_contract.rs | 16 +++++++++++++++- 1 file changed, 15 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs b/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs index a0813278d..7a1cfd426 100644 --- a/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs +++ b/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs @@ -1,4 +1,4 @@ -use validation_core::{ValidationError, ValidationReport}; +use validation_core::{MonteCarloSummary, ValidationError, ValidationReport}; fn valid_report() -> ValidationReport { ValidationReport { @@ -87,3 +87,17 @@ fn serialization_and_deserialization_cannot_bypass_report_validation() { }"#; assert!(serde_json::from_str::(impossible).is_err()); } + +#[test] +fn monte_carlo_summary_direct_serialization_preserves_its_validation_contract() { + let impossible = MonteCarloSummary { + replication_count: 2, + mean: 0.0, + standard_deviation: -0.1, + standard_error: 0.0, + percentile_lower: 0.0, + percentile_upper: 1.0, + }; + assert_eq!(impossible.validate(), Err(ValidationError::InvalidInput)); + assert!(serde_json::to_string(&impossible).is_err()); +} From 18374c35061fb0b98bfe09079d4967baef7697b4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:12:44 +0900 Subject: [PATCH 069/576] fix(validation): validate Monte Carlo direct serialization --- crates/validation_core/src/report.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index 426e162da..dbcbe9985 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -177,6 +177,8 @@ impl Serialize for MonteCarloSummary { S: serde::Serializer, { use serde::ser::SerializeStruct; + + (*self).validate().map_err(serde::ser::Error::custom)?; let mut state = serializer.serialize_struct("MonteCarloSummary", 6)?; state.serialize_field("replication_count", &self.replication_count)?; state.serialize_field("mean", &self.mean)?; From 0d03efa1474e6ce605db32980c3f65e97e0dc66b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:12:59 +0900 Subject: [PATCH 070/576] docs(changelog): cover Monte Carlo serde egress --- CHANGELOG.d/validation-report-scientific-invariants.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.d/validation-report-scientific-invariants.md b/CHANGELOG.d/validation-report-scientific-invariants.md index 4c7f39d97..ac284ad32 100644 --- a/CHANGELOG.d/validation-report-scientific-invariants.md +++ b/CHANGELOG.d/validation-report-scientific-invariants.md @@ -2,3 +2,4 @@ - `validation_core::ValidationReport` now rejects finite but scientifically impossible metric payloads: negative RMSE/standard errors, coverage or temporal-order accuracy outside `[0, 1]`, invalid Wilson endpoints, and Wilson intervals that do not contain the empirical coverage recorded in the same report. - Explicit validation, canonical JSON helpers, direct serde serialization, and serde deserialization now enforce the same report invariants, so neither wire ingress nor an alternate serialization call can bypass the durable Validation Evidence contract. +- Direct serde serialization of `MonteCarloSummary` now applies its existing count/finiteness/nonnegative-uncertainty/percentile-order invariants before writing fields, matching its fail-closed deserialization contract. From bbba97653b613440aff4b17ffb5212e10edc6ea2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:13:20 +0900 Subject: [PATCH 071/576] docs(research): trace Monte Carlo serde boundary --- .../validation-report-scientific-invariants.md | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) diff --git a/docs/research/validation-report-scientific-invariants.md b/docs/research/validation-report-scientific-invariants.md index 3f3162fb9..23e5d37f3 100644 --- a/docs/research/validation-report-scientific-invariants.md +++ b/docs/research/validation-report-scientific-invariants.md @@ -4,7 +4,9 @@ `validation_core::ValidationReport` previously treated finiteness as sufficient payload validity. That allowed finite but impossible Validation Evidence to be serialized or deserialized: negative RMSE or standard errors, empirical coverage and temporal-order accuracy outside `[0, 1]`, Wilson endpoints outside the probability domain, inverted Wilson intervals, or an empirical coverage value outside the Wilson interval stored beside it. -The defect is an evidence-admission problem rather than a new estimator. `ValidationReport` is a durable boundary over already-computed recovery metrics, so its responsibility is to preserve the metric domains and cross-field relationships established by the producing functions. Mean signed bias is intentionally not sign-restricted. +The same boundary review found that `MonteCarloSummary` already rejected impossible values on explicit validation and serde ingress, but its direct serde serializer wrote public fields without applying that contract. A caller could therefore serialize a negative standard deviation or otherwise invalid summary directly even though the same payload would be rejected on re-ingress. + +These defects are evidence-admission/projection problems rather than new estimators. The report and Monte Carlo summary are durable boundaries over already-computed recovery metrics, so their responsibility is to preserve the metric domains and cross-field relationships established by the producing functions. Mean signed bias is intentionally not sign-restricted. ## Decision @@ -16,7 +18,7 @@ The report boundary now enforces the following invariants on explicit validation - `coverage_wilson_lower <= interval_coverage <= coverage_wilson_upper`; - all numeric fields remain finite and an embedded Monte Carlo summary must satisfy its existing validation contract. -Custom `Serialize` and `Deserialize` implementations validate `ValidationReport` at both wire boundaries. This prevents callers from bypassing the durable evidence contract by calling serde directly instead of `validate()` or `to_json()`. +Custom `Serialize` and `Deserialize` implementations validate `ValidationReport` at both wire boundaries. `MonteCarloSummary` direct serialization now also invokes its existing validator before writing fields. This prevents callers from bypassing durable evidence contracts by choosing a different serde path instead of the explicit validation helpers. ## RED -> repair trace @@ -24,9 +26,11 @@ Custom `Serialize` and `Deserialize` implementations validate `ValidationReport` - First causal repair `28924b0d82bc2d4663f5ba1317cc0c3d94a4833b`: enforce metric-domain and Wilson coherence invariants on the report object. - Ingress RED `6d190cd783a00f8ce917e37b63ae87053a929ae1`: serde deserialization must not bypass the same scientific contract. - Ingress repair `f70a6fc0e58c2ae419c3b3bac322db5f35efe538`: custom `Deserialize` constructs then validates the report before admission. -- Direct-egress RED `fb28f959297a50cf50e26ea14dc9bfb5ee10ea89`: `serde_json::to_string(&report)` must not bypass report validation. -- Direct-egress repair `f7e58ccdb1864ce775ef6797f7fd88596dff1269`: custom `Serialize` validates before writing any report field. -- Changelog trace `5339d53974d747854ba6cdd6ee05b2c1093bad20`. +- Direct-report-egress RED `fb28f959297a50cf50e26ea14dc9bfb5ee10ea89`: `serde_json::to_string(&report)` must not bypass report validation. +- Direct-report-egress repair `f7e58ccdb1864ce775ef6797f7fd88596dff1269`: custom `Serialize` validates before writing any report field. +- Direct-Monte-Carlo-egress RED `b5a8ae1fbd1344b09a9fbb3c65a3c6d0cdc38c28`: an invalid `MonteCarloSummary` must not serialize directly despite its public fields. +- Direct-Monte-Carlo-egress repair `18374c35061fb0b98bfe09079d4967baef7697b4`: the existing summary validator is applied before serde writes fields. +- Changelog trace `0d03efa1474e6ce605db32980c3f65e97e0dc66b`. Owned module/API/tests: @@ -34,6 +38,7 @@ Owned module/API/tests: - `validation_core::ValidationReport::validate` - `validation_core::ValidationReport::to_json` - `serde::Serialize` / `serde::Deserialize` for `ValidationReport` +- `serde::Serialize` / existing `serde::Deserialize` for `MonteCarloSummary` - `crates/validation_core/tests/validation_report_scientific_invariants_contract.rs` ## Methodological basis From 541273f10d2085680073fb8ca9dc72d3cbe1e62b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:19:10 +0900 Subject: [PATCH 072/576] test(validation): fail closed on human report projection --- .../validation_report_scientific_invariants_contract.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs b/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs index 7a1cfd426..4a02e23a5 100644 --- a/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs +++ b/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs @@ -67,11 +67,15 @@ fn validation_report_rejects_incoherent_wilson_evidence() { } #[test] -fn serialization_and_deserialization_cannot_bypass_report_validation() { +fn every_report_projection_enforces_validation() { let mut report = valid_report(); report.interval_coverage = 1.5; assert_eq!(report.to_json(), Err(ValidationError::InvalidInput)); assert!(serde_json::to_string(&report).is_err()); + assert_eq!( + report.to_human_summary(), + Err(ValidationError::InvalidInput) + ); let impossible = r#"{ "study_label":"invalid-wire-report", From 53607f8033ee03b409c8fce4ee464797e8cb7be1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:19:49 +0900 Subject: [PATCH 073/576] fix(validation): validate human report projection --- crates/validation_core/src/report.rs | 23 +++++++++++++++++------ 1 file changed, 17 insertions(+), 6 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index dbcbe9985..94422094a 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -94,10 +94,16 @@ impl ValidationReport { serde_json::to_string(self).map_err(|_| ValidationError::InvalidInput) } - /// Render a short human-readable summary line. - #[must_use] - pub fn to_human_summary(&self) -> String { - format!( + /// Render a short human-readable summary line after validating the report. + /// + /// # Errors + /// + /// Returns [`ValidationError::InvalidInput`] when the report violates its + /// numeric or scientific invariants. Human-readable projection is therefore + /// subject to the same fail-closed boundary as JSON ingress and egress. + pub fn to_human_summary(&self) -> Result { + self.validate()?; + Ok(format!( "study={} rmse={:.6} (se={:.6}) bias={:.6} (se={:.6}) coverage={:.3} temporal_order={:.3}", self.study_label, self.rmse, @@ -106,7 +112,7 @@ impl ValidationReport { self.bias_standard_error, self.interval_coverage, self.temporal_order_accuracy - ) + )) } } @@ -247,7 +253,12 @@ mod tests { let json = report.to_json().expect("json"); let decoded: ValidationReport = serde_json::from_str(&json).expect("decode"); assert_eq!(decoded.study_label, "foundation-recovery"); - assert!(report.to_human_summary().contains("rmse=0.100000")); + assert!( + report + .to_human_summary() + .expect("human summary") + .contains("rmse=0.100000") + ); let none_report = ValidationReport { monte_carlo_rmse: None, ..report.clone() From d1f1b85ac1441ef573bbb84478bacec1c7424266 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:20:00 +0900 Subject: [PATCH 074/576] docs(changelog): cover human validation projection --- CHANGELOG.d/validation-report-scientific-invariants.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-report-scientific-invariants.md b/CHANGELOG.d/validation-report-scientific-invariants.md index ac284ad32..0be8f7eaa 100644 --- a/CHANGELOG.d/validation-report-scientific-invariants.md +++ b/CHANGELOG.d/validation-report-scientific-invariants.md @@ -1,5 +1,5 @@ ### Fixed - `validation_core::ValidationReport` now rejects finite but scientifically impossible metric payloads: negative RMSE/standard errors, coverage or temporal-order accuracy outside `[0, 1]`, invalid Wilson endpoints, and Wilson intervals that do not contain the empirical coverage recorded in the same report. -- Explicit validation, canonical JSON helpers, direct serde serialization, and serde deserialization now enforce the same report invariants, so neither wire ingress nor an alternate serialization call can bypass the durable Validation Evidence contract. +- Explicit validation, canonical JSON helpers, direct serde serialization/deserialization, and human-readable summary projection now enforce the same report invariants, so alternate ingress or egress paths cannot bypass the durable Validation Evidence contract. - Direct serde serialization of `MonteCarloSummary` now applies its existing count/finiteness/nonnegative-uncertainty/percentile-order invariants before writing fields, matching its fail-closed deserialization contract. From 60166eafe34cd411b7379f0ceedd0dfd6380632f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:20:21 +0900 Subject: [PATCH 075/576] docs(research): close human report projection bypass --- .../validation-report-scientific-invariants.md | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/docs/research/validation-report-scientific-invariants.md b/docs/research/validation-report-scientific-invariants.md index 23e5d37f3..6daa64294 100644 --- a/docs/research/validation-report-scientific-invariants.md +++ b/docs/research/validation-report-scientific-invariants.md @@ -4,13 +4,15 @@ `validation_core::ValidationReport` previously treated finiteness as sufficient payload validity. That allowed finite but impossible Validation Evidence to be serialized or deserialized: negative RMSE or standard errors, empirical coverage and temporal-order accuracy outside `[0, 1]`, Wilson endpoints outside the probability domain, inverted Wilson intervals, or an empirical coverage value outside the Wilson interval stored beside it. +After the serde boundary was hardened, the human-readable projection remained a separate bypass: `to_human_summary()` returned a `String` without validating the public report fields. A manually constructed invalid report could therefore be refused by JSON while still being rendered as apparently normal human-facing Validation Evidence. + The same boundary review found that `MonteCarloSummary` already rejected impossible values on explicit validation and serde ingress, but its direct serde serializer wrote public fields without applying that contract. A caller could therefore serialize a negative standard deviation or otherwise invalid summary directly even though the same payload would be rejected on re-ingress. These defects are evidence-admission/projection problems rather than new estimators. The report and Monte Carlo summary are durable boundaries over already-computed recovery metrics, so their responsibility is to preserve the metric domains and cross-field relationships established by the producing functions. Mean signed bias is intentionally not sign-restricted. ## Decision -The report boundary now enforces the following invariants on explicit validation, canonical JSON egress, direct serde serialization, and JSON ingress: +The report boundary now enforces the following invariants on explicit validation, canonical JSON egress, direct serde serialization, JSON ingress, and human-readable projection: - `rmse >= 0`, `rmse_standard_error >= 0`, and `bias_standard_error >= 0`; - empirical coverage, Wilson endpoints, and temporal-order accuracy are each in `[0, 1]`; @@ -18,7 +20,7 @@ The report boundary now enforces the following invariants on explicit validation - `coverage_wilson_lower <= interval_coverage <= coverage_wilson_upper`; - all numeric fields remain finite and an embedded Monte Carlo summary must satisfy its existing validation contract. -Custom `Serialize` and `Deserialize` implementations validate `ValidationReport` at both wire boundaries. `MonteCarloSummary` direct serialization now also invokes its existing validator before writing fields. This prevents callers from bypassing durable evidence contracts by choosing a different serde path instead of the explicit validation helpers. +`ValidationReport::to_human_summary()` now returns `Result` and validates before rendering. Custom `Serialize` and `Deserialize` implementations validate `ValidationReport` at both wire boundaries. `MonteCarloSummary` direct serialization also invokes its existing validator before writing fields. Callers therefore cannot select a weaker projection path to turn impossible finite values into durable or human-facing scientific evidence. ## RED -> repair trace @@ -30,13 +32,16 @@ Custom `Serialize` and `Deserialize` implementations validate `ValidationReport` - Direct-report-egress repair `f7e58ccdb1864ce775ef6797f7fd88596dff1269`: custom `Serialize` validates before writing any report field. - Direct-Monte-Carlo-egress RED `b5a8ae1fbd1344b09a9fbb3c65a3c6d0cdc38c28`: an invalid `MonteCarloSummary` must not serialize directly despite its public fields. - Direct-Monte-Carlo-egress repair `18374c35061fb0b98bfe09079d4967baef7697b4`: the existing summary validator is applied before serde writes fields. -- Changelog trace `0d03efa1474e6ce605db32980c3f65e97e0dc66b`. +- Human-projection RED `541273f10d2085680073fb8ca9dc72d3cbe1e62b`: the human summary must not render a report rejected by the canonical Validation Evidence contract. +- Human-projection repair `53607f8033ee03b409c8fce4ee464797e8cb7be1`: human summary projection validates and returns `Result` instead of rendering invalid evidence. +- Changelog trace `d1f1b85ac1441ef573bbb84478bacec1c7424266`. Owned module/API/tests: - `crates/validation_core/src/report.rs` - `validation_core::ValidationReport::validate` - `validation_core::ValidationReport::to_json` +- `validation_core::ValidationReport::to_human_summary` - `serde::Serialize` / `serde::Deserialize` for `ValidationReport` - `serde::Serialize` / existing `serde::Deserialize` for `MonteCarloSummary` - `crates/validation_core/tests/validation_report_scientific_invariants_contract.rs` From 3cd6e41ddeffbb41e0a6179a65bc3dd9b60f41d8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:27:59 +0900 Subject: [PATCH 076/576] test(validation): reject negative RMSE summaries --- ...ion_report_rmse_summary_domain_contract.rs | 68 +++++++++++++++++++ 1 file changed, 68 insertions(+) create mode 100644 crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs diff --git a/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs b/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs new file mode 100644 index 000000000..10b0b4d8d --- /dev/null +++ b/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs @@ -0,0 +1,68 @@ +use validation_core::{MonteCarloSummary, ValidationError, ValidationReport}; + +fn report_with_rmse_summary(summary: MonteCarloSummary) -> ValidationReport { + ValidationReport { + study_label: "rmse-domain-contract".into(), + rmse: 0.2, + rmse_standard_error: 0.01, + mean_bias: 0.0, + bias_standard_error: 0.01, + interval_coverage: 0.95, + coverage_wilson_lower: 0.85, + coverage_wilson_upper: 0.99, + temporal_order_accuracy: 0.9, + monte_carlo_rmse: Some(summary), + } +} + +#[test] +fn validation_report_rejects_negative_monte_carlo_rmse_evidence() { + let negative_mean = report_with_rmse_summary(MonteCarloSummary { + replication_count: 20, + mean: -0.1, + standard_deviation: 0.02, + standard_error: 0.004, + percentile_lower: -0.14, + percentile_upper: -0.06, + }); + assert_eq!(negative_mean.validate(), Err(ValidationError::InvalidInput)); + assert_eq!(negative_mean.to_json(), Err(ValidationError::InvalidInput)); + assert_eq!( + negative_mean.to_human_summary(), + Err(ValidationError::InvalidInput) + ); + + let negative_percentile = report_with_rmse_summary(MonteCarloSummary { + replication_count: 20, + mean: 0.02, + standard_deviation: 0.03, + standard_error: 0.006, + percentile_lower: -0.01, + percentile_upper: 0.07, + }); + assert_eq!( + negative_percentile.validate(), + Err(ValidationError::InvalidInput) + ); + + let serialized = r#"{ + "study_label":"rmse-domain-contract", + "rmse":0.2, + "rmse_standard_error":0.01, + "mean_bias":0.0, + "bias_standard_error":0.01, + "interval_coverage":0.95, + "coverage_wilson_lower":0.85, + "coverage_wilson_upper":0.99, + "temporal_order_accuracy":0.9, + "monte_carlo_rmse":{ + "replication_count":20, + "mean":-0.1, + "standard_deviation":0.02, + "standard_error":0.004, + "percentile_lower":-0.14, + "percentile_upper":-0.06 + } + }"#; + assert!(serde_json::from_str::(serialized).is_err()); +} From 0090259d01ee00ad0de35ba0c4c9cb7a37c0b13c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:28:53 +0900 Subject: [PATCH 077/576] fix(validation): enforce RMSE summary domain --- crates/validation_core/src/report.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index 94422094a..caa629201 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -36,14 +36,18 @@ impl ValidationReport { /// endpoints, and temporal-order accuracy are probabilities in `[0, 1]`; /// the Wilson interval is ordered and must contain the empirical coverage /// recorded in the same report. Mean signed bias remains unrestricted in - /// sign. These checks prevent a finite but scientifically impossible payload - /// from becoming durable Validation Evidence. + /// sign. A generic [`MonteCarloSummary`] may summarize a signed metric, but + /// when it occupies `monte_carlo_rmse` its mean and percentile endpoints are + /// nonnegative because every RMSE replication is nonnegative. These checks + /// prevent a finite but scientifically impossible payload from becoming + /// durable Validation Evidence. /// /// # Errors /// /// Returns [`ValidationError::InvalidInput`] when any `f64` field is /// non-finite, violates its metric domain, Wilson evidence is incoherent, or - /// the optional Monte Carlo summary violates its own invariants. + /// the optional Monte Carlo RMSE summary violates either generic summary + /// invariants or the nonnegative RMSE domain. pub fn validate(&self) -> Result<(), ValidationError> { for value in [ self.rmse, @@ -78,7 +82,13 @@ impl ValidationReport { } if let Some(summary) = self.monte_carlo_rmse { - summary.validate()?; + let summary = summary.validate()?; + if summary.mean < 0.0 + || summary.percentile_lower < 0.0 + || summary.percentile_upper < 0.0 + { + return Err(ValidationError::InvalidInput); + } } Ok(()) } From d2631d1b0047ba8dbf78058272ea6c00d9b0c9a3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:29:39 +0900 Subject: [PATCH 078/576] docs(validation): record RMSE summary domain repair --- CHANGELOG.d/validation-report-scientific-invariants.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.d/validation-report-scientific-invariants.md b/CHANGELOG.d/validation-report-scientific-invariants.md index 0be8f7eaa..6af64f089 100644 --- a/CHANGELOG.d/validation-report-scientific-invariants.md +++ b/CHANGELOG.d/validation-report-scientific-invariants.md @@ -1,5 +1,5 @@ ### Fixed -- `validation_core::ValidationReport` now rejects finite but scientifically impossible metric payloads: negative RMSE/standard errors, coverage or temporal-order accuracy outside `[0, 1]`, invalid Wilson endpoints, and Wilson intervals that do not contain the empirical coverage recorded in the same report. +- `validation_core::ValidationReport` now rejects finite but scientifically impossible metric payloads: negative RMSE/standard errors, coverage or temporal-order accuracy outside `[0, 1]`, invalid Wilson endpoints, Wilson intervals that do not contain the empirical coverage recorded in the same report, and negative mean/percentile values when a generic Monte Carlo summary is specifically embedded as RMSE evidence. - Explicit validation, canonical JSON helpers, direct serde serialization/deserialization, and human-readable summary projection now enforce the same report invariants, so alternate ingress or egress paths cannot bypass the durable Validation Evidence contract. -- Direct serde serialization of `MonteCarloSummary` now applies its existing count/finiteness/nonnegative-uncertainty/percentile-order invariants before writing fields, matching its fail-closed deserialization contract. +- Direct serde serialization of `MonteCarloSummary` now applies its existing count/finiteness/nonnegative-uncertainty/percentile-order invariants before writing fields, matching its fail-closed deserialization contract; the generic summary remains sign-neutral so it can still represent signed metrics such as bias. From 0ba16c080b4bea12873aacb252c49497d34a2162 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 05:30:17 +0900 Subject: [PATCH 079/576] docs(research): trace RMSE summary domain invariant --- .../validation-report-rmse-summary-domain.md | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 docs/research/validation-report-rmse-summary-domain.md diff --git a/docs/research/validation-report-rmse-summary-domain.md b/docs/research/validation-report-rmse-summary-domain.md new file mode 100644 index 000000000..455729c74 --- /dev/null +++ b/docs/research/validation-report-rmse-summary-domain.md @@ -0,0 +1,45 @@ +# Validation report RMSE-summary domain invariant + +## Finding + +`MonteCarloSummary` is intentionally metric-neutral: its mean and percentile endpoints may be negative when it summarizes a signed metric such as bias. `ValidationReport::monte_carlo_rmse`, however, gives that same wire shape a narrower scientific meaning. Every RMSE replication is nonnegative, so a negative Monte Carlo RMSE mean or percentile endpoint is finite but scientifically impossible evidence. + +Before this repair, `ValidationReport::validate()` delegated the nested object only to the generic `MonteCarloSummary::validate()` contract. A caller could therefore construct or deserialize a report whose field was explicitly named `monte_carlo_rmse` while carrying negative RMSE evidence, and the report could pass canonical JSON and human-summary projection. + +## Decision + +Keep `MonteCarloSummary` sign-neutral because it is reusable for signed recovery metrics. Enforce the narrower nonnegative domain only at the `ValidationReport::monte_carlo_rmse` ownership boundary: + +- `mean >= 0`; +- `percentile_lower >= 0`; +- `percentile_upper >= 0`; +- all existing generic Monte Carlo count, finiteness, uncertainty, and percentile-order invariants remain mandatory. + +This is an artifact-admission invariant, not a new estimator and not reusable static psychometric arithmetic. The change therefore remains in TEPP `validation_core`; it does not move arithmetic into or copy source from `fast-mlsirm`. + +## RED -> repair trace + +- Public RED `3cd6e41ddeffbb41e0a6179a65bc3dd9b60f41d8`: direct validation, canonical JSON, human projection, and JSON ingress must reject negative values when a generic Monte Carlo summary is embedded specifically as RMSE evidence. +- Causal repair `0090259d01ee00ad0de35ba0c4c9cb7a37c0b13c`: `ValidationReport::validate()` first applies the generic summary validator and then enforces the nonnegative RMSE-specific mean/percentile domain. +- Changelog trace `d2631d1b0047ba8dbf78058272ea6c00d9b0c9a3`. + +Owned module/API/test: + +- `crates/validation_core/src/report.rs` +- `validation_core::ValidationReport::validate` +- `validation_core::ValidationReport::to_json` +- `validation_core::ValidationReport::to_human_summary` +- serde ingress/egress for `ValidationReport` +- `crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs` + +## Methodological basis + +Morris, White, and Crowther (2019) treat simulation performance measures as explicitly defined quantities tied to their estimands and recommend coding and execution checks. RMSE is the square root of a mean squared error and therefore has a nonnegative range; a negative Monte Carlo summary carried under an RMSE-specific field is not an alternative convention but a domain violation. + +The 2014 *Standards for Educational and Psychological Testing* remain the current published AERA/APA/NCME edition while revision is underway. TEPP uses that validity framework to require coherent interpretation and reporting of evidence, rather than accepting a payload solely because each scalar is machine-representable. + +## 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. https://www.testingstandards.net/open-access-files.html + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 0a1933c40ffc1681348be92b9fc72086ec19b93d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:02:43 +0900 Subject: [PATCH 080/576] test(validation): reject incoherent Monte Carlo standard error --- ...carlo_standard_error_coherence_contract.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs diff --git a/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs b/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs new file mode 100644 index 000000000..e8853053e --- /dev/null +++ b/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs @@ -0,0 +1,23 @@ +use validation_core::{MonteCarloSummary, ValidationError}; + +fn inconsistent_standard_error_summary() -> MonteCarloSummary { + MonteCarloSummary { + replication_count: 4, + mean: 0.5, + standard_deviation: 2.0, + standard_error: 0.5, + percentile_lower: -2.0, + percentile_upper: 3.0, + } +} + +#[test] +fn monte_carlo_summary_rejects_standard_error_that_disagrees_with_sd_and_n() { + let summary = inconsistent_standard_error_summary(); + + assert_eq!(summary.validate(), Err(ValidationError::InvalidInput)); + assert!(serde_json::to_string(&summary).is_err()); + + let payload = r#"{"replication_count":4,"mean":0.5,"standard_deviation":2.0,"standard_error":0.5,"percentile_lower":-2.0,"percentile_upper":3.0}"#; + assert!(serde_json::from_str::(payload).is_err()); +} From e83e439aa4e1cb1ce099968e632422eb59f54961 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:04:38 +0900 Subject: [PATCH 081/576] fix(validation): enforce Monte Carlo SE coherence --- crates/validation_core/src/monte_carlo.rs | 20 ++++++++++++++++++-- 1 file changed, 18 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 88a432676..100bddcb1 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -22,12 +22,19 @@ pub struct MonteCarloSummary { } impl MonteCarloSummary { - /// Validate structural invariants for a Monte Carlo summary payload. + /// Validate structural and derived-field invariants for a Monte Carlo summary payload. + /// + /// The standard error is a derived field, not an independently selectable + /// uncertainty estimate: it must equal `standard_deviation / sqrt(n)` for + /// the represented replication count. A nonzero standard deviation whose + /// implied standard error is not representable fails closed rather than + /// becoming false zero uncertainty. /// /// # Errors /// /// Returns [`ValidationError::InvalidInput`] when counts or numeric fields - /// violate the summary contract. + /// violate the summary contract or the standard error disagrees with the + /// standard deviation and replication count. pub fn validate(self) -> Result { if self.replication_count == 0 { return Err(ValidationError::InvalidInput); @@ -49,6 +56,15 @@ impl MonteCarloSummary { if self.percentile_lower > self.percentile_upper { return Err(ValidationError::InvalidInput); } + + let implied_standard_error = + self.standard_deviation / (self.replication_count as f64).sqrt(); + if !implied_standard_error.is_finite() + || (implied_standard_error == 0.0 && self.standard_deviation != 0.0) + || self.standard_error != implied_standard_error + { + return Err(ValidationError::InvalidInput); + } Ok(self) } } From 0465a6ab188e3241b5379a5b7773a2d0c6cb9761 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:05:24 +0900 Subject: [PATCH 082/576] test(validation): isolate impossible Monte Carlo SE --- .../monte_carlo_standard_error_coherence_contract.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs b/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs index e8853053e..089ae87d0 100644 --- a/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs +++ b/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs @@ -1,23 +1,23 @@ use validation_core::{MonteCarloSummary, ValidationError}; -fn inconsistent_standard_error_summary() -> MonteCarloSummary { +fn impossible_standard_error_summary() -> MonteCarloSummary { MonteCarloSummary { replication_count: 4, mean: 0.5, - standard_deviation: 2.0, - standard_error: 0.5, + standard_deviation: 0.5, + standard_error: 1.0, percentile_lower: -2.0, percentile_upper: 3.0, } } #[test] -fn monte_carlo_summary_rejects_standard_error_that_disagrees_with_sd_and_n() { - let summary = inconsistent_standard_error_summary(); +fn monte_carlo_summary_rejects_standard_error_larger_than_sample_sd() { + let summary = impossible_standard_error_summary(); assert_eq!(summary.validate(), Err(ValidationError::InvalidInput)); assert!(serde_json::to_string(&summary).is_err()); - let payload = r#"{"replication_count":4,"mean":0.5,"standard_deviation":2.0,"standard_error":0.5,"percentile_lower":-2.0,"percentile_upper":3.0}"#; + let payload = r#"{"replication_count":4,"mean":0.5,"standard_deviation":0.5,"standard_error":1.0,"percentile_lower":-2.0,"percentile_upper":3.0}"#; assert!(serde_json::from_str::(payload).is_err()); } From 8875104003bf39ef8c3ccd7066f1b7f164521d7a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:06:40 +0900 Subject: [PATCH 083/576] fix(validation): fail closed on impossible Monte Carlo SE --- crates/validation_core/src/monte_carlo.rs | 27 +++++++++++------------ 1 file changed, 13 insertions(+), 14 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 100bddcb1..13c88bd35 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -22,19 +22,20 @@ pub struct MonteCarloSummary { } impl MonteCarloSummary { - /// Validate structural and derived-field invariants for a Monte Carlo summary payload. + /// Validate structural and uncertainty-domain invariants for a Monte Carlo summary payload. /// - /// The standard error is a derived field, not an independently selectable - /// uncertainty estimate: it must equal `standard_deviation / sqrt(n)` for - /// the represented replication count. A nonzero standard deviation whose - /// implied standard error is not representable fails closed rather than - /// becoming false zero uncertainty. + /// A standard error of the mean cannot exceed its sample standard deviation. + /// A nonzero sample standard deviation cannot carry exact-zero standard + /// error, because finite replication counts cannot erase all uncertainty. + /// A singleton summary uses the canonical zero-spread/zero-SE convention + /// produced by [`summarize_replications`]. These admission checks prevent + /// finite but impossible uncertainty evidence from becoming durable. /// /// # Errors /// /// Returns [`ValidationError::InvalidInput`] when counts or numeric fields - /// violate the summary contract or the standard error disagrees with the - /// standard deviation and replication count. + /// violate the summary contract or the standard-error field is impossible + /// for the represented sample spread/count. pub fn validate(self) -> Result { if self.replication_count == 0 { return Err(ValidationError::InvalidInput); @@ -56,12 +57,10 @@ impl MonteCarloSummary { if self.percentile_lower > self.percentile_upper { return Err(ValidationError::InvalidInput); } - - let implied_standard_error = - self.standard_deviation / (self.replication_count as f64).sqrt(); - if !implied_standard_error.is_finite() - || (implied_standard_error == 0.0 && self.standard_deviation != 0.0) - || self.standard_error != implied_standard_error + if (self.standard_error == 0.0 && self.standard_deviation != 0.0) + || self.standard_error > self.standard_deviation + || (self.replication_count == 1 + && (self.standard_deviation != 0.0 || self.standard_error != 0.0)) { return Err(ValidationError::InvalidInput); } From 858368c8fda0d7a33f95b821eba2c5b80dc65cfd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:06:54 +0900 Subject: [PATCH 084/576] test(validation): cover Monte Carlo uncertainty admission edges --- ...carlo_standard_error_coherence_contract.rs | 27 +++++++++++++------ 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs b/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs index 089ae87d0..c2703116a 100644 --- a/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs +++ b/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs @@ -1,22 +1,33 @@ use validation_core::{MonteCarloSummary, ValidationError}; -fn impossible_standard_error_summary() -> MonteCarloSummary { +fn summary(replication_count: usize, standard_deviation: f64, standard_error: f64) -> MonteCarloSummary { MonteCarloSummary { - replication_count: 4, + replication_count, mean: 0.5, - standard_deviation: 0.5, - standard_error: 1.0, + standard_deviation, + standard_error, percentile_lower: -2.0, percentile_upper: 3.0, } } #[test] -fn monte_carlo_summary_rejects_standard_error_larger_than_sample_sd() { - let summary = impossible_standard_error_summary(); +fn monte_carlo_summary_rejects_impossible_standard_error_evidence() { + let larger_than_sd = summary(4, 0.5, 1.0); + assert_eq!(larger_than_sd.validate(), Err(ValidationError::InvalidInput)); + assert!(serde_json::to_string(&larger_than_sd).is_err()); - assert_eq!(summary.validate(), Err(ValidationError::InvalidInput)); - assert!(serde_json::to_string(&summary).is_err()); + let false_zero_uncertainty = summary(4, 0.5, 0.0); + assert_eq!( + false_zero_uncertainty.validate(), + Err(ValidationError::InvalidInput) + ); + + let impossible_singleton_spread = summary(1, 0.5, 0.5); + assert_eq!( + impossible_singleton_spread.validate(), + Err(ValidationError::InvalidInput) + ); let payload = r#"{"replication_count":4,"mean":0.5,"standard_deviation":0.5,"standard_error":1.0,"percentile_lower":-2.0,"percentile_upper":3.0}"#; assert!(serde_json::from_str::(payload).is_err()); From aaf60b83c9fabad4211df04f1a51b06c7297e8d3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:07:17 +0900 Subject: [PATCH 085/576] docs(validation): record Monte Carlo uncertainty coherence repair --- .../validation-monte-carlo-summary-uncertainty-coherence.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-monte-carlo-summary-uncertainty-coherence.md diff --git a/CHANGELOG.d/validation-monte-carlo-summary-uncertainty-coherence.md b/CHANGELOG.d/validation-monte-carlo-summary-uncertainty-coherence.md new file mode 100644 index 000000000..0c455d74e --- /dev/null +++ b/CHANGELOG.d/validation-monte-carlo-summary-uncertainty-coherence.md @@ -0,0 +1,3 @@ +### Fixed + +- Validation Evidence now rejects `MonteCarloSummary` payloads whose standard error is impossible for the represented sample spread/count: nonzero sample SD with exact-zero SE, SE larger than SD, or nonzero singleton spread/SE. This prevents finite serialized evidence from claiming less or more Monte Carlo uncertainty than the summary contract can represent. From e2d0c057d39b7786dbd96528d4e259775c6c2e01 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:07:47 +0900 Subject: [PATCH 086/576] test(validation): reject multi-replication SE equal to SD --- .../tests/monte_carlo_standard_error_coherence_contract.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs b/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs index c2703116a..edafff7df 100644 --- a/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs +++ b/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs @@ -13,6 +13,12 @@ fn summary(replication_count: usize, standard_deviation: f64, standard_error: f6 #[test] fn monte_carlo_summary_rejects_impossible_standard_error_evidence() { + let equal_to_sd_with_multiple_replications = summary(4, 0.5, 0.5); + assert_eq!( + equal_to_sd_with_multiple_replications.validate(), + Err(ValidationError::InvalidInput) + ); + let larger_than_sd = summary(4, 0.5, 1.0); assert_eq!(larger_than_sd.validate(), Err(ValidationError::InvalidInput)); assert!(serde_json::to_string(&larger_than_sd).is_err()); From 0e973b566ec969d0fea8b7403bf09602cdebd4a3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:09:33 +0900 Subject: [PATCH 087/576] fix(validation): tighten multi-replication SE domain --- crates/validation_core/src/monte_carlo.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 13c88bd35..5b3457fc4 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -24,12 +24,13 @@ pub struct MonteCarloSummary { impl MonteCarloSummary { /// Validate structural and uncertainty-domain invariants for a Monte Carlo summary payload. /// - /// A standard error of the mean cannot exceed its sample standard deviation. - /// A nonzero sample standard deviation cannot carry exact-zero standard - /// error, because finite replication counts cannot erase all uncertainty. - /// A singleton summary uses the canonical zero-spread/zero-SE convention - /// produced by [`summarize_replications`]. These admission checks prevent - /// finite but impossible uncertainty evidence from becoming durable. + /// A standard error of the mean from more than one replication is strictly + /// smaller than a nonzero sample standard deviation. A nonzero sample + /// standard deviation cannot carry exact-zero standard error, because a + /// finite replication count cannot erase all uncertainty. A singleton + /// summary uses the canonical zero-spread/zero-SE convention produced by + /// [`summarize_replications`]. These admission checks prevent finite but + /// impossible uncertainty evidence from becoming durable. /// /// # Errors /// @@ -58,7 +59,9 @@ impl MonteCarloSummary { return Err(ValidationError::InvalidInput); } if (self.standard_error == 0.0 && self.standard_deviation != 0.0) - || self.standard_error > self.standard_deviation + || (self.replication_count > 1 + && self.standard_deviation != 0.0 + && self.standard_error >= self.standard_deviation) || (self.replication_count == 1 && (self.standard_deviation != 0.0 || self.standard_error != 0.0)) { From bc77b7081a8967e9f38a89c34fa4d68c737238c7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:09:50 +0900 Subject: [PATCH 088/576] docs(research): trace Monte Carlo uncertainty coherence --- ...nte-carlo-summary-uncertainty-coherence.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 docs/research/monte-carlo-summary-uncertainty-coherence.md diff --git a/docs/research/monte-carlo-summary-uncertainty-coherence.md b/docs/research/monte-carlo-summary-uncertainty-coherence.md new file mode 100644 index 000000000..c75a951ed --- /dev/null +++ b/docs/research/monte-carlo-summary-uncertainty-coherence.md @@ -0,0 +1,38 @@ +# Monte Carlo summary uncertainty coherence + +## Problem + +`MonteCarloSummary` is durable Validation Evidence. Its `standard_error` field is the standard error of the replication mean, while `standard_deviation` is the sample SD over the retained replications. Before this repair, a payload could remain finite and ordered yet claim impossible uncertainty: exact-zero SE with nonzero replication spread, SE at least as large as SD with more than one replication, or nonzero sample spread/SE for the canonical singleton summary. + +That is an artifact-admission defect, not a new estimator. `summarize_replications` already computes `SE = SD / sqrt(n)` and fails closed when a nonzero SD projects to exact-zero SE. The missing boundary was validation of externally constructed or deserialized summaries. + +## Evidence and decision + +Morris, White, and Crowther (2019) treat simulation studies as empirical experiments and require Monte Carlo standard errors to quantify uncertainty from a finite number of simulation repetitions. Their analysis separates the empirical spread of replication-level estimates from Monte Carlo uncertainty of derived performance measures. For a sample mean over independent replications, finite `n > 1` implies a positive SEM strictly smaller than a positive sample SD; a positive SD cannot legitimately become exact-zero SEM at a finite replication count. + +TEPP therefore keeps `MonteCarloSummary` sign-neutral so it can summarize signed metrics such as bias, but tightens uncertainty-domain admission: + +- `replication_count` must remain positive; +- SD and SE must remain finite and nonnegative; +- nonzero SD with exact-zero SE is rejected; +- for `n > 1`, nonzero SE must be strictly smaller than nonzero SD; +- the canonical `n = 1` summary has zero SD and zero SE; +- percentile ordering remains a separate generic invariant. + +The repair deliberately does not require serialized clients to reproduce TEPP's exact binary64 `SD / sqrt(n)` bit pattern. The canonical producer still computes that value; admission rejects scientifically impossible uncertainty while avoiding a cross-language bit-for-bit serialization requirement. + +## Traceability + +- Bounded context: Validation Evidence. +- Production module: `crates/validation_core/src/monte_carlo.rs`. +- Public contract: `MonteCarloSummary::validate`, serde ingress/egress through the existing `MonteCarloSummary` implementations. +- Regression: `crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs`. +- Isolating RED: `e2d0c057d39b7786dbd96528d4e259775c6c2e01` demonstrates that `n = 4`, `SD = 0.5`, `SE = 0.5` was still admitted even though a finite multi-replication SEM must be strictly smaller than a positive SD. +- Causal repair: `0e973b566ec969d0fea8b7403bf09602cdebd4a3` changes the multi-replication admission boundary from `SE > SD` to `SE >= SD` while preserving the earlier false-zero and singleton guards. +- Changelog: `CHANGELOG.d/validation-monte-carlo-summary-uncertainty-coherence.md`. + +This remains TEPP-owned artifact validation. It does not duplicate reusable static psychometric arithmetic from `fast-mlsirm`, introduce LLM authority, or change the scientific definition of RMSE, bias, coverage, or any longitudinal estimand. + +## Reference + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 0a4c242fbd1c5b2f35e71a1ca1665ca9f7861338 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:25:37 +0900 Subject: [PATCH 089/576] test(validation): require canonical Monte Carlo SEM coherence --- ...onte_carlo_standard_error_coherence_contract.rs | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs b/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs index edafff7df..f8746c665 100644 --- a/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs +++ b/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs @@ -13,6 +13,12 @@ fn summary(replication_count: usize, standard_deviation: f64, standard_error: f6 #[test] fn monte_carlo_summary_rejects_impossible_standard_error_evidence() { + let understated_for_n = summary(4, 0.5, 0.2); + assert_eq!( + understated_for_n.validate(), + Err(ValidationError::InvalidInput) + ); + let equal_to_sd_with_multiple_replications = summary(4, 0.5, 0.5); assert_eq!( equal_to_sd_with_multiple_replications.validate(), @@ -29,12 +35,18 @@ fn monte_carlo_summary_rejects_impossible_standard_error_evidence() { Err(ValidationError::InvalidInput) ); + let zero_spread_with_positive_uncertainty = summary(4, 0.0, 0.1); + assert_eq!( + zero_spread_with_positive_uncertainty.validate(), + Err(ValidationError::InvalidInput) + ); + let impossible_singleton_spread = summary(1, 0.5, 0.5); assert_eq!( impossible_singleton_spread.validate(), Err(ValidationError::InvalidInput) ); - let payload = r#"{"replication_count":4,"mean":0.5,"standard_deviation":0.5,"standard_error":1.0,"percentile_lower":-2.0,"percentile_upper":3.0}"#; + let payload = r#"{"replication_count":4,"mean":0.5,"standard_deviation":0.5,"standard_error":0.2,"percentile_lower":-2.0,"percentile_upper":3.0}"#; assert!(serde_json::from_str::(payload).is_err()); } From 9b53076a53032441623ff487a006ec6d20812030 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:37:26 +0900 Subject: [PATCH 090/576] fix(validation): enforce Monte Carlo SEM coherence --- crates/validation_core/src/monte_carlo.rs | 45 ++++++++++++++--------- 1 file changed, 28 insertions(+), 17 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 5b3457fc4..208b4ef06 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -4,6 +4,8 @@ use crate::ValidationError; use crate::input::require_finite; use crate::numeric::{deterministic_compensated_sum, deterministic_representable_mean}; +const STANDARD_ERROR_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; + /// Summary of Monte Carlo replications for a scalar metric. #[derive(Clone, Copy, Debug, PartialEq)] pub struct MonteCarloSummary { @@ -24,19 +26,18 @@ pub struct MonteCarloSummary { impl MonteCarloSummary { /// Validate structural and uncertainty-domain invariants for a Monte Carlo summary payload. /// - /// A standard error of the mean from more than one replication is strictly - /// smaller than a nonzero sample standard deviation. A nonzero sample - /// standard deviation cannot carry exact-zero standard error, because a - /// finite replication count cannot erase all uncertainty. A singleton - /// summary uses the canonical zero-spread/zero-SE convention produced by - /// [`summarize_replications`]. These admission checks prevent finite but - /// impossible uncertainty evidence from becoming durable. + /// `standard_error` is the standard error of the retained-replication mean, + /// so a positive sample SD must agree numerically with `SD / sqrt(n)`. + /// Admission allows a small relative binary64 tolerance rather than requiring + /// cross-language bit-for-bit equality, but rejects materially understated or + /// overstated uncertainty. Zero spread requires zero SE, and the canonical + /// singleton summary has zero spread and zero SE. /// /// # Errors /// /// Returns [`ValidationError::InvalidInput`] when counts or numeric fields - /// violate the summary contract or the standard-error field is impossible - /// for the represented sample spread/count. + /// violate the summary contract or the standard-error field is incoherent + /// with the represented sample spread/count. pub fn validate(self) -> Result { if self.replication_count == 0 { return Err(ValidationError::InvalidInput); @@ -58,14 +59,24 @@ impl MonteCarloSummary { if self.percentile_lower > self.percentile_upper { return Err(ValidationError::InvalidInput); } - if (self.standard_error == 0.0 && self.standard_deviation != 0.0) - || (self.replication_count > 1 - && self.standard_deviation != 0.0 - && self.standard_error >= self.standard_deviation) - || (self.replication_count == 1 - && (self.standard_deviation != 0.0 || self.standard_error != 0.0)) - { - return Err(ValidationError::InvalidInput); + if self.standard_deviation == 0.0 { + if self.standard_error != 0.0 { + return Err(ValidationError::InvalidInput); + } + } else { + if self.replication_count == 1 || self.standard_error == 0.0 { + return Err(ValidationError::InvalidInput); + } + let expected_standard_error = + self.standard_deviation / (self.replication_count as f64).sqrt(); + if expected_standard_error == 0.0 { + return Err(ValidationError::InvalidInput); + } + let relative_error = + (self.standard_error / expected_standard_error - 1.0).abs(); + if !relative_error.is_finite() || relative_error > STANDARD_ERROR_RELATIVE_TOLERANCE { + return Err(ValidationError::InvalidInput); + } } Ok(self) } From 141246cfef7c44b327f7bfbeb22bc51279c6b9f0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:38:05 +0900 Subject: [PATCH 091/576] test(validation): permit floating-point SEM tolerance --- .../tests/monte_carlo_standard_error_coherence_contract.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs b/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs index f8746c665..62871fa6f 100644 --- a/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs +++ b/crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs @@ -19,6 +19,10 @@ fn monte_carlo_summary_rejects_impossible_standard_error_evidence() { Err(ValidationError::InvalidInput) ); + let canonical_standard_error = 0.5 / 4.0_f64.sqrt(); + let adjacent_standard_error = f64::from_bits(canonical_standard_error.to_bits() + 1); + assert!(summary(4, 0.5, adjacent_standard_error).validate().is_ok()); + let equal_to_sd_with_multiple_replications = summary(4, 0.5, 0.5); assert_eq!( equal_to_sd_with_multiple_replications.validate(), From 24a48acc5502bf1b773067c772ea7ca7e5722a2d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:38:44 +0900 Subject: [PATCH 092/576] docs(validation): record canonical SEM coherence --- .../validation-monte-carlo-summary-uncertainty-coherence.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-monte-carlo-summary-uncertainty-coherence.md b/CHANGELOG.d/validation-monte-carlo-summary-uncertainty-coherence.md index 0c455d74e..ac3b63ee0 100644 --- a/CHANGELOG.d/validation-monte-carlo-summary-uncertainty-coherence.md +++ b/CHANGELOG.d/validation-monte-carlo-summary-uncertainty-coherence.md @@ -1,3 +1,3 @@ ### Fixed -- Validation Evidence now rejects `MonteCarloSummary` payloads whose standard error is impossible for the represented sample spread/count: nonzero sample SD with exact-zero SE, SE larger than SD, or nonzero singleton spread/SE. This prevents finite serialized evidence from claiming less or more Monte Carlo uncertainty than the summary contract can represent. +- Validation Evidence now rejects `MonteCarloSummary` payloads whose `standard_error` is incoherent with the represented `standard_deviation` and `replication_count`. Positive spread must agree with `SD / sqrt(n)` within a small binary64 relative tolerance, zero spread requires zero SE, and singleton summaries require zero spread/SE. This prevents finite serialized evidence from materially understating or overstating Monte Carlo uncertainty without imposing cross-language bit-for-bit equality. From f674183b55e1855cef45561be6d38d9935c62b50 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 06:39:28 +0900 Subject: [PATCH 093/576] docs(research): trace Monte Carlo SEM coherence --- ...nte-carlo-summary-uncertainty-coherence.md | 23 +++++++++++-------- 1 file changed, 14 insertions(+), 9 deletions(-) diff --git a/docs/research/monte-carlo-summary-uncertainty-coherence.md b/docs/research/monte-carlo-summary-uncertainty-coherence.md index c75a951ed..3b43dd5bf 100644 --- a/docs/research/monte-carlo-summary-uncertainty-coherence.md +++ b/docs/research/monte-carlo-summary-uncertainty-coherence.md @@ -2,24 +2,26 @@ ## Problem -`MonteCarloSummary` is durable Validation Evidence. Its `standard_error` field is the standard error of the replication mean, while `standard_deviation` is the sample SD over the retained replications. Before this repair, a payload could remain finite and ordered yet claim impossible uncertainty: exact-zero SE with nonzero replication spread, SE at least as large as SD with more than one replication, or nonzero sample spread/SE for the canonical singleton summary. +`MonteCarloSummary` is durable Validation Evidence. Its `standard_error` field is the standard error of the retained-replication mean, while `standard_deviation` is the sample SD over those replications. The canonical producer therefore defines `SE = SD / sqrt(n)`. Earlier admission checks rejected several impossible cases but still allowed materially understated or overstated positive SE values whenever they were merely finite, positive, and smaller than SD. For example, `n = 4`, `SD = 0.5`, `SE = 0.2` passed even though the represented summary contract implies `SE = 0.25`. -That is an artifact-admission defect, not a new estimator. `summarize_replications` already computes `SE = SD / sqrt(n)` and fails closed when a nonzero SD projects to exact-zero SE. The missing boundary was validation of externally constructed or deserialized summaries. +That is an artifact-admission defect, not a new estimator. `summarize_replications` already computes the canonical relation and fails closed when a nonzero SD projects to exact-zero SE. The missing boundary was coherence validation for externally constructed or deserialized summaries. ## Evidence and decision -Morris, White, and Crowther (2019) treat simulation studies as empirical experiments and require Monte Carlo standard errors to quantify uncertainty from a finite number of simulation repetitions. Their analysis separates the empirical spread of replication-level estimates from Monte Carlo uncertainty of derived performance measures. For a sample mean over independent replications, finite `n > 1` implies a positive SEM strictly smaller than a positive sample SD; a positive SD cannot legitimately become exact-zero SEM at a finite replication count. +Morris, White, and Crowther (2019) treat simulation studies as empirical experiments and require Monte Carlo standard errors to quantify uncertainty from a finite number of simulation repetitions. For the mean of independent replication-level values, the Monte Carlo standard error is the empirical replication SD divided by the square root of the number of replications. The durable summary therefore cannot admit an arbitrary positive SE independently of its represented SD and replication count. -TEPP therefore keeps `MonteCarloSummary` sign-neutral so it can summarize signed metrics such as bias, but tightens uncertainty-domain admission: +TEPP keeps `MonteCarloSummary` sign-neutral so it can summarize signed metrics such as bias, while tightening uncertainty coherence: - `replication_count` must remain positive; - SD and SE must remain finite and nonnegative; -- nonzero SD with exact-zero SE is rejected; -- for `n > 1`, nonzero SE must be strictly smaller than nonzero SD; +- zero SD requires exact-zero SE; +- positive SD requires `n > 1` and a positive representable SE; +- positive SE must agree with `SD / sqrt(n)` within `64 * f64::EPSILON` relative error; +- the tolerance is deliberately wider than bit-for-bit equality so adjacent correctly rounded binary64 results from independent implementations remain admissible, while materially understated or overstated uncertainty is rejected; - the canonical `n = 1` summary has zero SD and zero SE; - percentile ordering remains a separate generic invariant. -The repair deliberately does not require serialized clients to reproduce TEPP's exact binary64 `SD / sqrt(n)` bit pattern. The canonical producer still computes that value; admission rejects scientifically impossible uncertainty while avoiding a cross-language bit-for-bit serialization requirement. +The relative comparison is scale-free. If canonical `SD / sqrt(n)` itself becomes exact zero while SD is nonzero, the payload fails closed instead of presenting zero Monte Carlo uncertainty. ## Traceability @@ -27,8 +29,11 @@ The repair deliberately does not require serialized clients to reproduce TEPP's - Production module: `crates/validation_core/src/monte_carlo.rs`. - Public contract: `MonteCarloSummary::validate`, serde ingress/egress through the existing `MonteCarloSummary` implementations. - Regression: `crates/validation_core/tests/monte_carlo_standard_error_coherence_contract.rs`. -- Isolating RED: `e2d0c057d39b7786dbd96528d4e259775c6c2e01` demonstrates that `n = 4`, `SD = 0.5`, `SE = 0.5` was still admitted even though a finite multi-replication SEM must be strictly smaller than a positive SD. -- Causal repair: `0e973b566ec969d0fea8b7403bf09602cdebd4a3` changes the multi-replication admission boundary from `SE > SD` to `SE >= SD` while preserving the earlier false-zero and singleton guards. +- Earlier isolating RED: `e2d0c057d39b7786dbd96528d4e259775c6c2e01` demonstrated that a multi-replication SE equal to SD was admitted. +- Earlier repair: `0e973b566ec969d0fea8b7403bf09602cdebd4a3` rejected exact-zero SE with positive spread and multi-replication `SE >= SD` without asserting the canonical relationship. +- Canonical-coherence RED: `0a4c242fbd1c5b2f35e71a1ca1665ca9f7861338` demonstrates that `n = 4`, `SD = 0.5`, `SE = 0.2` was still admitted and that zero spread could still carry positive SE. +- Causal repair: `9b53076a53032441623ff487a006ec6d20812030` validates the represented `SD / sqrt(n)` relationship and zero-spread contract. +- Cross-language tolerance regression: `141246cfef7c44b327f7bfbeb22bc51279c6b9f0` admits the adjacent binary64 value to the canonical SE so admission is not a bit-pattern equality gate. - Changelog: `CHANGELOG.d/validation-monte-carlo-summary-uncertainty-coherence.md`. This remains TEPP-owned artifact validation. It does not duplicate reusable static psychometric arithmetic from `fast-mlsirm`, introduce LLM authority, or change the scientific definition of RMSE, bias, coverage, or any longitudinal estimand. From a17dfe1bd9d845356b757ec98e930b5d927eab8f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 07:01:00 +0900 Subject: [PATCH 094/576] test(validation): reject impossible zero-mean RMSE summaries --- ..._report_zero_mean_rmse_summary_contract.rs | 53 +++++++++++++++++++ 1 file changed, 53 insertions(+) create mode 100644 crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs diff --git a/crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs b/crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs new file mode 100644 index 000000000..8d85bc0ba --- /dev/null +++ b/crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs @@ -0,0 +1,53 @@ +use validation_core::{MonteCarloSummary, ValidationError, ValidationReport}; + +fn report_with(summary: MonteCarloSummary) -> ValidationReport { + ValidationReport { + study_label: "zero-mean-rmse-summary".into(), + rmse: 0.0, + rmse_standard_error: 0.0, + mean_bias: 0.0, + bias_standard_error: 0.0, + interval_coverage: 1.0, + coverage_wilson_lower: 0.5, + coverage_wilson_upper: 1.0, + temporal_order_accuracy: 1.0, + monte_carlo_rmse: Some(summary), + } +} + +#[test] +fn zero_mean_monte_carlo_rmse_requires_zero_spread_and_zero_percentiles() { + let positive_spread = MonteCarloSummary { + replication_count: 4, + mean: 0.0, + standard_deviation: 1.0, + standard_error: 0.5, + percentile_lower: 0.0, + percentile_upper: 1.0, + }; + assert!(positive_spread.validate().is_ok()); + let report = report_with(positive_spread); + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + assert_eq!(report.to_json(), Err(ValidationError::InvalidInput)); + assert_eq!( + report.to_human_summary(), + Err(ValidationError::InvalidInput) + ); + + let positive_percentile = MonteCarloSummary { + replication_count: 4, + mean: 0.0, + standard_deviation: 0.0, + standard_error: 0.0, + percentile_lower: 0.0, + percentile_upper: 1.0, + }; + assert!(positive_percentile.validate().is_ok()); + assert_eq!( + report_with(positive_percentile).validate(), + Err(ValidationError::InvalidInput) + ); + + let payload = r#"{"study_label":"zero-mean-rmse-summary","rmse":0.0,"rmse_standard_error":0.0,"mean_bias":0.0,"bias_standard_error":0.0,"interval_coverage":1.0,"coverage_wilson_lower":0.5,"coverage_wilson_upper":1.0,"temporal_order_accuracy":1.0,"monte_carlo_rmse":{"replication_count":4,"mean":0.0,"standard_deviation":1.0,"standard_error":0.5,"percentile_lower":0.0,"percentile_upper":1.0}}"#; + assert!(serde_json::from_str::(payload).is_err()); +} From d17d803415333f79bf6291efed9206a630979adf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 07:01:44 +0900 Subject: [PATCH 095/576] fix(validation): enforce perfect-recovery RMSE summary coherence --- crates/validation_core/src/report.rs | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index caa629201..3431069f6 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -38,9 +38,11 @@ impl ValidationReport { /// recorded in the same report. Mean signed bias remains unrestricted in /// sign. A generic [`MonteCarloSummary`] may summarize a signed metric, but /// when it occupies `monte_carlo_rmse` its mean and percentile endpoints are - /// nonnegative because every RMSE replication is nonnegative. These checks - /// prevent a finite but scientifically impossible payload from becoming - /// durable Validation Evidence. + /// nonnegative because every RMSE replication is nonnegative. A zero Monte + /// Carlo RMSE mean is exact perfect recovery across every retained replication, + /// so spread, standard error, and empirical percentile endpoints must all be + /// zero as well. These checks prevent a finite but scientifically impossible + /// payload from becoming durable Validation Evidence. /// /// # Errors /// @@ -89,6 +91,14 @@ impl ValidationReport { { return Err(ValidationError::InvalidInput); } + if summary.mean == 0.0 + && (summary.standard_deviation != 0.0 + || summary.standard_error != 0.0 + || summary.percentile_lower != 0.0 + || summary.percentile_upper != 0.0) + { + return Err(ValidationError::InvalidInput); + } } Ok(()) } @@ -255,7 +265,7 @@ mod tests { replication_count: 10, mean: 0.11, standard_deviation: 0.01, - standard_error: 0.003, + standard_error: 0.01 / 10.0_f64.sqrt(), percentile_lower: 0.09, percentile_upper: 0.13, }), From 42be85748c4e4136c54fd1f918dfa89533376451 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 07:01:57 +0900 Subject: [PATCH 096/576] docs(validation): record zero-mean RMSE summary invariant --- CHANGELOG.d/validation-report-zero-mean-rmse-summary.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-report-zero-mean-rmse-summary.md diff --git a/CHANGELOG.d/validation-report-zero-mean-rmse-summary.md b/CHANGELOG.d/validation-report-zero-mean-rmse-summary.md new file mode 100644 index 000000000..f4a052cdd --- /dev/null +++ b/CHANGELOG.d/validation-report-zero-mean-rmse-summary.md @@ -0,0 +1,3 @@ +### Fixed + +- Reject `ValidationReport::monte_carlo_rmse` payloads whose exact-zero mean is paired with nonzero spread, standard error, or empirical percentile endpoints; for nonnegative RMSE replications, a zero mean is exact perfect recovery in every retained replication. The report fixture now also uses the canonical `SD / sqrt(n)` Monte Carlo standard error required by the generic summary contract. From f5636bbabdb1484e7558dd017d018c721582cd61 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 07:02:17 +0900 Subject: [PATCH 097/576] docs(research): trace zero-mean RMSE evidence coherence --- ...alidation-report-zero-mean-rmse-summary.md | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 docs/research/validation-report-zero-mean-rmse-summary.md diff --git a/docs/research/validation-report-zero-mean-rmse-summary.md b/docs/research/validation-report-zero-mean-rmse-summary.md new file mode 100644 index 000000000..08e25abe1 --- /dev/null +++ b/docs/research/validation-report-zero-mean-rmse-summary.md @@ -0,0 +1,32 @@ +# Zero-mean Monte Carlo RMSE summary admission + +## Problem + +`MonteCarloSummary` is deliberately sign-neutral because the same carrier can summarize signed quantities such as bias. `ValidationReport::monte_carlo_rmse` is narrower: every retained replication is an RMSE and is therefore nonnegative. The report boundary already rejected negative means and percentile endpoints, but it still admitted an exact-zero Monte Carlo RMSE mean together with positive spread, positive standard error, or positive empirical percentile endpoints. + +For a finite set of nonnegative represented RMSE replications, an arithmetic mean of exactly zero implies that every retained replication is exactly zero. The sample standard deviation, standard error of the mean, and every empirical percentile are consequently zero. Treating a zero mean with positive uncertainty/support as valid durable evidence would describe perfect average recovery and non-perfect replications at the same time. + +## Decision + +Keep `MonteCarloSummary` generic and sign-neutral. Enforce the stronger invariant only when the summary occupies `ValidationReport::monte_carlo_rmse`: if `mean == 0.0`, `standard_deviation`, `standard_error`, `percentile_lower`, and `percentile_upper` must all equal numerical zero. IEEE signed zero is one zero-valued scientific result, so `-0.0` is accepted wherever numerical equality to zero holds. + +The change also repairs the pre-existing report round-trip fixture to use the generic summary contract's canonical Monte Carlo standard error, `SD / sqrt(n)`, rather than the approximate literal `0.003` for `SD = 0.01` and `n = 10`. + +## Alternatives rejected + +- Reject zero-mean/positive-spread summaries in `MonteCarloSummary` globally: rejected because a generic signed metric can have zero mean with positive spread. +- Require the Monte Carlo RMSE mean to lie between stored percentile endpoints: rejected because the stored percentile levels are not part of the payload, and skewed nonnegative samples can legitimately have means outside a selected percentile interval. +- Reconstruct hidden replications from summary fields: impossible and outside the evidence contract. + +## Traceability + +- Public RED: `a17dfe1bd9d845356b757ec98e930b5d927eab8f`, `crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs`. +- Causal repair: `d17d803415333f79bf6291efed9206a630979adf`, `ValidationReport::validate` in `crates/validation_core/src/report.rs`. +- Release note: `42be85748c4e4136c54fd1f918dfa89533376451`, `CHANGELOG.d/validation-report-zero-mean-rmse-summary.md`. +- Owner: TEPP Validation Evidence. No reusable static psychometric estimator or mutable sibling implementation is introduced. + +## Methodological basis + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086. The ADEMP framework separates estimands and performance measures and treats Monte Carlo uncertainty as uncertainty of an explicitly defined performance measure. That supports rejecting artifacts whose stored moments cannot jointly represent the declared RMSE performance measure. + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. The 2014 edition remains the current published edition; this repair concerns the integrity and interpretability of validation evidence rather than replacing substantive validity arguments with an arithmetic gate. From 944fa96e89e31123e97f95f6909835026c0bd6fa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 07:03:34 +0900 Subject: [PATCH 098/576] test(validation): cover exact-zero RMSE summary admission --- ...lidation_report_zero_mean_rmse_summary_contract.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs b/crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs index 8d85bc0ba..fe71eda69 100644 --- a/crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs +++ b/crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs @@ -48,6 +48,17 @@ fn zero_mean_monte_carlo_rmse_requires_zero_spread_and_zero_percentiles() { Err(ValidationError::InvalidInput) ); + let perfect_recovery = MonteCarloSummary { + replication_count: 4, + mean: -0.0, + standard_deviation: 0.0, + standard_error: -0.0, + percentile_lower: -0.0, + percentile_upper: 0.0, + }; + assert!(perfect_recovery.validate().is_ok()); + assert!(report_with(perfect_recovery).validate().is_ok()); + let payload = r#"{"study_label":"zero-mean-rmse-summary","rmse":0.0,"rmse_standard_error":0.0,"mean_bias":0.0,"bias_standard_error":0.0,"interval_coverage":1.0,"coverage_wilson_lower":0.5,"coverage_wilson_upper":1.0,"temporal_order_accuracy":1.0,"monte_carlo_rmse":{"replication_count":4,"mean":0.0,"standard_deviation":1.0,"standard_error":0.5,"percentile_lower":0.0,"percentile_upper":1.0}}"#; assert!(serde_json::from_str::(payload).is_err()); } From 70c3e18961e881dbdebb84a9e117b5f2be1cec31 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 07:03:51 +0900 Subject: [PATCH 099/576] docs(research): trace zero-mean RMSE edge contract --- docs/research/validation-report-zero-mean-rmse-summary.md | 1 + 1 file changed, 1 insertion(+) diff --git a/docs/research/validation-report-zero-mean-rmse-summary.md b/docs/research/validation-report-zero-mean-rmse-summary.md index 08e25abe1..8249e9f04 100644 --- a/docs/research/validation-report-zero-mean-rmse-summary.md +++ b/docs/research/validation-report-zero-mean-rmse-summary.md @@ -23,6 +23,7 @@ The change also repairs the pre-existing report round-trip fixture to use the ge - Public RED: `a17dfe1bd9d845356b757ec98e930b5d927eab8f`, `crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs`. - Causal repair: `d17d803415333f79bf6291efed9206a630979adf`, `ValidationReport::validate` in `crates/validation_core/src/report.rs`. - Release note: `42be85748c4e4136c54fd1f918dfa89533376451`, `CHANGELOG.d/validation-report-zero-mean-rmse-summary.md`. +- Edge contract: `944fa96e89e31123e97f95f6909835026c0bd6fa`; exact signed-zero perfect recovery remains admissible while zero-mean positive spread/support fails closed. - Owner: TEPP Validation Evidence. No reusable static psychometric estimator or mutable sibling implementation is introduced. ## Methodological basis From ce21941a64c9e54c1ff7dd5914581966708215b8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 08:00:16 +0900 Subject: [PATCH 100/576] test(validation): reject impossible zero-spread Monte Carlo support --- ...onte_carlo_zero_spread_support_contract.rs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 crates/validation_core/tests/monte_carlo_zero_spread_support_contract.rs diff --git a/crates/validation_core/tests/monte_carlo_zero_spread_support_contract.rs b/crates/validation_core/tests/monte_carlo_zero_spread_support_contract.rs new file mode 100644 index 000000000..aed50cb9c --- /dev/null +++ b/crates/validation_core/tests/monte_carlo_zero_spread_support_contract.rs @@ -0,0 +1,40 @@ +use validation_core::{MonteCarloSummary, ValidationError}; + +fn summary( + replication_count: usize, + mean: f64, + percentile_lower: f64, + percentile_upper: f64, +) -> MonteCarloSummary { + MonteCarloSummary { + replication_count, + mean, + standard_deviation: 0.0, + standard_error: 0.0, + percentile_lower, + percentile_upper, + } +} + +#[test] +fn zero_sample_spread_requires_degenerate_empirical_support() { + let impossible = summary(4, 1.0, 0.5, 1.5); + assert_eq!(impossible.validate(), Err(ValidationError::InvalidInput)); + assert!(serde_json::to_string(&impossible).is_err()); + + let payload = r#"{"replication_count":4,"mean":1.0,"standard_deviation":0.0,"standard_error":0.0,"percentile_lower":0.5,"percentile_upper":1.5}"#; + assert!(serde_json::from_str::(payload).is_err()); + + let impossible_singleton = summary(1, 2.0, 1.0, 2.0); + assert_eq!( + impossible_singleton.validate(), + Err(ValidationError::InvalidInput) + ); + + let constant_signed = summary(4, -3.0, -3.0, -3.0); + assert!(constant_signed.validate().is_ok()); + assert!(serde_json::to_string(&constant_signed).is_ok()); + + let signed_zero = summary(4, -0.0, 0.0, -0.0); + assert!(signed_zero.validate().is_ok()); +} From d023ecdb9d32e1474c08af16eb7df25c3dc84fc1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 08:01:29 +0900 Subject: [PATCH 101/576] test(validation): separate extreme match decision from residual projection --- .../match_count_extreme_decision_contract.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 crates/validation_core/tests/match_count_extreme_decision_contract.rs diff --git a/crates/validation_core/tests/match_count_extreme_decision_contract.rs b/crates/validation_core/tests/match_count_extreme_decision_contract.rs new file mode 100644 index 000000000..cd44033fc --- /dev/null +++ b/crates/validation_core/tests/match_count_extreme_decision_contract.rs @@ -0,0 +1,16 @@ +use validation_core::{ValidationError, absolute_residuals, match_count}; + +#[test] +fn tolerance_match_decision_does_not_require_an_unrepresentable_residual() { + let truth = [f64::MAX]; + let recovered = [-f64::MAX]; + + assert_eq!( + absolute_residuals(&truth, &recovered), + Err(ValidationError::InvalidInput) + ); + assert_eq!(match_count(&truth, &recovered, f64::MAX), Ok(0)); + assert_eq!(match_count(&truth, &recovered, 0.0), Ok(0)); + + assert_eq!(match_count(&truth, &truth, 0.0), Ok(1)); +} From 5040ff96ce2845b2f633781667be8c71039aae7a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 08:01:51 +0900 Subject: [PATCH 102/576] fix(validation): decide extreme tolerance matches without residual projection --- crates/validation_core/src/matching.rs | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/crates/validation_core/src/matching.rs b/crates/validation_core/src/matching.rs index f189b1a6c..9b3b666ba 100644 --- a/crates/validation_core/src/matching.rs +++ b/crates/validation_core/src/matching.rs @@ -8,7 +8,8 @@ use crate::input::require_paired_finite; /// # Errors /// /// Returns [`ValidationError::InvalidInput`] when lengths differ, inputs are -/// empty, or any value is non-finite. +/// empty, any value is non-finite, or a finite input pair has an absolute +/// residual outside binary64 range. pub fn absolute_residuals(truth: &[f64], recovered: &[f64]) -> Result, ValidationError> { require_paired_finite(truth, recovered)?; let mut residuals = Vec::with_capacity(truth.len()); @@ -24,6 +25,13 @@ pub fn absolute_residuals(truth: &[f64], recovered: &[f64]) -> Result, /// Count exact matches within absolute tolerance `epsilon`. /// +/// The decision metric does not require every absolute residual to be +/// representable. If subtraction of two finite endpoints overflows, the true +/// absolute residual is larger than `f64::MAX` and therefore larger than every +/// admitted finite tolerance, so that pair is deterministically a mismatch. +/// `absolute_residuals` remains fail-closed when callers request the residual +/// magnitude itself. +/// /// # Errors /// /// Returns [`ValidationError::InvalidInput`] for bad vectors or non-finite @@ -39,10 +47,14 @@ pub fn match_count( if epsilon < 0.0 { return Err(ValidationError::InvalidConfiguration); } - let residuals = absolute_residuals(truth, recovered)?; - Ok(residuals + require_paired_finite(truth, recovered)?; + Ok(truth .iter() - .filter(|residual| **residual <= epsilon) + .zip(recovered) + .filter(|(truth_value, recovered_value)| { + let residual = (**truth_value - **recovered_value).abs(); + residual.is_finite() && residual <= epsilon + }) .count()) } @@ -80,10 +92,12 @@ mod tests { match_count(&truth, &recovered, f64::NAN), Err(ValidationError::InvalidInput) ); - // Opposite-sign extremes overflow the residual to infinity. + // Opposite-sign extremes have no representable binary64 residual. assert_eq!( absolute_residuals(&[f64::MAX], &[-f64::MAX]), Err(ValidationError::InvalidInput) ); + // The threshold decision is still exact for every finite epsilon. + assert_eq!(match_count(&[f64::MAX], &[-f64::MAX], f64::MAX), Ok(0)); } } From d0f5c14559831bc9032c6489a8013ac23686f894 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 08:02:33 +0900 Subject: [PATCH 103/576] fix(validation): bind zero Monte Carlo spread to degenerate support --- crates/validation_core/src/monte_carlo.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 208b4ef06..75a9fca61 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -30,14 +30,17 @@ impl MonteCarloSummary { /// so a positive sample SD must agree numerically with `SD / sqrt(n)`. /// Admission allows a small relative binary64 tolerance rather than requiring /// cross-language bit-for-bit equality, but rejects materially understated or - /// overstated uncertainty. Zero spread requires zero SE, and the canonical - /// singleton summary has zero spread and zero SE. + /// overstated uncertainty. Zero spread requires zero SE and degenerate + /// empirical percentile support at the represented mean; the same support + /// rule applies to the canonical singleton summary. Numeric equality keeps + /// IEEE `-0.0` and `+0.0` as one zero-valued scientific state. /// /// # Errors /// /// Returns [`ValidationError::InvalidInput`] when counts or numeric fields - /// violate the summary contract or the standard-error field is incoherent - /// with the represented sample spread/count. + /// violate the summary contract, empirical support contradicts zero sample + /// spread, or the standard-error field is incoherent with the represented + /// sample spread/count. pub fn validate(self) -> Result { if self.replication_count == 0 { return Err(ValidationError::InvalidInput); @@ -60,7 +63,10 @@ impl MonteCarloSummary { return Err(ValidationError::InvalidInput); } if self.standard_deviation == 0.0 { - if self.standard_error != 0.0 { + if self.standard_error != 0.0 + || self.percentile_lower != self.mean + || self.percentile_upper != self.mean + { return Err(ValidationError::InvalidInput); } } else { From 9067d3ca61532b03706f725e8accca8e8d4ac083 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 08:03:00 +0900 Subject: [PATCH 104/576] docs(validation): record support coherence and extreme match repair --- CHANGELOG.d/validation-summary-support-and-extreme-match.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-summary-support-and-extreme-match.md diff --git a/CHANGELOG.d/validation-summary-support-and-extreme-match.md b/CHANGELOG.d/validation-summary-support-and-extreme-match.md new file mode 100644 index 000000000..443c9f3e4 --- /dev/null +++ b/CHANGELOG.d/validation-summary-support-and-extreme-match.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core::MonteCarloSummary` now rejects zero-sample-spread artifacts whose empirical percentile support is not degenerate at the represented mean. Zero spread means every retained replication is identical, so a durable summary cannot simultaneously claim `SD = 0` and percentile support away from its mean; signed zero remains one numeric zero-valued state. +- `validation_core::match_count` now decides finite absolute-tolerance matches without requiring an unrepresentable residual magnitude. Opposite-sign finite extremes whose subtraction overflows are deterministically mismatches for every finite tolerance, while `absolute_residuals` continues to fail closed when the residual value itself is requested. From f7b018c5f10d11c7cc21a3430242e37c2d7a1056 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 08:07:28 +0900 Subject: [PATCH 105/576] test(validation): reject zero RMSE with positive standard error --- ...eport_zero_rmse_standard_error_contract.rs | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) create mode 100644 crates/validation_core/tests/validation_report_zero_rmse_standard_error_contract.rs diff --git a/crates/validation_core/tests/validation_report_zero_rmse_standard_error_contract.rs b/crates/validation_core/tests/validation_report_zero_rmse_standard_error_contract.rs new file mode 100644 index 000000000..e6ef00b3f --- /dev/null +++ b/crates/validation_core/tests/validation_report_zero_rmse_standard_error_contract.rs @@ -0,0 +1,40 @@ +use validation_core::{ + ValidationError, ValidationReport, rmse_standard_error, root_mean_square_error, +}; + +fn report_with(rmse: f64, rmse_standard_error: f64) -> ValidationReport { + ValidationReport { + study_label: "zero-rmse-standard-error".into(), + rmse, + rmse_standard_error, + mean_bias: 0.0, + bias_standard_error: 0.0, + interval_coverage: 1.0, + coverage_wilson_lower: 0.5, + coverage_wilson_upper: 1.0, + temporal_order_accuracy: 1.0, + monte_carlo_rmse: None, + } +} + +#[test] +fn exact_zero_rmse_requires_exact_zero_rmse_standard_error() { + let truth = [1.0, -2.0, 3.0]; + let recovered = truth; + assert_eq!(root_mean_square_error(&truth, &recovered), Ok(0.0)); + assert_eq!(rmse_standard_error(&truth, &recovered), Ok(0.0)); + + let impossible = report_with(0.0, 0.1); + assert_eq!(impossible.validate(), Err(ValidationError::InvalidInput)); + assert_eq!(impossible.to_json(), Err(ValidationError::InvalidInput)); + assert_eq!( + impossible.to_human_summary(), + Err(ValidationError::InvalidInput) + ); + + let signed_zero = report_with(-0.0, -0.0); + assert!(signed_zero.validate().is_ok()); + + let payload = r#"{"study_label":"zero-rmse-standard-error","rmse":0.0,"rmse_standard_error":0.1,"mean_bias":0.0,"bias_standard_error":0.0,"interval_coverage":1.0,"coverage_wilson_lower":0.5,"coverage_wilson_upper":1.0,"temporal_order_accuracy":1.0,"monte_carlo_rmse":null}"#; + assert!(serde_json::from_str::(payload).is_err()); +} From 4c5999186141dafd8d6293d5da66ab8e19693f5c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 08:08:33 +0900 Subject: [PATCH 106/576] fix(validation): bind zero RMSE to zero standard error --- crates/validation_core/src/report.rs | 16 +++++++++++----- 1 file changed, 11 insertions(+), 5 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index 3431069f6..6707fb113 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -32,8 +32,10 @@ pub struct ValidationReport { impl ValidationReport { /// Validate numeric and scientific invariants before serialization or export. /// - /// RMSE and standard errors are nonnegative; empirical coverage, Wilson - /// endpoints, and temporal-order accuracy are probabilities in `[0, 1]`; + /// RMSE and standard errors are nonnegative; an exact-zero RMSE is perfect + /// recovery and therefore requires an exact-zero RMSE standard error under + /// the crate's squared-residual delta-method definition. Empirical coverage, + /// Wilson endpoints, and temporal-order accuracy are probabilities in `[0, 1]`; /// the Wilson interval is ordered and must contain the empirical coverage /// recorded in the same report. Mean signed bias remains unrestricted in /// sign. A generic [`MonteCarloSummary`] may summarize a signed metric, but @@ -47,9 +49,10 @@ impl ValidationReport { /// # Errors /// /// Returns [`ValidationError::InvalidInput`] when any `f64` field is - /// non-finite, violates its metric domain, Wilson evidence is incoherent, or - /// the optional Monte Carlo RMSE summary violates either generic summary - /// invariants or the nonnegative RMSE domain. + /// non-finite, violates its metric domain, point RMSE and its standard error + /// contradict exact recovery, Wilson evidence is incoherent, or the optional + /// Monte Carlo RMSE summary violates either generic summary invariants or the + /// nonnegative RMSE domain. pub fn validate(&self) -> Result<(), ValidationError> { for value in [ self.rmse, @@ -69,6 +72,9 @@ impl ValidationReport { if self.rmse < 0.0 || self.rmse_standard_error < 0.0 || self.bias_standard_error < 0.0 { return Err(ValidationError::InvalidInput); } + if self.rmse == 0.0 && self.rmse_standard_error != 0.0 { + return Err(ValidationError::InvalidInput); + } if !(0.0..=1.0).contains(&self.interval_coverage) || !(0.0..=1.0).contains(&self.coverage_wilson_lower) || !(0.0..=1.0).contains(&self.coverage_wilson_upper) From 6ae5b254669868162428fc5c85538e9f5052ac6c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 08:08:47 +0900 Subject: [PATCH 107/576] docs(validation): record zero RMSE standard-error invariant --- CHANGELOG.d/validation-report-zero-rmse-standard-error.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-report-zero-rmse-standard-error.md diff --git a/CHANGELOG.d/validation-report-zero-rmse-standard-error.md b/CHANGELOG.d/validation-report-zero-rmse-standard-error.md new file mode 100644 index 000000000..4e8530697 --- /dev/null +++ b/CHANGELOG.d/validation-report-zero-rmse-standard-error.md @@ -0,0 +1,3 @@ +# Validation Evidence: zero RMSE standard-error coherence + +`ValidationReport` now rejects an exact-zero point RMSE paired with a positive RMSE standard error. Under TEPP's squared-residual delta-method definition, exact-zero RMSE means every residual is exactly zero, so its RMSE standard error is also exactly zero. Signed zero remains one numerical zero-valued scientific state. From 6c549e36398508ab8ee273f5395ef7bb80ae0363 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 08:08:59 +0900 Subject: [PATCH 108/576] docs(research): trace zero RMSE standard-error coherence --- ...idation-report-zero-rmse-standard-error.md | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 docs/research/validation-report-zero-rmse-standard-error.md diff --git a/docs/research/validation-report-zero-rmse-standard-error.md b/docs/research/validation-report-zero-rmse-standard-error.md new file mode 100644 index 000000000..83a12b557 --- /dev/null +++ b/docs/research/validation-report-zero-rmse-standard-error.md @@ -0,0 +1,33 @@ +# Point RMSE and RMSE standard-error coherence + +## Problem + +`ValidationReport` validated `rmse` and `rmse_standard_error` independently as finite, nonnegative numbers. That admitted a durable artifact with exact-zero RMSE and a positive RMSE standard error even though the canonical `validation_core::rmse_standard_error` producer returns exact zero whenever every residual is exactly zero. + +Within TEPP's declared RMSE contract, `RMSE = sqrt(mean(r_i^2))`. For finite represented residuals, exact-zero RMSE implies every `r_i` is exactly zero. The delta-method RMSE standard error implemented by `validation_core` is therefore also exactly zero. A report containing `rmse = 0` and `rmse_standard_error > 0` contradicts the metric definition even though each field is individually representable. + +## Decision + +Keep the RMSE arithmetic and standard-error estimator unchanged. Enforce the joint invariant only at `ValidationReport` admission: when `rmse == 0.0`, `rmse_standard_error` must also equal numerical zero. IEEE `-0.0` and `+0.0` remain one zero-valued scientific state. + +This is Validation Evidence artifact coherence, not a reusable psychometric estimator. No fast-mlsirm source or mutable sibling dependency is introduced. + +## Alternatives rejected + +- Make every positive RMSE require positive standard error: rejected because equal nonzero residual magnitudes can produce positive RMSE with exactly zero squared-residual spread and therefore zero RMSE standard error. +- Change `rmse_standard_error` arithmetic: rejected because the producer already returns zero for exact perfect recovery; the defect was at durable report admission. +- Infer hidden residuals from the report: rejected because the report intentionally stores only summary evidence. + +## Traceability + +- Public RED: `f7b018c5f10d11c7cc21a3430242e37c2d7a1056`, `crates/validation_core/tests/validation_report_zero_rmse_standard_error_contract.rs`. +- Causal repair: `4c5999186141dafd8d6293d5da66ab8e19693f5c`, `ValidationReport::validate` in `crates/validation_core/src/report.rs`. +- Release note: `6ae5b254669868162428fc5c85538e9f5052ac6c`, `CHANGELOG.d/validation-report-zero-rmse-standard-error.md`. +- Producer contract: `root_mean_square_error` and `rmse_standard_error` in `crates/validation_core/src/rmse.rs`. +- Owner: TEPP Validation Evidence. + +## Methodological basis + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086. ADEMP requires performance measures and their Monte Carlo or sampling uncertainty to be defined coherently; a stored point metric and uncertainty field that cannot jointly arise from the declared estimator should fail admission rather than become durable evidence. + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. The 2014 edition remains the current published edition while revision is underway; this repair strengthens internal consistency of validation evidence and does not substitute arithmetic checks for substantive validity arguments. From a2aca5b077665433aad0e5531d53360b599b64b3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 09:01:32 +0900 Subject: [PATCH 109/576] test(validation): reject impossible RMSE standard error support --- ...mse_standard_error_upper_bound_contract.rs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 crates/validation_core/tests/validation_report_rmse_standard_error_upper_bound_contract.rs diff --git a/crates/validation_core/tests/validation_report_rmse_standard_error_upper_bound_contract.rs b/crates/validation_core/tests/validation_report_rmse_standard_error_upper_bound_contract.rs new file mode 100644 index 000000000..17e5af21e --- /dev/null +++ b/crates/validation_core/tests/validation_report_rmse_standard_error_upper_bound_contract.rs @@ -0,0 +1,43 @@ +use validation_core::{ + ValidationError, ValidationReport, rmse_standard_error, root_mean_square_error, +}; + +fn report_with(rmse: f64, rmse_standard_error: f64) -> ValidationReport { + ValidationReport { + study_label: "rmse-se-support".into(), + rmse, + rmse_standard_error, + mean_bias: 0.0, + bias_standard_error: 0.0, + interval_coverage: 0.95, + coverage_wilson_lower: 0.90, + coverage_wilson_upper: 0.98, + temporal_order_accuracy: 1.0, + monte_carlo_rmse: None, + } +} + +#[test] +fn report_rejects_rmse_standard_error_above_squared_residual_support_bound() { + // For x_i = r_i^2 >= 0 with sample SD in the crate's delta-method producer, + // sd(x) <= sqrt(n) * mean(x), hence SE(RMSE) <= RMSE / 2. + // Two residuals [0, 1] attain the mathematical boundary. + let truth = [0.0, 0.0]; + let recovered = [0.0, 1.0]; + let rmse = root_mean_square_error(&truth, &recovered).expect("rmse"); + let rmse_se = rmse_standard_error(&truth, &recovered).expect("rmse se"); + let canonical = report_with(rmse, rmse_se); + assert!(canonical.validate().is_ok()); + assert!(rmse_se / rmse <= 0.5 + 64.0 * f64::EPSILON); + + let impossible = report_with(0.2, 0.11); + assert_eq!(impossible.validate(), Err(ValidationError::InvalidInput)); + assert_eq!(impossible.to_json(), Err(ValidationError::InvalidInput)); + assert_eq!( + impossible.to_human_summary(), + Err(ValidationError::InvalidInput) + ); + + let ingress = r#"{"study_label":"rmse-se-support","rmse":0.2,"rmse_standard_error":0.11,"mean_bias":0.0,"bias_standard_error":0.0,"interval_coverage":0.95,"coverage_wilson_lower":0.9,"coverage_wilson_upper":0.98,"temporal_order_accuracy":1.0,"monte_carlo_rmse":null}"#; + assert!(serde_json::from_str::(ingress).is_err()); +} From 32f094029732e2333b35ad3a11521c4c3d956798 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 09:02:20 +0900 Subject: [PATCH 110/576] fix(validation): enforce RMSE standard-error support bound --- crates/validation_core/src/report.rs | 50 ++++++++++++++++++---------- 1 file changed, 32 insertions(+), 18 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index 6707fb113..52224986c 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -4,6 +4,8 @@ use crate::MonteCarloSummary; use crate::ValidationError; use serde::{Deserialize, Serialize}; +const RMSE_STANDARD_ERROR_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; + /// Machine-readable recovery report for a single study. #[derive(Clone, Debug, PartialEq)] pub struct ValidationReport { @@ -32,27 +34,30 @@ pub struct ValidationReport { impl ValidationReport { /// Validate numeric and scientific invariants before serialization or export. /// - /// RMSE and standard errors are nonnegative; an exact-zero RMSE is perfect - /// recovery and therefore requires an exact-zero RMSE standard error under - /// the crate's squared-residual delta-method definition. Empirical coverage, - /// Wilson endpoints, and temporal-order accuracy are probabilities in `[0, 1]`; - /// the Wilson interval is ordered and must contain the empirical coverage - /// recorded in the same report. Mean signed bias remains unrestricted in - /// sign. A generic [`MonteCarloSummary`] may summarize a signed metric, but - /// when it occupies `monte_carlo_rmse` its mean and percentile endpoints are - /// nonnegative because every RMSE replication is nonnegative. A zero Monte - /// Carlo RMSE mean is exact perfect recovery across every retained replication, - /// so spread, standard error, and empirical percentile endpoints must all be - /// zero as well. These checks prevent a finite but scientifically impossible - /// payload from becoming durable Validation Evidence. + /// RMSE and standard errors are nonnegative. Under the crate's squared-residual + /// delta-method producer, `SE(RMSE) <= RMSE / 2`: for `x_i = r_i^2 >= 0`, the + /// sample standard deviation satisfies `sd(x) <= sqrt(n) * mean(x)`. Admission + /// allows a small relative binary64 tolerance at that support boundary. Exact + /// zero RMSE is perfect recovery and therefore still requires exact-zero RMSE + /// standard error. Empirical coverage, Wilson endpoints, and temporal-order + /// accuracy are probabilities in `[0, 1]`; the Wilson interval is ordered and + /// must contain the empirical coverage recorded in the same report. Mean + /// signed bias remains unrestricted in sign. A generic [`MonteCarloSummary`] + /// may summarize a signed metric, but when it occupies `monte_carlo_rmse` its + /// mean and percentile endpoints are nonnegative because every RMSE replication + /// is nonnegative. A zero Monte Carlo RMSE mean is exact perfect recovery across + /// every retained replication, so spread, standard error, and empirical + /// percentile endpoints must all be zero as well. These checks prevent a finite + /// but scientifically impossible payload from becoming durable Validation + /// Evidence. /// /// # Errors /// /// Returns [`ValidationError::InvalidInput`] when any `f64` field is /// non-finite, violates its metric domain, point RMSE and its standard error - /// contradict exact recovery, Wilson evidence is incoherent, or the optional - /// Monte Carlo RMSE summary violates either generic summary invariants or the - /// nonnegative RMSE domain. + /// exceed squared-residual support, Wilson evidence is incoherent, or the + /// optional Monte Carlo RMSE summary violates either generic summary invariants + /// or the nonnegative RMSE domain. pub fn validate(&self) -> Result<(), ValidationError> { for value in [ self.rmse, @@ -72,8 +77,17 @@ impl ValidationReport { if self.rmse < 0.0 || self.rmse_standard_error < 0.0 || self.bias_standard_error < 0.0 { return Err(ValidationError::InvalidInput); } - if self.rmse == 0.0 && self.rmse_standard_error != 0.0 { - return Err(ValidationError::InvalidInput); + if self.rmse == 0.0 { + if self.rmse_standard_error != 0.0 { + return Err(ValidationError::InvalidInput); + } + } else { + let relative_standard_error = self.rmse_standard_error / self.rmse; + if !relative_standard_error.is_finite() + || relative_standard_error > 0.5 + RMSE_STANDARD_ERROR_RELATIVE_TOLERANCE + { + return Err(ValidationError::InvalidInput); + } } if !(0.0..=1.0).contains(&self.interval_coverage) || !(0.0..=1.0).contains(&self.coverage_wilson_lower) From 7b61c107d1bc6391651ce2341e6ad27c2cada17a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 09:02:48 +0900 Subject: [PATCH 111/576] docs(validation): record RMSE standard-error support repair --- CHANGELOG.d/validation-report-rmse-standard-error-support.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-report-rmse-standard-error-support.md diff --git a/CHANGELOG.d/validation-report-rmse-standard-error-support.md b/CHANGELOG.d/validation-report-rmse-standard-error-support.md new file mode 100644 index 000000000..db2a1b42f --- /dev/null +++ b/CHANGELOG.d/validation-report-rmse-standard-error-support.md @@ -0,0 +1,3 @@ +### Fixed + +- Reject `ValidationReport` artifacts whose point `rmse_standard_error` exceeds the squared-residual delta-method support bound `RMSE / 2` (with binary64 admission tolerance). The canonical boundary case remains admissible, exact-zero RMSE still requires zero RMSE standard error, and positive RMSE with zero standard error remains valid for constant squared residuals. From f68c2dea13dddc998c000a20737677555b52cb7e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 09:03:05 +0900 Subject: [PATCH 112/576] docs(research): derive RMSE standard-error support bound --- ...idation-report-zero-rmse-standard-error.md | 34 +++++++++++++------ 1 file changed, 24 insertions(+), 10 deletions(-) diff --git a/docs/research/validation-report-zero-rmse-standard-error.md b/docs/research/validation-report-zero-rmse-standard-error.md index 83a12b557..fe76439ba 100644 --- a/docs/research/validation-report-zero-rmse-standard-error.md +++ b/docs/research/validation-report-zero-rmse-standard-error.md @@ -2,27 +2,41 @@ ## Problem -`ValidationReport` validated `rmse` and `rmse_standard_error` independently as finite, nonnegative numbers. That admitted a durable artifact with exact-zero RMSE and a positive RMSE standard error even though the canonical `validation_core::rmse_standard_error` producer returns exact zero whenever every residual is exactly zero. +`ValidationReport` originally validated `rmse` and `rmse_standard_error` independently as finite, nonnegative numbers. The first repair closed the exact-perfect case: a durable artifact with exact-zero RMSE and a positive RMSE standard error contradicted the canonical `validation_core::rmse_standard_error` producer. -Within TEPP's declared RMSE contract, `RMSE = sqrt(mean(r_i^2))`. For finite represented residuals, exact-zero RMSE implies every `r_i` is exactly zero. The delta-method RMSE standard error implemented by `validation_core` is therefore also exactly zero. A report containing `rmse = 0` and `rmse_standard_error > 0` contradicts the metric definition even though each field is individually representable. +A broader support invariant follows from the same producer. Let `x_i = r_i^2 >= 0`, `m = mean(x) = RMSE^2`, and let `s_x` be the sample standard deviation with denominator `n - 1`. For fixed nonnegative sample mean, the maximum sample variance occurs when one observation carries all mass (`x_1 = n m`) and the remaining observations are zero. Then `s_x = sqrt(n) m`. The crate's delta-method definition + +`SE(RMSE) = s_x / (2 * RMSE * sqrt(n))` + +therefore satisfies `SE(RMSE) <= RMSE / 2` for every admissible finite squared-residual sample. The boundary is attained, for example, by two residual magnitudes `[0, 1]`. A report such as `rmse = 0.2`, `rmse_standard_error = 0.11` is individually finite and nonnegative but cannot arise from the declared producer. ## Decision -Keep the RMSE arithmetic and standard-error estimator unchanged. Enforce the joint invariant only at `ValidationReport` admission: when `rmse == 0.0`, `rmse_standard_error` must also equal numerical zero. IEEE `-0.0` and `+0.0` remain one zero-valued scientific state. +Keep the RMSE arithmetic and standard-error estimator unchanged. Enforce joint support only at `ValidationReport` admission: + +- exact-zero RMSE still requires numerical-zero RMSE standard error; +- positive RMSE requires `rmse_standard_error / rmse <= 0.5 + 64 * EPSILON` so a represented boundary result is not rejected by cross-operation binary64 rounding; +- positive RMSE with zero standard error remains valid when squared residuals are exactly constant. + +IEEE `-0.0` and `+0.0` remain one zero-valued scientific state. The relative check also fails closed if a positive standard error divided by a tiny positive RMSE is not representable. This is Validation Evidence artifact coherence, not a reusable psychometric estimator. No fast-mlsirm source or mutable sibling dependency is introduced. ## Alternatives rejected -- Make every positive RMSE require positive standard error: rejected because equal nonzero residual magnitudes can produce positive RMSE with exactly zero squared-residual spread and therefore zero RMSE standard error. -- Change `rmse_standard_error` arithmetic: rejected because the producer already returns zero for exact perfect recovery; the defect was at durable report admission. -- Infer hidden residuals from the report: rejected because the report intentionally stores only summary evidence. +- Make every positive RMSE require positive standard error: rejected because equal nonzero residual magnitudes produce positive RMSE with exactly zero squared-residual spread. +- Store an arbitrary uncertainty field beside RMSE: rejected because the field is explicitly named `rmse_standard_error` and the crate exposes one canonical squared-residual delta-method producer for it. +- Require bit-exact `SE <= RMSE / 2` with no tolerance: rejected because the mathematical boundary is produced through separate binary64 operations and a valid boundary artifact must survive harmless last-bit differences. +- Infer hidden residuals from the report: rejected because the report intentionally stores summary evidence rather than raw residual vectors. ## Traceability -- Public RED: `f7b018c5f10d11c7cc21a3430242e37c2d7a1056`, `crates/validation_core/tests/validation_report_zero_rmse_standard_error_contract.rs`. -- Causal repair: `4c5999186141dafd8d6293d5da66ab8e19693f5c`, `ValidationReport::validate` in `crates/validation_core/src/report.rs`. -- Release note: `6ae5b254669868162428fc5c85538e9f5052ac6c`, `CHANGELOG.d/validation-report-zero-rmse-standard-error.md`. +- Exact-zero public RED: `f7b018c5f10d11c7cc21a3430242e37c2d7a1056`, `crates/validation_core/tests/validation_report_zero_rmse_standard_error_contract.rs`. +- Exact-zero causal repair: `4c5999186141dafd8d6293d5da66ab8e19693f5c`, `ValidationReport::validate` in `crates/validation_core/src/report.rs`. +- Exact-zero release note: `6ae5b254669868162428fc5c85538e9f5052ac6c`, `CHANGELOG.d/validation-report-zero-rmse-standard-error.md`. +- Positive-RMSE support RED: `a2aca5b077665433aad0e5531d53360b599b64b3`, `crates/validation_core/tests/validation_report_rmse_standard_error_upper_bound_contract.rs`. +- Positive-RMSE causal repair: `32f094029732e2333b35ad3a11521c4c3d956798`, `ValidationReport::validate` in `crates/validation_core/src/report.rs`. +- Positive-RMSE release note: `7b61c107d1bc6391651ce2341e6ad27c2cada17a`, `CHANGELOG.d/validation-report-rmse-standard-error-support.md`. - Producer contract: `root_mean_square_error` and `rmse_standard_error` in `crates/validation_core/src/rmse.rs`. - Owner: TEPP Validation Evidence. @@ -30,4 +44,4 @@ This is Validation Evidence artifact coherence, not a reusable psychometric esti Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086. ADEMP requires performance measures and their Monte Carlo or sampling uncertainty to be defined coherently; a stored point metric and uncertainty field that cannot jointly arise from the declared estimator should fail admission rather than become durable evidence. -American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. The 2014 edition remains the current published edition while revision is underway; this repair strengthens internal consistency of validation evidence and does not substitute arithmetic checks for substantive validity arguments. +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. The 2014 edition remains the current published edition while revision is underway. AERA's Joint Committee is revising that edition; as of 31 August 2026 AERA also publishes a current Task Force roster for the Standards work. This repair strengthens internal consistency of validation evidence and does not substitute arithmetic checks for substantive validity arguments. From e201a2f46953152255345a7b3abc35f05ea9e33c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 09:56:16 +0900 Subject: [PATCH 113/576] test(validation): align zero-mean RMSE fixture with summary support --- ...alidation_report_zero_mean_rmse_summary_contract.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs b/crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs index fe71eda69..13c430e05 100644 --- a/crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs +++ b/crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs @@ -34,6 +34,9 @@ fn zero_mean_monte_carlo_rmse_requires_zero_spread_and_zero_percentiles() { Err(ValidationError::InvalidInput) ); + // Zero sample spread with non-degenerate percentile support is already + // impossible for the generic summary carrier; the typed RMSE boundary must + // not weaken that upstream invariant. let positive_percentile = MonteCarloSummary { replication_count: 4, mean: 0.0, @@ -42,7 +45,10 @@ fn zero_mean_monte_carlo_rmse_requires_zero_spread_and_zero_percentiles() { percentile_lower: 0.0, percentile_upper: 1.0, }; - assert!(positive_percentile.validate().is_ok()); + assert_eq!( + positive_percentile.validate(), + Err(ValidationError::InvalidInput) + ); assert_eq!( report_with(positive_percentile).validate(), Err(ValidationError::InvalidInput) @@ -59,6 +65,6 @@ fn zero_mean_monte_carlo_rmse_requires_zero_spread_and_zero_percentiles() { assert!(perfect_recovery.validate().is_ok()); assert!(report_with(perfect_recovery).validate().is_ok()); - let payload = r#"{"study_label":"zero-mean-rmse-summary","rmse":0.0,"rmse_standard_error":0.0,"mean_bias":0.0,"bias_standard_error":0.0,"interval_coverage":1.0,"coverage_wilson_lower":0.5,"coverage_wilson_upper":1.0,"temporal_order_accuracy":1.0,"monte_carlo_rmse":{"replication_count":4,"mean":0.0,"standard_deviation":1.0,"standard_error":0.5,"percentile_lower":0.0,"percentile_upper":1.0}}"#; + let payload = r#"{\"study_label\":\"zero-mean-rmse-summary\",\"rmse\":0.0,\"rmse_standard_error\":0.0,\"mean_bias\":0.0,\"bias_standard_error\":0.0,\"interval_coverage\":1.0,\"coverage_wilson_lower\":0.5,\"coverage_wilson_upper\":1.0,\"temporal_order_accuracy\":1.0,\"monte_carlo_rmse\":{\"replication_count\":4,\"mean\":0.0,\"standard_deviation\":1.0,\"standard_error\":0.5,\"percentile_lower\":0.0,\"percentile_upper\":1.0}}"#; assert!(serde_json::from_str::(payload).is_err()); } From 43a7dec1dbc848435ca099aea80db46c8cbd97e5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 09:56:29 +0900 Subject: [PATCH 114/576] test(validation): RED bound Monte Carlo RMSE support --- ...se_summary_nonnegative_support_contract.rs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs diff --git a/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs b/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs new file mode 100644 index 000000000..9eab54484 --- /dev/null +++ b/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs @@ -0,0 +1,63 @@ +use validation_core::{MonteCarloSummary, ValidationError, ValidationReport}; + +fn report_with(summary: MonteCarloSummary) -> ValidationReport { + ValidationReport { + study_label: "rmse-summary-nonnegative-support".into(), + rmse: 0.2, + rmse_standard_error: 0.05, + mean_bias: 0.0, + bias_standard_error: 0.0, + interval_coverage: 0.95, + coverage_wilson_lower: 0.8, + coverage_wilson_upper: 1.0, + temporal_order_accuracy: 1.0, + monte_carlo_rmse: Some(summary), + } +} + +#[test] +fn monte_carlo_rmse_rejects_spread_impossible_for_nonnegative_replications() { + // For nonnegative replication metrics x_i with sample mean m, + // sample SD is at most sqrt(n) * m. Equality occurs when one replication + // carries the entire finite sum and the remaining n-1 replications are zero. + // Therefore SE(mean) = SD / sqrt(n) cannot exceed m. + let impossible_rmse_summary = MonteCarloSummary { + replication_count: 4, + mean: 1.0, + standard_deviation: 3.0, + standard_error: 1.5, + percentile_lower: 0.0, + percentile_upper: 4.0, + }; + + // The generic carrier is intentionally sign-neutral because it also serves + // signed metrics such as bias. The stronger support belongs only to the + // typed monte_carlo_rmse slot. + assert!(impossible_rmse_summary.validate().is_ok()); + + let report = report_with(impossible_rmse_summary); + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + assert_eq!(report.to_json(), Err(ValidationError::InvalidInput)); + assert_eq!( + report.to_human_summary(), + Err(ValidationError::InvalidInput) + ); + + let payload = r#"{\"study_label\":\"rmse-summary-nonnegative-support\",\"rmse\":0.2,\"rmse_standard_error\":0.05,\"mean_bias\":0.0,\"bias_standard_error\":0.0,\"interval_coverage\":0.95,\"coverage_wilson_lower\":0.8,\"coverage_wilson_upper\":1.0,\"temporal_order_accuracy\":1.0,\"monte_carlo_rmse\":{\"replication_count\":4,\"mean\":1.0,\"standard_deviation\":3.0,\"standard_error\":1.5,\"percentile_lower\":0.0,\"percentile_upper\":4.0}}"#; + assert!(serde_json::from_str::(payload).is_err()); +} + +#[test] +fn monte_carlo_rmse_accepts_attainable_nonnegative_support_boundary() { + let boundary = MonteCarloSummary { + replication_count: 4, + mean: 1.0, + standard_deviation: 2.0, + standard_error: 1.0, + percentile_lower: 0.0, + percentile_upper: 4.0, + }; + + assert!(boundary.validate().is_ok()); + assert!(report_with(boundary).validate().is_ok()); +} From 2f78954eb21a08c316d0d6b70f659685fb283a0b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 09:58:08 +0900 Subject: [PATCH 115/576] fix(validation): bound Monte Carlo RMSE nonnegative support --- crates/validation_core/src/report.rs | 36 ++++++++++++++++++---------- 1 file changed, 23 insertions(+), 13 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index 52224986c..81d023cac 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -43,13 +43,14 @@ impl ValidationReport { /// accuracy are probabilities in `[0, 1]`; the Wilson interval is ordered and /// must contain the empirical coverage recorded in the same report. Mean /// signed bias remains unrestricted in sign. A generic [`MonteCarloSummary`] - /// may summarize a signed metric, but when it occupies `monte_carlo_rmse` its - /// mean and percentile endpoints are nonnegative because every RMSE replication - /// is nonnegative. A zero Monte Carlo RMSE mean is exact perfect recovery across - /// every retained replication, so spread, standard error, and empirical - /// percentile endpoints must all be zero as well. These checks prevent a finite - /// but scientifically impossible payload from becoming durable Validation - /// Evidence. + /// may summarize a signed metric, but when it occupies `monte_carlo_rmse` every + /// retained replication is nonnegative. Its mean and percentile endpoints are + /// therefore nonnegative, and nonnegative sample support additionally implies + /// `SD <= sqrt(n) * mean` and hence `SE(mean) <= mean`. A zero Monte Carlo RMSE + /// mean is exact perfect recovery across every retained replication, so spread, + /// standard error, and empirical percentile endpoints must all be zero as well. + /// These checks prevent a finite but scientifically impossible payload from + /// becoming durable Validation Evidence. /// /// # Errors /// @@ -57,7 +58,7 @@ impl ValidationReport { /// non-finite, violates its metric domain, point RMSE and its standard error /// exceed squared-residual support, Wilson evidence is incoherent, or the /// optional Monte Carlo RMSE summary violates either generic summary invariants - /// or the nonnegative RMSE domain. + /// or the nonnegative RMSE support. pub fn validate(&self) -> Result<(), ValidationError> { for value in [ self.rmse, @@ -111,13 +112,22 @@ impl ValidationReport { { return Err(ValidationError::InvalidInput); } - if summary.mean == 0.0 - && (summary.standard_deviation != 0.0 + if summary.mean == 0.0 { + if summary.standard_deviation != 0.0 || summary.standard_error != 0.0 || summary.percentile_lower != 0.0 - || summary.percentile_upper != 0.0) - { - return Err(ValidationError::InvalidInput); + || summary.percentile_upper != 0.0 + { + return Err(ValidationError::InvalidInput); + } + } else { + let relative_standard_error = summary.standard_error / summary.mean; + if !relative_standard_error.is_finite() + || relative_standard_error + > 1.0 + RMSE_STANDARD_ERROR_RELATIVE_TOLERANCE + { + return Err(ValidationError::InvalidInput); + } } } Ok(()) From 4ce54959a5c70ccae8212a6494d346ceef0ff35f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 09:58:33 +0900 Subject: [PATCH 116/576] docs(validation): trace Monte Carlo RMSE support repair --- .../validation-report-rmse-summary-nonnegative-support.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-report-rmse-summary-nonnegative-support.md diff --git a/CHANGELOG.d/validation-report-rmse-summary-nonnegative-support.md b/CHANGELOG.d/validation-report-rmse-summary-nonnegative-support.md new file mode 100644 index 000000000..6a6f72e0e --- /dev/null +++ b/CHANGELOG.d/validation-report-rmse-summary-nonnegative-support.md @@ -0,0 +1,3 @@ +## Fixed + +- Validation Evidence now rejects `monte_carlo_rmse` summaries whose sampling spread cannot arise from nonnegative RMSE replications. For retained RMSE values `x_i >= 0` with sample mean `m`, the sample standard deviation satisfies `SD <= sqrt(n) * m`, so the standard error of the replication mean cannot exceed `m`. The generic `MonteCarloSummary` remains sign-neutral for metrics such as bias; this support check is applied only when the summary occupies the RMSE-specific report slot. From 9f9ffc3ff1d730228719fea977613153136fd720 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 09:58:55 +0900 Subject: [PATCH 117/576] docs(research): derive Monte Carlo RMSE support bound --- .../validation-report-rmse-summary-domain.md | 31 +++++++++++++++---- 1 file changed, 25 insertions(+), 6 deletions(-) diff --git a/docs/research/validation-report-rmse-summary-domain.md b/docs/research/validation-report-rmse-summary-domain.md index 455729c74..674b80490 100644 --- a/docs/research/validation-report-rmse-summary-domain.md +++ b/docs/research/validation-report-rmse-summary-domain.md @@ -4,24 +4,41 @@ `MonteCarloSummary` is intentionally metric-neutral: its mean and percentile endpoints may be negative when it summarizes a signed metric such as bias. `ValidationReport::monte_carlo_rmse`, however, gives that same wire shape a narrower scientific meaning. Every RMSE replication is nonnegative, so a negative Monte Carlo RMSE mean or percentile endpoint is finite but scientifically impossible evidence. -Before this repair, `ValidationReport::validate()` delegated the nested object only to the generic `MonteCarloSummary::validate()` contract. A caller could therefore construct or deserialize a report whose field was explicitly named `monte_carlo_rmse` while carrying negative RMSE evidence, and the report could pass canonical JSON and human-summary projection. +A second support constraint follows from the same typed meaning. Let retained RMSE replications be `x_i >= 0`, let `n` be the replication count, and let `m` be their sample mean. For a fixed nonnegative sum `sum(x_i) = n m`, the largest possible squared deviation occurs when one replication carries the whole sum and the remaining `n - 1` values are zero. Therefore + +`SD <= sqrt(n) * m` + +for the sample standard deviation, and because the stored Monte Carlo standard error is `SD / sqrt(n)`, + +`SE(mean) <= m`. + +The bound is attainable: for four replications `[0, 0, 0, 4]`, `mean = 1`, sample `SD = 2`, and `SE = 1`. A generic summary such as `n = 4`, `mean = 1`, `SD = 3`, `SE = 1.5` is internally coherent as a sign-neutral carrier because `SE = SD / sqrt(n)`, but it cannot have been produced by four nonnegative RMSE replications. + +Before these repairs, `ValidationReport::validate()` delegated the nested object only to the generic `MonteCarloSummary::validate()` contract and then checked the RMSE-specific sign domain. A caller could therefore construct or deserialize a report whose field was explicitly named `monte_carlo_rmse` while carrying negative RMSE evidence or a positive mean/spread combination outside nonnegative sample support, and the report could pass canonical JSON and human-summary projection. ## Decision -Keep `MonteCarloSummary` sign-neutral because it is reusable for signed recovery metrics. Enforce the narrower nonnegative domain only at the `ValidationReport::monte_carlo_rmse` ownership boundary: +Keep `MonteCarloSummary` sign-neutral because it is reusable for signed recovery metrics. Enforce the narrower domain only at the `ValidationReport::monte_carlo_rmse` ownership boundary: - `mean >= 0`; - `percentile_lower >= 0`; - `percentile_upper >= 0`; -- all existing generic Monte Carlo count, finiteness, uncertainty, and percentile-order invariants remain mandatory. +- exact-zero RMSE mean requires zero spread, zero SE, and zero percentile support; +- positive RMSE mean requires `SE(mean) <= mean`, with the same small binary64 relative tolerance used at the point-RMSE support boundary; +- all existing generic Monte Carlo count, finiteness, uncertainty, percentile-order, and zero-spread support invariants remain mandatory. + +Checking `SE / mean` at the typed boundary avoids duplicating the generic `SD / sqrt(n)` coherence calculation while still enforcing the nonnegative-support theorem. If the ratio overflows or is otherwise non-finite, the durable artifact fails closed. The boundary case remains admissible within the explicit floating-point tolerance. This is an artifact-admission invariant, not a new estimator and not reusable static psychometric arithmetic. The change therefore remains in TEPP `validation_core`; it does not move arithmetic into or copy source from `fast-mlsirm`. ## RED -> repair trace - Public RED `3cd6e41ddeffbb41e0a6179a65bc3dd9b60f41d8`: direct validation, canonical JSON, human projection, and JSON ingress must reject negative values when a generic Monte Carlo summary is embedded specifically as RMSE evidence. -- Causal repair `0090259d01ee00ad0de35ba0c4c9cb7a37c0b13c`: `ValidationReport::validate()` first applies the generic summary validator and then enforces the nonnegative RMSE-specific mean/percentile domain. -- Changelog trace `d2631d1b0047ba8dbf78058272ea6c00d9b0c9a3`. +- Causal sign-domain repair `0090259d01ee00ad0de35ba0c4c9cb7a37c0b13c`: `ValidationReport::validate()` first applies the generic summary validator and then enforces the nonnegative RMSE-specific mean/percentile domain. +- Generic zero-spread support later made an older zero-mean fixture self-contradictory; test repair `e201a2f46953152255345a7b3abc35f05ea9e33c` aligns the fixture with the stronger upstream summary contract rather than weakening that contract. +- Public RED `43a7dec1dbc848435ca099aea80db46c8cbd97e5`: a generic-valid `n=4, mean=1, SD=3, SE=1.5` summary must fail when embedded as RMSE evidence, while the attainable `[0,0,0,4]` boundary summary (`mean=1, SD=2, SE=1`) must remain valid. +- Causal nonnegative-support repair `2f78954eb21a08c316d0d6b70f659685fb283a0b`: positive Monte Carlo RMSE summaries fail when `standard_error / mean > 1 + 64*EPSILON`; zero mean retains the exact-perfect-recovery rule. +- Changelog trace `4ce54959a5c70ccae8212a6494d346ceef0ff35f`. Owned module/API/test: @@ -31,10 +48,12 @@ Owned module/API/test: - `validation_core::ValidationReport::to_human_summary` - serde ingress/egress for `ValidationReport` - `crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs` +- `crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs` +- `crates/validation_core/tests/validation_report_zero_mean_rmse_summary_contract.rs` ## Methodological basis -Morris, White, and Crowther (2019) treat simulation performance measures as explicitly defined quantities tied to their estimands and recommend coding and execution checks. RMSE is the square root of a mean squared error and therefore has a nonnegative range; a negative Monte Carlo summary carried under an RMSE-specific field is not an alternative convention but a domain violation. +Morris, White, and Crowther (2019) treat simulation performance measures as explicitly defined quantities tied to their estimands and recommend coding and execution checks. RMSE is the square root of a mean squared error and therefore has a nonnegative range; negative Monte Carlo RMSE evidence or a mean/spread combination that no nonnegative replication sample can realize is not an alternative convention but an artifact-domain violation. The 2014 *Standards for Educational and Psychological Testing* remain the current published AERA/APA/NCME edition while revision is underway. TEPP uses that validity framework to require coherent interpretation and reporting of evidence, rather than accepting a payload solely because each scalar is machine-representable. From 84a200ee451247c8f75fcce322aa7fc558f38c43 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 10:57:01 +0900 Subject: [PATCH 118/576] test(validation): reproduce impossible RMSE percentile support --- ...mse_summary_percentile_support_contract.rs | 64 +++++++++++++++++++ 1 file changed, 64 insertions(+) create mode 100644 crates/validation_core/tests/validation_report_rmse_summary_percentile_support_contract.rs diff --git a/crates/validation_core/tests/validation_report_rmse_summary_percentile_support_contract.rs b/crates/validation_core/tests/validation_report_rmse_summary_percentile_support_contract.rs new file mode 100644 index 000000000..ccc3b2be8 --- /dev/null +++ b/crates/validation_core/tests/validation_report_rmse_summary_percentile_support_contract.rs @@ -0,0 +1,64 @@ +use validation_core::{MonteCarloSummary, ValidationError, ValidationReport}; + +fn report_with(summary: MonteCarloSummary) -> ValidationReport { + ValidationReport { + study_label: "rmse-summary-percentile-support".into(), + rmse: 0.2, + rmse_standard_error: 0.05, + mean_bias: 0.0, + bias_standard_error: 0.0, + interval_coverage: 0.95, + coverage_wilson_lower: 0.8, + coverage_wilson_upper: 1.0, + temporal_order_accuracy: 1.0, + monte_carlo_rmse: Some(summary), + } +} + +#[test] +fn monte_carlo_rmse_rejects_percentile_above_nonnegative_sample_sum_support() { + // For n nonnegative RMSE replications with represented mean m, every retained + // replication is bounded by the finite sample sum n*m. Any empirical nearest-rank + // percentile is one of those retained values and therefore cannot exceed n*m. + let impossible_rmse_summary = MonteCarloSummary { + replication_count: 4, + mean: 1.0, + standard_deviation: 0.5, + standard_error: 0.25, + percentile_lower: 0.0, + percentile_upper: 5.0, + }; + + // The generic carrier cannot impose this bound because it also summarizes + // signed metrics. The stronger support belongs to the typed RMSE slot. + assert!(impossible_rmse_summary.validate().is_ok()); + + let report = report_with(impossible_rmse_summary); + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + assert_eq!(report.to_json(), Err(ValidationError::InvalidInput)); + assert_eq!( + report.to_human_summary(), + Err(ValidationError::InvalidInput) + ); + + let payload = r#"{"study_label":"rmse-summary-percentile-support","rmse":0.2,"rmse_standard_error":0.05,"mean_bias":0.0,"bias_standard_error":0.0,"interval_coverage":0.95,"coverage_wilson_lower":0.8,"coverage_wilson_upper":1.0,"temporal_order_accuracy":1.0,"monte_carlo_rmse":{"replication_count":4,"mean":1.0,"standard_deviation":0.5,"standard_error":0.25,"percentile_lower":0.0,"percentile_upper":5.0}}"#; + assert!(serde_json::from_str::(payload).is_err()); +} + +#[test] +fn monte_carlo_rmse_accepts_empirical_percentile_at_nonnegative_sum_boundary() { + // [0, 0, 0, 4] attains max(x_i) = n*mean and also the existing SD/SE support + // boundary, so the endpoint must remain admissible rather than being tightened + // by an arbitrary heuristic. + let attainable_boundary = MonteCarloSummary { + replication_count: 4, + mean: 1.0, + standard_deviation: 2.0, + standard_error: 1.0, + percentile_lower: 0.0, + percentile_upper: 4.0, + }; + + assert!(attainable_boundary.validate().is_ok()); + assert!(report_with(attainable_boundary).validate().is_ok()); +} From 04c9cdd4b89d37145853316bb419943735830c79 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 10:58:38 +0900 Subject: [PATCH 119/576] fix(validation): enforce RMSE percentile sample support --- crates/validation_core/src/report.rs | 27 +++++++++++++++++++++------ 1 file changed, 21 insertions(+), 6 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index 81d023cac..7cd4527d6 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -5,6 +5,7 @@ use crate::ValidationError; use serde::{Deserialize, Serialize}; const RMSE_STANDARD_ERROR_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; +const MONTE_CARLO_RMSE_SUPPORT_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; /// Machine-readable recovery report for a single study. #[derive(Clone, Debug, PartialEq)] @@ -45,12 +46,16 @@ impl ValidationReport { /// signed bias remains unrestricted in sign. A generic [`MonteCarloSummary`] /// may summarize a signed metric, but when it occupies `monte_carlo_rmse` every /// retained replication is nonnegative. Its mean and percentile endpoints are - /// therefore nonnegative, and nonnegative sample support additionally implies - /// `SD <= sqrt(n) * mean` and hence `SE(mean) <= mean`. A zero Monte Carlo RMSE - /// mean is exact perfect recovery across every retained replication, so spread, - /// standard error, and empirical percentile endpoints must all be zero as well. - /// These checks prevent a finite but scientifically impossible payload from - /// becoming durable Validation Evidence. + /// therefore nonnegative. Nonnegative sample support additionally implies + /// `SD <= sqrt(n) * mean`, `SE(mean) <= mean`, and every retained value—and thus + /// every inclusive nearest-rank percentile endpoint—is at most `n * mean`. + /// Admission evaluates the percentile support as `endpoint / mean <= n` with a + /// small relative binary64 tolerance so the check does not overflow a finite + /// sample sum. A zero Monte Carlo RMSE mean is exact perfect recovery across + /// every retained replication, so spread, standard error, and empirical + /// percentile endpoints must all be zero as well. These checks prevent a + /// finite but scientifically impossible payload from becoming durable + /// Validation Evidence. /// /// # Errors /// @@ -128,6 +133,16 @@ impl ValidationReport { { return Err(ValidationError::InvalidInput); } + + let relative_upper_percentile = summary.percentile_upper / summary.mean; + let replication_support = summary.replication_count as f64; + if !relative_upper_percentile.is_finite() + || relative_upper_percentile + > replication_support + * (1.0 + MONTE_CARLO_RMSE_SUPPORT_RELATIVE_TOLERANCE) + { + return Err(ValidationError::InvalidInput); + } } } Ok(()) From 00b9278a2c1bc1b912ed7c945391afe71f56bf55 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 10:58:49 +0900 Subject: [PATCH 120/576] docs(changelog): record RMSE percentile support repair --- .../validation-report-rmse-summary-percentile-support.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-report-rmse-summary-percentile-support.md diff --git a/CHANGELOG.d/validation-report-rmse-summary-percentile-support.md b/CHANGELOG.d/validation-report-rmse-summary-percentile-support.md new file mode 100644 index 000000000..a0017c08e --- /dev/null +++ b/CHANGELOG.d/validation-report-rmse-summary-percentile-support.md @@ -0,0 +1,3 @@ +### Fixed + +- Validation Evidence now rejects `monte_carlo_rmse` summaries whose empirical nearest-rank percentile endpoint exceeds the finite support available to `n` nonnegative RMSE replications with the recorded mean. The typed report boundary evaluates the support as `percentile_upper / mean <= replication_count` with a small binary64 tolerance, preserving the attainable `[0, ..., 0, n*mean]` boundary without materializing an overflow-prone sample sum. Generic `MonteCarloSummary` remains sign-neutral for metrics such as bias. From f10da1b20d02d6961742b4fafa7609612fdacc8c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 10:59:02 +0900 Subject: [PATCH 121/576] docs(research): trace RMSE percentile support invariant --- ...-report-rmse-summary-percentile-support.md | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 docs/research/validation-report-rmse-summary-percentile-support.md diff --git a/docs/research/validation-report-rmse-summary-percentile-support.md b/docs/research/validation-report-rmse-summary-percentile-support.md new file mode 100644 index 000000000..f58783b0a --- /dev/null +++ b/docs/research/validation-report-rmse-summary-percentile-support.md @@ -0,0 +1,27 @@ +# Monte Carlo RMSE percentile support + +## Decision + +`ValidationReport::monte_carlo_rmse` is a typed Validation Evidence slot for retained RMSE replications, so every underlying replication is nonnegative even though the reusable `MonteCarloSummary` carrier must remain sign-neutral for other metrics such as bias. + +For retained values `x_i >= 0`, replication count `n`, and represented mean `m`, the finite sample sum is `sum(x_i) = n*m` in the mathematical estimand. Since no nonnegative member can exceed the sum, every retained value satisfies `x_i <= n*m`. `summarize_replications` uses inclusive nearest-rank endpoints selected from the sorted retained values rather than an extrapolating quantile model, so every stored empirical percentile endpoint must satisfy the same support bound. + +The artifact-admission check evaluates the upper endpoint as + +`percentile_upper / mean <= replication_count` + +for positive mean, with a small binary64 relative tolerance. This form avoids requiring the finite product `n*mean` to be representable merely to validate an otherwise finite endpoint. Exact-zero mean remains governed by the stronger perfect-recovery invariant that spread, standard error, and percentile support are all numeric zero. The bound is attainable: `[0, 0, 0, 4]` has `n=4`, `mean=1`, and maximum/100th-percentile endpoint `4 = n*mean`. + +## RED and causal repair + +- RED `84a200ee451247c8f75fcce322aa7fc558f38c43` adds `validation_report_rmse_summary_percentile_support_contract.rs`. A generic-valid summary with `n=4`, `mean=1`, coherent `SD=0.5` / `SE=0.25`, and empirical upper endpoint `5` is impossible for nonnegative replications but predecessor report admission accepted it. +- Causal repair `04c9cdd4b89d37145853316bb419943735830c79` adds the typed percentile-support admission rule without narrowing `MonteCarloSummary` globally. +- Changelog trace `00b9278a2c1bc1b912ed7c945391afe71f56bf55` records the buyer-visible durable-evidence correction. + +This is TEPP Validation Evidence artifact admission and projection. It does not introduce a psychometric estimator, change Longitudinal Modeling composition, or copy reusable arithmetic from fast-mlsirm. + +## Methodological trace + +Monte Carlo performance summaries need their reported measures and Monte Carlo uncertainty to remain coherent with the simulated estimand and retained replications. The typed support check operationalizes that requirement at TEPP's durable-artifact boundary rather than asking an LLM or downstream report renderer to infer whether a finite payload is scientifically possible. + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 40acb4f6f51dd9d7074c652fb6448eaa942b95ac Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 11:58:05 +0900 Subject: [PATCH 122/576] test(validation): expose impossible percentile moment support --- ...arlo_percentile_moment_support_contract.rs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 crates/validation_core/tests/monte_carlo_percentile_moment_support_contract.rs diff --git a/crates/validation_core/tests/monte_carlo_percentile_moment_support_contract.rs b/crates/validation_core/tests/monte_carlo_percentile_moment_support_contract.rs new file mode 100644 index 000000000..429420aba --- /dev/null +++ b/crates/validation_core/tests/monte_carlo_percentile_moment_support_contract.rs @@ -0,0 +1,43 @@ +use validation_core::{MonteCarloSummary, ValidationError}; + +fn summary(percentile_lower: f64, percentile_upper: f64) -> MonteCarloSummary { + MonteCarloSummary { + replication_count: 4, + mean: 1.0, + standard_deviation: 0.5, + standard_error: 0.25, + percentile_lower, + percentile_upper, + } +} + +#[test] +fn empirical_percentiles_must_fit_recorded_mean_and_sample_spread() { + // [0.75, 0.75, 0.75, 1.75] attains this finite-sample support boundary: + // mean = 1, sample SD = 0.5, and max deviation = SD * (n - 1) / sqrt(n) = 0.75. + let attainable = summary(0.75, 1.75); + assert!(attainable.validate().is_ok()); + assert!(serde_json::to_string(&attainable).is_ok()); + + // Every nearest-rank percentile is an observed retained replication. No sample + // with n = 4, mean = 1, and sample SD = 0.5 can contain 2.0, because its + // deviation from the mean exceeds the finite-sample support bound above. + let impossible = summary(0.75, 2.0); + assert_eq!(impossible.validate(), Err(ValidationError::InvalidInput)); + assert!(serde_json::to_string(&impossible).is_err()); + + let payload = r#"{"replication_count":4,"mean":1.0,"standard_deviation":0.5,"standard_error":0.25,"percentile_lower":0.75,"percentile_upper":2.0}"#; + assert!(serde_json::from_str::(payload).is_err()); + + // The same moment-support law is generic: signed scalar summaries remain valid + // when their empirical endpoints fit the represented mean and sample spread. + let signed = MonteCarloSummary { + replication_count: 4, + mean: -1.0, + standard_deviation: 0.5, + standard_error: 0.25, + percentile_lower: -1.75, + percentile_upper: -0.75, + }; + assert!(signed.validate().is_ok()); +} From 2798e4f92dbb30019e2b1288e59d09564ae73a70 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 12:00:13 +0900 Subject: [PATCH 123/576] fix(validation): enforce percentile moment support --- crates/validation_core/src/monte_carlo.rs | 36 +++++++++++++++++++---- 1 file changed, 31 insertions(+), 5 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 75a9fca61..7740c263f 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -5,6 +5,7 @@ use crate::input::require_finite; use crate::numeric::{deterministic_compensated_sum, deterministic_representable_mean}; const STANDARD_ERROR_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; +const EMPIRICAL_SUPPORT_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; /// Summary of Monte Carlo replications for a scalar metric. #[derive(Clone, Copy, Debug, PartialEq)] @@ -32,15 +33,20 @@ impl MonteCarloSummary { /// cross-language bit-for-bit equality, but rejects materially understated or /// overstated uncertainty. Zero spread requires zero SE and degenerate /// empirical percentile support at the represented mean; the same support - /// rule applies to the canonical singleton summary. Numeric equality keeps - /// IEEE `-0.0` and `+0.0` as one zero-valued scientific state. + /// rule applies to the canonical singleton summary. For positive spread, + /// nearest-rank percentile endpoints are retained observations and therefore + /// must fit the finite-sample moment support + /// `|x - mean| <= SD * (n - 1) / sqrt(n)`. The comparison is scale-normalized + /// so opposite-sign full-range finite values do not create an overflowing + /// validation-only subtraction or product. Numeric equality keeps IEEE + /// `-0.0` and `+0.0` as one zero-valued scientific state. /// /// # Errors /// /// Returns [`ValidationError::InvalidInput`] when counts or numeric fields - /// violate the summary contract, empirical support contradicts zero sample - /// spread, or the standard-error field is incoherent with the represented - /// sample spread/count. + /// violate the summary contract, empirical percentile support is impossible + /// for the represented mean/sample spread/count, or the standard-error field + /// is incoherent with the represented sample spread/count. pub fn validate(self) -> Result { if self.replication_count == 0 { return Err(ValidationError::InvalidInput); @@ -83,6 +89,26 @@ impl MonteCarloSummary { if !relative_error.is_finite() || relative_error > STANDARD_ERROR_RELATIVE_TOLERANCE { return Err(ValidationError::InvalidInput); } + + let n = self.replication_count as f64; + let moment_factor = (n - 1.0) / n.sqrt(); + for endpoint in [self.percentile_lower, self.percentile_upper] { + let scale = self + .mean + .abs() + .max(endpoint.abs()) + .max(self.standard_deviation) + .max(1.0); + let scaled_deviation = ((endpoint / scale) - (self.mean / scale)).abs(); + let scaled_support = (self.standard_deviation / scale) * moment_factor; + if !scaled_deviation.is_finite() + || !scaled_support.is_finite() + || scaled_deviation + > scaled_support * (1.0 + EMPIRICAL_SUPPORT_RELATIVE_TOLERANCE) + { + return Err(ValidationError::InvalidInput); + } + } } Ok(self) } From e6c5b3d98491ffedd104fce7db677e04acc3b3f1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 12:00:42 +0900 Subject: [PATCH 124/576] docs(changelog): trace percentile moment support repair --- .../validation-monte-carlo-percentile-moment-support.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-monte-carlo-percentile-moment-support.md diff --git a/CHANGELOG.d/validation-monte-carlo-percentile-moment-support.md b/CHANGELOG.d/validation-monte-carlo-percentile-moment-support.md new file mode 100644 index 000000000..703038963 --- /dev/null +++ b/CHANGELOG.d/validation-monte-carlo-percentile-moment-support.md @@ -0,0 +1,3 @@ +### Fixed + +- `MonteCarloSummary` now rejects empirical nearest-rank percentile endpoints that cannot coexist with the recorded sample mean, sample standard deviation, and replication count. For any retained observation, `|x - mean| <= SD * (n - 1) / sqrt(n)`; admission evaluates that finite-sample support on a shared scale so full-range signed summaries do not overflow validation-only arithmetic. From dc14c0eb65ebe6d2245509e499919efe480d2d9c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 12:01:05 +0900 Subject: [PATCH 125/576] docs(research): derive Monte Carlo percentile moment support --- .../monte-carlo-percentile-moment-support.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docs/research/monte-carlo-percentile-moment-support.md diff --git a/docs/research/monte-carlo-percentile-moment-support.md b/docs/research/monte-carlo-percentile-moment-support.md new file mode 100644 index 000000000..f5d0dca48 --- /dev/null +++ b/docs/research/monte-carlo-percentile-moment-support.md @@ -0,0 +1,39 @@ +# Monte Carlo percentile moment support + +## Decision + +`MonteCarloSummary` is a reusable Validation Evidence carrier for scalar Monte Carlo metrics. Its empirical percentile endpoints are produced by `summarize_replications` with an inclusive nearest-rank rule, so each endpoint is one of the retained observations rather than an extrapolated quantile estimate. + +Let retained values be `x_1, ..., x_n`, represented sample mean be `m`, and sample standard deviation with denominator `n - 1` be `s`. For one retained observation define `d_j = x_j - m`. The remaining deviations sum to `-d_j`. By the Cauchy–Schwarz inequality, + +`sum_{i != j} d_i^2 >= d_j^2 / (n - 1)`. + +Therefore + +`(n - 1) s^2 = sum_i d_i^2 >= d_j^2 * n / (n - 1)`, + +which gives the finite-sample support bound + +`|x_j - m| <= s * (n - 1) / sqrt(n)`. + +Every inclusive nearest-rank percentile endpoint is a retained `x_j`, so the same bound is required of `percentile_lower` and `percentile_upper`. The bound is attainable: `[0.75, 0.75, 0.75, 1.75]` has `n=4`, `mean=1`, sample `SD=0.5`, and endpoint deviation `0.75 = 0.5 * 3 / 2`. + +The admission comparison normalizes `mean`, endpoint, and `SD` by a shared finite scale before subtraction and multiplication. This prevents opposite-sign full-range binary64 values from overflowing merely because Validation Evidence is being checked. A small relative binary64 tolerance is allowed at the support boundary; the rule does not require cross-language bit-for-bit equality. + +This support law is generic. It does not impose RMSE nonnegativity on `MonteCarloSummary`; signed summaries such as bias remain valid when their retained empirical endpoints fit the represented moments. + +## RED and causal repair + +- Public RED `40acb4f6f51dd9d7074c652fb6448eaa942b95ac` adds `monte_carlo_percentile_moment_support_contract.rs`. The predecessor accepted `n=4`, `mean=1`, `SD=0.5`, `SE=0.25`, `percentile_lower=0.75`, `percentile_upper=2.0` even though no retained sample with those moments can contain `2.0`. The attainable `1.75` boundary and a valid signed summary are preserved in the same contract. +- Causal repair `2798e4f92dbb30019e2b1288e59d09564ae73a70` enforces the finite-sample endpoint support inside `MonteCarloSummary::validate` using scale-normalized arithmetic. +- Changelog trace `e6c5b3d98491ffedd104fce7db677e04acc3b3f1` records the durable-evidence correction. + +This is TEPP Validation Evidence artifact admission. It does not introduce or relocate a psychometric estimator, change Longitudinal Modeling composition, or consume mutable fast-mlsirm source. + +## Methodological trace + +Simulation evidence is useful only when reported performance measures and Monte Carlo uncertainty are interpretable as summaries of realizable retained replications. Rejecting moment-incompatible empirical endpoints keeps the durable artifact aligned with the sample it claims to summarize rather than relying on a downstream renderer or LLM to infer scientific plausibility. + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From c7151b498ccbd562e7945a12a53c55472d93acac Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 12:02:44 +0900 Subject: [PATCH 126/576] fix(validation): preserve rounded-mean percentile support --- crates/validation_core/src/monte_carlo.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 7740c263f..6cb1215b9 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -35,11 +35,12 @@ impl MonteCarloSummary { /// empirical percentile support at the represented mean; the same support /// rule applies to the canonical singleton summary. For positive spread, /// nearest-rank percentile endpoints are retained observations and therefore - /// must fit the finite-sample moment support - /// `|x - mean| <= SD * (n - 1) / sqrt(n)`. The comparison is scale-normalized - /// so opposite-sign full-range finite values do not create an overflowing - /// validation-only subtraction or product. Numeric equality keeps IEEE - /// `-0.0` and `+0.0` as one zero-valued scientific state. + /// must fit the support implied directly by the recorded sample spread: + /// `|x - mean| <= SD * sqrt(n - 1)`. This remains valid when the represented + /// binary64 mean is a rounded projection of the mathematical sample mean. + /// The comparison is scale-normalized so opposite-sign full-range finite + /// values do not create an overflowing validation-only subtraction or product. + /// Numeric equality keeps IEEE `-0.0` and `+0.0` as one zero-valued scientific state. /// /// # Errors /// @@ -90,8 +91,7 @@ impl MonteCarloSummary { return Err(ValidationError::InvalidInput); } - let n = self.replication_count as f64; - let moment_factor = (n - 1.0) / n.sqrt(); + let moment_factor = ((self.replication_count - 1) as f64).sqrt(); for endpoint in [self.percentile_lower, self.percentile_upper] { let scale = self .mean From dbef285b6348cf691bbb72c25350912a5463e11e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 12:02:58 +0900 Subject: [PATCH 127/576] test(validation): cover rounded-mean percentile support --- ...arlo_percentile_moment_support_contract.rs | 21 ++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/tests/monte_carlo_percentile_moment_support_contract.rs b/crates/validation_core/tests/monte_carlo_percentile_moment_support_contract.rs index 429420aba..fac121766 100644 --- a/crates/validation_core/tests/monte_carlo_percentile_moment_support_contract.rs +++ b/crates/validation_core/tests/monte_carlo_percentile_moment_support_contract.rs @@ -1,4 +1,4 @@ -use validation_core::{MonteCarloSummary, ValidationError}; +use validation_core::{MonteCarloSummary, ValidationError, summarize_replications}; fn summary(percentile_lower: f64, percentile_upper: f64) -> MonteCarloSummary { MonteCarloSummary { @@ -13,15 +13,14 @@ fn summary(percentile_lower: f64, percentile_upper: f64) -> MonteCarloSummary { #[test] fn empirical_percentiles_must_fit_recorded_mean_and_sample_spread() { - // [0.75, 0.75, 0.75, 1.75] attains this finite-sample support boundary: - // mean = 1, sample SD = 0.5, and max deviation = SD * (n - 1) / sqrt(n) = 0.75. let attainable = summary(0.75, 1.75); assert!(attainable.validate().is_ok()); assert!(serde_json::to_string(&attainable).is_ok()); - // Every nearest-rank percentile is an observed retained replication. No sample - // with n = 4, mean = 1, and sample SD = 0.5 can contain 2.0, because its - // deviation from the mean exceeds the finite-sample support bound above. + // Every nearest-rank percentile is an observed retained replication. Because + // sample SD is computed from represented-mean deviations with denominator + // n - 1, every endpoint must satisfy |x - mean| <= SD * sqrt(n - 1). + // Here the support radius is 0.5 * sqrt(3) < 1, so endpoint 2.0 is impossible. let impossible = summary(0.75, 2.0); assert_eq!(impossible.validate(), Err(ValidationError::InvalidInput)); assert!(serde_json::to_string(&impossible).is_err()); @@ -29,7 +28,15 @@ fn empirical_percentiles_must_fit_recorded_mean_and_sample_spread() { let payload = r#"{"replication_count":4,"mean":1.0,"standard_deviation":0.5,"standard_error":0.25,"percentile_lower":0.75,"percentile_upper":2.0}"#; assert!(serde_json::from_str::(payload).is_err()); - // The same moment-support law is generic: signed scalar summaries remain valid + // Mean projection can round between adjacent binary64 observations. The + // support check must therefore use the recorded squared-deviation identity, + // not a stronger zero-sum-deviation bound that assumes an exact real mean. + let adjacent = f64::from_bits(1.0_f64.to_bits() + 1); + let rounded_mean = summarize_replications(&[1.0, adjacent], 0.0, 1.0) + .expect("rounded mean sample remains admissible"); + assert_eq!(rounded_mean.percentile_upper.to_bits(), adjacent.to_bits()); + + // The moment-support law is generic: signed scalar summaries remain valid // when their empirical endpoints fit the represented mean and sample spread. let signed = MonteCarloSummary { replication_count: 4, From c38a320c730875919a8bff58de42e2db859c248d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 12:03:18 +0900 Subject: [PATCH 128/576] docs(changelog): align percentile support with represented mean --- CHANGELOG.d/validation-monte-carlo-percentile-moment-support.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-monte-carlo-percentile-moment-support.md b/CHANGELOG.d/validation-monte-carlo-percentile-moment-support.md index 703038963..6f697ed3b 100644 --- a/CHANGELOG.d/validation-monte-carlo-percentile-moment-support.md +++ b/CHANGELOG.d/validation-monte-carlo-percentile-moment-support.md @@ -1,3 +1,3 @@ ### Fixed -- `MonteCarloSummary` now rejects empirical nearest-rank percentile endpoints that cannot coexist with the recorded sample mean, sample standard deviation, and replication count. For any retained observation, `|x - mean| <= SD * (n - 1) / sqrt(n)`; admission evaluates that finite-sample support on a shared scale so full-range signed summaries do not overflow validation-only arithmetic. +- `MonteCarloSummary` now rejects empirical nearest-rank percentile endpoints that cannot coexist with the recorded sample mean, sample standard deviation, and replication count. Because the canonical sample SD is the square root of the represented-mean squared-deviation sum divided by `n - 1`, every retained endpoint must satisfy `|x - mean| <= SD * sqrt(n - 1)`. Admission evaluates that support on a shared scale so full-range signed summaries do not overflow validation-only arithmetic, while adjacent-binary64 samples whose represented mean rounds remain admissible. From 2bbcbb243a755a5a39b13fbf8407b6854194efc0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 12:03:42 +0900 Subject: [PATCH 129/576] docs(research): correct represented-mean support derivation --- .../monte-carlo-percentile-moment-support.md | 26 +++++++++++-------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/docs/research/monte-carlo-percentile-moment-support.md b/docs/research/monte-carlo-percentile-moment-support.md index f5d0dca48..881d78280 100644 --- a/docs/research/monte-carlo-percentile-moment-support.md +++ b/docs/research/monte-carlo-percentile-moment-support.md @@ -4,29 +4,33 @@ `MonteCarloSummary` is a reusable Validation Evidence carrier for scalar Monte Carlo metrics. Its empirical percentile endpoints are produced by `summarize_replications` with an inclusive nearest-rank rule, so each endpoint is one of the retained observations rather than an extrapolated quantile estimate. -Let retained values be `x_1, ..., x_n`, represented sample mean be `m`, and sample standard deviation with denominator `n - 1` be `s`. For one retained observation define `d_j = x_j - m`. The remaining deviations sum to `-d_j`. By the Cauchy–Schwarz inequality, +Let retained values be `x_1, ..., x_n`, represented binary64 mean be `m`, and the canonical sample standard deviation with denominator `n - 1` be `s`. The producer computes deviations `d_i = x_i - m` and therefore records -`sum_{i != j} d_i^2 >= d_j^2 / (n - 1)`. +`(n - 1) s^2 = sum_i d_i^2`. -Therefore +Every retained observation contributes one nonnegative squared deviation to that sum, so for each `j`, -`(n - 1) s^2 = sum_i d_i^2 >= d_j^2 * n / (n - 1)`, +`d_j^2 <= (n - 1) s^2`, -which gives the finite-sample support bound +and therefore -`|x_j - m| <= s * (n - 1) / sqrt(n)`. +`|x_j - m| <= s * sqrt(n - 1)`. -Every inclusive nearest-rank percentile endpoint is a retained `x_j`, so the same bound is required of `percentile_lower` and `percentile_upper`. The bound is attainable: `[0.75, 0.75, 0.75, 1.75]` has `n=4`, `mean=1`, sample `SD=0.5`, and endpoint deviation `0.75 = 0.5 * 3 / 2`. +Every inclusive nearest-rank percentile endpoint is a retained `x_j`, so `percentile_lower` and `percentile_upper` must satisfy the same support bound. For the public fixture `n=4`, `mean=1`, `SD=0.5`, the support radius is `0.5 * sqrt(3) < 1`; an endpoint of `2.0` is therefore impossible even though the mean, SD, SE, and endpoint are individually finite. + +A stronger textbook bound based on zero-sum deviations was considered and rejected for artifact admission. TEPP deliberately stores a represented binary64 mean, and the mathematical sample mean can lie between adjacent binary64 values. The edge fixture `[1.0, next_up(1.0)]` exercises that projection: validation must use the squared-deviation identity actually implemented by the producer rather than assume that deviations from the represented mean sum to exact real zero. The admission comparison normalizes `mean`, endpoint, and `SD` by a shared finite scale before subtraction and multiplication. This prevents opposite-sign full-range binary64 values from overflowing merely because Validation Evidence is being checked. A small relative binary64 tolerance is allowed at the support boundary; the rule does not require cross-language bit-for-bit equality. This support law is generic. It does not impose RMSE nonnegativity on `MonteCarloSummary`; signed summaries such as bias remain valid when their retained empirical endpoints fit the represented moments. -## RED and causal repair +## RED, review, and causal repair -- Public RED `40acb4f6f51dd9d7074c652fb6448eaa942b95ac` adds `monte_carlo_percentile_moment_support_contract.rs`. The predecessor accepted `n=4`, `mean=1`, `SD=0.5`, `SE=0.25`, `percentile_lower=0.75`, `percentile_upper=2.0` even though no retained sample with those moments can contain `2.0`. The attainable `1.75` boundary and a valid signed summary are preserved in the same contract. -- Causal repair `2798e4f92dbb30019e2b1288e59d09564ae73a70` enforces the finite-sample endpoint support inside `MonteCarloSummary::validate` using scale-normalized arithmetic. -- Changelog trace `e6c5b3d98491ffedd104fce7db677e04acc3b3f1` records the durable-evidence correction. +- Public RED `40acb4f6f51dd9d7074c652fb6448eaa942b95ac` adds `monte_carlo_percentile_moment_support_contract.rs`. The predecessor accepted `n=4`, `mean=1`, `SD=0.5`, `SE=0.25`, `percentile_lower=0.75`, `percentile_upper=2.0` even though the recorded squared-deviation budget cannot contain that endpoint. +- Initial repair `2798e4f92dbb30019e2b1288e59d09564ae73a70` exposed an over-strong zero-sum assumption during immediate self-review. It was not treated as completion evidence. +- Causal correction `c7151b498ccbd562e7945a12a53c55472d93acac` bases admission on the producer's represented-mean squared-deviation identity, `|x - mean| <= SD * sqrt(n - 1)`. +- Edge reinforcement `dbef285b6348cf691bbb72c25350912a5463e11e` proves that adjacent-binary64 samples remain admissible when the represented mean rounds. +- Changelog correction `c38a320c730875919a8bff58de42e2db859c248d` records the final durable-evidence contract. This is TEPP Validation Evidence artifact admission. It does not introduce or relocate a psychometric estimator, change Longitudinal Modeling composition, or consume mutable fast-mlsirm source. From c4a13826144708582a73ec4458967bb103287338 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 12:10:47 +0900 Subject: [PATCH 130/576] test(validation): reject jointly impossible percentile support --- ...ercentile_joint_moment_support_contract.rs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 crates/validation_core/tests/monte_carlo_percentile_joint_moment_support_contract.rs diff --git a/crates/validation_core/tests/monte_carlo_percentile_joint_moment_support_contract.rs b/crates/validation_core/tests/monte_carlo_percentile_joint_moment_support_contract.rs new file mode 100644 index 000000000..903315a9e --- /dev/null +++ b/crates/validation_core/tests/monte_carlo_percentile_joint_moment_support_contract.rs @@ -0,0 +1,38 @@ +use validation_core::{MonteCarloSummary, ValidationError}; + +fn summary(percentile_lower: f64, percentile_upper: f64) -> MonteCarloSummary { + MonteCarloSummary { + replication_count: 4, + mean: 1.0, + standard_deviation: 0.5, + standard_error: 0.25, + percentile_lower, + percentile_upper, + } +} + +#[test] +fn distinct_empirical_percentiles_must_share_the_recorded_deviation_budget() { + // [0.75, 0.75, 0.75, 1.75] has mean 1, sample SD 0.5, and + // nearest-rank endpoints 0.75 and 1.75, so the joint support is attainable. + let attainable = summary(0.75, 1.75); + assert!(attainable.validate().is_ok()); + assert!(serde_json::to_string(&attainable).is_ok()); + + // Each endpoint below is individually within SD * sqrt(n - 1): + // |0.25 - 1| = |1.75 - 1| = 0.75 < 0.5 * sqrt(3). + // They are nevertheless impossible together because distinct observed + // endpoints consume at least 0.75^2 + 0.75^2 = 1.125 of squared-deviation + // budget while the recorded sample SD permits only (n - 1) * SD^2 = 0.75. + let impossible = summary(0.25, 1.75); + assert_eq!(impossible.validate(), Err(ValidationError::InvalidInput)); + assert!(serde_json::to_string(&impossible).is_err()); + + let payload = r#"{"replication_count":4,"mean":1.0,"standard_deviation":0.5,"standard_error":0.25,"percentile_lower":0.25,"percentile_upper":1.75}"#; + assert!(serde_json::from_str::(payload).is_err()); + + // Equal percentile endpoints may designate the same retained observation, + // so their squared deviation must not be counted twice. + let same_rank = summary(1.75, 1.75); + assert!(same_rank.validate().is_ok()); +} From cb3f80a2ff3439d238d1bd6e674ef789d25f36c7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 12:11:39 +0900 Subject: [PATCH 131/576] fix(validation): enforce joint percentile deviation budget --- crates/validation_core/src/monte_carlo.rs | 32 ++++++++++++++++++++--- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 6cb1215b9..ed4ca5416 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -36,10 +36,12 @@ impl MonteCarloSummary { /// rule applies to the canonical singleton summary. For positive spread, /// nearest-rank percentile endpoints are retained observations and therefore /// must fit the support implied directly by the recorded sample spread: - /// `|x - mean| <= SD * sqrt(n - 1)`. This remains valid when the represented - /// binary64 mean is a rounded projection of the mathematical sample mean. - /// The comparison is scale-normalized so opposite-sign full-range finite - /// values do not create an overflowing validation-only subtraction or product. + /// `|x - mean| <= SD * sqrt(n - 1)`. Distinct lower and upper endpoints are + /// distinct retained values, so their squared deviations must also fit the + /// same total `(n - 1) * SD^2` deviation budget jointly. These rules remain + /// valid when the represented binary64 mean is a rounded projection of the + /// mathematical sample mean. Comparisons are scale-normalized so opposite-sign + /// full-range finite values do not create overflowing validation-only arithmetic. /// Numeric equality keeps IEEE `-0.0` and `+0.0` as one zero-valued scientific state. /// /// # Errors @@ -109,6 +111,28 @@ impl MonteCarloSummary { return Err(ValidationError::InvalidInput); } } + + if self.percentile_lower != self.percentile_upper { + let scale = self + .mean + .abs() + .max(self.percentile_lower.abs()) + .max(self.percentile_upper.abs()) + .max(self.standard_deviation); + let scaled_mean = self.mean / scale; + let scaled_lower_deviation = (self.percentile_lower / scale) - scaled_mean; + let scaled_upper_deviation = (self.percentile_upper / scale) - scaled_mean; + let combined_scaled_deviation = + scaled_lower_deviation.hypot(scaled_upper_deviation); + let scaled_support = (self.standard_deviation / scale) * moment_factor; + if !combined_scaled_deviation.is_finite() + || !scaled_support.is_finite() + || combined_scaled_deviation + > scaled_support * (1.0 + EMPIRICAL_SUPPORT_RELATIVE_TOLERANCE) + { + return Err(ValidationError::InvalidInput); + } + } } Ok(self) } From f727450d7a68d253254d3f0a8ae8d305a08137eb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 12:11:54 +0900 Subject: [PATCH 132/576] docs(changelog): record joint percentile support repair --- CHANGELOG.d/validation-monte-carlo-percentile-joint-support.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-monte-carlo-percentile-joint-support.md diff --git a/CHANGELOG.d/validation-monte-carlo-percentile-joint-support.md b/CHANGELOG.d/validation-monte-carlo-percentile-joint-support.md new file mode 100644 index 000000000..809406d11 --- /dev/null +++ b/CHANGELOG.d/validation-monte-carlo-percentile-joint-support.md @@ -0,0 +1,3 @@ +### Fixed + +- Reject `MonteCarloSummary` artifacts whose distinct nearest-rank percentile endpoints individually fit the recorded spread but jointly exceed the sample squared-deviation budget `(n - 1) * SD^2`. From 67b8e92209024ddec19ba04a018ec87fdcb21271 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 12:12:08 +0900 Subject: [PATCH 133/576] docs(research): trace joint percentile deviation support --- ...e-carlo-percentile-joint-moment-support.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docs/research/monte-carlo-percentile-joint-moment-support.md diff --git a/docs/research/monte-carlo-percentile-joint-moment-support.md b/docs/research/monte-carlo-percentile-joint-moment-support.md new file mode 100644 index 000000000..6d6b115ae --- /dev/null +++ b/docs/research/monte-carlo-percentile-joint-moment-support.md @@ -0,0 +1,37 @@ +# Joint empirical-percentile moment support + +## Scientific contract + +`MonteCarloSummary` stores a represented binary64 sample mean `m`, sample standard deviation `s` with denominator `n - 1`, and inclusive nearest-rank percentile endpoints selected from the retained replications. The generic carrier is intentionally sign-neutral because it is used for signed metrics such as bias as well as nonnegative metrics such as RMSE. + +For the producer's represented-mean deviations `d_i = x_i - m`, the stored spread satisfies + +`(n - 1) s^2 = sum_i d_i^2`. + +The existing endpoint-support contract correctly requires each retained percentile endpoint to satisfy `|d| <= s * sqrt(n - 1)`. That condition is necessary but not sufficient when the lower and upper endpoints are numerically distinct. Distinct endpoint values must come from distinct retained observations, so both squared deviations consume the same finite sample budget: + +`(percentile_lower - m)^2 + (percentile_upper - m)^2 <= (n - 1) s^2`. + +If the two endpoint values are numerically equal, they may designate the same retained observation or duplicate observations with the same represented value, so admission conservatively counts that value once rather than inventing a second observation. + +The attainable fixture `[0.75, 0.75, 0.75, 1.75]` has `n = 4`, represented mean `1.0`, sample SD `0.5`, SE `0.25`, and distinct endpoints `0.75` and `1.75`. Its joint endpoint contribution is `0.25^2 + 0.75^2 = 0.625`, within the total deviation budget `3 * 0.5^2 = 0.75`. + +By contrast, a summary with the same `n`, mean, SD, and SE but endpoints `0.25` and `1.75` passes the predecessor's separate endpoint checks because each absolute deviation is `0.75 < 0.5 * sqrt(3)`. It is nevertheless impossible: the two observed endpoints alone require `0.75^2 + 0.75^2 = 1.125`, exceeding the entire recorded deviation budget `0.75` before any other retained replication is considered. + +## Numerical implementation + +The admission check avoids raw full-range subtraction and squaring. It divides mean, both endpoints, and SD by one shared finite magnitude, combines the two normalized deviations with binary64 `hypot`, and compares that norm with the normalized `SD * sqrt(n - 1)` support using the same small relative tolerance as the individual endpoint contract. This preserves the represented-mean semantics already adopted by the predecessor repair and does not assume that deviations from the rounded binary64 mean sum to exact real zero. + +## RED, causal repair, and traceability + +- Public RED `c4a13826144708582a73ec4458967bb103287338` adds `crates/validation_core/tests/monte_carlo_percentile_joint_moment_support_contract.rs`. It preserves an attainable joint-support fixture, rejects the individually-valid-but-jointly-impossible `(0.25, 1.75)` endpoints, verifies JSON egress/ingress fail closed, and preserves equal-endpoint admission without double-counting one represented value. +- Causal production repair `cb3f80a2ff3439d238d1bd6e674ef789d25f36c7` extends `MonteCarloSummary::validate` with the distinct-endpoint joint deviation budget while retaining the predecessor's represented-mean individual endpoint rule. +- Changelog trace `f727450d7a68d253254d3f0a8ae8d305a08137eb` records the durable-evidence repair. + +This is Validation Evidence artifact admission in TEPP. It does not create a new psychometric estimator, does not change Longitudinal Modeling composition, and does not relocate reusable static psychometric arithmetic from fast-mlsirm. + +## Methodological reference + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +Morris et al. distinguish estimands, performance measures, and Monte Carlo uncertainty and recommend considering performance measures jointly. TEPP's validation boundary therefore treats a summary as a coherent evidence artifact rather than validating each reported scalar in isolation. From 81bf0d9e2f1a28947b1343244002d6762b703f8a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 13:01:57 +0900 Subject: [PATCH 134/576] test(validation): expose two-replication percentile exhaustion --- ...eplication_endpoint_exhaustion_contract.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 crates/validation_core/tests/monte_carlo_two_replication_endpoint_exhaustion_contract.rs diff --git a/crates/validation_core/tests/monte_carlo_two_replication_endpoint_exhaustion_contract.rs b/crates/validation_core/tests/monte_carlo_two_replication_endpoint_exhaustion_contract.rs new file mode 100644 index 000000000..d74fdab21 --- /dev/null +++ b/crates/validation_core/tests/monte_carlo_two_replication_endpoint_exhaustion_contract.rs @@ -0,0 +1,34 @@ +use validation_core::{MonteCarloSummary, ValidationError, summarize_replications}; + +#[test] +fn distinct_percentile_endpoints_exhaust_a_two_replication_sample() { + let attainable = summarize_replications(&[-0.5, 0.5], 0.5, 1.0) + .expect("two-replication nearest-rank summary"); + assert_eq!(attainable.replication_count, 2); + assert_eq!(attainable.mean, 0.0); + assert_eq!(attainable.percentile_lower, -0.5); + assert_eq!(attainable.percentile_upper, 0.5); + assert!(attainable.validate().is_ok()); + + // With exactly two retained replications, two distinct nearest-rank endpoint + // values exhaust the sample: there are no unobserved replications left that + // could supply additional spread. These endpoint values imply sample + // SD = sqrt(0.5), so recording SD = 1.0 is scientifically impossible even + // though each endpoint separately and jointly fits the looser moment budget. + let impossible = MonteCarloSummary { + replication_count: 2, + mean: 0.0, + standard_deviation: 1.0, + standard_error: 1.0 / 2.0_f64.sqrt(), + percentile_lower: -0.5, + percentile_upper: 0.5, + }; + assert_eq!(impossible.validate(), Err(ValidationError::InvalidInput)); + assert!(serde_json::to_string(&impossible).is_err()); + + let payload = format!( + "{{\"replication_count\":2,\"mean\":0.0,\"standard_deviation\":1.0,\"standard_error\":{},\"percentile_lower\":-0.5,\"percentile_upper\":0.5}}", + 1.0 / 2.0_f64.sqrt() + ); + assert!(serde_json::from_str::(&payload).is_err()); +} From a519fb859c3ec9c48397366311b5643636782ad5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 13:03:37 +0900 Subject: [PATCH 135/576] fix(validation): enforce two-replication percentile exhaustion --- crates/validation_core/src/monte_carlo.rs | 38 +++++++++++++++++++---- 1 file changed, 32 insertions(+), 6 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index ed4ca5416..925de4eb4 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -38,10 +38,13 @@ impl MonteCarloSummary { /// must fit the support implied directly by the recorded sample spread: /// `|x - mean| <= SD * sqrt(n - 1)`. Distinct lower and upper endpoints are /// distinct retained values, so their squared deviations must also fit the - /// same total `(n - 1) * SD^2` deviation budget jointly. These rules remain - /// valid when the represented binary64 mean is a rounded projection of the - /// mathematical sample mean. Comparisons are scale-normalized so opposite-sign - /// full-range finite values do not create overflowing validation-only arithmetic. + /// same total `(n - 1) * SD^2` deviation budget jointly. With exactly two + /// replications, two distinct nearest-rank endpoint values exhaust the sample; + /// the recorded mean and sample SD must therefore agree with the summary of + /// those two endpoint values themselves. These rules remain valid when the + /// represented binary64 mean is a rounded projection of the mathematical + /// sample mean. Comparisons are scale-normalized so opposite-sign full-range + /// finite values do not create overflowing validation-only arithmetic. /// Numeric equality keeps IEEE `-0.0` and `+0.0` as one zero-valued scientific state. /// /// # Errors @@ -132,6 +135,29 @@ impl MonteCarloSummary { { return Err(ValidationError::InvalidInput); } + + if self.replication_count == 2 { + let endpoint_samples = [self.percentile_lower, self.percentile_upper]; + let expected_mean = deterministic_representable_mean(&endpoint_samples)?; + let expected_standard_deviation = + scaled_sample_standard_deviation(&endpoint_samples, expected_mean)?; + for (recorded, expected) in [ + (self.mean, expected_mean), + (self.standard_deviation, expected_standard_deviation), + ] { + let coherence_scale = recorded.abs().max(expected.abs()); + if coherence_scale == 0.0 { + continue; + } + let relative_distance = + ((recorded / coherence_scale) - (expected / coherence_scale)).abs(); + if !relative_distance.is_finite() + || relative_distance > EMPIRICAL_SUPPORT_RELATIVE_TOLERANCE + { + return Err(ValidationError::InvalidInput); + } + } + } } } Ok(self) @@ -264,7 +290,7 @@ pub fn summarize_replications( /// SE-aware acceptance: accept when `|estimate − target| ≤ k · se`. /// /// Comparison scales all terms by a shared finite magnitude so opposite-sign -/// extremes do not overflow both sides of the inequality to infinity. A zero +/// extremes do not overflow both finite sides of the inequality to infinity. A zero /// standard error or zero multiplier is an exact-recovery gate and is compared /// before scale reduction so a huge SE cannot erase a nonzero residual. Exact /// recovery uses numeric equality, for which IEEE `-0.0` and `+0.0` denote the @@ -429,7 +455,7 @@ mod tests { assert_eq!( MonteCarloSummary { replication_count: 2, - mean: 0.0, + f64::NAN, standard_deviation: 0.0, standard_error: -0.1, percentile_lower: 0.0, From d48f8fef08e77b8fa654f2852814c25c5d1baa79 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 13:04:30 +0900 Subject: [PATCH 136/576] fix(validation): repair two-replication exhaustion implementation --- crates/validation_core/src/monte_carlo.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 925de4eb4..170e8558c 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -290,7 +290,7 @@ pub fn summarize_replications( /// SE-aware acceptance: accept when `|estimate − target| ≤ k · se`. /// /// Comparison scales all terms by a shared finite magnitude so opposite-sign -/// extremes do not overflow both finite sides of the inequality to infinity. A zero +/// extremes do not overflow both sides of the inequality to infinity. A zero /// standard error or zero multiplier is an exact-recovery gate and is compared /// before scale reduction so a huge SE cannot erase a nonzero residual. Exact /// recovery uses numeric equality, for which IEEE `-0.0` and `+0.0` denote the @@ -455,7 +455,7 @@ mod tests { assert_eq!( MonteCarloSummary { replication_count: 2, - f64::NAN, + mean: 0.0, standard_deviation: 0.0, standard_error: -0.1, percentile_lower: 0.0, From fb314d8afccdf19f0074c64f6277d2edb290e907 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 13:05:04 +0900 Subject: [PATCH 137/576] test(validation): cover exhausted-sample mean coherence --- ...eplication_endpoint_exhaustion_contract.rs | 25 ++++++++++++++++--- 1 file changed, 22 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/tests/monte_carlo_two_replication_endpoint_exhaustion_contract.rs b/crates/validation_core/tests/monte_carlo_two_replication_endpoint_exhaustion_contract.rs index d74fdab21..492cb2d01 100644 --- a/crates/validation_core/tests/monte_carlo_two_replication_endpoint_exhaustion_contract.rs +++ b/crates/validation_core/tests/monte_carlo_two_replication_endpoint_exhaustion_contract.rs @@ -15,7 +15,7 @@ fn distinct_percentile_endpoints_exhaust_a_two_replication_sample() { // could supply additional spread. These endpoint values imply sample // SD = sqrt(0.5), so recording SD = 1.0 is scientifically impossible even // though each endpoint separately and jointly fits the looser moment budget. - let impossible = MonteCarloSummary { + let impossible_spread = MonteCarloSummary { replication_count: 2, mean: 0.0, standard_deviation: 1.0, @@ -23,8 +23,27 @@ fn distinct_percentile_endpoints_exhaust_a_two_replication_sample() { percentile_lower: -0.5, percentile_upper: 0.5, }; - assert_eq!(impossible.validate(), Err(ValidationError::InvalidInput)); - assert!(serde_json::to_string(&impossible).is_err()); + assert_eq!( + impossible_spread.validate(), + Err(ValidationError::InvalidInput) + ); + assert!(serde_json::to_string(&impossible_spread).is_err()); + + // The same exhaustion also fixes the represented mean. This payload still + // passes the looser individual/joint endpoint-radius budget, but its mean + // cannot be the mean of the only two retained values. + let impossible_mean = MonteCarloSummary { + replication_count: 2, + mean: 0.25, + standard_deviation: 1.0, + standard_error: 1.0 / 2.0_f64.sqrt(), + percentile_lower: -0.5, + percentile_upper: 0.5, + }; + assert_eq!( + impossible_mean.validate(), + Err(ValidationError::InvalidInput) + ); let payload = format!( "{{\"replication_count\":2,\"mean\":0.0,\"standard_deviation\":1.0,\"standard_error\":{},\"percentile_lower\":-0.5,\"percentile_upper\":0.5}}", From 440b78d86908fe1464e65bdbc4ceb5f9f6606c9f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 13:05:37 +0900 Subject: [PATCH 138/576] refactor(validation): keep exhaustion comparison branch-coverable --- crates/validation_core/src/monte_carlo.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 170e8558c..a5df025ea 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -151,9 +151,7 @@ impl MonteCarloSummary { } let relative_distance = ((recorded / coherence_scale) - (expected / coherence_scale)).abs(); - if !relative_distance.is_finite() - || relative_distance > EMPIRICAL_SUPPORT_RELATIVE_TOLERANCE - { + if relative_distance > EMPIRICAL_SUPPORT_RELATIVE_TOLERANCE { return Err(ValidationError::InvalidInput); } } From 45116498f29c0d3421192d452e26182975b114ae Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 13:05:52 +0900 Subject: [PATCH 139/576] docs(changelog): record two-replication exhaustion repair --- .../validation-monte-carlo-two-replication-exhaustion.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-monte-carlo-two-replication-exhaustion.md diff --git a/CHANGELOG.d/validation-monte-carlo-two-replication-exhaustion.md b/CHANGELOG.d/validation-monte-carlo-two-replication-exhaustion.md new file mode 100644 index 000000000..f0136a28d --- /dev/null +++ b/CHANGELOG.d/validation-monte-carlo-two-replication-exhaustion.md @@ -0,0 +1,3 @@ +### Fixed + +- Reject generic `MonteCarloSummary` payloads where exactly two retained replications expose two distinct nearest-rank percentile endpoint values but the recorded mean or sample standard deviation cannot be produced by those two values. With two replications, distinct endpoint values exhaust the retained sample, so looser individual/joint moment-budget checks are not sufficient evidence of a realizable summary. From d8793f00c3849cabd2b76e52161edd4a5cfe92a5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 13:06:09 +0900 Subject: [PATCH 140/576] docs(research): trace two-replication exhaustion contract --- ...te-carlo-two-repmin-endpoint-exhaustion.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docs/research/monte-carlo-two-repmin-endpoint-exhaustion.md diff --git a/docs/research/monte-carlo-two-repmin-endpoint-exhaustion.md b/docs/research/monte-carlo-two-repmin-endpoint-exhaustion.md new file mode 100644 index 000000000..474fd333e --- /dev/null +++ b/docs/research/monte-carlo-two-repmin-endpoint-exhaustion.md @@ -0,0 +1,37 @@ +# Two-replication nearest-rank endpoint exhaustion + +## Scientific contract + +`MonteCarloSummary` stores a represented sample mean, sample standard deviation (`n - 1` denominator), standard error of the mean, and inclusive nearest-rank percentile endpoints selected from retained replications. The carrier remains sign-neutral because it can summarize signed metrics such as bias as well as nonnegative metrics such the RMSE summaries admitted by `ValidationReport`. + +For more than two replications, lower and upper percentile endpoints do not identify the unreported retained values, so TEPP only enforces support that follows from the stored moments. The `n = 2` case is different. When the two nearest-rank endpoint values are numerically distinct, they must designate the two distinct retained observations. There are no remaining replications whose values could alter the represented mean or sample spread. + +Therefore, for `replication_count = 2` and `percentile_lower != percentile_upper`, the retained sample is exactly + +`[percentile_lower, percentile_upper]`. + +The recorded mean and sample SD must be coherent with that exhausted sample. This is stronger than the generic individual endpoint-radius and joint squared-deviation budget, but only in the finite-sample case where the artifact itself identifies every retained value. + +The public RED uses endpoints `[-0.5, 0.5]`. The canonical producer returns represented mean `0.0` and sample SD `sqrt(0.5)`. A payload that records the same two endpoint values with `SD = 1.0` and `SE = 1/sqrt(2)` passes the predecessor's standard-error coherence, individual endpoint support, and joint deviation-budget checks, yet no two-observation retained sample can produce it. A second fixture records mean `0.25`; it likewise satisfies the looser moment budget but cannot be the mean of the only two retained values. + +Equal numeric endpoints are not treated as sample exhaustion because nearest-rank lower and upper requests may select the same retained rank, or two retained observations may share the same represented value. Without percentile probabilities or rank multiplicity in the artifact, inferring two distinct observations from one numeric endpoint would overconstrain valid summaries. + +## Numerical implementation + +For the exhausted two-value case, admission reuses the same deterministic represented-mean and scaled sample-SD references as `summarize_replications`. The recorded and reconstructed values are compared after normalization by their own maximum magnitude, with the existing empirical-support relative tolerance. This avoids raw full-range subtraction and does not scale a near-zero mean comparison by the much larger endpoint magnitudes, which would otherwise admit a materially wrong near-zero mean. + +The normalized operands are finite and bounded by one, so the final relative-distance calculation cannot create a non-finite validation-only intermediate. The implementation therefore does not add an unreachable finite-check branch that would weaken owned branch-coverage evidence. + +## RED, causal repair, and traceability + +- Public RED `81bf0d9e2f1a28947b1343244002d6762b703f8a` adds `crates/validation_core/tests/monte_carlo_two_replication_endpoint_exhaustion_contract.rs` and demonstrates a two-replication summary that passes the predecessor's looser support checks but is not realizable from the exposed endpoint values. +- Causal implementation was corrected and stabilized at `d48f8fef08e77b8fa654f2852814c25c5d1baa79` after immediate source review of the first contents update; `fb314d8afccdf19f0074c64f6277d2edb290e907` adds explicit represented-mean coverage, and `440b78d86908fe1464e65bdbc4ceb5f9f6606c9f` removes an unreachable non-finite comparison branch while retaining the same scientific contract. +- Changelog trace `45116498f29c0d3421192d452e26182975b114ae` records the durable-evidence repair. + +This remains TEPP Validation Evidence artifact admission. It does not add a psychometric estimator, change Longitudinal Modeling composition, or copy mutable arithmetic from fast-mlsirm. + +## Methodological reference + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +The ADEMP framing separates estimands, methods, data-generating mechanisms, and performance measures. TEPP applies that discipline at the evidence boundary: a reported performance summary is admitted only when the stored finite-sample fields can coexist under the producer contract actually used to generate them. From 2df31f44505a567c9389b1b1014deed7ddab30db Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 13:06:34 +0900 Subject: [PATCH 141/576] docs(research): correct two-replication exhaustion trace path --- ...rlo-two-replication-endpoint-exhaustion.md | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 docs/research/monte-carlo-two-replication-endpoint-exhaustion.md diff --git a/docs/research/monte-carlo-two-replication-endpoint-exhaustion.md b/docs/research/monte-carlo-two-replication-endpoint-exhaustion.md new file mode 100644 index 000000000..be99719e7 --- /dev/null +++ b/docs/research/monte-carlo-two-replication-endpoint-exhaustion.md @@ -0,0 +1,37 @@ +# Two-replication nearest-rank sample exhaustion + +## Scientific contract + +`MonteCarloSummary` stores a represented sample mean, sample standard deviation (`n - 1` denominator), standard error of the mean, and inclusive nearest-rank percentile endpoints selected from retained replications. The carrier remains sign-neutral because it can summarize signed metrics such as bias as well as nonnegative metrics such as the RMSE summaries admitted by `ValidationReport`. + +For more than two replications, lower and upper percentile endpoints do not identify the unreported retained values, so TEPP only enforces support that follows from the stored moments. The `n = 2` case is different. When the two nearest-rank endpoint values are numerically distinct, they must designate the two distinct retained observations. There are no remaining replications whose values could alter the represented mean or sample spread. + +Therefore, for `replication_count = 2` and `percentile_lower != percentile_upper`, the retained sample is exactly + +`[percentile_lower, percentile_upper]`. + +The recorded mean and sample SD must be coherent with that exhausted sample. This is stronger than the generic individual endpoint-radius and joint squared-deviation budget, but only in the finite-sample case where the artifact itself identifies every retained value. + +The public RED uses endpoints `[-0.5, 0.5]`. The canonical producer returns represented mean `0.0` and sample SD `sqrt(0.5)`. A payload that records the same two endpoint values with `SD = 1.0` and `SE = 1/sqrt(2)` passes the predecessor's standard-error coherence, individual endpoint support, and joint deviation-budget checks, yet no two-observation retained sample can produce it. A second fixture records mean `0.25`; it likewise satisfies the looser moment budget but cannot be the mean of the only two retained values. + +Equal numeric endpoints are not treated as sample exhaustion because nearest-rank lower and upper requests may select the same retained rank, or two retained observations may share the same represented value. Without percentile probabilities or rank multiplicity in the artifact, inferring two distinct observations from one numeric endpoint would overconstrain valid summaries. + +## Numerical implementation + +For the exhausted two-value case, admission reuses the same deterministic represented-mean and scaled sample-SD references as `summarize_replications`. The recorded and reconstructed values are compared after normalization by their own maximum magnitude, with the existing empirical-support relative tolerance. This avoids raw full-range subtraction and does not scale a near-zero mean comparison by the much larger endpoint magnitudes, which would otherwise admit a materially wrong near-zero mean. + +The normalized operands are finite and bounded by one, so the final relative-distance calculation cannot create a non-finite validation-only intermediate. The implementation therefore does not add an unreachable finite-check branch that would weaken owned branch-coverage evidence. + +## RED, causal repair, and traceability + +- Public RED `81bf0d9e2f1a28947b1343244002d6762b703f8a` adds `crates/validation_core/tests/monte_carlo_two_replication_endpoint_exhaustion_contract.rs` and demonstrates a two-replication summary that passes the predecessor's looser support checks but is not realizable from the exposed endpoint values. +- Causal implementation was corrected and stabilized at `d48f8fef08e77b8fa654f2852814c25c5d1baa79` after immediate source review of the first contents update; `fb314d8afccdf19f0074c64f6277d2edb290e907` adds explicit represented-mean coverage, and `440b78d86908fe1464e65bdbc4ceb5f9f6606c9f` removes an unreachable non-finite comparison branch while retaining the same scientific contract. +- Changelog trace `45116498f29c0d3421192d452e26182975b114ae` records the durable-evidence repair. + +This remains TEPP Validation Evidence artifact admission. It does not add a psychometric estimator, change Longital Modeling composition, or copy mutable arithmetic from fast-mlsirm. + +## Methodological reference + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +The ADEMP framing separates estimands, methods, data-generating mechanisms, and performance measures. TEPP applies that discipline at the evidence boundary: a reported performance summary is admitted only when the stored finite-sample fields can coexist under the producer contract actually used to generate them. From 0042b69b1baa3bc8ad2e9590d96bcd0c5df66461 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 13:06:53 +0900 Subject: [PATCH 142/576] placeholder --- .../monte-carlo-rank-two-replication-endpoint-exhaustion.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 docs/research/monte-carlo-rank-two-replication-endpoint-exhaustion.md diff --git a/docs/research/monte-carlo-rank-two-replication-endpoint-exhaustion.md b/docs/research/monte-carlo-rank-two-replication-endpoint-exhaustion.md new file mode 100644 index 000000000..b3a425249 --- /dev/null +++ b/docs/research/monte-carlo-rank-two-replication-endpoint-exhaustion.md @@ -0,0 +1 @@ +placeholder \ No newline at end of file From 066f14b62f5c3a81e2db7b79f58a60006754d138 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 13:07:16 +0900 Subject: [PATCH 143/576] chore: remove accidental placeholder research path --- .../monte-carlo-rank-two-replication-endpoint-exhaustion.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 docs/research/monte-carlo-rank-two-replication-endpoint-exhaustion.md diff --git a/docs/research/monte-carlo-rank-two-replication-endpoint-exhaustion.md b/docs/research/monte-carlo-rank-two-replication-endpoint-exhaustion.md deleted file mode 100644 index b3a425249..000000000 --- a/docs/research/monte-carlo-rank-two-replication-endpoint-exhaustion.md +++ /dev/null @@ -1 +0,0 @@ -placeholder \ No newline at end of file From 69abf5959ccb089b1ef10ecbfca4b56ede8652ff Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 13:07:43 +0900 Subject: [PATCH 144/576] docs(research): correct bounded-context wording --- .../research/monte-carlo-two-replication-endpoint-exhaustion.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/research/monte-carlo-two-replication-endpoint-exhaustion.md b/docs/research/monte-carlo-two-replication-endpoint-exhaustion.md index be99719e7..e543e38ef 100644 --- a/docs/research/monte-carlo-two-replication-endpoint-exhaustion.md +++ b/docs/research/monte-carlo-two-replication-endpoint-exhaustion.md @@ -28,7 +28,7 @@ The normalized operands are finite and bounded by one, so the final relative-dis - Causal implementation was corrected and stabilized at `d48f8fef08e77b8fa654f2852814c25c5d1baa79` after immediate source review of the first contents update; `fb314d8afccdf19f0074c64f6277d2edb290e907` adds explicit represented-mean coverage, and `440b78d86908fe1464e65bdbc4ceb5f9f6606c9f` removes an unreachable non-finite comparison branch while retaining the same scientific contract. - Changelog trace `45116498f29c0d3421192d452e26182975b114ae` records the durable-evidence repair. -This remains TEPP Validation Evidence artifact admission. It does not add a psychometric estimator, change Longital Modeling composition, or copy mutable arithmetic from fast-mlsirm. +This remains TEPP Validation Evidence artifact admission. It does not add a psychometric estimator, change Longitudinal Modeling composition, or copy mutable arithmetic from fast-mlsirm. ## Methodological reference From 4f1fdc52c1857072f79a3f91f80b5c4f9af8966d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 13:07:54 +0900 Subject: [PATCH 145/576] chore: remove misspelled research trace path --- ...te-carlo-two-repmin-endpoint-exhaustion.md | 37 ------------------- 1 file changed, 37 deletions(-) delete mode 100644 docs/research/monte-carlo-two-repmin-endpoint-exhaustion.md diff --git a/docs/research/monte-carlo-two-repmin-endpoint-exhaustion.md b/docs/research/monte-carlo-two-repmin-endpoint-exhaustion.md deleted file mode 100644 index 474fd333e..000000000 --- a/docs/research/monte-carlo-two-repmin-endpoint-exhaustion.md +++ /dev/null @@ -1,37 +0,0 @@ -# Two-replication nearest-rank endpoint exhaustion - -## Scientific contract - -`MonteCarloSummary` stores a represented sample mean, sample standard deviation (`n - 1` denominator), standard error of the mean, and inclusive nearest-rank percentile endpoints selected from retained replications. The carrier remains sign-neutral because it can summarize signed metrics such as bias as well as nonnegative metrics such the RMSE summaries admitted by `ValidationReport`. - -For more than two replications, lower and upper percentile endpoints do not identify the unreported retained values, so TEPP only enforces support that follows from the stored moments. The `n = 2` case is different. When the two nearest-rank endpoint values are numerically distinct, they must designate the two distinct retained observations. There are no remaining replications whose values could alter the represented mean or sample spread. - -Therefore, for `replication_count = 2` and `percentile_lower != percentile_upper`, the retained sample is exactly - -`[percentile_lower, percentile_upper]`. - -The recorded mean and sample SD must be coherent with that exhausted sample. This is stronger than the generic individual endpoint-radius and joint squared-deviation budget, but only in the finite-sample case where the artifact itself identifies every retained value. - -The public RED uses endpoints `[-0.5, 0.5]`. The canonical producer returns represented mean `0.0` and sample SD `sqrt(0.5)`. A payload that records the same two endpoint values with `SD = 1.0` and `SE = 1/sqrt(2)` passes the predecessor's standard-error coherence, individual endpoint support, and joint deviation-budget checks, yet no two-observation retained sample can produce it. A second fixture records mean `0.25`; it likewise satisfies the looser moment budget but cannot be the mean of the only two retained values. - -Equal numeric endpoints are not treated as sample exhaustion because nearest-rank lower and upper requests may select the same retained rank, or two retained observations may share the same represented value. Without percentile probabilities or rank multiplicity in the artifact, inferring two distinct observations from one numeric endpoint would overconstrain valid summaries. - -## Numerical implementation - -For the exhausted two-value case, admission reuses the same deterministic represented-mean and scaled sample-SD references as `summarize_replications`. The recorded and reconstructed values are compared after normalization by their own maximum magnitude, with the existing empirical-support relative tolerance. This avoids raw full-range subtraction and does not scale a near-zero mean comparison by the much larger endpoint magnitudes, which would otherwise admit a materially wrong near-zero mean. - -The normalized operands are finite and bounded by one, so the final relative-distance calculation cannot create a non-finite validation-only intermediate. The implementation therefore does not add an unreachable finite-check branch that would weaken owned branch-coverage evidence. - -## RED, causal repair, and traceability - -- Public RED `81bf0d9e2f1a28947b1343244002d6762b703f8a` adds `crates/validation_core/tests/monte_carlo_two_replication_endpoint_exhaustion_contract.rs` and demonstrates a two-replication summary that passes the predecessor's looser support checks but is not realizable from the exposed endpoint values. -- Causal implementation was corrected and stabilized at `d48f8fef08e77b8fa654f2852814c25c5d1baa79` after immediate source review of the first contents update; `fb314d8afccdf19f0074c64f6277d2edb290e907` adds explicit represented-mean coverage, and `440b78d86908fe1464e65bdbc4ceb5f9f6606c9f` removes an unreachable non-finite comparison branch while retaining the same scientific contract. -- Changelog trace `45116498f29c0d3421192d452e26182975b114ae` records the durable-evidence repair. - -This remains TEPP Validation Evidence artifact admission. It does not add a psychometric estimator, change Longitudinal Modeling composition, or copy mutable arithmetic from fast-mlsirm. - -## Methodological reference - -Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 - -The ADEMP framing separates estimands, methods, data-generating mechanisms, and performance measures. TEPP applies that discipline at the evidence boundary: a reported performance summary is admitted only when the stored finite-sample fields can coexist under the producer contract actually used to generate them. From a839c606fb2329ce1b339eb235c79e02abf40e16 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:00:20 +0900 Subject: [PATCH 146/576] test(validation): expose incoherent Wilson endpoint pairs --- ...n_report_wilson_pair_coherence_contract.rs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 crates/validation_core/tests/validation_report_wilson_pair_coherence_contract.rs diff --git a/crates/validation_core/tests/validation_report_wilson_pair_coherence_contract.rs b/crates/validation_core/tests/validation_report_wilson_pair_coherence_contract.rs new file mode 100644 index 000000000..aa8851835 --- /dev/null +++ b/crates/validation_core/tests/validation_report_wilson_pair_coherence_contract.rs @@ -0,0 +1,69 @@ +use validation_core::{ + ValidationError, ValidationReport, interval_coverage, wilson_coverage_interval, +}; + +fn base_report() -> ValidationReport { + ValidationReport { + study_label: "wilson-pair-coherence".into(), + rmse: 0.2, + rmse_standard_error: 0.05, + mean_bias: 0.0, + bias_standard_error: 0.01, + interval_coverage: 0.5, + coverage_wilson_lower: 0.2, + coverage_wilson_upper: 0.9, + temporal_order_accuracy: 1.0, + monte_carlo_rmse: None, + } +} + +#[test] +fn canonical_wilson_pair_remains_admissible() { + let truth = [0.0, 1.0, 2.0, 3.0]; + let lower = [-1.0, 0.0, 3.0, 4.0]; + let upper = [1.0, 2.0, 4.0, 5.0]; + let coverage = interval_coverage(&truth, &lower, &upper).expect("coverage"); + assert_eq!(coverage, 0.5); + let (wilson_lower, wilson_upper) = + wilson_coverage_interval(&truth, &lower, &upper, 1.96).expect("wilson"); + + let report = ValidationReport { + interval_coverage: coverage, + coverage_wilson_lower: wilson_lower, + coverage_wilson_upper: wilson_upper, + ..base_report() + }; + + assert_eq!(report.validate(), Ok(())); + assert!(report.to_json().is_ok()); + assert!(report.to_human_summary().is_ok()); +} + +#[test] +fn impossible_wilson_pair_fails_closed_across_report_boundaries() { + // At p = 0.5 every Wilson score interval is symmetric about 0.5 for every + // finite positive z and non-empty denominator. [0.2, 0.9] contains p but + // cannot be emitted by the canonical Wilson producer for that proportion. + let report = base_report(); + + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + assert_eq!(report.to_json(), Err(ValidationError::InvalidInput)); + assert_eq!( + report.to_human_summary(), + Err(ValidationError::InvalidInput) + ); + + let raw = r#"{ + "study_label":"wilson-pair-coherence", + "rmse":0.2, + "rmse_standard_error":0.05, + "mean_bias":0.0, + "bias_standard_error":0.01, + "interval_coverage":0.5, + "coverage_wilson_lower":0.2, + "coverage_wilson_upper":0.9, + "temporal_order_accuracy":1.0, + "monte_carlo_rmse":null + }"#; + assert!(serde_json::from_str::(raw).is_err()); +} From 38c5b8e83fe2433167afb6ece13e72b6608ceb03 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:02:10 +0900 Subject: [PATCH 147/576] fix(validation): enforce Wilson pair algebraic coherence --- crates/validation_core/src/report.rs | 75 +++++++++++++++++++++------- 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index 7cd4527d6..f746b0909 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -6,6 +6,37 @@ use serde::{Deserialize, Serialize}; const RMSE_STANDARD_ERROR_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; const MONTE_CARLO_RMSE_SUPPORT_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; +const WILSON_PAIR_ABSOLUTE_TOLERANCE: f64 = 64.0 * f64::EPSILON; + +/// Check whether a stored Wilson endpoint pair can arise from one Wilson score interval. +/// +/// For empirical coverage `p` and `a = z² / n`, the Wilson roots satisfy +/// `L * U = p² / (1 + a)` and `L + U = 1 + (2p - 1) / (1 + a)`. Eliminating +/// the unrecorded `a` gives a necessary endpoint-pair identity. For `p < 0.5`, +/// the equivalent identity on the uncovered proportion avoids squaring a tiny +/// `p`. All terms remain probability-scaled, so a small absolute binary64 +/// tolerance is sufficient without overflow-prone reconstruction of `n` or `z`. +fn wilson_pair_is_algebraically_coherent(p: f64, lower: f64, upper: f64) -> bool { + if p == 0.0 || p == 1.0 { + return true; + } + + let endpoint_sum = lower + upper; + let (left, right) = if p >= 0.5 { + ( + p * p * (endpoint_sum - 1.0), + (2.0 * p - 1.0) * lower * upper, + ) + } else { + let uncovered = 1.0 - p; + ( + uncovered * uncovered * (1.0 - endpoint_sum), + (1.0 - 2.0 * p) * (1.0 - lower) * (1.0 - upper), + ) + }; + + (left - right).abs() <= WILSON_PAIR_ABSOLUTE_TOLERANCE +} /// Machine-readable recovery report for a single study. #[derive(Clone, Debug, PartialEq)] @@ -41,21 +72,24 @@ impl ValidationReport { /// allows a small relative binary64 tolerance at that support boundary. Exact /// zero RMSE is perfect recovery and therefore still requires exact-zero RMSE /// standard error. Empirical coverage, Wilson endpoints, and temporal-order - /// accuracy are probabilities in `[0, 1]`; the Wilson interval is ordered and - /// must contain the empirical coverage recorded in the same report. Mean - /// signed bias remains unrestricted in sign. A generic [`MonteCarloSummary`] - /// may summarize a signed metric, but when it occupies `monte_carlo_rmse` every - /// retained replication is nonnegative. Its mean and percentile endpoints are - /// therefore nonnegative. Nonnegative sample support additionally implies - /// `SD <= sqrt(n) * mean`, `SE(mean) <= mean`, and every retained value—and thus - /// every inclusive nearest-rank percentile endpoint—is at most `n * mean`. - /// Admission evaluates the percentile support as `endpoint / mean <= n` with a - /// small relative binary64 tolerance so the check does not overflow a finite - /// sample sum. A zero Monte Carlo RMSE mean is exact perfect recovery across - /// every retained replication, so spread, standard error, and empirical - /// percentile endpoints must all be zero as well. These checks prevent a - /// finite but scientifically impossible payload from becoming durable - /// Validation Evidence. + /// accuracy are probabilities in `[0, 1]`; the Wilson interval is ordered, + /// contains the empirical coverage recorded in the same report, and its two + /// endpoints must satisfy the same Wilson-score root identity for that + /// coverage. This prevents two individually plausible bounds from being + /// combined into an interval that no finite positive Wilson `z² / n` can + /// produce. Mean signed bias remains unrestricted in sign. A generic + /// [`MonteCarloSummary`] may summarize a signed metric, but when it occupies + /// `monte_carlo_rmse` every retained replication is nonnegative. Its mean and + /// percentile endpoints are therefore nonnegative. Nonnegative sample support + /// additionally implies `SD <= sqrt(n) * mean`, `SE(mean) <= mean`, and every + /// retained value—and thus every inclusive nearest-rank percentile endpoint—is + /// at most `n * mean`. Admission evaluates the percentile support as + /// `endpoint / mean <= n` with a small relative binary64 tolerance so the check + /// does not overflow a finite sample sum. A zero Monte Carlo RMSE mean is exact + /// perfect recovery across every retained replication, so spread, standard + /// error, and empirical percentile endpoints must all be zero as well. These + /// checks prevent a finite but scientifically impossible payload from becoming + /// durable Validation Evidence. /// /// # Errors /// @@ -105,6 +139,11 @@ impl ValidationReport { if self.coverage_wilson_lower > self.coverage_wilson_upper || self.interval_coverage < self.coverage_wilson_lower || self.interval_coverage > self.coverage_wilson_upper + || !wilson_pair_is_algebraically_coherent( + self.interval_coverage, + self.coverage_wilson_lower, + self.coverage_wilson_upper, + ) { return Err(ValidationError::InvalidInput); } @@ -302,9 +341,9 @@ mod tests { rmse_standard_error: 0.01, mean_bias: 0.0, bias_standard_error: 0.02, - interval_coverage: 0.95, - coverage_wilson_lower: 0.9, - coverage_wilson_upper: 0.98, + interval_coverage: 0.5, + coverage_wilson_lower: 0.2, + coverage_wilson_upper: 0.8, temporal_order_accuracy: 1.0, monte_carlo_rmse: Some(MonteCarloSummary { replication_count: 10, From c1cb16a78499648c10d6d5a8dad5e212a267064a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:02:30 +0900 Subject: [PATCH 148/576] test(validation): cover complementary Wilson identity --- ...n_report_wilson_pair_coherence_contract.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/crates/validation_core/tests/validation_report_wilson_pair_coherence_contract.rs b/crates/validation_core/tests/validation_report_wilson_pair_coherence_contract.rs index aa8851835..d98313cd3 100644 --- a/crates/validation_core/tests/validation_report_wilson_pair_coherence_contract.rs +++ b/crates/validation_core/tests/validation_report_wilson_pair_coherence_contract.rs @@ -39,6 +39,25 @@ fn canonical_wilson_pair_remains_admissible() { assert!(report.to_human_summary().is_ok()); } +#[test] +fn complementary_identity_accepts_non_symmetric_canonical_pair() { + let truth = [0.0, 1.0, 2.0, 3.0]; + let lower = [-1.0, 2.0, 3.0, 4.0]; + let upper = [1.0, 3.0, 4.0, 5.0]; + let coverage = interval_coverage(&truth, &lower, &upper).expect("coverage"); + assert_eq!(coverage, 0.25); + let (wilson_lower, wilson_upper) = + wilson_coverage_interval(&truth, &lower, &upper, 1.96).expect("wilson"); + + let report = ValidationReport { + interval_coverage: coverage, + coverage_wilson_lower: wilson_lower, + coverage_wilson_upper: wilson_upper, + ..base_report() + }; + assert_eq!(report.validate(), Ok(())); +} + #[test] fn impossible_wilson_pair_fails_closed_across_report_boundaries() { // At p = 0.5 every Wilson score interval is symmetric about 0.5 for every From 6af0821bc2fca3e9f101cfa4ad36048ecfaa6ddd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:02:53 +0900 Subject: [PATCH 149/576] docs(changelog): record Wilson pair coherence repair --- CHANGELOG.d/validation-report-wilson-pair-coherence.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-report-wilson-pair-coherence.md diff --git a/CHANGELOG.d/validation-report-wilson-pair-coherence.md b/CHANGELOG.d/validation-report-wilson-pair-coherence.md new file mode 100644 index 000000000..02495f5da --- /dev/null +++ b/CHANGELOG.d/validation-report-wilson-pair-coherence.md @@ -0,0 +1,4 @@ +### Fixed + +- `ValidationReport` now rejects interval-coverage evidence whose stored Wilson lower and upper endpoints merely contain the empirical coverage but cannot be the two roots of one Wilson score interval for that same proportion. Admission uses the Wilson root identities after eliminating the unrecorded `z² / n` term, with the complementary uncovered-proportion identity below `p = 0.5` and a probability-scale binary64 tolerance. +- Canonical Wilson output remains admissible, including asymmetric interior coverage; JSON serialization, serde ingress, and human projection share the same fail-closed scientific boundary. From 5be03bcaaa040fbc0f8d2e749565cf40a8a2f20e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:03:08 +0900 Subject: [PATCH 150/576] docs(research): trace Wilson pair coherence evidence --- ...validation-report-wilson-pair-coherence.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 docs/research/validation-report-wilson-pair-coherence.md diff --git a/docs/research/validation-report-wilson-pair-coherence.md b/docs/research/validation-report-wilson-pair-coherence.md new file mode 100644 index 000000000..f9a9db381 --- /dev/null +++ b/docs/research/validation-report-wilson-pair-coherence.md @@ -0,0 +1,47 @@ +# ValidationReport Wilson endpoint-pair coherence + +## Problem + +`ValidationReport` previously required `coverage_wilson_lower <= interval_coverage <= coverage_wilson_upper`, but containment alone does not establish that the two stored endpoints came from one Wilson score interval for the same empirical coverage. For example, `p = 0.5`, `L = 0.2`, `U = 0.9` is ordered and contains `p`, yet no Wilson score interval can produce that pair: at `p = 0.5` the Wilson roots are symmetric about `0.5` for every finite positive `z² / n`. + +This matters at the Validation Evidence boundary because a durable artifact must not combine independently plausible numbers into a scientifically unrealizable interval. It does not change the Wilson estimator or psychometric model. + +## Algebraic invariant + +Let `p` be the empirical coverage and `a = z² / n > 0`. The Wilson roots satisfy + +`L * U = p² / (1 + a)` + +and + +`L + U = 1 + (2p - 1) / (1 + a)`. + +Eliminating `a` gives the necessary pair identity + +`p² * (L + U - 1) = (2p - 1) * L * U`. + +For `p < 0.5`, TEPP evaluates the equivalent identity on the uncovered proportion `q = 1 - p`: + +`q² * (1 - L - U) = (1 - 2p) * (1 - L) * (1 - U)`. + +Using the complementary form avoids needlessly squaring a tiny `p`. Every term is probability-scaled, so the admission comparison cannot overflow finite binary64 inputs. A `64 * EPSILON` absolute tolerance admits normal endpoint-rounding error while rejecting materially incoherent pairs such as `[0.2, 0.9]` at `p = 0.5`. + +This is a necessary, not sufficient, provenance check. The current report still does not retain the coverage denominator or the critical value `z`; therefore this repair does not claim full recomputation or denominator provenance. That remains a separate schema-level Validation Evidence gap rather than being inferred from unavailable data. + +## Executable trace + +- Public RED: `a839c606fb2329ce1b339eb235c79e02abf40e16`, `crates/validation_core/tests/validation_report_wilson_pair_coherence_contract.rs`. +- Causal source repair: `38c5b8e83fe2433167afb6ece13e72b6608ceb03`, `crates/validation_core/src/report.rs`. +- Complementary-identity coverage: `c1cb16a78499648c10d6d5a8dad5e212a267064a`. +- Changelog trace: `6af0821bc2fca3e9f101cfa4ad36048ecfaa6ddd`. +- Owner: TEPP Validation Evidence. No reusable static psychometric estimator is introduced; fast-mlsirm remains untouched. No semantic LLM path is involved. + +## Methodological trace + +Wilson's score interval is the primary statistical source for the interval family used by `validation_core::wilson_coverage_interval`. The AERA/APA/NCME *Standards for Educational and Psychological Testing* remain the current published testing standards while the sponsoring organizations revise the 2014 edition; this repair follows the evidence-integrity principle by refusing a durable summary whose reported components are mutually inconsistent. + +### 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. + +Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 From acf573526f955a7700ebd753b83e0baad120628d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:10:14 +0900 Subject: [PATCH 151/576] test(validation): keep RMSE-SE fixture Wilson-coherent --- ...ion_report_rmse_standard_error_upper_bound_contract.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/validation_core/tests/validation_report_rmse_standard_error_upper_bound_contract.rs b/crates/validation_core/tests/validation_report_rmse_standard_error_upper_bound_contract.rs index 17e5af21e..f8f62ac4c 100644 --- a/crates/validation_core/tests/validation_report_rmse_standard_error_upper_bound_contract.rs +++ b/crates/validation_core/tests/validation_report_rmse_standard_error_upper_bound_contract.rs @@ -9,9 +9,9 @@ fn report_with(rmse: f64, rmse_standard_error: f64) -> ValidationReport { rmse_standard_error, mean_bias: 0.0, bias_standard_error: 0.0, - interval_coverage: 0.95, - coverage_wilson_lower: 0.90, - coverage_wilson_upper: 0.98, + interval_coverage: 0.5, + coverage_wilson_lower: 0.2, + coverage_wilson_upper: 0.8, temporal_order_accuracy: 1.0, monte_carlo_rmse: None, } @@ -38,6 +38,6 @@ fn report_rejects_rmse_standard_error_above_squared_residual_support_bound() { Err(ValidationError::InvalidInput) ); - let ingress = r#"{"study_label":"rmse-se-support","rmse":0.2,"rmse_standard_error":0.11,"mean_bias":0.0,"bias_standard_error":0.0,"interval_coverage":0.95,"coverage_wilson_lower":0.9,"coverage_wilson_upper":0.98,"temporal_order_accuracy":1.0,"monte_carlo_rmse":null}"#; + let ingress = r#"{"study_label":"rmse-se-support","rmse":0.2,"rmse_standard_error":0.11,"mean_bias":0.0,"bias_standard_error":0.0,"interval_coverage":0.5,"coverage_wilson_lower":0.2,"coverage_wilson_upper":0.8,"temporal_order_accuracy":1.0,"monte_carlo_rmse":null}"#; assert!(serde_json::from_str::(ingress).is_err()); } From 3aabf9bbbf739d35b55fa613750bc8b7576d15ee Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:10:27 +0900 Subject: [PATCH 152/576] test(validation): isolate RMSE summary domain from Wilson evidence --- ...lidation_report_rmse_summary_domain_contract.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs b/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs index 10b0b4d8d..d44cefc1f 100644 --- a/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs +++ b/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs @@ -7,9 +7,9 @@ fn report_with_rmse_summary(summary: MonteCarloSummary) -> ValidationReport { rmse_standard_error: 0.01, mean_bias: 0.0, bias_standard_error: 0.01, - interval_coverage: 0.95, - coverage_wilson_lower: 0.85, - coverage_wilson_upper: 0.99, + interval_coverage: 0.5, + coverage_wilson_lower: 0.2, + coverage_wilson_upper: 0.8, temporal_order_accuracy: 0.9, monte_carlo_rmse: Some(summary), } @@ -37,7 +37,7 @@ fn validation_report_rejects_negative_monte_carlo_rmse_evidence() { mean: 0.02, standard_deviation: 0.03, standard_error: 0.006, - percentile_lower: -0.01, + percentile_lower: -0.1, percentile_upper: 0.07, }); assert_eq!( @@ -51,9 +51,9 @@ fn validation_report_rejects_negative_monte_carlo_rmse_evidence() { "rmse_standard_error":0.01, "mean_bias":0.0, "bias_standard_error":0.01, - "interval_coverage":0.95, - "coverage_wilson_lower":0.85, - "coverage_wilson_upper":0.99, + "interval_coverage":0.5, + "coverage_wilson_lower":0.2, + "coverage_wilson_upper":0.8, "temporal_order_accuracy":0.9, "monte_carlo_rmse":{ "replication_count":20, From 8448ab32e8566c8a778dee07e7aa2db180932c03 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:10:34 +0900 Subject: [PATCH 153/576] test(validation): restore intended negative-percentile edge --- .../tests/validation_report_rmse_summary_domain.rs | 1 + 1 file changed, 1 insertion(+) create mode 100644 crates/validation_core/tests/validation_report_rmse_summary_domain.rs diff --git a/crates/validation_core/tests/validation_report_rmse_summary_domain.rs b/crates/validation_core/tests/validation_report_rmse_summary_domain.rs new file mode 100644 index 000000000..b3a425249 --- /dev/null +++ b/crates/validation_core/tests/validation_report_rmse_summary_domain.rs @@ -0,0 +1 @@ +placeholder \ No newline at end of file From 378de50f6ac533ba5a89772a0dcf6916e3b5e419 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:10:49 +0900 Subject: [PATCH 154/576] chore(validation): remove accidental transient test path --- .../tests/validation_report_rmse_summary_domain.rs | 1 - 1 file changed, 1 deletion(-) delete mode 100644 crates/validation_core/tests/validation_report_rmse_summary_domain.rs diff --git a/crates/validation_core/tests/validation_report_rmse_summary_domain.rs b/crates/validation_core/tests/validation_report_rmse_summary_domain.rs deleted file mode 100644 index b3a425249..000000000 --- a/crates/validation_core/tests/validation_report_rmse_summary_domain.rs +++ /dev/null @@ -1 +0,0 @@ -placeholder \ No newline at end of file From 582e97f5b97090415d51bf0106714c67fa76dfcc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:11:08 +0900 Subject: [PATCH 155/576] test(validation): restore intended negative-percentile edge --- .../tests/validation_report_rmse_summary_domain_contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs b/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs index d44cefc1f..bb01f7d78 100644 --- a/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs +++ b/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs @@ -37,7 +37,7 @@ fn validation_report_rejects_negative_monte_carlo_rmse_evidence() { mean: 0.02, standard_deviation: 0.03, standard_error: 0.006, - percentile_lower: -0.1, + percentile_lower: -0.01, percentile_upper: 0.07, }); assert_eq!( From 37329b2be280665520903d5f7d4e808623de83bb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:11:21 +0900 Subject: [PATCH 156/576] test(validation): isolate RMSE support from Wilson evidence --- ...on_report_rmse_summary_nonnegative_support_contract.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs b/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs index 9eab54484..e1fb0e4cf 100644 --- a/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs +++ b/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs @@ -7,9 +7,9 @@ fn report_with(summary: MonteCarloSummary) -> ValidationReport { rmse_standard_error: 0.05, mean_bias: 0.0, bias_standard_error: 0.0, - interval_coverage: 0.95, - coverage_wilson_lower: 0.8, - coverage_wilson_upper: 1.0, + interval_coverage: 0.5, + coverage_wilson_lower: 0.2, + coverage_wilson_upper: 0.8, temporal_order_accuracy: 1.0, monte_carlo_rmse: Some(summary), } @@ -43,7 +43,7 @@ fn monte_carlo_rmse_rejects_spread_impossible_for_nonnegative_replications() { Err(ValidationError::InvalidInput) ); - let payload = r#"{\"study_label\":\"rmse-summary-nonnegative-support\",\"rmse\":0.2,\"rmse_standard_error\":0.05,\"mean_bias\":0.0,\"bias_standard_error\":0.0,\"interval_coverage\":0.95,\"coverage_wilson_lower\":0.8,\"coverage_wilson_upper\":1.0,\"temporal_order_accuracy\":1.0,\"monte_carlo_rmse\":{\"replication_count\":4,\"mean\":1.0,\"standard_deviation\":3.0,\"standard_error\":1.5,\"percentile_lower\":0.0,\"percentile_upper\":4.0}}"#; + let payload = r#"{\"study_label\":\"rmc-summary-nonnegative-support\",\"rmse\":0.2,\"rmse_standard_error\":0.05,\"mean_bias\":0.0,\"bias_standard_error\":0.0,\"interval_coverage\":0.5,\"coverage_wilson_lower\":0.2,\"coverage_wilson_upper\":0.8,\"temporal_order_accuracy\":1.0,\"monte_carlo_rmse\":{\"replication_count\":4,\"mean\":1.0,\"standard_deviation\":3.0,\"standard_error\":1.5,\"percentile_lower\":0.0,\"percentile_upper\":4.0}}"#; assert!(serde_json::from_str::(payload).is_err()); } From 361349912d0a1f5c018ee5c483b79d47b7febb75 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:11:35 +0900 Subject: [PATCH 157/576] test(validation): isolate percentile support from Wilson evidence --- ...ion_report_rmse_summary_percentile_support_contract.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/validation_core/tests/validation_report_rmse_summary_percentile_support_contract.rs b/crates/validation_core/tests/validation_report_rmse_summary_percentile_support_contract.rs index ccc3b2be8..5f67cdaf3 100644 --- a/crates/validation_core/tests/validation_report_rmse_summary_percentile_support_contract.rs +++ b/crates/validation_core/tests/validation_report_rmse_summary_percentile_support_contract.rs @@ -7,9 +7,9 @@ fn report_with(summary: MonteCarloSummary) -> ValidationReport { rmse_standard_error: 0.05, mean_bias: 0.0, bias_standard_error: 0.0, - interval_coverage: 0.95, - coverage_wilson_lower: 0.8, - coverage_wilson_upper: 1.0, + interval_coverage: 0.5, + coverage_wilson_lower: 0.2, + coverage_wilson_upper: 0.8, temporal_order_accuracy: 1.0, monte_carlo_rmse: Some(summary), } @@ -41,7 +41,7 @@ fn monte_carlo_rmse_rejects_percentile_above_nonnegative_sample_sum_support() { Err(ValidationError::InvalidInput) ); - let payload = r#"{"study_label":"rmse-summary-percentile-support","rmse":0.2,"rmse_standard_error":0.05,"mean_bias":0.0,"bias_standard_error":0.0,"interval_coverage":0.95,"coverage_wilson_lower":0.8,"coverage_wilson_upper":1.0,"temporal_order_accuracy":1.0,"monte_carlo_rmse":{"replication_count":4,"mean":1.0,"standard_deviation":0.5,"standard_error":0.25,"percentile_lower":0.0,"percentile_upper":5.0}}"#; + let payload = r#"{"study_label":"rmse-summary-percentile-support","rmse":0.2,"rmse_standard_error":0.05,"mean_bias":0.0,"coverage_wilson_lower":0.2,"bias_standard_error":0.0,"interval_coverage":0.5,"coverage_wilson_upper":0.8,"temporal_order_accuracy":1.0,"monte_carlo_rmse":{"replication_count":4,"mean":1.0,"standard_deviation":0.5,"standard_error":0.25,"percentile_lower":0.0,"percentile_upper":5.0}}"#; assert!(serde_json::from_str::(payload).is_err()); } From ccd30ede3b6e90e0ddff0c0c9ef764f320c2cec1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:11:50 +0900 Subject: [PATCH 158/576] test(validation): keep generic report fixture Wilson-coherent --- ...lidation_report_scientific_invariants_contract.rs | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs b/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs index 4a02e23a5..eb3ecd552 100644 --- a/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs +++ b/crates/validation_core/tests/validation_report_scientific_invariants_contract.rs @@ -7,9 +7,9 @@ fn valid_report() -> ValidationReport { rmse_standard_error: 0.01, mean_bias: -0.02, bias_standard_error: 0.02, - interval_coverage: 0.8, - coverage_wilson_lower: 0.6, - coverage_wilson_upper: 0.9, + interval_coverage: 0.5, + coverage_wilson_lower: 0.2, + coverage_wilson_upper: 0.8, temporal_order_accuracy: 0.75, monte_carlo_rmse: None, } @@ -62,7 +62,7 @@ fn validation_report_rejects_incoherent_wilson_evidence() { assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); let mut report = valid_report(); - report.coverage_wilson_upper = 0.79; + report.coverage_wilson_upper = 0.49; assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); } @@ -84,8 +84,8 @@ fn every_report_projection_enforces_validation() { "mean_bias":0.0, "bias_standard_error":0.01, "interval_coverage":1.5, - "coverage_wilson_lower":0.6, - "coverage_wilson_upper":0.9, + "coverage_wilson_lower":0.2, + "coverage_wilson_upper":0.8, "temporal_order_accuracy":0.75, "monte_carlo_rmse":null }"#; From dba62e6ecfefbf7e76a6f96b3a50a3493e4f9f80 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:12:07 +0900 Subject: [PATCH 159/576] test(validation): restore RMSE support fixture label --- ...lidation_report_rmse_summary_nonnegative_support_contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs b/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs index e1fb0e4cf..18f7de375 100644 --- a/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs +++ b/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs @@ -43,7 +43,7 @@ fn monte_carlo_rmse_rejects_spread_impossible_for_nonnegative_replications() { Err(ValidationError::InvalidInput) ); - let payload = r#"{\"study_label\":\"rmc-summary-nonnegative-support\",\"rmse\":0.2,\"rmse_standard_error\":0.05,\"mean_bias\":0.0,\"bias_standard_error\":0.0,\"interval_coverage\":0.5,\"coverage_wilson_lower\":0.2,\"coverage_wilson_upper\":0.8,\"temporal_order_accuracy\":1.0,\"monte_carlo_rmse\":{\"replication_count\":4,\"mean\":1.0,\"standard_deviation\":3.0,\"standard_error\":1.5,\"percentile_lower\":0.0,\"percentile_upper\":4.0}}"#; + let payload = r#"{\"study_label\":\"rmse-summary-nonnegative-support\",\"rmse\":0.2,\"rmse_standard_error\":0.05,\"mean_bias\":0.0,\"bias_standard_error\":0.0,\"interval_coverage\":0.5,\"coverage_wilson_lower\":0.0,\"coverage_wilson_upper\":0.8,\"temporal_order_accuracy\":1.0,\"monte_carlo_rmse\":{\"replication_count\":4,\"mean\":1.0,\"standard_deviation\":3.0,\"standard_error\":1.5,\"percentile_lower\":0.0,\"percentile_upper\":4.0}}"#; assert!(serde_json::from_str::(payload).is_err()); } From f76354e0c521e8b12e704929dc2ddc433ad4e0b4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:12:17 +0900 Subject: [PATCH 160/576] test(validation): keep ingress Wilson fixture coherent --- .../tests/validation_report_rmse_nonnegative_support_contract.rs | 1 + 1 file changed, 1 insertion(+) create mode 100644 crates/validation_core/tests/validation_report_rmse_nonnegative_support_contract.rs diff --git a/crates/validation_core/tests/validation_report_rmse_nonnegative_support_contract.rs b/crates/validation_core/tests/validation_report_rmse_nonnegative_support_contract.rs new file mode 100644 index 000000000..0fe80e68f --- /dev/null +++ b/crates/validation_core/tests/validation_report_rmse_nonnegative_support_contract.rs @@ -0,0 +1 @@ +use validation_core::{MonteCarloSummary, ValidationError, ValidationReport}; \ No newline at end of file From 055bf972d0e80e48a6f23a13162c13d05efc8acf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:12:26 +0900 Subject: [PATCH 161/576] chore(validation): remove accidental transient test file --- .../tests/validation_report_rmse_nonnegative_support_contract.rs | 1 - 1 file changed, 1 deletion(-) delete mode 100644 crates/validation_core/tests/validation_report_rmse_nonnegative_support_contract.rs diff --git a/crates/validation_core/tests/validation_report_rmse_nonnegative_support_contract.rs b/crates/validation_core/tests/validation_report_rmse_nonnegative_support_contract.rs deleted file mode 100644 index 0fe80e68f..000000000 --- a/crates/validation_core/tests/validation_report_rmse_nonnegative_support_contract.rs +++ /dev/null @@ -1 +0,0 @@ -use validation_core::{MonteCarloSummary, ValidationError, ValidationReport}; \ No newline at end of file From fee458966a9dfb64da2aba01f242fe9e2e613540 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:12:40 +0900 Subject: [PATCH 162/576] test(validation): keep ingress Wilson fixture coherent --- ...lidation_report_rmse_summary_nonnegative_support_contract.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs b/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs index 18f7de375..0594909d2 100644 --- a/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs +++ b/crates/validation_core/tests/validation_report_rmse_summary_nonnegative_support_contract.rs @@ -43,7 +43,7 @@ fn monte_carlo_rmse_rejects_spread_impossible_for_nonnegative_replications() { Err(ValidationError::InvalidInput) ); - let payload = r#"{\"study_label\":\"rmse-summary-nonnegative-support\",\"rmse\":0.2,\"rmse_standard_error\":0.05,\"mean_bias\":0.0,\"bias_standard_error\":0.0,\"interval_coverage\":0.5,\"coverage_wilson_lower\":0.0,\"coverage_wilson_upper\":0.8,\"temporal_order_accuracy\":1.0,\"monte_carlo_rmse\":{\"replication_count\":4,\"mean\":1.0,\"standard_deviation\":3.0,\"standard_error\":1.5,\"percentile_lower\":0.0,\"percentile_upper\":4.0}}"#; + let payload = r#"{\"study_label\":\"rmse-summary-nonnegative-support\",\"rmse\":0.2,\"rmse_standard_error\":0.05,\"mean_bias\":0.0,\"bias_standard_error\":0.0,\"interval_coverage\":0.5,\"coverage_wilson_lower\":0.2,\"coverage_wilson_upper\":0.8,\"temporal_order_accuracy\":1.0,\"monte_carlo_rmse\":{\"replication_count\":4,\"mean\":1.0,\"standard_deviation\":3.0,\"standard_error\":1.5,\"percentile_lower\":0.0,\"percentile_upper\":4.0}}"#; assert!(serde_json::from_str::(payload).is_err()); } From cdab822260bbb95a594e19c6617def69bb6fc116 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:13:09 +0900 Subject: [PATCH 163/576] test(validation): preserve generic-valid RMSE domain fixtures --- .../validation_report_rmse_summary_domain_contract.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs b/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs index bb01f7d78..c4f1eb10b 100644 --- a/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs +++ b/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs @@ -21,7 +21,7 @@ fn validation_report_rejects_negative_monte_carlo_rmse_evidence() { replication_count: 20, mean: -0.1, standard_deviation: 0.02, - standard_error: 0.004, + standard_error: 0.02 / 20.0_f64.sqrt(), percentile_lower: -0.14, percentile_upper: -0.06, }); @@ -36,7 +36,7 @@ fn validation_report_rejects_negative_monte_carlo_rmse_evidence() { replication_count: 20, mean: 0.02, standard_deviation: 0.03, - standard_error: 0.006, + standard_error: 0.03 / 20.0_f64.sqrt(), percentile_lower: -0.01, percentile_upper: 0.07, }); @@ -49,6 +49,7 @@ fn validation_report_rejects_negative_monte_carlo_rmse_evidence() { "study_label":"rmse-domain-contract", "rmse":0.2, "rmse_standard_error":0.01, + "mean":0.0, "mean_bias":0.0, "bias_standard_error":0.01, "interval_coverage":0.5, @@ -59,7 +60,7 @@ fn validation_report_rejects_negative_monte_carlo_rmse_evidence() { "replication_count":20, "mean":-0.1, "standard_deviation":0.02, - "standard_error":0.004, + "standard_error":0.004472135954999579, "percentile_lower":-0.14, "percentile_upper":-0.06 } From b9eb9465b155b0fd3f44d0ff88b429485f038c8e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:13:44 +0900 Subject: [PATCH 164/576] test(validation): remove stray wire-field mutation --- .../tests/validation_report_rmse_summary_domain_contract.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs b/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs index c4f1eb10b..b2731b584 100644 --- a/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs +++ b/crates/validation_core/tests/validation_report_rmse_summary_domain_contract.rs @@ -49,7 +49,6 @@ fn validation_report_rejects_negative_monte_carlo_rmse_evidence() { "study_label":"rmse-domain-contract", "rmse":0.2, "rmse_standard_error":0.01, - "mean":0.0, "mean_bias":0.0, "bias_standard_error":0.01, "interval_coverage":0.5, From 59e40b4f0ebbb3b991c84724ded157ffe974abcd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:14:00 +0900 Subject: [PATCH 165/576] test(validation): preserve generic-valid typed percentile RED --- ...ort_rmse_summary_percentile_support_contract.rs | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/crates/validation_core/tests/validation_report_rmse_summary_percentile_support_contract.rs b/crates/validation_core/tests/validation_report_rmse_summary_percentile_support_contract.rs index 5f67cdaf3..8576c28c7 100644 --- a/crates/validation_core/tests/validation_report_rmse_summary_percentile_support_contract.rs +++ b/crates/validation_core/tests/validation_report_rmse_summary_percentile_support_contract.rs @@ -20,17 +20,19 @@ fn monte_carlo_rmse_rejects_percentile_above_nonnegative_sample_sum_support() { // For n nonnegative RMSE replications with represented mean m, every retained // replication is bounded by the finite sample sum n*m. Any empirical nearest-rank // percentile is one of those retained values and therefore cannot exceed n*m. + // The generic signed carrier's moment support still admits this fixture. let impossible_rmse_summary = MonteCarloSummary { replication_count: 4, mean: 1.0, - standard_deviation: 0.5, - standard_error: 0.25, + standard_deviation: 2.0, + standard_error: 1.0, percentile_lower: 0.0, - percentile_upper: 5.0, + percentile_upper: 4.1, }; - // The generic carrier cannot impose this bound because it also summarizes - // signed metrics. The stronger support belongs to the typed RMSE slot. + // The generic carrier cannot impose the nonnegative sample-sum bound because + // it also summarizes signed metrics. The stronger support belongs to the typed + // RMSE slot. assert!(impossible_rmse_summary.validate().is_ok()); let report = report_with(impossible_rmse_summary); @@ -41,7 +43,7 @@ fn monte_carlo_rmse_rejects_percentile_above_nonnegative_sample_sum_support() { Err(ValidationError::InvalidInput) ); - let payload = r#"{"study_label":"rmse-summary-percentile-support","rmse":0.2,"rmse_standard_error":0.05,"mean_bias":0.0,"coverage_wilson_lower":0.2,"bias_standard_error":0.0,"interval_coverage":0.5,"coverage_wilson_upper":0.8,"temporal_order_accuracy":1.0,"monte_carlo_rmse":{"replication_count":4,"mean":1.0,"standard_deviation":0.5,"standard_error":0.25,"percentile_lower":0.0,"percentile_upper":5.0}}"#; + let payload = r#"{"study_label":"rmse-summary-percentile-support","rmse":0.2,"rmse_standard_error":0.05,"mean_bias":0.0,"bias_standard_error":0.0,"interval_coverage":0.5,"coverage_wilson_lower":0.2,"coverage_wilson_upper":0.8,"temporal_order_accuracy":1.0,"monte_carlo_rmse":{"replication_count":4,"mean":1.0,"standard_deviation":2.0,"standard_error":1.0,"percentile_lower":0.0,"percentile_upper":4.1}}"#; assert!(serde_json::from_str::(payload).is_err()); } From 1246c583161d7f64a7d33139cec46665ee745584 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:16:17 +0900 Subject: [PATCH 166/576] docs(research): record Wilson dependent-fixture doctoring --- .../validation-report-wilson-pair-coherence.md | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/docs/research/validation-report-wilson-pair-coherence.md b/docs/research/validation-report-wilson-pair-coherence.md index f9a9db381..dd6e7328a 100644 --- a/docs/research/validation-report-wilson-pair-coherence.md +++ b/docs/research/validation-report-wilson-pair-coherence.md @@ -26,7 +26,15 @@ For `p < 0.5`, TEPP evaluates the equivalent identity on the uncovered proportio Using the complementary form avoids needlessly squaring a tiny `p`. Every term is probability-scaled, so the admission comparison cannot overflow finite binary64 inputs. A `64 * EPSILON` absolute tolerance admits normal endpoint-rounding error while rejecting materially incoherent pairs such as `[0.2, 0.9]` at `p = 0.5`. -This is a necessary, not sufficient, provenance check. The current report still does not retain the coverage denominator or the critical value `z`; therefore this repair does not claim full recomputation or denominator provenance. That remains a separate schema-level Validation Evidence gap rather than being inferred from unavailable data. +This is a necessary, not sufficient, provenance check. The current report still does not retain the coverage denominator or the critical value `z`; therefore this repair does not claim full recomputation or denominator provenance. The existing producer can also admit a positive `z` whose squared binary64 representation underflows to zero, so this artifact-level identity is deliberately not strengthened into a non-degeneracy rule that the producer itself does not yet guarantee. Those are separate producer/schema questions rather than facts inferred from unavailable evidence. + +## Dependent-fixture review + +Adding a cross-field invariant changed the validity of old branch fixtures that used arbitrary Wilson-looking probabilities while testing unrelated RMSE/Monte Carlo behavior. Self-review therefore replaced those incidental values with the exactly coherent `p = 0.5`, `[0.2, 0.8]` pair, which corresponds to a positive finite `z² / n = 0.5625`. The intended RED condition in each test remains unchanged. + +The same review rechecked typed RMSE fixtures against the already-landed generic `MonteCarloSummary` moment contracts. Where an older typed test had become generic-invalid, its sample statistics were repaired so the generic carrier is valid and the typed RMSE boundary remains the sole reason for refusal. In particular, the nonnegative percentile RED now uses `n = 4`, `mean = 1`, `SD = 2`, `SE = 1`, and `upper = 4.1`: it fits the generic individual/joint moment support but exceeds the typed nonnegative RMSE sample-sum bound `n * mean = 4`. + +These fixture changes are test doctoring, not evidence-gate weakening. They remove confounding failure causes introduced by stronger predecessor contracts. ## Executable trace @@ -34,6 +42,7 @@ This is a necessary, not sufficient, provenance check. The current report still - Causal source repair: `38c5b8e83fe2433167afb6ece13e72b6608ceb03`, `crates/validation_core/src/report.rs`. - Complementary-identity coverage: `c1cb16a78499648c10d6d5a8dad5e212a267064a`. - Changelog trace: `6af0821bc2fca3e9f101cfa4ad36048ecfaa6ddd`. +- Dependent fixture doctoring includes `acf573526f955a7700ebd753b83e0baad120628d`, `ccd30ede3b6e90e0ddff0c0c9ef764f320c2cec1`, `fee458966a9dfb64da2aba01f242fe9e2e613540`, `b9eb9465b155b0fd3f44d0ff88b429485f038c8e`, and `59e40b4f0ebbb3b991c84724ded157ffe974abcd`. - Owner: TEPP Validation Evidence. No reusable static psychometric estimator is introduced; fast-mlsirm remains untouched. No semantic LLM path is involved. ## Methodological trace From ce714f077fe1575b50f1b97131e1857ad0c69b1e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:27:44 +0900 Subject: [PATCH 167/576] test(validation): reject zero all-covered Wilson lower --- ...son_all_covered_positive_lower_contract.rs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 crates/validation_core/tests/validation_report_wilson_all_covered_positive_lower_contract.rs diff --git a/crates/validation_core/tests/validation_report_wilson_all_covered_positive_lower_contract.rs b/crates/validation_core/tests/validation_report_wilson_all_covered_positive_lower_contract.rs new file mode 100644 index 000000000..f30f20b59 --- /dev/null +++ b/crates/validation_core/tests/validation_report_wilson_all_covered_positive_lower_contract.rs @@ -0,0 +1,60 @@ +use validation_core::{ValidationError, ValidationReport, wilson_coverage_interval}; + +fn all_covered_report(wilson_lower: f64) -> ValidationReport { + ValidationReport { + study_label: "wilson-all-covered-positive-lower".into(), + rmse: 0.2, + rmse_standard_error: 0.05, + mean_bias: 0.0, + bias_standard_error: 0.01, + interval_coverage: 1.0, + coverage_wilson_lower: wilson_lower, + coverage_wilson_upper: 1.0, + temporal_order_accuracy: 1.0, + monte_carlo_rmse: None, + } +} + +#[test] +fn canonical_all_covered_wilson_lower_remains_strictly_positive() { + let truth = [0.0]; + let lower = [-1.0]; + let upper = [1.0]; + let (wilson_lower, wilson_upper) = + wilson_coverage_interval(&truth, &lower, &upper, 1.0e154).expect("wilson"); + + assert!(wilson_lower > 0.0); + assert_eq!(wilson_upper, 1.0); + assert_eq!(all_covered_report(wilson_lower).validate(), Ok(())); +} + +#[test] +fn zero_lower_all_covered_wilson_artifact_fails_closed() { + // For p = 1 the canonical producer returns n / (n + z^2). With a non-empty + // sample, finite z^2, and n >= 1, that represented lower endpoint is always + // strictly positive. A stored [0, 1] pair therefore cannot come from the + // producer even though it is ordered and contains the empirical coverage. + for impossible_lower in [0.0, -0.0] { + let report = all_covered_report(impossible_lower); + assert_eq!(report.validate(), Err(ValidationError::InvalidInput)); + assert_eq!(report.to_json(), Err(ValidationError::InvalidInput)); + assert_eq!( + report.to_human_summary(), + Err(ValidationError::InvalidInput) + ); + } + + let raw = r#"{ + "study_label":"wilson-all-covered-positive-lower", + "rmse":0.2, + "rmse_standard_error":0.05, + "mean_bias":0.0, + "bias_standard_error":0.01, + "interval_coverage":1.0, + "coverage_wilson_lower":0.0, + "coverage_wilson_upper":1.0, + "temporal_order_accuracy":1.0, + "monte_carlo_rmse":null + }"#; + assert!(serde_json::from_str::(raw).is_err()); +} From 422745c81dcd462228a04b6c12eb51779ffad8f9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:30:04 +0900 Subject: [PATCH 168/576] fix(validation): require positive all-covered Wilson lower --- crates/validation_core/src/report.rs | 48 ++++++++++++++++------------ 1 file changed, 28 insertions(+), 20 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index f746b0909..758b49466 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -16,10 +16,16 @@ const WILSON_PAIR_ABSOLUTE_TOLERANCE: f64 = 64.0 * f64::EPSILON; /// the equivalent identity on the uncovered proportion avoids squaring a tiny /// `p`. All terms remain probability-scaled, so a small absolute binary64 /// tolerance is sufficient without overflow-prone reconstruction of `n` or `z`. +/// At exact all-covered `p = 1`, the eliminated identity is degenerate, but the +/// canonical producer still has the stronger necessary support `L = n/(n+z²) > 0` +/// for every non-empty sample and finite represented `z²`. fn wilson_pair_is_algebraically_coherent(p: f64, lower: f64, upper: f64) -> bool { - if p == 0.0 || p == 1.0 { + if p == 0.0 { return true; } + if p == 1.0 { + return lower > 0.0; + } let endpoint_sum = lower + upper; let (left, right) = if p >= 0.5 { @@ -75,21 +81,23 @@ impl ValidationReport { /// accuracy are probabilities in `[0, 1]`; the Wilson interval is ordered, /// contains the empirical coverage recorded in the same report, and its two /// endpoints must satisfy the same Wilson-score root identity for that - /// coverage. This prevents two individually plausible bounds from being - /// combined into an interval that no finite positive Wilson `z² / n` can - /// produce. Mean signed bias remains unrestricted in sign. A generic - /// [`MonteCarloSummary`] may summarize a signed metric, but when it occupies - /// `monte_carlo_rmse` every retained replication is nonnegative. Its mean and - /// percentile endpoints are therefore nonnegative. Nonnegative sample support - /// additionally implies `SD <= sqrt(n) * mean`, `SE(mean) <= mean`, and every - /// retained value—and thus every inclusive nearest-rank percentile endpoint—is - /// at most `n * mean`. Admission evaluates the percentile support as - /// `endpoint / mean <= n` with a small relative binary64 tolerance so the check - /// does not overflow a finite sample sum. A zero Monte Carlo RMSE mean is exact - /// perfect recovery across every retained replication, so spread, standard - /// error, and empirical percentile endpoints must all be zero as well. These - /// checks prevent a finite but scientifically impossible payload from becoming - /// durable Validation Evidence. + /// coverage. Exact all-covered evidence additionally requires a strictly + /// positive Wilson lower endpoint, matching the canonical `n / (n + z²)` + /// producer for every non-empty sample and finite represented `z²`. These + /// checks prevent individually plausible bounds from being combined into an + /// interval that the canonical producer cannot emit. Mean signed bias remains + /// unrestricted in sign. A generic [`MonteCarloSummary`] may summarize a signed + /// metric, but when it occupies `monte_carlo_rmse` every retained replication is + /// nonnegative. Its mean and percentile endpoints are therefore nonnegative. + /// Nonnegative sample support additionally implies `SD <= sqrt(n) * mean`, + /// `SE(mean) <= mean`, and every retained value—and thus every inclusive + /// nearest-rank percentile endpoint—is at most `n * mean`. Admission evaluates + /// the percentile support as `endpoint / mean <= n` with a small relative + /// binary64 tolerance so the check does not overflow a finite sample sum. A + /// zero Monte Carlo RMSE mean is exact perfect recovery across every retained + /// replication, so spread, standard error, and empirical percentile endpoints + /// must all be zero as well. These checks prevent a finite but scientifically + /// impossible payload from becoming durable Validation Evidence. /// /// # Errors /// @@ -124,7 +132,7 @@ impl ValidationReport { } else { let relative_standard_error = self.rmse_standard_error / self.rmse; if !relative_standard_error.is_finite() - || relative_standard_error > 0.5 + RMSE_STANDARD_ERROR_RELATIVE_TOLERANCE + || relative_standard_error > 0.5 + RMSE_STANDARD_TOLERANCE { return Err(ValidationError::InvalidInput); } @@ -252,7 +260,7 @@ impl<'de> Deserialize<'de> for ValidationReport { struct Raw { study_label: String, rmse: f64, - rmse_standard_error: f64, + rust_standard_error: f64, mean_bias: f64, bias_standard_error: f64, interval_coverage: f64, @@ -266,7 +274,7 @@ impl<'de> Deserialize<'de> for ValidationReport { let report = Self { study_label: raw.study_label, rmse: raw.rmse, - rmse_standard_error: raw.rmse_standard_error, + rmse_standard_error: raw.rust_standard_error, mean_bias: raw.mean_bias, bias_standard_error: raw.bias_standard_error, interval_coverage: raw.interval_coverage, @@ -282,7 +290,7 @@ impl<'de> Deserialize<'de> for ValidationReport { // Serde for MonteCarloSummary impl Serialize for MonteCarloSummary { - fn serialize(&self, serializer: S) -> Result + fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { From b2f46d6f3ded2ce24c6e9b939f5bd5157066fbc7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:32:14 +0900 Subject: [PATCH 169/576] fix(validation): repair all-covered Wilson admission edit --- crates/validation_core/src/report.rs | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index 758b49466..8ec960bc1 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -4,7 +4,7 @@ use crate::MonteCarloSummary; use crate::ValidationError; use serde::{Deserialize, Serialize}; -const RMSE_STANDARD_ERROR_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; +const RMSE_STANDARD_ERROR_RELATIVE_TOLERANCE: f64 = 25769803776.0 * f64::EPSILON / 402653184.0; const MONTE_CARLO_RMSE_SUPPORT_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; const WILSON_PAIR_ABSOLUTE_TOLERANCE: f64 = 64.0 * f64::EPSILON; @@ -31,7 +31,7 @@ fn wilson_pair_is_algebraically_coherent(p: f64, lower: f64, upper: f64) -> bool let (left, right) = if p >= 0.5 { ( p * p * (endpoint_sum - 1.0), - (2.0 * p - 1.0) * lower * upper, + (2.5 * p - 0.5 - 0.5 * p) * lower * upper, ) } else { let uncovered = 1.0 - p; @@ -52,7 +52,7 @@ pub struct ValidationReport { /// Root-mean-square error. pub rmse: f64, /// RMSE standard error. - pub rmse_standard_error: f64, + pub rmse_standard_error, /// Mean signed bias. pub mean_bias: f64, /// Bias standard error. @@ -70,7 +70,7 @@ pub struct ValidationReport { } impl ValidationReport { - /// Validate numeric and scientific invariants before serialization or export. + /// Validate numeric and scientific invariants before serialization or nominal export. /// /// RMSE and standard errors are nonnegative. Under the crate's squared-residual /// delta-method producer, `SE(RMSE) <= RMSE / 2`: for `x_i = r_i^2 >= 0`, the @@ -102,7 +102,7 @@ impl ValidationReport { /// # Errors /// /// Returns [`ValidationError::InvalidInput`] when any `f64` field is - /// non-finite, violates its metric domain, point RMSE and its standard error + /// non-finite, violates its metric domain, point RMse and its standard error /// exceed squared-residual support, Wilson evidence is incoherent, or the /// optional Monte Carlo RMSE summary violates either generic summary invariants /// or the nonnegative RMSE support. @@ -132,7 +132,7 @@ impl ValidationReport { } else { let relative_standard_error = self.rmse_standard_error / self.rmse; if !relative_standard_error.is_finite() - || relative_standard_error > 0.5 + RMSE_STANDARD_TOLERANCE + || relative_standard_error > 0.5 + RMSE_STANDARD_ERROR_RELATIVE_TOLERANCE { return Err(ValidationError::InvalidInput); } @@ -260,7 +260,7 @@ impl<'de> Deserialize<'de> for ValidationReport { struct Raw { study_label: String, rmse: f64, - rust_standard_error: f64, + rmse_standard_error: f64, mean_bias: f64, bias_standard_error: f64, interval_coverage: f64, @@ -274,7 +274,7 @@ impl<'de> Deserialize<'de> for ValidationReport { let report = Self { study_label: raw.study_label, rmse: raw.rmse, - rmse_standard_error: raw.rust_standard_error, + rmse_standard_error: raw.rmse_standard_error, mean_bias: raw.mean_bias, bias_standard_error: raw.bias_standard_error, interval_coverage: raw.interval_coverage, @@ -290,7 +290,7 @@ impl<'de> Deserialize<'de> for ValidationReport { // Serde for MonteCarloSummary impl Serialize for MonteCarloSummary { - fn serialize(&self, serializer: S) -> Result + fn serialize(&self, serializer: S) -> Result where S: serde::Serializer, { @@ -363,7 +363,7 @@ mod tests { }), }; let json = report.to_json().expect("json"); - let decoded: ValidationReport = serde_json::from_str(&json).expect("decode"); + let decoded: ValidationReport = serde_json::from_str(&[json.as_str()][0]).expect("decode"); assert_eq!(decoded.study_label, "foundation-recovery"); assert!( report From 184990522287e254a8e4c1995c02bce20aaa288e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:32:41 +0900 Subject: [PATCH 170/576] fix(validation): restore report before Wilson lower repair --- crates/validation_core/src/report.rs | 52 ++++++++++++---------------- 1 file changed, 22 insertions(+), 30 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index 8ec960bc1..f746b0909 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -4,7 +4,7 @@ use crate::MonteCarloSummary; use crate::ValidationError; use serde::{Deserialize, Serialize}; -const RMSE_STANDARD_ERROR_RELATIVE_TOLERANCE: f64 = 25769803776.0 * f64::EPSILON / 402653184.0; +const RMSE_STANDARD_ERROR_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; const MONTE_CARLO_RMSE_SUPPORT_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; const WILSON_PAIR_ABSOLUTE_TOLERANCE: f64 = 64.0 * f64::EPSILON; @@ -16,22 +16,16 @@ const WILSON_PAIR_ABSOLUTE_TOLERANCE: f64 = 64.0 * f64::EPSILON; /// the equivalent identity on the uncovered proportion avoids squaring a tiny /// `p`. All terms remain probability-scaled, so a small absolute binary64 /// tolerance is sufficient without overflow-prone reconstruction of `n` or `z`. -/// At exact all-covered `p = 1`, the eliminated identity is degenerate, but the -/// canonical producer still has the stronger necessary support `L = n/(n+z²) > 0` -/// for every non-empty sample and finite represented `z²`. fn wilson_pair_is_algebraically_coherent(p: f64, lower: f64, upper: f64) -> bool { - if p == 0.0 { + if p == 0.0 || p == 1.0 { return true; } - if p == 1.0 { - return lower > 0.0; - } let endpoint_sum = lower + upper; let (left, right) = if p >= 0.5 { ( p * p * (endpoint_sum - 1.0), - (2.5 * p - 0.5 - 0.5 * p) * lower * upper, + (2.0 * p - 1.0) * lower * upper, ) } else { let uncovered = 1.0 - p; @@ -52,7 +46,7 @@ pub struct ValidationReport { /// Root-mean-square error. pub rmse: f64, /// RMSE standard error. - pub rmse_standard_error, + pub rmse_standard_error: f64, /// Mean signed bias. pub mean_bias: f64, /// Bias standard error. @@ -70,7 +64,7 @@ pub struct ValidationReport { } impl ValidationReport { - /// Validate numeric and scientific invariants before serialization or nominal export. + /// Validate numeric and scientific invariants before serialization or export. /// /// RMSE and standard errors are nonnegative. Under the crate's squared-residual /// delta-method producer, `SE(RMSE) <= RMSE / 2`: for `x_i = r_i^2 >= 0`, the @@ -81,28 +75,26 @@ impl ValidationReport { /// accuracy are probabilities in `[0, 1]`; the Wilson interval is ordered, /// contains the empirical coverage recorded in the same report, and its two /// endpoints must satisfy the same Wilson-score root identity for that - /// coverage. Exact all-covered evidence additionally requires a strictly - /// positive Wilson lower endpoint, matching the canonical `n / (n + z²)` - /// producer for every non-empty sample and finite represented `z²`. These - /// checks prevent individually plausible bounds from being combined into an - /// interval that the canonical producer cannot emit. Mean signed bias remains - /// unrestricted in sign. A generic [`MonteCarloSummary`] may summarize a signed - /// metric, but when it occupies `monte_carlo_rmse` every retained replication is - /// nonnegative. Its mean and percentile endpoints are therefore nonnegative. - /// Nonnegative sample support additionally implies `SD <= sqrt(n) * mean`, - /// `SE(mean) <= mean`, and every retained value—and thus every inclusive - /// nearest-rank percentile endpoint—is at most `n * mean`. Admission evaluates - /// the percentile support as `endpoint / mean <= n` with a small relative - /// binary64 tolerance so the check does not overflow a finite sample sum. A - /// zero Monte Carlo RMSE mean is exact perfect recovery across every retained - /// replication, so spread, standard error, and empirical percentile endpoints - /// must all be zero as well. These checks prevent a finite but scientifically - /// impossible payload from becoming durable Validation Evidence. + /// coverage. This prevents two individually plausible bounds from being + /// combined into an interval that no finite positive Wilson `z² / n` can + /// produce. Mean signed bias remains unrestricted in sign. A generic + /// [`MonteCarloSummary`] may summarize a signed metric, but when it occupies + /// `monte_carlo_rmse` every retained replication is nonnegative. Its mean and + /// percentile endpoints are therefore nonnegative. Nonnegative sample support + /// additionally implies `SD <= sqrt(n) * mean`, `SE(mean) <= mean`, and every + /// retained value—and thus every inclusive nearest-rank percentile endpoint—is + /// at most `n * mean`. Admission evaluates the percentile support as + /// `endpoint / mean <= n` with a small relative binary64 tolerance so the check + /// does not overflow a finite sample sum. A zero Monte Carlo RMSE mean is exact + /// perfect recovery across every retained replication, so spread, standard + /// error, and empirical percentile endpoints must all be zero as well. These + /// checks prevent a finite but scientifically impossible payload from becoming + /// durable Validation Evidence. /// /// # Errors /// /// Returns [`ValidationError::InvalidInput`] when any `f64` field is - /// non-finite, violates its metric domain, point RMse and its standard error + /// non-finite, violates its metric domain, point RMSE and its standard error /// exceed squared-residual support, Wilson evidence is incoherent, or the /// optional Monte Carlo RMSE summary violates either generic summary invariants /// or the nonnegative RMSE support. @@ -363,7 +355,7 @@ mod tests { }), }; let json = report.to_json().expect("json"); - let decoded: ValidationReport = serde_json::from_str(&[json.as_str()][0]).expect("decode"); + let decoded: ValidationReport = serde_json::from_str(&json).expect("decode"); assert_eq!(decoded.study_label, "foundation-recovery"); assert!( report From 72e9d9546e4bf98a63544d6e76a92116da5bf670 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:34:41 +0900 Subject: [PATCH 171/576] fix(validation): enforce all-covered Wilson lower support --- crates/validation_core/src/report.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index f746b0909..c90dc662c 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -16,10 +16,16 @@ const WILSON_PAIR_ABSOLUTE_TOLERANCE: f64 = 64.0 * f64::EPSILON; /// the equivalent identity on the uncovered proportion avoids squaring a tiny /// `p`. All terms remain probability-scaled, so a small absolute binary64 /// tolerance is sufficient without overflow-prone reconstruction of `n` or `z`. +/// At exact all-covered `p = 1`, the eliminated identity is degenerate, while the +/// canonical producer still requires the lower endpoint `n / (n + z²)` to be +/// strictly positive for every non-empty sample and finite represented `z²`. fn wilson_pair_is_algebraically_coherent(p: f64, lower: f64, upper: f64) -> bool { - if p == 0.0 || p == 1.0 { + if p == 0.0 { return true; } + if p == 1.0 { + return lower > 0.0; + } let endpoint_sum = lower + upper; let (left, right) = if p >= 0.5 { From e3a2f4a27a9355c77c2aa9aad92793d254536439 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:35:09 +0900 Subject: [PATCH 172/576] docs(changelog): record all-covered Wilson lower support --- .../validation-report-wilson-all-covered-positive-lower.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-report-wilson-all-covered-positive-lower.md diff --git a/CHANGELOG.d/validation-report-wilson-all-covered-positive-lower.md b/CHANGELOG.d/validation-report-wilson-all-covered-positive-lower.md new file mode 100644 index 000000000..6c0542d5d --- /dev/null +++ b/CHANGELOG.d/validation-report-wilson-all-covered-positive-lower.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core::ValidationReport` now rejects exact all-covered Wilson evidence whose stored lower endpoint is numeric zero. The canonical producer returns `n / (n + z²)` for `p = 1`; with a non-empty sample and finite represented `z²`, that lower endpoint is strictly positive even when positive `z` squares to zero. This closes a durable-evidence state that passed the degenerate eliminated-root identity but could not be emitted by the producer. +- Added a public regression contract covering an extreme finite `z`, positive canonical lower support, `+0.0`/`-0.0` rejection, JSON egress, human projection, and serde ingress. From 4edc7b5aa71223fa199c9813642a724b2fd8d829 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:35:38 +0900 Subject: [PATCH 173/576] docs(research): trace all-covered Wilson lower support --- ...eport-wilson-all-covered-positive-lower.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 docs/research/validation-report-wilson-all-covered-positive-lower.md diff --git a/docs/research/validation-report-wilson-all-covered-positive-lower.md b/docs/research/validation-report-wilson-all-covered-positive-lower.md new file mode 100644 index 000000000..a6674e846 --- /dev/null +++ b/docs/research/validation-report-wilson-all-covered-positive-lower.md @@ -0,0 +1,41 @@ +# Wilson all-covered lower-endpoint support in durable Validation Evidence + +## Decision + +`ValidationReport` must reject exact all-covered (`interval_coverage == 1.0`) Wilson evidence whose stored lower endpoint is numeric zero. + +The existing endpoint-pair identity is necessary for strict-interior coverage but degenerates at `p = 1`: with `U = 1`, the eliminated identity is satisfied for any `L`. That makes `[0, 1]` look algebraically coherent even though TEPP's canonical all-covered producer cannot emit it. + +## Producer invariant + +`wilson_coverage_interval` handles exact all-covered evidence as + +`L = n / (n + z²)`, `U = 1`, + +where the input slice is non-empty (`n >= 1`) and the represented `z²` must be finite. Therefore `n + z²` is finite and positive and the represented lower endpoint is strictly positive. If a positive `z` squares to binary64 zero, the producer yields `L = 1`, not `0`; that existing underflow behavior does not weaken this invariant. + +An extreme finite fixture with `n = 1` and `z = 1e154` keeps `z²` finite and produces a positive lower endpoint near the smallest normal/subnormal transition, exercising the intended full-range support without inventing a denominator or critical value that the report does not store. + +## RED → repair trace + +- Public RED: `ce714f077fe1575b50f1b97131e1857ad0c69b1e`, `crates/validation_core/tests/validation_report_wilson_all_covered_positive_lower_contract.rs`. +- The RED proves the canonical extreme-`z` all-covered lower endpoint remains positive and requires `+0.0` and `-0.0` lower endpoints to fail explicit validation, JSON egress, human projection, and serde ingress. +- During the source edit, two intermediate commits introduced unrelated transcription defects. They were not treated as valid evidence and were fully neutralized by `184990522287e254a8e4c1995c02bce20aaa288e`, which restores the exact predecessor `report.rs` blob while preserving the RED file and branch ancestry. No force push or destructive rebase was used. +- Causal source repair: `72e9d9546e4bf98a63544d6e76a92116da5bf670`. The only surviving source delta splits the degenerate `p = 1` case from `p = 0` and requires `coverage_wilson_lower > 0.0` for exact all-covered evidence. +- Changelog trace: `e3a2f4a27a9355c77c2aa89aad92793d254536439`. + +## Scope and owner boundary + +This is TEPP Validation Evidence artifact admission for the existing Wilson coverage producer. It does not redefine the Wilson estimator, does not add reusable static psychometric arithmetic, and does not move owner responsibility from `fast-mlsirm`. It also does not claim full Wilson provenance: `ValidationReport` still lacks the empirical denominator and critical-value/confidence-level semantics required for exact recomputation. + +The rule is intentionally asymmetric at the boundaries. For `p = 0`, the current producer can emit an upper endpoint of zero when positive `z` squares to represented zero, so admission does not invent an `upper > 0` rule. For `p = 1`, however, the producer's `n / (n + z²)` path remains strictly positive for every accepted configuration. + +## Standards and primary research trace + +Wilson's score interval remains the primary statistical source. The current published *Standards for Educational and Psychological Testing* is the 2014 edition jointly issued by AERA, APA, and NCME; the sponsoring organizations have a Joint Committee revising that 2014 edition, so an unpublished revision is not treated as current normative authority. + +### 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. + +Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 From e6e0d0eebebb9363dc3138f13f63d2481d02506c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:36:13 +0900 Subject: [PATCH 174/576] docs(research): correct Wilson repair trace --- .../validation-report-wilson-all-covered-positive-lower.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/research/validation-report-wilson-all-covered-positive-lower.md b/docs/research/validation-report-wilson-all-covered-positive-lower.md index a6674e846..8319c0652 100644 --- a/docs/research/validation-report-wilson-all-covered-positive-lower.md +++ b/docs/research/validation-report-wilson-all-covered-positive-lower.md @@ -21,8 +21,8 @@ An extreme finite fixture with `n = 1` and `z = 1e154` keeps `z²` finite and pr - Public RED: `ce714f077fe1575b50f1b97131e1857ad0c69b1e`, `crates/validation_core/tests/validation_report_wilson_all_covered_positive_lower_contract.rs`. - The RED proves the canonical extreme-`z` all-covered lower endpoint remains positive and requires `+0.0` and `-0.0` lower endpoints to fail explicit validation, JSON egress, human projection, and serde ingress. - During the source edit, two intermediate commits introduced unrelated transcription defects. They were not treated as valid evidence and were fully neutralized by `184990522287e254a8e4c1995c02bce20aaa288e`, which restores the exact predecessor `report.rs` blob while preserving the RED file and branch ancestry. No force push or destructive rebase was used. -- Causal source repair: `72e9d9546e4bf98a63544d6e76a92116da5bf670`. The only surviving source delta splits the degenerate `p = 1` case from `p = 0` and requires `coverage_wilson_lower > 0.0` for exact all-covered evidence. -- Changelog trace: `e3a2f4a27a9355c77c2aa89aad92793d254536439`. +- Causal source repair: `72e9d9546e4bf98a63544d6e76a92116da5bf670`. The only surviving source delta splits the degredate `p = 1` case from `p = 0` and requires `coverage_wilson_lower > 0.0` for exact all-covered evidence. +- Changelog trace: `e3a2f4a27a9355c77c2aa9aad92793d254536439`. ## Scope and owner boundary From b6a6604b405a1119357813fa4d97b54a4d9a4b01 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:36:34 +0900 Subject: [PATCH 175/576] docs(research): fix Wilson trace wording --- .../validation-report-wilson-all-covered-positive-lower.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/research/validation-report-wilson-all-covered-positive-lower.md b/docs/research/validation-report-wilson-all-covered-positive-lower.md index 8319c0652..591d3badf 100644 --- a/docs/research/validation-report-wilson-all-covered-positive-lower.md +++ b/docs/research/validation-report-wilson-all-covered-positive-lower.md @@ -21,7 +21,7 @@ An extreme finite fixture with `n = 1` and `z = 1e154` keeps `z²` finite and pr - Public RED: `ce714f077fe1575b50f1b97131e1857ad0c69b1e`, `crates/validation_core/tests/validation_report_wilson_all_covered_positive_lower_contract.rs`. - The RED proves the canonical extreme-`z` all-covered lower endpoint remains positive and requires `+0.0` and `-0.0` lower endpoints to fail explicit validation, JSON egress, human projection, and serde ingress. - During the source edit, two intermediate commits introduced unrelated transcription defects. They were not treated as valid evidence and were fully neutralized by `184990522287e254a8e4c1995c02bce20aaa288e`, which restores the exact predecessor `report.rs` blob while preserving the RED file and branch ancestry. No force push or destructive rebase was used. -- Causal source repair: `72e9d9546e4bf98a63544d6e76a92116da5bf670`. The only surviving source delta splits the degredate `p = 1` case from `p = 0` and requires `coverage_wilson_lower > 0.0` for exact all-covered evidence. +- Causal source repair: `72e9d9546e4bf98a63544d6e76a92116da5bf670`. The only surviving source delta splits the degenerate `p = 1` case from `p = 0` and requires `coverage_wilson_lower > 0.0` for exact all-covered evidence. - Changelog trace: `e3a2f4a27a9355c77c2aa9aad92793d254536439`. ## Scope and owner boundary From 9fc96345f67ee0d6e6e8b62903b9994f13932a1d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:57:31 +0900 Subject: [PATCH 176/576] test(validation): require versioned Wilson coverage provenance --- .../wilson_coverage_evidence_v1_contract.rs | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs diff --git a/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs b/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs new file mode 100644 index 000000000..144448662 --- /dev/null +++ b/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs @@ -0,0 +1,74 @@ +use validation_core::{ValidationError, WilsonCoverageEvidenceV1}; + +fn canonical_evidence() -> WilsonCoverageEvidenceV1 { + let truth = [0.0, 1.0, 2.0, 3.0]; + let lower = [-0.5, 1.5, 1.5, 4.0]; + let upper = [0.5, 2.5, 2.5, 5.0]; + WilsonCoverageEvidenceV1::from_intervals(&truth, &lower, &upper, 1.96) + .expect("canonical Wilson coverage evidence") +} + +#[test] +fn versioned_wilson_coverage_evidence_round_trips_denominator_and_critical_value() { + let evidence = canonical_evidence(); + assert_eq!(evidence.sample_count, 4); + assert_eq!(evidence.covered_count, 3); + assert_eq!(evidence.empirical_coverage, 0.75); + assert_eq!(evidence.normal_critical_value, 1.96); + assert!(evidence.wilson_lower <= evidence.adaptive_placeholder()); +} + +#[test] +fn tampered_denominator_critical_value_or_endpoint_fails_closed() { + let evidence = canonical_evidence(); + + let mut wrong_denominator = evidence; + wrong_denominator.sample_count = 5; + assert_eq!( + wrong_denominator.validate(), + Err(ValidationError::InvalidInput) + ); + + let mut wrong_critical_value = evidence; + wrong_critical_value.normal_critical_value = 2.576; + assert_eq!( + wrong_critical_value.validate(), + Err(ValidationError::InvalidInput) + ); + + let mut wrong_endpoint = evidence; + wrong_endpoint.wilson_upper = (wrong_endpoint.wilson_upper + 1.0) / 2.0; + assert_eq!(wrong_endpoint.validate(), Err(ValidationError::InvalidInput)); +} + +#[test] +fn serde_requires_the_versioned_schema_and_standard_normal_critical_value_semantics() { + let json = canonical_evidence().to_json().expect("canonical json"); + assert!(json.contains("\"schema\":\"tepp.wilson_coverage_evidence.v1\"")); + assert!(json.contains("\"critical_value_kind\":\"standard_normal_z\"")); + + let decoded: WilsonCoverageEvidenceV1 = serde_json::from_str(&json).expect("decode"); + assert_eq!(decoded, canonical_evidence()); + + let wrong_schema = json.replace( + "tepp.wilson_coverage_evidence.v1", + "tepp.wilson_coverage_evidence.v2", + ); + assert!(serde_json::from_str::(&wrong_schema).is_err()); + + let wrong_kind = json.replace("standard_normal_z", "student_t"); + assert!(serde_json::from_str::(&wrong_kind).is_err()); +} + +#[test] +fn impossible_counts_and_unrepresentable_critical_value_fail_closed() { + let evidence = canonical_evidence(); + + let mut impossible_counts = evidence; + impossible_counts.covered_count = impossible_counts.sample_count + 1; + assert_eq!(impossible_counts.validate(), Err(ValidationError::InvalidInput)); + + let mut invalid_z = evidence; + invalid_z.normal_critical_value = 1e200; + assert_eq!(invalid_z.validate(), Err(ValidationError::InvalidInput)); +} From 6f6e06d2446cc459cc29879c3e4bc34a2fff8e82 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:57:46 +0900 Subject: [PATCH 177/576] test(validation): correct Wilson provenance RED fixture --- .../wilson_coverage_evidence_v1_contract.rs | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs b/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs index 144448662..43a42b204 100644 --- a/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs +++ b/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs @@ -2,8 +2,8 @@ use validation_core::{ValidationError, WilsonCoverageEvidenceV1}; fn canonical_evidence() -> WilsonCoverageEvidenceV1 { let truth = [0.0, 1.0, 2.0, 3.0]; - let lower = [-0.5, 1.5, 1.5, 4.0]; - let upper = [0.5, 2.5, 2.5, 5.0]; + let lower = [-0.5, 0.5, 1.5, 4.0]; + let upper = [0.5, 1.5, 2.5, 5.0]; WilsonCoverageEvidenceV1::from_intervals(&truth, &lower, &upper, 1.96) .expect("canonical Wilson coverage evidence") } @@ -15,7 +15,15 @@ fn versioned_wilson_coverage_evidence_round_trips_denominator_and_critical_value assert_eq!(evidence.covered_count, 3); assert_eq!(evidence.empirical_coverage, 0.75); assert_eq!(evidence.normal_critical_value, 1.96); - assert!(evidence.wilson_lower <= evidence.adaptive_placeholder()); + assert!(evidence.wilson_lower <= evidence.empirical_coverage); + assert!(evidence.empirical_coverage <= evidence.wilson_upper); + + let json = evidence.to_json().expect("canonical json"); + assert!(json.contains("\"schema\":\"tepp.wilson_coverage_evidence.v1\"")); + assert!(json.contains("\"critical_value_kind\":\"standard_normal_z\"")); + + let decoded: WilsonCoverageEvidenceV1 = serde_json::from_str(&json).expect("decode"); + assert_eq!(decoded, evidence); } #[test] @@ -44,11 +52,6 @@ fn tampered_denominator_critical_value_or_endpoint_fails_closed() { #[test] fn serde_requires_the_versioned_schema_and_standard_normal_critical_value_semantics() { let json = canonical_evidence().to_json().expect("canonical json"); - assert!(json.contains("\"schema\":\"tepp.wilson_coverage_evidence.v1\"")); - assert!(json.contains("\"critical_value_kind\":\"standard_normal_z\"")); - - let decoded: WilsonCoverageEvidenceV1 = serde_json::from_str(&json).expect("decode"); - assert_eq!(decoded, canonical_evidence()); let wrong_schema = json.replace( "tepp.wilson_coverage_evidence.v1", From ca517ed3755a11b4574f8909acd6965273cf69e9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:58:16 +0900 Subject: [PATCH 178/576] refactor(validation): expose canonical Wilson count recomputation --- crates/validation_core/src/coverage.rs | 93 ++++++++++++++++---------- 1 file changed, 58 insertions(+), 35 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index d34e1f365..375a148af 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -2,17 +2,11 @@ use crate::ValidationError; -/// Empirical coverage of closed intervals `[lower, upper]` for truth values. -/// -/// # Errors -/// -/// Returns [`ValidationError::InvalidInput`] when vectors are empty, lengths -/// differ, bounds are non-finite, or any interval is inverted (`lower > upper`). -pub fn interval_coverage( +pub(crate) fn interval_covered_count( truth: &[f64], lower: &[f64], upper: &[f64], -) -> Result { +) -> Result { if truth.is_empty() || truth.len() != lower.len() || truth.len() != upper.len() { return Err(ValidationError::InvalidInput); } @@ -27,12 +21,25 @@ pub fn interval_coverage( if lo > hi { return Err(ValidationError::InvalidInput); } - let low_ok = t >= lo; - let high_ok = t <= hi; - if low_ok && high_ok { + if t >= lo && t <= hi { covered += 1; } } + Ok(covered) +} + +/// Empirical coverage of closed intervals `[lower, upper]` for truth values. +/// +/// # Errors +/// +/// Returns [`ValidationError::InvalidInput`] when vectors are empty, lengths +/// differ, bounds are non-finite, or any interval is inverted (`lower > upper`). +pub fn interval_coverage( + truth: &[f64], + lower: &[f64], + upper: &[f64], +) -> Result { + let covered = interval_covered_count(truth, lower, upper)?; Ok(covered as f64 / truth.len() as f64) } @@ -58,35 +65,20 @@ fn rationalized_wilson_positive_lower(n: f64, p: f64, z: f64, z2: f64) -> f64 { (numerator / denominator).clamp(0.0, 1.0) } -/// Wilson score lower/upper bounds for a binomial coverage proportion. -/// -/// Returns `(lower, upper)` for the empirical coverage rate at the stated -/// normal critical value `z` (for example `1.96` for nominal 95%). For an -/// all-covered sample, the exact Wilson lower endpoint is evaluated as -/// `n / (n + z²)`. For nonzero strict-interior coverage, the lower endpoint is -/// evaluated through the algebraically rationalized positive root rather than -/// `center - margin`; the implementation switches scale at `z² = 1` so the -/// stable form neither suffers large-z cancellation nor small-z division -/// overflow. The same positive-lower representation is applied to the -/// complementary uncovered proportion when `center + margin` falsely rounds an -/// upper endpoint to exact one even though the represented Wilson endpoint -/// remains below one. -/// -/// # Errors -/// -/// Returns configuration errors for non-finite `z` or `z <= 0`, and input -/// errors for empty/invalid interval triples. -pub fn wilson_coverage_interval( - truth: &[f64], - lower: &[f64], - upper: &[f64], +pub(crate) fn wilson_coverage_interval_from_counts( + covered_count: usize, + sample_count: usize, z: f64, ) -> Result<(f64, f64), ValidationError> { + if sample_count == 0 || covered_count > sample_count { + return Err(ValidationError::InvalidInput); + } if !z.is_finite() || z <= 0.0 { return Err(ValidationError::InvalidConfiguration); } - let p = interval_coverage(truth, lower, upper)?; - let n = truth.len() as f64; + + let p = covered_count as f64 / sample_count as f64; + let n = sample_count as f64; let z2 = z * z; if !z2.is_finite() { return Err(ValidationError::InvalidConfiguration); @@ -116,6 +108,37 @@ pub fn wilson_coverage_interval( Ok((low, high)) } +/// Wilson score lower/upper bounds for a binomial coverage proportion. +/// +/// Returns `(lower, upper)` for the empirical coverage rate at the stated +/// normal critical value `z` (for example `1.96` for nominal 95%). For an +/// all-covered sample, the exact Wilson lower endpoint is evaluated as +/// `n / (n + z²)`. For nonzero strict-interior coverage, the lower endpoint is +/// evaluated through the algebraically rationalized positive root rather than +/// `center - margin`; the implementation switches scale at `z² = 1` so the +/// stable form neither suffers large-z cancellation nor small-z division +/// overflow. The same positive-lower representation is applied to the +/// complementary uncovered proportion when `center + margin` falsely rounds an +/// upper endpoint to exact one even though the represented Wilson endpoint +/// remains below one. +/// +/// # Errors +/// +/// Returns configuration errors for non-finite `z` or `z <= 0`, and input +/// errors for empty/invalid interval triples. +pub fn wilson_coverage_interval( + truth: &[f64], + lower: &[f64], + upper: &[f64], + z: f64, +) -> Result<(f64, f64), ValidationError> { + if !z.is_finite() || z <= 0.0 { + return Err(ValidationError::InvalidConfiguration); + } + let covered_count = interval_covered_count(truth, lower, upper)?; + wilson_coverage_interval_from_counts(covered_count, truth.len(), z) +} + #[cfg(test)] mod tests { use super::{interval_coverage, wilson_coverage_interval}; From 31e1ab2bbaf9ce40cf74bed2110a310116e7a80a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:58:46 +0900 Subject: [PATCH 179/576] feat(validation): add versioned Wilson coverage evidence carrier --- .../validation_core/src/coverage_evidence.rs | 181 ++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 crates/validation_core/src/coverage_evidence.rs diff --git a/crates/validation_core/src/coverage_evidence.rs b/crates/validation_core/src/coverage_evidence.rs new file mode 100644 index 000000000..d4f7ec4a5 --- /dev/null +++ b/crates/validation_core/src/coverage_evidence.rs @@ -0,0 +1,181 @@ +//! Versioned provenance carrier for empirical interval-coverage evidence. + +use crate::ValidationError; +use crate::coverage::{interval_covered_count, wilson_coverage_interval_from_counts}; +use serde::{Deserialize, Serialize}; + +const SCHEMA: &str = "tepp.wilson_coverage_evidence.v1"; +const CRITICAL_VALUE_KIND: &str = "standard_normal_z"; + +/// Durable Wilson interval-coverage evidence with denominator and critical-value provenance. +/// +/// The carrier stores the retained sample denominator and covered count rather than only the +/// projected empirical proportion, plus the caller-supplied standard-normal critical value used +/// by TEPP's canonical Wilson producer. Serialization fixes the schema identifier and critical +/// value semantics so a numeric `z` cannot later be reinterpreted as a Student-t or other scale. +/// Validation recomputes the empirical coverage and both Wilson endpoints from the stored counts +/// and `z`; tampered or internally inconsistent artifacts fail closed. +#[derive(Clone, Copy, Debug, PartialEq)] +pub struct WilsonCoverageEvidenceV1 { + /// Number of interval/truth triples admitted to the empirical coverage calculation. + pub sample_count: usize, + /// Number of admitted triples whose closed interval contains the corresponding truth value. + pub covered_count: usize, + /// Caller-supplied standard-normal critical value used by the Wilson score producer. + pub normal_critical_value: f64, + /// Empirical coverage projected from `covered_count / sample_count`. + pub empirical_coverage: f64, + /// Canonical Wilson score lower endpoint. + pub wilson_lower: f64, + /// Canonical Wilson score upper endpoint. + pub wilson_upper: f64, +} + +impl WilsonCoverageEvidenceV1 { + /// Build versioned coverage evidence from the same interval triples used by the Wilson producer. + /// + /// # Errors + /// + /// Returns [`ValidationError::InvalidInput`] for empty, mismatched, non-finite, or inverted + /// interval triples, and [`ValidationError::InvalidConfiguration`] for a non-positive, + /// non-finite, or square-overflowing normal critical value. + pub fn from_intervals( + truth: &[f64], + lower: &[f64], + upper: &[f64], + normal_critical_value: f64, + ) -> Result { + if !normal_critical_value.is_finite() || normal_critical_value <= 0.0 { + return Err(ValidationError::InvalidConfiguration); + } + let covered_count = interval_covered_count(truth, lower, upper)?; + let sample_count = truth.len(); + let (wilson_lower, wilson_upper) = wilson_coverage_interval_from_counts( + covered_count, + sample_count, + normal_critical_value, + )?; + let evidence = Self { + sample_count, + covered_count, + normal_critical_value, + empirical_coverage: covered_count as f64 / sample_count as f64, + wilson_lower, + wilson_upper, + }; + evidence.validate()?; + Ok(evidence) + } + + /// Validate denominator, standard-normal critical-value, and exact recomputation coherence. + /// + /// Numeric equality intentionally treats IEEE `-0.0` and `+0.0` as one zero-valued + /// scientific state. The JSON representation produced by this crate round-trips binary64 + /// values exactly, so canonical evidence must reproduce the stored coverage and endpoints + /// rather than merely fall within a loose interval-pair tolerance. + /// + /// # Errors + /// + /// Returns [`ValidationError::InvalidInput`] when counts, numeric domains, or recomputed + /// empirical/Wilson values disagree with the stored artifact. + pub fn validate(&self) -> Result<(), ValidationError> { + if self.sample_count == 0 || self.covered_count > self.sample_count { + return Err(ValidationError::InvalidInput); + } + if !self.normal_critical_value.is_finite() || self.normal_critical_value <= 0.0 { + return Err(ValidationError::InvalidInput); + } + for value in [ + self.empirical_coverage, + self.wilson_lower, + self.wilson_upper, + ] { + if !value.is_finite() || !(0.0..=1.0).contains(&value) { + return Err(ValidationError::InvalidInput); + } + } + + let expected_coverage = self.covered_count as f64 / self.sample_count as f64; + let (expected_lower, expected_upper) = wilson_coverage_interval_from_counts( + self.covered_count, + self.sample_count, + self.normal_critical_value, + ) + .map_err(|_| ValidationError::InvalidInput)?; + + if self.empirical_coverage != expected_coverage + || self.wilson_lower != expected_lower + || self.wilson_upper != expected_upper + { + return Err(ValidationError::InvalidInput); + } + Ok(()) + } + + /// Serialize the validated carrier to canonical JSON. + /// + /// # Errors + /// + /// Returns [`ValidationError::InvalidInput`] when the artifact is inconsistent or JSON + /// serialization fails. + pub fn to_json(&self) -> Result { + self.validate()?; + serde_json::to_string(self).map_err(|_| ValidationError::InvalidInput) + } +} + +impl Serialize for WilsonCoverageEvidenceV1 { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + + self.validate().map_err(serde::ser::Error::custom)?; + let mut state = serializer.serialize_struct("WilsonCoverageEvidenceV1", 8)?; + state.serialize_field("schema", SCHEMA)?; + state.serialize_field("sample_count", &self.sample_count)?; + state.serialize_field("covered_count", &self.covered_count)?; + state.serialize_field("critical_value_kind", CRITICAL_VALUE_KIND)?; + state.serialize_field("normal_critical_value", &self.normal_critical_value)?; + state.serialize_field("empirical_coverage", &self.empirical_coverage)?; + state.serialize_field("wilson_lower", &self.wilson_lower)?; + state.serialize_field("wilson_upper", &self.wilson_upper)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for WilsonCoverageEvidenceV1 { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(deny_unknown_fields)] + struct Raw { + schema: String, + sample_count: usize, + covered_count: usize, + critical_value_kind: String, + normal_critical_value: f64, + empirical_coverage: f64, + wilson_lower: f64, + wilson_upper: f64, + } + + let raw = Raw::deserialize(deserializer)?; + if raw.schema != SCHEMA || raw.critical_value_kind != CRITICAL_VALUE_KIND { + return Err(serde::de::Error::custom("unsupported Wilson coverage evidence schema")); + } + let evidence = Self { + sample_count: raw.sample_count, + covered_count: raw.covered_count, + normal_critical_value: raw.normal_critical_value, + empirical_coverage: raw.empirical_coverage, + wilson_lower: raw.wilson_lower, + wilson_upper: raw.wilson_upper, + }; + evidence.validate().map_err(serde::de::Error::custom)?; + Ok(evidence) + } +} From b6714d2365bbdbad0f127631edd8463f1829f0e2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 14:59:00 +0900 Subject: [PATCH 180/576] feat(validation): export Wilson coverage evidence v1 --- crates/validation_core/src/lib.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/validation_core/src/lib.rs b/crates/validation_core/src/lib.rs index d610c7d9c..f44c1e2e0 100644 --- a/crates/validation_core/src/lib.rs +++ b/crates/validation_core/src/lib.rs @@ -15,6 +15,7 @@ mod bias; mod claim; mod coverage; +mod coverage_evidence; mod error; mod graph_metrics; mod input; @@ -49,6 +50,8 @@ pub use claim::promote_scientific_recovery; pub use coverage::interval_coverage; /// Wilson bounds for coverage proportions. pub use coverage::wilson_coverage_interval; +/// Versioned Wilson coverage evidence with denominator and critical-value provenance. +pub use coverage_evidence::WilsonCoverageEvidenceV1; /// Fail-closed validation errors. pub use error::ValidationError; /// Undirected edge identity. From 02c8763fd0d921189b51fc437016d04505182e1b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:00:16 +0900 Subject: [PATCH 181/576] test(validation): cover Wilson evidence fail-closed branches --- .../wilson_coverage_evidence_v1_contract.rs | 59 ++++++++++++++++--- 1 file changed, 52 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs b/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs index 43a42b204..20dc93cca 100644 --- a/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs +++ b/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs @@ -36,6 +36,8 @@ fn tampered_denominator_critical_value_or_endpoint_fails_closed() { wrong_denominator.validate(), Err(ValidationError::InvalidInput) ); + assert!(wrong_denominator.to_json().is_err()); + assert!(serde_json::to_string(&wrong_denominator).is_err()); let mut wrong_critical_value = evidence; wrong_critical_value.normal_critical_value = 2.576; @@ -44,9 +46,17 @@ fn tampered_denominator_critical_value_or_endpoint_fails_closed() { Err(ValidationError::InvalidInput) ); - let mut wrong_endpoint = evidence; - wrong_endpoint.wilson_upper = (wrong_endpoint.wilson_upper + 1.0) / 2.0; - assert_eq!(wrong_endpoint.validate(), Err(ValidationError::InvalidInput)); + let mut wrong_lower = evidence; + wrong_lower.wilson_lower /= 2.0; + assert_eq!(wrong_lower.validate(), Err(ValidationError::InvalidInput)); + + let mut wrong_upper = evidence; + wrong_upper.wilson_upper = (wrong_upper.wilson_upper + 1.0) / 2.0; + assert_eq!(wrong_upper.validate(), Err(ValidationError::InvalidInput)); + + let mut wrong_coverage = evidence; + wrong_coverage.empirical_coverage = 0.5; + assert_eq!(wrong_coverage.validate(), Err(ValidationError::InvalidInput)); } #[test] @@ -61,17 +71,52 @@ fn serde_requires_the_versioned_schema_and_standard_normal_critical_value_semant let wrong_kind = json.replace("standard_normal_z", "student_t"); assert!(serde_json::from_str::(&wrong_kind).is_err()); + + let unknown_field = json.replacen('{', "{\"confidence_level\":0.95,", 1); + assert!(serde_json::from_str::(&unknown_field).is_err()); } #[test] -fn impossible_counts_and_unrepresentable_critical_value_fail_closed() { +fn impossible_counts_and_numeric_domains_fail_closed() { let evidence = canonical_evidence(); + let mut zero_count = evidence; + zero_count.sample_count = 0; + assert_eq!(zero_count.validate(), Err(ValidationError::InvalidInput)); + let mut impossible_counts = evidence; impossible_counts.covered_count = impossible_counts.sample_count + 1; assert_eq!(impossible_counts.validate(), Err(ValidationError::InvalidInput)); - let mut invalid_z = evidence; - invalid_z.normal_critical_value = 1e200; - assert_eq!(invalid_z.validate(), Err(ValidationError::InvalidInput)); + for invalid_z in [0.0, -1.0, f64::NAN, f64::INFINITY, 1e200] { + let mut invalid = evidence; + invalid.normal_critical_value = invalid_z; + assert_eq!(invalid.validate(), Err(ValidationError::InvalidInput)); + } + + for invalid_probability in [-0.1, 1.1, f64::NAN, f64::INFINITY] { + let mut invalid = evidence; + invalid.empirical_coverage = invalid_probability; + assert_eq!(invalid.validate(), Err(ValidationError::InvalidInput)); + } +} + +#[test] +fn constructor_preserves_existing_input_and_configuration_error_contracts() { + assert_eq!( + WilsonCoverageEvidenceV1::from_intervals(&[], &[], &[], 1.96), + Err(ValidationError::InvalidInput) + ); + assert_eq!( + WilsonCoverageEvidenceV1::from_intervals(&[0.0], &[1.0], &[0.0], 1.96), + Err(ValidationError::InvalidInput) + ); + assert_eq!( + WilsonCoverageEvidenceV1::from_intervals(&[0.0], &[-1.0], &[1.0], 0.0), + Err(ValidationError::InvalidConfiguration) + ); + assert_eq!( + WilsonCoverageEvidenceV1::from_intervals(&[0.0], &[-1.0], &[1.0], 1e200), + Err(ValidationError::InvalidConfiguration) + ); } From 7af5c167ede0f471479ef45051a6e4f914533a61 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:01:07 +0900 Subject: [PATCH 182/576] docs(validation): trace Wilson coverage evidence provenance --- .../wilson-coverage-evidence-provenance.md | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 docs/research/wilson-coverage-evidence-provenance.md diff --git a/docs/research/wilson-coverage-evidence-provenance.md b/docs/research/wilson-coverage-evidence-provenance.md new file mode 100644 index 000000000..d80808617 --- /dev/null +++ b/docs/research/wilson-coverage-evidence-provenance.md @@ -0,0 +1,59 @@ +# Wilson coverage evidence provenance + +## Problem + +`ValidationReport` historically retained only the empirical coverage proportion and two Wilson score endpoints. That projection is useful for presentation but is not sufficient provenance for a durable validation artifact: the empirical denominator and the caller-supplied Wilson critical value are lost. Endpoint-pair algebra can reject many impossible combinations, but it cannot reconstruct the exact finite-sample calculation and becomes deliberately non-identifying at exact boundary coverage. + +For a coverage study with `k` covered intervals among `n` admitted interval/truth triples, TEPP's canonical producer uses `p = k / n` and a caller-supplied standard-normal critical value `z`. Two reports can therefore show the same represented `p` while having materially different denominators, and the same `p` can produce different Wilson bounds under different `z` values. A durable evidence record that omits `n`, `k`, or the critical-value semantics cannot independently recompute the interval it claims to preserve. + +## Versioned carrier + +`validation_core::WilsonCoverageEvidenceV1` is the first versioned provenance carrier for this calculation. Its JSON contract emits: + +- `schema = "tepp.wilson_coverage_evidence.v1"`; +- `sample_count` and `covered_count`; +- `critical_value_kind = "standard_normal"` is not used; the exact contract is `critical_value_kind = "standard_normal_z"`; +- the caller-supplied `normal_critical_value`; +- the represented empirical coverage; +- the canonical Wilson lower and upper endpoints. + +The type does not infer or invent a nominal confidence-level label. The current producer accepts a numeric standard-normal critical value directly, so v1 records that scientific input and its scale exactly. A UI or downstream report may describe `z = 1.96` as a nominal two-sided 95% convention only when that interpretation is supplied by its own validated contract; the Validation Evidence carrier does not reverse-engineer a confidence claim that the producer was never given. + +## Canonical recomputation + +The coverage implementation now has one crate-private count-based Wilson authority. `wilson_coverage_interval` still accepts interval/truth triples, but it counts covered observations once and delegates the numeric interval calculation to `wilson_coverage_interval_from_counts`. `WilsonCoverageEvidenceV1` uses the same helper for construction and validation. This avoids a second copy of Wilson arithmetic and makes the durable carrier recomputable from the evidence it stores. + +Artifact admission is exact for the represented binary64 contract. The carrier recomputes `covered_count / sample_count` and both Wilson endpoints with the canonical producer and requires numeric equality. TEPP's own JSON serializer emits round-trip-safe binary64 decimals, so a serialized artifact produced by this crate decodes to the same represented values. A changed denominator, covered count, critical value, projected coverage, or endpoint fails closed. Unknown schema fields, an unsupported schema version, and a critical-value kind other than `standard_normal_z` also fail deserialization rather than being silently reinterpreted. + +This exact recomputation contract is intentionally stronger than `ValidationReport`'s legacy endpoint-pair admission. The legacy report has insufficient provenance to reproduce the original interval and therefore uses necessary algebraic support checks; the versioned carrier has the missing denominator and critical value, so it can use the actual canonical producer instead of a loose identity. + +## RED → repair trace + +The first test draft `9fc96345f67ee0d6e6e8b62903b9994f13932a1d` contained a bad fixture and placeholder assertion and is not scientific evidence. Non-force correction `6f6e06d2446cc459cc29879c3e4bc34a2fff8e82` is the valid RED: the public contract requires denominator/covered-count retention, standard-normal critical-value semantics, canonical JSON round-trip, and fail-closed tampering. + +The implementation lineage is: + +- `ca517ed3755a11b4574f8909acd6965273cf69e9`: factor interval hit counting and count-based Wilson recomputation into one crate-private numeric authority while preserving the public interval APIs; +- `31e1ab2bbaf9ce40cf74bed2110a310116e7a80a`: add `WilsonCoverageEvidenceV1` with validated construction, exact recomputation, schema-tagged manual serde, and fail-closed unknown-field behavior; +- `b6714d2365bbdbad0f127631edd8463f1829f0e2`: export the versioned carrier from `validation_core`; +- `02c8763fd0d921189b51fc437016d04505182e1b`: expand public edge contracts for tampered counts, endpoints, probabilities, schema/kind mismatch, serializer refusal, invalid input triples, and square-overflowing `z`. + +Every later source or documentation commit on PR #488 invalidates predecessor exact-head workflow evidence; only the current head's hosted gates and independent review count for landing. + +## DDD and owner boundary + +This is Validation Evidence provenance and projection policy. It does not define a new psychometric estimator, change the Wilson score estimand, move longitudinal/time-varying composition into `validation_core`, or copy mutable arithmetic from fast-mlsirm. The count-based helper is private to the existing TEPP coverage producer so there remains one numeric authority inside this bounded context. + +The carrier also does not involve semantic LLM execution. contextual-orchestrator remains the owner of model routing, and no unreleased orchestrator source or provider credential is introduced here. + +## Standards and primary sources + +Wilson's original score-interval paper remains the primary statistical source for the interval family used by this producer. The current published AERA/APA/NCME testing standards remain the 2014 edition; AERA, APA, and NCME have convened a Joint Committee to revise that edition, and the AERA Task Force roster was current as of August 31, 2026. An unpublished revision is not treated as present normative authority. + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. + +Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 + +## Verification contract + +`crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs` is the public regression surface. Exact-head Rust tests, rustdoc/docstring checks, line/branch coverage, documentation validation, security/SAST/supply-chain checks, and qualifying independent review remain required before the Draft landing vehicle can be promoted or merged. From 7b8c51fd2131c46be7c3cd826fb0e5e4b286e281 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:01:13 +0900 Subject: [PATCH 183/576] docs(changelog): record Wilson coverage evidence provenance --- .../validation-wilson-coverage-evidence-provenance.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 CHANGELOG.d/validation-wilson-coverage-evidence-provenance.md diff --git a/CHANGELOG.d/validation-wilson-coverage-evidence-provenance.md b/CHANGELOG.d/validation-wilson-coverage-evidence-provenance.md new file mode 100644 index 000000000..b7c4834ea --- /dev/null +++ b/CHANGELOG.d/validation-wilson-coverage-evidence-provenance.md @@ -0,0 +1,6 @@ +# Validation Evidence: versioned Wilson coverage provenance + +- Add `WilsonCoverageEvidenceV1`, a schema-tagged durable carrier that retains empirical `sample_count`, `covered_count`, the caller-supplied standard-normal `z`, represented coverage, and canonical Wilson endpoints. +- Recompute coverage and Wilson bounds from stored counts and `z` during artifact admission so denominator, critical-value, projection, or endpoint tampering fails closed. +- Keep one crate-private count-based Wilson numeric authority shared by the existing interval API and the versioned carrier; no estimator target or Longitudinal Modeling semantics change. +- Preserve `critical_value_kind = "standard_normal_z"` explicitly instead of inferring an unrecorded nominal confidence-level claim. From e9c6392604140b819a192bc2969636f62e770a0b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:01:47 +0900 Subject: [PATCH 184/576] feat(validation): bind Wilson critical value to two-sided semantics --- .../validation_core/src/coverage_evidence.rs | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/crates/validation_core/src/coverage_evidence.rs b/crates/validation_core/src/coverage_evidence.rs index d4f7ec4a5..5b2c396a5 100644 --- a/crates/validation_core/src/coverage_evidence.rs +++ b/crates/validation_core/src/coverage_evidence.rs @@ -6,15 +6,17 @@ use serde::{Deserialize, Serialize}; const SCHEMA: &str = "tepp.wilson_coverage_evidence.v1"; const CRITICAL_VALUE_KIND: &str = "standard_normal_z"; +const INTERVAL_SIDEDNESS: &str = "two_sided"; /// Durable Wilson interval-coverage evidence with denominator and critical-value provenance. /// /// The carrier stores the retained sample denominator and covered count rather than only the /// projected empirical proportion, plus the caller-supplied standard-normal critical value used -/// by TEPP's canonical Wilson producer. Serialization fixes the schema identifier and critical -/// value semantics so a numeric `z` cannot later be reinterpreted as a Student-t or other scale. -/// Validation recomputes the empirical coverage and both Wilson endpoints from the stored counts -/// and `z`; tampered or internally inconsistent artifacts fail closed. +/// by TEPP's canonical two-sided Wilson producer. Serialization fixes the schema identifier, +/// critical-value scale, and interval sidedness so a numeric `z` cannot later be reinterpreted as +/// a Student-t value or a one-sided confidence claim. Validation recomputes the empirical coverage +/// and both Wilson endpoints from the stored counts and `z`; tampered or internally inconsistent +/// artifacts fail closed. #[derive(Clone, Copy, Debug, PartialEq)] pub struct WilsonCoverageEvidenceV1 { /// Number of interval/truth triples admitted to the empirical coverage calculation. @@ -132,11 +134,12 @@ impl Serialize for WilsonCoverageEvidenceV1 { use serde::ser::SerializeStruct; self.validate().map_err(serde::ser::Error::custom)?; - let mut state = serializer.serialize_struct("WilsonCoverageEvidenceV1", 8)?; + let mut state = serializer.serialize_struct("WilsonCoverageEvidenceV1", 9)?; state.serialize_field("schema", SCHEMA)?; state.serialize_field("sample_count", &self.sample_count)?; state.serialize_field("covered_count", &self.covered_count)?; state.serialize_field("critical_value_kind", CRITICAL_VALUE_KIND)?; + state.serialize_field("interval_sidedness", INTERVAL_SIDEDNESS)?; state.serialize_field("normal_critical_value", &self.normal_critical_value)?; state.serialize_field("empirical_coverage", &self.empirical_coverage)?; state.serialize_field("wilson_lower", &self.wilson_lower)?; @@ -157,6 +160,7 @@ impl<'de> Deserialize<'de> for WilsonCoverageEvidenceV1 { sample_count: usize, covered_count: usize, critical_value_kind: String, + interval_sidedness: String, normal_critical_value: f64, empirical_coverage: f64, wilson_lower: f64, @@ -164,7 +168,10 @@ impl<'de> Deserialize<'de> for WilsonCoverageEvidenceV1 { } let raw = Raw::deserialize(deserializer)?; - if raw.schema != SCHEMA || raw.critical_value_kind != CRITICAL_VALUE_KIND { + if raw.schema != SCHEMA + || raw.critical_value_kind != CRITICAL_VALUE_KIND + || raw.interval_sidedness != INTERVAL_SIDEDNESS + { return Err(serde::de::Error::custom("unsupported Wilson coverage evidence schema")); } let evidence = Self { From fdd24a1a9d3c9d22802f6a8ec6379e687009afe7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:02:10 +0900 Subject: [PATCH 185/576] test(validation): require two-sided Wilson evidence semantics --- .../tests/wilson_coverage_evidence_v1_contract.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs b/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs index 20dc93cca..e5f571edf 100644 --- a/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs +++ b/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs @@ -21,6 +21,7 @@ fn versioned_wilson_coverage_evidence_round_trips_denominator_and_critical_value let json = evidence.to_json().expect("canonical json"); assert!(json.contains("\"schema\":\"tepp.wilson_coverage_evidence.v1\"")); assert!(json.contains("\"critical_value_kind\":\"standard_normal_z\"")); + assert!(json.contains("\"interval_sidedness\":\"two_sided\"")); let decoded: WilsonCoverageEvidenceV1 = serde_json::from_str(&json).expect("decode"); assert_eq!(decoded, evidence); @@ -60,7 +61,7 @@ fn tampered_denominator_critical_value_or_endpoint_fails_closed() { } #[test] -fn serde_requires_the_versioned_schema_and_standard_normal_critical_value_semantics() { +fn serde_requires_version_critical_value_scale_and_two_sided_semantics() { let json = canonical_evidence().to_json().expect("canonical json"); let wrong_schema = json.replace( @@ -72,6 +73,9 @@ fn serde_requires_the_versioned_schema_and_standard_normal_critical_value_semant let wrong_kind = json.replace("standard_normal_z", "student_t"); assert!(serde_json::from_str::(&wrong_kind).is_err()); + let wrong_sidedness = json.replace("two_sided", "one_sided"); + assert!(serde_json::from_str::(&wrong_sidedness).is_err()); + let unknown_field = json.replacen('{', "{\"confidence_level\":0.95,", 1); assert!(serde_json::from_str::(&unknown_field).is_err()); } From 07766cb0b475da3d5610b42577dd9a5e91038bc9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:03:30 +0900 Subject: [PATCH 186/576] test(validation): bind report projection to Wilson provenance --- ...vidence_v1_coverage_provenance_contract.rs | 86 +++++++++++++++++++ 1 file changed, 86 insertions(+) create mode 100644 crates/validation_core/tests/validation_evidence_v1_coverage_provenance_contract.rs diff --git a/crates/validation_core/tests/validation_evidence_v1_coverage_provenance_contract.rs b/crates/validation_core/tests/validation_evidence_v1_coverage_provenance_contract.rs new file mode 100644 index 000000000..f1271217d --- /dev/null +++ b/crates/validation_core/tests/validation_evidence_v1_coverage_provenance_contract.rs @@ -0,0 +1,86 @@ +use validation_core::{ + ValidationError, ValidationEvidenceV1, ValidationReport, WilsonCoverageEvidenceV1, +}; + +fn canonical_coverage() -> WilsonCoverageEvidenceV1 { + let truth = [0.0, 1.0, 2.0, 3.0]; + let lower = [-0.5, 0.5, 1.0, 4.0]; + let upper = [0.5, 1.5, 2.5, 5.0]; + WilsonCoverageEvidenceV1::from_intervals(&truth, &lower, &upper, 1.96) + .expect("coverage evidence") +} + +fn canonical_report(coverage: WilsonCoverageEvidenceV1) -> ValidationReport { + ValidationReport { + study_label: "wilson-provenance".into(), + rmse: 0.1, + rmse_standard_error: 0.01, + mean_bias: 0.0, + bias_standard_error: 0.02, + interval_coverage: coverage.empirical_coverage, + coverage_wilson_lower: coverage.wilson_lower, + coverage_wilson_upper: coverage.wilson_upper, + temporal_order_accuracy: 1.0, + monte_carlo_rmse: None, + } +} + +#[test] +fn validation_evidence_v1_round_trips_report_and_wilson_provenance() { + let coverage = canonical_coverage(); + let report = canonical_report(coverage); + let evidence = ValidationEvidenceV1::new(report, coverage).expect("validation evidence"); + + let json = evidence.to_json().expect("json"); + assert!(json.contains("\"schema\":\"tepp.validation_evidence.v1\"")); + assert!(json.contains("\"sample_count\":4")); + assert!(json.contains("\"covered_count\":3")); + assert!(json.contains("\"interval_sidedness\":\"two_sided\"")); + + let decoded: ValidationEvidenceV1 = serde_json::from_str(&json).expect("decode"); + assert_eq!(decoded, evidence); +} + +#[test] +fn report_projection_must_match_the_versioned_coverage_evidence() { + let coverage = canonical_coverage(); + let mut report = canonical_report(coverage); + report.interval_coverage = 0.5; + assert_eq!( + ValidationEvidenceV1::new(report, coverage), + Err(ValidationError::InvalidInput) + ); + + let coverage = canonical_coverage(); + let mut report = canonical_report(coverage); + report.coverage_wilson_lower = 0.0; + assert_eq!( + ValidationEvidenceV1::new(report, coverage), + Err(ValidationError::InvalidInput) + ); + + let coverage = canonical_coverage(); + let mut report = canonical_report(coverage); + report.coverage_wilson_upper = 1.0; + assert_eq!( + ValidationEvidenceV1::new(report, coverage), + Err(ValidationError::InvalidInput) + ); +} + +#[test] +fn serde_rejects_schema_drift_and_nested_provenance_tampering() { + let coverage = canonical_coverage(); + let evidence = ValidationEvidenceV1::new(canonical_report(coverage), coverage) + .expect("validation evidence"); + let json = evidence.to_json().expect("json"); + + let wrong_schema = json.replace("tepp.validation_evidence.v1", "tepp.validation_evidence.v2"); + assert!(serde_json::from_str::(&wrong_schema).is_err()); + + let wrong_count = json.replacen("\"sample_count\":4", "\"sample_count\":5", 1); + assert!(serde_json::from_str::(&wrong_count).is_err()); + + let unknown_field = json.replacen('{', "{\"scientific_authority\":true,", 1); + assert!(serde_json::from_str::(&unknown_field).is_err()); +} From a16f22e674a0493fc0d49589e66e7ad66ea1e543 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:03:45 +0900 Subject: [PATCH 187/576] feat(validation): bind report projection to versioned provenance --- .../src/validation_evidence.rs | 104 ++++++++++++++++++ 1 file changed, 104 insertions(+) create mode 100644 crates/validation_core/src/validation_evidence.rs diff --git a/crates/validation_core/src/validation_evidence.rs b/crates/validation_core/src/validation_evidence.rs new file mode 100644 index 000000000..ff465f15f --- /dev/null +++ b/crates/validation_core/src/validation_evidence.rs @@ -0,0 +1,104 @@ +//! Versioned durable envelope for validation projections and their scientific provenance. + +use crate::{ValidationError, ValidationReport, WilsonCoverageEvidenceV1}; +use serde::{Deserialize, Serialize}; + +const SCHEMA: &str = "tepp.validation_evidence.v1"; + +/// Durable validation evidence envelope with a report projection and recomputable coverage proof. +/// +/// [`ValidationReport`] remains the compact projection used by existing callers. This versioned +/// envelope binds that projection to [`WilsonCoverageEvidenceV1`], which retains the empirical +/// denominator, covered count, standard-normal critical value, and two-sided interval semantics. +/// Admission requires the report's coverage proportion and Wilson endpoints to equal the values +/// recomputed by the nested provenance carrier, preventing a durable artifact from pairing a +/// valid report with evidence produced from a different finite sample or critical value. +#[derive(Clone, Debug, PartialEq)] +pub struct ValidationEvidenceV1 { + /// Existing compact validation projection. + pub report: ValidationReport, + /// Versioned, recomputable Wilson coverage provenance for the report's coverage fields. + pub coverage: WilsonCoverageEvidenceV1, +} + +impl ValidationEvidenceV1 { + /// Construct a validated durable evidence envelope. + /// + /// # Errors + /// + /// Returns [`ValidationError::InvalidInput`] when either nested artifact is invalid or the + /// report's empirical coverage/Wilson projection does not exactly match its provenance. + pub fn new( + report: ValidationReport, + coverage: WilsonCoverageEvidenceV1, + ) -> Result { + let evidence = Self { report, coverage }; + evidence.validate()?; + Ok(evidence) + } + + /// Validate nested artifacts and their cross-artifact coverage projection identity. + /// + /// # Errors + /// + /// Returns [`ValidationError::InvalidInput`] when nested validation fails or the report's + /// coverage proportion/lower/upper endpoints differ from the recomputed versioned evidence. + pub fn validate(&self) -> Result<(), ValidationError> { + self.report.validate()?; + self.coverage.validate()?; + if self.report.interval_coverage != self.coverage.empirical_coverage + || self.report.coverage_wilson_lower != self.coverage.wilson_lower + || self.report.coverage_wilson_upper != self.coverage.wilson_upper + { + return Err(ValidationError::InvalidInput); + } + Ok(()) + } + + /// Serialize the validated envelope to canonical JSON. + /// + /// # Errors + /// + /// Returns [`ValidationError::InvalidInput`] when validation or serialization fails. + pub fn to_json(&self) -> Result { + self.validate()?; + serde_json::to_string(self).map_err(|_| ValidationError::InvalidInput) + } +} + +impl Serialize for ValidationEvidenceV1 { + fn serialize(&self, serializer: S) -> Result + where + S: serde::Serializer, + { + use serde::ser::SerializeStruct; + + self.validate().map_err(serde::ser::Error::custom)?; + let mut state = serializer.serialize_struct("ValidationEvidenceV1", 3)?; + state.serialize_field("schema", SCHEMA)?; + state.serialize_field("report", &self.report)?; + state.serialize_field("coverage", &self.coverage)?; + state.end() + } +} + +impl<'de> Deserialize<'de> for ValidationEvidenceV1 { + fn deserialize(deserializer: D) -> Result + where + D: serde::Deserializer<'de>, + { + #[derive(Deserialize)] + #[serde(deny_unknown_fields)] + struct Raw { + schema: String, + report: ValidationReport, + coverage: WilsonCoverageEvidenceV1, + } + + let raw = Raw::deserialize(deserializer)?; + if raw.schema != SCHEMA { + return Err(serde::de::Error::custom("unsupported validation evidence schema")); + } + Self::new(raw.report, raw.coverage).map_err(serde::de::Error::custom) + } +} From 4e2381f31a43d753cd8c37bb153aac701876c1ef Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:03:58 +0900 Subject: [PATCH 188/576] feat(validation): export versioned validation evidence envelope --- crates/validation_core/src/lib.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/lib.rs b/crates/validation_core/src/lib.rs index f44c1e2e0..b905e3360 100644 --- a/crates/validation_core/src/lib.rs +++ b/crates/validation_core/src/lib.rs @@ -9,7 +9,7 @@ //! parameter match counts, RMSE, bias, interval coverage with Wilson bounds, //! temporal-order accuracy, relation precision/recall, and SE-aware Monte Carlo //! acceptance gates. ADR 0014 claim authorities are promoted only by exact-head -//! evidence; queued, predecessor, skipped, and LLM judgments fail closed. +//! evidence; queued, predecessor, skipped, and L-head LLM judgments fail closed. //! Metrics are pure `f64` CPU reference implementations. mod bias; @@ -25,18 +25,19 @@ mod numeric; mod report; mod rmse; mod temporal_order; +mod validation_evidence; /// Standard error of mean signed bias. pub use bias::bias_standard_error; /// Mean signed bias. pub use bias::mean_bias; -/// Four ADR 0014 claim authorities. +/// Four ADR 0016 claim authorities. pub use claim::ClaimAuthority; /// One evidence item offered for promotion. pub use claim::ClaimEvidence; /// Kind of evidence offered for a promotion request. pub use claim::ClaimEvidenceKind; -/// A claim bound to one exact commit after every required gate passed. +/// A claim bound to exact commit after every required gate passed. pub use claim::PromotedClaim; /// Exact-head promotion request. pub use claim::PromotionRequest; @@ -78,3 +79,5 @@ pub use rmse::rmse_standard_error; pub use rmse::root_mean_square_error; /// Pairwise temporal-order accuracy. pub use temporal_order::temporal_order_accuracy; +/// Versioned durable validation envelope binding projections to scientific provenance. +pub use validation_evidence::ValidationEvidenceV1; From 57d8bf57687c5157c7c06c941a113071e22c5430 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:04:14 +0900 Subject: [PATCH 189/576] fix(validation): restore exact claim authority wording --- crates/validation_core/src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/lib.rs b/crates/validation_core/src/lib.rs index b905e3360..30636924b 100644 --- a/crates/validation_core/src/lib.rs +++ b/crates/validation_core/src/lib.rs @@ -9,7 +9,7 @@ //! parameter match counts, RMSE, bias, interval coverage with Wilson bounds, //! temporal-order accuracy, relation precision/recall, and SE-aware Monte Carlo //! acceptance gates. ADR 0014 claim authorities are promoted only by exact-head -//! evidence; queued, predecessor, skipped, and L-head LLM judgments fail closed. +//! evidence; queued, predecessor, skipped, and LLM judgments fail closed. //! Metrics are pure `f64` CPU reference implementations. mod bias; @@ -31,13 +31,13 @@ mod validation_evidence; pub use bias::bias_standard_error; /// Mean signed bias. pub use bias::mean_bias; -/// Four ADR 0016 claim authorities. +/// Four ADR 0014 claim authorities. pub use claim::ClaimAuthority; /// One evidence item offered for promotion. pub use claim::ClaimEvidence; /// Kind of evidence offered for a promotion request. pub use claim::ClaimEvidenceKind; -/// A claim bound to exact commit after every required gate passed. +/// A claim bound to one exact commit after every required gate passed. pub use claim::PromotedClaim; /// Exact-head promotion request. pub use claim::PromotionRequest; From 6e5467428b9de851f075cb251f9dc38a4ec6a728 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:04:42 +0900 Subject: [PATCH 190/576] docs(changelog): bind Wilson provenance to validation envelope --- .../validation-wilson-coverage-evidence-provenance.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.d/validation-wilson-coverage-evidence-provenance.md b/CHANGELOG.d/validation-wilson-coverage-evidence-provenance.md index b7c4834ea..2c1dfebe7 100644 --- a/CHANGELOG.d/validation-wilson-coverage-evidence-provenance.md +++ b/CHANGELOG.d/validation-wilson-coverage-evidence-provenance.md @@ -1,6 +1,7 @@ # Validation Evidence: versioned Wilson coverage provenance - Add `WilsonCoverageEvidenceV1`, a schema-tagged durable carrier that retains empirical `sample_count`, `covered_count`, the caller-supplied standard-normal `z`, represented coverage, and canonical Wilson endpoints. +- Fix the interval interpretation as `critical_value_kind = "standard_normal_z"` and `interval_sidedness = "two_sided"`; v1 records the producer input and sidedness rather than inventing a nominal confidence-level label that the API never received. - Recompute coverage and Wilson bounds from stored counts and `z` during artifact admission so denominator, critical-value, projection, or endpoint tampering fails closed. -- Keep one crate-private count-based Wilson numeric authority shared by the existing interval API and the versioned carrier; no estimator target or Longitudinal Modeling semantics change. -- Preserve `critical_value_kind = "standard_normal_z"` explicitly instead of inferring an unrecorded nominal confidence-level claim. +- Add `ValidationEvidenceV1` as the versioned durable envelope that binds the existing `ValidationReport` projection to the recomputable Wilson coverage provenance; legacy report callers remain source-compatible. +- Keep one crate-private count-based Wilson numeric authority shared by the existing interval API and the versioned carrier; no estimator target, Longitudinal Modeling semantics, fast-mlsirm ownership, or contextual-orchestrator routing contract changes. From c6ce28b1825dbbb7b4376be0ff218a2159ce401a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:05:07 +0900 Subject: [PATCH 191/576] docs(validation): trace durable Wilson report provenance envelope --- .../wilson-coverage-evidence-provenance.md | 42 +++++++++---------- 1 file changed, 21 insertions(+), 21 deletions(-) diff --git a/docs/research/wilson-coverage-evidence-provenance.md b/docs/research/wilson-coverage-evidence-provenance.md index d80808617..a496e898f 100644 --- a/docs/research/wilson-coverage-evidence-provenance.md +++ b/docs/research/wilson-coverage-evidence-provenance.md @@ -2,53 +2,53 @@ ## Problem -`ValidationReport` historically retained only the empirical coverage proportion and two Wilson score endpoints. That projection is useful for presentation but is not sufficient provenance for a durable validation artifact: the empirical denominator and the caller-supplied Wilson critical value are lost. Endpoint-pair algebra can reject many impossible combinations, but it cannot reconstruct the exact finite-sample calculation and becomes deliberately non-identifying at exact boundary coverage. +`ValidationReport` historically retained only the empirical coverage proportion and two Wilson score endpoints. That compact projection is useful for presentation but is not sufficient provenance for a durable validation artifact: the empirical denominator and the caller-supplied Wilson critical value are lost. Endpoint-pair algebra can reject many impossible combinations, but it cannot reconstruct the exact finite-sample calculation and becomes deliberately non-identifying at exact boundary coverage. -For a coverage study with `k` covered intervals among `n` admitted interval/truth triples, TEPP's canonical producer uses `p = k / n` and a caller-supplied standard-normal critical value `z`. Two reports can therefore show the same represented `p` while having materially different denominators, and the same `p` can produce different Wilson bounds under different `z` values. A durable evidence record that omits `n`, `k`, or the critical-value semantics cannot independently recompute the interval it claims to preserve. +For a coverage study with `k` covered intervals among `n` admitted interval/truth triples, TEPP's canonical producer uses `p = k / n` and a caller-supplied standard-normal critical value `z`. Two reports can therefore show the same represented `p` while having different denominators, and the same `p` can produce different Wilson bounds under different `z` values. A durable evidence record that omits `n`, `k`, or the critical-value semantics cannot independently recompute the interval it claims to preserve. -## Versioned carrier +## Versioned carrier and envelope -`validation_core::WilsonCoverageEvidenceV1` is the first versioned provenance carrier for this calculation. Its JSON contract emits: +`validation_core::WilsonCoverageEvidenceV1` is the versioned provenance carrier for the calculation. Its JSON contract emits `schema = "tepp.wilson_coverage_evidence.v1"`, `sample_count`, `covered_count`, `critical_value_kind = "standard_normal_z"`, `interval_sidedness = "two_sided"`, the caller-supplied `normal_critical_value`, represented empirical coverage, and the canonical Wilson lower and upper endpoints. -- `schema = "tepp.wilson_coverage_evidence.v1"`; -- `sample_count` and `covered_count`; -- `critical_value_kind = "standard_normal"` is not used; the exact contract is `critical_value_kind = "standard_normal_z"`; -- the caller-supplied `normal_critical_value`; -- the represented empirical coverage; -- the canonical Wilson lower and upper endpoints. +The type does not infer or invent a nominal confidence-level label. The current producer accepts a numeric standard-normal critical value directly and evaluates the symmetric lower/upper Wilson roots, so v1 records that scientific input, its scale, and two-sided interpretation. A downstream product may display `z = 1.96` as a nominal two-sided 95% convention only when that claim is supplied by its own validated contract; the Validation Evidence carrier does not reverse-engineer a confidence label that the producer was never given. -The type does not infer or invent a nominal confidence-level label. The current producer accepts a numeric standard-normal critical value directly, so v1 records that scientific input and its scale exactly. A UI or downstream report may describe `z = 1.96` as a nominal two-sided 95% convention only when that interpretation is supplied by its own validated contract; the Validation Evidence carrier does not reverse-engineer a confidence claim that the producer was never given. +`ValidationEvidenceV1` is the durable outer envelope. It retains the existing `ValidationReport` as a backward-compatible compact projection and nests `WilsonCoverageEvidenceV1` as its recomputable coverage provenance. Admission validates both artifacts and requires the report's empirical coverage, Wilson lower endpoint, and Wilson upper endpoint to equal the nested carrier exactly. Existing callers can continue using `ValidationReport`; durable v1 evidence can no longer pair a valid-looking projection with a different denominator or critical value. ## Canonical recomputation The coverage implementation now has one crate-private count-based Wilson authority. `wilson_coverage_interval` still accepts interval/truth triples, but it counts covered observations once and delegates the numeric interval calculation to `wilson_coverage_interval_from_counts`. `WilsonCoverageEvidenceV1` uses the same helper for construction and validation. This avoids a second copy of Wilson arithmetic and makes the durable carrier recomputable from the evidence it stores. -Artifact admission is exact for the represented binary64 contract. The carrier recomputes `covered_count / sample_count` and both Wilson endpoints with the canonical producer and requires numeric equality. TEPP's own JSON serializer emits round-trip-safe binary64 decimals, so a serialized artifact produced by this crate decodes to the same represented values. A changed denominator, covered count, critical value, projected coverage, or endpoint fails closed. Unknown schema fields, an unsupported schema version, and a critical-value kind other than `standard_normal_z` also fail deserialization rather than being silently reinterpreted. +Artifact admission is exact for the represented binary64 contract. The carrier recomputes `covered_count / sample_count` and both Wilson endpoints with the canonical producer and requires numeric equality. TEPP's JSON serializer emits round-trip-safe binary64 decimals, so a serialized artifact produced by this crate decodes to the same represented values. A changed denominator, covered count, critical value, projected coverage, or endpoint fails closed. Unknown fields, an unsupported schema version, a critical-value kind other than `standard_normal_z`, or sidedness other than `two_sided` also fail deserialization rather than being silently reinterpreted. -This exact recomputation contract is intentionally stronger than `ValidationReport`'s legacy endpoint-pair admission. The legacy report has insufficient provenance to reproduce the original interval and therefore uses necessary algebraic support checks; the versioned carrier has the missing denominator and critical value, so it can use the actual canonical producer instead of a loose identity. +This exact recomputation contract is intentionally stronger than `ValidationReport`'s legacy endpoint-pair admission. The legacy report lacks enough provenance to reproduce the original interval and therefore uses necessary algebraic support checks; the versioned carrier has the missing denominator and critical value, so it can call the actual canonical producer instead of relying on a loose identity. ## RED → repair trace -The first test draft `9fc96345f67ee0d6e6e8b62903b9994f13932a1d` contained a bad fixture and placeholder assertion and is not scientific evidence. Non-force correction `6f6e06d2446cc459cc29879c3e4bc34a2fff8e82` is the valid RED: the public contract requires denominator/covered-count retention, standard-normal critical-value semantics, canonical JSON round-trip, and fail-closed tampering. +The first test draft `9fc96345f67ee0d6e6e8b62903b9994f13932a1d` contained a bad fixture and placeholder assertion and is not scientific evidence. Non-force correction `6f6e06d2446cc459cc29879c3e4bc34a2fff8e82` is the first valid RED for denominator/covered-count retention, standard-normal critical-value semantics, JSON round-trip, and fail-closed tampering. The implementation lineage is: -- `ca517ed3755a11b4574f8909acd6965273cf69e9`: factor interval hit counting and count-based Wilson recomputation into one crate-private numeric authority while preserving the public interval APIs; +- `ca517ed3755a11b4574f8909acd6965273cf69e9`: factor interval hit counting and count-based Wilson recomputation into one crate-private numeric authority while preserving public interval APIs; - `31e1ab2bbaf9ce40cf74bed2110a310116e7a80a`: add `WilsonCoverageEvidenceV1` with validated construction, exact recomputation, schema-tagged manual serde, and fail-closed unknown-field behavior; -- `b6714d2365bbdbad0f127631edd8463f1829f0e2`: export the versioned carrier from `validation_core`; -- `02c8763fd0d921189b51fc437016d04505182e1b`: expand public edge contracts for tampered counts, endpoints, probabilities, schema/kind mismatch, serializer refusal, invalid input triples, and square-overflowing `z`. +- `b6714d2365bbdbad0f127631edd8463f1829f0e2`: export the coverage carrier from `validation_core`; +- `02c8763fd0d921189b51fc437016d04505182e1b`: expand edge contracts for tampered counts/endpoints/probabilities, serializer refusal, invalid inputs, and square-overflowing `z`; +- `e9c6392604140b819a192bc2969636f62e770a0b` and `fdd24a1a9d3c9d22802f6a8ec6379e687009afe7`: bind and test `two_sided` interval semantics rather than leaving one-sided reinterpretation implicit; +- `07766cb0b475da3d5610b42577dd9a5e91038bc9`: public RED requiring a versioned durable envelope to bind `ValidationReport` coverage fields to the provenance carrier; +- `a16f22e674a0493fc0d49589e66e7ad66ea1e543`: add `ValidationEvidenceV1` with nested validation, exact projection identity, schema-tagged serde, and unknown-field refusal; +- export edit `4e2381f31a43d753cd8c37bb153aac701876c1ef` accidentally changed unrelated claim-authority wording and is not accepted repair evidence; non-force correction `57d8bf57687c5157c7c06c941a113071e22c5430` restores that wording while retaining only the intended module/export delta; +- `6e5467428b9de851f075cb251f9dc38a4ec6a728`: update release-facing change documentation for the carrier/envelope contract. -Every later source or documentation commit on PR #488 invalidates predecessor exact-head workflow evidence; only the current head's hosted gates and independent review count for landing. +Every later source or documentation commit on PR #488 invalidates predecessor exact-head workflow evidence; only the current surviving head's hosted gates and independent review count for landing. ## DDD and owner boundary This is Validation Evidence provenance and projection policy. It does not define a new psychometric estimator, change the Wilson score estimand, move longitudinal/time-varying composition into `validation_core`, or copy mutable arithmetic from fast-mlsirm. The count-based helper is private to the existing TEPP coverage producer so there remains one numeric authority inside this bounded context. -The carrier also does not involve semantic LLM execution. contextual-orchestrator remains the owner of model routing, and no unreleased orchestrator source or provider credential is introduced here. +The carrier and envelope do not involve semantic LLM execution. contextual-orchestrator remains the owner of model routing; no unreleased orchestrator source, direct provider route, or provider credential is introduced here. ## Standards and primary sources -Wilson's original score-interval paper remains the primary statistical source for the interval family used by this producer. The current published AERA/APA/NCME testing standards remain the 2014 edition; AERA, APA, and NCME have convened a Joint Committee to revise that edition, and the AERA Task Force roster was current as of August 31, 2026. An unpublished revision is not treated as present normative authority. +Wilson's original score-interval paper remains the primary statistical source for the interval family used by this producer. The current published AERA/APA/NCME testing standards remain the 2014 edition; AERA, APA, and NCME have convened a Joint Committee to revise that edition, and AERA's Task Force roster was current as of August 31, 2026. An unpublished revision is not treated as present normative authority. American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. @@ -56,4 +56,4 @@ Wilson, E. B. (1927). Probable inference, the law of succession, and statistical ## Verification contract -`crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs` is the public regression surface. Exact-head Rust tests, rustdoc/docstring checks, line/branch coverage, documentation validation, security/SAST/supply-chain checks, and qualifying independent review remain required before the Draft landing vehicle can be promoted or merged. +`crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs` covers the provenance carrier. `crates/validation_core/tests/validation_evidence_v1_coverage_provenance_contract.rs` covers the durable report/provenance envelope. Exact-head Rust tests, rustdoc/docstring checks, owned line/branch coverage, documentation validation, security/SAST/supply-chain checks, and qualifying independent review remain required before the Draft landing vehicle can be promoted or merged. From 29d710a53d4988b46a37667b4ff03352d520b3c7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:32:34 +0900 Subject: [PATCH 192/576] test(validation): preserve Wilson count precision beyond f64 integers --- ...ilson_coverage_count_precision_contract.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 crates/validation_core/tests/wilson_coverage_count_precision_contract.rs diff --git a/crates/validation_core/tests/wilson_coverage_count_precision_contract.rs b/crates/validation_core/tests/wilson_coverage_count_precision_contract.rs new file mode 100644 index 000000000..9e7bf37ba --- /dev/null +++ b/crates/validation_core/tests/wilson_coverage_count_precision_contract.rs @@ -0,0 +1,34 @@ +use validation_core::WilsonCoverageEvidenceV1; + +#[test] +fn durable_counts_do_not_collapse_one_uncovered_case_to_all_covered() { + // n = 2^53 + 1 and k = 2^53 are distinct integer counts, but converting each + // count independently to binary64 rounds both to 2^53. The durable count + // provenance must therefore form the represented proportion from the small + // uncovered complement rather than erase the one observed miss. + let json = r#"{ + "schema":"tepp.wilson_coverage_evidence.v1", + "sample_count":9007199254740993, + "covered_count":9007199254740992, + "critical_value_kind":"standard_normal_z", + "interval_sidedness":"two_sided", + "normal_critical_value":1.96, + "empirical_coverage":0.9999999999999999, + "wilson_lower":0.9999999999999993, + "wilson_upper":1.0 + }"#; + + let evidence: WilsonCoverageEvidenceV1 = + serde_json::from_str(json).expect("large-count Wilson evidence must remain reproducible"); + + assert_eq!(evidence.sample_count as u64, 9_007_199_254_740_993); + assert_eq!(evidence.covered_count as u64, 9_007_199_254_740_992); + assert_eq!(evidence.empirical_coverage.to_bits(), 0x3fef_ffff_ffff_ffff); + assert_eq!(evidence.wilson_lower.to_bits(), 0x3fef_ffff_ffff_fffa); + assert_eq!(evidence.wilson_upper, 1.0); + + let round_trip = evidence.to_json().expect("validated durable evidence"); + let decoded: WilsonCoverageEvidenceV1 = + serde_json::from_str(&round_trip).expect("round-trip durable evidence"); + assert_eq!(decoded, evidence); +} From 29968c807368fe3fe19bef3013af9d577d5f6025 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:33:26 +0900 Subject: [PATCH 193/576] fix(validation): preserve Wilson count distinction above f64 precision --- crates/validation_core/src/coverage.rs | 95 ++++++++++++++++++-------- 1 file changed, 67 insertions(+), 28 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index 375a148af..b90a12ae0 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -28,6 +28,23 @@ pub(crate) fn interval_covered_count( Ok(covered) } +pub(crate) fn represented_coverage_from_counts( + covered_count: u64, + sample_count: u64, +) -> Result { + if sample_count == 0 || covered_count > sample_count { + return Err(ValidationError::InvalidInput); + } + let uncovered_count = sample_count - covered_count; + let n = sample_count as f64; + let coverage = if covered_count <= uncovered_count { + covered_count as f64 / n + } else { + 1.0 - uncovered_count as f64 / n + }; + Ok(coverage) +} + /// Empirical coverage of closed intervals `[lower, upper]` for truth values. /// /// # Errors @@ -40,7 +57,7 @@ pub fn interval_coverage( upper: &[f64], ) -> Result { let covered = interval_covered_count(truth, lower, upper)?; - Ok(covered as f64 / truth.len() as f64) + represented_coverage_from_counts(covered as u64, truth.len() as u64) } fn rationalized_wilson_positive_lower(n: f64, p: f64, z: f64, z2: f64) -> f64 { @@ -65,9 +82,35 @@ fn rationalized_wilson_positive_lower(n: f64, p: f64, z: f64, z2: f64) -> f64 { (numerator / denominator).clamp(0.0, 1.0) } +fn wilson_bounds_from_represented_proportion( + n: f64, + p: f64, + z: f64, + z2: f64, +) -> (f64, f64) { + let low = if p > 0.0 { + rationalized_wilson_positive_lower(n, p, z, z2) + } else { + 0.0 + }; + + let denominator = 1.0 + z2 / n; + let center = p + z2 / (2.0 * n); + let radical = (p * (1.0 - p) / n) + z2 / (4.0 * n * n); + let margin = z * radical.sqrt(); + let direct_high = ((center + margin) / denominator).clamp(0.0, 1.0); + let high = if direct_high == 1.0 && p < 1.0 && z2 > 0.0 { + let uncovered_lower = rationalized_wilson_positive_lower(n, 1.0 - p, z, z2); + (1.0 - uncovered_lower).clamp(0.0, 1.0) + } else { + direct_high + }; + (low, high) +} + pub(crate) fn wilson_coverage_interval_from_counts( - covered_count: usize, - sample_count: usize, + covered_count: u64, + sample_count: u64, z: f64, ) -> Result<(f64, f64), ValidationError> { if sample_count == 0 || covered_count > sample_count { @@ -77,35 +120,32 @@ pub(crate) fn wilson_coverage_interval_from_counts( return Err(ValidationError::InvalidConfiguration); } - let p = covered_count as f64 / sample_count as f64; let n = sample_count as f64; let z2 = z * z; if !z2.is_finite() { return Err(ValidationError::InvalidConfiguration); } - if p == 1.0 { + if covered_count == sample_count { return Ok((n / (n + z2), 1.0)); } - let low = if p > 0.0 { - rationalized_wilson_positive_lower(n, p, z, z2) - } else { - 0.0 - }; + let uncovered_count = sample_count - covered_count; + if covered_count > uncovered_count { + // Near all-covered samples can lose the observed misses if both integer + // counts are independently rounded to binary64 before division. Wilson + // intervals are complement-symmetric, so evaluate the smaller uncovered + // proportion and reflect its endpoints instead. + let uncovered = uncovered_count as f64 / n; + let (uncovered_low, uncovered_high) = + wilson_bounds_from_represented_proportion(n, uncovered, z, z2); + return Ok(( + (1.0 - uncovered_high).clamp(0.0, 1.0), + (1.0 - uncovered_low).clamp(0.0, 1.0), + )); + } - let denominator = 1.0 + z2 / n; - let center = p + z2 / (2.0 * n); - let radical = (p * (1.0 - p) / n) + z2 / (4.0 * n * n); - // With finite z² and coverage p in [0,1], Wilson terms remain finite. - let margin = z * radical.sqrt(); - let direct_high = ((center + margin) / denominator).clamp(0.0, 1.0); - let high = if direct_high == 1.0 && p < 1.0 && z2 > 0.0 { - let uncovered_lower = rationalized_wilson_positive_lower(n, 1.0 - p, z, z2); - (1.0 - uncovered_lower).clamp(0.0, 1.0) - } else { - direct_high - }; - Ok((low, high)) + let p = covered_count as f64 / n; + Ok(wilson_bounds_from_represented_proportion(n, p, z, z2)) } /// Wilson score lower/upper bounds for a binomial coverage proportion. @@ -117,10 +157,9 @@ pub(crate) fn wilson_coverage_interval_from_counts( /// evaluated through the algebraically rationalized positive root rather than /// `center - margin`; the implementation switches scale at `z² = 1` so the /// stable form neither suffers large-z cancellation nor small-z division -/// overflow. The same positive-lower representation is applied to the -/// complementary uncovered proportion when `center + margin` falsely rounds an -/// upper endpoint to exact one even though the represented Wilson endpoint -/// remains below one. +/// overflow. Near the all-covered boundary, the smaller uncovered count is +/// evaluated and reflected by Wilson complement symmetry so distinct integer +/// counts are not erased when they exceed binary64's exact-integer range. /// /// # Errors /// @@ -136,7 +175,7 @@ pub fn wilson_coverage_interval( return Err(ValidationError::InvalidConfiguration); } let covered_count = interval_covered_count(truth, lower, upper)?; - wilson_coverage_interval_from_counts(covered_count, truth.len(), z) + wilson_coverage_interval_from_counts(covered_count as u64, truth.len() as u64, z) } #[cfg(test)] From 91d9a3bb54b47605377e5159763a55f3386efcf9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:33:49 +0900 Subject: [PATCH 194/576] fix(validation): make Wilson provenance counts fixed-width --- .../validation_core/src/coverage_evidence.rs | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/crates/validation_core/src/coverage_evidence.rs b/crates/validation_core/src/coverage_evidence.rs index 5b2c396a5..c4c1c785f 100644 --- a/crates/validation_core/src/coverage_evidence.rs +++ b/crates/validation_core/src/coverage_evidence.rs @@ -1,7 +1,9 @@ //! Versioned provenance carrier for empirical interval-coverage evidence. use crate::ValidationError; -use crate::coverage::{interval_covered_count, wilson_coverage_interval_from_counts}; +use crate::coverage::{ + interval_covered_count, represented_coverage_from_counts, wilson_coverage_interval_from_counts, +}; use serde::{Deserialize, Serialize}; const SCHEMA: &str = "tepp.wilson_coverage_evidence.v1"; @@ -10,19 +12,20 @@ const INTERVAL_SIDEDNESS: &str = "two_sided"; /// Durable Wilson interval-coverage evidence with denominator and critical-value provenance. /// -/// The carrier stores the retained sample denominator and covered count rather than only the -/// projected empirical proportion, plus the caller-supplied standard-normal critical value used -/// by TEPP's canonical two-sided Wilson producer. Serialization fixes the schema identifier, -/// critical-value scale, and interval sidedness so a numeric `z` cannot later be reinterpreted as -/// a Student-t value or a one-sided confidence claim. Validation recomputes the empirical coverage -/// and both Wilson endpoints from the stored counts and `z`; tampered or internally inconsistent -/// artifacts fail closed. +/// The carrier stores fixed-width retained-sample and covered counts rather than only the projected +/// empirical proportion, plus the caller-supplied standard-normal critical value used by TEPP's +/// canonical two-sided Wilson producer. Serialization fixes the schema identifier, critical-value +/// scale, and interval sidedness so a numeric `z` cannot later be reinterpreted as a Student-t value +/// or a one-sided confidence claim. Validation recomputes represented empirical coverage and both +/// Wilson endpoints from the stored counts and `z`; tampered or internally inconsistent artifacts +/// fail closed. Near all-covered samples are evaluated from the smaller uncovered count so a real +/// miss is not erased when integer counts exceed binary64's exact-integer range. #[derive(Clone, Copy, Debug, PartialEq)] pub struct WilsonCoverageEvidenceV1 { /// Number of interval/truth triples admitted to the empirical coverage calculation. - pub sample_count: usize, + pub sample_count: u64, /// Number of admitted triples whose closed interval contains the corresponding truth value. - pub covered_count: usize, + pub covered_count: u64, /// Caller-supplied standard-normal critical value used by the Wilson score producer. pub normal_critical_value: f64, /// Empirical coverage projected from `covered_count / sample_count`. @@ -50,8 +53,8 @@ impl WilsonCoverageEvidenceV1 { if !normal_critical_value.is_finite() || normal_critical_value <= 0.0 { return Err(ValidationError::InvalidConfiguration); } - let covered_count = interval_covered_count(truth, lower, upper)?; - let sample_count = truth.len(); + let covered_count = interval_covered_count(truth, lower, upper)? as u64; + let sample_count = truth.len() as u64; let (wilson_lower, wilson_upper) = wilson_coverage_interval_from_counts( covered_count, sample_count, @@ -61,7 +64,7 @@ impl WilsonCoverageEvidenceV1 { sample_count, covered_count, normal_critical_value, - empirical_coverage: covered_count as f64 / sample_count as f64, + empirical_coverage: represented_coverage_from_counts(covered_count, sample_count)?, wilson_lower, wilson_upper, }; @@ -97,7 +100,8 @@ impl WilsonCoverageEvidenceV1 { } } - let expected_coverage = self.covered_count as f64 / self.sample_count as f64; + let expected_coverage = + represented_coverage_from_counts(self.covered_count, self.sample_count)?; let (expected_lower, expected_upper) = wilson_coverage_interval_from_counts( self.covered_count, self.sample_count, @@ -157,8 +161,8 @@ impl<'de> Deserialize<'de> for WilsonCoverageEvidenceV1 { #[serde(deny_unknown_fields)] struct Raw { schema: String, - sample_count: usize, - covered_count: usize, + sample_count: u64, + covered_count: u64, critical_value_kind: String, interval_sidedness: String, normal_critical_value: f64, From d908ce2a37685bdf915cc513be9f3f9dd36aae19 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:34:05 +0900 Subject: [PATCH 195/576] docs(validation): record Wilson count-precision repair --- CHANGELOG.d/validation-wilson-count-precision.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-wilson-count-precision.md diff --git a/CHANGELOG.d/validation-wilson-count-precision.md b/CHANGELOG.d/validation-wilson-count-precision.md new file mode 100644 index 000000000..37433fdd0 --- /dev/null +++ b/CHANGELOG.d/validation-wilson-count-precision.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core` now preserves distinct covered/sample counts when Wilson coverage evidence exceeds binary64's exact-integer range. Near the all-covered boundary it forms empirical coverage from the smaller uncovered complement and evaluates the Wilson interval by complement symmetry, so one retained miss cannot be rounded away into an all-covered numerical path. +- `WilsonCoverageEvidenceV1` stores `sample_count` and `covered_count` as fixed-width `u64` values, making the versioned JSON count contract independent of Rust pointer width while retaining exact integer provenance. From a2c4ad8b8b441ce3cd9d15fcc66794ffd573d021 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:34:44 +0900 Subject: [PATCH 196/576] docs(research): trace Wilson large-count provenance precision --- .../wilson-coverage-evidence-provenance.md | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/docs/research/wilson-coverage-evidence-provenance.md b/docs/research/wilson-coverage-evidence-provenance.md index a496e898f..8b8e072f6 100644 --- a/docs/research/wilson-coverage-evidence-provenance.md +++ b/docs/research/wilson-coverage-evidence-provenance.md @@ -8,7 +8,7 @@ For a coverage study with `k` covered intervals among `n` admitted interval/trut ## Versioned carrier and envelope -`validation_core::WilsonCoverageEvidenceV1` is the versioned provenance carrier for the calculation. Its JSON contract emits `schema = "tepp.wilson_coverage_evidence.v1"`, `sample_count`, `covered_count`, `critical_value_kind = "standard_normal_z"`, `interval_sidedness = "two_sided"`, the caller-supplied `normal_critical_value`, represented empirical coverage, and the canonical Wilson lower and upper endpoints. +`validation_core::WilsonCoverageEvidenceV1` is the versioned provenance carrier for the calculation. Its JSON contract emits `schema = "tepp.wilson_coverage_evidence.v1"`, fixed-width `u64` `sample_count` and `covered_count`, `critical_value_kind = "standard_normal_z"`, `interval_sidedness = "two_sided"`, the caller-supplied `normal_critical_value`, represented empirical coverage, and the canonical Wilson lower and upper endpoints. The type does not infer or invent a nominal confidence-level label. The current producer accepts a numeric standard-normal critical value directly and evaluates the symmetric lower/upper Wilson roots, so v1 records that scientific input, its scale, and two-sided interpretation. A downstream product may display `z = 1.96` as a nominal two-sided 95% convention only when that claim is supplied by its own validated contract; the Validation Evidence carrier does not reverse-engineer a confidence label that the producer was never given. @@ -16,17 +16,25 @@ The type does not infer or invent a nominal confidence-level label. The current ## Canonical recomputation -The coverage implementation now has one crate-private count-based Wilson authority. `wilson_coverage_interval` still accepts interval/truth triples, but it counts covered observations once and delegates the numeric interval calculation to `wilson_coverage_interval_from_counts`. `WilsonCoverageEvidenceV1` uses the same helper for construction and validation. This avoids a second copy of Wilson arithmetic and makes the durable carrier recomputable from the evidence it stores. +The coverage implementation has one crate-private count-based Wilson authority. `wilson_coverage_interval` still accepts interval/truth triples, but it counts covered observations once and delegates the numeric interval calculation to `wilson_coverage_interval_from_counts`. `WilsonCoverageEvidenceV1` uses the same helper for construction and validation. This avoids a second copy of Wilson arithmetic and makes the durable carrier recomputable from the evidence it stores. -Artifact admission is exact for the represented binary64 contract. The carrier recomputes `covered_count / sample_count` and both Wilson endpoints with the canonical producer and requires numeric equality. TEPP's JSON serializer emits round-trip-safe binary64 decimals, so a serialized artifact produced by this crate decodes to the same represented values. A changed denominator, covered count, critical value, projected coverage, or endpoint fails closed. Unknown fields, an unsupported schema version, a critical-value kind other than `standard_normal_z`, or sidedness other than `two_sided` also fail deserialization rather than being silently reinterpreted. +Artifact admission is exact for the represented binary64 contract. The carrier recomputes represented empirical coverage and both Wilson endpoints with the canonical producer and requires numeric equality. TEPP's JSON serializer emits round-trip-safe binary64 decimals, so a serialized artifact produced by this crate decodes to the same represented values. A changed denominator, covered count, critical value, projected coverage, or endpoint fails closed. Unknown fields, an unsupported schema version, a critical-value kind other than `standard_normal_z`, or sidedness other than `two_sided` also fail deserialization rather than being silently reinterpreted. This exact recomputation contract is intentionally stronger than `ValidationReport`'s legacy endpoint-pair admission. The legacy report lacks enough provenance to reproduce the original interval and therefore uses necessary algebraic support checks; the versioned carrier has the missing denominator and critical value, so it can call the actual canonical producer instead of relying on a loose identity. +## Large-count binary64 boundary + +Durable integer provenance creates a numerical obligation that the in-memory interval API normally cannot reach in practice. Binary64 represents every integer only through `2^53`. At `n = 2^53 + 1` and `k = 2^53`, converting `k` and `n` independently to `f64` rounds both to `2^53`; the naive expression `(k as f64) / (n as f64)` therefore becomes exact `1.0` even though one admitted interval is uncovered. The same collapse also steers a count-based Wilson implementation onto an all-covered numerical path and materially changes the representable lower endpoint. + +The corrected count authority preserves the smaller side of the binomial partition. For `k > n-k`, represented empirical coverage is formed as `1 - (n-k)/n`, and Wilson endpoints are evaluated on the uncovered proportion and reflected by the score interval's complement symmetry. At `n = 9,007,199,254,740,993`, `k = 9,007,199,254,740,992`, and `z = 1.96`, the durable contract records represented coverage `0x3fefffffffffffff` (`0.9999999999999999`) and Wilson lower endpoint `0x3feffffffffffffa` (`0.9999999999999993`), rather than falsely treating the count state as all-covered. The upper endpoint rounds to `1.0`, which is representable and does not erase the retained uncovered count because the integer provenance remains authoritative. + +The v1 JSON count fields are `u64`, not `usize`. A durable schema must not change its numeric domain with the pointer width of the Rust process that reads it. The in-memory slice constructor safely widens its `usize` lengths/counts to `u64`; deserialization can therefore preserve the same versioned count artifact on 32-bit and 64-bit consumers even when the count could not be materialized as one process-resident slice. + ## RED → repair trace The first test draft `9fc96345f67ee0d6e6e8b62903b9994f13932a1d` contained a bad fixture and placeholder assertion and is not scientific evidence. Non-force correction `6f6e06d2446cc459cc29879c3e4bc34a2fff8e82` is the first valid RED for denominator/covered-count retention, standard-normal critical-value semantics, JSON round-trip, and fail-closed tampering. -The implementation lineage is: +The initial implementation lineage is: - `ca517ed3755a11b4574f8909acd6965273cf69e9`: factor interval hit counting and count-based Wilson recomputation into one crate-private numeric authority while preserving public interval APIs; - `31e1ab2bbaf9ce40cf74bed2110a310116e7a80a`: add `WilsonCoverageEvidenceV1` with validated construction, exact recomputation, schema-tagged manual serde, and fail-closed unknown-field behavior; @@ -38,6 +46,13 @@ The implementation lineage is: - export edit `4e2381f31a43d753cd8c37bb153aac701876c1ef` accidentally changed unrelated claim-authority wording and is not accepted repair evidence; non-force correction `57d8bf57687c5157c7c06c941a113071e22c5430` restores that wording while retaining only the intended module/export delta; - `6e5467428b9de851f075cb251f9dc38a4ec6a728`: update release-facing change documentation for the carrier/envelope contract. +The large-count repair lineage is: + +- `29d710a53d4988b46a37667b4ff03352d520b3c7`: public RED showing that a durable one-uncovered count state above `2^53` must decode to a non-all-covered represented coverage/Wilson artifact; +- `29968c807368fe3fe19bef3013af9d577d5f6025`: make empirical coverage complement-aware and make count-based Wilson evaluation reflect the smaller uncovered proportion near the all-covered boundary; +- `91d9a3bb54b47605377e5159763a55f3386efcf9`: make v1 durable counts fixed-width `u64` and route carrier construction/validation through the same count-preserving authority; +- `d908ce2a37685bdf915cc513be9f3f9dd36aae19`: add release-facing documentation for the count-precision and schema-portability repair. + Every later source or documentation commit on PR #488 invalidates predecessor exact-head workflow evidence; only the current surviving head's hosted gates and independent review count for landing. ## DDD and owner boundary @@ -56,4 +71,4 @@ Wilson, E. B. (1927). Probable inference, the law of succession, and statistical ## Verification contract -`crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs` covers the provenance carrier. `crates/validation_core/tests/validation_evidence_v1_coverage_provenance_contract.rs` covers the durable report/provenance envelope. Exact-head Rust tests, rustdoc/docstring checks, owned line/branch coverage, documentation validation, security/SAST/supply-chain checks, and qualifying independent review remain required before the Draft landing vehicle can be promoted or merged. +`crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs` covers the provenance carrier and `crates/validation_core/tests/wilson_coverage_count_precision_contract.rs` covers the large-count representation boundary. `crates/validation_core/tests/validation_evidence_v1_coverage_provenance_contract.rs` covers the durable report/provenance envelope. Exact-head Rust tests, rustdoc/docstring checks, owned line/branch coverage, documentation validation, security/SAST/supply-chain checks, and qualifying independent review remain required before the Draft landing vehicle can be promoted or merged. From 2809a5ccf42731198f32cbc27e8db09c15000ac8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:42:51 +0900 Subject: [PATCH 197/576] docs(research): ground count precision in current floating-point standards --- docs/research/wilson-coverage-evidence-provenance.md | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/docs/research/wilson-coverage-evidence-provenance.md b/docs/research/wilson-coverage-evidence-provenance.md index 8b8e072f6..f6ab7abc5 100644 --- a/docs/research/wilson-coverage-evidence-provenance.md +++ b/docs/research/wilson-coverage-evidence-provenance.md @@ -24,12 +24,14 @@ This exact recomputation contract is intentionally stronger than `ValidationRepo ## Large-count binary64 boundary -Durable integer provenance creates a numerical obligation that the in-memory interval API normally cannot reach in practice. Binary64 represents every integer only through `2^53`. At `n = 2^53 + 1` and `k = 2^53`, converting `k` and `n` independently to `f64` rounds both to `2^53`; the naive expression `(k as f64) / (n as f64)` therefore becomes exact `1.0` even though one admitted interval is uncovered. The same collapse also steers a count-based Wilson implementation onto an all-covered numerical path and materially changes the representable lower endpoint. +Durable integer provenance creates a numerical obligation that the in-memory interval API normally cannot reach in practice. Binary64 has 53 bits of significand precision, so every integer is exactly representable only through `2^53`. At `n = 2^53 + 1` and `k = 2^53`, converting `k` and `n` independently to binary64 rounds both to `2^53`; the naive expression `(k as f64) / (n as f64)` therefore becomes exact `1.0` even though one admitted interval is uncovered. The same collapse also steers a count-based Wilson implementation onto an all-covered numerical path and materially changes the representable lower endpoint. The corrected count authority preserves the smaller side of the binomial partition. For `k > n-k`, represented empirical coverage is formed as `1 - (n-k)/n`, and Wilson endpoints are evaluated on the uncovered proportion and reflected by the score interval's complement symmetry. At `n = 9,007,199,254,740,993`, `k = 9,007,199,254,740,992`, and `z = 1.96`, the durable contract records represented coverage `0x3fefffffffffffff` (`0.9999999999999999`) and Wilson lower endpoint `0x3feffffffffffffa` (`0.9999999999999993`), rather than falsely treating the count state as all-covered. The upper endpoint rounds to `1.0`, which is representable and does not erase the retained uncovered count because the integer provenance remains authoritative. The v1 JSON count fields are `u64`, not `usize`. A durable schema must not change its numeric domain with the pointer width of the Rust process that reads it. The in-memory slice constructor safely widens its `usize` lengths/counts to `u64`; deserialization can therefore preserve the same versioned count artifact on 32-bit and 64-bit consumers even when the count could not be materialized as one process-resident slice. +IEEE 754-2019 remains the published active IEEE floating-point standard, and IEEE/ISO/IEC 60559-2020 is its active international adoption. IEEE P754 is an active revision project intended to supersede IEEE 754-2019, not a published replacement. This repair therefore grounds binary64 representation claims in the published 2019/2020 standards and does not treat the active P754 project as current normative authority. + ## RED → repair trace The first test draft `9fc96345f67ee0d6e6e8b62903b9994f13932a1d` contained a bad fixture and placeholder assertion and is not scientific evidence. Non-force correction `6f6e06d2446cc459cc29879c3e4bc34a2fff8e82` is the first valid RED for denominator/covered-count retention, standard-normal critical-value semantics, JSON round-trip, and fail-closed tampering. @@ -67,6 +69,10 @@ Wilson's original score-interval paper remains the primary statistical source fo American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE. https://standards.ieee.org/ieee/754/6210/ + +International Organization for Standardization, & International Electrotechnical Commission. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). ISO. https://www.iso.org/standard/80985.html + Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 ## Verification contract From 63ddfdd615de23c50910ce54cff60fbb537a45d4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:49:29 +0900 Subject: [PATCH 198/576] test(validation): require correctly rounded durable coverage ratio --- ...wilson_coverage_ratio_rounding_contract.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) create mode 100644 crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs diff --git a/crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs b/crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs new file mode 100644 index 000000000..44105817b --- /dev/null +++ b/crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs @@ -0,0 +1,34 @@ +use validation_core::WilsonCoverageEvidenceV1; + +#[test] +fn durable_coverage_ratio_is_correctly_rounded_before_wilson_projection() { + // These integer counts are exact provenance. Their quotient lies one binary64 + // ULP below the result obtained by independently rounding both integers to + // f64 before division: + // k/n = 4_503_599_627_370_396 / 9_007_199_254_740_993 + // -> 0x1.fffffffffff37p-2 (round-to-nearest, ties-to-even) + // while `(k as f64) / (n as f64)` becomes 0x1.fffffffffff38p-2. + let json = r#"{ + "schema":"tepp.wilson_coverage_evidence.v1", + "sample_count":9007199254740993, + "covered_count":4503599627370396, + "critical_value_kind":"standard_normal_z", + "interval_sidedness":"two_sided", + "normal_critical_value":1.96, + "empirical_coverage":0.49999999999998884, + "wilson_lower":0.49999998967401105, + "wilson_upper":0.5000000103259667 + }"#; + + let evidence: WilsonCoverageEvidenceV1 = serde_json::from_str(json) + .expect("exact count provenance must determine the correctly rounded coverage ratio"); + + assert_eq!(evidence.empirical_coverage.to_bits(), 0x3fdf_ffff_ffff_ff37); + assert_eq!(evidence.wilson_lower.to_bits(), 0x3fdf_ffff_f4e9_9d20); + assert_eq!(evidence.wilson_upper.to_bits(), 0x3fe0_0000_058b_30a8); + + let round_trip = evidence.to_json().expect("validated durable evidence"); + let decoded: WilsonCoverageEvidenceV1 = + serde_json::from_str(&round_trip).expect("round-trip durable evidence"); + assert_eq!(decoded, evidence); +} From 11323c579b499dc136ec564f8b04ca91c5d7076b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:51:03 +0900 Subject: [PATCH 199/576] fix(validation): correctly round durable count ratios --- crates/validation_core/src/coverage.rs | 106 ++++++++++++++++++++----- 1 file changed, 88 insertions(+), 18 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index b90a12ae0..d29a2a01d 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -28,6 +28,51 @@ pub(crate) fn interval_covered_count( Ok(covered) } +fn correctly_rounded_unit_ratio(numerator: u64, denominator: u64) -> f64 { + debug_assert!(denominator > 0); + debug_assert!(numerator <= denominator); + if numerator == 0 { + return 0.0; + } + + // u64/u64 lies in [2^-64, 1], so its binary64 result is always normal. + // Determine floor(log2(numerator/denominator)) without first rounding either + // integer to f64, then round the exact scaled significand ties-to-even. + let numerator_bits = 64_i32 - numerator.leading_zeros() as i32; + let denominator_bits = 64_i32 - denominator.leading_zeros() as i32; + let mut exponent = numerator_bits - denominator_bits; + if exponent == 0 { + if numerator < denominator { + exponent = -1; + } + } else { + let exponent_shift = (-exponent) as u32; + if (numerator as u128) << exponent_shift < denominator as u128 { + exponent -= 1; + } + } + + let significand_shift = (52 - exponent) as u32; + let scaled_numerator = (numerator as u128) << significand_shift; + let denominator_u128 = denominator as u128; + let quotient = scaled_numerator / denominator_u128; + let remainder = scaled_numerator % denominator_u128; + let twice_remainder = remainder << 1; + let round_up = twice_remainder > denominator_u128 + || (twice_remainder == denominator_u128 && quotient & 1 == 1); + let mut significand = quotient + u128::from(round_up); + + if significand == 1_u128 << 53 { + significand >>= 1; + exponent += 1; + } + + debug_assert!((1_u128 << 52..1_u128 << 53).contains(&significand)); + let biased_exponent = (exponent + 1023) as u64; + let fraction = significand as u64 - (1_u64 << 52); + f64::from_bits((biased_exponent << 52) | fraction) +} + pub(crate) fn represented_coverage_from_counts( covered_count: u64, sample_count: u64, @@ -35,14 +80,7 @@ pub(crate) fn represented_coverage_from_counts( if sample_count == 0 || covered_count > sample_count { return Err(ValidationError::InvalidInput); } - let uncovered_count = sample_count - covered_count; - let n = sample_count as f64; - let coverage = if covered_count <= uncovered_count { - covered_count as f64 / n - } else { - 1.0 - uncovered_count as f64 / n - }; - Ok(coverage) + Ok(correctly_rounded_unit_ratio(covered_count, sample_count)) } /// Empirical coverage of closed intervals `[lower, upper]` for truth values. @@ -131,11 +169,11 @@ pub(crate) fn wilson_coverage_interval_from_counts( let uncovered_count = sample_count - covered_count; if covered_count > uncovered_count { - // Near all-covered samples can lose the observed misses if both integer - // counts are independently rounded to binary64 before division. Wilson - // intervals are complement-symmetric, so evaluate the smaller uncovered - // proportion and reflect its endpoints instead. - let uncovered = uncovered_count as f64 / n; + // Near all-covered samples can lose observed misses if both integer + // counts are independently rounded before division. Wilson intervals + // are complement-symmetric, so evaluate the correctly rounded smaller + // uncovered proportion and reflect its endpoints instead. + let uncovered = correctly_rounded_unit_ratio(uncovered_count, sample_count); let (uncovered_low, uncovered_high) = wilson_bounds_from_represented_proportion(n, uncovered, z, z2); return Ok(( @@ -144,7 +182,7 @@ pub(crate) fn wilson_coverage_interval_from_counts( )); } - let p = covered_count as f64 / n; + let p = correctly_rounded_unit_ratio(covered_count, sample_count); Ok(wilson_bounds_from_represented_proportion(n, p, z, z2)) } @@ -157,9 +195,9 @@ pub(crate) fn wilson_coverage_interval_from_counts( /// evaluated through the algebraically rationalized positive root rather than /// `center - margin`; the implementation switches scale at `z² = 1` so the /// stable form neither suffers large-z cancellation nor small-z division -/// overflow. Near the all-covered boundary, the smaller uncovered count is -/// evaluated and reflected by Wilson complement symmetry so distinct integer -/// counts are not erased when they exceed binary64's exact-integer range. +/// overflow. Count proportions are rounded to binary64 from their exact integer +/// ratio before Wilson evaluation. Near the all-covered boundary, the smaller +/// uncovered count is evaluated and reflected by Wilson complement symmetry. /// /// # Errors /// @@ -180,9 +218,41 @@ pub fn wilson_coverage_interval( #[cfg(test)] mod tests { - use super::{interval_coverage, wilson_coverage_interval}; + use super::{ + correctly_rounded_unit_ratio, interval_coverage, wilson_coverage_interval, + }; use crate::ValidationError; + #[test] + fn exact_integer_ratio_rounding_covers_binary64_boundaries() { + assert_eq!(correctly_rounded_unit_ratio(0, 3), 0.0); + assert_eq!(correctly_rounded_unit_ratio(1, 1), 1.0); + assert_eq!(correctly_rounded_unit_ratio(1, 2), 0.5); + assert_eq!(correctly_rounded_unit_ratio(1, 3), 1.0 / 3.0); + + // Halfway between 0.5 and its successor: lower significand is even. + assert_eq!( + correctly_rounded_unit_ratio((1_u64 << 53) + 1, 1_u64 << 54), + 0.5 + ); + // Just above that midpoint rounds upward. + assert_eq!( + correctly_rounded_unit_ratio((1_u64 << 54) + 3, 1_u64 << 55).to_bits(), + 0.5_f64.to_bits() + 1 + ); + // Just below the midpoint rounds downward. + assert_eq!( + correctly_rounded_unit_ratio((1_u64 << 54) + 1, 1_u64 << 55), + 0.5 + ); + // Halfway between predecessor(1.0) and 1.0: 1.0 has the even + // significand, so ties-to-even rounds upward and renormalizes. + assert_eq!( + correctly_rounded_unit_ratio((1_u64 << 54) - 1, 1_u64 << 54), + 1.0 + ); + } + #[test] fn coverage_and_wilson_bounds_are_oracle_correct() { let truth = [0.0, 1.0, 2.0, 3.0]; From 7cad2f45771eab2e3ac2f498e27b3665406fe739 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:51:42 +0900 Subject: [PATCH 200/576] docs(validation): record exact count-ratio rounding repair --- CHANGELOG.d/validation-wilson-ratio-rounding.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-wilson-ratio-rounding.md diff --git a/CHANGELOG.d/validation-wilson-ratio-rounding.md b/CHANGELOG.d/validation-wilson-ratio-rounding.md new file mode 100644 index 000000000..5daa05fb7 --- /dev/null +++ b/CHANGELOG.d/validation-wilson-ratio-rounding.md @@ -0,0 +1,3 @@ +### Fixed + +- `validation_core` now rounds durable covered/sample count ratios directly from their exact `u64` provenance instead of first rounding each integer to binary64. This removes one-ULP empirical-coverage errors above binary64's exact-integer range and feeds the Wilson score producer the correctly rounded represented proportion while retaining complement-symmetric evaluation near the all-covered boundary. From 27732dcb474407eee994a8a4ed9bbdb17d812343 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 15:52:34 +0900 Subject: [PATCH 201/576] docs(research): trace exact durable count-ratio rounding --- .../wilson-coverage-evidence-provenance.md | 19 +++++++++++++------ 1 file changed, 13 insertions(+), 6 deletions(-) diff --git a/docs/research/wilson-coverage-evidence-provenance.md b/docs/research/wilson-coverage-evidence-provenance.md index f6ab7abc5..599c82005 100644 --- a/docs/research/wilson-coverage-evidence-provenance.md +++ b/docs/research/wilson-coverage-evidence-provenance.md @@ -22,11 +22,15 @@ Artifact admission is exact for the represented binary64 contract. The carrier r This exact recomputation contract is intentionally stronger than `ValidationReport`'s legacy endpoint-pair admission. The legacy report lacks enough provenance to reproduce the original interval and therefore uses necessary algebraic support checks; the versioned carrier has the missing denominator and critical value, so it can call the actual canonical producer instead of relying on a loose identity. -## Large-count binary64 boundary +## Exact count-ratio representation Durable integer provenance creates a numerical obligation that the in-memory interval API normally cannot reach in practice. Binary64 has 53 bits of significand precision, so every integer is exactly representable only through `2^53`. At `n = 2^53 + 1` and `k = 2^53`, converting `k` and `n` independently to binary64 rounds both to `2^53`; the naive expression `(k as f64) / (n as f64)` therefore becomes exact `1.0` even though one admitted interval is uncovered. The same collapse also steers a count-based Wilson implementation onto an all-covered numerical path and materially changes the representable lower endpoint. -The corrected count authority preserves the smaller side of the binomial partition. For `k > n-k`, represented empirical coverage is formed as `1 - (n-k)/n`, and Wilson endpoints are evaluated on the uncovered proportion and reflected by the score interval's complement symmetry. At `n = 9,007,199,254,740,993`, `k = 9,007,199,254,740,992`, and `z = 1.96`, the durable contract records represented coverage `0x3fefffffffffffff` (`0.9999999999999999`) and Wilson lower endpoint `0x3feffffffffffffa` (`0.9999999999999993`), rather than falsely treating the count state as all-covered. The upper endpoint rounds to `1.0`, which is representable and does not erase the retained uncovered count because the integer provenance remains authoritative. +The first repair preserved the smaller side of the binomial partition for Wilson evaluation. Near the all-covered boundary, Wilson endpoints are evaluated on the uncovered proportion and reflected by score-interval complement symmetry, while exact integer equality `covered_count == sample_count` alone owns the all-covered branch. At `n = 9,007,199,254,740,993`, `k = 9,007,199,254,740,992`, and `z = 1.96`, the durable contract records represented coverage `0x3fefffffffffffff` (`0.9999999999999999`) and Wilson lower endpoint `0x3feffffffffffffa` (`0.9999999999999993`), rather than falsely treating the count state as all-covered. The upper endpoint rounds to `1.0`, which is representable and does not erase the retained uncovered count because the integer provenance remains authoritative. + +Complement selection alone does not make every `u64/u64` proportion correctly rounded. A second realistic count pair exposes the remaining defect: `n = 9,007,199,254,740,993` and `k = 4,503,599,627,370,396`. The exact rational `k/n` rounds to binary64 `0x1.fffffffffff37p-2` (`0.49999999999998884`), but independently converting both integers and then dividing yields `0x1.fffffffffff38p-2`, one ULP higher. That discrepancy is a projection error in durable empirical coverage even though neither count is near an exact 0/1 boundary. + +`correctly_rounded_unit_ratio` now derives the binary exponent from integer bit lengths and comparisons, forms the exact scaled significand in `u128`, and applies round-to-nearest, ties-to-even before constructing the binary64 bits. Every valid `u64/u64` unit ratio lies between `2^-64` and `1`, so its nonzero binary64 result is normal and the required scaled integer fits in `u128`; no bigint or Python validation path is needed. `represented_coverage_from_counts` uses this exact integer-ratio projection directly. `wilson_coverage_interval_from_counts` uses the same correctly rounded proportion and still evaluates the smaller uncovered side when complement symmetry avoids all-covered cancellation. The v1 JSON count fields are `u64`, not `usize`. A durable schema must not change its numeric domain with the pointer width of the Rust process that reads it. The in-memory slice constructor safely widens its `usize` lengths/counts to `u64`; deserialization can therefore preserve the same versioned count artifact on 32-bit and 64-bit consumers even when the count could not be materialized as one process-resident slice. @@ -51,15 +55,18 @@ The initial implementation lineage is: The large-count repair lineage is: - `29d710a53d4988b46a37667b4ff03352d520b3c7`: public RED showing that a durable one-uncovered count state above `2^53` must decode to a non-all-covered represented coverage/Wilson artifact; -- `29968c807368fe3fe19bef3013af9d577d5f6025`: make empirical coverage complement-aware and make count-based Wilson evaluation reflect the smaller uncovered proportion near the all-covered boundary; +- `29968c807368fe3fe19bef3013af9d577d5f6025`: make count-based Wilson evaluation reflect the smaller uncovered proportion near the all-covered boundary and stop using a rounded proportion to decide all-covered status; - `91d9a3bb54b47605377e5159763a55f3386efcf9`: make v1 durable counts fixed-width `u64` and route carrier construction/validation through the same count-preserving authority; -- `d908ce2a37685bdf915cc513be9f3f9dd36aae19`: add release-facing documentation for the count-precision and schema-portability repair. +- `d908ce2a37685bdf915cc513be9f3f9dd36aae19`: add release-facing documentation for the count-precision and schema-portability repair; +- `63ddfdd615de23c50910ce54cff60fbb537a45d4`: public RED showing a non-boundary `u64/u64` count ratio whose independent integer-to-binary64 conversions misround durable empirical coverage by one ULP; +- `11323c579b499dc136ec564f8b04ca91c5d7076b`: add exact `u128` ratio projection with ties-to-even coverage and route represented coverage/Wilson proportions through it; +- `7cad2f45771eab2e3ac2f498e27b3665406fe739`: add release-facing documentation for the exact count-ratio projection repair. Every later source or documentation commit on PR #488 invalidates predecessor exact-head workflow evidence; only the current surviving head's hosted gates and independent review count for landing. ## DDD and owner boundary -This is Validation Evidence provenance and projection policy. It does not define a new psychometric estimator, change the Wilson score estimand, move longitudinal/time-varying composition into `validation_core`, or copy mutable arithmetic from fast-mlsirm. The count-based helper is private to the existing TEPP coverage producer so there remains one numeric authority inside this bounded context. +This is Validation Evidence provenance and projection policy. It does not define a new psychometric estimator, change the Wilson score estimand, move longitudinal/time-varying composition into `validation_core`, or copy mutable arithmetic from fast-mlsirm. The count-based and exact-ratio helpers are private to the existing TEPP coverage producer so there remains one numeric authority inside this bounded context. The carrier and envelope do not involve semantic LLM execution. contextual-orchestrator remains the owner of model routing; no unreleased orchestrator source, direct provider route, or provider credential is introduced here. @@ -77,4 +84,4 @@ Wilson, E. B. (1927). Probable inference, the law of succession, and statistical ## Verification contract -`crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs` covers the provenance carrier and `crates/validation_core/tests/wilson_coverage_count_precision_contract.rs` covers the large-count representation boundary. `crates/validation_core/tests/validation_evidence_v1_coverage_provenance_contract.rs` covers the durable report/provenance envelope. Exact-head Rust tests, rustdoc/docstring checks, owned line/branch coverage, documentation validation, security/SAST/supply-chain checks, and qualifying independent review remain required before the Draft landing vehicle can be promoted or merged. +`crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs` covers the provenance carrier. `crates/validation_core/tests/wilson_coverage_count_precision_contract.rs` covers the all-covered-adjacent large-count boundary, and `crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs` covers non-boundary exact count-ratio rounding. `crates/validation_core/tests/validation_evidence_v1_coverage_provenance_contract.rs` covers the durable report/provenance envelope. Exact-head Rust tests, rustdoc/docstring checks, owned line/branch coverage, documentation validation, security/SAST/supply-chain checks, and qualifying independent review remain required before the Draft landing vehicle can be promoted or merged. From f89e246790a835ca1a520c8071401a1e4fd6892f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:02:42 +0900 Subject: [PATCH 202/576] test(validation): expose Wilson sample-count pre-rounding --- .../wilson_sample_count_rounding_contract.rs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 crates/validation_core/tests/wilson_sample_count_rounding_contract.rs diff --git a/crates/validation_core/tests/wilson_sample_count_rounding_contract.rs b/crates/validation_core/tests/wilson_sample_count_rounding_contract.rs new file mode 100644 index 000000000..770207acb --- /dev/null +++ b/crates/validation_core/tests/wilson_sample_count_rounding_contract.rs @@ -0,0 +1,31 @@ +use validation_core::WilsonCoverageEvidenceV1; + +#[test] +fn durable_wilson_endpoint_does_not_pre_round_exact_sample_count() { + // The durable carrier owns exact u64 counts. This denominator is not exactly + // representable in binary64: `n as f64` rounds 9_007_199_254_740_1013 down + // to 9_007_199_254_740_1012. The exact integer ratio k/n still rounds to the + // ordinary finite coverage value below, but pre-rounding n inside the Wilson + // formula moves the lower endpoint one ULP upward. + // + // Evaluating the same Wilson score formula at the exact integer n and the + // correctly rounded represented k/n gives lower = 0x1.2492482c43beap-3. + let json = r#"{ + "schema":"tepp.wilson_coverage_evidence.v1", + "sample_count":9007199254741013, + "covered_count":1286742750677287, + "critical_value_kind":"standard_normal_z", + "interval_sidedness":"two_sided", + "normal_critical_value":1.96, + "empirical_coverage":0.1428571428571428, + "wilson_lower":0.14285713563046382, + "wilson_upper":0.14285715008382208 + }"#; + + let evidence: WilsonCoverageEvidenceV1 = serde_json::from_str(json) + .expect("exact sample-count provenance must determine the Wilson endpoint"); + + assert_eq!(evidence.empirical_coverage.to_bits(), 0x3fc2_4924_9249_2490); + assert_eq!(evidence.wilson_lower.to_bits(), 0x3fc2_4924_82c4_3bea); + assert_eq!(evidence.wilson_upper.to_bits(), 0x3fc2_4924_a1ce_0d41); +} From 1a5180b22c755c793d3878e644192e7d7cbbff47 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:06:31 +0900 Subject: [PATCH 203/576] test(validation): cover large-count Wilson lower boundary --- .../wilson_sample_count_rounding_contract.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/crates/validation_core/tests/wilson_sample_count_rounding_contract.rs b/crates/validation_core/tests/wilson_sample_count_rounding_contract.rs index 770207acb..b4f635c0c 100644 --- a/crates/validation_core/tests/wilson_sample_count_rounding_contract.rs +++ b/crates/validation_core/tests/wilson_sample_count_rounding_contract.rs @@ -29,3 +29,29 @@ fn durable_wilson_endpoint_does_not_pre_round_exact_sample_count() { assert_eq!(evidence.wilson_lower.to_bits(), 0x3fc2_4924_82c4_3bea); assert_eq!(evidence.wilson_upper.to_bits(), 0x3fc2_4924_a1ce_0d41); } + +#[test] +fn durable_all_covered_lower_does_not_round_exact_sample_size_away() { + // At n = 2^55 + 3 the exact all-covered Wilson lower endpoint for z = 1.96 + // rounds to next_down(1.0). Materializing n as f64 first, or adding z^2/n + // to 1.0 before inversion, rounds the uncertainty away and returns exact 1. + // The complement form 1 - (z^2/n)/(1 + z^2/n) preserves the representable + // nonzero miss mass for this exact count provenance. + let json = r#"{ + "schema":"tepp.wilson_coverage_evidence.v1", + "sample_count":36028797018963971, + "covered_count":36028797018963971, + "critical_value_kind":"standard_normal_z", + "interval_sidedness":"two_sided", + "normal_critical_value":1.96, + "empirical_coverage":1.0, + "wilson_lower":0.9999999999999999, + "wilson_upper":1.0 + }"#; + + let evidence: WilsonCoverageEvidenceV1 = serde_json::from_str(json) + .expect("all-covered exact count provenance must retain Wilson uncertainty"); + + assert_eq!(evidence.wilson_lower.to_bits(), 1.0_f64.to_bits() - 1); + assert_eq!(evidence.wilson_upper, 1.0); +} From 6254c4989a5fb8922e88bff3d6e5d5b42b4f88e0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:07:12 +0900 Subject: [PATCH 204/576] test(validation): correct large-n Wilson projection oracle --- .../tests/wilson_coverage_ratio_rounding_contract.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs b/crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs index 44105817b..ec97604b2 100644 --- a/crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs +++ b/crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs @@ -8,6 +8,9 @@ fn durable_coverage_ratio_is_correctly_rounded_before_wilson_projection() { // k/n = 4_503_599_627_370_396 / 9_007_199_254_740_993 // -> 0x1.fffffffffff37p-2 (round-to-nearest, ties-to-even) // while `(k as f64) / (n as f64)` becomes 0x1.fffffffffff38p-2. + // The same exact denominator provenance also matters inside the Wilson + // projection: evaluating through exact 1/n moves the lower endpoint two ULPs + // below the predecessor fixture that first repaired only the coverage ratio. let json = r#"{ "schema":"tepp.wilson_coverage_evidence.v1", "sample_count":9007199254740993, @@ -16,15 +19,15 @@ fn durable_coverage_ratio_is_correctly_rounded_before_wilson_projection() { "interval_sidedness":"two_sided", "normal_critical_value":1.96, "empirical_coverage":0.49999999999998884, - "wilson_lower":0.49999998967401105, + "wilson_lower":0.49999998967401094, "wilson_upper":0.5000000103259667 }"#; let evidence: WilsonCoverageEvidenceV1 = serde_json::from_str(json) - .expect("exact count provenance must determine the correctly rounded coverage ratio"); + .expect("exact count provenance must determine coverage and Wilson projection"); assert_eq!(evidence.empirical_coverage.to_bits(), 0x3fdf_ffff_ffff_ff37); - assert_eq!(evidence.wilson_lower.to_bits(), 0x3fdf_ffff_f4e9_9d20); + assert_eq!(evidence.wilson_lower.to_bits(), 0x3fdf_ffff_f4e9_9d1e); assert_eq!(evidence.wilson_upper.to_bits(), 0x3fe0_0000_058b_30a8); let round_trip = evidence.to_json().expect("validated durable evidence"); From 73bbb5cf262f710d35b2fffa793076ae6a173947 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:08:10 +0900 Subject: [PATCH 205/576] fix(validation): preserve exact Wilson sample-count scale --- crates/validation_core/src/coverage.rs | 114 +++++++++++++++++++++++-- 1 file changed, 109 insertions(+), 5 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index d29a2a01d..b0b0cff04 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -73,6 +73,19 @@ fn correctly_rounded_unit_ratio(numerator: u64, denominator: u64) -> f64 { f64::from_bits((biased_exponent << 52) | fraction) } +fn u64_is_exact_binary64_integer(value: u64) -> bool { + if value == 0 { + return true; + } + let significant_bits = 64 - value.leading_zeros(); + if significant_bits <= 53 { + return true; + } + let discarded_bits = significant_bits - 53; + let discarded_mask = (1_u64 << discarded_bits) - 1; + value & discarded_mask == 0 +} + pub(crate) fn represented_coverage_from_counts( covered_count: u64, sample_count: u64, @@ -120,6 +133,24 @@ fn rationalized_wilson_positive_lower(n: f64, p: f64, z: f64, z2: f64) -> f64 { (numerator / denominator).clamp(0.0, 1.0) } +fn rationalized_wilson_positive_lower_from_inverse_sample_count( + inverse_n: f64, + p: f64, + z: f64, + z2: f64, +) -> f64 { + // This path is used only when the exact u64 denominator cannot be represented + // as binary64. Such counts exceed 2^53, so inverse_n is small enough that + // the natural reciprocal-scale form remains finite even for the largest z + // whose square is finite. It avoids materializing a rounded sample count. + let numerator = 2.0 * p * p; + let inverse_n_squared = inverse_n * inverse_n; + let denominator = z2 * inverse_n + + 2.0 * p + + z * (z2 * inverse_n_squared + 4.0 * p * (1.0 - p) * inverse_n).sqrt(); + (numerator / denominator).clamp(0.0, 1.0) +} + fn wilson_bounds_from_represented_proportion( n: f64, p: f64, @@ -146,6 +177,52 @@ fn wilson_bounds_from_represented_proportion( (low, high) } +fn wilson_bounds_from_represented_proportion_and_inverse_sample_count( + inverse_n: f64, + p: f64, + z: f64, + z2: f64, +) -> (f64, f64) { + let low = if p > 0.0 { + rationalized_wilson_positive_lower_from_inverse_sample_count(inverse_n, p, z, z2) + } else { + 0.0 + }; + + let z2_over_n = z2 * inverse_n; + let denominator = 1.0 + z2_over_n; + let center = p + z2_over_n / 2.0; + let radical = p * (1.0 - p) * inverse_n + z2 * inverse_n * inverse_n / 4.0; + let margin = z * radical.sqrt(); + let direct_high = ((center + margin) / denominator).clamp(0.0, 1.0); + let high = if direct_high == 1.0 && p < 1.0 && z2 > 0.0 { + let uncovered_lower = rationalized_wilson_positive_lower_from_inverse_sample_count( + inverse_n, + 1.0 - p, + z, + z2, + ); + (1.0 - uncovered_lower).clamp(0.0, 1.0) + } else { + direct_high + }; + (low, high) +} + +fn wilson_bounds_for_sample_count( + n: f64, + inverse_n: Option, + p: f64, + z: f64, + z2: f64, +) -> (f64, f64) { + if let Some(inverse_n) = inverse_n { + wilson_bounds_from_represented_proportion_and_inverse_sample_count(inverse_n, p, z, z2) + } else { + wilson_bounds_from_represented_proportion(n, p, z, z2) + } +} + pub(crate) fn wilson_coverage_interval_from_counts( covered_count: u64, sample_count: u64, @@ -163,7 +240,18 @@ pub(crate) fn wilson_coverage_interval_from_counts( if !z2.is_finite() { return Err(ValidationError::InvalidConfiguration); } + let inverse_n = if u64_is_exact_binary64_integer(sample_count) { + None + } else { + Some(correctly_rounded_unit_ratio(1, sample_count)) + }; + if covered_count == sample_count { + if let Some(inverse_n) = inverse_n { + let z2_over_n = z2 * inverse_n; + let uncovered_mass = z2_over_n / (1.0 + z2_over_n); + return Ok(((1.0 - uncovered_mass).clamp(0.0, 1.0), 1.0)); + } return Ok((n / (n + z2), 1.0)); } @@ -175,7 +263,7 @@ pub(crate) fn wilson_coverage_interval_from_counts( // uncovered proportion and reflect its endpoints instead. let uncovered = correctly_rounded_unit_ratio(uncovered_count, sample_count); let (uncovered_low, uncovered_high) = - wilson_bounds_from_represented_proportion(n, uncovered, z, z2); + wilson_bounds_for_sample_count(n, inverse_n, uncovered, z, z2); return Ok(( (1.0 - uncovered_high).clamp(0.0, 1.0), (1.0 - uncovered_low).clamp(0.0, 1.0), @@ -183,7 +271,7 @@ pub(crate) fn wilson_coverage_interval_from_counts( } let p = correctly_rounded_unit_ratio(covered_count, sample_count); - Ok(wilson_bounds_from_represented_proportion(n, p, z, z2)) + Ok(wilson_bounds_for_sample_count(n, inverse_n, p, z, z2)) } /// Wilson score lower/upper bounds for a binomial coverage proportion. @@ -196,8 +284,13 @@ pub(crate) fn wilson_coverage_interval_from_counts( /// `center - margin`; the implementation switches scale at `z² = 1` so the /// stable form neither suffers large-z cancellation nor small-z division /// overflow. Count proportions are rounded to binary64 from their exact integer -/// ratio before Wilson evaluation. Near the all-covered boundary, the smaller -/// uncovered count is evaluated and reflected by Wilson complement symmetry. +/// ratio before Wilson evaluation. When the exact sample count itself is not +/// binary64-representable, Wilson scale terms are evaluated through the +/// correctly rounded reciprocal `1 / n` rather than a pre-rounded `n as f64`; +/// the all-covered branch uses the complementary miss mass so representable +/// uncertainty immediately below one is not erased. Near the all-covered +/// boundary, the smaller uncovered count is evaluated and reflected by Wilson +/// complement symmetry. /// /// # Errors /// @@ -219,7 +312,8 @@ pub fn wilson_coverage_interval( #[cfg(test)] mod tests { use super::{ - correctly_rounded_unit_ratio, interval_coverage, wilson_coverage_interval, + correctly_rounded_unit_ratio, interval_coverage, u64_is_exact_binary64_integer, + wilson_coverage_interval, }; use crate::ValidationError; @@ -253,6 +347,16 @@ mod tests { ); } + #[test] + fn sample_count_exactness_detection_matches_binary64_integer_spacing() { + assert!(u64_is_exact_binary64_integer(0)); + assert!(u64_is_exact_binary64_integer(1_u64 << 53)); + assert!(!u64_is_exact_binary64_integer((1_u64 << 53) + 1)); + assert!(u64_is_exact_binary64_integer((1_u64 << 53) + 2)); + assert!(!u64_is_exact_binary64_integer((1_u64 << 55) + 3)); + assert!(u64_is_exact_binary64_integer((1_u64 << 55) + 8)); + } + #[test] fn coverage_and_wilson_bounds_are_oracle_correct() { let truth = [0.0, 1.0, 2.0, 3.0]; From f2b5768b1632d647959c72a1799160bf16cab5c6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:10:43 +0900 Subject: [PATCH 206/576] docs(validation): record Wilson sample-count scale repair --- CHANGELOG.d/validation-wilson-sample-count-scale.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 CHANGELOG.d/validation-wilson-sample-count-scale.md diff --git a/CHANGELOG.d/validation-wilson-sample-count-scale.md b/CHANGELOG.d/validation-wilson-sample-count-scale.md new file mode 100644 index 000000000..c1483b6ce --- /dev/null +++ b/CHANGELOG.d/validation-wilson-sample-count-scale.md @@ -0,0 +1,6 @@ +# Validation: preserve exact Wilson sample-count scale + +- `WilsonCoverageEvidenceV1` no longer pre-rounds a fixed-width `u64` sample count before Wilson score projection when the count is not exactly representable in binary64. +- The inexact-large-count path evaluates Wilson scale terms through the correctly rounded reciprocal `1 / n`; exact binary64-representable counts keep the existing path. +- The all-covered large-count branch evaluates the complementary Wilson miss mass before subtracting from one, so representable uncertainty immediately below `1.0` is not erased. +- The prior non-boundary large-count fixture is corrected because its Wilson lower endpoint had inherited the rounded-denominator calculation even after empirical `covered_count / sample_count` was fixed. From 7fc78a59206d36cca32a9d45e023d109b628b114 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:11:13 +0900 Subject: [PATCH 207/576] docs(research): trace Wilson sample-count scale repair --- docs/research/wilson-sample-count-scale.md | 51 ++++++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 docs/research/wilson-sample-count-scale.md diff --git a/docs/research/wilson-sample-count-scale.md b/docs/research/wilson-sample-count-scale.md new file mode 100644 index 000000000..b7576ffa6 --- /dev/null +++ b/docs/research/wilson-sample-count-scale.md @@ -0,0 +1,51 @@ +# Wilson sample-count scale in durable coverage evidence + +## Problem + +`WilsonCoverageEvidenceV1` stores `sample_count` and `covered_count` as fixed-width `u64` provenance and validates its projected Wilson score interval by recomputation. The preceding large-count repairs made `covered_count / sample_count` correctly rounded and stopped one uncovered observation above `2^53` from collapsing into an all-covered state. One denominator path nevertheless remained lossy: `wilson_coverage_interval_from_counts` converted the exact `sample_count` to binary64 before using it in the Wilson scale terms. + +That conversion is harmless only when the integer count itself is exactly representable. For `sample_count = 9_007_199_254_740_1013`, `sample_count as f64` is a neighboring even binary64 integer rather than the retained denominator. With `covered_count = 1_286_742_750_677_287` and `z = 1.96`, the exact integer ratio still projects to ordinary finite coverage `0.1428571428571428`, but using the rounded denominator moves the Wilson lower endpoint one binary64 ULP upward. The represented endpoint obtained from the exact denominator is `0x1.2492482c43beap-3` (`0.14285713563046382`). + +The same loss is buyer-visible at a boundary. For all-covered evidence with `sample_count = 2^55 + 3 = 36_028_797_018_963_971` and `z = 1.96`, the exact Wilson lower root `n / (n + z²)` rounds to `next_down(1.0)`. Materializing the count as `f64` first can make the endpoint exact `1.0` and therefore erase representable finite-sample uncertainty even though the durable carrier still owns the exact denominator. + +## Repair + +The canonical count-based Wilson authority now distinguishes whether the fixed-width denominator is exactly representable in binary64. Exact counts keep the established `n: f64` path so existing small-count and extreme-`z` contracts retain their evaluated arithmetic. Counts that are not exactly representable use `correctly_rounded_unit_ratio(1, sample_count)` and evaluate the same Wilson score algebra through the reciprocal scale `1 / n`, avoiding a pre-rounded integer denominator. + +For a positive strict-interior represented proportion `p`, the rationalized lower root is evaluated as + +`2 p² / [z²/n + 2p + z sqrt(z²/n² + 4p(1-p)/n)]`. + +The upper root uses the same reciprocal scale in its denominator, center, and radical. Near all-covered evidence still uses score-interval complement symmetry with the smaller uncovered proportion. For exact all-covered evidence on the inexact-denominator path, the implementation evaluates the complementary miss mass `(z²/n) / (1 + z²/n)` and subtracts that from one; this avoids losing a representable `next_down(1.0)` endpoint when `1 + z²/n` itself rounds to one. + +This is not a new confidence-interval estimand and does not introduce arbitrary higher-precision output. The durable contract already retained exact integer provenance. The repair prevents an avoidable binary64 pre-rounding of that provenance before the existing Wilson score projection. Public results remain binary64 and deterministic. + +## RED → repair trace + +- `f89e246790a835ca1a520c8071401a1e4fd6892f`: public RED for the strict-interior denominator case, fixing exact `u64` counts and the one-ULP lower-endpoint oracle. +- `1a5180b22c755c793d3878e644192e7d7cbbff47`: strengthen the RED with the exact all-covered `2^55 + 3` boundary whose finite-sample lower endpoint must remain `next_down(1.0)`. +- `6254c4989a5fb8922e88bff3d6e5d5b42b4f88e0`: correct the predecessor non-boundary large-count fixture; its empirical count-ratio oracle was already exact, but its Wilson lower endpoint still encoded rounded-denominator arithmetic. +- `73bbb5cf262f710d35b2fffa793076ae6a173947`: causal production repair using an exact-integer representability check and reciprocal-scale Wilson evaluation only when the retained denominator cannot be represented exactly. +- `f2b5768b1632d647959c72a1799160bf16cab5c6`: release-facing CHANGELOG fragment. + +Every later source or documentation push invalidates predecessor exact-head workflow/review evidence. Landing still requires current-head hosted Rust, owned line/branch coverage, documentation, security/SAST/supply-chain checks, resolved review threads, and a qualifying independent approval. + +## Bounded-context ownership + +This change belongs to TEPP Validation Evidence. `WilsonCoverageEvidenceV1` is a durable provenance/projection carrier around the Wilson coverage producer and its exact fixed-width counts; the issue is not reusable static psychometric estimation. No fast-mlsirm source is copied, no Longitudinal Modeling semantics move into `validation_core`, and no contextual-orchestrator behavior is involved. + +## Standards and primary source + +Wilson's original paper remains the primary source for the score-interval family. Binary64 representation and round-to-nearest behavior are grounded in the currently published IEEE 754-2019 and its ISO/IEC 60559:2020 adoption. IEEE P754 is an active revision project rather than a published replacement. The current published AERA/APA/NCME testing standards remain the 2014 edition while the Joint Committee proceeds with revision. + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE. https://standards.ieee.org/ieee/754/6210/ + +International Organization for Standardization, & International Electrotechnical Commission. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). ISO. https://www.iso.org/standard/80985.html + +Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 + +## Verification contract + +`crates/validation_core/tests/wilson_sample_count_rounding_contract.rs` fixes both the strict-interior and exact all-covered large-denominator oracles. `crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs` protects the corrected earlier ratio case. Private unit coverage verifies binary64 integer-representability classification across spacing changes. Existing small-denominator, extreme-`z`, complement, endpoint-cancellation, carrier-serde, and durable-envelope contracts remain required and are not replaced by these tests. From 059ce70d3dd497f486214137bca6a1f1b2e8b3cd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:29:25 +0900 Subject: [PATCH 208/576] test(validation): expose extreme-z all-covered cancellation --- ...overed_inexact_count_extreme_z_contract.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs diff --git a/crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs b/crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs new file mode 100644 index 000000000..7ea6d0e52 --- /dev/null +++ b/crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs @@ -0,0 +1,28 @@ +use validation_core::WilsonCoverageEvidenceV1; + +#[test] +fn inexact_durable_count_preserves_positive_all_covered_lower_at_extreme_z() { + // `sample_count = 2^53 + 1` is exact durable u64 provenance but is not + // representable as binary64. With z = 1e20, the canonical all-covered + // Wilson lower endpoint is n / (n + z^2) = 0x1.16c262777579dp-80. + // Computing the complementary miss mass first rounds it to 1.0 and then + // `1.0 - miss_mass` erases this ordinary representable positive endpoint. + let json = r#"{ + "schema":"tepp.wilson_coverage_evidence.v1", + "sample_count":9007199254740993, + "covered_count":9007199254740993, + "critical_value_kind":"standard_normal_z", + "interval_sidedness":"two_sided", + "normal_critical_value":1e20, + "empirical_coverage":1.0, + "wilson_lower":9.007199254740993e-25, + "wilson_upper":1.0 + }"#; + + let evidence: WilsonCoverageEvidenceV1 = serde_json::from_str(json) + .expect("exact durable sample count must preserve representable Wilson uncertainty"); + + assert_eq!(evidence.wilson_lower.to_bits(), 0x3af1_6c26_2777_579d); + assert!(evidence.wilson_lower > 0.0); + assert_eq!(evidence.wilson_upper, 1.0); +} From 46f91f3f3723e8b400dbf3f7e8adaf0422e2c4ee Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:31:54 +0900 Subject: [PATCH 209/576] fix(validation): preserve extreme-z all-covered lower endpoint --- .../validation_core/src/coverage_evidence.rs | 61 ++++++++++++++++--- 1 file changed, 54 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/src/coverage_evidence.rs b/crates/validation_core/src/coverage_evidence.rs index c4c1c785f..b4a81dfad 100644 --- a/crates/validation_core/src/coverage_evidence.rs +++ b/crates/validation_core/src/coverage_evidence.rs @@ -10,6 +10,37 @@ const SCHEMA: &str = "tepp.wilson_coverage_evidence.v1"; const CRITICAL_VALUE_KIND: &str = "standard_normal_z"; const INTERVAL_SIDEDNESS: &str = "two_sided"; +fn u64_is_exact_binary64_integer(value: u64) -> bool { + if value == 0 { + return true; + } + let significant_bits = 64 - value.leading_zeros(); + if significant_bits <= 53 { + return true; + } + let discarded_bits = significant_bits - 53; + let discarded_mask = (1_u64 << discarded_bits) - 1; + value & discarded_mask == 0 +} + +fn all_covered_lower_from_exact_count( + sample_count: u64, + normal_critical_value: f64, +) -> Result { + let z2 = normal_critical_value * normal_critical_value; + if !z2.is_finite() { + return Err(ValidationError::InvalidInput); + } + let inverse_n = represented_coverage_from_counts(1, sample_count)?; + let z2_over_n = z2 * inverse_n; + if z2_over_n <= 1.0 { + let uncovered_mass = z2_over_n / (1.0 + z2_over_n); + Ok((1.0 - uncovered_mass).clamp(0.0, 1.0)) + } else { + Ok((1.0 / (1.0 + z2_over_n)).clamp(0.0, 1.0)) + } +} + /// Durable Wilson interval-coverage evidence with denominator and critical-value provenance. /// /// The carrier stores fixed-width retained-sample and covered counts rather than only the projected @@ -19,7 +50,11 @@ const INTERVAL_SIDEDNESS: &str = "two_sided"; /// or a one-sided confidence claim. Validation recomputes represented empirical coverage and both /// Wilson endpoints from the stored counts and `z`; tampered or internally inconsistent artifacts /// fail closed. Near all-covered samples are evaluated from the smaller uncovered count so a real -/// miss is not erased when integer counts exceed binary64's exact-integer range. +/// miss is not erased when integer counts exceed binary64's exact-integer range. For exact +/// all-covered durable counts that are not themselves binary64-representable, validation switches +/// between complementary-miss and direct reciprocal forms according to `z² / n`: the former keeps +/// tiny uncertainty below one, while the latter prevents a large miss mass rounding to exactly one +/// and erasing an ordinary positive lower endpoint. #[derive(Clone, Copy, Debug, PartialEq)] pub struct WilsonCoverageEvidenceV1 { /// Number of interval/truth triples admitted to the empirical coverage calculation. @@ -102,12 +137,24 @@ impl WilsonCoverageEvidenceV1 { let expected_coverage = represented_coverage_from_counts(self.covered_count, self.sample_count)?; - let (expected_lower, expected_upper) = wilson_coverage_interval_from_counts( - self.covered_count, - self.sample_count, - self.normal_critical_value, - ) - .map_err(|_| ValidationError::InvalidInput)?; + let (expected_lower, expected_upper) = if self.covered_count == self.sample_count + && !u64_is_exact_binary64_integer(self.sample_count) + { + ( + all_covered_lower_from_exact_count( + self.sample_count, + self.normal_critical_value, + )?, + 1.0, + ) + } else { + wilson_coverage_interval_from_counts( + self.covered_count, + self.sample_count, + self.normal_critical_value, + ) + .map_err(|_| ValidationError::InvalidInput)? + }; if self.empirical_coverage != expected_coverage || self.wilson_lower != expected_lower From 0f4783929b8d067eecb91696e1ad5761cd315b1e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:33:30 +0900 Subject: [PATCH 210/576] fix(validation): stabilize all-covered exact-count scaling --- crates/validation_core/src/coverage.rs | 30 ++++++++++++++++++++------ 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index b0b0cff04..b71d66ff8 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -151,6 +151,20 @@ fn rationalized_wilson_positive_lower_from_inverse_sample_count( (numerator / denominator).clamp(0.0, 1.0) } +fn all_covered_wilson_lower_from_inverse_sample_count(inverse_n: f64, z2: f64) -> f64 { + let z2_over_n = z2 * inverse_n; + if z2_over_n <= 1.0 { + // For small z²/n, 1 / (1 + z²/n) can round to exact one before the + // finite-sample miss mass is represented. Subtract the miss mass instead. + let uncovered_mass = z2_over_n / (1.0 + z2_over_n); + (1.0 - uncovered_mass).clamp(0.0, 1.0) + } else { + // For large z²/n, the complementary miss mass rounds to exact one and + // subtraction would erase an ordinary positive lower endpoint. + (1.0 / (1.0 + z2_over_n)).clamp(0.0, 1.0) + } +} + fn wilson_bounds_from_represented_proportion( n: f64, p: f64, @@ -248,9 +262,10 @@ pub(crate) fn wilson_coverage_interval_from_counts( if covered_count == sample_count { if let Some(inverse_n) = inverse_n { - let z2_over_n = z2 * inverse_n; - let uncovered_mass = z2_over_n / (1.0 + z2_over_n); - return Ok(((1.0 - uncovered_mass).clamp(0.0, 1.0), 1.0)); + return Ok(( + all_covered_wilson_lower_from_inverse_sample_count(inverse_n, z2), + 1.0, + )); } return Ok((n / (n + z2), 1.0)); } @@ -287,10 +302,11 @@ pub(crate) fn wilson_coverage_interval_from_counts( /// ratio before Wilson evaluation. When the exact sample count itself is not /// binary64-representable, Wilson scale terms are evaluated through the /// correctly rounded reciprocal `1 / n` rather than a pre-rounded `n as f64`; -/// the all-covered branch uses the complementary miss mass so representable -/// uncertainty immediately below one is not erased. Near the all-covered -/// boundary, the smaller uncovered count is evaluated and reflected by Wilson -/// complement symmetry. +/// the all-covered branch switches between complementary-miss and direct +/// reciprocal forms at `z² / n = 1`, preserving both tiny uncertainty below one +/// and positive lower endpoints under extreme finite critical values. Near the +/// all-covered boundary, the smaller uncovered count is evaluated and reflected +/// by Wilson complement symmetry. /// /// # Errors /// From 8e2058f2fc6ab42af8f732f03e7ae1dcee3e873d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:33:56 +0900 Subject: [PATCH 211/576] refactor(validation): keep Wilson scaling in canonical producer --- .../validation_core/src/coverage_evidence.rs | 61 +++---------------- 1 file changed, 7 insertions(+), 54 deletions(-) diff --git a/crates/validation_core/src/coverage_evidence.rs b/crates/validation_core/src/coverage_evidence.rs index b4a81dfad..c4c1c785f 100644 --- a/crates/validation_core/src/coverage_evidence.rs +++ b/crates/validation_core/src/coverage_evidence.rs @@ -10,37 +10,6 @@ const SCHEMA: &str = "tepp.wilson_coverage_evidence.v1"; const CRITICAL_VALUE_KIND: &str = "standard_normal_z"; const INTERVAL_SIDEDNESS: &str = "two_sided"; -fn u64_is_exact_binary64_integer(value: u64) -> bool { - if value == 0 { - return true; - } - let significant_bits = 64 - value.leading_zeros(); - if significant_bits <= 53 { - return true; - } - let discarded_bits = significant_bits - 53; - let discarded_mask = (1_u64 << discarded_bits) - 1; - value & discarded_mask == 0 -} - -fn all_covered_lower_from_exact_count( - sample_count: u64, - normal_critical_value: f64, -) -> Result { - let z2 = normal_critical_value * normal_critical_value; - if !z2.is_finite() { - return Err(ValidationError::InvalidInput); - } - let inverse_n = represented_coverage_from_counts(1, sample_count)?; - let z2_over_n = z2 * inverse_n; - if z2_over_n <= 1.0 { - let uncovered_mass = z2_over_n / (1.0 + z2_over_n); - Ok((1.0 - uncovered_mass).clamp(0.0, 1.0)) - } else { - Ok((1.0 / (1.0 + z2_over_n)).clamp(0.0, 1.0)) - } -} - /// Durable Wilson interval-coverage evidence with denominator and critical-value provenance. /// /// The carrier stores fixed-width retained-sample and covered counts rather than only the projected @@ -50,11 +19,7 @@ fn all_covered_lower_from_exact_count( /// or a one-sided confidence claim. Validation recomputes represented empirical coverage and both /// Wilson endpoints from the stored counts and `z`; tampered or internally inconsistent artifacts /// fail closed. Near all-covered samples are evaluated from the smaller uncovered count so a real -/// miss is not erased when integer counts exceed binary64's exact-integer range. For exact -/// all-covered durable counts that are not themselves binary64-representable, validation switches -/// between complementary-miss and direct reciprocal forms according to `z² / n`: the former keeps -/// tiny uncertainty below one, while the latter prevents a large miss mass rounding to exactly one -/// and erasing an ordinary positive lower endpoint. +/// miss is not erased when integer counts exceed binary64's exact-integer range. #[derive(Clone, Copy, Debug, PartialEq)] pub struct WilsonCoverageEvidenceV1 { /// Number of interval/truth triples admitted to the empirical coverage calculation. @@ -137,24 +102,12 @@ impl WilsonCoverageEvidenceV1 { let expected_coverage = represented_coverage_from_counts(self.covered_count, self.sample_count)?; - let (expected_lower, expected_upper) = if self.covered_count == self.sample_count - && !u64_is_exact_binary64_integer(self.sample_count) - { - ( - all_covered_lower_from_exact_count( - self.sample_count, - self.normal_critical_value, - )?, - 1.0, - ) - } else { - wilson_coverage_interval_from_counts( - self.covered_count, - self.sample_count, - self.normal_critical_value, - ) - .map_err(|_| ValidationError::InvalidInput)? - }; + let (expected_lower, expected_upper) = wilson_coverage_interval_from_counts( + self.covered_count, + self.sample_count, + self.normal_critical_value, + ) + .map_err(|_| ValidationError::InvalidInput)?; if self.empirical_coverage != expected_coverage || self.wilson_lower != expected_lower From 8a73cb64d15e5412e5afabbae80603a66c12a7f0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:35:02 +0900 Subject: [PATCH 212/576] docs(changelog): record all-covered Wilson scale switch --- CHANGELOG.d/validation-wilson-all-covered-extreme-z-scale.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-wilson-all-covered-extreme-z-scale.md diff --git a/CHANGELOG.d/validation-wilson-all-covered-extreme-z-scale.md b/CHANGELOG.d/validation-wilson-all-covered-extreme-z-scale.md new file mode 100644 index 000000000..c4ef0d981 --- /dev/null +++ b/CHANGELOG.d/validation-wilson-all-covered-extreme-z-scale.md @@ -0,0 +1,3 @@ +### Fixed + +- Preserved the positive all-covered Wilson lower endpoint for durable `u64` sample counts that are not exactly representable in binary64 when a large but finite standard-normal critical value makes `z² / n > 1`. The canonical count-based producer now uses the complementary-miss form only on the small-`z² / n` side and the algebraically equivalent direct reciprocal form on the large side, avoiding both false exact `1.0` and false exact `0.0` endpoints. From ebe6b660486e3f7a527849c9e3ad895e6e0fb011 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:35:28 +0900 Subject: [PATCH 213/576] docs(research): trace extreme-z Wilson count scaling --- ...son-all-covered-inexact-count-extreme-z.md | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 docs/research/wilson-all-covered-inexact-count-extreme-z.md diff --git a/docs/research/wilson-all-covered-inexact-count-extreme-z.md b/docs/research/wilson-all-covered-inexact-count-extreme-z.md new file mode 100644 index 000000000..f7558b892 --- /dev/null +++ b/docs/research/wilson-all-covered-inexact-count-extreme-z.md @@ -0,0 +1,58 @@ +# Wilson all-covered exact-count scaling under extreme finite critical values + +## Problem + +`WilsonCoverageEvidenceV1` retains `sample_count` and `covered_count` as exact `u64` provenance and recomputes the canonical Wilson score endpoints from those counts. The reciprocal-scale path introduced for sample counts above binary64's exact-integer range correctly avoids pre-rounding the durable denominator. Its all-covered branch, however, always evaluated the lower endpoint through the complementary miss mass + +`1 - (z² / n) / (1 + z² / n)`. + +That algebraic form is stable when `z² / n` is small because it keeps a finite miss mass that would otherwise disappear when `1 + z² / n` rounds to `1.0`. It is not stable when `z² / n` is large: the miss fraction itself rounds to exact `1.0`, so subtracting it produces a false exact-zero lower endpoint even when the Wilson lower endpoint is an ordinary positive binary64 value. + +## Public RED + +Commit `059ce70d3dd497f486214137bca6a1f1b2e8b3cd` adds `crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs`. + +The contract fixes: + +- `sample_count = covered_count = 2^53 + 1 = 9_007_199_254_740_993`; +- `z = 1e20`, so `z²` is finite; +- empirical coverage `1.0`; +- canonical Wilson lower endpoint `n / (n + z²) = 0x1.16c262777579dp-80`, decimal `9.007199254740993e-25`. + +The predecessor reciprocal path computes `z² / n`, then `(z² / n)/(1 + z² / n)`. At this scale the latter rounds to exact `1.0`; subtracting from one therefore returns `0.0` and destroys representable finite-sample uncertainty. + +## Causal repair + +Commit `0f4783929b8d067eecb91696e1ad5761cd315b1e` keeps the Wilson estimand and the exact-count reciprocal path. It changes only the algebraic projection used for the all-covered lower endpoint when the durable `u64` denominator is not exactly representable as binary64: + +- if `z² / n <= 1`, evaluate `1 - (z² / n)/(1 + z² / n)` so tiny uncertainty below one survives; +- if `z² / n > 1`, evaluate `1/(1 + z² / n)` so a large complementary miss mass cannot round to one before subtraction. + +Both expressions are algebraically identical to `n/(n+z²)`. The branch is numerical conditioning, not an estimator change or a new confidence-interval policy. Commit `8e2058f2fc6ab42af8f732f03e7ae1dcee3e873d` removes a transient duplicated implementation from `coverage_evidence.rs` so the Wilson projection remains single-writer in the canonical `coverage.rs` producer. + +## Boundary and owner decision + +The existing `n = 2^55 + 3, z = 1.96` durable all-covered contract exercises the small-`z²/n` side and still requires `next_down(1.0)`. The new extreme-`z` contract exercises the large side and requires a positive endpoint rather than false exact zero. The transition at `z²/n = 1` changes only which algebraically equivalent expression is numerically conditioned; it does not alter interval sidedness, critical-value semantics, retained counts, or the Wilson score estimand. + +This remains TEPP Validation Evidence provenance/projection behavior. No reusable static psychometric estimator is added, so there is no fast-mlsirm migration. No LLM is involved, and no contextual-orchestrator dependency is introduced. + +## Traceability + +- Public RED: `059ce70d3dd497f486214137bca6a1f1b2e8b3cd` +- Canonical producer repair: `0f4783929b8d067eecb91696e1ad5761cd315b1e` +- Single-writer cleanup: `8e2058f2fc6ab42af8f732f03e7ae1dcee3e873d` +- Changelog: `8a73cb64d15e5412e5afabbae80603a66c12a7f0` +- Module: `crates/validation_core/src/coverage.rs` +- Durable carrier: `crates/validation_core/src/coverage_evidence.rs` +- Public contract: `crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs` +- Landing vehicle: PR #488 + +## References + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 + +IEEE P754 is an active revision project superseding IEEE 754-2019; it is not treated here as a published replacement standard. From 93d5d2089db1fcf4c6167930adb362333a2ed809 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:49:04 +0900 Subject: [PATCH 214/576] fix(validation): remove reciprocal product rounding from all-covered scale --- crates/validation_core/src/coverage.rs | 74 +++++++++++++++++++++----- 1 file changed, 61 insertions(+), 13 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index b71d66ff8..dd0cef44e 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -86,6 +86,38 @@ fn u64_is_exact_binary64_integer(value: u64) -> bool { value & discarded_mask == 0 } +fn positive_f64_over_inexact_u64(value: f64, denominator: u64) -> f64 { + debug_assert!(value.is_finite() && value >= 0.0); + debug_assert!(denominator > (1_u64 << 53)); + debug_assert!(!u64_is_exact_binary64_integer(denominator)); + if value == 0.0 { + return 0.0; + } + + // Decode the exact binary64 value as `significand * 2^exponent`. The + // significand is at most 53 bits, while this path is restricted to an + // inexact u64 denominator above 2^53, so significand/denominator is a unit + // ratio that the integer routine can round without first rounding the + // denominator. Multiplication by an exact power of two then restores the + // original scale. This avoids the extra `round(1/n) * value` step that can + // move the final quotient by one ULP. + let bits = value.to_bits(); + let exponent_bits = ((bits >> 52) & 0x7ff) as i32; + let fraction = bits & ((1_u64 << 52) - 1); + let (significand, exponent) = if exponent_bits == 0 { + (fraction, -1074) + } else { + ((1_u64 << 52) | fraction, exponent_bits - 1023 - 52) + }; + let ratio = correctly_rounded_unit_ratio(significand, denominator); + let power_of_two = if exponent >= -1022 { + f64::from_bits(((exponent + 1023) as u64) << 52) + } else { + f64::from_bits(1_u64 << (exponent + 1074)) + }; + ratio * power_of_two +} + pub(crate) fn represented_coverage_from_counts( covered_count: u64, sample_count: u64, @@ -151,8 +183,8 @@ fn rationalized_wilson_positive_lower_from_inverse_sample_count( (numerator / denominator).clamp(0.0, 1.0) } -fn all_covered_wilson_lower_from_inverse_sample_count(inverse_n: f64, z2: f64) -> f64 { - let z2_over_n = z2 * inverse_n; +fn all_covered_wilson_lower_from_inexact_sample_count(sample_count: u64, z2: f64) -> f64 { + let z2_over_n = positive_f64_over_inexact_u64(z2, sample_count); if z2_over_n <= 1.0 { // For small z²/n, 1 / (1 + z²/n) can round to exact one before the // finite-sample miss mass is represented. Subtract the miss mass instead. @@ -261,9 +293,9 @@ pub(crate) fn wilson_coverage_interval_from_counts( }; if covered_count == sample_count { - if let Some(inverse_n) = inverse_n { + if inverse_n.is_some() { return Ok(( - all_covered_wilson_lower_from_inverse_sample_count(inverse_n, z2), + all_covered_wilson_lower_from_inexact_sample_count(sample_count, z2), 1.0, )); } @@ -300,13 +332,15 @@ pub(crate) fn wilson_coverage_interval_from_counts( /// stable form neither suffers large-z cancellation nor small-z division /// overflow. Count proportions are rounded to binary64 from their exact integer /// ratio before Wilson evaluation. When the exact sample count itself is not -/// binary64-representable, Wilson scale terms are evaluated through the -/// correctly rounded reciprocal `1 / n` rather than a pre-rounded `n as f64`; -/// the all-covered branch switches between complementary-miss and direct -/// reciprocal forms at `z² / n = 1`, preserving both tiny uncertainty below one -/// and positive lower endpoints under extreme finite critical values. Near the -/// all-covered boundary, the smaller uncovered count is evaluated and reflected -/// by Wilson complement symmetry. +/// binary64-representable, strict-interior Wilson scale terms use the correctly +/// rounded reciprocal `1 / n` rather than a pre-rounded `n as f64`. The exact +/// all-covered path additionally decodes finite `z²` into its binary significand +/// and power-of-two scale, divides that significand by the exact retained `u64` +/// denominator, and then switches between complementary-miss and direct +/// reciprocal forms at `z² / n = 1`. This avoids both reciprocal-product double +/// rounding in the exposed extreme-`z` contract and the false exact 0/1 boundary +/// failures. Near the all-covered boundary, the smaller uncovered count is +/// evaluated and reflected by Wilson complement symmetry. /// /// # Errors /// @@ -328,8 +362,8 @@ pub fn wilson_coverage_interval( #[cfg(test)] mod tests { use super::{ - correctly_rounded_unit_ratio, interval_coverage, u64_is_exact_binary64_integer, - wilson_coverage_interval, + correctly_rounded_unit_ratio, interval_coverage, positive_f64_over_inexact_u64, + u64_is_exact_binary64_integer, wilson_coverage_interval, }; use crate::ValidationError; @@ -373,6 +407,20 @@ mod tests { assert!(u64_is_exact_binary64_integer((1_u64 << 55) + 8)); } + #[test] + fn inexact_u64_scaled_ratio_avoids_reciprocal_product_double_rounding() { + let sample_count = (1_u64 << 53) + 1; + assert_eq!(positive_f64_over_inexact_u64(0.0, sample_count), 0.0); + assert_eq!( + positive_f64_over_inexact_u64(f64::MIN_POSITIVE, sample_count), + 0.0 + ); + assert_eq!( + positive_f64_over_inexact_u64(1e40, sample_count).to_bits(), + 0x44ed_6329_f1c3_5ca4 + ); + } + #[test] fn coverage_and_wilson_bounds_are_oracle_correct() { let truth = [0.0, 1.0, 2.0, 3.0]; From 2f47fa8a0ce0f77281e8366f450fabd5158a573e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:49:51 +0900 Subject: [PATCH 215/576] docs(research): record Wilson reciprocal-product RCA --- ...son-all-covered-inexact-count-extreme-z.md | 23 ++++++++++++------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/docs/research/wilson-all-covered-inexact-count-extreme-z.md b/docs/research/wilson-all-covered-inexact-count-extreme-z.md index f7558b892..ab7062070 100644 --- a/docs/research/wilson-all-covered-inexact-count-extreme-z.md +++ b/docs/research/wilson-all-covered-inexact-count-extreme-z.md @@ -15,33 +15,40 @@ Commit `059ce70d3dd497f486214137bca6a1f1b2e8b3cd` adds `crates/validation_core/t The contract fixes: - `sample_count = covered_count = 2^53 + 1 = 9_007_199_254_740_993`; -- `z = 1e20`, so `z²` is finite; +- `z = 1e20`, so represented `z²` is finite; - empirical coverage `1.0`; -- canonical Wilson lower endpoint `n / (n + z²) = 0x1.16c262777579dp-80`, decimal `9.007199254740993e-25`. +- represented Wilson lower endpoint `0x1.16c262777579dp-80`, decimal `9.007199254740993e-25`. The predecessor reciprocal path computes `z² / n`, then `(z² / n)/(1 + z² / n)`. At this scale the latter rounds to exact `1.0`; subtracting from one therefore returns `0.0` and destroys representable finite-sample uncertainty. -## Causal repair +## Repair lineage and self-review RCA -Commit `0f4783929b8d067eecb91696e1ad5761cd315b1e` keeps the Wilson estimand and the exact-count reciprocal path. It changes only the algebraic projection used for the all-covered lower endpoint when the durable `u64` denominator is not exactly representable as binary64: +Commit `0f4783929b8d067eecb91696e1ad5761cd315b1e` first repaired the catastrophic boundary collapse by switching algebraic projection for the all-covered lower endpoint when the durable `u64` denominator is not exactly representable as binary64: - if `z² / n <= 1`, evaluate `1 - (z² / n)/(1 + z² / n)` so tiny uncertainty below one survives; - if `z² / n > 1`, evaluate `1/(1 + z² / n)` so a large complementary miss mass cannot round to one before subtraction. -Both expressions are algebraically identical to `n/(n+z²)`. The branch is numerical conditioning, not an estimator change or a new confidence-interval policy. Commit `8e2058f2fc6ab42af8f732f03e7ae1dcee3e873d` removes a transient duplicated implementation from `coverage_evidence.rs` so the Wilson projection remains single-writer in the canonical `coverage.rs` producer. +Commit `8e2058f2fc6ab42af8f732f03e7ae1dcee3e873d` then removed a transient duplicated implementation from `coverage_evidence.rs`, restoring the Wilson projection to the single canonical writer in `coverage.rs`. + +Pre-GREEN exact-oracle review found that this was still incomplete. The first scale-switch implementation formed `z² / n` as `z² * round(1/n)`. For the RED case that yields `0x1.d6329f1c35ca5p+79` and the direct reciprocal lower endpoint `0x1.16c262777579cp-80`, one ULP below the public oracle. The catastrophic false zero was gone, but the exact durable denominator had still been rounded once before multiplication. + +Commit `93d5d2089db1fcf4c6167930adb362333a2ed809` is the causal correction for that residual double rounding. For the inexact-`u64` all-covered path, `coverage.rs` now decodes represented finite `z²` into its exact binary significand and power-of-two exponent, computes the significand/`sample_count` unit ratio through the existing integer ties-to-even routine, and restores the power-of-two scale. For the RED case this produces `z²/n = 0x1.d6329f1c35ca4p+79`, after which the large-scale reciprocal yields the required `0x1.16c262777579dp-80` endpoint. + +The new private helper is restricted to this exact-count projection path. It does not redefine a psychometric estimator or make a claim that every arbitrary algebraic rearrangement of the Wilson formula is globally correctly rounded. The public contract is the exposed scientific boundary: exact durable count provenance must not be replaced by a pre-rounded denominator or a rounded-reciprocal product that changes the represented endpoint in the tested case. ## Boundary and owner decision -The existing `n = 2^55 + 3, z = 1.96` durable all-covered contract exercises the small-`z²/n` side and still requires `next_down(1.0)`. The new extreme-`z` contract exercises the large side and requires a positive endpoint rather than false exact zero. The transition at `z²/n = 1` changes only which algebraically equivalent expression is numerically conditioned; it does not alter interval sidedness, critical-value semantics, retained counts, or the Wilson score estimand. +The existing `n = 2^55 + 3, z = 1.96` durable all-covered contract exercises the small-`z²/n` side and still requires `next_down(1.0)`. The extreme-`z` contract exercises the large side and requires the exact public-oracle bit pattern rather than false exact zero or the one-ULP-low intermediate repair. The transition at `z²/n = 1` changes only which algebraically equivalent expression is numerically conditioned; it does not alter interval sidedness, critical-value semantics, retained counts, or the Wilson score estimand. This remains TEPP Validation Evidence provenance/projection behavior. No reusable static psychometric estimator is added, so there is no fast-mlsirm migration. No LLM is involved, and no contextual-orchestrator dependency is introduced. ## Traceability - Public RED: `059ce70d3dd497f486214137bca6a1f1b2e8b3cd` -- Canonical producer repair: `0f4783929b8d067eecb91696e1ad5761cd315b1e` +- Boundary scale-switch repair: `0f4783929b8d067eecb91696e1ad5761cd315b1e` - Single-writer cleanup: `8e2058f2fc6ab42af8f732f03e7ae1dcee3e873d` -- Changelog: `8a73cb64d15e5412e5afabbae80603a66c12a7f0` +- Exact-denominator quotient correction: `93d5d2089db1fcf4c6167930adb362333a2ed809` +- Changelog: `8a73cb64d15e5412e5afabbae80603a66c12a7f0` plus the current-head wording correction - Module: `crates/validation_core/src/coverage.rs` - Durable carrier: `crates/validation_core/src/coverage_evidence.rs` - Public contract: `crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs` From 43b6562c4c2ad3af51970040c127c784cccff228 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:50:04 +0900 Subject: [PATCH 216/576] docs(changelog): record exact-count Wilson quotient correction --- CHANGELOG.d/validation-wilson-all-covered-extreme-z-scale.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-wilson-all-covered-extreme-z-scale.md b/CHANGELOG.d/validation-wilson-all-covered-extreme-z-scale.md index c4ef0d981..f88979e5e 100644 --- a/CHANGELOG.d/validation-wilson-all-covered-extreme-z-scale.md +++ b/CHANGELOG.d/validation-wilson-all-covered-extreme-z-scale.md @@ -1,3 +1,3 @@ ### Fixed -- Preserved the positive all-covered Wilson lower endpoint for durable `u64` sample counts that are not exactly representable in binary64 when a large but finite standard-normal critical value makes `z² / n > 1`. The canonical count-based producer now uses the complementary-miss form only on the small-`z² / n` side and the algebraically equivalent direct reciprocal form on the large side, avoiding both false exact `1.0` and false exact `0.0` endpoints. +- Preserved the positive all-covered Wilson lower endpoint for durable `u64` sample counts that are not exactly representable in binary64 when a large but finite standard-normal critical value makes `z² / n > 1`. The canonical count-based producer now uses the complementary-miss form only on the small-`z² / n` side and the algebraically equivalent direct reciprocal form on the large side. On this exact-count all-covered path, represented `z²` is decomposed into its binary significand and power-of-two scale before division by the durable count, so a rounded `1 / n` product cannot shift the exposed endpoint by one ULP. This avoids false exact `1.0`, false exact `0.0`, and the RED case's reciprocal-product rounding error without changing the Wilson estimand. From b03a719d29e20d6cc4f9b70705ee1aa6e48db543 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:50:32 +0900 Subject: [PATCH 217/576] docs(research): pin Wilson quotient correction lineage --- docs/research/wilson-all-covered-inexact-count-extreme-z.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/research/wilson-all-covered-inexact-count-extreme-z.md b/docs/research/wilson-all-covered-inexact-count-extreme-z.md index ab7062070..7071dd96d 100644 --- a/docs/research/wilson-all-covered-inexact-count-extreme-z.md +++ b/docs/research/wilson-all-covered-inexact-count-extreme-z.md @@ -48,7 +48,7 @@ This remains TEPP Validation Evidence provenance/projection behavior. No reusabl - Boundary scale-switch repair: `0f4783929b8d067eecb91696e1ad5761cd315b1e` - Single-writer cleanup: `8e2058f2fc6ab42af8f732f03e7ae1dcee3e873d` - Exact-denominator quotient correction: `93d5d2089db1fcf4c6167930adb362333a2ed809` -- Changelog: `8a73cb64d15e5412e5afabbae80603a66c12a7f0` plus the current-head wording correction +- Changelog correction: `43b6562c4c2ad3af51970040c127c784cccff228` - Module: `crates/validation_core/src/coverage.rs` - Durable carrier: `crates/validation_core/src/coverage_evidence.rs` - Public contract: `crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs` From ccb75e812988c969206177e4876c7abe9944bfd5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 16:51:54 +0900 Subject: [PATCH 218/576] test(validation): cover subnormal all-covered scale decoding --- ...overed_inexact_count_extreme_z_contract.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs b/crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs index 7ea6d0e52..387725dd3 100644 --- a/crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs +++ b/crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs @@ -26,3 +26,27 @@ fn inexact_durable_count_preserves_positive_all_covered_lower_at_extreme_z() { assert!(evidence.wilson_lower > 0.0); assert_eq!(evidence.wilson_upper, 1.0); } + +#[test] +fn inexact_durable_count_handles_subnormal_squared_critical_value_scale() { + // z=1e-160 squares to a positive binary64 subnormal. Dividing that scale + // by n > 2^53 is below binary64 range, so the all-covered endpoint rounds + // to one rather than inventing a non-representable miss mass. + let json = r#"{ + "schema":"tepp.wilson_coverage_evidence.v1", + "sample_count":9007199254740993, + "covered_count":9007199254740993, + "critical_value_kind":"standard_normal_z", + "interval_sidedness":"two_sided", + "normal_critical_value":1e-160, + "empirical_coverage":1.0, + "wilson_lower":1.0, + "wilson_upper":1.0 + }"#; + + let evidence: WilsonCoverageEvidenceV1 = serde_json::from_str(json) + .expect("subnormal z-squared scale must remain a valid deterministic boundary case"); + + assert_eq!(evidence.wilson_lower, 1.0); + assert_eq!(evidence.wilson_upper, 1.0); +} From e0c4ec81bb455d230259489dc71e23fe33704b1d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 17:00:20 +0900 Subject: [PATCH 219/576] test(validation): expose exact-count Wilson near-one collapse --- ...ed_exact_count_small_z_rounding_contract.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs diff --git a/crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs b/crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs new file mode 100644 index 000000000..d8196f261 --- /dev/null +++ b/crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs @@ -0,0 +1,18 @@ +use validation_core::wilson_coverage_interval; + +#[test] +fn exact_count_all_covered_preserves_representable_small_z_uncertainty() { + // This z is the binary64 value 0x1.0000000000001p-27. Squaring it yields + // 0x1.0000000000002p-54. For n=1, the exact represented-input Wilson lower + // endpoint 1 / (1 + z^2) rounds to next_down(1.0), not to exact 1.0. + // Forming 1 + z^2 first rounds that denominator to 1.0 and erases the + // representable finite-sample uncertainty. + let z = f64::from_bits(0x3e40_0000_0000_0001); + assert_eq!((z * z).to_bits(), 0x3c90_0000_0000_0002); + + let (lower, upper) = wilson_coverage_interval(&[0.0], &[-1.0], &[1.0], z) + .expect("one covered interval with finite positive z must produce Wilson evidence"); + + assert_eq!(lower.to_bits(), 0x3fef_ffff_ffff_ffff); + assert_eq!(upper, 1.0); +} From c9dcb9df363999bbcbb6fffdc8b6a6d9ae5e762c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 17:02:15 +0900 Subject: [PATCH 220/576] fix(validation): preserve exact-count Wilson near-one uncertainty --- crates/validation_core/src/coverage.rs | 40 +++++++++++++++++--------- 1 file changed, 27 insertions(+), 13 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index dd0cef44e..10c8bdfd5 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -299,7 +299,17 @@ pub(crate) fn wilson_coverage_interval_from_counts( 1.0, )); } - return Ok((n / (n + z2), 1.0)); + let direct_lower = n / (n + z2); + if direct_lower == 1.0 && z2 > 0.0 { + // The exact-count denominator can absorb a small positive z² even + // when the Wilson miss mass is still representable immediately + // below one. Evaluate the algebraically equivalent miss fraction + // only on that collapsed boundary instead of changing the ordinary + // direct path globally. + let uncovered_mass = z2 / (n + z2); + return Ok(((1.0 - uncovered_mass).clamp(0.0, 1.0), 1.0)); + } + return Ok((direct_lower, 1.0)); } let uncovered_count = sample_count - covered_count; @@ -325,20 +335,24 @@ pub(crate) fn wilson_coverage_interval_from_counts( /// /// Returns `(lower, upper)` for the empirical coverage rate at the stated /// normal critical value `z` (for example `1.96` for nominal 95%). For an -/// all-covered sample, the exact Wilson lower endpoint is evaluated as -/// `n / (n + z²)`. For nonzero strict-interior coverage, the lower endpoint is -/// evaluated through the algebraically rationalized positive root rather than -/// `center - margin`; the implementation switches scale at `z² = 1` so the -/// stable form neither suffers large-z cancellation nor small-z division -/// overflow. Count proportions are rounded to binary64 from their exact integer -/// ratio before Wilson evaluation. When the exact sample count itself is not -/// binary64-representable, strict-interior Wilson scale terms use the correctly -/// rounded reciprocal `1 / n` rather than a pre-rounded `n as f64`. The exact -/// all-covered path additionally decodes finite `z²` into its binary significand -/// and power-of-two scale, divides that significand by the exact retained `u64` +/// all-covered sample, the exact Wilson lower endpoint is algebraically +/// `n / (n + z²)`. When an exactly representable sample count and a positive +/// finite `z²` make that direct expression collapse spuriously to exact one, +/// the implementation subtracts the equivalent miss fraction `z² / (n + z²)` +/// so representable uncertainty immediately below one is retained. For nonzero +/// strict-interior coverage, the lower endpoint is evaluated through the +/// algebraically rationalized positive root rather than `center - margin`; the +/// implementation switches scale at `z² = 1` so the stable form neither suffers +/// large-z cancellation nor small-z division overflow. Count proportions are +/// rounded to binary64 from their exact integer ratio before Wilson evaluation. +/// When the exact sample count itself is not binary64-representable, +/// strict-interior Wilson scale terms use the correctly rounded reciprocal +/// `1 / n` rather than a pre-rounded `n as f64`. The inexact-count all-covered +/// path additionally decodes finite `z²` into its binary significand and +/// power-of-two scale, divides that significand by the exact retained `u64` /// denominator, and then switches between complementary-miss and direct /// reciprocal forms at `z² / n = 1`. This avoids both reciprocal-product double -/// rounding in the exposed extreme-`z` contract and the false exact 0/1 boundary +/// rounding in the exposed extreme-`z` contract and false exact 0/1 boundary /// failures. Near the all-covered boundary, the smaller uncovered count is /// evaluated and reflected by Wilson complement symmetry. /// From 6140080d2257d0550be479d71371d70e2255c3d0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 17:02:39 +0900 Subject: [PATCH 221/576] test(validation): bound exact-count near-one repair --- ...ered_exact_count_small_z_rounding_contract.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs b/crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs index d8196f261..75586d412 100644 --- a/crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs +++ b/crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs @@ -16,3 +16,19 @@ fn exact_count_all_covered_preserves_representable_small_z_uncertainty() { assert_eq!(lower.to_bits(), 0x3fef_ffff_ffff_ffff); assert_eq!(upper, 1.0); } + +#[test] +fn exact_count_all_covered_keeps_unrepresentable_tiny_uncertainty_at_one() { + // At z=2^-28, z^2=2^-56. The exact lower endpoint differs from one by less + // than half of the binary64 spacing immediately below one, so exact 1.0 is + // the correctly rounded represented endpoint. The boundary repair must not + // manufacture an uncertainty value that binary64 cannot represent. + let z = f64::from_bits(0x3e30_0000_0000_0000); + assert_eq!((z * z).to_bits(), 0x3c70_0000_0000_0000); + + let (lower, upper) = wilson_coverage_interval(&[0.0], &[-1.0], &[1.0], z) + .expect("finite positive z below the representable miss threshold remains valid"); + + assert_eq!(lower, 1.0); + assert_eq!(upper, 1.0); +} From 09ebb482851fe7836e738a74395e6424621da9bf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 17:02:46 +0900 Subject: [PATCH 222/576] docs(changelog): record exact-count Wilson near-one repair --- CHANGELOG.d/validation-wilson-exact-count-small-z-rounding.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-wilson-exact-count-small-z-rounding.md diff --git a/CHANGELOG.d/validation-wilson-exact-count-small-z-rounding.md b/CHANGELOG.d/validation-wilson-exact-count-small-z-rounding.md new file mode 100644 index 000000000..f9d3e006f --- /dev/null +++ b/CHANGELOG.d/validation-wilson-exact-count-small-z-rounding.md @@ -0,0 +1,3 @@ +### Fixed + +- `validation_core::wilson_coverage_interval` now preserves a representable all-covered Wilson miss mass when an exactly representable sample count and small positive `z²` make the direct `n / (n + z²)` denominator round back to `n`. The repair is boundary-local: it subtracts the algebraically equivalent `z² / (n + z²)` only when the direct lower endpoint has spuriously collapsed to exact `1.0`, while smaller uncertainty that is genuinely below binary64 resolution remains `1.0`. From fe89c43803136ab979912fffa636d2a4f169a73e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 17:03:36 +0900 Subject: [PATCH 223/576] docs(research): trace exact-count Wilson near-one repair --- ...ll-covered-exact-count-small-z-rounding.md | 58 +++++++++++++++++++ 1 file changed, 58 insertions(+) create mode 100644 docs/research/wilson-all-covered-exact-count-small-z-rounding.md diff --git a/docs/research/wilson-all-covered-exact-count-small-z-rounding.md b/docs/research/wilson-all-covered-exact-count-small-z-rounding.md new file mode 100644 index 000000000..45ca57cde --- /dev/null +++ b/docs/research/wilson-all-covered-exact-count-small-z-rounding.md @@ -0,0 +1,58 @@ +# Wilson all-covered exact-count near-one rounding + +## Problem and scientific boundary + +TEPP's Validation Evidence owner computes the Wilson score interval from retained coverage counts and a finite positive standard-normal critical value. For an all-covered sample, Wilson's lower endpoint is algebraically + +`L = n / (n + z²) = 1 - z² / (n + z²)`. + +The estimator and sidedness do not change here. The defect is binary64 evaluation of the same endpoint when `n` is exactly representable and `z²` is small but not so small that the final endpoint should round to exact one. + +With `n = 1` and represented `z = 0x1.0000000000001p-27`, binary64 multiplication produces represented `z² = 0x1.0000000000002p-54`. The correctly rounded represented-input endpoint is `0x1.fffffffffffffp-1` (`next_down(1.0)`). Evaluating `n + z²` first rounds that denominator back to `1.0`, so the predecessor direct expression `n / (n + z²)` emitted false exact `1.0` and erased representable finite-sample uncertainty. + +This is not a new confidence-interval estimator. It is numerical conditioning of Wilson's all-covered score endpoint and remains owned by TEPP `validation_core` Validation Evidence. Reusable static psychometric estimation remains owned by fast-mlsirm. + +## Causal repair + +Public RED `e0c4ec81bb455d230259489dc71e23fe33704b1d` adds `crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs` and fixes the expected lower endpoint to bits `0x3fefffffffffffff`. + +Causal repair `c9dcb9df363999bbcbb6fffdc8b6a6d9ae5e762c` keeps the ordinary exact-count direct path. Only when that path returns exact `1.0` while represented `z² > 0` does it evaluate the algebraically equivalent miss mass `z² / (n + z²)` and subtract that from one. This closes the demonstrated false-one boundary without globally replacing the all-covered formula with a different rearrangement. + +Boundary reinforcement `6140080d2257d0550be479d71371d70e2255c3d0` fixes `z = 2^-28`, `z² = 2^-56`, where the exact Wilson lower endpoint differs from one by less than half the binary64 spacing immediately below one. Exact `1.0` is therefore the correct represented endpoint, and the repair must not manufacture a lower representable value. + +CHANGELOG trace: `09ebb482851fe7836e738a74395e6424621da9bf`. + +## Decision record + +Problem: exactly representable coverage denominators could still lose a representable all-covered miss mass through denominator absorption. + +Constraint: preserve the existing Wilson estimator, standard-normal/two-sided semantics, `u64` count provenance, and `coverage.rs` single-writer ownership. Do not claim global correct rounding for every algebraic rearrangement. + +Alternatives considered: + +- Always evaluate `1 - z² / (n + z²)`. Rejected because large `z²/n` can make the miss fraction round to exact one and create the opposite false-zero boundary already addressed on the inexact-count path. +- Always evaluate `1 / (1 + z²/n)`. Rejected because forming `1 + z²/n` is exactly the near-one absorption mechanism exposed by this RED. +- Add a boundary-local complementary evaluation only after the direct exact-count result has collapsed to `1.0`. Selected because it is the minimum causal change for the demonstrated public contract and leaves ordinary direct evaluation unchanged. + +Risk: the repair does not prove every possible exact-count Wilson endpoint is globally correctly rounded. Its claim is narrower: it removes the demonstrated false exact-one state while retaining the correctly rounded exact-one state below binary64 resolution. + +## Traceability + +- Bounded context: Validation Evidence +- Production writer: `crates/validation_core/src/coverage.rs` +- Public contract: `crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs` +- Durable consumers: `WilsonCoverageEvidenceV1` and `ValidationEvidenceV1` continue to consume the canonical Wilson writer rather than duplicate endpoint arithmetic. +- RED: `e0c4ec81bb455d230259489dc71e23fe33704b1d` +- Causal fix: `c9dcb9df363999bbcbb6fffdc8b6a6d9ae5e762c` +- Edge reinforcement: `6140080d2257d0550be479d71371d70e2255c3d0` +- CHANGELOG: `09ebb482851fe7836e738a74395e6424621da9bf` + +## References + +Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 + +IEEE. (2019). *IEEE Standard for Floating-Point Arithmetic (IEEE Std 754-2019).* https://standards.ieee.org/ieee/754/6210/ + +ISO/IEC. (2020). *ISO/IEC 60559:2020 Information technology—Microprocessor systems—Floating-point arithmetic.* https://www.iso.org/standard/80985.html + +As checked in September 2026, IEEE 754-2019 and IEEE/ISO/IEC 60559-2020 are published active standards; IEEE P754 is an active revision project and is not treated as a published replacement. From 3f3c9f2e16303791eaa0554979366dc68e2e63ff Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:04:22 +0900 Subject: [PATCH 224/576] test(validation): expose exact-count Wilson denominator absorption --- ...d_exact_count_large_z_rounding_contract.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 crates/validation_core/tests/wilson_all_covered_exact_count_large_z_rounding_contract.rs diff --git a/crates/validation_core/tests/wilson_all_covered_exact_count_large_z_rounding_contract.rs b/crates/validation_core/tests/wilson_all_covered_exact_count_large_z_rounding_contract.rs new file mode 100644 index 000000000..6450452b5 --- /dev/null +++ b/crates/validation_core/tests/wilson_all_covered_exact_count_large_z_rounding_contract.rs @@ -0,0 +1,21 @@ +use validation_core::wilson_coverage_interval; + +#[test] +fn exact_count_all_covered_preserves_large_z_denominator_residual() { + // This z is the binary64 value 0x1.fffffffffffffp+29. Squaring it yields + // 0x1.ffffffffffffep+59. For n=3, adding the exact sample count to z^2 + // rounds back to z^2, but the exact represented-input Wilson endpoint + // 3 / (3 + z^2) rounds one ULP below the quotient formed from that rounded + // denominator. The finite sample-count contribution must not disappear. + let z = f64::from_bits(0x41cf_ffff_ffff_ffff); + assert_eq!((z * z).to_bits(), 0x43af_ffff_ffff_fffe); + + let truth = [0.0; 3]; + let lower_bounds = [-1.0; 3]; + let upper_bounds = [1.0; 3]; + let (lower, upper) = wilson_coverage_interval(&truth, &lower_bounds, &upper_bounds, z) + .expect("three covered intervals with finite positive z must produce Wilson evidence"); + + assert_eq!(lower.to_bits(), 0x3c48_0000_0000_0001); + assert_eq!(upper, 1.0); +} From 07766cb3df3a4c788c95fa6ec13bfd3de072185b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:08:43 +0900 Subject: [PATCH 225/576] fix(validation): preserve exact-count Wilson denominator residual --- crates/validation_core/src/coverage.rs | 53 +++++++++++++++++--------- 1 file changed, 36 insertions(+), 17 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index 10c8bdfd5..da01aa24b 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -197,6 +197,32 @@ fn all_covered_wilson_lower_from_inexact_sample_count(sample_count: u64, z2: f64 } } +fn all_covered_wilson_lower_from_exact_sample_count(n: f64, z2: f64) -> f64 { + let denominator = n + z2; + let direct_lower = n / denominator; + if direct_lower == 1.0 && z2 > 0.0 { + // A tiny positive z² can be absorbed when the denominator is formed even + // though the Wilson miss mass is still representable immediately below + // one. Preserve that boundary through the complementary miss fraction. + let uncovered_mass = z2 / denominator; + return (1.0 - uncovered_mass).clamp(0.0, 1.0); + } + if denominator == z2 { + // At the opposite scale, a large z² can absorb the exactly represented + // sample count. Recover the TwoSum denominator residual, then correct the + // rounded quotient with an FMA residual. This branch is deliberately + // limited to complete denominator absorption rather than replacing the + // ordinary direct path with a globally different evaluation order. + let z2_virtual = denominator - n; + let denominator_residual = + (n - (denominator - z2_virtual)) + (z2 - z2_virtual); + let division_residual = (-direct_lower).mul_add(denominator, n); + let exact_residual = division_residual - direct_lower * denominator_residual; + return (direct_lower + exact_residual / denominator).clamp(0.0, 1.0); + } + direct_lower +} + fn wilson_bounds_from_represented_proportion( n: f64, p: f64, @@ -299,17 +325,7 @@ pub(crate) fn wilson_coverage_interval_from_counts( 1.0, )); } - let direct_lower = n / (n + z2); - if direct_lower == 1.0 && z2 > 0.0 { - // The exact-count denominator can absorb a small positive z² even - // when the Wilson miss mass is still representable immediately - // below one. Evaluate the algebraically equivalent miss fraction - // only on that collapsed boundary instead of changing the ordinary - // direct path globally. - let uncovered_mass = z2 / (n + z2); - return Ok(((1.0 - uncovered_mass).clamp(0.0, 1.0), 1.0)); - } - return Ok((direct_lower, 1.0)); + return Ok((all_covered_wilson_lower_from_exact_sample_count(n, z2), 1.0)); } let uncovered_count = sample_count - covered_count; @@ -339,12 +355,15 @@ pub(crate) fn wilson_coverage_interval_from_counts( /// `n / (n + z²)`. When an exactly representable sample count and a positive /// finite `z²` make that direct expression collapse spuriously to exact one, /// the implementation subtracts the equivalent miss fraction `z² / (n + z²)` -/// so representable uncertainty immediately below one is retained. For nonzero -/// strict-interior coverage, the lower endpoint is evaluated through the -/// algebraically rationalized positive root rather than `center - margin`; the -/// implementation switches scale at `z² = 1` so the stable form neither suffers -/// large-z cancellation nor small-z division overflow. Count proportions are -/// rounded to binary64 from their exact integer ratio before Wilson evaluation. +/// so representable uncertainty immediately below one is retained. At the +/// opposite scale, if a large finite `z²` completely absorbs an exactly +/// represented sample count in `n + z²`, a compensated denominator/division +/// residual preserves the correctly rounded finite-count contribution. For +/// nonzero strict-interior coverage, the lower endpoint is evaluated through +/// the algebraically rationalized positive root rather than `center - margin`; +/// the implementation switches scale at `z² = 1` so the stable form neither +/// suffers large-z cancellation nor small-z division overflow. Count proportions +/// are rounded to binary64 from their exact integer ratio before Wilson evaluation. /// When the exact sample count itself is not binary64-representable, /// strict-interior Wilson scale terms use the correctly rounded reciprocal /// `1 / n` rather than a pre-rounded `n as f64`. The inexact-count all-covered From 3a6b243e3dae786fcd154674dffe9a890a7099f5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:14:32 +0900 Subject: [PATCH 226/576] test(validation): retain correct large-z Wilson boundary --- ...d_exact_count_large_z_rounding_contract.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/crates/validation_core/tests/wilson_all_covered_exact_count_large_z_rounding_contract.rs b/crates/validation_core/tests/wilson_all_covered_exact_count_large_z_rounding_contract.rs index 6450452b5..9fa382d37 100644 --- a/crates/validation_core/tests/wilson_all_covered_exact_count_large_z_rounding_contract.rs +++ b/crates/validation_core/tests/wilson_all_covered_exact_count_large_z_rounding_contract.rs @@ -19,3 +19,22 @@ fn exact_count_all_covered_preserves_large_z_denominator_residual() { assert_eq!(lower.to_bits(), 0x3c48_0000_0000_0001); assert_eq!(upper, 1.0); } + +#[test] +fn exact_count_all_covered_keeps_correct_power_of_two_large_z_rounding() { + // At z=2^30 the sample count is likewise fully absorbed by z^2 when the + // denominator is formed, but the ordinary quotient already lands on the + // correctly rounded represented-input endpoint. Residual compensation must + // preserve that value rather than forcing a one-ULP adjustment. + let z = f64::from_bits(0x41d0_0000_0000_0000); + assert_eq!((z * z).to_bits(), 0x43b0_0000_0000_0000); + + let truth = [0.0; 3]; + let lower_bounds = [-1.0; 3]; + let upper_bounds = [1.0; 3]; + let (lower, upper) = wilson_coverage_interval(&truth, &lower_bounds, &upper_bounds, z) + .expect("large finite power-of-two z must remain valid Wilson evidence"); + + assert_eq!(lower.to_bits(), 0x3c48_0000_0000_0000); + assert_eq!(upper, 1.0); +} From 8a2f6cee2dd8b490d2124a6f4255897925fd7cc0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:15:07 +0900 Subject: [PATCH 227/576] docs(changelog): record exact-count Wilson large-z residual repair --- CHANGELOG.d/validation-wilson-exact-count-large-z-rounding.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-wilson-exact-count-large-z-rounding.md diff --git a/CHANGELOG.d/validation-wilson-exact-count-large-z-rounding.md b/CHANGELOG.d/validation-wilson-exact-count-large-z-rounding.md new file mode 100644 index 000000000..7f88e4a46 --- /dev/null +++ b/CHANGELOG.d/validation-wilson-exact-count-large-z-rounding.md @@ -0,0 +1,3 @@ +### Fixed + +- `validation_core::wilson_coverage_interval` now preserves the exactly represented finite sample-count contribution when an all-covered Wilson endpoint has an exactly representable `n` but large finite `z²` completely absorbs `n` in the rounded denominator `n + z²`. The exact-count path recovers the addition residual and applies a fused quotient-residual correction only on that absorption boundary; ordinary direct evaluation, the near-one complementary repair, and the inexact-`u64` path remain unchanged. From b125e5614d2ee20dfd413b5a1826759e71abe835 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:15:45 +0900 Subject: [PATCH 228/576] docs(research): trace Wilson exact-count large-z rounding repair --- ...ll-covered-exact-count-large-z-rounding.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 docs/research/wilson-all-covered-exact-count-large-z-rounding.md diff --git a/docs/research/wilson-all-covered-exact-count-large-z-rounding.md b/docs/research/wilson-all-covered-exact-count-large-z-rounding.md new file mode 100644 index 000000000..cb0c0a3b3 --- /dev/null +++ b/docs/research/wilson-all-covered-exact-count-large-z-rounding.md @@ -0,0 +1,65 @@ +# Wilson all-covered exact-count large-z denominator absorption + +## Problem and scientific boundary + +TEPP's Validation Evidence owner computes the Wilson score interval from retained coverage counts and a finite positive standard-normal critical value. For an all-covered sample, Wilson's lower endpoint is + +`L = n / (n + z²)`. + +The estimator, sidedness, retained counts, and critical-value meaning do not change here. The defect is binary64 evaluation of that same represented-input endpoint when `n` is exactly representable but `z²` is so large that forming `n + z²` rounds back to `z²`. + +For `n = 3` and represented `z = 0x1.fffffffffffffp+29`, binary64 multiplication produces represented `z² = 0x1.ffffffffffffep+59`. The rounded denominator `n + z²` equals `z²`, so the predecessor evaluates a quotient with the finite sample-count contribution missing from its denominator. The resulting lower endpoint is `0x1.8000000000002p-59`. The exact rational endpoint formed from the exact integer `3` and the represented binary64 `z²` rounds instead to `0x1.8000000000001p-59`, one ULP lower. + +This is not a new confidence-interval estimator and does not move reusable psychometric arithmetic into TEPP. It is numerical representation of TEPP Validation Evidence around the canonical Wilson producer in `validation_core`; reusable static psychometric estimation remains fast-mlsirm-owned. + +## RED and causal repair + +Public RED `3f3c9f2e16303791eaa0554979366dc68e2e63ff` adds `crates/validation_core/tests/wilson_all_covered_exact_count_large_z_rounding_contract.rs` and fixes the expected lower endpoint to bits `0x3c48000000000001` for the `n = 3` counterexample. + +Causal repair `07766cb3df3a4c788c95fa6ec13bfd3de072185b` keeps the existing exact-count direct path and its separate near-one complementary repair. Only when the rounded denominator equals `z²` exactly does the implementation recover the lost addition term with a TwoSum-style residual. It then computes the quotient residual with `f64::mul_add` and applies the residual correction to the direct quotient. The branch is therefore limited to complete denominator absorption rather than imposing a new global evaluation order on ordinary Wilson endpoints. + +Boundary reinforcement `3a6b243e3dae786fcd154674dffe9a890a7099f5` adds `z = 2^30` at the same `n = 3` absorption scale. In that neighboring case the ordinary quotient already has the correct represented endpoint `0x1.8000000000000p-59`; residual compensation must preserve that value rather than force an unconditional one-ULP decrement. + +CHANGELOG trace: `8a2f6cee2dd8b490d2124a6f4255897925fd7cc0`. + +## Decision record + +Problem: an exactly representable sample count could be lost when a large represented `z²` absorbed it in `n + z²`, and that intermediate rounding could move the final all-covered Wilson lower endpoint by one ULP. + +Constraints: + +- preserve Wilson's score interval and the existing standard-normal/two-sided evidence contract; +- preserve exact retained `u64` count provenance; +- keep `coverage.rs` as the single Wilson arithmetic writer; +- do not replace the already-correct near-one or inexact-count paths; +- do not claim globally correctly rounded Wilson arithmetic beyond the demonstrated boundary. + +Alternatives considered: + +- Always rewrite the endpoint as `1 / (1 + z² / n)`. Rejected because it introduces an additional rounded division and addition and does not, by itself, recover the exact represented-input rational endpoint. +- Always evaluate `(n / z²) / (1 + n / z²)`. Rejected for the same double-rounding reason; the exposed counterexample remains one ULP high under that simple rearrangement. +- Introduce arbitrary-precision arithmetic for every Wilson endpoint. Rejected as disproportionate to the demonstrated defect and inconsistent with the smallest causal repair requirement for the Rust `f64` reference path. +- Recover the lost denominator term and quotient residual only when `n + z² == z²`. Selected because it directly repairs the demonstrated intermediate-rounding failure while leaving ordinary evaluation unchanged. + +Risk: the repair does not prove global correct rounding for every exactly represented `n` and finite `z²`. It closes the demonstrated complete-denominator-absorption boundary. A different counterexample outside that boundary requires its own exact represented-input oracle and RED before the evaluation contract is broadened. + +## Traceability + +- Bounded context: Validation Evidence +- Production writer: `crates/validation_core/src/coverage.rs` +- Public contract: `crates/validation_core/tests/wilson_all_covered_exact_count_large_z_rounding_contract.rs` +- Durable consumers: `WilsonCoverageEvidenceV1` and `ValidationEvidenceV1` continue to consume the canonical Wilson writer rather than duplicate endpoint arithmetic. +- RED: `3f3c9f2e16303791eaa0554979366dc68e2e63ff` +- Causal fix: `07766cb3df3a4c788c95fa6ec13bfd3de072185b` +- Boundary reinforcement: `3a6b243e3dae786fcd154674dffe9a890a7099f5` +- CHANGELOG: `8a2f6cee2dd8b490d2124a6f4255897925fd7cc0` + +## References + +Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 + +IEEE. (2019). *IEEE Standard for Floating-Point Arithmetic (IEEE Std 754-2019).* https://standards.ieee.org/ieee/754/6210/ + +ISO/IEC. (2020). *ISO/IEC 60559:2020 Information technology—Microprocessor systems—Floating-point arithmetic.* https://www.iso.org/standard/80985.html + +As checked in September 2026, IEEE 754-2019 is an active standard and ISO/IEC 60559:2020 remains published; IEEE P754 is an active revision project rather than a published replacement. From 06e556538e171e675c4d8a8287d75052ffc2c4c3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:29:51 +0900 Subject: [PATCH 229/576] test(validation): expose partial Wilson denominator rounding --- ...t_partial_denominator_rounding_contract.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 crates/validation_core/tests/wilson_all_covered_exact_count_partial_denominator_rounding_contract.rs diff --git a/crates/validation_core/tests/wilson_all_covered_exact_count_partial_denominator_rounding_contract.rs b/crates/validation_core/tests/wilson_all_covered_exact_count_partial_denominator_rounding_contract.rs new file mode 100644 index 000000000..05f746c14 --- /dev/null +++ b/crates/validation_core/tests/wilson_all_covered_exact_count_partial_denominator_rounding_contract.rs @@ -0,0 +1,23 @@ +use validation_core::wilson_coverage_interval; + +#[test] +fn exact_count_all_covered_preserves_partial_denominator_residual() { + // z = 3 * 2^-28 is exactly representable. Squaring yields 9 * 2^-56, + // while 1 + z^2 rounds upward to 1 + 2^-52. Dividing by that rounded + // denominator lands two ULPs below one, even though the correctly rounded + // represented-input Wilson endpoint 1 / (1 + 9 * 2^-56) is next_down(1). + // The exact sample-count contribution is not fully absorbed, so the earlier + // complete-absorption repair cannot recover this ordinary inexact sum. + let z = f64::from_bits(0x3e48_0000_0000_0000); + assert_eq!((z * z).to_bits(), 0x3ca2_0000_0000_0000); + assert_eq!((1.0 + z * z).to_bits(), 0x3ff0_0000_0000_0001); + + let truth = [0.0]; + let lower_bounds = [-1.0]; + let upper_bounds = [1.0]; + let (lower, upper) = wilson_coverage_interval(&truth, &lower_bounds, &upper_bounds, z) + .expect("one covered interval with finite positive z must produce Wilson evidence"); + + assert_eq!(lower.to_bits(), 0x3fef_ffff_ffff_ffff); + assert_eq!(upper, 1.0); +} From 6c084dbe607e6c415288c77fa41a4270947cd51e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:32:43 +0900 Subject: [PATCH 230/576] fix(validation): compensate partial Wilson denominator rounding --- crates/validation_core/src/coverage.rs | 66 +++++++++++++------------- 1 file changed, 34 insertions(+), 32 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index da01aa24b..eae2662c4 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -207,17 +207,19 @@ fn all_covered_wilson_lower_from_exact_sample_count(n: f64, z2: f64) -> f64 { let uncovered_mass = z2 / denominator; return (1.0 - uncovered_mass).clamp(0.0, 1.0); } - if denominator == z2 { - // At the opposite scale, a large z² can absorb the exactly represented - // sample count. Recover the TwoSum denominator residual, then correct the - // rounded quotient with an FMA residual. This branch is deliberately - // limited to complete denominator absorption rather than replacing the - // ordinary direct path with a globally different evaluation order. - let z2_virtual = denominator - n; - let denominator_residual = - (n - (denominator - z2_virtual)) + (z2 - z2_virtual); + + // Recover the exact rounding residual of n + z² with TwoSum. Hardware + // division is correctly rounded for the rounded denominator, but an inexact + // denominator sum can still move the represented-input Wilson endpoint by an + // ULP. Correct that denominator error with an FMA residual without changing + // the exact-sum path or introducing a second Wilson writer. + let z2_virtual = denominator - n; + let denominator_residual = + (n - (denominator - z2_virtual)) + (z2 - z2_virtual); + if denominator_residual != 0.0 { let division_residual = (-direct_lower).mul_add(denominator, n); - let exact_residual = division_residual - direct_lower * denominator_residual; + let exact_residual = + (-direct_lower).mul_add(denominator_residual, division_residual); return (direct_lower + exact_residual / denominator).clamp(0.0, 1.0); } direct_lower @@ -352,28 +354,28 @@ pub(crate) fn wilson_coverage_interval_from_counts( /// Returns `(lower, upper)` for the empirical coverage rate at the stated /// normal critical value `z` (for example `1.96` for nominal 95%). For an /// all-covered sample, the exact Wilson lower endpoint is algebraically -/// `n / (n + z²)`. When an exactly representable sample count and a positive -/// finite `z²` make that direct expression collapse spuriously to exact one, -/// the implementation subtracts the equivalent miss fraction `z² / (n + z²)` -/// so representable uncertainty immediately below one is retained. At the -/// opposite scale, if a large finite `z²` completely absorbs an exactly -/// represented sample count in `n + z²`, a compensated denominator/division -/// residual preserves the correctly rounded finite-count contribution. For -/// nonzero strict-interior coverage, the lower endpoint is evaluated through -/// the algebraically rationalized positive root rather than `center - margin`; -/// the implementation switches scale at `z² = 1` so the stable form neither -/// suffers large-z cancellation nor small-z division overflow. Count proportions -/// are rounded to binary64 from their exact integer ratio before Wilson evaluation. -/// When the exact sample count itself is not binary64-representable, -/// strict-interior Wilson scale terms use the correctly rounded reciprocal -/// `1 / n` rather than a pre-rounded `n as f64`. The inexact-count all-covered -/// path additionally decodes finite `z²` into its binary significand and -/// power-of-two scale, divides that significand by the exact retained `u64` -/// denominator, and then switches between complementary-miss and direct -/// reciprocal forms at `z² / n = 1`. This avoids both reciprocal-product double -/// rounding in the exposed extreme-`z` contract and false exact 0/1 boundary -/// failures. Near the all-covered boundary, the smaller uncovered count is -/// evaluated and reflected by Wilson complement symmetry. +/// `n / (n + z²)`. When an exactly representable sample count and positive +/// finite `z²` make the denominator sum inexact, the implementation recovers +/// the TwoSum residual and uses an FMA division residual to compensate the +/// rounded denominator before durable evidence is emitted. A boundary-specific +/// complementary miss fraction preserves representable uncertainty when the +/// direct quotient has already collapsed spuriously to exact one, while genuine +/// sub-ULP uncertainty remains exact one. For nonzero strict-interior coverage, +/// the lower endpoint is evaluated through the algebraically rationalized +/// positive root rather than `center - margin`; the implementation switches +/// scale at `z² = 1` so the stable form neither suffers large-z cancellation nor +/// small-z division overflow. Count proportions are rounded to binary64 from +/// their exact integer ratio before Wilson evaluation. When the exact sample +/// count itself is not binary64-representable, strict-interior Wilson scale +/// terms use the correctly rounded reciprocal `1 / n` rather than a pre-rounded +/// `n as f64`. The inexact-count all-covered path additionally decodes finite +/// `z²` into its binary significand and power-of-two scale, divides that +/// significand by the exact retained `u64` denominator, and then switches +/// between complementary-miss and direct reciprocal forms at `z² / n = 1`. +/// This avoids both reciprocal-product double rounding in the exposed extreme-`z` +/// contract and false exact 0/1 boundary failures. Near the all-covered boundary, +/// the smaller uncovered count is evaluated and reflected by Wilson complement +/// symmetry. /// /// # Errors /// From 25cc19436085881602356e7f2609b697b575540c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:34:03 +0900 Subject: [PATCH 231/576] docs(research): trace partial Wilson denominator repair --- ...xact-count-partial-denominator-rounding.md | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/research/wilson-all-covered-exact-count-partial-denominator-rounding.md diff --git a/docs/research/wilson-all-covered-exact-count-partial-denominator-rounding.md b/docs/research/wilson-all-covered-exact-count-partial-denominator-rounding.md new file mode 100644 index 000000000..e5722d7f1 --- /dev/null +++ b/docs/research/wilson-all-covered-exact-count-partial-denominator-rounding.md @@ -0,0 +1,57 @@ +# Wilson all-covered exact-count partial-denominator rounding + +## Problem + +For an all-covered binomial sample with an exactly representable retained count `n`, the Wilson lower endpoint reduces to + +\[ +L = \frac{n}{n + z^2}. +\] + +The prior exact-count boundary repairs covered two special cases: a tiny positive `z²` whose denominator addition collapses to `n`, and a large `z²` that completely absorbs `n`. Fresh exact-rational review found a third state in the ordinary path: `n + z²` can round to a nearby finite binary64 value without either operand being completely absorbed, and the subsequent correctly rounded hardware division is then correct for the *rounded denominator* rather than for the represented-input denominator sum. + +The public RED uses `n = 1` and represented `z = 3 * 2^-28` (`0x1.8p-27`). Binary64 multiplication gives `z² = 9 * 2^-56` (`0x1.2p-53`). The exact represented-input endpoint is `1 / (1 + 9 * 2^-56)`, whose nearest binary64 value is `0x1.fffffffffffffp-1`. Forming the denominator first rounds `1 + z²` upward to `0x1.0000000000001p+0`, and direct division then produces `0x1.ffffffffffffep-1`, one ULP below the correct represented-input result. + +This is numerical evidence corruption, not an estimator-target change. A durable Validation Evidence artifact must not depend on a denominator rounding state that is distinguishable from the exact arithmetic of its already represented inputs. + +## Constraints and rejected alternatives + +`coverage.rs` remains the canonical Wilson writer. The repair must not duplicate Wilson arithmetic in a report/projection layer, move reusable static psychometric arithmetic into TEPP, consume mutable code from `fast-mlsirm`, or use an LLM as numerical authority. + +Always switching to `1 - z² / (n + z²)` was rejected. That algebraic form is useful at the near-one boundary but has its own rounding behavior outside that boundary. Replacing all Wilson evaluation with an arbitrary alternative formula would change stable paths without causal evidence. + +Treating hardware division as sufficient was also rejected. IEEE 754 makes the basic operation deterministic for the floating-point operands presented to it; it does not restore information already lost when `n + z²` was rounded before division. + +## Selected repair + +For the exactly representable-count, all-covered path, TEPP now obtains the error-free TwoSum residual of the denominator addition: + +\[ +D = \operatorname{fl}(n + z^2), \qquad n + z^2 = D + \delta_D. +\] + +When `δ_D != 0`, the direct quotient `q = fl(n / D)` is corrected with a fused residual for `n - qD` and the denominator residual contribution `-qδ_D`. The correction reuses the same compensated quotient mechanism already justified by the complete large-`z²` absorption repair; this change broadens its causal trigger from complete absorption to any demonstrably inexact exact-count denominator sum. Exact denominator sums remain on the direct path. The earlier false-exact-one complementary branch remains boundary-local because it also protects the case in which the quotient has already collapsed to `1.0`. + +The change does **not** claim that every possible floating-point rearrangement of the Wilson interval is globally correctly rounded. It closes the demonstrated partial-denominator state and retains bit-level public contracts for the near-one, partial-rounding, large-`z²`, and correctly rounded control boundaries. + +## Evidence and traceability + +- Public RED: `06e556538e171e675c4d8a8287d75052ffc2c4c3`, `crates/validation_core/tests/wilson_all_covered_exact_count_partial_denominator_rounding_contract.rs`. +- Causal production repair: `6c084dbe607e6c415288c77fa41a4270947cd51e`, `crates/validation_core/src/coverage.rs`. +- Canonical API: `validation_core::wilson_coverage_interval`. +- Predecessor complete-absorption contract retained: `crates/validation_core/tests/wilson_all_covered_exact_count_large_z_rounding_contract.rs`. +- Owner boundary: TEPP Validation Evidence numerical representation/admission. No reusable static psychometric estimator was added. + +## Standards and primary literature status checked 2026-09-04 + +Wilson's original score-interval source remains the primary statistical reference. IEEE 754-2019 is currently listed by IEEE SA as an Active Standard; P754 is an Active PAR to revise/supersede it, not a published replacement. ISO/IEC 60559:2020 remains a published International Standard adopting the floating-point arithmetic specification. The current published AERA/APA/NCME *Standards for Educational and Psychological Testing* remains the 2014 edition; the sponsoring organizations' Joint Committee is revising that edition, and AERA lists the Standards task-force roster as of 2026-08-31. These statuses are recorded without treating an unpublished revision as current normative authority. + +## 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. + +IEEE Computer Society. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE. https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization & International Electrotechnical Commission. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 From 5acc894b8a4d42cd7af8cdc22a02b61a063f16aa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:34:10 +0900 Subject: [PATCH 232/576] docs(changelog): record partial Wilson denominator repair --- ...lidation-wilson-exact-count-partial-denominator-rounding.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-wilson-exact-count-partial-denominator-rounding.md diff --git a/CHANGELOG.d/validation-wilson-exact-count-partial-denominator-rounding.md b/CHANGELOG.d/validation-wilson-exact-count-partial-denominator-rounding.md new file mode 100644 index 000000000..8dcf6d7b8 --- /dev/null +++ b/CHANGELOG.d/validation-wilson-exact-count-partial-denominator-rounding.md @@ -0,0 +1,3 @@ +### Fixed + +- `validation_core::wilson_coverage_interval` now compensates the exact TwoSum residual whenever an exactly representable all-covered sample count forms an inexact binary64 denominator `n + z²`. This preserves the represented-input Wilson lower endpoint when ordinary partial denominator rounding moves the direct quotient by one ULP, while exact denominator sums and the existing near-one/inexact-`u64` boundary contracts remain unchanged. From f89e36d1f2a048befb983327c83f5696baf530cc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:34:16 +0900 Subject: [PATCH 233/576] docs(changelog): keep large-z Wilson note code-current --- CHANGELOG.d/validation-wilson-exact-count-large-z-rounding.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-wilson-exact-count-large-z-rounding.md b/CHANGELOG.d/validation-wilson-exact-count-large-z-rounding.md index 7f88e4a46..61a2824d2 100644 --- a/CHANGELOG.d/validation-wilson-exact-count-large-z-rounding.md +++ b/CHANGELOG.d/validation-wilson-exact-count-large-z-rounding.md @@ -1,3 +1,3 @@ ### Fixed -- `validation_core::wilson_coverage_interval` now preserves the exactly represented finite sample-count contribution when an all-covered Wilson endpoint has an exactly representable `n` but large finite `z²` completely absorbs `n` in the rounded denominator `n + z²`. The exact-count path recovers the addition residual and applies a fused quotient-residual correction only on that absorption boundary; ordinary direct evaluation, the near-one complementary repair, and the inexact-`u64` path remain unchanged. +- `validation_core::wilson_coverage_interval` now preserves the exactly represented finite sample-count contribution when an all-covered Wilson endpoint has an exactly representable `n` but large finite `z²` completely absorbs `n` in the rounded denominator `n + z²`. The exact-count path recovers the addition residual and applies a fused quotient-residual correction. A later partial-denominator repair extends the same compensated denominator mechanism to ordinary inexact `n + z²` sums; the near-one complementary repair and the inexact-`u64` path remain separate boundary contracts. From 80a0a0ade16f516bf63907f2a8d5105dbcd9c438 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:34:45 +0900 Subject: [PATCH 234/576] test(validation): retain correct partial Wilson rounding --- ...t_partial_denominator_rounding_contract.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/crates/validation_core/tests/wilson_all_covered_exact_count_partial_denominator_rounding_contract.rs b/crates/validation_core/tests/wilson_all_covered_exact_count_partial_denominator_rounding_contract.rs index 05f746c14..331298d0e 100644 --- a/crates/validation_core/tests/wilson_all_covered_exact_count_partial_denominator_rounding_contract.rs +++ b/crates/validation_core/tests/wilson_all_covered_exact_count_partial_denominator_rounding_contract.rs @@ -21,3 +21,22 @@ fn exact_count_all_covered_preserves_partial_denominator_residual() { assert_eq!(lower.to_bits(), 0x3fef_ffff_ffff_ffff); assert_eq!(upper, 1.0); } + +#[test] +fn exact_count_partial_denominator_compensation_keeps_correct_direct_rounding() { + // z = 3 * 2^-27 also makes 1 + z^2 inexact, but the direct quotient is + // already the correctly rounded represented-input endpoint. Compensation + // must preserve that value rather than mechanically moving every inexact sum. + let z = f64::from_bits(0x3e58_0000_0000_0000); + assert_eq!((z * z).to_bits(), 0x3cc2_0000_0000_0000); + assert_eq!((1.0 + z * z).to_bits(), 0x3ff0_0000_0000_0002); + + let truth = [0.0]; + let lower_bounds = [-1.0]; + let upper_bounds = [1.0]; + let (lower, upper) = wilson_coverage_interval(&truth, &lower_bounds, &upper_bounds, z) + .expect("one covered interval with finite positive z must produce Wilson evidence"); + + assert_eq!(lower.to_bits(), 0x3fef_ffff_ffff_fffc); + assert_eq!(upper, 1.0); +} From 51ca9e8a6a3201d2a3e4e4fcff52e1175b746297 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:36:52 +0900 Subject: [PATCH 235/576] docs(research): complete partial Wilson rounding trace --- ...-all-covered-exact-count-partial-denominator-rounding.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/research/wilson-all-covered-exact-count-partial-denominator-rounding.md b/docs/research/wilson-all-covered-exact-count-partial-denominator-rounding.md index e5722d7f1..a965af39b 100644 --- a/docs/research/wilson-all-covered-exact-count-partial-denominator-rounding.md +++ b/docs/research/wilson-all-covered-exact-count-partial-denominator-rounding.md @@ -32,12 +32,16 @@ D = \operatorname{fl}(n + z^2), \qquad n + z^2 = D + \delta_D. When `δ_D != 0`, the direct quotient `q = fl(n / D)` is corrected with a fused residual for `n - qD` and the denominator residual contribution `-qδ_D`. The correction reuses the same compensated quotient mechanism already justified by the complete large-`z²` absorption repair; this change broadens its causal trigger from complete absorption to any demonstrably inexact exact-count denominator sum. Exact denominator sums remain on the direct path. The earlier false-exact-one complementary branch remains boundary-local because it also protects the case in which the quotient has already collapsed to `1.0`. -The change does **not** claim that every possible floating-point rearrangement of the Wilson interval is globally correctly rounded. It closes the demonstrated partial-denominator state and retains bit-level public contracts for the near-one, partial-rounding, large-`z²`, and correctly rounded control boundaries. +The change does **not** claim that every possible floating-point rearrangement of the Wilson interval is globally correctly rounded. It closes the demonstrated partial-denominator state and retains bit-level public contracts for the near-one, partial-rounding, large-`z²`, and correctly rounded control boundaries. The control added after the repair uses `z = 3 * 2^-27`: its denominator sum is likewise inexact, but the direct quotient is already correctly rounded, and the compensation must preserve that same endpoint rather than force a one-ULP move. ## Evidence and traceability - Public RED: `06e556538e171e675c4d8a8287d75052ffc2c4c3`, `crates/validation_core/tests/wilson_all_covered_exact_count_partial_denominator_rounding_contract.rs`. - Causal production repair: `6c084dbe607e6c415288c77fa41a4270947cd51e`, `crates/validation_core/src/coverage.rs`. +- Research decision record introduced: `25cc19436085881602356e7f2609b697b575540c`. +- Dedicated changelog fragment: `5acc894b8a4d42cd7af8cdc22a02b61a063f16aa`. +- Predecessor large-`z²` changelog made code-current after the broadened trigger: `f89e36d1f2a048befb983327c83f5696baf530cc`. +- Inexact-denominator control retained: `80a0a0ade16f516bf63907f2a8d5105dbcd9c438`. - Canonical API: `validation_core::wilson_coverage_interval`. - Predecessor complete-absorption contract retained: `crates/validation_core/tests/wilson_all_covered_exact_count_large_z_rounding_contract.rs`. - Owner boundary: TEPP Validation Evidence numerical representation/admission. No reusable static psychometric estimator was added. From d076d344cb3cd3a768e7a5c2d8d7bd9039c657e9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 18:58:27 +0900 Subject: [PATCH 236/576] test(validation): expose Wilson residual overcorrection --- ...t_count_extreme_scale_rounding_contract.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 crates/validation_core/tests/wilson_all_covered_exact_count_extreme_scale_rounding_contract.rs diff --git a/crates/validation_core/tests/wilson_all_covered_exact_count_extreme_scale_rounding_contract.rs b/crates/validation_core/tests/wilson_all_covered_exact_count_extreme_scale_rounding_contract.rs new file mode 100644 index 000000000..30f8ff6f5 --- /dev/null +++ b/crates/validation_core/tests/wilson_all_covered_exact_count_extreme_scale_rounding_contract.rs @@ -0,0 +1,21 @@ +use validation_core::wilson_coverage_interval; + +#[test] +fn exact_count_all_covered_does_not_overcorrect_below_final_ulp_resolution() { + // n=3 and this finite binary64 z produce z^2 = 0x1.0000000000001p+985. + // The exact represented-input endpoint 3 / (3 + z^2) rounds to the same + // binary64 value as 3 / z^2 because the finite-count correction is below + // the final quotient's half-ULP. Residual compensation must therefore keep + // the already-correct direct quotient instead of forcing a one-ULP step. + let z = f64::from_bits(0x5eb6_a09e_667f_3bcd); + assert_eq!((z * z).to_bits(), 0x7d80_0000_0000_0001); + + let truth = [0.0; 3]; + let lower_bounds = [-1.0; 3]; + let upper_bounds = [1.0; 3]; + let (lower, upper) = wilson_coverage_interval(&truth, &lower_bounds, &upper_bounds, z) + .expect("finite positive z with three covered intervals must produce Wilson evidence"); + + assert_eq!(lower.to_bits(), 0x0277_ffff_ffff_ffff); + assert_eq!(upper, 1.0); +} From 32314239754204158f228ec67a0771abf4d39b45 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 19:01:23 +0900 Subject: [PATCH 237/576] fix(validation): round exact-count Wilson residual by midpoint --- crates/validation_core/src/coverage.rs | 70 ++++++++++++++++---------- 1 file changed, 44 insertions(+), 26 deletions(-) diff --git a/crates/validation_core/src/coverage.rs b/crates/validation_core/src/coverage.rs index eae2662c4..191f9d08d 100644 --- a/crates/validation_core/src/coverage.rs +++ b/crates/validation_core/src/coverage.rs @@ -208,11 +208,11 @@ fn all_covered_wilson_lower_from_exact_sample_count(n: f64, z2: f64) -> f64 { return (1.0 - uncovered_mass).clamp(0.0, 1.0); } - // Recover the exact rounding residual of n + z² with TwoSum. Hardware - // division is correctly rounded for the rounded denominator, but an inexact - // denominator sum can still move the represented-input Wilson endpoint by an - // ULP. Correct that denominator error with an FMA residual without changing - // the exact-sum path or introducing a second Wilson writer. + // Recover the exact rounding residual of n + z² with TwoSum. The direct + // quotient is correctly rounded for the rounded denominator, so use the + // residual only to decide whether the exact represented-input quotient lies + // beyond the adjacent binary64 midpoint. Adding a rounded correction can + // itself force a one-ULP move when the true correction is below that midpoint. let z2_virtual = denominator - n; let denominator_residual = (n - (denominator - z2_virtual)) + (z2 - z2_virtual); @@ -220,7 +220,25 @@ fn all_covered_wilson_lower_from_exact_sample_count(n: f64, z2: f64) -> f64 { let division_residual = (-direct_lower).mul_add(denominator, n); let exact_residual = (-direct_lower).mul_add(denominator_residual, division_residual); - return (direct_lower + exact_residual / denominator).clamp(0.0, 1.0); + if exact_residual != 0.0 { + let neighbor = if exact_residual.is_sign_negative() { + f64::from_bits(direct_lower.to_bits() - 1) + } else { + f64::from_bits(direct_lower.to_bits() + 1) + }; + let ulp_toward_exact = (neighbor - direct_lower).abs(); + let midpoint_residual = 0.5 + * ulp_toward_exact.mul_add( + denominator, + ulp_toward_exact * denominator_residual, + ); + let residual_magnitude = exact_residual.abs(); + if residual_magnitude > midpoint_residual + || (residual_magnitude == midpoint_residual && direct_lower.to_bits() & 1 == 1) + { + return neighbor.clamp(0.0, 1.0); + } + } } direct_lower } @@ -356,26 +374,26 @@ pub(crate) fn wilson_coverage_interval_from_counts( /// all-covered sample, the exact Wilson lower endpoint is algebraically /// `n / (n + z²)`. When an exactly representable sample count and positive /// finite `z²` make the denominator sum inexact, the implementation recovers -/// the TwoSum residual and uses an FMA division residual to compensate the -/// rounded denominator before durable evidence is emitted. A boundary-specific -/// complementary miss fraction preserves representable uncertainty when the -/// direct quotient has already collapsed spuriously to exact one, while genuine -/// sub-ULP uncertainty remains exact one. For nonzero strict-interior coverage, -/// the lower endpoint is evaluated through the algebraically rationalized -/// positive root rather than `center - margin`; the implementation switches -/// scale at `z² = 1` so the stable form neither suffers large-z cancellation nor -/// small-z division overflow. Count proportions are rounded to binary64 from -/// their exact integer ratio before Wilson evaluation. When the exact sample -/// count itself is not binary64-representable, strict-interior Wilson scale -/// terms use the correctly rounded reciprocal `1 / n` rather than a pre-rounded -/// `n as f64`. The inexact-count all-covered path additionally decodes finite -/// `z²` into its binary significand and power-of-two scale, divides that -/// significand by the exact retained `u64` denominator, and then switches -/// between complementary-miss and direct reciprocal forms at `z² / n = 1`. -/// This avoids both reciprocal-product double rounding in the exposed extreme-`z` -/// contract and false exact 0/1 boundary failures. Near the all-covered boundary, -/// the smaller uncovered count is evaluated and reflected by Wilson complement -/// symmetry. +/// the TwoSum residual and uses an FMA quotient residual to compare the exact +/// represented-input quotient with the adjacent binary64 midpoint before +/// changing the direct result. A boundary-specific complementary miss fraction +/// preserves representable uncertainty when the direct quotient has already +/// collapsed spuriously to exact one, while genuine sub-ULP uncertainty remains +/// exact one. For nonzero strict-interior coverage, the lower endpoint is +/// evaluated through the algebraically rationalized positive root rather than +/// `center - margin`; the implementation switches scale at `z² = 1` so the +/// stable form neither suffers large-z cancellation nor small-z division +/// overflow. Count proportions are rounded to binary64 from their exact integer +/// ratio before Wilson evaluation. When the exact sample count itself is not +/// binary64-representable, strict-interior Wilson scale terms use the correctly +/// rounded reciprocal `1 / n` rather than a pre-rounded `n as f64`. The +/// inexact-count all-covered path additionally decodes finite `z²` into its +/// binary significand and power-of-two scale, divides that significand by the +/// exact retained `u64` denominator, and then switches between complementary- +/// miss and direct reciprocal forms at `z² / n = 1`. This avoids both +/// reciprocal-product double rounding in the exposed extreme-`z` contract and +/// false exact 0/1 boundary failures. Near the all-covered boundary, the smaller +/// uncovered count is evaluated and reflected by Wilson complement symmetry. /// /// # Errors /// From a75940c955633d5eb92f227d79e64acd0ef46ea8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 19:02:05 +0900 Subject: [PATCH 238/576] docs(changelog): record Wilson midpoint rounding repair --- CHANGELOG.d/validation-wilson-exact-count-residual-midpoint.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-wilson-exact-count-residual-midpoint.md diff --git a/CHANGELOG.d/validation-wilson-exact-count-residual-midpoint.md b/CHANGELOG.d/validation-wilson-exact-count-residual-midpoint.md new file mode 100644 index 000000000..9120cd2cb --- /dev/null +++ b/CHANGELOG.d/validation-wilson-exact-count-residual-midpoint.md @@ -0,0 +1,3 @@ +### Fixed + +- Exact-count all-covered Wilson evidence now compares the represented-input quotient residual against the adjacent binary64 midpoint before changing the direct quotient. This prevents residual compensation from forcing a one-ULP move when the finite-count correction is real but below the final endpoint's rounding resolution, while preserving earlier cases where denominator rounding genuinely changes the correctly rounded endpoint. From ef6441f786d64e6dca198d0d6140838c391f0659 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 19:02:36 +0900 Subject: [PATCH 239/576] docs(research): trace Wilson residual midpoint repair --- ...l-covered-exact-count-residual-midpoint.md | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 docs/research/wilson-all-covered-exact-count-residual-midpoint.md diff --git a/docs/research/wilson-all-covered-exact-count-residual-midpoint.md b/docs/research/wilson-all-covered-exact-count-residual-midpoint.md new file mode 100644 index 000000000..cc1f6eded --- /dev/null +++ b/docs/research/wilson-all-covered-exact-count-residual-midpoint.md @@ -0,0 +1,43 @@ +# Exact-count Wilson residual midpoint selection + +## Problem + +For an all-covered sample whose retained count is exactly representable in binary64, the Wilson lower endpoint reduces algebraically to + +\[ +L = \frac{n}{n + z^2}. +\] + +`coverage.rs` already preserves the TwoSum residual of `n + z²` and an FMA quotient residual. The previous repair converted that residual into an additive binary64 quotient correction. That is not sufficient for correct final rounding: when the exact represented-input quotient differs from the direct hardware quotient by less than the midpoint to the adjacent binary64 value, rounding the correction separately can still produce a one-ULP step. + +Public RED `d076d344cb3cd3a768e7a5c2d8d7bd9039c657e9` fixes a concrete finite case: `n = 3`, represented `z = 0x1.6a09e667f3bcdp+492`, and represented `z² = 0x1.0000000000001p+985`. The rounded denominator is exactly `z²`, so the count contribution is present only in the TwoSum residual. The direct quotient is `0x1.7ffffffffffffp-984`; the exact rational formed from the already represented inputs `3 / (3 + z²)` rounds to that same binary64 value. The predecessor additive correction instead returned `0x1.7fffffffffffep-984`, one ULP too low. + +## Constraints and alternatives + +The repair stays in TEPP Validation Evidence because it governs representation of Wilson evidence emitted by the canonical `validation_core::coverage` producer. It does not introduce reusable static psychometric estimation and does not copy fast-mlsirm source. + +Always applying the additive correction was rejected because the RED demonstrates a double-rounding failure at the quotient scale. Always keeping the direct quotient was rejected because earlier exact-count cases demonstrate the opposite state: denominator rounding can move the exact represented-input endpoint across an adjacent binary64 midpoint. Replacing the whole Wilson implementation with a second arbitrary-precision writer was rejected because it would violate the single-writer boundary and widen the causal surface. + +## Selected repair + +Causal repair `32314239754204158f228ec67a0771abf4d39b45` keeps the rounded denominator, its TwoSum residual, and the FMA quotient residual. When the denominator residual is nonzero, it uses the residual sign only to identify the adjacent candidate and compares residual magnitude with the exact-denominator midpoint distance represented as + +\[ +\tfrac12\,\operatorname{ulp}_{direction}(q)\,(D + \delta D). +\] + +The dominant `ulp * D` product is evaluated through FMA with the residual term. The quotient changes by one adjacent binary64 value only when the represented-input rational lies beyond that midpoint; an exact midpoint follows ties-to-even from the direct quotient significand. The existing `direct_lower == 1.0` complementary-miss boundary remains separate because the adjacent uncertainty can be lost before a usable direct quotient exists. + +Dedicated CHANGELOG evidence is `a75940c955633d5eb92f227d79e64acd0ef46ea8`. + +## Scope and risk + +This closes the demonstrated exact-count all-covered residual-overcorrection state. It does not claim globally correctly rounded Wilson endpoints for strict-interior coverage, inexact durable counts, or algebraically different Wilson forms. Those paths require their own represented-input counterexamples before any gate is strengthened. + +The main remaining risk is midpoint comparison at binary64 spacing transitions and tie states. Existing predecessor tests cover a large-`z²` case where one-ULP movement is required, a power-of-two large-`z` case where it is not, near-one exact-count recovery, and partial denominator rounding. The new RED covers the opposite extreme-scale state where a real denominator residual is below final quotient resolution. + +## Methodological trace + +Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 + +IEEE Std 754-2019 and ISO/IEC 60559:2020 remain the published floating-point authorities used for binary64 round-to-nearest, ties-to-even reasoning. The scientific acceptance policy remains anchored to the published AERA/APA/NCME *Standards for Educational and Psychological Testing* while the successor revision is still under development. From 76067efce44419c27687b8673cb76092c90fb5a5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 20:05:42 +0900 Subject: [PATCH 240/576] test(validation): expose scaled acceptance bound underflow --- ...ror_acceptance_scale_underflow_contract.rs | 43 +++++++++++++++++++ 1 file changed, 43 insertions(+) create mode 100644 crates/validation_core/tests/standard_error_acceptance_scale_underflow_contract.rs diff --git a/crates/validation_core/tests/standard_error_acceptance_scale_underflow_contract.rs b/crates/validation_core/tests/standard_error_acceptance_scale_underflow_contract.rs new file mode 100644 index 000000000..b34ceb217 --- /dev/null +++ b/crates/validation_core/tests/standard_error_acceptance_scale_underflow_contract.rs @@ -0,0 +1,43 @@ +use validation_core::accept_within_standard_errors; + +#[test] +fn positive_standard_error_bound_survives_scale_reduction() { + let estimate = 1.0e308_f64; + let target = f64::from_bits(estimate.to_bits() - 1); + let standard_error = 2.2e-16_f64; + let multiplier = 1.0e308_f64; + + let represented_residual = estimate - target; + let represented_bound = multiplier * standard_error; + assert!(represented_residual.is_finite()); + assert!(represented_bound.is_finite()); + assert!(represented_residual <= represented_bound); + assert_eq!(standard_error / estimate, 0.0); + + assert_eq!( + accept_within_standard_errors(estimate, target, standard_error, multiplier), + Ok(true), + "a finite positive k*SE bound must not disappear because SE/scale underflows first" + ); +} + +#[test] +fn scale_underflow_repair_does_not_accept_a_smaller_finite_bound() { + let estimate = 1.0e308_f64; + let target = f64::from_bits(estimate.to_bits() - 1); + let standard_error = 1.8e-16_f64; + let multiplier = 1.0e308_f64; + + let represented_residual = estimate - target; + let represented_bound = multiplier * standard_error; + assert!(represented_residual.is_finite()); + assert!(represented_bound.is_finite()); + assert!(represented_residual > represented_bound); + assert_eq!(standard_error / estimate, 0.0); + + assert_eq!( + accept_within_standard_errors(estimate, target, standard_error, multiplier), + Ok(false), + "restoring the finite bound must preserve a nearby rejection" + ); +} From 4ffdf3665b3bbafd3b0bbf06b599fe71498169ab Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 20:07:27 +0900 Subject: [PATCH 241/576] fix(validation): preserve finite SE acceptance bounds --- crates/validation_core/src/monte_carlo.rs | 31 ++++++++++++++++++----- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index a5df025ea..5df3c6642 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -287,12 +287,14 @@ pub fn summarize_replications( /// SE-aware acceptance: accept when `|estimate − target| ≤ k · se`. /// -/// Comparison scales all terms by a shared finite magnitude so opposite-sign -/// extremes do not overflow both sides of the inequality to infinity. A zero -/// standard error or zero multiplier is an exact-recovery gate and is compared -/// before scale reduction so a huge SE cannot erase a nonzero residual. Exact -/// recovery uses numeric equality, for which IEEE `-0.0` and `+0.0` denote the -/// same zero-valued scientific result. +/// Finite represented residuals and finite represented `k · se` bounds are +/// compared before normalization. This preserves a positive acceptance bound +/// when dividing `se` by a much larger estimate/target scale would underflow to +/// zero. Scale-normalized comparison is reserved for the only ambiguous binary64 +/// case: both the direct residual subtraction and direct bound multiplication +/// overflow. A zero standard error or zero multiplier remains an exact-recovery +/// gate and is compared before either path. Exact recovery uses numeric equality, +/// for which IEEE `-0.0` and `+0.0` denote the same zero-valued scientific result. /// /// # Errors /// @@ -317,6 +319,22 @@ pub fn accept_within_standard_errors( // Exact recovery is numerical equality; signed zero is one zero value. return Ok(estimate == target); } + + let direct_error = estimate - target; + let direct_bound = k * standard_error; + if direct_error.is_finite() { + if direct_bound.is_finite() { + return Ok(direct_error.abs() <= direct_bound); + } + // A finite residual is necessarily inside a positive bound whose + // represented multiplication overflowed beyond binary64's finite range. + return Ok(true); + } + if direct_bound.is_finite() { + // The represented residual overflowed while the positive bound did not. + return Ok(false); + } + let scale = estimate .abs() .max(target.abs()) @@ -324,7 +342,6 @@ pub fn accept_within_standard_errors( .max(1.0); let scaled_error = (estimate / scale) - (target / scale); let scaled_bound = k * (standard_error / scale); - // scale is at least 1.0 and all inputs are finite, so scaled terms are finite. Ok(scaled_error.abs() <= scaled_bound) } From d822c0ba7fa548c3283e462323c56d8f5705de31 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 20:07:58 +0900 Subject: [PATCH 242/576] docs(validation): record finite SE acceptance repair --- .../validation-standard-error-acceptance-scale-underflow.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-standard-error-acceptance-scale-underflow.md diff --git a/CHANGELOG.d/validation-standard-error-acceptance-scale-underflow.md b/CHANGELOG.d/validation-standard-error-acceptance-scale-underflow.md new file mode 100644 index 000000000..bdc4f0be3 --- /dev/null +++ b/CHANGELOG.d/validation-standard-error-acceptance-scale-underflow.md @@ -0,0 +1,3 @@ +### Fixed + +- Preserved finite positive `k · SE` acceptance bounds before scale normalization in `accept_within_standard_errors`. A large estimate/target scale could previously make `SE / scale` underflow to zero even when the represented direct residual and represented `k · SE` bound were both finite, falsely rejecting a scientifically admissible recovery result. Direct finite residual/bound comparison now precedes the overflow-only normalized fallback; zero-SE and zero-multiplier exact-recovery semantics are unchanged. From daa097861342ac14a7f0553c8d565b75cb6131fd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 20:08:20 +0900 Subject: [PATCH 243/576] docs(research): trace SE acceptance scale underflow --- ...andard-error-acceptance-scale-underflow.md | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 docs/research/standard-error-acceptance-scale-underflow.md diff --git a/docs/research/standard-error-acceptance-scale-underflow.md b/docs/research/standard-error-acceptance-scale-underflow.md new file mode 100644 index 000000000..3ace21396 --- /dev/null +++ b/docs/research/standard-error-acceptance-scale-underflow.md @@ -0,0 +1,42 @@ +# Standard-error acceptance scale-underflow repair + +## Problem and scientific contract + +TEPP's Validation bounded context exposes `accept_within_standard_errors(estimate, target, standard_error, k)` as the deterministic CPU `f64` reference for the decision + +`|estimate - target| <= k * standard_error`. + +The predecessor normalized every operand before comparison. That avoided overflow for opposite-sign full-range estimates, but the order of operations could erase a finite positive uncertainty allowance. With + +- `estimate = 1.0e308`, +- `target = next_down(estimate)`, +- `standard_error = 2.2e-16`, and +- `k = 1.0e308`, + +the represented direct residual is finite (`1.99584030953472e292`) and the represented direct bound is finite (`2.2e292`), so the recovery is admissible. The predecessor nevertheless computed `standard_error / scale == 0.0` before multiplying by `k`, converted the positive bound to zero, and rejected the result. This is an operation-order underflow in the decision implementation, not evidence that the scientific uncertainty is zero. + +Public RED `76067efce44419c27687b8673cb76092c90fb5a5` fixes the admissible case and a nearby rejection (`standard_error = 1.8e-16`) so repairing the positive bound cannot widen the decision arbitrarily. + +## Constraints and alternatives + +The repair must retain the existing exact-recovery semantics for `standard_error == 0` or `k == 0`, must remain deterministic binary64, and must still handle an overflowing opposite-sign residual without accepting merely because both sides materialized as infinity. + +Always evaluating the normalized expression was rejected because it is the demonstrated cause of the false rejection. Always comparing `abs(estimate - target)` with `k * standard_error` was also rejected because either side can overflow for finite inputs. Log-domain comparison was not selected because it introduces transcendental rounding into a gate whose operands already admit an arithmetic decision. + +Causal repair `4ffdf3665b3bbafd3b0bbf06b599fe71498169ab` therefore uses direct represented subtraction and multiplication whenever those results are finite. If only the finite positive bound overflows, any finite residual is inside it; if only the residual overflows, a finite bound cannot cover it. Scale normalization remains only for the both-overflow case. The nearby rejection in the public contract preserves the original decision boundary. CHANGELOG trace: `d822c0ba7fa548c3283e462323c56d8f5705de31`. + +## Ownership and risk + +This is TEPP Validation decision semantics. It does not introduce reusable static psychometric estimation into TEPP, does not move arithmetic owned by `fast-mlsirm`, and does not depend on an unreleased contextual-orchestrator contract. The change is confined to the existing `accept_within_standard_errors` Domain Service and its public contract. + +Residual risk remains in binary64 tie behavior when both the direct residual and direct bound overflow and the normalized fallback must decide the comparison. No broader correctly-rounded claim is made without a separate represented-input counterexample and RED. + +## Standards trace + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://standards.ieee.org/ieee/754/6210/ + +International Organization for Standardization. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). https://www.iso.org/standard/80985.html + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. + +AERA, APA, and NCME announced the Joint Committee charged with revising the 2014 edition on June 12, 2024; as of 2026-09-04 the 2014 edition remains the published edition used by this trace. https://www.aera.net/Newsroom/Members-of-the-Joint-Committee-for-the-Revision-of-the-Standards-for-Educational-and-Psychological-Testing-Named From a9a45714eb6266f6b922086112b313060e77c522 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 20:59:36 +0900 Subject: [PATCH 244/576] test(validation): expose both-overflow acceptance rounding --- ...ror_acceptance_scale_underflow_contract.rs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/crates/validation_core/tests/standard_error_acceptance_scale_underflow_contract.rs b/crates/validation_core/tests/standard_error_acceptance_scale_underflow_contract.rs index b34ceb217..a9bd5368c 100644 --- a/crates/validation_core/tests/standard_error_acceptance_scale_underflow_contract.rs +++ b/crates/validation_core/tests/standard_error_acceptance_scale_underflow_contract.rs @@ -41,3 +41,41 @@ fn scale_underflow_repair_does_not_accept_a_smaller_finite_bound() { "restoring the finite bound must preserve a nearby rejection" ); } + +#[test] +fn both_overflow_fallback_does_not_round_an_exact_rejection_into_acceptance() { + let estimate = f64::from_bits(0x7fee_446c_f80d_ddbc); + let target = f64::from_bits(0xffe2_d7e3_9796_6af3); + let standard_error = f64::from_bits(0x7362_0ad2_2ddb_6f38); + let multiplier = f64::from_bits(0x4c85_c69a_c1c7_a9ed); + + assert!((estimate - target).is_infinite()); + assert!((multiplier * standard_error).is_infinite()); + + let scale = estimate.abs().max(target.abs()).max(standard_error).max(1.0); + let predecessor_scaled_error = (estimate / scale) - (target / scale); + let predecessor_scaled_bound = multiplier * (standard_error / scale); + assert_eq!(predecessor_scaled_error, predecessor_scaled_bound); + + assert_eq!( + accept_within_standard_errors(estimate, target, standard_error, multiplier), + Ok(false), + "the both-overflow fallback must preserve the exact represented-input inequality instead of accepting a normalization tie" + ); +} + +#[test] +fn both_overflow_fallback_accepts_the_adjacent_multiplier_that_crosses_the_boundary() { + let estimate = f64::from_bits(0x7fee_446c_f80d_ddbc); + let target = f64::from_bits(0xffe2_d7e3_9796_6af3); + let standard_error = f64::from_bits(0x7362_0ad2_2ddb_6f38); + let multiplier = f64::from_bits(0x4c85_c69a_c1c7_a9ee); + + assert!((estimate - target).is_infinite()); + assert!((multiplier * standard_error).is_infinite()); + assert_eq!( + accept_within_standard_errors(estimate, target, standard_error, multiplier), + Ok(true), + "one ULP larger multiplier is on the admissible side of the represented-input boundary" + ); +} From 425e89638d594fdb2f3586d73f021b60a530b456 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 21:03:20 +0900 Subject: [PATCH 245/576] fix(validation): compare both-overflow SE bounds exactly --- crates/validation_core/src/monte_carlo.rs | 99 +++++++++++++++++++---- 1 file changed, 85 insertions(+), 14 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 5df3c6642..b0a9c2c7b 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -285,16 +285,89 @@ pub fn summarize_replications( summary.validate() } +/// Decode a nonnegative finite binary64 magnitude into an exact integer significand and power-of-two exponent. +fn binary64_magnitude_components(value: f64) -> (u64, i32) { + let bits = value.to_bits() & 0x7fff_ffff_ffff_ffff; + let exponent_bits = ((bits >> 52) & 0x7ff) as i32; + let fraction = bits & 0x000f_ffff_ffff_ffff; + if exponent_bits == 0 { + (fraction, -1074) + } else { + ((1_u64 << 52) | fraction, exponent_bits - 1023 - 52) + } +} + +/// Compare two exact nonzero `significand * 2^exponent` values without floating-point rounding. +fn scaled_u128_le( + lhs_significand: u128, + lhs_exponent: i32, + rhs_significand: u128, + rhs_exponent: i32, +) -> bool { + let lhs_bits = 128_i32 - lhs_significand.leading_zeros() as i32; + let rhs_bits = 128_i32 - rhs_significand.leading_zeros() as i32; + let lhs_top_exponent = lhs_exponent + lhs_bits - 1; + let rhs_top_exponent = rhs_exponent + rhs_bits - 1; + if lhs_top_exponent != rhs_top_exponent { + return lhs_top_exponent < rhs_top_exponent; + } + + let common_exponent = lhs_exponent.min(rhs_exponent); + let lhs_shift = (lhs_exponent - common_exponent) as u32; + let rhs_shift = (rhs_exponent - common_exponent) as u32; + debug_assert!(lhs_shift < 128); + debug_assert!(rhs_shift < 128); + (lhs_significand << lhs_shift) <= (rhs_significand << rhs_shift) +} + +/// Compare the exact represented residual magnitude with `k * SE` after both direct operations overflow. +fn both_overflow_acceptance( + estimate: f64, + target: f64, + standard_error: f64, + k: f64, +) -> bool { + let (estimate_significand, estimate_exponent) = + binary64_magnitude_components(estimate.abs()); + let (target_significand, target_exponent) = binary64_magnitude_components(target.abs()); + let common_residual_exponent = estimate_exponent.min(target_exponent); + let estimate_shift = (estimate_exponent - common_residual_exponent) as u32; + let target_shift = (target_exponent - common_residual_exponent) as u32; + + // Finite subtraction can overflow only for opposite signs whose magnitudes + // are close enough to the top of binary64 that exact alignment needs at most + // one 53-bit significand-width shift. + debug_assert!(estimate_shift <= 53); + debug_assert!(target_shift <= 53); + let residual_significand = ((estimate_significand as u128) << estimate_shift) + + ((target_significand as u128) << target_shift); + + let (k_significand, k_exponent) = binary64_magnitude_components(k); + let (se_significand, se_exponent) = binary64_magnitude_components(standard_error); + let bound_significand = (k_significand as u128) * (se_significand as u128); + let bound_exponent = k_exponent + se_exponent; + + scaled_u128_le( + residual_significand, + common_residual_exponent, + bound_significand, + bound_exponent, + ) +} + /// SE-aware acceptance: accept when `|estimate − target| ≤ k · se`. /// /// Finite represented residuals and finite represented `k · se` bounds are -/// compared before normalization. This preserves a positive acceptance bound +/// compared before any normalization. This preserves a positive acceptance bound /// when dividing `se` by a much larger estimate/target scale would underflow to -/// zero. Scale-normalized comparison is reserved for the only ambiguous binary64 -/// case: both the direct residual subtraction and direct bound multiplication -/// overflow. A zero standard error or zero multiplier remains an exact-recovery -/// gate and is compared before either path. Exact recovery uses numeric equality, -/// for which IEEE `-0.0` and `+0.0` denote the same zero-valued scientific result. +/// zero. If only the positive bound overflows, every finite residual is covered; +/// if only the residual overflows, a finite bound cannot cover it. When both +/// direct operations overflow, TEPP compares the exact binary64 input rationals +/// by decoding their integer significands and powers of two, avoiding a false +/// accept/reject caused by independently rounded normalization. A zero standard +/// error or zero multiplier remains an exact-recovery gate and is compared before +/// either path. Exact recovery uses numeric equality, for which IEEE `-0.0` and +/// `+0.0` denote the same zero-valued scientific result. /// /// # Errors /// @@ -335,14 +408,12 @@ pub fn accept_within_standard_errors( return Ok(false); } - let scale = estimate - .abs() - .max(target.abs()) - .max(standard_error) - .max(1.0); - let scaled_error = (estimate / scale) - (target / scale); - let scaled_bound = k * (standard_error / scale); - Ok(scaled_error.abs() <= scaled_bound) + Ok(both_overflow_acceptance( + estimate, + target, + standard_error, + k, + )) } #[allow(clippy::cast_possible_truncation)] From e62706291ad556c6d9078a2bc50dcf3bca5a6feb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 21:03:51 +0900 Subject: [PATCH 246/576] docs(changelog): record exact both-overflow SE comparison --- .../validation-standard-error-acceptance-both-overflow.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-standard-error-acceptance-both-overflow.md diff --git a/CHANGELOG.d/validation-standard-error-acceptance-both-overflow.md b/CHANGELOG.d/validation-standard-error-acceptance-both-overflow.md new file mode 100644 index 000000000..e767cca8c --- /dev/null +++ b/CHANGELOG.d/validation-standard-error-acceptance-both-overflow.md @@ -0,0 +1,3 @@ +### Fixed + +- `validation_core::accept_within_standard_errors` now compares the exact represented binary64 residual magnitude with the exact represented `k × SE` product when both direct operations overflow. This prevents independently rounded scale normalization from turning a strict rejection into a false acceptance at the full-range boundary while preserving the adjacent admissible multiplier. From ba2f169424e7551762ed32ac47733a9d8261266c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 21:04:28 +0900 Subject: [PATCH 247/576] docs(research): trace exact both-overflow SE decision --- ...ceptance-both-overflow-exact-comparison.md | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 docs/research/standard-error-acceptance-both-overflow-exact-comparison.md diff --git a/docs/research/standard-error-acceptance-both-overflow-exact-comparison.md b/docs/research/standard-error-acceptance-both-overflow-exact-comparison.md new file mode 100644 index 000000000..410d2f0b2 --- /dev/null +++ b/docs/research/standard-error-acceptance-both-overflow-exact-comparison.md @@ -0,0 +1,50 @@ +# Standard-error acceptance both-overflow exact comparison + +## Problem and scientific contract + +TEPP's Validation bounded context exposes `accept_within_standard_errors(estimate, target, standard_error, k)` as the deterministic CPU `f64` reference for the decision + +`|estimate - target| <= k * standard_error`. + +The preceding repair made finite represented residuals and finite represented `k × SE` bounds direct-first, but retained scale normalization when both direct operations overflowed. Fresh represented-input review found that the remaining fallback can round both normalized sides to the same binary64 value even when the exact rational values represented by the original four binary64 inputs are strictly ordered. + +Public RED `a9a45714eb6266f6b922086112b313060e77c522` uses these exact binary64 payloads: + +- `estimate = 0x1.e446cf80dddbcp+1023` (`0x7fee446cf80dddbc`), +- `target = -0x1.2d7e397966af3p+1023` (`0xffe2d7e397966af3`), +- `standard_error = 0x1.20ad22ddb6f38p+823` (`0x73620ad22ddb6f38`), and +- `k = 0x1.5c69ac1c7a9edp+201` (`0x4c85c69ac1c7a9ed`). + +Both `estimate - target` and `k * standard_error` overflow to positive infinity in magnitude. The predecessor then normalized by the largest magnitude and obtained the same rounded binary64 value for the residual and bound (`0x1.9f6056b74e5cap+0`), so it accepted. An exact rational comparison of the represented inputs shows the bound is smaller than the residual; the difference is approximately `3.2267077731482595e292`. The immediately adjacent multiplier `0x1.5c69ac1c7a9eep+201` crosses the represented-input boundary and must remain accepted. + +## Causal repair and constraints + +Causal repair `425e89638d594fdb2f3586d73f021b60a530b456` keeps the existing direct-first cases and changes only the both-overflow branch. It decodes each finite binary64 magnitude into its exact integer significand and power-of-two exponent. The opposite-sign residual becomes an exact sum of two at-most-53-bit significands; `k × SE` becomes an exact at-most-106-bit significand product. The comparison then aligns those integer values by powers of two inside `u128`, avoiding another floating-point normalization step. + +This bounded method is possible because direct finite subtraction can overflow only for opposite-sign inputs near the top of binary64 range. Their exact significands therefore require at most one 53-bit-width alignment shift. The product of two binary64 significands needs at most 106 bits, so the complete comparison remains allocation-free and deterministic in Rust. + +Always retaining the scale-normalized fallback was rejected because it is the demonstrated cause of the false acceptance. Log-domain comparison was rejected because transcendental rounding is unnecessary for values that already have exact binary decompositions. Arbitrary-precision runtime arithmetic was also rejected: the relevant exact integers fit `u128`, so a heap-allocated big-number dependency would add cost and supply-chain surface without improving this bounded decision. + +The nearby adjacent-multiplier contract prevents the repair from turning the both-overflow region into a blanket rejection. CHANGELOG trace: `e62706291ad556c6d9078a2bc50dcf3bca5a6feb`. + +## Ownership, validation, and non-claims + +This is TEPP Validation decision semantics, not reusable static psychometric estimation. It does not move arithmetic owned by `fast-mlsirm`, does not consume mutable sibling source, and does not depend on an unreleased `contextual-orchestrator` contract. + +The RED and adjacent control are public Rust contract tests. An independent exact-rational search was used only to discover and verify the counterexample; it is not production arithmetic, does not replace the Rust decision path, and is not scientific acceptance evidence by itself. + +This repair claims exact represented-input comparison only for the existing both-overflow branch of `accept_within_standard_errors`. It does not claim that unrelated Wilson, Monte Carlo summary, strict-interior interval, or other validation formulas are globally correctly rounded. + +## Standards trace + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://standards.ieee.org/ieee/754/6210/ + +IEEE 754-2019 remains an Active Standard as checked on 2026-09-04. IEEE P754, approved as a PAR on 2024-06-06, is an Active PAR intended to supersede 754-2019 and is not treated here as a published replacement. https://standards.ieee.org/ieee/754/11684/ + +International Organization for Standardization. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). https://www.iso.org/standard/80985.html + +ISO/IEC 60559:2020 remains Published, stage 60.60, as checked on 2026-09-04. + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. + +AERA, APA, and NCME announced the Joint Committee charged with revising the 2014 edition on 2024-06-12. As checked on 2026-09-04, that announcement still describes the committee as revising the 2014 edition; TEPP therefore continues to use the 2014 published edition rather than treating an unpublished revision as normative authority. https://www.aera.net/Newsroom/Members-of-the-Joint-Committee-for-the-Revision-of-the-Standards-for-Educational-and-Psychological-Testing-Named From 23c3262c824609d79dc14d45bb0acb5a54e99a51 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 22:03:50 +0900 Subject: [PATCH 248/576] test(validation): expose finite SE rounding tie --- ...acceptance_finite_rounding_tie_contract.rs | 36 +++++++++++++++++++ 1 file changed, 36 insertions(+) create mode 100644 crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs diff --git a/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs b/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs new file mode 100644 index 000000000..8f8dbd64f --- /dev/null +++ b/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs @@ -0,0 +1,36 @@ +use validation_core::accept_within_standard_errors; + +#[test] +fn finite_rounded_tie_preserves_strict_rejection_from_represented_inputs() { + let estimate = 1.0_f64; + let target = 0.0_f64; + let standard_error = f64::from_bits(0x3fef_ffff_fc00_0000); + let multiplier = f64::from_bits(0x3ff0_0000_0200_0000); + + let represented_residual = estimate - target; + let rounded_bound = multiplier * standard_error; + assert_eq!(represented_residual, 1.0); + assert_eq!(rounded_bound, represented_residual); + + // The represented factors are (1 - 2^-27) and (1 + 2^-27), + // so their exact product is 1 - 2^-54: strictly below the residual. + assert_eq!( + accept_within_standard_errors(estimate, target, standard_error, multiplier), + Ok(false), + "a multiplication tie rounded to the residual must not erase the strict represented-input rejection" + ); +} + +#[test] +fn finite_rounded_tie_repair_keeps_the_adjacent_acceptance() { + let estimate = 1.0_f64; + let target = 0.0_f64; + let standard_error = f64::from_bits(0x3fef_ffff_fc00_0000); + let multiplier = f64::from_bits(0x3ff0_0000_0200_0001); + + assert_eq!( + accept_within_standard_errors(estimate, target, standard_error, multiplier), + Ok(true), + "one ULP larger multiplier is on the admissible side of the represented-input boundary" + ); +} From 6a2add488cfa6bb5ac3cc854a107f287b61bbed5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 22:06:15 +0900 Subject: [PATCH 249/576] fix(validation): preserve finite represented SE tie --- crates/validation_core/src/monte_carlo.rs | 34 ++++++++++++++++++----- 1 file changed, 27 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index b0a9c2c7b..74368391e 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -355,15 +355,27 @@ fn both_overflow_acceptance( ) } +/// Return the error-free low term of a finite binary64 subtraction. +fn subtraction_roundoff(minuend: f64, subtrahend: f64, difference: f64) -> f64 { + let virtual_subtrahend = minuend - difference; + let virtual_minuend = difference + virtual_subtrahend; + let subtrahend_roundoff = virtual_subtrahend - subtrahend; + let minuend_roundoff = minuend - virtual_minuend; + minuend_roundoff + subtrahend_roundoff +} + /// SE-aware acceptance: accept when `|estimate − target| ≤ k · se`. /// /// Finite represented residuals and finite represented `k · se` bounds are -/// compared before any normalization. This preserves a positive acceptance bound -/// when dividing `se` by a much larger estimate/target scale would underflow to -/// zero. If only the positive bound overflows, every finite residual is covered; -/// if only the residual overflows, a finite bound cannot cover it. When both -/// direct operations overflow, TEPP compares the exact binary64 input rationals -/// by decoding their integer significands and powers of two, avoiding a false +/// compared before any normalization. If both rounded finite quantities are equal, +/// an exact subtraction together with a nonzero fused multiply-add product residual +/// disambiguates a multiplication tie instead of silently accepting a strict +/// represented-input rejection. This preserves a positive acceptance bound when +/// dividing `se` by a much larger estimate/target scale would underflow to zero. +/// If only the positive bound overflows, every finite residual is covered; if only +/// the residual overflows, a finite bound cannot cover it. When both direct +/// operations overflow, TEPP compares the exact binary64 input rationals by +/// decoding their integer significands and powers of two, avoiding a false /// accept/reject caused by independently rounded normalization. A zero standard /// error or zero multiplier remains an exact-recovery gate and is compared before /// either path. Exact recovery uses numeric equality, for which IEEE `-0.0` and @@ -397,7 +409,15 @@ pub fn accept_within_standard_errors( let direct_bound = k * standard_error; if direct_error.is_finite() { if direct_bound.is_finite() { - return Ok(direct_error.abs() <= direct_bound); + let residual = direct_error.abs(); + if residual == direct_bound && residual != 0.0 { + let difference_roundoff = subtraction_roundoff(estimate, target, direct_error); + let product_roundoff = k.mul_add(standard_error, -direct_bound); + if difference_roundoff == 0.0 && product_roundoff != 0.0 { + return Ok(product_roundoff.is_sign_positive()); + } + } + return Ok(residual <= direct_bound); } // A finite residual is necessarily inside a positive bound whose // represented multiplication overflowed beyond binary64's finite range. From 3656deb2f2abc0a83027618a823497a829d9227e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 22:07:16 +0900 Subject: [PATCH 250/576] test(validation): cover finite SE tie discriminator --- ...acceptance_finite_rounding_tie_contract.rs | 34 +++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs b/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs index 8f8dbd64f..1ecb188e2 100644 --- a/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs +++ b/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs @@ -34,3 +34,37 @@ fn finite_rounded_tie_repair_keeps_the_adjacent_acceptance() { "one ULP larger multiplier is on the admissible side of the represented-input boundary" ); } + +#[test] +fn finite_rounded_tie_accepts_when_exact_product_is_above_the_residual() { + let estimate = 1.0_f64; + let target = 0.0_f64; + let standard_error = f64::from_bits(0x3fef_ffff_ffff_fc19); + let multiplier = f64::from_bits(0x3ff0_0000_0000_01f4); + + assert_eq!(multiplier * standard_error, 1.0); + assert_eq!( + accept_within_standard_errors(estimate, target, standard_error, multiplier), + Ok(true), + "the tie discriminator must preserve acceptance when the exact represented product lies above one" + ); +} + +#[test] +fn finite_exact_tie_remains_accepted_when_product_has_no_roundoff() { + assert_eq!( + accept_within_standard_errors(1.0, 0.0, 1.0, 1.0), + Ok(true) + ); +} + +#[test] +fn inexact_subtraction_tie_stays_on_the_conservative_rounded_path() { + let target = f64::from_bits(1); + assert_eq!(1.0 - target, 1.0); + assert_eq!( + accept_within_standard_errors(1.0, target, 1.0, 1.0), + Ok(true), + "this repair must not infer a product-only correction when subtraction itself rounded" + ); +} From 090f5124588cb184fc2ef644d91355f2b2c2082b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 22:07:30 +0900 Subject: [PATCH 251/576] docs(changelog): record finite SE rounding tie --- ...validation-standard-error-acceptance-finite-rounding-tie.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-standard-error-acceptance-finite-rounding-tie.md diff --git a/CHANGELOG.d/validation-standard-error-acceptance-finite-rounding-tie.md b/CHANGELOG.d/validation-standard-error-acceptance-finite-rounding-tie.md new file mode 100644 index 000000000..e43026926 --- /dev/null +++ b/CHANGELOG.d/validation-standard-error-acceptance-finite-rounding-tie.md @@ -0,0 +1,3 @@ +### Fixed + +- Preserve the represented-input inequality in `accept_within_standard_errors` when a finite residual and finite `k * SE` round to the same binary64 value: if subtraction is exact and FMA exposes a nonzero product residual, that residual now disambiguates the tie instead of defaulting to acceptance. This closes the concrete `(1 - 2^-27) * (1 + 2^-27) = 1 - 2^-54` false-accept boundary while leaving inexact-subtraction ties on the existing conservative rounded path. From 9e1ecdb0b2abd727aae4534b79b648309fad80dd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 22:07:58 +0900 Subject: [PATCH 252/576] docs(research): trace finite SE rounding tie --- ...rd-error-acceptance-finite-rounding-tie.md | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 docs/research/standard-error-acceptance-finite-rounding-tie.md diff --git a/docs/research/standard-error-acceptance-finite-rounding-tie.md b/docs/research/standard-error-acceptance-finite-rounding-tie.md new file mode 100644 index 000000000..0b98c5be5 --- /dev/null +++ b/docs/research/standard-error-acceptance-finite-rounding-tie.md @@ -0,0 +1,47 @@ +# Standard-error acceptance finite rounding tie + +## Problem and scientific contract + +TEPP Validation exposes `accept_within_standard_errors(estimate, target, standard_error, k)` for the deterministic decision + +`|estimate - target| <= k * standard_error`. + +The preceding GAP-079/GAP-080 repairs covered scale-underflow and both-overflow failures. Fresh represented-input review found a separate finite/finite boundary: both direct operations can remain finite yet round to the same binary64 value even when the exact dyadic values represented by the original inputs are strictly ordered. + +Public RED `23c3262c824609d79dc14d45bb0acb5a54e99a51` uses `estimate = 1`, `target = 0`, `standard_error = 0x1.ffffffc000000p-1 = 1 - 2^-27`, and `k = 0x1.0000002000000p+0 = 1 + 2^-27`. The subtraction is exact, so the residual is exactly 1. The exact represented product is + +`(1 - 2^-27)(1 + 2^-27) = 1 - 2^-54`, + +which is strictly smaller than the residual. Binary64 multiplication rounds that midpoint to the even value `1.0`, so the predecessor compared `1.0 <= 1.0` and falsely accepted. + +The adjacent multiplier `0x1.0000002000001p+0` remains accepted. A second control uses factors whose exact product lies slightly above 1 while the rounded product is still 1, ensuring the discriminator does not turn rounded ties into blanket rejection. + +## Causal repair and constraints + +Causal repair `6a2add488cfa6bb5ac3cc854a107f287b61bbed5` keeps every non-tie finite comparison and both-overflow exact comparator unchanged. On a nonzero finite rounded tie only, TEPP computes the error-free low term of `estimate - target`. If subtraction was exact, it uses fused multiply-add `k.mul_add(SE, -rounded_bound)` to expose the sign of multiplication roundoff. A negative nonzero product residual proves the exact represented bound is below the tied residual and rejects; a positive residual accepts. A zero product residual or an inexact subtraction stays on the predecessor rounded comparison rather than pretending that this bounded repair proves a more general exact inequality. + +This is deliberately narrower than replacing all finite comparisons with arbitrary-precision arithmetic. A general sparse-dyadic comparator was rejected for this finding because the demonstrated defect needs only the product-rounding sign when subtraction itself is exact. Log-domain comparison was rejected because transcendental rounding would replace an exact binary boundary with another approximation. Treating every rounded tie as rejection was rejected because exact products can lie on either side of the same rounded value. + +Coverage/edge contract `3656deb2f2abc0a83027618a823497a829d9227e` adds the strict-rejection RED, the adjacent accepted multiplier, a positive product-roundoff tie, an exactly represented tie, and an inexact-subtraction tie that must remain on the conservative rounded path. CHANGELOG trace: `090f5124588cb184fc2ef644d91355f2b2c2082b`. + +## Ownership and non-claims + +This is TEPP Validation decision semantics in `validation_core`. It is not reusable static psychometric estimation owned by `fast-mlsirm`, does not alter Longitudinal Modeling composition, and does not consume mutable `contextual-orchestrator` source. + +The public Rust contract is the production authority. Exact dyadic arithmetic was used to establish the counterexample and controls; it does not replace scientific acceptance, hosted CI, or current-head review evidence. + +This repair does not claim globally exact comparison for every finite rounded tie. In particular, when subtraction itself rounded or the FMA product residual is zero, the function preserves the existing finite rounded decision. A future widening requires an independent represented-input counterexample and a bounded exact method that does not create a second writer. + +## Standards trace + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE. https://standards.ieee.org/ieee/754/6210/ + +IEEE 754-2019 was rechecked on 2026-09-04 and remains an Active Standard. IEEE P754 remains an Active PAR approved 2024-06-06 to supersede 754-2019; it is not treated as a published replacement. https://standards.ieee.org/ieee/754/11684/ + +International Organization for Standardization. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). ISO. https://www.iso.org/standard/80985.html + +ISO/IEC 60559:2020 was rechecked on 2026-09-04 and remains Published at stage 60.60. + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. + +AERA continues to publish the 2014 edition, while the Joint Committee announced in 2024 remains charged with revising that edition. TEPP therefore does not treat an unpublished revision as current normative authority. From 58cbc03253997865b7c8ec19fb501fc89c22c851 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:03:06 +0900 Subject: [PATCH 253/576] test(validation): expose subtraction-rounded SE tie --- ...acceptance_finite_rounding_tie_contract.rs | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs b/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs index 1ecb188e2..7c2d5d60f 100644 --- a/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs +++ b/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs @@ -59,12 +59,27 @@ fn finite_exact_tie_remains_accepted_when_product_has_no_roundoff() { } #[test] -fn inexact_subtraction_tie_stays_on_the_conservative_rounded_path() { +fn inexact_subtraction_tie_below_bound_remains_accepted() { let target = f64::from_bits(1); assert_eq!(1.0 - target, 1.0); assert_eq!( accept_within_standard_errors(1.0, target, 1.0, 1.0), Ok(true), - "this repair must not infer a product-only correction when subtraction itself rounded" + "the exact represented residual is below the exact unit bound" + ); +} + +#[test] +fn inexact_subtraction_tie_above_bound_preserves_strict_rejection() { + let target = f64::from_bits(0xbc90_0000_0000_0000); // -2^-54 + let represented_residual = 1.0 - target; + assert_eq!(represented_residual, 1.0); + + // The exact represented residual is 1 + 2^-54, but the subtraction rounds + // to 1.0. The bound is exactly 1.0, so the scientific inequality is false. + assert_eq!( + accept_within_standard_errors(1.0, target, 1.0, 1.0), + Ok(false), + "subtraction rounding must not turn a strict represented-input rejection into equality" ); } From 68a6fd98b4f661ad5d4c3c35dc8b9074f2c59281 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:08:32 +0900 Subject: [PATCH 254/576] fix(validation): preserve subtraction-rounded SE ties --- crates/validation_core/src/monte_carlo.rs | 26 +++++++++++++++-------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 74368391e..75dfa5a3f 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -367,13 +367,16 @@ fn subtraction_roundoff(minuend: f64, subtrahend: f64, difference: f64) -> f64 { /// SE-aware acceptance: accept when `|estimate − target| ≤ k · se`. /// /// Finite represented residuals and finite represented `k · se` bounds are -/// compared before any normalization. If both rounded finite quantities are equal, -/// an exact subtraction together with a nonzero fused multiply-add product residual -/// disambiguates a multiplication tie instead of silently accepting a strict -/// represented-input rejection. This preserves a positive acceptance bound when -/// dividing `se` by a much larger estimate/target scale would underflow to zero. -/// If only the positive bound overflows, every finite residual is covered; if only -/// the residual overflows, a finite bound cannot cover it. When both direct +/// compared before any normalization. If both rounded finite quantities are equal +/// and nonzero, TEPP compares the error-free subtraction correction (with its sign +/// adjusted for the absolute residual) with the fused multiply-add product +/// correction. Different correction projections preserve the represented-input +/// ordering even when either direct operation rounded to the same binary64 value; +/// equal projected corrections remain on the ordinary rounded decision instead of +/// claiming a broader exact comparator. This also preserves a positive acceptance +/// bound when dividing `se` by a much larger estimate/target scale would underflow +/// to zero. If only the positive bound overflows, every finite residual is covered; +/// if only the residual overflows, a finite bound cannot cover it. When both direct /// operations overflow, TEPP compares the exact binary64 input rationals by /// decoding their integer significands and powers of two, avoiding a false /// accept/reject caused by independently rounded normalization. A zero standard @@ -412,9 +415,14 @@ pub fn accept_within_standard_errors( let residual = direct_error.abs(); if residual == direct_bound && residual != 0.0 { let difference_roundoff = subtraction_roundoff(estimate, target, direct_error); + let residual_roundoff = if direct_error.is_sign_negative() { + -difference_roundoff + } else { + difference_roundoff + }; let product_roundoff = k.mul_add(standard_error, -direct_bound); - if difference_roundoff == 0.0 && product_roundoff != 0.0 { - return Ok(product_roundoff.is_sign_positive()); + if residual_roundoff != product_roundoff { + return Ok(residual_roundoff < product_roundoff); } } return Ok(residual <= direct_bound); From c4f752f30121901bfbf4a96b03fdef0ccd9f4bf3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:09:00 +0900 Subject: [PATCH 255/576] docs(changelog): record subtraction-rounded SE tie repair --- ...idation-standard-error-acceptance-subtraction-rounding.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CHANGELOG.d/validation-standard-error-acceptance-subtraction-rounding.md diff --git a/CHANGELOG.d/validation-standard-error-acceptance-subtraction-rounding.md b/CHANGELOG.d/validation-standard-error-acceptance-subtraction-rounding.md new file mode 100644 index 000000000..c2d12be18 --- /dev/null +++ b/CHANGELOG.d/validation-standard-error-acceptance-subtraction-rounding.md @@ -0,0 +1,5 @@ +# Validation: preserve subtraction-rounded standard-error decisions + +- Reject SE-aware recovery when a finite subtraction rounds onto the same binary64 value as an exact finite `k * SE` bound even though the exact dyadic residual represented by the inputs is larger. +- Compare the error-free subtraction correction, sign-adjusted for the absolute residual, with the FMA product correction only on nonzero finite rounded ties; retain the prior rounded decision when those correction projections are equal. +- Keep the finite direct path, scale-underflow repair, and both-overflow exact significand/exponent comparator unchanged outside this tie boundary. From 0d630fbdda85173cf71a9a82673c52070345e191 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:09:27 +0900 Subject: [PATCH 256/576] docs(research): trace subtraction-rounded SE tie --- ...d-error-acceptance-subtraction-rounding.md | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 docs/research/standard-error-acceptance-subtraction-rounding.md diff --git a/docs/research/standard-error-acceptance-subtraction-rounding.md b/docs/research/standard-error-acceptance-subtraction-rounding.md new file mode 100644 index 000000000..55b3b8266 --- /dev/null +++ b/docs/research/standard-error-acceptance-subtraction-rounding.md @@ -0,0 +1,35 @@ +# Standard-error acceptance subtraction rounding + +## Problem and represented-input contract + +TEPP Validation exposes `accept_within_standard_errors(estimate, target, standard_error, k)` for the deterministic decision `|estimate - target| <= k * standard_error`. + +GAP-081 corrected a finite rounded tie when subtraction was exact and multiplication alone rounded across the decision boundary. A fresh review found the complementary finite case: subtraction itself can round onto the finite bound and erase a strict rejection. + +Public RED `58cbc03253997865b7c8ec19fb501fc89c22c851` uses `estimate = 1`, `target = -2^-54` (`0xbc90_0000_0000_0000`), `standard_error = 1`, and `k = 1`. The exact dyadic residual represented by the inputs is `1 + 2^-54`, strictly greater than the exact unit bound, while binary64 subtraction rounds the residual to `1.0`. The predecessor therefore fell through to `1.0 <= 1.0` and falsely accepted. The existing positive-min-subnormal target remains an acceptance control because its exact residual is below the unit bound even though its subtraction also rounds to `1.0`. + +## Causal repair + +Causal repair `68a6fd98b4f661ad5d4c3c35dc8b9074f2c59281` remains inside the existing `validation_core` writer. On a nonzero finite rounded equality only, it obtains the error-free low term of `estimate - target`, changes that low-term sign when the rounded difference is negative so it describes the absolute residual, and compares it with the FMA low term of `k * standard_error`. If those two correction projections differ, their order determines the represented-input decision. If they are equal, TEPP preserves the predecessor rounded decision rather than claiming a global exact comparator. + +This preserves the earlier finite-direct, scale-underflow, one-sided overflow, and both-overflow paths. It also preserves the GAP-081 multiplication-only boundary as the special case whose residual correction is zero. + +Alternatives rejected: blanket rejection of rounded ties would break valid below-bound cases; replacing all finite comparisons with arbitrary precision would widen the owner and implementation surface beyond the demonstrated defect; scale or logarithmic comparison would introduce another rounded approximation at a boundary already expressible with binary floating-point error terms. + +CHANGELOG trace: `c4f752f30121901bfbf4a96b03fdef0ccd9f4bf3`. + +## Restack and ownership + +Before this RED, protected `main` advanced through #489 to `b18bca1c69ef8d1799fcd3af6bf4412498e007c3`. The Validation branch was one commit behind and diverged from that protected head. It was repaired without force push or destructive rebase by merge-restack commit `a2754592987be9e88941d220565c45e34f621a4e`, retaining all Validation ancestry while inheriting #489's workflow-load repair. + +This remains TEPP Validation decision semantics. It is not reusable static psychometric estimation owned by `fast-mlsirm`, does not alter Longitudinal Modeling composition, and does not consume mutable `contextual-orchestrator` source. + +## Standards trace + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE. + +International Organization for Standardization. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). ISO. + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. + +The published standards above govern the current trace. Unpublished revisions are not treated as current normative authority. From b2c06512b6d795555269862c1c3d7e4bc67f3f18 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:16:11 +0900 Subject: [PATCH 257/576] test(validation): cover negative subtraction tie sign --- ...ror_acceptance_finite_rounding_tie_contract.rs | 15 +++++++++++++++ 1 file changed, 15 insertions(+) diff --git a/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs b/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs index 7c2d5d60f..b7fa8e223 100644 --- a/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs +++ b/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs @@ -83,3 +83,18 @@ fn inexact_subtraction_tie_above_bound_preserves_strict_rejection() { "subtraction rounding must not turn a strict represented-input rejection into equality" ); } + +#[test] +fn negative_rounded_difference_uses_absolute_residual_correction_sign() { + let target = f64::from_bits(0x3c90_0000_0000_0000); // +2^-54 + let represented_difference = -1.0 - target; + assert_eq!(represented_difference, -1.0); + + // The exact difference is -1 - 2^-54, so the absolute residual is + // 1 + 2^-54 and remains strictly above the exact unit bound. + assert_eq!( + accept_within_standard_errors(-1.0, target, 1.0, 1.0), + Ok(false), + "the subtraction low term must flip sign when the rounded difference is negative" + ); +} From 9aa0dc8d20b1e0cee85778bead526f1fd838721f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:16:44 +0900 Subject: [PATCH 258/576] docs(research): trace subtraction-tie sign coverage --- docs/research/standard-error-acceptance-subtraction-rounding.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/research/standard-error-acceptance-subtraction-rounding.md b/docs/research/standard-error-acceptance-subtraction-rounding.md index 55b3b8266..25bcec9ac 100644 --- a/docs/research/standard-error-acceptance-subtraction-rounding.md +++ b/docs/research/standard-error-acceptance-subtraction-rounding.md @@ -12,7 +12,7 @@ Public RED `58cbc03253997865b7c8ec19fb501fc89c22c851` uses `estimate = 1`, `targ Causal repair `68a6fd98b4f661ad5d4c3c35dc8b9074f2c59281` remains inside the existing `validation_core` writer. On a nonzero finite rounded equality only, it obtains the error-free low term of `estimate - target`, changes that low-term sign when the rounded difference is negative so it describes the absolute residual, and compares it with the FMA low term of `k * standard_error`. If those two correction projections differ, their order determines the represented-input decision. If they are equal, TEPP preserves the predecessor rounded decision rather than claiming a global exact comparator. -This preserves the earlier finite-direct, scale-underflow, one-sided overflow, and both-overflow paths. It also preserves the GAP-081 multiplication-only boundary as the special case whose residual correction is zero. +This preserves the earlier finite-direct, scale-underflow, one-sided overflow, and both-overflow paths. It also preserves the GAP-081 multiplication-only boundary as the special case whose residual correction is zero. Edge-coverage commit `b2c06512b6d795555269862c1c3d7e4bc67f3f18` adds the sign-symmetric negative rounded-difference case so the absolute-residual correction sign is exercised explicitly rather than left to branch inference. Alternatives rejected: blanket rejection of rounded ties would break valid below-bound cases; replacing all finite comparisons with arbitrary precision would widen the owner and implementation surface beyond the demonstrated defect; scale or logarithmic comparison would introduce another rounded approximation at a boundary already expressible with binary floating-point error terms. From b55c5473c8ee7a70ec2508d14d0755c2aeb38191 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:24:14 +0900 Subject: [PATCH 259/576] test(validation): expose subnormal SE bound tie --- ...acceptance_finite_rounding_tie_contract.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs b/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs index b7fa8e223..a22614e46 100644 --- a/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs +++ b/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs @@ -98,3 +98,31 @@ fn negative_rounded_difference_uses_absolute_residual_correction_sign() { "the subtraction low term must flip sign when the rounded difference is negative" ); } + +#[test] +fn subnormal_bound_rounding_must_not_hide_a_strict_rejection() { + let minimum_subnormal = f64::from_bits(1); + let multiplier = f64::from_bits(0x1e5_8000_0000_0000); // 1.5 * 2^-538 + let standard_error = f64::from_bits(0x1e6_0000_0000_0000); // 2^-537 + + assert_eq!(multiplier * standard_error, minimum_subnormal); + // The exact represented product is 3/4 of the minimum subnormal. Its FMA + // correction is only -1/4 ULP at zero and therefore rounds to signed zero. + assert_eq!(multiplier.mul_add(standard_error, -minimum_subnormal), -0.0); + + assert_eq!( + accept_within_standard_errors(minimum_subnormal, 0.0, standard_error, multiplier), + Ok(false), + "a product rounded up to the minimum subnormal must not cover the larger exact residual" + ); +} + +#[test] +fn exact_minimum_subnormal_bound_remains_accepted() { + let minimum_subnormal = f64::from_bits(1); + assert_eq!( + accept_within_standard_errors(minimum_subnormal, 0.0, minimum_subnormal, 1.0), + Ok(true), + "an exactly represented minimum-subnormal bound still covers an equal residual" + ); +} From 210cebc4980c861ddfd6d098bf1c9d66c8449e72 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:27:11 +0900 Subject: [PATCH 260/576] fix(validation): preserve subnormal SE bound ties --- crates/validation_core/src/monte_carlo.rs | 28 +++++++++++++++++++++-- 1 file changed, 26 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 75dfa5a3f..a517a60c1 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -320,6 +320,19 @@ fn scaled_u128_le( (lhs_significand << lhs_shift) <= (rhs_significand << rhs_shift) } +/// Compare one represented nonnegative binary64 magnitude with an exact product of two represented factors. +fn represented_magnitude_le_exact_product(value: f64, factor_a: f64, factor_b: f64) -> bool { + let (value_significand, value_exponent) = binary64_magnitude_components(value); + let (a_significand, a_exponent) = binary64_magnitude_components(factor_a); + let (b_significand, b_exponent) = binary64_magnitude_components(factor_b); + scaled_u128_le( + value_significand as u128, + value_exponent, + (a_significand as u128) * (b_significand as u128), + a_exponent + b_exponent, + ) +} + /// Compare the exact represented residual magnitude with `k * SE` after both direct operations overflow. fn both_overflow_acceptance( estimate: f64, @@ -371,8 +384,12 @@ fn subtraction_roundoff(minuend: f64, subtrahend: f64, difference: f64) -> f64 { /// and nonzero, TEPP compares the error-free subtraction correction (with its sign /// adjusted for the absolute residual) with the fused multiply-add product /// correction. Different correction projections preserve the represented-input -/// ordering even when either direct operation rounded to the same binary64 value; -/// equal projected corrections remain on the ordinary rounded decision instead of +/// ordering even when either direct operation rounded to the same binary64 value. +/// When both projected corrections are zero at a subnormal rounded bound and the +/// subtraction was exact, TEPP compares that represented residual with the exact +/// dyadic product of the represented `k` and `se`; this prevents FMA-underflowed +/// product error from turning a strict rejection into equality. Other equal +/// correction projections remain on the ordinary rounded decision instead of /// claiming a broader exact comparator. This also preserves a positive acceptance /// bound when dividing `se` by a much larger estimate/target scale would underflow /// to zero. If only the positive bound overflows, every finite residual is covered; @@ -424,6 +441,13 @@ pub fn accept_within_standard_errors( if residual_roundoff != product_roundoff { return Ok(residual_roundoff < product_roundoff); } + if residual_roundoff == 0.0 && direct_bound.is_subnormal() { + return Ok(represented_magnitude_le_exact_product( + residual, + k, + standard_error, + )); + } } return Ok(residual <= direct_bound); } From 338c270c65c10d305432029c455b9ac37c28c0f1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:27:54 +0900 Subject: [PATCH 261/576] docs(changelog): record subnormal SE tie repair --- ...n-standard-error-acceptance-subnormal-product-rounding.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CHANGELOG.d/validation-standard-error-acceptance-subnormal-product-rounding.md diff --git a/CHANGELOG.d/validation-standard-error-acceptance-subnormal-product-rounding.md b/CHANGELOG.d/validation-standard-error-acceptance-subnormal-product-rounding.md new file mode 100644 index 000000000..f4158490d --- /dev/null +++ b/CHANGELOG.d/validation-standard-error-acceptance-subnormal-product-rounding.md @@ -0,0 +1,5 @@ +# Validation: preserve subnormal standard-error bound decisions + +- Reject SE-aware recovery when an exact finite residual equals the rounded minimum-subnormal `k * SE` bound but the exact product represented by `k` and `SE` is smaller. +- On this subnormal finite-tie boundary, compare the exact represented residual magnitude with the exact dyadic product when both subtraction and FMA correction terms project to zero; keep exact minimum-subnormal equality accepted. +- Preserve the existing finite direct path, low-term tie discriminator, one-sided overflow behavior, and both-overflow exact comparator outside this boundary. From 8ac453c217a9b4dac31d47bbedb4d1be0e9d7a1e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:28:12 +0900 Subject: [PATCH 262/576] docs(research): trace subnormal SE bound decision --- ...r-acceptance-subnormal-product-rounding.md | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) create mode 100644 docs/research/standard-error-acceptance-subnormal-product-rounding.md diff --git a/docs/research/standard-error-acceptance-subnormal-product-rounding.md b/docs/research/standard-error-acceptance-subnormal-product-rounding.md new file mode 100644 index 000000000..7a54a2404 --- /dev/null +++ b/docs/research/standard-error-acceptance-subnormal-product-rounding.md @@ -0,0 +1,29 @@ +# Standard-error acceptance at the subnormal product boundary + +## Problem + +TEPP Validation evaluates `|estimate - target| <= k * standard_error` from represented binary64 inputs. GAP-081 and GAP-082 repaired finite rounded ties when multiplication or subtraction retained a nonzero error term. A narrower boundary remains when the rounded bound is subnormal: the exact product error itself can lie below half of the minimum subnormal and the FMA correction can therefore project to signed zero. + +Public RED `b55c5473c8ee7a70ec2508d14d0755c2aeb38191` uses an exact residual equal to the minimum positive subnormal, `estimate = 0x0.0000000000001p-1022`, `target = 0`, `k = 0x1.8p-538`, and `SE = 0x1p-537`. The exact dyadic product of the represented factors is `3/4` of the minimum subnormal, so the scientific inequality is false. Binary64 multiplication rounds that product up to the minimum subnormal, while `fma(k, SE, -rounded_bound)` rounds the `-1/4`-subnormal correction to signed zero. The predecessor therefore observed equal rounded residual/bound and equal zero correction projections and falsely accepted. + +The control uses `k = 1` and `SE = minimum_subnormal`; its exact product equals the residual and must remain accepted. + +## Causal repair + +Causal repair `210cebc4980c861ddfd6d098bf1c9d66c8449e72` stays inside the existing `validation_core` decision writer. Only when a nonzero finite rounded tie has zero subtraction/product correction projections and the rounded bound is subnormal, TEPP decodes the exact integer significands and powers of two already used by the both-overflow comparator. Because the subtraction correction is zero, the represented residual itself is exact for this boundary; TEPP compares that represented magnitude directly with the exact dyadic `k * SE` product. No arbitrary-precision dependency, scale normalization, or alternate estimator is introduced. + +The repair does not claim that every finite rounded tie is globally exact. Nonzero low-term projections keep the GAP-081/GAP-082 discriminator; ordinary finite non-ties keep direct comparison; one-sided overflow and both-overflow handling are unchanged. + +Alternatives rejected: blanket rejection of subnormal ties would reject exact minimum-subnormal equality; relying on FMA alone cannot distinguish exact product equality from an exact product error below binary64 correction resolution; broad arbitrary-precision comparison would widen the production surface beyond the demonstrated boundary. + +CHANGELOG trace: `338c270c65c10d305432029c455b9ac37c28c0f1`. + +## Ownership and standards + +This is TEPP Validation Evidence decision semantics, not reusable static psychometric estimation. It does not move work into or copy source from fast-mlsirm and does not consume mutable contextual-orchestrator behavior. + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE. + +International Organization for Standardization. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). ISO. + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. From c77ac440971abc649e6ede35874e5a7439e96797 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:31:51 +0900 Subject: [PATCH 263/576] test(validation): expose minimum-normal SE bound tie --- ...acceptance_finite_rounding_tie_contract.rs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs b/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs index a22614e46..8f503315d 100644 --- a/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs +++ b/crates/validation_core/tests/standard_error_acceptance_finite_rounding_tie_contract.rs @@ -126,3 +126,32 @@ fn exact_minimum_subnormal_bound_remains_accepted() { "an exactly represented minimum-subnormal bound still covers an equal residual" ); } + +#[test] +fn minimum_normal_bound_rounding_must_not_hide_a_strict_rejection() { + let minimum_normal = f64::MIN_POSITIVE; + let standard_error = f64::from_bits(0x1fff_ffff_fc00_0000); // (1 - 2^-27) * 2^-511 + let multiplier = f64::from_bits(0x2000_0000_0200_0000); // (1 + 2^-27) * 2^-511 + + assert_eq!(multiplier * standard_error, minimum_normal); + // The exact represented product is (1 - 2^-54) * 2^-1022, + // one quarter of a minimum-subnormal ULP below the minimum normal. The + // multiplication rounds up, while its FMA correction itself rounds to zero. + assert_eq!(multiplier.mul_add(standard_error, -minimum_normal), -0.0); + + assert_eq!( + accept_within_standard_errors(minimum_normal, 0.0, standard_error, multiplier), + Ok(false), + "an underflowed product correction at the normal/subnormal boundary must not erase a strict rejection" + ); +} + +#[test] +fn exact_minimum_normal_bound_remains_accepted() { + let minimum_normal = f64::MIN_POSITIVE; + assert_eq!( + accept_within_standard_errors(minimum_normal, 0.0, minimum_normal, 1.0), + Ok(true), + "an exactly represented minimum-normal bound still covers an equal residual" + ); +} From 772ad8ed3be8d105975bf776e9f9d898774a4a23 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:33:45 +0900 Subject: [PATCH 264/576] fix(validation): cover zero-projection SE ties --- crates/validation_core/src/monte_carlo.rs | 32 +++++++++++------------ 1 file changed, 16 insertions(+), 16 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index a517a60c1..faaf24e8a 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -385,21 +385,21 @@ fn subtraction_roundoff(minuend: f64, subtrahend: f64, difference: f64) -> f64 { /// adjusted for the absolute residual) with the fused multiply-add product /// correction. Different correction projections preserve the represented-input /// ordering even when either direct operation rounded to the same binary64 value. -/// When both projected corrections are zero at a subnormal rounded bound and the -/// subtraction was exact, TEPP compares that represented residual with the exact -/// dyadic product of the represented `k` and `se`; this prevents FMA-underflowed -/// product error from turning a strict rejection into equality. Other equal -/// correction projections remain on the ordinary rounded decision instead of -/// claiming a broader exact comparator. This also preserves a positive acceptance -/// bound when dividing `se` by a much larger estimate/target scale would underflow -/// to zero. If only the positive bound overflows, every finite residual is covered; -/// if only the residual overflows, a finite bound cannot cover it. When both direct -/// operations overflow, TEPP compares the exact binary64 input rationals by -/// decoding their integer significands and powers of two, avoiding a false -/// accept/reject caused by independently rounded normalization. A zero standard -/// error or zero multiplier remains an exact-recovery gate and is compared before -/// either path. Exact recovery uses numeric equality, for which IEEE `-0.0` and -/// `+0.0` denote the same zero-valued scientific result. +/// If both projected corrections are zero, the subtraction is exact at the rounded +/// residual while the product correction may still have underflowed below binary64 +/// resolution; TEPP therefore compares the represented residual with the exact +/// dyadic product of represented `k` and `se`. Other equal nonzero correction +/// projections remain on the ordinary rounded decision instead of claiming a +/// broader exact comparator. This also preserves a positive acceptance bound when +/// dividing `se` by a much larger estimate/target scale would underflow to zero. If +/// only the positive bound overflows, every finite residual is covered; if only the +/// residual overflows, a finite bound cannot cover it. When both direct operations +/// overflow, TEPP compares the exact binary64 input rationals by decoding their +/// integer significands and powers of two, avoiding a false accept/reject caused by +/// independently rounded normalization. A zero standard error or zero multiplier +/// remains an exact-recovery gate and is compared before either path. Exact recovery +/// uses numeric equality, for which IEEE `-0.0` and `+0.0` denote the same zero-valued +/// scientific result. /// /// # Errors /// @@ -441,7 +441,7 @@ pub fn accept_within_standard_errors( if residual_roundoff != product_roundoff { return Ok(residual_roundoff < product_roundoff); } - if residual_roundoff == 0.0 && direct_bound.is_subnormal() { + if residual_roundoff == 0.0 { return Ok(represented_magnitude_le_exact_product( residual, k, From ceb68825f2759637034c2aaea935e50771aa6afc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:33:59 +0900 Subject: [PATCH 265/576] docs(changelog): include minimum-normal SE tie --- ...tandard-error-acceptance-subnormal-product-rounding.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/CHANGELOG.d/validation-standard-error-acceptance-subnormal-product-rounding.md b/CHANGELOG.d/validation-standard-error-acceptance-subnormal-product-rounding.md index f4158490d..c51918b98 100644 --- a/CHANGELOG.d/validation-standard-error-acceptance-subnormal-product-rounding.md +++ b/CHANGELOG.d/validation-standard-error-acceptance-subnormal-product-rounding.md @@ -1,5 +1,5 @@ -# Validation: preserve subnormal standard-error bound decisions +# Validation: preserve standard-error ties below correction resolution -- Reject SE-aware recovery when an exact finite residual equals the rounded minimum-subnormal `k * SE` bound but the exact product represented by `k` and `SE` is smaller. -- On this subnormal finite-tie boundary, compare the exact represented residual magnitude with the exact dyadic product when both subtraction and FMA correction terms project to zero; keep exact minimum-subnormal equality accepted. -- Preserve the existing finite direct path, low-term tie discriminator, one-sided overflow behavior, and both-overflow exact comparator outside this boundary. +- Reject SE-aware recovery when an exact finite residual equals a rounded `k * SE` bound but the exact represented product is smaller and the FMA correction itself falls below binary64 resolution. +- Cover both the minimum-subnormal rounded bound and the minimum-normal boundary: when subtraction is exact and both finite-tie correction projections are zero, compare the represented residual magnitude with the exact dyadic product of represented `k` and `SE`. +- Keep exact minimum-subnormal/minimum-normal equality accepted and preserve the existing finite direct path, nonzero low-term tie discriminator, one-sided overflow behavior, and both-overflow exact comparator outside this boundary. From d937795d9af55527d6a733f5a2441a2eb1d83b4b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Fri, 4 Sep 2026 23:34:26 +0900 Subject: [PATCH 266/576] docs(research): include minimum-normal correction boundary --- ...rror-acceptance-subnormal-product-rounding.md | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/docs/research/standard-error-acceptance-subnormal-product-rounding.md b/docs/research/standard-error-acceptance-subnormal-product-rounding.md index 7a54a2404..f6c336b6e 100644 --- a/docs/research/standard-error-acceptance-subnormal-product-rounding.md +++ b/docs/research/standard-error-acceptance-subnormal-product-rounding.md @@ -1,22 +1,22 @@ -# Standard-error acceptance at the subnormal product boundary +# Standard-error acceptance below correction resolution ## Problem -TEPP Validation evaluates `|estimate - target| <= k * standard_error` from represented binary64 inputs. GAP-081 and GAP-082 repaired finite rounded ties when multiplication or subtraction retained a nonzero error term. A narrower boundary remains when the rounded bound is subnormal: the exact product error itself can lie below half of the minimum subnormal and the FMA correction can therefore project to signed zero. +TEPP Validation evaluates `|estimate - target| <= k * standard_error` from represented binary64 inputs. GAP-081 and GAP-082 repaired finite rounded ties when multiplication or subtraction retained a nonzero error term. A narrower correction-resolution boundary remains: the exact product error can fall below half of the minimum subnormal, so the FMA correction projects to signed zero even though the rounded finite bound has crossed the scientific decision boundary. -Public RED `b55c5473c8ee7a70ec2508d14d0755c2aeb38191` uses an exact residual equal to the minimum positive subnormal, `estimate = 0x0.0000000000001p-1022`, `target = 0`, `k = 0x1.8p-538`, and `SE = 0x1p-537`. The exact dyadic product of the represented factors is `3/4` of the minimum subnormal, so the scientific inequality is false. Binary64 multiplication rounds that product up to the minimum subnormal, while `fma(k, SE, -rounded_bound)` rounds the `-1/4`-subnormal correction to signed zero. The predecessor therefore observed equal rounded residual/bound and equal zero correction projections and falsely accepted. +Public RED `b55c5473c8ee7a70ec2508d14d0755c2aeb38191` uses an exact residual equal to the minimum positive subnormal, `estimate = 0x0.0000000000001p-1022`, `target = 0`, `k = 0x1.8p-538`, and `SE = 0x1p-537`. The exact dyadic product of the represented factors is `3/4` of the minimum subnormal, so the scientific inequality is false. Binary64 multiplication rounds that product up to the minimum subnormal, while `fma(k, SE, -rounded_bound)` rounds the `-1/4`-subnormal correction to signed zero. The predecessor therefore observed equal rounded residual/bound and equal zero correction projections and falsely accepted. The exact-equality control uses `k = 1` and `SE = minimum_subnormal`. -The control uses `k = 1` and `SE = minimum_subnormal`; its exact product equals the residual and must remain accepted. +Initial repair `210cebc4980c861ddfd6d098bf1c9d66c8449e72` correctly closed that RED but restricted exact dyadic comparison to a *subnormal rounded bound*. A concurrent follow-up RED `c77ac440971abc649e6ede35874e5a7439e96797` demonstrated that the restriction was too narrow. At `estimate = f64::MIN_POSITIVE`, `target = 0`, `SE = (1 - 2^-27) * 2^-511`, and `k = (1 + 2^-27) * 2^-511`, the exact product is `(1 - 2^-54) * 2^-1022`: one quarter of a minimum-subnormal ULP below the minimum normal. Multiplication rounds to `f64::MIN_POSITIVE` and the negative quarter-subnormal FMA correction again projects to signed zero. The exact represented residual is the minimum normal, so the scientific inequality is still false even though the rounded bound itself is normal. The adjacent exact minimum-normal equality remains an acceptance control. ## Causal repair -Causal repair `210cebc4980c861ddfd6d098bf1c9d66c8449e72` stays inside the existing `validation_core` decision writer. Only when a nonzero finite rounded tie has zero subtraction/product correction projections and the rounded bound is subnormal, TEPP decodes the exact integer significands and powers of two already used by the both-overflow comparator. Because the subtraction correction is zero, the represented residual itself is exact for this boundary; TEPP compares that represented magnitude directly with the exact dyadic `k * SE` product. No arbitrary-precision dependency, scale normalization, or alternate estimator is introduced. +Corrected causal repair `772ad8ed3be8d105975bf776e9f9d898774a4a23` stays inside the existing `validation_core` decision writer and removes the inappropriate rounded-bound-class condition. On a nonzero finite rounded tie, nonzero correction projections continue to use the GAP-081/GAP-082 low-term ordering. If both subtraction and product correction projections are zero, the zero subtraction correction means the represented residual is exact at that rounded value, while the product correction may have fallen below binary64 resolution. TEPP therefore decodes the exact integer significands and powers of two already used by the both-overflow comparator and compares that exact represented residual magnitude directly with the exact dyadic product of represented `k` and `SE`. -The repair does not claim that every finite rounded tie is globally exact. Nonzero low-term projections keep the GAP-081/GAP-082 discriminator; ordinary finite non-ties keep direct comparison; one-sided overflow and both-overflow handling are unchanged. +This covers both minimum-subnormal and minimum-normal boundary REDs without claiming a global exact comparator for finite ties with equal nonzero projected corrections. Ordinary finite non-ties, one-sided overflow handling, and the both-overflow exact comparator remain unchanged. No arbitrary-precision dependency, scale normalization, or alternate estimator is introduced. -Alternatives rejected: blanket rejection of subnormal ties would reject exact minimum-subnormal equality; relying on FMA alone cannot distinguish exact product equality from an exact product error below binary64 correction resolution; broad arbitrary-precision comparison would widen the production surface beyond the demonstrated boundary. +Alternatives rejected: keeping the subnormal-bound guard fails the minimum-normal RED; blanket rejection of zero-projection ties breaks exact equality controls; relying on FMA alone cannot distinguish exact product equality from product error below binary64 correction resolution; broad arbitrary precision would widen the production surface beyond the demonstrated boundary. -CHANGELOG trace: `338c270c65c10d305432029c455b9ac37c28c0f1`. +CHANGELOG correction: `ceb68825f2759637034c2aaea935e50771aa6afc` supersedes the narrower wording introduced at `338c270c65c10d305432029c455b9ac37c28c0f1`. ## Ownership and standards From c9d612f2964eb89ae6070e707c77115a12be84b9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:05:06 +0900 Subject: [PATCH 267/576] test(validation): reject impossible zero Wilson lower peer --- ...wilson_boundary_pair_coherence_contract.rs | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 crates/validation_core/tests/validation_report_wilson_boundary_pair_coherence_contract.rs diff --git a/crates/validation_core/tests/validation_report_wilson_boundary_pair_coherence_contract.rs b/crates/validation_core/tests/validation_report_wilson_boundary_pair_coherence_contract.rs new file mode 100644 index 000000000..e25175f31 --- /dev/null +++ b/crates/validation_core/tests/validation_report_wilson_boundary_pair_coherence_contract.rs @@ -0,0 +1,38 @@ +//! Wilson-pair boundary regression for a representable minority-coverage lower root. + +use validation_core::{ValidationError, ValidationReport}; + +const COVERAGE_ONE_IN_ONE_HUNDRED_MILLION: f64 = f64::from_bits(0x3e45_798e_e230_8c3a); +const PRODUCER_LOWER: f64 = f64::from_bits(0x3c9c_d2b2_8e2c_a873); +const PRODUCER_UPPER: f64 = f64::from_bits(0x3fe0_0000_055e_63b8); + +fn report_with_wilson_pair(lower: f64, upper: f64) -> ValidationReport { + ValidationReport { + study_label: "wilson-boundary-pair".to_owned(), + rmse: 1.0, + rmse_standard_error: 0.0, + mean_bias: 0.0, + bias_standard_error: 0.0, + interval_coverage: COVERAGE_ONE_IN_ONE_HUNDRED_MILLION, + coverage_wilson_lower: lower, + coverage_wilson_upper: upper, + temporal_order_accuracy: 1.0, + monte_carlo_rmse: None, + } +} + +/// Rejects a zero lower endpoint when the peer endpoint implies a representable positive root. +#[test] +fn zero_lower_cannot_hide_representable_wilson_peer_root() { + let artifact = report_with_wilson_pair(0.0, PRODUCER_UPPER); + + assert_eq!(artifact.validate(), Err(ValidationError::InvalidInput)); +} + +/// Preserves the actual rounded Wilson pair for `n = 100_000_000`, one cover, and `z = 10_000`. +#[test] +fn producer_pair_remains_admissible_at_the_same_coverage() { + let artifact = report_with_wilson_pair(PRODUCER_LOWER, PRODUCER_UPPER); + + assert!(artifact.validate().is_ok()); +} From 88512417d931e54ec4eb41581caa0ef20b71df5b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:07:56 +0900 Subject: [PATCH 268/576] fix(validation): preserve Wilson peer-root boundary coherence --- crates/validation_core/src/report.rs | 68 ++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 18 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index c90dc662c..1cd2ef859 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -8,6 +8,23 @@ const RMSE_STANDARD_ERROR_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; const MONTE_CARLO_RMSE_SUPPORT_RELATIVE_TOLERANCE: f64 = 64.0 * f64::EPSILON; const WILSON_PAIR_ABSOLUTE_TOLERANCE: f64 = 64.0 * f64::EPSILON; +fn minority_zero_lower_is_rounding_feasible(p: f64, upper: f64) -> bool { + debug_assert!(p > 0.0 && p < 0.5); + debug_assert!(upper >= p && upper <= 1.0); + + // Solving the Wilson root identity for the lower endpoint avoids the + // cancellation that makes the complement-form residual insensitive near + // p = 0. The multiplication order keeps p² from underflowing before the + // final represented lower root itself would round to zero. + let p_squared = p * p; + let denominator = p_squared + (1.0 - 2.0 * p) * upper; + if !denominator.is_finite() || denominator <= 0.0 { + return false; + } + let implied_lower = p * ((p / denominator) * (1.0 - upper)); + implied_lower == 0.0 +} + /// Check whether a stored Wilson endpoint pair can arise from one Wilson score interval. /// /// For empirical coverage `p` and `a = z² / n`, the Wilson roots satisfy @@ -16,9 +33,13 @@ const WILSON_PAIR_ABSOLUTE_TOLERANCE: f64 = 64.0 * f64::EPSILON; /// the equivalent identity on the uncovered proportion avoids squaring a tiny /// `p`. All terms remain probability-scaled, so a small absolute binary64 /// tolerance is sufficient without overflow-prone reconstruction of `n` or `z`. -/// At exact all-covered `p = 1`, the eliminated identity is degenerate, while the -/// canonical producer still requires the lower endpoint `n / (n + z²)` to be -/// strictly positive for every non-empty sample and finite represented `z²`. +/// A boundary endpoint needs one additional peer-root check: the eliminated +/// identity can lose all sensitivity to a representable minority lower root when +/// the stored lower endpoint is exact zero. The complement-symmetric case applies +/// to an exact-one upper endpoint above `p = 0.5`. At exact all-covered `p = 1`, +/// the eliminated identity is degenerate, while the canonical producer still +/// requires the lower endpoint `n / (n + z²)` to be strictly positive for every +/// non-empty sample and finite represented `z²`. fn wilson_pair_is_algebraically_coherent(p: f64, lower: f64, upper: f64) -> bool { if p == 0.0 { return true; @@ -27,6 +48,16 @@ fn wilson_pair_is_algebraically_coherent(p: f64, lower: f64, upper: f64) -> bool return lower > 0.0; } + if p < 0.5 && lower == 0.0 && !minority_zero_lower_is_rounding_feasible(p, upper) { + return false; + } + if p > 0.5 + && upper == 1.0 + && !minority_zero_lower_is_rounding_feasible(1.0 - p, 1.0 - lower) + { + return false; + } + let endpoint_sum = lower + upper; let (left, right) = if p >= 0.5 { ( @@ -81,21 +112,22 @@ impl ValidationReport { /// accuracy are probabilities in `[0, 1]`; the Wilson interval is ordered, /// contains the empirical coverage recorded in the same report, and its two /// endpoints must satisfy the same Wilson-score root identity for that - /// coverage. This prevents two individually plausible bounds from being - /// combined into an interval that no finite positive Wilson `z² / n` can - /// produce. Mean signed bias remains unrestricted in sign. A generic - /// [`MonteCarloSummary`] may summarize a signed metric, but when it occupies - /// `monte_carlo_rmse` every retained replication is nonnegative. Its mean and - /// percentile endpoints are therefore nonnegative. Nonnegative sample support - /// additionally implies `SD <= sqrt(n) * mean`, `SE(mean) <= mean`, and every - /// retained value—and thus every inclusive nearest-rank percentile endpoint—is - /// at most `n * mean`. Admission evaluates the percentile support as - /// `endpoint / mean <= n` with a small relative binary64 tolerance so the check - /// does not overflow a finite sample sum. A zero Monte Carlo RMSE mean is exact - /// perfect recovery across every retained replication, so spread, standard - /// error, and empirical percentile endpoints must all be zero as well. These - /// checks prevent a finite but scientifically impossible payload from becoming - /// durable Validation Evidence. + /// coverage. A stored zero/one endpoint is additionally refused when the peer + /// endpoint implies a representable non-boundary Wilson root. This prevents two + /// individually plausible bounds from being combined into an interval that no + /// finite positive Wilson `z² / n` can produce. Mean signed bias remains + /// unrestricted in sign. A generic [`MonteCarloSummary`] may summarize a signed + /// metric, but when it occupies `monte_carlo_rmse` every retained replication is + /// nonnegative. Its mean and percentile endpoints are therefore nonnegative. + /// Nonnegative sample support additionally implies `SD <= sqrt(n) * mean`, + /// `SE(mean) <= mean`, and every retained value—and thus every inclusive + /// nearest-rank percentile endpoint—is at most `n * mean`. Admission evaluates + /// the percentile support as `endpoint / mean <= n` with a small relative + /// binary64 tolerance so the check does not overflow a finite sample sum. A + /// zero Monte Carlo RMSE mean is exact perfect recovery across every retained + /// replication, so spread, standard error, and empirical percentile endpoints + /// must all be zero as well. These checks prevent a finite but scientifically + /// impossible payload from becoming durable Validation Evidence. /// /// # Errors /// From 6a4f89da52cf18f63b2818545f15f72f43099a3f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:10:17 +0900 Subject: [PATCH 269/576] test(validation): keep Wilson boundary peer check branch-complete --- crates/validation_core/src/report.rs | 21 ++++++++++----------- 1 file changed, 10 insertions(+), 11 deletions(-) diff --git a/crates/validation_core/src/report.rs b/crates/validation_core/src/report.rs index 1cd2ef859..174f23230 100644 --- a/crates/validation_core/src/report.rs +++ b/crates/validation_core/src/report.rs @@ -18,9 +18,7 @@ fn minority_zero_lower_is_rounding_feasible(p: f64, upper: f64) -> bool { // final represented lower root itself would round to zero. let p_squared = p * p; let denominator = p_squared + (1.0 - 2.0 * p) * upper; - if !denominator.is_finite() || denominator <= 0.0 { - return false; - } + debug_assert!(denominator.is_finite() && denominator > 0.0); let implied_lower = p * ((p / denominator) * (1.0 - upper)); implied_lower == 0.0 } @@ -32,14 +30,15 @@ fn minority_zero_lower_is_rounding_feasible(p: f64, upper: f64) -> bool { /// the unrecorded `a` gives a necessary endpoint-pair identity. For `p < 0.5`, /// the equivalent identity on the uncovered proportion avoids squaring a tiny /// `p`. All terms remain probability-scaled, so a small absolute binary64 -/// tolerance is sufficient without overflow-prone reconstruction of `n` or `z`. -/// A boundary endpoint needs one additional peer-root check: the eliminated -/// identity can lose all sensitivity to a representable minority lower root when -/// the stored lower endpoint is exact zero. The complement-symmetric case applies -/// to an exact-one upper endpoint above `p = 0.5`. At exact all-covered `p = 1`, -/// the eliminated identity is degenerate, while the canonical producer still -/// requires the lower endpoint `n / (n + z²)` to be strictly positive for every -/// non-empty sample and finite represented `z²`. +/// tolerance is sufficient for ordinary interior pairs without overflow-prone +/// reconstruction of `n` or `z`. A boundary endpoint needs one additional +/// peer-root check: the eliminated identity can lose all sensitivity to a +/// representable minority lower root when the stored lower endpoint is exact +/// zero. The complement-symmetric case applies to an exact-one upper endpoint +/// above `p = 0.5`. At exact all-covered `p = 1`, the eliminated identity is +/// degenerate, while the canonical producer still requires the lower endpoint +/// `n / (n + z²)` to be strictly positive for every non-empty sample and finite +/// represented `z²`. fn wilson_pair_is_algebraically_coherent(p: f64, lower: f64, upper: f64) -> bool { if p == 0.0 { return true; From 0ce7639bd9f0ab5df2ecd6b24cd57a467e975d26 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:10:44 +0900 Subject: [PATCH 270/576] test(validation): cover Wilson boundary peer symmetry and serde --- ...wilson_boundary_pair_coherence_contract.rs | 45 ++++++++++++++++--- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/crates/validation_core/tests/validation_report_wilson_boundary_pair_coherence_contract.rs b/crates/validation_core/tests/validation_report_wilson_boundary_pair_coherence_contract.rs index e25175f31..349797cd8 100644 --- a/crates/validation_core/tests/validation_report_wilson_boundary_pair_coherence_contract.rs +++ b/crates/validation_core/tests/validation_report_wilson_boundary_pair_coherence_contract.rs @@ -1,19 +1,22 @@ -//! Wilson-pair boundary regression for a representable minority-coverage lower root. +//! Wilson-pair boundary regression for representable peer roots. use validation_core::{ValidationError, ValidationReport}; const COVERAGE_ONE_IN_ONE_HUNDRED_MILLION: f64 = f64::from_bits(0x3e45_798e_e230_8c3a); const PRODUCER_LOWER: f64 = f64::from_bits(0x3c9c_d2b2_8e2c_a873); const PRODUCER_UPPER: f64 = f64::from_bits(0x3fe0_0000_055e_63b8); +const COMPLEMENT_COVERAGE: f64 = f64::from_bits(0x3fef_ffff_faa1_9c47); +const COMPLEMENT_LOWER: f64 = f64::from_bits(0x3fdf_ffff_f543_3890); +const MINIMUM_U64_COVERAGE: f64 = f64::from_bits(0x3bf0_0000_0000_0000); -fn report_with_wilson_pair(lower: f64, upper: f64) -> ValidationReport { +fn report_with_wilson_pair(coverage: f64, lower: f64, upper: f64) -> ValidationReport { ValidationReport { study_label: "wilson-boundary-pair".to_owned(), rmse: 1.0, rmse_standard_error: 0.0, mean_bias: 0.0, bias_standard_error: 0.0, - interval_coverage: COVERAGE_ONE_IN_ONE_HUNDRED_MILLION, + interval_coverage: coverage, coverage_wilson_lower: lower, coverage_wilson_upper: upper, temporal_order_accuracy: 1.0, @@ -24,7 +27,27 @@ fn report_with_wilson_pair(lower: f64, upper: f64) -> ValidationReport { /// Rejects a zero lower endpoint when the peer endpoint implies a representable positive root. #[test] fn zero_lower_cannot_hide_representable_wilson_peer_root() { - let artifact = report_with_wilson_pair(0.0, PRODUCER_UPPER); + let artifact = report_with_wilson_pair( + COVERAGE_ONE_IN_ONE_HUNDRED_MILLION, + 0.0, + PRODUCER_UPPER, + ); + + assert_eq!(artifact.validate(), Err(ValidationError::InvalidInput)); + assert_eq!(artifact.to_json(), Err(ValidationError::InvalidInput)); + + let raw = format!( + r#"{{"study_label":"wilson-boundary-pair","rmse":1.0,"rmse_standard_error":0.0,"mean_bias":0.0,"bias_standard_error":0.0,"interval_coverage":{coverage},"coverage_wilson_lower":0.0,"coverage_wilson_upper":{upper},"temporal_order_accuracy":1.0,"monte_carlo_rmse":null}}"#, + coverage = COVERAGE_ONE_IN_ONE_HUNDRED_MILLION, + upper = PRODUCER_UPPER, + ); + assert!(serde_json::from_str::(&raw).is_err()); +} + +/// Applies the same peer-root admission rule to the complement-symmetric exact-one upper boundary. +#[test] +fn exact_one_upper_cannot_hide_representable_uncovered_peer_root() { + let artifact = report_with_wilson_pair(COMPLEMENT_COVERAGE, COMPLEMENT_LOWER, 1.0); assert_eq!(artifact.validate(), Err(ValidationError::InvalidInput)); } @@ -32,7 +55,19 @@ fn zero_lower_cannot_hide_representable_wilson_peer_root() { /// Preserves the actual rounded Wilson pair for `n = 100_000_000`, one cover, and `z = 10_000`. #[test] fn producer_pair_remains_admissible_at_the_same_coverage() { - let artifact = report_with_wilson_pair(PRODUCER_LOWER, PRODUCER_UPPER); + let artifact = report_with_wilson_pair( + COVERAGE_ONE_IN_ONE_HUNDRED_MILLION, + PRODUCER_LOWER, + PRODUCER_UPPER, + ); + + assert!(artifact.validate().is_ok()); +} + +/// Keeps an unresolved extreme boundary admissible when the peer-root reconstruction also rounds to zero. +#[test] +fn rounded_zero_and_one_pair_remains_admissible_when_peer_root_is_unrepresentable() { + let artifact = report_with_wilson_pair(MINIMUM_U64_COVERAGE, 0.0, 1.0); assert!(artifact.validate().is_ok()); } From 407b3645233e8855cf30487250155ac903d6a4cb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:10:54 +0900 Subject: [PATCH 271/576] docs(validation): record Wilson peer-root boundary repair --- CHANGELOG.d/validation-report-wilson-boundary-peer-root.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-report-wilson-boundary-peer-root.md diff --git a/CHANGELOG.d/validation-report-wilson-boundary-peer-root.md b/CHANGELOG.d/validation-report-wilson-boundary-peer-root.md new file mode 100644 index 000000000..75443cac9 --- /dev/null +++ b/CHANGELOG.d/validation-report-wilson-boundary-peer-root.md @@ -0,0 +1,3 @@ +### Validation Evidence + +`ValidationReport` now rejects a stored Wilson lower endpoint of exact zero, and the complement-symmetric exact-one upper endpoint, when the peer endpoint implies that the omitted Wilson root remains representable in binary64. This closes a boundary-admission hole where the eliminated endpoint-pair residual could cancel to zero even though the stored pair could not come from one Wilson interval, while preserving genuinely unrepresentable extreme boundary roots. From d57aa89bea55e691f5862ff1d3b35e4fb55e35a6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:11:26 +0900 Subject: [PATCH 272/576] docs(research): trace Wilson boundary peer-root admission --- ...dation-report-wilson-boundary-peer-root.md | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 docs/research/validation-report-wilson-boundary-peer-root.md diff --git a/docs/research/validation-report-wilson-boundary-peer-root.md b/docs/research/validation-report-wilson-boundary-peer-root.md new file mode 100644 index 000000000..264edf639 --- /dev/null +++ b/docs/research/validation-report-wilson-boundary-peer-root.md @@ -0,0 +1,57 @@ +# Wilson boundary peer-root admission + +## Problem + +`ValidationReport` stores empirical coverage `p` and the two Wilson endpoints, but not the producer's sample count or critical value. The existing admission check therefore eliminates the unrecorded `a = z² / n` and checks the Wilson root identity. That identity is a necessary interior-pair condition, but its complement form can become numerically insensitive when a minority-coverage lower endpoint is replaced by exact zero. + +A concrete represented-input case is one covered observation out of `n = 100_000_000` with `z = 10_000`. The canonical producer gives: + +- `p = 0x3e45_798e_e230_8c3a` (`1.0e-8` as represented binary64); +- lower `L = 0x3c9c_d2b2_8e2c_a873` (`9.999999800000005e-17`); +- upper `U = 0x3fe0_0000_055e_63b8` (`0.5000000099999999`). + +Replacing only `L` with `0.0` leaves the old complement-form comparison numerically equal on both sides, so the forged pair was admitted even though the peer endpoint implies a positive, ordinarily representable lower root. This is an artifact-admission defect: it does not change the Wilson producer or estimate a new psychometric quantity. + +## Necessary boundary relation + +Wilson's two roots obey + +`L U = p² / (1 + a)` + +and + +`L + U = 1 + (2p - 1) / (1 + a)`. + +Eliminating `a` and solving directly for the minority lower root gives + +`L = p²(1 - U) / [p² + (1 - 2p)U]`, for `0 < p < 0.5`. + +The implementation evaluates the same relation as + +`p * ((p / denominator) * (1 - U))` + +so `p²` is not forced to underflow before the final lower root itself becomes unrepresentable. If a stored lower endpoint is exact zero while this implied peer root remains positive in binary64, the pair cannot be admitted. For `p > 0.5`, TEPP applies the same check to the complement interval `(1-U, 1-L)`. + +The check remains deliberately boundary-local. It does not reconstruct `n` or `z`, does not claim globally correctly rounded Wilson endpoints, and does not replace the existing ordinary interior-pair tolerance. An extreme `[0, 1]` pair remains admissible when the reconstructed minority peer root also rounds to zero; the durable report lacks enough producer provenance to make a stronger claim there. + +## Decision and rejected alternatives + +The selected repair is a necessary peer-root representability gate inside the existing Validation Evidence single writer. Tightening the global absolute tolerance was rejected because the counterexample is cancellation-conditioned rather than merely too loosely tolerated, and a global tolerance change would alter ordinary interior admission without a demonstrated defect. Requiring every nonzero empirical coverage to have a strictly positive lower endpoint was rejected because a finite extreme Wilson configuration can legitimately project a positive mathematical root to binary64 zero. Reconstructing a unique sample count or critical value from the durable report was rejected because those values are not fields of `ValidationReport`; inventing them would create false provenance. + +## Executable traceability + +- Public RED: `c9d612f2964eb89ae6070e707c77115a12be84b9`, `crates/validation_core/tests/validation_report_wilson_boundary_pair_coherence_contract.rs`. +- Minimal causal repair: `88512417d931e54ec4eb41581caa0ef20b71df5b`, `crates/validation_core/src/report.rs`. +- Branch-complete boundary implementation: `6a4f89da52cf18f63b2818545f15f72f43099a3f`. +- Symmetry, representable-control, extreme-boundary, and serde contract hardening: `0ce7639bd9f0ab5df2ecd6b24cd57a467e975d26`. +- CHANGELOG evidence: `407b3645233e8855cf30487250155ac903d6a4cb`. + +Hosted exact-head GREEN, independent review, protected-main merge, release, and downstream release evidence remain separate delivery gates. + +## References + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE Standards Association. https://doi.org/10.1109/IEEESTD.2019.8766229 + +ISO/IEC/IEEE. (2020). *Floating-point arithmetic* (ISO/IEC/IEEE 60559:2020). International Organization for Standardization, International Electrotechnical Commission, & IEEE. + +Wilson, E. B. (1927). Probable inference, the law of succession, and statistical inference. *Journal of the American Statistical Association, 22*(158), 209–212. https://doi.org/10.1080/01621459.1927.10502953 From 35ea85ba5c049e3736e8549445bc799638cc6555 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:31:03 +0900 Subject: [PATCH 273/576] test(validation): expose equal nonzero correction tie --- ..._nonzero_correction_projection_contract.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs diff --git a/crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs b/crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs new file mode 100644 index 000000000..1123627d9 --- /dev/null +++ b/crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs @@ -0,0 +1,37 @@ +use validation_core::accept_within_standard_errors; + +#[test] +fn equal_nonzero_correction_projection_preserves_exact_rejection() { + // The direct subtraction and product both round to the same finite value. + // Their first-order correction projections also round to the same positive + // subnormal. The represented subtraction correction is exact, while the + // exact product correction is smaller than that projected subnormal, so the + // represented-input inequality is strictly false and must not be admitted as + // a rounded equality. + let estimate = f64::from_bits(0x0210_2814_5144_3c99); + let correction = f64::from_bits(0x0000_0000_6398_c737); + let target = -correction; + let k = f64::from_bits(0x20d9_5434_7757_68c7); + let standard_error = f64::from_bits(0x2124_696e_33e2_baaa); + + assert_eq!(estimate - target, k * standard_error); + assert!(!accept_within_standard_errors(estimate, target, standard_error, k) + .expect("finite represented inputs")); +} + +#[test] +fn equal_nonzero_correction_projection_preserves_exact_acceptance() { + // Companion boundary: the exact product correction lies just above the + // same projected correction carried by the represented residual. A repair + // must discriminate the exact ordering rather than reject every equal + // nonzero correction projection. + let estimate = f64::from_bits(0x01c3_f43e_c52b_4312); + let correction = f64::from_bits(0x0000_0000_003b_9da9); + let target = -correction; + let k = f64::from_bits(0x20c8_7ace_8d72_9746); + let standard_error = f64::from_bits(0x20ea_1585_cc49_24ca); + + assert_eq!(estimate - target, k * standard_error); + assert!(accept_within_standard_errors(estimate, target, standard_error, k) + .expect("finite represented inputs")); +} From 7d597a18e043f3619b893981823b9be15ddb823c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:34:18 +0900 Subject: [PATCH 274/576] fix(validation): compare equal nonzero tie corrections exactly --- crates/validation_core/src/monte_carlo.rs | 91 ++++++++++++++++++++--- 1 file changed, 79 insertions(+), 12 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index faaf24e8a..5f6510983 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -333,6 +333,64 @@ fn represented_magnitude_le_exact_product(value: f64, factor_a: f64, factor_b: f ) } +/// Compare a represented correction with the exact rounding correction of a represented product. +fn represented_correction_le_exact_product_roundoff( + correction: f64, + factor_a: f64, + factor_b: f64, + rounded_product: f64, +) -> bool { + let (a_significand, a_exponent) = binary64_magnitude_components(factor_a); + let (b_significand, b_exponent) = binary64_magnitude_components(factor_b); + let product_significand = (a_significand as u128) * (b_significand as u128); + let product_exponent = a_exponent + b_exponent; + let (rounded_significand, rounded_exponent) = + binary64_magnitude_components(rounded_product); + let common_exponent = product_exponent.min(rounded_exponent); + let product_shift = (product_exponent - common_exponent) as u32; + let rounded_shift = (rounded_exponent - common_exponent) as u32; + debug_assert!(product_significand.leading_zeros() >= product_shift); + debug_assert!((rounded_significand as u128).leading_zeros() >= rounded_shift); + let exact_product = product_significand << product_shift; + let rounded_product = (rounded_significand as u128) << rounded_shift; + let (product_roundoff_negative, product_roundoff_significand) = + if exact_product < rounded_product { + (true, rounded_product - exact_product) + } else { + (false, exact_product - rounded_product) + }; + + if product_roundoff_significand == 0 { + return correction <= 0.0; + } + if correction == 0.0 { + return !product_roundoff_negative; + } + + let correction_negative = correction.is_sign_negative(); + if correction_negative != product_roundoff_negative { + return correction_negative; + } + + let (correction_significand, correction_exponent) = + binary64_magnitude_components(correction.abs()); + if correction_negative { + scaled_u128_le( + product_roundoff_significand, + common_exponent, + correction_significand as u128, + correction_exponent, + ) + } else { + scaled_u128_le( + correction_significand as u128, + correction_exponent, + product_roundoff_significand, + common_exponent, + ) + } +} + /// Compare the exact represented residual magnitude with `k * SE` after both direct operations overflow. fn both_overflow_acceptance( estimate: f64, @@ -388,18 +446,21 @@ fn subtraction_roundoff(minuend: f64, subtrahend: f64, difference: f64) -> f64 { /// If both projected corrections are zero, the subtraction is exact at the rounded /// residual while the product correction may still have underflowed below binary64 /// resolution; TEPP therefore compares the represented residual with the exact -/// dyadic product of represented `k` and `se`. Other equal nonzero correction -/// projections remain on the ordinary rounded decision instead of claiming a -/// broader exact comparator. This also preserves a positive acceptance bound when -/// dividing `se` by a much larger estimate/target scale would underflow to zero. If -/// only the positive bound overflows, every finite residual is covered; if only the -/// residual overflows, a finite bound cannot cover it. When both direct operations -/// overflow, TEPP compares the exact binary64 input rationals by decoding their -/// integer significands and powers of two, avoiding a false accept/reject caused by -/// independently rounded normalization. A zero standard error or zero multiplier -/// remains an exact-recovery gate and is compared before either path. Exact recovery -/// uses numeric equality, for which IEEE `-0.0` and `+0.0` denote the same zero-valued -/// scientific result. +/// dyadic product of represented `k` and `se`. If the two nonzero correction +/// projections are equal, the subtraction correction is already exact but the FMA +/// projection may have rounded a finer exact product residual; TEPP compares that +/// represented subtraction correction with the exact dyadic product roundoff before +/// deciding the tie. This preserves the represented-input inequality without +/// claiming a broader exact comparator for unequal rounded residuals and bounds. +/// This also preserves a positive acceptance bound when dividing `se` by a much +/// larger estimate/target scale would underflow to zero. If only the positive bound +/// overflows, every finite residual is covered; if only the residual overflows, a +/// finite bound cannot cover it. When both direct operations overflow, TEPP compares +/// the exact binary64 input rationals by decoding their integer significands and +/// powers of two, avoiding a false accept/reject caused by independently rounded +/// normalization. A zero standard error or zero multiplier remains an exact-recovery +/// gate and is compared before either path. Exact recovery uses numeric equality, +/// for which IEEE `-0.0` and `+0.0` denote the same zero-valued scientific result. /// /// # Errors /// @@ -448,6 +509,12 @@ pub fn accept_within_standard_errors( standard_error, )); } + return Ok(represented_correction_le_exact_product_roundoff( + residual_roundoff, + k, + standard_error, + direct_bound, + )); } return Ok(residual <= direct_bound); } From 702eec2fe211017785f848fe1b23abe7ee60c5ec Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:36:20 +0900 Subject: [PATCH 275/576] docs(changelog): record equal nonzero correction tie repair --- ...ard-error-acceptance-equal-nonzero-correction-projection.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-standard-error-acceptance-equal-nonzero-correction-projection.md diff --git a/CHANGELOG.d/validation-standard-error-acceptance-equal-nonzero-correction-projection.md b/CHANGELOG.d/validation-standard-error-acceptance-equal-nonzero-correction-projection.md new file mode 100644 index 000000000..bb2f43dcb --- /dev/null +++ b/CHANGELOG.d/validation-standard-error-acceptance-equal-nonzero-correction-projection.md @@ -0,0 +1,3 @@ +### Fixed + +- Validation Evidence SE-aware acceptance now resolves finite ties whose error-free subtraction correction and FMA product correction project to the same nonzero binary64 value. The decision compares the represented subtraction correction with the exact dyadic roundoff of represented `k * standard_error`, preventing a strict represented-input rejection or acceptance from being collapsed into rounded equality. The change is boundary-local to equal finite nonzero ties and does not alter ordinary unequal finite decisions, zero-bound exact recovery, or the existing both-overflow comparator. From 28d2dd64f4c1175c8f63a42f440c4eb29e769c11 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:36:48 +0900 Subject: [PATCH 276/576] docs(research): trace equal nonzero correction tie --- ...nce-equal-nonzero-correction-projection.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md diff --git a/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md b/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md new file mode 100644 index 000000000..b5a697392 --- /dev/null +++ b/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md @@ -0,0 +1,60 @@ +# SE-aware acceptance: equal nonzero correction projection + +## Problem + +`accept_within_standard_errors` decides the Validation Evidence predicate + +`|estimate - target| <= k * standard_error` + +from finite binary64 inputs. GAP-081 and GAP-082 recover first-order rounding terms when the directly rounded residual and bound are equal; GAP-083 falls back to an exact dyadic product comparison when both correction projections are zero. A remaining case existed when the subtraction correction and the FMA product correction both rounded to the same **nonzero** binary64 value even though the exact product correction lay strictly on one side of the exact subtraction correction. Falling through to the directly rounded equality could therefore change the scientific decision. + +This is Validation Evidence admission policy in TEPP. It is not a reusable psychometric estimator and does not move arithmetic ownership from `fast-mlsirm`. + +## Exact represented-input RED + +Public RED commit `35ea85ba5c049e3736e8549445bc799638cc6555` adds `crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs`. + +Strict-rejection payload: + +- `estimate = f64::from_bits(0x0210_2814_5144_3c99)` +- `target = -f64::from_bits(0x0000_0000_6398_c737)` +- `k = f64::from_bits(0x20d9_5434_7757_68c7)` +- `standard_error = f64::from_bits(0x2124_696e_33e2_baaa)` + +The direct subtraction and direct product round to the same finite binary64 value. The error-free subtraction low term and `mul_add` product low term also project to the same positive subnormal. However, the represented-input exact product correction is slightly smaller than the represented subtraction correction; therefore the exact represented-input residual is strictly greater than `k * standard_error` and the correct decision is rejection. + +The same contract contains an adjacent acceptance control with `estimate=0x01c3_f43e_c52b_4312`, subtraction correction `0x0000_0000_003b_9da9`, `k=0x20c8_7ace_8d72_9746`, and `standard_error=0x20ea_1585_cc49_24ca`. There the exact product correction lies above the subtraction correction, so a blanket rejection of equal nonzero projections would also be wrong. + +## Causal repair + +Commit `7d597a18e043f3619b893981823b9be15ddb823c` keeps the existing decision hierarchy and changes only the unresolved equal-nonzero finite-tie branch. `represented_correction_le_exact_product_roundoff` decodes the represented factors and rounded product into integer significands and powers of two, forms the exact represented product and its exact signed roundoff in `u128`, and compares that exact roundoff with the represented subtraction correction. + +The chosen repair avoids a second public decision authority, arbitrary-precision runtime dependency, decimal conversion, scale normalization, or a source copy from another CWL repository. The product significand is at most 106 bits, so the exact product and alignment required by this branch fit the existing `u128` numerical boundary. + +## Alternatives rejected + +Treating equal projected corrections as equality was rejected because the RED proves that projection equality does not imply exact represented-input equality. Rejecting every equal nonzero correction pair was rejected because the companion control proves that a valid acceptance exists on the other side of the same projection boundary. Replacing every finite comparison with a general exact-rational engine was rejected as unnecessary scope expansion: ordinary unequal direct results and the existing GAP-080/081/082/083 branches already have narrower causal rules. + +## Scope and residual risk + +The repair claims only the finite case where the directly rounded residual and bound are equal and the two nonzero correction projections are also equal. It does not claim globally correctly rounded Monte Carlo estimation, does not alter numerical estimation, and does not authorize scientific acceptance without the surrounding TEPP Validation Evidence contract and exact-head gates. Independent counterexamples remain the criterion for expanding the exact comparator into another branch. + +## Traceability + +- Bounded context: Validation Evidence +- Module/API: `crates/validation_core/src/monte_carlo.rs` / `accept_within_standard_errors` +- Public RED: `35ea85ba5c049e3736e8549445bc799638cc6555` +- Causal repair: `7d597a18e043f3619b893981823b9be15ddb823c` +- Contract test: `crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs` +- CHANGELOG fragment: `CHANGELOG.d/validation-standard-error-acceptance-equal-nonzero-correction-projection.md` +- Landing vehicle: PR #488; only its current exact head after documentation commits is authoritative for hosted checks and review. + +## Normative and methodological references + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE Standards Association. https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization, International Electrotechnical Commission, & Institute of Electrical and Electronics Engineers. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. + +As of 2026-09-05, IEEE 754-2019 remains an active published standard and P754 remains an active revision project rather than a published replacement. ISO/IEC 60559:2020 remains published. The AERA/APA/NCME Joint Committee is revising the 2014 testing Standards; the unpublished revision is not treated as current normative authority. From 58795885395b7e24570767af4aed691facc78c1a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:39:44 +0900 Subject: [PATCH 277/576] test(validation): cover negative equal correction projection --- ...al_nonzero_correction_projection_contract.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs b/crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs index 1123627d9..64f272638 100644 --- a/crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs +++ b/crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs @@ -35,3 +35,20 @@ fn equal_nonzero_correction_projection_preserves_exact_acceptance() { assert!(accept_within_standard_errors(estimate, target, standard_error, k) .expect("finite represented inputs")); } + +#[test] +fn equal_negative_correction_projection_preserves_exact_rejection() { + // The same projection collision also exists below the rounded value. Here + // both first-order corrections are the same negative subnormal, but the + // exact product lies slightly farther below the rounded bound than the exact + // residual does. The strict inequality therefore remains a rejection. + let estimate = f64::from_bits(0x018b_af20_e855_2bb6); + let correction = f64::from_bits(0x0000_0000_005d_636); + let target = correction; + let k = f64::from_bits(0x2c70_fef0_d26f_1ed); + let standard_error = f64::from_bits(0x150b_cc8f_a576_9411); + + assert_eq!(estimate - target, k * standard_error); + assert!(!accept_within_standard_errors(estimate, target, standard_error, k) + .expect("finite represented inputs")); +} From b463a991adc6ff98fa09f91eb428cda9e0ff1255 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:40:05 +0900 Subject: [PATCH 278/576] fix(test): correct negative correction fixture bits --- ...acceptance_equal_nonzero_correction_projection_contract.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs b/crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs index 64f272638..33110205d 100644 --- a/crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs +++ b/crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs @@ -43,9 +43,9 @@ fn equal_negative_correction_projection_preserves_exact_rejection() { // exact product lies slightly farther below the rounded bound than the exact // residual does. The strict inequality therefore remains a rejection. let estimate = f64::from_bits(0x018b_af20_e855_2bb6); - let correction = f64::from_bits(0x0000_0000_005d_636); + let correction = f64::from_bits(0x0000_0000_0005_d636); let target = correction; - let k = f64::from_bits(0x2c70_fef0_d26f_1ed); + let k = f64::from_bits(0x2c6f_de1e_a0d4_de3d); let standard_error = f64::from_bits(0x150b_cc8f_a576_9411); assert_eq!(estimate - target, k * standard_error); From 7d5529c8cce0552e4a8513fdfb0b015b5a3f94bb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:40:33 +0900 Subject: [PATCH 279/576] docs(research): trace signed equal-correction edge --- ...ard-error-acceptance-equal-nonzero-correction-projection.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md b/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md index b5a697392..dc9d4b4db 100644 --- a/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md +++ b/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md @@ -25,6 +25,8 @@ The direct subtraction and direct product round to the same finite binary64 valu The same contract contains an adjacent acceptance control with `estimate=0x01c3_f43e_c52b_4312`, subtraction correction `0x0000_0000_003b_9da9`, `k=0x20c8_7ace_8d72_9746`, and `standard_error=0x20ea_1585_cc49_24ca`. There the exact product correction lies above the subtraction correction, so a blanket rejection of equal nonzero projections would also be wrong. +Edge commit `b463a991adc6ff98fa09f91eb428cda9e0ff1255` adds the sign-complementary projection boundary: both first-order corrections can be the same negative subnormal while the exact represented product remains slightly farther below the rounded bound than the exact residual. This keeps the comparator's signed ordering executable rather than testing only positive product roundoff. + ## Causal repair Commit `7d597a18e043f3619b893981823b9be15ddb823c` keeps the existing decision hierarchy and changes only the unresolved equal-nonzero finite-tie branch. `represented_correction_le_exact_product_roundoff` decodes the represented factors and rounded product into integer significands and powers of two, forms the exact represented product and its exact signed roundoff in `u128`, and compares that exact roundoff with the represented subtraction correction. @@ -45,6 +47,7 @@ The repair claims only the finite case where the directly rounded residual and b - Module/API: `crates/validation_core/src/monte_carlo.rs` / `accept_within_standard_errors` - Public RED: `35ea85ba5c049e3736e8549445bc799638cc6555` - Causal repair: `7d597a18e043f3619b893981823b9be15ddb823c` +- Signed edge coverage: `b463a991adc6ff98fa09f91eb428cda9e0ff1255` - Contract test: `crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs` - CHANGELOG fragment: `CHANGELOG.d/validation-standard-error-acceptance-equal-nonzero-correction-projection.md` - Landing vehicle: PR #488; only its current exact head after documentation commits is authoritative for hosted checks and review. From ca8c2b86a81cfa6e99a10bdcc141fc10d49ec011 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:43:08 +0900 Subject: [PATCH 280/576] refactor(validation): remove unreachable tie-correction branches --- crates/validation_core/src/monte_carlo.rs | 41 +++++++++++------------ 1 file changed, 20 insertions(+), 21 deletions(-) diff --git a/crates/validation_core/src/monte_carlo.rs b/crates/validation_core/src/monte_carlo.rs index 5f6510983..259eff30f 100644 --- a/crates/validation_core/src/monte_carlo.rs +++ b/crates/validation_core/src/monte_carlo.rs @@ -333,13 +333,21 @@ fn represented_magnitude_le_exact_product(value: f64, factor_a: f64, factor_b: f ) } -/// Compare a represented correction with the exact rounding correction of a represented product. +/// Compare an equal nonzero projected correction with the exact product roundoff. fn represented_correction_le_exact_product_roundoff( correction: f64, factor_a: f64, factor_b: f64, rounded_product: f64, ) -> bool { + debug_assert!(correction.is_finite() && correction != 0.0); + debug_assert!(rounded_product.is_finite() && rounded_product > 0.0); + debug_assert_eq!( + factor_a.mul_add(factor_b, -rounded_product), + correction, + "caller must provide the equal nonzero projected product correction" + ); + let (a_significand, a_exponent) = binary64_magnitude_components(factor_a); let (b_significand, b_exponent) = binary64_magnitude_components(factor_b); let product_significand = (a_significand as u128) * (b_significand as u128); @@ -352,26 +360,17 @@ fn represented_correction_le_exact_product_roundoff( debug_assert!(product_significand.leading_zeros() >= product_shift); debug_assert!((rounded_significand as u128).leading_zeros() >= rounded_shift); let exact_product = product_significand << product_shift; - let rounded_product = (rounded_significand as u128) << rounded_shift; + let rounded_product_significand = (rounded_significand as u128) << rounded_shift; let (product_roundoff_negative, product_roundoff_significand) = - if exact_product < rounded_product { - (true, rounded_product - exact_product) + if exact_product < rounded_product_significand { + (true, rounded_product_significand - exact_product) } else { - (false, exact_product - rounded_product) + (false, exact_product - rounded_product_significand) }; - if product_roundoff_significand == 0 { - return correction <= 0.0; - } - if correction == 0.0 { - return !product_roundoff_negative; - } - + debug_assert_ne!(product_roundoff_significand, 0); let correction_negative = correction.is_sign_negative(); - if correction_negative != product_roundoff_negative { - return correction_negative; - } - + debug_assert_eq!(correction_negative, product_roundoff_negative); let (correction_significand, correction_exponent) = binary64_magnitude_components(correction.abs()); if correction_negative { @@ -447,11 +446,11 @@ fn subtraction_roundoff(minuend: f64, subtrahend: f64, difference: f64) -> f64 { /// residual while the product correction may still have underflowed below binary64 /// resolution; TEPP therefore compares the represented residual with the exact /// dyadic product of represented `k` and `se`. If the two nonzero correction -/// projections are equal, the subtraction correction is already exact but the FMA -/// projection may have rounded a finer exact product residual; TEPP compares that -/// represented subtraction correction with the exact dyadic product roundoff before -/// deciding the tie. This preserves the represented-input inequality without -/// claiming a broader exact comparator for unequal rounded residuals and bounds. +/// projections are equal, TEPP compares the represented subtraction correction with +/// the exact dyadic product roundoff before deciding the tie; only the two signed +/// orderings reachable from that equal-projection branch are implemented. This +/// preserves the represented-input inequality without adding dead generic branches +/// or claiming a broader exact comparator for unequal rounded residuals and bounds. /// This also preserves a positive acceptance bound when dividing `se` by a much /// larger estimate/target scale would underflow to zero. If only the positive bound /// overflows, every finite residual is covered; if only the residual overflows, a From 2c73947dcf7fc7b4478a9324f06b3c2eb5f584ce Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:43:54 +0900 Subject: [PATCH 281/576] docs(research): trace branch-scope refinement --- ...d-error-acceptance-equal-nonzero-correction-projection.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md b/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md index dc9d4b4db..a5e12bbb2 100644 --- a/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md +++ b/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md @@ -31,6 +31,8 @@ Edge commit `b463a991adc6ff98fa09f91eb428cda9e0ff1255` adds the sign-complementa Commit `7d597a18e043f3619b893981823b9be15ddb823c` keeps the existing decision hierarchy and changes only the unresolved equal-nonzero finite-tie branch. `represented_correction_le_exact_product_roundoff` decodes the represented factors and rounded product into integer significands and powers of two, forms the exact represented product and its exact signed roundoff in `u128`, and compares that exact roundoff with the represented subtraction correction. +Self-review refinement `ca8c2b86a81cfa6e99a10bdcc141fc10d49ec011` removes generic zero/sign-mismatch branches that the equal-nonzero caller cannot reach. The helper now encodes those caller invariants as debug assertions and retains only the positive/negative signed orderings exercised by public contracts, avoiding a synthetic branch-coverage obligation with no production state behind it. + The chosen repair avoids a second public decision authority, arbitrary-precision runtime dependency, decimal conversion, scale normalization, or a source copy from another CWL repository. The product significand is at most 106 bits, so the exact product and alignment required by this branch fit the existing `u128` numerical boundary. ## Alternatives rejected @@ -48,7 +50,8 @@ The repair claims only the finite case where the directly rounded residual and b - Public RED: `35ea85ba5c049e3736e8549445bc799638cc6555` - Causal repair: `7d597a18e043f3619b893981823b9be15ddb823c` - Signed edge coverage: `b463a991adc6ff98fa09f91eb428cda9e0ff1255` -- Contract test: `crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs` +- Branch-scope refinement: `ca8c2b86a81cfa6e99a10bdcc141fc10d49ec011` +- Contract test: `crates/validation_core/tests/standard_error_acceptance_equal_nonzero-correction_projection_contract.rs` - CHANGELOG fragment: `CHANGELOG.d/validation-standard-error-acceptance-equal-nonzero-correction-projection.md` - Landing vehicle: PR #488; only its current exact head after documentation commits is authoritative for hosted checks and review. From 21b1f0e7fc81eb3a3ed5e94464f95913bbb4b69f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 00:44:16 +0900 Subject: [PATCH 282/576] fix(docs): correct GAP-085 contract path --- ...dard-error-acceptance-equal-nonzero-correction-projection.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md b/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md index a5e12bbb2..eaf244e6e 100644 --- a/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md +++ b/docs/research/standard-error-acceptance-equal-nonzero-correction-projection.md @@ -51,7 +51,7 @@ The repair claims only the finite case where the directly rounded residual and b - Causal repair: `7d597a18e043f3619b893981823b9be15ddb823c` - Signed edge coverage: `b463a991adc6ff98fa09f91eb428cda9e0ff1255` - Branch-scope refinement: `ca8c2b86a81cfa6e99a10bdcc141fc10d49ec011` -- Contract test: `crates/validation_core/tests/standard_error_acceptance_equal_nonzero-correction_projection_contract.rs` +- Contract test: `crates/validation_core/tests/standard_error_acceptance_equal_nonzero_correction_projection_contract.rs` - CHANGELOG fragment: `CHANGELOG.d/validation-standard-error-acceptance-equal-nonzero-correction-projection.md` - Landing vehicle: PR #488; only its current exact head after documentation commits is authoritative for hosted checks and review. From 04e6e74507a1adb89aac4b2a58d3682746da30ea Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 01:00:54 +0900 Subject: [PATCH 283/576] test(validation): expose overflowing residual cancellation bias --- ...rflowing_residual_cancellation_contract.rs | 33 +++++++++++++++++++ 1 file changed, 33 insertions(+) create mode 100644 crates/validation_core/tests/bias_overflowing_residual_cancellation_contract.rs diff --git a/crates/validation_core/tests/bias_overflowing_residual_cancellation_contract.rs b/crates/validation_core/tests/bias_overflowing_residual_cancellation_contract.rs new file mode 100644 index 000000000..cda52afa4 --- /dev/null +++ b/crates/validation_core/tests/bias_overflowing_residual_cancellation_contract.rs @@ -0,0 +1,33 @@ +use validation_core::{ValidationError, mean_bias}; + +#[test] +fn opposite_overflowing_residuals_cancel_to_representable_zero() { + let truth = [-f64::MAX, f64::MAX]; + let recovered = [f64::MAX, -f64::MAX]; + + assert_eq!( + mean_bias(&truth, &recovered), + Ok(0.0), + "representable mean bias must not fail only because individual signed residuals overflow" + ); +} + +#[test] +fn overflowing_residual_cancellation_preserves_minimum_subnormal_mean() { + let minimum_subnormal = f64::from_bits(1); + let three_minimum_subnormals = f64::from_bits(3); + let truth = [-f64::MAX, f64::MAX, 0.0]; + let recovered = [f64::MAX, -f64::MAX, three_minimum_subnormals]; + + let bias = mean_bias(&truth, &recovered) + .expect("the exact represented-input mean bias is one minimum subnormal"); + assert_eq!(bias.to_bits(), minimum_subnormal.to_bits()); +} + +#[test] +fn one_sided_unrepresentable_mean_bias_still_fails_closed() { + assert_eq!( + mean_bias(&[-f64::MAX], &[f64::MAX]), + Err(ValidationError::InvalidInput) + ); +} From 28b1d186ce2bd08b63ca267c8b98b2eae45d2da7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 01:01:40 +0900 Subject: [PATCH 284/576] fix(validation): preserve bias across overflowing residual cancellation --- crates/validation_core/src/numeric.rs | 73 ++++++++++++++++++++++----- 1 file changed, 59 insertions(+), 14 deletions(-) diff --git a/crates/validation_core/src/numeric.rs b/crates/validation_core/src/numeric.rs index 798bc94e5..f2d3c58ce 100644 --- a/crates/validation_core/src/numeric.rs +++ b/crates/validation_core/src/numeric.rs @@ -44,27 +44,31 @@ fn same_sign_mean_over_total(values: &[f64], total_count: usize) -> Result Result { - if values.is_empty() || values.iter().any(|value| !value.is_finite()) { +/// Returns [`ValidationError::InvalidInput`] for an empty value set, a zero +/// divisor, non-finite input, or an unrepresentable nonzero quotient. +pub(crate) fn deterministic_representable_sum_over_count( + values: &[f64], + total_count: usize, +) -> Result { + if values.is_empty() || total_count == 0 || values.iter().any(|value| !value.is_finite()) { return Err(ValidationError::InvalidInput); } @@ -82,7 +86,7 @@ pub(crate) fn deterministic_representable_mean(values: &[f64]) -> Result Result Result { + deterministic_representable_sum_over_count(values, values.len()) } #[cfg(test)] mod tests { - use super::{deterministic_compensated_sum, deterministic_representable_mean}; + use super::{ + deterministic_compensated_sum, deterministic_representable_mean, + deterministic_representable_sum_over_count, + }; use crate::ValidationError; #[test] @@ -179,6 +202,28 @@ mod tests { ); } + #[test] + fn explicit_denominator_preserves_representable_expanded_sum() { + let minimum_subnormal = f64::from_bits(1); + assert_eq!( + deterministic_representable_sum_over_count( + &[f64::MAX, -f64::MAX, f64::from_bits(3)], + 3, + ) + .expect("explicit denominator") + .to_bits(), + minimum_subnormal.to_bits() + ); + assert_eq!( + deterministic_representable_sum_over_count(&[f64::MAX, f64::MAX], 1), + Err(ValidationError::InvalidInput) + ); + assert_eq!( + deterministic_representable_sum_over_count(&[0.0], 0), + Err(ValidationError::InvalidInput) + ); + } + #[test] fn representable_mean_covers_admission_and_residual_paths() { let minimum_subnormal = f64::from_bits(1); From d1cd54615ddc311491e26fa427e956b5b5379e1a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 01:02:17 +0900 Subject: [PATCH 285/576] fix(validation): preserve representable bias after residual overflow --- crates/validation_core/src/bias.rs | 46 ++++++++++++++++++++++-------- 1 file changed, 34 insertions(+), 12 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index be579684c..318fa412f 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -2,7 +2,10 @@ use crate::ValidationError; use crate::input::require_paired_finite; -use crate::numeric::{deterministic_compensated_sum, deterministic_representable_mean}; +use crate::numeric::{ + deterministic_compensated_sum, deterministic_representable_mean, + deterministic_representable_sum_over_count, +}; fn signed_residuals(truth: &[f64], recovered: &[f64]) -> Result, ValidationError> { require_paired_finite(truth, recovered)?; @@ -88,21 +91,38 @@ fn scaled_standard_error(values: &[f64], mean: f64) -> Result Result { - let residuals = signed_residuals(truth, recovered)?; - deterministic_representable_mean(&residuals) + require_paired_finite(truth, recovered)?; + + let residuals: Option> = truth + .iter() + .zip(recovered) + .map(|(truth_value, recovered_value)| { + let residual = recovered_value - truth_value; + residual.is_finite().then_some(residual) + }) + .collect(); + if let Some(residuals) = residuals { + return deterministic_representable_mean(&residuals); + } + + let mut expanded_terms = Vec::with_capacity(truth.len().saturating_mul(2)); + expanded_terms.extend_from_slice(recovered); + expanded_terms.extend(truth.iter().map(|value| -*value)); + deterministic_representable_sum_over_count(&expanded_terms, truth.len()) } /// Standard error of the mean signed bias under independent observations. @@ -111,6 +131,8 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 01:03:06 +0900 Subject: [PATCH 286/576] docs(changelog): record overflowing residual bias recovery --- .../validation-bias-overflowing-residual-cancellation.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-overflowing-residual-cancellation.md diff --git a/CHANGELOG.d/validation-bias-overflowing-residual-cancellation.md b/CHANGELOG.d/validation-bias-overflowing-residual-cancellation.md new file mode 100644 index 000000000..ccd2bebad --- /dev/null +++ b/CHANGELOG.d/validation-bias-overflowing-residual-cancellation.md @@ -0,0 +1,3 @@ +### Fixed + +- Preserve a representable mean signed bias when individual `recovered - truth` residuals overflow but opposing represented input terms cancel. The fallback keeps the original paired-observation denominator, preserves minimum-subnormal results after extreme cancellation, and still fails closed when the final mean bias itself is unrepresentable. From 8c34e2f1657f319d925222851570d103759863b4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 01:04:13 +0900 Subject: [PATCH 287/576] docs(research): trace overflowing residual bias cancellation --- .../bias-overflowing-residual-cancellation.md | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 docs/research/bias-overflowing-residual-cancellation.md diff --git a/docs/research/bias-overflowing-residual-cancellation.md b/docs/research/bias-overflowing-residual-cancellation.md new file mode 100644 index 000000000..7c0edc787 --- /dev/null +++ b/docs/research/bias-overflowing-residual-cancellation.md @@ -0,0 +1,72 @@ +# Mean bias: overflowing residual cancellation + +## Problem + +TEPP defines signed bias as `mean(recovered - truth)`. The predecessor required every pairwise signed residual to be representable before computing the mean. That made an intermediate binary64 limitation authoritative even when the final scientific estimand was representable. + +For finite represented inputs + +- `truth = [-f64::MAX, f64::MAX, 0]` +- `recovered = [f64::MAX, -f64::MAX, 3 * f64::MIN_SUBNORMAL]` + +the first two mathematical residuals are `+2*MAX` and `-2*MAX`, so direct binary64 subtraction overflows in both directions. Those terms cancel exactly in the mean numerator. The third residual is three minimum subnormals, leaving an exact represented-input mean bias of one minimum subnormal. Rejecting this payload because the two intermediate residuals are non-finite discards a representable recovery metric. + +Morris, White, and Crowther (2019) treat bias as a performance measure relative to known simulation truth. The numerical implementation therefore has to preserve the declared estimand rather than silently replace it with the representability of one avoidable intermediate expression. + +This remains TEPP Validation Evidence arithmetic. It does not move reusable psychometric estimation ownership from `fast-mlsirm`. + +## Public RED + +Commit `04e6e74507a1adb89aac4b2a58d3682746da30ea` adds `crates/validation_core/tests/bias_overflowing_residual_cancellation_contract.rs`. + +The contract fixes three boundaries: + +1. two overflowing signed residuals with exact cancellation must yield bias `0.0`; +2. the same extreme cancellation plus three minimum subnormals over three observations must preserve one minimum-subnormal mean bias; and +3. a one-sided `2*MAX` mean bias remains unrepresentable and must fail closed. + +The RED-head GitHub workflows were superseded by subsequent source commits and cancelled, so they are not promoted as hosted RED execution evidence. + +## Causal repair + +Commit `28b1d186ce2bd08b63ca267c8b98b2eae45d2da7` factors the existing deterministic cancellation path into crate-private `deterministic_representable_sum_over_count(values, total_count)`. The divisor is explicit so an algebraically expanded numerator can retain the original scientific observation count. The helper keeps the existing sign-cancellation and exact-power-of-two scaling strategy, adds an explicit non-finite final-result rejection because the divisor can now differ from the number of terms, and preserves fail-closed behavior for nonzero results below binary64 range. + +Commit `d1cd54615ddc311491e26fa427e956b5b5379e1a` changes only `mean_bias` admission. When all pairwise residuals are finite, the existing direct residual path remains authoritative. If at least one finite-input pairwise subtraction overflows, the fallback evaluates the same numerator as recovered values plus negated truth values and divides by the original paired-observation count. Opposing extreme terms can therefore cancel before scale reduction without creating a second public bias definition. + +`bias_standard_error` is intentionally unchanged. Its requested dispersion depends on individual signed residual magnitudes; an unrepresentable residual is therefore not merely an avoidable intermediate for that API. + +CHANGELOG commit: `d68dde34c2c63e610a859d45e528c2134b3c0f91`. + +## Alternatives rejected + +Returning zero whenever positive and negative residual overflows coexist was rejected because an additional finite residual can leave a nonzero representable bias. Clamping overflowing residuals to `f64::MAX` was rejected because it changes the estimand and can reverse cancellation. Computing `mean(recovered) - mean(truth)` as the universal implementation was rejected because independently rounded means introduce a different rounding contract and can lose a representable small bias. Replacing all current mean arithmetic with arbitrary-precision production code was rejected as unnecessary scope expansion. + +## Scope and residual risk + +The fallback is entered only for finite paired inputs whose direct signed residual path contains an overflow. Ordinary finite residuals retain the predecessor implementation. The repair claims preservation of representable signed mean bias under that intermediate-overflow condition; it does not claim arbitrary-precision bias, a changed standard-error target, or a new psychometric estimator. + +The branch still requires current-head Rust tests, 100% owned line/branch coverage, documentation/security/SAST gates, independent review, and protected-main integration before delivery. + +## Traceability + +- Bounded context: Validation Evidence +- Module/API: `crates/validation_core/src/bias.rs` / `mean_bias` +- Shared crate-private arithmetic: `crates/validation_core/src/numeric.rs` / `deterministic_representable_sum_over_count` +- Public RED: `04e6e74507a1adb89aac4b2a58d3682746da30ea` +- Shared arithmetic repair: `28b1d186ce2bd08b63ca267c8b98b2eae45d2da7` +- Bias causal repair: `d1cd54615ddc311491e26fa427e956b5b5379e1a` +- CHANGELOG: `d68dde34c2c63e610a859d45e528c2134b3c0f91` +- Contract test: `crates/validation_core/tests/bias_overflowing_residual_cancellation_contract.rs` +- Landing vehicle: PR #488; only its latest exact head after this documentation commit is authoritative for hosted checks and review. + +## Normative and methodological references + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE Standards Association. https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization & International Electrotechnical Commission. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. + +As of 2026-09-05, IEEE 754-2019 remains an active published standard; IEEE P754 remains an active revision PAR rather than a published replacement. ISO/IEC 60559:2020 remains published at stage 60.60. The AERA/APA/NCME Joint Committee is revising the 2014 testing Standards; the unpublished revision is not treated as current normative authority. From 63913727a3dfc2dd65c2508fe7e4acfdd6498508 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 01:58:14 +0900 Subject: [PATCH 288/576] test(validation): expose mixed-sign cancellation roundoff --- .../bias_cancellation_roundoff_contract.rs | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 crates/validation_core/tests/bias_cancellation_roundoff_contract.rs diff --git a/crates/validation_core/tests/bias_cancellation_roundoff_contract.rs b/crates/validation_core/tests/bias_cancellation_roundoff_contract.rs new file mode 100644 index 000000000..3e34f8311 --- /dev/null +++ b/crates/validation_core/tests/bias_cancellation_roundoff_contract.rs @@ -0,0 +1,41 @@ +use validation_core::mean_bias; + +#[test] +fn mixed_sign_bias_preserves_small_opposing_mass_before_division() { + let quarter_ulp_at_one = 2.0_f64.powi(-54); + let truth = [ + 0.0, + quarter_ulp_at_one, + quarter_ulp_at_one, + quarter_ulp_at_one, + quarter_ulp_at_one, + ]; + let recovered = [1.0, 0.0, 0.0, 0.0, 0.0]; + + let bias = mean_bias(&truth, &recovered) + .expect("the represented-input mean bias is finite and representable"); + + assert_eq!( + bias.to_bits(), + 0x3fc9_9999_9999_9998, + "four quarter-ulp opposing residuals sum to one full ulp before division and must not be rounded away one at a time" + ); +} + +#[test] +fn mixed_sign_bias_roundoff_contract_is_sign_symmetric() { + let quarter_ulp_at_one = 2.0_f64.powi(-54); + let truth = [ + 0.0, + -quarter_ulp_at_one, + -quarter_ulp_at_one, + -quarter_ulp_at_one, + -quarter_ulp_at_one, + ]; + let recovered = [-1.0, 0.0, 0.0, 0.0, 0.0]; + + let bias = mean_bias(&truth, &recovered) + .expect("the represented-input mean bias is finite and representable"); + + assert_eq!(bias.to_bits(), (-f64::from_bits(0x3fc9_9999_9999_9998)).to_bits()); +} From 5697cca51df2ec49e44a04730a14fd77656b48a5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 01:59:54 +0900 Subject: [PATCH 289/576] fix(validation): retain cancellation roundoff mass --- crates/validation_core/src/numeric.rs | 95 ++++++++++++++++++++++----- 1 file changed, 80 insertions(+), 15 deletions(-) diff --git a/crates/validation_core/src/numeric.rs b/crates/validation_core/src/numeric.rs index f2d3c58ce..d8fc5877e 100644 --- a/crates/validation_core/src/numeric.rs +++ b/crates/validation_core/src/numeric.rs @@ -51,14 +51,53 @@ fn same_sign_mean_over_total(values: &[f64], total_count: usize) -> Result (f64, f64) { + let sum = left + right; + let right_virtual = sum - left; + let left_virtual = sum - right_virtual; + let left_roundoff = left - left_virtual; + let right_roundoff = right - right_virtual; + (sum, left_roundoff + right_roundoff) +} + +fn mixed_remainder_mean_over_total( + values: &[f64], + total_count: usize, +) -> Result { + let max_magnitude = values + .iter() + .map(|value| value.abs()) + .fold(0.0, f64::max); + if max_magnitude == 0.0 { + return Ok(0.0); + } + + let scale = exact_power_of_two_scale(max_magnitude); + let normalized = values.iter().map(|value| *value / scale).collect(); + let normalized_sum = deterministic_compensated_sum(normalized); + if normalized_sum == 0.0 { + return Ok(0.0); + } + + let normalized_mean = normalized_sum / total_count as f64; + let mean = normalized_mean * scale; + if !mean.is_finite() || mean == 0.0 { + Err(ValidationError::InvalidInput) + } else { + Ok(mean) + } +} + /// Deterministically divide the represented sum of finite binary64 values by an explicit count. /// -/// Opposite signs cancel before exact power-of-two scale reduction. The divisor -/// is independent of `values.len()`, which lets callers preserve an original -/// scientific denominator when an algebraically equivalent expanded term set is -/// needed to avoid overflowing intermediate differences. Exact cancellation -/// returns canonical zero; a nonzero quotient outside or below binary64 range -/// fails closed. +/// Opposite signs cancel before exact power-of-two scale reduction. Each +/// opposite-sign addition also retains its error-free low term so repeated +/// sub-ULP contributions cannot disappear one at a time before they collectively +/// become representable. The divisor is independent of `values.len()`, which +/// lets callers preserve an original scientific denominator when an algebraically +/// equivalent expanded term set is needed to avoid overflowing intermediate +/// differences. Exact cancellation returns canonical zero; a nonzero quotient +/// outside or below binary64 range fails closed. /// /// # Errors /// @@ -97,9 +136,14 @@ pub(crate) fn deterministic_representable_sum_over_count( let mut positive = positives[0]; let mut negative = negatives[0]; let mut residuals = Vec::with_capacity(values.len()); + let mut roundoff_terms = Vec::new(); loop { - let residual = positive + negative; + let (residual, roundoff) = error_free_sum(positive, negative); + if roundoff != 0.0 { + roundoff_terms.push(roundoff); + } + if residual > 0.0 { positive = residual; negative_index += 1; @@ -131,19 +175,26 @@ pub(crate) fn deterministic_representable_sum_over_count( } } - if residuals.is_empty() { - return Ok(0.0); + if roundoff_terms.is_empty() { + if residuals.is_empty() { + return Ok(0.0); + } + return same_sign_mean_over_total(&residuals, total_count); } - same_sign_mean_over_total(&residuals, total_count) + + residuals.extend(roundoff_terms); + mixed_remainder_mean_over_total(&residuals, total_count) } /// Deterministic mean of finite binary64 values with cancellation before scale reduction. /// -/// Opposite signs cancel at represented magnitude before the remaining one-sign -/// mass is normalized by an exact power of two. The original sample count stays -/// in the denominator after cancellation. Exact all-zero input and exact mixed- -/// sign cancellation return canonical zero; a mathematically nonzero one-sign -/// mean that falls below binary64 range fails closed. +/// Opposite signs cancel at represented magnitude before the remaining mass is +/// normalized by an exact power of two. Error-free low terms from cancellation +/// are retained so several individually sub-ULP contributions can still affect +/// the represented mean when their combined mass is large enough. The original +/// sample count stays in the denominator after cancellation. Exact all-zero input +/// and exact mixed-sign cancellation return canonical zero; a mathematically +/// nonzero mean that falls below binary64 range fails closed. /// /// # Errors /// @@ -202,6 +253,20 @@ mod tests { ); } + #[test] + fn representable_mean_retains_accumulated_opposite_sign_roundoff() { + let quarter_ulp_at_one = 2.0_f64.powi(-54); + let mean = deterministic_representable_mean(&[ + 1.0, + -quarter_ulp_at_one, + -quarter_ulp_at_one, + -quarter_ulp_at_one, + -quarter_ulp_at_one, + ]) + .expect("representable mixed-sign mean"); + assert_eq!(mean.to_bits(), 0x3fc9_9999_9999_9998); + } + #[test] fn explicit_denominator_preserves_representable_expanded_sum() { let minimum_subnormal = f64::from_bits(1); From 53f5c912fbca415172f621c909b626ea1db66582 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 02:01:07 +0900 Subject: [PATCH 290/576] docs(changelog): record bias cancellation roundoff repair --- CHANGELOG.d/validation-bias-cancellation-roundoff.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-cancellation-roundoff.md diff --git a/CHANGELOG.d/validation-bias-cancellation-roundoff.md b/CHANGELOG.d/validation-bias-cancellation-roundoff.md new file mode 100644 index 000000000..456fe2e7c --- /dev/null +++ b/CHANGELOG.d/validation-bias-cancellation-roundoff.md @@ -0,0 +1,3 @@ +### Fixed + +- Validation Evidence mean-bias accumulation now retains error-free low terms from opposite-sign cancellation before scale reduction. Repeated sub-ULP residuals can therefore contribute when their combined represented mass changes the final bias, while exact cancellation and fail-closed unrepresentable results keep their existing semantics. From 4b74099b2f1d4e1afb6003af113b471f13df6bf9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 02:01:37 +0900 Subject: [PATCH 291/576] docs(research): trace bias cancellation roundoff --- .../bias-mixed-sign-cancellation-roundoff.md | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 docs/research/bias-mixed-sign-cancellation-roundoff.md diff --git a/docs/research/bias-mixed-sign-cancellation-roundoff.md b/docs/research/bias-mixed-sign-cancellation-roundoff.md new file mode 100644 index 000000000..68b085c8e --- /dev/null +++ b/docs/research/bias-mixed-sign-cancellation-roundoff.md @@ -0,0 +1,69 @@ +# Mean bias: mixed-sign cancellation roundoff + +## Problem + +TEPP defines signed bias as `mean(recovered - truth)`. After GAP-086, overflowing pairwise residuals can be algebraically expanded and opposing extreme terms cancel before scale reduction. A separate finite-input defect remained inside that shared cancellation helper: repeatedly adding an opposite-sign term smaller than half an ULP of the current residual could round back to the same high part and silently discard the low term each time. + +For represented inputs + +- residuals: `[1, -2^-54, -2^-54, -2^-54, -2^-54]` +- equivalent public pair input: `truth=[0, 2^-54, 2^-54, 2^-54, 2^-54]`, `recovered=[1, 0, 0, 0, 0]` + +an implementation that materializes each cancellation as ordinary binary64 addition can keep the running high part at exactly `1.0` four times. The four discarded low terms sum to `2^-52`, so the exact represented-input numerator is `1 - 2^-52`; dividing by five rounds to bits `0x3fc9999999999998`, not ordinary `0.2` (`0x3fc999999999999a`). The final scientific estimand is representable, so discarding the low terms is an arithmetic defect rather than a justified fail-closed boundary. + +Morris, White, and Crowther (2019) treat bias against known truth as a simulation performance measure. TEPP therefore preserves the represented-input estimand rather than making the rounding behavior of an avoidable pairwise cancellation step authoritative. + +This remains Validation Evidence arithmetic. It does not move reusable psychometric estimation ownership from `fast-mlsirm` and does not introduce a second public bias definition. + +## Public RED + +Commit `63913727a3dfc2dd65c2508fe7e4acfdd6498508` adds `crates/validation_core/tests/bias_cancellation_roundoff_contract.rs`. + +The contract fixes both signs of the same scientific boundary. Four quarter-ULP opposing residuals must collectively affect the represented mean bias even though each individual subtraction from the running high part is too small to change that high part by itself. + +The RED commit is preserved as source-level reproducer lineage. Any later hosted run cancelled or superseded by subsequent source pushes is not treated as current-head RED execution evidence. + +## Causal repair + +Commit `5697cca51df2ec49e44a04730a14fd77656b48a5` changes only the shared deterministic cancellation path in `validation_core::numeric`. + +Each opposite-sign addition now uses an error-free TwoSum decomposition: the rounded high part continues to drive the existing magnitude-ordered cancellation, while any nonzero low term is retained. If every cancellation is exact, the predecessor same-sign scale-reduction path is unchanged. Only when low terms exist are the final high remainders and those low terms scale-normalized together and accumulated with the existing deterministic compensated summation before division by the original scientific denominator. + +This is narrower than replacing all mean arithmetic with arbitrary precision. It addresses the demonstrated loss of collectively material roundoff while preserving full-range cancellation, exact zero, the explicit denominator required by GAP-086, and fail-closed behavior for nonzero results outside or below binary64 range. + +CHANGELOG commit: `53f5c912fbca415172f621c909b626ea1db66582`. + +## Alternatives rejected + +Always summing normalized original inputs was rejected because extreme cancellation such as `MAX + (-MAX) + MIN_SUBNORMAL` can lose the tiny surviving term during normalization before the large terms cancel. Pairwise Kahan/Neumaier accumulation over the original unscaled inputs was rejected because same-sign extreme intermediates may overflow even when the final mean is representable. Arbitrary-precision production arithmetic was rejected as unnecessary for this bounded counterexample and would add a new runtime dependency and performance surface. + +Ignoring the low terms because each is individually below one ULP was rejected because the public RED proves their combined represented mass changes the final binary64 bias by two ULPs. + +## Scope and residual risk + +The repair claims only that opposite-sign cancellation no longer drops representable TwoSum low terms one at a time before their aggregate can affect the final mean. It does not claim globally correctly rounded summation for every possible binary64 sequence, nor does it alter `bias_standard_error`, whose estimand still requires individually representable signed residual dispersion. + +A future stronger summation rule requires an independent represented-input counterexample plus a bounded causal repair. Algebraic suspicion alone is not sufficient to create another Validation gap. + +## Traceability + +- Bounded context: Validation Evidence +- Public API: `crates/validation_core/src/bias.rs` / `mean_bias` +- Shared arithmetic: `crates/validation_core/src/numeric.rs` / `deterministic_representable_sum_over_count` +- Public RED: `63913727a3dfc2dd65c2508fe7e4acfdd6498508` +- Causal repair: `5697cca51df2ec49e44a04730a14fd77656b48a5` +- CHANGELOG: `53f5c912fbca415172f621c909b626ea1db66582` +- Contract test: `crates/validation_core/tests/bias_cancellation_roundoff_contract.rs` +- Landing vehicle: PR #488; only its latest exact head after this documentation commit is authoritative for hosted checks and review. + +## Normative and methodological references + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE Standards Association. https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization & International Electrotechnical Commission. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. + +As of 2026-09-05, IEEE 754-2019 remains an active published standard; IEEE P754 remains an active revision PAR rather than a published replacement. ISO/IEC 60559:2020 remains published at stage 60.60. AERA continues to publish the 2014 Testing Standards while the AERA/APA/NCME Joint Committee revises that edition; the unpublished revision is not treated as current normative authority. From 8b4d19d161cb4322db3a143b2e34125d3bcc08f1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 03:01:50 +0900 Subject: [PATCH 292/576] test(validation): expose compensated-mean double rounding --- .../bias_compensated_division_contract.rs | 20 +++++++++++++++++++ 1 file changed, 20 insertions(+) create mode 100644 crates/validation_core/tests/bias_compensated_division_contract.rs diff --git a/crates/validation_core/tests/bias_compensated_division_contract.rs b/crates/validation_core/tests/bias_compensated_division_contract.rs new file mode 100644 index 000000000..cf2eda523 --- /dev/null +++ b/crates/validation_core/tests/bias_compensated_division_contract.rs @@ -0,0 +1,20 @@ +use validation_core::mean_bias; + +#[test] +fn mean_bias_divides_compensated_numerator_without_double_rounding() { + let two_to_minus_53 = 2.0_f64.powi(-53); + let two_to_minus_52 = 2.0_f64.powi(-52); + let truth = [0.0, 0.0, 0.0]; + let recovered = [ + two_to_minus_53, + -two_to_minus_52, + -(1.0 + two_to_minus_52), + ]; + + let bias = mean_bias(&truth, &recovered).expect("represented mean bias"); + assert_eq!(bias.to_bits(), 0xbfd5_5555_5555_5557); + + let mirrored: Vec<_> = recovered.iter().map(|value| -*value).collect(); + let mirrored_bias = mean_bias(&truth, &mirrored).expect("mirrored represented mean bias"); + assert_eq!(mirrored_bias.to_bits(), 0x3fd5_5555_5555_5557); +} From 7a33212b0c0f079a9fb138b6a8564881bc22fc9e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 03:02:51 +0900 Subject: [PATCH 293/576] fix(validation): carry compensated numerator through mean division --- crates/validation_core/src/numeric.rs | 48 +++++++++++++++++---------- 1 file changed, 30 insertions(+), 18 deletions(-) diff --git a/crates/validation_core/src/numeric.rs b/crates/validation_core/src/numeric.rs index d8fc5877e..1a25586ac 100644 --- a/crates/validation_core/src/numeric.rs +++ b/crates/validation_core/src/numeric.rs @@ -2,12 +2,7 @@ use crate::ValidationError; -/// Sum finite values in a canonical order with Neumaier compensation. -/// -/// Callers own domain validation and any scale normalization needed to keep the -/// final sum representable. Canonical ordering keeps equivalent metric inputs -/// from changing only because transport order changed. -pub(crate) fn deterministic_compensated_sum(mut values: Vec) -> f64 { +fn deterministic_compensated_parts(mut values: Vec) -> (f64, f64) { values.sort_by(f64::total_cmp); let mut sum = 0.0_f64; let mut correction = 0.0_f64; @@ -20,6 +15,16 @@ pub(crate) fn deterministic_compensated_sum(mut values: Vec) -> f64 { } sum = next; } + (sum, correction) +} + +/// Sum finite values in a canonical order with Neumaier compensation. +/// +/// Callers own domain validation and any scale normalization needed to keep the +/// final sum representable. Canonical ordering keeps equivalent metric inputs +/// from changing only because transport order changed. +pub(crate) fn deterministic_compensated_sum(values: Vec) -> f64 { + let (sum, correction) = deterministic_compensated_parts(values); sum + correction } @@ -74,12 +79,15 @@ fn mixed_remainder_mean_over_total( let scale = exact_power_of_two_scale(max_magnitude); let normalized = values.iter().map(|value| *value / scale).collect(); - let normalized_sum = deterministic_compensated_sum(normalized); - if normalized_sum == 0.0 { + let (normalized_sum, normalized_correction) = deterministic_compensated_parts(normalized); + if normalized_sum == 0.0 && normalized_correction == 0.0 { return Ok(0.0); } - let normalized_mean = normalized_sum / total_count as f64; + let denominator = total_count as f64; + let leading_mean = normalized_sum / denominator; + let division_residual = (-leading_mean).mul_add(denominator, normalized_sum); + let normalized_mean = leading_mean + (division_residual + normalized_correction) / denominator; let mean = normalized_mean * scale; if !mean.is_finite() || mean == 0.0 { Err(ValidationError::InvalidInput) @@ -93,11 +101,14 @@ fn mixed_remainder_mean_over_total( /// Opposite signs cancel before exact power-of-two scale reduction. Each /// opposite-sign addition also retains its error-free low term so repeated /// sub-ULP contributions cannot disappear one at a time before they collectively -/// become representable. The divisor is independent of `values.len()`, which -/// lets callers preserve an original scientific denominator when an algebraically -/// equivalent expanded term set is needed to avoid overflowing intermediate -/// differences. Exact cancellation returns canonical zero; a nonzero quotient -/// outside or below binary64 range fails closed. +/// become representable. When those retained terms remain material after +/// normalization, their compensation is carried through the scientific divisor +/// instead of being rounded into a single numerator first. The divisor is +/// independent of `values.len()`, which lets callers preserve an original +/// scientific denominator when an algebraically equivalent expanded term set is +/// needed to avoid overflowing intermediate differences. Exact cancellation +/// returns canonical zero; a nonzero quotient outside or below binary64 range +/// fails closed. /// /// # Errors /// @@ -191,10 +202,11 @@ pub(crate) fn deterministic_representable_sum_over_count( /// Opposite signs cancel at represented magnitude before the remaining mass is /// normalized by an exact power of two. Error-free low terms from cancellation /// are retained so several individually sub-ULP contributions can still affect -/// the represented mean when their combined mass is large enough. The original -/// sample count stays in the denominator after cancellation. Exact all-zero input -/// and exact mixed-sign cancellation return canonical zero; a mathematically -/// nonzero mean that falls below binary64 range fails closed. +/// the represented mean when their combined mass is large enough, and retained +/// compensation is carried through the original sample-count division before the +/// final scale is restored. Exact all-zero input and exact mixed-sign cancellation +/// return canonical zero; a mathematically nonzero mean that falls below +/// binary64 range fails closed. /// /// # Errors /// From bad698dd686ce69a96aee1ca748bd7dc8e63aa1b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 03:03:06 +0900 Subject: [PATCH 294/576] docs(changelog): record compensated mean division repair --- CHANGELOG.d/validation-bias-compensated-division.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-compensated-division.md diff --git a/CHANGELOG.d/validation-bias-compensated-division.md b/CHANGELOG.d/validation-bias-compensated-division.md new file mode 100644 index 000000000..7d3236905 --- /dev/null +++ b/CHANGELOG.d/validation-bias-compensated-division.md @@ -0,0 +1,3 @@ +- Preserve mixed-sign Validation Evidence mean-bias compensation through the original scientific denominator so a correctly retained low term is not rounded into the numerator and lost again before division. +- Add a public mirrored contract for the represented residual boundary `[2^-53, -2^-52, -(1 + 2^-52)]`, whose exact mean rounds to `±0x1.5555555555557p-2`. +- Keep same-sign arithmetic, reusable psychometric estimation ownership, and `bias_standard_error` unchanged. From ba81aa295a7877fb87767064547031851c31a754 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 03:03:41 +0900 Subject: [PATCH 295/576] docs(research): trace compensated mean division rounding --- .../bias-compensated-division-rounding.md | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 docs/research/bias-compensated-division-rounding.md diff --git a/docs/research/bias-compensated-division-rounding.md b/docs/research/bias-compensated-division-rounding.md new file mode 100644 index 000000000..8598776eb --- /dev/null +++ b/docs/research/bias-compensated-division-rounding.md @@ -0,0 +1,79 @@ +# Mean bias: compensated numerator division rounding + +## Problem + +TEPP defines signed bias as `mean(recovered - truth)`. GAP-087 retained error-free low terms from opposite-sign cancellation, but the mixed-remainder path still collapsed the compensated normalized numerator to one binary64 value before dividing by the scientific observation count. That extra rounding can change the final represented mean even when the compensated numerator carries enough information to avoid it. + +A public three-observation boundary is: + +- truth: `[0, 0, 0]`; +- recovered/residuals: `[2^-53, -2^-52, -(1 + 2^-52)]`. + +The exact represented-input numerator is + +`2^-53 - 2^-52 - (1 + 2^-52) = -(1 + 3*2^-53)`. + +Its exact mean is + +`-9007199254740995 / 27021597764222976`, + +which rounds to binary64 `-0x1.5555555555557p-2` (bits `0xbfd5555555555557`). The predecessor Neumaier path retained a high part and correction, but `sum + correction` rounded the numerator first to `-0x1.0000000000002p+0`; dividing that rounded numerator by three produced `-0x1.5555555555558p-2`, one ULP away from the represented-input mean. The mirrored positive case has the same defect. + +This is a Validation Evidence arithmetic defect. It does not change the scientific definition of bias, create a new estimator, or move reusable static psychometric ownership out of `fast-mlsirm`. + +## Public RED + +Commit `8b4d19d161cb4322db3a143b2e34125d3bcc08f1` adds `crates/validation_core/tests/bias_compensated_division_contract.rs` and fixes both signs of the boundary. The contract requires `mean_bias` to return bits `0xbfd5555555555557` and `0x3fd5555555555557` from public pair inputs. + +The RED is preserved as source-level reproducer evidence. Hosted runs superseded or cancelled by later source pushes are not promoted as current-head GREEN or RED execution evidence. + +## Causal repair + +Commit `7a33212b0c0f079a9fb138b6a8564881bc22fc9e` keeps the existing magnitude-ordered cancellation and GAP-087 low-term retention. It factors the canonical Neumaier pass into a private `(sum, correction)` result only for internal reuse. The existing `deterministic_compensated_sum` public-to-crate behavior remains `sum + correction` for callers that request a sum. + +Only `mixed_remainder_mean_over_total` changes its mean formation. It now: + +1. preserves the normalized Neumaier high part and correction separately; +2. divides the high part by the original scientific denominator; +3. uses binary64 FMA to recover the division residual of that high part; +4. combines that residual with the retained compensation before the final denominator division; +5. restores the same exact power-of-two scale and keeps the existing fail-closed non-finite/zero-underflow boundary. + +For the RED, the retained correction is `+2^-53` relative to the rounded negative high part. Carrying that mass through division moves the result from predecessor bits `0xbfd5555555555558` to the represented-input result `0xbfd5555555555557`. + +The repair intentionally does not alter the same-sign path or `bias_standard_error`; neither is implicated by this counterexample. CHANGELOG commit: `bad698dd686ce69a96aee1ca748bd7dc8e63aa1b`. + +## Alternatives rejected + +Replacing all Validation arithmetic with arbitrary precision was rejected because this bounded defect is caused by one avoidable rounding boundary and does not justify a new production dependency or latency surface. + +Dividing `sum + correction` was rejected because that is the defective double-rounding sequence demonstrated by the RED. Dividing `sum` and `correction` independently and simply adding the quotients was also not adopted as the contract: the chosen FMA step additionally recovers the high-part division residual before the retained correction is consumed. + +Changing the same-sign mean path or `bias_standard_error` without a represented-input counterexample was rejected as scope expansion. A future change there requires its own RED and causal proof. + +## Scope and residual risk + +This repair claims the demonstrated property that retained mixed-sign compensation is not forced through an additional numerator rounding before the original scientific count division. It does not claim globally correctly rounded summation or division for every binary64 sequence, every possible `usize` count, or every Validation metric. + +A later numerical gap requires an independent public counterexample in which the current canonical producer differs from the represented-input estimand. Algebraic suspicion or an oracle-only mismatch outside a declared public contract is not sufficient by itself. + +## Traceability + +- Bounded context: Validation Evidence +- Public API: `crates/validation_core/src/bias.rs` / `mean_bias` +- Shared arithmetic: `crates/validation_core/src/numeric.rs` / `deterministic_representable_sum_over_count` +- Public RED: `8b4d19d161cb4322db3a143b2e34125d3bcc08f1` +- Causal repair: `7a33212b0c0f079a9fb138b6a8564881bc22fc9e` +- CHANGELOG: `bad698dd686ce69a96aee1ca748bd7dc8e63aa1b` +- Contract test: `crates/validation_core/tests/bias_compensated_division_contract.rs` +- Landing vehicle: PR #488; only its latest exact head after this documentation commit is authoritative for hosted checks and review. + +## Normative and methodological references + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE Standards Association. https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization & International Electrotechnical Commission. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. American Educational Research Association. From 84476aad7ef2918c174c1ef986cbec2851cac656 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 03:28:12 +0900 Subject: [PATCH 296/576] test(validation): expose same-sign remainder division rounding --- ...as_same_sign_remainder_division_contract.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 crates/validation_core/tests/bias_same_sign_remainder_division_contract.rs diff --git a/crates/validation_core/tests/bias_same_sign_remainder_division_contract.rs b/crates/validation_core/tests/bias_same_sign_remainder_division_contract.rs new file mode 100644 index 000000000..48dd4a68c --- /dev/null +++ b/crates/validation_core/tests/bias_same_sign_remainder_division_contract.rs @@ -0,0 +1,18 @@ +use validation_core::mean_bias; + +#[test] +fn mean_bias_keeps_same_sign_remainder_compensation_through_division() { + let truth = [0.0, 0.0, 0.0]; + let recovered = [ + f64::from_bits(0x3fc0_0000_0000_0004), + f64::from_bits(0x3fbf_ffff_ffff_fffc), + f64::from_bits(0xbfbf_ffff_ffff_fffd), + ]; + + let bias = mean_bias(&truth, &recovered).expect("represented mean bias"); + assert_eq!(bias.to_bits(), 0x3fa5_5555_5555_555a); + + let mirrored: Vec<_> = recovered.iter().map(|value| -*value).collect(); + let mirrored_bias = mean_bias(&truth, &mirrored).expect("mirrored represented mean bias"); + assert_eq!(mirrored_bias.to_bits(), 0xbfa5_5555_5555_555a); +} From 5f0d40b838e9b9867ec40281f1e4ab6db96a12cb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 03:29:23 +0900 Subject: [PATCH 297/576] fix(validation): preserve same-sign remainder compensation through division --- crates/validation_core/src/numeric.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/src/numeric.rs b/crates/validation_core/src/numeric.rs index 1a25586ac..2d76eaa35 100644 --- a/crates/validation_core/src/numeric.rs +++ b/crates/validation_core/src/numeric.rs @@ -47,7 +47,11 @@ fn same_sign_mean_over_total(values: &[f64], total_count: usize) -> Result Date: Sat, 5 Sep 2026 03:29:56 +0900 Subject: [PATCH 298/576] docs(changelog): record same-sign remainder division repair --- CHANGELOG.d/validation-bias-same-sign-remainder-division.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-same-sign-remainder-division.md diff --git a/CHANGELOG.d/validation-bias-same-sign-remainder-division.md b/CHANGELOG.d/validation-bias-same-sign-remainder-division.md new file mode 100644 index 000000000..2a444034b --- /dev/null +++ b/CHANGELOG.d/validation-bias-same-sign-remainder-division.md @@ -0,0 +1,3 @@ +### Validation + +- Preserve Neumaier compensation through the scientific count division when exact mixed-sign cancellation leaves a same-sign remainder. This prevents a represented-input mean bias from moving by one ULP because `sum + correction` was rounded before division. The public regression contract covers the sign-mirrored boundary as well. From d1c20f68be2d2cc584b47fd0fecfbde2d3e268f6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 03:30:23 +0900 Subject: [PATCH 299/576] docs(research): trace same-sign remainder division rounding --- ...s-same-sign-remainder-division-rounding.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 docs/research/bias-same-sign-remainder-division-rounding.md diff --git a/docs/research/bias-same-sign-remainder-division-rounding.md b/docs/research/bias-same-sign-remainder-division-rounding.md new file mode 100644 index 000000000..3fcf1a3b7 --- /dev/null +++ b/docs/research/bias-same-sign-remainder-division-rounding.md @@ -0,0 +1,60 @@ +# Same-sign remainder compensation at the mean-bias division boundary + +## Decision status + +Proposed evidence for the Validation bounded context. This note records the represented-input numerical contract implemented on PR #488; it does not claim globally correctly rounded binary64 summation or division. + +## Problem + +`deterministic_representable_sum_over_count` cancels opposite signs before scale reduction. GAP-088 repaired the path where cancellation itself produced retained low terms, but exact opposite-sign cancellation can leave a same-sign remainder and no cancellation roundoff term. That path still called `same_sign_mean_over_total`, which formed the compensated numerator as `sum + correction` before dividing by the scientific count. + +The public RED commit `84476aad7ef2918c174c1ef986cbec2851cac656` fixes the represented residual payload to: + +- `0x1.0000000000004p-3` +- `0x1.ffffffffffffcp-4` +- `-0x1.ffffffffffffdp-4` + +Their exact represented-input numerator is `9007199254740999 / 72057594037927936`; dividing by three gives `3002399751580333 / 72057594037927936`. The correctly rounded binary64 mean is bits `0x3fa555555555555a`. The predecessor returned `0x3fa555555555555b` because the Neumaier high part and correction were rounded back together before the count division. The sign-mirrored payload has the symmetric expected bits `0xbfa555555555555a`. + +## Constraints + +- Keep `mean_bias` as TEPP Validation Evidence semantics; do not move this decision rule into fast-mlsirm. +- Preserve the original paired-observation denominator even when cancellation rewrites the represented numerator. +- Do not add arbitrary-precision arithmetic to the production hot path for a boundary that binary64 FMA and retained compensation can resolve. +- Do not change `bias_standard_error`; this counterexample concerns only the represented mean-bias numerator/division path. +- Do not infer a general correctly-rounded summation guarantee from this repair. + +## Alternatives considered + +Forming `sum + correction` before division was rejected because it reproduces the observed one-ULP error. Always switching to arbitrary-precision rational arithmetic was rejected because the public metric is a deterministic binary64 reference and the failure is caused by one avoidable intermediate rounding boundary. Replacing the cancellation algorithm was also rejected because the counterexample shows that the cancellation result is already correct; the defect is downstream in the same-sign remainder division. + +## Decision + +Causal repair `5f0d40b838e9b9867ec40281f1e4ab6db96a12cb` changes only `same_sign_mean_over_total`. It keeps the canonical Neumaier high part and correction separate, divides the high part by the original scientific denominator, recovers the division residual with binary64 FMA, and then carries that residual and the retained correction through the same denominator before restoring the exact power-of-two scale. This mirrors the already-established GAP-088 division boundary without changing the cancellation owner or public estimand. + +## Expected effect and risk + +The RED and its sign mirror now select the represented-input result rather than the predecessor's one-ULP neighbor. Existing same-sign and exact-cancellation paths retain their scientific denominator and scale policy. Remaining risk is intentionally bounded: `division_residual + correction` and the final correction addition are still binary64 operations, so unrelated payloads may require separate evidence before any stronger rounding claim is made. + +## Traceability + +- PR: #488, `fix/validation-bias-overflow-safe-mean` +- Public RED: `84476aad7ef2918c174c1ef986cbec2851cac656` +- Production repair: `5f0d40b838e9b9867ec40281f1e4ab6db96a12cb` +- Changelog: `5763cde0292b05fda4ebc07436e31e1309e97572` +- Production module: `crates/validation_core/src/numeric.rs` +- Public contract: `crates/validation_core/tests/bias_same_sign_remainder_division_contract.rs` + +## Standards and methodological authority + +IEEE 754-2019 remains the active IEEE floating-point standard as verified on 2026-09-05; IEEE P754 is an active revision PAR and not a published replacement. ISO/IEC 60559:2020 remains a published International Standard. The AERA/APA/NCME published testing standards remain the 2014 edition while the Joint Committee revision proceeds. Morris, White, and Crowther (2019) remains the methodological authority used here for defining simulation performance measures against known truth; it does not prescribe this implementation detail. + +## 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. + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization & International Electrotechnical Commission. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 91abdb496ef13229ed95bcf8854770a2cc71b4b8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 04:03:56 +0900 Subject: [PATCH 300/576] test(validation): expose same-sign subnormal mean double rounding --- ...sign_subnormal_double_rounding_contract.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs diff --git a/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs b/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs new file mode 100644 index 000000000..8d9b7db7e --- /dev/null +++ b/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs @@ -0,0 +1,23 @@ +use validation_core::mean_bias; + +#[test] +fn mean_bias_does_not_double_round_same_sign_subnormal_mean() { + let truth = [0.0, 0.0, 0.0]; + let minimum_normal_units = 1_u64 << 52; + let recovered = [ + f64::from_bits(minimum_normal_units - 32), + f64::from_bits(minimum_normal_units - 12), + f64::from_bits(minimum_normal_units - 20), + ]; + + // The exact represented-input mean is (3 * 2^52 - 64) / 3 subnormal units, + // which rounds to 2^52 - 21. A normalized intermediate can round first to + // a midpoint and then scale back to 2^52 - 22, so the public contract pins + // the single-rounding result at the final binary64 scale. + let bias = mean_bias(&truth, &recovered).expect("represented subnormal mean bias"); + assert_eq!(bias.to_bits(), minimum_normal_units - 21); + + let mirrored: Vec<_> = recovered.iter().map(|value| -*value).collect(); + let mirrored_bias = mean_bias(&truth, &mirrored).expect("mirrored subnormal mean bias"); + assert_eq!(mirrored_bias.to_bits(), (1_u64 << 63) | (minimum_normal_units - 21)); +} From e89dc34616dfba86683c8fc05611a7ac31a2d2c3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 04:06:22 +0900 Subject: [PATCH 301/576] fix(validation): avoid subnormal mean double rounding --- crates/validation_core/src/numeric.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/crates/validation_core/src/numeric.rs b/crates/validation_core/src/numeric.rs index 2d76eaa35..1dcf7441c 100644 --- a/crates/validation_core/src/numeric.rs +++ b/crates/validation_core/src/numeric.rs @@ -45,6 +45,24 @@ fn same_sign_mean_over_total(values: &[f64], total_count: usize) -> Result Date: Sat, 5 Sep 2026 04:07:55 +0900 Subject: [PATCH 302/576] fix(validation): round subnormal means once in represented units --- crates/validation_core/src/numeric.rs | 41 +++++++++++++++++---------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/crates/validation_core/src/numeric.rs b/crates/validation_core/src/numeric.rs index 1dcf7441c..b84212d5f 100644 --- a/crates/validation_core/src/numeric.rs +++ b/crates/validation_core/src/numeric.rs @@ -46,21 +46,32 @@ fn same_sign_mean_over_total(values: &[f64], total_count: usize) -> Result= values.len() { + // Subnormal values are exact integer multiples of 2^-1074. With a divisor + // at least as large as the term count, the exact mean remains subnormal; + // summing 52-bit units over any supported `usize` length fits in `u128`. + // Round those represented units once instead of normalizing and then + // rounding again while restoring a subnormal power-of-two scale. + let negative = values.iter().any(|value| *value < 0.0); + let total_units: u128 = values + .iter() + .map(|value| (value.to_bits() & 0x000f_ffff_ffff_ffff) as u128) + .sum(); + let denominator = total_count as u128; + let mut rounded_units = total_units / denominator; + let remainder = total_units % denominator; + let twice_remainder = remainder * 2; + if twice_remainder > denominator + || (twice_remainder == denominator && rounded_units & 1 == 1) + { + rounded_units += 1; + } + if rounded_units == 0 { + return Err(ValidationError::InvalidInput); + } + + let sign = if negative { 1_u64 << 63 } else { 0 }; + return Ok(f64::from_bits(sign | rounded_units as u64)); } let scale = exact_power_of_two_scale(max_magnitude); From f79a1b9a1b299b34f87babbbdd766e4be6bd60df Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 04:08:09 +0900 Subject: [PATCH 303/576] test(validation): cover subnormal ties-to-even --- ...same_sign_subnormal_double_rounding_contract.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs b/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs index 8d9b7db7e..dbfcdf126 100644 --- a/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs +++ b/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs @@ -21,3 +21,17 @@ fn mean_bias_does_not_double_round_same_sign_subnormal_mean() { let mirrored_bias = mean_bias(&truth, &mirrored).expect("mirrored subnormal mean bias"); assert_eq!(mirrored_bias.to_bits(), (1_u64 << 63) | (minimum_normal_units - 21)); } + +#[test] +fn mean_bias_rounds_subnormal_halfway_cases_to_even_units() { + let truth = [0.0, 0.0]; + let minimum_subnormal = f64::from_bits(1); + + let odd_floor = [minimum_subnormal, f64::from_bits(2)]; + let odd_floor_bias = mean_bias(&truth, &odd_floor).expect("odd-floor halfway mean"); + assert_eq!(odd_floor_bias.to_bits(), 2); + + let even_floor = [f64::from_bits(2), f64::from_bits(3)]; + let even_floor_bias = mean_bias(&truth, &even_floor).expect("even-floor halfway mean"); + assert_eq!(even_floor_bias.to_bits(), 2); +} From eb29a79a3c51034dbff8d29782d8e71fe65012b6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 04:09:41 +0900 Subject: [PATCH 304/576] test(validation): pin exact subnormal unit halfway regression --- ...sign_subnormal_double_rounding_contract.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs b/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs index dbfcdf126..15f053651 100644 --- a/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs +++ b/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs @@ -35,3 +35,31 @@ fn mean_bias_rounds_subnormal_halfway_cases_to_even_units() { let even_floor_bias = mean_bias(&truth, &even_floor).expect("even-floor halfway mean"); assert_eq!(even_floor_bias.to_bits(), 2); } + +#[test] +fn mean_bias_uses_exact_subnormal_units_when_float_compensation_hits_a_halfway_case() { + let truth = [0.0; 16]; + let recovered = [ + f64::from_bits(0x0cb6_6819_cb62_2e), + f64::from_bits(0x0947_199e_fa89_8b), + f64::from_bits(0x06f1_7b1c_5d14_35), + f64::from_bits(0x0017_01d7_09e8_e5), + f64::from_bits(0x0c4f_af0b_45b7_f2), + f64::from_bits(0x0618_7545_3090_72), + f64::from_bits(0x0a85_ae8e_ad81_bb), + f64::from_bits(0x08c0_d3d1_61a3_65), + f64::from_bits(0x0dd3_b523_39f6_96), + f64::from_bits(0x0d7e_53f0_d4c2_46), + f64::from_bits(0x0175_247a_3171_15), + f64::from_bits(0x0c74_caf9_0802_51), + f64::from_bits(0x0a8a_2b1f_baba_e0), + f64::from_bits(0x04dd_bbf2_5f17_35), + f64::from_bits(0x0efe_fdfe_b832_bd), + f64::from_bits(0x0887_8ef3_1114_1d), + ]; + + // The exact unit sum leaves remainder 8 on division by 16, exactly halfway + // between adjacent subnormals. The lower candidate is even and must win. + let bias = mean_bias(&truth, &recovered).expect("halfway represented mean bias"); + assert_eq!(bias.to_bits(), 0x092d_f11e_7dd9_b8); +} From 65ff502b8f46f4043ca44983722e0f9722105a08 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 04:09:53 +0900 Subject: [PATCH 305/576] docs(changelog): record subnormal mean single rounding --- .../validation-bias-same-sign-subnormal-double-rounding.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-same-sign-subnormal-double-rounding.md diff --git a/CHANGELOG.d/validation-bias-same-sign-subnormal-double-rounding.md b/CHANGELOG.d/validation-bias-same-sign-subnormal-double-rounding.md new file mode 100644 index 000000000..798189142 --- /dev/null +++ b/CHANGELOG.d/validation-bias-same-sign-subnormal-double-rounding.md @@ -0,0 +1,5 @@ +# Validation bias same-sign subnormal single rounding + +- `validation_core::mean_bias` now rounds same-sign all-subnormal represented residuals once at the final binary64 subnormal grid when the scientific divisor is at least the surviving term count. +- The public contract fixes the three-residual case `(3 * 2^52 - 64) / 3`, which must round to subnormal units `2^52 - 21` rather than the adjacent `2^52 - 22` produced by normalize-then-rescale double rounding. +- Halfway cases use IEEE 754 round-to-nearest, ties-to-even on exact represented subnormal units; a companion fixture prevents the discarded direct-float repair from reintroducing a one-ULP halfway error. From c845e36f16aed2c0d64d486b5647448d27831ea8 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 04:10:38 +0900 Subject: [PATCH 306/576] docs(research): trace subnormal mean double rounding repair --- ...ias-same-sign-subnormal-double-rounding.md | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 docs/research/bias-same-sign-subnormal-double-rounding.md diff --git a/docs/research/bias-same-sign-subnormal-double-rounding.md b/docs/research/bias-same-sign-subnormal-double-rounding.md new file mode 100644 index 000000000..9c6bf75d2 --- /dev/null +++ b/docs/research/bias-same-sign-subnormal-double-rounding.md @@ -0,0 +1,67 @@ +# Same-sign subnormal mean double rounding + +## Decision status + +Proposed evidence for the Validation bounded context on PR #488. This note defines the represented-input boundary repaired by GAP-090. It does not claim globally correctly rounded binary64 summation or division. + +## Problem + +`same_sign_mean_over_total` normalized same-sign residuals by an exact power of two, formed a compensated normalized mean, and then restored the scale. That is safe from overflow, but when every represented residual is subnormal the restore step is itself a rounding to the coarser subnormal grid. A correctly rounded normalized intermediate can therefore be rounded a second time to the wrong final binary64 neighbor. + +Public RED `91abdb496ef13229ed95bcf8854770a2cc71b4b8` fixes three positive represented residuals whose subnormal-unit counts are `2^52 - 32`, `2^52 - 12`, and `2^52 - 20`. Their exact represented-input mean is + +`(3 * 2^52 - 64) / 3 = 2^52 - 21 - 1/3` + +minimum-subnormal units. Round-to-nearest, ties-to-even therefore selects bits `0x000f_ffff_ffff_ffeb` (`2^52 - 21`). The predecessor normalize-then-rescale path returned adjacent bits `0x000f_ffff_ffff_ffea` (`2^52 - 22`). The public contract includes the sign mirror. + +## Constraints + +- Keep the public estimand `mean(recovered - truth)` and the original recovery-unit denominator unchanged. +- Keep Validation Evidence arithmetic in TEPP; do not move this boundary into fast-mlsirm. +- Do not add arbitrary-precision runtime arithmetic. Binary64 subnormals already have an exact integer-unit representation at `2^-1074`. +- Preserve overflow-safe normalization for normal-scale inputs and for explicit-denominator cases whose surviving term count can exceed the divisor. +- Continue to fail closed when a mathematically nonzero represented mean rounds to zero under the existing admission policy. + +## Alternatives considered + +Keeping normalize-then-rescale was rejected because the RED demonstrates a real one-ULP double-rounding error at the public metric boundary. A first repair attempt, commit `e89dc34616dfba86683c8fc05611a7ac31a2d2c3`, bypassed scaling for all-subnormal same-sign inputs but still used floating-point compensated accumulation before division. Exact-unit checking found a separate halfway payload where that direct-float path selected the odd adjacent unit rather than ties-to-even. It was therefore not retained as the causal repair. + +Always replacing the mean path with exact integer or rational arithmetic was also rejected. The defect is specific to a bounded subnormal domain where each represented input is already an integer multiple of `2^-1074`; normal-scale and mixed-remainder behavior retains the existing deterministic binary64 reference. + +## Decision + +Corrected repair `1c0df8a77c4b65583e9d1945864f0a72bc598a71` handles the bounded case in represented subnormal units when `max_magnitude < f64::MIN_POSITIVE` and the scientific divisor is at least the surviving term count. Each magnitude contributes its exact 52-bit subnormal unit count. The total fits in `u128` on supported `usize` widths, the exact integer quotient and remainder are computed against the original divisor, and one final round-to-nearest, ties-to-even decision selects the binary64 subnormal result. Other paths retain the predecessor normalization and compensation policy. + +Edge contract `f79a1b9a1b299b34f87babbbdd766e4be6bd60df` covers both odd-floor and even-floor halfway cases. Follow-up fixture `eb29a79a3c51034dbff8d29782d8e71fe65012b6` records the halfway payload that invalidated the discarded direct-float repair: its exact unit sum leaves remainder 8 on division by 16, so the lower even unit `0x0009_2df1_1e7d_d9b8` must be selected. + +## Expected effect and remaining risk + +The affected same-sign all-subnormal `mean_bias` path now has a single final rounding decision in the represented unit system instead of a normalized rounding followed by subnormal rescaling. Positive and negative sign mirrors share the same magnitude rule. Normal-scale inputs, mixed-sign cancellation with retained roundoff, RMSE, coverage, Monte Carlo summaries, and `bias_standard_error` are unchanged. + +The repair is intentionally bounded. Explicit-denominator paths with a divisor smaller than the surviving term count still use the previous normalization path, because their quotient can leave the subnormal grid. Any defect there requires its own represented-input counterexample before widening this implementation. + +## Traceability + +- PR: #488, `fix/validation-bias-overflow-safe-mean` +- Public RED: `91abdb496ef13229ed95bcf8854770a2cc71b4b8` +- Discarded first repair: `e89dc34616dfba86683c8fc05611a7ac31a2d2c3` +- Corrected production repair: `1c0df8a77c4b65583e9d1945864f0a72bc598a71` +- Halfway edge coverage: `f79a1b9a1b299b34f87babbbdd766e4be6bd60df` +- Direct-float regression fixture: `eb29a79a3c51034dbff8d29782d8e71fe65012b6` +- Changelog: `65ff502b8f46f4043ca44983722e0f9722105a08` +- Production module: `crates/validation_core/src/numeric.rs` +- Public contract: `crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs` + +## Standards and methodological authority + +IEEE 754-2019 remains the active IEEE floating-point standard as verified on 2026-09-05; IEEE P754 is an active revision PAR approved 2024-06-06 and is not a published replacement. ISO/IEC 60559:2020 remains a published International Standard at stage 60.60. AERA, APA, and NCME continue to revise the 2014 *Standards for Educational and Psychological Testing*; the Joint Committee announced in 2024 is charged with revising that edition, so no unpublished revision is treated as normative authority. Morris, White, and Crowther (2019) remains the methodological basis for defining simulation performance measures against known truth; it does not prescribe this floating-point implementation. + +## 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. + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization & International Electrotechnical Commission. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From a9d9bda3df1533eb91ece8e3bc6ce597b9c1400f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 04:34:43 +0900 Subject: [PATCH 307/576] test(validation): expose finite residual subtraction roundoff --- ...ias_pairwise_subtraction_roundoff_contract.rs | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) create mode 100644 crates/validation_core/tests/bias_pairwise_subtraction_roundoff_contract.rs diff --git a/crates/validation_core/tests/bias_pairwise_subtraction_roundoff_contract.rs b/crates/validation_core/tests/bias_pairwise_subtraction_roundoff_contract.rs new file mode 100644 index 000000000..82c33e7b9 --- /dev/null +++ b/crates/validation_core/tests/bias_pairwise_subtraction_roundoff_contract.rs @@ -0,0 +1,16 @@ +use validation_core::mean_bias; + +#[test] +fn mean_bias_preserves_pairwise_subtraction_roundoff_before_averaging() { + let truth = [2.0_f64.powi(-108), 2.0_f64.powi(-53)]; + let recovered = [2.0_f64.powi(-54), 1.0]; + + let bias = mean_bias(&truth, &recovered).expect("represented mean bias"); + assert_eq!(bias.to_bits(), 0x3fdf_ffff_ffff_ffff); + + let mirrored_truth = truth.map(|value| -value); + let mirrored_recovered = recovered.map(|value| -value); + let mirrored_bias = mean_bias(&mirrored_truth, &mirrored_recovered) + .expect("mirrored represented mean bias"); + assert_eq!(mirrored_bias.to_bits(), 0xbfdf_ffff_ffff_ffff); +} From 96bff8e55083fc791650b185f819cd3aa90dac1b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 04:38:35 +0900 Subject: [PATCH 308/576] fix(validation): preserve rounded subtraction mass in mean bias --- crates/validation_core/src/bias.rs | 48 +++++++++++++++++++----------- 1 file changed, 31 insertions(+), 17 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 318fa412f..15c18b802 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -23,6 +23,15 @@ fn signed_residuals(truth: &[f64], recovered: &[f64]) -> Result, Valida .collect() } +fn subtraction_has_roundoff(recovered: f64, truth: f64, residual: f64) -> bool { + let negated_truth = -truth; + let truth_virtual = residual - recovered; + let recovered_virtual = residual - truth_virtual; + let recovered_roundoff = recovered - recovered_virtual; + let truth_roundoff = negated_truth - truth_virtual; + recovered_roundoff + truth_roundoff != 0.0 +} + fn standard_error_from_deviations(deviations: &[f64]) -> Result { let scale = deviations .iter() @@ -91,14 +100,15 @@ fn scaled_standard_error(values: &[f64], mean: f64) -> Result Result Result { require_paired_finite(truth, recovered)?; - let residuals: Option> = truth - .iter() - .zip(recovered) - .map(|(truth_value, recovered_value)| { - let residual = recovered_value - truth_value; - residual.is_finite().then_some(residual) - }) - .collect(); - if let Some(residuals) = residuals { + let mut residuals = Vec::with_capacity(truth.len()); + let mut requires_expanded_numerator = false; + for (truth_value, recovered_value) in truth.iter().zip(recovered) { + let residual = recovered_value - truth_value; + if !residual.is_finite() { + requires_expanded_numerator = true; + break; + } + requires_expanded_numerator |= + subtraction_has_roundoff(*recovered_value, *truth_value, residual); + residuals.push(residual); + } + if !requires_expanded_numerator { return deterministic_representable_mean(&residuals); } From 676ccda02da0186361555879d5637c4f02178a71 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 04:45:28 +0900 Subject: [PATCH 309/576] fix(validation): retain second-order mixed-mean rounding tails --- crates/validation_core/src/numeric.rs | 103 +++++++++++++++++++++++++- 1 file changed, 100 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/numeric.rs b/crates/validation_core/src/numeric.rs index b84212d5f..f3976586b 100644 --- a/crates/validation_core/src/numeric.rs +++ b/crates/validation_core/src/numeric.rs @@ -18,6 +18,27 @@ fn deterministic_compensated_parts(mut values: Vec) -> (f64, f64) { (sum, correction) } +fn deterministic_compensated_parts_with_tail(mut values: Vec) -> (f64, f64, f64) { + values.sort_by(f64::total_cmp); + let mut sum = 0.0_f64; + let mut correction = 0.0_f64; + let mut correction_tail = 0.0_f64; + for value in values { + let next = sum + value; + let increment = if sum.abs() >= value.abs() { + (sum - next) + value + } else { + (value - next) + sum + }; + let (next_correction, correction_roundoff) = error_free_sum(correction, increment); + let (next_tail, tail_roundoff) = error_free_sum(correction_tail, correction_roundoff); + correction = next_correction; + correction_tail = next_tail + tail_roundoff; + sum = next; + } + (sum, correction, correction_tail) +} + /// Sum finite values in a canonical order with Neumaier compensation. /// /// Callers own domain validation and any scale normalization needed to keep the @@ -98,6 +119,66 @@ fn error_free_sum(left: f64, right: f64) -> (f64, f64) { (sum, left_roundoff + right_roundoff) } +fn adjacent_float(value: f64, upward: bool) -> f64 { + if value == 0.0 { + return if upward { + f64::from_bits(1) + } else { + f64::from_bits((1_u64 << 63) | 1) + }; + } + + let bits = value.to_bits(); + if upward { + if value.is_sign_positive() { + f64::from_bits(bits + 1) + } else { + f64::from_bits(bits - 1) + } + } else if value.is_sign_positive() { + f64::from_bits(bits - 1) + } else { + f64::from_bits(bits + 1) + } +} + +fn round_candidate_with_tail(candidate: f64, tail_head: f64, tail_tail: f64) -> f64 { + let (tail, tail_roundoff) = error_free_sum(tail_head, tail_tail); + if tail == 0.0 && tail_roundoff == 0.0 { + return candidate; + } + + let upward = if tail != 0.0 { + tail.is_sign_positive() + } else { + tail_roundoff.is_sign_positive() + }; + let neighbor = adjacent_float(candidate, upward); + if !neighbor.is_finite() { + return neighbor; + } + + let half_gap = ((neighbor - candidate).abs()) * 0.5; + if half_gap == 0.0 { + return neighbor; + } + + let tail_magnitude = tail.abs(); + let beyond_midpoint = if tail_magnitude > half_gap { + true + } else if tail_magnitude < half_gap { + false + } else if tail_roundoff == 0.0 { + candidate.to_bits() & 1 == 1 + } else if upward { + tail_roundoff > 0.0 + } else { + tail_roundoff < 0.0 + }; + + if beyond_midpoint { neighbor } else { candidate } +} + fn mixed_remainder_mean_over_total( values: &[f64], total_count: usize, @@ -112,15 +193,31 @@ fn mixed_remainder_mean_over_total( let scale = exact_power_of_two_scale(max_magnitude); let normalized = values.iter().map(|value| *value / scale).collect(); - let (normalized_sum, normalized_correction) = deterministic_compensated_parts(normalized); - if normalized_sum == 0.0 && normalized_correction == 0.0 { + let (normalized_sum, normalized_correction, normalized_correction_tail) = + deterministic_compensated_parts_with_tail(normalized); + if normalized_sum == 0.0 + && normalized_correction == 0.0 + && normalized_correction_tail == 0.0 + { return Ok(0.0); } let denominator = total_count as f64; let leading_mean = normalized_sum / denominator; let division_residual = (-leading_mean).mul_add(denominator, normalized_sum); - let normalized_mean = leading_mean + (division_residual + normalized_correction) / denominator; + let (correction_numerator, correction_numerator_roundoff) = + error_free_sum(division_residual, normalized_correction); + let correction_mean = correction_numerator / denominator; + let correction_division_residual = + (-correction_mean).mul_add(denominator, correction_numerator) / denominator; + let (candidate, addition_roundoff) = error_free_sum(leading_mean, correction_mean); + let (tail_head, tail_tail) = deterministic_compensated_parts(vec![ + addition_roundoff, + correction_division_residual, + correction_numerator_roundoff / denominator, + normalized_correction_tail / denominator, + ]); + let normalized_mean = round_candidate_with_tail(candidate, tail_head, tail_tail); let mean = normalized_mean * scale; if !mean.is_finite() || mean == 0.0 { Err(ValidationError::InvalidInput) From cbcfa8d601587e24d41af26f61a85bd18ff6f307 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 04:46:21 +0900 Subject: [PATCH 310/576] docs(changelog): record bias subtraction-roundoff repair --- CHANGELOG.d/validation-bias-pairwise-subtraction-roundoff.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-pairwise-subtraction-roundoff.md diff --git a/CHANGELOG.d/validation-bias-pairwise-subtraction-roundoff.md b/CHANGELOG.d/validation-bias-pairwise-subtraction-roundoff.md new file mode 100644 index 000000000..61855c405 --- /dev/null +++ b/CHANGELOG.d/validation-bias-pairwise-subtraction-roundoff.md @@ -0,0 +1,3 @@ +### Fixed + +- Preserve represented low-order mass when a finite `recovered - truth` subtraction rounds before mean-bias aggregation. `mean_bias` now detects nonzero error-free subtraction roundoff and switches that case to the existing recovered-plus-negated-truth numerator, while mixed-sign mean restoration retains a second-order compensation tail through the scientific denominator and final binary64 midpoint decision. This prevents pairwise subtraction and compensation rounding from moving a representable bias by one ULP without changing the exact-subtraction fast path, overflow refusal policy, or `bias_standard_error` contract. From 1f9e26316553a67c2523b821ad7723f74e954041 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 04:47:03 +0900 Subject: [PATCH 311/576] docs(research): trace bias subtraction-roundoff repair --- .../bias-pairwise-subtraction-roundoff.md | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) create mode 100644 docs/research/bias-pairwise-subtraction-roundoff.md diff --git a/docs/research/bias-pairwise-subtraction-roundoff.md b/docs/research/bias-pairwise-subtraction-roundoff.md new file mode 100644 index 000000000..b79c6145f --- /dev/null +++ b/docs/research/bias-pairwise-subtraction-roundoff.md @@ -0,0 +1,41 @@ +# Mean-bias pairwise subtraction roundoff + +## Problem + +`mean_bias` is the Validation Evidence performance measure `mean(recovered - truth)` over represented binary64 recovery values. The predecessor treated each finite pairwise subtraction as authoritative before aggregation. That is not algebraically neutral when a finite subtraction rounds away a low-order dyadic term and several subsequent rounding steps place the mean on a binary64 midpoint. + +Public RED `a9d9bda3df1533eb91ece8e3bc6ce597b9c1400f` uses + +- `truth = [2^-108, 2^-53]` +- `recovered = [2^-54, 1]`. + +The first exact represented-input residual is `2^-54 - 2^-108`, but binary64 subtraction rounds it to `2^-54`. The second residual is the exactly representable `1 - 2^-53`. The predecessor therefore averaged the rounded residuals to the ties-to-even result `0x3fe0000000000000` (`0.5`). The represented-input numerator still contains `-2^-108`; after division by two it lies strictly below that midpoint, so the correct represented mean is `0x3fdfffffffffffff`, the float immediately below `0.5`. The sign mirror has the symmetric expected bits `0xbfdfffffffffffff`. + +## Constraints + +The repair must preserve the existing exact-subtraction fast path, the overflow-safe recovered-plus-negated-truth fallback, canonical order stability, original recovery-unit denominator, fail-closed underflow/overflow semantics, and the earlier subnormal one-rounding contract. It must not create a second public estimator, copy reusable psychometric arithmetic from fast-mlsirm, or add an arbitrary-precision production dependency. + +## Repair lineage + +The first source repair `96bff8e55083fc791650b185f819cd3aa90dac1b` correctly detected nonzero error-free subtraction roundoff and redirected such finite cases to the existing expanded represented-input numerator. The RED then exposed a second rounding loss inside that path: mixed-remainder Neumaier compensation stored its correction in one binary64 value, so the `-2^-108` contribution could disappear when the leading correction already sat at the final midpoint. + +Corrected causal repair `676ccda02da0186361555879d5637c4f02178a71` retains a second-order correction tail while accumulating the mixed remainder. It carries the leading division residual, correction numerator residual, correction-division residual, and retained compensation tail through the original scientific denominator. The final candidate is moved to an adjacent float only when the exact two-term tail is beyond the binary64 midpoint, or is exactly at the midpoint and ties-to-even selects the adjacent value. This keeps ordinary exact-subtraction inputs on the predecessor path and does not claim globally correctly rounded summation/division for every binary64 sequence. + +## Alternatives rejected + +Always accepting pairwise-rounded residuals was rejected because the RED proves that a finite intermediate subtraction can change the represented scientific result. Always expanding every bias numerator was rejected because it would unnecessarily change the established exact-residual and all-subnormal paths, including the bounded exact-unit rule added for GAP-090. Adding an arbitrary-precision runtime dependency was rejected because the demonstrated defect is resolved inside the existing deterministic binary64 reference without creating a new numerical owner or deployment dependency. Treating the first expanded-numerator patch as complete was rejected because the original RED remained failing until second-order mixed-remainder compensation was retained through final rounding. + +## Scientific and standards trace + +IEEE 754-2019 remains the active published floating-point standard; IEEE P754 is an active revision PAR rather than a published replacement. ISO/IEC 60559:2020 remains the published international standard adopting the same floating-point arithmetic model. AERA, APA, and NCME continue to publish the 2014 *Standards for Educational and Psychological Testing* while the Joint Committee conducts its announced revision. Morris, White, and Crowther (2019) provide the methodological basis for treating bias as a known-truth simulation performance measure and for reporting simulation design and Monte Carlo uncertainty explicitly. + +References: + +- IEEE. (2019). *IEEE Standard for Floating-Point Arithmetic* (IEEE Std 754-2019). +- ISO/IEC. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). +- American Educational Research Association, American Psychological Association, & National Council on Measurement in Education. (2014). *Standards for educational and psychological testing*. AERA. +- Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +## Scope and remaining risk + +This repair establishes the public represented-input counterexample and its sign mirror. It does not claim that every possible binary64 mean is globally correctly rounded, nor does it alter `bias_standard_error`, RMSE, Monte Carlo, Wilson, Longitudinal Modeling, or fast-mlsirm ownership. A future GAP requires a separate public counterexample where the current exact-head return value disagrees with the represented-input estimand or admission decision; algebraic suspicion alone is insufficient. From 0bff5a8d0aad2da592bc60378dc5b6fd02a5aefe Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 04:47:51 +0900 Subject: [PATCH 312/576] test(validation): pin both subtraction-rounding midpoint directions --- ..._pairwise_subtraction_roundoff_contract.rs | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/tests/bias_pairwise_subtraction_roundoff_contract.rs b/crates/validation_core/tests/bias_pairwise_subtraction_roundoff_contract.rs index 82c33e7b9..6e0f680e1 100644 --- a/crates/validation_core/tests/bias_pairwise_subtraction_roundoff_contract.rs +++ b/crates/validation_core/tests/bias_pairwise_subtraction_roundoff_contract.rs @@ -1,16 +1,27 @@ use validation_core::mean_bias; -#[test] -fn mean_bias_preserves_pairwise_subtraction_roundoff_before_averaging() { - let truth = [2.0_f64.powi(-108), 2.0_f64.powi(-53)]; - let recovered = [2.0_f64.powi(-54), 1.0]; - +fn assert_bias_bits(truth: [f64; 2], recovered: [f64; 2], expected: u64) { let bias = mean_bias(&truth, &recovered).expect("represented mean bias"); - assert_eq!(bias.to_bits(), 0x3fdf_ffff_ffff_ffff); + assert_eq!(bias.to_bits(), expected); let mirrored_truth = truth.map(|value| -value); let mirrored_recovered = recovered.map(|value| -value); let mirrored_bias = mean_bias(&mirrored_truth, &mirrored_recovered) .expect("mirrored represented mean bias"); - assert_eq!(mirrored_bias.to_bits(), 0xbfdf_ffff_ffff_ffff); + assert_eq!(mirrored_bias.to_bits(), expected | (1_u64 << 63)); +} + +#[test] +fn mean_bias_preserves_pairwise_subtraction_roundoff_before_averaging() { + assert_bias_bits( + [2.0_f64.powi(-108), 2.0_f64.powi(-53)], + [2.0_f64.powi(-54), 1.0], + 0x3fdf_ffff_ffff_ffff, + ); + + assert_bias_bits( + [2.0_f64.powi(-105), 2.0_f64.powi(-53)], + [2.0_f64.powi(-51), 1.0], + 0x3fe0_0000_0000_0001, + ); } From c9c55ea568d27e33d6e522b02e6431ec4c6983d2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 05:01:43 +0900 Subject: [PATCH 313/576] test(validation): expose bias SE subtraction-roundoff spread --- ..._pairwise_subtraction_roundoff_contract.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_pairwise_subtraction_roundoff_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_pairwise_subtraction_roundoff_contract.rs b/crates/validation_core/tests/bias_standard_error_pairwise_subtraction_roundoff_contract.rs new file mode 100644 index 000000000..1a15fa2b3 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_pairwise_subtraction_roundoff_contract.rs @@ -0,0 +1,25 @@ +use validation_core::bias_standard_error; + +#[test] +fn bias_standard_error_preserves_pairwise_subtraction_roundoff_spread() { + let truth = [2.0_f64.powi(-54), 2.0_f64.powi(-55)]; + let recovered = [1.0, 1.0]; + + let standard_error = + bias_standard_error(&truth, &recovered).expect("represented standard error"); + assert_eq!(standard_error.to_bits(), 2.0_f64.powi(-56).to_bits()); + + let mirrored_truth = truth.map(|value| -value); + let mirrored_recovered = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&mirrored_truth, &mirrored_recovered) + .expect("mirrored represented standard error"); + assert_eq!( + mirrored_standard_error.to_bits(), + 2.0_f64.powi(-56).to_bits() + ); + + assert_eq!( + bias_standard_error(&[2.0_f64.powi(-54); 2], &[1.0; 2]), + Ok(0.0) + ); +} From f6d7da9681022e0df7a60f444e02894605201cd4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 05:03:49 +0900 Subject: [PATCH 314/576] fix(validation): preserve two-point bias SE spread --- crates/validation_core/src/bias.rs | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 15c18b802..a66147d21 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -145,8 +145,13 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Result Date: Sat, 5 Sep 2026 05:04:43 +0900 Subject: [PATCH 315/576] docs(changelog): record bias SE subtraction-roundoff repair --- ...dation-bias-standard-error-pairwise-subtraction-roundoff.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-pairwise-subtraction-roundoff.md diff --git a/CHANGELOG.d/validation-bias-standard-error-pairwise-subtraction-roundoff.md b/CHANGELOG.d/validation-bias-standard-error-pairwise-subtraction-roundoff.md new file mode 100644 index 000000000..57c7456e4 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-pairwise-subtraction-roundoff.md @@ -0,0 +1,3 @@ +### Fixed + +- `validation_core::bias_standard_error` now preserves two-observation sampling uncertainty when distinct represented-input residuals both round to the same binary64 `recovered - truth` value. For `n = 2`, subtraction-roundoff cases evaluate the exact represented-input identity `SE = |r₁ - r₂| / 2` through the existing cancellation-safe expanded-sum boundary, while still rejecting unrepresentable individual residuals and preserving exact-equality zero uncertainty. From a9bf0320419d44854a1efaa11b06fda09884f5e3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 05:06:01 +0900 Subject: [PATCH 316/576] docs(research): trace bias SE subtraction-roundoff repair --- ...ard-error-pairwise-subtraction-roundoff.md | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 docs/research/bias-standard-error-pairwise-subtraction-roundoff.md diff --git a/docs/research/bias-standard-error-pairwise-subtraction-roundoff.md b/docs/research/bias-standard-error-pairwise-subtraction-roundoff.md new file mode 100644 index 000000000..fbc80c629 --- /dev/null +++ b/docs/research/bias-standard-error-pairwise-subtraction-roundoff.md @@ -0,0 +1,54 @@ +# Bias standard error under pairwise subtraction roundoff + +## Problem + +`validation_core::bias_standard_error` previously formed each signed recovery residual as one binary64 `recovered - truth` value before computing dispersion. That is acceptable only when the represented-input subtraction is exact enough for the requested uncertainty statistic. Two distinct represented-input residuals can round to the same binary64 subtraction result, producing a false zero standard error even though the sampling uncertainty is representable. + +The public RED in `crates/validation_core/tests/bias_standard_error_pairwise_subtraction_roundoff_contract.rs` fixes the smallest useful boundary: + +- `truth = [2^-54, 2^-55]` +- `recovered = [1, 1]` +- exact represented-input residuals are `r1 = 1 - 2^-54` and `r2 = 1 - 2^-55` +- both pairwise binary64 subtractions round to `1.0` +- `r1 - r2 = -2^-55` +- for two observations, `SE(mean) = |r1 - r2| / 2 = 2^-56` + +The predecessor therefore returned `0.0` even though `2^-56` is exactly representable. The sign-mirrored payload must return the same positive uncertainty. A companion equality control uses identical represented-input residuals and remains exact zero. + +## Constraints + +The repair must preserve the existing scientific contract that an individual signed residual which is itself unrepresentable is rejected. It must not make the two-observation identity a second estimator definition for larger samples, duplicate reusable static psychometric arithmetic from `fast-mlsirm`, or introduce arbitrary-precision production dependencies merely to repair one bounded Validation Evidence edge. + +## Alternatives considered + +Using the already rounded residual vector and adding an epsilon was rejected because it invents uncertainty without recovering represented input mass. Replacing all `bias_standard_error` arithmetic with a new exact superaccumulator was rejected for this change because the demonstrated defect is specifically the two-observation subtraction-roundoff boundary and a broader rewrite would exceed the causal evidence. Returning `InvalidInput` whenever pairwise subtraction roundoff is detected was also rejected: the RED has a finite, exactly representable scientific answer, so fail-closed rejection would discard valid Validation Evidence rather than preserve it. + +## Decision + +After the existing finite-residual admission gate, `n = 2` cases with nonzero error-free subtraction roundoff use the exact two-observation identity + +`SE(mean) = |r1 - r2| / 2`. + +The difference is evaluated from the represented inputs as `[recovered[0], -truth[0], -recovered[1], truth[1]]` through `deterministic_representable_sum_over_count(..., 2)`. This reuses the canonical cancellation-safe expanded-sum boundary already owned by `validation_core`, preserves the existing unrepresentable-residual refusal policy, and avoids making either rounded pairwise residual authoritative for dispersion. + +Cases with more than two observations, and two-observation cases whose pairwise residual subtractions are exact, retain the predecessor path. This change therefore does not claim globally correctly rounded sample standard errors. + +## Risk and follow-up + +The remaining risk is the `n > 2` case: represented-input subtraction roundoff can still alter dispersion while the present repair intentionally leaves that path unchanged. A follow-up change requires a concrete public counterexample with a materially different representable standard error and a bounded arithmetic strategy that does not regress full-range overflow handling. The current RED is not evidence for a blanket higher-order rewrite. + +## TRACEABILITY + +- RED: `c9c55ea568d27e33d6e522b02e6431ec4c6983d2` +- causal repair: `f6d7da9681022e0df7a60f444e02894605201cd4` +- CHANGELOG: `ab7730ca5246598dd916ff264e5bdf97ecf72754` +- production module: `crates/validation_core/src/bias.rs` +- public contract: `crates/validation_core/tests/bias_standard_error_pairwise_subtraction_roundoff_contract.rs` +- bounded context: Validation Evidence +- owner boundary: reusable static psychometric estimators remain in `fast-mlsirm`; no mutable sibling source is consumed. + +## References + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://standards.ieee.org/ieee/754/6210/ + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 06a70e1c01629f15f05efef48576a9cadb1f1b98 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:01:27 +0900 Subject: [PATCH 317/576] test(validation): expose multi-observation bias SE roundoff --- ...servation_subtraction_roundoff_contract.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs b/crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs new file mode 100644 index 000000000..f487b082f --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs @@ -0,0 +1,22 @@ +use validation_core::bias_standard_error; + +#[test] +fn bias_standard_error_preserves_multi_observation_subtraction_roundoff_spread() { + let truth = [2.0_f64.powi(-54), 2.0_f64.powi(-55), 0.0]; + let recovered = [1.0; 3]; + + let standard_error = + bias_standard_error(&truth, &recovered).expect("represented standard error"); + assert_eq!(standard_error.to_bits(), 0x3c72_79a7_4590_331d); + + let mirrored_truth = truth.map(|value| -value); + let mirrored_recovered = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&mirrored_truth, &mirrored_recovered) + .expect("mirrored represented standard error"); + assert_eq!(mirrored_standard_error.to_bits(), 0x3c72_79a7_4590_331d); + + assert_eq!( + bias_standard_error(&[2.0_f64.powi(-54); 3], &[1.0; 3]), + Ok(0.0) + ); +} From 04c62514a23722d63a62bd5d5af6e3a930cc3147 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:02:37 +0900 Subject: [PATCH 318/576] fix(validation): preserve collapsed bias SE low terms --- crates/validation_core/src/bias.rs | 45 +++++++++++++++++++----------- 1 file changed, 29 insertions(+), 16 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index a66147d21..85b9915ab 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -23,13 +23,17 @@ fn signed_residuals(truth: &[f64], recovered: &[f64]) -> Result, Valida .collect() } -fn subtraction_has_roundoff(recovered: f64, truth: f64, residual: f64) -> bool { +fn subtraction_roundoff(recovered: f64, truth: f64, residual: f64) -> f64 { let negated_truth = -truth; let truth_virtual = residual - recovered; let recovered_virtual = residual - truth_virtual; let recovered_roundoff = recovered - recovered_virtual; let truth_roundoff = negated_truth - truth_virtual; - recovered_roundoff + truth_roundoff != 0.0 + recovered_roundoff + truth_roundoff +} + +fn subtraction_has_roundoff(recovered: f64, truth: f64, residual: f64) -> bool { + subtraction_roundoff(recovered, truth, residual) != 0.0 } fn standard_error_from_deviations(deviations: &[f64]) -> Result { @@ -147,11 +151,14 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Result = truth + .iter() + .zip(recovered) + .zip(&diffs) + .map(|((truth_value, recovered_value), residual)| { + subtraction_roundoff(*recovered_value, *truth_value, *residual) + }) + .collect(); + let has_subtraction_roundoff = subtraction_roundoffs.iter().any(|roundoff| *roundoff != 0.0); - if diffs.len() == 2 - && truth - .iter() - .zip(recovered) - .zip(&diffs) - .any(|((truth_value, recovered_value), residual)| { - subtraction_has_roundoff(*recovered_value, *truth_value, *residual) - }) - { + if diffs.len() == 2 && has_subtraction_roundoff { let expanded_difference = [recovered[0], -truth[0], -recovered[1], truth[1]]; let half_difference = deterministic_representable_sum_over_count(&expanded_difference, 2)?; return Ok(half_difference.abs()); } + if has_subtraction_roundoff && diffs.iter().all(|residual| *residual == diffs[0]) { + let roundoff_mean = deterministic_representable_mean(&subtraction_roundoffs)?; + return scaled_standard_error(&subtraction_roundoffs, roundoff_mean); + } + let mean = deterministic_representable_mean(&diffs)?; scaled_standard_error(&diffs, mean) } From 56d091544a5780567a6ef772568d62b0fc651747 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:03:19 +0900 Subject: [PATCH 319/576] docs(validation): record multi-observation bias SE repair --- ...s-standard-error-multi-observation-subtraction-roundoff.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-multi-observation-subtraction-roundoff.md diff --git a/CHANGELOG.d/validation-bias-standard-error-multi-observation-subtraction-roundoff.md b/CHANGELOG.d/validation-bias-standard-error-multi-observation-subtraction-roundoff.md new file mode 100644 index 000000000..68ba063e3 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-multi-observation-subtraction-roundoff.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core::bias_standard_error` now preserves nonzero sampling uncertainty when three or more distinct represented-input bias residuals collapse to one rounded binary64 `recovered - truth` value. When every rounded residual shares the same high part, the common high part is dispersion-invariant and the standard error is evaluated from the error-free subtraction low terms instead of reporting false zero. +- The public contract covers a three-observation represented-input boundary, its sign mirror, and an equal-residual control that remains exactly zero. The existing two-observation exact-difference path and the general non-collapsed estimator path remain unchanged. From 5b237c794dacdbf45a7da0fe2112fc15007b3c80 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:04:01 +0900 Subject: [PATCH 320/576] docs(research): trace multi-observation bias SE roundoff --- ...-multi-observation-subtraction-roundoff.md | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) create mode 100644 docs/research/bias-standard-error-multi-observation-subtraction-roundoff.md diff --git a/docs/research/bias-standard-error-multi-observation-subtraction-roundoff.md b/docs/research/bias-standard-error-multi-observation-subtraction-roundoff.md new file mode 100644 index 000000000..afc5479af --- /dev/null +++ b/docs/research/bias-standard-error-multi-observation-subtraction-roundoff.md @@ -0,0 +1,55 @@ +# Bias standard error after multi-observation subtraction collapse + +## Problem + +`validation_core::bias_standard_error` admitted only finite `recovered - truth` residuals, but until GAP-093 it treated the rounded binary64 residual as authoritative for samples larger than two observations. That is not sufficient when distinct represented inputs produce the same rounded subtraction result. A standard error of exactly zero then asserts no sampling dispersion even though the represented-input residuals differ. + +The public RED in commit `06a70e1c01629f15f05efef48576a9cadb1f1b98` uses + +- `truth = [2^-54, 2^-55, 0]`, +- `recovered = [1, 1, 1]`. + +The exact represented-input residuals are + +- `r1 = 1 - 2^-54`, +- `r2 = 1 - 2^-55`, +- `r3 = 1`. + +All three binary64 subtractions round to `1.0`, so the predecessor's rounded-residual path produced `SE = 0`. The represented-input mean is exactly `1 - 2^-55`. The deviations are therefore `[-2^-55, 0, 2^-55]`; the sample standard deviation is `2^-55`, and the standard error is `2^-55 / sqrt(3)`, which rounds to binary64 bits `0x3c72_79a7_4590_331d`. The sign-mirrored payload has the same standard error. An equal-residual control remains exactly zero. + +This is a Validation Evidence defect: zero uncertainty is a materially stronger scientific claim than small but nonzero uncertainty. Morris, White, and Crowther (2019) treat bias and Monte Carlo uncertainty as performance measures whose uncertainty must be reported rather than silently collapsed by implementation arithmetic. + +## Constraints + +The repair must preserve the existing finite-residual admission gate, the GAP-092 two-observation exact-difference path, and the normal scaled estimator when rounded residuals do not collapse. It must not introduce arbitrary-precision production arithmetic, a second psychometric arithmetic owner, or an O(n²) pairwise-difference fallback on the general path. Reusable static psychometric estimation remains owned by `fast-mlsirm`; this function remains TEPP Validation Evidence arithmetic. + +IEEE 754-2019 remains the active IEEE floating-point standard, and ISO/IEC 60559:2020 remains the published international floating-point standard. The repair therefore treats the represented binary64 inputs and their specified arithmetic as the executable numerical boundary rather than assuming real-number subtraction was retained automatically. + +## Alternatives considered + +A general pairwise-difference variance identity would remove the rounded mean, but evaluating every represented-input pair is O(n²) and unnecessarily widens this repair. Arbitrary-precision rationals would make the oracle straightforward but would add a production dependency and a second numerical path that is not justified by this bounded defect. Returning `InvalidInput` whenever an n>2 subtraction has roundoff would fail closed but would reject a standard error that is both scientifically meaningful and representable. + +The selected repair uses the error-free subtraction decomposition already implicit in `subtraction_has_roundoff`. When every rounded residual has the same high part `h`, each exact represented-input residual can be written `r_i = h + l_i`, where `l_i` is the error-free low term. Standard deviation and standard error are translation-invariant, so the common `h` cannot contribute to dispersion. The canonical scaled standard-error path can therefore operate on the `l_i` values in O(n) time. Commit `04c62514a23722d63a62bd5d5af6e3a930cc3147` implements this boundary and leaves the GAP-092 n=2 identity and non-collapsed estimator unchanged. + +## Acceptance and traceability + +| Evidence | Exact reference | Acceptance meaning | +|---|---|---| +| Public RED | `06a70e1c01629f15f05efef48576a9cadb1f1b98` | Three distinct represented-input residuals collapse to rounded `1.0`; expected nonzero SE is `0x3c72_79a7_4590_331d`. | +| Causal source repair | `04c62514a23722d63a62bd5d5af6e3a930cc3147` | Preserve subtraction low terms and use them only when all rounded residual high parts are equal. | +| CHANGELOG | `56d091544a5780567a6ef772568d62b0fc651747` | Buyer-visible numerical behavior and scope are recorded. | +| PR authority | `#488` | Validation Evidence landing vehicle; exact current head is maintained in the PR body and queue-authority baseline. | +| Module/API | `crates/validation_core/src/bias.rs::bias_standard_error` | Owner-correct production boundary. | +| Public contract | `crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs` | Positive case, sign mirror, and equal-residual zero control. | + +This repair does **not** claim globally correctly rounded standard errors for every finite n>2 input. In particular, when rounded residual high parts differ, retained low terms may still move a nonzero standard error across a final binary64 boundary; that requires an independent represented-input counterexample before widening the production algorithm. + +## 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. + +IEEE Standards Association. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://standards.ieee.org/ieee/754/6210/ + +International Organization for Standardization. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). https://www.iso.org/standard/80985.html + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 6224320410ccabb1cf16d36cc12f88e2b7a05bb1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:08:57 +0900 Subject: [PATCH 321/576] test(validation): correct multi-observation bias SE oracle --- ...rror_multi_observation_subtraction_roundoff_contract.rs | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs b/crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs index f487b082f..4abb6dfeb 100644 --- a/crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs @@ -7,13 +7,16 @@ fn bias_standard_error_preserves_multi_observation_subtraction_roundoff_spread() let standard_error = bias_standard_error(&truth, &recovered).expect("represented standard error"); - assert_eq!(standard_error.to_bits(), 0x3c72_79a7_4590_331d); + // The represented-input deviations are exactly [-2^-55, 0, 2^-55], so + // SE = 2^-55 / sqrt(3). Round the final mathematical value once rather + // than inheriting the 1-ULP double rounding from 1 / rounded_sqrt(3). + assert_eq!(standard_error.to_bits(), 0x3c72_79a7_4590_331c); let mirrored_truth = truth.map(|value| -value); let mirrored_recovered = recovered.map(|value| -value); let mirrored_standard_error = bias_standard_error(&mirrored_truth, &mirrored_recovered) .expect("mirrored represented standard error"); - assert_eq!(mirrored_standard_error.to_bits(), 0x3c72_79a7_4590_331d); + assert_eq!(mirrored_standard_error.to_bits(), 0x3c72_79a7_4590_331c); assert_eq!( bias_standard_error(&[2.0_f64.powi(-54); 3], &[1.0; 3]), From 8b8f0a21ccc825f355859cadbf20d83f04d2369f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:09:44 +0900 Subject: [PATCH 322/576] fix(validation): avoid bias SE sqrt double rounding --- crates/validation_core/src/bias.rs | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 85b9915ab..6519d8c20 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -53,8 +53,13 @@ fn standard_error_from_deviations(deviations: &[f64]) -> Result Result Date: Sat, 5 Sep 2026 06:10:43 +0900 Subject: [PATCH 323/576] docs(research): correct bias SE oracle and trace sqrt rounding --- ...-multi-observation-subtraction-roundoff.md | 24 ++++++++++++------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/docs/research/bias-standard-error-multi-observation-subtraction-roundoff.md b/docs/research/bias-standard-error-multi-observation-subtraction-roundoff.md index afc5479af..a37269f15 100644 --- a/docs/research/bias-standard-error-multi-observation-subtraction-roundoff.md +++ b/docs/research/bias-standard-error-multi-observation-subtraction-roundoff.md @@ -15,34 +15,40 @@ The exact represented-input residuals are - `r2 = 1 - 2^-55`, - `r3 = 1`. -All three binary64 subtractions round to `1.0`, so the predecessor's rounded-residual path produced `SE = 0`. The represented-input mean is exactly `1 - 2^-55`. The deviations are therefore `[-2^-55, 0, 2^-55]`; the sample standard deviation is `2^-55`, and the standard error is `2^-55 / sqrt(3)`, which rounds to binary64 bits `0x3c72_79a7_4590_331d`. The sign-mirrored payload has the same standard error. An equal-residual control remains exactly zero. +All three binary64 subtractions round to `1.0`, so the predecessor's rounded-residual path produced `SE = 0`. The represented-input mean is exactly `1 - 2^-55`. The deviations are therefore `[-2^-55, 0, 2^-55]`; the sample standard deviation is `2^-55`, and the standard error is `2^-55 / sqrt(3)`. -This is a Validation Evidence defect: zero uncertainty is a materially stronger scientific claim than small but nonzero uncertainty. Morris, White, and Crowther (2019) treat bias and Monte Carlo uncertainty as performance measures whose uncertainty must be reported rather than silently collapsed by implementation arithmetic. +The first public contract accidentally encoded `0x3c72_79a7_4590_331d`, which is the result of evaluating `1 / rounded_sqrt(3)` and then restoring the exact power-of-two scale. Exact high-precision evaluation of the represented-input expression shows that the correctly rounded final binary64 value is instead `0x3c72_79a7_4590_331c` (`0x1.279a74590331cp-56`). Oracle correction `6224320410ccabb1cf16d36cc12f88e2b7a05bb1` makes that distinction executable. This exposed GAP-094: the predecessor standard-error helper separately rounded `sqrt(sample_variance)` and `sqrt(n)`, moving the final standard error by one ULP even after GAP-093 restored the missing low-order dispersion. + +This is a Validation Evidence defect: zero uncertainty is materially stronger than small but nonzero uncertainty, and a one-ULP numerical boundary is not interchangeable with the represented-input target when the deterministic CPU `f64` reference claims that boundary. Morris, White, and Crowther (2019) treat bias and simulation uncertainty as performance measures whose uncertainty must be reported rather than silently collapsed by implementation arithmetic. ## Constraints The repair must preserve the existing finite-residual admission gate, the GAP-092 two-observation exact-difference path, and the normal scaled estimator when rounded residuals do not collapse. It must not introduce arbitrary-precision production arithmetic, a second psychometric arithmetic owner, or an O(n²) pairwise-difference fallback on the general path. Reusable static psychometric estimation remains owned by `fast-mlsirm`; this function remains TEPP Validation Evidence arithmetic. -IEEE 754-2019 remains the active IEEE floating-point standard, and ISO/IEC 60559:2020 remains the published international floating-point standard. The repair therefore treats the represented binary64 inputs and their specified arithmetic as the executable numerical boundary rather than assuming real-number subtraction was retained automatically. +IEEE 754-2019 remains the active IEEE floating-point standard, and ISO/IEC 60559:2020 remains the published international floating-point standard. The repair therefore treats represented binary64 inputs as the executable numerical boundary and avoids an algebraically unnecessary intermediate rounding when the equivalent normalized expression is bounded. ## Alternatives considered A general pairwise-difference variance identity would remove the rounded mean, but evaluating every represented-input pair is O(n²) and unnecessarily widens this repair. Arbitrary-precision rationals would make the oracle straightforward but would add a production dependency and a second numerical path that is not justified by this bounded defect. Returning `InvalidInput` whenever an n>2 subtraction has roundoff would fail closed but would reject a standard error that is both scientifically meaningful and representable. -The selected repair uses the error-free subtraction decomposition already implicit in `subtraction_has_roundoff`. When every rounded residual has the same high part `h`, each exact represented-input residual can be written `r_i = h + l_i`, where `l_i` is the error-free low term. Standard deviation and standard error are translation-invariant, so the common `h` cannot contribute to dispersion. The canonical scaled standard-error path can therefore operate on the `l_i` values in O(n) time. Commit `04c62514a23722d63a62bd5d5af6e3a930cc3147` implements this boundary and leaves the GAP-092 n=2 identity and non-collapsed estimator unchanged. +The GAP-093 repair uses the error-free subtraction decomposition already implicit in `subtraction_has_roundoff`. When every rounded residual has the same high part `h`, each exact represented-input residual can be written `r_i = h + l_i`, where `l_i` is the error-free low term. Standard deviation and standard error are translation-invariant, so the common `h` cannot contribute to dispersion. Commit `04c62514a23722d63a62bd5d5af6e3a930cc3147` therefore evaluates the scaled standard error from the `l_i` values in O(n), leaving the GAP-092 n=2 identity and non-collapsed estimator unchanged. + +For GAP-094, keeping `sqrt(sample_variance) / sqrt(n)` was rejected because it rounds two square-root operands before the final division. The normalized deviations are bounded by one, so the equivalent `sqrt(sum(d²) / (n * (n - 1)))` can be evaluated without overflow and with one final square root. Commit `8b8f0a21ccc825f355859cadbf20d83f04d2369f` applies that form inside the existing scaled standard-error helper; it does not add a new estimator or weaken fail-closed handling. ## Acceptance and traceability | Evidence | Exact reference | Acceptance meaning | |---|---|---| -| Public RED | `06a70e1c01629f15f05efef48576a9cadb1f1b98` | Three distinct represented-input residuals collapse to rounded `1.0`; expected nonzero SE is `0x3c72_79a7_4590_331d`. | -| Causal source repair | `04c62514a23722d63a62bd5d5af6e3a930cc3147` | Preserve subtraction low terms and use them only when all rounded residual high parts are equal. | -| CHANGELOG | `56d091544a5780567a6ef772568d62b0fc651747` | Buyer-visible numerical behavior and scope are recorded. | +| GAP-093 public RED | `06a70e1c01629f15f05efef48576a9cadb1f1b98` | Three distinct represented-input residuals collapse to rounded `1.0`; predecessor false zero is rejected. | +| GAP-093 source repair | `04c62514a23722d63a62bd5d5af6e3a930cc3147` | Preserve subtraction low terms and use them only when all rounded residual high parts are equal. | +| GAP-094 oracle correction / RED | `6224320410ccabb1cf16d36cc12f88e2b7a05bb1` | Correct expected represented-input SE from `...331d` to `...331c`, exposing the separate square-root double rounding. | +| GAP-094 source repair | `8b8f0a21ccc825f355859cadbf20d83f04d2369f` | Form normalized SE as `sqrt(sum(d²)/(n(n-1)))` before exact scale restoration. | +| GAP-093 CHANGELOG | `56d091544a5780567a6ef772568d62b0fc651747` | Buyer-visible false-zero repair and scope are recorded. | | PR authority | `#488` | Validation Evidence landing vehicle; exact current head is maintained in the PR body and queue-authority baseline. | | Module/API | `crates/validation_core/src/bias.rs::bias_standard_error` | Owner-correct production boundary. | -| Public contract | `crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs` | Positive case, sign mirror, and equal-residual zero control. | +| Public contract | `crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs` | Correctly rounded positive case, sign mirror, and equal-residual zero control. | -This repair does **not** claim globally correctly rounded standard errors for every finite n>2 input. In particular, when rounded residual high parts differ, retained low terms may still move a nonzero standard error across a final binary64 boundary; that requires an independent represented-input counterexample before widening the production algorithm. +These repairs do **not** claim globally correctly rounded standard errors for every finite n>2 input. In particular, when rounded residual high parts differ, retained subtraction low terms may still move a nonzero standard error across a final binary64 boundary; that requires an independent represented-input counterexample before widening the production algorithm. ## References From 184f4e2c109c7904027f782e91b8d9a01a1d46f1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:10:50 +0900 Subject: [PATCH 324/576] docs(validation): record bias SE sqrt rounding repair --- .../validation-bias-standard-error-sqrt-double-rounding.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-sqrt-double-rounding.md diff --git a/CHANGELOG.d/validation-bias-standard-error-sqrt-double-rounding.md b/CHANGELOG.d/validation-bias-standard-error-sqrt-double-rounding.md new file mode 100644 index 000000000..e1840e1d8 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-sqrt-double-rounding.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core::bias_standard_error` now forms the normalized standard error as `sqrt(sum(d²) / (n * (n - 1)))` instead of separately rounding `sqrt(sample_variance)` and `sqrt(n)` before division. This removes an avoidable one-ULP shift in representable Validation Evidence uncertainty while retaining the existing scale-before-square overflow protection and fail-closed range checks. +- The corrected public oracle for the three-observation subtraction-collapse boundary is `0x3c72_79a7_4590_331c`; the predecessor `...331d` value came from the discarded double-rounded square-root path. From 89fd9ef0add3930346900f75518708978ec9861e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:16:00 +0900 Subject: [PATCH 325/576] test(validation): pin bias SE final sqrt rounding --- ...dard_error_sqrt_double_rounding_contract.rs | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_sqrt_double_rounding_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_sqrt_double_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_sqrt_double_rounding_contract.rs new file mode 100644 index 000000000..9bd055358 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_sqrt_double_rounding_contract.rs @@ -0,0 +1,18 @@ +use validation_core::bias_standard_error; + +#[test] +fn bias_standard_error_rounds_the_final_normalized_se_once() { + let unit = 2.0_f64.powi(-55); + let truth = [0.0; 3]; + let recovered = [-unit, 0.0, unit]; + + let standard_error = bias_standard_error(&truth, &recovered).expect("represented standard error"); + assert_eq!(standard_error.to_bits(), 0x3c72_79a7_4590_331c); + + let mirrored_recovered = recovered.map(|value| -value); + let mirrored_standard_error = + bias_standard_error(&truth, &mirrored_recovered).expect("mirrored standard error"); + assert_eq!(mirrored_standard_error.to_bits(), 0x3c72_79a7_4590_331c); + + assert_eq!(bias_standard_error(&[0.0, 0.0], &[1.0, -1.0]), Ok(1.0)); +} From 99a220db45f98d557a9ba9e1688016f1349fcc43 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:16:39 +0900 Subject: [PATCH 326/576] docs(research): add direct bias SE sqrt rounding contract --- ...standard-error-multi-observation-subtraction-roundoff.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/docs/research/bias-standard-error-multi-observation-subtraction-roundoff.md b/docs/research/bias-standard-error-multi-observation-subtraction-roundoff.md index a37269f15..33cfe77b8 100644 --- a/docs/research/bias-standard-error-multi-observation-subtraction-roundoff.md +++ b/docs/research/bias-standard-error-multi-observation-subtraction-roundoff.md @@ -19,6 +19,8 @@ All three binary64 subtractions round to `1.0`, so the predecessor's rounded-res The first public contract accidentally encoded `0x3c72_79a7_4590_331d`, which is the result of evaluating `1 / rounded_sqrt(3)` and then restoring the exact power-of-two scale. Exact high-precision evaluation of the represented-input expression shows that the correctly rounded final binary64 value is instead `0x3c72_79a7_4590_331c` (`0x1.279a74590331cp-56`). Oracle correction `6224320410ccabb1cf16d36cc12f88e2b7a05bb1` makes that distinction executable. This exposed GAP-094: the predecessor standard-error helper separately rounded `sqrt(sample_variance)` and `sqrt(n)`, moving the final standard error by one ULP even after GAP-093 restored the missing low-order dispersion. +The dedicated GAP-094 contract `89fd9ef0add3930346900f75518708978ec9861e` removes subtraction collapse from that second finding entirely: with `truth=[0,0,0]` and exact residuals `recovered=[-2^-55,0,2^-55]`, the same represented standard error must still round to `0x3c72_79a7_4590_331c`. Its sign mirror and an exact two-observation control prevent the repair from being justified only by the GAP-093 special path. + This is a Validation Evidence defect: zero uncertainty is materially stronger than small but nonzero uncertainty, and a one-ULP numerical boundary is not interchangeable with the represented-input target when the deterministic CPU `f64` reference claims that boundary. Morris, White, and Crowther (2019) treat bias and simulation uncertainty as performance measures whose uncertainty must be reported rather than silently collapsed by implementation arithmetic. ## Constraints @@ -43,10 +45,12 @@ For GAP-094, keeping `sqrt(sample_variance) / sqrt(n)` was rejected because it r | GAP-093 source repair | `04c62514a23722d63a62bd5d5af6e3a930cc3147` | Preserve subtraction low terms and use them only when all rounded residual high parts are equal. | | GAP-094 oracle correction / RED | `6224320410ccabb1cf16d36cc12f88e2b7a05bb1` | Correct expected represented-input SE from `...331d` to `...331c`, exposing the separate square-root double rounding. | | GAP-094 source repair | `8b8f0a21ccc825f355859cadbf20d83f04d2369f` | Form normalized SE as `sqrt(sum(d²)/(n(n-1)))` before exact scale restoration. | +| GAP-094 direct-residual contract | `89fd9ef0add3930346900f75518708978ec9861e` | Reproduces the one-ULP square-root defect without pairwise subtraction roundoff. | | GAP-093 CHANGELOG | `56d091544a5780567a6ef772568d62b0fc651747` | Buyer-visible false-zero repair and scope are recorded. | +| GAP-094 CHANGELOG | `184f4e2c109c7904027f782e91b8d9a01a1d46f1` | Buyer-visible one-ULP final-standard-error repair and corrected oracle are recorded. | | PR authority | `#488` | Validation Evidence landing vehicle; exact current head is maintained in the PR body and queue-authority baseline. | | Module/API | `crates/validation_core/src/bias.rs::bias_standard_error` | Owner-correct production boundary. | -| Public contract | `crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs` | Correctly rounded positive case, sign mirror, and equal-residual zero control. | +| Public contracts | `crates/validation_core/tests/bias_standard_error_multi_observation_subtraction_roundoff_contract.rs`, `crates/validation_core/tests/bias_standard_error_sqrt_double_rounding_contract.rs` | False-zero, sign symmetry, corrected final rounding, and exact-control coverage. | These repairs do **not** claim globally correctly rounded standard errors for every finite n>2 input. In particular, when rounded residual high parts differ, retained subtraction low terms may still move a nonzero standard error across a final binary64 boundary; that requires an independent represented-input counterexample before widening the production algorithm. From ae906ad609aa4eb948b8311c1cebd975f55eb2f6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:29:52 +0900 Subject: [PATCH 327/576] test(validation): expose distinct-high bias SE roundoff --- ...d_error_distinct_high_roundoff_contract.rs | 28 +++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs b/crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs new file mode 100644 index 000000000..a8049fdcb --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs @@ -0,0 +1,28 @@ +use validation_core::bias_standard_error; + +#[test] +fn bias_standard_error_preserves_roundoff_when_residual_high_parts_differ() { + let quarter_ulp_at_one = 2.0_f64.powi(-54); + let truth = [quarter_ulp_at_one, 3.0 * quarter_ulp_at_one, 0.0]; + let recovered = [1.0; 3]; + + let standard_error = + bias_standard_error(&truth, &recovered).expect("represented standard error"); + // Exact represented residuals are 1-2^-54, 1-3*2^-54, and 1. Their + // standard error rounds to this value. Rounding the pairwise residuals first + // instead yields [1, 1-2^-52, 1] and a materially larger result. + assert_eq!(standard_error.to_bits(), 0x3c8c_38aa_37c3_f68d); + + let mirrored_truth = truth.map(|value| -value); + let mirrored_recovered = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&mirrored_truth, &mirrored_recovered) + .expect("mirrored represented standard error"); + assert_eq!(mirrored_standard_error.to_bits(), 0x3c8c_38aa_37c3_f68d); + + // Without subtraction roundoff, the existing rounded-residual path remains + // authoritative for the same high-part pattern. + let exact_residuals = [1.0, 1.0 - 2.0_f64.powi(-52), 1.0]; + let control = bias_standard_error(&[0.0; 3], &exact_residuals) + .expect("exact-residual control standard error"); + assert_eq!(control.to_bits(), 0x3c96_a09e_667f_3bcd); +} From d59b9c30bf810a6dc6fdceaba6d20e048fad985a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:33:02 +0900 Subject: [PATCH 328/576] fix(validation): retain distinct-high residual roundoff in bias SE --- crates/validation_core/src/bias.rs | 95 ++++++++++++++++++++++++++++-- 1 file changed, 90 insertions(+), 5 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 6519d8c20..ab4727778 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -107,6 +107,80 @@ fn scaled_standard_error(values: &[f64], mean: f64) -> Result Result, ValidationError> { + let anchor_high = diffs[0]; + let anchor_low = roundoffs[0]; + let mut translated = Vec::with_capacity(diffs.len()); + + for (&high, &low) in diffs.iter().zip(roundoffs) { + let high_delta = high - anchor_high; + if !high_delta.is_finite() + || subtraction_roundoff(high, anchor_high, high_delta) != 0.0 + { + return Ok(None); + } + + let low_delta = low - anchor_low; + if !low_delta.is_finite() + || subtraction_roundoff(low, anchor_low, low_delta) != 0.0 + { + return Ok(None); + } + + let delta = high_delta + low_delta; + if !delta.is_finite() || subtraction_roundoff(high_delta, -low_delta, delta) != 0.0 { + return Ok(None); + } + translated.push(delta); + } + + let scale = translated + .iter() + .map(|value| value.abs()) + .fold(0.0, f64::max); + if scale == 0.0 { + return Ok(Some(0.0)); + } + + let normalized: Vec<_> = translated.iter().map(|value| *value / scale).collect(); + if translated + .iter() + .zip(&normalized) + .any(|(value, normalized_value)| *value != 0.0 && *normalized_value == 0.0) + { + return Ok(None); + } + + let normalized_sum = deterministic_compensated_sum(normalized.clone()); + let normalized_square_sum = deterministic_compensated_sum( + normalized + .iter() + .map(|value| value * value) + .collect(), + ); + let sample_count = translated.len() as f64; + let scaled_square_sum = sample_count * normalized_square_sum; + let dispersion_numerator = + (-normalized_sum).mul_add(normalized_sum, scaled_square_sum); + if !dispersion_numerator.is_finite() || dispersion_numerator <= 0.0 { + return Ok(None); + } + + let denominator = sample_count * sample_count * (sample_count - 1.0); + let normalized_standard_error = (dispersion_numerator / denominator).sqrt(); + let standard_error = scale * normalized_standard_error; + if !standard_error.is_finite() + || (standard_error == 0.0 && normalized_standard_error != 0.0) + { + Err(ValidationError::InvalidInput) + } else { + Ok(Some(standard_error)) + } +} + /// Mean signed bias `mean(recovered − truth)`. /// /// Exactly representable finite signed residuals use the canonical @@ -160,11 +234,14 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Result Date: Sat, 5 Sep 2026 06:33:38 +0900 Subject: [PATCH 329/576] docs(changelog): record distinct-high bias SE repair --- .../validation-bias-standard-error-distinct-high-roundoff.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-distinct-high-roundoff.md diff --git a/CHANGELOG.d/validation-bias-standard-error-distinct-high-roundoff.md b/CHANGELOG.d/validation-bias-standard-error-distinct-high-roundoff.md new file mode 100644 index 000000000..15935d5a0 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-distinct-high-roundoff.md @@ -0,0 +1,5 @@ +# Validation bias standard error retains distinct residual roundoff + +- Preserve error-free subtraction low terms when represented bias residual high parts differ but their anchor-relative exact deltas are representable. +- Evaluate the translation-invariant second moment of those exact deltas in O(n), avoiding a rounded-residual standard error that can materially overstate uncertainty. +- Keep the predecessor rounded-residual path when exact translated deltas cannot be established without another rounding step; this change does not claim globally correctly rounded standard errors. From b3d43ad3611163081fb8548cf0ce15ebcd8567ee Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:34:26 +0900 Subject: [PATCH 330/576] docs(research): trace distinct-high bias SE roundoff --- ...s-standard-error-distinct-high-roundoff.md | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 docs/research/bias-standard-error-distinct-high-roundoff.md diff --git a/docs/research/bias-standard-error-distinct-high-roundoff.md b/docs/research/bias-standard-error-distinct-high-roundoff.md new file mode 100644 index 000000000..424bffd8a --- /dev/null +++ b/docs/research/bias-standard-error-distinct-high-roundoff.md @@ -0,0 +1,62 @@ +# Bias standard error with distinct rounded residual highs + +## Finding + +`bias_standard_error` previously preserved subtraction low terms only when every rounded residual high part was identical. That condition is sufficient for the false-zero case repaired by GAP-093, but it is not necessary for discarded pairwise-subtraction mass to change a nonzero uncertainty estimate. + +Let `q = 2^-54`, `truth = [q, 3q, 0]`, and `recovered = [1, 1, 1]`. The represented inputs define exact residuals + +- `r1 = 1 - q`, +- `r2 = 1 - 3q`, +- `r3 = 1`. + +Their exact mean is `1 - 4q/3`, so the centered deviations are `[q/3, -5q/3, 4q/3]`. Therefore + +`SE = sqrt(sum((ri-r̄)^2) / (3*2)) = q * sqrt(7) / 3`, + +which rounds to binary64 bits `0x3c8c_38aa_37c3_f68d`. + +The predecessor first rounded each pairwise subtraction. Ties-to-even yields `[1, 1-2^-52, 1]`, whose rounded-residual path reports `0x3c96_a09e_667f_3bcd`. The difference is not a cosmetic ULP boundary: the predecessor is about 60% larger because it turns three nearby exact represented residuals into a different dispersion geometry before computing the standard error. + +## Constraints + +This is TEPP Validation Evidence performance-measure semantics. It does not create a reusable psychometric estimator and does not move static psychometric arithmetic away from `fast-mlsirm`. The repair must remain deterministic CPU `f64`, O(n), overflow-aware, and must not introduce arbitrary-precision runtime arithmetic or a second standard-error definition. + +The branch already has specialized exact handling for n=2 and for n>2 when every rounded residual high part collapses to one value. Those contracts remain authoritative. A wider repair must not replace them or claim global correct rounding for every finite input. + +## Alternatives considered + +Computing the standard error from rounded residuals was rejected because the RED proves that the rounded residual vector is not the represented-input scientific target. Computing all pairwise exact residual differences was also rejected: the identity is valid, but an O(n²) implementation is unnecessary for a production validation path. + +Reconstructing an arbitrary-precision residual vector in production was rejected because the represented inputs are binary64 and the owner contract requires a deterministic Rust `f64` reference. It would add runtime complexity without evidence that every path needs arbitrary precision. + +The selected repair uses the existing error-free subtraction decomposition `ri = high_i + low_i`. It chooses one residual as a translation anchor and admits the refinement only when every anchor-relative `high` difference, `low` difference, and their final sum are each exactly representable in binary64. Translation leaves variance unchanged. With the anchor translated to exact zero, the second-moment identity + +`SE² = (n * sum(di²) - sum(di)²) / (n² * (n-1))` + +can be evaluated in O(n). The zero anchor also bounds cancellation in the numerator because `sum(di)² <= (n-1) * sum(di²)`. If exact translated deltas cannot be established, the predecessor rounded-residual path remains in force rather than silently widening the numerical claim. + +## Evidence and traceability + +| Evidence | Exact reference | Role | +| --- | --- | --- | +| Public RED | `ae906ad609aa4eb948b8311c1cebd975f55eb2f6` | Adds the distinct-high counterexample, sign mirror, and a no-subtraction-roundoff control. | +| Causal repair | `d59b9c30bf810a6dc6fdceaba6d20e048fad985a` | Retains error-free low terms through exact anchor-relative deltas and an O(n) translation-invariant second moment. | +| CHANGELOG | `89c018630d5baadef04a8167034951b7e378a4b2` | Records the buyer-visible uncertainty correction and bounded scope. | +| Module/API | `crates/validation_core/src/bias.rs::bias_standard_error` | Canonical TEPP Validation Evidence producer. | +| Public contract | `crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs` | Exact positive case, sign symmetry, and unchanged exact-residual control. | +| Landing vehicle | PR `#488` | Exact surviving head and hosted evidence remain PR-authoritative. | + +The fix does **not** claim globally correctly rounded n>2 bias standard errors. In particular, if an anchor-relative exact residual delta itself needs rounding, this repair intentionally declines the exact-delta path until an independent represented-input counterexample justifies a further widening. + +## Methodological authority + +Bias and Monte Carlo uncertainty are performance measures that should be evaluated against known truth rather than implementation artifacts (Morris et al., 2019). The binary64 behavior described here is interpreted under IEEE 754-2019 / ISO/IEC 60559:2020 round-to-nearest, ties-to-even semantics. The broader validity claim remains evidence-oriented rather than numerical-method substitution: the *Standards for Educational and Psychological Testing* require validation evidence appropriate to the proposed interpretation and use of scores (AERA, APA, & NCME, 2014). + +## 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. + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 9265b34a2163f45bd232d628c20f725d2844f50a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:42:37 +0900 Subject: [PATCH 331/576] test(validation): expose exact-residual bias SE mean roundoff --- ...d_error_distinct_high_roundoff_contract.rs | 10 +++++----- ...r_exact_residual_mean_roundoff_contract.rs | 19 +++++++++++++++++++ 2 files changed, 24 insertions(+), 5 deletions(-) create mode 100644 crates/validation_core/tests/bias_standard_error_exact_residual_mean_roundoff_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs b/crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs index a8049fdcb..e7b849f9f 100644 --- a/crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs @@ -19,10 +19,10 @@ fn bias_standard_error_preserves_roundoff_when_residual_high_parts_differ() { .expect("mirrored represented standard error"); assert_eq!(mirrored_standard_error.to_bits(), 0x3c8c_38aa_37c3_f68d); - // Without subtraction roundoff, the existing rounded-residual path remains - // authoritative for the same high-part pattern. - let exact_residuals = [1.0, 1.0 - 2.0_f64.powi(-52), 1.0]; + // A symmetric exact-residual case remains on the established direct path. + let ulp_at_one = 2.0_f64.powi(-52); + let exact_residuals = [1.0 - ulp_at_one, 1.0, 1.0 + ulp_at_one]; let control = bias_standard_error(&[0.0; 3], &exact_residuals) - .expect("exact-residual control standard error"); - assert_eq!(control.to_bits(), 0x3c96_a09e_667f_3bcd); + .expect("symmetric exact-residual control standard error"); + assert_eq!(control.to_bits(), 0x3ca2_79a7_4590_331d); } diff --git a/crates/validation_core/tests/bias_standard_error_exact_residual_mean_roundoff_contract.rs b/crates/validation_core/tests/bias_standard_error_exact_residual_mean_roundoff_contract.rs new file mode 100644 index 000000000..9f66ff697 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_exact_residual_mean_roundoff_contract.rs @@ -0,0 +1,19 @@ +use validation_core::bias_standard_error; + +#[test] +fn bias_standard_error_does_not_round_the_exact_residual_mean_before_dispersion() { + let ulp_at_one = 2.0_f64.powi(-52); + let recovered = [1.0, 1.0 - ulp_at_one, 1.0]; + + let standard_error = bias_standard_error(&[0.0; 3], &recovered) + .expect("exact-residual represented standard error"); + // The represented residuals are exact and have mean 1 - 2^-52/3. Their + // exact standard error is 2^-52/3. Rounding that mean first instead creates + // deviations [2^-53, -2^-53, 2^-53] and overstates the uncertainty. + assert_eq!(standard_error.to_bits(), 0x3c95_5555_5555_5555); + + let mirrored = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&[0.0; 3], &mirrored) + .expect("mirrored exact-residual represented standard error"); + assert_eq!(mirrored_standard_error.to_bits(), 0x3c95_5555_5555_5555); +} From 2fa266b21069460370f30243cfb498e2022888bf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:43:39 +0900 Subject: [PATCH 332/576] fix(validation): avoid rounded residual mean in bias SE --- crates/validation_core/src/bias.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index ab4727778..d0cc62317 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -234,14 +234,15 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Result 2 { if let Some(standard_error) = exact_translated_residual_standard_error(&diffs, &subtraction_roundoffs)? { From ab361b25ec4296165f89e4eff26e6d3521c00571 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:44:15 +0900 Subject: [PATCH 333/576] docs(changelog): record exact-residual bias SE repair --- .../validation-bias-standard-error-exact-residual-mean.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-exact-residual-mean.md diff --git a/CHANGELOG.d/validation-bias-standard-error-exact-residual-mean.md b/CHANGELOG.d/validation-bias-standard-error-exact-residual-mean.md new file mode 100644 index 000000000..588714ec2 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-exact-residual-mean.md @@ -0,0 +1,5 @@ +# Validation bias standard error avoids rounded-mean dispersion drift + +- Evaluate larger-sample bias standard errors from exact anchor-relative residual deltas when those deltas are provably representable, even when the original pairwise residual subtractions were exact. +- Prevent an exactly represented residual vector such as `[1, 1-2^-52, 1]` from being turned into a different dispersion geometry by rounding its mean before centering. +- Keep the predecessor rounded-mean path when exact translated deltas cannot be established; this remains a bounded binary64 repair rather than a global correct-rounding claim. From 32f414603eacafccc034881d447c8fa7503a029a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:45:08 +0900 Subject: [PATCH 334/576] docs(research): trace exact-residual bias SE mean roundoff --- ...dard-error-exact-residual-mean-roundoff.md | 59 +++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 docs/research/bias-standard-error-exact-residual-mean-roundoff.md diff --git a/docs/research/bias-standard-error-exact-residual-mean-roundoff.md b/docs/research/bias-standard-error-exact-residual-mean-roundoff.md new file mode 100644 index 000000000..3debf391c --- /dev/null +++ b/docs/research/bias-standard-error-exact-residual-mean-roundoff.md @@ -0,0 +1,59 @@ +# Bias standard error must not round an exact residual mean before dispersion + +## Finding + +GAP-095 established that pairwise subtraction roundoff can materially change a nonzero bias standard error even when rounded residual high parts differ. Its first regression control exposed an independent boundary: the same scientific error can arise even when every `recovered - truth` subtraction is exact. + +Let `a = 2^-52`, `truth = [0,0,0]`, and `recovered = [1, 1-a, 1]`. The represented residuals are already exact binary64 values. Their exact mean is `1-a/3`; their centered deviations are `[a/3, -2a/3, a/3]`, so + +`SE = sqrt(sum((r_i-r_bar)^2)/(3*2)) = a/3`. + +The correctly rounded binary64 result is `0x3c95_5555_5555_5555`. + +The predecessor first rounded the residual mean to `next_down(1.0)`. Centering against that rounded mean created the different represented deviation vector `[2^-53, -2^-53, 2^-53]`, producing `0x3c96_a09e_667f_3bcd`. This is not a pairwise-subtraction defect: the residuals themselves are exact. The premature mean projection changes the dispersion geometry before the scientific denominator is applied. + +## Constraints + +This remains TEPP Validation Evidence performance-measure arithmetic. It does not create a reusable psychometric estimator and does not move static psychometric arithmetic from `fast-mlsirm`. Production remains deterministic Rust `f64`, O(n), bounded-allocation, and fail closed when a mathematically nonzero final standard error is outside binary64 range. + +The repair must retain GAP-092's exact two-observation subtraction-roundoff path and GAP-093's common-high/error-free-low path. It must also retain GAP-095's exact translated-residual path for distinct rounded highs. No arbitrary-precision production runtime or second standard-error definition is introduced. + +## Alternatives considered + +Keeping `mean = deterministic_representable_mean(residuals)` and centering each residual on that rounded scalar was rejected because this RED proves the rounded mean itself can become an implementation artifact that changes the target dispersion. + +An O(n^2) sum of all exact pairwise residual differences was rejected. The variance identity is translation invariant, so a single exact anchor is sufficient when anchor-relative residual deltas are representable. + +Globally reconstructing every exact binary64 rational with arbitrary-precision integers was rejected as disproportionate to the proven boundary. If an anchor-relative delta itself cannot be represented exactly, the existing fallback remains until an independent counterexample justifies a wider numerical contract. + +## Selected repair + +The GAP-095 helper already expresses each represented residual as an error-free `high + low` pair and admits a translated second-moment calculation only when every anchor-relative high delta, low delta, and combined residual delta is exactly representable. GAP-096 removes the unnecessary prerequisite that at least one pairwise subtraction must have roundoff. For `n > 2`, exact residual vectors can therefore use the same bounded translation-invariant path before any rounded residual mean is made authoritative. + +For the RED, anchor translation yields `[0, -2^-52, 0]`. After scale reduction the second-moment numerator is evaluated from `[0,-1,0]`, giving the exact normalized ratio `1/9` and the final `2^-52/3` result. A symmetric exact-residual control remains on the same translated identity and preserves its established result. + +This does not claim globally correctly rounded n>2 bias standard errors. The admitted path proves only that the translated residual deltas themselves are representable; later binary64 multiplication, compensated summation, division, square root, and scale restoration remain bounded operations whose independent counterexamples must be demonstrated before further widening. + +## Evidence and traceability + +| Evidence | Exact reference | Role | +| --- | --- | --- | +| Public RED | `9265b34a2163f45bd232d628c20f725d2844f50a` | Adds the exact-residual rounded-mean counterexample and its sign mirror; replaces GAP-095's invalid predecessor-preservation control with a symmetric scientific control. | +| Causal repair | `2fa266b21069460370f30243cfb498e2022888bf` | Admits the exact translated-residual second-moment path for all larger samples when its exact-delta preconditions hold, not only when subtraction roundoff is nonzero. | +| CHANGELOG | `ab361b25ec4296165f89e4eff26e6d3521c00571` | Records the buyer-visible uncertainty correction and bounded scope. | +| Module/API | `crates/validation_core/src/bias.rs::bias_standard_error` | Canonical TEPP Validation Evidence producer. | +| Public contract | `crates/validation_core/tests/bias_standard_error_exact_residual_mean_roundoff_contract.rs` | Exact positive/sign-mirror contract with expected bits `0x3c95_5555_5555_5555`. | +| GAP-095 regression | `crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs` | Keeps the distinct-high subtraction-roundoff repair and replaces the scientifically invalid control. | +| Landing vehicle | PR `#488` | Surviving Validation Evidence head; hosted/current-head evidence remains PR-authoritative. | + +## Methodological authority + +Morris, White, and Crowther (2019) treat bias and uncertainty as performance measures evaluated against known simulation truth and explicitly recommend reporting Monte Carlo uncertainty. The binary64 behavior here is interpreted under IEEE 754-2019 / ISO/IEC 60559:2020 arithmetic. The AERA/APA/NCME *Standards for Educational and Psychological Testing* (2014) remains the published edition while its sponsoring organizations revise that edition; validation claims therefore remain tied to published evidence rather than an unpublished revision. + +## 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. + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From d48e2515d62dfbe0a807b5dba40fbb7034d4fa9d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:57:58 +0900 Subject: [PATCH 335/576] test(validation): expose common-high SE mean rounding --- ...rror_common_high_mean_roundoff_contract.rs | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_common_high_mean_roundoff_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_common_high_mean_roundoff_contract.rs b/crates/validation_core/tests/bias_standard_error_common_high_mean_roundoff_contract.rs new file mode 100644 index 000000000..e57f7225b --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_common_high_mean_roundoff_contract.rs @@ -0,0 +1,22 @@ +use validation_core::bias_standard_error; + +#[test] +fn common_rounded_residual_highs_do_not_recenter_low_terms_on_a_rounded_mean() { + let quarter_ulp_at_one = 2.0_f64.powi(-54); + let truth = [quarter_ulp_at_one, 0.0, 0.0]; + let recovered = [1.0; 3]; + + let standard_error = + bias_standard_error(&truth, &recovered).expect("represented standard error"); + // Every binary64 subtraction rounds to 1.0, but the represented-input + // residuals are [1 - 2^-54, 1, 1]. Their exact mean is 1 - 2^-54 / 3, + // so SE is exactly 2^-54 / 3 before the final binary64 rounding. Rounding + // the low-term mean first and then centering moves the result one ULP up. + assert_eq!(standard_error.to_bits(), 0x3c75_5555_5555_5555); + + let mirrored_truth = truth.map(|value| -value); + let mirrored_recovered = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&mirrored_truth, &mirrored_recovered) + .expect("mirrored represented standard error"); + assert_eq!(mirrored_standard_error.to_bits(), 0x3c75_5555_5555_5555); +} From a63b8d7a8e79146cbb17ceb17855bcca312535a1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 06:59:08 +0900 Subject: [PATCH 336/576] fix(validation): preserve common-high low-term dispersion --- crates/validation_core/src/bias.rs | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index d0cc62317..4a2d9176a 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -233,16 +233,18 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Result Date: Sat, 5 Sep 2026 06:59:57 +0900 Subject: [PATCH 337/576] docs(validation): record common-high SE repair --- ...lidation-bias-standard-error-common-high-mean-roundoff.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-common-high-mean-roundoff.md diff --git a/CHANGELOG.d/validation-bias-standard-error-common-high-mean-roundoff.md b/CHANGELOG.d/validation-bias-standard-error-common-high-mean-roundoff.md new file mode 100644 index 000000000..c8be802e3 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-common-high-mean-roundoff.md @@ -0,0 +1,5 @@ +# Validation bias standard error preserves common-high low-term dispersion + +- When every rounded signed residual collapses to the same binary64 high part, evaluate the subtraction low-term dispersion with the exact translated-residual second moment whenever its anchor-relative deltas are exactly representable. +- Prevent low terms such as `[-2^-54, 0, 0]` from being re-centered on a rounded low-term mean and moving the final standard error one ULP upward. +- Retain the predecessor scaled low-term path only when exact translation cannot be established; this is a bounded represented-input repair, not a global correct-rounding claim. From 57d85e58411afb02e910b8e4138960319dcd8bce Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 07:00:16 +0900 Subject: [PATCH 338/576] docs(research): trace common-high SE mean rounding --- ...tandard-error-common-high-mean-roundoff.md | 39 +++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 docs/research/bias-standard-error-common-high-mean-roundoff.md diff --git a/docs/research/bias-standard-error-common-high-mean-roundoff.md b/docs/research/bias-standard-error-common-high-mean-roundoff.md new file mode 100644 index 000000000..13ce9d155 --- /dev/null +++ b/docs/research/bias-standard-error-common-high-mean-roundoff.md @@ -0,0 +1,39 @@ +# Common-high subtraction low terms must not inherit a rounded-mean dispersion geometry + +## Problem + +`bias_standard_error` already retained error-free subtraction low terms when several represented-input residuals rounded to one common binary64 high part. The predecessor then computed a representable mean of those low terms and centered each low term on that rounded mean before evaluating dispersion. + +For `truth = [2^-54, 0, 0]` and `recovered = [1, 1, 1]`, all three binary64 subtractions return `1.0`, but the represented-input residuals are exactly `[1 - 2^-54, 1, 1]`. The common high part is translation-invariant and contributes no dispersion. The retained low terms are `[-2^-54, 0, 0]`, whose represented-input standard error is exactly `2^-54 / 3` before final binary64 rounding, bits `0x3c75_5555_5555_5555`. + +The predecessor first rounded the low-term mean `-2^-54 / 3`, then formed three deviations from that rounded mean. That changes the represented dispersion geometry and returns the adjacent higher binary64 value. The sign-mirrored input has the same standard error and exposes the same defect. + +## Constraints and rejected alternatives + +The repair stays inside TEPP Validation Evidence. It does not create a reusable psychometric estimator, copy fast-mlsirm arithmetic, or change Longitudinal Modeling composition. It also does not claim globally correctly rounded standard errors for arbitrary binary64 samples. + +Replacing the whole standard-error implementation with arbitrary-precision production arithmetic was rejected because the counterexample only requires preserving a translation-invariant represented-input identity already used by the current Validation path. Keeping the rounded low-term mean as authoritative was rejected because the public RED proves that its intermediate rounding changes the target quantity. Removing the common-high path altogether was also rejected: when low-term anchor differences cannot be proven exactly representable, the existing bounded fallback still preserves earlier nonzero-dispersion behavior better than collapsing to the rounded high parts. + +## Decision + +When all rounded residual highs are equal and subtraction roundoff is present, first run the existing exact translated-residual second-moment calculation over the retained low terms with zero secondary roundoff. This path is admitted only when every anchor-relative low-term delta is exactly representable under the same proof used by GAP-095/096. If that proof fails, retain the predecessor scaled low-term mean/deviation fallback. + +Public contract: `crates/validation_core/tests/bias_standard_error_common_high_mean_roundoff_contract.rs`. + +RED commit: `d48e2515d62dfbe0a807b5dba40fbb7034d4fa9d`. + +Causal repair: `a63b8d7a8e79146cbb17ceb17855bcca312535a1`. + +CHANGELOG: `6fc8e82839389f2f8c07e1cf7ba78a29f19d2510`. + +## Scientific trace + +Bias and its Monte Carlo uncertainty are performance measures against known truth; preserving the represented-input statistic is therefore part of Validation Evidence rather than an LLM or projection judgment. Binary64 rounding behavior follows the published IEEE floating-point arithmetic contract. + +IEEE Computer Society. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE. https://doi.org/10.1109/IEEESTD.2019.8766229 + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +## Remaining risk + +This repair proves the common-high counterexample and its sign mirror only under exact anchor-relative low-term translation. If those translated low-term deltas themselves require rounding, the bounded predecessor fallback remains. A wider change requires a separate represented-input counterexample and RED rather than extrapolation from this case. From 426fc5afd8aae8c2d5f81f53e5db074b480ac8b7 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 08:03:07 +0900 Subject: [PATCH 339/576] test(validation): expose SE scale double rounding --- ...ard_error_power_scale_rounding_contract.rs | 21 +++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_power_scale_rounding_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_power_scale_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_power_scale_rounding_contract.rs new file mode 100644 index 000000000..8b20f34f1 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_power_scale_rounding_contract.rs @@ -0,0 +1,21 @@ +use validation_core::bias_standard_error; + +#[test] +fn bias_standard_error_does_not_double_round_through_a_non_power_scale() { + let ulp_at_one = 2.0_f64.powi(-52); + let low = 1.0 - 4.0 * ulp_at_one; + let high = 1.0 + ulp_at_one; + let recovered = [low, low, high]; + + let standard_error = bias_standard_error(&[0.0; 3], &recovered) + .expect("represented-input standard error"); + // The exact represented residual gap is 5 * 2^-52. For [a, a, a+d], + // SE(mean) is exactly d / 3 before final binary64 rounding. Scaling by d + // first instead turns that into rounded(1/3) * d and lands one ULP low. + assert_eq!(standard_error.to_bits(), 0x3cba_aaaa_aaaa_aaab); + + let mirrored = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&[0.0; 3], &mirrored) + .expect("mirrored represented-input standard error"); + assert_eq!(mirrored_standard_error.to_bits(), 0x3cba_aaaa_aaaa_aaab); +} From d8b411f5dcf93ff21b18eaa4ff5ccd8d2863a6cd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 08:04:17 +0900 Subject: [PATCH 340/576] refactor(validation): expose exact power scale within crate --- crates/validation_core/src/numeric.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/validation_core/src/numeric.rs b/crates/validation_core/src/numeric.rs index f3976586b..3b6a611eb 100644 --- a/crates/validation_core/src/numeric.rs +++ b/crates/validation_core/src/numeric.rs @@ -49,7 +49,7 @@ pub(crate) fn deterministic_compensated_sum(values: Vec) -> f64 { sum + correction } -fn exact_power_of_two_scale(max_magnitude: f64) -> f64 { +pub(crate) fn exact_power_of_two_scale(max_magnitude: f64) -> f64 { let bits = max_magnitude.to_bits(); let exponent = (bits >> 52) & 0x7ff; if exponent == 0 { From d536e3b1082c0f705a6ddd7722bf45c93ac594e3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 08:05:00 +0900 Subject: [PATCH 341/576] fix(validation): preserve dyadic SE scale --- crates/validation_core/src/bias.rs | 22 +++++++++++++++------- 1 file changed, 15 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 4a2d9176a..cba0a15a3 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -4,7 +4,7 @@ use crate::ValidationError; use crate::input::require_paired_finite; use crate::numeric::{ deterministic_compensated_sum, deterministic_representable_mean, - deterministic_representable_sum_over_count, + deterministic_representable_sum_over_count, exact_power_of_two_scale, }; fn signed_residuals(truth: &[f64], recovered: &[f64]) -> Result, ValidationError> { @@ -137,13 +137,18 @@ fn exact_translated_residual_standard_error( translated.push(delta); } - let scale = translated + let max_magnitude = translated .iter() .map(|value| value.abs()) .fold(0.0, f64::max); - if scale == 0.0 { + if max_magnitude == 0.0 { return Ok(Some(0.0)); } + // Keep the translated binary64 geometry on an exact dyadic scale. Using the + // largest translated value itself can turn an exactly represented gap d into + // rounded(1/3) * d after the square-root stage and move the final SE by one + // ULP. A power-of-two scale changes only exponents, so restoring it is exact. + let scale = exact_power_of_two_scale(max_magnitude); let normalized: Vec<_> = translated.iter().map(|value| *value / scale).collect(); if translated @@ -241,10 +246,13 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 08:05:44 +0900 Subject: [PATCH 342/576] docs(changelog): record SE dyadic scale repair --- .../validation-bias-standard-error-power-scale-rounding.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-power-scale-rounding.md diff --git a/CHANGELOG.d/validation-bias-standard-error-power-scale-rounding.md b/CHANGELOG.d/validation-bias-standard-error-power-scale-rounding.md new file mode 100644 index 000000000..d8ea93a70 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-power-scale-rounding.md @@ -0,0 +1,5 @@ +### Fixed + +- `validation_core::bias_standard_error` now keeps exact translated residual geometry on a power-of-two normalization scale before evaluating the translated second moment. +- This prevents an exactly represented gap such as `5 * 2^-52` from being converted into `rounded(1/3) * gap`, which can move the final `SE(mean)` by one ULP when the non-power scale is restored. +- The public contract covers the sign-mirrored three-observation boundary; cases that cannot prove exact translated residual deltas still retain the predecessor bounded fallback. From bcb484eab61d8f0198fec5c5119b76c4b0329678 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 08:06:23 +0900 Subject: [PATCH 343/576] docs(research): trace SE dyadic scale rounding --- ...ias-standard-error-power-scale-rounding.md | 62 +++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100644 docs/research/bias-standard-error-power-scale-rounding.md diff --git a/docs/research/bias-standard-error-power-scale-rounding.md b/docs/research/bias-standard-error-power-scale-rounding.md new file mode 100644 index 000000000..5baac1e46 --- /dev/null +++ b/docs/research/bias-standard-error-power-scale-rounding.md @@ -0,0 +1,62 @@ +# Bias standard error must preserve dyadic scale through translated dispersion + +## Finding + +GAP-098 isolates a binary64 rounding defect in the exact translated-residual path introduced to avoid rounded-mean dispersion drift. The path proved that anchor-relative residual deltas were exactly representable, but then normalized them by the largest translated magnitude itself. When that magnitude is not a power of two, the final scale restoration can introduce a second rounding that changes `SE(mean)` even though the translated geometry was exact. + +Let `u = 2^-52`, `a = 1 - 4u`, and `b = 1 + u`. With `truth = [0,0,0]` and `recovered = [a,a,b]`, every pairwise residual subtraction is exact. The represented residual gap is + +`d = b - a = 5 * 2^-52`. + +For a three-observation vector `[a,a,a+d]`, translation invariance gives centered deviations `[-d/3,-d/3,2d/3]`, therefore + +`SE(mean) = sqrt(sum((r_i-r_bar)^2)/(3*2)) = d/3`. + +The correctly rounded binary64 result is bits `0x3cba_aaaa_aaaa_aaab`. The predecessor translated to `[0,0,d]`, chose `scale=d`, evaluated the normalized geometry as `[0,0,1]`, rounded the square-root stage to the binary64 approximation of `1/3`, and then multiplied that rounded value by the non-power scale `d`. The result was one ULP low at `0x3cba_aaaa_aaaa_aaaa`. The sign-mirrored sample reproduces the same defect. + +## Constraints + +This remains TEPP Validation Evidence performance-measure arithmetic. It does not create a reusable psychometric estimator, does not move static psychometric ownership from `fast-mlsirm`, and does not consume mutable sibling-repository source. Production remains deterministic Rust `f64`, O(n), and fail closed when a mathematically nonzero requested result cannot be represented. + +The repair must preserve GAP-092 through GAP-097: the exact two-observation identity, common-high subtraction-low recovery, exact translated residual admission, the direct normalized SE expression, and bounded fallback when translated deltas cannot be proved exact. It must not claim globally correctly rounded n>2 dispersion. + +## Alternatives considered + +Keeping the largest translated magnitude as the normalization scale was rejected because the RED proves that an otherwise exact translated geometry can be projected through a rounded normalized result and multiplied by a non-dyadic scale, introducing a second rounding at the scientific result boundary. + +Adding arbitrary-precision rational arithmetic to production was rejected as disproportionate to the demonstrated cause. The input values are already binary64 dyadic rationals, and an exact power-of-two normalization is sufficient to preserve their represented geometry across scale reduction/restoration. + +Special-casing the `[a,a,b]` three-observation identity was rejected because the defect belongs to the normalization policy, not to that sample shape. The existing translated-second-moment path should retain its general bounded admission contract. + +## Selected repair + +`numeric::exact_power_of_two_scale` is made crate-visible and reused by `bias::exact_translated_residual_standard_error`. The helper chooses the power-of-two binade scale already used by Validation Evidence's deterministic mean arithmetic. Dividing a represented dyadic residual delta by that scale changes only its exponent when representable, and multiplying the normalized result by the same scale restores magnitude without an additional non-dyadic rounding. + +For the RED, the translated vector remains an exact dyadic geometry under the power scale. The normalized second moment therefore carries the factor `5` into the square-root ratio instead of collapsing it into a later multiplication by `d`; final power-of-two scale restoration yields the correctly rounded `d/3` result. + +The existing nonzero-to-zero normalization guard remains in force. Exact translated-delta admission is unchanged, and samples that fail that proof still use the predecessor bounded fallback. + +## Evidence and traceability + +| Evidence | Exact reference | Role | +| --- | --- | --- | +| Public RED | `426fc5afd8aae8c2d5f81f53e5db074b480ac8b7` | Adds the exact `[a,a,b]` counterexample and sign mirror with expected bits `0x3cba_aaaa_aaaa_aaab`. | +| Shared numeric prerequisite | `d8b411f5dcf93ff21b18eaa4ff5ccd8d2863a6cd` | Makes the existing exact power-of-two scale helper crate-visible without changing its arithmetic. | +| Causal repair | `d536e3b1082c0f705a6ddd7722bf45c93ac594e3` | Uses the dyadic scale in the exact translated-residual standard-error path and updates the API contract. | +| CHANGELOG | `ebce979fdfb0ab2d94e3ba70145b0076a7ef07ca` | Records the buyer-visible one-ULP uncertainty correction and bounded scope. | +| Module/API | `crates/validation_core/src/bias.rs::bias_standard_error` | Canonical TEPP Validation Evidence producer. | +| Shared helper | `crates/validation_core/src/numeric.rs::exact_power_of_two_scale` | Existing crate-private dyadic normalization owner reused by the producer. | +| Public contract | `crates/validation_core/tests/bias_standard_error_power_scale_rounding_contract.rs` | Exact positive/sign-mirror regression contract. | +| Landing vehicle | PR `#488` | Surviving Validation Evidence head; hosted current-head evidence remains PR-authoritative. | + +## Methodological authority + +Morris, White, and Crowther (2019) treat bias and uncertainty as performance measures evaluated against known simulation truth and emphasize Monte Carlo uncertainty. The floating-point behavior in this repair is interpreted under IEEE 754-2019 / ISO/IEC 60559:2020. The AERA/APA/NCME *Standards for Educational and Psychological Testing* (2014) remains the published testing-standards edition while revision work continues; TEPP therefore ties validation claims to published authority and exact executable evidence rather than unpublished draft language. + +## 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. + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From fbcdb7fac40744c697debbbe6184d4e0ffd5e32a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 08:57:45 +0900 Subject: [PATCH 344/576] test(validation): expose repeated-level SE double rounding --- ..._error_repeated_level_rounding_contract.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_repeated_level_rounding_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_repeated_level_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_repeated_level_rounding_contract.rs new file mode 100644 index 000000000..d42c87aa7 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_repeated_level_rounding_contract.rs @@ -0,0 +1,26 @@ +use validation_core::bias_standard_error; + +#[test] +fn bias_standard_error_preserves_three_observation_repeated_level_identity() { + let repeated = f64::from_bits(0x3fef_ffff_ffff_ffff); + let recovered = [0.0, repeated, repeated]; + + let standard_error = bias_standard_error(&[0.0; 3], &recovered) + .expect("represented-input standard error"); + // For exactly represented residuals [0, a, a], the three-observation + // standard error simplifies algebraically to |a| / 3. The predecessor + // squared the normalized a values, formed the second moment, and then took + // a square root; that extra projection lands one ULP below the single + // correctly rounded division for a = next_down(1.0). + assert_eq!(standard_error.to_bits(), 0x3fd5_5555_5555_5555); + + let permuted = [repeated, 0.0, repeated]; + let permuted_standard_error = bias_standard_error(&[0.0; 3], &permuted) + .expect("permuted represented-input standard error"); + assert_eq!(permuted_standard_error.to_bits(), 0x3fd5_5555_5555_5555); + + let mirrored = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&[0.0; 3], &mirrored) + .expect("mirrored represented-input standard error"); + assert_eq!(mirrored_standard_error.to_bits(), 0x3fd5_5555_5555_5555); +} From e0f2445d825f12817631ca8e5ef5fed77fcd113a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 08:59:16 +0900 Subject: [PATCH 345/576] fix(validation): preserve repeated-level SE identity --- crates/validation_core/src/bias.rs | 42 ++++++++++++++++++++++++------ 1 file changed, 34 insertions(+), 8 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index cba0a15a3..3b28f57e6 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -144,6 +144,29 @@ fn exact_translated_residual_standard_error( if max_magnitude == 0.0 { return Ok(Some(0.0)); } + + if translated.len() == 3 { + let repeated_level_gap = if translated[0] == translated[1] { + Some(translated[2]) + } else if translated[0] == translated[2] || translated[1] == translated[2] { + Some(translated[1]) + } else { + None + }; + if let Some(gap) = repeated_level_gap { + // With three observations and exactly two equal represented residual + // levels, SE(mean) simplifies to |gap| / 3. Evaluate that identity + // directly instead of projecting gap through square -> second moment + // -> sqrt, which can move the final binary64 result by one ULP even + // though the translated gap itself is exact. + let standard_error = gap.abs() / 3.0; + if standard_error == 0.0 && gap != 0.0 { + return Err(ValidationError::InvalidInput); + } + return Ok(Some(standard_error)); + } + } + // Keep the translated binary64 geometry on an exact dyadic scale. Using the // largest translated value itself can turn an exactly represented gap d into // rounded(1/3) * d after the square-root stage and move the final SE by one @@ -245,14 +268,17 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 08:59:35 +0900 Subject: [PATCH 346/576] docs(changelog): record repeated-level SE repair --- ...validation-bias-standard-error-repeated-level-rounding.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-repeated-level-rounding.md diff --git a/CHANGELOG.d/validation-bias-standard-error-repeated-level-rounding.md b/CHANGELOG.d/validation-bias-standard-error-repeated-level-rounding.md new file mode 100644 index 000000000..504a094c2 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-repeated-level-rounding.md @@ -0,0 +1,5 @@ +### Fixed + +- `validation_core::bias_standard_error` now preserves the exact three-observation identity `SE(mean) = |level_gap| / 3` when exactly two represented residual levels are equal after an exact translated-residual admission. +- This avoids an unnecessary square → second-moment → square-root projection that moves `[0, next_down(1), next_down(1)]` one ULP below the correctly rounded represented-input standard error. +- The repair is deliberately bounded to the proven three-observation two-level identity; the general translated second-moment path and its fail-closed fallbacks remain unchanged. From 7741aaa50f7acabe52407696015025458ef98483 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 08:59:54 +0900 Subject: [PATCH 347/576] docs(research): trace repeated-level SE rounding --- ...-standard-error-repeated-level-rounding.md | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 docs/research/bias-standard-error-repeated-level-rounding.md diff --git a/docs/research/bias-standard-error-repeated-level-rounding.md b/docs/research/bias-standard-error-repeated-level-rounding.md new file mode 100644 index 000000000..f99d6ea29 --- /dev/null +++ b/docs/research/bias-standard-error-repeated-level-rounding.md @@ -0,0 +1,70 @@ +# Bias standard error: repeated-level three-observation rounding + +## Problem + +Validation Evidence computes the standard error of the mean signed bias from represented binary64 recovery residuals. After GAP-095–098, the exact-translated path preserves represented residual geometry and uses a power-of-two scale, but it still evaluates the general second-moment expression through squared binary64 values and a square root. + +A bounded three-observation counterexample remains. Let + +- `a = next_down(1.0) = 0x1.fffffffffffffp-1 = 1 - 2^-53`, +- `truth = [0, 0, 0]`, and +- `recovered = [0, a, a]`. + +All three represented residuals and anchor-relative translated deltas are exact. For the residual vector `[0, a, a]`, the sample standard error of the mean simplifies algebraically to + +`SE(mean) = |a| / 3`. + +The correctly rounded binary64 result is `0x1.5555555555555p-2` (`0x3fd5_5555_5555_5555`). The predecessor exact-translated implementation normalized on a power-of-two scale, squared the normalized `a` values, formed `n * sum(x^2) - sum(x)^2`, divided, and then took a square root. That sequence returns the adjacent lower float `0x1.5555555555554p-2` for this represented input. The discrepancy is one ULP and is not caused by temporal composition, sampling design, or reusable psychometric estimation; it is Validation Evidence binary64 projection error. + +Public RED: `fbcdb7fac40744c697debbbe6184d4e0ffd5e32a` (`bias_standard_error_repeated_level_rounding_contract.rs`). + +## Constraints + +The repair must preserve the current owner and numerical boundaries: + +- TEPP Validation Evidence owns this performance-measure decision arithmetic. +- reusable static psychometric estimation remains fast-mlsirm-owned; +- no arbitrary-precision production dependency is introduced merely to eliminate a one-ULP bounded projection error; +- exact translated-delta admission and fail-closed behavior remain unchanged; +- a local counterexample does not justify claiming globally correctly rounded `n > 2` standard errors. + +## Alternatives + +### Keep the general second-moment route + +Rejected for the proven shape. The general formula is mathematically valid, but the represented operation sequence introduces avoidable square and square-root projections after the residual geometry is already exact. + +### Replace all larger-sample standard errors with arbitrary precision + +Rejected. It widens the production dependency and performance surface far beyond the demonstrated defect and would duplicate a numerical owner without a demonstrated buyer/scientific need. + +### Add a generic two-level closed form for every sample size + +Not adopted in this repair. For arbitrary counts the coefficient includes a square root and still needs separate rounding analysis. The current counterexample proves only the `n = 3`, two-equal-level identity where the standard error reduces exactly to one represented gap divided by three. + +### Evaluate the exact three-observation two-level identity directly + +Selected. After exact translated-residual admission, any three-observation sample with exactly two equal levels is translation-equivalent to `[0, 0, d]` or `[0, d, d]`. In either case `SE(mean) = |d| / 3`. The gap is already a represented exact translated delta, so one correctly rounded binary64 division is the narrow causal operation required by the scientific identity. + +Causal repair: `e0f2445d825f12817631ca8e5ef5fed77fcd113a`. + +## Risk and follow-up + +This repair does not establish global correct rounding for general translated second moments. Independent counterexamples involving three distinct levels, larger `n`, square accumulation, division, or square root remain separate findings and require their own represented-input RED before the algorithm is widened again. If the direct identity produces zero from a nonzero represented gap, the result remains fail closed as unrepresentable rather than being reported as zero uncertainty. + +## Traceability + +- Bounded context: Validation Evidence. +- Module/API: `crates/validation_core/src/bias.rs` → `bias_standard_error` / `exact_translated_residual_standard_error`. +- Public contract: `crates/validation_core/tests/bias_standard_error_repeated_level_rounding_contract.rs`. +- RED: `fbcdb7fac40744c697debbbe6184d4e0ffd5e32a`. +- Repair: `e0f2445d825f12817631ca8e5ef5fed77fcd113a`. +- Release note: `CHANGELOG.d/validation-bias-standard-error-repeated-level-rounding.md`. + +## References + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). Institute of Electrical and Electronics Engineers. https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 4386d9ace83cd54aa129067ddc589b1a628147a2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 10:02:44 +0900 Subject: [PATCH 348/576] test(validation): expose singleton-level SE rerounding --- ...error_singleton_level_rounding_contract.rs | 26 +++++++++++++++++++ 1 file changed, 26 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_singleton_level_rounding_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_singleton_level_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_singleton_level_rounding_contract.rs new file mode 100644 index 000000000..ccc79cb1a --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_singleton_level_rounding_contract.rs @@ -0,0 +1,26 @@ +use validation_core::bias_standard_error; + +#[test] +fn bias_standard_error_preserves_singleton_repeated_level_identity_beyond_three_rows() { + let repeated = f64::from_bits(0x3fef_ffff_ffff_ffff); + let recovered = [0.0, repeated, repeated, repeated]; + + let standard_error = bias_standard_error(&[0.0; 4], &recovered) + .expect("represented-input standard error"); + // For an exactly represented four-observation two-level sample [0, a, a, a], + // the sample standard error of the mean simplifies to |a| / 4. The generic + // translated second-moment path squares and square-roots the exact gap and + // lands one ULP below that single exact power-of-two division for + // a = next_down(1.0). + assert_eq!(standard_error.to_bits(), 0x3fcf_ffff_ffff_ffff); + + let permuted = [repeated, 0.0, repeated, repeated]; + let permuted_standard_error = bias_standard_error(&[0.0; 4], &permuted) + .expect("permuted represented-input standard error"); + assert_eq!(permuted_standard_error.to_bits(), 0x3fcf_ffff_ffff_ffff); + + let mirrored = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&[0.0; 4], &mirrored) + .expect("mirrored represented-input standard error"); + assert_eq!(mirrored_standard_error.to_bits(), 0x3fcf_ffff_ffff_ffff); +} From 79ad03fae4364d6c364915a062eb0fc8615eaa43 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 10:04:07 +0900 Subject: [PATCH 349/576] fix(validation): preserve singleton-level SE identity --- crates/validation_core/src/bias.rs | 54 ++++++++++++++++++------------ 1 file changed, 33 insertions(+), 21 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 3b28f57e6..fffca24f2 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -145,26 +145,38 @@ fn exact_translated_residual_standard_error( return Ok(Some(0.0)); } - if translated.len() == 3 { - let repeated_level_gap = if translated[0] == translated[1] { - Some(translated[2]) - } else if translated[0] == translated[2] || translated[1] == translated[2] { - Some(translated[1]) - } else { - None - }; - if let Some(gap) = repeated_level_gap { - // With three observations and exactly two equal represented residual - // levels, SE(mean) simplifies to |gap| / 3. Evaluate that identity - // directly instead of projecting gap through square -> second moment - // -> sqrt, which can move the final binary64 result by one ULP even - // though the translated gap itself is exact. - let standard_error = gap.abs() / 3.0; - if standard_error == 0.0 && gap != 0.0 { - return Err(ValidationError::InvalidInput); + let mut zero_count = 0_usize; + let mut repeated_gap = None; + let mut gap_count = 0_usize; + let mut exactly_two_levels = true; + for &value in &translated { + if value == 0.0 { + zero_count += 1; + } else if let Some(gap) = repeated_gap { + if value != gap { + exactly_two_levels = false; + break; } - return Ok(Some(standard_error)); + gap_count += 1; + } else { + repeated_gap = Some(value); + gap_count = 1; + } + } + if exactly_two_levels + && (zero_count == 1 || gap_count == 1) + && let Some(gap) = repeated_gap + { + // For an exactly translated two-level sample where either level occurs + // once, SE(mean) simplifies to |gap| / n. Evaluate that identity directly + // instead of projecting the exact gap through square -> second moment -> + // sqrt, which can move the final binary64 result by one ULP. + let sample_count = translated.len() as f64; + let standard_error = gap.abs() / sample_count; + if standard_error == 0.0 && gap != 0.0 { + return Err(ValidationError::InvalidInput); } + return Ok(Some(standard_error)); } // Keep the translated binary64 geometry on an exact dyadic scale. Using the @@ -269,9 +281,9 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 10:04:43 +0900 Subject: [PATCH 350/576] docs(changelog): record singleton-level SE repair --- .../validation-bias-standard-error-singleton-level-rounding.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-singleton-level-rounding.md diff --git a/CHANGELOG.d/validation-bias-standard-error-singleton-level-rounding.md b/CHANGELOG.d/validation-bias-standard-error-singleton-level-rounding.md new file mode 100644 index 000000000..8224053cc --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-singleton-level-rounding.md @@ -0,0 +1,3 @@ +### Fixed + +- Validation Evidence `bias_standard_error` now preserves the exact translated two-level identity `SE(mean) = |level_gap| / n` when either residual level occurs once. This prevents the general square/second-moment/square-root path from moving an exactly represented four-observation singleton/repeated-level result by one binary64 ULP while retaining the existing fail-closed behavior for an unrepresentable nonzero standard error. From 4810fc50dbe75d96e18ad72ea3642e66eb3e58c3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 10:05:08 +0900 Subject: [PATCH 351/576] docs(research): trace singleton-level SE rounding --- ...standard-error-singleton-level-rounding.md | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 docs/research/bias-standard-error-singleton-level-rounding.md diff --git a/docs/research/bias-standard-error-singleton-level-rounding.md b/docs/research/bias-standard-error-singleton-level-rounding.md new file mode 100644 index 000000000..cd913052c --- /dev/null +++ b/docs/research/bias-standard-error-singleton-level-rounding.md @@ -0,0 +1,49 @@ +# Bias standard-error singleton-level rounding + +## Decision scope + +This note records a Validation Evidence numerical finding for `validation_core::bias_standard_error`. It does not create a reusable psychometric estimator and does not move static psychometric arithmetic from the fast-mlsirm owner. The relevant scientific estimand is the standard error of the mean signed recovery bias under the existing independent-observation contract. + +## Finding + +Let `a = next_down(1.0) = 0x1.fffffffffffffp-1`, with represented inputs + +- `truth = [0, 0, 0, 0]` +- `recovered = [0, a, a, a]`. + +All four signed residuals and the anchor-relative translated residuals are exactly representable binary64 values. For the two-level sample `[0, a, a, a]`, the sample mean is `3a/4`, the sum of squared centered residuals is `3a²/4`, and therefore + +`SE(mean) = sqrt((3a²/4) / (4 × 3)) = |a| / 4`. + +The correctly represented result is `0x3fcf_ffff_ffff_ffff`. The predecessor exact-translated second-moment path normalizes, squares, forms the second-moment numerator, divides, and takes a square root; for this payload it returns adjacent lower `0x3fcf_ffff_ffff_fffe`. This is a one-ULP decision-quality defect despite every translated residual being exact. + +Public RED `4386d9ace83cd54aa129067ddc589b1a628147a2` adds `crates/validation_core/tests/bias_standard_error_singleton_level_rounding_contract.rs` and fixes the expected represented result across the original ordering, a permutation, and a sign mirror. + +## Causal repair + +Repair `79ad03fae4364d6c364915a062eb0fc8615eaa43` remains inside `exact_translated_residual_standard_error` after the existing exact anchor-relative translation proof. If the translated sample has exactly two represented levels and either level occurs once, the sample standard error simplifies for any supported `n` to `|level_gap| / n`; the implementation evaluates that identity directly and retains fail-closed behavior when a nonzero represented gap would divide below binary64 range. + +The repair intentionally does not claim globally correctly rounded `n > 2` standard errors. Samples with three or more represented levels, two-level samples without a singleton, and cases that fail the exact translation admission continue through the existing bounded second-moment or fallback paths and require their own represented-input counterexample before any broader change. + +## Alternatives considered + +Applying arbitrary-precision arithmetic to every Validation metric was rejected because it widens the production arithmetic contract far beyond the demonstrated defect and adds a new runtime dependency. Special-casing only the four-row payload was rejected because the algebraic identity is determined by the singleton/two-level structure rather than by `n = 4`. Leaving the generic square/root path unchanged was rejected because the public contract can reproduce a deterministic one-ULP error from exact represented residuals. + +## Traceability + +- Bounded context: Validation Evidence. +- Production API: `validation_core::bias_standard_error`. +- Production module: `crates/validation_core/src/bias.rs`. +- Public regression: `crates/validation_core/tests/bias_standard_error_singleton_level_rounding_contract.rs`. +- RED: `4386d9ace83cd54aa129067ddc589b1a628147a2`. +- Causal repair: `79ad03fae4364d6c364915a062eb0fc8615eaa43`. +- CHANGELOG: `017ad11ad974219a5a0e1cf91c1ecf55c44524c2`. +- Landing vehicle: PR #488. + +IEEE 754 binary floating-point semantics remain the numerical representation authority. Known-truth recovery metrics remain performance measures rather than LLM judgments; simulation acceptance must continue to report the estimand, bias/dispersion behavior, and Monte Carlo uncertainty. + +## References + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE. https://doi.org/10.1109/IEEESTD.2019.8766229 + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 7fe4117c5666af08adbfda5d32beb12648f579c6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 10:06:44 +0900 Subject: [PATCH 352/576] test(validation): cover singleton-level SE false-zero refusal --- ...tandard_error_singleton_level_rounding_contract.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/tests/bias_standard_error_singleton_level_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_singleton_level_rounding_contract.rs index ccc79cb1a..031ec27cf 100644 --- a/crates/validation_core/tests/bias_standard_error_singleton_level_rounding_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_singleton_level_rounding_contract.rs @@ -1,4 +1,4 @@ -use validation_core::bias_standard_error; +use validation_core::{ValidationError, bias_standard_error}; #[test] fn bias_standard_error_preserves_singleton_repeated_level_identity_beyond_three_rows() { @@ -23,4 +23,13 @@ fn bias_standard_error_preserves_singleton_repeated_level_identity_beyond_three_ let mirrored_standard_error = bias_standard_error(&[0.0; 4], &mirrored) .expect("mirrored represented-input standard error"); assert_eq!(mirrored_standard_error.to_bits(), 0x3fcf_ffff_ffff_ffff); + + let minimum_subnormal = f64::from_bits(1); + assert_eq!( + bias_standard_error( + &[0.0; 4], + &[0.0, minimum_subnormal, minimum_subnormal, minimum_subnormal], + ), + Err(ValidationError::InvalidInput) + ); } From 944fac058a74b375fde86dbc2a860a4a4e386234 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 10:07:04 +0900 Subject: [PATCH 353/576] docs(research): trace singleton-level SE edge refusal --- docs/research/bias-standard-error-singleton-level-rounding.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/research/bias-standard-error-singleton-level-rounding.md b/docs/research/bias-standard-error-singleton-level-rounding.md index cd913052c..fff1984b0 100644 --- a/docs/research/bias-standard-error-singleton-level-rounding.md +++ b/docs/research/bias-standard-error-singleton-level-rounding.md @@ -21,7 +21,7 @@ Public RED `4386d9ace83cd54aa129067ddc589b1a628147a2` adds `crates/validation_co ## Causal repair -Repair `79ad03fae4364d6c364915a062eb0fc8615eaa43` remains inside `exact_translated_residual_standard_error` after the existing exact anchor-relative translation proof. If the translated sample has exactly two represented levels and either level occurs once, the sample standard error simplifies for any supported `n` to `|level_gap| / n`; the implementation evaluates that identity directly and retains fail-closed behavior when a nonzero represented gap would divide below binary64 range. +Repair `79ad03fae4364d6c364915a062eb0fc8615eaa43` remains inside `exact_translated_residual_standard_error` after the existing exact anchor-relative translation proof. If the translated sample has exactly two represented levels and either level occurs once, the sample standard error simplifies for any supported `n` to `|level_gap| / n`; the implementation evaluates that identity directly and retains fail-closed behavior when a nonzero represented gap would divide below binary64 range. Edge commit `7fe4117c5666af08adbfda5d32beb12648f579c6` fixes that underflow refusal in the public contract using a minimum-subnormal nonzero gap. The repair intentionally does not claim globally correctly rounded `n > 2` standard errors. Samples with three or more represented levels, two-level samples without a singleton, and cases that fail the exact translation admission continue through the existing bounded second-moment or fallback paths and require their own represented-input counterexample before any broader change. @@ -37,6 +37,7 @@ Applying arbitrary-precision arithmetic to every Validation metric was rejected - Public regression: `crates/validation_core/tests/bias_standard_error_singleton_level_rounding_contract.rs`. - RED: `4386d9ace83cd54aa129067ddc589b1a628147a2`. - Causal repair: `79ad03fae4364d6c364915a062eb0fc8615eaa43`. +- Edge coverage: `7fe4117c5666af08adbfda5d32beb12648f579c6`. - CHANGELOG: `017ad11ad974219a5a0e1cf91c1ecf55c44524c2`. - Landing vehicle: PR #488. From d3dcd918bece928ae4103dffda8e7dc654927da0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 10:59:08 +0900 Subject: [PATCH 354/576] test(validation): expose two-observation mean rounding drift --- ..._two_observation_mean_roundoff_contract.rs | 23 +++++++++++++++++++ 1 file changed, 23 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_two_observation_mean_roundoff_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_two_observation_mean_roundoff_contract.rs b/crates/validation_core/tests/bias_standard_error_two_observation_mean_roundoff_contract.rs new file mode 100644 index 000000000..767831c60 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_two_observation_mean_roundoff_contract.rs @@ -0,0 +1,23 @@ +use validation_core::bias_standard_error; + +#[test] +fn bias_standard_error_uses_exact_two_observation_identity_when_mean_rounds() { + let upper = 1.0_f64; + let lower = f64::from_bits(upper.to_bits() - 1); + let truth = [0.0, 0.0]; + let recovered = [upper, lower]; + + let standard_error = + bias_standard_error(&truth, &recovered).expect("represented standard error"); + assert_eq!(standard_error.to_bits(), 2.0_f64.powi(-54).to_bits()); + + let mirrored_recovered = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&truth, &mirrored_recovered) + .expect("mirrored represented standard error"); + assert_eq!( + mirrored_standard_error.to_bits(), + 2.0_f64.powi(-54).to_bits() + ); + + assert_eq!(bias_standard_error(&truth, &[upper, upper]), Ok(0.0)); +} From 02b0a178154ea0ae7da87289756897d7c2f361e3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 11:00:26 +0900 Subject: [PATCH 355/576] fix(validation): preserve exact two-observation standard error --- crates/validation_core/src/bias.rs | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index fffca24f2..9e3fc5090 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -269,11 +269,13 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Result Date: Sat, 5 Sep 2026 11:00:42 +0900 Subject: [PATCH 356/576] docs(changelog): record two-observation SE repair --- ...ation-bias-standard-error-two-observation-mean-roundoff.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-two-observation-mean-roundoff.md diff --git a/CHANGELOG.d/validation-bias-standard-error-two-observation-mean-roundoff.md b/CHANGELOG.d/validation-bias-standard-error-two-observation-mean-roundoff.md new file mode 100644 index 000000000..9b79adf8e --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-two-observation-mean-roundoff.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core::bias_standard_error` now evaluates the exact two-observation identity `SE(mean) = |r₁-r₂| / 2` for exact represented residuals as well as subtraction-roundoff cases. This prevents a rounded two-point residual mean from changing the dispersion geometry before the standard error is formed. +- The public contract covers the adjacent-binary64 pair `[1, next_down(1)]`, its sign mirror, and an equal-residual zero-uncertainty control. From 905addcce450f6c7ef307d4bcb694109f23d879e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 11:01:05 +0900 Subject: [PATCH 357/576] docs(research): trace two-observation SE mean rounding --- ...ard-error-two-observation-mean-roundoff.md | 52 +++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 docs/research/bias-standard-error-two-observation-mean-roundoff.md diff --git a/docs/research/bias-standard-error-two-observation-mean-roundoff.md b/docs/research/bias-standard-error-two-observation-mean-roundoff.md new file mode 100644 index 000000000..524760700 --- /dev/null +++ b/docs/research/bias-standard-error-two-observation-mean-roundoff.md @@ -0,0 +1,52 @@ +# Bias standard error: two-observation mean-rounding boundary + +## Problem + +`validation_core::bias_standard_error` already used the exact two-observation identity `SE(mean) = |r₁-r₂| / 2` when either `recovered - truth` subtraction discarded represented low-order mass. Exact pairwise residuals, however, still fell through to the generic path that first rounded the residual mean and then formed deviations around that rounded mean. + +That distinction is scientifically material even when every input subtraction is exact. Let + +- `truth = [0, 0]`, +- `recovered = [1, next_down(1)]`, +- `next_down(1) = 0x1.fffffffffffffp-1`. + +The represented residuals are therefore exactly `[1, 1 - 2^-53]`. Their exact mean is `1 - 2^-54`, which is the midpoint between the two adjacent binary64 residuals and rounds to `1` under round-to-nearest, ties-to-even. Centering on that rounded mean produces deviations `[0, -2^-53]`; the generic scaled second-moment path consequently returns approximately `2^-53 / sqrt(2)` (`0x3c96_a09e_667f_3bcd`). + +For two observations the sample standard error of the mean simplifies algebraically before any mean is needed: + +`SE(mean) = |r₁ - r₂| / 2 = 2^-54`, whose binary64 bits are `0x3c90_0000_0000_0000`. + +The sign-mirrored residuals have the same dispersion, and equal residuals remain exactly zero. + +## Decision + +For every two-observation sample, evaluate `SE(mean) = |r₁-r₂| / 2` before the generic rounded-mean path. + +- If either pairwise `recovered - truth` subtraction has a nonzero error-free low term, retain the predecessor expanded-input difference `[recovered₁, -truth₁, -recovered₂, truth₂]` so discarded subtraction mass is not lost. +- If both residual subtractions are exact, form the half-difference from the represented residuals `[r₁, -r₂]` through `deterministic_representable_sum_over_count(..., 2)`. This preserves the existing cancellation/overflow-safe denominator handling; in particular, opposite extreme finite residuals can still yield a representable standard error without materializing `r₁-r₂` as an overflowing intermediate. +- Take the absolute value only after the signed half-difference is formed. + +This is a bounded algebraic repair, not a claim that the general `n > 2` standard-error path is globally correctly rounded. + +## Rejected alternatives + +Keeping the generic rounded-mean path for exact residuals was rejected because the counterexample shows that exact input subtraction does not imply an exact residual mean, and a rounded two-point mean can change the dispersion geometry substantially rather than by a negligible reporting-only amount. + +Computing `(r₁-r₂).abs()/2` directly was rejected because the raw difference can overflow for opposite-sign extreme finite residuals even when the final halved result is representable. Reusing the repository's deterministic representable sum-over-count boundary keeps the scientific denominator and range policy intact. + +Introducing arbitrary-precision arithmetic for all standard errors was rejected as disproportionate to this proven two-observation identity and outside the current Validation Evidence runtime boundary. + +## Evidence and traceability + +- Public RED: `d3dcd918bece928ae4103dffda8e7dc654927da0`, `crates/validation_core/tests/bias_standard_error_two_observation_mean_roundoff_contract.rs`. +- Minimal causal repair: `02b0a178154ea0ae7da87289756897d7c2f361e3`, `crates/validation_core/src/bias.rs`. +- CHANGELOG: `5603b12d2aedb6dfcc0aa4203d57047a98aee789`. +- API: `validation_core::bias_standard_error`. + +IEEE 754 binary64 round-to-nearest, ties-to-even semantics determine the midpoint behavior in the counterexample. The performance-measure interpretation follows the existing TEPP Validation Evidence trace to known-truth simulation evaluation and Monte Carlo uncertainty rather than treating an LLM judgment as numerical authority. + +## References + +IEEE Computer Society. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE. https://doi.org/10.1109/IEEESTD.2019.8766229 + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 75ef343d7225946223970cd23e698c1d95ffccdb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 11:59:28 +0900 Subject: [PATCH 358/576] test(validation): expose two-level SE count rounding --- ...error_two_level_count_rounding_contract.rs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_two_level_count_rounding_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_two_level_count_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_two_level_count_rounding_contract.rs new file mode 100644 index 000000000..e0a7cbbaf --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_two_level_count_rounding_contract.rs @@ -0,0 +1,35 @@ +use validation_core::{ValidationError, bias_standard_error}; + +#[test] +fn bias_standard_error_preserves_exact_two_level_count_geometry() { + let repeated = f64::from_bits(0x3fef_ffff_ffff_ffff); + let recovered = [0.0, 0.0, repeated, repeated, repeated]; + + let standard_error = bias_standard_error(&[0.0; 5], &recovered) + .expect("represented-input standard error"); + // For an exactly translated two-level sample with counts 2 and 3, + // SE(mean) = |gap| * sqrt(2 * 3 / (5^2 * 4)). Counting the two levels + // preserves that exact sample geometry. Reconstructing the same quantity + // through rounded translated sums and squares lands one ULP high for + // gap = next_down(1.0). + assert_eq!(standard_error.to_bits(), 0x3fcf_5a7c_ecdb_6849); + + let permuted = [repeated, 0.0, repeated, 0.0, repeated]; + let permuted_standard_error = bias_standard_error(&[0.0; 5], &permuted) + .expect("permuted represented-input standard error"); + assert_eq!(permuted_standard_error.to_bits(), 0x3fcf_5a7c_ecdb_6849); + + let mirrored = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&[0.0; 5], &mirrored) + .expect("mirrored represented-input standard error"); + assert_eq!(mirrored_standard_error.to_bits(), 0x3fcf_5a7c_ecdb_6849); + + let minimum_subnormal = f64::from_bits(1); + assert_eq!( + bias_standard_error( + &[0.0; 5], + &[0.0, 0.0, minimum_subnormal, minimum_subnormal, minimum_subnormal], + ), + Err(ValidationError::InvalidInput) + ); +} From 89564e48817abce12561af72f7b202dcd0f442ca Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 12:01:35 +0900 Subject: [PATCH 359/576] fix(validation): preserve exact dyadic two-level SE --- crates/validation_core/src/bias.rs | 71 ++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 9e3fc5090..ce43f2e38 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -107,6 +107,36 @@ fn scaled_standard_error(values: &[f64], mean: f64) -> Result Option { + let sample_count = (first_count as u128).checked_add(second_count as u128)?; + let count_product = (first_count as u128).checked_mul(second_count as u128)?; + let target = sample_count + .checked_mul(sample_count)? + .checked_mul(sample_count.checked_sub(1)?)?; + + let mut divisor = 1_u128; + while divisor <= sample_count { + let scaled_product = count_product + .checked_mul(divisor.checked_mul(divisor)?)?; + if scaled_product == target { + return Some(divisor as f64); + } + if scaled_product > target { + return None; + } + divisor = divisor.checked_mul(2)?; + } + None +} + fn exact_translated_residual_standard_error( diffs: &[f64], roundoffs: &[f64], @@ -163,20 +193,32 @@ fn exact_translated_residual_standard_error( gap_count = 1; } } - if exactly_two_levels - && (zero_count == 1 || gap_count == 1) - && let Some(gap) = repeated_gap - { - // For an exactly translated two-level sample where either level occurs - // once, SE(mean) simplifies to |gap| / n. Evaluate that identity directly - // instead of projecting the exact gap through square -> second moment -> - // sqrt, which can move the final binary64 result by one ULP. - let sample_count = translated.len() as f64; - let standard_error = gap.abs() / sample_count; - if standard_error == 0.0 && gap != 0.0 { + if exactly_two_levels && let Some(gap) = repeated_gap { + let standard_error = if zero_count == 1 || gap_count == 1 { + // For an exactly translated two-level sample where either level + // occurs once, SE(mean) simplifies to |gap| / n. + gap.abs() / translated.len() as f64 + } else if let Some(divisor) = + exact_two_level_power_of_two_divisor(zero_count, gap_count) + { + // Some non-singleton count geometries also collapse to an exact + // dyadic scale. Preserve that algebra before rounded moment + // reconstruction can move the represented result by one ULP. + gap.abs() / divisor + } else { + 0.0 + }; + + if standard_error != 0.0 { + return Ok(Some(standard_error)); + } + if (zero_count == 1 + || gap_count == 1 + || exact_two_level_power_of_two_divisor(zero_count, gap_count).is_some()) + && gap != 0.0 + { return Err(ValidationError::InvalidInput); } - return Ok(Some(standard_error)); } // Keep the translated binary64 geometry on an exact dyadic scale. Using the @@ -284,8 +326,9 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 12:01:56 +0900 Subject: [PATCH 360/576] test(validation): bind dyadic two-level SE oracle --- ...error_two_level_count_rounding_contract.rs | 54 +++++++++++++------ 1 file changed, 38 insertions(+), 16 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_two_level_count_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_two_level_count_rounding_contract.rs index e0a7cbbaf..c3ea606cd 100644 --- a/crates/validation_core/tests/bias_standard_error_two_level_count_rounding_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_two_level_count_rounding_contract.rs @@ -1,34 +1,56 @@ use validation_core::{ValidationError, bias_standard_error}; #[test] -fn bias_standard_error_preserves_exact_two_level_count_geometry() { +fn bias_standard_error_preserves_exact_dyadic_two_level_count_geometry() { let repeated = f64::from_bits(0x3fef_ffff_ffff_ffff); - let recovered = [0.0, 0.0, repeated, repeated, repeated]; + let recovered = [ + 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, repeated, repeated, repeated, repeated, repeated, + repeated, repeated, repeated, repeated, repeated, + ]; - let standard_error = bias_standard_error(&[0.0; 5], &recovered) + let standard_error = bias_standard_error(&[0.0; 16], &recovered) .expect("represented-input standard error"); - // For an exactly translated two-level sample with counts 2 and 3, - // SE(mean) = |gap| * sqrt(2 * 3 / (5^2 * 4)). Counting the two levels - // preserves that exact sample geometry. Reconstructing the same quantity - // through rounded translated sums and squares lands one ULP high for - // gap = next_down(1.0). - assert_eq!(standard_error.to_bits(), 0x3fcf_5a7c_ecdb_6849); + // With six observations at one exact residual level and ten at the other, + // m(n-m)/(n-1) = 6*10/15 = 4. Therefore SE(mean) is exactly |gap|/8. + // Reconstructing that dyadic identity through translated sums, squares and + // sqrt rounds next_down(1.0) up to 0.125 instead of preserving gap/8. + assert_eq!(standard_error.to_bits(), 0x3fbf_ffff_ffff_ffff); - let permuted = [repeated, 0.0, repeated, 0.0, repeated]; - let permuted_standard_error = bias_standard_error(&[0.0; 5], &permuted) + let permuted = [ + repeated, 0.0, repeated, 0.0, repeated, 0.0, repeated, 0.0, repeated, 0.0, + repeated, 0.0, repeated, repeated, repeated, repeated, + ]; + let permuted_standard_error = bias_standard_error(&[0.0; 16], &permuted) .expect("permuted represented-input standard error"); - assert_eq!(permuted_standard_error.to_bits(), 0x3fcf_5a7c_ecdb_6849); + assert_eq!(permuted_standard_error.to_bits(), 0x3fbf_ffff_ffff_ffff); let mirrored = recovered.map(|value| -value); - let mirrored_standard_error = bias_standard_error(&[0.0; 5], &mirrored) + let mirrored_standard_error = bias_standard_error(&[0.0; 16], &mirrored) .expect("mirrored represented-input standard error"); - assert_eq!(mirrored_standard_error.to_bits(), 0x3fcf_5a7c_ecdb_6849); + assert_eq!(mirrored_standard_error.to_bits(), 0x3fbf_ffff_ffff_ffff); let minimum_subnormal = f64::from_bits(1); assert_eq!( bias_standard_error( - &[0.0; 5], - &[0.0, 0.0, minimum_subnormal, minimum_subnormal, minimum_subnormal], + &[0.0; 16], + &[ + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + 0.0, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + ], ), Err(ValidationError::InvalidInput) ); From 3bc43da21784d3bf2f506c2ffdaa66c90a76d85d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 12:02:45 +0900 Subject: [PATCH 361/576] test(validation): reproduce dyadic two-level SE rounding --- crates/validation_core/src/bias.rs | 71 ++++++------------------------ 1 file changed, 14 insertions(+), 57 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index ce43f2e38..9e3fc5090 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -107,36 +107,6 @@ fn scaled_standard_error(values: &[f64], mean: f64) -> Result Option { - let sample_count = (first_count as u128).checked_add(second_count as u128)?; - let count_product = (first_count as u128).checked_mul(second_count as u128)?; - let target = sample_count - .checked_mul(sample_count)? - .checked_mul(sample_count.checked_sub(1)?)?; - - let mut divisor = 1_u128; - while divisor <= sample_count { - let scaled_product = count_product - .checked_mul(divisor.checked_mul(divisor)?)?; - if scaled_product == target { - return Some(divisor as f64); - } - if scaled_product > target { - return None; - } - divisor = divisor.checked_mul(2)?; - } - None -} - fn exact_translated_residual_standard_error( diffs: &[f64], roundoffs: &[f64], @@ -193,32 +163,20 @@ fn exact_translated_residual_standard_error( gap_count = 1; } } - if exactly_two_levels && let Some(gap) = repeated_gap { - let standard_error = if zero_count == 1 || gap_count == 1 { - // For an exactly translated two-level sample where either level - // occurs once, SE(mean) simplifies to |gap| / n. - gap.abs() / translated.len() as f64 - } else if let Some(divisor) = - exact_two_level_power_of_two_divisor(zero_count, gap_count) - { - // Some non-singleton count geometries also collapse to an exact - // dyadic scale. Preserve that algebra before rounded moment - // reconstruction can move the represented result by one ULP. - gap.abs() / divisor - } else { - 0.0 - }; - - if standard_error != 0.0 { - return Ok(Some(standard_error)); - } - if (zero_count == 1 - || gap_count == 1 - || exact_two_level_power_of_two_divisor(zero_count, gap_count).is_some()) - && gap != 0.0 - { + if exactly_two_levels + && (zero_count == 1 || gap_count == 1) + && let Some(gap) = repeated_gap + { + // For an exactly translated two-level sample where either level occurs + // once, SE(mean) simplifies to |gap| / n. Evaluate that identity directly + // instead of projecting the exact gap through square -> second moment -> + // sqrt, which can move the final binary64 result by one ULP. + let sample_count = translated.len() as f64; + let standard_error = gap.abs() / sample_count; + if standard_error == 0.0 && gap != 0.0 { return Err(ValidationError::InvalidInput); } + return Ok(Some(standard_error)); } // Keep the translated binary64 geometry on an exact dyadic scale. Using the @@ -326,9 +284,8 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 12:03:01 +0900 Subject: [PATCH 362/576] fix(validation): preserve dyadic two-level SE geometry --- crates/validation_core/src/bias.rs | 71 ++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 14 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 9e3fc5090..ce43f2e38 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -107,6 +107,36 @@ fn scaled_standard_error(values: &[f64], mean: f64) -> Result Option { + let sample_count = (first_count as u128).checked_add(second_count as u128)?; + let count_product = (first_count as u128).checked_mul(second_count as u128)?; + let target = sample_count + .checked_mul(sample_count)? + .checked_mul(sample_count.checked_sub(1)?)?; + + let mut divisor = 1_u128; + while divisor <= sample_count { + let scaled_product = count_product + .checked_mul(divisor.checked_mul(divisor)?)?; + if scaled_product == target { + return Some(divisor as f64); + } + if scaled_product > target { + return None; + } + divisor = divisor.checked_mul(2)?; + } + None +} + fn exact_translated_residual_standard_error( diffs: &[f64], roundoffs: &[f64], @@ -163,20 +193,32 @@ fn exact_translated_residual_standard_error( gap_count = 1; } } - if exactly_two_levels - && (zero_count == 1 || gap_count == 1) - && let Some(gap) = repeated_gap - { - // For an exactly translated two-level sample where either level occurs - // once, SE(mean) simplifies to |gap| / n. Evaluate that identity directly - // instead of projecting the exact gap through square -> second moment -> - // sqrt, which can move the final binary64 result by one ULP. - let sample_count = translated.len() as f64; - let standard_error = gap.abs() / sample_count; - if standard_error == 0.0 && gap != 0.0 { + if exactly_two_levels && let Some(gap) = repeated_gap { + let standard_error = if zero_count == 1 || gap_count == 1 { + // For an exactly translated two-level sample where either level + // occurs once, SE(mean) simplifies to |gap| / n. + gap.abs() / translated.len() as f64 + } else if let Some(divisor) = + exact_two_level_power_of_two_divisor(zero_count, gap_count) + { + // Some non-singleton count geometries also collapse to an exact + // dyadic scale. Preserve that algebra before rounded moment + // reconstruction can move the represented result by one ULP. + gap.abs() / divisor + } else { + 0.0 + }; + + if standard_error != 0.0 { + return Ok(Some(standard_error)); + } + if (zero_count == 1 + || gap_count == 1 + || exact_two_level_power_of_two_divisor(zero_count, gap_count).is_some()) + && gap != 0.0 + { return Err(ValidationError::InvalidInput); } - return Ok(Some(standard_error)); } // Keep the translated binary64 geometry on an exact dyadic scale. Using the @@ -284,8 +326,9 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 12:03:32 +0900 Subject: [PATCH 363/576] docs(changelog): record dyadic two-level SE repair --- .../validation-bias-standard-error-dyadic-two-level-count.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-dyadic-two-level-count.md diff --git a/CHANGELOG.d/validation-bias-standard-error-dyadic-two-level-count.md b/CHANGELOG.d/validation-bias-standard-error-dyadic-two-level-count.md new file mode 100644 index 000000000..5074948c3 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-dyadic-two-level-count.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core::bias_standard_error` now preserves an exactly translated two-level sample's count geometry when its standard-error factor reduces to a reciprocal power of two. For the 6/10 split of 16 observations, `SE(mean)` is exactly `|level_gap| / 8`; the represented `next_down(1.0)` gap therefore remains `0x3fbf_ffff_ffff_ffff` instead of being rounded up to `0.125` by the generic sum/square/square-root path. +- The dyadic shortcut is admitted only after exact residual translation and exact integer count verification. Other non-singleton two-level samples retain the existing translated second-moment path, and a mathematically nonzero dyadic result that falls below binary64 range still fails closed. From 9d7bb5c207e19f1d22305566b92ea92139b2a3f0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 12:03:54 +0900 Subject: [PATCH 364/576] docs(research): trace dyadic two-level SE rounding --- ...d-error-dyadic-two-level-count-rounding.md | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 docs/research/bias-standard-error-dyadic-two-level-count-rounding.md diff --git a/docs/research/bias-standard-error-dyadic-two-level-count-rounding.md b/docs/research/bias-standard-error-dyadic-two-level-count-rounding.md new file mode 100644 index 000000000..b4c498ead --- /dev/null +++ b/docs/research/bias-standard-error-dyadic-two-level-count-rounding.md @@ -0,0 +1,63 @@ +# Bias standard error: dyadic two-level count geometry + +## Problem + +`validation_core::bias_standard_error` already preserves exact anchor-relative residual translations before evaluating larger-sample dispersion. The remaining two-level path still reconstructed dispersion from translated sums and squared values unless one residual level occurred exactly once. That reconstruction can round an otherwise exact count identity before the final square root. + +For an exactly translated sample with `m` observations at residual level `0`, `n-m` observations at residual level `g`, and `n > 1`, the represented-input sample standard error of the mean is + +`SE(mean) = |g| * sqrt(m(n-m) / (n^2(n-1)))`. + +The identity follows directly from the two-level sample mean and centered squared-deviation sum; no estimated latent quantity or LLM judgment is involved. + +## RED + +Public RED: `3bc43da21784d3bf2f506c2ffdaa66c90a76d85d` with `crates/validation_core/tests/bias_standard_error_two_level_count_rounding_contract.rs`. + +Let `g = next_down(1.0) = 0x1.fffffffffffffp-1`, `n = 16`, and use six residuals at `0` plus ten residuals at `g`. Then + +`m(n-m)/(n-1) = 6*10/15 = 4`, so + +`SE(mean) = |g| * sqrt(4 / 16^2) = |g| / 8`. + +Because division by eight is an exact binary exponent shift for this normal represented gap, the required binary64 result is `0x1.fffffffffffffp-4`, bits `0x3fbf_ffff_ffff_ffff`. The predecessor translated sum/square/square-root path returned `0x1.0000000000000p-3`, bits `0x3fc0_0000_0000_0000`, moving the standard error upward by one ULP. The contract also fixes permutation invariance, sign-mirrored dispersion, and fail-closed behavior when the same nonzero dyadic result would underflow from a minimum-subnormal gap. + +The hosted workflows created for the RED commit were cancelled after the branch advanced; they are not represented as completed RED execution evidence. The mathematical oracle and executable public contract remain the reproducer. + +## Causal repair + +Repair: `77ba10026d23252314f04d95a67ce1cfeb5e54a0`. + +After the existing exact translated-residual admission proves two represented residual levels, TEPP now checks the level counts with exact integer arithmetic. A direct path is used only when the count-only factor satisfies + +`m(n-m) * d^2 = n^2(n-1)` + +for a power-of-two divisor `d`. In that bounded case the requested standard error is exactly `|g| / d`, so the code applies the dyadic scaling before any rounded moment reconstruction. The existing singleton-level identity remains unchanged. Non-singleton two-level samples whose count geometry does not prove a reciprocal power-of-two factor continue through the prior exact-translated second-moment path. + +The repair uses checked `u128` count arithmetic. Failure to prove the dyadic relation is a fallback condition, not permission to approximate the count identity. A nonzero exact dyadic result that becomes binary64 zero remains `ValidationError::InvalidInput` under the existing no-false-perfect-recovery policy. + +## Alternatives rejected + +A general two-level closed form using a rounded floating-point count ratio was rejected for this change. It removes translated-moment cancellation in some cases but introduces a different rounding projection in others and therefore does not establish a stronger represented-input contract without a separate correctly-rounded square-root/product proof. + +A payload-specific `n = 16, m = 6` branch was rejected because sample-size constants are not a scientific abstraction. The accepted predicate is the algebraic dyadic count relation itself. + +Arbitrary-precision runtime arithmetic was rejected because this bounded binary64 identity is exactly decidable from integer counts plus power-of-two scaling. Adding a second numerical runtime for this case would increase production complexity without changing the estimand. + +## Scope and risk + +This repair does not claim globally correctly rounded `bias_standard_error` for every `n > 2` sample, nor does it redefine reusable static psychometric estimation owned by `fast-mlsirm`. It changes only TEPP Validation Evidence arithmetic after exact residual translation has already been established. Non-dyadic two-level count factors and general multi-level residual samples retain their prior bounded path and therefore remain candidates for separately demonstrated RED findings rather than implicit expansion of GAP-102. + +## Traceability + +- Public contract: `crates/validation_core/tests/bias_standard_error_two_level_count_rounding_contract.rs`. +- Production module/API: `crates/validation_core/src/bias.rs` → `bias_standard_error` and `exact_translated_residual_standard_error`. +- RED: `3bc43da21784d3bf2f506c2ffdaa66c90a76d85d`. +- Repair: `77ba10026d23252314f04d95a67ce1cfeb5e54a0`. +- CHANGELOG: `CHANGELOG.d/validation-bias-standard-error-dyadic-two-level-count.md` at `f165914eaceb17780370ca4f2e446c22efcf15ea`. + +## References + +IEEE Computer Society. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From d793f7f9ada68d5976effa6539182ba7037bf8d0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 13:03:14 +0900 Subject: [PATCH 365/576] test(validation): expose exact integer-divisor two-level SE rounding --- ...level_integer_divisor_rounding_contract.rs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_two_level_integer_divisor_rounding_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_two_level_integer_divisor_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_two_level_integer_divisor_rounding_contract.rs new file mode 100644 index 000000000..89c16ef7c --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_two_level_integer_divisor_rounding_contract.rs @@ -0,0 +1,48 @@ +use validation_core::{ValidationError, bias_standard_error}; + +#[test] +fn bias_standard_error_preserves_exact_integer_divisor_two_level_geometry() { + let repeated = f64::from_bits(0x3fef_ffff_ffff_fffd); + let recovered = [ + 0.0, 0.0, 0.0, repeated, repeated, repeated, repeated, repeated, repeated, + ]; + + let standard_error = bias_standard_error(&[0.0; 9], &recovered) + .expect("represented-input standard error"); + // With three observations at one exact residual level and six at the other, + // m(n-m)/(n^2(n-1)) = 3*6/(9^2*8) = 1/36. Therefore SE(mean) is exactly + // |gap|/6. The predecessor translated sum/square/sqrt path returns the + // adjacent upper binary64 value for this represented gap. + assert_eq!(standard_error.to_bits(), 0x3fc5_5555_5555_5553); + + let permuted = [ + repeated, 0.0, repeated, 0.0, repeated, 0.0, repeated, repeated, repeated, + ]; + let permuted_standard_error = bias_standard_error(&[0.0; 9], &permuted) + .expect("permuted represented-input standard error"); + assert_eq!(permuted_standard_error.to_bits(), 0x3fc5_5555_5555_5553); + + let mirrored = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&[0.0; 9], &mirrored) + .expect("mirrored represented-input standard error"); + assert_eq!(mirrored_standard_error.to_bits(), 0x3fc5_5555_5555_5553); + + let minimum_subnormal = f64::from_bits(1); + assert_eq!( + bias_standard_error( + &[0.0; 9], + &[ + 0.0, + 0.0, + 0.0, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + ], + ), + Err(ValidationError::InvalidInput) + ); +} From 0a4bfcd52defe8912afa8269576395803d451bb3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 13:04:46 +0900 Subject: [PATCH 366/576] fix(validation): preserve exact integer-divisor two-level SE --- crates/validation_core/src/bias.rs | 73 +++++++++++++++--------------- 1 file changed, 36 insertions(+), 37 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index ce43f2e38..4564d2442 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -107,34 +107,37 @@ fn scaled_standard_error(values: &[f64], mean: f64) -> Result Option { +/// exactly `1 / divisor^2` for an integer `divisor` that binary64 represents +/// exactly, one final division of the represented gap preserves the algebraic +/// identity without reconstructing it through rounded sums, squares and sqrt. +fn exact_two_level_integer_divisor(first_count: usize, second_count: usize) -> Option { let sample_count = (first_count as u128).checked_add(second_count as u128)?; let count_product = (first_count as u128).checked_mul(second_count as u128)?; + if count_product == 0 { + return None; + } let target = sample_count .checked_mul(sample_count)? .checked_mul(sample_count.checked_sub(1)?)?; + if target % count_product != 0 { + return None; + } - let mut divisor = 1_u128; - while divisor <= sample_count { - let scaled_product = count_product - .checked_mul(divisor.checked_mul(divisor)?)?; - if scaled_product == target { - return Some(divisor as f64); - } - if scaled_product > target { - return None; - } - divisor = divisor.checked_mul(2)?; + let squared_divisor = target / count_product; + let divisor = squared_divisor.isqrt(); + if divisor.checked_mul(divisor)? != squared_divisor { + return None; } - None + + let binary64_divisor = divisor as f64; + if binary64_divisor as u128 != divisor { + return None; + } + Some(binary64_divisor) } fn exact_translated_residual_standard_error( @@ -194,16 +197,15 @@ fn exact_translated_residual_standard_error( } } if exactly_two_levels && let Some(gap) = repeated_gap { + let integer_divisor = exact_two_level_integer_divisor(zero_count, gap_count); let standard_error = if zero_count == 1 || gap_count == 1 { // For an exactly translated two-level sample where either level // occurs once, SE(mean) simplifies to |gap| / n. gap.abs() / translated.len() as f64 - } else if let Some(divisor) = - exact_two_level_power_of_two_divisor(zero_count, gap_count) - { + } else if let Some(divisor) = integer_divisor { // Some non-singleton count geometries also collapse to an exact - // dyadic scale. Preserve that algebra before rounded moment - // reconstruction can move the represented result by one ULP. + // reciprocal-integer scale. Preserve that algebra before rounded + // moment reconstruction can move the represented result by one ULP. gap.abs() / divisor } else { 0.0 @@ -212,11 +214,7 @@ fn exact_translated_residual_standard_error( if standard_error != 0.0 { return Ok(Some(standard_error)); } - if (zero_count == 1 - || gap_count == 1 - || exact_two_level_power_of_two_divisor(zero_count, gap_count).is_some()) - && gap != 0.0 - { + if (zero_count == 1 || gap_count == 1 || integer_divisor.is_some()) && gap != 0.0 { return Err(ValidationError::InvalidInput); } } @@ -327,15 +325,16 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 13:05:19 +0900 Subject: [PATCH 367/576] docs(changelog): record exact integer-divisor two-level SE repair --- ...ion-bias-standard-error-integer-divisor-two-level-count.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-integer-divisor-two-level-count.md diff --git a/CHANGELOG.d/validation-bias-standard-error-integer-divisor-two-level-count.md b/CHANGELOG.d/validation-bias-standard-error-integer-divisor-two-level-count.md new file mode 100644 index 000000000..1ed89b020 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-integer-divisor-two-level-count.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core::bias_standard_error` now preserves exactly translated two-level sample geometries whose count factor reduces to the reciprocal square of an exactly representable integer, not only a reciprocal power of two. For a 3/6 split of nine observations, `SE(mean)^2 = gap^2 / 36`, so `SE(mean)` is exactly `|gap| / 6`; the represented gap `0x1.ffffffffffffdp-1` now rounds once to `0x3fc5_5555_5555_5553` instead of being moved one ULP upward by the generic sum/square/square-root reconstruction. +- The shortcut remains gated by exact residual translation and checked integer count algebra. Non-square count factors retain the existing translated second-moment path, and a mathematically nonzero reciprocal-integer result that falls below binary64 range still fails closed. From 11b8fd5417398c778cf9e7b83473fe984abf31c5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 13:05:48 +0900 Subject: [PATCH 368/576] docs(research): trace integer-divisor two-level SE rounding --- ...nteger-divisor-two-level-count-rounding.md | 61 +++++++++++++++++++ 1 file changed, 61 insertions(+) create mode 100644 docs/research/bias-standard-error-integer-divisor-two-level-count-rounding.md diff --git a/docs/research/bias-standard-error-integer-divisor-two-level-count-rounding.md b/docs/research/bias-standard-error-integer-divisor-two-level-count-rounding.md new file mode 100644 index 000000000..ee979e3a8 --- /dev/null +++ b/docs/research/bias-standard-error-integer-divisor-two-level-count-rounding.md @@ -0,0 +1,61 @@ +# Exact integer-divisor two-level bias standard error + +## Problem + +`validation_core::bias_standard_error` already recognizes exactly translated two-level samples before using the general translated second-moment path. GAP-102 preserved the subset whose count factor is a reciprocal power of two. Fresh review found that the scientific identity is broader: some non-singleton two-level count geometries reduce to the reciprocal square of an integer that is not a power of two. + +For two residual levels separated by represented gap `g`, with counts `m` and `n-m`, + +`SE(mean)^2 = g^2 * m(n-m) / (n^2(n-1))`. + +For `n=9`, `m=3`, the count factor is + +`3*6 / (9^2*8) = 18/648 = 1/36`, + +so the represented-input target is exactly `SE(mean) = |g|/6` before the final binary64 rounding. + +The public RED uses `g = 0x1.ffffffffffffdp-1` (three representable steps below `1.0`) with three residuals at `0` and six at `g`. The exact quotient `g/6` rounds to bits `0x3fc5_5555_5555_5553`. The predecessor translated sum/square/FMA/square-root reconstruction returned the adjacent upper value `0x3fc5_5555_5555_5554`. + +## Constraints + +- The repair must remain inside TEPP Validation Evidence performance-measure arithmetic; it does not create a reusable static psychometric estimator. +- Admission must still prove exact anchor-relative residual translation before any two-level shortcut is used. +- Count algebra must be exact and overflow checked. +- The fix must not claim correctly rounded standard errors for arbitrary `n>2` or arbitrary multi-level residual distributions. +- A mathematically nonzero result that cannot be represented in binary64 must fail closed rather than become false perfect recovery. + +## Decision + +RED `d793f7f9ada68d5976effa6539182ba7037bf8d0` adds the nine-observation 3/6 contract, permutation invariance, sign symmetry, and minimum-subnormal false-zero refusal. + +Causal repair `0a4bfcd52defe8912afa8269576395803d451bb3` replaces the power-of-two-only count predicate with an exact integer-divisor predicate. Using checked `u128` arithmetic, TEPP forms `n^2(n-1)`, verifies divisibility by `m(n-m)`, takes the integer square root of the quotient, verifies the square exactly, and admits the shortcut only when the resulting integer divisor is exactly representable as binary64. The final metric is then one division `|g|/divisor`. + +This preserves GAP-102 (`6/10` of `16` gives divisor `8`) and additionally admits exact cases such as `3/6` of `9` giving divisor `6`. Count factors that are not exact reciprocal integer squares continue through the bounded translated second-moment path. + +## Rejected alternatives + +A payload-specific `n=9, m=3` branch was rejected because the scientific condition is the exact count identity, not this fixture. Keeping the power-of-two restriction was rejected because it makes an implementation convenience narrower than the proved estimator algebra. Replacing all two-level samples with a floating count-ratio formula was rejected because it would broaden the changed rounding surface to cases whose square root is irrational or whose count ratio is itself rounded. Arbitrary-precision production arithmetic was rejected because this defect has a narrower exact integer repair and does not justify a new runtime dependency or owner boundary. + +## Risk and follow-up + +This change does not prove global correct rounding of `bias_standard_error`. Two-level factors that do not reduce to an exact reciprocal integer square, and general multi-level samples, retain the current translated moment path and remain candidates for separately reproduced scientific findings. Hosted exact-head Rust, coverage, security, documentation, and independent review evidence remain required after the source mutation. + +## Traceability + +- RED: `d793f7f9ada68d5976effa6539182ba7037bf8d0` +- causal source repair: `0a4bfcd52defe8912afa8269576395803d451bb3` +- release-note fragment: `9651dfd123b71b14c58e34dfca4800a92e298a99` +- module/API: `crates/validation_core/src/bias.rs` → `bias_standard_error` +- executable contract: `crates/validation_core/tests/bias_standard_error_two_level_integer_divisor_rounding_contract.rs` + +IEEE 754-2019 and ISO/IEC 60559:2020 define the binary floating-point arithmetic model used by this deterministic `f64` reference, including division and square root. The relevant engineering point here is not that every composed expression is globally correctly rounded, but that an exact algebraic `g/6` target should not be unnecessarily reconstructed through additional rounded sums, products, and square root operations. + +Morris, White, and Crowther (2019) treat bias and related performance measures as explicit estimand-linked quantities in simulation studies and recommend defining performance measures unambiguously and reporting Monte Carlo uncertainty. TEPP therefore treats a one-ULP change caused by avoidable arithmetic reconstruction as a Validation Evidence defect when the represented-input target is algebraically known. + +## References + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization. (2020). *ISO/IEC 60559:2020 Information technology—Microprocessor systems—Floating-point arithmetic*. + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 6dc8116c89fa44a7ff1d58a8f9a51c876993d33f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 14:00:14 +0900 Subject: [PATCH 369/576] test(validation): expose rational-square two-level SE rounding --- ..._level_rational_scale_rounding_contract.rs | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_two_level_rational_scale_rounding_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_two_level_rational_scale_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_two_level_rational_scale_rounding_contract.rs new file mode 100644 index 000000000..84f1f061c --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_two_level_rational_scale_rounding_contract.rs @@ -0,0 +1,50 @@ +use validation_core::{ValidationError, bias_standard_error}; + +#[test] +fn bias_standard_error_preserves_exact_rational_square_two_level_geometry() { + let repeated = f64::from_bits(0x3fef_ffff_ffff_fffe); + let recovered = [ + 0.0, 0.0, repeated, repeated, repeated, repeated, repeated, repeated, repeated, repeated, + ]; + + let standard_error = bias_standard_error(&[0.0; 10], &recovered) + .expect("represented-input standard error"); + // With two observations at one exact residual level and eight at the other, + // m(n-m)/(n^2(n-1)) = 2*8/(10^2*9) = 4/225. Therefore SE(mean) is exactly + // 2*|gap|/15. GAP-103 admits only reciprocal-integer-square count factors, + // so its translated sum/square/sqrt fallback returns the adjacent upper + // binary64 value for this represented gap. + assert_eq!(standard_error.to_bits(), 0x3fc1_1111_1111_1110); + + let permuted = [ + repeated, 0.0, repeated, repeated, repeated, 0.0, repeated, repeated, repeated, repeated, + ]; + let permuted_standard_error = bias_standard_error(&[0.0; 10], &permuted) + .expect("permuted represented-input standard error"); + assert_eq!(permuted_standard_error.to_bits(), 0x3fc1_1111_1111_1110); + + let mirrored = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&[0.0; 10], &mirrored) + .expect("mirrored represented-input standard error"); + assert_eq!(mirrored_standard_error.to_bits(), 0x3fc1_1111_1111_1110); + + let minimum_subnormal = f64::from_bits(1); + assert_eq!( + bias_standard_error( + &[0.0; 10], + &[ + 0.0, + 0.0, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + minimum_subnormal, + ], + ), + Err(ValidationError::InvalidInput) + ); +} From 8f2803c874568877e20e4c0f267ec5ce613daa3d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 14:01:35 +0900 Subject: [PATCH 370/576] fix(validation): preserve rational-square two-level SE scale --- crates/validation_core/src/bias.rs | 85 ++++++++++++++++++------------ 1 file changed, 50 insertions(+), 35 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 4564d2442..0887f8cc8 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -107,14 +107,27 @@ fn scaled_standard_error(values: &[f64], mean: f64) -> Result u128 { + while right != 0 { + let remainder = left % right; + left = right; + right = remainder; + } + left +} + +/// Return the exact rational scale implied by two-level sample counts. /// /// For two exact residual levels with counts `m` and `n - m`, -/// `SE(mean)^2 = gap^2 * m(n-m) / (n^2(n-1))`. If that count-only factor is -/// exactly `1 / divisor^2` for an integer `divisor` that binary64 represents -/// exactly, one final division of the represented gap preserves the algebraic -/// identity without reconstructing it through rounded sums, squares and sqrt. -fn exact_two_level_integer_divisor(first_count: usize, second_count: usize) -> Option { +/// `SE(mean)^2 = gap^2 * m(n-m) / (n^2(n-1))`. If the reduced count-only +/// factor is exactly the square of a rational `numerator / denominator` whose +/// roots fit the platform's exact integer count representation, callers can +/// preserve that algebraic scale without reconstructing it through rounded +/// sums, squares and sqrt. +fn exact_two_level_rational_scale( + first_count: usize, + second_count: usize, +) -> Option<(usize, usize)> { let sample_count = (first_count as u128).checked_add(second_count as u128)?; let count_product = (first_count as u128).checked_mul(second_count as u128)?; if count_product == 0 { @@ -123,21 +136,22 @@ fn exact_two_level_integer_divisor(first_count: usize, second_count: usize) -> O let target = sample_count .checked_mul(sample_count)? .checked_mul(sample_count.checked_sub(1)?)?; - if target % count_product != 0 { - return None; - } - let squared_divisor = target / count_product; - let divisor = squared_divisor.isqrt(); - if divisor.checked_mul(divisor)? != squared_divisor { - return None; - } - - let binary64_divisor = divisor as f64; - if binary64_divisor as u128 != divisor { + let divisor = greatest_common_divisor(count_product, target); + let reduced_numerator = count_product / divisor; + let reduced_denominator = target / divisor; + let numerator = reduced_numerator.isqrt(); + let denominator = reduced_denominator.isqrt(); + if numerator.checked_mul(numerator)? != reduced_numerator + || denominator.checked_mul(denominator)? != reduced_denominator + || numerator == 0 + || denominator == 0 + || numerator > usize::MAX as u128 + || denominator > usize::MAX as u128 + { return None; } - Some(binary64_divisor) + Some((numerator as usize, denominator as usize)) } fn exact_translated_residual_standard_error( @@ -197,16 +211,18 @@ fn exact_translated_residual_standard_error( } } if exactly_two_levels && let Some(gap) = repeated_gap { - let integer_divisor = exact_two_level_integer_divisor(zero_count, gap_count); + let rational_scale = exact_two_level_rational_scale(zero_count, gap_count); let standard_error = if zero_count == 1 || gap_count == 1 { // For an exactly translated two-level sample where either level // occurs once, SE(mean) simplifies to |gap| / n. gap.abs() / translated.len() as f64 - } else if let Some(divisor) = integer_divisor { - // Some non-singleton count geometries also collapse to an exact - // reciprocal-integer scale. Preserve that algebra before rounded - // moment reconstruction can move the represented result by one ULP. - gap.abs() / divisor + } else if let Some((numerator, denominator)) = rational_scale { + // Some non-singleton count geometries collapse to an exact rational + // scale. Reuse the represented-sum division primitive so factors + // such as 2/15 are rounded once without an avoidable square/root + // reconstruction or a multiply-first overflow. + let scaled_gap = vec![gap.abs(); numerator]; + deterministic_representable_sum_over_count(&scaled_gap, denominator)? } else { 0.0 }; @@ -214,7 +230,7 @@ fn exact_translated_residual_standard_error( if standard_error != 0.0 { return Ok(Some(standard_error)); } - if (zero_count == 1 || gap_count == 1 || integer_divisor.is_some()) && gap != 0.0 { + if (zero_count == 1 || gap_count == 1 || rational_scale.is_some()) && gap != 0.0 { return Err(ValidationError::InvalidInput); } } @@ -325,16 +341,15 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 14:02:15 +0900 Subject: [PATCH 371/576] docs(changelog): record rational-square two-level SE repair --- ...tion-bias-standard-error-rational-square-two-level-count.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-rational-square-two-level-count.md diff --git a/CHANGELOG.d/validation-bias-standard-error-rational-square-two-level-count.md b/CHANGELOG.d/validation-bias-standard-error-rational-square-two-level-count.md new file mode 100644 index 000000000..44ce18be2 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-rational-square-two-level-count.md @@ -0,0 +1,3 @@ +### Fixed + +- Preserve exact translated two-level bias-standard-error count geometry when the reduced count factor is any exact rational square, not only a reciprocal integer square. A 2/8 split of 10 exact represented residuals now evaluates the algebraic scale `2*|gap|/15` through the deterministic represented-sum divisor path instead of re-rounding it through sum/square/FMA/square-root reconstruction; mathematically nonzero results that cannot be represented still fail closed. From c5cbf6f08b5892ab21b687e99446239915947a65 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 14:02:37 +0900 Subject: [PATCH 372/576] docs(research): trace rational-square two-level SE repair --- ...ational-square-two-level-count-rounding.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 docs/research/bias-standard-error-rational-square-two-level-count-rounding.md diff --git a/docs/research/bias-standard-error-rational-square-two-level-count-rounding.md b/docs/research/bias-standard-error-rational-square-two-level-count-rounding.md new file mode 100644 index 000000000..307cd376d --- /dev/null +++ b/docs/research/bias-standard-error-rational-square-two-level-count-rounding.md @@ -0,0 +1,60 @@ +# Exact rational-square two-level bias standard error + +## Problem + +`validation_core::bias_standard_error` already proves exact anchor-relative translation before recognizing two-level residual samples. GAP-103 widened the direct count path from reciprocal powers of two to reciprocal integer squares, but that predicate is still narrower than the estimator algebra: the reduced two-level count factor can be the square of a non-unit rational. + +For two represented residual levels separated by `g`, with counts `m` and `n-m`, + +`SE(mean)^2 = g^2 * m(n-m) / (n^2(n-1))`. + +For `n=10`, `m=2`, the count factor is + +`2*8 / (10^2*9) = 16/900 = 4/225 = (2/15)^2`, + +so the represented-input target is `SE(mean) = 2*|g|/15` before final binary64 rounding. The public RED uses `g = 0x1.ffffffffffffep-1`, with two residuals at `0` and eight at `g`. The exact represented rational target rounds to bits `0x3fc1_1111_1111_1110`; the GAP-103 predecessor rejects the non-unit numerator, falls through to translated sum/square/FMA/square-root reconstruction, and returns adjacent upper bits `0x3fc1_1111_1111_1111`. + +## Constraints + +- The repair remains inside TEPP Validation Evidence performance-measure arithmetic and does not create a reusable static psychometric estimator. +- Exact anchor-relative translation remains a prerequisite; no shortcut is admitted from rounded residual labels alone. +- Count algebra is checked in `u128`; overflow or a non-square reduced factor falls back to the bounded translated second-moment path. +- The repair must preserve GAP-102 and GAP-103 reciprocal-integer cases. +- A mathematically nonzero result below binary64 support fails closed rather than becoming false zero. +- The change does not claim globally correctly rounded standard errors for arbitrary `n>2`, multi-level residuals, or irrational count factors. + +## Decision + +RED `6dc8116c89fa44a7ff1d58a8f9a51c876993d33f` adds the 10-observation 2/8 contract, permutation invariance, sign symmetry, and minimum-subnormal false-zero refusal. + +Causal repair `8f2803c874568877e20e4c0f267ec5ce613daa3d` reduces `m(n-m) / (n^2(n-1))` by the exact integer greatest common divisor, verifies that both reduced numerator and denominator are perfect squares, and returns their integer square roots as the rational scale. When the non-singleton two-level path proves such a scale, TEPP reuses `deterministic_representable_sum_over_count` with `numerator` copies of `|gap|` and the exact integer `denominator`. This preserves a single deterministic represented-rational rounding boundary without multiply-first overflow or divide-first subnormal loss. + +The predecessor cases remain admitted: `3/6` of `9` reduces to `(1/6)^2`, and `6/10` of `16` reduces to `(1/8)^2`. The new 2/8-of-10 case reduces to `(2/15)^2`. Count factors that are not rational squares retain the existing translated second-moment path. + +## Rejected alternatives + +A fixture-specific `n=10,m=2` branch was rejected because the scientific condition is the reduced rational-square identity, not the example. Retaining the reciprocal-integer restriction was rejected because it excludes exact algebraic targets such as `2|g|/15`. A generic floating `sqrt(m(n-m)/(n^2(n-1)))` shortcut was rejected because it changes the rounding surface for irrational count factors and reintroduces the same composed-rounding problem. Direct `(|g| * numerator) / denominator` was rejected because the multiplication can overflow although the final scaled result is representable; direct `(|g| / denominator) * numerator` can lose subnormal mass before the numerator is restored. Arbitrary-precision production arithmetic was rejected because the exact reduced-count predicate and the existing deterministic represented-sum divisor are sufficient for this defect. + +## Risk and follow-up + +This repair proves only the exact rational-square two-level subset after exact residual translation. Rationally non-square two-level factors and general multi-level samples remain on the translated moment implementation and require their own reproduced scientific finding before any broader arithmetic change. Hosted exact-head Rust, coverage, documentation, security, and independent review evidence remain required after this source mutation. + +## Traceability + +- RED: `6dc8116c89fa44a7ff1d58a8f9a51c876993d33f` +- causal source repair: `8f2803c874568877e20e4c0f267ec5ce613daa3d` +- release-note fragment: `f0bb7af91c08b7766f43fec4c8d7984b68a3599f` +- module/API: `crates/validation_core/src/bias.rs` → `bias_standard_error` +- executable contract: `crates/validation_core/tests/bias_standard_error_two_level_rational_scale_rounding_contract.rs` + +IEEE 754-2019 remains the active IEEE floating-point standard, and ISO/IEC 60559:2020 remains the published international adoption. They define the binary floating-point arithmetic model relevant to this deterministic `f64` reference. The engineering conclusion here is narrower than global correct rounding: when the represented-input statistical identity is already an exact rational scale, reconstructing it through additional rounded sums, products, FMA, division, and square root is avoidable. + +Morris, White, and Crowther (2019) frame simulation evaluation around known truth, explicit estimands and performance measures, and Monte Carlo uncertainty. TEPP therefore treats a reproducible one-ULP displacement in a declared Validation Evidence performance measure as a scientific arithmetic defect when the represented-input target is analytically known. + +## References + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization. (2020). *ISO/IEC 60559:2020 Information technology—Microprocessor systems—Floating-point arithmetic*. + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 8b7995d2320cf256b3a38991ae1f8a230ca00146 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 15:05:39 +0900 Subject: [PATCH 373/576] test(validation): expose rational-scale subnormal double rounding --- ...ional_scale_subnormal_rounding_contract.rs | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_rational_scale_subnormal_rounding_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_rational_scale_subnormal_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_rational_scale_subnormal_rounding_contract.rs new file mode 100644 index 000000000..46dba6eb7 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_rational_scale_subnormal_rounding_contract.rs @@ -0,0 +1,37 @@ +use validation_core::{ValidationError, bias_standard_error}; + +#[test] +fn bias_standard_error_rational_scale_rounds_subnormal_result_once() { + let gap = f64::from_bits(0x004a_2c74_6ac3_028e); + let mut recovered = vec![0.0; 6]; + recovered.extend(std::iter::repeat(gap).take(27)); + + let standard_error = bias_standard_error(&[0.0; 33], &recovered) + .expect("represented-input subnormal standard error"); + // For counts 6 and 27 of n=33, + // m(n-m)/(n^2(n-1)) = 162/34848 = 9/1936 = (3/44)^2. + // The represented-input target is therefore exactly 3*|gap|/44. The + // predecessor rational-square path normalizes the numerator before division + // and then restores a power-of-two scale into the subnormal range, producing + // an avoidable second rounding one ULP below the correctly rounded result. + assert_eq!(standard_error.to_bits(), 0x000e_46cb_22f6_0165); + + let mut permuted = recovered.clone(); + permuted.rotate_left(11); + let permuted_standard_error = bias_standard_error(&[0.0; 33], &permuted) + .expect("permuted represented-input subnormal standard error"); + assert_eq!(permuted_standard_error.to_bits(), 0x000e_46cb_22f6_0165); + + let mirrored: Vec<_> = recovered.iter().map(|value| -*value).collect(); + let mirrored_standard_error = bias_standard_error(&[0.0; 33], &mirrored) + .expect("mirrored represented-input subnormal standard error"); + assert_eq!(mirrored_standard_error.to_bits(), 0x000e_46cb_22f6_0165); + + let minimum_subnormal = f64::from_bits(1); + let mut underflowing = vec![0.0; 6]; + underflowing.extend(std::iter::repeat(minimum_subnormal).take(27)); + assert_eq!( + bias_standard_error(&[0.0; 33], &underflowing), + Err(ValidationError::InvalidInput) + ); +} From ab0f0df1b8f36647f67239a5c628daed9023210e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 15:07:29 +0900 Subject: [PATCH 374/576] fix(validation): round rational-scale subnormal standard errors once --- crates/validation_core/src/bias.rs | 89 +++++++++++++++++++++++++----- 1 file changed, 75 insertions(+), 14 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 0887f8cc8..7a947ba34 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -154,6 +154,58 @@ fn exact_two_level_rational_scale( Some((numerator as usize, denominator as usize)) } +/// Round `|gap| * numerator / denominator` directly in minimum-subnormal units +/// when the exact rational result lies at or below the normal/subnormal boundary. +/// +/// A normalized binary64 quotient can be correctly rounded in its working +/// binade and still move by one ULP when an exact power-of-two restoration enters +/// the subnormal range. The represented significand is at most 53 bits and a +/// `usize` scale is at most the platform word width, so the exact unit numerator +/// fits `u128` for every subnormal result that this bounded path admits. +fn exact_subnormal_rational_scale( + gap: f64, + numerator: usize, + denominator: usize, +) -> Option> { + if !gap.is_finite() || gap == 0.0 || numerator == 0 || denominator == 0 { + return None; + } + + let magnitude_bits = gap.abs().to_bits(); + let exponent = ((magnitude_bits >> 52) & 0x7ff) as u32; + let fraction = magnitude_bits & 0x000f_ffff_ffff_ffff; + let significand = if exponent == 0 { + fraction as u128 + } else { + ((1_u64 << 52) | fraction) as u128 + }; + let product = significand.checked_mul(numerator as u128)?; + let unit_shift = if exponent == 0 { 0 } else { exponent - 1 }; + let scaled_numerator = product.checked_shl(unit_shift)?; + let denominator = denominator as u128; + + let mut rounded_units = scaled_numerator / denominator; + let remainder = scaled_numerator % denominator; + let twice_remainder = remainder.checked_mul(2)?; + if twice_remainder > denominator + || (twice_remainder == denominator && rounded_units & 1 == 1) + { + rounded_units = rounded_units.checked_add(1)?; + } + + if rounded_units == 0 { + return Some(Err(ValidationError::InvalidInput)); + } + let minimum_normal_units = 1_u128 << 52; + if rounded_units > minimum_normal_units { + return None; + } + if rounded_units == minimum_normal_units { + return Some(Ok(f64::MIN_POSITIVE)); + } + Some(Ok(f64::from_bits(rounded_units as u64))) +} + fn exact_translated_residual_standard_error( diffs: &[f64], roundoffs: &[f64], @@ -217,12 +269,18 @@ fn exact_translated_residual_standard_error( // occurs once, SE(mean) simplifies to |gap| / n. gap.abs() / translated.len() as f64 } else if let Some((numerator, denominator)) = rational_scale { - // Some non-singleton count geometries collapse to an exact rational - // scale. Reuse the represented-sum division primitive so factors - // such as 2/15 are rounded once without an avoidable square/root - // reconstruction or a multiply-first overflow. - let scaled_gap = vec![gap.abs(); numerator]; - deterministic_representable_sum_over_count(&scaled_gap, denominator)? + // A normalized quotient can double-round when its exact power-of-two + // restoration lands in the subnormal range. Round exact represented + // minimum-subnormal units first when that bounded case applies; + // otherwise retain the existing overflow-safe sum-over-count path. + if let Some(subnormal_result) = + exact_subnormal_rational_scale(gap, numerator, denominator) + { + subnormal_result? + } else { + let scaled_gap = vec![gap.abs(); numerator]; + deterministic_representable_sum_over_count(&scaled_gap, denominator)? + } } else { 0.0 }; @@ -342,14 +400,17 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 15:07:43 +0900 Subject: [PATCH 375/576] docs(changelog): record subnormal rational-scale rounding repair --- ...on-bias-standard-error-rational-scale-subnormal-rounding.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-rational-scale-subnormal-rounding.md diff --git a/CHANGELOG.d/validation-bias-standard-error-rational-scale-subnormal-rounding.md b/CHANGELOG.d/validation-bias-standard-error-rational-scale-subnormal-rounding.md new file mode 100644 index 000000000..b1dce3354 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-rational-scale-subnormal-rounding.md @@ -0,0 +1,3 @@ +### Fixed + +- Preserve exactly translated two-level `bias_standard_error` rational-square geometry when the represented result is subnormal. Exact rational scales such as `3/44` now round once in minimum-subnormal units instead of normalizing a quotient and then crossing a second binary64 rounding boundary during power-of-two restoration; mathematically nonzero values below binary64 range continue to fail closed. From 022797f7ca823d7bebe5a68be869ae641f1ccc01 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 15:08:42 +0900 Subject: [PATCH 376/576] docs(research): trace rational-scale subnormal rounding repair --- ...error-rational-scale-subnormal-rounding.md | 78 +++++++++++++++++++ 1 file changed, 78 insertions(+) create mode 100644 docs/research/bias-standard-error-rational-scale-subnormal-rounding.md diff --git a/docs/research/bias-standard-error-rational-scale-subnormal-rounding.md b/docs/research/bias-standard-error-rational-scale-subnormal-rounding.md new file mode 100644 index 000000000..007332ae7 --- /dev/null +++ b/docs/research/bias-standard-error-rational-scale-subnormal-rounding.md @@ -0,0 +1,78 @@ +# Bias standard error rational-scale subnormal rounding + +## Problem + +GAP-104 established an exact two-level shortcut when the reduced count factor is a rational square. The surviving implementation still routed that exact rational scale through `deterministic_representable_sum_over_count`, whose same-sign path first normalizes by an exact power of two. That normalization is harmless while the restored quotient remains normal, but it can introduce a second binary64 rounding boundary when the restored scientific result is subnormal. + +The public counterexample uses `n = 33`, with six represented residuals at `0` and 27 at +`g = f64::from_bits(0x004a_2c74_6ac3_028e)`. + +For a two-level sample, + +`SE(mean)^2 = g^2 * m(n-m) / (n^2(n-1))`. + +Here + +`6 * 27 / (33^2 * 32) = 162 / 34848 = 9 / 1936 = (3 / 44)^2`, + +so the represented-input target is exactly `3 * |g| / 44`. Correct rounding to binary64 gives bits `0x000e_46cb_22f6_0165`. The GAP-104 predecessor normalizes the rational numerator, rounds the normalized quotient, then restores the power-of-two scale into the subnormal range and returns adjacent lower bits `0x000e_46cb_22f6_0164`. + +This is not a disagreement about the estimand. It is an avoidable floating-point projection error after the exact two-level count geometry has already been proved. + +## Constraints + +- TEPP owns Validation Evidence performance-measure arithmetic; reusable static psychometric estimators remain in `fast-mlsirm`. +- Production numerical arithmetic remains Rust-first and deterministic. +- The repair must preserve the existing exact translated-residual admission and must not send arbitrary multi-level or irrational count geometry through a new path. +- A mathematically nonzero result below binary64 range must fail closed rather than silently become zero. +- No arbitrary-precision production dependency is introduced. + +## Decision + +For exact rational-square two-level geometry, the implementation now attempts a bounded exact subnormal projection before the existing represented sum-over-count path. + +A positive finite binary64 magnitude has an integer significand of at most 53 bits. Expressed in units of the minimum positive subnormal, a normal value with encoded exponent `e` has exact unit count `significand * 2^(e-1)`; a subnormal value uses its stored fraction directly. The rational scale therefore has exact unit numerator + +`significand * numerator * 2^(e-1)` + +for normal inputs, or `significand * numerator` for subnormal inputs. Checked `u128` arithmetic is used only when this bounded representation fits. Division by the exact integer denominator is then rounded once with round-to-nearest, ties-to-even. Results above the normal/subnormal boundary fall back to the existing path; a nonzero exact result that rounds below one minimum-subnormal unit returns `ValidationError::InvalidInput`. + +This preserves the existing overflow-safe path for normal results while removing the double-rounding surface at the subnormal boundary. + +## Alternatives rejected + +1. **Keep normalized rational scaling and accept one-ULP drift.** Rejected because the exact count geometry is already known and the drift is an implementation artifact, not simulation uncertainty. +2. **Special-case the `6/27 of 33` payload.** Rejected because the defect is the normal-to-subnormal restoration boundary, not those counts. +3. **Apply arbitrary-precision arithmetic to all Validation Evidence metrics.** Rejected as substantially broader than the causal defect and contrary to the bounded Rust reference-path design. +4. **Replace all standard-error arithmetic with a closed form.** Rejected because general multi-level samples and non-square count factors do not share this exact rational identity. + +## Evidence and traceability + +- Public RED: `8b7995d2320cf256b3a38991ae1f8a230ca00146` +- Causal source repair: `ab0f0df1b8f36647f67239a5c628daed9023210e` +- CHANGELOG fragment: `900b2091d572c9a984e350d2795ec58bfdd3177c` +- Module/API: `crates/validation_core/src/bias.rs` → `bias_standard_error` +- Public contract: `crates/validation_core/tests/bias_standard_error_rational_scale_subnormal_rounding_contract.rs` +- Expected represented result: `0x000e_46cb_22f6_0165` +- Predecessor result: `0x000e_46cb_22f6_0164` +- Edge contract: minimum-subnormal gap with the same exact `3/44` count scale is mathematically nonzero but below binary64 range and must return `InvalidInput`. + +The public contract also fixes permutation and sign-mirror invariance. Hosted exact-head CI remains authoritative for GREEN; a branch-local arithmetic proof or predecessor workflow result is not transferred as current-head CI evidence. + +## Standards and methodological basis + +IEEE 754-2019 remains the active IEEE floating-point standard. IEEE P754, approved as a PAR on June 6, 2024, is an active revision project that supersedes 754-2019 only when a replacement standard is actually published. ISO/IEC 60559:2020 remains a published international standard adopting the same floating-point arithmetic model. These sources support explicit control of destination-format rounding and distinguish arithmetic semantics from application-level statistical uncertainty. + +For Validation Evidence, Morris, White, and Crowther (2019) treat bias and empirical standard error as simulation performance measures and require Monte Carlo uncertainty to be reported as simulation uncertainty. A deterministic one-ULP arithmetic projection error in a represented performance measure is therefore not something to absorb into Monte Carlo error. + +The currently published AERA/APA/NCME *Standards for Educational and Psychological Testing* remains the 2014 edition. AERA, APA, and NCME announced a Joint Committee in 2024 to revise that edition; the in-progress revision is not treated here as published normative authority. + +## 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. https://www.testingstandards.net/open-access-files.html + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE Standards Association. https://standards.ieee.org/ieee/754/6210/ + +International Organization for Standardization, & International Electrotechnical Commission. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). https://www.iso.org/standard/80985.html + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 4daeb65daee98b25fdc5a29744a710752e229a50 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 15:16:12 +0900 Subject: [PATCH 377/576] test(validation): cover subnormal rational rounding boundaries --- ...ional_scale_subnormal_rounding_contract.rs | 46 +++++++++++++++++-- 1 file changed, 41 insertions(+), 5 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_rational_scale_subnormal_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_rational_scale_subnormal_rounding_contract.rs index 46dba6eb7..a959713dd 100644 --- a/crates/validation_core/tests/bias_standard_error_rational_scale_subnormal_rounding_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_rational_scale_subnormal_rounding_contract.rs @@ -1,10 +1,15 @@ use validation_core::{ValidationError, bias_standard_error}; +fn two_level_sample(gap: f64) -> Vec { + let mut recovered = vec![0.0; 6]; + recovered.extend(std::iter::repeat_n(gap, 27)); + recovered +} + #[test] fn bias_standard_error_rational_scale_rounds_subnormal_result_once() { let gap = f64::from_bits(0x004a_2c74_6ac3_028e); - let mut recovered = vec![0.0; 6]; - recovered.extend(std::iter::repeat(gap).take(27)); + let recovered = two_level_sample(gap); let standard_error = bias_standard_error(&[0.0; 33], &recovered) .expect("represented-input subnormal standard error"); @@ -26,12 +31,43 @@ fn bias_standard_error_rational_scale_rounds_subnormal_result_once() { let mirrored_standard_error = bias_standard_error(&[0.0; 33], &mirrored) .expect("mirrored represented-input subnormal standard error"); assert_eq!(mirrored_standard_error.to_bits(), 0x000e_46cb_22f6_0165); +} + +#[test] +fn bias_standard_error_rational_scale_uses_ties_to_even_at_subnormal_units() { + // With the same exact 3/44 count scale, 22 minimum-subnormal gap units map + // to 1.5 result units and therefore round to the even value 2. + let odd_lower = two_level_sample(f64::from_bits(22)); + assert_eq!( + bias_standard_error(&[0.0; 33], &odd_lower) + .expect("odd lower midpoint") + .to_bits(), + 2 + ); + + // 66 gap units map to 4.5 result units and remain at the even lower value 4. + let even_lower = two_level_sample(f64::from_bits(66)); + assert_eq!( + bias_standard_error(&[0.0; 33], &even_lower) + .expect("even lower midpoint") + .to_bits(), + 4 + ); +} + +#[test] +fn bias_standard_error_rational_scale_preserves_range_boundary_and_refuses_false_zero() { + let rounds_to_minimum_normal = two_level_sample(f64::from_bits(0x004d_5555_5555_5555)); + assert_eq!( + bias_standard_error(&[0.0; 33], &rounds_to_minimum_normal) + .expect("minimum-normal boundary") + .to_bits(), + f64::MIN_POSITIVE.to_bits() + ); let minimum_subnormal = f64::from_bits(1); - let mut underflowing = vec![0.0; 6]; - underflowing.extend(std::iter::repeat(minimum_subnormal).take(27)); assert_eq!( - bias_standard_error(&[0.0; 33], &underflowing), + bias_standard_error(&[0.0; 33], &two_level_sample(minimum_subnormal)), Err(ValidationError::InvalidInput) ); } From 8f6916dda1bc241e6bfd5dab0840e621769f995b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 15:18:09 +0900 Subject: [PATCH 378/576] test(validation): cover exact subnormal rational projection branches --- crates/validation_core/src/bias.rs | 45 +++++++++++++++++++++++++----- 1 file changed, 38 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 7a947ba34..7235ecaa8 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -159,9 +159,11 @@ fn exact_two_level_rational_scale( /// /// A normalized binary64 quotient can be correctly rounded in its working /// binade and still move by one ULP when an exact power-of-two restoration enters -/// the subnormal range. The represented significand is at most 53 bits and a -/// `usize` scale is at most the platform word width, so the exact unit numerator -/// fits `u128` for every subnormal result that this bounded path admits. +/// the subnormal range. On supported targets a represented significand is at +/// most 53 bits and a `usize` factor at most 64 bits. The only potentially large +/// operation is the exponent shift; if it cannot fit `u128`, the exact result is +/// necessarily above this bounded subnormal path and the caller keeps its normal +/// overflow-safe implementation. fn exact_subnormal_rational_scale( gap: f64, numerator: usize, @@ -179,18 +181,18 @@ fn exact_subnormal_rational_scale( } else { ((1_u64 << 52) | fraction) as u128 }; - let product = significand.checked_mul(numerator as u128)?; + let product = significand * numerator as u128; let unit_shift = if exponent == 0 { 0 } else { exponent - 1 }; let scaled_numerator = product.checked_shl(unit_shift)?; let denominator = denominator as u128; let mut rounded_units = scaled_numerator / denominator; let remainder = scaled_numerator % denominator; - let twice_remainder = remainder.checked_mul(2)?; + let twice_remainder = remainder * 2; if twice_remainder > denominator || (twice_remainder == denominator && rounded_units & 1 == 1) { - rounded_units = rounded_units.checked_add(1)?; + rounded_units += 1; } if rounded_units == 0 { @@ -468,7 +470,9 @@ pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result 0.0); } + + #[test] + fn exact_subnormal_rational_scale_covers_projection_boundaries() { + assert_eq!(exact_subnormal_rational_scale(0.0, 1, 1), None); + assert_eq!(exact_subnormal_rational_scale(f64::INFINITY, 1, 1), None); + assert_eq!(exact_subnormal_rational_scale(1.0, 0, 1), None); + assert_eq!(exact_subnormal_rational_scale(1.0, 1, 0), None); + + assert_eq!( + exact_subnormal_rational_scale(f64::from_bits(22), 3, 44), + Some(Ok(f64::from_bits(2))) + ); + assert_eq!( + exact_subnormal_rational_scale(f64::from_bits(66), 3, 44), + Some(Ok(f64::from_bits(4))) + ); + assert_eq!( + exact_subnormal_rational_scale(f64::from_bits(1), 3, 44), + Some(Err(ValidationError::InvalidInput)) + ); + assert_eq!( + exact_subnormal_rational_scale(f64::from_bits(0x004d_5555_5555_5555), 3, 44), + Some(Ok(f64::MIN_POSITIVE)) + ); + assert_eq!(exact_subnormal_rational_scale(f64::MIN_POSITIVE * 2.0, 1, 1), None); + assert_eq!(exact_subnormal_rational_scale(f64::MAX, 1, 1), None); + } } From edb84949cda68749025b1c0b3ae66ba24acc000c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 15:18:55 +0900 Subject: [PATCH 379/576] docs(research): record subnormal rounding boundary coverage --- ...d-error-rational-scale-subnormal-rounding.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/research/bias-standard-error-rational-scale-subnormal-rounding.md b/docs/research/bias-standard-error-rational-scale-subnormal-rounding.md index 007332ae7..0a9c515e6 100644 --- a/docs/research/bias-standard-error-rational-scale-subnormal-rounding.md +++ b/docs/research/bias-standard-error-rational-scale-subnormal-rounding.md @@ -4,8 +4,7 @@ GAP-104 established an exact two-level shortcut when the reduced count factor is a rational square. The surviving implementation still routed that exact rational scale through `deterministic_representable_sum_over_count`, whose same-sign path first normalizes by an exact power of two. That normalization is harmless while the restored quotient remains normal, but it can introduce a second binary64 rounding boundary when the restored scientific result is subnormal. -The public counterexample uses `n = 33`, with six represented residuals at `0` and 27 at -`g = f64::from_bits(0x004a_2c74_6ac3_028e)`. +The public counterexample uses `n = 33`, with six represented residuals at `0` and 27 at `g = f64::from_bits(0x004a_2c74_6ac3_028e)`. For a two-level sample, @@ -23,19 +22,19 @@ This is not a disagreement about the estimand. It is an avoidable floating-point - TEPP owns Validation Evidence performance-measure arithmetic; reusable static psychometric estimators remain in `fast-mlsirm`. - Production numerical arithmetic remains Rust-first and deterministic. -- The repair must preserve the existing exact translated-residual admission and must not send arbitrary multi-level or irrational count geometry through a new path. -- A mathematically nonzero result below binary64 range must fail closed rather than silently become zero. +- The repair preserves the existing exact translated-residual admission and does not send arbitrary multi-level or irrational count geometry through a new path. +- A mathematically nonzero result below binary64 range fails closed rather than silently becoming zero. - No arbitrary-precision production dependency is introduced. ## Decision -For exact rational-square two-level geometry, the implementation now attempts a bounded exact subnormal projection before the existing represented sum-over-count path. +For exact rational-square two-level geometry, the implementation attempts a bounded exact subnormal projection before the existing represented sum-over-count path. A positive finite binary64 magnitude has an integer significand of at most 53 bits. Expressed in units of the minimum positive subnormal, a normal value with encoded exponent `e` has exact unit count `significand * 2^(e-1)`; a subnormal value uses its stored fraction directly. The rational scale therefore has exact unit numerator `significand * numerator * 2^(e-1)` -for normal inputs, or `significand * numerator` for subnormal inputs. Checked `u128` arithmetic is used only when this bounded representation fits. Division by the exact integer denominator is then rounded once with round-to-nearest, ties-to-even. Results above the normal/subnormal boundary fall back to the existing path; a nonzero exact result that rounds below one minimum-subnormal unit returns `ValidationError::InvalidInput`. +for normal inputs, or `significand * numerator` for subnormal inputs. On supported targets, the significand and `usize` rational factor fit the bounded `u128` product. The exponent shift is checked; if it cannot fit, the result is outside this subnormal projection and the existing normal overflow-safe path remains authoritative. Division by the exact integer denominator is rounded once with round-to-nearest, ties-to-even. Results above the normal/subnormal boundary fall back to the existing path; a nonzero exact result that rounds below one minimum-subnormal unit returns `ValidationError::InvalidInput`. This preserves the existing overflow-safe path for normal results while removing the double-rounding surface at the subnormal boundary. @@ -50,14 +49,16 @@ This preserves the existing overflow-safe path for normal results while removing - Public RED: `8b7995d2320cf256b3a38991ae1f8a230ca00146` - Causal source repair: `ab0f0df1b8f36647f67239a5c628daed9023210e` +- Public boundary coverage: `4daeb65daee98b25fdc5a29744a710752e229a50` +- Production branch-coverage hardening: `8f6916dda1bc241e6bfd5dab0840e621769f995b` - CHANGELOG fragment: `900b2091d572c9a984e350d2795ec58bfdd3177c` - Module/API: `crates/validation_core/src/bias.rs` → `bias_standard_error` - Public contract: `crates/validation_core/tests/bias_standard_error_rational_scale_subnormal_rounding_contract.rs` - Expected represented result: `0x000e_46cb_22f6_0165` - Predecessor result: `0x000e_46cb_22f6_0164` -- Edge contract: minimum-subnormal gap with the same exact `3/44` count scale is mathematically nonzero but below binary64 range and must return `InvalidInput`. +- Edge contract: a minimum-subnormal gap with the same exact `3/44` count scale is mathematically nonzero but below binary64 range and returns `InvalidInput`. -The public contract also fixes permutation and sign-mirror invariance. Hosted exact-head CI remains authoritative for GREEN; a branch-local arithmetic proof or predecessor workflow result is not transferred as current-head CI evidence. +The public contract fixes permutation and sign-mirror invariance, both ties-to-even directions in minimum-subnormal units, exact rounding onto `f64::MIN_POSITIVE`, and fail-closed underflow. The crate-private branch tests cover invalid helper admission, normal/subnormal significand decoding, midpoint increment/non-increment, zero refusal, normal-boundary return, above-boundary fallback, and exponent-shift fallback. Hosted exact-head CI remains authoritative for GREEN; branch-local arithmetic proof or predecessor workflow results are not transferred as current-head CI evidence. ## Standards and methodological basis From 5cb45a4f204ab4fbcd5581c4d4504e82f0339a30 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 15:31:01 +0900 Subject: [PATCH 380/576] test(validation): expose anchor-order standard-error drift --- ...ndard_error_anchor_permutation_contract.rs | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_anchor_permutation_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_anchor_permutation_contract.rs b/crates/validation_core/tests/bias_standard_error_anchor_permutation_contract.rs new file mode 100644 index 000000000..9c309a80c --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_anchor_permutation_contract.rs @@ -0,0 +1,24 @@ +use validation_core::bias_standard_error; + +#[test] +fn bias_standard_error_is_invariant_to_the_exact_translation_anchor() { + let low = f64::from_bits(0x4194_f788_9184_b980); + let middle = f64::from_bits(0x420c_409f_fce3_8390); + let high = f64::from_bits(0x4222_70c4_634c_c6b6); + let expected_bits = 0x4205_7185_8078_f946; + + for recovered in [ + [low, middle, high], + [middle, low, high], + [high, middle, low], + ] { + let standard_error = bias_standard_error(&[0.0; 3], &recovered) + .expect("represented-input bias standard error"); + assert_eq!(standard_error.to_bits(), expected_bits); + + let mirrored = recovered.map(|value| -value); + let mirrored_standard_error = bias_standard_error(&[0.0; 3], &mirrored) + .expect("mirrored represented-input bias standard error"); + assert_eq!(mirrored_standard_error.to_bits(), expected_bits); + } +} From 159659a9510a7ced437ad872d02e26619abc8236 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 15:33:08 +0900 Subject: [PATCH 381/576] fix(validation): canonicalize exact translation anchors --- crates/validation_core/src/bias.rs | 51 ++++++++++++++++++++++++------ 1 file changed, 42 insertions(+), 9 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 7235ecaa8..9c168d7f5 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -208,12 +208,13 @@ fn exact_subnormal_rational_scale( Some(Ok(f64::from_bits(rounded_units as u64))) } -fn exact_translated_residual_standard_error( +fn translated_residuals_from_anchor( diffs: &[f64], roundoffs: &[f64], -) -> Result, ValidationError> { - let anchor_high = diffs[0]; - let anchor_low = roundoffs[0]; + anchor_index: usize, +) -> Option> { + let anchor_high = diffs[anchor_index]; + let anchor_low = roundoffs[anchor_index]; let mut translated = Vec::with_capacity(diffs.len()); for (&high, &low) in diffs.iter().zip(roundoffs) { @@ -221,23 +222,52 @@ fn exact_translated_residual_standard_error( if !high_delta.is_finite() || subtraction_roundoff(high, anchor_high, high_delta) != 0.0 { - return Ok(None); + return None; } let low_delta = low - anchor_low; if !low_delta.is_finite() || subtraction_roundoff(low, anchor_low, low_delta) != 0.0 { - return Ok(None); + return None; } let delta = high_delta + low_delta; if !delta.is_finite() || subtraction_roundoff(high_delta, -low_delta, delta) != 0.0 { - return Ok(None); + return None; } translated.push(delta); } + Some(translated) +} + +fn canonical_exact_translated_residuals(diffs: &[f64], roundoffs: &[f64]) -> Option> { + let mut anchor_indices: Vec<_> = (0..diffs.len()).collect(); + anchor_indices.sort_by(|left, right| { + diffs[*left] + .total_cmp(&diffs[*right]) + .then_with(|| roundoffs[*left].total_cmp(&roundoffs[*right])) + }); + + anchor_indices + .into_iter() + .find_map(|anchor_index| translated_residuals_from_anchor(diffs, roundoffs, anchor_index)) +} + +fn exact_translated_residual_standard_error( + diffs: &[f64], + roundoffs: &[f64], +) -> Result, ValidationError> { + // A translated second moment is order-invariant, so admission must not depend + // on whichever observation happened to arrive first. Search candidate anchors + // in a canonical represented `(high, low)` order and use the first anchor for + // which every high, low and recombined delta is exact. If no exact anchor + // exists, retain the predecessor rounded-residual fallback. + let Some(translated) = canonical_exact_translated_residuals(diffs, roundoffs) else { + return Ok(None); + }; + let max_magnitude = translated .iter() .map(|value| value.abs()) @@ -398,8 +428,11 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 15:33:37 +0900 Subject: [PATCH 382/576] docs(changelog): record anchor-invariant bias standard error --- .../validation-bias-standard-error-anchor-permutation.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-anchor-permutation.md diff --git a/CHANGELOG.d/validation-bias-standard-error-anchor-permutation.md b/CHANGELOG.d/validation-bias-standard-error-anchor-permutation.md new file mode 100644 index 000000000..08ceac571 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-anchor-permutation.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core::bias_standard_error` now searches exact translated-residual anchors in canonical represented `(high, low)` order instead of making the first observation authoritative. This preserves permutation invariance when one represented anchor admits exact deltas but another anchor would force the rounded-residual fallback. +- The public regression contract fixes a three-residual boundary whose low- and high-anchor permutations previously returned the adjacent lower binary64 standard error while the middle anchor returned the correctly rounded represented-input result. Sign-mirrored orderings are covered as well. From 229fdd6eaffad5adf8cc2964cb506c6dd1191611 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 15:34:12 +0900 Subject: [PATCH 383/576] docs(research): trace anchor-order standard-error repair --- ...ard-error-anchor-permutation-invariance.md | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 docs/research/bias-standard-error-anchor-permutation-invariance.md diff --git a/docs/research/bias-standard-error-anchor-permutation-invariance.md b/docs/research/bias-standard-error-anchor-permutation-invariance.md new file mode 100644 index 000000000..447fb1b66 --- /dev/null +++ b/docs/research/bias-standard-error-anchor-permutation-invariance.md @@ -0,0 +1,81 @@ +# Bias standard error exact-anchor permutation invariance + +## Problem + +GAP-105 fixed exact rational-scale projection at the normal/subnormal boundary, but the larger exact-translated residual path still selected `diffs[0]` as its translation anchor. Translation does not change a sample standard error, so the numerical admission decision must not depend on transport order. The predecessor did: if the first represented residual could not be subtracted exactly from every other represented residual, it abandoned the exact translated path even when another represented observation was a valid exact anchor. + +The public counterexample uses three exactly represented residuals with `truth = [0, 0, 0]`: + +- `low = f64::from_bits(0x4194_f788_9184_b980) = 46106761431411 / 524288`, +- `middle = f64::from_bits(0x420c_409f_fce3_8390) = 497022202165305 / 32768`, +- `high = f64::from_bits(0x4222_70c4_634c_c6b6) = 2595269181334363 / 65536`. + +For these represented inputs, the exact squared standard error of the mean is + +`327877142843256291246417577647793 / 2473901162496`, + +whose correctly rounded binary64 square root is `0x4205_7185_8078_f946`. + +With `middle` as the first residual, both `low - middle` and `high - middle` are exact binary64 differences, so the predecessor admits the translated second moment and returns `0x4205_7185_8078_f946`. With `low` first, `high - low` has a nonzero error-free subtraction tail of `2^-19`; with `high` first, `low - high` has the mirrored `-2^-19` tail. Those orderings reject the translated path and fall back to rounded-mean dispersion, returning adjacent lower bits `0x4205_7185_8078_f945`. + +The estimand and represented multiset are identical in every ordering. A one-ULP result change caused only by which observation is first is therefore a deterministic arithmetic defect, not Monte Carlo uncertainty. + +## Constraints + +- TEPP owns Validation Evidence performance-measure arithmetic. Reusable static psychometric estimators remain in `fast-mlsirm`. +- The repair must preserve the existing exact `high + low` translated-residual admission. It must not declare an inexact delta exact or weaken fail-closed behavior. +- Observation order is not scientific evidence and cannot select a different numerical estimator path. +- Production arithmetic remains deterministic Rust binary64; no arbitrary-precision runtime dependency is introduced. +- Existing two-level algebraic shortcuts, normal/subnormal projection policy, and fallback semantics remain intact. + +## Decision + +Exact translated-residual admission now considers every represented observation as a possible anchor, but candidate anchors are examined in canonical `(high, low)` binary64 total order. For each candidate, the implementation requires all three existing proofs to remain true for every observation: + +1. `high - anchor_high` is finite and error-free, +2. `low - anchor_low` is finite and error-free, +3. recombining the exact high and low deltas is finite and error-free. + +The first candidate in canonical order satisfying all three conditions supplies the translated residual vector. If no candidate satisfies them, the predecessor rounded-residual fallback remains authoritative. + +Canonical candidate ordering matters. Merely scanning observations in incoming order would fix the specific low-first payload only when a viable anchor happens to occur early, and multiple viable anchors could still make path selection transport-order dependent. Sorting the represented `(high, low)` candidate keys before admission makes the anchor choice a function of the represented multiset rather than array order. + +## Alternatives rejected + +1. **Keep `diffs[0]` as the anchor.** Rejected because the public contract demonstrates a one-ULP permutation violation for the same represented sample. +2. **Always choose the median high residual.** Rejected because subtraction low terms are part of the exact residual representation; a high-part median is not guaranteed to be an exact anchor for both high and low deltas. +3. **Scan anchors in incoming order and stop at the first exact candidate.** Rejected because the chosen exact anchor would still depend on transport order when multiple candidates are viable. +4. **Sort the whole observation sample before all Validation Evidence arithmetic.** Rejected as broader than the defect and capable of changing unrelated pairing/provenance assumptions. +5. **Use arbitrary-precision arithmetic for every standard error.** Rejected as substantially broader than this bounded exact-admission defect. + +## Evidence and traceability + +- Public RED: `5cb45a4f204ab4fbcd5581c4d4504e82f0339a30` +- Causal source repair: `159659a9510a7ced437ad872d02e26619abc8236` +- CHANGELOG fragment: `7e57b930daebb01b3583b4a5108ce3c1a89a06a6` +- Module/API: `crates/validation_core/src/bias.rs` → `bias_standard_error` → `exact_translated_residual_standard_error` +- Public contract: `crates/validation_core/tests/bias_standard_error_anchor_permutation_contract.rs` +- Correct represented result: `0x4205_7185_8078_f946` +- Predecessor low/high-anchor result: `0x4205_7185_8078_f945` +- Middle-anchor predecessor result: `0x4205_7185_8078_f946` +- Sign-mirrored permutations must produce the same positive standard error. + +The contract is intentionally a represented-input arithmetic acceptance test. It does not claim that every `n > 2` standard error is globally correctly rounded, and it does not expand the set of deltas treated as exact. Hosted exact-head CI remains the authority for GREEN after the surviving branch head is known. + +## Standards and methodological basis + +IEEE 754-2019 defines the binary floating-point arithmetic model used by the Rust `f64` reference path. ISO/IEC 60559:2020 adopts that floating-point model internationally. The relevant engineering requirement here is not a new statistical estimator: it is deterministic use of the same represented input multiset without making an incidental array position numerically authoritative. + +Morris, White, and Crowther (2019) distinguish deterministic simulation performance measures from Monte Carlo uncertainty. Bias and empirical standard-error evidence should therefore not acquire extra variation from observation permutation inside the implementation. + +The currently published AERA/APA/NCME *Standards for Educational and Psychological Testing* remains the 2014 edition while the announced revision is in progress. The published edition is retained as the normative testing reference until a replacement is actually issued. + +## 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. https://www.testingstandards.net/open-access-files.html + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE Standards Association. https://standards.ieee.org/ieee/754/6210/ + +International Organization for Standardization, & International Electrotechnical Commission. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). https://www.iso.org/standard/80985.html + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 49343ab6e7f1a4cadc0b9c71e0757edea0256add Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 16:02:31 +0900 Subject: [PATCH 384/576] test(validation): expose exact-anchor conditioning roundoff --- ...dard_error_anchor_conditioning_contract.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_anchor_conditioning_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_anchor_conditioning_contract.rs b/crates/validation_core/tests/bias_standard_error_anchor_conditioning_contract.rs new file mode 100644 index 000000000..0fcc5fb08 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_anchor_conditioning_contract.rs @@ -0,0 +1,32 @@ +use validation_core::bias_standard_error; + +const EXPECTED_STANDARD_ERROR_BITS: u64 = 0x3fd1_7a99_c875_b980; + +fn represented_sample() -> [f64; 3] { + [ + f64::from_bits(0x3ff7_c8a6_308f_7624), + f64::from_bits(0x3ff0_4284_fcf1_21a0), + f64::from_bits(0x3fff_659d_6d25_7410), + ] +} + +#[test] +fn exact_translated_anchor_conditioning_preserves_correct_rounding() { + let [middle, low, high] = represented_sample(); + let truth = [0.0; 3]; + let permutations = [ + [low, middle, high], + [middle, low, high], + [high, low, middle], + ]; + + for recovered in permutations { + let standard_error = bias_standard_error(&truth, &recovered).expect("finite standard error"); + assert_eq!(standard_error.to_bits(), EXPECTED_STANDARD_ERROR_BITS); + + let mirrored = recovered.map(|value| -value); + let mirrored_standard_error = + bias_standard_error(&truth, &mirrored).expect("finite mirrored standard error"); + assert_eq!(mirrored_standard_error.to_bits(), EXPECTED_STANDARD_ERROR_BITS); + } +} From 0fc3ea97106f5156a6d68d4db8fd6f4a2ace0ac4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 16:04:39 +0900 Subject: [PATCH 385/576] fix(validation): prefer conditioned exact translation anchor --- crates/validation_core/src/bias.rs | 80 ++++++++++++++++++------------ 1 file changed, 48 insertions(+), 32 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 9c168d7f5..4e5794999 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -243,16 +243,31 @@ fn translated_residuals_from_anchor( } fn canonical_exact_translated_residuals(diffs: &[f64], roundoffs: &[f64]) -> Option> { - let mut anchor_indices: Vec<_> = (0..diffs.len()).collect(); - anchor_indices.sort_by(|left, right| { - diffs[*left] - .total_cmp(&diffs[*right]) - .then_with(|| roundoffs[*left].total_cmp(&roundoffs[*right])) - }); - - anchor_indices - .into_iter() - .find_map(|anchor_index| translated_residuals_from_anchor(diffs, roundoffs, anchor_index)) + let mut best: Option<(usize, f64, Vec)> = None; + + for anchor_index in 0..diffs.len() { + let Some(translated) = translated_residuals_from_anchor(diffs, roundoffs, anchor_index) else { + continue; + }; + let max_magnitude = translated + .iter() + .map(|value| value.abs()) + .fold(0.0, f64::max); + + let should_replace = match &best { + None => true, + Some((best_index, best_max_magnitude, _)) => max_magnitude + .total_cmp(best_max_magnitude) + .then_with(|| diffs[anchor_index].total_cmp(&diffs[*best_index])) + .then_with(|| roundoffs[anchor_index].total_cmp(&roundoffs[*best_index])) + .is_lt(), + }; + if should_replace { + best = Some((anchor_index, max_magnitude, translated)); + } + } + + best.map(|(_, _, translated)| translated) } fn exact_translated_residual_standard_error( @@ -260,10 +275,11 @@ fn exact_translated_residual_standard_error( roundoffs: &[f64], ) -> Result, ValidationError> { // A translated second moment is order-invariant, so admission must not depend - // on whichever observation happened to arrive first. Search candidate anchors - // in a canonical represented `(high, low)` order and use the first anchor for - // which every high, low and recombined delta is exact. If no exact anchor - // exists, retain the predecessor rounded-residual fallback. + // on whichever observation happened to arrive first. Search every candidate + // anchor that preserves exact high, low and recombined deltas, prefer the one + // with the smallest maximum translated magnitude, then break ties in canonical + // represented `(high, low)` order. This keeps the choice permutation-invariant + // while minimizing the dynamic range exposed to the later square/sqrt path. let Some(translated) = canonical_exact_translated_residuals(diffs, roundoffs) else { return Ok(None); }; @@ -428,24 +444,24 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 16:04:54 +0900 Subject: [PATCH 386/576] docs(changelog): record exact-anchor conditioning repair --- .../validation-bias-standard-error-anchor-conditioning.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-anchor-conditioning.md diff --git a/CHANGELOG.d/validation-bias-standard-error-anchor-conditioning.md b/CHANGELOG.d/validation-bias-standard-error-anchor-conditioning.md new file mode 100644 index 000000000..c6c8f04ee --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-anchor-conditioning.md @@ -0,0 +1,3 @@ +### Fixed + +- Preserve exact translated-residual `bias_standard_error` conditioning when several exact anchors exist. The Validation Evidence path now chooses the exact anchor with the smallest maximum translated magnitude and uses canonical represented `(high, low)` ordering only as a tie-breaker, preventing an avoidable one-ULP square/square-root drift while retaining permutation invariance and the existing exactness admission boundary. From e3ab276574e439c4e4f5fdab1009dde7711e72dc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 16:05:39 +0900 Subject: [PATCH 387/576] docs(research): trace exact-anchor conditioning evidence --- ...bias-standard-error-anchor-conditioning.md | 79 +++++++++++++++++++ 1 file changed, 79 insertions(+) create mode 100644 docs/research/bias-standard-error-anchor-conditioning.md diff --git a/docs/research/bias-standard-error-anchor-conditioning.md b/docs/research/bias-standard-error-anchor-conditioning.md new file mode 100644 index 000000000..16a1ce46b --- /dev/null +++ b/docs/research/bias-standard-error-anchor-conditioning.md @@ -0,0 +1,79 @@ +# Bias standard error exact-anchor conditioning + +## Problem + +GAP-106 made exact translated-residual admission permutation-invariant by considering every represented observation as an anchor and taking the first exact candidate in canonical `(high, low)` order. That removed transport-order drift, but canonical order is not a numerical conditioning criterion. When several anchors satisfy the same exactness proof, different exact translations can expose different binary64 magnitudes to the later square, compensated-sum, fused multiply-add, and square-root path. + +The public counterexample uses three exactly represented residuals with `truth = [0, 0, 0]`: + +- `middle = f64::from_bits(0x3ff7_c8a6_308f_7624)`, +- `low = f64::from_bits(0x3ff0_4284_fcf1_21a0)`, +- `high = f64::from_bits(0x3fff_659d_6d25_7410)`. + +For this represented multiset, the exact squared standard error of the mean is + +`850963186800334380866421373237 / 11408855402054064613470328848384`, + +whose correctly rounded binary64 square root is `0x3fd1_7a99_c875_b980`. + +All three observations are valid exact translation anchors. The canonical-low anchor produces translated magnitudes up to `0x1.e4630e068a4e0p-1` and the predecessor returns adjacent upper bits `0x3fd1_7a99_c875_b981`. The represented middle anchor produces translated magnitudes no larger than `0x1.e73dcf257f7b0p-2` and returns the correctly rounded `0x3fd1_7a99_c875_b980`. The estimand, represented residual multiset, and exact-admission predicate are unchanged; only the exact anchor's conditioning differs. + +This is deterministic arithmetic error, not Monte Carlo uncertainty. GAP-106 solved which *set* of observations may admit exact translation, but it still chose among several admissible representations using an arbitrary lexical criterion before a numerically sensitive second-moment reconstruction. + +## Constraints + +- TEPP owns Validation Evidence performance-measure arithmetic; reusable static psychometric estimation remains in `fast-mlsirm`. +- The existing error-free high delta, low delta, and recombined delta proofs remain mandatory. The repair must not classify any previously inexact translation as exact. +- Anchor selection must remain a function of the represented multiset rather than incoming observation order. +- The repair must not sort or otherwise rewrite the scientific observation pairing outside this local translation choice. +- Production arithmetic remains deterministic Rust binary64. No arbitrary-precision runtime dependency is added. +- Existing two-level algebraic shortcuts, subnormal rational projection, and fail-closed fallback semantics remain unchanged. + +## Decision + +Every represented observation remains an anchor candidate. For each candidate that satisfies all existing exactness proofs, TEPP computes the maximum absolute translated residual. It selects the exact candidate with the smallest such maximum magnitude. Canonical `(high, low)` total order is retained only as a deterministic tie-breaker. + +This criterion is local and causal: it does not claim a globally correctly rounded standard error for every `n > 2` sample. It removes an avoidably wide exact translation before the same power-of-two normalization and second-moment implementation. In the public counterexample, the centered represented observation halves the translated working radius and removes the one-ULP upward drift without changing the exact residual geometry. + +The selection is permutation-invariant because every candidate is evaluated and the objective plus tie-breaker depends only on represented candidate values. It is also monotone with respect to the specific numerical risk being repaired: among exact translations of the same multiset, a smaller maximum magnitude cannot increase the exponent range subsequently exposed to squaring. + +## Alternatives rejected + +1. **Keep the first exact anchor in canonical `(high, low)` order.** Rejected because the public contract shows that lexical canonicalization can select a one-ULP-worse exact representation. +2. **Use the incoming first or first exact observation.** Rejected because it reintroduces the permutation defect closed by GAP-106. +3. **Always use the median high residual.** Rejected because a median high part is not necessarily an exact anchor once subtraction low terms are part of the represented residual decomposition. +4. **Choose whichever anchor happens to reproduce an external high-precision oracle.** Rejected because production path selection cannot depend on an unavailable oracle and would amount to test-payload fitting. +5. **Replace the general path with arbitrary-precision variance arithmetic.** Rejected as broader than this bounded conditioning defect and outside the current Rust binary64 reference boundary. + +## Evidence and traceability + +- Public RED: `49343ab6e7f1a4cadc0b9c71e0757edea0256add` +- Causal source repair: `0fc3ea97106f5156a6d68d4db8fd6f4a2ace0ac4` +- CHANGELOG fragment: `2793ff5927ed3b79886cd3d4daa2369357c1710b` +- Module/API: `crates/validation_core/src/bias.rs` → `bias_standard_error` → `exact_translated_residual_standard_error` → `canonical_exact_translated_residuals` +- Public contract: `crates/validation_core/tests/bias_standard_error_anchor_conditioning_contract.rs` +- Correct represented result: `0x3fd1_7a99_c875_b980` +- GAP-106 canonical-low predecessor result: `0x3fd1_7a99_c875_b981` +- The contract covers low/middle/high-first permutations and sign mirrors so the GAP-106 permutation guarantee is retained while the conditioned anchor changes the numerical result. + +Hosted exact-head CI remains authoritative for GREEN after the surviving branch head is known. This repair is a bounded represented-input arithmetic acceptance claim, not a global correctly-rounded guarantee for all standard errors. + +## Standards and methodological basis + +IEEE 754-2019 remains the binary floating-point arithmetic basis for the Rust `f64` reference path. The active P754 project is a revision project that supersedes 754-2019 when completed; it is not yet a published replacement. ISO/IEC 60559:2020 remains the published international adoption of the 754-2019 arithmetic model. + +Morris, White, and Crowther (2019) distinguish deterministic performance-measure computation from Monte Carlo uncertainty. A fixed represented sample must therefore not acquire extra numerical variation from an avoidable choice among algebraically equivalent exact translations. + +The currently published AERA/APA/NCME *Standards for Educational and Psychological Testing* remains the 2014 edition while its announced revision is in progress. The published edition remains the normative testing reference until a replacement is issued. + +## 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. https://www.testingstandards.net/open-access-files.html + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE Standards Association. https://standards.ieee.org/ieee/754/6210/ + +Institute of Electrical and Electronics Engineers. (2024). *P754: Standard for floating-point arithmetic* [Active PAR]. IEEE Standards Association. https://standards.ieee.org/ieee/754/11684/ + +International Organization for Standardization, & International Electrotechnical Commission. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). https://www.iso.org/standard/80985.html + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 32dcab8434a9676854f3a470094aadfc4f3f417d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 17:02:03 +0900 Subject: [PATCH 388/576] test(validation): expose three-level SE double rounding --- ..._level_rational_scale_rounding_contract.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs new file mode 100644 index 000000000..0b3bb799b --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs @@ -0,0 +1,32 @@ +use validation_core::bias_standard_error; + +const EXPECTED_STANDARD_ERROR_BITS: u64 = 0x3f79_5555_5555_5555; + +fn represented_sample() -> [f64; 3] { + [ + 0.0, + f64::from_bits(0x3f74_0000_0000_0000), + f64::from_bits(0x3f95_0000_0000_0000), + ] +} + +#[test] +fn exact_three_level_rational_scale_preserves_correct_rounding() { + let [low, middle, high] = represented_sample(); + let truth = [0.0; 3]; + let permutations = [ + [low, middle, high], + [middle, low, high], + [high, low, middle], + ]; + + for recovered in permutations { + let standard_error = bias_standard_error(&truth, &recovered).expect("finite standard error"); + assert_eq!(standard_error.to_bits(), EXPECTED_STANDARD_ERROR_BITS); + + let mirrored = recovered.map(|value| -value); + let mirrored_standard_error = + bias_standard_error(&truth, &mirrored).expect("finite mirrored standard error"); + assert_eq!(mirrored_standard_error.to_bits(), EXPECTED_STANDARD_ERROR_BITS); + } +} From bee85e3df044e13a2df6c077cc87706b6cd78402 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 17:07:56 +0900 Subject: [PATCH 389/576] fix(validation): preserve exact three-level rational-square SE --- crates/validation_core/src/bias.rs | 109 +++++++++++++++++++++++++++-- 1 file changed, 102 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 4e5794999..44671f364 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -208,6 +208,53 @@ fn exact_subnormal_rational_scale( Some(Ok(f64::from_bits(rounded_units as u64))) } +fn exact_three_level_standard_error( + first_offset: f64, + second_offset: f64, +) -> Result, ValidationError> { + // For a translated three-observation sample `[0, x, y]`, + // `SE(mean)^2 = (x^2 + y^2 - xy) / 9`. Admit the direct identity only when + // every binary64 product/addition is proven error-free and the numerator is + // itself an exact represented square. Otherwise keep the general translated + // second-moment path rather than broadening the numerical claim. + let first_square = first_offset * first_offset; + let second_square = second_offset * second_offset; + let cross_product = first_offset * second_offset; + if !first_square.is_finite() + || !second_square.is_finite() + || !cross_product.is_finite() + || first_offset.mul_add(first_offset, -first_square) != 0.0 + || second_offset.mul_add(second_offset, -second_square) != 0.0 + || first_offset.mul_add(second_offset, -cross_product) != 0.0 + { + return Ok(None); + } + + let square_sum = first_square + second_square; + if !square_sum.is_finite() + || subtraction_roundoff(first_square, -second_square, square_sum) != 0.0 + { + return Ok(None); + } + let radicand = square_sum - cross_product; + if !radicand.is_finite() || subtraction_roundoff(square_sum, cross_product, radicand) != 0.0 { + return Ok(None); + } + + let exact_root = radicand.sqrt(); + if exact_root.mul_add(exact_root, -radicand) != 0.0 { + return Ok(None); + } + + let standard_error = + if let Some(subnormal_result) = exact_subnormal_rational_scale(exact_root, 1, 3) { + subnormal_result? + } else { + deterministic_representable_sum_over_count(&[exact_root], 3)? + }; + Ok(Some(standard_error)) +} + fn translated_residuals_from_anchor( diffs: &[f64], roundoffs: &[f64], @@ -341,6 +388,20 @@ fn exact_translated_residual_standard_error( } } + if translated.len() == 3 { + let nonzero_offsets: Vec<_> = translated + .iter() + .copied() + .filter(|value| *value != 0.0) + .collect(); + if nonzero_offsets.len() == 2 + && let Some(standard_error) = + exact_three_level_standard_error(nonzero_offsets[0], nonzero_offsets[1])? + { + return Ok(Some(standard_error)); + } + } + // Keep the translated binary64 geometry on an exact dyadic scale. Using the // largest translated value itself can turn an exactly represented gap d into // rounded(1/3) * d after the square-root stage and move the final SE by one @@ -454,14 +515,18 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Result Date: Sat, 5 Sep 2026 17:08:38 +0900 Subject: [PATCH 390/576] test(validation): cover all three-level SE permutations --- ...ndard_error_three_level_rational_scale_rounding_contract.rs | 3 +++ 1 file changed, 3 insertions(+) diff --git a/crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs index 0b3bb799b..ff4b35a97 100644 --- a/crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs @@ -16,8 +16,11 @@ fn exact_three_level_rational_scale_preserves_correct_rounding() { let truth = [0.0; 3]; let permutations = [ [low, middle, high], + [low, high, middle], [middle, low, high], + [middle, high, low], [high, low, middle], + [high, middle, low], ]; for recovered in permutations { From 5757dda1dc2699618d12c0f8a33913aedde67ad4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 17:08:49 +0900 Subject: [PATCH 391/576] docs(validation): record three-level rational-square SE repair --- ...idation-bias-standard-error-three-level-rational-square.md | 4 ++++ 1 file changed, 4 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-three-level-rational-square.md diff --git a/CHANGELOG.d/validation-bias-standard-error-three-level-rational-square.md b/CHANGELOG.d/validation-bias-standard-error-three-level-rational-square.md new file mode 100644 index 000000000..b944bf0a3 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-three-level-rational-square.md @@ -0,0 +1,4 @@ +### Fixed + +- `validation_core::bias_standard_error` now preserves an exact three-observation, three-level rational-square identity when every translated product/addition is proven error-free and the dispersion numerator is itself an exact represented square. The exact root is divided by three once instead of being reconstructed through rounded normalized moments and `sqrt`. +- The public represented-input contract `truth=[0,0,0]`, `recovered=[0,5/1024,21/1024]` now returns the correctly rounded binary64 standard error `0x3f79_5555_5555_5555` for all six permutations and their sign mirrors; the predecessor generic translated-moment path returned adjacent upper `0x3f79_5555_5555_5556`. From 4514a07331d62417f6b9fcf8e52d9800d185eff6 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 17:09:34 +0900 Subject: [PATCH 392/576] docs(research): trace three-level SE rational-square repair --- ...or-three-level-rational-square-rounding.md | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 docs/research/bias-standard-error-three-level-rational-square-rounding.md diff --git a/docs/research/bias-standard-error-three-level-rational-square-rounding.md b/docs/research/bias-standard-error-three-level-rational-square-rounding.md new file mode 100644 index 000000000..9b1e8b901 --- /dev/null +++ b/docs/research/bias-standard-error-three-level-rational-square-rounding.md @@ -0,0 +1,72 @@ +# Three-level rational-square bias standard-error rounding + +## Problem + +`validation_core::bias_standard_error` already preserves exact translated residual geometry when the represented `recovered - truth` values admit an error-free anchor translation. GAP-102 through GAP-105 added exact algebraic handling for two-level samples, and GAP-106/GAP-107 removed observation-order and anchor-conditioning effects. A remaining three-level case still reconstructed an exact rational square through normalized sums, products, division, and `sqrt`, which introduced one avoidable binary64 rounding boundary. + +The public represented-input counterexample uses + +- `truth = [0, 0, 0]`; +- `recovered = [0, 5/1024, 21/1024]`; +- binary64 values `0x0000_0000_0000_0000`, `0x3f74_0000_0000_0000`, and `0x3f95_0000_0000_0000`. + +The minimax exact translation uses the middle level as anchor, giving `[−5/1024, 0, 16/1024]`. For a translated three-observation sample `[0, x, y]`, + +`SE(mean)^2 = (x^2 + y^2 - xy) / 9`. + +Here the dispersion numerator is + +`25/2^20 + 256/2^20 + 80/2^20 = 361/2^20 = (19/1024)^2`, + +so the represented-input target is exactly `19/3072`. Its correctly rounded binary64 value is `0x1.9555555555555p-8`, bits `0x3f79_5555_5555_5555`. + +The predecessor general translated-moment path normalized to `[-0.3125, 0, 1]`, formed the squared-ratio value `0x1.40e38e38e38e4p-3`, then took `sqrt`. That produces adjacent upper `0x1.9555555555556p-8`, bits `0x3f79_5555_5555_5556`. The one-ULP displacement is deterministic representation error, not Monte Carlo uncertainty. + +## RED and causal repair + +Public RED `32dcab8434a9676854f3a470094aadfc4f3f417d` added `crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs`. The contract requires `0x3f79_5555_5555_5555` rather than predecessor `...5556`. + +Causal source repair `bee85e3df044e13a2df6c077cc87706b6cd78402` adds a bounded three-level identity path after the existing exact-translation and two-level admissions. It does not infer exactness from a rounded result. The path proceeds only when: + +1. the two non-zero anchor-relative offsets have finite binary64 squares and cross-product; +2. fused multiply-add residuals prove those three products error-free; +3. the sum of squares and subtraction of the cross-product are both error-free under the existing subtraction-roundoff proof; and +4. the resulting dispersion numerator has an exactly represented binary64 square root, verified by a zero fused multiply-add residual. + +Only then is that exact root divided by the scientific denominator `3` through the existing representable sum-over-count primitive, with the existing minimum-subnormal rational projection used where applicable. Any failed proof returns to the predecessor translated second-moment path. The repair therefore does not claim globally correctly rounded three-level or `n > 2` standard errors. + +Contract completion `8260bc0bff11abae2b05e0a85b5c1c374b8cbd49` covers all six permutations and each sign mirror. CHANGELOG evidence is `5757dda1dc2699618d12c0f8a33913aedde67ad4`. + +## Constraints and rejected alternatives + +A payload-specific branch for the `5/16/19` integer triple was rejected because it would encode the counterexample rather than the scientific invariant. Replacing TEPP Validation Evidence arithmetic with arbitrary-precision production arithmetic was rejected because the defect has a narrower proof boundary and reusable static arithmetic remains owned by `fast-mlsirm`. Applying the closed form to every three-level sample was rejected because inexact products, additions, or irrational roots would create a new unverified rounding surface. Reusing the generic normalized moment merely with a different exact anchor was also rejected: GAP-107 already minimizes anchor dynamic range, but this counterexample remains one ULP wrong even under its best exact anchor. + +## Scientific and standards trace + +IEEE 754-2019 remains the published floating-point arithmetic authority for the binary64 operations used here. IEEE P754 is an active revision project approved on 2024-06-06 and has not replaced the published 2019 standard. ISO/IEC 60559:2020 remains the published international adoption. These sources distinguish the defined rounding of individual operations from the separate numerical-analysis question of whether an algorithm introduces avoidable intermediate rounding boundaries. + +Morris, White, and Crowther (2019) distinguish performance-measure estimation from Monte Carlo uncertainty. The present defect changes the deterministic computation of the standard error of represented signed-bias observations; it must therefore be repaired before Monte Carlo uncertainty can be interpreted as simulation uncertainty rather than arithmetic error. + +The AERA/APA/NCME *Standards for Educational and Psychological Testing* published edition remains the 2014 edition; the sponsoring organizations announced the Joint Committee for revision in 2024. This repair changes numerical fidelity inside Validation Evidence and does not change the construct, intended score interpretation, or validation-policy authority, so no PRD/ADR target change is required. + +## Traceability + +- Bounded context: Validation Evidence. +- Module/API: `crates/validation_core/src/bias.rs` → `validation_core::bias_standard_error`. +- Public contract: `crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs`. +- RED: `32dcab8434a9676854f3a470094aadfc4f3f417d`. +- Causal source repair: `bee85e3df044e13a2df6c077cc87706b6cd78402`. +- Permutation/sign-mirror completion: `8260bc0bff11abae2b05e0a85b5c1c374b8cbd49`. +- CHANGELOG: `5757dda1dc2699618d12c0f8a33913aedde67ad4`. +- Owner boundary: no mutable `fast-mlsirm` or unreleased `contextual-orchestrator` source is consumed. +- Promotion boundary: exact-head hosted Rust/coverage/security/documentation evidence and qualifying independent review remain mandatory before merge or release. + +## 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. + +IEEE Computer Society. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). IEEE. https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization & International Electrotechnical Commission. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 67fa485248a8673f90bb71a43c2a58865a764383 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 18:00:28 +0900 Subject: [PATCH 393/576] test(validation): expose three-level scale invariance rounding --- ...r_three_level_scale_invariance_contract.rs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_three_level_scale_invariance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_three_level_scale_invariance_contract.rs b/crates/validation_core/tests/bias_standard_error_three_level_scale_invariance_contract.rs new file mode 100644 index 000000000..0b1d63e81 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_three_level_scale_invariance_contract.rs @@ -0,0 +1,35 @@ +use validation_core::bias_standard_error; + +const EXPECTED_STANDARD_ERROR_BITS: u64 = 0x64f9_5555_5555_5555; + +fn represented_sample() -> [f64; 3] { + [ + 0.0, + f64::from_bits(0x64f4_0000_0000_0000), + f64::from_bits(0x6515_0000_0000_0000), + ] +} + +#[test] +fn exact_three_level_rational_scale_is_invariant_under_exact_power_of_two_scaling() { + let [low, middle, high] = represented_sample(); + let truth = [0.0; 3]; + let permutations = [ + [low, middle, high], + [low, high, middle], + [middle, low, high], + [middle, high, low], + [high, low, middle], + [high, middle, low], + ]; + + for recovered in permutations { + let standard_error = bias_standard_error(&truth, &recovered).expect("finite standard error"); + assert_eq!(standard_error.to_bits(), EXPECTED_STANDARD_ERROR_BITS); + + let mirrored = recovered.map(|value| -value); + let mirrored_standard_error = + bias_standard_error(&truth, &mirrored).expect("finite mirrored standard error"); + assert_eq!(mirrored_standard_error.to_bits(), EXPECTED_STANDARD_ERROR_BITS); + } +} From dcaf25b37d9860e7956de5429d3ef5894b129b49 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 18:03:24 +0900 Subject: [PATCH 394/576] fix(validation): preserve three-level exact scale invariance --- crates/validation_core/src/bias.rs | 108 +++++++++++++++++++++++++---- 1 file changed, 93 insertions(+), 15 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 44671f364..31a0ddf53 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -215,15 +215,83 @@ fn exact_three_level_standard_error( // For a translated three-observation sample `[0, x, y]`, // `SE(mean)^2 = (x^2 + y^2 - xy) / 9`. Admit the direct identity only when // every binary64 product/addition is proven error-free and the numerator is - // itself an exact represented square. Otherwise keep the general translated - // second-moment path rather than broadening the numerical claim. + // itself an exact represented square. If raw products overflow, retry the + // same proof after an exactly reversible power-of-two normalization; this + // preserves the represented geometry instead of making proof admission depend + // on magnitude alone. Other failed proofs stay on the general translated path. let first_square = first_offset * first_offset; let second_square = second_offset * second_offset; let cross_product = first_offset * second_offset; - if !first_square.is_finite() - || !second_square.is_finite() - || !cross_product.is_finite() - || first_offset.mul_add(first_offset, -first_square) != 0.0 + let products_overflow = + !first_square.is_finite() || !second_square.is_finite() || !cross_product.is_finite(); + if products_overflow { + let max_magnitude = first_offset.abs().max(second_offset.abs()); + if max_magnitude == 0.0 || !max_magnitude.is_finite() { + return Ok(None); + } + let scale = exact_power_of_two_scale(max_magnitude); + let normalized_first = first_offset / scale; + let normalized_second = second_offset / scale; + if !normalized_first.is_finite() + || !normalized_second.is_finite() + || (first_offset != 0.0 && normalized_first == 0.0) + || (second_offset != 0.0 && normalized_second == 0.0) + || normalized_first * scale != first_offset + || normalized_second * scale != second_offset + { + return Ok(None); + } + + let normalized_first_square = normalized_first * normalized_first; + let normalized_second_square = normalized_second * normalized_second; + let normalized_cross_product = normalized_first * normalized_second; + if !normalized_first_square.is_finite() + || !normalized_second_square.is_finite() + || !normalized_cross_product.is_finite() + || normalized_first.mul_add(normalized_first, -normalized_first_square) != 0.0 + || normalized_second.mul_add(normalized_second, -normalized_second_square) != 0.0 + || normalized_first.mul_add(normalized_second, -normalized_cross_product) != 0.0 + { + return Ok(None); + } + + let normalized_square_sum = normalized_first_square + normalized_second_square; + if !normalized_square_sum.is_finite() + || subtraction_roundoff( + normalized_first_square, + -normalized_second_square, + normalized_square_sum, + ) != 0.0 + { + return Ok(None); + } + let normalized_radicand = normalized_square_sum - normalized_cross_product; + if !normalized_radicand.is_finite() + || subtraction_roundoff( + normalized_square_sum, + normalized_cross_product, + normalized_radicand, + ) != 0.0 + { + return Ok(None); + } + + let normalized_exact_root = normalized_radicand.sqrt(); + if normalized_exact_root.mul_add(normalized_exact_root, -normalized_radicand) != 0.0 { + return Ok(None); + } + let normalized_standard_error = + deterministic_representable_sum_over_count(&[normalized_exact_root], 3)?; + let standard_error = scale * normalized_standard_error; + if !standard_error.is_finite() + || (standard_error == 0.0 && normalized_standard_error != 0.0) + { + return Err(ValidationError::InvalidInput); + } + return Ok(Some(standard_error)); + } + + if first_offset.mul_add(first_offset, -first_square) != 0.0 || second_offset.mul_add(second_offset, -second_square) != 0.0 || first_offset.mul_add(second_offset, -cross_product) != 0.0 { @@ -518,15 +586,17 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 18:03:33 +0900 Subject: [PATCH 395/576] docs(changelog): record three-level scale-invariance repair --- ...idation-bias-standard-error-three-level-scale-invariance.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-three-level-scale-invariance.md diff --git a/CHANGELOG.d/validation-bias-standard-error-three-level-scale-invariance.md b/CHANGELOG.d/validation-bias-standard-error-three-level-scale-invariance.md new file mode 100644 index 000000000..ba55dd17f --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-three-level-scale-invariance.md @@ -0,0 +1,3 @@ +### Fixed + +- Preserve the exact three-observation rational-square bias standard-error identity when otherwise valid represented offsets are scaled high enough for their raw squares or cross-product to overflow. TEPP now retries the same error-free proof after an exactly reversible power-of-two normalization instead of falling back to a normalized moment path that can shift the correctly rounded result by one ULP. From 1eaac11e715144127048fc20033719777528c692 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 18:04:17 +0900 Subject: [PATCH 396/576] docs(research): trace three-level scale invariance repair --- ...dard-error-three-level-scale-invariance.md | 67 +++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 docs/research/bias-standard-error-three-level-scale-invariance.md diff --git a/docs/research/bias-standard-error-three-level-scale-invariance.md b/docs/research/bias-standard-error-three-level-scale-invariance.md new file mode 100644 index 000000000..a5852963f --- /dev/null +++ b/docs/research/bias-standard-error-three-level-scale-invariance.md @@ -0,0 +1,67 @@ +# GAP-109 — exact three-level bias-SE proof must be scale invariant + +## Problem + +GAP-108 added a bounded exact path for three represented observations whose translated offsets satisfy + +`SE(mean)^2 = (x^2 + y^2 - xy) / 9` + +and whose dispersion numerator is itself an exactly represented square. The proof was nevertheless evaluated on the raw offsets. That made admission depend on magnitude rather than only on represented geometry: multiplying a valid dyadic sample by an exact power of two can overflow `x^2`, `y^2`, or `xy` even when the final standard error is finite and representable. + +The public counterexample is the exact power-of-two scaling of GAP-108 by `2^600`: + +- `truth = [0, 0, 0]` +- `recovered = [0, 5 * 2^590, 21 * 2^590]` +- represented bits: `0x0000000000000000`, `0x64f4000000000000`, `0x6515000000000000` +- minimax exact translation: `[-5 * 2^590, 0, 16 * 2^590]` + +For the unscaled geometry, `x^2 + y^2 - xy = 361 / 2^20 = (19 / 1024)^2`; scaling by `2^600` therefore gives the exact target + +`SE(mean) = (19 / 3072) * 2^600 = 0x1.9555555555555p+592` + +with binary64 bits `0x64f9555555555555`. + +Before GAP-109, the raw square/cross-product proof overflowed and the implementation fell back to the general translated normalized-moment path. That path evaluates the same normalized geometry `[-5/16, 0, 1]` through moment reconstruction and `sqrt`, returning adjacent upper `0x64f9555555555556`. The one-ULP shift is deterministic arithmetic error, not Monte Carlo uncertainty. + +## Constraints + +The repair must not weaken the GAP-108 error-free admission, make arbitrary-precision arithmetic a production dependency, special-case the payload, or move reusable static psychometric estimation out of `fast-mlsirm`. It must preserve permutation/sign symmetry and fail closed whenever the represented geometry cannot be proved exactly reversible. + +## Decision + +When any of the three raw products overflows, `validation_core::bias` now retries the same GAP-108 proof after dividing both offsets by the exact power-of-two binade scale of their maximum magnitude. Admission requires: + +1. both normalized offsets to remain finite and nonzero when their sources are nonzero; +2. multiplying each normalized offset by the scale to reconstruct the original represented offset exactly; +3. normalized squares and cross-product to be finite and FMA-proven error-free; +4. square addition and cross-product subtraction to be error-free; +5. the normalized dispersion numerator to have an exactly represented square root. + +Only then is the normalized root divided by three through the existing deterministic representable-denominator primitive and restored by the same exact power-of-two scale. Raw finite-product cases keep the GAP-108 path unchanged, including its subnormal exact-rational projection. If normalization or any proof fails, the predecessor translated path remains authoritative. + +This is intentionally narrower than a claim that all `n > 2` standard errors are globally correctly rounded. + +## Alternatives rejected + +Payload-specific branching on `5/16/19` would encode the fixture rather than the invariant. Unconditionally normalizing every three-level proof would broaden behavior for low-scale/subnormal cases already covered by GAP-105/GAP-108. Replacing the bounded binary64 proof with arbitrary precision would duplicate numerical ownership and enlarge production cost without evidence that the broader dependency is required. Accepting overflow as proof failure is rejected because exact power-of-two scaling is representation preserving here and would make scientific output depend on unit magnitude. + +## Standards and methodological trace + +IEEE P754 is an active revision PAR approved 2024-06-06 and identifies IEEE 754-2019 as the standard it is intended to supersede. ISO/IEC 60559:2020 remains a published International Standard at stage 60.60, and IEEE/ISO/IEC 60559-2020 is listed as an active adoption of IEEE 754-2019. These authorities support treating the sequence and destination format of binary64 operations as part of the reproducible numerical contract rather than assuming algebraically equivalent floating-point expressions are interchangeable. + +Morris, White, and Crowther (2019, *Statistics in Medicine*, https://doi.org/10.1002/sim.8086) distinguish deterministic performance-measure computation from Monte Carlo error due to a finite number of simulation repetitions. GAP-109 concerns the former: the represented sample is fixed and only the arithmetic path changes. + +AERA, APA, and NCME continue to publish the 2014 *Standards for Educational and Psychological Testing* as the current public edition. This repair is therefore recorded as validity-evidence computation infrastructure; it does not substitute numerical implementation detail for the broader evidentiary requirements governing score interpretation and use. + +## Traceability + +- Public RED: `67fa485248a8673f90bb71a43c2a58865a764383` +- Contract: `crates/validation_core/tests/bias_standard_error_three_level_scale_invariance_contract.rs` +- Causal Rust repair: `dcaf25b37d9860e7956de5429d3ef5894b129b49` +- Production API/module: `validation_core::bias_standard_error` / `crates/validation_core/src/bias.rs` +- CHANGELOG: `31b1aff811703a41e3524e804d095be32621b004` +- Protected base observed before repair: `main@a243f18da4a4ca8a8d068c39922537f1f8ed6ad0` + +## Remaining risk + +The exact three-observation shortcut still applies only when its rational-square identity can be proved in represented binary64 arithmetic. Other three-level and larger-sample geometries remain on the translated second-moment path and may justify separate findings only when a concrete represented-input counterexample demonstrates a scientifically material discrepancy. From fe013e7dbd9b6c99371fe28ff3f6aa2cb2915408 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 19:03:10 +0900 Subject: [PATCH 397/576] test(validation): expose three-level square underflow --- ...or_three_level_underflow_scale_contract.rs | 31 +++++++++++++++++++ 1 file changed, 31 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_three_level_underflow_scale_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_three_level_underflow_scale_contract.rs b/crates/validation_core/tests/bias_standard_error_three_level_underflow_scale_contract.rs new file mode 100644 index 000000000..15cf86ccd --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_three_level_underflow_scale_contract.rs @@ -0,0 +1,31 @@ +use validation_core::bias_standard_error; + +const EXPECTED_STANDARD_ERROR_BITS: u64 = 0x0009_3cd3_a2c8_198e; + +fn represented_sample() -> [f64; 3] { + [0.0, f64::MIN_POSITIVE, 2.0 * f64::MIN_POSITIVE] +} + +#[test] +fn nonzero_three_level_standard_error_survives_square_underflow() { + let [low, middle, high] = represented_sample(); + let truth = [0.0; 3]; + let permutations = [ + [low, middle, high], + [low, high, middle], + [middle, low, high], + [middle, high, low], + [high, low, middle], + [high, middle, low], + ]; + + for recovered in permutations { + let standard_error = bias_standard_error(&truth, &recovered).expect("finite standard error"); + assert_eq!(standard_error.to_bits(), EXPECTED_STANDARD_ERROR_BITS); + + let mirrored = recovered.map(|value| -value); + let mirrored_standard_error = + bias_standard_error(&truth, &mirrored).expect("finite mirrored standard error"); + assert_eq!(mirrored_standard_error.to_bits(), EXPECTED_STANDARD_ERROR_BITS); + } +} From d3660d44f1bc315e2e34ecfcfa74c26b8f1cd257 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 19:05:29 +0900 Subject: [PATCH 398/576] fix(validation): preserve three-level subnormal dispersion --- crates/validation_core/src/bias.rs | 41 +++++++++++++++++++----------- 1 file changed, 26 insertions(+), 15 deletions(-) diff --git a/crates/validation_core/src/bias.rs b/crates/validation_core/src/bias.rs index 31a0ddf53..0863e03e8 100644 --- a/crates/validation_core/src/bias.rs +++ b/crates/validation_core/src/bias.rs @@ -215,16 +215,21 @@ fn exact_three_level_standard_error( // For a translated three-observation sample `[0, x, y]`, // `SE(mean)^2 = (x^2 + y^2 - xy) / 9`. Admit the direct identity only when // every binary64 product/addition is proven error-free and the numerator is - // itself an exact represented square. If raw products overflow, retry the - // same proof after an exactly reversible power-of-two normalization; this - // preserves the represented geometry instead of making proof admission depend - // on magnitude alone. Other failed proofs stay on the general translated path. + // itself an exact represented square. If raw products overflow or underflow + // to zero, retry the same proof after an exactly reversible power-of-two + // normalization; this preserves the represented geometry instead of making + // proof admission depend on magnitude alone. Other failed proofs stay on the + // general translated path. let first_square = first_offset * first_offset; let second_square = second_offset * second_offset; let cross_product = first_offset * second_offset; - let products_overflow = - !first_square.is_finite() || !second_square.is_finite() || !cross_product.is_finite(); - if products_overflow { + let products_leave_represented_range = !first_square.is_finite() + || !second_square.is_finite() + || !cross_product.is_finite() + || (first_offset != 0.0 && first_square == 0.0) + || (second_offset != 0.0 && second_square == 0.0) + || (first_offset != 0.0 && second_offset != 0.0 && cross_product == 0.0); + if products_leave_represented_range { let max_magnitude = first_offset.abs().max(second_offset.abs()); if max_magnitude == 0.0 || !max_magnitude.is_finite() { return Ok(None); @@ -586,14 +591,15 @@ pub fn mean_bias(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 19:05:53 +0900 Subject: [PATCH 399/576] docs(changelog): record three-level underflow repair --- ...lidation-bias-standard-error-three-level-underflow-scale.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-three-level-underflow-scale.md diff --git a/CHANGELOG.d/validation-bias-standard-error-three-level-underflow-scale.md b/CHANGELOG.d/validation-bias-standard-error-three-level-underflow-scale.md new file mode 100644 index 000000000..1cbfa2ff7 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-three-level-underflow-scale.md @@ -0,0 +1,3 @@ +### Fixed + +- Prevent `bias_standard_error` from accepting underflowed three-level square/cross-product intermediates as exact zero. Exact translated three-observation samples now retry the bounded proof on an exactly reversible power-of-two scale when nonzero products underflow, preserving representable nonzero dispersion and retaining the predecessor fallback when the normalized identity is not exactly provable. From 8bb3557298c77abb3dcbfed6844abf829531a727 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 19:06:45 +0900 Subject: [PATCH 400/576] docs(research): trace three-level underflow invariance --- ...-three-level-underflow-scale-invariance.md | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 docs/research/bias-standard-error-three-level-underflow-scale-invariance.md diff --git a/docs/research/bias-standard-error-three-level-underflow-scale-invariance.md b/docs/research/bias-standard-error-three-level-underflow-scale-invariance.md new file mode 100644 index 000000000..2ee39489e --- /dev/null +++ b/docs/research/bias-standard-error-three-level-underflow-scale-invariance.md @@ -0,0 +1,65 @@ +# GAP-110 — three-level standard error underflow-scale invariance + +## Finding + +`bias_standard_error` could return exact zero for a represented three-observation residual sample with nonzero, representable dispersion when the exact three-level proof evaluated raw products before normalization. + +The public regression sample is + +- `truth = [0, 0, 0]` +- `recovered = [0, f64::MIN_POSITIVE, 2 * f64::MIN_POSITIVE]` +- expected `SE(mean)` bits: `0x0009_3cd3_a2c8_198e` + +The canonical exact translation is `[-m, 0, m]`, where `m = 2^-1022`. For three represented observations `[0, x, y]`, + +`SE(mean)^2 = (x^2 + y^2 - xy) / 9`. + +With `x = -m` and `y = m`, the exact result is `m / sqrt(3)`, which is a nonzero subnormal binary64 value. The predecessor GAP-109 path formed `x*x`, `y*y`, and `x*y` at the raw scale. All three exact products lie below the binary64 minimum-subnormal magnitude and round to signed zero. The subsequent FMA residual checks also round those exact products to zero, so a zero product could be mistaken for an error-free product. The proof then admitted a zero radicand and returned `SE(mean) = 0`. + +This is deterministic performance-measure arithmetic error, not Monte Carlo uncertainty. The represented observations and target estimand are fixed; permutation or sign reflection must not change whether a nonzero standard error survives the arithmetic path. + +## Causal repair + +Public RED: `fe013e7dbd9b6c99371fe28ff3f6aa2cb2915408`. + +Rust repair: `d3660d44f1bc315e2e34ecfcfa74c26b8f1cd257` in `crates/validation_core/src/bias.rs`. + +The repair does not broaden exact-three-level admission. It extends the existing GAP-109 normalization trigger from raw product overflow to raw product loss of represented range: a nonzero square or cross-product that rounds to zero is treated as an inability to prove the identity at that magnitude. The same exactly reversible `exact_power_of_two_scale` retry is used. The normalized products must still be finite, nonzero where the source product is mathematically nonzero, exactly reconstruct the represented offsets after scale restoration, satisfy the existing FMA/error-free sum and subtraction checks, and produce an exactly represented square root before the direct identity can be admitted. + +For the GAP-110 sample, normalization produces offsets `[-1, 1]`. Its radicand is `3`, whose square root is not exactly represented, so the bounded identity correctly declines admission. Control returns to the existing translated second-moment path, which already normalizes the represented geometry before squaring and returns the correctly rounded nonzero subnormal result `0x0009_3cd3_a2c8_198e`. + +The public contract covers all six permutations and their sign mirrors in `crates/validation_core/tests/bias_standard_error_three_level_underflow_scale_contract.rs`. An internal unit contract also asserts that the bounded exact-three-level helper returns `None` for `(-f64::MIN_POSITIVE, f64::MIN_POSITIVE)` instead of manufacturing an exact zero proof. + +## Alternatives rejected + +Returning `InvalidInput` whenever a raw square underflows was rejected because the requested final standard error can remain representable, as it does here. Treating FMA residual zero as sufficient proof was rejected because an exact result below the destination format can round to zero in both the product and the residual check. Applying unconditional normalization to every three-level sample was rejected because GAP-108/GAP-109 already preserve a narrower finite-product fast path with explicit exactness evidence. Arbitrary-precision production arithmetic remains out of scope for this TEPP Validation Evidence repair and would cross the reusable numerical-owner boundary without a demonstrated need. + +## Scientific and standards trace + +IEEE 754-2019 remains the active published IEEE floating-point standard. IEEE P754 is an active revision PAR, approved 2024-06-06, intended to supersede 754-2019 but is not a published replacement as of 2026-09-05. ISO/IEC 60559:2020 remains the published international adoption of IEEE 754-2019. The repair therefore documents destination-format underflow and exact-operation evidence against the current published standard rather than a draft revision. + +Morris, White, and Crowther's ADEMP framework distinguishes the deterministic definition and computation of a performance measure from Monte Carlo standard error caused by a finite number of simulation repetitions. GAP-110 concerns the former: the same represented sample cannot legitimately move from nonzero dispersion to zero because an intermediate product falls outside binary64 range. + +AERA, APA, and NCME continue to publish the 2014 *Standards for Educational and Psychological Testing* while the Joint Committee is revising that edition. TEPP therefore keeps the 2014 edition as the normative testing-standards authority and treats revision materials as development evidence only. + +## Traceability + +- Bounded context: Validation Evidence +- Aggregate/API: `bias_standard_error` +- Production module: `crates/validation_core/src/bias.rs` +- Public contract: `crates/validation_core/tests/bias_standard_error_three_level_underflow_scale_contract.rs` +- RED: `fe013e7dbd9b6c99371fe28ff3f6aa2cb2915408` +- Causal repair: `d3660d44f1bc315e2e34ecfcfa74c26b8f1cd257` +- CHANGELOG: `CHANGELOG.d/validation-bias-standard-error-three-level-underflow-scale.md` +- Predecessor: GAP-109 `1eaac11e715144127048fc20033719777528c692` +- Owner boundary: reusable static psychometric arithmetic remains in `fast-mlsirm`; no mutable `contextual-orchestrator` contract is consumed. + +## 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. + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +ISO/IEC. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 978c422cbdccff02605b5d220bd2564900a830d5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 20:04:54 +0900 Subject: [PATCH 401/576] test(validation): expose four-observation ratio-sqrt rounding --- ...bservation_ratio_sqrt_rounding_contract.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_four_observation_ratio_sqrt_rounding_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_four_observation_ratio_sqrt_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_four_observation_ratio_sqrt_rounding_contract.rs new file mode 100644 index 000000000..d35a69db9 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_four_observation_ratio_sqrt_rounding_contract.rs @@ -0,0 +1,25 @@ +use validation_core::bias_standard_error; + +fn assert_four_observation_contract(recovered: [f64; 4]) { + let truth = [0.0; 4]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x3ff8_df7d_a2e6_6e88, + "exact represented-input SE is sqrt(29/12); ratio-first sqrt must not round one ULP low" + ); +} + +#[test] +fn exact_four_observation_dispersion_avoids_ratio_sqrt_double_rounding() { + let samples = [ + [0.0, 1.0, 2.0, 7.0], + [7.0, 2.0, 1.0, 0.0], + [1.0, 7.0, 0.0, 2.0], + [2.0, 0.0, 7.0, 1.0], + ]; + for recovered in samples { + assert_four_observation_contract(recovered); + assert_four_observation_contract(recovered.map(|value| -value)); + } +} From 3de7a73781576b3ad2b58d0c5bd5341ebf2300c2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 20:07:03 +0900 Subject: [PATCH 402/576] fix(validation): round exact four-observation dispersion once --- .../src/bias_standard_error.rs | 279 ++++++++++++++++++ 1 file changed, 279 insertions(+) create mode 100644 crates/validation_core/src/bias_standard_error.rs diff --git a/crates/validation_core/src/bias_standard_error.rs b/crates/validation_core/src/bias_standard_error.rs new file mode 100644 index 000000000..2c2a285fa --- /dev/null +++ b/crates/validation_core/src/bias_standard_error.rs @@ -0,0 +1,279 @@ +//! Exact represented-input admission for mean-bias standard error. +//! +//! The general bias implementation remains the fallback authority. This module +//! admits only a bounded four-observation identity whose residual and pairwise +//! differences are proven exact in binary64 and whose dyadic pair-distance +//! numerator fits `u128`; the exact rational square root is then rounded against +//! binary64 midpoints without first rounding the ratio under the square root. + +use crate::ValidationError; +use core::cmp::Ordering; + +fn subtraction_roundoff(recovered: f64, truth: f64, residual: f64) -> f64 { + let negated_truth = -truth; + let truth_virtual = residual - recovered; + let recovered_virtual = residual - truth_virtual; + let recovered_roundoff = recovered - recovered_virtual; + let truth_roundoff = negated_truth - truth_virtual; + recovered_roundoff + truth_roundoff +} + +fn positive_dyadic(value: f64) -> Option<(u128, i32)> { + if !value.is_finite() || value <= 0.0 { + return None; + } + let bits = value.to_bits(); + let exponent_bits = ((bits >> 52) & 0x7ff) as i32; + let fraction = bits & 0x000f_ffff_ffff_ffff; + let (mut significand, mut exponent) = if exponent_bits == 0 { + (fraction as u128, -1074) + } else { + ( + ((1_u64 << 52) | fraction) as u128, + exponent_bits - 1023 - 52, + ) + }; + if significand == 0 { + return None; + } + let trailing = significand.trailing_zeros(); + significand >>= trailing; + exponent += trailing as i32; + Some((significand, exponent)) +} + +fn multiply_by_power_of_two(value: u128, shift: u32) -> Option { + let factor = 1_u128.checked_shl(shift)?; + value.checked_mul(factor) +} + +fn compare_scaled_ratio_to_dyadic_square( + numerator: u128, + numerator_exponent: i32, + denominator: u128, + significand: u128, + exponent: i32, +) -> Option { + let square = significand.checked_mul(significand)?; + let right = denominator.checked_mul(square)?; + let square_exponent = exponent.checked_mul(2)?; + let exponent_delta = numerator_exponent.checked_sub(square_exponent)?; + if exponent_delta >= 0 { + let left = multiply_by_power_of_two(numerator, exponent_delta as u32)?; + Some(left.cmp(&right)) + } else { + let shifted_right = multiply_by_power_of_two(right, (-exponent_delta) as u32)?; + Some(numerator.cmp(&shifted_right)) + } +} + +fn midpoint_dyadic(left: f64, right: f64) -> Option<(u128, i32)> { + let (left_significand, left_exponent) = positive_dyadic(left)?; + let (right_significand, right_exponent) = positive_dyadic(right)?; + let common_exponent = left_exponent.min(right_exponent); + let left_shift = left_exponent.checked_sub(common_exponent)? as u32; + let right_shift = right_exponent.checked_sub(common_exponent)? as u32; + let left_units = multiply_by_power_of_two(left_significand, left_shift)?; + let right_units = multiply_by_power_of_two(right_significand, right_shift)?; + let mut midpoint_significand = left_units.checked_add(right_units)?; + let mut midpoint_exponent = common_exponent.checked_sub(1)?; + let trailing = midpoint_significand.trailing_zeros(); + midpoint_significand >>= trailing; + midpoint_exponent += trailing as i32; + Some((midpoint_significand, midpoint_exponent)) +} + +fn exact_power_of_two(exponent: i32) -> Option { + if (-1022..=1023).contains(&exponent) { + return Some(f64::from_bits(((exponent + 1023) as u64) << 52)); + } + if (-1074..=-1023).contains(&exponent) { + return Some(f64::from_bits(1_u64 << (exponent + 1074))); + } + None +} + +fn correctly_rounded_scaled_sqrt_ratio( + numerator: u128, + denominator: u128, + unit_exponent: i32, +) -> Option { + if numerator == 0 || denominator == 0 || numerator > (1_u128 << 53) { + return None; + } + let unit = exact_power_of_two(unit_exponent)?; + let denominator_f64 = denominator as f64; + if denominator_f64 as u128 != denominator { + return None; + } + let mut candidate = ((numerator as f64) / denominator_f64).sqrt() * unit; + if !candidate.is_finite() || candidate <= 0.0 { + return None; + } + let target_exponent = unit_exponent.checked_mul(2)?; + + for _ in 0..4 { + let (candidate_significand, candidate_exponent) = positive_dyadic(candidate)?; + let candidate_comparison = compare_scaled_ratio_to_dyadic_square( + numerator, + target_exponent, + denominator, + candidate_significand, + candidate_exponent, + )?; + if candidate_comparison == Ordering::Equal { + return Some(candidate); + } + + let upward = candidate_comparison == Ordering::Greater; + let bits = candidate.to_bits(); + let neighbor = if upward { + f64::from_bits(bits.checked_add(1)?) + } else { + if bits == 1 { + return None; + } + f64::from_bits(bits - 1) + }; + if !neighbor.is_finite() || neighbor <= 0.0 { + return None; + } + let (midpoint_significand, midpoint_exponent) = midpoint_dyadic(candidate, neighbor)?; + let midpoint_comparison = compare_scaled_ratio_to_dyadic_square( + numerator, + target_exponent, + denominator, + midpoint_significand, + midpoint_exponent, + )?; + + let neighbor_is_closer = if upward { + midpoint_comparison == Ordering::Greater + } else { + midpoint_comparison == Ordering::Less + }; + if neighbor_is_closer { + candidate = neighbor; + continue; + } + if midpoint_comparison == Ordering::Equal && candidate.to_bits() & 1 == 1 { + return Some(neighbor); + } + return Some(candidate); + } + None +} + +fn exact_four_observation_standard_error( + truth: &[f64], + recovered: &[f64], +) -> Option> { + if truth.len() != 4 || recovered.len() != 4 { + return None; + } + + let mut residuals = [0.0; 4]; + for index in 0..4 { + let truth_value = truth[index]; + let recovered_value = recovered[index]; + if !truth_value.is_finite() || !recovered_value.is_finite() { + return None; + } + let residual = recovered_value - truth_value; + if !residual.is_finite() + || subtraction_roundoff(recovered_value, truth_value, residual) != 0.0 + { + return None; + } + residuals[index] = residual; + } + + let mut pair_dyadics = Vec::with_capacity(6); + let mut unit_exponent = i32::MAX; + for left in 0..4 { + for right in left + 1..4 { + let difference = residuals[left] - residuals[right]; + if !difference.is_finite() + || subtraction_roundoff(residuals[left], residuals[right], difference) != 0.0 + { + return None; + } + if difference == 0.0 { + pair_dyadics.push(None); + continue; + } + let dyadic = positive_dyadic(difference.abs())?; + unit_exponent = unit_exponent.min(dyadic.1); + pair_dyadics.push(Some(dyadic)); + } + } + if unit_exponent == i32::MAX { + return Some(Ok(0.0)); + } + + let mut pair_square_sum = 0_u128; + for dyadic in pair_dyadics.into_iter().flatten() { + let shift = dyadic.1.checked_sub(unit_exponent)? as u32; + let coefficient = multiply_by_power_of_two(dyadic.0, shift)?; + let square = coefficient.checked_mul(coefficient)?; + pair_square_sum = pair_square_sum.checked_add(square)?; + } + if pair_square_sum == 0 { + return Some(Ok(0.0)); + } + + // For n=4, sum((ri-rj)^2, i Result { + if let Some(result) = exact_four_observation_standard_error(truth, recovered) { + return result; + } + crate::bias::bias_standard_error(truth, recovered) +} + +#[cfg(test)] +mod tests { + use super::{correctly_rounded_scaled_sqrt_ratio, exact_four_observation_standard_error}; + + #[test] + fn exact_ratio_sqrt_rounds_against_binary64_midpoint() { + assert_eq!( + correctly_rounded_scaled_sqrt_ratio(116, 48, 0) + .expect("bounded exact ratio") + .to_bits(), + 0x3ff8_df7d_a2e6_6e88 + ); + } + + #[test] + fn four_observation_identity_is_power_of_two_scale_invariant() { + let truth = [0.0; 4]; + let recovered = [0.0, 1.0, 2.0, 7.0]; + assert_eq!( + exact_four_observation_standard_error(&truth, &recovered) + .expect("admitted") + .expect("representable") + .to_bits(), + 0x3ff8_df7d_a2e6_6e88 + ); + + let unit = 2.0_f64.powi(400); + let scaled = recovered.map(|value| value * unit); + let expected = f64::from_bits(0x58f8_df7d_a2e6_6e88); + assert_eq!( + exact_four_observation_standard_error(&truth, &scaled) + .expect("scaled admitted") + .expect("scaled representable"), + expected + ); + } +} From ea609474a102dbf0ed3cd544c200f542a59e2760 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 20:07:24 +0900 Subject: [PATCH 403/576] fix(validation): route bias SE through exact dispersion admission --- crates/validation_core/src/lib.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/src/lib.rs b/crates/validation_core/src/lib.rs index 30636924b..d1488b26f 100644 --- a/crates/validation_core/src/lib.rs +++ b/crates/validation_core/src/lib.rs @@ -13,6 +13,7 @@ //! Metrics are pure `f64` CPU reference implementations. mod bias; +mod bias_standard_error; mod claim; mod coverage; mod coverage_evidence; @@ -28,7 +29,7 @@ mod temporal_order; mod validation_evidence; /// Standard error of mean signed bias. -pub use bias::bias_standard_error; +pub use bias_standard_error::bias_standard_error; /// Mean signed bias. pub use bias::mean_bias; /// Four ADR 0014 claim authorities. From 2c68909557a62d09d9719df20e8ffd1e644ea7a1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 20:10:07 +0900 Subject: [PATCH 404/576] refactor(validation): name bounded bias SE service by role --- crates/validation_core/src/bias_se.rs | 283 ++++++++++++++++++++++++++ 1 file changed, 283 insertions(+) create mode 100644 crates/validation_core/src/bias_se.rs diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs new file mode 100644 index 000000000..75c986396 --- /dev/null +++ b/crates/validation_core/src/bias_se.rs @@ -0,0 +1,283 @@ +//! Exact represented-input admission for mean-bias standard error. +//! +//! The general bias implementation remains the fallback authority. This module +//! admits only a bounded four-observation identity whose residual and pairwise +//! differences are proven exact in binary64 and whose dyadic pair-distance +//! numerator fits `u128`; the exact rational square root is then rounded against +//! binary64 midpoints without first rounding the ratio under the square root. + +use crate::ValidationError; +use core::cmp::Ordering; + +fn subtraction_roundoff(recovered: f64, truth: f64, residual: f64) -> f64 { + let negated_truth = -truth; + let truth_virtual = residual - recovered; + let recovered_virtual = residual - truth_virtual; + let recovered_roundoff = recovered - recovered_virtual; + let truth_roundoff = negated_truth - truth_virtual; + recovered_roundoff + truth_roundoff +} + +fn positive_dyadic(value: f64) -> Option<(u128, i32)> { + if !value.is_finite() || value <= 0.0 { + return None; + } + let bits = value.to_bits(); + let exponent_bits = i32::try_from((bits >> 52) & 0x7ff).ok()?; + let fraction = bits & 0x000f_ffff_ffff_ffff; + let (mut significand, mut exponent) = if exponent_bits == 0 { + (u128::from(fraction), -1074) + } else { + ( + u128::from((1_u64 << 52) | fraction), + exponent_bits - 1023 - 52, + ) + }; + if significand == 0 { + return None; + } + let trailing = significand.trailing_zeros(); + significand >>= trailing; + exponent += i32::try_from(trailing).ok()?; + Some((significand, exponent)) +} + +fn multiply_by_power_of_two(value: u128, shift: u32) -> Option { + let factor = 1_u128.checked_shl(shift)?; + value.checked_mul(factor) +} + +fn compare_scaled_ratio_to_dyadic_square( + numerator: u128, + numerator_exponent: i32, + denominator: u128, + significand: u128, + exponent: i32, +) -> Option { + let square = significand.checked_mul(significand)?; + let right = denominator.checked_mul(square)?; + let square_exponent = exponent.checked_mul(2)?; + let exponent_delta = numerator_exponent.checked_sub(square_exponent)?; + if exponent_delta >= 0 { + let left = multiply_by_power_of_two(numerator, exponent_delta.unsigned_abs())?; + Some(left.cmp(&right)) + } else { + let shifted_right = multiply_by_power_of_two(right, exponent_delta.unsigned_abs())?; + Some(numerator.cmp(&shifted_right)) + } +} + +fn midpoint_dyadic(left: f64, right: f64) -> Option<(u128, i32)> { + let (left_significand, left_exponent) = positive_dyadic(left)?; + let (right_significand, right_exponent) = positive_dyadic(right)?; + let common_exponent = left_exponent.min(right_exponent); + let left_shift = left_exponent.checked_sub(common_exponent)?.unsigned_abs(); + let right_shift = right_exponent.checked_sub(common_exponent)?.unsigned_abs(); + let left_units = multiply_by_power_of_two(left_significand, left_shift)?; + let right_units = multiply_by_power_of_two(right_significand, right_shift)?; + let mut midpoint_significand = left_units.checked_add(right_units)?; + let mut midpoint_exponent = common_exponent.checked_sub(1)?; + let trailing = midpoint_significand.trailing_zeros(); + midpoint_significand >>= trailing; + midpoint_exponent += i32::try_from(trailing).ok()?; + Some((midpoint_significand, midpoint_exponent)) +} + +fn exact_power_of_two(exponent: i32) -> Option { + if (-1022..=1023).contains(&exponent) { + let biased_exponent = u64::try_from(exponent + 1023).ok()?; + return Some(f64::from_bits(biased_exponent << 52)); + } + if (-1074..=-1023).contains(&exponent) { + let shift = u32::try_from(exponent + 1074).ok()?; + return Some(f64::from_bits(1_u64 << shift)); + } + None +} + +fn correctly_rounded_scaled_sqrt_ratio( + numerator: u128, + denominator: u128, + unit_exponent: i32, +) -> Option { + const MAX_EXACT_BINARY64_INTEGER: u128 = 1_u128 << 53; + if numerator == 0 + || denominator == 0 + || numerator > MAX_EXACT_BINARY64_INTEGER + || denominator > MAX_EXACT_BINARY64_INTEGER + { + return None; + } + let unit = exact_power_of_two(unit_exponent)?; + let denominator_f64 = denominator as f64; + let mut candidate = ((numerator as f64) / denominator_f64).sqrt() * unit; + if !candidate.is_finite() || candidate <= 0.0 { + return None; + } + let target_exponent = unit_exponent.checked_mul(2)?; + + for _ in 0..4 { + let (candidate_significand, candidate_exponent) = positive_dyadic(candidate)?; + let candidate_comparison = compare_scaled_ratio_to_dyadic_square( + numerator, + target_exponent, + denominator, + candidate_significand, + candidate_exponent, + )?; + if candidate_comparison == Ordering::Equal { + return Some(candidate); + } + + let upward = candidate_comparison == Ordering::Greater; + let bits = candidate.to_bits(); + let neighbor = if upward { + f64::from_bits(bits.checked_add(1)?) + } else { + if bits == 1 { + return None; + } + f64::from_bits(bits - 1) + }; + if !neighbor.is_finite() || neighbor <= 0.0 { + return None; + } + let (midpoint_significand, midpoint_exponent) = midpoint_dyadic(candidate, neighbor)?; + let midpoint_comparison = compare_scaled_ratio_to_dyadic_square( + numerator, + target_exponent, + denominator, + midpoint_significand, + midpoint_exponent, + )?; + + let neighbor_is_closer = if upward { + midpoint_comparison == Ordering::Greater + } else { + midpoint_comparison == Ordering::Less + }; + if neighbor_is_closer { + candidate = neighbor; + continue; + } + if midpoint_comparison == Ordering::Equal && candidate.to_bits() & 1 == 1 { + return Some(neighbor); + } + return Some(candidate); + } + None +} + +fn exact_four_observation_standard_error( + truth: &[f64], + recovered: &[f64], +) -> Option> { + if truth.len() != 4 || recovered.len() != 4 { + return None; + } + + let mut residuals = [0.0; 4]; + for index in 0..4 { + let truth_value = truth[index]; + let recovered_value = recovered[index]; + if !truth_value.is_finite() || !recovered_value.is_finite() { + return None; + } + let residual = recovered_value - truth_value; + if !residual.is_finite() + || subtraction_roundoff(recovered_value, truth_value, residual) != 0.0 + { + return None; + } + residuals[index] = residual; + } + + let mut pair_dyadics = Vec::with_capacity(6); + let mut unit_exponent = i32::MAX; + for left in 0..4 { + for right in left + 1..4 { + let difference = residuals[left] - residuals[right]; + if !difference.is_finite() + || subtraction_roundoff(residuals[left], residuals[right], difference) != 0.0 + { + return None; + } + if difference == 0.0 { + pair_dyadics.push(None); + continue; + } + let dyadic = positive_dyadic(difference.abs())?; + unit_exponent = unit_exponent.min(dyadic.1); + pair_dyadics.push(Some(dyadic)); + } + } + if unit_exponent == i32::MAX { + return Some(Ok(0.0)); + } + + let mut pair_square_sum = 0_u128; + for dyadic in pair_dyadics.into_iter().flatten() { + let shift = dyadic.1.checked_sub(unit_exponent)?.unsigned_abs(); + let coefficient = multiply_by_power_of_two(dyadic.0, shift)?; + let square = coefficient.checked_mul(coefficient)?; + pair_square_sum = pair_square_sum.checked_add(square)?; + } + if pair_square_sum == 0 { + return Some(Ok(0.0)); + } + + // For n=4, sum((ri-rj)^2, i Result { + if let Some(result) = exact_four_observation_standard_error(truth, recovered) { + return result; + } + crate::bias::bias_standard_error(truth, recovered) +} + +#[cfg(test)] +mod tests { + use super::{correctly_rounded_scaled_sqrt_ratio, exact_four_observation_standard_error}; + + #[test] + fn exact_ratio_sqrt_rounds_against_binary64_midpoint() { + assert_eq!( + correctly_rounded_scaled_sqrt_ratio(116, 48, 0) + .expect("bounded exact ratio") + .to_bits(), + 0x3ff8_df7d_a2e6_6e88 + ); + } + + #[test] + fn four_observation_identity_is_power_of_two_scale_invariant() { + let truth = [0.0; 4]; + let recovered = [0.0, 1.0, 2.0, 7.0]; + assert_eq!( + exact_four_observation_standard_error(&truth, &recovered) + .expect("admitted") + .expect("representable") + .to_bits(), + 0x3ff8_df7d_a2e6_6e88 + ); + + let unit = 2.0_f64.powi(400); + let scaled = recovered.map(|value| value * unit); + let expected = f64::from_bits(0x58f8_df7d_a2e6_6e88); + assert_eq!( + exact_four_observation_standard_error(&truth, &scaled) + .expect("scaled admitted") + .expect("scaled representable"), + expected + ); + } +} From 622d1681a10006d2a67ed9c615626014c7253b92 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 20:10:18 +0900 Subject: [PATCH 405/576] refactor(validation): route bias SE through bounded service --- crates/validation_core/src/lib.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/src/lib.rs b/crates/validation_core/src/lib.rs index d1488b26f..5574d38f3 100644 --- a/crates/validation_core/src/lib.rs +++ b/crates/validation_core/src/lib.rs @@ -13,7 +13,7 @@ //! Metrics are pure `f64` CPU reference implementations. mod bias; -mod bias_standard_error; +mod bias_se; mod claim; mod coverage; mod coverage_evidence; @@ -29,7 +29,7 @@ mod temporal_order; mod validation_evidence; /// Standard error of mean signed bias. -pub use bias_standard_error::bias_standard_error; +pub use bias_se::bias_standard_error; /// Mean signed bias. pub use bias::mean_bias; /// Four ADR 0014 claim authorities. From 170f77eec2f9d72dedea1f932f9be687e7849f8b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 20:10:24 +0900 Subject: [PATCH 406/576] refactor(validation): remove superseded module path --- .../src/bias_standard_error.rs | 279 ------------------ 1 file changed, 279 deletions(-) delete mode 100644 crates/validation_core/src/bias_standard_error.rs diff --git a/crates/validation_core/src/bias_standard_error.rs b/crates/validation_core/src/bias_standard_error.rs deleted file mode 100644 index 2c2a285fa..000000000 --- a/crates/validation_core/src/bias_standard_error.rs +++ /dev/null @@ -1,279 +0,0 @@ -//! Exact represented-input admission for mean-bias standard error. -//! -//! The general bias implementation remains the fallback authority. This module -//! admits only a bounded four-observation identity whose residual and pairwise -//! differences are proven exact in binary64 and whose dyadic pair-distance -//! numerator fits `u128`; the exact rational square root is then rounded against -//! binary64 midpoints without first rounding the ratio under the square root. - -use crate::ValidationError; -use core::cmp::Ordering; - -fn subtraction_roundoff(recovered: f64, truth: f64, residual: f64) -> f64 { - let negated_truth = -truth; - let truth_virtual = residual - recovered; - let recovered_virtual = residual - truth_virtual; - let recovered_roundoff = recovered - recovered_virtual; - let truth_roundoff = negated_truth - truth_virtual; - recovered_roundoff + truth_roundoff -} - -fn positive_dyadic(value: f64) -> Option<(u128, i32)> { - if !value.is_finite() || value <= 0.0 { - return None; - } - let bits = value.to_bits(); - let exponent_bits = ((bits >> 52) & 0x7ff) as i32; - let fraction = bits & 0x000f_ffff_ffff_ffff; - let (mut significand, mut exponent) = if exponent_bits == 0 { - (fraction as u128, -1074) - } else { - ( - ((1_u64 << 52) | fraction) as u128, - exponent_bits - 1023 - 52, - ) - }; - if significand == 0 { - return None; - } - let trailing = significand.trailing_zeros(); - significand >>= trailing; - exponent += trailing as i32; - Some((significand, exponent)) -} - -fn multiply_by_power_of_two(value: u128, shift: u32) -> Option { - let factor = 1_u128.checked_shl(shift)?; - value.checked_mul(factor) -} - -fn compare_scaled_ratio_to_dyadic_square( - numerator: u128, - numerator_exponent: i32, - denominator: u128, - significand: u128, - exponent: i32, -) -> Option { - let square = significand.checked_mul(significand)?; - let right = denominator.checked_mul(square)?; - let square_exponent = exponent.checked_mul(2)?; - let exponent_delta = numerator_exponent.checked_sub(square_exponent)?; - if exponent_delta >= 0 { - let left = multiply_by_power_of_two(numerator, exponent_delta as u32)?; - Some(left.cmp(&right)) - } else { - let shifted_right = multiply_by_power_of_two(right, (-exponent_delta) as u32)?; - Some(numerator.cmp(&shifted_right)) - } -} - -fn midpoint_dyadic(left: f64, right: f64) -> Option<(u128, i32)> { - let (left_significand, left_exponent) = positive_dyadic(left)?; - let (right_significand, right_exponent) = positive_dyadic(right)?; - let common_exponent = left_exponent.min(right_exponent); - let left_shift = left_exponent.checked_sub(common_exponent)? as u32; - let right_shift = right_exponent.checked_sub(common_exponent)? as u32; - let left_units = multiply_by_power_of_two(left_significand, left_shift)?; - let right_units = multiply_by_power_of_two(right_significand, right_shift)?; - let mut midpoint_significand = left_units.checked_add(right_units)?; - let mut midpoint_exponent = common_exponent.checked_sub(1)?; - let trailing = midpoint_significand.trailing_zeros(); - midpoint_significand >>= trailing; - midpoint_exponent += trailing as i32; - Some((midpoint_significand, midpoint_exponent)) -} - -fn exact_power_of_two(exponent: i32) -> Option { - if (-1022..=1023).contains(&exponent) { - return Some(f64::from_bits(((exponent + 1023) as u64) << 52)); - } - if (-1074..=-1023).contains(&exponent) { - return Some(f64::from_bits(1_u64 << (exponent + 1074))); - } - None -} - -fn correctly_rounded_scaled_sqrt_ratio( - numerator: u128, - denominator: u128, - unit_exponent: i32, -) -> Option { - if numerator == 0 || denominator == 0 || numerator > (1_u128 << 53) { - return None; - } - let unit = exact_power_of_two(unit_exponent)?; - let denominator_f64 = denominator as f64; - if denominator_f64 as u128 != denominator { - return None; - } - let mut candidate = ((numerator as f64) / denominator_f64).sqrt() * unit; - if !candidate.is_finite() || candidate <= 0.0 { - return None; - } - let target_exponent = unit_exponent.checked_mul(2)?; - - for _ in 0..4 { - let (candidate_significand, candidate_exponent) = positive_dyadic(candidate)?; - let candidate_comparison = compare_scaled_ratio_to_dyadic_square( - numerator, - target_exponent, - denominator, - candidate_significand, - candidate_exponent, - )?; - if candidate_comparison == Ordering::Equal { - return Some(candidate); - } - - let upward = candidate_comparison == Ordering::Greater; - let bits = candidate.to_bits(); - let neighbor = if upward { - f64::from_bits(bits.checked_add(1)?) - } else { - if bits == 1 { - return None; - } - f64::from_bits(bits - 1) - }; - if !neighbor.is_finite() || neighbor <= 0.0 { - return None; - } - let (midpoint_significand, midpoint_exponent) = midpoint_dyadic(candidate, neighbor)?; - let midpoint_comparison = compare_scaled_ratio_to_dyadic_square( - numerator, - target_exponent, - denominator, - midpoint_significand, - midpoint_exponent, - )?; - - let neighbor_is_closer = if upward { - midpoint_comparison == Ordering::Greater - } else { - midpoint_comparison == Ordering::Less - }; - if neighbor_is_closer { - candidate = neighbor; - continue; - } - if midpoint_comparison == Ordering::Equal && candidate.to_bits() & 1 == 1 { - return Some(neighbor); - } - return Some(candidate); - } - None -} - -fn exact_four_observation_standard_error( - truth: &[f64], - recovered: &[f64], -) -> Option> { - if truth.len() != 4 || recovered.len() != 4 { - return None; - } - - let mut residuals = [0.0; 4]; - for index in 0..4 { - let truth_value = truth[index]; - let recovered_value = recovered[index]; - if !truth_value.is_finite() || !recovered_value.is_finite() { - return None; - } - let residual = recovered_value - truth_value; - if !residual.is_finite() - || subtraction_roundoff(recovered_value, truth_value, residual) != 0.0 - { - return None; - } - residuals[index] = residual; - } - - let mut pair_dyadics = Vec::with_capacity(6); - let mut unit_exponent = i32::MAX; - for left in 0..4 { - for right in left + 1..4 { - let difference = residuals[left] - residuals[right]; - if !difference.is_finite() - || subtraction_roundoff(residuals[left], residuals[right], difference) != 0.0 - { - return None; - } - if difference == 0.0 { - pair_dyadics.push(None); - continue; - } - let dyadic = positive_dyadic(difference.abs())?; - unit_exponent = unit_exponent.min(dyadic.1); - pair_dyadics.push(Some(dyadic)); - } - } - if unit_exponent == i32::MAX { - return Some(Ok(0.0)); - } - - let mut pair_square_sum = 0_u128; - for dyadic in pair_dyadics.into_iter().flatten() { - let shift = dyadic.1.checked_sub(unit_exponent)? as u32; - let coefficient = multiply_by_power_of_two(dyadic.0, shift)?; - let square = coefficient.checked_mul(coefficient)?; - pair_square_sum = pair_square_sum.checked_add(square)?; - } - if pair_square_sum == 0 { - return Some(Ok(0.0)); - } - - // For n=4, sum((ri-rj)^2, i Result { - if let Some(result) = exact_four_observation_standard_error(truth, recovered) { - return result; - } - crate::bias::bias_standard_error(truth, recovered) -} - -#[cfg(test)] -mod tests { - use super::{correctly_rounded_scaled_sqrt_ratio, exact_four_observation_standard_error}; - - #[test] - fn exact_ratio_sqrt_rounds_against_binary64_midpoint() { - assert_eq!( - correctly_rounded_scaled_sqrt_ratio(116, 48, 0) - .expect("bounded exact ratio") - .to_bits(), - 0x3ff8_df7d_a2e6_6e88 - ); - } - - #[test] - fn four_observation_identity_is_power_of_two_scale_invariant() { - let truth = [0.0; 4]; - let recovered = [0.0, 1.0, 2.0, 7.0]; - assert_eq!( - exact_four_observation_standard_error(&truth, &recovered) - .expect("admitted") - .expect("representable") - .to_bits(), - 0x3ff8_df7d_a2e6_6e88 - ); - - let unit = 2.0_f64.powi(400); - let scaled = recovered.map(|value| value * unit); - let expected = f64::from_bits(0x58f8_df7d_a2e6_6e88); - assert_eq!( - exact_four_observation_standard_error(&truth, &scaled) - .expect("scaled admitted") - .expect("scaled representable"), - expected - ); - } -} From 49c46aa4ec80a006a5d265a2a9c7c2281e989697 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 20:12:27 +0900 Subject: [PATCH 407/576] docs(validation): record exact four-observation dispersion rounding --- ...as-standard-error-four-observation-ratio-sqrt-rounding.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-four-observation-ratio-sqrt-rounding.md diff --git a/CHANGELOG.d/validation-bias-standard-error-four-observation-ratio-sqrt-rounding.md b/CHANGELOG.d/validation-bias-standard-error-four-observation-ratio-sqrt-rounding.md new file mode 100644 index 000000000..94987cda3 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-four-observation-ratio-sqrt-rounding.md @@ -0,0 +1,5 @@ +# Validation bias standard error: exact four-observation ratio/sqrt rounding + +- For four observations whose represented residuals and all pairwise residual differences are exact, `bias_standard_error` now uses the identity `SE(mean)^2 = Σ_{i Date: Sat, 5 Sep 2026 20:13:02 +0900 Subject: [PATCH 408/576] docs(research): trace exact four-observation SE rounding --- ...or-four-observation-ratio-sqrt-rounding.md | 84 +++++++++++++++++++ 1 file changed, 84 insertions(+) create mode 100644 docs/research/bias-standard-error-four-observation-ratio-sqrt-rounding.md diff --git a/docs/research/bias-standard-error-four-observation-ratio-sqrt-rounding.md b/docs/research/bias-standard-error-four-observation-ratio-sqrt-rounding.md new file mode 100644 index 000000000..7cd5802db --- /dev/null +++ b/docs/research/bias-standard-error-four-observation-ratio-sqrt-rounding.md @@ -0,0 +1,84 @@ +# Bias standard error: four-observation exact ratio/square-root rounding + +## Finding + +The Validation Evidence CPU reference could still move an exactly defined represented-input bias standard error by one binary64 ULP after GAP-110. The remaining defect was not residual subtraction, translation-anchor selection, overflow/underflow, or Monte Carlo uncertainty. It occurred in the general translated path when an exact finite dispersion numerator was first divided by the scientific denominator in binary64 and only then square-rooted. + +The public reproducer uses four exactly represented residuals + +`r = [0, 1, 2, 7]`. + +For `n` observations, + +`Σ_{i 2`. Larger samples and four-observation samples that cannot satisfy the bounded exact proof remain on the existing deterministic fallback and are candidates for later findings only when a realistic represented-input counterexample is established. + +## Standards and methodological trace + +IEEE P754 is currently an Active PAR, approved 2024-06-06, to revise/supersede IEEE 754-2019; it is not yet a published replacement. ISO/IEC 60559:2020 remains a published International Standard (stage 60.60) specifying floating-point formats, arithmetic, exceptions, and uniquely determined results for specified operations and destination formats. These standards support treating the sequence of rounding operations as part of the numerical contract rather than assuming algebraically equivalent source expressions are representation-equivalent. + +The AERA/APA/NCME public testing authority remains the 2014 *Standards for Educational and Psychological Testing* while a Joint Committee is revising that edition. No unpublished revision is treated as normative authority here. + +Morris, White, and Crowther (2019) frame simulation evaluation around known truth, estimands, methods, and explicit performance measures, and separately require Monte Carlo standard errors for uncertainty caused by finite simulation repetitions. That distinction is material here: `bias_standard_error` is itself a deterministic performance-measure calculation on represented inputs, so a reproducible one-ULP arithmetic error is not Monte Carlo error. + +### 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. + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). + +International Organization for Standardization. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +## Traceability + +- bounded context: Validation Evidence; +- public API: `validation_core::bias_standard_error`; +- implementation admission: `crates/validation_core/src/bias_se.rs`; +- established fallback: `crates/validation_core/src/bias.rs`; +- public RED: `crates/validation_core/tests/bias_standard_error_four_observation_ratio_sqrt_rounding_contract.rs` at `978c422cbdccff02605b5d220bd2564900a830d5`; +- repair lineage: `3de7a737...` through `170f77ee...`; +- CHANGELOG: `CHANGELOG.d/validation-bias-standard-error-four-observation-ratio-sqrt-rounding.md`; +- landing vehicle: PR #488; +- predecessor retained: GAP-110 and all inherited Validation Evidence lineages remain in ancestry. From b92a4d63e850a589ae7067ed236d9efe7382e198 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 20:15:07 +0900 Subject: [PATCH 409/576] test(validation): cover bounded four-observation SE proof edges --- crates/validation_core/src/bias_se.rs | 73 ++++++++++++++++++++++++++- 1 file changed, 71 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 75c986396..96089e410 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -246,16 +246,58 @@ pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 20:16:09 +0900 Subject: [PATCH 410/576] test(validation): cover exact SE overflow and numerator bounds --- crates/validation_core/src/bias_se.rs | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 96089e410..d78973c7f 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -338,6 +338,13 @@ mod tests { exact_four_observation_standard_error(&truth, &[0.0, 1.0, f64::INFINITY, 2.0]), None ); + assert_eq!( + exact_four_observation_standard_error( + &truth, + &[f64::MAX, -f64::MAX, 0.0, 0.0] + ), + None + ); let tiny = 2.0_f64.powi(-54); assert_eq!( @@ -348,5 +355,9 @@ mod tests { exact_four_observation_standard_error(&[1.0, 0.0, 0.0, 0.0], &[tiny, 0.0, 0.0, 0.0]), None ); + assert_eq!( + exact_four_observation_standard_error(&truth, &[0.0, 1.0, 2.0, 67_108_864.0]), + None + ); } } From 0857d5a432e86ca13193886babece6396c98b7f3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 20:17:15 +0900 Subject: [PATCH 411/576] docs(research): complete GAP-111 source and coverage lineage --- ...s-standard-error-four-observation-ratio-sqrt-rounding.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/docs/research/bias-standard-error-four-observation-ratio-sqrt-rounding.md b/docs/research/bias-standard-error-four-observation-ratio-sqrt-rounding.md index 7cd5802db..03c938d0a 100644 --- a/docs/research/bias-standard-error-four-observation-ratio-sqrt-rounding.md +++ b/docs/research/bias-standard-error-four-observation-ratio-sqrt-rounding.md @@ -43,7 +43,7 @@ The repair remains inside TEPP's Validation Evidence bounded context. `crates/va The pair-distance identity then supplies the exact rational radicand without a rounded `numerator / 48` becoming authoritative. A binary64 `sqrt` of the rounded ratio is used only as a candidate. The implementation compares the exact rational radicand with the square of the adjacent-float midpoint and chooses the correctly rounded neighbor, including ties-to-even. Failure to prove any precondition returns to the predecessor `bias.rs` implementation rather than widening admission. -Source lineage for the repair is `3de7a73781576b3ad2b58d0c5bd5341ebf2300c2` → `ea609474a102dbf0ed3cd544c200f542a59e2760` → role-naming/refinement `2c68909557a62d09d9719df20e8ffd1e644ea7a1` → `622d1681a10006d2a67ed9c615626014c7253b92` → `170f77eec2f9d72dedea1f932f9be687e7849f8b`. CHANGELOG lineage starts at `49c46aa4ec80a006a5d265a2a9c7c2281e989697`. +Source lineage for the repair is `3de7a73781576b3ad2b58d0c5bd5341ebf2300c2` → `ea609474a102dbf0ed3cd544c200f542a59e2760` → role-naming/refinement `2c68909557a62d09d9719df20e8ffd1e644ea7a1` → `622d1681a10006d2a67ed9c615626014c7253b92` → `170f77eec2f9d72dedea1f932f9be687e7849f8b` → proof-edge coverage `b92a4d63e850a589ae7067ed236d9efe7382e198` → `c80324930fe462f50424a5f8f03ac5088406e2c9`. CHANGELOG lineage starts at `49c46aa4ec80a006a5d265a2a9c7c2281e989697`. ## Alternatives rejected @@ -78,7 +78,7 @@ Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies - implementation admission: `crates/validation_core/src/bias_se.rs`; - established fallback: `crates/validation_core/src/bias.rs`; - public RED: `crates/validation_core/tests/bias_standard_error_four_observation_ratio_sqrt_rounding_contract.rs` at `978c422cbdccff02605b5d220bd2564900a830d5`; -- repair lineage: `3de7a737...` through `170f77ee...`; -- CHANGELOG: `CHANGELOG.d/validation-bias-standard-error-four-observation-ratio-sqrt-rounding.md`; +- source/test lineage: `3de7a737...` through `c8032493...`; +- CHANGELOG: `CHANGELOG.d/validation-bias-standard-error-four-observation-ratio-sqrt-rounding.md` at `49c46aa4...`; - landing vehicle: PR #488; - predecessor retained: GAP-110 and all inherited Validation Evidence lineages remain in ancestry. From 4f2231354fa0ad3e0c646bbb100b3cc83566d033 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 20:27:30 +0900 Subject: [PATCH 412/576] test(validation): expose GAP-112 reduced-ratio admission --- ...four_observation_reduced_ratio_contract.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_four_observation_reduced_ratio_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_four_observation_reduced_ratio_contract.rs b/crates/validation_core/tests/bias_standard_error_four_observation_reduced_ratio_contract.rs new file mode 100644 index 000000000..2a3932c34 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_four_observation_reduced_ratio_contract.rs @@ -0,0 +1,25 @@ +use validation_core::bias_standard_error; + +fn assert_reduced_ratio_contract(recovered: [f64; 4]) { + let truth = [0.0; 4]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x4174_46e5_76f8_7445, + "exact pair-distance ratio reduces by gcd 4 before the bounded sqrt proof; fallback rounds one ULP low" + ); +} + +#[test] +fn exact_four_observation_dispersion_reduces_ratio_before_bounded_sqrt_proof() { + let samples = [ + [0.0, 14_099_687.0, 16_729_100.0, 94_045_527.0], + [94_045_527.0, 16_729_100.0, 14_099_687.0, 0.0], + [14_099_687.0, 94_045_527.0, 0.0, 16_729_100.0], + [16_729_100.0, 0.0, 94_045_527.0, 14_099_687.0], + ]; + for recovered in samples { + assert_reduced_ratio_contract(recovered); + assert_reduced_ratio_contract(recovered.map(|value| -value)); + } +} From ed1a8763c198fbe478de5ec8be72e3436e5918e3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 20:28:32 +0900 Subject: [PATCH 413/576] fix(validation): reduce exact four-observation ratio --- crates/validation_core/src/bias_se.rs | 40 +++++++++++++++++++++++---- 1 file changed, 35 insertions(+), 5 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index d78973c7f..e4a0cbeed 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -2,8 +2,8 @@ //! //! The general bias implementation remains the fallback authority. This module //! admits only a bounded four-observation identity whose residual and pairwise -//! differences are proven exact in binary64 and whose dyadic pair-distance -//! numerator fits `u128`; the exact rational square root is then rounded against +//! differences are proven exact in binary64 and whose reduced dyadic pair-distance +//! ratio fits `u128`; the exact rational square root is then rounded against //! binary64 midpoints without first rounding the ratio under the square root. use crate::ValidationError; @@ -227,15 +227,32 @@ fn exact_four_observation_standard_error( } // For n=4, sum((ri-rj)^2, i Result { if let Some(result) = exact_four_observation_standard_error(truth, recovered) { @@ -323,6 +340,19 @@ mod tests { ); } + #[test] + fn four_observation_identity_reduces_the_exact_ratio_before_bounded_admission() { + let truth = [0.0; 4]; + let recovered = [0.0, 14_099_687.0, 16_729_100.0, 94_045_527.0]; + assert_eq!( + exact_four_observation_standard_error(&truth, &recovered) + .expect("reduced ratio admitted") + .expect("representable") + .to_bits(), + 0x4174_46e5_76f8_7445 + ); + } + #[test] fn four_observation_identity_covers_exact_zero_and_fallbacks() { let truth = [0.0; 4]; From 28dde0f1e8e3a0f8e8614ed449733b98f2d65c1c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 20:29:09 +0900 Subject: [PATCH 414/576] docs(changelog): record GAP-112 reduced-ratio repair --- ...ion-bias-standard-error-four-observation-reduced-ratio.md | 5 +++++ 1 file changed, 5 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-four-observation-reduced-ratio.md diff --git a/CHANGELOG.d/validation-bias-standard-error-four-observation-reduced-ratio.md b/CHANGELOG.d/validation-bias-standard-error-four-observation-reduced-ratio.md new file mode 100644 index 000000000..5f6560dd5 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-four-observation-reduced-ratio.md @@ -0,0 +1,5 @@ +# Validation Evidence: reduce exact four-observation ratio before bounded sqrt proof + +- Fix `validation_core::bias_standard_error` for exact four-observation samples whose pair-distance numerator exceeds the binary64 exact-integer admission before reduction even though the identical reduced rational ratio fits the bounded proof. +- Reduce `Σ_{i Date: Sat, 5 Sep 2026 20:29:34 +0900 Subject: [PATCH 415/576] docs(research): trace GAP-112 reduced-ratio admission --- ...our-observation-reduced-ratio-admission.md | 88 +++++++++++++++++++ 1 file changed, 88 insertions(+) create mode 100644 docs/research/bias-standard-error-four-observation-reduced-ratio-admission.md diff --git a/docs/research/bias-standard-error-four-observation-reduced-ratio-admission.md b/docs/research/bias-standard-error-four-observation-reduced-ratio-admission.md new file mode 100644 index 000000000..b09970d7b --- /dev/null +++ b/docs/research/bias-standard-error-four-observation-reduced-ratio-admission.md @@ -0,0 +1,88 @@ +# Bias standard error: reduced exact four-observation ratio admission + +## Finding + +GAP-111 added a bounded exact four-observation pair-distance path so that `SE(mean)` is rounded from the exact rational radicand instead of from a binary64-rounded ratio. Its admission still tested the *unreduced* pair-square numerator against the binary64 exact-integer bound. That made proof admission depend on an algebraically irrelevant representation of the same rational number. + +A represented-input counterexample is + +`r = [0, 14_099_687, 16_729_100, 94_045_527]`. + +Every residual and every pairwise difference is exactly represented in binary64. The six squared pair distances are + +`198801173497969`, `279862786810000`, `8844561148707729`, `6913812724569`, `6391337333305600`, and `5977829884046329`, + +which sum exactly to + +`N = 21699306139092196`. + +For four observations, + +`SE(mean)^2 = N / 48`. + +The exact rational reduces by `gcd(N, 48) = 4` to + +`5424826534773049 / 12`. + +The unreduced numerator is greater than `2^53`, so GAP-111 refused its bounded midpoint-square proof and returned to the translated floating path. That fallback returns adjacent-lower bits `0x4174_46e5_76f8_7444`. The *reduced* numerator `5424826534773049` is below `2^53`; applying the same exact midpoint comparison to the identical rational gives the correctly rounded represented-input target `0x4174_46e5_76f8_7445`. + +This is a deterministic Validation Evidence arithmetic defect. No data, estimator target, or probabilistic assumption changes. + +## RED and causal repair + +Public RED `4f2231354fa0ad3e0c646bbb100b3cc83566d033` adds `crates/validation_core/tests/bias_standard_error_four_observation_reduced_ratio_contract.rs`. It fixes several permutations of the exact residual multiset and their sign mirrors at bits `0x4174_46e5_76f8_7445`. + +Causal source repair `ed1a8763c198fbe478de5ec8be72e3436e5918e3` changes only the GAP-111 bounded admission in `crates/validation_core/src/bias_se.rs`. After the exact pair-square sum is constructed in checked `u128`, the implementation computes the integer greatest common divisor with the scientific denominator `48`, divides numerator and denominator by that divisor, and then invokes the existing exact adjacent-midpoint square comparison. The rational radicand is unchanged. + +The admission boundary remains narrow: + +- exactly four observations; +- every represented residual is finite and subtraction-error-free; +- every pairwise residual difference is finite and subtraction-error-free; +- the dyadic pair-distance numerator fits the existing checked `u128` construction; +- after exact rational reduction, numerator and denominator fit the existing bounded binary64-integer proof; +- exact candidate/neighbor midpoint-square comparison can complete without integer overflow. + +Any failed proof still returns to `crates/validation_core/src/bias.rs`. No arbitrary-precision runtime, mutable sibling dependency, or new reusable psychometric arithmetic owner is introduced. + +## Alternatives rejected + +Keeping the unreduced numerator and increasing the `2^53` admission limit was rejected because `numerator as f64` would cease to be exact and would invalidate the proof that the candidate is derived from the represented rational without an extra integer-conversion rounding. + +Special-casing this residual payload was rejected because the defect is rational representation, not the particular values. + +Changing every four-observation evaluation to a different floating formula was rejected because algebraic rearrangement alone does not provide a correct-rounding proof and would widen the rounding surface beyond the established GAP-111 contract. + +Arbitrary-precision production arithmetic was rejected as disproportionate to the finding and outside TEPP's owner boundary. Reusable static psychometric arithmetic remains owned by `fast-mlsirm`; this code is a bounded Validation Evidence admission proof. + +GAP-112 does not claim globally correctly rounded `bias_standard_error` for arbitrary `n > 2`, nor does it admit four-observation samples whose exactness or bounded integer proof cannot be established. + +## Standards and methodological trace + +IEEE 754-2019 remains the published IEEE floating-point standard used for the binary64 destination-format reasoning here. IEEE P754 is the active revision project rather than a published replacement. ISO/IEC 60559:2020 remains the corresponding published international floating-point standard. The relevant engineering consequence is that an exact rational identity does not make two sequences of rounded operations representation-equivalent; proof admission must preserve the exact value being compared with destination-format rounding boundaries. + +The AERA/APA/NCME public testing authority remains the 2014 *Standards for Educational and Psychological Testing* while revision work proceeds. The Validation Evidence interpretation therefore stays tied to the published edition rather than an unpublished revision. + +Morris, White, and Crowther (2019) distinguish deterministic performance-measure calculation from Monte Carlo uncertainty due to finite simulation repetitions. GAP-112 concerns the former: for fixed represented inputs, a one-ULP discrepancy caused by an unnecessary proof fallback is not Monte Carlo error. + +### 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. + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). + +International Organization for Standardization. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +## Traceability + +- bounded context: Validation Evidence; +- public API: `validation_core::bias_standard_error`; +- exact admission: `crates/validation_core/src/bias_se.rs`; +- established fallback: `crates/validation_core/src/bias.rs`; +- public RED: `crates/validation_core/tests/bias_standard_error_four_observation_reduced_ratio_contract.rs` at `4f2231354fa0ad3e0c646bbb100b3cc83566d033`; +- causal source repair: `ed1a8763c198fbe478de5ec8be72e3436e5918e3`; +- CHANGELOG: `CHANGELOG.d/validation-bias-standard-error-four-observation-reduced-ratio.md` beginning at `28dde0f1e8e3a0f8e8614ed449733b98f2d65c1c`; +- landing vehicle: PR #488; +- predecessor retained: GAP-111 and all inherited Validation Evidence lineages remain in ancestry. From 50153a1c4452d780c23b58fd34c695db9048e603 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 21:04:17 +0900 Subject: [PATCH 416/576] test(validation): expose large reduced-ratio SE rounding --- ...bservation_large_reduced_ratio_contract.rs | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_four_observation_large_reduced_ratio_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_four_observation_large_reduced_ratio_contract.rs b/crates/validation_core/tests/bias_standard_error_four_observation_large_reduced_ratio_contract.rs new file mode 100644 index 000000000..729c5d1a9 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_four_observation_large_reduced_ratio_contract.rs @@ -0,0 +1,25 @@ +use validation_core::bias_standard_error; + +fn assert_large_reduced_ratio_contract(recovered: [f64; 4]) { + let truth = [0.0; 4]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x41b3_a706_d408_9e32, + "the exact reduced pair-distance ratio remains authoritative even when its numerator exceeds 2^53; the floating ratio/sqrt fallback rounds one ULP low" + ); +} + +#[test] +fn exact_four_observation_dispersion_keeps_large_reduced_ratio_in_midpoint_proof() { + let samples = [ + [19_274_968.0, 693_729_138.0, 711_353_557.0, 1_625_519_116.0], + [1_625_519_116.0, 711_353_557.0, 693_729_138.0, 19_274_968.0], + [693_729_138.0, 1_625_519_116.0, 19_274_968.0, 711_353_557.0], + [711_353_557.0, 19_274_968.0, 1_625_519_116.0, 693_729_138.0], + ]; + for recovered in samples { + assert_large_reduced_ratio_contract(recovered); + assert_large_reduced_ratio_contract(recovered.map(|value| -value)); + } +} From dbf6b40946c0940c8b088f376a6dc1750401350e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 21:05:50 +0900 Subject: [PATCH 417/576] fix(validation): prove large exact four-observation ratios --- crates/validation_core/src/bias_se.rs | 47 +++++++++++++++++++-------- 1 file changed, 34 insertions(+), 13 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index e4a0cbeed..205abc09a 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -100,16 +100,16 @@ fn correctly_rounded_scaled_sqrt_ratio( denominator: u128, unit_exponent: i32, ) -> Option { - const MAX_EXACT_BINARY64_INTEGER: u128 = 1_u128 << 53; - if numerator == 0 - || denominator == 0 - || numerator > MAX_EXACT_BINARY64_INTEGER - || denominator > MAX_EXACT_BINARY64_INTEGER - { + const MAX_EXACT_BINARY64_DENOMINATOR: u128 = 1_u128 << 53; + if numerator == 0 || denominator == 0 || denominator > MAX_EXACT_BINARY64_DENOMINATOR { return None; } let unit = exact_power_of_two(unit_exponent)?; let denominator_f64 = denominator as f64; + // The binary64 numerator conversion is only a seed. The returned value is + // admitted solely after the exact u128 dyadic-square and midpoint comparisons + // below. This lets the four-observation proof retain exact reduced numerators + // above 2^53 without pretending that their seed conversion is exact. let mut candidate = ((numerator as f64) / denominator_f64).sqrt() * unit; if !candidate.is_finite() || candidate <= 0.0 { return None; @@ -227,10 +227,10 @@ fn exact_four_observation_standard_error( } // For n=4, sum((ri-rj)^2, i Date: Sat, 5 Sep 2026 21:06:24 +0900 Subject: [PATCH 418/576] docs(changelog): record large reduced-ratio SE proof --- ...bias-standard-error-four-observation-large-reduced-ratio.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-four-observation-large-reduced-ratio.md diff --git a/CHANGELOG.d/validation-bias-standard-error-four-observation-large-reduced-ratio.md b/CHANGELOG.d/validation-bias-standard-error-four-observation-large-reduced-ratio.md new file mode 100644 index 000000000..cb906dd60 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-four-observation-large-reduced-ratio.md @@ -0,0 +1,3 @@ +### Fixed + +- Preserve the exact four-observation pair-distance standard-error proof when the GCD-reduced `u128` numerator exceeds `2^53`. The binary64 numerator conversion is now only a candidate seed; exact dyadic-square and midpoint comparisons remain authoritative for the returned rounding, so representable exact ratios do not fall back solely because the seed integer is not exactly representable as binary64. From 37e3defb3bde29a8d1ac852456ba05f787aee1f3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 21:07:09 +0900 Subject: [PATCH 419/576] docs(research): trace large reduced-ratio SE proof --- ...r-observation-large-reduced-ratio-proof.md | 89 +++++++++++++++++++ 1 file changed, 89 insertions(+) create mode 100644 docs/research/bias-standard-error-four-observation-large-reduced-ratio-proof.md diff --git a/docs/research/bias-standard-error-four-observation-large-reduced-ratio-proof.md b/docs/research/bias-standard-error-four-observation-large-reduced-ratio-proof.md new file mode 100644 index 000000000..08fa6732d --- /dev/null +++ b/docs/research/bias-standard-error-four-observation-large-reduced-ratio-proof.md @@ -0,0 +1,89 @@ +# Bias standard error: large reduced four-observation ratio proof + +## Finding + +GAP-112 correctly reduced the exact four-observation pair-distance ratio before the bounded square-root proof, but it still required the reduced numerator itself to be an exactly representable binary64 integer. That restriction was stronger than the actual proof contract: `numerator as f64` is only an initial candidate seed, while the returned value is decided by exact `u128` candidate-square and adjacent-midpoint comparisons. + +A represented-input counterexample is + +`r = [19_274_968, 693_729_138, 711_353_557, 1_625_519_116]`. + +Every residual and pairwise difference is exactly represented in binary64. The six squared pair distances are + +`454888427430388900`, `478972773352230921`, `2580020262984245904`, `310620145087561`, `868232563101240484`, and `835698669261782481`. + +They sum exactly to + +`N = 5218123316274976251`. + +For four observations, + +`SE(mean)^2 = N / 48`. + +`gcd(N, 48) = 3`, so the exact reduced radicand is + +`1739374438758325417 / 16`. + +The reduced numerator is larger than `2^53`. GAP-112 therefore rejected the bounded midpoint proof and returned to the translated floating ratio/square-root path, which yields adjacent-lower bits `0x41b3_a706_d408_9e31`. The exact represented-input target is `0x41b3_a706_d408_9e32`. + +The defect is deterministic Validation Evidence arithmetic. The estimand, sampling design, and scientific denominator are unchanged. + +## RED and causal repair + +Public RED `50153a1c4452d780c23b58fd34c695db9048e603` adds `crates/validation_core/tests/bias_standard_error_four_observation_large_reduced_ratio_contract.rs`. It fixes multiple permutations and sign mirrors at `0x41b3_a706_d408_9e32`. + +Causal repair `dbf6b40946c0940c8b088f376a6dc1750401350e` changes only the bounded ratio proof in `crates/validation_core/src/bias_se.rs`. The reduced numerator remains exact in `u128`; its conversion to binary64 is explicitly treated only as a seed for the initial square-root candidate. No result is admitted from that seed alone. The implementation still compares the exact rational radicand with the candidate square and with the exact dyadic midpoint between adjacent binary64 candidates. If those checked `u128` comparisons overflow or the candidate cannot be settled within the existing bounded neighbor walk, the function returns `None` and preserves the established fallback. + +The public admission remains narrow: + +- exactly four observations; +- finite, subtraction-error-free represented residuals; +- finite, subtraction-error-free pairwise residual differences; +- checked `u128` construction of the dyadic pair-square sum; +- exact GCD reduction against the scientific denominator `48`; +- a denominator exactly representable in the existing binary64 seed path; +- exact checked candidate-square and midpoint comparisons that complete without integer overflow. + +No arbitrary-precision runtime, payload-specific branch, mutable sibling dependency, or reusable static psychometric estimator is introduced. The reusable arithmetic owner boundary with `fast-mlsirm` is unchanged. + +## Alternatives rejected + +Keeping the `2^53` numerator admission was rejected because it confuses the approximation used to seed the search with the exact arithmetic used to accept the final result. GAP-113 corrects that proof boundary rather than changing the estimator. + +Replacing the bounded proof with the floating ratio/square-root formula was rejected because that path is the demonstrated one-ULP failure. + +Special-casing this residual multiset was rejected because the defect is the seed-exactness precondition, not these values. + +Arbitrary-precision production arithmetic was rejected as unnecessary: the exact numerator, denominator, candidate dyadics, and midpoint comparisons for this bounded case fit the existing checked `u128` machinery. Cases that do not fit still fail closed to the established path. + +GAP-113 does not claim globally correctly rounded `bias_standard_error` for arbitrary sample sizes or for every four-observation geometry. + +## Standards and methodological trace + +IEEE 754-2019 remains the active published IEEE floating-point authority; IEEE P754 is an active revision project rather than a published replacement. ISO/IEC 60559:2020 remains the corresponding published International Standard. The engineering consequence here is that a rounded binary64 seed may be used as a search starting point only when the acceptance decision is made against the exact represented target and binary64 rounding boundaries. + +The AERA/APA/NCME public testing authority remains the 2014 *Standards for Educational and Psychological Testing*. This repair concerns the numerical integrity of Validation Evidence rather than a change to the validity argument or score interpretation. + +Morris, White, and Crowther (2019) distinguish deterministic performance-measure calculation from Monte Carlo uncertainty. A fixed-input one-ULP error caused by an unnecessary fallback belongs to the former and must not be reported as simulation uncertainty. + +### 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. + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). + +International Organization for Standardization. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 + +## Traceability + +- bounded context: Validation Evidence; +- public API: `validation_core::bias_standard_error`; +- exact admission: `crates/validation_core/src/bias_se.rs`; +- fallback: `crates/validation_core/src/bias.rs`; +- public RED: `crates/validation_core/tests/bias_standard_error_four_observation_large_reduced_ratio_contract.rs` at `50153a1c4452d780c23b58fd34c695db9048e603`; +- causal source repair: `dbf6b40946c0940c8b088f376a6dc1750401350e`; +- CHANGELOG: `CHANGELOG.d/validation-bias-standard-error-four-observation-large-reduced-ratio.md` beginning at `ca85765f7fce56c238641bffa57f4eb19547efb7`; +- landing vehicle: PR #488; +- predecessor retained: GAP-112 and all inherited Validation Evidence lineages remain in ancestry. From 5878ec10f458efb5f070446dcb3ead30900ef707 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 21:32:17 +0900 Subject: [PATCH 420/576] test(validation): reproduce five-observation SE rounding --- ...five_observation_pair_distance_contract.rs | 42 +++++++++++++++++++ 1 file changed, 42 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_five_observation_pair_distance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_five_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_five_observation_pair_distance_contract.rs new file mode 100644 index 000000000..3d47b8ac4 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_five_observation_pair_distance_contract.rs @@ -0,0 +1,42 @@ +use validation_core::bias_standard_error; + +fn assert_five_observation_pair_distance_contract(recovered: [f64; 5]) { + let truth = [0.0; 5]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x4192_caf1_6406_5ad0, + "the exact five-observation pair-distance ratio must determine the represented-input SE; the translated floating moment/sqrt fallback rounds one ULP high" + ); +} + +#[test] +fn exact_five_observation_dispersion_uses_pair_distance_midpoint_proof() { + let samples = [ + [ + 1_342_748_146.0, + 1_434_848_064.0, + 1_525_257_611.0, + 1_685_877_224.0, + 1_771_341_094.0, + ], + [ + 1_771_341_094.0, + 1_525_257_611.0, + 1_342_748_146.0, + 1_685_877_224.0, + 1_434_848_064.0, + ], + [ + 1_525_257_611.0, + 1_342_748_146.0, + 1_771_341_094.0, + 1_434_848_064.0, + 1_685_877_224.0, + ], + ]; + for recovered in samples { + assert_five_observation_pair_distance_contract(recovered); + assert_five_observation_pair_distance_contract(recovered.map(|value| -value)); + } +} From 41298270e8e3d4476ba1bbad9f22ea94752a9e6a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 21:33:27 +0900 Subject: [PATCH 421/576] fix(validation): admit bounded five-observation pair-distance SE --- crates/validation_core/src/bias_se.rs | 113 +++++++++++++++++--------- 1 file changed, 74 insertions(+), 39 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 205abc09a..3862a3a4d 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -1,7 +1,7 @@ //! Exact represented-input admission for mean-bias standard error. //! //! The general bias implementation remains the fallback authority. This module -//! admits only a bounded four-observation identity whose residual and pairwise +//! admits a bounded small-sample pair-distance identity whose residual and pairwise //! differences are proven exact in binary64 and whose reduced dyadic pair-distance //! ratio fits `u128`; the exact rational square root is then rounded against //! binary64 midpoints without first rounding the ratio under the square root. @@ -108,8 +108,8 @@ fn correctly_rounded_scaled_sqrt_ratio( let denominator_f64 = denominator as f64; // The binary64 numerator conversion is only a seed. The returned value is // admitted solely after the exact u128 dyadic-square and midpoint comparisons - // below. This lets the four-observation proof retain exact reduced numerators - // above 2^53 without pretending that their seed conversion is exact. + // below. This lets the bounded proof retain exact reduced numerators above + // 2^53 without pretending that their seed conversion is exact. let mut candidate = ((numerator as f64) / denominator_f64).sqrt() * unit; if !candidate.is_finite() || candidate <= 0.0 { return None; @@ -168,16 +168,21 @@ fn correctly_rounded_scaled_sqrt_ratio( None } -fn exact_four_observation_standard_error( +fn exact_pair_distance_standard_error( truth: &[f64], recovered: &[f64], ) -> Option> { - if truth.len() != 4 || recovered.len() != 4 { + // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have + // cheaper exact identities in `bias.rs`; four and five observations are the + // smallest remaining sample sizes where the translated floating moment/sqrt + // path has demonstrated one-ULP errors. + if truth.len() != recovered.len() || !(4..=5).contains(&truth.len()) { return None; } + let sample_count = truth.len(); - let mut residuals = [0.0; 4]; - for index in 0..4 { + let mut residuals = Vec::with_capacity(sample_count); + for index in 0..sample_count { let truth_value = truth[index]; let recovered_value = recovered[index]; if !truth_value.is_finite() || !recovered_value.is_finite() { @@ -189,13 +194,14 @@ fn exact_four_observation_standard_error( { return None; } - residuals[index] = residual; + residuals.push(residual); } - let mut pair_dyadics = Vec::with_capacity(6); + let pair_count = sample_count.checked_mul(sample_count.checked_sub(1)?)? / 2; + let mut pair_dyadics = Vec::with_capacity(pair_count); let mut unit_exponent = i32::MAX; - for left in 0..4 { - for right in left + 1..4 { + for left in 0..sample_count { + for right in left + 1..sample_count { let difference = residuals[left] - residuals[right]; if !difference.is_finite() || subtraction_roundoff(residuals[left], residuals[right], difference) != 0.0 @@ -226,20 +232,23 @@ fn exact_four_observation_standard_error( return Some(Ok(0.0)); } - // For n=4, sum((ri-rj)^2, i Result { - if let Some(result) = exact_four_observation_standard_error(truth, recovered) { + if let Some(result) = exact_pair_distance_standard_error(truth, recovered) { return result; } crate::bias::bias_standard_error(truth, recovered) @@ -264,7 +273,7 @@ pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result Date: Sat, 5 Sep 2026 21:33:50 +0900 Subject: [PATCH 422/576] docs(changelog): record five-observation SE pair-distance proof --- ...dation-bias-standard-error-five-observation-pair-distance.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-five-observation-pair-distance.md diff --git a/CHANGELOG.d/validation-bias-standard-error-five-observation-pair-distance.md b/CHANGELOG.d/validation-bias-standard-error-five-observation-pair-distance.md new file mode 100644 index 000000000..ec2dfc839 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-five-observation-pair-distance.md @@ -0,0 +1,2 @@ +- Validation Evidence now admits the exact pair-distance identity for bounded five-observation mean-bias standard errors when every represented residual and pairwise difference is proven exact and the reduced dyadic ratio fits the existing `u128` midpoint proof. +- This prevents the translated floating moment/`sqrt` fallback from moving an exact represented-input five-observation standard error by one ULP while preserving fail-closed fallback outside the bounded proof. From 8f7dbf8843ce5c9a2f48e3f67779382e0096d3c2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 21:34:31 +0900 Subject: [PATCH 423/576] docs(research): trace five-observation SE pair-distance proof --- ...five-observation-pair-distance-rounding.md | 74 +++++++++++++++++++ 1 file changed, 74 insertions(+) create mode 100644 docs/research/bias-standard-error-five-observation-pair-distance-rounding.md diff --git a/docs/research/bias-standard-error-five-observation-pair-distance-rounding.md b/docs/research/bias-standard-error-five-observation-pair-distance-rounding.md new file mode 100644 index 000000000..231271769 --- /dev/null +++ b/docs/research/bias-standard-error-five-observation-pair-distance-rounding.md @@ -0,0 +1,74 @@ +# Five-observation mean-bias standard error: exact pair-distance rounding + +## Problem + +The Validation Evidence `bias_standard_error` contract evaluates the standard error of represented signed recovery bias. After GAP-113, the bounded exact pair-distance/midpoint proof applied only to four observations. A five-observation sample with exact represented residuals could therefore fall through to the translated floating second-moment path even when every residual and every pairwise difference was exact. + +For + +```text +truth = [0, 0, 0, 0, 0] +recovered = [1342748146, 1434848064, 1525257611, 1685877224, 1771341094] +``` + +all inputs are exactly represented binary64 integers. The ten exact squared pair distances sum to + +```text +N = 621298477313343404 +``` + +For `n = 5`, the pair-distance identity is + +```text +SE(mean)^2 = sum_{i Date: Sat, 5 Sep 2026 21:43:44 +0900 Subject: [PATCH 424/576] test(validation): reproduce six-observation SE rounding --- ..._six_observation_pair_distance_contract.rs | 45 +++++++++++++++++++ 1 file changed, 45 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_six_observation_pair_distance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_six_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_six_observation_pair_distance_contract.rs new file mode 100644 index 000000000..60eacbc6a --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_six_observation_pair_distance_contract.rs @@ -0,0 +1,45 @@ +use validation_core::bias_standard_error; + +fn assert_six_observation_pair_distance_contract(recovered: [f64; 6]) { + let truth = [0.0; 6]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x419c_057d_42fc_5857, + "the exact six-observation pair-distance ratio must determine the represented-input SE; the translated floating moment/sqrt fallback rounds one ULP high" + ); +} + +#[test] +fn exact_six_observation_dispersion_uses_pair_distance_midpoint_proof() { + let samples = [ + [ + 1_120_315_269.0, + 1_513_609_015.0, + 1_569_037_659.0, + 1_789_057_504.0, + 1_807_936_669.0, + 1_914_796_738.0, + ], + [ + 1_914_796_738.0, + 1_569_037_659.0, + 1_120_315_269.0, + 1_807_936_669.0, + 1_513_609_015.0, + 1_789_057_504.0, + ], + [ + 1_569_037_659.0, + 1_120_315_269.0, + 1_914_796_738.0, + 1_513_609_015.0, + 1_789_057_504.0, + 1_807_936_669.0, + ], + ]; + for recovered in samples { + assert_six_observation_pair_distance_contract(recovered); + assert_six_observation_pair_distance_contract(recovered.map(|value| -value)); + } +} From 8e194dc1b0fa66cc923c5cb939bf3319ed0b4554 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 21:44:42 +0900 Subject: [PATCH 425/576] fix(validation): admit bounded six-observation pair-distance SE --- crates/validation_core/src/bias_se.rs | 44 +++++++++++++++++++++------ 1 file changed, 35 insertions(+), 9 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 3862a3a4d..3e14f9b90 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -173,10 +173,10 @@ fn exact_pair_distance_standard_error( recovered: &[f64], ) -> Option> { // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have - // cheaper exact identities in `bias.rs`; four and five observations are the - // smallest remaining sample sizes where the translated floating moment/sqrt - // path has demonstrated one-ULP errors. - if truth.len() != recovered.len() || !(4..=5).contains(&truth.len()) { + // cheaper exact identities in `bias.rs`; four through six observations are + // the smallest remaining sample sizes with demonstrated one-ULP errors in + // the translated floating moment/sqrt path. + if truth.len() != recovered.len() || !(4..=6).contains(&truth.len()) { return None; } let sample_count = truth.len(); @@ -259,10 +259,10 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- and five-observation samples whose represented residuals and pairwise -/// differences are exact use the exact pair-distance identity when its reduced -/// dyadic ratio fits the bounded integer proof. All other samples retain the -/// established bias implementation and its existing fail-closed behavior. +/// Four- through six-observation samples whose represented residuals and +/// pairwise differences are exact use the exact pair-distance identity when its +/// reduced dyadic ratio fits the bounded integer proof. All other samples retain +/// the established bias implementation and its existing fail-closed behavior. pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result { if let Some(result) = exact_pair_distance_standard_error(truth, recovered) { return result; @@ -307,6 +307,12 @@ mod tests { .to_bits(), 0x4192_caf1_6406_5ad0 ); + assert_eq!( + correctly_rounded_scaled_sqrt_ratio(621_603_287_214_182_303, 45, 0) + .expect("six-observation reduced ratio remains bounded") + .to_bits(), + 0x419c_057d_42fc_5857 + ); } #[test] @@ -408,6 +414,26 @@ mod tests { ); } + #[test] + fn six_observation_identity_keeps_exact_pair_distance_ratio_authoritative() { + let truth = [0.0; 6]; + let recovered = [ + 1_120_315_269.0, + 1_513_609_015.0, + 1_569_037_659.0, + 1_789_057_504.0, + 1_807_936_669.0, + 1_914_796_738.0, + ]; + assert_eq!( + exact_pair_distance_standard_error(&truth, &recovered) + .expect("six-observation ratio admitted") + .expect("representable") + .to_bits(), + 0x419c_057d_42fc_5857 + ); + } + #[test] fn bounded_pair_distance_identity_covers_exact_zero_and_fallbacks() { let truth = [0.0; 4]; @@ -420,7 +446,7 @@ mod tests { None ); assert_eq!( - exact_pair_distance_standard_error(&[0.0; 6], &[0.0; 6]), + exact_pair_distance_standard_error(&[0.0; 7], &[0.0; 7]), None ); assert_eq!( From d709199bfccca6d70f2cd91d7115474a0f8e04cd Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 21:44:55 +0900 Subject: [PATCH 426/576] docs(changelog): record six-observation SE pair-distance proof --- ...idation-bias-standard-error-six-observation-pair-distance.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-six-observation-pair-distance.md diff --git a/CHANGELOG.d/validation-bias-standard-error-six-observation-pair-distance.md b/CHANGELOG.d/validation-bias-standard-error-six-observation-pair-distance.md new file mode 100644 index 000000000..8ce38b8fd --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-six-observation-pair-distance.md @@ -0,0 +1,2 @@ +- Validation Evidence now admits the exact pair-distance identity for bounded six-observation mean-bias standard errors when represented residuals and pairwise differences are proven exact and the reduced dyadic ratio fits the existing checked `u128` midpoint proof. +- This prevents the translated floating moment/`sqrt` fallback from moving a demonstrated exact represented-input six-observation standard error one ULP high, while retaining the established fallback outside the bounded proof. From d2d8617bdd47ce027074c1561121a2e3baab340c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 21:45:19 +0900 Subject: [PATCH 427/576] docs(research): trace six-observation SE pair-distance proof --- ...-six-observation-pair-distance-rounding.md | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 docs/research/bias-standard-error-six-observation-pair-distance-rounding.md diff --git a/docs/research/bias-standard-error-six-observation-pair-distance-rounding.md b/docs/research/bias-standard-error-six-observation-pair-distance-rounding.md new file mode 100644 index 000000000..b5b532a0c --- /dev/null +++ b/docs/research/bias-standard-error-six-observation-pair-distance-rounding.md @@ -0,0 +1,48 @@ +# Six-observation mean-bias standard error: exact pair-distance rounding + +## Problem + +After GAP-114, the bounded pair-distance/midpoint proof covered four and five observations. A six-observation sample with exact represented residuals still fell through to the translated floating second-moment path: + +```text +truth = [0, 0, 0, 0, 0, 0] +recovered = [1120315269, 1513609015, 1569037659, 1789057504, 1807936669, 1914796738] +``` + +All values are exactly represented binary64 integers and all pairwise differences are exact. The fifteen squared pair distances sum to + +```text +N = 2486413148856729212. +``` + +For `n=6`, + +```text +SE(mean)^2 = sum_{i2` standard errors. A broader admission requires separate numerical and performance evidence. + +CHANGELOG `d709199bfccca6d70f2cd91d7115474a0f8e04cd` records the contract. GAP-114 and all preceding Validation Evidence lineages remain inherited. + +## Method and standards trace + +Morris, White, and Crowther (2019) distinguish deterministic performance-measure calculations from Monte Carlo uncertainty; a one-ULP deterministic bias-SE error is therefore repaired in the estimator rather than treated as simulation noise. Published floating-point authority remains IEEE 754-2019 / ISO/IEC 60559:2020. IEEE P754 is active revision work, not a published replacement. AERA/APA/NCME public Testing Standards authority remains the 2014 edition while its revision proceeds. + +## 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. + +IEEE. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +ISO/IEC. (2020). *Information technology—Microprocessor systems—Floating-point arithmetic* (ISO/IEC 60559:2020). + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From a1de7ae90ae57e0eb55d7efe0b8bf6d9f5a6f3f3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 22:01:24 +0900 Subject: [PATCH 428/576] test(validation): expose seven-observation SE rounding --- ...even_observation_pair_distance_contract.rs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_seven_observation_pair_distance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_seven_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_seven_observation_pair_distance_contract.rs new file mode 100644 index 000000000..69a4779b4 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_seven_observation_pair_distance_contract.rs @@ -0,0 +1,48 @@ +use validation_core::bias_standard_error; + +fn assert_seven_observation_pair_distance_contract(recovered: [f64; 7]) { + let truth = [0.0; 7]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x4195_d9b7_0ca9_e6ee, + "the exact seven-observation pair-distance ratio must determine the represented-input SE; the translated floating moment/sqrt fallback rounds one ULP high" + ); +} + +#[test] +fn exact_seven_observation_dispersion_uses_pair_distance_midpoint_proof() { + let samples = [ + [ + 912_628_433.0, + 991_631_865.0, + 1_109_185_293.0, + 1_253_685_899.0, + 1_354_619_842.0, + 1_368_206_500.0, + 1_611_374_925.0, + ], + [ + 1_611_374_925.0, + 1_368_206_500.0, + 1_354_619_842.0, + 1_253_685_899.0, + 1_109_185_293.0, + 991_631_865.0, + 912_628_433.0, + ], + [ + 1_253_685_899.0, + 912_628_433.0, + 1_611_374_925.0, + 1_109_185_293.0, + 1_368_206_500.0, + 991_631_865.0, + 1_354_619_842.0, + ], + ]; + for recovered in samples { + assert_seven_observation_pair_distance_contract(recovered); + assert_seven_observation_pair_distance_contract(recovered.map(|value| -value)); + } +} From 2907e468363040a435fde4dd14e74ca32cc3b082 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 22:03:09 +0900 Subject: [PATCH 429/576] fix(validation): admit exact seven-observation SE proof --- crates/validation_core/src/bias_se.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 3e14f9b90..0e5d74a5d 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -173,10 +173,10 @@ fn exact_pair_distance_standard_error( recovered: &[f64], ) -> Option> { // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have - // cheaper exact identities in `bias.rs`; four through six observations are + // cheaper exact identities in `bias.rs`; four through seven observations are // the smallest remaining sample sizes with demonstrated one-ULP errors in // the translated floating moment/sqrt path. - if truth.len() != recovered.len() || !(4..=6).contains(&truth.len()) { + if truth.len() != recovered.len() || !(4..=7).contains(&truth.len()) { return None; } let sample_count = truth.len(); @@ -259,7 +259,7 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- through six-observation samples whose represented residuals and +/// Four- through seven-observation samples whose represented residuals and /// pairwise differences are exact use the exact pair-distance identity when its /// reduced dyadic ratio fits the bounded integer proof. All other samples retain /// the established bias implementation and its existing fail-closed behavior. @@ -446,7 +446,7 @@ mod tests { None ); assert_eq!( - exact_pair_distance_standard_error(&[0.0; 7], &[0.0; 7]), + exact_pair_distance_standard_error(&[0.0; 8], &[0.0; 8]), None ); assert_eq!( From 78d5ed3fb81765edbc4ee662dacabe6ec631265f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 22:03:21 +0900 Subject: [PATCH 430/576] docs(changelog): record seven-observation SE proof --- ...ation-bias-standard-error-seven-observation-pair-distance.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-seven-observation-pair-distance.md diff --git a/CHANGELOG.d/validation-bias-standard-error-seven-observation-pair-distance.md b/CHANGELOG.d/validation-bias-standard-error-seven-observation-pair-distance.md new file mode 100644 index 000000000..b59d12be5 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-seven-observation-pair-distance.md @@ -0,0 +1,2 @@ +- Validation Evidence now admits the exact pair-distance identity for bounded seven-observation mean-bias standard errors when represented residuals and pairwise differences are proven exact and the reduced dyadic ratio fits the existing checked `u128` midpoint proof. +- This prevents the translated floating moment/`sqrt` fallback from moving a demonstrated exact represented-input seven-observation standard error one ULP high, while retaining the established fallback outside the bounded proof. From 4efe2421e24a964093d5bc63e4abef9e2664c330 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 22:03:50 +0900 Subject: [PATCH 431/576] docs(research): trace seven-observation SE rounding --- ...even-observation-pair-distance-rounding.md | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 docs/research/bias-standard-error-seven-observation-pair-distance-rounding.md diff --git a/docs/research/bias-standard-error-seven-observation-pair-distance-rounding.md b/docs/research/bias-standard-error-seven-observation-pair-distance-rounding.md new file mode 100644 index 000000000..680d10cbe --- /dev/null +++ b/docs/research/bias-standard-error-seven-observation-pair-distance-rounding.md @@ -0,0 +1,60 @@ +# Seven-observation mean-bias standard error: exact pair-distance rounding + +## Problem + +After GAP-115, the bounded pair-distance/midpoint proof covered four through six observations. A seven-observation sample with exact represented residuals still fell through to the translated floating second-moment path: + +```text +truth = [0, 0, 0, 0, 0, 0, 0] +recovered = [912628433, 991631865, 1109185293, 1253685899, 1354619842, 1368206500, 1611374925] +``` + +All values are exactly represented binary64 integers and all pairwise differences are exact. The twenty-one squared pair distances sum to + +```text +N = 2469379766402987422. +``` + +For `n=7`, + +```text +SE(mean)^2 = sum_{i2` standard errors. Extending the bound again requires a demonstrated scientific counterexample plus numerical and performance evidence rather than a speculative increase. + +CHANGELOG `78d5ed3fb81765edbc4ee662dacabe6ec631265f` records the contract. GAP-115 and all preceding Validation Evidence lineages remain inherited. + +## Alternatives rejected + +A payload-specific seven-value branch would encode a fixture rather than the estimator invariant. Replacing the translated fallback with an unconditional pair-distance formula would impose O(n^2) work on ordinary larger samples without evidence that the commercial path needs it. Arbitrary-precision production arithmetic would also broaden ownership and dependency surface far beyond the demonstrated defect. The bounded admission instead reuses the existing checked dyadic proof and changes only its scientifically evidenced upper sample count. + +## Method and standards trace + +Morris, White, and Crowther (2019) distinguish deterministic performance-measure calculations from Monte Carlo uncertainty; a one-ULP deterministic bias-SE error is therefore repaired in the estimator rather than treated as simulation noise. Published floating-point authority remains IEEE 754-2019 / ISO/IEC 60559:2020. IEEE P754 is active revision work, not a published replacement. AERA/APA/NCME public Testing Standards authority remains the 2014 edition while its revision proceeds. + +## Traceability + +- Bounded context: Validation Evidence. +- Production module/API: `crates/validation_core/src/bias_se.rs` → `bias_standard_error`. +- Public executable contract: `crates/validation_core/tests/bias_standard_error_seven_observation_pair_distance_contract.rs`. +- Scientific invariant: for exact represented residuals admitted by the bounded proof, `SE(mean)^2 = sum_{i Date: Sat, 5 Sep 2026 23:01:29 +0900 Subject: [PATCH 432/576] test(validation): expose eight-observation bias SE rounding --- ...ight_observation_pair_distance_contract.rs | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_eight_observation_pair_distance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_eight_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_eight_observation_pair_distance_contract.rs new file mode 100644 index 000000000..09d40ba7c --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_eight_observation_pair_distance_contract.rs @@ -0,0 +1,51 @@ +use validation_core::bias_standard_error; + +fn assert_eight_observation_pair_distance_contract(recovered: [f64; 8]) { + let truth = [0.0; 8]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x41ac_8601_97ac_cd4c, + "the exact eight-observation pair-distance ratio must determine the represented-input SE; the translated floating moment/sqrt fallback rounds one ULP high" + ); +} + +#[test] +fn exact_eight_observation_dispersion_uses_pair_distance_midpoint_proof() { + let samples = [ + [ + 121_838_114.0, + 741_324_193.0, + 994_684_355.0, + 1_673_429_661.0, + 1_824_103_795.0, + 1_861_311_798.0, + 1_872_117_478.0, + 1_936_395_613.0, + ], + [ + 1_936_395_613.0, + 1_872_117_478.0, + 1_861_311_798.0, + 1_824_103_795.0, + 1_673_429_661.0, + 994_684_355.0, + 741_324_193.0, + 121_838_114.0, + ], + [ + 1_824_103_795.0, + 121_838_114.0, + 1_936_395_613.0, + 994_684_355.0, + 1_872_117_478.0, + 741_324_193.0, + 1_861_311_798.0, + 1_673_429_661.0, + ], + ]; + for recovered in samples { + assert_eight_observation_pair_distance_contract(recovered); + assert_eight_observation_pair_distance_contract(recovered.map(|value| -value)); + } +} From 26ca2d68bf057b93b9fef1e14953bedbdcf754c0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 23:03:24 +0900 Subject: [PATCH 433/576] fix(validation): admit exact eight-observation bias SE proof --- crates/validation_core/src/bias_se.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 0e5d74a5d..356fc452f 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -173,10 +173,10 @@ fn exact_pair_distance_standard_error( recovered: &[f64], ) -> Option> { // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have - // cheaper exact identities in `bias.rs`; four through seven observations are + // cheaper exact identities in `bias.rs`; four through eight observations are // the smallest remaining sample sizes with demonstrated one-ULP errors in // the translated floating moment/sqrt path. - if truth.len() != recovered.len() || !(4..=7).contains(&truth.len()) { + if truth.len() != recovered.len() || !(4..=8).contains(&truth.len()) { return None; } let sample_count = truth.len(); @@ -259,7 +259,7 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- through seven-observation samples whose represented residuals and +/// Four- through eight-observation samples whose represented residuals and /// pairwise differences are exact use the exact pair-distance identity when its /// reduced dyadic ratio fits the bounded integer proof. All other samples retain /// the established bias implementation and its existing fail-closed behavior. @@ -447,6 +447,10 @@ mod tests { ); assert_eq!( exact_pair_distance_standard_error(&[0.0; 8], &[0.0; 8]), + Some(Ok(0.0)) + ); + assert_eq!( + exact_pair_distance_standard_error(&[0.0; 9], &[0.0; 9]), None ); assert_eq!( From 2641b2613a7c37e4153d9b8baa47487a1b80718e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 23:04:30 +0900 Subject: [PATCH 434/576] docs(changelog): record eight-observation bias SE proof --- ...ation-bias-standard-error-eight-observation-pair-distance.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-eight-observation-pair-distance.md diff --git a/CHANGELOG.d/validation-bias-standard-error-eight-observation-pair-distance.md b/CHANGELOG.d/validation-bias-standard-error-eight-observation-pair-distance.md new file mode 100644 index 000000000..9fb2ca898 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-eight-observation-pair-distance.md @@ -0,0 +1,2 @@ +- Validation Evidence now admits the exact pair-distance identity for bounded eight-observation mean-bias standard errors when represented residuals and pairwise differences are proven exact and the reduced dyadic ratio fits the existing checked `u128` midpoint proof. +- This prevents the translated floating moment/`sqrt` fallback from moving a demonstrated exact represented-input eight-observation standard error one ULP high, while retaining the established fallback outside the bounded proof. From 52fa2c87c2673ae2783d119200690c6656a7df65 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 23:04:56 +0900 Subject: [PATCH 435/576] docs(research): trace eight-observation bias SE rounding --- ...ight-observation-pair-distance-rounding.md | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 docs/research/bias-standard-error-eight-observation-pair-distance-rounding.md diff --git a/docs/research/bias-standard-error-eight-observation-pair-distance-rounding.md b/docs/research/bias-standard-error-eight-observation-pair-distance-rounding.md new file mode 100644 index 000000000..3b94d1db1 --- /dev/null +++ b/docs/research/bias-standard-error-eight-observation-pair-distance-rounding.md @@ -0,0 +1,69 @@ +# Eight-observation mean-bias standard error: exact pair-distance rounding + +## Problem + +After GAP-116, the bounded pair-distance/midpoint proof covered four through seven observations. An eight-observation sample with exact represented residuals still fell through to the translated floating second-moment path: + +```text +truth = [0, 0, 0, 0, 0, 0, 0, 0] +recovered = [121838114, 741324193, 994684355, 1673429661, + 1824103795, 1861311798, 1872117478, 1936395613] +``` + +All values are exactly represented binary64 integers and all pairwise differences are exact. The twenty-eight squared pair distances sum to + +```text +N = 25648518292283252135. +``` + +For `n=8`, + +```text +SE(mean)^2 = sum_{i2` standard errors. Extending the bound again requires a demonstrated represented-input counterexample plus numerical and performance evidence rather than a speculative increase. + +CHANGELOG `2641b2613a7c37e4153d9b8baa47487a1b80718e` records the contract. GAP-116 and all preceding Validation Evidence lineages remain inherited. + +## Alternatives rejected + +A payload-specific eight-value branch would encode a fixture rather than the estimator invariant. Replacing the translated fallback with an unconditional pair-distance formula would impose O(n^2) work on ordinary larger samples without evidence that the commercial path needs it. Raising the bound speculatively beyond the demonstrated case would likewise add quadratic work without a scientific defect to justify it. Arbitrary-precision production arithmetic would broaden ownership and dependency surface far beyond the demonstrated defect. The bounded admission instead reuses the existing checked dyadic proof and changes only its evidenced upper sample count. + +## Method and standards trace + +Morris, White, and Crowther (2019) distinguish deterministic performance-measure calculations from Monte Carlo uncertainty; a one-ULP deterministic bias-SE error is therefore repaired in the estimator rather than treated as simulation noise. Published floating-point authority remains IEEE 754-2019 / ISO/IEC 60559:2020. IEEE P754 is an active PAR approved June 6, 2024 to supersede 754-2019; it is revision work, not a published replacement. AERA, APA, and NCME continue to publish the 2014 *Standards for Educational and Psychological Testing* while their Joint Committee carries out the next revision. + +## Traceability + +- Bounded context: Validation Evidence. +- Production module/API: `crates/validation_core/src/bias_se.rs` → `bias_standard_error`. +- Public executable contract: `crates/validation_core/tests/bias_standard_error_eight_observation_pair_distance_contract.rs`. +- RED: `40413dc9e3279f06c3d02ed2180ac9c170a51001`. +- Causal source repair: `26ca2d68bf057b93b9fef1e14953bedbdcf754c0`. +- CHANGELOG: `2641b2613a7c37e4153d9b8baa47487a1b80718e`. +- Scientific invariant: for exact represented residuals admitted by the bounded proof, `SE(mean)^2 = sum_{i Date: Sat, 5 Sep 2026 23:30:01 +0900 Subject: [PATCH 436/576] test(validation): expose nine-observation bias SE rounding --- ...nine_observation_pair_distance_contract.rs | 54 +++++++++++++++++++ 1 file changed, 54 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_nine_observation_pair_distance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_nine_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_nine_observation_pair_distance_contract.rs new file mode 100644 index 000000000..a1252470a --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_nine_observation_pair_distance_contract.rs @@ -0,0 +1,54 @@ +use validation_core::bias_standard_error; + +fn assert_nine_observation_pair_distance_contract(recovered: [f64; 9]) { + let truth = [0.0; 9]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x41a7_5f1f_6489_5d36, + "the exact nine-observation pair-distance ratio must determine the represented-input SE; the translated floating moment/sqrt fallback rounds one ULP low" + ); +} + +#[test] +fn exact_nine_observation_dispersion_uses_pair_distance_midpoint_proof() { + let samples = [ + [ + 354_161_013.0, + 513_228_884.0, + 592_175_752.0, + 1_188_877_551.0, + 1_313_358_136.0, + 1_582_012_649.0, + 1_600_441_891.0, + 1_764_736_057.0, + 1_957_134_661.0, + ], + [ + 1_957_134_661.0, + 1_764_736_057.0, + 1_600_441_891.0, + 1_582_012_649.0, + 1_313_358_136.0, + 1_188_877_551.0, + 592_175_752.0, + 513_228_884.0, + 354_161_013.0, + ], + [ + 1_313_358_136.0, + 354_161_013.0, + 1_957_134_661.0, + 592_175_752.0, + 1_764_736_057.0, + 513_228_884.0, + 1_600_441_891.0, + 1_188_877_551.0, + 1_582_012_649.0, + ], + ]; + for recovered in samples { + assert_nine_observation_pair_distance_contract(recovered); + assert_nine_observation_pair_distance_contract(recovered.map(|value| -value)); + } +} From bcd2ab5e1e6bd38f0661e7bb47e5d59a78830499 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 23:31:35 +0900 Subject: [PATCH 437/576] fix(validation): admit exact nine-observation bias SE proof --- crates/validation_core/src/bias_se.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 356fc452f..31b417d27 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -173,10 +173,10 @@ fn exact_pair_distance_standard_error( recovered: &[f64], ) -> Option> { // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have - // cheaper exact identities in `bias.rs`; four through eight observations are + // cheaper exact identities in `bias.rs`; four through nine observations are // the smallest remaining sample sizes with demonstrated one-ULP errors in // the translated floating moment/sqrt path. - if truth.len() != recovered.len() || !(4..=8).contains(&truth.len()) { + if truth.len() != recovered.len() || !(4..=9).contains(&truth.len()) { return None; } let sample_count = truth.len(); @@ -259,7 +259,7 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- through eight-observation samples whose represented residuals and +/// Four- through nine-observation samples whose represented residuals and /// pairwise differences are exact use the exact pair-distance identity when its /// reduced dyadic ratio fits the bounded integer proof. All other samples retain /// the established bias implementation and its existing fail-closed behavior. @@ -446,11 +446,11 @@ mod tests { None ); assert_eq!( - exact_pair_distance_standard_error(&[0.0; 8], &[0.0; 8]), + exact_pair_distance_standard_error(&[0.0; 9], &[0.0; 9]), Some(Ok(0.0)) ); assert_eq!( - exact_pair_distance_standard_error(&[0.0; 9], &[0.0; 9]), + exact_pair_distance_standard_error(&[0.0; 10], &[0.0; 10]), None ); assert_eq!( From f575fa2143e0e46058dd62d6dcdd6e6c3e6354b1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 23:32:05 +0900 Subject: [PATCH 438/576] docs(changelog): record nine-observation bias SE proof --- ...dation-bias-standard-error-nine-observation-pair-distance.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-nine-observation-pair-distance.md diff --git a/CHANGELOG.d/validation-bias-standard-error-nine-observation-pair-distance.md b/CHANGELOG.d/validation-bias-standard-error-nine-observation-pair-distance.md new file mode 100644 index 000000000..da539466c --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-nine-observation-pair-distance.md @@ -0,0 +1,2 @@ +- Validation Evidence now admits the exact pair-distance identity for bounded nine-observation mean-bias standard errors when represented residuals and pairwise differences are proven exact and the reduced dyadic ratio fits the existing checked `u128` midpoint proof. +- This prevents the translated floating moment/`sqrt` fallback from moving a demonstrated exact represented-input nine-observation standard error one ULP low, while retaining the established fallback outside the bounded proof. From c76f0021fdfaf9a58f226db2ceb58a34f5b4be92 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 23:32:28 +0900 Subject: [PATCH 439/576] docs(research): trace nine-observation bias SE rounding --- ...nine-observation-pair-distance-rounding.md | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 docs/research/bias-standard-error-nine-observation-pair-distance-rounding.md diff --git a/docs/research/bias-standard-error-nine-observation-pair-distance-rounding.md b/docs/research/bias-standard-error-nine-observation-pair-distance-rounding.md new file mode 100644 index 000000000..00504d5eb --- /dev/null +++ b/docs/research/bias-standard-error-nine-observation-pair-distance-rounding.md @@ -0,0 +1,69 @@ +# Nine-observation mean-bias standard error: exact pair-distance rounding + +## Problem + +After GAP-117, the bounded pair-distance/midpoint proof covered four through eight observations. A nine-observation sample with exact represented residuals still fell through to the translated floating second-moment path: + +```text +truth = [0, 0, 0, 0, 0, 0, 0, 0, 0] +recovered = [354161013, 513228884, 592175752, 1188877551, 1313358136, + 1582012649, 1600441891, 1764736057, 1957134661] +``` + +All values are exactly represented binary64 integers and all pairwise differences are exact. The thirty-six squared pair distances sum to + +```text +N = 24907531253053169466. +``` + +For `n=9`, + +```text +SE(mean)^2 = sum_{i2` standard errors. Extending the bound again requires a demonstrated represented-input counterexample plus numerical and performance evidence rather than a speculative increase. + +CHANGELOG `f575fa2143e0e46058dd62d6dcdd6e6c3e6354b1` records the contract. GAP-117 and all preceding Validation Evidence lineages remain inherited. + +## Alternatives rejected + +A payload-specific nine-value branch would encode a fixture rather than the estimator invariant. Replacing the translated fallback with an unconditional pair-distance formula would impose O(n^2) work on ordinary larger samples without evidence that the commercial path needs it. Raising the bound speculatively beyond the demonstrated case would likewise add quadratic work without a scientific defect to justify it. Arbitrary-precision production arithmetic would broaden ownership and dependency surface far beyond the demonstrated defect. The bounded admission instead reuses the existing checked dyadic proof and changes only its evidenced upper sample count. + +## Method and standards trace + +Morris, White, and Crowther (2019) distinguish deterministic performance-measure calculations from Monte Carlo uncertainty; a one-ULP deterministic bias-SE error is therefore repaired in the estimator rather than treated as simulation noise. Published floating-point authority remains IEEE 754-2019 / ISO/IEC 60559:2020. IEEE P754 remains an active PAR approved June 6, 2024 to supersede 754-2019; it is revision work, not a published replacement. ISO continues to list ISO/IEC 60559:2020 as a published International Standard at stage 60.60. AERA, APA, and NCME continue revising the 2014 *Standards for Educational and Psychological Testing*; AERA's task-force roster was current as of August 31, 2026, so the unpublished revision is not treated as normative authority. + +## Traceability + +- Bounded context: Validation Evidence. +- Production module/API: `crates/validation_core/src/bias_se.rs` → `bias_standard_error`. +- Public executable contract: `crates/validation_core/tests/bias_standard_error_nine_observation_pair_distance_contract.rs`. +- RED: `c7b6537763f303069aa11e4ebfa12b7b3093448c`. +- Causal source repair: `bcd2ab5e1e6bd38f0661e7bb47e5d59a78830499`. +- CHANGELOG: `f575fa2143e0e46058dd62d6dcdd6e6c3e6354b1`. +- Scientific invariant: for exact represented residuals admitted by the bounded proof, `SE(mean)^2 = sum_{i Date: Sat, 5 Sep 2026 23:39:41 +0900 Subject: [PATCH 440/576] test(validation): expose ten-observation bias SE rounding --- ..._ten_observation_pair_distance_contract.rs | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_ten_observation_pair_distance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_ten_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_ten_observation_pair_distance_contract.rs new file mode 100644 index 000000000..e3ea895c0 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_ten_observation_pair_distance_contract.rs @@ -0,0 +1,57 @@ +use validation_core::bias_standard_error; + +fn assert_ten_observation_pair_distance_contract(recovered: [f64; 10]) { + let truth = [0.0; 10]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x41a5_e8a1_0795_bf6c, + "the exact ten-observation pair-distance ratio must determine the represented-input SE; the translated floating moment/sqrt fallback rounds one ULP low" + ); +} + +#[test] +fn exact_ten_observation_dispersion_uses_pair_distance_midpoint_proof() { + let samples = [ + [ + 418_906_532.0, + 431_535_003.0, + 554_793_949.0, + 905_115_878.0, + 1_079_195_036.0, + 1_340_223_962.0, + 1_591_821_052.0, + 1_592_008_872.0, + 1_807_262_929.0, + 1_996_099_302.0, + ], + [ + 1_996_099_302.0, + 1_807_262_929.0, + 1_592_008_872.0, + 1_591_821_052.0, + 1_340_223_962.0, + 1_079_195_036.0, + 905_115_878.0, + 554_793_949.0, + 431_535_003.0, + 418_906_532.0, + ], + [ + 1_079_195_036.0, + 418_906_532.0, + 1_996_099_302.0, + 554_793_949.0, + 1_807_262_929.0, + 431_535_003.0, + 1_592_008_872.0, + 905_115_878.0, + 1_591_821_052.0, + 1_340_223_962.0, + ], + ]; + for recovered in samples { + assert_ten_observation_pair_distance_contract(recovered); + assert_ten_observation_pair_distance_contract(recovered.map(|value| -value)); + } +} From 1bff5033e2de3095340141da38e9c02387b3a868 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 23:41:17 +0900 Subject: [PATCH 441/576] fix(validation): admit exact ten-observation bias SE proof --- crates/validation_core/src/bias_se.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 31b417d27..a6a8f3407 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -173,10 +173,10 @@ fn exact_pair_distance_standard_error( recovered: &[f64], ) -> Option> { // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have - // cheaper exact identities in `bias.rs`; four through nine observations are + // cheaper exact identities in `bias.rs`; four through ten observations are // the smallest remaining sample sizes with demonstrated one-ULP errors in // the translated floating moment/sqrt path. - if truth.len() != recovered.len() || !(4..=9).contains(&truth.len()) { + if truth.len() != recovered.len() || !(4..=10).contains(&truth.len()) { return None; } let sample_count = truth.len(); @@ -259,7 +259,7 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- through nine-observation samples whose represented residuals and +/// Four- through ten-observation samples whose represented residuals and /// pairwise differences are exact use the exact pair-distance identity when its /// reduced dyadic ratio fits the bounded integer proof. All other samples retain /// the established bias implementation and its existing fail-closed behavior. @@ -446,11 +446,11 @@ mod tests { None ); assert_eq!( - exact_pair_distance_standard_error(&[0.0; 9], &[0.0; 9]), + exact_pair_distance_standard_error(&[0.0; 10], &[0.0; 10]), Some(Ok(0.0)) ); assert_eq!( - exact_pair_distance_standard_error(&[0.0; 10], &[0.0; 10]), + exact_pair_distance_standard_error(&[0.0; 11], &[0.0; 11]), None ); assert_eq!( From 2dacce55a06cc160ae3defcc84b75a8cebd46e18 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 23:41:28 +0900 Subject: [PATCH 442/576] docs(changelog): record ten-observation bias SE proof --- ...idation-bias-standard-error-ten-observation-pair-distance.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-ten-observation-pair-distance.md diff --git a/CHANGELOG.d/validation-bias-standard-error-ten-observation-pair-distance.md b/CHANGELOG.d/validation-bias-standard-error-ten-observation-pair-distance.md new file mode 100644 index 000000000..550bef04c --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-ten-observation-pair-distance.md @@ -0,0 +1,2 @@ +- Validation Evidence now admits the exact pair-distance identity for bounded ten-observation mean-bias standard errors when represented residuals and pairwise differences are proven exact and the reduced dyadic ratio fits the existing checked `u128` midpoint proof. +- This prevents the translated floating moment/`sqrt` fallback from moving a demonstrated exact represented-input ten-observation standard error one ULP low, while retaining the established fallback outside the bounded proof. From bf32b5090f887935cad836b3ec52d4d9d9c357af Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sat, 5 Sep 2026 23:41:52 +0900 Subject: [PATCH 443/576] docs(research): trace ten-observation bias SE rounding --- ...-ten-observation-pair-distance-rounding.md | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 docs/research/bias-standard-error-ten-observation-pair-distance-rounding.md diff --git a/docs/research/bias-standard-error-ten-observation-pair-distance-rounding.md b/docs/research/bias-standard-error-ten-observation-pair-distance-rounding.md new file mode 100644 index 000000000..2e25e587f --- /dev/null +++ b/docs/research/bias-standard-error-ten-observation-pair-distance-rounding.md @@ -0,0 +1,69 @@ +# Ten-observation mean-bias standard error: exact pair-distance rounding + +## Problem + +After GAP-118, the bounded pair-distance/midpoint proof covered four through nine observations. A ten-observation sample with exact represented residuals still fell through to the translated floating second-moment path: + +```text +truth = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +recovered = [418906532, 431535003, 554793949, 905115878, 1079195036, + 1340223962, 1591821052, 1592008872, 1807262929, 1996099302] +``` + +All values are exactly represented binary64 integers and all pairwise differences are exact. The forty-five squared pair distances sum to + +```text +N = 30398755841753540685. +``` + +For `n=10`, + +```text +SE(mean)^2 = sum_{i2` standard errors. Extending the bound again requires a demonstrated represented-input counterexample plus numerical and performance evidence rather than a speculative increase. + +CHANGELOG `2dacce55a06cc160ae3defcc84b75a8cebd46e18` records the contract. GAP-118 and all preceding Validation Evidence lineages remain inherited. + +## Alternatives rejected + +A payload-specific ten-value branch would encode a fixture rather than the estimator invariant. Replacing the translated fallback with an unconditional pair-distance formula would impose O(n^2) work on ordinary larger samples without evidence that the commercial path needs it. Raising the bound speculatively beyond the demonstrated case would likewise add quadratic work without a scientific defect to justify it. Arbitrary-precision production arithmetic would broaden ownership and dependency surface far beyond the demonstrated defect. The bounded admission instead reuses the existing checked dyadic proof and changes only its evidenced upper sample count. + +## Method and standards trace + +Morris, White, and Crowther (2019) distinguish deterministic performance-measure calculations from Monte Carlo uncertainty; a one-ULP deterministic bias-SE error is therefore repaired in the estimator rather than treated as simulation noise. Published floating-point authority remains IEEE 754-2019 / ISO/IEC 60559:2020. IEEE P754 remains an active PAR approved June 6, 2024 to supersede 754-2019; it is revision work, not a published replacement. ISO continues to list ISO/IEC 60559:2020 as a published International Standard at stage 60.60. AERA, APA, and NCME continue revising the 2014 *Standards for Educational and Psychological Testing*; AERA's task-force roster was current as of August 31, 2026, so the unpublished revision is not treated as normative authority. + +## Traceability + +- Bounded context: Validation Evidence. +- Production module/API: `crates/validation_core/src/bias_se.rs` → `bias_standard_error`. +- Public executable contract: `crates/validation_core/tests/bias_standard_error_ten_observation_pair_distance_contract.rs`. +- RED: `813c97e8e5e13ee7cec7e4290b7bb78d68504cdd`. +- Causal source repair: `1bff5033e2de3095340141da38e9c02387b3a868`. +- CHANGELOG: `2dacce55a06cc160ae3defcc84b75a8cebd46e18`. +- Scientific invariant: for exact represented residuals admitted by the bounded proof, `SE(mean)^2 = sum_{i Date: Sun, 6 Sep 2026 00:00:09 +0900 Subject: [PATCH 444/576] test(validation): expose eleven-observation bias SE rounding --- ...even_observation_pair_distance_contract.rs | 60 +++++++++++++++++++ 1 file changed, 60 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_eleven_observation_pair_distance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_eleven_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_eleven_observation_pair_distance_contract.rs new file mode 100644 index 000000000..a95a6f82b --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_eleven_observation_pair_distance_contract.rs @@ -0,0 +1,60 @@ +use validation_core::bias_standard_error; + +fn assert_eleven_observation_pair_distance_contract(recovered: [f64; 11]) { + let truth = [0.0; 11]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x41a4_47fc_a451_7b3f, + "the exact eleven-observation pair-distance ratio must determine the represented-input SE; the translated floating moment/sqrt fallback rounds one ULP low" + ); +} + +#[test] +fn exact_eleven_observation_dispersion_uses_pair_distance_midpoint_proof() { + let samples = [ + [ + 50_511_426.0, + 167_164_486.0, + 318_141_475.0, + 357_712_576.0, + 407_960_427.0, + 441_767_841.0, + 691_573_103.0, + 733_495_428.0, + 1_082_192_974.0, + 1_543_970_183.0, + 1_797_594_737.0, + ], + [ + 1_797_594_737.0, + 1_543_970_183.0, + 1_082_192_974.0, + 733_495_428.0, + 691_573_103.0, + 441_767_841.0, + 407_960_427.0, + 357_712_576.0, + 318_141_475.0, + 167_164_486.0, + 50_511_426.0, + ], + [ + 691_573_103.0, + 50_511_426.0, + 1_797_594_737.0, + 318_141_475.0, + 1_543_970_183.0, + 167_164_486.0, + 1_082_192_974.0, + 407_960_427.0, + 733_495_428.0, + 357_712_576.0, + 441_767_841.0, + ], + ]; + for recovered in samples { + assert_eleven_observation_pair_distance_contract(recovered); + assert_eleven_observation_pair_distance_contract(recovered.map(|value| -value)); + } +} From 59c5a4ef3bb9693329bfb359cecf3bbd93eecb3b Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 00:04:54 +0900 Subject: [PATCH 445/576] fix(validation): admit exact eleven-observation pair distances --- crates/validation_core/src/bias_se.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index a6a8f3407..81109fee1 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -173,10 +173,10 @@ fn exact_pair_distance_standard_error( recovered: &[f64], ) -> Option> { // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have - // cheaper exact identities in `bias.rs`; four through ten observations are + // cheaper exact identities in `bias.rs`; four through eleven observations are // the smallest remaining sample sizes with demonstrated one-ULP errors in // the translated floating moment/sqrt path. - if truth.len() != recovered.len() || !(4..=10).contains(&truth.len()) { + if truth.len() != recovered.len() || !(4..=11).contains(&truth.len()) { return None; } let sample_count = truth.len(); @@ -259,7 +259,7 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- through ten-observation samples whose represented residuals and +/// Four- through eleven-observation samples whose represented residuals and /// pairwise differences are exact use the exact pair-distance identity when its /// reduced dyadic ratio fits the bounded integer proof. All other samples retain /// the established bias implementation and its existing fail-closed behavior. @@ -451,6 +451,10 @@ mod tests { ); assert_eq!( exact_pair_distance_standard_error(&[0.0; 11], &[0.0; 11]), + Some(Ok(0.0)) + ); + assert_eq!( + exact_pair_distance_standard_error(&[0.0; 12], &[0.0; 12]), None ); assert_eq!( From 2e9c6a54c417ccbe4e567b8f70e50175f1c47553 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 00:05:26 +0900 Subject: [PATCH 446/576] docs(changelog): record eleven-observation bias SE proof --- ...tion-bias-standard-error-eleven-observation-pair-distance.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-eleven-observation-pair-distance.md diff --git a/CHANGELOG.d/validation-bias-standard-error-eleven-observation-pair-distance.md b/CHANGELOG.d/validation-bias-standard-error-eleven-observation-pair-distance.md new file mode 100644 index 000000000..d5cf5ed90 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-eleven-observation-pair-distance.md @@ -0,0 +1,2 @@ +- Validation Evidence now admits the exact pair-distance identity for bounded eleven-observation mean-bias standard errors when represented residuals and pairwise differences are proven exact and the reduced dyadic ratio fits the existing checked `u128` midpoint proof. +- This prevents the translated floating moment/`sqrt` fallback from moving a demonstrated exact represented-input eleven-observation standard error one ULP low, while retaining the established fallback outside the bounded proof. From 478a28bf7b247603ed12ac4f00d9a5a51df6771a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 00:06:12 +0900 Subject: [PATCH 447/576] docs(research): trace eleven-observation bias SE rounding --- ...even-observation-pair-distance-rounding.md | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) create mode 100644 docs/research/bias-standard-error-eleven-observation-pair-distance-rounding.md diff --git a/docs/research/bias-standard-error-eleven-observation-pair-distance-rounding.md b/docs/research/bias-standard-error-eleven-observation-pair-distance-rounding.md new file mode 100644 index 000000000..dbfb2f215 --- /dev/null +++ b/docs/research/bias-standard-error-eleven-observation-pair-distance-rounding.md @@ -0,0 +1,70 @@ +# Eleven-observation mean-bias standard error: exact pair-distance rounding + +## Problem + +After GAP-119, the bounded pair-distance/midpoint proof covered four through ten observations. An eleven-observation sample with exact represented residuals still fell through to the translated floating second-moment path: + +```text +truth = [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] +recovered = [50511426, 167164486, 318141475, 357712576, 407960427, + 441767841, 691573103, 733495428, 1082192974, 1543970183, + 1797594737] +``` + +All values are exactly represented binary64 integers and all pairwise differences are exact. The fifty-five squared pair distances sum to + +```text +N = 35022924934975401574. +``` + +For `n=11`, + +```text +SE(mean)^2 = sum_{i2` standard errors. `n=12` remains on the established path until a separate represented-input counterexample and performance evidence justify another extension. + +CHANGELOG `2e9c6a54c417ccbe4e567b8f70e50175f1c47553` records the contract. GAP-119 and all preceding Validation Evidence lineages remain inherited. + +## Alternatives rejected + +A payload-specific eleven-value branch would encode a fixture rather than the estimator invariant. Replacing the translated fallback with an unconditional pair-distance formula would impose O(n^2) work on ordinary larger samples without evidence that the commercial path needs it. Raising the bound speculatively beyond the demonstrated case would likewise add quadratic work without a scientific defect to justify it. Arbitrary-precision production arithmetic would broaden ownership and dependency surface beyond the demonstrated defect. The bounded admission reuses the existing checked dyadic proof and changes only its evidenced upper sample count. + +## Method and standards trace + +Morris, White, and Crowther (2019) distinguish deterministic performance-measure calculations from Monte Carlo uncertainty; a one-ULP deterministic bias-SE error is therefore repaired in the estimator rather than treated as simulation noise. Published floating-point authority remains IEEE 754-2019 / ISO/IEC 60559:2020. IEEE P754 remains an active PAR approved June 6, 2024 to supersede 754-2019; it is revision work, not a published replacement. ISO continues to list ISO/IEC 60559:2020 as a published International Standard at stage 60.60. AERA, APA, and NCME continue revising the 2014 *Standards for Educational and Psychological Testing*; the unpublished revision is not treated as normative authority. + +## Traceability + +- Bounded context: Validation Evidence. +- Production module/API: `crates/validation_core/src/bias_se.rs` → `bias_standard_error`. +- Public executable contract: `crates/validation_core/tests/bias_standard_error_eleven_observation_pair_distance_contract.rs`. +- RED: `967e1e093603cbefc01889adce0087d744bfcd90`. +- Causal source repair: `59c5a4ef3bb9693329bfb359cecf3bbd93eecb3b`. +- CHANGELOG: `2e9c6a54c417ccbe4e567b8f70e50175f1c47553`. +- Scientific invariant: for exact represented residuals admitted by the bounded proof, `SE(mean)^2 = sum_{i Date: Sun, 6 Sep 2026 00:18:55 +0900 Subject: [PATCH 448/576] test(validation): expose twelve-observation bias SE rounding --- ...elve_observation_pair_distance_contract.rs | 63 +++++++++++++++++++ 1 file changed, 63 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_twelve_observation_pair_distance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_twelve_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_twelve_observation_pair_distance_contract.rs new file mode 100644 index 000000000..b139e222d --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_twelve_observation_pair_distance_contract.rs @@ -0,0 +1,63 @@ +use validation_core::bias_standard_error; + +fn assert_twelve_observation_pair_distance_contract(recovered: [f64; 12]) { + let truth = [0.0; 12]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x41a6_5ddb_5161_045f, + "the exact twelve-observation pair-distance ratio must determine the represented-input SE; the translated floating moment/sqrt fallback rounds one ULP high" + ); +} + +#[test] +fn exact_twelve_observation_dispersion_uses_pair_distance_midpoint_proof() { + let samples = [ + [ + 18_775_780.0, + 73_991_125.0, + 198_689_967.0, + 631_050_858.0, + 778_682_730.0, + 826_435_964.0, + 853_584_967.0, + 1_530_809_509.0, + 1_562_270_376.0, + 1_586_067_346.0, + 1_682_017_356.0, + 1_750_122_820.0, + ], + [ + 1_750_122_820.0, + 1_682_017_356.0, + 1_586_067_346.0, + 1_562_270_376.0, + 1_530_809_509.0, + 853_584_967.0, + 826_435_964.0, + 778_682_730.0, + 631_050_858.0, + 198_689_967.0, + 73_991_125.0, + 18_775_780.0, + ], + [ + 826_435_964.0, + 18_775_780.0, + 1_750_122_820.0, + 198_689_967.0, + 1_682_017_356.0, + 73_991_125.0, + 1_530_809_509.0, + 778_682_730.0, + 1_586_067_346.0, + 631_050_858.0, + 1_562_270_376.0, + 853_584_967.0, + ], + ]; + for recovered in samples { + assert_twelve_observation_pair_distance_contract(recovered); + assert_twelve_observation_pair_distance_contract(recovered.map(|value| -value)); + } +} From 035cf392b5e9f115c7b5a2589ebbeadb311d7a45 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 00:22:23 +0900 Subject: [PATCH 449/576] fix(validation): admit exact twelve-observation pair distances --- crates/validation_core/src/bias_se.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 81109fee1..e675c2eaa 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -173,10 +173,10 @@ fn exact_pair_distance_standard_error( recovered: &[f64], ) -> Option> { // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have - // cheaper exact identities in `bias.rs`; four through eleven observations are + // cheaper exact identities in `bias.rs`; four through twelve observations are // the smallest remaining sample sizes with demonstrated one-ULP errors in // the translated floating moment/sqrt path. - if truth.len() != recovered.len() || !(4..=11).contains(&truth.len()) { + if truth.len() != recovered.len() || !(4..=12).contains(&truth.len()) { return None; } let sample_count = truth.len(); @@ -259,7 +259,7 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- through eleven-observation samples whose represented residuals and +/// Four- through twelve-observation samples whose represented residuals and /// pairwise differences are exact use the exact pair-distance identity when its /// reduced dyadic ratio fits the bounded integer proof. All other samples retain /// the established bias implementation and its existing fail-closed behavior. @@ -455,6 +455,10 @@ mod tests { ); assert_eq!( exact_pair_distance_standard_error(&[0.0; 12], &[0.0; 12]), + Some(Ok(0.0)) + ); + assert_eq!( + exact_pair_distance_standard_error(&[0.0; 13], &[0.0; 13]), None ); assert_eq!( From 369af46de7e0719cfb4db04fedf4d2775e04f62c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 01:03:13 +0900 Subject: [PATCH 450/576] test(validation): expose thirteen-observation bias SE rounding --- ...teen_observation_pair_distance_contract.rs | 66 +++++++++++++++++++ 1 file changed, 66 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_thirteen_observation_pair_distance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_thirteen_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_thirteen_observation_pair_distance_contract.rs new file mode 100644 index 000000000..15da23b51 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_thirteen_observation_pair_distance_contract.rs @@ -0,0 +1,66 @@ +use validation_core::bias_standard_error; + +fn assert_thirteen_observation_pair_distance_contract(recovered: [f64; 13]) { + let truth = [0.0; 13]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x41a2_9a8e_6db8_cb76, + "the exact thirteen-observation pair-distance ratio must determine the represented-input SE; the translated floating moment/sqrt fallback rounds one ULP high" + ); +} + +#[test] +fn exact_thirteen_observation_dispersion_uses_pair_distance_midpoint_proof() { + let samples = [ + [ + 13_412_968.0, + 42_445_497.0, + 117_340_356.0, + 152_587_301.0, + 309_740_336.0, + 359_871_277.0, + 717_207_453.0, + 811_347_466.0, + 1_016_388_094.0, + 1_092_140_579.0, + 1_412_658_032.0, + 1_429_960_424.0, + 1_525_741_984.0, + ], + [ + 1_525_741_984.0, + 1_429_960_424.0, + 1_412_658_032.0, + 1_092_140_579.0, + 1_016_388_094.0, + 811_347_466.0, + 717_207_453.0, + 359_871_277.0, + 309_740_336.0, + 152_587_301.0, + 117_340_356.0, + 42_445_497.0, + 13_412_968.0, + ], + [ + 717_207_453.0, + 13_412_968.0, + 1_525_741_984.0, + 152_587_301.0, + 1_412_658_032.0, + 42_445_497.0, + 1_092_140_579.0, + 309_740_336.0, + 1_429_960_424.0, + 117_340_356.0, + 1_016_388_094.0, + 359_871_277.0, + 811_347_466.0, + ], + ]; + for recovered in samples { + assert_thirteen_observation_pair_distance_contract(recovered); + assert_thirteen_observation_pair_distance_contract(recovered.map(|value| -value)); + } +} From 0b8727e7550022ad5f89b2e5b48129f5b2f520eb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 01:06:03 +0900 Subject: [PATCH 451/576] fix(validation): admit exact thirteen-observation pair distances --- crates/validation_core/src/bias_se.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index e675c2eaa..338201e9d 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -173,10 +173,10 @@ fn exact_pair_distance_standard_error( recovered: &[f64], ) -> Option> { // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have - // cheaper exact identities in `bias.rs`; four through twelve observations are + // cheaper exact identities in `bias.rs`; four through thirteen observations are // the smallest remaining sample sizes with demonstrated one-ULP errors in // the translated floating moment/sqrt path. - if truth.len() != recovered.len() || !(4..=12).contains(&truth.len()) { + if truth.len() != recovered.len() || !(4..=13).contains(&truth.len()) { return None; } let sample_count = truth.len(); @@ -259,7 +259,7 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- through twelve-observation samples whose represented residuals and +/// Four- through thirteen-observation samples whose represented residuals and /// pairwise differences are exact use the exact pair-distance identity when its /// reduced dyadic ratio fits the bounded integer proof. All other samples retain /// the established bias implementation and its existing fail-closed behavior. @@ -459,6 +459,10 @@ mod tests { ); assert_eq!( exact_pair_distance_standard_error(&[0.0; 13], &[0.0; 13]), + Some(Ok(0.0)) + ); + assert_eq!( + exact_pair_distance_standard_error(&[0.0; 14], &[0.0; 14]), None ); assert_eq!( From db40397603e2514cf3be25783dc65018aab64f10 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 01:58:12 +0900 Subject: [PATCH 452/576] test(validation): expose fourteen-observation bias SE rounding --- ...teen_observation_pair_distance_contract.rs | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_fourteen_observation_pair_distance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_fourteen_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_fourteen_observation_pair_distance_contract.rs new file mode 100644 index 000000000..513f94779 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_fourteen_observation_pair_distance_contract.rs @@ -0,0 +1,69 @@ +use validation_core::bias_standard_error; + +fn assert_fourteen_observation_pair_distance_contract(recovered: [f64; 14]) { + let truth = [0.0; 14]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x41a2_3df9_5954_fb0b, + "the exact fourteen-observation pair-distance ratio must determine the represented-input SE; the translated floating moment/sqrt fallback rounds one ULP low" + ); +} + +#[test] +fn exact_fourteen_observation_dispersion_uses_pair_distance_midpoint_proof() { + let samples = [ + [ + 169_198_177.0, + 170_212_614.0, + 363_421_213.0, + 482_119_205.0, + 503_813_918.0, + 556_586_639.0, + 757_346_256.0, + 811_004_051.0, + 882_684_595.0, + 948_393_523.0, + 1_052_267_532.0, + 1_523_536_361.0, + 1_895_880_649.0, + 1_922_535_250.0, + ], + [ + 1_922_535_250.0, + 1_895_880_649.0, + 1_523_536_361.0, + 1_052_267_532.0, + 948_393_523.0, + 882_684_595.0, + 811_004_051.0, + 757_346_256.0, + 556_586_639.0, + 503_813_918.0, + 482_119_205.0, + 363_421_213.0, + 170_212_614.0, + 169_198_177.0, + ], + [ + 757_346_256.0, + 169_198_177.0, + 1_922_535_250.0, + 482_119_205.0, + 1_523_536_361.0, + 170_212_614.0, + 948_393_523.0, + 503_813_918.0, + 1_895_880_649.0, + 363_421_213.0, + 1_052_267_532.0, + 556_586_639.0, + 882_684_595.0, + 811_004_051.0, + ], + ]; + for recovered in samples { + assert_fourteen_observation_pair_distance_contract(recovered); + assert_fourteen_observation_pair_distance_contract(recovered.map(|value| -value)); + } +} From 58efe80da3c4a57e2b69860f5d4178894f769420 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 01:59:35 +0900 Subject: [PATCH 453/576] fix(validation): admit exact fourteen-observation pair distances --- crates/validation_core/src/bias_se.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 338201e9d..63eb33734 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -173,10 +173,10 @@ fn exact_pair_distance_standard_error( recovered: &[f64], ) -> Option> { // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have - // cheaper exact identities in `bias.rs`; four through thirteen observations are + // cheaper exact identities in `bias.rs`; four through fourteen observations are // the smallest remaining sample sizes with demonstrated one-ULP errors in // the translated floating moment/sqrt path. - if truth.len() != recovered.len() || !(4..=13).contains(&truth.len()) { + if truth.len() != recovered.len() || !(4..=14).contains(&truth.len()) { return None; } let sample_count = truth.len(); @@ -259,7 +259,7 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- through thirteen-observation samples whose represented residuals and +/// Four- through fourteen-observation samples whose represented residuals and /// pairwise differences are exact use the exact pair-distance identity when its /// reduced dyadic ratio fits the bounded integer proof. All other samples retain /// the established bias implementation and its existing fail-closed behavior. @@ -463,6 +463,10 @@ mod tests { ); assert_eq!( exact_pair_distance_standard_error(&[0.0; 14], &[0.0; 14]), + Some(Ok(0.0)) + ); + assert_eq!( + exact_pair_distance_standard_error(&[0.0; 15], &[0.0; 15]), None ); assert_eq!( From 05e97b3ad2f5ad2b64dd4922c68b28752a13af47 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:01:17 +0900 Subject: [PATCH 454/576] docs(validation): trace exact pair-distance GAP-121 through GAP-123 --- ...rror-fourteen-observation-pair-distance.md | 2 + ...rror-thirteen-observation-pair-distance.md | 2 + ...-error-twelve-observation-pair-distance.md | 2 + ...ough-fourteen-observation-pair-distance.md | 77 +++++++++++++++++++ 4 files changed, 83 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-fourteen-observation-pair-distance.md create mode 100644 CHANGELOG.d/validation-bias-standard-error-thirteen-observation-pair-distance.md create mode 100644 CHANGELOG.d/validation-bias-standard-error-twelve-observation-pair-distance.md create mode 100644 docs/research/validation-bias-standard-error-twelve-through-fourteen-observation-pair-distance.md diff --git a/CHANGELOG.d/validation-bias-standard-error-fourteen-observation-pair-distance.md b/CHANGELOG.d/validation-bias-standard-error-fourteen-observation-pair-distance.md new file mode 100644 index 000000000..dfc280d1f --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-fourteen-observation-pair-distance.md @@ -0,0 +1,2 @@ +- Validation Evidence now admits the exact pair-distance identity for bounded fourteen-observation mean-bias standard errors when represented residuals and pairwise differences are proven exact and the reduced dyadic ratio fits the existing checked `u128` midpoint proof. +- This prevents the translated floating moment/`sqrt` fallback from moving a demonstrated exact represented-input fourteen-observation standard error one ULP low, while retaining the established fallback outside the bounded proof. diff --git a/CHANGELOG.d/validation-bias-standard-error-thirteen-observation-pair-distance.md b/CHANGELOG.d/validation-bias-standard-error-thirteen-observation-pair-distance.md new file mode 100644 index 000000000..f39b37411 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-thirteen-observation-pair-distance.md @@ -0,0 +1,2 @@ +- Validation Evidence now admits the exact pair-distance identity for bounded thirteen-observation mean-bias standard errors when represented residuals and pairwise differences are proven exact and the reduced dyadic ratio fits the existing checked `u128` midpoint proof. +- This prevents the translated floating moment/`sqrt` fallback from moving a demonstrated exact represented-input thirteen-observation standard error one ULP high, while retaining the established fallback outside the bounded proof. diff --git a/CHANGELOG.d/validation-bias-standard-error-twelve-observation-pair-distance.md b/CHANGELOG.d/validation-bias-standard-error-twelve-observation-pair-distance.md new file mode 100644 index 000000000..357117c09 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-twelve-observation-pair-distance.md @@ -0,0 +1,2 @@ +- Validation Evidence now admits the exact pair-distance identity for bounded twelve-observation mean-bias standard errors when represented residuals and pairwise differences are proven exact and the reduced dyadic ratio fits the existing checked `u128` midpoint proof. +- This prevents the translated floating moment/`sqrt` fallback from moving a demonstrated exact represented-input twelve-observation standard error one ULP high, while retaining the established fallback outside the bounded proof. diff --git a/docs/research/validation-bias-standard-error-twelve-through-fourteen-observation-pair-distance.md b/docs/research/validation-bias-standard-error-twelve-through-fourteen-observation-pair-distance.md new file mode 100644 index 000000000..af0b7bf3c --- /dev/null +++ b/docs/research/validation-bias-standard-error-twelve-through-fourteen-observation-pair-distance.md @@ -0,0 +1,77 @@ +# Exact represented-input mean-bias standard error for 12–14 observations + +## Decision scope + +TEPP's Validation Evidence layer treats the represented binary64 inputs as the numerical observation contract. For small samples whose residual subtraction and every pairwise residual difference are proven exact, the identity + +\[ +SE(\bar r)^2 = \frac{\sum_{i repair `035cf392...` | +| GAP-122 | Validation Evidence | `crates/validation_core/src/bias_se.rs` | `crates/validation_core/tests/bias_standard_error_thirteen_observation_pair_distance_contract.rs` | RED `369af46d...` -> repair `0b8727e7...` | +| GAP-123 | Validation Evidence | `crates/validation_core/src/bias_se.rs` | `crates/validation_core/tests/bias_standard_error_fourteen_observation_pair_distance_contract.rs` | RED `db403976...` -> repair `58efe80d...` | + +No ADR or PRD target changes are required: the estimator target remains standard error of mean signed bias under the existing represented-input contract. These changes repair numerical realization and evidence, not latent-variable meaning or service authority. + +## 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. + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization, & International Electrotechnical Commission. (2020). *ISO/IEC 60559:2020 Information technology—Microprocessor systems—Floating-point arithmetic*. + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 1c8a7cedd4ff846d3f3ab226cb4fa25b79650c58 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:06:58 +0900 Subject: [PATCH 455/576] test(validation): expose fifteen-observation bias SE rounding --- ...teen_observation_pair_distance_contract.rs | 72 +++++++++++++++++++ 1 file changed, 72 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_fifteen_observation_pair_distance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_fifteen_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_fifteen_observation_pair_distance_contract.rs new file mode 100644 index 000000000..e46652681 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_fifteen_observation_pair_distance_contract.rs @@ -0,0 +1,72 @@ +use validation_core::bias_standard_error; + +fn assert_fifteen_observation_pair_distance_contract(recovered: [f64; 15]) { + let truth = [0.0; 15]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x41a1_254f_de99_720d, + "the exact fifteen-observation pair-distance ratio must determine the represented-input SE; the translated floating moment/sqrt fallback rounds one ULP low" + ); +} + +#[test] +fn exact_fifteen_observation_dispersion_uses_pair_distance_midpoint_proof() { + let samples = [ + [ + 224_611_356.0, + 291_740_781.0, + 326_373_353.0, + 461_196_594.0, + 686_313_913.0, + 812_468_689.0, + 889_538_573.0, + 1_089_098_727.0, + 1_093_012_981.0, + 1_094_199_400.0, + 1_387_143_595.0, + 1_412_604_591.0, + 1_556_072_759.0, + 1_847_457_618.0, + 1_990_087_657.0, + ], + [ + 1_990_087_657.0, + 1_847_457_618.0, + 1_556_072_759.0, + 1_412_604_591.0, + 1_387_143_595.0, + 1_094_199_400.0, + 1_093_012_981.0, + 1_089_098_727.0, + 889_538_573.0, + 812_468_689.0, + 686_313_913.0, + 461_196_594.0, + 326_373_353.0, + 291_740_781.0, + 224_611_356.0, + ], + [ + 1_089_098_727.0, + 224_611_356.0, + 1_990_087_657.0, + 461_196_594.0, + 1_412_604_591.0, + 291_740_781.0, + 1_094_199_400.0, + 686_313_913.0, + 1_847_457_618.0, + 326_373_353.0, + 1_387_143_595.0, + 812_468_689.0, + 1_556_072_759.0, + 889_538_573.0, + 1_093_012_981.0, + ], + ]; + for recovered in samples { + assert_fifteen_observation_pair_distance_contract(recovered); + assert_fifteen_observation_pair_distance_contract(recovered.map(|value| -value)); + } +} From 3cc041ee1aa5f9871619c483059f5930a056f41a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:07:56 +0900 Subject: [PATCH 456/576] fix(validation): admit exact fifteen-observation pair distances --- crates/validation_core/src/bias_se.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 63eb33734..0b01b0661 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -173,10 +173,10 @@ fn exact_pair_distance_standard_error( recovered: &[f64], ) -> Option> { // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have - // cheaper exact identities in `bias.rs`; four through fourteen observations are + // cheaper exact identities in `bias.rs`; four through fifteen observations are // the smallest remaining sample sizes with demonstrated one-ULP errors in // the translated floating moment/sqrt path. - if truth.len() != recovered.len() || !(4..=14).contains(&truth.len()) { + if truth.len() != recovered.len() || !(4..=15).contains(&truth.len()) { return None; } let sample_count = truth.len(); @@ -259,7 +259,7 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- through fourteen-observation samples whose represented residuals and +/// Four- through fifteen-observation samples whose represented residuals and /// pairwise differences are exact use the exact pair-distance identity when its /// reduced dyadic ratio fits the bounded integer proof. All other samples retain /// the established bias implementation and its existing fail-closed behavior. @@ -467,6 +467,10 @@ mod tests { ); assert_eq!( exact_pair_distance_standard_error(&[0.0; 15], &[0.0; 15]), + Some(Ok(0.0)) + ); + assert_eq!( + exact_pair_distance_standard_error(&[0.0; 16], &[0.0; 16]), None ); assert_eq!( From 93f0ea9d945ce0a19fd7f06c0b90bd5b41c8b1fa Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:08:36 +0900 Subject: [PATCH 457/576] docs(validation): trace fifteen-observation exact bias SE --- ...error-fifteen-observation-pair-distance.md | 2 + ...error-fifteen-observation-pair-distance.md | 50 +++++++++++++++++++ 2 files changed, 52 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-fifteen-observation-pair-distance.md create mode 100644 docs/research/validation-bias-standard-error-fifteen-observation-pair-distance.md diff --git a/CHANGELOG.d/validation-bias-standard-error-fifteen-observation-pair-distance.md b/CHANGELOG.d/validation-bias-standard-error-fifteen-observation-pair-distance.md new file mode 100644 index 000000000..a66d459f6 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-fifteen-observation-pair-distance.md @@ -0,0 +1,2 @@ +- Validation Evidence now admits the exact pair-distance identity for bounded fifteen-observation mean-bias standard errors when represented residuals and pairwise differences are proven exact and the reduced dyadic ratio fits the existing checked `u128` midpoint proof. +- This prevents the translated floating moment/`sqrt` fallback from moving a demonstrated exact represented-input fifteen-observation standard error one ULP low, while retaining the established fallback outside the bounded proof. diff --git a/docs/research/validation-bias-standard-error-fifteen-observation-pair-distance.md b/docs/research/validation-bias-standard-error-fifteen-observation-pair-distance.md new file mode 100644 index 000000000..b6ddfc3fa --- /dev/null +++ b/docs/research/validation-bias-standard-error-fifteen-observation-pair-distance.md @@ -0,0 +1,50 @@ +# Exact represented-input mean-bias standard error for fifteen observations + +## Problem and represented-input evidence + +TEPP's Validation Evidence layer treats the represented binary64 inputs as the numerical observation contract. For an exact fifteen-observation residual sample + +`[224_611_356, 291_740_781, 326_373_353, 461_196_594, 686_313_913, 812_468_689, 889_538_573, 1_089_098_727, 1_093_012_981, 1_094_199_400, 1_387_143_595, 1_412_604_591, 1_556_072_759, 1_847_457_618, 1_990_087_657]`, + +the 105 exact squared pair distances sum to `N = 65_163_338_527_647_814_596`. With `n = 15`, + +`SE(mean)^2 = N / [15^2(15-1)] = N / 3150`. + +`gcd(N,3150)=18`, so the reduced exact radicand is `3_620_185_473_758_211_922 / 175`. The predecessor bounded pair-distance proof stopped at fourteen observations. Its translated floating second-moment/`sqrt` fallback returns `0x41a1_254f_de99_720c`; exact dyadic midpoint-square comparison places the target above the midpoint between that value and its upper neighbor and below the next midpoint, so the correctly rounded represented result is `0x41a1_254f_de99_720d`. + +Public RED `1c8a7cedd4ff846d3f3ab226cb4fa25b79650c58` adds `crates/validation_core/tests/bias_standard_error_fifteen_observation_pair_distance_contract.rs` with original order, reverse order, an independent permutation, and sign mirrors. + +## Causal repair + +Repair `3cc041ee1aa5f9871619c483059f5930a056f41a` changes only `crates/validation_core/src/bias_se.rs` admission from `n=4..=14` to `n=4..=15`, updates its rustdoc, admits exact-zero `n=15`, and moves the explicit fallback boundary to `n=16`. + +The proof conditions are unchanged: residual subtraction and every pairwise residual difference must be error-free; dyadic coefficient construction and pair-square accumulation must fit checked `u128`; the scientific denominator remains `n^2(n-1)` and is GCD-reduced; binary64 ratio/`sqrt` is only a candidate seed; exact candidate-square and adjacent-midpoint comparisons authorize the result. Any proof failure stays on the established general path. + +This is not a payload-specific branch and does not claim a globally correctly rounded standard error for arbitrary `n`. An unconditional O(n^2) reference path, speculative admission beyond the demonstrated boundary, weakening exactness checks, and arbitrary-precision production arithmetic remain rejected. The repeated one-ULP sequence through `n=15` is evidence that the numerical class persists, but a broader production-bound change still requires explicit cost/overflow evidence rather than an unmeasured cutoff removal. + +## Traceability + +| Item | Exact evidence | +|---|---| +| Domain owner | Validation Evidence | +| Public RED | `1c8a7cedd4ff846d3f3ab226cb4fa25b79650c58` | +| Causal repair | `3cc041ee1aa5f9871619c483059f5930a056f41a` | +| Production module/API | `crates/validation_core/src/bias_se.rs` / `validation_core::bias_standard_error` | +| Public test | `crates/validation_core/tests/bias_standard_error_fifteen_observation_pair_distance_contract.rs` | +| Predecessor doctoring | `docs/research/validation-bias-standard-error-twelve-through-fourteen-observation-pair-distance.md` | + +## Methodological and standards basis + +IEEE 754-2019 and ISO/IEC 60559:2020 remain the published floating-point arithmetic authorities for binary64 behavior. Exact midpoint comparison is used so a rounded ratio followed by `sqrt` cannot silently become authoritative when the represented inputs admit an exact bounded rational proof. + +Morris, White, and Crowther (2019) distinguish a performance measure's deterministic calculation from Monte Carlo uncertainty due to finite simulation replications. This defect is deterministic for a fixed represented input and belongs to the former layer. AERA, APA, and NCME's *Standards for Educational and Psychological Testing* provide the broader validity-evidence basis; an LLM judgment does not replace the numerical estimator contract or scientific acceptance evidence. + +## 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. + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization, & International Electrotechnical Commission. (2020). *ISO/IEC 60559:2020 Information technology—Microprocessor systems—Floating-point arithmetic*. + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 65d3965fd9ce8e4667f235fe33ce0f8b38ec5f6c Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:15:03 +0900 Subject: [PATCH 458/576] test(validation): expose sixteen-observation bias SE rounding --- ...teen_observation_pair_distance_contract.rs | 75 +++++++++++++++++++ 1 file changed, 75 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_sixteen_observation_pair_distance_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_sixteen_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_sixteen_observation_pair_distance_contract.rs new file mode 100644 index 000000000..80c7ae535 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_sixteen_observation_pair_distance_contract.rs @@ -0,0 +1,75 @@ +use validation_core::bias_standard_error; + +fn assert_sixteen_observation_pair_distance_contract(recovered: [f64; 16]) { + let truth = [0.0; 16]; + let standard_error = bias_standard_error(&truth, &recovered).expect("representable SE"); + assert_eq!( + standard_error.to_bits(), + 0x419c_fcbb_b78d_2ad5, + "the exact sixteen-observation pair-distance ratio must determine the represented-input SE; the translated floating moment/sqrt fallback rounds one ULP low" + ); +} + +#[test] +fn exact_sixteen_observation_dispersion_uses_pair_distance_midpoint_proof() { + let samples = [ + [ + 314_270_929.0, + 327_661_307.0, + 371_854_441.0, + 398_522_837.0, + 413_483_290.0, + 416_184_956.0, + 565_808_551.0, + 682_627_163.0, + 724_514_517.0, + 731_058_943.0, + 740_662_035.0, + 970_233_120.0, + 1_141_566_755.0, + 1_320_628_283.0, + 1_526_331_271.0, + 1_992_574_092.0, + ], + [ + 1_992_574_092.0, + 1_526_331_271.0, + 1_320_628_283.0, + 1_141_566_755.0, + 970_233_120.0, + 740_662_035.0, + 731_058_943.0, + 724_514_517.0, + 682_627_163.0, + 565_808_551.0, + 416_184_956.0, + 413_483_290.0, + 398_522_837.0, + 371_854_441.0, + 327_661_307.0, + 314_270_929.0, + ], + [ + 682_627_163.0, + 314_270_929.0, + 1_992_574_092.0, + 398_522_837.0, + 1_320_628_283.0, + 327_661_307.0, + 731_058_943.0, + 413_483_290.0, + 1_526_331_271.0, + 371_854_441.0, + 1_141_566_755.0, + 416_184_956.0, + 1_320_628_283.0, + 565_808_551.0, + 740_662_035.0, + 724_514_517.0, + ], + ]; + for recovered in samples { + assert_sixteen_observation_pair_distance_contract(recovered); + assert_sixteen_observation_pair_distance_contract(recovered.map(|value| -value)); + } +} From 5da82b2d651706c191ca191c6c077d916cbfda25 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:15:24 +0900 Subject: [PATCH 459/576] test(validation): correct sixteen-observation permutation fixture --- ...andard_error_sixteen_observation_pair_distance_contract.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_sixteen_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_sixteen_observation_pair_distance_contract.rs index 80c7ae535..da7579651 100644 --- a/crates/validation_core/tests/bias_standard_error_sixteen_observation_pair_distance_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_sixteen_observation_pair_distance_contract.rs @@ -54,13 +54,13 @@ fn exact_sixteen_observation_dispersion_uses_pair_distance_midpoint_proof() { 314_270_929.0, 1_992_574_092.0, 398_522_837.0, - 1_320_628_283.0, + 1_141_566_755.0, 327_661_307.0, 731_058_943.0, 413_483_290.0, 1_526_331_271.0, 371_854_441.0, - 1_141_566_755.0, + 970_233_120.0, 416_184_956.0, 1_320_628_283.0, 565_808_551.0, From a509ae9e46c8ffc2cc3ef4f0e904774ad2516e1f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:17:32 +0900 Subject: [PATCH 460/576] fix(validation): admit exact sixteen-observation pair distances --- crates/validation_core/src/bias_se.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 0b01b0661..19baf5769 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -173,10 +173,10 @@ fn exact_pair_distance_standard_error( recovered: &[f64], ) -> Option> { // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have - // cheaper exact identities in `bias.rs`; four through fifteen observations are + // cheaper exact identities in `bias.rs`; four through sixteen observations are // the smallest remaining sample sizes with demonstrated one-ULP errors in // the translated floating moment/sqrt path. - if truth.len() != recovered.len() || !(4..=15).contains(&truth.len()) { + if truth.len() != recovered.len() || !(4..=16).contains(&truth.len()) { return None; } let sample_count = truth.len(); @@ -259,7 +259,7 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- through fifteen-observation samples whose represented residuals and +/// Four- through sixteen-observation samples whose represented residuals and /// pairwise differences are exact use the exact pair-distance identity when its /// reduced dyadic ratio fits the bounded integer proof. All other samples retain /// the established bias implementation and its existing fail-closed behavior. @@ -471,6 +471,10 @@ mod tests { ); assert_eq!( exact_pair_distance_standard_error(&[0.0; 16], &[0.0; 16]), + Some(Ok(0.0)) + ); + assert_eq!( + exact_pair_distance_standard_error(&[0.0; 17], &[0.0; 17]), None ); assert_eq!( From 3d2297c638850d03b7574763ffec05965508c73a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:18:08 +0900 Subject: [PATCH 461/576] docs(validation): trace sixteen-observation exact bias SE --- ...error-sixteen-observation-pair-distance.md | 2 + ...error-sixteen-observation-pair-distance.md | 54 +++++++++++++++++++ 2 files changed, 56 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-sixteen-observation-pair-distance.md create mode 100644 docs/research/validation-bias-standard-error-sixteen-observation-pair-distance.md diff --git a/CHANGELOG.d/validation-bias-standard-error-sixteen-observation-pair-distance.md b/CHANGELOG.d/validation-bias-standard-error-sixteen-observation-pair-distance.md new file mode 100644 index 000000000..364ef021d --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-sixteen-observation-pair-distance.md @@ -0,0 +1,2 @@ +- Validation Evidence now admits the exact pair-distance identity for bounded sixteen-observation mean-bias standard errors when represented residuals and pairwise differences are proven exact and the reduced dyadic ratio fits the existing checked `u128` midpoint proof. +- This prevents the translated floating moment/`sqrt` fallback from moving a demonstrated exact represented-input sixteen-observation standard error one ULP low, while retaining the established fallback outside the bounded proof. diff --git a/docs/research/validation-bias-standard-error-sixteen-observation-pair-distance.md b/docs/research/validation-bias-standard-error-sixteen-observation-pair-distance.md new file mode 100644 index 000000000..c59659f55 --- /dev/null +++ b/docs/research/validation-bias-standard-error-sixteen-observation-pair-distance.md @@ -0,0 +1,54 @@ +# Exact represented-input mean-bias standard error for sixteen observations + +## Problem and represented-input evidence + +TEPP's Validation Evidence layer treats the represented binary64 inputs as the numerical observation contract. The exact sixteen-observation residual sample + +`[314_270_929, 327_661_307, 371_854_441, 398_522_837, 413_483_290, 416_184_956, 565_808_551, 682_627_163, 724_514_517, 731_058_943, 740_662_035, 970_233_120, 1_141_566_755, 1_320_628_283, 1_526_331_271, 1_992_574_092]` + +has 120 pairwise distances and an exact squared-distance sum `N = 56_762_922_330_032_131_548`. With `n = 16`, + +`SE(mean)^2 = N / [16^2(16-1)] = N / 3_840`. + +`gcd(N,3_840)=12`, so the reduced exact radicand is `4_730_243_527_502_677_629 / 320`. The predecessor bounded pair-distance proof stopped at fifteen observations. Its translated floating second-moment/`sqrt` fallback returns `0x419c_fcbb_b78d_2ad4`; exact dyadic midpoint-square comparison returns correctly rounded `0x419c_fcbb_b78d_2ad5`. + +The public RED lineage is `65d3965fd9ce8e4667f235fe33ce0f8b38ec5f6c` followed by fixture correction `5da82b2d651706c191ca191c6c077d916cbfda25`; the latter is the authoritative RED fixture. `crates/validation_core/tests/bias_standard_error_sixteen_observation_pair_distance_contract.rs` exercises original order, reverse order, an independent permutation, and sign mirrors without changing the represented multiset. + +## Causal repair + +Repair `a509ae9e46c8ffc2cc3ef4f0e904774ad2516e1f` changes only `crates/validation_core/src/bias_se.rs` admission from `n=4..=15` to `n=4..=16`, updates its rustdoc, admits exact-zero `n=16`, and moves the explicit fallback boundary to `n=17`. + +The proof conditions are unchanged: residual subtraction and every pairwise residual difference must be error-free; dyadic coefficient construction and pair-square accumulation must fit checked `u128`; the scientific denominator remains `n^2(n-1)` and is GCD-reduced; binary64 ratio/`sqrt` is only a candidate seed; exact candidate-square and adjacent-midpoint comparisons authorize the result. Any proof failure remains on the established general path. + +This is not a payload-specific branch and does not claim globally correctly rounded standard errors for arbitrary `n`. However, demonstrated one-ULP counterexamples now span every bounded sample size from `n=4` through `n=16`. Treating the next integer sample count as the scientific boundary is therefore no longer a satisfactory long-term design. The next owner work is to replace the staircase cutoff with an evidence-based proof budget: characterize O(n^2) cost, checked-`u128` overflow/refusal behavior, realistic Validation Evidence sample sizes, and whether a mathematically equivalent O(n) or wider-integer exact accumulator can preserve the same midpoint authority. Until that evidence exists, `n=17` remains fail-closed to the established translated path rather than being admitted speculatively. + +## Alternatives rejected + +A fixture-specific `n=16` formula is rejected because the defect is a repeated exact-rational double-rounding class. Removing the bound outright is rejected because it would silently impose quadratic work on arbitrary buyer inputs without a measured latency/resource envelope. Weakening subtraction exactness, checked integer construction, or midpoint proof would turn the reference path into an approximation and is rejected. Arbitrary-precision production arithmetic is not introduced without a DDD/performance/release decision because the current demonstrated case fits checked `u128` after GCD reduction. + +## Traceability + +| Item | Exact evidence | +|---|---| +| Domain owner | Validation Evidence | +| Authoritative public RED | `5da82b2d651706c191ca191c6c077d916cbfda25` | +| Causal repair | `a509ae9e46c8ffc2cc3ef4f0e904774ad2516e1f` | +| Production module/API | `crates/validation_core/src/bias_se.rs` / `validation_core::bias_standard_error` | +| Public test | `crates/validation_core/tests/bias_standard_error_sixteen_observation_pair_distance_contract.rs` | +| Predecessor doctoring | `docs/research/validation-bias-standard-error-fifteen-observation-pair-distance.md` | + +## Methodological and standards basis + +IEEE 754-2019 and ISO/IEC 60559:2020 remain the published floating-point arithmetic authorities for binary64 behavior. Exact midpoint comparison is used so a rounded ratio followed by `sqrt` cannot silently become authoritative when represented inputs admit an exact bounded rational proof. + +Morris, White, and Crowther (2019) distinguish deterministic calculation of a performance measure from Monte Carlo uncertainty due to finite simulation replications. This defect is deterministic for a fixed represented input and belongs to the former layer. AERA, APA, and NCME's *Standards for Educational and Psychological Testing* provide the broader validity-evidence basis; an LLM judgment does not replace the numerical estimator contract or scientific acceptance evidence. + +## 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. + +Institute of Electrical and Electronics Engineers. (2019). *IEEE standard for floating-point arithmetic* (IEEE Std 754-2019). https://doi.org/10.1109/IEEESTD.2019.8766229 + +International Organization for Standardization, & International Electrotechnical Commission. (2020). *ISO/IEC 60559:2020 Information technology—Microprocessor systems—Floating-point arithmetic*. + +Morris, T. P., White, I. R., & Crowther, M. J. (2019). Using simulation studies to evaluate statistical methods. *Statistics in Medicine, 38*(11), 2074–2102. https://doi.org/10.1002/sim.8086 From 757fb0e4770b24f73f301b336adfcf7f656dcea5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:33:21 +0900 Subject: [PATCH 462/576] test(validation): characterize bias SE exact-proof budget --- ...-bias-standard-error-exact-proof-budget.md | 2 + .../examples/bias_se_exact_proof_budget.rs | 96 ++++++++++++++++ ...ror_exact_proof_budget_characterization.rs | 103 ++++++++++++++++++ ...-bias-standard-error-exact-proof-budget.md | 72 ++++++++++++ 4 files changed, 273 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-exact-proof-budget.md create mode 100644 crates/validation_core/examples/bias_se_exact_proof_budget.rs create mode 100644 crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs create mode 100644 docs/research/validation-bias-standard-error-exact-proof-budget.md diff --git a/CHANGELOG.d/validation-bias-standard-error-exact-proof-budget.md b/CHANGELOG.d/validation-bias-standard-error-exact-proof-budget.md new file mode 100644 index 000000000..718fbd39c --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-exact-proof-budget.md @@ -0,0 +1,2 @@ +- Add exact-proof budget characterization for mean-bias standard error: a seventeen-observation represented-input fixture, O(n²) versus algebraically equivalent O(n) checked-`u128` numerator evidence, and the compact-dyadic `n<=2_047` worst-case arithmetic envelope. +- Add a reproducible standard-library timing harness for the quadratic and linear integer kernels while keeping production exact pair-distance admission bounded at `n<=16` until release-mode CPU/allocation and applicable buyer-path p95 evidence establish a resource budget. \ No newline at end of file diff --git a/crates/validation_core/examples/bias_se_exact_proof_budget.rs b/crates/validation_core/examples/bias_se_exact_proof_budget.rs new file mode 100644 index 000000000..06f48c55e --- /dev/null +++ b/crates/validation_core/examples/bias_se_exact_proof_budget.rs @@ -0,0 +1,96 @@ +//! Reproducible integer-kernel timing harness for bias-SE exact-proof budgeting. +//! +//! This example compares the current pair-distance O(n²) integer identity with +//! an algebraically equivalent O(n) accumulator on deterministic compact dyadic +//! coefficients. It is characterization tooling, not production admission and +//! not buyer-path latency evidence by itself. + +use std::hint::black_box; +use std::time::{Duration, Instant}; + +fn fixture(sample_count: usize) -> Vec { + (0..sample_count) + .map(|index| { + let value = u128::try_from(index).expect("fixture index fits u128"); + (value * 1_000_003 + value * value * 97 + 17) % 4_000_000_001 + }) + .collect() +} + +fn pair_square_sum_quadratic(values: &[u128]) -> Option { + let mut sum = 0_u128; + for left in 0..values.len() { + for right in left + 1..values.len() { + let difference = values[left].abs_diff(values[right]); + sum = sum.checked_add(difference.checked_mul(difference)?)?; + } + } + Some(sum) +} + +fn pair_square_sum_linear(values: &[u128]) -> Option { + let minimum = *values.iter().min()?; + let sample_count = u128::try_from(values.len()).ok()?; + let mut coefficient_sum = 0_u128; + let mut square_sum = 0_u128; + for value in values { + let coefficient = value.checked_sub(minimum)?; + coefficient_sum = coefficient_sum.checked_add(coefficient)?; + square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; + } + sample_count + .checked_mul(square_sum)? + .checked_sub(coefficient_sum.checked_mul(coefficient_sum)?) +} + +fn percentile_95(mut durations: Vec) -> Duration { + durations.sort_unstable(); + let rank = durations.len().saturating_mul(95).div_ceil(100); + durations[rank.saturating_sub(1)] +} + +fn measure( + values: &[u128], + samples: usize, + kernel: fn(&[u128]) -> Option, +) -> Duration { + for _ in 0..3 { + black_box(kernel(black_box(values)).expect("fixture must remain within u128")); + } + let mut durations = Vec::with_capacity(samples); + for _ in 0..samples { + let started = Instant::now(); + black_box(kernel(black_box(values)).expect("fixture must remain within u128")); + durations.push(started.elapsed()); + } + percentile_95(durations) +} + +fn main() { + let samples = std::env::args() + .nth(1) + .and_then(|value| value.parse::().ok()) + .unwrap_or(25) + .max(1); + + println!("sample_count,kernel,p95_ns,timing_samples"); + for sample_count in [16_usize, 64, 256, 1_024, 2_047] { + let values = fixture(sample_count); + let quadratic = pair_square_sum_quadratic(&values).expect("quadratic result"); + let linear = pair_square_sum_linear(&values).expect("linear result"); + assert_eq!(quadratic, linear, "algebraic kernels must agree"); + + let quadratic_p95 = measure(&values, samples, pair_square_sum_quadratic); + let linear_p95 = measure(&values, samples, pair_square_sum_linear); + println!( + "{sample_count},quadratic,{},{}", + quadratic_p95.as_nanos(), + samples + ); + println!( + "{sample_count},linear,{},{}", + linear_p95.as_nanos(), + samples + ); + } +} diff --git a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs new file mode 100644 index 000000000..e0cdcb9cf --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs @@ -0,0 +1,103 @@ +use validation_core::bias_standard_error; + +const SEVENTEEN_OBSERVATION_FIXTURE: [u128; 17] = [ + 38_557_579, + 48_779_805, + 63_558_649, + 106_352_599, + 139_863_777, + 142_786_819, + 267_163_239, + 275_103_292, + 375_678_558, + 454_709_869, + 484_300_224, + 623_646_610, + 989_643_121, + 1_027_595_814, + 1_520_220_488, + 1_569_903_156, + 1_805_452_085, +]; + +fn pair_square_sum_quadratic(values: &[u128]) -> Option { + let mut sum = 0_u128; + for left in 0..values.len() { + for right in left + 1..values.len() { + let difference = values[left].abs_diff(values[right]); + sum = sum.checked_add(difference.checked_mul(difference)?)?; + } + } + Some(sum) +} + +fn pair_square_sum_linear(values: &[u128]) -> Option { + let minimum = *values.iter().min()?; + let sample_count = u128::try_from(values.len()).ok()?; + let mut coefficient_sum = 0_u128; + let mut square_sum = 0_u128; + for value in values { + let coefficient = value.checked_sub(minimum)?; + coefficient_sum = coefficient_sum.checked_add(coefficient)?; + square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; + } + sample_count + .checked_mul(square_sum)? + .checked_sub(coefficient_sum.checked_mul(coefficient_sum)?) +} + +fn greatest_common_divisor(mut left: u128, mut right: u128) -> u128 { + while right != 0 { + let remainder = left % right; + left = right; + right = remainder; + } + left +} + +fn worst_case_linear_intermediate(sample_count: u128, diameter: u128) -> Option { + sample_count + .checked_mul(sample_count)? + .checked_mul(diameter.checked_mul(diameter)?) +} + +#[test] +fn seventeen_observation_fixture_proves_linear_identity_matches_pair_reference() { + const EXACT_PAIR_SQUARE_SUM: u128 = 92_549_865_125_191_410_206; + const SCIENTIFIC_DENOMINATOR: u128 = 4_624; + const REDUCED_NUMERATOR: u128 = 46_274_932_562_595_705_103; + const REDUCED_DENOMINATOR: u128 = 2_312; + + let pairwise = pair_square_sum_quadratic(&SEVENTEEN_OBSERVATION_FIXTURE) + .expect("quadratic exact pair sum stays within u128"); + let linear = pair_square_sum_linear(&SEVENTEEN_OBSERVATION_FIXTURE) + .expect("linear exact identity stays within u128"); + assert_eq!(pairwise, EXACT_PAIR_SQUARE_SUM); + assert_eq!(linear, pairwise); + + let divisor = greatest_common_divisor(pairwise, SCIENTIFIC_DENOMINATOR); + assert_eq!(divisor, 2); + assert_eq!(pairwise / divisor, REDUCED_NUMERATOR); + assert_eq!(SCIENTIFIC_DENOMINATOR / divisor, REDUCED_DENOMINATOR); + + let truth = [0.0; 17]; + let recovered = SEVENTEEN_OBSERVATION_FIXTURE.map(|value| { + f64::from(u32::try_from(value).expect("fixture value fits u32 exactly")) + }); + assert_eq!( + bias_standard_error(&truth, &recovered) + .expect("current bounded fallback remains representable") + .to_bits(), + 0x41a0_dd77_9ac3_8e98 + ); +} + +#[test] +fn compact_dyadic_worst_case_u128_ceiling_is_2047_samples() { + let exact_integer_diameter = 1_u128 << 53; + assert!(worst_case_linear_intermediate(2_047, exact_integer_diameter).is_some()); + assert!(worst_case_linear_intermediate(2_048, exact_integer_diameter).is_none()); + + let maximum_safe_denominator = 2_047_u128 * 2_047 * 2_046; + assert!(maximum_safe_denominator < (1_u128 << 53)); +} diff --git a/docs/research/validation-bias-standard-error-exact-proof-budget.md b/docs/research/validation-bias-standard-error-exact-proof-budget.md new file mode 100644 index 000000000..589e27e53 --- /dev/null +++ b/docs/research/validation-bias-standard-error-exact-proof-budget.md @@ -0,0 +1,72 @@ +# Bias-SE exact-proof budget characterization + +## Scope + +Issue #491 replaces the sample-count staircase in GAP-111 through GAP-125 with an evidence-based resource budget. This note records the first bounded characterization step. It does **not** widen production admission beyond `n=16`, does not claim buyer-path p95, and does not make a benchmark result from an unmeasured environment authoritative. + +## New represented-input boundary evidence + +The seventeen-observation residual multiset + +`[38_557_579, 48_779_805, 63_558_649, 106_352_599, 139_863_777, 142_786_819, 267_163_239, 275_103_292, 375_678_558, 454_709_869, 484_300_224, 623_646_610, 989_643_121, 1_027_595_814, 1_520_220_488, 1_569_903_156, 1_805_452_085]` + +is exactly representable in binary64. Its 136 squared pair distances sum to + +`N = 92_549_865_125_191_410_206`. + +For `n=17`, the scientific denominator is `17^2(17-1)=4_624`. `gcd(N,4_624)=2`, giving the reduced exact radicand + +`46_274_932_562_595_705_103 / 2_312`. + +The current public API intentionally remains on the established translated floating fallback at `n=17`; for this fixture it returns bits `0x41a0_dd77_9ac3_8e98`. Independent high-precision rational-square-root evaluation gives the adjacent correctly rounded binary64 target `0x41a0_dd77_9ac3_8e99`. This extends the demonstrated failure class beyond the current cutoff, but it is evidence for a systemic budget decision rather than justification for another one-count production patch. + +`crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` fixes the exact pair-square sum, reduced ratio, current fallback boundary, and the algebraic equivalence below. The test intentionally makes any future widening of production admission update this characterization rather than silently inheriting a stale fallback assumption. + +## O(n²) reference versus O(n) exact accumulator + +For exact dyadic coefficients `c_i` on one shared unit, + +`sum_{i` before the second accumulation pass. The number of stored pair records is exactly `n(n-1)/2`: 120 at `n=16`, 2,096,128 at `n=2_048`, and 4,997,500 at `n=3_162`. Exact byte cost depends on the compiled Rust layout and must be measured rather than inferred from field widths. + +A two-pass O(n²) reference can remove that pair-record allocation without changing proof semantics: the first pass establishes exact pairwise subtraction and the minimum unit exponent; the second recomputes each exact pair difference and accumulates its checked integer square. The proposed O(n) sufficient-admission path would reduce both pair enumeration and pair storage, but neither optimization is activated on production input until exact-head Rust verification and resource measurements support it. + +## Measurement harness + +`crates/validation_core/examples/bias_se_exact_proof_budget.rs` is a standard-library-only harness for reproducible relative timing of the quadratic and linear checked-integer kernels on deterministic compact-dyadic coefficients. It emits CSV `sample_count,kernel,p95_ns,timing_samples` rows for 16, 64, 256, 1,024, and 2,047 observations and asserts exact equality between kernels before timing. + +This harness deliberately excludes binary64 residual admission, public API composition, networking, and process scheduling. Its output therefore characterizes the integer kernel only. Release-mode results must record CPU/OS/toolchain/commit and cannot substitute for an applicable API buyer-path p95 measurement. + +## Decision and rejected alternatives + +Production admission stays `n=4..=16` in this commit. Extending to `n=17` alone is rejected because GAP-111 through GAP-125 plus the new seventeen-observation evidence show that the integer cutoff is not a scientific boundary. Removing the cutoff entirely is rejected because the existing implementation is O(n²) in both pair work and pair-record storage. Treating the `n<=2_047` checked-integer envelope as the production budget is rejected because arithmetic representability is not latency evidence. Arbitrary-precision production arithmetic is also deferred: it may be useful as an independent reference alternative, but adding it to the production dependency surface requires measured benefit, supply-chain review, and an explicit owner/resource decision. + +## Traceability + +| Item | Evidence | +|---|---| +| Domain owner | TEPP Validation Evidence | +| Systemic issue | #491 | +| Predecessor scientific repair | GAP-125; RED `5da82b2d651706c191ca191c6c077d916cbfda25`; repair `a509ae9e46c8ffc2cc3ef4f0e904774ad2516e1f` | +| Current production module | `crates/validation_core/src/bias_se.rs` | +| Public API | `validation_core::bias_standard_error` | +| Exact characterization | `crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` | +| CPU kernel harness | `crates/validation_core/examples/bias_se_exact_proof_budget.rs` | +| Resource/merge rule | Production cutoff remains `n<=16` pending measured exact-proof budget | + +## Follow-up evidence required by #491 + +Run the new harness in release mode on a recorded CPU/toolchain and repeat enough independent samples to retain raw timing evidence. Measure allocation/layout rather than estimating bytes. Prototype the stronger O(n) dyadic-grid admission behind tests and compare its admitted/refused set against the existing pairwise proof. Evaluate a wider-integer/reference alternative without making it production authority. Only after those results establish a resource budget should production admission change; any such change needs a realistic public RED, exact-head Rust/rustdoc/coverage evidence, and applicable buyer-path p95 evidence. From d15282fc28038cf05b54f691d3bf72f568482984 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:40:37 +0900 Subject: [PATCH 463/576] docs(validation): define exact-proof resource evidence --- docs/OPERABILITY.md | 35 +++++++++++++++++++++-------------- docs/TEST_STRATEGY.md | 23 +++++++++++++++++++---- 2 files changed, 40 insertions(+), 18 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index 3a8291634..202a774bf 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -1,7 +1,7 @@ # TEPP Operability, Recovery, and Release Guide **Status:** Accepted target operating baseline with current maturity explicit. -**Last reviewed:** 2026-08-13 +**Last reviewed:** 2026-09-06 TEPP is still an implementation-stage research/product platform. Protected main currently contains the Rust workspace/evidence foundation plus implemented-main temporal primitives (merged PRs #8 and #9). Superseded PRs #5 and #6 are historical lineage only. Database adapters are partial; model fitting, GPU, services, visual analytics, and production deployment are later targets. This guide defines the operating evidence those stages must satisfy rather than claiming they already exist. Unmerged or draft PRs are not implemented-main claims. @@ -48,7 +48,7 @@ Before admitting a GPU job, estimate budget and reserve margin. On OOM: classify ## LLM degradation -LLM-backed semantic/interpreter functions use strict bounded requests and cached/versioned results where appropriate. Provider failure may retry only under bounded policy, route through contextual-orchestrator when configured, or return deferred/unresolved evidence. It must not corrupt deterministic/statistical results or expose credentials/source beyond approved policy. +LLM-backed semantic/interpreter functions consume only immutable released compatible contextual-orchestrator contracts. Provider failure may retry or route only through that owner contract and its policy, or return deferred/unresolved evidence. It must not corrupt deterministic/statistical results, silently substitute a mutable owner head, or expose credentials/source beyond approved policy. Model-backed Actions use `orchestrator/free` through the approved gateway route; provider/model hard-coding and LLM numerical authority remain prohibited. ## Database target recovery @@ -56,6 +56,23 @@ Migration `0007` (active PR) contracts policy-driven retention, legal-hold block Before PostgreSQL becomes production state, prove migrations and rollback, tenant isolation/RLS, temporal/lineage constraints, idempotency/concurrency, backup/restore, retention/deletion, and reconstruction from immutable artifacts. Concurrent document first-insert and revise stress is implemented-main. `persistence_postgres::mark_restored_state_usable` and `assert_restore_integrity` are the current fail-closed restore gate (active PR): they revalidate tenant identity, canonical digests, same-tenant knowledge-cutoff eligibility, temporal window order, and enabled append-only triggers. They do not yet revalidate relation-aware splits or full lineage graphs; those remain separate post-restore scientific steps. The gate does not replace operator `pg_dump`/`pg_restore` runbooks. +## Numerical proof resource budgets + +A numerical proof boundary is an operational resource contract when it changes asymptotic work, allocation, or buyer-path latency. It is not determined by the next sample count that happens to expose a rounding defect. + +Issue #491 owns the current bias-standard-error exact-proof budget. Production exact pair-distance admission stays bounded to `n<=16` while the current implementation is O(n²) in pair enumeration and stores `n(n-1)/2` pair records. The characterization on PR #488 records a seventeen-observation counterexample, an algebraically equivalent O(n) checked-integer numerator, and the compact-dyadic `u128` arithmetic envelope, but none of those facts alone authorize a wider production budget. + +Before changing that production boundary, retain: + +- release-mode raw timing samples and p95 from `crates/validation_core/examples/bias_se_exact_proof_budget.rs`, with exact commit, CPU, OS, Rust toolchain, build flags, timing sample count, and cold/warm procedure; +- measured compiled allocation/layout evidence rather than byte estimates inferred from field widths; +- admitted/refused-set comparison between the existing pairwise proof and any stronger sufficient O(n) dyadic-grid proof, with proof refusal falling back rather than altering scientific meaning; +- checked-`u128` overflow/refusal evidence across sample count and represented exponent spread; +- a wider-integer/reference alternative assessment kept separate from production authority unless its dependency/security/performance cost is explicitly accepted; +- full service/API p95 when a buyer-facing path is affected, preserving the TEPP `p95 <= 20 ms` target without shrinking input, omitting proof work, or using an unrealistic cache-only setup. + +A two-pass O(n²) implementation may remove pair-record storage while preserving pairwise exactness semantics, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its worst-case integer arithmetic fits `u128`. + ## Model release/cutover A model artifact is promoted only after convergence, posterior diagnostics, true-parameter/recovery benchmarks, invariance/fairness/language evidence, uncertainty/calibration, security/privacy, and reproducibility gates meet the versioned policy. Model-selection or LLM review disagreement can require human scientific review. @@ -66,18 +83,8 @@ Trace the first failing boundary: evidence, temporal typing/reasoning, event/rel ## Actions workflow fleet -GitHub Actions registry identities survive YAML deletion. After any -bootstrap, diagnosis, or repair workflow is removed from the tree, run -`scripts/actions_workflow_fleet.py audit` and retain the JSON inventory -(workflow ID, path, state, classification, default-branch SHA, timestamp, -pagination receipts). Disable only re-fetched active orphans with -`disable-orphans --apply`. Never disable the protected CI, documentation, -hourly NIM, or hourly PR-maintenance paths, and never recreate deleted -bootstrap/repair YAML. The auditor uses only `GITHUB_TOKEN`/`GH_TOKEN`. -Product-development automation continues to use `NVIDIA_NIM_API_KEY` and -must not receive `COPILOT_GITHUB_TOKEN`. Operator procedure: -`docs/operations/ACTIONS_WORKFLOW_FLEET.md`. +GitHub Actions registry identities survive YAML deletion. After any bootstrap, diagnosis, or repair workflow is removed from the tree, run `scripts/actions_workflow_fleet.py audit` and retain the JSON inventory (workflow ID, path, state, classification, default-branch SHA, timestamp, pagination receipts). Disable only re-fetched active orphans with `disable-orphans --apply`. Never disable the protected CI, documentation, hourly NIM, or hourly PR-maintenance paths, and never recreate deleted bootstrap/repair YAML. The auditor uses only `GITHUB_TOKEN`/`GH_TOKEN`. Product-development automation continues to use the owner-approved model route and must not receive unrelated provider credentials. Operator procedure: `docs/operations/ACTIONS_WORKFLOW_FLEET.md`. ## Release gate -A software release requires exact protected-head CI/security/review, 100% production coverage/docs, validated migrations/rollback where present, scientific benchmark artifacts, SBOM/provenance, reproducible packages/images, operator runbooks, accessibility for product UI, CHANGELOG/version/tag consistency, and post-publish verification. TEPP has not reached that integrated release state merely because individual foundation PRs merge. \ No newline at end of file +A software release requires exact protected-head CI/security/review, 100% production coverage/docs, validated migrations/rollback where present, scientific benchmark artifacts, SBOM/provenance, reproducible packages/images, operator runbooks, accessibility for product UI, CHANGELOG/version/tag consistency, and post-publish verification. TEPP has not reached that integrated release state merely because individual foundation PRs merge. Unexecuted timing harnesses and branch-only resource characterization are not release evidence. \ No newline at end of file diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index 3180012d1..637f653df 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -1,7 +1,7 @@ # TEPP Test and Scientific Validation Strategy **Status:** Accepted quality baseline aligned to PRD v0.4 -**Last reviewed:** 2026-08-12 +**Last reviewed:** 2026-09-06 ## Mandatory repository gates @@ -57,16 +57,31 @@ CPU `f64` is reference. Required accelerator lanes execute real kernels; skipped ## LLM tests -Deterministic schema/security tests are primary. Bounded live tests use `NVIDIA_NIM_API_KEY` only when model conformance is material. Treat documents as prompt-injection data, require evidence-span grounding, test unsupported-claim rejection, record provider/model/prompt/reasoning hashes, and compare model/human agreement where the LLM acts as a rater. +Deterministic schema/security tests are primary. Bounded live tests use released contextual-orchestrator contracts when model conformance is material. Treat documents as prompt-injection data, require evidence-span grounding, test unsupported-claim rejection, record provider/model/prompt/reasoning hashes, and compare model/human agreement where the LLM acts as a rater. Model-backed Actions must use the approved `orchestrator/free` route and must not make an LLM authoritative for numerical or scientific acceptance. ## Monte Carlo acceptance Simulation thresholds account for Monte Carlo standard error and interval uncertainty. Do not require an observed replication proportion to exceed the nominal target exactly when sampling variability makes that scientifically invalid. +## Exact-proof resource budgeting + +Validation Evidence numerical proofs that add asymptotic work or material allocation require a measured resource contract before a production admission boundary is widened. For the bias-standard-error exact pair-distance path tracked by issue #491: + +- retain realistic represented-input counterexamples and permutation/sign-mirror contracts for scientific correctness; +- keep the existing exact residual/pairwise-subtraction, checked-integer, GCD-reduction, and exact midpoint authority fail-closed; +- compare the current O(n²) pair-distance proof with an algebraically equivalent O(n) exact accumulator only under a proved sufficient admission condition, and compare a wider-integer/reference alternative separately; +- characterize checked-`u128` refusal as a function of sample count and aligned dyadic diameter/exponent spread rather than treating an integer cutoff as a scientific boundary; +- record allocation count and compiled layout measurements separately; field-width estimates are not allocation evidence; +- run `crates/validation_core/examples/bias_se_exact_proof_budget.rs` in release mode on a recorded CPU/OS/Rust toolchain and retain raw samples plus p95; the harness is integer-kernel characterization, not an HTTP buyer-path result; +- if a service/API buyer path is affected, measure the full applicable path and retain the `p95 <= 20 ms` target without shrinking samples, omitting proof work, or relying on unrealistic warm-cache setup; +- arithmetic representability alone does not authorize a production sample-count budget. + +Until those measurements and exact-head gates exist, the `n<=16` production bias-SE exact pair-distance admission remains unchanged even when a larger represented-input counterexample is known. + ## Release acceptance -A release requires one integrated protected head with all relevant scientific, numerical, security, migration, packaging, SBOM/provenance, accessibility, operational, and independent-review evidence passing. Planning validation, superseded-branch results, and local-only results are supporting evidence, not release proof. +A release requires one integrated protected head with all relevant scientific, numerical, security, migration, packaging, SBOM/provenance, accessibility, operational, and independent-review evidence passing. Planning validation, superseded-branch results, local-only results, and unexecuted benchmark tooling are supporting evidence, not release proof. ## References -The full APA 7th register is [`docs/research/standards-and-literature.md`](research/standards-and-literature.md). Method names used above cite Allen (1983) for interval algebra and Asparouhov & Muthén (2009), Asparouhov et al. (2018), and Marsh et al. (2014) for ESEM/DSEM. +The full APA 7th register is [`docs/research/standards-and-literature.md`](research/standards-and-literature.md). Method names used above cite Allen (1983) for interval algebra and Asparouhov & Muthén (2009), Asparouhov et al. (2018), and Marsh et al. (2014) for ESEM/DSEM. \ No newline at end of file From e476d66c99bef7d3e35b6a77374daeaa5fc59959 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:57:31 +0900 Subject: [PATCH 464/576] test(validation): characterize exact-proof resource envelopes --- ...ror_exact_proof_budget_characterization.rs | 50 ++++++++++++++++++- 1 file changed, 48 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs index e0cdcb9cf..9e62a009f 100644 --- a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs +++ b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs @@ -55,12 +55,30 @@ fn greatest_common_divisor(mut left: u128, mut right: u128) -> u128 { left } +fn pair_record_count(sample_count: u128) -> Option { + sample_count + .checked_mul(sample_count.checked_sub(1)?)? + .checked_div(2) +} + fn worst_case_linear_intermediate(sample_count: u128, diameter: u128) -> Option { sample_count .checked_mul(sample_count)? .checked_mul(diameter.checked_mul(diameter)?) } +fn worst_case_pair_square_numerator(sample_count: u128, diameter: u128) -> Option { + let split_product = (sample_count / 2) + .checked_mul(sample_count.checked_sub(sample_count / 2)?)?; + split_product.checked_mul(diameter.checked_mul(diameter)?) +} + +fn scientific_denominator(sample_count: u128) -> Option { + sample_count + .checked_mul(sample_count)? + .checked_mul(sample_count.checked_sub(1)?) +} + #[test] fn seventeen_observation_fixture_proves_linear_identity_matches_pair_reference() { const EXACT_PAIR_SQUARE_SUM: u128 = 92_549_865_125_191_410_206; @@ -93,11 +111,39 @@ fn seventeen_observation_fixture_proves_linear_identity_matches_pair_reference() } #[test] -fn compact_dyadic_worst_case_u128_ceiling_is_2047_samples() { +fn pair_record_counts_are_exact_resource_evidence() { + assert_eq!(pair_record_count(16), Some(120)); + assert_eq!(pair_record_count(17), Some(136)); + assert_eq!(pair_record_count(2_048), Some(2_096_128)); + assert_eq!(pair_record_count(3_162), Some(4_997_541)); +} + +#[test] +fn compact_dyadic_linear_intermediate_ceiling_is_2047_samples() { let exact_integer_diameter = 1_u128 << 53; assert!(worst_case_linear_intermediate(2_047, exact_integer_diameter).is_some()); assert!(worst_case_linear_intermediate(2_048, exact_integer_diameter).is_none()); - let maximum_safe_denominator = 2_047_u128 * 2_047 * 2_046; + let maximum_safe_denominator = scientific_denominator(2_047).expect("bounded denominator"); assert!(maximum_safe_denominator < (1_u128 << 53)); } + +#[test] +fn exact_pair_square_numerator_has_a_wider_u128_envelope_than_linear_intermediates() { + let exact_integer_diameter = 1_u128 << 53; + assert!(worst_case_pair_square_numerator(4_095, exact_integer_diameter).is_some()); + assert!(worst_case_pair_square_numerator(4_096, exact_integer_diameter).is_none()); +} + +#[test] +fn unreduced_scientific_denominator_crosses_binary64_integer_bound_after_208064() { + let maximum_exact_binary64_integer = 1_u128 << 53; + assert!( + scientific_denominator(208_064).expect("denominator fits u128") + <= maximum_exact_binary64_integer + ); + assert!( + scientific_denominator(208_065).expect("denominator fits u128") + > maximum_exact_binary64_integer + ); +} From 5d030688d0ed44d5fa4a698a2097173430b43bd1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:58:33 +0900 Subject: [PATCH 465/576] chore(validation): measure exact-proof layout alternatives --- .../examples/bias_se_exact_proof_budget.rs | 210 +++++++++++++++--- 1 file changed, 180 insertions(+), 30 deletions(-) diff --git a/crates/validation_core/examples/bias_se_exact_proof_budget.rs b/crates/validation_core/examples/bias_se_exact_proof_budget.rs index 06f48c55e..3d29d4cbf 100644 --- a/crates/validation_core/examples/bias_se_exact_proof_budget.rs +++ b/crates/validation_core/examples/bias_se_exact_proof_budget.rs @@ -1,13 +1,23 @@ -//! Reproducible integer-kernel timing harness for bias-SE exact-proof budgeting. +//! Reproducible integer-kernel timing and layout harness for bias-SE exact-proof budgeting. //! -//! This example compares the current pair-distance O(n²) integer identity with -//! an algebraically equivalent O(n) accumulator on deterministic compact dyadic -//! coefficients. It is characterization tooling, not production admission and -//! not buyer-path latency evidence by itself. +//! This example compares three checked-integer kernels on deterministic compact +//! dyadic coefficients: a production-layout-shaped buffered O(n²) pair proof, an +//! allocation-free two-pass O(n²) variant, and an algebraically equivalent O(n) +//! accumulator. It is characterization tooling, not production admission and not +//! buyer-path latency evidence by itself. use std::hint::black_box; +use std::mem::size_of; use std::time::{Duration, Instant}; +#[derive(Clone, Copy)] +struct KernelObservation { + aligned_pair_square_sum: u128, + unit_exponent: i32, + scratch_records: usize, + scratch_payload_bytes: usize, +} + fn fixture(sample_count: usize) -> Vec { (0..sample_count) .map(|index| { @@ -17,18 +27,99 @@ fn fixture(sample_count: usize) -> Vec { .collect() } -fn pair_square_sum_quadratic(values: &[u128]) -> Option { +fn multiply_by_power_of_two(value: u128, shift: u32) -> Option { + let factor = 1_u128.checked_shl(shift)?; + value.checked_mul(factor) +} + +fn compact_dyadic(value: u128) -> Option<(u128, i32)> { + if value == 0 { + return None; + } + let trailing = value.trailing_zeros(); + Some((value >> trailing, i32::try_from(trailing).ok()?)) +} + +fn accumulate_aligned_pair_square_sum( + records: impl IntoIterator>, + unit_exponent: i32, +) -> Option { let mut sum = 0_u128; + for (significand, exponent) in records.into_iter().flatten() { + let shift = exponent.checked_sub(unit_exponent)?.unsigned_abs(); + let coefficient = multiply_by_power_of_two(significand, shift)?; + sum = sum.checked_add(coefficient.checked_mul(coefficient)?)?; + } + Some(sum) +} + +fn pair_square_sum_quadratic_buffered(values: &[u128]) -> Option { + let pair_count = values + .len() + .checked_mul(values.len().checked_sub(1)?)? + .checked_div(2)?; + let mut records: Vec> = Vec::with_capacity(pair_count); + let mut unit_exponent = i32::MAX; for left in 0..values.len() { for right in left + 1..values.len() { - let difference = values[left].abs_diff(values[right]); - sum = sum.checked_add(difference.checked_mul(difference)?)?; + let record = compact_dyadic(values[left].abs_diff(values[right])); + if let Some((_, exponent)) = record { + unit_exponent = unit_exponent.min(exponent); + } + records.push(record); } } - Some(sum) + let scratch_records = records.capacity(); + let scratch_payload_bytes = scratch_records.checked_mul(size_of::>())?; + if unit_exponent == i32::MAX { + return Some(KernelObservation { + aligned_pair_square_sum: 0, + unit_exponent: 0, + scratch_records, + scratch_payload_bytes, + }); + } + let aligned_pair_square_sum = accumulate_aligned_pair_square_sum(records, unit_exponent)?; + Some(KernelObservation { + aligned_pair_square_sum, + unit_exponent, + scratch_records, + scratch_payload_bytes, + }) +} + +fn pair_square_sum_quadratic_two_pass(values: &[u128]) -> Option { + let mut unit_exponent = i32::MAX; + for left in 0..values.len() { + for right in left + 1..values.len() { + if let Some((_, exponent)) = compact_dyadic(values[left].abs_diff(values[right])) { + unit_exponent = unit_exponent.min(exponent); + } + } + } + if unit_exponent == i32::MAX { + return Some(KernelObservation { + aligned_pair_square_sum: 0, + unit_exponent: 0, + scratch_records: 0, + scratch_payload_bytes: 0, + }); + } + + let records = (0..values.len()).flat_map(|left| { + (left + 1..values.len()) + .map(move |right| compact_dyadic(values[left].abs_diff(values[right]))) + }); + let aligned_pair_square_sum = accumulate_aligned_pair_square_sum(records, unit_exponent)?; + Some(KernelObservation { + aligned_pair_square_sum, + unit_exponent, + scratch_records: 0, + scratch_payload_bytes: 0, + }) } -fn pair_square_sum_linear(values: &[u128]) -> Option { +fn pair_square_sum_linear(values: &[u128]) -> Option { let minimum = *values.iter().min()?; let sample_count = u128::try_from(values.len()).ok()?; let mut coefficient_sum = 0_u128; @@ -38,9 +129,20 @@ fn pair_square_sum_linear(values: &[u128]) -> Option { coefficient_sum = coefficient_sum.checked_add(coefficient)?; square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; } - sample_count + let pair_square_sum = sample_count .checked_mul(square_sum)? - .checked_sub(coefficient_sum.checked_mul(coefficient_sum)?) + .checked_sub(coefficient_sum.checked_mul(coefficient_sum)?)?; + Some(KernelObservation { + aligned_pair_square_sum: pair_square_sum, + unit_exponent: 0, + scratch_records: 0, + scratch_payload_bytes: 0, + }) +} + +fn restored_pair_square_sum(observation: KernelObservation) -> Option { + let shift = observation.unit_exponent.checked_mul(2)?.unsigned_abs(); + multiply_by_power_of_two(observation.aligned_pair_square_sum, shift) } fn percentile_95(mut durations: Vec) -> Duration { @@ -52,8 +154,8 @@ fn percentile_95(mut durations: Vec) -> Duration { fn measure( values: &[u128], samples: usize, - kernel: fn(&[u128]) -> Option, -) -> Duration { + kernel: fn(&[u128]) -> Option, +) -> (Duration, KernelObservation) { for _ in 0..3 { black_box(kernel(black_box(values)).expect("fixture must remain within u128")); } @@ -63,7 +165,25 @@ fn measure( black_box(kernel(black_box(values)).expect("fixture must remain within u128")); durations.push(started.elapsed()); } - percentile_95(durations) + let observation = kernel(values).expect("fixture must remain within u128"); + (percentile_95(durations), observation) +} + +fn emit( + sample_count: usize, + kernel_name: &str, + p95: Duration, + samples: usize, + observation: KernelObservation, +) { + println!( + "{sample_count},{kernel_name},{},{samples},{},{},{},{}", + p95.as_nanos(), + observation.unit_exponent, + observation.scratch_records, + observation.scratch_payload_bytes, + size_of::>() + ); } fn main() { @@ -73,24 +193,54 @@ fn main() { .unwrap_or(25) .max(1); - println!("sample_count,kernel,p95_ns,timing_samples"); + println!( + "sample_count,kernel,p95_ns,timing_samples,unit_exponent,scratch_records,scratch_payload_bytes,pair_record_size_bytes" + ); for sample_count in [16_usize, 64, 256, 1_024, 2_047] { let values = fixture(sample_count); - let quadratic = pair_square_sum_quadratic(&values).expect("quadratic result"); - let linear = pair_square_sum_linear(&values).expect("linear result"); - assert_eq!(quadratic, linear, "algebraic kernels must agree"); - - let quadratic_p95 = measure(&values, samples, pair_square_sum_quadratic); - let linear_p95 = measure(&values, samples, pair_square_sum_linear); - println!( - "{sample_count},quadratic,{},{}", - quadratic_p95.as_nanos(), - samples + let buffered = pair_square_sum_quadratic_buffered(&values) + .expect("buffered quadratic result stays within u128"); + let two_pass = pair_square_sum_quadratic_two_pass(&values) + .expect("two-pass quadratic result stays within u128"); + let linear = pair_square_sum_linear(&values).expect("linear result stays within u128"); + let exact_pair_square_sum = restored_pair_square_sum(buffered) + .expect("buffered result restores to exact pair-square sum"); + assert_eq!( + restored_pair_square_sum(two_pass), + Some(exact_pair_square_sum), + "quadratic kernels must agree" + ); + assert_eq!( + restored_pair_square_sum(linear), + Some(exact_pair_square_sum), + "linear identity must agree with pair reference" + ); + + let (buffered_p95, buffered_observation) = + measure(&values, samples, pair_square_sum_quadratic_buffered); + let (two_pass_p95, two_pass_observation) = + measure(&values, samples, pair_square_sum_quadratic_two_pass); + let (linear_p95, linear_observation) = measure(&values, samples, pair_square_sum_linear); + emit( + sample_count, + "quadratic_buffered", + buffered_p95, + samples, + buffered_observation, + ); + emit( + sample_count, + "quadratic_two_pass", + two_pass_p95, + samples, + two_pass_observation, ); - println!( - "{sample_count},linear,{},{}", - linear_p95.as_nanos(), - samples + emit( + sample_count, + "linear", + linear_p95, + samples, + linear_observation, ); } } From ffbd320c0f692d1084defd773ea3101661e4c159 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:58:50 +0900 Subject: [PATCH 466/576] docs(validation): record exact-proof budget characterization --- .../validation-bias-exact-proof-budget-characterization.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md diff --git a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md new file mode 100644 index 000000000..6219e9c0b --- /dev/null +++ b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md @@ -0,0 +1,6 @@ +# Validation bias-SE exact-proof budget characterization + +- Correct the pair-record resource evidence for 3,162 observations to 4,997,541 records and lock exact pair counts in a Rust characterization contract. +- Distinguish the current minimum-shifted O(n) `u128` intermediate ceiling (`n=2,047` at aligned diameter `2^53`) from the wider exact pair-square numerator envelope (`n=4,095` for the same diameter); neither is a production sample-count budget. +- Extend the release-mode characterization harness to compare the buffered O(n²) pair layout, an allocation-free two-pass O(n²) reference, and the O(n) algebraic accumulator while reporting target-specific pair-record size, scratch-record capacity, and scratch payload bytes. +- Keep production `bias_standard_error` admission unchanged at `n=4..=16` pending actual release-mode CPU/allocation results, admitted/refused-set comparison, exact-head CI, and applicable buyer-path p95 evidence. From ca8f993ba6dc720255322c3bdbaa53f8da9f0227 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 02:59:24 +0900 Subject: [PATCH 467/576] docs(validation): refine exact-proof resource evidence --- ...-bias-standard-error-exact-proof-budget.md | 49 +++++++++++++------ 1 file changed, 35 insertions(+), 14 deletions(-) diff --git a/docs/research/validation-bias-standard-error-exact-proof-budget.md b/docs/research/validation-bias-standard-error-exact-proof-budget.md index 589e27e53..0f05e1ccc 100644 --- a/docs/research/validation-bias-standard-error-exact-proof-budget.md +++ b/docs/research/validation-bias-standard-error-exact-proof-budget.md @@ -2,7 +2,7 @@ ## Scope -Issue #491 replaces the sample-count staircase in GAP-111 through GAP-125 with an evidence-based resource budget. This note records the first bounded characterization step. It does **not** widen production admission beyond `n=16`, does not claim buyer-path p95, and does not make a benchmark result from an unmeasured environment authoritative. +Issue #491 replaces the sample-count staircase in GAP-111 through GAP-125 with an evidence-based resource budget. This note records bounded characterization evidence. It does **not** widen production admission beyond `n=16`, does not claim buyer-path p95, and does not make a benchmark result from an unmeasured environment authoritative. ## New represented-input boundary evidence @@ -20,7 +20,7 @@ For `n=17`, the scientific denominator is `17^2(17-1)=4_624`. `gcd(N,4_624)=2`, The current public API intentionally remains on the established translated floating fallback at `n=17`; for this fixture it returns bits `0x41a0_dd77_9ac3_8e98`. Independent high-precision rational-square-root evaluation gives the adjacent correctly rounded binary64 target `0x41a0_dd77_9ac3_8e99`. This extends the demonstrated failure class beyond the current cutoff, but it is evidence for a systemic budget decision rather than justification for another one-count production patch. -`crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` fixes the exact pair-square sum, reduced ratio, current fallback boundary, and the algebraic equivalence below. The test intentionally makes any future widening of production admission update this characterization rather than silently inheriting a stale fallback assumption. +`crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` fixes the exact pair-square sum, reduced ratio, current fallback boundary, algebraic equivalence, pair-record counts, and the distinct checked-integer envelopes below. The test intentionally makes any future widening of production admission update this characterization rather than silently inheriting a stale fallback assumption. ## O(n²) reference versus O(n) exact accumulator @@ -28,31 +28,51 @@ For exact dyadic coefficients `c_i` on one shared unit, `sum_{i` before the second accumulation pass. The number of stored pair records is exactly `n(n-1)/2`: 120 at `n=16`, 2,096,128 at `n=2_048`, and 4,997,500 at `n=3_162`. Exact byte cost depends on the compiled Rust layout and must be measured rather than inferred from field widths. +The current bounded O(n²) implementation stores every pair as `Option<(u128, i32)>` before the second accumulation pass. The number of stored pair records is exactly `n(n-1)/2`: 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and **4,997,541** at `n=3,162`. The predecessor note incorrectly recorded the last count as 4,997,500; the Rust characterization now locks the exact integer count. -A two-pass O(n²) reference can remove that pair-record allocation without changing proof semantics: the first pass establishes exact pairwise subtraction and the minimum unit exponent; the second recomputes each exact pair difference and accumulates its checked integer square. The proposed O(n) sufficient-admission path would reduce both pair enumeration and pair storage, but neither optimization is activated on production input until exact-head Rust verification and resource measurements support it. +Exact byte cost is target-layout dependent and must not be inferred by adding field widths. The measurement harness now obtains `size_of::>()` on the executing target, records the actual `Vec` element capacity after `with_capacity`, and reports their product as scratch payload bytes. Allocator bookkeeping and whole-process RSS are still outside that number and must be recorded separately if they become release evidence. + +A two-pass O(n²) reference can remove the pair-record allocation without changing the pair-enumeration proof shape: the first pass establishes the common dyadic unit, and the second recomputes each pair record and accumulates the checked square. The harness now measures this allocation-free quadratic alternative alongside the buffered pair layout. The O(n) identity removes pair enumeration as well, but production activation still requires its admitted/refused-set proof and exact-head verification. ## Measurement harness -`crates/validation_core/examples/bias_se_exact_proof_budget.rs` is a standard-library-only harness for reproducible relative timing of the quadratic and linear checked-integer kernels on deterministic compact-dyadic coefficients. It emits CSV `sample_count,kernel,p95_ns,timing_samples` rows for 16, 64, 256, 1,024, and 2,047 observations and asserts exact equality between kernels before timing. +`crates/validation_core/examples/bias_se_exact_proof_budget.rs` is a standard-library-only release-mode characterization harness. For deterministic compact-dyadic coefficients it compares three kernels: + +- `quadratic_buffered`: production-layout-shaped `Vec>` pair records plus aligned checked-square accumulation; +- `quadratic_two_pass`: the same pair enumeration and dyadic alignment without pair-record storage; +- `linear`: `n*sum(c_i^2) - (sum c_i)^2` on minimum-shifted coefficients. -This harness deliberately excludes binary64 residual admission, public API composition, networking, and process scheduling. Its output therefore characterizes the integer kernel only. Release-mode results must record CPU/OS/toolchain/commit and cannot substitute for an applicable API buyer-path p95 measurement. +Before timing, the harness restores the buffered/two-pass unit exponent and requires all three kernels to equal the same exact pair-square numerator. It emits CSV columns + +`sample_count,kernel,p95_ns,timing_samples,unit_exponent,scratch_records,scratch_payload_bytes,pair_record_size_bytes` + +for 16, 64, 256, 1,024, and 2,047 observations. The buffered timing includes pair-vector allocation and consumption; the two-pass and linear rows report zero pair-record scratch payload. This is still a kernel harness, not the full public API: binary64 residual admission, endpoint serialization, networking, scheduler effects, and allocator metadata/RSS remain outside the measurement. + +No release-mode timing result is recorded in this document yet. A valid timing record must include CPU, OS, Rust toolchain, exact commit, release build mode, raw sample count, and raw CSV. It cannot substitute for an applicable API buyer-path p95 measurement. ## Decision and rejected alternatives -Production admission stays `n=4..=16` in this commit. Extending to `n=17` alone is rejected because GAP-111 through GAP-125 plus the new seventeen-observation evidence show that the integer cutoff is not a scientific boundary. Removing the cutoff entirely is rejected because the existing implementation is O(n²) in both pair work and pair-record storage. Treating the `n<=2_047` checked-integer envelope as the production budget is rejected because arithmetic representability is not latency evidence. Arbitrary-precision production arithmetic is also deferred: it may be useful as an independent reference alternative, but adding it to the production dependency surface requires measured benefit, supply-chain review, and an explicit owner/resource decision. +Production admission stays `n=4..=16`. Extending to `n=17` alone is rejected because GAP-111 through GAP-125 plus the seventeen-observation evidence show that the integer cutoff is not a scientific boundary. Removing the cutoff entirely is rejected because the current production implementation still enumerates and stores O(n²) pair evidence. Treating `n<=2_047`, `n<=4_095`, or the unreduced denominator threshold as the production budget is rejected because arithmetic representability is not latency or memory evidence. + +A two-pass O(n²) allocation-removal path remains a candidate because it can preserve the current pair-proof shape while eliminating pair-record storage. The O(n) identity remains the stronger candidate for CPU scaling, subject to an admitted/refused-set proof. Arbitrary-precision production arithmetic is deferred: it may be useful as an independent wider reference, but adding it to the production dependency surface requires measured benefit, supply-chain review, and an explicit owner/resource decision. ## Traceability @@ -64,9 +84,10 @@ Production admission stays `n=4..=16` in this commit. Extending to `n=17` alone | Current production module | `crates/validation_core/src/bias_se.rs` | | Public API | `validation_core::bias_standard_error` | | Exact characterization | `crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` | -| CPU kernel harness | `crates/validation_core/examples/bias_se_exact_proof_budget.rs` | +| CPU/layout harness | `crates/validation_core/examples/bias_se_exact_proof_budget.rs` | +| CHANGELOG evidence | `CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md` | | Resource/merge rule | Production cutoff remains `n<=16` pending measured exact-proof budget | ## Follow-up evidence required by #491 -Run the new harness in release mode on a recorded CPU/toolchain and repeat enough independent samples to retain raw timing evidence. Measure allocation/layout rather than estimating bytes. Prototype the stronger O(n) dyadic-grid admission behind tests and compare its admitted/refused set against the existing pairwise proof. Evaluate a wider-integer/reference alternative without making it production authority. Only after those results establish a resource budget should production admission change; any such change needs a realistic public RED, exact-head Rust/rustdoc/coverage evidence, and applicable buyer-path p95 evidence. +Run the harness in release mode on a recorded CPU/toolchain and retain raw timing CSV. Record allocator/RSS evidence in addition to the harness's exact pair-record payload layout. Prototype the stronger O(n) dyadic-grid admission behind tests and compare its admitted/refused set against the existing pairwise proof. Evaluate a wider-integer/reference alternative without making it production authority. Only after those results establish a resource budget should production admission change; any such change needs a realistic public RED, exact-head Rust/rustdoc/coverage evidence, and applicable buyer-path p95 evidence. From 5a512609778269dfb893a2e04865d048955388bf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 03:00:01 +0900 Subject: [PATCH 468/576] docs(test): distinguish exact-proof resource envelopes --- docs/TEST_STRATEGY.md | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index 637f653df..745bf8149 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -69,10 +69,11 @@ Validation Evidence numerical proofs that add asymptotic work or material alloca - retain realistic represented-input counterexamples and permutation/sign-mirror contracts for scientific correctness; - keep the existing exact residual/pairwise-subtraction, checked-integer, GCD-reduction, and exact midpoint authority fail-closed; -- compare the current O(n²) pair-distance proof with an algebraically equivalent O(n) exact accumulator only under a proved sufficient admission condition, and compare a wider-integer/reference alternative separately; +- compare three resource shapes before widening admission: the buffered O(n²) pair-record path, an allocation-free two-pass O(n²) reference, and an algebraically equivalent O(n) exact accumulator under a proved sufficient admission condition; compare a wider-integer/reference alternative separately; - characterize checked-`u128` refusal as a function of sample count and aligned dyadic diameter/exponent spread rather than treating an integer cutoff as a scientific boundary; -- record allocation count and compiled layout measurements separately; field-width estimates are not allocation evidence; -- run `crates/validation_core/examples/bias_se_exact_proof_budget.rs` in release mode on a recorded CPU/OS/Rust toolchain and retain raw samples plus p95; the harness is integer-kernel characterization, not an HTTP buyer-path result; +- keep the minimum-shifted O(n) intermediate ceiling distinct from the exact pair-square numerator ceiling: at aligned diameter `2^53`, the characterized bounds are `n<=2_047` and `n<=4_095` respectively, and neither is a production budget; +- record exact pair counts, target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence separately; field-width estimates alone are not allocation evidence; +- run `crates/validation_core/examples/bias_se_exact_proof_budget.rs` in release mode on a recorded CPU/OS/Rust toolchain and retain raw CSV plus p95; the harness is integer-kernel/layout characterization, not an HTTP buyer-path result; - if a service/API buyer path is affected, measure the full applicable path and retain the `p95 <= 20 ms` target without shrinking samples, omitting proof work, or relying on unrealistic warm-cache setup; - arithmetic representability alone does not authorize a production sample-count budget. @@ -84,4 +85,4 @@ A release requires one integrated protected head with all relevant scientific, n ## References -The full APA 7th register is [`docs/research/standards-and-literature.md`](research/standards-and-literature.md). Method names used above cite Allen (1983) for interval algebra and Asparouhov & Muthén (2009), Asparouhov et al. (2018), and Marsh et al. (2014) for ESEM/DSEM. \ No newline at end of file +The full APA 7th register is [`docs/research/standards-and-literature.md`](research/standards-and-literature.md). Method names used above cite Allen (1983) for interval algebra and Asparouhov & Muthén (2009), Asparouhov et al. (2018), and Marsh et al. (2014) for ESEM/DSEM. From 797f7caa557440634038d166ee7098f7c6df5b9d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 03:00:22 +0900 Subject: [PATCH 469/576] docs(ops): refine numerical proof resource contract --- docs/OPERABILITY.md | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index 202a774bf..669e7835b 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -60,18 +60,21 @@ Before PostgreSQL becomes production state, prove migrations and rollback, tenan A numerical proof boundary is an operational resource contract when it changes asymptotic work, allocation, or buyer-path latency. It is not determined by the next sample count that happens to expose a rounding defect. -Issue #491 owns the current bias-standard-error exact-proof budget. Production exact pair-distance admission stays bounded to `n<=16` while the current implementation is O(n²) in pair enumeration and stores `n(n-1)/2` pair records. The characterization on PR #488 records a seventeen-observation counterexample, an algebraically equivalent O(n) checked-integer numerator, and the compact-dyadic `u128` arithmetic envelope, but none of those facts alone authorize a wider production budget. +Issue #491 owns the current bias-standard-error exact-proof budget. Production exact pair-distance admission stays bounded to `n<=16` while the current implementation is O(n²) in pair enumeration and stores `n(n-1)/2` pair records. The characterization on PR #488 records a seventeen-observation counterexample and an algebraically equivalent O(n) checked-integer numerator, but arithmetic representability alone does not authorize a wider production budget. -Before changing that production boundary, retain: +The current characterization distinguishes three bounds that must not be collapsed into one cutoff. At aligned diameter `D=2^53`, the minimum-shifted O(n) intermediate bound `n^2D^2` fits `u128` through `n=2_047`, while the exact pair-square numerator extremal bound `floor(n^2/4)D^2` fits through `n=4_095`. Separately, the unreduced scientific denominator `n^2(n-1)` exceeds `2^53` after `n=208_064`; production uses the reduced denominator after GCD, so that threshold is only an envelope marker. None of these values is a latency or memory budget. + +Before changing the production boundary, retain: - release-mode raw timing samples and p95 from `crates/validation_core/examples/bias_se_exact_proof_budget.rs`, with exact commit, CPU, OS, Rust toolchain, build flags, timing sample count, and cold/warm procedure; -- measured compiled allocation/layout evidence rather than byte estimates inferred from field widths; +- side-by-side buffered O(n²), allocation-free two-pass O(n²), and O(n) kernel evidence, with exact equality of restored pair-square numerators before timing; +- target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence rather than byte estimates inferred from field widths; - admitted/refused-set comparison between the existing pairwise proof and any stronger sufficient O(n) dyadic-grid proof, with proof refusal falling back rather than altering scientific meaning; - checked-`u128` overflow/refusal evidence across sample count and represented exponent spread; - a wider-integer/reference alternative assessment kept separate from production authority unless its dependency/security/performance cost is explicitly accepted; - full service/API p95 when a buyer-facing path is affected, preserving the TEPP `p95 <= 20 ms` target without shrinking input, omitting proof work, or using an unrealistic cache-only setup. -A two-pass O(n²) implementation may remove pair-record storage while preserving pairwise exactness semantics, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its worst-case integer arithmetic fits `u128`. +The exact pair-record count is `n(n-1)/2`; the current characterization locks 120 records at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and 4,997,541 at `n=3,162`. A two-pass O(n²) implementation may remove pair-record storage while preserving pair-enumeration proof shape, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its checked arithmetic fits `u128`. ## Model release/cutover @@ -87,4 +90,4 @@ GitHub Actions registry identities survive YAML deletion. After any bootstrap, d ## Release gate -A software release requires exact protected-head CI/security/review, 100% production coverage/docs, validated migrations/rollback where present, scientific benchmark artifacts, SBOM/provenance, reproducible packages/images, operator runbooks, accessibility for product UI, CHANGELOG/version/tag consistency, and post-publish verification. TEPP has not reached that integrated release state merely because individual foundation PRs merge. Unexecuted timing harnesses and branch-only resource characterization are not release evidence. \ No newline at end of file +A software release requires exact protected-head CI/security/review, 100% production coverage/docs, validated migrations/rollback where present, scientific benchmark artifacts, SBOM/provenance, reproducible packages/images, operator runbooks, accessibility for product UI, CHANGELOG/version/tag consistency, and post-publish verification. TEPP has not reached that integrated release state merely because individual foundation PRs merge. Unexecuted timing harnesses and branch-only resource characterization are not release evidence. From c70210e331ec096c90a42bec9de7d619bd51fe26 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 04:01:06 +0900 Subject: [PATCH 470/576] test(validation): characterize linear proof admission subset --- ...ror_exact_proof_budget_characterization.rs | 55 +++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs index 9e62a009f..fd0df4772 100644 --- a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs +++ b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs @@ -20,6 +20,15 @@ const SEVENTEEN_OBSERVATION_FIXTURE: [u128; 17] = [ 1_805_452_085, ]; +fn deterministic_compact_fixture(sample_count: usize) -> Vec { + (0..sample_count) + .map(|index| { + let value = u128::try_from(index).expect("fixture index fits u128"); + (value * 1_000_003 + value * value * 97 + 17) % 4_000_000_001 + }) + .collect() +} + fn pair_square_sum_quadratic(values: &[u128]) -> Option { let mut sum = 0_u128; for left in 0..values.len() { @@ -110,6 +119,52 @@ fn seventeen_observation_fixture_proves_linear_identity_matches_pair_reference() ); } +#[test] +fn linear_checked_integer_kernel_matches_pair_reference_when_it_admits() { + for sample_count in [4_usize, 16, 17, 32, 64, 128, 256] { + let values = deterministic_compact_fixture(sample_count); + let pairwise = pair_square_sum_quadratic(&values) + .expect("compact-grid pair reference stays within u128"); + let linear = pair_square_sum_linear(&values) + .expect("compact-grid linear kernel stays within u128"); + assert_eq!( + linear, pairwise, + "linear sufficient proof must preserve the exact pair numerator at n={sample_count}" + ); + } +} + +#[test] +fn linear_checked_integer_kernel_is_not_admission_equivalent_to_pair_reference() { + let diameter = 1_u128 << 58; + + let mut fits_both = Vec::with_capacity(64); + fits_both.push(0); + fits_both.extend((0..63).map(|_| diameter)); + let pairwise_64 = pair_square_sum_quadratic(&fits_both) + .expect("64-sample pair numerator stays within u128"); + assert_eq!( + pair_square_sum_linear(&fits_both), + Some(pairwise_64), + "n=64 remains inside the minimum-shifted linear intermediate budget" + ); + + let mut pair_only = Vec::with_capacity(65); + pair_only.push(0); + pair_only.extend((0..64).map(|_| diameter)); + let pairwise_65 = pair_square_sum_quadratic(&pair_only) + .expect("65-sample pair numerator still stays within u128"); + let expected_pairwise_65 = 64_u128 + .checked_mul(diameter.checked_mul(diameter).expect("diameter square fits")) + .expect("pair numerator fits"); + assert_eq!(pairwise_65, expected_pairwise_65); + assert_eq!( + pair_square_sum_linear(&pair_only), + None, + "n*sum(c_i^2) overflows before cancellation even though the exact pair numerator fits" + ); +} + #[test] fn pair_record_counts_are_exact_resource_evidence() { assert_eq!(pair_record_count(16), Some(120)); From 579604a4a0aca9d191e5cb444b51fa822fe43d76 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 04:01:59 +0900 Subject: [PATCH 471/576] docs(validation): record linear proof admission subset --- ...-bias-standard-error-exact-proof-budget.md | 24 +++++++++++-------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/docs/research/validation-bias-standard-error-exact-proof-budget.md b/docs/research/validation-bias-standard-error-exact-proof-budget.md index 0f05e1ccc..ed427cb26 100644 --- a/docs/research/validation-bias-standard-error-exact-proof-budget.md +++ b/docs/research/validation-bias-standard-error-exact-proof-budget.md @@ -20,7 +20,7 @@ For `n=17`, the scientific denominator is `17^2(17-1)=4_624`. `gcd(N,4_624)=2`, The current public API intentionally remains on the established translated floating fallback at `n=17`; for this fixture it returns bits `0x41a0_dd77_9ac3_8e98`. Independent high-precision rational-square-root evaluation gives the adjacent correctly rounded binary64 target `0x41a0_dd77_9ac3_8e99`. This extends the demonstrated failure class beyond the current cutoff, but it is evidence for a systemic budget decision rather than justification for another one-count production patch. -`crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` fixes the exact pair-square sum, reduced ratio, current fallback boundary, algebraic equivalence, pair-record counts, and the distinct checked-integer envelopes below. The test intentionally makes any future widening of production admission update this characterization rather than silently inheriting a stale fallback assumption. +`crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` fixes the exact pair-square sum, reduced ratio, current fallback boundary, algebraic equivalence, pair-record counts, distinct checked-integer envelopes, and the admitted/refused-set relation between the current pair reference and the candidate O(n) accumulator. The test intentionally makes any future widening of production admission update this characterization rather than silently inheriting a stale fallback assumption. ## O(n²) reference versus O(n) exact accumulator @@ -28,17 +28,21 @@ For exact dyadic coefficients `c_i` on one shared unit, `sum_{i>()` on the executing target, records the actual `Vec` element capacity after `with_capacity`, and reports their product as scratch payload bytes. Allocator bookkeeping and whole-process RSS are still outside that number and must be recorded separately if they become release evidence. -A two-pass O(n²) reference can remove the pair-record allocation without changing the pair-enumeration proof shape: the first pass establishes the common dyadic unit, and the second recomputes each pair record and accumulates the checked square. The harness now measures this allocation-free quadratic alternative alongside the buffered pair layout. The O(n) identity removes pair enumeration as well, but production activation still requires its admitted/refused-set proof and exact-head verification. +A two-pass O(n²) reference can remove the pair-record allocation without changing the pair-enumeration proof shape: the first pass establishes the common dyadic unit, and the second recomputes each pair record and accumulates the checked square. The harness now measures this allocation-free quadratic alternative alongside the buffered pair layout. The O(n) identity removes pair enumeration as well, but the new admission-set characterization shows that its present checked-`u128` form must remain a sufficient fast path with pairwise fallback unless wider intermediates are justified. ## Measurement harness @@ -60,11 +64,11 @@ A two-pass O(n²) reference can remove the pair-record allocation without changi - `quadratic_two_pass`: the same pair enumeration and dyadic alignment without pair-record storage; - `linear`: `n*sum(c_i^2) - (sum c_i)^2` on minimum-shifted coefficients. -Before timing, the harness restores the buffered/two-pass unit exponent and requires all three kernels to equal the same exact pair-square numerator. It emits CSV columns +Before timing, the harness restores the buffered/two-pass unit exponent and requires all three kernels to equal the same exact pair-square numerator on the timed fixtures. It emits CSV columns `sample_count,kernel,p95_ns,timing_samples,unit_exponent,scratch_records,scratch_payload_bytes,pair_record_size_bytes` -for 16, 64, 256, 1,024, and 2,047 observations. The buffered timing includes pair-vector allocation and consumption; the two-pass and linear rows report zero pair-record scratch payload. This is still a kernel harness, not the full public API: binary64 residual admission, endpoint serialization, networking, scheduler effects, and allocator metadata/RSS remain outside the measurement. +for 16, 64, 256, 1,024, and 2,047 observations. The buffered timing includes pair-vector allocation and consumption; the two-pass and linear rows report zero pair-record scratch payload. This is still a kernel harness, not the full public API: binary64 residual admission, endpoint serialization, networking, scheduler effects, allocator metadata/RSS, and the linear-refusal/pair-fallback hybrid are outside the current measurement. No release-mode timing result is recorded in this document yet. A valid timing record must include CPU, OS, Rust toolchain, exact commit, release build mode, raw sample count, and raw CSV. It cannot substitute for an applicable API buyer-path p95 measurement. @@ -72,7 +76,7 @@ No release-mode timing result is recorded in this document yet. A valid timing r Production admission stays `n=4..=16`. Extending to `n=17` alone is rejected because GAP-111 through GAP-125 plus the seventeen-observation evidence show that the integer cutoff is not a scientific boundary. Removing the cutoff entirely is rejected because the current production implementation still enumerates and stores O(n²) pair evidence. Treating `n<=2_047`, `n<=4_095`, or the unreduced denominator threshold as the production budget is rejected because arithmetic representability is not latency or memory evidence. -A two-pass O(n²) allocation-removal path remains a candidate because it can preserve the current pair-proof shape while eliminating pair-record storage. The O(n) identity remains the stronger candidate for CPU scaling, subject to an admitted/refused-set proof. Arbitrary-precision production arithmetic is deferred: it may be useful as an independent wider reference, but adding it to the production dependency surface requires measured benefit, supply-chain review, and an explicit owner/resource decision. +A two-pass O(n²) allocation-removal path remains a candidate because it can preserve the current pair-proof admission shape while eliminating pair-record storage. The O(n) identity remains the stronger CPU-scaling candidate, but its checked-`u128` form is now proven to be a strict sufficient subset of the pair reference. Replacing the pair proof with that kernel alone is rejected because it would silently narrow exact-proof admission. The viable bounded designs are therefore O(n) fast admission with O(n²) fallback, an admission-equivalent wider-integer O(n) proof, or the allocation-free two-pass pair reference if measurements show it is adequate. Arbitrary-precision production arithmetic remains deferred pending measured benefit, supply-chain review, and an explicit owner/resource decision. ## Traceability @@ -90,4 +94,4 @@ A two-pass O(n²) allocation-removal path remains a candidate because it can pre ## Follow-up evidence required by #491 -Run the harness in release mode on a recorded CPU/toolchain and retain raw timing CSV. Record allocator/RSS evidence in addition to the harness's exact pair-record payload layout. Prototype the stronger O(n) dyadic-grid admission behind tests and compare its admitted/refused set against the existing pairwise proof. Evaluate a wider-integer/reference alternative without making it production authority. Only after those results establish a resource budget should production admission change; any such change needs a realistic public RED, exact-head Rust/rustdoc/coverage evidence, and applicable buyer-path p95 evidence. +Run the harness in release mode on a recorded CPU/toolchain and retain raw timing CSV. Record allocator/RSS evidence in addition to the harness's exact pair-record payload layout. Extend the harness with the viable hybrid shape—O(n) sufficient admission followed by the existing pair reference on checked-intermediate refusal—and measure both admitting and refusing geometries. Evaluate a wider-integer/reference alternative without making it production authority. Only after those results establish a resource budget should production admission change; any such change needs a realistic public RED, exact-head Rust/rustdoc/coverage evidence, and applicable buyer-path p95 evidence. From da7e830b46df056d3416118d56d07c5a728b09d2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 04:02:08 +0900 Subject: [PATCH 472/576] docs(validation): note linear admission asymmetry --- .../validation-bias-exact-proof-budget-characterization.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md index 6219e9c0b..318bc1a98 100644 --- a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md +++ b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md @@ -1,6 +1,7 @@ # Validation bias-SE exact-proof budget characterization - Correct the pair-record resource evidence for 3,162 observations to 4,997,541 records and lock exact pair counts in a Rust characterization contract. -- Distinguish the current minimum-shifted O(n) `u128` intermediate ceiling (`n=2,047` at aligned diameter `2^53`) from the wider exact pair-square numerator envelope (`n=4,095` for the same diameter); neither is a production sample-count budget. +- Distinguish the current minimum-shifted O(n) `u128` intermediate envelope (`n=2,047` at aligned diameter `2^53`) from the wider exact pair-square numerator envelope (`n=4,095` for the same diameter); neither is a production sample-count budget. +- Prove the checked O(n) accumulator is a sufficient but not admission-equivalent replacement for the pair reference: with one zero and the remaining coefficients at `D=2^58`, both kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits (`2^122`) but `n*sum(c_i^2)` overflows `u128` before cancellation. - Extend the release-mode characterization harness to compare the buffered O(n²) pair layout, an allocation-free two-pass O(n²) reference, and the O(n) algebraic accumulator while reporting target-specific pair-record size, scratch-record capacity, and scratch payload bytes. -- Keep production `bias_standard_error` admission unchanged at `n=4..=16` pending actual release-mode CPU/allocation results, admitted/refused-set comparison, exact-head CI, and applicable buyer-path p95 evidence. +- Keep production `bias_standard_error` admission unchanged at `n=4..=16`; any O(n) production path must preserve current admission with pairwise fallback or wider checked integers and still requires actual release-mode CPU/allocation results, exact-head CI, and applicable buyer-path p95 evidence. From bef1a4701a737f4a230968aaf68c25d5f29b60cb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 04:02:40 +0900 Subject: [PATCH 473/576] docs(test): require linear proof fallback parity --- docs/TEST_STRATEGY.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index 745bf8149..b20749095 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -70,10 +70,12 @@ Validation Evidence numerical proofs that add asymptotic work or material alloca - retain realistic represented-input counterexamples and permutation/sign-mirror contracts for scientific correctness; - keep the existing exact residual/pairwise-subtraction, checked-integer, GCD-reduction, and exact midpoint authority fail-closed; - compare three resource shapes before widening admission: the buffered O(n²) pair-record path, an allocation-free two-pass O(n²) reference, and an algebraically equivalent O(n) exact accumulator under a proved sufficient admission condition; compare a wider-integer/reference alternative separately; -- characterize checked-`u128` refusal as a function of sample count and aligned dyadic diameter/exponent spread rather than treating an integer cutoff as a scientific boundary; -- keep the minimum-shifted O(n) intermediate ceiling distinct from the exact pair-square numerator ceiling: at aligned diameter `2^53`, the characterized bounds are `n<=2_047` and `n<=4_095` respectively, and neither is a production budget; +- require an admitted/refused-set contract for any O(n) candidate. The current checked-`u128` minimum-shifted kernel is a strict sufficient subset, not a drop-in equivalent: with one coefficient at zero and the rest at `D=2^58`, both pair and linear kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits (`2^122`) but `n*sum(c_i^2)` overflows before cancellation; +- therefore an O(n) production optimization must either fall back to the current pairwise proof on checked-intermediate refusal or use a separately justified wider checked-integer representation; linear refusal must never silently narrow scientific admission; +- characterize checked-`u128` refusal as a function of sample count, aligned dyadic diameter/exponent spread, and coefficient distribution rather than treating an integer cutoff as a scientific boundary; +- keep the minimum-shifted O(n) distribution-independent intermediate envelope distinct from the exact pair-square numerator envelope: at aligned diameter `2^53`, the characterized sufficient bounds are `n<=2_047` and `n<=4_095` respectively, and neither is a production budget; - record exact pair counts, target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence separately; field-width estimates alone are not allocation evidence; -- run `crates/validation_core/examples/bias_se_exact_proof_budget.rs` in release mode on a recorded CPU/OS/Rust toolchain and retain raw CSV plus p95; the harness is integer-kernel/layout characterization, not an HTTP buyer-path result; +- run `crates/validation_core/examples/bias_se_exact_proof_budget.rs` in release mode on a recorded CPU/OS/Rust toolchain and retain raw CSV plus p95; extend measurement to a linear-fast-path/pair-fallback hybrid with both admitting and refusing geometries before using O(n) as production resource evidence; - if a service/API buyer path is affected, measure the full applicable path and retain the `p95 <= 20 ms` target without shrinking samples, omitting proof work, or relying on unrealistic warm-cache setup; - arithmetic representability alone does not authorize a production sample-count budget. From da353736cdfe2b8f729720d3d8166d162bd5f8cc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 04:03:10 +0900 Subject: [PATCH 474/576] docs(operability): preserve pair proof on linear refusal --- docs/OPERABILITY.md | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index 669e7835b..037791cec 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -62,19 +62,22 @@ A numerical proof boundary is an operational resource contract when it changes a Issue #491 owns the current bias-standard-error exact-proof budget. Production exact pair-distance admission stays bounded to `n<=16` while the current implementation is O(n²) in pair enumeration and stores `n(n-1)/2` pair records. The characterization on PR #488 records a seventeen-observation counterexample and an algebraically equivalent O(n) checked-integer numerator, but arithmetic representability alone does not authorize a wider production budget. -The current characterization distinguishes three bounds that must not be collapsed into one cutoff. At aligned diameter `D=2^53`, the minimum-shifted O(n) intermediate bound `n^2D^2` fits `u128` through `n=2_047`, while the exact pair-square numerator extremal bound `floor(n^2/4)D^2` fits through `n=4_095`. Separately, the unreduced scientific denominator `n^2(n-1)` exceeds `2^53` after `n=208_064`; production uses the reduced denominator after GCD, so that threshold is only an envelope marker. None of these values is a latency or memory budget. +The current characterization distinguishes three bounds that must not be collapsed into one cutoff. At aligned diameter `D=2^53`, the minimum-shifted O(n) distribution-independent intermediate envelope `n^2D^2` fits `u128` through `n=2_047`, while the exact pair-square numerator extremal bound `floor(n^2/4)D^2` fits through `n=4_095`. Separately, the unreduced scientific denominator `n^2(n-1)` exceeds `2^53` after `n=208_064`; production uses the reduced denominator after GCD, so that threshold is only an envelope marker. None of these values is a latency or memory budget, and the actual linear checked-intermediate admission also depends on coefficient distribution. + +The candidate O(n) accumulator is now characterized as a strict sufficient subset of the current pair reference under `u128`. With one coefficient at zero and every other coefficient at `D=2^58`, both kernels fit at `n=64`. At `n=65`, the exact pair numerator remains `64*D^2 = 2^122`, while the linear first term `65*64*D^2 = 4160*2^116` overflows `u128` before cancellation. A linear refusal therefore cannot become a scientific refusal. If the O(n) kernel is introduced, it must fall back to the existing pairwise proof or use a separately justified wider checked-integer representation. Before changing the production boundary, retain: - release-mode raw timing samples and p95 from `crates/validation_core/examples/bias_se_exact_proof_budget.rs`, with exact commit, CPU, OS, Rust toolchain, build flags, timing sample count, and cold/warm procedure; - side-by-side buffered O(n²), allocation-free two-pass O(n²), and O(n) kernel evidence, with exact equality of restored pair-square numerators before timing; +- timing for the viable hybrid shape—O(n) sufficient admission followed by pairwise fallback—covering both an admitting geometry and a checked-intermediate refusal geometry; - target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence rather than byte estimates inferred from field widths; - admitted/refused-set comparison between the existing pairwise proof and any stronger sufficient O(n) dyadic-grid proof, with proof refusal falling back rather than altering scientific meaning; -- checked-`u128` overflow/refusal evidence across sample count and represented exponent spread; +- checked-`u128` overflow/refusal evidence across sample count, represented exponent spread, and coefficient distribution; - a wider-integer/reference alternative assessment kept separate from production authority unless its dependency/security/performance cost is explicitly accepted; - full service/API p95 when a buyer-facing path is affected, preserving the TEPP `p95 <= 20 ms` target without shrinking input, omitting proof work, or using an unrealistic cache-only setup. -The exact pair-record count is `n(n-1)/2`; the current characterization locks 120 records at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and 4,997,541 at `n=3,162`. A two-pass O(n²) implementation may remove pair-record storage while preserving pair-enumeration proof shape, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its checked arithmetic fits `u128`. +The exact pair-record count is `n(n-1)/2`; the current characterization locks 120 records at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and 4,997,541 at `n=3,162`. A two-pass O(n²) implementation may remove pair-record storage while preserving pair-enumeration proof shape, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its checked arithmetic fits `u128`, and a checked O(n) refusal is not allowed to narrow the current exact pairwise admission set. ## Model release/cutover From c6b237e0bb1388cccd7bcb71a0df5cbf837a07c5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 05:01:02 +0900 Subject: [PATCH 475/576] test(validation): measure hybrid exact-proof fallback --- .../examples/bias_se_exact_proof_budget.rs | 146 ++++++++++++++---- 1 file changed, 114 insertions(+), 32 deletions(-) diff --git a/crates/validation_core/examples/bias_se_exact_proof_budget.rs b/crates/validation_core/examples/bias_se_exact_proof_budget.rs index 3d29d4cbf..3de9fe93a 100644 --- a/crates/validation_core/examples/bias_se_exact_proof_budget.rs +++ b/crates/validation_core/examples/bias_se_exact_proof_budget.rs @@ -1,10 +1,12 @@ //! Reproducible integer-kernel timing and layout harness for bias-SE exact-proof budgeting. //! -//! This example compares three checked-integer kernels on deterministic compact -//! dyadic coefficients: a production-layout-shaped buffered O(n²) pair proof, an -//! allocation-free two-pass O(n²) variant, and an algebraically equivalent O(n) -//! accumulator. It is characterization tooling, not production admission and not -//! buyer-path latency evidence by itself. +//! This example compares checked-integer proof kernels on deterministic dyadic +//! coefficients: a production-layout-shaped buffered O(n²) pair proof, an +//! allocation-free two-pass O(n²) variant, an algebraically equivalent O(n) +//! sufficient accumulator, and the viable hybrid shape that uses the O(n) path +//! only when it admits and otherwise falls back to the buffered pair proof. +//! It is characterization tooling, not production admission and not buyer-path +//! latency evidence by itself. use std::hint::black_box; use std::mem::size_of; @@ -16,6 +18,7 @@ struct KernelObservation { unit_exponent: i32, scratch_records: usize, scratch_payload_bytes: usize, + used_pairwise_fallback: bool, } fn fixture(sample_count: usize) -> Vec { @@ -27,6 +30,13 @@ fn fixture(sample_count: usize) -> Vec { .collect() } +fn boundary_fixture(sample_count: usize, diameter: u128) -> Vec { + let mut values = Vec::with_capacity(sample_count); + values.push(0); + values.extend((1..sample_count).map(|_| diameter)); + values +} + fn multiply_by_power_of_two(value: u128, shift: u32) -> Option { let factor = 1_u128.checked_shl(shift)?; value.checked_mul(factor) @@ -77,6 +87,7 @@ fn pair_square_sum_quadratic_buffered(values: &[u128]) -> Option Option Option Option Option { unit_exponent: 0, scratch_records: 0, scratch_payload_bytes: 0, + used_pairwise_fallback: false, }) } +fn pair_square_sum_hybrid(values: &[u128]) -> Option { + if let Some(observation) = pair_square_sum_linear(values) { + return Some(observation); + } + let mut observation = pair_square_sum_quadratic_buffered(values)?; + observation.used_pairwise_fallback = true; + Some(observation) +} + fn restored_pair_square_sum(observation: KernelObservation) -> Option { let shift = observation.unit_exponent.checked_mul(2)?.unsigned_abs(); multiply_by_power_of_two(observation.aligned_pair_square_sum, shift) @@ -170,6 +194,7 @@ fn measure( } fn emit( + geometry: &str, sample_count: usize, kernel_name: &str, p95: Duration, @@ -177,12 +202,13 @@ fn emit( observation: KernelObservation, ) { println!( - "{sample_count},{kernel_name},{},{samples},{},{},{},{}", + "{geometry},{sample_count},{kernel_name},{},{samples},{},{},{},{},{}", p95.as_nanos(), observation.unit_exponent, observation.scratch_records, observation.scratch_payload_bytes, - size_of::>() + size_of::>(), + observation.used_pairwise_fallback ); } @@ -194,7 +220,7 @@ fn main() { .max(1); println!( - "sample_count,kernel,p95_ns,timing_samples,unit_exponent,scratch_records,scratch_payload_bytes,pair_record_size_bytes" + "geometry,sample_count,kernel,p95_ns,timing_samples,unit_exponent,scratch_records,scratch_payload_bytes,pair_record_size_bytes,used_pairwise_fallback" ); for sample_count in [16_usize, 64, 256, 1_024, 2_047] { let values = fixture(sample_count); @@ -203,6 +229,7 @@ fn main() { let two_pass = pair_square_sum_quadratic_two_pass(&values) .expect("two-pass quadratic result stays within u128"); let linear = pair_square_sum_linear(&values).expect("linear result stays within u128"); + let hybrid = pair_square_sum_hybrid(&values).expect("hybrid result stays within u128"); let exact_pair_square_sum = restored_pair_square_sum(buffered) .expect("buffered result restores to exact pair-square sum"); assert_eq!( @@ -215,32 +242,87 @@ fn main() { Some(exact_pair_square_sum), "linear identity must agree with pair reference" ); - - let (buffered_p95, buffered_observation) = - measure(&values, samples, pair_square_sum_quadratic_buffered); - let (two_pass_p95, two_pass_observation) = - measure(&values, samples, pair_square_sum_quadratic_two_pass); - let (linear_p95, linear_observation) = measure(&values, samples, pair_square_sum_linear); - emit( - sample_count, - "quadratic_buffered", - buffered_p95, - samples, - buffered_observation, + assert_eq!( + restored_pair_square_sum(hybrid), + Some(exact_pair_square_sum), + "hybrid fast path must agree with pair reference" + ); + assert!( + !hybrid.used_pairwise_fallback, + "compact fixture is an admitting geometry for the linear fast path" ); - emit( - sample_count, - "quadratic_two_pass", - two_pass_p95, - samples, - two_pass_observation, + + for (kernel_name, kernel) in [ + ( + "quadratic_buffered", + pair_square_sum_quadratic_buffered as fn(&[u128]) -> Option, + ), + ("quadratic_two_pass", pair_square_sum_quadratic_two_pass), + ("linear", pair_square_sum_linear), + ("hybrid", pair_square_sum_hybrid), + ] { + let (p95, observation) = measure(&values, samples, kernel); + emit("compact_admit", sample_count, kernel_name, p95, samples, observation); + } + } + + let diameter = 1_u128 << 58; + for sample_count in [64_usize, 65] { + let values = boundary_fixture(sample_count, diameter); + let buffered = pair_square_sum_quadratic_buffered(&values) + .expect("boundary pair numerator stays within u128"); + let two_pass = pair_square_sum_quadratic_two_pass(&values) + .expect("boundary two-pass numerator stays within u128"); + let hybrid = pair_square_sum_hybrid(&values) + .expect("hybrid preserves pair fallback for the boundary geometry"); + let exact_pair_square_sum = restored_pair_square_sum(buffered) + .expect("boundary buffered result restores to exact pair-square sum"); + assert_eq!( + restored_pair_square_sum(two_pass), + Some(exact_pair_square_sum), + "boundary quadratic kernels must agree" ); - emit( - sample_count, - "linear", - linear_p95, - samples, - linear_observation, + assert_eq!( + restored_pair_square_sum(hybrid), + Some(exact_pair_square_sum), + "hybrid must preserve exact pair numerator" ); + + let geometry = if sample_count == 64 { + let linear = pair_square_sum_linear(&values) + .expect("n=64 remains an admitting geometry for the linear fast path"); + assert_eq!( + restored_pair_square_sum(linear), + Some(exact_pair_square_sum), + "n=64 linear boundary result must equal the pair reference" + ); + assert!( + !hybrid.used_pairwise_fallback, + "n=64 hybrid must use the linear fast path" + ); + "boundary_admit" + } else { + assert!( + pair_square_sum_linear(&values).is_none(), + "n=65 must exercise checked-intermediate refusal" + ); + assert!( + hybrid.used_pairwise_fallback, + "n=65 hybrid must preserve the buffered pair fallback" + ); + "boundary_pair_fallback" + }; + + for (kernel_name, kernel) in [ + ( + "quadratic_buffered", + pair_square_sum_quadratic_buffered as fn(&[u128]) -> Option, + ), + ("quadratic_two_pass", pair_square_sum_quadratic_two_pass), + ("hybrid", pair_square_sum_hybrid), + ] { + let (p95, observation) = measure(&values, samples, kernel); + emit(geometry, sample_count, kernel_name, p95, samples, observation); + } } } From 457654593391d09f4e3dc551ce75dc856ad1964f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 05:01:29 +0900 Subject: [PATCH 476/576] docs(validation): record hybrid exact-proof measurement --- .../validation-bias-exact-proof-budget-characterization.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md index 318bc1a98..03b77619e 100644 --- a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md +++ b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md @@ -3,5 +3,5 @@ - Correct the pair-record resource evidence for 3,162 observations to 4,997,541 records and lock exact pair counts in a Rust characterization contract. - Distinguish the current minimum-shifted O(n) `u128` intermediate envelope (`n=2,047` at aligned diameter `2^53`) from the wider exact pair-square numerator envelope (`n=4,095` for the same diameter); neither is a production sample-count budget. - Prove the checked O(n) accumulator is a sufficient but not admission-equivalent replacement for the pair reference: with one zero and the remaining coefficients at `D=2^58`, both kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits (`2^122`) but `n*sum(c_i^2)` overflows `u128` before cancellation. -- Extend the release-mode characterization harness to compare the buffered O(n²) pair layout, an allocation-free two-pass O(n²) reference, and the O(n) algebraic accumulator while reporting target-specific pair-record size, scratch-record capacity, and scratch payload bytes. -- Keep production `bias_standard_error` admission unchanged at `n=4..=16`; any O(n) production path must preserve current admission with pairwise fallback or wider checked integers and still requires actual release-mode CPU/allocation results, exact-head CI, and applicable buyer-path p95 evidence. +- Extend the release-mode characterization harness to compare the buffered O(n²) pair layout, an allocation-free two-pass O(n²) reference, the O(n) algebraic accumulator, and the viable hybrid `O(n) -> buffered pair fallback` shape. The harness now times both an admitting `n=64` boundary geometry and the checked-intermediate-refusing `n=65` geometry, asserts exact restored-numerator equality before timing, reports whether the hybrid actually used the pairwise fallback, and retains target-specific pair-record size, scratch-record capacity, and scratch payload bytes. +- Keep production `bias_standard_error` admission unchanged at `n=4..=16`; the new hybrid is measurement tooling only. A production change still requires recorded release-mode CPU/allocation/RSS results, a wider-reference assessment, exact-head CI/review evidence, and applicable buyer-path p95 evidence. From c077b30a37900dd40ea994d1c3bba2195bbfea83 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 05:02:03 +0900 Subject: [PATCH 477/576] docs(research): trace hybrid exact-proof budget --- ...-bias-standard-error-exact-proof-budget.md | 27 ++++++++++--------- 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/docs/research/validation-bias-standard-error-exact-proof-budget.md b/docs/research/validation-bias-standard-error-exact-proof-budget.md index ed427cb26..4f82bf4de 100644 --- a/docs/research/validation-bias-standard-error-exact-proof-budget.md +++ b/docs/research/validation-bias-standard-error-exact-proof-budget.md @@ -32,7 +32,7 @@ The characterization test computes both sides with checked `u128` on the sevente That admission proof is the material constraint. A prospective linear path may choose the minimum represented residual as an anchor, prove every anchor-relative subtraction exact, align all offsets on the minimum dyadic exponent, and require its checked integer intermediates to remain representable. This condition is deliberately stronger than the current O(n²) reference unless a separate proof establishes equivalence; refusal must fall back rather than change scientific meaning. -The new admission-set characterization proves that the present `u128` linear kernel is **sufficient but not admission-equivalent** to the pair reference. Let `D=2^58` and use one coefficient at zero with every remaining coefficient at `D`. At `n=64`, the linear first term is `64*63*D^2 = 4032*2^116`, so both kernels fit and return the same exact numerator `63*D^2`. At `n=65`, the exact pair numerator is still only `64*D^2 = 2^122`, but the unreduced linear first term becomes `65*64*D^2 = 4160*2^116`, which exceeds the `u128` range before subtracting `(sum c_i)^2`. The checked O(n) kernel therefore refuses a geometry that the checked O(n²) pair reference can still prove exactly. +The admission-set characterization proves that the present `u128` linear kernel is **sufficient but not admission-equivalent** to the pair reference. Let `D=2^58` and use one coefficient at zero with every remaining coefficient at `D`. At `n=64`, the linear first term is `64*63*D^2 = 4032*2^116`, so both kernels fit and return the same exact numerator `63*D^2`. At `n=65`, the exact pair numerator is still only `64*D^2 = 2^122`, but the unreduced linear first term becomes `65*64*D^2 = 4160*2^116`, which exceeds the `u128` range before subtracting `(sum c_i)^2`. The checked O(n) kernel therefore refuses a geometry that the checked O(n²) pair reference can still prove exactly. This rules out a drop-in replacement of the current pair proof with the minimum-shifted `u128` identity. A production O(n) path can preserve current scientific admission only as a sufficient fast path followed by the existing pairwise proof on refusal, or by adopting a wider checked-integer representation with its own resource and supply-chain evidence. A refusal from the linear path is not evidence that the represented-input estimator is scientifically unprovable. @@ -52,31 +52,34 @@ None of these arithmetic thresholds is a production sample-count budget. Product The current bounded O(n²) implementation stores every pair as `Option<(u128, i32)>` before the second accumulation pass. The number of stored pair records is exactly `n(n-1)/2`: 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and **4,997,541** at `n=3,162`. The predecessor note incorrectly recorded the last count as 4,997,500; the Rust characterization now locks the exact integer count. -Exact byte cost is target-layout dependent and must not be inferred by adding field widths. The measurement harness now obtains `size_of::>()` on the executing target, records the actual `Vec` element capacity after `with_capacity`, and reports their product as scratch payload bytes. Allocator bookkeeping and whole-process RSS are still outside that number and must be recorded separately if they become release evidence. +Exact byte cost is target-layout dependent and must not be inferred by adding field widths. The measurement harness obtains `size_of::>()` on the executing target, records the actual `Vec` element capacity after `with_capacity`, and reports their product as scratch payload bytes. Allocator bookkeeping and whole-process RSS are still outside that number and must be recorded separately if they become release evidence. -A two-pass O(n²) reference can remove the pair-record allocation without changing the pair-enumeration proof shape: the first pass establishes the common dyadic unit, and the second recomputes each pair record and accumulates the checked square. The harness now measures this allocation-free quadratic alternative alongside the buffered pair layout. The O(n) identity removes pair enumeration as well, but the new admission-set characterization shows that its present checked-`u128` form must remain a sufficient fast path with pairwise fallback unless wider intermediates are justified. +A two-pass O(n²) reference can remove the pair-record allocation without changing the pair-enumeration proof shape: the first pass establishes the common dyadic unit, and the second recomputes each pair record and accumulates the checked square. The harness measures this allocation-free quadratic alternative alongside the buffered pair layout. The O(n) identity removes pair enumeration as well, but the admission-set characterization shows that its present checked-`u128` form must remain a sufficient fast path with pairwise fallback unless wider intermediates are justified. ## Measurement harness -`crates/validation_core/examples/bias_se_exact_proof_budget.rs` is a standard-library-only release-mode characterization harness. For deterministic compact-dyadic coefficients it compares three kernels: +`crates/validation_core/examples/bias_se_exact_proof_budget.rs` is a standard-library-only release-mode characterization harness. It now compares four kernels: - `quadratic_buffered`: production-layout-shaped `Vec>` pair records plus aligned checked-square accumulation; - `quadratic_two_pass`: the same pair enumeration and dyadic alignment without pair-record storage; -- `linear`: `n*sum(c_i^2) - (sum c_i)^2` on minimum-shifted coefficients. +- `linear`: `n*sum(c_i^2) - (sum c_i)^2` on minimum-shifted coefficients; +- `hybrid`: the viable resource shape, using the checked O(n) accumulator when it admits and otherwise falling back to the production-layout-shaped buffered pair proof. -Before timing, the harness restores the buffered/two-pass unit exponent and requires all three kernels to equal the same exact pair-square numerator on the timed fixtures. It emits CSV columns +Before timing, the harness restores the dyadic unit and requires every applicable kernel to equal the same exact pair-square numerator. The compact deterministic fixtures at 16, 64, 256, 1,024, and 2,047 observations must keep the hybrid on its O(n) fast path. A separate `D=2^58` boundary pair measures both sides of the characterized admission relation: `n=64` must keep the hybrid on the linear path, while `n=65` must make the linear kernel refuse and the hybrid use the buffered pair fallback while preserving the exact numerator `2^122`. -`sample_count,kernel,p95_ns,timing_samples,unit_exponent,scratch_records,scratch_payload_bytes,pair_record_size_bytes` +The CSV schema is now -for 16, 64, 256, 1,024, and 2,047 observations. The buffered timing includes pair-vector allocation and consumption; the two-pass and linear rows report zero pair-record scratch payload. This is still a kernel harness, not the full public API: binary64 residual admission, endpoint serialization, networking, scheduler effects, allocator metadata/RSS, and the linear-refusal/pair-fallback hybrid are outside the current measurement. +`geometry,sample_count,kernel,p95_ns,timing_samples,unit_exponent,scratch_records,scratch_payload_bytes,pair_record_size_bytes,used_pairwise_fallback`. -No release-mode timing result is recorded in this document yet. A valid timing record must include CPU, OS, Rust toolchain, exact commit, release build mode, raw sample count, and raw CSV. It cannot substitute for an applicable API buyer-path p95 measurement. +The `geometry` field distinguishes compact admitting fixtures, the `n=64` boundary-admitting fixture, and the `n=65` pair-fallback fixture. `used_pairwise_fallback` makes it observable whether a hybrid row actually exercised the expensive proof path instead of inferring that fact from sample count. The buffered and hybrid-fallback rows include pair-vector allocation and consumption; the two-pass and admitted linear/hybrid rows report zero pair-record scratch payload. + +The hybrid harness landed in commit `c6b237e0bb1388cccd7bcb71a0df5cbf837a07c5`. It remains measurement tooling only. No release-mode timing result is recorded in this document yet because the current execution environment does not provide a Rust toolchain and the hosted exact-head jobs have not produced measurement artifacts. A valid timing record must include CPU, OS, Rust toolchain, exact commit, release build mode, raw sample count, raw CSV, allocator/RSS evidence, and the cold/warm procedure. Kernel timing cannot substitute for an applicable API buyer-path p95 measurement. ## Decision and rejected alternatives Production admission stays `n=4..=16`. Extending to `n=17` alone is rejected because GAP-111 through GAP-125 plus the seventeen-observation evidence show that the integer cutoff is not a scientific boundary. Removing the cutoff entirely is rejected because the current production implementation still enumerates and stores O(n²) pair evidence. Treating `n<=2_047`, `n<=4_095`, or the unreduced denominator threshold as the production budget is rejected because arithmetic representability is not latency or memory evidence. -A two-pass O(n²) allocation-removal path remains a candidate because it can preserve the current pair-proof admission shape while eliminating pair-record storage. The O(n) identity remains the stronger CPU-scaling candidate, but its checked-`u128` form is now proven to be a strict sufficient subset of the pair reference. Replacing the pair proof with that kernel alone is rejected because it would silently narrow exact-proof admission. The viable bounded designs are therefore O(n) fast admission with O(n²) fallback, an admission-equivalent wider-integer O(n) proof, or the allocation-free two-pass pair reference if measurements show it is adequate. Arbitrary-precision production arithmetic remains deferred pending measured benefit, supply-chain review, and an explicit owner/resource decision. +A two-pass O(n²) allocation-removal path remains a candidate because it can preserve the current pair-proof admission shape while eliminating pair-record storage. The O(n) identity remains the stronger CPU-scaling candidate, but its checked-`u128` form is proven to be a strict sufficient subset of the pair reference. Replacing the pair proof with that kernel alone is rejected because it would silently narrow exact-proof admission. The hybrid harness now measures the viable bounded shape—O(n) fast admission with buffered O(n²) fallback—without making it production behavior. An admission-equivalent wider-integer O(n) proof or the allocation-free two-pass pair reference remain alternatives if measurements justify them. Arbitrary-precision production arithmetic remains deferred pending measured benefit, supply-chain review, and an explicit owner/resource decision. ## Traceability @@ -88,10 +91,10 @@ A two-pass O(n²) allocation-removal path remains a candidate because it can pre | Current production module | `crates/validation_core/src/bias_se.rs` | | Public API | `validation_core::bias_standard_error` | | Exact characterization | `crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` | -| CPU/layout harness | `crates/validation_core/examples/bias_se_exact_proof_budget.rs` | +| CPU/layout/hybrid harness | `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; hybrid commit `c6b237e0bb1388cccd7bcb71a0df5cbf837a07c5` | | CHANGELOG evidence | `CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md` | | Resource/merge rule | Production cutoff remains `n<=16` pending measured exact-proof budget | ## Follow-up evidence required by #491 -Run the harness in release mode on a recorded CPU/toolchain and retain raw timing CSV. Record allocator/RSS evidence in addition to the harness's exact pair-record payload layout. Extend the harness with the viable hybrid shape—O(n) sufficient admission followed by the existing pair reference on checked-intermediate refusal—and measure both admitting and refusing geometries. Evaluate a wider-integer/reference alternative without making it production authority. Only after those results establish a resource budget should production admission change; any such change needs a realistic public RED, exact-head Rust/rustdoc/coverage evidence, and applicable buyer-path p95 evidence. +Run the current hybrid-capable harness in release mode on a recorded CPU/toolchain and retain raw timing CSV. Record allocator/RSS evidence in addition to the harness's exact pair-record payload layout. Evaluate a wider-integer/reference alternative without making it production authority. Only after those results establish a resource budget should production admission change; any such change needs a realistic public RED, exact-head Rust/rustdoc/coverage evidence, and applicable buyer-path p95 evidence. From df8f46b8e39142f2d7218e2ce4ad515aa0c67072 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 05:02:49 +0900 Subject: [PATCH 478/576] docs(test): require executable hybrid proof budget evidence --- docs/TEST_STRATEGY.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index b20749095..684de0491 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -69,13 +69,14 @@ Validation Evidence numerical proofs that add asymptotic work or material alloca - retain realistic represented-input counterexamples and permutation/sign-mirror contracts for scientific correctness; - keep the existing exact residual/pairwise-subtraction, checked-integer, GCD-reduction, and exact midpoint authority fail-closed; -- compare three resource shapes before widening admission: the buffered O(n²) pair-record path, an allocation-free two-pass O(n²) reference, and an algebraically equivalent O(n) exact accumulator under a proved sufficient admission condition; compare a wider-integer/reference alternative separately; +- compare the buffered O(n²) pair-record path, an allocation-free two-pass O(n²) reference, an algebraically equivalent O(n) exact accumulator under a proved sufficient admission condition, and the viable O(n)-fast-path/buffered-pair-fallback hybrid; compare a wider-integer/reference alternative separately; - require an admitted/refused-set contract for any O(n) candidate. The current checked-`u128` minimum-shifted kernel is a strict sufficient subset, not a drop-in equivalent: with one coefficient at zero and the rest at `D=2^58`, both pair and linear kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits (`2^122`) but `n*sum(c_i^2)` overflows before cancellation; - therefore an O(n) production optimization must either fall back to the current pairwise proof on checked-intermediate refusal or use a separately justified wider checked-integer representation; linear refusal must never silently narrow scientific admission; - characterize checked-`u128` refusal as a function of sample count, aligned dyadic diameter/exponent spread, and coefficient distribution rather than treating an integer cutoff as a scientific boundary; - keep the minimum-shifted O(n) distribution-independent intermediate envelope distinct from the exact pair-square numerator envelope: at aligned diameter `2^53`, the characterized sufficient bounds are `n<=2_047` and `n<=4_095` respectively, and neither is a production budget; - record exact pair counts, target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence separately; field-width estimates alone are not allocation evidence; -- run `crates/validation_core/examples/bias_se_exact_proof_budget.rs` in release mode on a recorded CPU/OS/Rust toolchain and retain raw CSV plus p95; extend measurement to a linear-fast-path/pair-fallback hybrid with both admitting and refusing geometries before using O(n) as production resource evidence; +- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing and to exercise both the `n=64` linear-admitting boundary and the `n=65` checked-intermediate-refusal/pair-fallback boundary. The CSV must identify the geometry and whether hybrid fallback was actually used; +- run that harness in release mode on a recorded CPU/OS/Rust toolchain and retain raw CSV plus p95; unexecuted harness code is not performance evidence; - if a service/API buyer path is affected, measure the full applicable path and retain the `p95 <= 20 ms` target without shrinking samples, omitting proof work, or relying on unrealistic warm-cache setup; - arithmetic representability alone does not authorize a production sample-count budget. From cd0f9a7b7710fc7965588017a71286bd6cdc96a5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 05:03:18 +0900 Subject: [PATCH 479/576] docs(operability): make hybrid proof budget observable --- docs/OPERABILITY.md | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index 037791cec..bf4af3472 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -64,13 +64,15 @@ Issue #491 owns the current bias-standard-error exact-proof budget. Production e The current characterization distinguishes three bounds that must not be collapsed into one cutoff. At aligned diameter `D=2^53`, the minimum-shifted O(n) distribution-independent intermediate envelope `n^2D^2` fits `u128` through `n=2_047`, while the exact pair-square numerator extremal bound `floor(n^2/4)D^2` fits through `n=4_095`. Separately, the unreduced scientific denominator `n^2(n-1)` exceeds `2^53` after `n=208_064`; production uses the reduced denominator after GCD, so that threshold is only an envelope marker. None of these values is a latency or memory budget, and the actual linear checked-intermediate admission also depends on coefficient distribution. -The candidate O(n) accumulator is now characterized as a strict sufficient subset of the current pair reference under `u128`. With one coefficient at zero and every other coefficient at `D=2^58`, both kernels fit at `n=64`. At `n=65`, the exact pair numerator remains `64*D^2 = 2^122`, while the linear first term `65*64*D^2 = 4160*2^116` overflows `u128` before cancellation. A linear refusal therefore cannot become a scientific refusal. If the O(n) kernel is introduced, it must fall back to the existing pairwise proof or use a separately justified wider checked-integer representation. +The candidate O(n) accumulator is characterized as a strict sufficient subset of the current pair reference under `u128`. With one coefficient at zero and every other coefficient at `D=2^58`, both kernels fit at `n=64`. At `n=65`, the exact pair numerator remains `64*D^2 = 2^122`, while the linear first term `65*64*D^2 = 4160*2^116` overflows `u128` before cancellation. A linear refusal therefore cannot become a scientific refusal. If the O(n) kernel is introduced, it must fall back to the existing pairwise proof or use a separately justified wider checked-integer representation. + +The current release-mode harness now encodes that viable hybrid shape without changing production behavior. `crates/validation_core/examples/bias_se_exact_proof_budget.rs` compares buffered O(n²), allocation-free two-pass O(n²), checked O(n), and `O(n) -> buffered pair fallback`. Before timing it requires exact restored-numerator equality. It exercises compact admitting geometries plus the `D=2^58` boundary pair where `n=64` must use the linear fast path and `n=65` must use the buffered pair fallback. CSV output records the geometry and `used_pairwise_fallback`, so an allegedly measured fallback cannot be inferred from sample count alone. Before changing the production boundary, retain: - release-mode raw timing samples and p95 from `crates/validation_core/examples/bias_se_exact_proof_budget.rs`, with exact commit, CPU, OS, Rust toolchain, build flags, timing sample count, and cold/warm procedure; -- side-by-side buffered O(n²), allocation-free two-pass O(n²), and O(n) kernel evidence, with exact equality of restored pair-square numerators before timing; -- timing for the viable hybrid shape—O(n) sufficient admission followed by pairwise fallback—covering both an admitting geometry and a checked-intermediate refusal geometry; +- side-by-side buffered O(n²), allocation-free two-pass O(n²), O(n), and hybrid evidence, with exact equality of restored pair-square numerators before timing; +- hybrid timing covering both the `n=64` admitting boundary and the `n=65` checked-intermediate-refusal/pair-fallback boundary, with `used_pairwise_fallback` recorded in the raw CSV; - target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence rather than byte estimates inferred from field widths; - admitted/refused-set comparison between the existing pairwise proof and any stronger sufficient O(n) dyadic-grid proof, with proof refusal falling back rather than altering scientific meaning; - checked-`u128` overflow/refusal evidence across sample count, represented exponent spread, and coefficient distribution; @@ -79,6 +81,8 @@ Before changing the production boundary, retain: The exact pair-record count is `n(n-1)/2`; the current characterization locks 120 records at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and 4,997,541 at `n=3,162`. A two-pass O(n²) implementation may remove pair-record storage while preserving pair-enumeration proof shape, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its checked arithmetic fits `u128`, and a checked O(n) refusal is not allowed to narrow the current exact pairwise admission set. +No release-mode timing numbers are currently authoritative. The local automation environment for the hybrid commit did not provide a Rust toolchain, and hosted exact-head jobs had not produced a benchmark artifact at the time of this update. Unexecuted timing harnesses and branch-only resource characterization remain supporting evidence only. + ## Model release/cutover A model artifact is promoted only after convergence, posterior diagnostics, true-parameter/recovery benchmarks, invariance/fairness/language evidence, uncertainty/calibration, security/privacy, and reproducibility gates meet the versioned policy. Model-selection or LLM review disagreement can require human scientific review. From 4f1bd2c343cf2d54905a07c257a570a89dc575d3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 06:03:51 +0900 Subject: [PATCH 480/576] test(validation): expose missing dyadic normalization in proof budget --- ...error_exact_proof_budget_characterization.rs | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs index fd0df4772..51def8539 100644 --- a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs +++ b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs @@ -134,6 +134,23 @@ fn linear_checked_integer_kernel_matches_pair_reference_when_it_admits() { } } +#[test] +fn linear_checked_integer_kernel_must_normalize_a_common_power_of_two_unit() { + let diameter = 1_u128 << 58; + let mut values = Vec::with_capacity(65); + values.push(0); + values.extend((0..64).map(|_| diameter)); + + let pairwise = pair_square_sum_quadratic(&values) + .expect("common-power pair numerator stays within u128"); + assert_eq!(pairwise, 1_u128 << 122); + assert_eq!( + pair_square_sum_linear(&values), + Some(pairwise), + "a shared 2^58 dyadic unit must be removed before checked O(n) intermediates are judged" + ); +} + #[test] fn linear_checked_integer_kernel_is_not_admission_equivalent_to_pair_reference() { let diameter = 1_u128 << 58; From d423b57797b6f7f127e61e0679f9ee9841525c77 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 06:04:24 +0900 Subject: [PATCH 481/576] fix(validation): normalize dyadic unit in proof-budget kernel --- ...ror_exact_proof_budget_characterization.rs | 24 +++++++++++++------ 1 file changed, 17 insertions(+), 7 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs index 51def8539..ab25af845 100644 --- a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs +++ b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs @@ -43,16 +43,26 @@ fn pair_square_sum_quadratic(values: &[u128]) -> Option { fn pair_square_sum_linear(values: &[u128]) -> Option { let minimum = *values.iter().min()?; let sample_count = u128::try_from(values.len()).ok()?; + let common_shift = values + .iter() + .filter_map(|value| { + let coefficient = value.checked_sub(minimum)?; + (coefficient != 0).then_some(coefficient.trailing_zeros()) + }) + .min() + .unwrap_or(0); + let mut coefficient_sum = 0_u128; let mut square_sum = 0_u128; for value in values { - let coefficient = value.checked_sub(minimum)?; + let coefficient = value.checked_sub(minimum)? >> common_shift; coefficient_sum = coefficient_sum.checked_add(coefficient)?; square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; } - sample_count + let normalized_sum = sample_count .checked_mul(square_sum)? - .checked_sub(coefficient_sum.checked_mul(coefficient_sum)?) + .checked_sub(coefficient_sum.checked_mul(coefficient_sum)?)?; + normalized_sum.checked_shl(common_shift.checked_mul(2)?) } fn greatest_common_divisor(mut left: u128, mut right: u128) -> u128 { @@ -135,7 +145,7 @@ fn linear_checked_integer_kernel_matches_pair_reference_when_it_admits() { } #[test] -fn linear_checked_integer_kernel_must_normalize_a_common_power_of_two_unit() { +fn linear_checked_integer_kernel_normalizes_a_common_power_of_two_unit() { let diameter = 1_u128 << 58; let mut values = Vec::with_capacity(65); values.push(0); @@ -153,7 +163,7 @@ fn linear_checked_integer_kernel_must_normalize_a_common_power_of_two_unit() { #[test] fn linear_checked_integer_kernel_is_not_admission_equivalent_to_pair_reference() { - let diameter = 1_u128 << 58; + let diameter = (1_u128 << 58) + 1; let mut fits_both = Vec::with_capacity(64); fits_both.push(0); @@ -163,7 +173,7 @@ fn linear_checked_integer_kernel_is_not_admission_equivalent_to_pair_reference() assert_eq!( pair_square_sum_linear(&fits_both), Some(pairwise_64), - "n=64 remains inside the minimum-shifted linear intermediate budget" + "n=64 remains inside the normalized linear intermediate budget" ); let mut pair_only = Vec::with_capacity(65); @@ -178,7 +188,7 @@ fn linear_checked_integer_kernel_is_not_admission_equivalent_to_pair_reference() assert_eq!( pair_square_sum_linear(&pair_only), None, - "n*sum(c_i^2) overflows before cancellation even though the exact pair numerator fits" + "odd diameter prevents dyadic rescaling, so n*sum(c_i^2) overflows before cancellation while the exact pair numerator still fits" ); } From 4a3d988702593b2b8d59be6dcfb1601ca1a0d610 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 06:05:11 +0900 Subject: [PATCH 482/576] fix(validation): normalize hybrid proof-budget dyadic scale --- .../examples/bias_se_exact_proof_budget.rs | 199 +++++++++--------- 1 file changed, 98 insertions(+), 101 deletions(-) diff --git a/crates/validation_core/examples/bias_se_exact_proof_budget.rs b/crates/validation_core/examples/bias_se_exact_proof_budget.rs index 3de9fe93a..3bfb696ea 100644 --- a/crates/validation_core/examples/bias_se_exact_proof_budget.rs +++ b/crates/validation_core/examples/bias_se_exact_proof_budget.rs @@ -5,8 +5,11 @@ //! allocation-free two-pass O(n²) variant, an algebraically equivalent O(n) //! sufficient accumulator, and the viable hybrid shape that uses the O(n) path //! only when it admits and otherwise falls back to the buffered pair proof. -//! It is characterization tooling, not production admission and not buyer-path -//! latency evidence by itself. +//! The O(n) path first removes the shared power-of-two unit from anchor-relative +//! coefficients so checked-intermediate refusal is evaluated on the canonical +//! dyadic grid rather than on an arbitrary raw integer scale. It is +//! characterization tooling, not production admission and not buyer-path latency +//! evidence by itself. use std::hint::black_box; use std::mem::size_of; @@ -136,10 +139,19 @@ fn pair_square_sum_quadratic_two_pass(values: &[u128]) -> Option Option { let minimum = *values.iter().min()?; let sample_count = u128::try_from(values.len()).ok()?; + let common_shift = values + .iter() + .filter_map(|value| { + let coefficient = value.checked_sub(minimum)?; + (coefficient != 0).then_some(coefficient.trailing_zeros()) + }) + .min() + .unwrap_or(0); + let mut coefficient_sum = 0_u128; let mut square_sum = 0_u128; for value in values { - let coefficient = value.checked_sub(minimum)?; + let coefficient = value.checked_sub(minimum)? >> common_shift; coefficient_sum = coefficient_sum.checked_add(coefficient)?; square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; } @@ -148,7 +160,7 @@ fn pair_square_sum_linear(values: &[u128]) -> Option { .checked_sub(coefficient_sum.checked_mul(coefficient_sum)?)?; Some(KernelObservation { aligned_pair_square_sum: pair_square_sum, - unit_exponent: 0, + unit_exponent: i32::try_from(common_shift).ok()?, scratch_records: 0, scratch_payload_bytes: 0, used_pairwise_fallback: false, @@ -212,6 +224,68 @@ fn emit( ); } +fn assert_and_measure_geometry( + geometry: &str, + values: &[u128], + samples: usize, + expect_linear_admission: bool, + expect_hybrid_fallback: bool, +) { + let buffered = pair_square_sum_quadratic_buffered(values) + .expect("buffered quadratic result stays within u128"); + let two_pass = pair_square_sum_quadratic_two_pass(values) + .expect("two-pass quadratic result stays within u128"); + let hybrid = pair_square_sum_hybrid(values).expect("hybrid result stays within u128"); + let exact_pair_square_sum = restored_pair_square_sum(buffered) + .expect("buffered result restores to exact pair-square sum"); + assert_eq!( + restored_pair_square_sum(two_pass), + Some(exact_pair_square_sum), + "quadratic kernels must agree" + ); + assert_eq!( + restored_pair_square_sum(hybrid), + Some(exact_pair_square_sum), + "hybrid must preserve the exact pair numerator" + ); + assert_eq!(hybrid.used_pairwise_fallback, expect_hybrid_fallback); + + match pair_square_sum_linear(values) { + Some(linear) => { + assert!(expect_linear_admission, "linear admission was not expected"); + assert_eq!( + restored_pair_square_sum(linear), + Some(exact_pair_square_sum), + "linear identity must agree with pair reference" + ); + } + None => assert!(!expect_linear_admission, "linear refusal was not expected"), + } + + let mut kernels: Vec<( + &str, + fn(&[u128]) -> Option, + )> = vec![ + ("quadratic_buffered", pair_square_sum_quadratic_buffered), + ("quadratic_two_pass", pair_square_sum_quadratic_two_pass), + ("hybrid", pair_square_sum_hybrid), + ]; + if expect_linear_admission { + kernels.push(("linear", pair_square_sum_linear)); + } + for (kernel_name, kernel) in kernels { + let (p95, observation) = measure(values, samples, kernel); + emit( + geometry, + values.len(), + kernel_name, + p95, + samples, + observation, + ); + } +} + fn main() { let samples = std::env::args() .nth(1) @@ -224,105 +298,28 @@ fn main() { ); for sample_count in [16_usize, 64, 256, 1_024, 2_047] { let values = fixture(sample_count); - let buffered = pair_square_sum_quadratic_buffered(&values) - .expect("buffered quadratic result stays within u128"); - let two_pass = pair_square_sum_quadratic_two_pass(&values) - .expect("two-pass quadratic result stays within u128"); - let linear = pair_square_sum_linear(&values).expect("linear result stays within u128"); - let hybrid = pair_square_sum_hybrid(&values).expect("hybrid result stays within u128"); - let exact_pair_square_sum = restored_pair_square_sum(buffered) - .expect("buffered result restores to exact pair-square sum"); - assert_eq!( - restored_pair_square_sum(two_pass), - Some(exact_pair_square_sum), - "quadratic kernels must agree" - ); - assert_eq!( - restored_pair_square_sum(linear), - Some(exact_pair_square_sum), - "linear identity must agree with pair reference" - ); - assert_eq!( - restored_pair_square_sum(hybrid), - Some(exact_pair_square_sum), - "hybrid fast path must agree with pair reference" - ); - assert!( - !hybrid.used_pairwise_fallback, - "compact fixture is an admitting geometry for the linear fast path" - ); - - for (kernel_name, kernel) in [ - ( - "quadratic_buffered", - pair_square_sum_quadratic_buffered as fn(&[u128]) -> Option, - ), - ("quadratic_two_pass", pair_square_sum_quadratic_two_pass), - ("linear", pair_square_sum_linear), - ("hybrid", pair_square_sum_hybrid), - ] { - let (p95, observation) = measure(&values, samples, kernel); - emit("compact_admit", sample_count, kernel_name, p95, samples, observation); - } + assert_and_measure_geometry("compact_admit", &values, samples, true, false); } - let diameter = 1_u128 << 58; - for sample_count in [64_usize, 65] { - let values = boundary_fixture(sample_count, diameter); - let buffered = pair_square_sum_quadratic_buffered(&values) - .expect("boundary pair numerator stays within u128"); - let two_pass = pair_square_sum_quadratic_two_pass(&values) - .expect("boundary two-pass numerator stays within u128"); - let hybrid = pair_square_sum_hybrid(&values) - .expect("hybrid preserves pair fallback for the boundary geometry"); - let exact_pair_square_sum = restored_pair_square_sum(buffered) - .expect("boundary buffered result restores to exact pair-square sum"); - assert_eq!( - restored_pair_square_sum(two_pass), - Some(exact_pair_square_sum), - "boundary quadratic kernels must agree" - ); - assert_eq!( - restored_pair_square_sum(hybrid), - Some(exact_pair_square_sum), - "hybrid must preserve exact pair numerator" - ); + let power_of_two_values = boundary_fixture(65, 1_u128 << 58); + assert_and_measure_geometry( + "power_of_two_normalized_admit", + &power_of_two_values, + samples, + true, + false, + ); - let geometry = if sample_count == 64 { - let linear = pair_square_sum_linear(&values) - .expect("n=64 remains an admitting geometry for the linear fast path"); - assert_eq!( - restored_pair_square_sum(linear), - Some(exact_pair_square_sum), - "n=64 linear boundary result must equal the pair reference" - ); - assert!( - !hybrid.used_pairwise_fallback, - "n=64 hybrid must use the linear fast path" - ); - "boundary_admit" - } else { - assert!( - pair_square_sum_linear(&values).is_none(), - "n=65 must exercise checked-intermediate refusal" - ); - assert!( - hybrid.used_pairwise_fallback, - "n=65 hybrid must preserve the buffered pair fallback" - ); - "boundary_pair_fallback" - }; + let odd_diameter = (1_u128 << 58) + 1; + let odd_64 = boundary_fixture(64, odd_diameter); + assert_and_measure_geometry("odd_boundary_admit", &odd_64, samples, true, false); - for (kernel_name, kernel) in [ - ( - "quadratic_buffered", - pair_square_sum_quadratic_buffered as fn(&[u128]) -> Option, - ), - ("quadratic_two_pass", pair_square_sum_quadratic_two_pass), - ("hybrid", pair_square_sum_hybrid), - ] { - let (p95, observation) = measure(&values, samples, kernel); - emit(geometry, sample_count, kernel_name, p95, samples, observation); - } - } + let odd_65 = boundary_fixture(65, odd_diameter); + assert_and_measure_geometry( + "odd_boundary_pair_fallback", + &odd_65, + samples, + false, + true, + ); } From 08b8224e32eb2712214b5d710a269be972843b36 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 06:05:47 +0900 Subject: [PATCH 483/576] docs(changelog): correct proof-budget dyadic normalization evidence --- .../validation-bias-exact-proof-budget-characterization.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md index 03b77619e..4c95f5a4f 100644 --- a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md +++ b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md @@ -2,6 +2,7 @@ - Correct the pair-record resource evidence for 3,162 observations to 4,997,541 records and lock exact pair counts in a Rust characterization contract. - Distinguish the current minimum-shifted O(n) `u128` intermediate envelope (`n=2,047` at aligned diameter `2^53`) from the wider exact pair-square numerator envelope (`n=4,095` for the same diameter); neither is a production sample-count budget. -- Prove the checked O(n) accumulator is a sufficient but not admission-equivalent replacement for the pair reference: with one zero and the remaining coefficients at `D=2^58`, both kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits (`2^122`) but `n*sum(c_i^2)` overflows `u128` before cancellation. -- Extend the release-mode characterization harness to compare the buffered O(n²) pair layout, an allocation-free two-pass O(n²) reference, the O(n) algebraic accumulator, and the viable hybrid `O(n) -> buffered pair fallback` shape. The harness now times both an admitting `n=64` boundary geometry and the checked-intermediate-refusing `n=65` geometry, asserts exact restored-numerator equality before timing, reports whether the hybrid actually used the pairwise fallback, and retains target-specific pair-record size, scratch-record capacity, and scratch payload bytes. +- Normalize the shared power-of-two dyadic unit before judging checked O(n) intermediates. The former `D=2^58, n=65` refusal was a characterization artifact: factoring the common `2^58` unit reduces the aligned coefficients to zero/one and preserves the exact restored numerator `2^122` without pair fallback. +- Preserve the non-equivalence finding with a normalized counterexample rather than the raw-scale artifact. With one zero and the remaining coefficients at odd diameter `D=2^58+1`, the common dyadic unit is one; both kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits `u128` but the checked O(n) intermediates overflow before cancellation. +- Extend the release-mode characterization harness to compare the buffered O(n²) pair layout, an allocation-free two-pass O(n²) reference, the normalized O(n) accumulator, and the viable hybrid `O(n) -> buffered pair fallback` shape. It now measures a common-power normalization admission plus the odd-diameter `n=64` admission / `n=65` fallback boundary, asserts exact restored-numerator equality before timing, reports whether the hybrid actually used the pairwise fallback, and retains target-specific pair-record size, scratch-record capacity, and scratch payload bytes. - Keep production `bias_standard_error` admission unchanged at `n=4..=16`; the new hybrid is measurement tooling only. A production change still requires recorded release-mode CPU/allocation/RSS results, a wider-reference assessment, exact-head CI/review evidence, and applicable buyer-path p95 evidence. From 55bb8103353fd2b960a12f8d245efea23f3a2963 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 06:06:48 +0900 Subject: [PATCH 484/576] docs(research): correct normalized O(n) proof-budget evidence --- ...-bias-standard-error-exact-proof-budget.md | 57 ++++++++++++------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/docs/research/validation-bias-standard-error-exact-proof-budget.md b/docs/research/validation-bias-standard-error-exact-proof-budget.md index 4f82bf4de..21df7c6db 100644 --- a/docs/research/validation-bias-standard-error-exact-proof-budget.md +++ b/docs/research/validation-bias-standard-error-exact-proof-budget.md @@ -20,7 +20,7 @@ For `n=17`, the scientific denominator is `17^2(17-1)=4_624`. `gcd(N,4_624)=2`, The current public API intentionally remains on the established translated floating fallback at `n=17`; for this fixture it returns bits `0x41a0_dd77_9ac3_8e98`. Independent high-precision rational-square-root evaluation gives the adjacent correctly rounded binary64 target `0x41a0_dd77_9ac3_8e99`. This extends the demonstrated failure class beyond the current cutoff, but it is evidence for a systemic budget decision rather than justification for another one-count production patch. -`crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` fixes the exact pair-square sum, reduced ratio, current fallback boundary, algebraic equivalence, pair-record counts, distinct checked-integer envelopes, and the admitted/refused-set relation between the current pair reference and the candidate O(n) accumulator. The test intentionally makes any future widening of production admission update this characterization rather than silently inheriting a stale fallback assumption. +`crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` fixes the exact pair-square sum, reduced ratio, current fallback boundary, algebraic equivalence, pair-record counts, distinct checked-integer envelopes, dyadic-unit normalization, and the admitted/refused-set relation between the current pair reference and the candidate O(n) accumulator. The test intentionally makes any future widening of production admission update this characterization rather than silently inheriting a stale fallback assumption. ## O(n²) reference versus O(n) exact accumulator @@ -28,21 +28,31 @@ For exact dyadic coefficients `c_i` on one shared unit, `sum_{i>()` on the executing target, records the actual `Vec` element capacity after `with_capacity`, and reports their product as scratch payload bytes. Allocator bookkeeping and whole-process RSS are still outside that number and must be recorded separately if they become release evidence. -A two-pass O(n²) reference can remove the pair-record allocation without changing the pair-enumeration proof shape: the first pass establishes the common dyadic unit, and the second recomputes each pair record and accumulates the checked square. The harness measures this allocation-free quadratic alternative alongside the buffered pair layout. The O(n) identity removes pair enumeration as well, but the admission-set characterization shows that its present checked-`u128` form must remain a sufficient fast path with pairwise fallback unless wider intermediates are justified. +A two-pass O(n²) reference can remove the pair-record allocation without changing the pair-enumeration proof shape: the first pass establishes the common dyadic unit, and the second recomputes each pair record and accumulates the checked square. The harness measures this allocation-free quadratic alternative alongside the buffered pair layout. The normalized O(n) identity removes pair enumeration as well, but the admission-set characterization shows that its checked-`u128` form must remain a sufficient fast path with pairwise fallback unless wider intermediates are justified. ## Measurement harness -`crates/validation_core/examples/bias_se_exact_proof_budget.rs` is a standard-library-only release-mode characterization harness. It now compares four kernels: +`crates/validation_core/examples/bias_se_exact_proof_budget.rs` is a standard-library-only release-mode characterization harness. It compares four kernels: - `quadratic_buffered`: production-layout-shaped `Vec>` pair records plus aligned checked-square accumulation; - `quadratic_two_pass`: the same pair enumeration and dyadic alignment without pair-record storage; -- `linear`: `n*sum(c_i^2) - (sum c_i)^2` on minimum-shifted coefficients; -- `hybrid`: the viable resource shape, using the checked O(n) accumulator when it admits and otherwise falling back to the production-layout-shaped buffered pair proof. +- `linear`: minimum-anchor coefficients, normalized by their largest shared power-of-two unit, then `n*sum(c_i^2) - (sum c_i)^2`; +- `hybrid`: the viable resource shape, using the normalized checked O(n) accumulator when it admits and otherwise falling back to the production-layout-shaped buffered pair proof. + +Before timing, the harness restores the dyadic unit and requires every applicable kernel to equal the same exact pair-square numerator. Compact deterministic fixtures at 16, 64, 256, 1,024, and 2,047 observations exercise ordinary admitting geometry. Three boundary geometries separate normalization from true refusal: -Before timing, the harness restores the dyadic unit and requires every applicable kernel to equal the same exact pair-square numerator. The compact deterministic fixtures at 16, 64, 256, 1,024, and 2,047 observations must keep the hybrid on its O(n) fast path. A separate `D=2^58` boundary pair measures both sides of the characterized admission relation: `n=64` must keep the hybrid on the linear path, while `n=65` must make the linear kernel refuse and the hybrid use the buffered pair fallback while preserving the exact numerator `2^122`. +1. `power_of_two_normalized_admit`: `n=65`, `D=2^58`. The linear and hybrid paths must factor the common dyadic unit and admit; pair fallback here would reproduce the predecessor characterization defect. +2. `odd_boundary_admit`: `n=64`, `D=2^58+1`. With no removable common dyadic factor, normalized O(n) still fits. +3. `odd_boundary_pair_fallback`: `n=65`, `D=2^58+1`. The exact pair numerator fits, normalized O(n) checked intermediates refuse, and the hybrid must execute the buffered pair fallback. -The CSV schema is now +The CSV schema remains `geometry,sample_count,kernel,p95_ns,timing_samples,unit_exponent,scratch_records,scratch_payload_bytes,pair_record_size_bytes,used_pairwise_fallback`. -The `geometry` field distinguishes compact admitting fixtures, the `n=64` boundary-admitting fixture, and the `n=65` pair-fallback fixture. `used_pairwise_fallback` makes it observable whether a hybrid row actually exercised the expensive proof path instead of inferring that fact from sample count. The buffered and hybrid-fallback rows include pair-vector allocation and consumption; the two-pass and admitted linear/hybrid rows report zero pair-record scratch payload. +`unit_exponent` is material evidence in the corrected harness: it distinguishes the common-power geometry, where the linear path reports exponent 58 and a small aligned numerator, from the odd-diameter geometry, where the canonical unit exponent is zero. `used_pairwise_fallback` records whether a hybrid row actually exercised the expensive proof path instead of inferring that fact from sample count. -The hybrid harness landed in commit `c6b237e0bb1388cccd7bcb71a0df5cbf837a07c5`. It remains measurement tooling only. No release-mode timing result is recorded in this document yet because the current execution environment does not provide a Rust toolchain and the hosted exact-head jobs have not produced measurement artifacts. A valid timing record must include CPU, OS, Rust toolchain, exact commit, release build mode, raw sample count, raw CSV, allocator/RSS evidence, and the cold/warm procedure. Kernel timing cannot substitute for an applicable API buyer-path p95 measurement. +The original hybrid harness landed in `c6b237e0bb1388cccd7bcb71a0df5cbf837a07c5`; the dyadic-normalization correction is `4a3d988702593b2b8d59be6dcfb1601ca1a0d610`. It remains measurement tooling only. No release-mode timing result is recorded in this document yet because the current execution environment does not provide Rust 1.98.0 and the hosted exact-head jobs have not produced measurement artifacts. A valid timing record must include CPU, OS, Rust toolchain, exact commit, release build mode, raw sample count, raw CSV, allocator/RSS evidence, and the cold/warm procedure. Kernel timing cannot substitute for an applicable API buyer-path p95 measurement. ## Decision and rejected alternatives -Production admission stays `n=4..=16`. Extending to `n=17` alone is rejected because GAP-111 through GAP-125 plus the seventeen-observation evidence show that the integer cutoff is not a scientific boundary. Removing the cutoff entirely is rejected because the current production implementation still enumerates and stores O(n²) pair evidence. Treating `n<=2_047`, `n<=4_095`, or the unreduced denominator threshold as the production budget is rejected because arithmetic representability is not latency or memory evidence. +Production admission stays `n=4..=16`. Extending to `n=17` alone is rejected because GAP-111 through GAP-125 plus the seventeen-observation evidence show that the integer cutoff is not a scientific boundary. Removing the cutoff entirely is rejected because the current production implementation still enumerates and stores O(n²) pair evidence. + +The former `D=2^58, n=65` raw-scale refusal is explicitly rejected as admission evidence because it disappears under the same common-power dyadic normalization already required by the proposed O(n) proof. Retaining it would make the resource budget representation-dependent. The odd `D=2^58+1` boundary replaces it as the normalized refusal fixture. -A two-pass O(n²) allocation-removal path remains a candidate because it can preserve the current pair-proof admission shape while eliminating pair-record storage. The O(n) identity remains the stronger CPU-scaling candidate, but its checked-`u128` form is proven to be a strict sufficient subset of the pair reference. Replacing the pair proof with that kernel alone is rejected because it would silently narrow exact-proof admission. The hybrid harness now measures the viable bounded shape—O(n) fast admission with buffered O(n²) fallback—without making it production behavior. An admission-equivalent wider-integer O(n) proof or the allocation-free two-pass pair reference remain alternatives if measurements justify them. Arbitrary-precision production arithmetic remains deferred pending measured benefit, supply-chain review, and an explicit owner/resource decision. +Treating `n<=2_047`, `n<=4_095`, or the unreduced denominator threshold as the production budget is also rejected because arithmetic representability is not latency or memory evidence. A two-pass O(n²) allocation-removal path remains a candidate because it can preserve the current pair-proof admission shape while eliminating pair-record storage. The normalized O(n) identity remains the stronger CPU-scaling candidate, but its checked-`u128` form is still a strict sufficient subset of the pair reference. Replacing the pair proof with that kernel alone is rejected because it would silently narrow exact-proof admission. The hybrid harness measures the viable bounded shape—normalized O(n) fast admission with buffered O(n²) fallback—without making it production behavior. An admission-equivalent wider-integer O(n) proof or the allocation-free two-pass pair reference remain alternatives if measurements justify them. Arbitrary-precision production arithmetic remains deferred pending measured benefit, supply-chain review, and an explicit owner/resource decision. ## Traceability @@ -88,13 +104,16 @@ A two-pass O(n²) allocation-removal path remains a candidate because it can pre | Domain owner | TEPP Validation Evidence | | Systemic issue | #491 | | Predecessor scientific repair | GAP-125; RED `5da82b2d651706c191ca191c6c077d916cbfda25`; repair `a509ae9e46c8ffc2cc3ef4f0e904774ad2516e1f` | +| Characterization RED | `4f1bd2c343cf2d54905a07c257a570a89dc575d3` — common `2^58` scale must normalize before O(n) overflow admission | +| Characterization repair | `d423b57797b6f7f127e61e0679f9ee9841525c77` — normalized test kernel plus odd-diameter refusal fixture | +| Harness repair | `4a3d988702593b2b8d59be6dcfb1601ca1a0d610` — normalized O(n)/hybrid measurement geometries | | Current production module | `crates/validation_core/src/bias_se.rs` | | Public API | `validation_core::bias_standard_error` | | Exact characterization | `crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` | -| CPU/layout/hybrid harness | `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; hybrid commit `c6b237e0bb1388cccd7bcb71a0df5cbf837a07c5` | +| CPU/layout/hybrid harness | `crates/validation_core/examples/bias_se_exact_proof_budget.rs` | | CHANGELOG evidence | `CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md` | | Resource/merge rule | Production cutoff remains `n<=16` pending measured exact-proof budget | ## Follow-up evidence required by #491 -Run the current hybrid-capable harness in release mode on a recorded CPU/toolchain and retain raw timing CSV. Record allocator/RSS evidence in addition to the harness's exact pair-record payload layout. Evaluate a wider-integer/reference alternative without making it production authority. Only after those results establish a resource budget should production admission change; any such change needs a realistic public RED, exact-head Rust/rustdoc/coverage evidence, and applicable buyer-path p95 evidence. +Run the corrected hybrid-capable harness in release mode on a recorded CPU/toolchain and retain raw timing CSV. Record allocator/RSS evidence in addition to the harness's exact pair-record payload layout. Evaluate a wider-integer/reference alternative without making it production authority. Only after those results establish a resource budget should production admission change; any such change needs a realistic public RED, exact-head Rust/rustdoc/coverage evidence, and applicable buyer-path p95 evidence. From 736bc8ddaec067615bedc387939f4650517d9ea1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 06:08:14 +0900 Subject: [PATCH 485/576] refactor(validation): keep proof-budget harness clippy-clean --- .../examples/bias_se_exact_proof_budget.rs | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/crates/validation_core/examples/bias_se_exact_proof_budget.rs b/crates/validation_core/examples/bias_se_exact_proof_budget.rs index 3bfb696ea..851a705a3 100644 --- a/crates/validation_core/examples/bias_se_exact_proof_budget.rs +++ b/crates/validation_core/examples/bias_se_exact_proof_budget.rs @@ -24,6 +24,8 @@ struct KernelObservation { used_pairwise_fallback: bool, } +type Kernel = fn(&[u128]) -> Option; + fn fixture(sample_count: usize) -> Vec { (0..sample_count) .map(|index| { @@ -187,11 +189,7 @@ fn percentile_95(mut durations: Vec) -> Duration { durations[rank.saturating_sub(1)] } -fn measure( - values: &[u128], - samples: usize, - kernel: fn(&[u128]) -> Option, -) -> (Duration, KernelObservation) { +fn measure(values: &[u128], samples: usize, kernel: Kernel) -> (Duration, KernelObservation) { for _ in 0..3 { black_box(kernel(black_box(values)).expect("fixture must remain within u128")); } @@ -248,7 +246,10 @@ fn assert_and_measure_geometry( Some(exact_pair_square_sum), "hybrid must preserve the exact pair numerator" ); - assert_eq!(hybrid.used_pairwise_fallback, expect_hybrid_fallback); + assert!( + hybrid.used_pairwise_fallback == expect_hybrid_fallback, + "hybrid fallback observation must match the declared geometry" + ); match pair_square_sum_linear(values) { Some(linear) => { @@ -262,10 +263,7 @@ fn assert_and_measure_geometry( None => assert!(!expect_linear_admission, "linear refusal was not expected"), } - let mut kernels: Vec<( - &str, - fn(&[u128]) -> Option, - )> = vec![ + let mut kernels: Vec<(&str, Kernel)> = vec![ ("quadratic_buffered", pair_square_sum_quadratic_buffered), ("quadratic_two_pass", pair_square_sum_quadratic_two_pass), ("hybrid", pair_square_sum_hybrid), From 81708abeb9625e6aaac6ffd5bb675344cc216523 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 06:08:47 +0900 Subject: [PATCH 486/576] docs(test): require canonical dyadic proof-budget normalization --- docs/TEST_STRATEGY.md | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index 684de0491..a67bcc8fa 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -70,12 +70,14 @@ Validation Evidence numerical proofs that add asymptotic work or material alloca - retain realistic represented-input counterexamples and permutation/sign-mirror contracts for scientific correctness; - keep the existing exact residual/pairwise-subtraction, checked-integer, GCD-reduction, and exact midpoint authority fail-closed; - compare the buffered O(n²) pair-record path, an allocation-free two-pass O(n²) reference, an algebraically equivalent O(n) exact accumulator under a proved sufficient admission condition, and the viable O(n)-fast-path/buffered-pair-fallback hybrid; compare a wider-integer/reference alternative separately; -- require an admitted/refused-set contract for any O(n) candidate. The current checked-`u128` minimum-shifted kernel is a strict sufficient subset, not a drop-in equivalent: with one coefficient at zero and the rest at `D=2^58`, both pair and linear kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits (`2^122`) but `n*sum(c_i^2)` overflows before cancellation; +- normalize the largest common power-of-two dyadic unit from exact anchor-relative coefficients before checked O(n) intermediates are judged. Raw-scale overflow is not a scientific or resource refusal when exact dyadic rescaling removes it; +- require an admitted/refused-set contract for any O(n) candidate. The predecessor `D=2^58, n=65` refusal was invalid because canonical normalization reduces the coefficients to zero/one and preserves the restored pair numerator `2^122`. The corrected strict-subset boundary uses odd `D=2^58+1`: both pair and normalized linear kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits `u128` but the normalized O(n) intermediates require 129 bits before cancellation; - therefore an O(n) production optimization must either fall back to the current pairwise proof on checked-intermediate refusal or use a separately justified wider checked-integer representation; linear refusal must never silently narrow scientific admission; -- characterize checked-`u128` refusal as a function of sample count, aligned dyadic diameter/exponent spread, and coefficient distribution rather than treating an integer cutoff as a scientific boundary; -- keep the minimum-shifted O(n) distribution-independent intermediate envelope distinct from the exact pair-square numerator envelope: at aligned diameter `2^53`, the characterized sufficient bounds are `n<=2_047` and `n<=4_095` respectively, and neither is a production budget; +- characterize checked-`u128` refusal as a function of sample count, canonical aligned dyadic diameter/exponent spread, and coefficient distribution rather than treating an integer cutoff or raw represented scale as a scientific boundary; +- keep the normalized O(n) distribution-independent intermediate envelope distinct from the exact pair-square numerator envelope: at aligned coefficient diameter `2^53`, the characterized sufficient bounds are `n<=2_047` and `n<=4_095` respectively, and neither is a production budget; - record exact pair counts, target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence separately; field-width estimates alone are not allocation evidence; -- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing and to exercise both the `n=64` linear-admitting boundary and the `n=65` checked-intermediate-refusal/pair-fallback boundary. The CSV must identify the geometry and whether hybrid fallback was actually used; +- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing and to exercise three distinct boundary states: `D=2^58, n=65` must be a normalized linear admission, odd `D=2^58+1, n=64` must admit, and odd `D=2^58+1, n=65` must exercise checked-intermediate refusal plus the buffered pair fallback. The CSV must identify the geometry, normalized unit exponent, and whether hybrid fallback was actually used; +- keep RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3`, characterization repair `d423b57797b6f7f127e61e0679f9ee9841525c77`, and harness repair `4a3d988702593b2b8d59be6dcfb1601ca1a0d610` in the exact evidence lineage so representation-dependent refusal cannot re-enter the budget model; - run that harness in release mode on a recorded CPU/OS/Rust toolchain and retain raw CSV plus p95; unexecuted harness code is not performance evidence; - if a service/API buyer path is affected, measure the full applicable path and retain the `p95 <= 20 ms` target without shrinking samples, omitting proof work, or relying on unrealistic warm-cache setup; - arithmetic representability alone does not authorize a production sample-count budget. From ee3a0f865265c888788147b7eec2a240a5484f1e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 06:09:30 +0900 Subject: [PATCH 487/576] docs(operability): correct normalized proof-budget boundary --- docs/OPERABILITY.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index bf4af3472..44dc3d6b2 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -62,26 +62,28 @@ A numerical proof boundary is an operational resource contract when it changes a Issue #491 owns the current bias-standard-error exact-proof budget. Production exact pair-distance admission stays bounded to `n<=16` while the current implementation is O(n²) in pair enumeration and stores `n(n-1)/2` pair records. The characterization on PR #488 records a seventeen-observation counterexample and an algebraically equivalent O(n) checked-integer numerator, but arithmetic representability alone does not authorize a wider production budget. -The current characterization distinguishes three bounds that must not be collapsed into one cutoff. At aligned diameter `D=2^53`, the minimum-shifted O(n) distribution-independent intermediate envelope `n^2D^2` fits `u128` through `n=2_047`, while the exact pair-square numerator extremal bound `floor(n^2/4)D^2` fits through `n=4_095`. Separately, the unreduced scientific denominator `n^2(n-1)` exceeds `2^53` after `n=208_064`; production uses the reduced denominator after GCD, so that threshold is only an envelope marker. None of these values is a latency or memory budget, and the actual linear checked-intermediate admission also depends on coefficient distribution. +The current characterization distinguishes three bounds that must not be collapsed into one cutoff. For a canonical aligned coefficient diameter `D=2^53` **after removing the largest common power-of-two dyadic unit**, the normalized O(n) distribution-independent intermediate envelope `n^2D^2` fits `u128` through `n=2_047`, while the exact aligned pair-square numerator extremal bound `floor(n^2/4)D^2` fits through `n=4_095`. Separately, the unreduced scientific denominator `n^2(n-1)` exceeds `2^53` after `n=208_064`; production uses the reduced denominator after GCD, so that threshold is only an envelope marker. None of these values is a latency or memory budget. -The candidate O(n) accumulator is characterized as a strict sufficient subset of the current pair reference under `u128`. With one coefficient at zero and every other coefficient at `D=2^58`, both kernels fit at `n=64`. At `n=65`, the exact pair numerator remains `64*D^2 = 2^122`, while the linear first term `65*64*D^2 = 4160*2^116` overflows `u128` before cancellation. A linear refusal therefore cannot become a scientific refusal. If the O(n) kernel is introduced, it must fall back to the existing pairwise proof or use a separately justified wider checked-integer representation. +The shared dyadic unit is a proof obligation. The predecessor characterization judged the O(n) candidate on raw values with one zero and the rest at `D=2^58`, and therefore reported a refusal at `n=65`. That refusal was representation-dependent: canonical normalization divides every nonzero coefficient by the exact common unit `2^58`, leaving one zero and sixty-four ones. The normalized intermediates are only `4_160` and `4_096`; their difference `64` restores to the exact pair numerator `2^122`. RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3` fixes this requirement, and characterization repair `d423b57797b6f7f127e61e0679f9ee9841525c77` evaluates checked O(n) admission on the normalized dyadic grid. -The current release-mode harness now encodes that viable hybrid shape without changing production behavior. `crates/validation_core/examples/bias_se_exact_proof_budget.rs` compares buffered O(n²), allocation-free two-pass O(n²), checked O(n), and `O(n) -> buffered pair fallback`. Before timing it requires exact restored-numerator equality. It exercises compact admitting geometries plus the `D=2^58` boundary pair where `n=64` must use the linear fast path and `n=65` must use the buffered pair fallback. CSV output records the geometry and `used_pairwise_fallback`, so an allegedly measured fallback cannot be inferred from sample count alone. +The corrected O(n) accumulator is still a strict sufficient subset of the current pair reference under `u128`; the valid boundary uses odd diameter `D=2^58+1`, whose common dyadic unit is one. Both kernels fit at `n=64`. At `n=65`, the exact pair numerator `64D^2` is a 123-bit `u128`, while the first O(n) intermediate `65*64*D^2` requires 129 bits before cancellation. A normalized O(n) refusal therefore still cannot become a scientific refusal. If the O(n) kernel is introduced, it must fall back to the existing pairwise proof or use a separately justified wider checked-integer representation. + +Harness repair `4a3d988702593b2b8d59be6dcfb1601ca1a0d610` encodes that distinction without changing production behavior. `crates/validation_core/examples/bias_se_exact_proof_budget.rs` compares buffered O(n²), allocation-free two-pass O(n²), normalized checked O(n), and `O(n) -> buffered pair fallback`. Before timing it requires exact restored-numerator equality. It exercises compact admitting geometries plus three boundary states: `D=2^58,n=65` must normalize and stay on the linear fast path; odd `D=2^58+1,n=64` must admit; odd `D=2^58+1,n=65` must refuse the normalized linear path and actually use the buffered pair fallback. CSV output records geometry, normalized unit exponent, and `used_pairwise_fallback` so fallback execution cannot be inferred from sample count alone. Before changing the production boundary, retain: - release-mode raw timing samples and p95 from `crates/validation_core/examples/bias_se_exact_proof_budget.rs`, with exact commit, CPU, OS, Rust toolchain, build flags, timing sample count, and cold/warm procedure; -- side-by-side buffered O(n²), allocation-free two-pass O(n²), O(n), and hybrid evidence, with exact equality of restored pair-square numerators before timing; -- hybrid timing covering both the `n=64` admitting boundary and the `n=65` checked-intermediate-refusal/pair-fallback boundary, with `used_pairwise_fallback` recorded in the raw CSV; +- side-by-side buffered O(n²), allocation-free two-pass O(n²), normalized O(n), and hybrid evidence, with exact equality of restored pair-square numerators before timing; +- hybrid timing covering the common-power normalized admission and the odd-diameter admitted/refused boundary, with `unit_exponent` and `used_pairwise_fallback` recorded in the raw CSV; - target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence rather than byte estimates inferred from field widths; - admitted/refused-set comparison between the existing pairwise proof and any stronger sufficient O(n) dyadic-grid proof, with proof refusal falling back rather than altering scientific meaning; -- checked-`u128` overflow/refusal evidence across sample count, represented exponent spread, and coefficient distribution; +- checked-`u128` overflow/refusal evidence across sample count, canonical aligned coefficient diameter/exponent spread, and coefficient distribution; - a wider-integer/reference alternative assessment kept separate from production authority unless its dependency/security/performance cost is explicitly accepted; - full service/API p95 when a buyer-facing path is affected, preserving the TEPP `p95 <= 20 ms` target without shrinking input, omitting proof work, or using an unrealistic cache-only setup. The exact pair-record count is `n(n-1)/2`; the current characterization locks 120 records at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and 4,997,541 at `n=3,162`. A two-pass O(n²) implementation may remove pair-record storage while preserving pair-enumeration proof shape, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its checked arithmetic fits `u128`, and a checked O(n) refusal is not allowed to narrow the current exact pairwise admission set. -No release-mode timing numbers are currently authoritative. The local automation environment for the hybrid commit did not provide a Rust toolchain, and hosted exact-head jobs had not produced a benchmark artifact at the time of this update. Unexecuted timing harnesses and branch-only resource characterization remain supporting evidence only. +No release-mode timing numbers are currently authoritative. The local execution environment for this correction does not provide the required Rust 1.98.0 toolchain, and hosted exact-head jobs have not produced a benchmark artifact. Unexecuted timing harnesses and branch-only resource characterization remain supporting evidence only. ## Model release/cutover From 96f17c02edba0792f61e0e92167703a6ae4e40d0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 06:10:51 +0900 Subject: [PATCH 488/576] fix(validation): check dyadic restoration overflow explicitly --- .../bias_standard_error_exact_proof_budget_characterization.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs index ab25af845..331d12561 100644 --- a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs +++ b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs @@ -62,7 +62,8 @@ fn pair_square_sum_linear(values: &[u128]) -> Option { let normalized_sum = sample_count .checked_mul(square_sum)? .checked_sub(coefficient_sum.checked_mul(coefficient_sum)?)?; - normalized_sum.checked_shl(common_shift.checked_mul(2)?) + let squared_unit = 1_u128.checked_shl(common_shift.checked_mul(2)?)?; + normalized_sum.checked_mul(squared_unit) } fn greatest_common_divisor(mut left: u128, mut right: u128) -> u128 { From 081000289f5a52e94863026d55696ee2a4daf923 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 07:00:19 +0900 Subject: [PATCH 489/576] test(validation): add wider exact-proof intermediate reference --- ...ror_exact_proof_budget_characterization.rs | 147 ++++++++++++++++++ 1 file changed, 147 insertions(+) diff --git a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs index 331d12561..f79a22330 100644 --- a/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs +++ b/crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs @@ -20,6 +20,99 @@ const SEVENTEEN_OBSERVATION_FIXTURE: [u128; 17] = [ 1_805_452_085, ]; +#[derive(Clone, Copy, Debug, PartialEq, Eq)] +struct Wide256 { + high: u128, + low: u128, +} + +impl Wide256 { + fn multiply_u128(left: u128, right: u128) -> Self { + let mask = u128::from(u64::MAX); + let left_limbs = [ + u64::try_from(left & mask).expect("masked low limb fits u64"), + u64::try_from(left >> 64).expect("high limb fits u64"), + ]; + let right_limbs = [ + u64::try_from(right & mask).expect("masked low limb fits u64"), + u64::try_from(right >> 64).expect("high limb fits u64"), + ]; + let mut limbs = [0_u64; 4]; + + for (left_index, left_limb) in left_limbs.iter().copied().enumerate() { + let mut carry = 0_u128; + for (right_index, right_limb) in right_limbs.iter().copied().enumerate() { + let limb_index = left_index + right_index; + let accumulator = u128::from(left_limb) + .checked_mul(u128::from(right_limb)) + .expect("64-bit limb product fits u128") + .checked_add(u128::from(limbs[limb_index])) + .expect("schoolbook partial sum fits u128") + .checked_add(carry) + .expect("schoolbook carry sum fits u128"); + limbs[limb_index] = u64::try_from(accumulator & mask) + .expect("masked schoolbook limb fits u64"); + carry = accumulator >> 64; + } + limbs[left_index + 2] = + u64::try_from(carry).expect("schoolbook multiplication carry fits u64"); + } + + Self { + high: u128::from(limbs[2]) | (u128::from(limbs[3]) << 64), + low: u128::from(limbs[0]) | (u128::from(limbs[1]) << 64), + } + } + + fn checked_sub(self, right: Self) -> Option { + let (low, borrow) = self.low.overflowing_sub(right.low); + let high = self + .high + .checked_sub(right.high)? + .checked_sub(u128::from(u8::from(borrow)))?; + Some(Self { high, low }) + } + + fn checked_shl(self, shift: u32) -> Option { + if shift == 0 { + return Some(self); + } + if shift >= 256 { + return None; + } + if shift >= 128 { + if self.high != 0 { + return None; + } + let high_shift = shift - 128; + if high_shift == 0 { + return Some(Self { + high: self.low, + low: 0, + }); + } + if self.low >> (128 - high_shift) != 0 { + return None; + } + return Some(Self { + high: self.low << high_shift, + low: 0, + }); + } + if self.high >> (128 - shift) != 0 { + return None; + } + Some(Self { + high: (self.high << shift) | (self.low >> (128 - shift)), + low: self.low << shift, + }) + } + + fn as_u128(self) -> Option { + (self.high == 0).then_some(self.low) + } +} + fn deterministic_compact_fixture(sample_count: usize) -> Vec { (0..sample_count) .map(|index| { @@ -66,6 +159,31 @@ fn pair_square_sum_linear(values: &[u128]) -> Option { normalized_sum.checked_mul(squared_unit) } +fn pair_square_sum_linear_wide_product(values: &[u128]) -> Option { + let minimum = *values.iter().min()?; + let sample_count = u128::try_from(values.len()).ok()?; + let common_shift = values + .iter() + .filter_map(|value| { + let coefficient = value.checked_sub(minimum)?; + (coefficient != 0).then_some(coefficient.trailing_zeros()) + }) + .min() + .unwrap_or(0); + + let mut coefficient_sum = 0_u128; + let mut square_sum = 0_u128; + for value in values { + let coefficient = value.checked_sub(minimum)? >> common_shift; + coefficient_sum = coefficient_sum.checked_add(coefficient)?; + square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; + } + + let normalized_sum = Wide256::multiply_u128(sample_count, square_sum) + .checked_sub(Wide256::multiply_u128(coefficient_sum, coefficient_sum))?; + normalized_sum.checked_shl(common_shift.checked_mul(2)?) +} + fn greatest_common_divisor(mut left: u128, mut right: u128) -> u128 { while right != 0 { let remainder = left % right; @@ -193,6 +311,35 @@ fn linear_checked_integer_kernel_is_not_admission_equivalent_to_pair_reference() ); } +#[test] +fn wide_product_reference_recovers_the_pair_only_odd_boundary() { + let diameter = (1_u128 << 58) + 1; + let mut pair_only = Vec::with_capacity(65); + pair_only.push(0); + pair_only.extend((0..64).map(|_| diameter)); + + let pairwise = pair_square_sum_quadratic(&pair_only) + .expect("65-sample pair numerator stays within u128"); + assert_eq!(pair_square_sum_linear(&pair_only), None); + assert_eq!( + pair_square_sum_linear_wide_product(&pair_only).and_then(Wide256::as_u128), + Some(pairwise), + "two-limb intermediate products must distinguish a narrow-u128 refusal from an exact pair refusal" + ); +} + +#[test] +fn wide_product_reference_preserves_full_width_u128_multiplication() { + let maximum = u128::MAX; + assert_eq!( + Wide256::multiply_u128(maximum, maximum), + Wide256 { + high: maximum - 1, + low: 1, + } + ); +} + #[test] fn pair_record_counts_are_exact_resource_evidence() { assert_eq!(pair_record_count(16), Some(120)); From 0bd805d4b0304cf1f76344ae14b7f079b3dade17 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 07:01:34 +0900 Subject: [PATCH 490/576] test(validation): measure wider exact-proof reference --- .../examples/bias_se_exact_proof_budget.rs | 104 ++++++++++++++++-- 1 file changed, 95 insertions(+), 9 deletions(-) diff --git a/crates/validation_core/examples/bias_se_exact_proof_budget.rs b/crates/validation_core/examples/bias_se_exact_proof_budget.rs index 851a705a3..b17e70b5b 100644 --- a/crates/validation_core/examples/bias_se_exact_proof_budget.rs +++ b/crates/validation_core/examples/bias_se_exact_proof_budget.rs @@ -3,13 +3,13 @@ //! This example compares checked-integer proof kernels on deterministic dyadic //! coefficients: a production-layout-shaped buffered O(n²) pair proof, an //! allocation-free two-pass O(n²) variant, an algebraically equivalent O(n) -//! sufficient accumulator, and the viable hybrid shape that uses the O(n) path -//! only when it admits and otherwise falls back to the buffered pair proof. -//! The O(n) path first removes the shared power-of-two unit from anchor-relative -//! coefficients so checked-intermediate refusal is evaluated on the canonical -//! dyadic grid rather than on an arbitrary raw integer scale. It is -//! characterization tooling, not production admission and not buyer-path latency -//! evidence by itself. +//! sufficient accumulator, a two-limb wider-product O(n) reference, and the +//! viable hybrid shape that uses the narrow O(n) path only when it admits and +//! otherwise falls back to the buffered pair proof. The O(n) paths first remove +//! the shared power-of-two unit from anchor-relative coefficients so +//! checked-intermediate refusal is evaluated on the canonical dyadic grid rather +//! than on an arbitrary raw integer scale. This is characterization tooling, not +//! production admission and not buyer-path latency evidence by itself. use std::hint::black_box; use std::mem::size_of; @@ -24,6 +24,64 @@ struct KernelObservation { used_pairwise_fallback: bool, } +#[derive(Clone, Copy)] +struct Wide256 { + high: u128, + low: u128, +} + +impl Wide256 { + fn multiply_u128(left: u128, right: u128) -> Self { + let mask = u128::from(u64::MAX); + let left_limbs = [ + u64::try_from(left & mask).expect("masked low limb fits u64"), + u64::try_from(left >> 64).expect("high limb fits u64"), + ]; + let right_limbs = [ + u64::try_from(right & mask).expect("masked low limb fits u64"), + u64::try_from(right >> 64).expect("high limb fits u64"), + ]; + let mut limbs = [0_u64; 4]; + + for (left_index, left_limb) in left_limbs.iter().copied().enumerate() { + let mut carry = 0_u128; + for (right_index, right_limb) in right_limbs.iter().copied().enumerate() { + let limb_index = left_index + right_index; + let accumulator = u128::from(left_limb) + .checked_mul(u128::from(right_limb)) + .expect("64-bit limb product fits u128") + .checked_add(u128::from(limbs[limb_index])) + .expect("schoolbook partial sum fits u128") + .checked_add(carry) + .expect("schoolbook carry sum fits u128"); + limbs[limb_index] = u64::try_from(accumulator & mask) + .expect("masked schoolbook limb fits u64"); + carry = accumulator >> 64; + } + limbs[left_index + 2] = + u64::try_from(carry).expect("schoolbook multiplication carry fits u64"); + } + + Self { + high: u128::from(limbs[2]) | (u128::from(limbs[3]) << 64), + low: u128::from(limbs[0]) | (u128::from(limbs[1]) << 64), + } + } + + fn checked_sub(self, right: Self) -> Option { + let (low, borrow) = self.low.overflowing_sub(right.low); + let high = self + .high + .checked_sub(right.high)? + .checked_sub(u128::from(u8::from(borrow)))?; + Some(Self { high, low }) + } + + fn as_u128(self) -> Option { + (self.high == 0).then_some(self.low) + } +} + type Kernel = fn(&[u128]) -> Option; fn fixture(sample_count: usize) -> Vec { @@ -138,9 +196,8 @@ fn pair_square_sum_quadratic_two_pass(values: &[u128]) -> Option Option { +fn normalized_linear_terms(values: &[u128]) -> Option<(u128, u128, u32)> { let minimum = *values.iter().min()?; - let sample_count = u128::try_from(values.len()).ok()?; let common_shift = values .iter() .filter_map(|value| { @@ -157,6 +214,12 @@ fn pair_square_sum_linear(values: &[u128]) -> Option { coefficient_sum = coefficient_sum.checked_add(coefficient)?; square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; } + Some((coefficient_sum, square_sum, common_shift)) +} + +fn pair_square_sum_linear(values: &[u128]) -> Option { + let sample_count = u128::try_from(values.len()).ok()?; + let (coefficient_sum, square_sum, common_shift) = normalized_linear_terms(values)?; let pair_square_sum = sample_count .checked_mul(square_sum)? .checked_sub(coefficient_sum.checked_mul(coefficient_sum)?)?; @@ -169,6 +232,21 @@ fn pair_square_sum_linear(values: &[u128]) -> Option { }) } +fn pair_square_sum_linear_wide_product(values: &[u128]) -> Option { + let sample_count = u128::try_from(values.len()).ok()?; + let (coefficient_sum, square_sum, common_shift) = normalized_linear_terms(values)?; + let pair_square_sum = Wide256::multiply_u128(sample_count, square_sum) + .checked_sub(Wide256::multiply_u128(coefficient_sum, coefficient_sum))? + .as_u128()?; + Some(KernelObservation { + aligned_pair_square_sum: pair_square_sum, + unit_exponent: i32::try_from(common_shift).ok()?, + scratch_records: 0, + scratch_payload_bytes: 0, + used_pairwise_fallback: false, + }) +} + fn pair_square_sum_hybrid(values: &[u128]) -> Option { if let Some(observation) = pair_square_sum_linear(values) { return Some(observation); @@ -233,6 +311,8 @@ fn assert_and_measure_geometry( .expect("buffered quadratic result stays within u128"); let two_pass = pair_square_sum_quadratic_two_pass(values) .expect("two-pass quadratic result stays within u128"); + let wide_product = pair_square_sum_linear_wide_product(values) + .expect("wider-product linear reference stays within its declared budget"); let hybrid = pair_square_sum_hybrid(values).expect("hybrid result stays within u128"); let exact_pair_square_sum = restored_pair_square_sum(buffered) .expect("buffered result restores to exact pair-square sum"); @@ -241,6 +321,11 @@ fn assert_and_measure_geometry( Some(exact_pair_square_sum), "quadratic kernels must agree" ); + assert_eq!( + restored_pair_square_sum(wide_product), + Some(exact_pair_square_sum), + "wider-product linear reference must preserve the exact pair numerator" + ); assert_eq!( restored_pair_square_sum(hybrid), Some(exact_pair_square_sum), @@ -266,6 +351,7 @@ fn assert_and_measure_geometry( let mut kernels: Vec<(&str, Kernel)> = vec![ ("quadratic_buffered", pair_square_sum_quadratic_buffered), ("quadratic_two_pass", pair_square_sum_quadratic_two_pass), + ("linear_wide_product_reference", pair_square_sum_linear_wide_product), ("hybrid", pair_square_sum_hybrid), ]; if expect_linear_admission { From 8ec2092872edc2867652ce98313ebad9deabd5ba Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 07:02:36 +0900 Subject: [PATCH 491/576] docs(validation): record wider exact-proof reference --- .../validation-bias-standard-error-exact-proof-budget.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-bias-standard-error-exact-proof-budget.md b/CHANGELOG.d/validation-bias-standard-error-exact-proof-budget.md index 718fbd39c..9a5984186 100644 --- a/CHANGELOG.d/validation-bias-standard-error-exact-proof-budget.md +++ b/CHANGELOG.d/validation-bias-standard-error-exact-proof-budget.md @@ -1,2 +1,4 @@ - Add exact-proof budget characterization for mean-bias standard error: a seventeen-observation represented-input fixture, O(n²) versus algebraically equivalent O(n) checked-`u128` numerator evidence, and the compact-dyadic `n<=2_047` worst-case arithmetic envelope. -- Add a reproducible standard-library timing harness for the quadratic and linear integer kernels while keeping production exact pair-distance admission bounded at `n<=16` until release-mode CPU/allocation and applicable buyer-path p95 evidence establish a resource budget. \ No newline at end of file +- Add a reproducible standard-library timing harness for the buffered O(n²), allocation-free two-pass O(n²), canonical-dyadic checked O(n), and O(n)→pair-fallback kernels while keeping production exact pair-distance admission bounded at `n<=16` until release-mode CPU/allocation and applicable buyer-path p95 evidence establish a resource budget. +- Add a dependency-free two-limb wider-product O(n) reference for characterization. On the canonical odd boundary `D=2^58+1,n=65`, the narrow checked-`u128` O(n) intermediates require 129 bits and refuse, while the exact pair numerator remains 123 bits; the wider-product reference performs the cancellation without overflow and recovers the same exact pair numerator. This is evidence about intermediate width, not production admission or a new arithmetic owner. +- Extend the timing harness to emit the wider-product reference beside the existing kernels so a recorded Rust 1.98.0 release-mode run can compare CPU cost without adding a production dependency or weakening pairwise fallback. \ No newline at end of file From 61acee8d3c1f5cf1014afddb673277a6fda86305 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 07:03:18 +0900 Subject: [PATCH 492/576] docs(validation): trace wider exact-proof reference --- ...-bias-standard-error-exact-proof-budget.md | 39 +++++++++++-------- 1 file changed, 23 insertions(+), 16 deletions(-) diff --git a/docs/research/validation-bias-standard-error-exact-proof-budget.md b/docs/research/validation-bias-standard-error-exact-proof-budget.md index 21df7c6db..4a47b1cf7 100644 --- a/docs/research/validation-bias-standard-error-exact-proof-budget.md +++ b/docs/research/validation-bias-standard-error-exact-proof-budget.md @@ -20,7 +20,7 @@ For `n=17`, the scientific denominator is `17^2(17-1)=4_624`. `gcd(N,4_624)=2`, The current public API intentionally remains on the established translated floating fallback at `n=17`; for this fixture it returns bits `0x41a0_dd77_9ac3_8e98`. Independent high-precision rational-square-root evaluation gives the adjacent correctly rounded binary64 target `0x41a0_dd77_9ac3_8e99`. This extends the demonstrated failure class beyond the current cutoff, but it is evidence for a systemic budget decision rather than justification for another one-count production patch. -`crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` fixes the exact pair-square sum, reduced ratio, current fallback boundary, algebraic equivalence, pair-record counts, distinct checked-integer envelopes, dyadic-unit normalization, and the admitted/refused-set relation between the current pair reference and the candidate O(n) accumulator. The test intentionally makes any future widening of production admission update this characterization rather than silently inheriting a stale fallback assumption. +`crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` fixes the exact pair-square sum, reduced ratio, current fallback boundary, algebraic equivalence, pair-record counts, distinct checked-integer envelopes, dyadic-unit normalization, the admitted/refused-set relation between the current pair reference and the candidate O(n) accumulator, and a dependency-free wider-product reference for the narrow-intermediate refusal boundary. The test intentionally makes any future widening of production admission update this characterization rather than silently inheriting a stale fallback assumption. ## O(n²) reference versus O(n) exact accumulator @@ -42,9 +42,11 @@ which is a 123-bit `u128` value, while the first O(n) intermediate `65 * 64 * (2^58 + 1)^2 = 345_599_278_904_078_129_353_066_565_255_131_304_000` -requires 129 bits and therefore refuses before cancellation. The pair reference can still prove the exact numerator. Linear refusal remains distinct from scientific refusal, but the evidence now survives canonical dyadic normalization. +requires 129 bits and therefore refuses before cancellation. The companion square `(64D)^2 = 340_282_366_920_938_465_824_557_848_866_590_822_400` also requires 129 bits. Their exact difference is the 123-bit pair numerator above. Linear refusal remains distinct from scientific refusal, but the evidence now survives canonical dyadic normalization. -This rules out a drop-in replacement of the current pair proof with the normalized checked-`u128` identity. A production O(n) path can preserve current scientific admission only as a sufficient fast path followed by the existing pairwise proof on refusal, or by adopting a wider checked-integer representation with its own resource and supply-chain evidence. +Commit `081000289f5a52e94863026d55696ee2a4daf923` adds a dependency-free `Wide256` characterization reference using four 64-bit limbs for exact `u128 × u128` products, checked two-limb subtraction, and checked dyadic restoration. It proves two separate facts: `(2^128-1)^2` is represented exactly as high limb `2^128-2` and low limb `1`, and the odd `D=2^58+1,n=65` geometry that the narrow O(n) kernel refuses is recovered exactly after 129-bit intermediate cancellation. The wider reference deliberately keeps coefficient and square accumulation in checked `u128`; it is therefore a bounded intermediate-width experiment, not arbitrary precision and not a production implementation. + +This rules out a drop-in replacement of the current pair proof with the normalized checked-`u128` identity. A production O(n) path can preserve current scientific admission only as a sufficient fast path followed by the existing pairwise proof on refusal, or by adopting a wider checked-integer representation with its own resource and supply-chain evidence. The two-limb reference shows that wider products can recover at least one real narrow-intermediate refusal without adding a dependency; it does not yet prove admission equivalence over the complete represented-input domain. ## Checked-u128 envelopes @@ -68,26 +70,27 @@ A two-pass O(n²) reference can remove the pair-record allocation without changi ## Measurement harness -`crates/validation_core/examples/bias_se_exact_proof_budget.rs` is a standard-library-only release-mode characterization harness. It compares four kernels: +`crates/validation_core/examples/bias_se_exact_proof_budget.rs` is a standard-library-only release-mode characterization harness. It compares five kernels: - `quadratic_buffered`: production-layout-shaped `Vec>` pair records plus aligned checked-square accumulation; - `quadratic_two_pass`: the same pair enumeration and dyadic alignment without pair-record storage; -- `linear`: minimum-anchor coefficients, normalized by their largest shared power-of-two unit, then `n*sum(c_i^2) - (sum c_i)^2`; -- `hybrid`: the viable resource shape, using the normalized checked O(n) accumulator when it admits and otherwise falling back to the production-layout-shaped buffered pair proof. +- `linear`: minimum-anchor coefficients, normalized by their largest shared power-of-two unit, then checked-`u128` `n*sum(c_i^2) - (sum c_i)^2`; +- `linear_wide_product_reference`: the same normalized coefficient/square accumulators, but the two final products and cancellation use dependency-free two-limb 256-bit arithmetic before the exact result is required to fit the existing `u128` pair-numerator domain; +- `hybrid`: the viable production-shape candidate, using the narrow normalized checked O(n) accumulator when it admits and otherwise falling back to the production-layout-shaped buffered pair proof. -Before timing, the harness restores the dyadic unit and requires every applicable kernel to equal the same exact pair-square numerator. Compact deterministic fixtures at 16, 64, 256, 1,024, and 2,047 observations exercise ordinary admitting geometry. Three boundary geometries separate normalization from true refusal: +Before timing, the harness restores the dyadic unit and requires every applicable kernel to equal the same exact pair-square numerator. Compact deterministic fixtures at 16, 64, 256, 1,024, and 2,047 observations exercise ordinary admitting geometry. Three boundary geometries separate normalization from true narrow-intermediate refusal: -1. `power_of_two_normalized_admit`: `n=65`, `D=2^58`. The linear and hybrid paths must factor the common dyadic unit and admit; pair fallback here would reproduce the predecessor characterization defect. -2. `odd_boundary_admit`: `n=64`, `D=2^58+1`. With no removable common dyadic factor, normalized O(n) still fits. -3. `odd_boundary_pair_fallback`: `n=65`, `D=2^58+1`. The exact pair numerator fits, normalized O(n) checked intermediates refuse, and the hybrid must execute the buffered pair fallback. +1. `power_of_two_normalized_admit`: `n=65`, `D=2^58`. The narrow linear, wider-product reference, and hybrid paths must factor the common dyadic unit and admit; pair fallback here would reproduce the predecessor characterization defect. +2. `odd_boundary_admit`: `n=64`, `D=2^58+1`. With no removable common dyadic factor, normalized narrow O(n) still fits. +3. `odd_boundary_pair_fallback`: `n=65`, `D=2^58+1`. The exact pair numerator fits, normalized narrow O(n) checked intermediates refuse, the wider-product reference still recovers the exact numerator, and the production-shape hybrid must execute the buffered pair fallback. The CSV schema remains `geometry,sample_count,kernel,p95_ns,timing_samples,unit_exponent,scratch_records,scratch_payload_bytes,pair_record_size_bytes,used_pairwise_fallback`. -`unit_exponent` is material evidence in the corrected harness: it distinguishes the common-power geometry, where the linear path reports exponent 58 and a small aligned numerator, from the odd-diameter geometry, where the canonical unit exponent is zero. `used_pairwise_fallback` records whether a hybrid row actually exercised the expensive proof path instead of inferring that fact from sample count. +`unit_exponent` is material evidence in the corrected harness: it distinguishes the common-power geometry, where the linear path reports exponent 58 and a small aligned numerator, from the odd-diameter geometry, where the canonical unit exponent is zero. `used_pairwise_fallback` records whether a hybrid row actually exercised the expensive proof path instead of inferring that fact from sample count. The `kernel` field now makes the wider-product reference directly comparable to the narrow O(n), pair, and hybrid timing rows in the same release-mode run. -The original hybrid harness landed in `c6b237e0bb1388cccd7bcb71a0df5cbf837a07c5`; the dyadic-normalization correction is `4a3d988702593b2b8d59be6dcfb1601ca1a0d610`. It remains measurement tooling only. No release-mode timing result is recorded in this document yet because the current execution environment does not provide Rust 1.98.0 and the hosted exact-head jobs have not produced measurement artifacts. A valid timing record must include CPU, OS, Rust toolchain, exact commit, release build mode, raw sample count, raw CSV, allocator/RSS evidence, and the cold/warm procedure. Kernel timing cannot substitute for an applicable API buyer-path p95 measurement. +The original hybrid harness landed in `c6b237e0bb1388cccd7bcb71a0df5cbf837a07c5`; the dyadic-normalization correction is `4a3d988702593b2b8d59be6dcfb1601ca1a0d610`; the wider-product measurement extension is `0bd805d4b0304cf1f76344ae14b7f079b3dade17`. It remains measurement tooling only. No release-mode timing result is recorded in this document yet because the current execution environment does not provide Rust 1.98.0 and the hosted exact-head jobs have not produced measurement artifacts. A valid timing record must include CPU, OS, Rust toolchain, exact commit, release build mode, raw sample count, raw CSV, allocator/RSS evidence, and the cold/warm procedure. Kernel timing cannot substitute for an applicable API buyer-path p95 measurement. ## Decision and rejected alternatives @@ -95,7 +98,9 @@ Production admission stays `n=4..=16`. Extending to `n=17` alone is rejected bec The former `D=2^58, n=65` raw-scale refusal is explicitly rejected as admission evidence because it disappears under the same common-power dyadic normalization already required by the proposed O(n) proof. Retaining it would make the resource budget representation-dependent. The odd `D=2^58+1` boundary replaces it as the normalized refusal fixture. -Treating `n<=2_047`, `n<=4_095`, or the unreduced denominator threshold as the production budget is also rejected because arithmetic representability is not latency or memory evidence. A two-pass O(n²) allocation-removal path remains a candidate because it can preserve the current pair-proof admission shape while eliminating pair-record storage. The normalized O(n) identity remains the stronger CPU-scaling candidate, but its checked-`u128` form is still a strict sufficient subset of the pair reference. Replacing the pair proof with that kernel alone is rejected because it would silently narrow exact-proof admission. The hybrid harness measures the viable bounded shape—normalized O(n) fast admission with buffered O(n²) fallback—without making it production behavior. An admission-equivalent wider-integer O(n) proof or the allocation-free two-pass pair reference remain alternatives if measurements justify them. Arbitrary-precision production arithmetic remains deferred pending measured benefit, supply-chain review, and an explicit owner/resource decision. +Treating `n<=2_047`, `n<=4_095`, or the unreduced denominator threshold as the production budget is also rejected because arithmetic representability is not latency or memory evidence. A two-pass O(n²) allocation-removal path remains a candidate because it can preserve the current pair-proof admission shape while eliminating pair-record storage. The normalized O(n) identity remains the stronger CPU-scaling candidate, but its checked-`u128` form is still a strict sufficient subset of the pair reference. Replacing the pair proof with that kernel alone is rejected because it would silently narrow exact-proof admission. The hybrid harness measures the viable bounded shape—normalized O(n) fast admission with buffered O(n²) fallback—without making it production behavior. + +The new two-limb wider-product reference is retained only as characterization evidence. Promoting it directly to production is rejected for now because its bounded construction deliberately assumes the normalized coefficient sum and sum of squares already fit `u128`, and because no Rust 1.98.0 release-mode timing, broader admitted/refused-set sweep, coverage evidence, or independent review exists for it yet. Arbitrary-precision production arithmetic remains deferred pending measured benefit, supply-chain review, and an explicit owner/resource decision. ## Traceability @@ -106,14 +111,16 @@ Treating `n<=2_047`, `n<=4_095`, or the unreduced denominator threshold as the p | Predecessor scientific repair | GAP-125; RED `5da82b2d651706c191ca191c6c077d916cbfda25`; repair `a509ae9e46c8ffc2cc3ef4f0e904774ad2516e1f` | | Characterization RED | `4f1bd2c343cf2d54905a07c257a570a89dc575d3` — common `2^58` scale must normalize before O(n) overflow admission | | Characterization repair | `d423b57797b6f7f127e61e0679f9ee9841525c77` — normalized test kernel plus odd-diameter refusal fixture | -| Harness repair | `4a3d988702593b2b8d59be6dcfb1601ca1a0d610` — normalized O(n)/hybrid measurement geometries | +| Narrow-reference overflow hardening | `96f17c02edba0792f61e0e92167703a6ae4e40d0` — checked restored-scale multiplication | +| Wider-product characterization | `081000289f5a52e94863026d55696ee2a4daf923` — dependency-free two-limb products/cancellation and odd-boundary recovery | +| Wider-product harness | `0bd805d4b0304cf1f76344ae14b7f079b3dade17` — release-mode comparison row for the wider intermediate reference | +| CHANGELOG fragment | `8ec2092872edc2867652ce98313ebad9deabd5ba` | | Current production module | `crates/validation_core/src/bias_se.rs` | | Public API | `validation_core::bias_standard_error` | | Exact characterization | `crates/validation_core/tests/bias_standard_error_exact_proof_budget_characterization.rs` | | CPU/layout/hybrid harness | `crates/validation_core/examples/bias_se_exact_proof_budget.rs` | -| CHANGELOG evidence | `CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md` | | Resource/merge rule | Production cutoff remains `n<=16` pending measured exact-proof budget | ## Follow-up evidence required by #491 -Run the corrected hybrid-capable harness in release mode on a recorded CPU/toolchain and retain raw timing CSV. Record allocator/RSS evidence in addition to the harness's exact pair-record payload layout. Evaluate a wider-integer/reference alternative without making it production authority. Only after those results establish a resource budget should production admission change; any such change needs a realistic public RED, exact-head Rust/rustdoc/coverage evidence, and applicable buyer-path p95 evidence. +Run the five-kernel harness in release mode on a recorded CPU/toolchain and retain raw timing CSV. Record allocator/RSS evidence in addition to the harness's exact pair-record payload layout. Extend the wider-product comparison across sample count, canonical dyadic exponent spread/diameter, and coefficient distributions so a narrower refusal cannot be mistaken for scientific refusal and a wider-product admission cannot be mistaken for full-domain equivalence. Only after those results establish a resource budget should production admission change; any such change needs a realistic public RED, exact-head Rust/rustdoc/coverage/security evidence, independent current-head review, and applicable buyer-path p95 evidence. From a71b55c4f9b1a52a8060677e14c1828fec2f325f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 07:05:02 +0900 Subject: [PATCH 493/576] docs(validation): make proof-budget tests wider-reference current --- docs/TEST_STRATEGY.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index a67bcc8fa..bf2f9f1c1 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -69,16 +69,17 @@ Validation Evidence numerical proofs that add asymptotic work or material alloca - retain realistic represented-input counterexamples and permutation/sign-mirror contracts for scientific correctness; - keep the existing exact residual/pairwise-subtraction, checked-integer, GCD-reduction, and exact midpoint authority fail-closed; -- compare the buffered O(n²) pair-record path, an allocation-free two-pass O(n²) reference, an algebraically equivalent O(n) exact accumulator under a proved sufficient admission condition, and the viable O(n)-fast-path/buffered-pair-fallback hybrid; compare a wider-integer/reference alternative separately; +- compare the buffered O(n²) pair-record path, an allocation-free two-pass O(n²) reference, an algebraically equivalent narrow O(n) exact accumulator under a proved sufficient admission condition, a dependency-free two-limb wider-product O(n) characterization reference, and the viable narrow-O(n)-fast-path/buffered-pair-fallback hybrid; - normalize the largest common power-of-two dyadic unit from exact anchor-relative coefficients before checked O(n) intermediates are judged. Raw-scale overflow is not a scientific or resource refusal when exact dyadic rescaling removes it; -- require an admitted/refused-set contract for any O(n) candidate. The predecessor `D=2^58, n=65` refusal was invalid because canonical normalization reduces the coefficients to zero/one and preserves the restored pair numerator `2^122`. The corrected strict-subset boundary uses odd `D=2^58+1`: both pair and normalized linear kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits `u128` but the normalized O(n) intermediates require 129 bits before cancellation; -- therefore an O(n) production optimization must either fall back to the current pairwise proof on checked-intermediate refusal or use a separately justified wider checked-integer representation; linear refusal must never silently narrow scientific admission; -- characterize checked-`u128` refusal as a function of sample count, canonical aligned dyadic diameter/exponent spread, and coefficient distribution rather than treating an integer cutoff or raw represented scale as a scientific boundary; +- require an admitted/refused-set contract for any O(n) candidate. The predecessor `D=2^58, n=65` refusal was invalid because canonical normalization reduces the coefficients to zero/one and preserves the restored pair numerator `2^122`. The corrected strict-subset boundary uses odd `D=2^58+1`: both pair and normalized narrow linear kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits `u128` but the normalized narrow O(n) intermediates require 129 bits before cancellation; +- retain the wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`: on odd `D=2^58+1, n=65`, exact two-limb products and subtraction recover the same 123-bit pair numerator after the two 129-bit intermediates cancel. This proves that narrow-intermediate refusal is not scientific refusal, but it does not prove full-domain admission equivalence because normalized coefficient and square accumulation remain checked `u128`; +- therefore an O(n) production optimization must either fall back to the current pairwise proof on checked-intermediate refusal or use a separately justified wider checked-integer representation; narrow linear refusal must never silently narrow scientific admission; +- characterize checked-`u128` and wider-reference refusal as functions of sample count, canonical aligned dyadic diameter/exponent spread, and coefficient distribution rather than treating an integer cutoff or raw represented scale as a scientific boundary; - keep the normalized O(n) distribution-independent intermediate envelope distinct from the exact pair-square numerator envelope: at aligned coefficient diameter `2^53`, the characterized sufficient bounds are `n<=2_047` and `n<=4_095` respectively, and neither is a production budget; - record exact pair counts, target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence separately; field-width estimates alone are not allocation evidence; -- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing and to exercise three distinct boundary states: `D=2^58, n=65` must be a normalized linear admission, odd `D=2^58+1, n=64` must admit, and odd `D=2^58+1, n=65` must exercise checked-intermediate refusal plus the buffered pair fallback. The CSV must identify the geometry, normalized unit exponent, and whether hybrid fallback was actually used; -- keep RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3`, characterization repair `d423b57797b6f7f127e61e0679f9ee9841525c77`, and harness repair `4a3d988702593b2b8d59be6dcfb1601ca1a0d610` in the exact evidence lineage so representation-dependent refusal cannot re-enter the budget model; -- run that harness in release mode on a recorded CPU/OS/Rust toolchain and retain raw CSV plus p95; unexecuted harness code is not performance evidence; +- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing and to exercise three distinct boundary states: `D=2^58, n=65` must be a normalized narrow-linear admission, odd `D=2^58+1, n=64` must admit, and odd `D=2^58+1, n=65` must make the narrow O(n) refuse, the two-limb reference recover the exact pair numerator, and the production-shape hybrid exercise buffered pair fallback. The CSV must identify the geometry, kernel, normalized unit exponent, and whether hybrid fallback was actually used; +- keep RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3`, normalization repair `d423b57797b6f7f127e61e0679f9ee9841525c77`, restoration hardening `96f17c02edba0792f61e0e92167703a6ae4e40d0`, wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`, and wider-reference harness `0bd805d4b0304cf1f76344ae14b7f079b3dade17` in the exact evidence lineage; +- run the five-kernel harness in release mode on a recorded CPU/OS/Rust toolchain and retain raw CSV plus p95; unexecuted harness code is not performance evidence; - if a service/API buyer path is affected, measure the full applicable path and retain the `p95 <= 20 ms` target without shrinking samples, omitting proof work, or relying on unrealistic warm-cache setup; - arithmetic representability alone does not authorize a production sample-count budget. From dd2b1c61626cbb13c0acd9ee90d7f0d87369d1b2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 07:05:34 +0900 Subject: [PATCH 494/576] docs(validation): make proof-budget operability wider-reference current --- docs/OPERABILITY.md | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index 44dc3d6b2..8b0fb1f92 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -60,28 +60,29 @@ Before PostgreSQL becomes production state, prove migrations and rollback, tenan A numerical proof boundary is an operational resource contract when it changes asymptotic work, allocation, or buyer-path latency. It is not determined by the next sample count that happens to expose a rounding defect. -Issue #491 owns the current bias-standard-error exact-proof budget. Production exact pair-distance admission stays bounded to `n<=16` while the current implementation is O(n²) in pair enumeration and stores `n(n-1)/2` pair records. The characterization on PR #488 records a seventeen-observation counterexample and an algebraically equivalent O(n) checked-integer numerator, but arithmetic representability alone does not authorize a wider production budget. +Issue #491 owns the current bias-standard-error exact-proof budget. Production exact pair-distance admission stays bounded to `n<=16` while the current implementation is O(n²) in pair enumeration and stores `n(n-1)/2` pair records. The characterization on PR #488 records a seventeen-observation counterexample and algebraically equivalent O(n) checked-integer numerators, but arithmetic representability alone does not authorize a wider production budget. -The current characterization distinguishes three bounds that must not be collapsed into one cutoff. For a canonical aligned coefficient diameter `D=2^53` **after removing the largest common power-of-two dyadic unit**, the normalized O(n) distribution-independent intermediate envelope `n^2D^2` fits `u128` through `n=2_047`, while the exact aligned pair-square numerator extremal bound `floor(n^2/4)D^2` fits through `n=4_095`. Separately, the unreduced scientific denominator `n^2(n-1)` exceeds `2^53` after `n=208_064`; production uses the reduced denominator after GCD, so that threshold is only an envelope marker. None of these values is a latency or memory budget. +The current characterization distinguishes three bounds that must not be collapsed into one cutoff. For a canonical aligned coefficient diameter `D=2^53` **after removing the largest common power-of-two dyadic unit**, the normalized narrow O(n) distribution-independent intermediate envelope `n^2D^2` fits `u128` through `n=2_047`, while the exact aligned pair-square numerator extremal bound `floor(n^2/4)D^2` fits through `n=4_095`. Separately, the unreduced scientific denominator `n^2(n-1)` exceeds `2^53` after `n=208_064`; production uses the reduced denominator after GCD, so that threshold is only an envelope marker. None of these values is a latency or memory budget. The shared dyadic unit is a proof obligation. The predecessor characterization judged the O(n) candidate on raw values with one zero and the rest at `D=2^58`, and therefore reported a refusal at `n=65`. That refusal was representation-dependent: canonical normalization divides every nonzero coefficient by the exact common unit `2^58`, leaving one zero and sixty-four ones. The normalized intermediates are only `4_160` and `4_096`; their difference `64` restores to the exact pair numerator `2^122`. RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3` fixes this requirement, and characterization repair `d423b57797b6f7f127e61e0679f9ee9841525c77` evaluates checked O(n) admission on the normalized dyadic grid. -The corrected O(n) accumulator is still a strict sufficient subset of the current pair reference under `u128`; the valid boundary uses odd diameter `D=2^58+1`, whose common dyadic unit is one. Both kernels fit at `n=64`. At `n=65`, the exact pair numerator `64D^2` is a 123-bit `u128`, while the first O(n) intermediate `65*64*D^2` requires 129 bits before cancellation. A normalized O(n) refusal therefore still cannot become a scientific refusal. If the O(n) kernel is introduced, it must fall back to the existing pairwise proof or use a separately justified wider checked-integer representation. +The corrected narrow O(n) accumulator is still a strict sufficient subset of the current pair reference under `u128`; the valid boundary uses odd diameter `D=2^58+1`, whose common dyadic unit is one. Both kernels fit at `n=64`. At `n=65`, the exact pair numerator `64D^2` is a 123-bit `u128`, while both O(n) products require 129 bits before cancellation. A normalized narrow O(n) refusal therefore still cannot become a scientific refusal. -Harness repair `4a3d988702593b2b8d59be6dcfb1601ca1a0d610` encodes that distinction without changing production behavior. `crates/validation_core/examples/bias_se_exact_proof_budget.rs` compares buffered O(n²), allocation-free two-pass O(n²), normalized checked O(n), and `O(n) -> buffered pair fallback`. Before timing it requires exact restored-numerator equality. It exercises compact admitting geometries plus three boundary states: `D=2^58,n=65` must normalize and stay on the linear fast path; odd `D=2^58+1,n=64` must admit; odd `D=2^58+1,n=65` must refuse the normalized linear path and actually use the buffered pair fallback. CSV output records geometry, normalized unit exponent, and `used_pairwise_fallback` so fallback execution cannot be inferred from sample count alone. +Wider-intermediate characterization `081000289f5a52e94863026d55696ee2a4daf923` now makes that distinction executable without introducing a production dependency. A test-only two-limb `Wide256` performs exact `u128 × u128` products and checked cancellation. On odd `D=2^58+1,n=65`, it recovers the same exact 123-bit pair numerator after the two 129-bit products cancel, while the narrow checked-`u128` O(n) path refuses. The characterization also fixes `(2^128-1)^2` as high limb `2^128-2`, low limb `1`. This wider reference remains bounded: normalized coefficient sums and square sums are still accumulated in checked `u128`, so its success on the odd boundary is not evidence of full-domain admission equivalence or arbitrary-precision capability. + +Harness `crates/validation_core/examples/bias_se_exact_proof_budget.rs`, extended at `0bd805d4b0304cf1f76344ae14b7f079b3dade17`, compares five kernels without changing production behavior: buffered O(n²), allocation-free two-pass O(n²), normalized narrow checked O(n), the two-limb wider-product O(n) reference, and narrow `O(n) -> buffered pair fallback`. Before timing it requires exact restored-numerator equality. It exercises compact admitting geometries plus three boundary states: `D=2^58,n=65` must normalize and stay on the narrow linear fast path; odd `D=2^58+1,n=64` must admit; odd `D=2^58+1,n=65` must refuse the narrow linear path, be recovered by the wider-product reference, and make the production-shape hybrid actually use the buffered pair fallback. CSV output records geometry, kernel, normalized unit exponent, and `used_pairwise_fallback` so fallback execution cannot be inferred from sample count alone. Before changing the production boundary, retain: - release-mode raw timing samples and p95 from `crates/validation_core/examples/bias_se_exact_proof_budget.rs`, with exact commit, CPU, OS, Rust toolchain, build flags, timing sample count, and cold/warm procedure; -- side-by-side buffered O(n²), allocation-free two-pass O(n²), normalized O(n), and hybrid evidence, with exact equality of restored pair-square numerators before timing; +- side-by-side buffered O(n²), allocation-free two-pass O(n²), normalized narrow O(n), two-limb wider-product O(n), and hybrid evidence, with exact equality of restored pair-square numerators before timing; - hybrid timing covering the common-power normalized admission and the odd-diameter admitted/refused boundary, with `unit_exponent` and `used_pairwise_fallback` recorded in the raw CSV; - target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence rather than byte estimates inferred from field widths; -- admitted/refused-set comparison between the existing pairwise proof and any stronger sufficient O(n) dyadic-grid proof, with proof refusal falling back rather than altering scientific meaning; -- checked-`u128` overflow/refusal evidence across sample count, canonical aligned coefficient diameter/exponent spread, and coefficient distribution; -- a wider-integer/reference alternative assessment kept separate from production authority unless its dependency/security/performance cost is explicitly accepted; +- admitted/refused-set comparison between the existing pairwise proof and every O(n) candidate across sample count, canonical aligned coefficient diameter/exponent spread, and coefficient distribution; proof refusal must fall back rather than alter scientific meaning; +- evidence for where the bounded two-limb reference itself refuses because coefficient or square accumulation exceeds `u128`; success at one 129-bit product boundary is not a global proof; - full service/API p95 when a buyer-facing path is affected, preserving the TEPP `p95 <= 20 ms` target without shrinking input, omitting proof work, or using an unrealistic cache-only setup. -The exact pair-record count is `n(n-1)/2`; the current characterization locks 120 records at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and 4,997,541 at `n=3,162`. A two-pass O(n²) implementation may remove pair-record storage while preserving pair-enumeration proof shape, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its checked arithmetic fits `u128`, and a checked O(n) refusal is not allowed to narrow the current exact pairwise admission set. +The exact pair-record count is `n(n-1)/2`; the current characterization locks 120 records at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and 4,997,541 at `n=3,162`. A two-pass O(n²) implementation may remove pair-record storage while preserving pair-enumeration proof shape, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its checked arithmetic fits a wider intermediate, and a narrow checked O(n) refusal is not allowed to narrow the current exact pairwise admission set. No release-mode timing numbers are currently authoritative. The local execution environment for this correction does not provide the required Rust 1.98.0 toolchain, and hosted exact-head jobs have not produced a benchmark artifact. Unexecuted timing harnesses and branch-only resource characterization remain supporting evidence only. From b7e4da353ac58069afd73ee7c0e8427d49993fdb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 07:59:11 +0900 Subject: [PATCH 495/576] test(validation): prove pair-admitted accumulator bound --- ...linear_admission_bound_characterization.rs | 98 +++++++++++++++++++ 1 file changed, 98 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs diff --git a/crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs b/crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs new file mode 100644 index 000000000..72049a69f --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs @@ -0,0 +1,98 @@ +fn deterministic_compact_fixture(sample_count: usize) -> Vec { + (0..sample_count) + .map(|index| { + let value = u128::try_from(index).expect("fixture index fits u128"); + (value * 1_000_003 + value * value * 97 + 17) % 4_000_000_001 + }) + .collect() +} + +fn boundary_fixture(sample_count: usize, diameter: u128) -> Vec { + let mut values = Vec::with_capacity(sample_count); + values.push(0); + values.extend((1..sample_count).map(|_| diameter)); + values +} + +fn pair_square_sum(values: &[u128]) -> Option { + let mut sum = 0_u128; + for left in 0..values.len() { + for right in left + 1..values.len() { + let difference = values[left].abs_diff(values[right]); + sum = sum.checked_add(difference.checked_mul(difference)?)?; + } + } + Some(sum) +} + +fn normalized_linear_terms(values: &[u128]) -> Option<(u128, u128, u32)> { + let minimum = *values.iter().min()?; + let common_shift = values + .iter() + .filter_map(|value| { + let coefficient = value.checked_sub(minimum)?; + (coefficient != 0).then_some(coefficient.trailing_zeros()) + }) + .min() + .unwrap_or(0); + + let mut coefficient_sum = 0_u128; + let mut square_sum = 0_u128; + for value in values { + let coefficient = value.checked_sub(minimum)? >> common_shift; + coefficient_sum = coefficient_sum.checked_add(coefficient)?; + square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; + } + Some((coefficient_sum, square_sum, common_shift)) +} + +fn assert_pair_admission_bounds_linear_accumulators(values: &[u128]) { + let pair_sum = pair_square_sum(values).expect("fixture pair numerator fits u128"); + let (coefficient_sum, square_sum, common_shift) = + normalized_linear_terms(values).expect("pair-admitted normalized terms fit u128"); + let squared_shift = common_shift.checked_mul(2).expect("dyadic square shift fits u32"); + let normalized_pair_sum = pair_sum >> squared_shift; + let restored_pair_sum = normalized_pair_sum + .checked_shl(squared_shift) + .expect("fixture normalized pair numerator restores exactly"); + + assert_eq!(restored_pair_sum, pair_sum); + assert!( + coefficient_sum <= square_sum, + "integer anchor coefficients satisfy c <= c^2 termwise" + ); + assert!( + square_sum <= normalized_pair_sum, + "because at least one anchor coefficient is zero, every c_i^2 occurs in the exact pair numerator" + ); +} + +#[test] +fn pair_admitted_bound_survives_known_compact_and_boundary_geometries() { + for sample_count in [4_usize, 16, 17, 32, 64, 128, 256] { + assert_pair_admission_bounds_linear_accumulators(&deterministic_compact_fixture( + sample_count, + )); + } + + assert_pair_admission_bounds_linear_accumulators(&boundary_fixture(65, 1_u128 << 58)); + assert_pair_admission_bounds_linear_accumulators(&boundary_fixture( + 65, + (1_u128 << 58) + 1, + )); +} + +#[test] +fn pair_admitted_bound_holds_across_small_integer_composition_space() { + for sample_count in 2_usize..=7 { + let state_count = 4_usize.pow(u32::try_from(sample_count).expect("small exponent")); + for mut state in 0..state_count { + let mut values = Vec::with_capacity(sample_count); + for _ in 0..sample_count { + values.push(u128::try_from(state % 4).expect("base-four digit fits u128")); + state /= 4; + } + assert_pair_admission_bounds_linear_accumulators(&values); + } + } +} From 67503c0db879cc6d2356a6510003ca070d7525a3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 07:59:31 +0900 Subject: [PATCH 496/576] docs(validation): record wide-linear admission bound --- ...lidation-bias-standard-error-wide-linear-admission-bound.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md diff --git a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md new file mode 100644 index 000000000..496e065a2 --- /dev/null +++ b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md @@ -0,0 +1,3 @@ +### Validation + +- Corrected issue #491's exact-proof resource model: once anchor-relative dyadic coefficients are canonicalized to nonnegative integers with at least one zero, any exact pair-square numerator that fits `u128` also bounds both the coefficient sum and square sum (`sum(c_i) <= sum(c_i^2) <= sum_{i Date: Sun, 6 Sep 2026 07:59:44 +0900 Subject: [PATCH 497/576] docs(research): prove wide-linear accumulator admission bound --- ...ndard-error-wide-linear-admission-bound.md | 38 +++++++++++++++++++ 1 file changed, 38 insertions(+) create mode 100644 docs/research/bias-standard-error-wide-linear-admission-bound.md diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md new file mode 100644 index 000000000..6bd75e77e --- /dev/null +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -0,0 +1,38 @@ +# Bias standard-error wide-linear accumulator admission bound + +## Problem + +Issue #491 previously required a fixture where the two-limb O(n) exact pair-numerator reference would refuse because the normalized coefficient sum or normalized square sum overflowed `u128` while the O(n²) pair numerator still fit. That search target is inconsistent with the canonical anchor-relative integer representation used by the characterization. + +Let `c_i` be canonical nonnegative integer coefficients after subtracting the minimum represented value and removing the greatest common power-of-two unit. At least one `c_i` is zero. Define + +`P = sum_{i Date: Sun, 6 Sep 2026 08:01:27 +0900 Subject: [PATCH 498/576] docs(validation): correct wide-linear test budget --- docs/TEST_STRATEGY.md | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index bf2f9f1c1..2c6c8f37e 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -69,17 +69,18 @@ Validation Evidence numerical proofs that add asymptotic work or material alloca - retain realistic represented-input counterexamples and permutation/sign-mirror contracts for scientific correctness; - keep the existing exact residual/pairwise-subtraction, checked-integer, GCD-reduction, and exact midpoint authority fail-closed; -- compare the buffered O(n²) pair-record path, an allocation-free two-pass O(n²) reference, an algebraically equivalent narrow O(n) exact accumulator under a proved sufficient admission condition, a dependency-free two-limb wider-product O(n) characterization reference, and the viable narrow-O(n)-fast-path/buffered-pair-fallback hybrid; +- compare the buffered O(n²) pair-record path, an allocation-free two-pass O(n²) reference, an algebraically equivalent narrow O(n) exact accumulator under a proved sufficient admission condition, a dependency-free two-limb wider-product O(n) characterization reference, the current narrow-O(n)-fast-path/buffered-pair-fallback hybrid, and a `narrow O(n) -> Wide256 O(n) -> pairwise fail-closed fallback` candidate; - normalize the largest common power-of-two dyadic unit from exact anchor-relative coefficients before checked O(n) intermediates are judged. Raw-scale overflow is not a scientific or resource refusal when exact dyadic rescaling removes it; -- require an admitted/refused-set contract for any O(n) candidate. The predecessor `D=2^58, n=65` refusal was invalid because canonical normalization reduces the coefficients to zero/one and preserves the restored pair numerator `2^122`. The corrected strict-subset boundary uses odd `D=2^58+1`: both pair and normalized narrow linear kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits `u128` but the normalized narrow O(n) intermediates require 129 bits before cancellation; -- retain the wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`: on odd `D=2^58+1, n=65`, exact two-limb products and subtraction recover the same 123-bit pair numerator after the two 129-bit intermediates cancel. This proves that narrow-intermediate refusal is not scientific refusal, but it does not prove full-domain admission equivalence because normalized coefficient and square accumulation remain checked `u128`; -- therefore an O(n) production optimization must either fall back to the current pairwise proof on checked-intermediate refusal or use a separately justified wider checked-integer representation; narrow linear refusal must never silently narrow scientific admission; -- characterize checked-`u128` and wider-reference refusal as functions of sample count, canonical aligned dyadic diameter/exponent spread, and coefficient distribution rather than treating an integer cutoff or raw represented scale as a scientific boundary; +- require an admitted/refused-set contract for any O(n) candidate. The predecessor `D=2^58, n=65` refusal was invalid because canonical normalization reduces the coefficients to zero/one and preserves the restored pair numerator `2^122`. The corrected narrow-path strict-subset boundary uses odd `D=2^58+1`: both pair and normalized narrow linear kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits `u128` but the normalized narrow O(n) products require 129 bits before cancellation; +- retain the wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`: on odd `D=2^58+1, n=65`, exact two-limb products and subtraction recover the same 123-bit pair numerator after the two 129-bit intermediates cancel. This proves that narrow-intermediate refusal is not scientific refusal; +- retain accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb`: after canonical minimum anchoring, all coefficients are nonnegative integers and at least one is zero, so `Σc_i <= Σc_i² <= Σ(i>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence separately; field-width estimates alone are not allocation evidence; -- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing and to exercise three distinct boundary states: `D=2^58, n=65` must be a normalized narrow-linear admission, odd `D=2^58+1, n=64` must admit, and odd `D=2^58+1, n=65` must make the narrow O(n) refuse, the two-limb reference recover the exact pair numerator, and the production-shape hybrid exercise buffered pair fallback. The CSV must identify the geometry, kernel, normalized unit exponent, and whether hybrid fallback was actually used; -- keep RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3`, normalization repair `d423b57797b6f7f127e61e0679f9ee9841525c77`, restoration hardening `96f17c02edba0792f61e0e92167703a6ae4e40d0`, wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`, and wider-reference harness `0bd805d4b0304cf1f76344ae14b7f079b3dade17` in the exact evidence lineage; -- run the five-kernel harness in release mode on a recorded CPU/OS/Rust toolchain and retain raw CSV plus p95; unexecuted harness code is not performance evidence; +- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing. Its current odd `D=2^58+1, n=65` hybrid pair fallback is a comparison baseline, not a proof that pair allocation is necessary; the next candidate must route narrow refusal through `Wide256` before pair fallback and record the route explicitly; +- keep RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3`, normalization repair `d423b57797b6f7f127e61e0679f9ee9841525c77`, restoration hardening `96f17c02edba0792f61e0e92167703a6ae4e40d0`, wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`, wider-reference harness `0bd805d4b0304cf1f76344ae14b7f079b3dade17`, and accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb` in the exact evidence lineage; +- run the characterization harness in release mode on a recorded CPU/OS/Rust toolchain and retain raw CSV plus p95; unexecuted harness code is not performance evidence; - if a service/API buyer path is affected, measure the full applicable path and retain the `p95 <= 20 ms` target without shrinking samples, omitting proof work, or relying on unrealistic warm-cache setup; - arithmetic representability alone does not authorize a production sample-count budget. From dbd55e2c8dc4ee6ce06fd39c6d3d7e25874c90b2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 08:03:24 +0900 Subject: [PATCH 499/576] docs(validation): correct wide-linear operability budget --- docs/OPERABILITY.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index 8b0fb1f92..de82ff84a 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -68,18 +68,19 @@ The shared dyadic unit is a proof obligation. The predecessor characterization j The corrected narrow O(n) accumulator is still a strict sufficient subset of the current pair reference under `u128`; the valid boundary uses odd diameter `D=2^58+1`, whose common dyadic unit is one. Both kernels fit at `n=64`. At `n=65`, the exact pair numerator `64D^2` is a 123-bit `u128`, while both O(n) products require 129 bits before cancellation. A normalized narrow O(n) refusal therefore still cannot become a scientific refusal. -Wider-intermediate characterization `081000289f5a52e94863026d55696ee2a4daf923` now makes that distinction executable without introducing a production dependency. A test-only two-limb `Wide256` performs exact `u128 × u128` products and checked cancellation. On odd `D=2^58+1,n=65`, it recovers the same exact 123-bit pair numerator after the two 129-bit products cancel, while the narrow checked-`u128` O(n) path refuses. The characterization also fixes `(2^128-1)^2` as high limb `2^128-2`, low limb `1`. This wider reference remains bounded: normalized coefficient sums and square sums are still accumulated in checked `u128`, so its success on the odd boundary is not evidence of full-domain admission equivalence or arbitrary-precision capability. +Wider-intermediate characterization `081000289f5a52e94863026d55696ee2a4daf923` makes that distinction executable without introducing a production dependency. A test-only two-limb `Wide256` performs exact `u128 × u128` products and checked cancellation. On odd `D=2^58+1,n=65`, it recovers the same exact 123-bit pair numerator after the two 129-bit products cancel, while the narrow checked-`u128` O(n) path refuses. The characterization also fixes `(2^128-1)^2` as high limb `2^128-2`, low limb `1`. -Harness `crates/validation_core/examples/bias_se_exact_proof_budget.rs`, extended at `0bd805d4b0304cf1f76344ae14b7f079b3dade17`, compares five kernels without changing production behavior: buffered O(n²), allocation-free two-pass O(n²), normalized narrow checked O(n), the two-limb wider-product O(n) reference, and narrow `O(n) -> buffered pair fallback`. Before timing it requires exact restored-numerator equality. It exercises compact admitting geometries plus three boundary states: `D=2^58,n=65` must normalize and stay on the narrow linear fast path; odd `D=2^58+1,n=64` must admit; odd `D=2^58+1,n=65` must refuse the narrow linear path, be recovered by the wider-product reference, and make the production-shape hybrid actually use the buffered pair fallback. CSV output records geometry, kernel, normalized unit exponent, and `used_pairwise_fallback` so fallback execution cannot be inferred from sample count alone. +Accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb` removes a further false resource boundary. After canonical minimum anchoring, every coefficient `c_i` is a nonnegative integer and at least one coefficient is zero. Therefore `Σc_i <= Σc_i²`; the zero-anchor pair terms contain every `c_i²`, while all other pair-square terms are nonnegative, so `Σc_i² <= Σ(i buffered pair fallback`. Before timing it requires exact restored-numerator equality. It exercises compact admitting geometries plus three boundary states: `D=2^58,n=65` must normalize and stay on the narrow linear fast path; odd `D=2^58+1,n=64` must admit; odd `D=2^58+1,n=65` must refuse the narrow linear path and be recovered by the wider-product reference. The current hybrid still uses buffered pair fallback on that last boundary, which is retained as a comparison baseline rather than evidence that pair allocation is necessary. The next candidate must route `narrow O(n) -> Wide256 O(n) -> pairwise fail-closed fallback` and record which route actually executed. Before changing the production boundary, retain: - release-mode raw timing samples and p95 from `crates/validation_core/examples/bias_se_exact_proof_budget.rs`, with exact commit, CPU, OS, Rust toolchain, build flags, timing sample count, and cold/warm procedure; -- side-by-side buffered O(n²), allocation-free two-pass O(n²), normalized narrow O(n), two-limb wider-product O(n), and hybrid evidence, with exact equality of restored pair-square numerators before timing; -- hybrid timing covering the common-power normalized admission and the odd-diameter admitted/refused boundary, with `unit_exponent` and `used_pairwise_fallback` recorded in the raw CSV; +- side-by-side buffered O(n²), allocation-free two-pass O(n²), normalized narrow O(n), two-limb wider-product O(n), current hybrid, and `narrow -> Wide256 -> pair` candidate evidence, with exact equality of restored pair-square numerators before timing; +- route-aware timing covering the common-power normalized admission and odd-diameter admitted/refused boundary, including normalized unit exponent, whether the wider path was used, and whether pair fallback was used; - target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence rather than byte estimates inferred from field widths; -- admitted/refused-set comparison between the existing pairwise proof and every O(n) candidate across sample count, canonical aligned coefficient diameter/exponent spread, and coefficient distribution; proof refusal must fall back rather than alter scientific meaning; -- evidence for where the bounded two-limb reference itself refuses because coefficient or square accumulation exceeds `u128`; success at one 129-bit product boundary is not a global proof; +- admitted/refused-set comparison at the actual represented-input boundary across sample count, canonical aligned coefficient diameter/exponent spread, and coefficient distribution; verify the accumulator-bound theorem against conversion into canonical coefficients and classify any remaining refusal by full-width multiply/subtract, scale restoration, denominator/midpoint proof, or upstream exact-residual admission; - full service/API p95 when a buyer-facing path is affected, preserving the TEPP `p95 <= 20 ms` target without shrinking input, omitting proof work, or using an unrealistic cache-only setup. The exact pair-record count is `n(n-1)/2`; the current characterization locks 120 records at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and 4,997,541 at `n=3,162`. A two-pass O(n²) implementation may remove pair-record storage while preserving pair-enumeration proof shape, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its checked arithmetic fits a wider intermediate, and a narrow checked O(n) refusal is not allowed to narrow the current exact pairwise admission set. From 3136739460ef0c8e13c044a7e5b04891e4f4e23d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 08:57:54 +0900 Subject: [PATCH 500/576] test(validation): require narrow-wide-pair proof route --- .../examples/bias_se_exact_proof_budget.rs | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/crates/validation_core/examples/bias_se_exact_proof_budget.rs b/crates/validation_core/examples/bias_se_exact_proof_budget.rs index b17e70b5b..40d4b6928 100644 --- a/crates/validation_core/examples/bias_se_exact_proof_budget.rs +++ b/crates/validation_core/examples/bias_se_exact_proof_budget.rs @@ -314,6 +314,8 @@ fn assert_and_measure_geometry( let wide_product = pair_square_sum_linear_wide_product(values) .expect("wider-product linear reference stays within its declared budget"); let hybrid = pair_square_sum_hybrid(values).expect("hybrid result stays within u128"); + let wide_hybrid = pair_square_sum_wide_hybrid(values) + .expect("narrow-wide-pair hybrid result stays within u128"); let exact_pair_square_sum = restored_pair_square_sum(buffered) .expect("buffered result restores to exact pair-square sum"); assert_eq!( @@ -331,6 +333,11 @@ fn assert_and_measure_geometry( Some(exact_pair_square_sum), "hybrid must preserve the exact pair numerator" ); + assert_eq!( + restored_pair_square_sum(wide_hybrid), + Some(exact_pair_square_sum), + "narrow-wide-pair hybrid must preserve the exact pair numerator" + ); assert!( hybrid.used_pairwise_fallback == expect_hybrid_fallback, "hybrid fallback observation must match the declared geometry" @@ -353,6 +360,7 @@ fn assert_and_measure_geometry( ("quadratic_two_pass", pair_square_sum_quadratic_two_pass), ("linear_wide_product_reference", pair_square_sum_linear_wide_product), ("hybrid", pair_square_sum_hybrid), + ("hybrid_narrow_wide_pair", pair_square_sum_wide_hybrid), ]; if expect_linear_admission { kernels.push(("linear", pair_square_sum_linear)); From ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 08:58:37 +0900 Subject: [PATCH 501/576] fix(validation): route exact proof through wide product before pairs --- .../examples/bias_se_exact_proof_budget.rs | 87 +++++++++++++++---- 1 file changed, 70 insertions(+), 17 deletions(-) diff --git a/crates/validation_core/examples/bias_se_exact_proof_budget.rs b/crates/validation_core/examples/bias_se_exact_proof_budget.rs index 40d4b6928..fb8fffa62 100644 --- a/crates/validation_core/examples/bias_se_exact_proof_budget.rs +++ b/crates/validation_core/examples/bias_se_exact_proof_budget.rs @@ -3,13 +3,13 @@ //! This example compares checked-integer proof kernels on deterministic dyadic //! coefficients: a production-layout-shaped buffered O(n²) pair proof, an //! allocation-free two-pass O(n²) variant, an algebraically equivalent O(n) -//! sufficient accumulator, a two-limb wider-product O(n) reference, and the -//! viable hybrid shape that uses the narrow O(n) path only when it admits and -//! otherwise falls back to the buffered pair proof. The O(n) paths first remove -//! the shared power-of-two unit from anchor-relative coefficients so -//! checked-intermediate refusal is evaluated on the canonical dyadic grid rather -//! than on an arbitrary raw integer scale. This is characterization tooling, not -//! production admission and not buyer-path latency evidence by itself. +//! sufficient accumulator, a two-limb wider-product O(n) reference, the existing +//! narrow-to-pair hybrid, and a candidate narrow-to-wide-to-pair hybrid. The O(n) +//! paths first remove the shared power-of-two unit from anchor-relative +//! coefficients so checked-intermediate refusal is evaluated on the canonical +//! dyadic grid rather than on an arbitrary raw integer scale. This is +//! characterization tooling, not production admission and not buyer-path latency +//! evidence by itself. use std::hint::black_box; use std::mem::size_of; @@ -21,6 +21,7 @@ struct KernelObservation { unit_exponent: i32, scratch_records: usize, scratch_payload_bytes: usize, + used_wide_product: bool, used_pairwise_fallback: bool, } @@ -150,6 +151,7 @@ fn pair_square_sum_quadratic_buffered(values: &[u128]) -> Option Option Option Option Option { unit_exponent: i32::try_from(common_shift).ok()?, scratch_records: 0, scratch_payload_bytes: 0, + used_wide_product: false, used_pairwise_fallback: false, }) } @@ -243,6 +249,7 @@ fn pair_square_sum_linear_wide_product(values: &[u128]) -> Option Option { Some(observation) } +fn pair_square_sum_wide_hybrid(values: &[u128]) -> Option { + if let Some(observation) = pair_square_sum_linear(values) { + return Some(observation); + } + if let Some(observation) = pair_square_sum_linear_wide_product(values) { + return Some(observation); + } + let mut observation = pair_square_sum_quadratic_buffered(values)?; + observation.used_pairwise_fallback = true; + Some(observation) +} + fn restored_pair_square_sum(observation: KernelObservation) -> Option { let shift = observation.unit_exponent.checked_mul(2)?.unsigned_abs(); multiply_by_power_of_two(observation.aligned_pair_square_sum, shift) @@ -290,12 +309,13 @@ fn emit( observation: KernelObservation, ) { println!( - "{geometry},{sample_count},{kernel_name},{},{samples},{},{},{},{},{}", + "{geometry},{sample_count},{kernel_name},{},{samples},{},{},{},{},{},{}", p95.as_nanos(), observation.unit_exponent, observation.scratch_records, observation.scratch_payload_bytes, size_of::>(), + observation.used_wide_product, observation.used_pairwise_fallback ); } @@ -306,6 +326,8 @@ fn assert_and_measure_geometry( samples: usize, expect_linear_admission: bool, expect_hybrid_fallback: bool, + expect_wide_hybrid_wide_product: bool, + expect_wide_hybrid_pair_fallback: bool, ) { let buffered = pair_square_sum_quadratic_buffered(values) .expect("buffered quadratic result stays within u128"); @@ -331,16 +353,27 @@ fn assert_and_measure_geometry( assert_eq!( restored_pair_square_sum(hybrid), Some(exact_pair_square_sum), - "hybrid must preserve the exact pair numerator" + "narrow-pair hybrid must preserve the exact pair numerator" ); assert_eq!( restored_pair_square_sum(wide_hybrid), Some(exact_pair_square_sum), "narrow-wide-pair hybrid must preserve the exact pair numerator" ); - assert!( - hybrid.used_pairwise_fallback == expect_hybrid_fallback, - "hybrid fallback observation must match the declared geometry" + assert_eq!( + hybrid.used_pairwise_fallback, + expect_hybrid_fallback, + "narrow-pair fallback observation must match the declared geometry" + ); + assert_eq!( + wide_hybrid.used_wide_product, + expect_wide_hybrid_wide_product, + "wide-route observation must match the declared geometry" + ); + assert_eq!( + wide_hybrid.used_pairwise_fallback, + expect_wide_hybrid_pair_fallback, + "wide-hybrid pair fallback observation must match the declared geometry" ); match pair_square_sum_linear(values) { @@ -359,7 +392,7 @@ fn assert_and_measure_geometry( ("quadratic_buffered", pair_square_sum_quadratic_buffered), ("quadratic_two_pass", pair_square_sum_quadratic_two_pass), ("linear_wide_product_reference", pair_square_sum_linear_wide_product), - ("hybrid", pair_square_sum_hybrid), + ("hybrid_narrow_pair", pair_square_sum_hybrid), ("hybrid_narrow_wide_pair", pair_square_sum_wide_hybrid), ]; if expect_linear_admission { @@ -386,11 +419,19 @@ fn main() { .max(1); println!( - "geometry,sample_count,kernel,p95_ns,timing_samples,unit_exponent,scratch_records,scratch_payload_bytes,pair_record_size_bytes,used_pairwise_fallback" + "geometry,sample_count,kernel,p95_ns,timing_samples,unit_exponent,scratch_records,scratch_payload_bytes,pair_record_size_bytes,used_wide_product,used_pairwise_fallback" ); for sample_count in [16_usize, 64, 256, 1_024, 2_047] { let values = fixture(sample_count); - assert_and_measure_geometry("compact_admit", &values, samples, true, false); + assert_and_measure_geometry( + "compact_admit", + &values, + samples, + true, + false, + false, + false, + ); } let power_of_two_values = boundary_fixture(65, 1_u128 << 58); @@ -400,18 +441,30 @@ fn main() { samples, true, false, + false, + false, ); let odd_diameter = (1_u128 << 58) + 1; let odd_64 = boundary_fixture(64, odd_diameter); - assert_and_measure_geometry("odd_boundary_admit", &odd_64, samples, true, false); + assert_and_measure_geometry( + "odd_boundary_admit", + &odd_64, + samples, + true, + false, + false, + false, + ); let odd_65 = boundary_fixture(65, odd_diameter); assert_and_measure_geometry( - "odd_boundary_pair_fallback", + "odd_boundary_wide_recovery", &odd_65, samples, false, true, + true, + false, ); } From 5ad7021c80a8dc571568b33be40fe3341bed2d21 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 08:59:03 +0900 Subject: [PATCH 502/576] docs(changelog): record narrow-wide-pair proof route --- ...validation-bias-exact-proof-budget-characterization.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md index 4c95f5a4f..ffa8b990d 100644 --- a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md +++ b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md @@ -3,6 +3,8 @@ - Correct the pair-record resource evidence for 3,162 observations to 4,997,541 records and lock exact pair counts in a Rust characterization contract. - Distinguish the current minimum-shifted O(n) `u128` intermediate envelope (`n=2,047` at aligned diameter `2^53`) from the wider exact pair-square numerator envelope (`n=4,095` for the same diameter); neither is a production sample-count budget. - Normalize the shared power-of-two dyadic unit before judging checked O(n) intermediates. The former `D=2^58, n=65` refusal was a characterization artifact: factoring the common `2^58` unit reduces the aligned coefficients to zero/one and preserves the exact restored numerator `2^122` without pair fallback. -- Preserve the non-equivalence finding with a normalized counterexample rather than the raw-scale artifact. With one zero and the remaining coefficients at odd diameter `D=2^58+1`, the common dyadic unit is one; both kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits `u128` but the checked O(n) intermediates overflow before cancellation. -- Extend the release-mode characterization harness to compare the buffered O(n²) pair layout, an allocation-free two-pass O(n²) reference, the normalized O(n) accumulator, and the viable hybrid `O(n) -> buffered pair fallback` shape. It now measures a common-power normalization admission plus the odd-diameter `n=64` admission / `n=65` fallback boundary, asserts exact restored-numerator equality before timing, reports whether the hybrid actually used the pairwise fallback, and retains target-specific pair-record size, scratch-record capacity, and scratch payload bytes. -- Keep production `bias_standard_error` admission unchanged at `n=4..=16`; the new hybrid is measurement tooling only. A production change still requires recorded release-mode CPU/allocation/RSS results, a wider-reference assessment, exact-head CI/review evidence, and applicable buyer-path p95 evidence. +- Preserve the non-equivalence finding with a normalized counterexample rather than the raw-scale artifact. With one zero and the remaining coefficients at odd diameter `D=2^58+1`, the common dyadic unit is one; both pair and narrow O(n) kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits `u128` but the narrow O(n) products require 129-bit intermediates before cancellation. +- Record the canonical accumulator bound `Σc_i <= Σc_i² <= Σ(i Wide256 O(n) -> buffered pair fail-closed fallback`. RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` requires the missing route; repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` implements it and records both wide-product selection and pairwise fallback separately in CSV. +- The corrected harness keeps the existing narrow-to-pair hybrid for comparison. On odd `D=2^58+1, n=65`, that predecessor hybrid still allocates the pair buffer, while the new narrow-to-wide-to-pair candidate must recover the same exact numerator through `Wide256` with `used_wide_product=true` and `used_pairwise_fallback=false`. Power-of-two-normalized `n=65` and odd `n=64` remain narrow-path admissions. +- Keep production `bias_standard_error` admission unchanged at `n=4..=16`; all O(n)/Wide256 hybrids remain characterization tooling only. A production change still requires recorded Rust 1.98.0 release-mode CPU/raw CSV, allocator/RSS evidence, represented-input admission comparison, applicable buyer-path p95 evidence, exact-head CI/security/documentation GREEN, and qualifying independent current-head review. From 387be8455eeb6971b4c3a823d8db12124e9e0138 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 08:59:20 +0900 Subject: [PATCH 503/576] docs(research): trace narrow-wide-pair exact-proof candidate --- ...andard-error-wide-linear-admission-bound.md | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index 6bd75e77e..47592453a 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -20,19 +20,25 @@ If the exact canonical pair numerator `P` fits `u128`, both O(n) accumulators ne The normalized narrow O(n) implementation can still refuse while `P` fits because the products `n*S2` and `S1^2` may require more than 128 bits before cancellation. The existing odd-diameter fixture `D = 2^58 + 1`, `n = 65` demonstrates that case: both products require 129 bits while the exact pair numerator is 123 bits. The dependency-free `Wide256` reference exists specifically to preserve those full-width products and their exact subtraction. -This proof narrows the remaining resource question. For the canonical `u128` coefficient domain, future refusal analysis should focus on correctness of the full-width multiply/subtract, exact restoration of the common dyadic unit, reduced scientific denominator and midpoint proof, and the upstream represented-residual admission contract. Searching for a pair-admitted coefficient-sum or square-sum overflow case is no longer a valid acceptance criterion. +RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` made the next missing delivery artifact executable by requiring a narrow-to-wide-to-pair route in the resource harness before that route existed. Repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` adds the candidate route `narrow O(n) -> Wide256 O(n) -> buffered pair fail-closed fallback` without changing production admission. The harness retains the predecessor `narrow O(n) -> buffered pair` hybrid as a comparison baseline. -The executable characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. It covers deterministic compact fixtures, the power-of-two normalization boundary, the odd `2^58+1` / `n=65` narrow-refusal boundary, and every base-four composition for sample counts 2 through 7. +For odd `D = 2^58 + 1`, `n = 65`, the predecessor hybrid must still select the pair fallback because its narrow products overflow. The new candidate must select the wider-product route, recover the same exact restored numerator as both O(n²) references, and report `used_wide_product=true` with `used_pairwise_fallback=false`. The power-of-two-normalized `D=2^58,n=65` geometry and odd `n=64` geometry remain narrow-path admissions. Separate route observability matters because a correct exact numerator does not by itself show whether the candidate avoided O(n²) allocation. + +This proof and route repair narrow the remaining resource question. For the canonical `u128` coefficient domain, future refusal analysis should focus on correctness of the full-width multiply/subtract, exact restoration of the common dyadic unit, reduced scientific denominator and midpoint proof, and the upstream represented-residual admission contract. Searching for a pair-admitted coefficient-sum or square-sum overflow case is no longer a valid acceptance criterion. + +The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The executable timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it now records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. ## Decision -Do not promote the wider O(n) reference into production solely from this arithmetic proof. Production `validation_core::bias_standard_error` remains bounded to `n=4..=16` until current-head Rust/rustdoc/coverage/security checks execute, release-mode resource evidence is recorded, and the wider path is compared against the present pairwise admission semantics. The proof removes an impossible research task; it does not waive verification, release, or independent-review gates. +Do not promote the wider O(n) route into production solely from this arithmetic proof or the new characterization path. Production `validation_core::bias_standard_error` remains bounded to `n=4..=16`. A production change requires recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, represented-input admission comparison, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. Pairwise proof remains the fail-closed reference until those gates are satisfied. ## Traceability - Issue: #491 - Landing PR: #488 -- Characterization: `b7e4da353ac58069afd73ee7c0e8427d49993fdb` -- CHANGELOG fragment: `CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md` +- Accumulator-bound characterization: `b7e4da353ac58069afd73ee7c0e8427d49993fdb` +- Narrow-wide-pair RED: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` +- Narrow-wide-pair repair: `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` +- CHANGELOG fragment: `CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md` - Production module under decision: `crates/validation_core/src/bias_se.rs` -- Existing exact-proof budget harness: `crates/validation_core/examples/bias_se_exact_proof_budget.rs` +- Exact-proof budget harness: `crates/validation_core/examples/bias_se_exact_proof_budget.rs` From a2c776eef1e61138fe8e143f4e732f1bea5e0b73 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 09:03:39 +0900 Subject: [PATCH 504/576] docs(test): make wide-hybrid route executable authority --- docs/TEST_STRATEGY.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index 2c6c8f37e..bce13bc18 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -69,7 +69,7 @@ Validation Evidence numerical proofs that add asymptotic work or material alloca - retain realistic represented-input counterexamples and permutation/sign-mirror contracts for scientific correctness; - keep the existing exact residual/pairwise-subtraction, checked-integer, GCD-reduction, and exact midpoint authority fail-closed; -- compare the buffered O(n²) pair-record path, an allocation-free two-pass O(n²) reference, an algebraically equivalent narrow O(n) exact accumulator under a proved sufficient admission condition, a dependency-free two-limb wider-product O(n) characterization reference, the current narrow-O(n)-fast-path/buffered-pair-fallback hybrid, and a `narrow O(n) -> Wide256 O(n) -> pairwise fail-closed fallback` candidate; +- compare the buffered O(n²) pair-record path, an allocation-free two-pass O(n²) reference, an algebraically equivalent narrow O(n) exact accumulator under a proved sufficient admission condition, a dependency-free two-limb wider-product O(n) characterization reference, the predecessor narrow-O(n)-fast-path/buffered-pair-fallback hybrid, and the implemented `narrow O(n) -> Wide256 O(n) -> pairwise fail-closed fallback` candidate; - normalize the largest common power-of-two dyadic unit from exact anchor-relative coefficients before checked O(n) intermediates are judged. Raw-scale overflow is not a scientific or resource refusal when exact dyadic rescaling removes it; - require an admitted/refused-set contract for any O(n) candidate. The predecessor `D=2^58, n=65` refusal was invalid because canonical normalization reduces the coefficients to zero/one and preserves the restored pair numerator `2^122`. The corrected narrow-path strict-subset boundary uses odd `D=2^58+1`: both pair and normalized narrow linear kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits `u128` but the normalized narrow O(n) products require 129 bits before cancellation; - retain the wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`: on odd `D=2^58+1, n=65`, exact two-limb products and subtraction recover the same 123-bit pair numerator after the two 129-bit intermediates cancel. This proves that narrow-intermediate refusal is not scientific refusal; @@ -78,9 +78,9 @@ Validation Evidence numerical proofs that add asymptotic work or material alloca - characterize checked-`u128` and wider-reference behavior as functions of sample count, canonical aligned dyadic diameter/exponent spread, and coefficient distribution rather than treating an integer cutoff or raw represented scale as a scientific boundary; - keep the normalized O(n) distribution-independent intermediate envelope distinct from the exact pair-square numerator envelope: at aligned coefficient diameter `2^53`, the characterized sufficient bounds are `n<=2_047` and `n<=4_095` respectively, and neither is a production budget; - record exact pair counts, target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence separately; field-width estimates alone are not allocation evidence; -- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing. Its current odd `D=2^58+1, n=65` hybrid pair fallback is a comparison baseline, not a proof that pair allocation is necessary; the next candidate must route narrow refusal through `Wide256` before pair fallback and record the route explicitly; -- keep RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3`, normalization repair `d423b57797b6f7f127e61e0679f9ee9841525c77`, restoration hardening `96f17c02edba0792f61e0e92167703a6ae4e40d0`, wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`, wider-reference harness `0bd805d4b0304cf1f76344ae14b7f079b3dade17`, and accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb` in the exact evidence lineage; -- run the characterization harness in release mode on a recorded CPU/OS/Rust toolchain and retain raw CSV plus p95; unexecuted harness code is not performance evidence; +- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing. RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` required the missing narrow→Wide256→pair route and repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` implements it. On odd `D=2^58+1, n=65`, the predecessor hybrid remains a pair-allocation comparison baseline while the new candidate must recover through `Wide256` with `used_wide_product=true` and `used_pairwise_fallback=false`; the harness records both route flags explicitly; +- keep RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3`, normalization repair `d423b57797b6f7f127e61e0679f9ee9841525c77`, restoration hardening `96f17c02edba0792f61e0e92167703a6ae4e40d0`, wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`, wider-reference harness `0bd805d4b0304cf1f76344ae14b7f079b3dade17`, accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb`, narrow-wide-pair RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d`, and narrow-wide-pair repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` in the exact evidence lineage; +- run the characterization harness in release mode on a recorded CPU/OS/Rust 1.98.0 toolchain and retain raw CSV plus p95; unexecuted harness code is not performance evidence; - if a service/API buyer path is affected, measure the full applicable path and retain the `p95 <= 20 ms` target without shrinking samples, omitting proof work, or relying on unrealistic warm-cache setup; - arithmetic representability alone does not authorize a production sample-count budget. From abd95474396f8a0f2e5dca6718f2bcf582b51b45 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 09:04:59 +0900 Subject: [PATCH 505/576] docs(operability): route bias proof through Wide256 before pairs --- docs/OPERABILITY.md | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index de82ff84a..b87d1258f 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -72,20 +72,22 @@ Wider-intermediate characterization `081000289f5a52e94863026d55696ee2a4daf923` m Accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb` removes a further false resource boundary. After canonical minimum anchoring, every coefficient `c_i` is a nonnegative integer and at least one coefficient is zero. Therefore `Σc_i <= Σc_i²`; the zero-anchor pair terms contain every `c_i²`, while all other pair-square terms are nonnegative, so `Σc_i² <= Σ(i buffered pair fallback`. Before timing it requires exact restored-numerator equality. It exercises compact admitting geometries plus three boundary states: `D=2^58,n=65` must normalize and stay on the narrow linear fast path; odd `D=2^58+1,n=64` must admit; odd `D=2^58+1,n=65` must refuse the narrow linear path and be recovered by the wider-product reference. The current hybrid still uses buffered pair fallback on that last boundary, which is retained as a comparison baseline rather than evidence that pair allocation is necessary. The next candidate must route `narrow O(n) -> Wide256 O(n) -> pairwise fail-closed fallback` and record which route actually executed. +RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` makes the next resource route executable by requiring a `narrow O(n) -> Wide256 O(n) -> pairwise fail-closed fallback` kernel before it existed. Repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` implements that candidate in `crates/validation_core/examples/bias_se_exact_proof_budget.rs` without changing production arithmetic. The predecessor narrow→buffered-pair hybrid remains in the same harness so the cost of unnecessary pair allocation is measurable rather than inferred. + +The corrected harness now compares six resource shapes: buffered O(n²), allocation-free two-pass O(n²), normalized narrow checked O(n), the two-limb wider-product O(n) reference, the predecessor narrow→pair hybrid, and the new narrow→Wide256→pair hybrid. Before timing it requires exact restored-numerator equality. It records `used_wide_product` and `used_pairwise_fallback` independently. `D=2^58,n=65` must normalize and remain on the narrow route; odd `D=2^58+1,n=64` must remain narrow; odd `D=2^58+1,n=65` makes the predecessor hybrid use pair fallback while the new candidate must recover through `Wide256` with no pair allocation. This is route characterization, not proof that the new hybrid is production-ready. Before changing the production boundary, retain: -- release-mode raw timing samples and p95 from `crates/validation_core/examples/bias_se_exact_proof_budget.rs`, with exact commit, CPU, OS, Rust toolchain, build flags, timing sample count, and cold/warm procedure; -- side-by-side buffered O(n²), allocation-free two-pass O(n²), normalized narrow O(n), two-limb wider-product O(n), current hybrid, and `narrow -> Wide256 -> pair` candidate evidence, with exact equality of restored pair-square numerators before timing; -- route-aware timing covering the common-power normalized admission and odd-diameter admitted/refused boundary, including normalized unit exponent, whether the wider path was used, and whether pair fallback was used; +- release-mode raw timing samples and p95 from `crates/validation_core/examples/bias_se_exact_proof_budget.rs`, with exact commit, CPU, OS, Rust 1.98.0 toolchain, build flags, timing sample count, and cold/warm procedure; +- side-by-side buffered O(n²), allocation-free two-pass O(n²), normalized narrow O(n), two-limb wider-product O(n), predecessor narrow→pair hybrid, and narrow→Wide256→pair candidate evidence, with exact equality of restored pair-square numerators before timing; +- route-aware timing covering common-power normalized admission and odd-diameter admitted/refused boundaries, including normalized unit exponent, `used_wide_product`, and `used_pairwise_fallback`; - target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence rather than byte estimates inferred from field widths; - admitted/refused-set comparison at the actual represented-input boundary across sample count, canonical aligned coefficient diameter/exponent spread, and coefficient distribution; verify the accumulator-bound theorem against conversion into canonical coefficients and classify any remaining refusal by full-width multiply/subtract, scale restoration, denominator/midpoint proof, or upstream exact-residual admission; - full service/API p95 when a buyer-facing path is affected, preserving the TEPP `p95 <= 20 ms` target without shrinking input, omitting proof work, or using an unrealistic cache-only setup. The exact pair-record count is `n(n-1)/2`; the current characterization locks 120 records at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and 4,997,541 at `n=3,162`. A two-pass O(n²) implementation may remove pair-record storage while preserving pair-enumeration proof shape, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its checked arithmetic fits a wider intermediate, and a narrow checked O(n) refusal is not allowed to narrow the current exact pairwise admission set. -No release-mode timing numbers are currently authoritative. The local execution environment for this correction does not provide the required Rust 1.98.0 toolchain, and hosted exact-head jobs have not produced a benchmark artifact. Unexecuted timing harnesses and branch-only resource characterization remain supporting evidence only. +No release-mode timing numbers are currently authoritative. The current execution environment does not provide the required Rust 1.98.0 toolchain, and hosted exact-head jobs have not produced a benchmark artifact. Unexecuted timing harnesses and branch-only resource characterization remain supporting evidence only. ## Model release/cutover From d3f5c85c638b9ad7cd3ad3c3db6d450bb8b4ceaf Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 10:20:30 +0900 Subject: [PATCH 506/576] test(validation): document six-observation exact SE contract --- ...standard_error_six_observation_pair_distance_contract.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/validation_core/tests/bias_standard_error_six_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_six_observation_pair_distance_contract.rs index 60eacbc6a..eea906a3c 100644 --- a/crates/validation_core/tests/bias_standard_error_six_observation_pair_distance_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_six_observation_pair_distance_contract.rs @@ -1,3 +1,9 @@ +//! Regression contract for exact six-observation bias standard-error rounding. +//! +//! The represented-input fixture requires the bounded pair-distance midpoint proof +//! to remain permutation- and sign-invariant where the translated floating-moment +//! fallback rounds one binary64 ULP high. + use validation_core::bias_standard_error; fn assert_six_observation_pair_distance_contract(recovered: [f64; 6]) { From ee0ae9f19da76036ac72eb5fc3a8e4e56f04f670 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 10:20:43 +0900 Subject: [PATCH 507/576] test(validation): document Wilson exact-count rounding contract --- .../tests/wilson_coverage_ratio_rounding_contract.rs | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs b/crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs index ec97604b2..6fbf42d5e 100644 --- a/crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs +++ b/crates/validation_core/tests/wilson_coverage_ratio_rounding_contract.rs @@ -1,3 +1,9 @@ +//! Regression contract for exact-count Wilson coverage ratio rounding. +//! +//! Durable integer provenance must determine both the empirical coverage ratio and +//! the Wilson projection before binary64 rounding, including counts beyond the exact +//! integer range of a standalone `f64` conversion. + use validation_core::WilsonCoverageEvidenceV1; #[test] From b8a4efcf6e2705d7beaaa27d865d6d5d16784f21 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 10:29:41 +0900 Subject: [PATCH 508/576] ci(validation): materialize pinned rustfmt evidence --- .../workflows/validation-rustfmt-evidence.yml | 49 +++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 .github/workflows/validation-rustfmt-evidence.yml diff --git a/.github/workflows/validation-rustfmt-evidence.yml b/.github/workflows/validation-rustfmt-evidence.yml new file mode 100644 index 000000000..ab28bba6c --- /dev/null +++ b/.github/workflows/validation-rustfmt-evidence.yml @@ -0,0 +1,49 @@ +name: Validation Rustfmt Evidence + +on: + push: + branches: + - fix/validation-bias-overflow-safe-mean + paths: + - "crates/**" + - ".github/workflows/validation-rustfmt-evidence.yml" + +permissions: + contents: read + +concurrency: + group: validation-rustfmt-evidence-${{ github.ref }} + cancel-in-progress: true + +jobs: + format: + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - name: Checkout exact branch head + uses: actions/checkout@631c942040754b6e095e929c1677c07e10ed4f87 + with: + persist-credentials: false + - name: Install pinned Rust formatter + run: rustup toolchain install 1.98.0 --profile minimal --component rustfmt + - name: Materialize canonical rustfmt output + run: | + set -euo pipefail + cargo +1.98.0 fmt --all + git diff --check + git diff --binary > "${RUNNER_TEMP}/rustfmt.patch" + git diff --name-only --diff-filter=ACM -z | tar --null -T - -cf "${RUNNER_TEMP}/rustfmt-tree.tar" + git diff --name-only --diff-filter=ACM > "${RUNNER_TEMP}/rustfmt-files.txt" + test -s "${RUNNER_TEMP}/rustfmt.patch" + test -s "${RUNNER_TEMP}/rustfmt-files.txt" + - name: Upload immutable formatting evidence + uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: validation-rustfmt-${{ github.sha }} + path: | + ${{ runner.temp }}/rustfmt.patch + ${{ runner.temp }}/rustfmt-tree.tar + ${{ runner.temp }}/rustfmt-files.txt + retention-days: 1 + if-no-files-found: error + compression-level: 0 From f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 10:40:50 +0900 Subject: [PATCH 509/576] test(validation): require pair-to-Wide256 admission proof --- ...or_wide_linear_admission_bound_characterization.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs b/crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs index 72049a69f..2037b0064 100644 --- a/crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs +++ b/crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs @@ -1,3 +1,5 @@ +//! Characterizes exact-proof admission bounds for canonical bias-standard-error coefficients. + fn deterministic_compact_fixture(sample_count: usize) -> Vec { (0..sample_count) .map(|index| { @@ -50,7 +52,9 @@ fn assert_pair_admission_bounds_linear_accumulators(values: &[u128]) { let pair_sum = pair_square_sum(values).expect("fixture pair numerator fits u128"); let (coefficient_sum, square_sum, common_shift) = normalized_linear_terms(values).expect("pair-admitted normalized terms fit u128"); - let squared_shift = common_shift.checked_mul(2).expect("dyadic square shift fits u32"); + let squared_shift = common_shift + .checked_mul(2) + .expect("dyadic square shift fits u32"); let normalized_pair_sum = pair_sum >> squared_shift; let restored_pair_sum = normalized_pair_sum .checked_shl(squared_shift) @@ -96,3 +100,8 @@ fn pair_admitted_bound_holds_across_small_integer_composition_space() { } } } + +#[test] +fn pair_admission_implies_wide_linear_product_capacity() { + assert_pair_admission_implies_wide_linear_product_capacity(); +} From e9a7dee29afb97542bfe2965f850c8ab5a34368e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 10:41:18 +0900 Subject: [PATCH 510/576] fix(validation): prove pair admission fits Wide256 products --- ...linear_admission_bound_characterization.rs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) diff --git a/crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs b/crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs index 2037b0064..12ff965fb 100644 --- a/crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs +++ b/crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs @@ -48,6 +48,30 @@ fn normalized_linear_terms(values: &[u128]) -> Option<(u128, u128, u32)> { Some((coefficient_sum, square_sum, common_shift)) } +fn product_bit_upper_bound(left: u128, right: u128) -> u32 { + let left_bits = u128::BITS - left.leading_zeros(); + let right_bits = u128::BITS - right.leading_zeros(); + left_bits.saturating_add(right_bits) +} + +fn assert_pair_admission_implies_wide_linear_product_capacity() { + assert!( + usize::BITS <= u128::BITS, + "the supported target must convert Vec length to u128 without truncation" + ); + + // Pair admission gives P <= u128::MAX. For canonical nonnegative + // anchor-relative coefficients with at least one zero, S1 <= S2 <= P. + // Therefore both linear-identity products are products of two u128 values: + // n*S2 and S1*S1. Their exact bit-width upper bound is 256, which is exactly + // the capacity of the two-limb Wide256 characterization reference. + assert_eq!( + product_bit_upper_bound(u128::MAX, u128::MAX), + 256, + "the extremal u128-by-u128 product requires but does not exceed 256 bits" + ); +} + fn assert_pair_admission_bounds_linear_accumulators(values: &[u128]) { let pair_sum = pair_square_sum(values).expect("fixture pair numerator fits u128"); let (coefficient_sum, square_sum, common_shift) = @@ -69,6 +93,17 @@ fn assert_pair_admission_bounds_linear_accumulators(values: &[u128]) { square_sum <= normalized_pair_sum, "because at least one anchor coefficient is zero, every c_i^2 occurs in the exact pair numerator" ); + assert!( + product_bit_upper_bound( + u128::try_from(values.len()).expect("supported Vec length fits u128"), + square_sum, + ) <= 256, + "pair-admitted n*S2 cannot exceed Wide256 product capacity" + ); + assert!( + product_bit_upper_bound(coefficient_sum, coefficient_sum) <= 256, + "pair-admitted S1^2 cannot exceed Wide256 product capacity" + ); } #[test] From 180de9ab5d234e682312e85150ba176b11ac292a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 10:43:00 +0900 Subject: [PATCH 511/576] docs(validation): record Wide256 admission bound --- CHANGELOG.d/validation-wide256-admission-bound.md | 1 + 1 file changed, 1 insertion(+) create mode 100644 CHANGELOG.d/validation-wide256-admission-bound.md diff --git a/CHANGELOG.d/validation-wide256-admission-bound.md b/CHANGELOG.d/validation-wide256-admission-bound.md new file mode 100644 index 000000000..5093ab99f --- /dev/null +++ b/CHANGELOG.d/validation-wide256-admission-bound.md @@ -0,0 +1 @@ +Characterized the bias-standard-error exact-proof resource bound: once canonical pair-distance numerator admission fits `u128`, the normalized linear accumulators also fit `u128` and both cancellation products fit the dependency-free two-limb `Wide256` reference. Production `bias_standard_error` admission remains unchanged at `n=4..=16` pending exact-head validation and measured release-mode evidence. From 1d85c82c975fe4a1502148439bff42f74ae3d4a1 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 10:43:40 +0900 Subject: [PATCH 512/576] docs(validation): consolidate Wide256 admission note --- CHANGELOG.d/validation-wide256-admission-bound.md | 1 - 1 file changed, 1 deletion(-) delete mode 100644 CHANGELOG.d/validation-wide256-admission-bound.md diff --git a/CHANGELOG.d/validation-wide256-admission-bound.md b/CHANGELOG.d/validation-wide256-admission-bound.md deleted file mode 100644 index 5093ab99f..000000000 --- a/CHANGELOG.d/validation-wide256-admission-bound.md +++ /dev/null @@ -1 +0,0 @@ -Characterized the bias-standard-error exact-proof resource bound: once canonical pair-distance numerator admission fits `u128`, the normalized linear accumulators also fit `u128` and both cancellation products fit the dependency-free two-limb `Wide256` reference. Production `bias_standard_error` admission remains unchanged at `n=4..=16` pending exact-head validation and measured release-mode evidence. From f236dbed70197b1c540f9eee496423b34fe9c5f5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 10:43:47 +0900 Subject: [PATCH 513/576] docs(validation): tighten Wide256 admission bound --- ...lidation-bias-standard-error-wide-linear-admission-bound.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md index 496e065a2..037bacb14 100644 --- a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md +++ b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md @@ -1,3 +1,4 @@ ### Validation -- Corrected issue #491's exact-proof resource model: once anchor-relative dyadic coefficients are canonicalized to nonnegative integers with at least one zero, any exact pair-square numerator that fits `u128` also bounds both the coefficient sum and square sum (`sum(c_i) <= sum(c_i^2) <= sum_{i Date: Sun, 6 Sep 2026 10:44:04 +0900 Subject: [PATCH 514/576] docs(research): prove Wide256 product capacity --- ...ndard-error-wide-linear-admission-bound.md | 28 +++++++++++++------ 1 file changed, 19 insertions(+), 9 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index 47592453a..b09a81177 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -1,4 +1,4 @@ -# Bias standard-error wide-linear accumulator admission bound +# Bias standard-error wide-linear admission bound ## Problem @@ -16,29 +16,39 @@ For every nonnegative integer `c_i`, `c_i <= c_i^2`. Therefore `S1 <= S2`. Becau If the exact canonical pair numerator `P` fits `u128`, both O(n) accumulators necessarily fit `u128`. A pair-admitted fixture cannot fail the wider O(n) reference solely because `S1` or `S2` overflowed. -## Consequence for #491 +## Full-width product bound + +The normalized narrow O(n) implementation can still refuse while `P` fits because the products `n*S2` and `S1^2` may require more than 128 bits before cancellation. The odd-diameter fixture `D = 2^58 + 1`, `n = 65` demonstrates that case: both products require 129 bits while the exact pair numerator is 123 bits. + +That narrow refusal does not imply that a wider product requires arbitrary precision. On supported targets the sample count is converted from `usize` to `u128`; pair admission gives `S2 <= P <= u128::MAX` and `S1 <= S2 <= u128::MAX`. Consequently each exact cancellation product is a product of two `u128` values. Its maximum width is 256 bits: `(2^128 - 1)^2 < 2^256`. The dependency-free two-limb `Wide256` product representation is therefore wide enough for every canonical coefficient set whose exact pair numerator is admitted by `u128`. -The normalized narrow O(n) implementation can still refuse while `P` fits because the products `n*S2` and `S1^2` may require more than 128 bits before cancellation. The existing odd-diameter fixture `D = 2^58 + 1`, `n = 65` demonstrates that case: both products require 129 bits while the exact pair numerator is 123 bits. The dependency-free `Wide256` reference exists specifically to preserve those full-width products and their exact subtraction. +This is a width theorem, not yet a production-equivalence claim. The implementation still has to prove that normalization, full-width multiplication/subtraction, dyadic restoration, reduced denominator/midpoint rounding, and upstream represented-residual admission compose without introducing a stricter refusal than the existing pair proof. + +RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` made that product-capacity theorem executable by adding a characterization test that referenced a not-yet-defined proof helper. Repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e` adds the helper and checks the 256-bit extremum together with the known compact, common-power, odd-boundary, and exhaustive small-integer composition geometries. + +## Consequence for #491 -RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` made the next missing delivery artifact executable by requiring a narrow-to-wide-to-pair route in the resource harness before that route existed. Repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` adds the candidate route `narrow O(n) -> Wide256 O(n) -> buffered pair fail-closed fallback` without changing production admission. The harness retains the predecessor `narrow O(n) -> buffered pair` hybrid as a comparison baseline. +The existing candidate route remains `narrow O(n) -> Wide256 O(n) -> buffered pair fail-closed fallback`, introduced by RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` and repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5`. The predecessor narrow-to-pair hybrid remains a comparison baseline. -For odd `D = 2^58 + 1`, `n = 65`, the predecessor hybrid must still select the pair fallback because its narrow products overflow. The new candidate must select the wider-product route, recover the same exact restored numerator as both O(n²) references, and report `used_wide_product=true` with `used_pairwise_fallback=false`. The power-of-two-normalized `D=2^58,n=65` geometry and odd `n=64` geometry remain narrow-path admissions. Separate route observability matters because a correct exact numerator does not by itself show whether the candidate avoided O(n²) allocation. +For odd `D = 2^58 + 1`, `n = 65`, the predecessor hybrid must still select the pair fallback because its narrow products overflow. The newer candidate must select the wider-product route, recover the same exact restored numerator as both O(n²) references, and report `used_wide_product=true` with `used_pairwise_fallback=false`. The power-of-two-normalized `D=2^58,n=65` geometry and odd `n=64` geometry remain narrow-path admissions. Separate route observability matters because a correct exact numerator does not by itself show whether the candidate avoided O(n²) allocation. -This proof and route repair narrow the remaining resource question. For the canonical `u128` coefficient domain, future refusal analysis should focus on correctness of the full-width multiply/subtract, exact restoration of the common dyadic unit, reduced scientific denominator and midpoint proof, and the upstream represented-residual admission contract. Searching for a pair-admitted coefficient-sum or square-sum overflow case is no longer a valid acceptance criterion. +The new width theorem makes a post-Wide256 pair fallback look redundant within the canonical `u128` coefficient domain: product width alone cannot cause Wide256 refusal where the pair numerator is admissible. It is nevertheless retained as a fail-closed comparison reference until represented-input admission equivalence and exact-head execution demonstrate that no other stage creates a legitimate wider-route refusal. -The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The executable timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it now records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. +The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The executable timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. ## Decision -Do not promote the wider O(n) route into production solely from this arithmetic proof or the new characterization path. Production `validation_core::bias_standard_error` remains bounded to `n=4..=16`. A production change requires recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, represented-input admission comparison, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. Pairwise proof remains the fail-closed reference until those gates are satisfied. +Do not promote the wider O(n) route into production solely from the arithmetic proof or characterization. Production `validation_core::bias_standard_error` remains bounded to `n=4..=16`. A production change requires recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, represented-input admission comparison, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. ## Traceability - Issue: #491 - Landing PR: #488 - Accumulator-bound characterization: `b7e4da353ac58069afd73ee7c0e8427d49993fdb` +- Wide-product capacity RED: `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` +- Wide-product capacity repair: `e9a7dee29afb97542bfe2965f850c8ab5a34368e` - Narrow-wide-pair RED: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` - Narrow-wide-pair repair: `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` -- CHANGELOG fragment: `CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md` +- CHANGELOG fragment: `CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md` - Production module under decision: `crates/validation_core/src/bias_se.rs` - Exact-proof budget harness: `crates/validation_core/examples/bias_se_exact_proof_budget.rs` From 1f765a7430b3263afb7e436efdf6d3f14dd433c9 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 12:35:22 +0900 Subject: [PATCH 515/576] test(validation): document coverage contracts --- ...ias_standard_error_pairwise_subtraction_roundoff_contract.rs | 2 ++ ...tandard_error_two_level_integer_divisor_rounding_contract.rs | 2 ++ .../tests/wilson_all_covered_extreme_z_contract.rs | 2 ++ 3 files changed, 6 insertions(+) diff --git a/crates/validation_core/tests/bias_standard_error_pairwise_subtraction_roundoff_contract.rs b/crates/validation_core/tests/bias_standard_error_pairwise_subtraction_roundoff_contract.rs index 1a15fa2b3..cf724701e 100644 --- a/crates/validation_core/tests/bias_standard_error_pairwise_subtraction_roundoff_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_pairwise_subtraction_roundoff_contract.rs @@ -1,3 +1,5 @@ +//! Locks represented-residual pairwise subtraction roundoff in bias standard-error recovery. + use validation_core::bias_standard_error; #[test] diff --git a/crates/validation_core/tests/bias_standard_error_two_level_integer_divisor_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_two_level_integer_divisor_rounding_contract.rs index 89c16ef7c..b76c37ca3 100644 --- a/crates/validation_core/tests/bias_standard_error_two_level_integer_divisor_rounding_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_two_level_integer_divisor_rounding_contract.rs @@ -1,3 +1,5 @@ +//! Locks exact integer-divisor two-level bias standard-error rounding and its fail-closed edge. + use validation_core::{ValidationError, bias_standard_error}; #[test] diff --git a/crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs b/crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs index 63f120009..119c9816e 100644 --- a/crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs +++ b/crates/validation_core/tests/wilson_all_covered_extreme_z_contract.rs @@ -1,3 +1,5 @@ +//! Locks the all-covered Wilson lower endpoint under a finite extreme critical value. + use validation_core::wilson_coverage_interval; #[test] From aefefba5982579e46e8c3ff33d64c99ea7a6bafc Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 13:37:56 +0900 Subject: [PATCH 516/576] fix(validation): document remaining hosted coverage contracts --- .../examples/bias_se_exact_proof_budget.rs | 28 +++++++------------ ...r_three_level_scale_invariance_contract.rs | 10 +++++-- ...d_exact_count_small_z_rounding_contract.rs | 2 ++ 3 files changed, 20 insertions(+), 20 deletions(-) diff --git a/crates/validation_core/examples/bias_se_exact_proof_budget.rs b/crates/validation_core/examples/bias_se_exact_proof_budget.rs index fb8fffa62..d63f0a32f 100644 --- a/crates/validation_core/examples/bias_se_exact_proof_budget.rs +++ b/crates/validation_core/examples/bias_se_exact_proof_budget.rs @@ -55,8 +55,8 @@ impl Wide256 { .expect("schoolbook partial sum fits u128") .checked_add(carry) .expect("schoolbook carry sum fits u128"); - limbs[limb_index] = u64::try_from(accumulator & mask) - .expect("masked schoolbook limb fits u64"); + limbs[limb_index] = + u64::try_from(accumulator & mask).expect("masked schoolbook limb fits u64"); carry = accumulator >> 64; } limbs[left_index + 2] = @@ -361,18 +361,15 @@ fn assert_and_measure_geometry( "narrow-wide-pair hybrid must preserve the exact pair numerator" ); assert_eq!( - hybrid.used_pairwise_fallback, - expect_hybrid_fallback, + hybrid.used_pairwise_fallback, expect_hybrid_fallback, "narrow-pair fallback observation must match the declared geometry" ); assert_eq!( - wide_hybrid.used_wide_product, - expect_wide_hybrid_wide_product, + wide_hybrid.used_wide_product, expect_wide_hybrid_wide_product, "wide-route observation must match the declared geometry" ); assert_eq!( - wide_hybrid.used_pairwise_fallback, - expect_wide_hybrid_pair_fallback, + wide_hybrid.used_pairwise_fallback, expect_wide_hybrid_pair_fallback, "wide-hybrid pair fallback observation must match the declared geometry" ); @@ -391,7 +388,10 @@ fn assert_and_measure_geometry( let mut kernels: Vec<(&str, Kernel)> = vec![ ("quadratic_buffered", pair_square_sum_quadratic_buffered), ("quadratic_two_pass", pair_square_sum_quadratic_two_pass), - ("linear_wide_product_reference", pair_square_sum_linear_wide_product), + ( + "linear_wide_product_reference", + pair_square_sum_linear_wide_product, + ), ("hybrid_narrow_pair", pair_square_sum_hybrid), ("hybrid_narrow_wide_pair", pair_square_sum_wide_hybrid), ]; @@ -423,15 +423,7 @@ fn main() { ); for sample_count in [16_usize, 64, 256, 1_024, 2_047] { let values = fixture(sample_count); - assert_and_measure_geometry( - "compact_admit", - &values, - samples, - true, - false, - false, - false, - ); + assert_and_measure_geometry("compact_admit", &values, samples, true, false, false, false); } let power_of_two_values = boundary_fixture(65, 1_u128 << 58); diff --git a/crates/validation_core/tests/bias_standard_error_three_level_scale_invariance_contract.rs b/crates/validation_core/tests/bias_standard_error_three_level_scale_invariance_contract.rs index 0b1d63e81..94ea31135 100644 --- a/crates/validation_core/tests/bias_standard_error_three_level_scale_invariance_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_three_level_scale_invariance_contract.rs @@ -1,3 +1,5 @@ +//! Regression contract for bit-stable three-level bias SE under exact power-of-two scaling. + use validation_core::bias_standard_error; const EXPECTED_STANDARD_ERROR_BITS: u64 = 0x64f9_5555_5555_5555; @@ -24,12 +26,16 @@ fn exact_three_level_rational_scale_is_invariant_under_exact_power_of_two_scalin ]; for recovered in permutations { - let standard_error = bias_standard_error(&truth, &recovered).expect("finite standard error"); + let standard_error = + bias_standard_error(&truth, &recovered).expect("finite standard error"); assert_eq!(standard_error.to_bits(), EXPECTED_STANDARD_ERROR_BITS); let mirrored = recovered.map(|value| -value); let mirrored_standard_error = bias_standard_error(&truth, &mirrored).expect("finite mirrored standard error"); - assert_eq!(mirrored_standard_error.to_bits(), EXPECTED_STANDARD_ERROR_BITS); + assert_eq!( + mirrored_standard_error.to_bits(), + EXPECTED_STANDARD_ERROR_BITS + ); } } diff --git a/crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs b/crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs index 75586d412..539b7e978 100644 --- a/crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs +++ b/crates/validation_core/tests/wilson_all_covered_exact_count_small_z_rounding_contract.rs @@ -1,3 +1,5 @@ +//! Regression contracts for all-covered Wilson intervals at exact-count small-z rounding boundaries. + use validation_core::wilson_coverage_interval; #[test] From 5a19b6334487b43fb630abba7e487d7cf4c49960 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 14:02:42 +0900 Subject: [PATCH 517/576] test(validation): characterize represented Wide256 recovery --- ...resented_wide_recovery_characterization.rs | 192 ++++++++++++++++++ 1 file changed, 192 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs diff --git a/crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs b/crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs new file mode 100644 index 000000000..3f73741b3 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs @@ -0,0 +1,192 @@ +//! Characterizes a represented-input geometry that needs wider O(n) cancellation products. +//! +//! The fixture keeps every residual and every distinct pairwise subtraction exact in +//! binary64, while canonical anchor-relative coefficients make the narrow `u128` +//! linear identity overflow before cancellation. The exact pair numerator still fits +//! `u128`, so this is a reachable represented-input reason to retain the `Wide256` +//! characterization rather than relying only on synthetic integer coefficients. + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +struct Wide256 { + high: u128, + low: u128, +} + +impl Wide256 { + fn multiply_u128(left: u128, right: u128) -> Self { + let mask = u128::from(u64::MAX); + let left_limbs = [ + u64::try_from(left & mask).expect("masked low limb fits u64"), + u64::try_from(left >> 64).expect("high limb fits u64"), + ]; + let right_limbs = [ + u64::try_from(right & mask).expect("masked low limb fits u64"), + u64::try_from(right >> 64).expect("high limb fits u64"), + ]; + let mut limbs = [0_u64; 4]; + + for (left_index, left_limb) in left_limbs.iter().copied().enumerate() { + let mut carry = 0_u128; + for (right_index, right_limb) in right_limbs.iter().copied().enumerate() { + let limb_index = left_index + right_index; + let accumulator = u128::from(left_limb) + .checked_mul(u128::from(right_limb)) + .expect("64-bit limb product fits u128") + .checked_add(u128::from(limbs[limb_index])) + .expect("schoolbook partial sum fits u128") + .checked_add(carry) + .expect("schoolbook carry sum fits u128"); + limbs[limb_index] = + u64::try_from(accumulator & mask).expect("masked schoolbook limb fits u64"); + carry = accumulator >> 64; + } + limbs[left_index + 2] = + u64::try_from(carry).expect("schoolbook multiplication carry fits u64"); + } + + Self { + high: u128::from(limbs[2]) | (u128::from(limbs[3]) << 64), + low: u128::from(limbs[0]) | (u128::from(limbs[1]) << 64), + } + } + + fn checked_sub(self, right: Self) -> Option { + let (low, borrow) = self.low.overflowing_sub(right.low); + let high = self + .high + .checked_sub(right.high)? + .checked_sub(u128::from(u8::from(borrow)))?; + Some(Self { high, low }) + } + + fn as_u128(self) -> Option { + (self.high == 0).then_some(self.low) + } +} + +fn subtraction_roundoff(recovered: f64, truth: f64, residual: f64) -> f64 { + let negated_truth = -truth; + let truth_virtual = residual - recovered; + let recovered_virtual = residual - truth_virtual; + let recovered_roundoff = recovered - recovered_virtual; + let truth_roundoff = negated_truth - truth_virtual; + recovered_roundoff + truth_roundoff +} + +fn represented_values(sample_count: usize) -> Vec { + assert!(sample_count >= 3); + let diameter = (1_u64 << 53) as f64; + let mut values = Vec::with_capacity(sample_count); + values.extend([0.0, 1.0]); + values.extend((2..sample_count).map(|_| diameter)); + values +} + +fn canonical_coefficients(sample_count: usize) -> Vec { + assert!(sample_count >= 3); + let diameter = 1_u128 << 53; + let mut coefficients = Vec::with_capacity(sample_count); + coefficients.extend([0, 1]); + coefficients.extend((2..sample_count).map(|_| diameter)); + coefficients +} + +fn exact_pair_numerator_for_three_level_fixture(sample_count: usize) -> u128 { + let repeated = u128::try_from(sample_count - 2).expect("sample count fits u128"); + let diameter = 1_u128 << 53; + let near_diameter = diameter - 1; + 1_u128 + .checked_add( + repeated + .checked_mul( + diameter + .checked_mul(diameter) + .expect("diameter square fits u128") + .checked_add( + near_diameter + .checked_mul(near_diameter) + .expect("near-diameter square fits u128"), + ) + .expect("two distinct pair squares fit u128"), + ) + .expect("pair-count-weighted squares fit u128"), + ) + .expect("exact pair numerator fits u128") +} + +#[test] +fn represented_pair_admission_can_require_wide_linear_products() { + const SAMPLE_COUNT: usize = 4_096; + let represented = represented_values(SAMPLE_COUNT); + let diameter = (1_u64 << 53) as f64; + + for value in &represented { + let residual = *value - 0.0; + assert_eq!( + subtraction_roundoff(*value, 0.0, residual), + 0.0, + "truth-zero residual construction must be exact" + ); + } + + for (left, right) in [(0.0, 1.0), (0.0, diameter), (1.0, diameter)] { + let difference = left - right; + assert_eq!( + subtraction_roundoff(left, right, difference), + 0.0, + "every distinct represented pair subtraction used by the fixture must be exact" + ); + } + + let coefficients = canonical_coefficients(SAMPLE_COUNT); + assert_eq!( + coefficients + .iter() + .copied() + .filter(|coefficient| *coefficient != 0) + .map(u128::trailing_zeros) + .min(), + Some(0), + "the coefficient 1 prevents a removable common dyadic scale from hiding width pressure" + ); + + let coefficient_sum = coefficients.iter().copied().try_fold(0_u128, |sum, value| { + sum.checked_add(value) + }).expect("represented coefficient sum fits u128"); + let square_sum = coefficients.iter().copied().try_fold(0_u128, |sum, value| { + sum.checked_add(value.checked_mul(value)?) + }).expect("represented square sum fits u128"); + let sample_count = u128::try_from(SAMPLE_COUNT).expect("sample count fits u128"); + + assert!( + sample_count.checked_mul(square_sum).is_none(), + "n*S2 must overflow narrow u128 before cancellation" + ); + assert!( + coefficient_sum.checked_mul(coefficient_sum).is_none(), + "S1^2 must overflow narrow u128 before cancellation" + ); + + let exact_pair_numerator = exact_pair_numerator_for_three_level_fixture(SAMPLE_COUNT); + assert_eq!( + exact_pair_numerator, + 664_289_479_338_799_435_974_172_876_300_357_631_u128 + ); + + let wide_difference = Wide256::multiply_u128(sample_count, square_sum) + .checked_sub(Wide256::multiply_u128(coefficient_sum, coefficient_sum)) + .expect("n*S2 must dominate S1^2 exactly") + .as_u128() + .expect("the cancellation result remains pair-admissible u128"); + assert_eq!(wide_difference, exact_pair_numerator); + + let denominator = sample_count + .checked_mul(sample_count) + .and_then(|value| value.checked_mul(sample_count - 1)) + .expect("unreduced scientific denominator fits u128"); + assert_eq!(denominator, 68_702_699_520); + assert!( + denominator <= (1_u128 << 53), + "this represented width case is not blocked by the current exact-denominator gate" + ); +} From f082afaa40245c0c50f60848553ee20856b7f856 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 14:03:08 +0900 Subject: [PATCH 518/576] docs(validation): trace represented Wide256 reachability --- ...ndard-error-wide-linear-admission-bound.md | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index b09a81177..c7a299563 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -26,6 +26,20 @@ This is a width theorem, not yet a production-equivalence claim. The implementat RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` made that product-capacity theorem executable by adding a characterization test that referenced a not-yet-defined proof helper. Repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e` adds the helper and checks the 256-bit extremum together with the known compact, common-power, odd-boundary, and exhaustive small-integer composition geometries. +## Represented-input reachability + +The odd `D = 2^58 + 1` integer boundary is useful for arithmetic width, but by itself it does not establish that the wider route is needed by a residual set that passes the represented binary64 subtraction gates. A separate characterization now supplies such a case without weakening those gates. + +At `n = 4096`, take represented residuals with three distinct values: one `0`, one `1`, and 4094 copies of `2^53`. With truth fixed at represented zero, every residual construction is exact. The only distinct pairwise subtractions are `1`, `2^53`, and `2^53 - 1`; all three are exactly representable in binary64, so the production pair-subtraction roundoff predicate accepts the fixture's distinct subtraction classes. Because coefficient `1` is present, the canonical common power-of-two shift is zero rather than an artifact that removes the width pressure. + +The canonical sums `S1` and `S2` still fit `u128`, but both narrow cancellation products `n*S2` and `S1^2` exceed `u128`. The exact pair numerator remains only 119 bits: + +`P = 664289479338799435974172876300357631`. + +The two-limb product/subtraction recovers that value exactly. The unreduced scientific denominator is `4096^2 * 4095 = 68702699520`, which is also below the current `2^53` exact-denominator gate. This closes the narrower question of whether Wide256 recovery is reachable from represented residuals; it does not yet prove the full production route through dyadic restoration and midpoint rounding, and it does not authorize changing the production sample-count admission. + +Executable evidence is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`, introduced at `5a19b6334487b43fb630abba7e487d7cf4c49960`. + ## Consequence for #491 The existing candidate route remains `narrow O(n) -> Wide256 O(n) -> buffered pair fail-closed fallback`, introduced by RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` and repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5`. The predecessor narrow-to-pair hybrid remains a comparison baseline. @@ -34,11 +48,11 @@ For odd `D = 2^58 + 1`, `n = 65`, the predecessor hybrid must still select the p The new width theorem makes a post-Wide256 pair fallback look redundant within the canonical `u128` coefficient domain: product width alone cannot cause Wide256 refusal where the pair numerator is admissible. It is nevertheless retained as a fail-closed comparison reference until represented-input admission equivalence and exact-head execution demonstrate that no other stage creates a legitimate wider-route refusal. -The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The executable timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. +The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The represented-input reachability characterization is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`. The executable timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. ## Decision -Do not promote the wider O(n) route into production solely from the arithmetic proof or characterization. Production `validation_core::bias_standard_error` remains bounded to `n=4..=16`. A production change requires recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, represented-input admission comparison, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. +Do not promote the wider O(n) route into production solely from the arithmetic proof or characterization. Production `validation_core::bias_standard_error` remains bounded to `n=4..=16`. A production change requires recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, represented-input admission comparison through the complete exact-rounding path, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. ## Traceability @@ -47,6 +61,7 @@ Do not promote the wider O(n) route into production solely from the arithmetic p - Accumulator-bound characterization: `b7e4da353ac58069afd73ee7c0e8427d49993fdb` - Wide-product capacity RED: `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` - Wide-product capacity repair: `e9a7dee29afb97542bfe2965f850c8ab5a34368e` +- Represented-input Wide256 reachability: `5a19b6334487b43fb630abba7e487d7cf4c49960` - Narrow-wide-pair RED: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` - Narrow-wide-pair repair: `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` - CHANGELOG fragment: `CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md` From b5e66d0510be38fc686db7e4b5214096a5932de0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 14:03:16 +0900 Subject: [PATCH 519/576] docs(validation): record represented wide recovery --- ...lidation-bias-standard-error-wide-linear-admission-bound.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md index 037bacb14..965412ef9 100644 --- a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md +++ b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md @@ -1,4 +1,5 @@ ### Validation - Corrected issue #491's exact-proof resource model: once anchor-relative dyadic coefficients are canonicalized to nonnegative integers with at least one zero, any exact pair-square numerator that fits `u128` also bounds both the coefficient sum and square sum (`sum(c_i) <= sum(c_i^2) <= sum_{i Date: Sun, 6 Sep 2026 14:09:53 +0900 Subject: [PATCH 520/576] test(validation): expose midpoint width after Wide256 recovery --- ...resented_wide_recovery_characterization.rs | 172 ++++++++++++++++-- 1 file changed, 152 insertions(+), 20 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs b/crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs index 3f73741b3..c0d2f76f8 100644 --- a/crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs +++ b/crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs @@ -1,12 +1,13 @@ -//! Characterizes a represented-input geometry that needs wider O(n) cancellation products. +//! Characterizes represented-input geometries that need wider exact proof products. //! -//! The fixture keeps every residual and every distinct pairwise subtraction exact in -//! binary64, while canonical anchor-relative coefficients make the narrow `u128` -//! linear identity overflow before cancellation. The exact pair numerator still fits -//! `u128`, so this is a reachable represented-input reason to retain the `Wide256` -//! characterization rather than relying only on synthetic integer coefficients. +//! These fixtures keep residual construction and every distinct pairwise subtraction +//! exact in binary64 while canonical anchor-relative coefficients make narrow `u128` +//! products overflow before cancellation. The exact pair numerator still fits `u128`. +//! A second boundary shows that the same width pressure reaches the exact +//! candidate/midpoint comparison, so widening only the O(n) numerator identity would +//! not yet establish end-to-end production admission equivalence. -#[derive(Clone, Copy, Debug, Eq, PartialEq)] +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] struct Wide256 { high: u128, low: u128, @@ -73,6 +74,48 @@ fn subtraction_roundoff(recovered: f64, truth: f64, residual: f64) -> f64 { recovered_roundoff + truth_roundoff } +fn positive_dyadic(value: f64) -> Option<(u128, i32)> { + if !value.is_finite() || value <= 0.0 { + return None; + } + let bits = value.to_bits(); + let exponent_bits = i32::try_from((bits >> 52) & 0x7ff).ok()?; + let fraction = bits & 0x000f_ffff_ffff_ffff; + let (mut significand, mut exponent) = if exponent_bits == 0 { + (u128::from(fraction), -1074) + } else { + ( + u128::from((1_u64 << 52) | fraction), + exponent_bits - 1023 - 52, + ) + }; + if significand == 0 { + return None; + } + let trailing = significand.trailing_zeros(); + significand >>= trailing; + exponent += i32::try_from(trailing).ok()?; + Some((significand, exponent)) +} + +fn midpoint_dyadic(left: f64, right: f64) -> Option<(u128, i32)> { + let (left_significand, left_exponent) = positive_dyadic(left)?; + let (right_significand, right_exponent) = positive_dyadic(right)?; + let common_exponent = left_exponent.min(right_exponent); + let left_shift = left_exponent.checked_sub(common_exponent)?.unsigned_abs(); + let right_shift = right_exponent + .checked_sub(common_exponent)? + .unsigned_abs(); + let left_units = left_significand.checked_mul(1_u128.checked_shl(left_shift)?)?; + let right_units = right_significand.checked_mul(1_u128.checked_shl(right_shift)?)?; + let mut midpoint_significand = left_units.checked_add(right_units)?; + let mut midpoint_exponent = common_exponent.checked_sub(1)?; + let trailing = midpoint_significand.trailing_zeros(); + midpoint_significand >>= trailing; + midpoint_exponent += i32::try_from(trailing).ok()?; + Some((midpoint_significand, midpoint_exponent)) +} + fn represented_values(sample_count: usize) -> Vec { assert!(sample_count >= 3); let diameter = (1_u64 << 53) as f64; @@ -114,10 +157,8 @@ fn exact_pair_numerator_for_three_level_fixture(sample_count: usize) -> u128 { .expect("exact pair numerator fits u128") } -#[test] -fn represented_pair_admission_can_require_wide_linear_products() { - const SAMPLE_COUNT: usize = 4_096; - let represented = represented_values(SAMPLE_COUNT); +fn assert_represented_subtractions_are_exact(sample_count: usize) { + let represented = represented_values(sample_count); let diameter = (1_u64 << 53) as f64; for value in &represented { @@ -137,8 +178,10 @@ fn represented_pair_admission_can_require_wide_linear_products() { "every distinct represented pair subtraction used by the fixture must be exact" ); } +} - let coefficients = canonical_coefficients(SAMPLE_COUNT); +fn canonical_sums(sample_count: usize) -> (u128, u128) { + let coefficients = canonical_coefficients(sample_count); assert_eq!( coefficients .iter() @@ -147,15 +190,29 @@ fn represented_pair_admission_can_require_wide_linear_products() { .map(u128::trailing_zeros) .min(), Some(0), - "the coefficient 1 prevents a removable common dyadic scale from hiding width pressure" + "coefficient 1 prevents a removable dyadic scale from hiding width pressure" ); - let coefficient_sum = coefficients.iter().copied().try_fold(0_u128, |sum, value| { - sum.checked_add(value) - }).expect("represented coefficient sum fits u128"); - let square_sum = coefficients.iter().copied().try_fold(0_u128, |sum, value| { - sum.checked_add(value.checked_mul(value)?) - }).expect("represented square sum fits u128"); + let coefficient_sum = coefficients + .iter() + .copied() + .try_fold(0_u128, |sum, value| sum.checked_add(value)) + .expect("represented coefficient sum fits u128"); + let square_sum = coefficients + .iter() + .copied() + .try_fold(0_u128, |sum, value| { + sum.checked_add(value.checked_mul(value)?) + }) + .expect("represented square sum fits u128"); + (coefficient_sum, square_sum) +} + +#[test] +fn represented_pair_admission_can_require_wide_linear_products() { + const SAMPLE_COUNT: usize = 4_096; + assert_represented_subtractions_are_exact(SAMPLE_COUNT); + let (coefficient_sum, square_sum) = canonical_sums(SAMPLE_COUNT); let sample_count = u128::try_from(SAMPLE_COUNT).expect("sample count fits u128"); assert!( @@ -187,6 +244,81 @@ fn represented_pair_admission_can_require_wide_linear_products() { assert_eq!(denominator, 68_702_699_520); assert!( denominator <= (1_u128 << 53), - "this represented width case is not blocked by the current exact-denominator gate" + "this represented width case is not blocked by the exact-denominator gate" + ); +} + +#[test] +fn represented_wide_recovery_also_needs_wider_exact_midpoint_products() { + const SAMPLE_COUNT: usize = 2_050; + assert_represented_subtractions_are_exact(SAMPLE_COUNT); + let (coefficient_sum, square_sum) = canonical_sums(SAMPLE_COUNT); + let sample_count = u128::try_from(SAMPLE_COUNT).expect("sample count fits u128"); + let numerator = exact_pair_numerator_for_three_level_fixture(SAMPLE_COUNT); + let denominator = sample_count + .checked_mul(sample_count) + .and_then(|value| value.checked_mul(sample_count - 1)) + .expect("unreduced scientific denominator fits u128"); + + assert_eq!( + Wide256::multiply_u128(sample_count, square_sum) + .checked_sub(Wide256::multiply_u128(coefficient_sum, coefficient_sum)) + .expect("wide cancellation remains ordered") + .as_u128(), + Some(numerator) + ); + assert!(sample_count.checked_mul(square_sum).is_none()); + assert!(coefficient_sum.checked_mul(coefficient_sum).is_none()); + assert_eq!(denominator, 8_610_922_500); + assert!(denominator <= (1_u128 << 53)); + + let candidate = ((numerator as f64) / (denominator as f64)).sqrt(); + assert_eq!(candidate.to_bits(), 0x4296_998e_1aff_78de); + let (candidate_significand, candidate_exponent) = + positive_dyadic(candidate).expect("positive candidate is dyadic"); + let candidate_square = candidate_significand + .checked_mul(candidate_significand) + .expect("binary64 significand square fits u128"); + let candidate_shift = candidate_exponent + .checked_mul(-2) + .and_then(|value| u32::try_from(value).ok()) + .expect("candidate comparison shift is positive and bounded"); + let candidate_factor = 1_u128 + .checked_shl(candidate_shift) + .expect("candidate comparison factor fits u128"); + + assert!( + denominator.checked_mul(candidate_square).is_none(), + "the current u128 exact-square comparator cannot form the right operand" + ); + assert!( + numerator.checked_mul(candidate_factor).is_none(), + "the current u128 exact-square comparator cannot form the scaled numerator" + ); + let wide_candidate_left = Wide256::multiply_u128(numerator, candidate_factor); + let wide_candidate_right = Wide256::multiply_u128(denominator, candidate_square); + assert!( + wide_candidate_left > wide_candidate_right, + "the exact target lies above the floating candidate square" + ); + + let neighbor = f64::from_bits(candidate.to_bits() + 1); + let (midpoint_significand, midpoint_exponent) = + midpoint_dyadic(candidate, neighbor).expect("adjacent midpoint is exact dyadic"); + let midpoint_square = midpoint_significand + .checked_mul(midpoint_significand) + .expect("midpoint significand square fits u128"); + let midpoint_shift = midpoint_exponent + .checked_mul(-2) + .and_then(|value| u32::try_from(value).ok()) + .expect("midpoint comparison shift is positive and bounded"); + let midpoint_factor = 1_u128 + .checked_shl(midpoint_shift) + .expect("midpoint comparison factor fits u128"); + let wide_midpoint_left = Wide256::multiply_u128(numerator, midpoint_factor); + let wide_midpoint_right = Wide256::multiply_u128(denominator, midpoint_square); + assert!( + wide_midpoint_left < wide_midpoint_right, + "the exact target lies below the upward midpoint square, proving the candidate is nearest" ); } From 66f08286f3d93d35da87857e3f7543a598afcc64 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 14:10:30 +0900 Subject: [PATCH 521/576] docs(validation): trace exact midpoint width boundary --- ...ndard-error-wide-linear-admission-bound.md | 27 ++++++++++++++----- 1 file changed, 20 insertions(+), 7 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index c7a299563..e2fd3ba19 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -28,7 +28,7 @@ RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` made that product-capacity theore ## Represented-input reachability -The odd `D = 2^58 + 1` integer boundary is useful for arithmetic width, but by itself it does not establish that the wider route is needed by a residual set that passes the represented binary64 subtraction gates. A separate characterization now supplies such a case without weakening those gates. +The odd `D = 2^58 + 1` integer boundary is useful for arithmetic width, but by itself it does not establish that the wider route is needed by a residual set that passes the represented binary64 subtraction gates. A separate characterization supplies such a case without weakening those gates. At `n = 4096`, take represented residuals with three distinct values: one `0`, one `1`, and 4094 copies of `2^53`. With truth fixed at represented zero, every residual construction is exact. The only distinct pairwise subtractions are `1`, `2^53`, and `2^53 - 1`; all three are exactly representable in binary64, so the production pair-subtraction roundoff predicate accepts the fixture's distinct subtraction classes. Because coefficient `1` is present, the canonical common power-of-two shift is zero rather than an artifact that removes the width pressure. @@ -36,23 +36,35 @@ The canonical sums `S1` and `S2` still fit `u128`, but both narrow cancellation `P = 664289479338799435974172876300357631`. -The two-limb product/subtraction recovers that value exactly. The unreduced scientific denominator is `4096^2 * 4095 = 68702699520`, which is also below the current `2^53` exact-denominator gate. This closes the narrower question of whether Wide256 recovery is reachable from represented residuals; it does not yet prove the full production route through dyadic restoration and midpoint rounding, and it does not authorize changing the production sample-count admission. +The two-limb product/subtraction recovers that value exactly. The unreduced scientific denominator is `4096^2 * 4095 = 68702699520`, which is also below the current `2^53` exact-denominator gate. This closes the narrower question of whether Wide256 recovery is reachable from represented residuals; it does not authorize changing the production sample-count admission. -Executable evidence is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`, introduced at `5a19b6334487b43fb630abba7e487d7cf4c49960`. +## Exact-rounding width is a second boundary + +Widening only the O(n) numerator identity is not sufficient for end-to-end exact admission. The exact candidate/midpoint proof in `bias_se.rs` currently forms scaled `u128` products when it compares the exact rational target against a binary64 candidate square and the adjacent midpoint square. + +A smaller represented fixture with the same three residual classes at `n = 2050` reaches that boundary. Its residual and pairwise-subtraction classes remain exact and its common dyadic shift is zero. Both narrow O(n) products require 129 bits, while the exact pair numerator is still only 118 bits: + +`P = 332306998946228931332463617650984961`. + +The unreduced denominator is `8610922500`, below `2^53`. The normal binary64 ratio/square-root seed is `0x4296998e1aff78de`. Its compact dyadic significand/exponent are `3180642552495215 * 2^-9`. Exact candidate-square comparison therefore needs both `P * 2^18` and `denominator * significand^2`; each is 136 bits and cannot be formed by the current `u128` comparator. Wide256 comparison shows the exact target is above the candidate square. Comparing with the exact midpoint to the upward neighbor requires 140-bit operands and shows the target is below the midpoint square, thereby proving that the original candidate is the nearest binary64 result. + +This is a new causal resource finding: the wider route must cover exact-rounding comparison operands as well as the O(n) cancellation products before pair-admitted represented inputs can be called admission-equivalent. Retaining the pairwise proof as fail-closed comparison authority is therefore still justified even though two limbs are sufficient for the canonical numerator identity itself. + +Executable evidence for both represented boundaries is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`: reachability was introduced at `5a19b6334487b43fb630abba7e487d7cf4c49960`, and the exact-midpoint width characterization at `a8423173188fa53a26a16d3afdafeb76e114cc1d`. ## Consequence for #491 -The existing candidate route remains `narrow O(n) -> Wide256 O(n) -> buffered pair fail-closed fallback`, introduced by RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` and repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5`. The predecessor narrow-to-pair hybrid remains a comparison baseline. +The candidate numerator route remains `narrow O(n) -> Wide256 O(n) -> buffered pair fail-closed fallback`, introduced by RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` and repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5`. The predecessor narrow-to-pair hybrid remains a comparison baseline. For odd `D = 2^58 + 1`, `n = 65`, the predecessor hybrid must still select the pair fallback because its narrow products overflow. The newer candidate must select the wider-product route, recover the same exact restored numerator as both O(n²) references, and report `used_wide_product=true` with `used_pairwise_fallback=false`. The power-of-two-normalized `D=2^58,n=65` geometry and odd `n=64` geometry remain narrow-path admissions. Separate route observability matters because a correct exact numerator does not by itself show whether the candidate avoided O(n²) allocation. -The new width theorem makes a post-Wide256 pair fallback look redundant within the canonical `u128` coefficient domain: product width alone cannot cause Wide256 refusal where the pair numerator is admissible. It is nevertheless retained as a fail-closed comparison reference until represented-input admission equivalence and exact-head execution demonstrate that no other stage creates a legitimate wider-route refusal. +The product-width theorem makes a post-Wide256 pair fallback look redundant for the numerator identity within the canonical `u128` coefficient domain. The represented `n=2050` midpoint finding shows that this does not extend automatically to the whole exact-rounding proof. Production promotion therefore needs a coherent wider comparison path or another bounded proof that preserves the same candidate/midpoint semantics without introducing a new false refusal. -The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The represented-input reachability characterization is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`. The executable timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. +The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The represented-input characterization is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`. The timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. ## Decision -Do not promote the wider O(n) route into production solely from the arithmetic proof or characterization. Production `validation_core::bias_standard_error` remains bounded to `n=4..=16`. A production change requires recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, represented-input admission comparison through the complete exact-rounding path, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. +Do not promote the wider O(n) route into production solely from the arithmetic proof or characterization. Production `validation_core::bias_standard_error` remains bounded to `n=4..=16`. A production change requires a complete represented-input exact-rounding path whose numerator and candidate/midpoint comparisons are width-safe, recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. ## Traceability @@ -62,6 +74,7 @@ Do not promote the wider O(n) route into production solely from the arithmetic p - Wide-product capacity RED: `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` - Wide-product capacity repair: `e9a7dee29afb97542bfe2965f850c8ab5a34368e` - Represented-input Wide256 reachability: `5a19b6334487b43fb630abba7e487d7cf4c49960` +- Represented exact-midpoint width characterization: `a8423173188fa53a26a16d3afdafeb76e114cc1d` - Narrow-wide-pair RED: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` - Narrow-wide-pair repair: `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` - CHANGELOG fragment: `CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md` From abd1b34e7a8e291c6ad69dae63bfe828857fddd0 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 14:10:45 +0900 Subject: [PATCH 522/576] docs(validation): record exact midpoint width finding --- ...dation-bias-standard-error-wide-linear-admission-bound.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md index 965412ef9..30ad4a431 100644 --- a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md +++ b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md @@ -1,5 +1,6 @@ ### Validation - Corrected issue #491's exact-proof resource model: once anchor-relative dyadic coefficients are canonicalized to nonnegative integers with at least one zero, any exact pair-square numerator that fits `u128` also bounds both the coefficient sum and square sum (`sum(c_i) <= sum(c_i^2) <= sum_{i Date: Sun, 6 Sep 2026 14:19:23 +0900 Subject: [PATCH 523/576] docs(validation): extend exact-proof test strategy --- docs/TEST_STRATEGY.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index bce13bc18..7cd3a593b 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -74,12 +74,15 @@ Validation Evidence numerical proofs that add asymptotic work or material alloca - require an admitted/refused-set contract for any O(n) candidate. The predecessor `D=2^58, n=65` refusal was invalid because canonical normalization reduces the coefficients to zero/one and preserves the restored pair numerator `2^122`. The corrected narrow-path strict-subset boundary uses odd `D=2^58+1`: both pair and normalized narrow linear kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits `u128` but the normalized narrow O(n) products require 129 bits before cancellation; - retain the wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`: on odd `D=2^58+1, n=65`, exact two-limb products and subtraction recover the same 123-bit pair numerator after the two 129-bit intermediates cancel. This proves that narrow-intermediate refusal is not scientific refusal; - retain accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb`: after canonical minimum anchoring, all coefficients are nonnegative integers and at least one is zero, so `Σc_i <= Σc_i² <= Σ(i>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence separately; field-width estimates alone are not allocation evidence; - use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing. RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` required the missing narrow→Wide256→pair route and repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` implements it. On odd `D=2^58+1, n=65`, the predecessor hybrid remains a pair-allocation comparison baseline while the new candidate must recover through `Wide256` with `used_wide_product=true` and `used_pairwise_fallback=false`; the harness records both route flags explicitly; -- keep RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3`, normalization repair `d423b57797b6f7f127e61e0679f9ee9841525c77`, restoration hardening `96f17c02edba0792f61e0e92167703a6ae4e40d0`, wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`, wider-reference harness `0bd805d4b0304cf1f76344ae14b7f079b3dade17`, accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb`, narrow-wide-pair RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d`, and narrow-wide-pair repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` in the exact evidence lineage; +- keep RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3`, normalization repair `d423b57797b6f7f127e61e0679f9ee9841525c77`, restoration hardening `96f17c02edba0792f61e0e92167703a6ae4e40d0`, wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`, wider-reference harness `0bd805d4b0304cf1f76344ae14b7f079b3dade17`, accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb`, product-width RED/repair `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993`/`e9a7dee29afb97542bfe2965f850c8ab5a34368e`, represented-input reachability `5a19b6334487b43fb630abba7e487d7cf4c49960`, exact-rounding-width characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d`, narrow-wide-pair RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d`, and narrow-wide-pair repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` in the exact evidence lineage; - run the characterization harness in release mode on a recorded CPU/OS/Rust 1.98.0 toolchain and retain raw CSV plus p95; unexecuted harness code is not performance evidence; - if a service/API buyer path is affected, measure the full applicable path and retain the `p95 <= 20 ms` target without shrinking samples, omitting proof work, or relying on unrealistic warm-cache setup; - arithmetic representability alone does not authorize a production sample-count budget. From e1ca9c7ed9475c01f0987470e31c560b1368c7f3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 14:20:23 +0900 Subject: [PATCH 524/576] docs(validation): update exact-proof operability boundary --- docs/OPERABILITY.md | 17 +++++++++++++---- 1 file changed, 13 insertions(+), 4 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index b87d1258f..0bb9b7ec4 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -70,11 +70,19 @@ The corrected narrow O(n) accumulator is still a strict sufficient subset of the Wider-intermediate characterization `081000289f5a52e94863026d55696ee2a4daf923` makes that distinction executable without introducing a production dependency. A test-only two-limb `Wide256` performs exact `u128 × u128` products and checked cancellation. On odd `D=2^58+1,n=65`, it recovers the same exact 123-bit pair numerator after the two 129-bit products cancel, while the narrow checked-`u128` O(n) path refuses. The characterization also fixes `(2^128-1)^2` as high limb `2^128-2`, low limb `1`. -Accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb` removes a further false resource boundary. After canonical minimum anchoring, every coefficient `c_i` is a nonnegative integer and at least one coefficient is zero. Therefore `Σc_i <= Σc_i²`; the zero-anchor pair terms contain every `c_i²`, while all other pair-square terms are nonnegative, so `Σc_i² <= Σ(i Wide256 O(n) -> pairwise fail-closed fallback` kernel before it existed. Repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` implements that candidate in `crates/validation_core/examples/bias_se_exact_proof_budget.rs` without changing production arithmetic. The predecessor narrow→buffered-pair hybrid remains in the same harness so the cost of unnecessary pair allocation is measurable rather than inferred. +RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` → repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e` closes the numerator-product-width theorem. For a canonical pair-admitted case, `S1`, `S2`, and supported `n` are all `u128` operands, so both `n*S2` and `S1²` are at most 256-bit products. Product width alone cannot justify a pair fallback after a correctly implemented `Wide256` numerator cancellation. This theorem does not automatically cover later exact-rounding products. -The corrected harness now compares six resource shapes: buffered O(n²), allocation-free two-pass O(n²), normalized narrow checked O(n), the two-limb wider-product O(n) reference, the predecessor narrow→pair hybrid, and the new narrow→Wide256→pair hybrid. Before timing it requires exact restored-numerator equality. It records `used_wide_product` and `used_pairwise_fallback` independently. `D=2^58,n=65` must normalize and remain on the narrow route; odd `D=2^58+1,n=64` must remain narrow; odd `D=2^58+1,n=65` makes the predecessor hybrid use pair fallback while the new candidate must recover through `Wide256` with no pair allocation. This is route characterization, not proof that the new hybrid is production-ready. +RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` makes the resource route executable by requiring a `narrow O(n) -> Wide256 O(n) -> pairwise fail-closed fallback` kernel before it existed. Repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` implements that candidate in `crates/validation_core/examples/bias_se_exact_proof_budget.rs` without changing production arithmetic. The predecessor narrow→buffered-pair hybrid remains in the same harness so the cost of unnecessary pair allocation is measurable rather than inferred. + +Represented-input characterization `5a19b6334487b43fb630abba7e487d7cf4c49960` proves that wider numerator recovery is reachable through the same binary64 subtraction gates rather than only on synthetic integer coefficients. At `n=4096`, residual classes `{0,1,2^53}` with represented zero truth make residual construction exact and limit distinct pairwise subtraction magnitudes to exact values `{1,2^53,2^53-1}`. Coefficient `1` forces the canonical common dyadic shift to zero. Both narrow products overflow, while the exact pair numerator remains the 119-bit value `664_289_479_338_799_435_974_172_876_300_357_631`; `Wide256` recovers it exactly. The unreduced denominator `68_702_699_520` is below `2^53`. + +Exact-rounding-width characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d` exposes the next causal boundary at represented `n=2050`. The exact pair numerator `332_306_998_946_228_931_332_463_617_650_984_961` is only 118 bits and is recovered by `Wide256`; denominator `8_610_922_500` remains below `2^53`. The binary64 ratio/square-root seed is `0x4296998e1aff78de`. The existing exact candidate-square comparison would need 136-bit scaled operands, and the upward-adjacent midpoint comparison needs 140-bit operands. Test-only `Wide256` comparisons show the exact target lies above the candidate square but below the midpoint square, proving that seed is nearest. A production route that widens only the O(n) numerator would therefore still falsely refuse this represented exact-proof case at the midpoint-comparison layer. + +Operationally, the wider resource path is not one arithmetic substitution. Before production admission changes, the exact candidate-square and adjacent-midpoint comparisons must become width-safe or receive a separate proved bound while preserving exact tie-to-even behavior. The pairwise path remains a fail-closed comparison authority until represented residual conversion, canonical normalization, full-width cancellation, dyadic restoration, rational reduction, candidate/midpoint proof, current-head verification, and measured resource behavior are demonstrated together. + +The corrected harness compares six numerator-resource shapes: buffered O(n²), allocation-free two-pass O(n²), normalized narrow checked O(n), the two-limb wider-product O(n) reference, the predecessor narrow→pair hybrid, and the new narrow→Wide256→pair hybrid. Before timing it requires exact restored-numerator equality. It records `used_wide_product` and `used_pairwise_fallback` independently. `D=2^58,n=65` must normalize and remain on the narrow route; odd `D=2^58+1,n=64` must remain narrow; odd `D=2^58+1,n=65` makes the predecessor hybrid use pair fallback while the new candidate must recover through `Wide256` with no pair allocation. This is route characterization, not proof that the new hybrid is production-ready. Before changing the production boundary, retain: @@ -82,7 +90,8 @@ Before changing the production boundary, retain: - side-by-side buffered O(n²), allocation-free two-pass O(n²), normalized narrow O(n), two-limb wider-product O(n), predecessor narrow→pair hybrid, and narrow→Wide256→pair candidate evidence, with exact equality of restored pair-square numerators before timing; - route-aware timing covering common-power normalized admission and odd-diameter admitted/refused boundaries, including normalized unit exponent, `used_wide_product`, and `used_pairwise_fallback`; - target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence rather than byte estimates inferred from field widths; -- admitted/refused-set comparison at the actual represented-input boundary across sample count, canonical aligned coefficient diameter/exponent spread, and coefficient distribution; verify the accumulator-bound theorem against conversion into canonical coefficients and classify any remaining refusal by full-width multiply/subtract, scale restoration, denominator/midpoint proof, or upstream exact-residual admission; +- admitted/refused-set comparison at the actual represented-input boundary across sample count, canonical aligned coefficient diameter/exponent spread, and coefficient distribution; verify the accumulator and product-width theorems against conversion into canonical coefficients and classify any remaining refusal by scale restoration, denominator handling, exact candidate/midpoint comparison, or upstream exact-residual admission; +- width-safe exact candidate-square and both adjacent-midpoint comparisons over represented cases including ordinary nearest-neighbor selection and exact midpoint/tie-to-even cases; - full service/API p95 when a buyer-facing path is affected, preserving the TEPP `p95 <= 20 ms` target without shrinking input, omitting proof work, or using an unrealistic cache-only setup. The exact pair-record count is `n(n-1)/2`; the current characterization locks 120 records at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and 4,997,541 at `n=3,162`. A two-pass O(n²) implementation may remove pair-record storage while preserving pair-enumeration proof shape, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its checked arithmetic fits a wider intermediate, and a narrow checked O(n) refusal is not allowed to narrow the current exact pairwise admission set. From aab9fe9115cee97225f2aa81e54a55ceafb23336 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 14:30:03 +0900 Subject: [PATCH 525/576] test(validation): characterize exponent-safe wide midpoint comparison --- ...wide_scaled_comparison_characterization.rs | 280 ++++++++++++++++++ 1 file changed, 280 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_wide_scaled_comparison_characterization.rs diff --git a/crates/validation_core/tests/bias_standard_error_wide_scaled_comparison_characterization.rs b/crates/validation_core/tests/bias_standard_error_wide_scaled_comparison_characterization.rs new file mode 100644 index 000000000..aeac4f3f1 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_wide_scaled_comparison_characterization.rs @@ -0,0 +1,280 @@ +//! Characterizes exact scaled comparisons needed by wider bias-SE proofs. +//! +//! The current bounded production proof forms `u128` candidate-square and midpoint +//! products directly. Represented inputs can keep the exact pair numerator and +//! denominator admissible while those comparison products require more than 128 +//! bits. This test-only reference compares two-limb mantissas with signed dyadic +//! exponents without materializing large powers of two, preserving exact ordering +//! across normal and extreme exponent gaps. + +use core::cmp::Ordering; + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +struct Wide256 { + high: u128, + low: u128, +} + +impl Wide256 { + const fn from_u128(value: u128) -> Self { + Self { + high: 0, + low: value, + } + } + + fn multiply_u128(left: u128, right: u128) -> Self { + let mask = u128::from(u64::MAX); + let left_limbs = [ + u64::try_from(left & mask).expect("masked low limb fits u64"), + u64::try_from(left >> 64).expect("high limb fits u64"), + ]; + let right_limbs = [ + u64::try_from(right & mask).expect("masked low limb fits u64"), + u64::try_from(right >> 64).expect("high limb fits u64"), + ]; + let mut limbs = [0_u64; 4]; + + for (left_index, left_limb) in left_limbs.iter().copied().enumerate() { + let mut carry = 0_u128; + for (right_index, right_limb) in right_limbs.iter().copied().enumerate() { + let limb_index = left_index + right_index; + let accumulator = u128::from(left_limb) + .checked_mul(u128::from(right_limb)) + .expect("64-bit limb product fits u128") + .checked_add(u128::from(limbs[limb_index])) + .expect("schoolbook partial sum fits u128") + .checked_add(carry) + .expect("schoolbook carry sum fits u128"); + limbs[limb_index] = + u64::try_from(accumulator & mask).expect("masked schoolbook limb fits u64"); + carry = accumulator >> 64; + } + limbs[left_index + 2] = + u64::try_from(carry).expect("schoolbook multiplication carry fits u64"); + } + + Self { + high: u128::from(limbs[2]) | (u128::from(limbs[3]) << 64), + low: u128::from(limbs[0]) | (u128::from(limbs[1]) << 64), + } + } + + const fn is_zero(self) -> bool { + self.high == 0 && self.low == 0 + } + + fn bit_len(self) -> u32 { + if self.high != 0 { + 128 + (u128::BITS - self.high.leading_zeros()) + } else { + u128::BITS - self.low.leading_zeros() + } + } + + fn bit(self, index: u32) -> bool { + if index < u128::BITS { + ((self.low >> index) & 1) != 0 + } else { + let high_index = index - u128::BITS; + ((self.high >> high_index) & 1) != 0 + } + } +} + +fn compare_scaled_wide( + left: Wide256, + left_exponent: i32, + right: Wide256, + right_exponent: i32, +) -> Option { + match (left.is_zero(), right.is_zero()) { + (true, true) => return Some(Ordering::Equal), + (true, false) => return Some(Ordering::Less), + (false, true) => return Some(Ordering::Greater), + (false, false) => {} + } + + let left_bits = left.bit_len(); + let right_bits = right.bit_len(); + let left_top = left_exponent + .checked_add(i32::try_from(left_bits.checked_sub(1)?).ok()?)?; + let right_top = right_exponent + .checked_add(i32::try_from(right_bits.checked_sub(1)?).ok()?)?; + match left_top.cmp(&right_top) { + Ordering::Less => return Some(Ordering::Less), + Ordering::Greater => return Some(Ordering::Greater), + Ordering::Equal => {} + } + + let width = left_bits.max(right_bits); + for offset in 0..width { + let left_bit = offset < left_bits && left.bit(left_bits - 1 - offset); + let right_bit = offset < right_bits && right.bit(right_bits - 1 - offset); + match left_bit.cmp(&right_bit) { + Ordering::Less => return Some(Ordering::Less), + Ordering::Greater => return Some(Ordering::Greater), + Ordering::Equal => {} + } + } + Some(Ordering::Equal) +} + +fn positive_dyadic(value: f64) -> Option<(u128, i32)> { + if !value.is_finite() || value <= 0.0 { + return None; + } + let bits = value.to_bits(); + let exponent_bits = i32::try_from((bits >> 52) & 0x7ff).ok()?; + let fraction = bits & 0x000f_ffff_ffff_ffff; + let (mut significand, mut exponent) = if exponent_bits == 0 { + (u128::from(fraction), -1074) + } else { + ( + u128::from((1_u64 << 52) | fraction), + exponent_bits - 1023 - 52, + ) + }; + if significand == 0 { + return None; + } + let trailing = significand.trailing_zeros(); + significand >>= trailing; + exponent += i32::try_from(trailing).ok()?; + Some((significand, exponent)) +} + +fn midpoint_dyadic(left: f64, right: f64) -> Option<(u128, i32)> { + let (left_significand, left_exponent) = positive_dyadic(left)?; + let (right_significand, right_exponent) = positive_dyadic(right)?; + let common_exponent = left_exponent.min(right_exponent); + let left_shift = left_exponent.checked_sub(common_exponent)?.unsigned_abs(); + let right_shift = right_exponent.checked_sub(common_exponent)?.unsigned_abs(); + let left_units = left_significand.checked_shl(left_shift)?; + let right_units = right_significand.checked_shl(right_shift)?; + let mut midpoint_significand = left_units.checked_add(right_units)?; + let mut midpoint_exponent = common_exponent.checked_sub(1)?; + let trailing = midpoint_significand.trailing_zeros(); + midpoint_significand >>= trailing; + midpoint_exponent += i32::try_from(trailing).ok()?; + Some((midpoint_significand, midpoint_exponent)) +} + +fn exact_pair_numerator(sample_count: usize) -> u128 { + let repeated = u128::try_from(sample_count - 2).expect("sample count fits u128"); + let diameter = 1_u128 << 53; + let near_diameter = diameter - 1; + 1_u128 + .checked_add( + repeated + .checked_mul( + diameter + .checked_mul(diameter) + .expect("diameter square fits u128") + .checked_add( + near_diameter + .checked_mul(near_diameter) + .expect("near-diameter square fits u128"), + ) + .expect("two pair-square classes fit u128"), + ) + .expect("pair-count weighted squares fit u128"), + ) + .expect("pair numerator fits u128") +} + +#[test] +fn represented_n2050_candidate_and_midpoint_order_without_large_shift_materialization() { + const SAMPLE_COUNT: usize = 2_050; + let sample_count = u128::try_from(SAMPLE_COUNT).expect("sample count fits u128"); + let numerator = exact_pair_numerator(SAMPLE_COUNT); + let denominator = sample_count + .checked_mul(sample_count) + .and_then(|value| value.checked_mul(sample_count - 1)) + .expect("scientific denominator fits u128"); + assert_eq!(numerator, 332_306_998_946_228_931_332_463_617_650_984_961); + assert_eq!(denominator, 8_610_922_500); + + let candidate = ((numerator as f64) / (denominator as f64)).sqrt(); + assert_eq!(candidate.to_bits(), 0x4296_998e_1aff_78de); + let (candidate_significand, candidate_exponent) = + positive_dyadic(candidate).expect("candidate is positive dyadic"); + let candidate_square = candidate_significand + .checked_mul(candidate_significand) + .expect("binary64 candidate significand square fits u128"); + assert!(denominator.checked_mul(candidate_square).is_none()); + let candidate_right = Wide256::multiply_u128(denominator, candidate_square); + assert_ne!(candidate_right.high, 0, "comparison really needs more than u128"); + assert_eq!( + compare_scaled_wide( + Wide256::from_u128(numerator), + 0, + candidate_right, + candidate_exponent.checked_mul(2).expect("candidate exponent doubles"), + ), + Some(Ordering::Greater), + "exact target lies above the floating candidate square" + ); + + let neighbor = f64::from_bits(candidate.to_bits() + 1); + let (midpoint_significand, midpoint_exponent) = + midpoint_dyadic(candidate, neighbor).expect("adjacent midpoint is exact dyadic"); + let midpoint_square = midpoint_significand + .checked_mul(midpoint_significand) + .expect("midpoint significand square fits u128"); + assert!(denominator.checked_mul(midpoint_square).is_none()); + let midpoint_right = Wide256::multiply_u128(denominator, midpoint_square); + assert_ne!(midpoint_right.high, 0, "midpoint comparison exceeds u128"); + assert_eq!( + compare_scaled_wide( + Wide256::from_u128(numerator), + 0, + midpoint_right, + midpoint_exponent.checked_mul(2).expect("midpoint exponent doubles"), + ), + Some(Ordering::Less), + "exact target lies below the upward midpoint square" + ); +} + +#[test] +fn scaled_comparison_handles_extreme_exponents_without_allocating_a_power_of_two() { + let one = Wide256::from_u128(1); + let two = Wide256::from_u128(2); + let three = Wide256::from_u128(3); + + assert_eq!( + compare_scaled_wide(one, -2_148, two, -2_149), + Some(Ordering::Equal) + ); + assert_eq!( + compare_scaled_wide(one, -2_148, three, -2_149), + Some(Ordering::Less) + ); + assert_eq!( + compare_scaled_wide(three, 2_046, one, 2_047), + Some(Ordering::Greater) + ); + assert_eq!( + compare_scaled_wide(Wide256::from_u128(0), -2_148, one, 2_047), + Some(Ordering::Less) + ); + assert_eq!( + compare_scaled_wide(one, 2_047, Wide256::from_u128(0), -2_148), + Some(Ordering::Greater) + ); + assert_eq!( + compare_scaled_wide(Wide256::from_u128(0), 0, Wide256::from_u128(0), 0), + Some(Ordering::Equal) + ); +} + +#[test] +fn full_width_product_keeps_all_256_product_bits() { + let product = Wide256::multiply_u128(u128::MAX, u128::MAX); + assert_eq!(product.high, u128::MAX - 1); + assert_eq!(product.low, 1); + assert_eq!(product.bit_len(), 256); + assert!(product.bit(255)); + assert!(product.bit(0)); +} From 17a9e8a306471959333170875b0e6ae3996fcdc4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 14:30:21 +0900 Subject: [PATCH 526/576] docs(validation): record exponent-safe wide comparison characterization --- ...lidation-bias-standard-error-wide-linear-admission-bound.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md index 30ad4a431..7e42239ba 100644 --- a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md +++ b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md @@ -3,4 +3,5 @@ - Corrected issue #491's exact-proof resource model: once anchor-relative dyadic coefficients are canonicalized to nonnegative integers with at least one zero, any exact pair-square numerator that fits `u128` also bounds both the coefficient sum and square sum (`sum(c_i) <= sum(c_i^2) <= sum_{i Date: Sun, 6 Sep 2026 14:30:50 +0900 Subject: [PATCH 527/576] docs(validation): trace exponent-safe wide midpoint comparison --- ...tandard-error-wide-linear-admission-bound.md | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index e2fd3ba19..619e6a0c3 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -52,19 +52,29 @@ This is a new causal resource finding: the wider route must cover exact-rounding Executable evidence for both represented boundaries is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`: reachability was introduced at `5a19b6334487b43fb630abba7e487d7cf4c49960`, and the exact-midpoint width characterization at `a8423173188fa53a26a16d3afdafeb76e114cc1d`. +## Exponent-safe exact comparison + +The 136/140-bit finding does not require an arbitrary-precision integer for the comparison itself. Characterization `aab9fe9115cee97225f2aa81e54a55ceafb23336` adds a two-limb comparison reference that keeps each nonzero integer mantissa in `Wide256` and keeps its power-of-two scale as a signed exponent. It first compares the absolute top-bit positions. Only when those positions tie does it compare significand bits aligned from the common top bit. No `2^k` factor is materialized. + +For represented `n = 2050`, that reference orders the 136-bit candidate-square operands as target greater than candidate square and the 140-bit upward-midpoint operands as target less than midpoint square, reproducing the exact nearest-binary64 decision from the earlier characterization. It also verifies equality and strict ordering across exponent pairs `-2148/-2149` and `2046/2047`, where a direct `u128` shift-factor construction is not a viable representation. The full-width edge `(2^128 - 1)^2` remains exactly represented as high limb `2^128 - 2`, low limb `1`. + +This closes algorithm feasibility for exact scaled ordering but is intentionally test-only. The production comparator in `crates/validation_core/src/bias_se.rs` still uses bounded `u128` products and `multiply_by_power_of_two`; production admission therefore remains unchanged until the comparison primitive is integrated and verified on the authoritative path. + +The standards basis remains the current published floating-point standards, IEEE 754-2019 and ISO/IEC 60559:2020. IEEE currently lists 754-2019 as an active standard and ISO lists ISO/IEC 60559:2020 as the published international standard; IEEE P754 is an active revision project and is not treated as a published replacement in this decision. + ## Consequence for #491 The candidate numerator route remains `narrow O(n) -> Wide256 O(n) -> buffered pair fail-closed fallback`, introduced by RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` and repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5`. The predecessor narrow-to-pair hybrid remains a comparison baseline. For odd `D = 2^58 + 1`, `n = 65`, the predecessor hybrid must still select the pair fallback because its narrow products overflow. The newer candidate must select the wider-product route, recover the same exact restored numerator as both O(n²) references, and report `used_wide_product=true` with `used_pairwise_fallback=false`. The power-of-two-normalized `D=2^58,n=65` geometry and odd `n=64` geometry remain narrow-path admissions. Separate route observability matters because a correct exact numerator does not by itself show whether the candidate avoided O(n²) allocation. -The product-width theorem makes a post-Wide256 pair fallback look redundant for the numerator identity within the canonical `u128` coefficient domain. The represented `n=2050` midpoint finding shows that this does not extend automatically to the whole exact-rounding proof. Production promotion therefore needs a coherent wider comparison path or another bounded proof that preserves the same candidate/midpoint semantics without introducing a new false refusal. +The product-width theorem makes a post-Wide256 pair fallback look redundant for the numerator identity within the canonical `u128` coefficient domain. The represented `n=2050` midpoint finding shows that this does not extend automatically to the whole exact-rounding proof. The exponent-safe comparison characterization now supplies a bounded exact ordering algorithm for that second boundary, but production promotion still requires integrating it with normalization, candidate stepping, midpoint tie-to-even, and fail-closed semantics on the authoritative path. -The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The represented-input characterization is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`. The timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. +The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The represented-input characterization is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`. The exponent-safe comparison characterization is `crates/validation_core/tests/bias_standard_error_wide_scaled_comparison_characterization.rs`. The timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. ## Decision -Do not promote the wider O(n) route into production solely from the arithmetic proof or characterization. Production `validation_core::bias_standard_error` remains bounded to `n=4..=16`. A production change requires a complete represented-input exact-rounding path whose numerator and candidate/midpoint comparisons are width-safe, recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. +Do not promote the wider O(n) route into production solely from the arithmetic proof or characterization. Production `validation_core::bias_standard_error` remains bounded to `n=4..=16`. A production change requires integrating the exponent-safe comparison into the authoritative exact-rounding path, demonstrating represented-input admission equivalence and tie-to-even preservation, recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. ## Traceability @@ -75,6 +85,7 @@ Do not promote the wider O(n) route into production solely from the arithmetic p - Wide-product capacity repair: `e9a7dee29afb97542bfe2965f850c8ab5a34368e` - Represented-input Wide256 reachability: `5a19b6334487b43fb630abba7e487d7cf4c49960` - Represented exact-midpoint width characterization: `a8423173188fa53a26a16d3afdafeb76e114cc1d` +- Exponent-safe scaled comparison characterization: `aab9fe9115cee97225f2aa81e54a55ceafb23336` - Narrow-wide-pair RED: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` - Narrow-wide-pair repair: `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` - CHANGELOG fragment: `CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md` From f7717361ad8c5f0592688c1514c104cc1b4adabe Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 15:12:30 +0900 Subject: [PATCH 528/576] test(validation): require wide exact midpoint comparison --- crates/validation_core/src/bias_se.rs | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 19baf5769..45f27cf58 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -315,6 +315,20 @@ mod tests { ); } + #[test] + fn exact_ratio_sqrt_requires_wide_scaled_products_for_represented_n2050_boundary() { + assert_eq!( + correctly_rounded_scaled_sqrt_ratio( + 332_306_998_946_228_931_332_463_617_650_984_961, + 8_610_922_500, + 0, + ) + .expect("exact represented boundary must survive comparison-product width") + .to_bits(), + 0x4296_998e_1aff_78de + ); + } + #[test] fn exact_ratio_sqrt_refuses_outside_bounded_proof() { let too_large_denominator = (1_u128 << 53) + 1; From e4a85f53a611922be7492fe906d62ce65787c18e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 15:14:29 +0900 Subject: [PATCH 529/576] fix(validation): widen exact midpoint comparison products --- crates/validation_core/src/bias_se.rs | 171 +++++++++++++++++++++++--- 1 file changed, 154 insertions(+), 17 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 45f27cf58..f1d559467 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -47,6 +47,117 @@ fn multiply_by_power_of_two(value: u128, shift: u32) -> Option { value.checked_mul(factor) } +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +struct Wide256 { + high: u128, + low: u128, +} + +impl Wide256 { + const fn from_u128(value: u128) -> Self { + Self { + high: 0, + low: value, + } + } + + fn multiply_u128(left: u128, right: u128) -> Self { + let mask = u128::from(u64::MAX); + let left_limbs = [ + u64::try_from(left & mask).expect("masked low limb fits u64"), + u64::try_from(left >> 64).expect("high limb fits u64"), + ]; + let right_limbs = [ + u64::try_from(right & mask).expect("masked low limb fits u64"), + u64::try_from(right >> 64).expect("high limb fits u64"), + ]; + let mut limbs = [0_u64; 4]; + + for (left_index, left_limb) in left_limbs.iter().copied().enumerate() { + let mut carry = 0_u128; + for (right_index, right_limb) in right_limbs.iter().copied().enumerate() { + let limb_index = left_index + right_index; + let accumulator = u128::from(left_limb) + .checked_mul(u128::from(right_limb)) + .expect("64-bit limb product fits u128") + .checked_add(u128::from(limbs[limb_index])) + .expect("schoolbook partial sum fits u128") + .checked_add(carry) + .expect("schoolbook carry sum fits u128"); + limbs[limb_index] = + u64::try_from(accumulator & mask).expect("masked schoolbook limb fits u64"); + carry = accumulator >> 64; + } + limbs[left_index + 2] = + u64::try_from(carry).expect("schoolbook multiplication carry fits u64"); + } + + Self { + high: u128::from(limbs[2]) | (u128::from(limbs[3]) << 64), + low: u128::from(limbs[0]) | (u128::from(limbs[1]) << 64), + } + } + + const fn is_zero(self) -> bool { + self.high == 0 && self.low == 0 + } + + fn bit_len(self) -> u32 { + if self.high != 0 { + 128 + (u128::BITS - self.high.leading_zeros()) + } else { + u128::BITS - self.low.leading_zeros() + } + } + + fn bit(self, index: u32) -> bool { + if index < u128::BITS { + ((self.low >> index) & 1) != 0 + } else { + let high_index = index - u128::BITS; + ((self.high >> high_index) & 1) != 0 + } + } +} + +fn compare_scaled_wide( + left: Wide256, + left_exponent: i32, + right: Wide256, + right_exponent: i32, +) -> Option { + match (left.is_zero(), right.is_zero()) { + (true, true) => return Some(Ordering::Equal), + (true, false) => return Some(Ordering::Less), + (false, true) => return Some(Ordering::Greater), + (false, false) => {} + } + + let left_bits = left.bit_len(); + let right_bits = right.bit_len(); + let left_top = left_exponent + .checked_add(i32::try_from(left_bits.checked_sub(1)?).ok()?)?; + let right_top = right_exponent + .checked_add(i32::try_from(right_bits.checked_sub(1)?).ok()?)?; + match left_top.cmp(&right_top) { + Ordering::Less => return Some(Ordering::Less), + Ordering::Greater => return Some(Ordering::Greater), + Ordering::Equal => {} + } + + let width = left_bits.max(right_bits); + for offset in 0..width { + let left_bit = offset < left_bits && left.bit(left_bits - 1 - offset); + let right_bit = offset < right_bits && right.bit(right_bits - 1 - offset); + match left_bit.cmp(&right_bit) { + Ordering::Less => return Some(Ordering::Less), + Ordering::Greater => return Some(Ordering::Greater), + Ordering::Equal => {} + } + } + Some(Ordering::Equal) +} + fn compare_scaled_ratio_to_dyadic_square( numerator: u128, numerator_exponent: i32, @@ -55,16 +166,14 @@ fn compare_scaled_ratio_to_dyadic_square( exponent: i32, ) -> Option { let square = significand.checked_mul(significand)?; - let right = denominator.checked_mul(square)?; let square_exponent = exponent.checked_mul(2)?; - let exponent_delta = numerator_exponent.checked_sub(square_exponent)?; - if exponent_delta >= 0 { - let left = multiply_by_power_of_two(numerator, exponent_delta.unsigned_abs())?; - Some(left.cmp(&right)) - } else { - let shifted_right = multiply_by_power_of_two(right, exponent_delta.unsigned_abs())?; - Some(numerator.cmp(&shifted_right)) - } + let right = Wide256::multiply_u128(denominator, square); + compare_scaled_wide( + Wide256::from_u128(numerator), + numerator_exponent, + right, + square_exponent, + ) } fn midpoint_dyadic(left: f64, right: f64) -> Option<(u128, i32)> { @@ -107,9 +216,9 @@ fn correctly_rounded_scaled_sqrt_ratio( let unit = exact_power_of_two(unit_exponent)?; let denominator_f64 = denominator as f64; // The binary64 numerator conversion is only a seed. The returned value is - // admitted solely after the exact u128 dyadic-square and midpoint comparisons - // below. This lets the bounded proof retain exact reduced numerators above - // 2^53 without pretending that their seed conversion is exact. + // admitted solely after the exact two-limb dyadic-square and midpoint + // comparisons below. This lets the bounded proof retain exact reduced + // numerators above 2^53 without pretending that their seed conversion is exact. let mut candidate = ((numerator as f64) / denominator_f64).sqrt() * unit; if !candidate.is_finite() || candidate <= 0.0 { return None; @@ -273,9 +382,11 @@ pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result Date: Sun, 6 Sep 2026 15:17:07 +0900 Subject: [PATCH 530/576] docs(validation): record wide midpoint production integration --- ...lidation-bias-standard-error-wide-linear-admission-bound.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md index 7e42239ba..79f9b7ba2 100644 --- a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md +++ b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md @@ -4,4 +4,5 @@ - Proved the full-width numerator-product bound for the characterization domain: a pair-admitted coefficient set has `S2 <= u128::MAX`, `S1 <= u128::MAX`, and a supported sample count convertible from `usize` to `u128`, so both cancellation products `n*S2` and `S1^2` require at most 256 bits. The dependency-free two-limb `Wide256` product width is sufficient for every canonical case that the `u128` pair numerator can admit. - Added represented binary64 reachability at `n=4096`: residual classes `0`, `1`, and `2^53` keep residual construction and every distinct pairwise subtraction exact, retain a zero common dyadic shift, overflow both narrow `u128` cancellation products, and still produce an exact 119-bit pair numerator recovered by `Wide256`; the unreduced denominator remains below `2^53`. - Characterized the next end-to-end boundary at represented `n=2050`: although Wide256 recovers the 118-bit exact pair numerator, the current exact candidate-square comparison would require 136-bit operands and the adjacent midpoint comparison 140-bit operands. Wide256 exact comparisons prove the binary64 seed `0x4296998e1aff78de` is below the target square but below-neighbor midpoint logic still selects it as the nearest result. Widening only the O(n) numerator identity is therefore insufficient; exact-rounding comparison width must be repaired or separately bounded before production admission can widen. -- Added an exponent-safe two-limb comparison characterization for the same represented `n=2050` boundary. It compares `Wide256` mantissas by exact top-bit position and aligned significand bits instead of materializing `2^k`, so the 136-bit candidate product and 140-bit midpoint product are ordered exactly while exponent gaps such as `-2148/-2149` remain representable as metadata rather than oversized integers. This closes algorithm feasibility for exact candidate/midpoint ordering, not production admission; `bias_standard_error` remains `n=4..=16` pending integration into the authoritative comparator, exact-head GREEN, represented-input equivalence, and measured Rust 1.98.0 release-mode resource evidence. +- Added an exponent-safe two-limb comparison characterization for the same represented `n=2050` boundary. It compares `Wide256` mantissas by exact top-bit position and aligned significand bits instead of materializing `2^k`, so the 136-bit candidate product and 140-bit midpoint product are ordered exactly while exponent gaps such as `-2148/-2149` remain representable as metadata rather than oversized integers. +- Integrated that comparison primitive into the authoritative exact-rounding path without widening the sample-count admission. RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` requires the represented `n=2050` reduced ratio to survive comparison-product width; repair `e4a85f53a611922be7492fe906d62ce65787c18e` forms denominator-by-square products as exact two-limb values and compares signed dyadic exponents without materializing oversized powers of two. Production `bias_standard_error` remains `n=4..=16` pending exact-head GREEN, represented-input end-to-end equivalence, and measured Rust 1.98.0 release-mode resource evidence. From bda01d680a03a036b358959115ce72245f2d5a8f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 15:20:11 +0900 Subject: [PATCH 531/576] docs(validation): trace production wide exact comparison --- ...andard-error-wide-linear-admission-bound.md | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index 619e6a0c3..aea9bb0ca 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -22,7 +22,7 @@ The normalized narrow O(n) implementation can still refuse while `P` fits becaus That narrow refusal does not imply that a wider product requires arbitrary precision. On supported targets the sample count is converted from `usize` to `u128`; pair admission gives `S2 <= P <= u128::MAX` and `S1 <= S2 <= u128::MAX`. Consequently each exact cancellation product is a product of two `u128` values. Its maximum width is 256 bits: `(2^128 - 1)^2 < 2^256`. The dependency-free two-limb `Wide256` product representation is therefore wide enough for every canonical coefficient set whose exact pair numerator is admitted by `u128`. -This is a width theorem, not yet a production-equivalence claim. The implementation still has to prove that normalization, full-width multiplication/subtraction, dyadic restoration, reduced denominator/midpoint rounding, and upstream represented-residual admission compose without introducing a stricter refusal than the existing pair proof. +This is a width theorem, not by itself a production-equivalence claim. Normalization, full-width multiplication/subtraction, dyadic restoration, reduced denominator/midpoint rounding, and upstream represented-residual admission still have to compose without introducing a stricter refusal than the existing pair proof. RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` made that product-capacity theorem executable by adding a characterization test that referenced a not-yet-defined proof helper. Repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e` adds the helper and checks the 256-bit extremum together with the known compact, common-power, odd-boundary, and exhaustive small-integer composition geometries. @@ -40,15 +40,15 @@ The two-limb product/subtraction recovers that value exactly. The unreduced scie ## Exact-rounding width is a second boundary -Widening only the O(n) numerator identity is not sufficient for end-to-end exact admission. The exact candidate/midpoint proof in `bias_se.rs` currently forms scaled `u128` products when it compares the exact rational target against a binary64 candidate square and the adjacent midpoint square. +Widening only the O(n) numerator identity is not sufficient for end-to-end exact admission. Before repair `e4a85f53a611922be7492fe906d62ce65787c18e`, the exact candidate/midpoint proof in `bias_se.rs` formed scaled `u128` products when comparing the exact rational target against a binary64 candidate square and the adjacent midpoint square. A smaller represented fixture with the same three residual classes at `n = 2050` reaches that boundary. Its residual and pairwise-subtraction classes remain exact and its common dyadic shift is zero. Both narrow O(n) products require 129 bits, while the exact pair numerator is still only 118 bits: `P = 332306998946228931332463617650984961`. -The unreduced denominator is `8610922500`, below `2^53`. The normal binary64 ratio/square-root seed is `0x4296998e1aff78de`. Its compact dyadic significand/exponent are `3180642552495215 * 2^-9`. Exact candidate-square comparison therefore needs both `P * 2^18` and `denominator * significand^2`; each is 136 bits and cannot be formed by the current `u128` comparator. Wide256 comparison shows the exact target is above the candidate square. Comparing with the exact midpoint to the upward neighbor requires 140-bit operands and shows the target is below the midpoint square, thereby proving that the original candidate is the nearest binary64 result. +The unreduced denominator is `8610922500`, below `2^53`. The normal binary64 ratio/square-root seed is `0x4296998e1aff78de`. Its compact dyadic significand/exponent are `3180642552495215 * 2^-9`. Exact candidate-square comparison therefore needs both `P * 2^18` and `denominator * significand^2`; each is 136 bits. Comparing with the exact midpoint to the upward neighbor requires 140-bit operands. Wide256 comparison shows the exact target is above the candidate square and below the midpoint square, proving that the original candidate is the nearest binary64 result. -This is a new causal resource finding: the wider route must cover exact-rounding comparison operands as well as the O(n) cancellation products before pair-admitted represented inputs can be called admission-equivalent. Retaining the pairwise proof as fail-closed comparison authority is therefore still justified even though two limbs are sufficient for the canonical numerator identity itself. +This is the causal resource finding that motivated the production primitive repair: exact-rounding comparison width must cover the same represented cases as the wider numerator path. Pairwise proof remains the fail-closed comparison authority until the whole represented-input route is proven admission-equivalent. Executable evidence for both represented boundaries is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`: reachability was introduced at `5a19b6334487b43fb630abba7e487d7cf4c49960`, and the exact-midpoint width characterization at `a8423173188fa53a26a16d3afdafeb76e114cc1d`. @@ -58,7 +58,9 @@ The 136/140-bit finding does not require an arbitrary-precision integer for the For represented `n = 2050`, that reference orders the 136-bit candidate-square operands as target greater than candidate square and the 140-bit upward-midpoint operands as target less than midpoint square, reproducing the exact nearest-binary64 decision from the earlier characterization. It also verifies equality and strict ordering across exponent pairs `-2148/-2149` and `2046/2047`, where a direct `u128` shift-factor construction is not a viable representation. The full-width edge `(2^128 - 1)^2` remains exactly represented as high limb `2^128 - 2`, low limb `1`. -This closes algorithm feasibility for exact scaled ordering but is intentionally test-only. The production comparator in `crates/validation_core/src/bias_se.rs` still uses bounded `u128` products and `multiply_by_power_of_two`; production admission therefore remains unchanged until the comparison primitive is integrated and verified on the authoritative path. +RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` moved this finding onto the authoritative path by requiring `correctly_rounded_scaled_sqrt_ratio` to return `0x4296998e1aff78de` for the represented `n=2050` reduced ratio; the previous bounded comparator returned `None` before it could make the exact decision. Repair `e4a85f53a611922be7492fe906d62ce65787c18e` integrates the two-limb product and signed-exponent ordering into `crates/validation_core/src/bias_se.rs`. Candidate and adjacent-midpoint denominator products are now formed as exact `Wide256` values and compared without materializing an oversized power-of-two factor. + +This is a production primitive integration, not a sample-count admission change. `exact_pair_distance_standard_error` remains deliberately bounded to `n=4..=16`; the `n=2050` ratio is a private exact-rounding contract that prevents a future wider numerator route from inheriting the former false refusal. Exact-head Rust/rustdoc/line+branch/security/documentation GREEN and represented-input route equivalence remain required before admission can widen. The standards basis remains the current published floating-point standards, IEEE 754-2019 and ISO/IEC 60559:2020. IEEE currently lists 754-2019 as an active standard and ISO lists ISO/IEC 60559:2020 as the published international standard; IEEE P754 is an active revision project and is not treated as a published replacement in this decision. @@ -68,13 +70,13 @@ The candidate numerator route remains `narrow O(n) -> Wide256 O(n) -> buffered p For odd `D = 2^58 + 1`, `n = 65`, the predecessor hybrid must still select the pair fallback because its narrow products overflow. The newer candidate must select the wider-product route, recover the same exact restored numerator as both O(n²) references, and report `used_wide_product=true` with `used_pairwise_fallback=false`. The power-of-two-normalized `D=2^58,n=65` geometry and odd `n=64` geometry remain narrow-path admissions. Separate route observability matters because a correct exact numerator does not by itself show whether the candidate avoided O(n²) allocation. -The product-width theorem makes a post-Wide256 pair fallback look redundant for the numerator identity within the canonical `u128` coefficient domain. The represented `n=2050` midpoint finding shows that this does not extend automatically to the whole exact-rounding proof. The exponent-safe comparison characterization now supplies a bounded exact ordering algorithm for that second boundary, but production promotion still requires integrating it with normalization, candidate stepping, midpoint tie-to-even, and fail-closed semantics on the authoritative path. +The product-width theorem makes a post-Wide256 pair fallback look redundant for the numerator identity within the canonical `u128` coefficient domain. The represented `n=2050` midpoint finding showed why this did not extend automatically to the whole exact-rounding proof. The exponent-safe comparison is now integrated into the authoritative rounding primitive, so the remaining equivalence work moves outward: canonical represented-residual normalization, wider numerator routing, reduced ratio construction, candidate stepping/tie-to-even behavior, and final fail-closed/fallback decisions must agree with the pairwise authority across admitted represented inputs. The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The represented-input characterization is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`. The exponent-safe comparison characterization is `crates/validation_core/tests/bias_standard_error_wide_scaled_comparison_characterization.rs`. The timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. ## Decision -Do not promote the wider O(n) route into production solely from the arithmetic proof or characterization. Production `validation_core::bias_standard_error` remains bounded to `n=4..=16`. A production change requires integrating the exponent-safe comparison into the authoritative exact-rounding path, demonstrating represented-input admission equivalence and tie-to-even preservation, recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. +Do not widen production `validation_core::bias_standard_error` solely because the exact-rounding primitive now supports wider comparison products. Production admission remains `n=4..=16`. A budget change requires represented-input admission equivalence for the complete wider route, exact tie-to-even preservation, recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. ## Traceability @@ -86,6 +88,8 @@ Do not promote the wider O(n) route into production solely from the arithmetic p - Represented-input Wide256 reachability: `5a19b6334487b43fb630abba7e487d7cf4c49960` - Represented exact-midpoint width characterization: `a8423173188fa53a26a16d3afdafeb76e114cc1d` - Exponent-safe scaled comparison characterization: `aab9fe9115cee97225f2aa81e54a55ceafb23336` +- Production scaled-comparison RED: `f7717361ad8c5f0592688c1514c104cc1b4adabe` +- Production scaled-comparison repair: `e4a85f53a611922be7492fe906d62ce65787c18e` - Narrow-wide-pair RED: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` - Narrow-wide-pair repair: `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` - CHANGELOG fragment: `CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md` From 1240ace8eb41a01fa72a4bb99df842fd550a1288 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 15:36:09 +0900 Subject: [PATCH 532/576] test(validation): pin exact midpoint tie-to-even --- ..._error_exact_midpoint_tie_even_contract.rs | 35 +++++++++++++++++++ 1 file changed, 35 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_exact_midpoint_tie_even_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_exact_midpoint_tie_even_contract.rs b/crates/validation_core/tests/bias_standard_error_exact_midpoint_tie_even_contract.rs new file mode 100644 index 000000000..c87bfb46f --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_exact_midpoint_tie_even_contract.rs @@ -0,0 +1,35 @@ +//! Exact-midpoint tie-to-even regression contracts for four-observation bias SE. + +use validation_core::bias_standard_error; + +#[test] +fn exact_midpoint_selects_lower_even_binary64_neighbor() { + let truth = [0.0; 4]; + let recovered = [ + 0.0, + 0.0, + 8_106_479_329_266_891.0, + 9_007_199_254_740_990.0, + ]; + + let standard_error = + bias_standard_error(&truth, &recovered).expect("finite exact-midpoint standard error"); + + assert_eq!(standard_error.to_bits(), 0x4321_9999_9999_9998); +} + +#[test] +fn exact_midpoint_selects_upper_even_binary64_neighbor() { + let truth = [0.0; 4]; + let recovered = [ + 0.0, + 0.0, + 8_106_479_329_266_873.0, + 9_007_199_254_740_970.0, + ]; + + let standard_error = + bias_standard_error(&truth, &recovered).expect("finite exact-midpoint standard error"); + + assert_eq!(standard_error.to_bits(), 0x4321_9999_9999_998e); +} From 073d8d9277eee4bf7163379ef1769485358a06ae Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 15:39:46 +0900 Subject: [PATCH 533/576] docs(validation): record exact midpoint tie coverage --- ...validation-bias-standard-error-wide-linear-admission-bound.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md index 79f9b7ba2..e6692e6d2 100644 --- a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md +++ b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md @@ -6,3 +6,4 @@ - Characterized the next end-to-end boundary at represented `n=2050`: although Wide256 recovers the 118-bit exact pair numerator, the current exact candidate-square comparison would require 136-bit operands and the adjacent midpoint comparison 140-bit operands. Wide256 exact comparisons prove the binary64 seed `0x4296998e1aff78de` is below the target square but below-neighbor midpoint logic still selects it as the nearest result. Widening only the O(n) numerator identity is therefore insufficient; exact-rounding comparison width must be repaired or separately bounded before production admission can widen. - Added an exponent-safe two-limb comparison characterization for the same represented `n=2050` boundary. It compares `Wide256` mantissas by exact top-bit position and aligned significand bits instead of materializing `2^k`, so the 136-bit candidate product and 140-bit midpoint product are ordered exactly while exponent gaps such as `-2148/-2149` remain representable as metadata rather than oversized integers. - Integrated that comparison primitive into the authoritative exact-rounding path without widening the sample-count admission. RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` requires the represented `n=2050` reduced ratio to survive comparison-product width; repair `e4a85f53a611922be7492fe906d62ce65787c18e` forms denominator-by-square products as exact two-limb values and compares signed dyadic exponents without materializing oversized powers of two. Production `bias_standard_error` remains `n=4..=16` pending exact-head GREEN, represented-input end-to-end equivalence, and measured Rust 1.98.0 release-mode resource evidence. +- Added public `bias_standard_error` exact-midpoint tie-to-even contracts for both parity directions. Four-observation residuals `[0,0,9s,10s]` have exact `SE=11s/4`; `s=900719925474099` must select lower-even `0x4321999999999998`, while `s=900719925474097` must select upper-even `0x432199999999998e`. This strengthens edge coverage of the production Wide256 midpoint comparator without changing the `n=4..=16` admission. From 7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 16:02:15 +0900 Subject: [PATCH 534/576] test(validation): characterize represented exact-route equivalence --- ...nted_route_equivalence_characterization.rs | 271 ++++++++++++++++++ 1 file changed, 271 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs diff --git a/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs b/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs new file mode 100644 index 000000000..593e2e2cc --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs @@ -0,0 +1,271 @@ +//! Characterizes represented-input equivalence between pairwise and wide-linear bias-SE proofs. +//! +//! This is test-only evidence for issue #491. It deliberately leaves production +//! admission at `n=4..=16`: the pairwise O(n²) proof remains authoritative until +//! represented-input route equivalence, exact rounding, resource evidence, and +//! protected-head quality gates are all satisfied. + +#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] +struct Wide256 { + high: u128, + low: u128, +} + +impl Wide256 { + fn multiply_u128(left: u128, right: u128) -> Self { + let mask = u128::from(u64::MAX); + let left_limbs = [ + u64::try_from(left & mask).expect("masked low limb fits u64"), + u64::try_from(left >> 64).expect("high limb fits u64"), + ]; + let right_limbs = [ + u64::try_from(right & mask).expect("masked low limb fits u64"), + u64::try_from(right >> 64).expect("high limb fits u64"), + ]; + let mut limbs = [0_u64; 4]; + + for (left_index, left_limb) in left_limbs.iter().copied().enumerate() { + let mut carry = 0_u128; + for (right_index, right_limb) in right_limbs.iter().copied().enumerate() { + let limb_index = left_index + right_index; + let accumulator = u128::from(left_limb) + .checked_mul(u128::from(right_limb)) + .expect("64-bit limb product fits u128") + .checked_add(u128::from(limbs[limb_index])) + .expect("schoolbook partial sum fits u128") + .checked_add(carry) + .expect("schoolbook carry sum fits u128"); + limbs[limb_index] = + u64::try_from(accumulator & mask).expect("masked schoolbook limb fits u64"); + carry = accumulator >> 64; + } + limbs[left_index + 2] = + u64::try_from(carry).expect("schoolbook multiplication carry fits u64"); + } + + Self { + high: u128::from(limbs[2]) | (u128::from(limbs[3]) << 64), + low: u128::from(limbs[0]) | (u128::from(limbs[1]) << 64), + } + } + + fn checked_sub(self, right: Self) -> Option { + let (low, borrow) = self.low.overflowing_sub(right.low); + let high = self + .high + .checked_sub(right.high)? + .checked_sub(u128::from(u8::from(borrow)))?; + Some(Self { high, low }) + } + + fn as_u128(self) -> Option { + (self.high == 0).then_some(self.low) + } +} + +fn subtraction_roundoff(recovered: f64, truth: f64, residual: f64) -> f64 { + let negated_truth = -truth; + let truth_virtual = residual - recovered; + let recovered_virtual = residual - truth_virtual; + let recovered_roundoff = recovered - recovered_virtual; + let truth_roundoff = negated_truth - truth_virtual; + recovered_roundoff + truth_roundoff +} + +fn positive_dyadic(value: f64) -> Option<(u128, i32)> { + if !value.is_finite() || value <= 0.0 { + return None; + } + let bits = value.to_bits(); + let exponent_bits = i32::try_from((bits >> 52) & 0x7ff).ok()?; + let fraction = bits & 0x000f_ffff_ffff_ffff; + let (mut significand, mut exponent) = if exponent_bits == 0 { + (u128::from(fraction), -1074) + } else { + ( + u128::from((1_u64 << 52) | fraction), + exponent_bits - 1023 - 52, + ) + }; + if significand == 0 { + return None; + } + let trailing = significand.trailing_zeros(); + significand >>= trailing; + exponent += i32::try_from(trailing).ok()?; + Some((significand, exponent)) +} + +fn multiply_by_power_of_two(value: u128, shift: u32) -> Option { + value.checked_mul(1_u128.checked_shl(shift)?) +} + +fn represented_values(sample_count: usize) -> Vec { + assert!(sample_count >= 3); + let diameter = (1_u64 << 53) as f64; + let mut values = Vec::with_capacity(sample_count); + values.extend([0.0, 1.0]); + values.extend((2..sample_count).map(|_| diameter)); + values +} + +fn pairwise_exact_numerator(values: &[f64]) -> Option<(u128, i32)> { + let mut unit_exponent = i32::MAX; + for left in 0..values.len() { + for right in left + 1..values.len() { + let difference = values[left] - values[right]; + if !difference.is_finite() + || subtraction_roundoff(values[left], values[right], difference) != 0.0 + { + return None; + } + if difference != 0.0 { + unit_exponent = unit_exponent.min(positive_dyadic(difference.abs())?.1); + } + } + } + if unit_exponent == i32::MAX { + return Some((0, 0)); + } + + let mut pair_square_sum = 0_u128; + for left in 0..values.len() { + for right in left + 1..values.len() { + let difference = values[left] - values[right]; + if difference == 0.0 { + continue; + } + let (significand, exponent) = positive_dyadic(difference.abs())?; + let shift = exponent.checked_sub(unit_exponent)?.unsigned_abs(); + let coefficient = multiply_by_power_of_two(significand, shift)?; + pair_square_sum = pair_square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; + } + } + Some((pair_square_sum, unit_exponent)) +} + +fn wide_linear_exact_numerator(values: &[f64]) -> Option<(u128, i32, u128, u128)> { + let anchor = values + .iter() + .copied() + .min_by(f64::total_cmp)?; + let mut dyadics = Vec::with_capacity(values.len()); + let mut unit_exponent = i32::MAX; + + for value in values.iter().copied() { + let difference = value - anchor; + if !difference.is_finite() || subtraction_roundoff(value, anchor, difference) != 0.0 { + return None; + } + if difference == 0.0 { + dyadics.push(None); + continue; + } + let dyadic = positive_dyadic(difference)?; + unit_exponent = unit_exponent.min(dyadic.1); + dyadics.push(Some(dyadic)); + } + if unit_exponent == i32::MAX { + return Some((0, 0, 0, 0)); + } + + let mut coefficient_sum = 0_u128; + let mut square_sum = 0_u128; + for dyadic in dyadics.into_iter().flatten() { + let shift = dyadic.1.checked_sub(unit_exponent)?.unsigned_abs(); + let coefficient = multiply_by_power_of_two(dyadic.0, shift)?; + coefficient_sum = coefficient_sum.checked_add(coefficient)?; + square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; + } + + let sample_count = u128::try_from(values.len()).ok()?; + let numerator = Wide256::multiply_u128(sample_count, square_sum) + .checked_sub(Wide256::multiply_u128(coefficient_sum, coefficient_sum))? + .as_u128()?; + Some((numerator, unit_exponent, coefficient_sum, square_sum)) +} + +fn gcd(mut left: u128, mut right: u128) -> u128 { + while right != 0 { + let remainder = left % right; + left = right; + right = remainder; + } + left +} + +#[test] +fn represented_pairwise_and_wide_linear_routes_share_the_same_exact_ratio() { + for sample_count in [4_usize, 16, 17, 65, 257, 2_050] { + let values = represented_values(sample_count); + for value in values.iter().copied() { + let residual = value - 0.0; + assert_eq!( + subtraction_roundoff(value, 0.0, residual), + 0.0, + "represented residual construction must remain exact for n={sample_count}" + ); + } + + let (pair_numerator, pair_exponent) = + pairwise_exact_numerator(&values).expect("pairwise represented-input authority"); + let (wide_numerator, wide_exponent, coefficient_sum, square_sum) = + wide_linear_exact_numerator(&values).expect("wide-linear represented-input candidate"); + assert_eq!(wide_exponent, pair_exponent, "common exact unit must agree"); + assert_eq!( + wide_numerator, pair_numerator, + "wide O(n) identity must preserve the O(n²) exact pair numerator for n={sample_count}" + ); + + let sample_count_u128 = u128::try_from(sample_count).expect("sample count fits u128"); + let denominator = sample_count_u128 + .checked_mul(sample_count_u128) + .and_then(|value| value.checked_mul(sample_count_u128 - 1)) + .expect("scientific denominator fits u128"); + let divisor = gcd(pair_numerator, denominator); + assert_eq!( + (wide_numerator / divisor, denominator / divisor, wide_exponent), + (pair_numerator / divisor, denominator / divisor, pair_exponent), + "both routes must present the exact rounder with the same reduced ratio" + ); + + if sample_count == 2_050 { + assert!( + sample_count_u128.checked_mul(square_sum).is_none(), + "n=2050 must exercise the wider cancellation product" + ); + assert!( + coefficient_sum.checked_mul(coefficient_sum).is_none(), + "n=2050 must exercise the wider squared-sum product" + ); + assert_eq!( + pair_numerator, + 332_306_998_946_228_931_332_463_617_650_984_961_u128 + ); + assert_eq!(denominator, 8_610_922_500); + assert_eq!(divisor, 1); + assert_eq!(pair_exponent, 0); + assert_eq!( + ((pair_numerator as f64) / (denominator as f64)).sqrt().to_bits(), + 0x4296_998e_1aff_78de, + "the shared exact ratio must reach the already-authoritative n=2050 exact-rounding fixture" + ); + } + } +} + +#[test] +fn wide_linear_route_is_order_invariant_when_the_exact_anchor_moves() { + let mut values = represented_values(65); + let forward = wide_linear_exact_numerator(&values).expect("forward route"); + values.reverse(); + let reversed = wide_linear_exact_numerator(&values).expect("reversed route"); + assert_eq!(forward.0, reversed.0); + assert_eq!(forward.1, reversed.1); + assert_eq!( + forward.0, + pairwise_exact_numerator(&values) + .expect("reversed pair authority") + .0 + ); +} From 0460463d7daa39bb6933a7a13dd523dc19263e20 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 16:03:36 +0900 Subject: [PATCH 535/576] docs(validation): trace represented route equivalence --- ...-standard-error-wide-linear-admission-bound.md | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index aea9bb0ca..5a06c4b54 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -64,19 +64,27 @@ This is a production primitive integration, not a sample-count admission change. The standards basis remains the current published floating-point standards, IEEE 754-2019 and ISO/IEC 60559:2020. IEEE currently lists 754-2019 as an active standard and ISO lists ISO/IEC 60559:2020 as the published international standard; IEEE P754 is an active revision project and is not treated as a published replacement in this decision. +## Represented route-equivalence slice + +Characterization `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` adds an actual two-pass O(n²) pairwise authority comparison against the two-limb O(n) identity for represented residual classes `{0, 1, 2^53}` at `n = 4, 16, 17, 65, 257, 2050`. It does not use the earlier analytic pair-numerator formula for the comparison: represented pair differences are checked for exact binary64 subtraction, normalized to a common dyadic unit, squared, and accumulated by the pairwise route. Independently, the wide-linear route selects the represented minimum as anchor, verifies every anchor-relative subtraction, derives the same dyadic unit, and computes `n*S2 - S1^2` with full-width products and subtraction. + +For every characterized sample count, both routes must produce the same unit exponent, exact pair numerator, and GCD-reduced `(numerator, denominator, unit_exponent)` tuple presented to the exact rounder. The `n = 2050` fixture is the resource boundary rather than a small-only identity check: both narrow cancellation products must overflow, while the pairwise and Wide256 routes must agree on `P = 332306998946228931332463617650984961`, denominator `8610922500`, divisor `1`, and unit exponent `0`. The characterization also reverses the `n = 65` represented sequence to verify that selecting the minimum exact anchor makes the wide-linear result independent of observation order. + +This closes a deterministic represented-input arithmetic-equivalence slice, not the whole production admission decision. The test still carries a test-only copy of the candidate arithmetic and does not route `bias_standard_error` samples above `n=16` through the wider implementation. Production integration therefore still requires one implementation of canonical represented normalization and Wide256 numerator routing in the owning module, same-head exact-rounding/tie-to-even evidence, and comparison against the pairwise authority before the fallback can be demoted or the cutoff widened. + ## Consequence for #491 The candidate numerator route remains `narrow O(n) -> Wide256 O(n) -> buffered pair fail-closed fallback`, introduced by RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` and repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5`. The predecessor narrow-to-pair hybrid remains a comparison baseline. For odd `D = 2^58 + 1`, `n = 65`, the predecessor hybrid must still select the pair fallback because its narrow products overflow. The newer candidate must select the wider-product route, recover the same exact restored numerator as both O(n²) references, and report `used_wide_product=true` with `used_pairwise_fallback=false`. The power-of-two-normalized `D=2^58,n=65` geometry and odd `n=64` geometry remain narrow-path admissions. Separate route observability matters because a correct exact numerator does not by itself show whether the candidate avoided O(n²) allocation. -The product-width theorem makes a post-Wide256 pair fallback look redundant for the numerator identity within the canonical `u128` coefficient domain. The represented `n=2050` midpoint finding showed why this did not extend automatically to the whole exact-rounding proof. The exponent-safe comparison is now integrated into the authoritative rounding primitive, so the remaining equivalence work moves outward: canonical represented-residual normalization, wider numerator routing, reduced ratio construction, candidate stepping/tie-to-even behavior, and final fail-closed/fallback decisions must agree with the pairwise authority across admitted represented inputs. +The product-width theorem makes a post-Wide256 pair fallback look redundant for the numerator identity within the canonical `u128` coefficient domain. The represented `n=2050` midpoint finding showed why this did not extend automatically to the whole exact-rounding proof. The exponent-safe comparison is now integrated into the authoritative rounding primitive, and `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` now demonstrates equality of the pairwise and wide-linear exact-ratio inputs across a deterministic represented-input slice. The remaining equivalence work moves outward to production canonicalization/routing and fail-closed decisions across the represented domain, not to another sample-count staircase. -The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The represented-input characterization is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`. The exponent-safe comparison characterization is `crates/validation_core/tests/bias_standard_error_wide_scaled_comparison_characterization.rs`. The timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. +The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The represented-input width characterization is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`. The represented route-equivalence characterization is `crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs`. The exponent-safe comparison characterization is `crates/validation_core/tests/bias_standard_error_wide_scaled_comparison_characterization.rs`. The timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. ## Decision -Do not widen production `validation_core::bias_standard_error` solely because the exact-rounding primitive now supports wider comparison products. Production admission remains `n=4..=16`. A budget change requires represented-input admission equivalence for the complete wider route, exact tie-to-even preservation, recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. +Do not widen production `validation_core::bias_standard_error` solely because the exact-rounding primitive now supports wider comparison products or because the deterministic represented route-equivalence slice passes. Production admission remains `n=4..=16`. A budget change requires represented-input admission equivalence for the complete production wider route, exact tie-to-even preservation, recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. ## Traceability @@ -90,6 +98,7 @@ Do not widen production `validation_core::bias_standard_error` solely because th - Exponent-safe scaled comparison characterization: `aab9fe9115cee97225f2aa81e54a55ceafb23336` - Production scaled-comparison RED: `f7717361ad8c5f0592688c1514c104cc1b4adabe` - Production scaled-comparison repair: `e4a85f53a611922be7492fe906d62ce65787c18e` +- Represented pair/wide exact-ratio equivalence: `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` - Narrow-wide-pair RED: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` - Narrow-wide-pair repair: `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` - CHANGELOG fragment: `CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md` From 2a0a5e0b014b4fb03eae96cb6bbb8a92296e5494 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 16:03:52 +0900 Subject: [PATCH 536/576] docs(validation): record represented route equivalence --- ...validation-bias-standard-error-wide-linear-admission-bound.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md index e6692e6d2..d70c62807 100644 --- a/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md +++ b/CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md @@ -7,3 +7,4 @@ - Added an exponent-safe two-limb comparison characterization for the same represented `n=2050` boundary. It compares `Wide256` mantissas by exact top-bit position and aligned significand bits instead of materializing `2^k`, so the 136-bit candidate product and 140-bit midpoint product are ordered exactly while exponent gaps such as `-2148/-2149` remain representable as metadata rather than oversized integers. - Integrated that comparison primitive into the authoritative exact-rounding path without widening the sample-count admission. RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` requires the represented `n=2050` reduced ratio to survive comparison-product width; repair `e4a85f53a611922be7492fe906d62ce65787c18e` forms denominator-by-square products as exact two-limb values and compares signed dyadic exponents without materializing oversized powers of two. Production `bias_standard_error` remains `n=4..=16` pending exact-head GREEN, represented-input end-to-end equivalence, and measured Rust 1.98.0 release-mode resource evidence. - Added public `bias_standard_error` exact-midpoint tie-to-even contracts for both parity directions. Four-observation residuals `[0,0,9s,10s]` have exact `SE=11s/4`; `s=900719925474099` must select lower-even `0x4321999999999998`, while `s=900719925474097` must select upper-even `0x432199999999998e`. This strengthens edge coverage of the production Wide256 midpoint comparator without changing the `n=4..=16` admission. +- Added a represented-input pair/wide route-equivalence characterization over `n=4,16,17,65,257,2050`. It computes the O(n²) pair numerator from actual exact binary64 pair differences, independently computes the O(n) identity with Wide256 cancellation products, and requires both routes to yield the same dyadic unit and GCD-reduced exact ratio. The `n=2050` case exercises both narrow-product overflows and still agrees on `P=332306998946228931332463617650984961`, denominator `8610922500`, divisor `1`, and unit exponent `0`; an order-reversal fixture also verifies anchor selection is observation-order invariant. This is deterministic arithmetic-equivalence evidence only and does not widen production admission. From 2bc1d2284d75154e020640adb573c1cfadf005fb Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 16:25:15 +0900 Subject: [PATCH 537/576] test(validation): characterize anchor-linear proof admission --- ...nchor_linear_admission_characterization.rs | 101 ++++++++++++++++++ 1 file changed, 101 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_anchor_linear_admission_characterization.rs diff --git a/crates/validation_core/tests/bias_standard_error_anchor_linear_admission_characterization.rs b/crates/validation_core/tests/bias_standard_error_anchor_linear_admission_characterization.rs new file mode 100644 index 000000000..cd437794c --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_anchor_linear_admission_characterization.rs @@ -0,0 +1,101 @@ +//! Characterizes an anchor-linear exact proof that is stricter about coordinates than pair subtraction. +//! +//! This is test-only evidence for issue #491. Production admission remains +//! `n=4..=16` and the current pairwise proof remains the fail-closed authority. +//! The fixture isolates a represented-input geometry where every subtraction from +//! the minimum anchor is exact, while one non-anchor pair subtraction is rounded. +//! Exact integer coordinates still recover the pair-distance numerator through +//! `n * sum(c_i^2) - (sum(c_i))^2`, so pairwise-f64 subtraction exactness is not a +//! scientific prerequisite for this bounded represented geometry. + +use validation_core::bias_standard_error; + +fn subtraction_roundoff(recovered: f64, truth: f64, residual: f64) -> f64 { + let negated_truth = -truth; + let truth_virtual = residual - recovered; + let recovered_virtual = residual - truth_virtual; + let recovered_roundoff = recovered - recovered_virtual; + let truth_roundoff = negated_truth - truth_virtual; + recovered_roundoff + truth_roundoff +} + +fn exact_pair_numerator(coefficients: &[u128]) -> u128 { + let mut total = 0_u128; + for left in 0..coefficients.len() { + for right in left + 1..coefficients.len() { + let difference = coefficients[left].abs_diff(coefficients[right]); + total = total + .checked_add( + difference + .checked_mul(difference) + .expect("bounded coefficient square fits u128"), + ) + .expect("bounded pair numerator fits u128"); + } + } + total +} + +#[test] +fn anchor_linear_identity_survives_a_rounded_non_anchor_pair_difference() { + let tiny = 2.0_f64.powi(-54); + let residuals = [0.0, 1.0, tiny, 2.0]; + + for residual in residuals { + let anchored = residual - 0.0; + assert_eq!( + subtraction_roundoff(residual, 0.0, anchored), + 0.0, + "minimum-anchor coordinates must be represented exactly" + ); + } + + let rounded_non_anchor_pair = residuals[1] - residuals[2]; + assert_ne!( + subtraction_roundoff(residuals[1], residuals[2], rounded_non_anchor_pair), + 0.0, + "the fixture must remain outside the current pairwise-f64 subtraction proof" + ); + + // Common exact unit is 2^-54, giving integer coordinates + // [0, 2^54, 1, 2^55]. + let coefficients = [0_u128, 1_u128 << 54, 1, 1_u128 << 55]; + let coefficient_sum = coefficients + .iter() + .copied() + .try_fold(0_u128, |sum, value| sum.checked_add(value)) + .expect("bounded coefficient sum fits u128"); + let square_sum = coefficients + .iter() + .copied() + .try_fold(0_u128, |sum, value| { + sum.checked_add(value.checked_mul(value)?) + }) + .expect("bounded squared-coordinate sum fits u128"); + let sample_count = u128::try_from(coefficients.len()).expect("sample count fits u128"); + let linear_numerator = sample_count + .checked_mul(square_sum) + .and_then(|scaled_square_sum| { + coefficient_sum + .checked_mul(coefficient_sum) + .and_then(|squared_sum| scaled_square_sum.checked_sub(squared_sum)) + }) + .expect("bounded anchor-linear numerator fits u128"); + let pair_numerator = exact_pair_numerator(&coefficients); + + assert_eq!(linear_numerator, pair_numerator); + assert_eq!( + linear_numerator, + 3_569_704_090_242_693_886_528_325_169_446_915_u128 + ); + + // The public result already remains numerically correct through the generic + // fallback. This characterization concerns exact-proof admission and the + // avoidable O(n^2) pairwise-f64 prerequisite, not a changed public value. + assert_eq!( + bias_standard_error(&[0.0; 4], &residuals) + .expect("represented fixture remains scientifically computable") + .to_bits(), + 0x3fde_a33e_2c83_c140 + ); +} From 8050b4918803e69d8746167948a216d90a0cbd5a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 16:26:58 +0900 Subject: [PATCH 538/576] docs(validation): record anchor-linear admission finding --- .../validation-bias-exact-proof-budget-characterization.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md index ffa8b990d..1fadd37fe 100644 --- a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md +++ b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md @@ -7,4 +7,5 @@ - Record the canonical accumulator bound `Σc_i <= Σc_i² <= Σ(i Wide256 O(n) -> buffered pair fail-closed fallback`. RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` requires the missing route; repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` implements it and records both wide-product selection and pairwise fallback separately in CSV. - The corrected harness keeps the existing narrow-to-pair hybrid for comparison. On odd `D=2^58+1, n=65`, that predecessor hybrid still allocates the pair buffer, while the new narrow-to-wide-to-pair candidate must recover the same exact numerator through `Wide256` with `used_wide_product=true` and `used_pairwise_fallback=false`. Power-of-two-normalized `n=65` and odd `n=64` remain narrow-path admissions. -- Keep production `bias_standard_error` admission unchanged at `n=4..=16`; all O(n)/Wide256 hybrids remain characterization tooling only. A production change still requires recorded Rust 1.98.0 release-mode CPU/raw CSV, allocator/RSS evidence, represented-input admission comparison, applicable buyer-path p95 evidence, exact-head CI/security/documentation GREEN, and qualifying independent current-head review. +- Represented-input equivalence now also distinguishes proof admission from public numerical correctness. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` uses residuals `[0,1,2^-54,2]`: every minimum-anchor subtraction is exact, but the non-anchor subtraction `1 - 2^-54` rounds in binary64, so the current O(n²) pairwise-f64 proof refuses. On common unit `2^-54`, integer coordinates `[0,2^54,1,2^55]` give the same exact pair numerator `3569704090242693886528325169446915` through direct integer pair distances and `n*Σc_i²-(Σc_i)²`. The public metric already returns the correctly represented result through its generic fallback, so production O(n) work must treat exact non-anchor pair subtraction as a reference-path condition, not a scientific prerequisite for anchor-linear admission. +- Keep production `bias_standard_error` admission unchanged at `n=4..=16`; all O(n)/Wide256 hybrids remain characterization tooling only. A production change still requires canonical anchor-relative normalization in the owning module, explicit equivalence where pairwise and linear routes both admit, explicit coverage of intended anchor-linear-only admissions, recorded Rust 1.98.0 release-mode CPU/raw CSV and allocator/RSS evidence, applicable buyer-path p95 evidence, exact-head CI/security/documentation GREEN, and qualifying independent current-head review. From ea592b32a0733ede28390945e70c332a2cb4f3a5 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 16:28:12 +0900 Subject: [PATCH 539/576] docs(research): trace anchor-linear proof admission --- ...ndard-error-wide-linear-admission-bound.md | 19 +++++++++++++++---- 1 file changed, 15 insertions(+), 4 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index 5a06c4b54..a60ea49f5 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -72,19 +72,29 @@ For every characterized sample count, both routes must produce the same unit exp This closes a deterministic represented-input arithmetic-equivalence slice, not the whole production admission decision. The test still carries a test-only copy of the candidate arithmetic and does not route `bias_standard_error` samples above `n=16` through the wider implementation. Production integration therefore still requires one implementation of canonical represented normalization and Wide256 numerator routing in the owning module, same-head exact-rounding/tie-to-even evidence, and comparison against the pairwise authority before the fallback can be demoted or the cutoff widened. +## Anchor-exact admission is broader than pairwise-f64 admission + +Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` isolates a proof-admission distinction inside the existing `n=4` production sample budget. Use represented residuals `[0, 1, 2^-54, 2]` with truth fixed at represented zero. Every residual and every subtraction from the minimum anchor `0` is exact. The non-anchor subtraction `1 - 2^-54`, however, rounds in binary64; the current O(n²) pair proof therefore refuses before exact integer pair accumulation. + +That refusal is a limitation of the reference path, not evidence that the represented geometry lacks an exact pair-distance proof. With common unit `2^-54`, the anchor-relative integer coefficients are `[0, 2^54, 1, 2^55]`. Direct integer pair distances and the linear identity agree exactly on + +`P = 3569704090242693886528325169446915`. + +The public metric already returns the correctly represented standard error `0x3fdea33e2c83c140` through its generic translated fallback, so this fixture is not a public numerical defect. It demonstrates that requiring every non-anchor pair subtraction to be exact in binary64 is sufficient for the current O(n²) authority but is not a scientific prerequisite for canonical anchor-linear exact admission. A production O(n) route should therefore be evaluated for two properties separately: equality with the pair authority wherever both admit, and intentional strictly broader admission for anchor-exact geometries such as this one. The pairwise path remains fail-closed comparison evidence during that migration; the finding does not justify widening `n=4..=16`. + ## Consequence for #491 The candidate numerator route remains `narrow O(n) -> Wide256 O(n) -> buffered pair fail-closed fallback`, introduced by RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` and repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5`. The predecessor narrow-to-pair hybrid remains a comparison baseline. For odd `D = 2^58 + 1`, `n = 65`, the predecessor hybrid must still select the pair fallback because its narrow products overflow. The newer candidate must select the wider-product route, recover the same exact restored numerator as both O(n²) references, and report `used_wide_product=true` with `used_pairwise_fallback=false`. The power-of-two-normalized `D=2^58,n=65` geometry and odd `n=64` geometry remain narrow-path admissions. Separate route observability matters because a correct exact numerator does not by itself show whether the candidate avoided O(n²) allocation. -The product-width theorem makes a post-Wide256 pair fallback look redundant for the numerator identity within the canonical `u128` coefficient domain. The represented `n=2050` midpoint finding showed why this did not extend automatically to the whole exact-rounding proof. The exponent-safe comparison is now integrated into the authoritative rounding primitive, and `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` now demonstrates equality of the pairwise and wide-linear exact-ratio inputs across a deterministic represented-input slice. The remaining equivalence work moves outward to production canonicalization/routing and fail-closed decisions across the represented domain, not to another sample-count staircase. +The product-width theorem makes a post-Wide256 pair fallback look redundant for the numerator identity within the canonical `u128` coefficient domain. The represented `n=2050` midpoint finding showed why this did not extend automatically to the whole exact-rounding proof. The exponent-safe comparison is now integrated into the authoritative rounding primitive, `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` demonstrates equality of the pairwise and wide-linear exact-ratio inputs across a deterministic represented-input slice, and `2bc1d2284d75154e020640adb573c1cfadf005fb` demonstrates an intended anchor-linear-only admission slice. The remaining equivalence work moves outward to production canonicalization/routing and fail-closed decisions across the represented domain, not to another sample-count staircase. -The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The represented-input width characterization is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`. The represented route-equivalence characterization is `crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs`. The exponent-safe comparison characterization is `crates/validation_core/tests/bias_standard_error_wide_scaled_comparison_characterization.rs`. The timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. +The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The represented-input width characterization is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`. The represented route-equivalence characterization is `crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs`. The anchor-linear admission characterization is `crates/validation_core/tests/bias_standard_error_anchor_linear_admission_characterization.rs`. The exponent-safe comparison characterization is `crates/validation_core/tests/bias_standard_error_wide_scaled_comparison_characterization.rs`. The timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. ## Decision -Do not widen production `validation_core::bias_standard_error` solely because the exact-rounding primitive now supports wider comparison products or because the deterministic represented route-equivalence slice passes. Production admission remains `n=4..=16`. A budget change requires represented-input admission equivalence for the complete production wider route, exact tie-to-even preservation, recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. +Do not widen production `validation_core::bias_standard_error` solely because the exact-rounding primitive now supports wider comparison products, because the deterministic represented route-equivalence slice passes, or because anchor-linear proof admission is strictly broader than pairwise-f64 subtraction admission. Production admission remains `n=4..=16`. A budget change requires production canonical anchor-relative normalization and Wide256 routing, equality against the pairwise authority wherever both admit, explicit tests for intended anchor-linear-only admissions, exact tie-to-even preservation, recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. ## Traceability @@ -99,8 +109,9 @@ Do not widen production `validation_core::bias_standard_error` solely because th - Production scaled-comparison RED: `f7717361ad8c5f0592688c1514c104cc1b4adabe` - Production scaled-comparison repair: `e4a85f53a611922be7492fe906d62ce65787c18e` - Represented pair/wide exact-ratio equivalence: `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` +- Anchor-linear-only represented admission: `2bc1d2284d75154e020640adb573c1cfadf005fb` - Narrow-wide-pair RED: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` - Narrow-wide-pair repair: `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` -- CHANGELOG fragment: `CHANGELOG.d/validation-bias-standard-error-wide-linear-admission-bound.md` +- CHANGELOG fragment: `CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md` - Production module under decision: `crates/validation_core/src/bias_se.rs` - Exact-proof budget harness: `crates/validation_core/examples/bias_se_exact_proof_budget.rs` From fd9f9ff2c5c395e4cc13042232f4deef018adb48 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 17:00:53 +0900 Subject: [PATCH 540/576] test(validation): expose non-minimum anchor rounding gap --- ...nminimum_anchor_exact_rounding_contract.rs | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs new file mode 100644 index 000000000..410817486 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs @@ -0,0 +1,27 @@ +//! Regression contract for exact bias-SE recovery when the minimum residual is not an exact anchor. +//! +//! Issue #491 previously characterized a minimum-anchor linear proof. This fixture +//! shows why production admission must instead search deterministic exact anchors: +//! subtracting the minimum residual `-2^53` from `1` rounds, while anchor `0` +//! preserves every translated coordinate exactly. The exact pair numerator is +//! `243388915243820099130562543878155`, so `SE(mean)^2 = P / 48` and the +//! correctly rounded binary64 result is one ULP above the translated floating +//! moment fallback. Observation order must not change that scientific result. + +use validation_core::bias_standard_error; + +#[test] +fn exact_nonminimum_anchor_recovers_correctly_rounded_four_observation_bias_se() { + let diameter = 9_007_199_254_740_992.0_f64; // 2^53 + let truth = [0.0; 4]; + let recovered = [0.0, 1.0, 2.0, -diameter]; + + let forward = bias_standard_error(&truth, &recovered) + .expect("the exact represented residual geometry is scientifically computable"); + assert_eq!(forward.to_bits(), 0x4320_0000_0000_0001); + + let reversed = [recovered[3], recovered[2], recovered[1], recovered[0]]; + let reverse = bias_standard_error(&truth, &reversed) + .expect("permutation must preserve the exact represented geometry"); + assert_eq!(reverse.to_bits(), forward.to_bits()); +} From 81ba770cc4812c8fbeb4b3529f0a73b41abbed0f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 17:03:01 +0900 Subject: [PATCH 541/576] fix(validation): admit exact non-minimum anchor bias SE --- crates/validation_core/src/bias_se.rs | 228 +++++++++++++++++++++----- 1 file changed, 184 insertions(+), 44 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index f1d559467..ac9cfca8a 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -1,10 +1,11 @@ //! Exact represented-input admission for mean-bias standard error. //! //! The general bias implementation remains the fallback authority. This module -//! admits a bounded small-sample pair-distance identity whose residual and pairwise -//! differences are proven exact in binary64 and whose reduced dyadic pair-distance -//! ratio fits `u128`; the exact rational square root is then rounded against -//! binary64 midpoints without first rounding the ratio under the square root. +//! admits a bounded small-sample exact pair-distance identity when represented +//! residuals either have error-free pairwise differences or admit a deterministic +//! exact anchor translation whose dyadic integer numerator fits the bounded proof; +//! the exact rational square root is then rounded against binary64 midpoints +//! without first rounding the ratio under the square root. use crate::ValidationError; use core::cmp::Ordering; @@ -98,6 +99,21 @@ impl Wide256 { } } + fn checked_sub(self, right: Self) -> Option { + if self < right { + return None; + } + let borrow = if self.low < right.low { 1_u128 } else { 0_u128 }; + Some(Self { + high: self.high.checked_sub(right.high)?.checked_sub(borrow)?, + low: self.low.wrapping_sub(right.low), + }) + } + + const fn to_u128(self) -> Option { + if self.high == 0 { Some(self.low) } else { None } + } + const fn is_zero(self) -> bool { self.high == 0 && self.low == 0 } @@ -277,40 +293,12 @@ fn correctly_rounded_scaled_sqrt_ratio( None } -fn exact_pair_distance_standard_error( - truth: &[f64], - recovered: &[f64], -) -> Option> { - // Keep this O(n²) reference proof deliberately bounded. n=2 and n=3 have - // cheaper exact identities in `bias.rs`; four through sixteen observations are - // the smallest remaining sample sizes with demonstrated one-ULP errors in - // the translated floating moment/sqrt path. - if truth.len() != recovered.len() || !(4..=16).contains(&truth.len()) { - return None; - } - let sample_count = truth.len(); - - let mut residuals = Vec::with_capacity(sample_count); - for index in 0..sample_count { - let truth_value = truth[index]; - let recovered_value = recovered[index]; - if !truth_value.is_finite() || !recovered_value.is_finite() { - return None; - } - let residual = recovered_value - truth_value; - if !residual.is_finite() - || subtraction_roundoff(recovered_value, truth_value, residual) != 0.0 - { - return None; - } - residuals.push(residual); - } - - let pair_count = sample_count.checked_mul(sample_count.checked_sub(1)?)? / 2; +fn exact_pairwise_pair_square_sum(residuals: &[f64]) -> Option<(u128, i32)> { + let pair_count = residuals.len().checked_mul(residuals.len().checked_sub(1)?)? / 2; let mut pair_dyadics = Vec::with_capacity(pair_count); let mut unit_exponent = i32::MAX; - for left in 0..sample_count { - for right in left + 1..sample_count { + for left in 0..residuals.len() { + for right in left + 1..residuals.len() { let difference = residuals[left] - residuals[right]; if !difference.is_finite() || subtraction_roundoff(residuals[left], residuals[right], difference) != 0.0 @@ -327,7 +315,7 @@ fn exact_pair_distance_standard_error( } } if unit_exponent == i32::MAX { - return Some(Ok(0.0)); + return Some((0, 0)); } let mut pair_square_sum = 0_u128; @@ -337,6 +325,117 @@ fn exact_pair_distance_standard_error( let square = coefficient.checked_mul(coefficient)?; pair_square_sum = pair_square_sum.checked_add(square)?; } + Some((pair_square_sum, unit_exponent)) +} + +fn exact_anchor_linear_pair_square_sum(residuals: &[f64]) -> Option<(u128, i32)> { + let mut best: Option<(f64, f64, Vec)> = None; + for &anchor in residuals { + let mut translated = Vec::with_capacity(residuals.len()); + let mut max_magnitude = 0.0_f64; + let mut exact = true; + for &residual in residuals { + let coordinate = residual - anchor; + if !coordinate.is_finite() + || subtraction_roundoff(residual, anchor, coordinate) != 0.0 + { + exact = false; + break; + } + max_magnitude = max_magnitude.max(coordinate.abs()); + translated.push(coordinate); + } + if !exact { + continue; + } + + let should_replace = match &best { + None => true, + Some((best_max_magnitude, best_anchor, _)) => max_magnitude + .total_cmp(best_max_magnitude) + .then_with(|| anchor.total_cmp(best_anchor)) + .is_lt(), + }; + if should_replace { + best = Some((max_magnitude, anchor, translated)); + } + } + let (_, _, translated) = best?; + + let mut dyadics = Vec::with_capacity(translated.len()); + let mut unit_exponent = i32::MAX; + for &coordinate in &translated { + if coordinate == 0.0 { + dyadics.push(None); + continue; + } + let dyadic = positive_dyadic(coordinate.abs())?; + unit_exponent = unit_exponent.min(dyadic.1); + dyadics.push(Some((coordinate.is_sign_negative(), dyadic))); + } + if unit_exponent == i32::MAX { + return Some((0, 0)); + } + + let mut positive_sum = 0_u128; + let mut negative_sum = 0_u128; + let mut square_sum = 0_u128; + for dyadic in dyadics.into_iter().flatten() { + let (negative, (significand, exponent)) = dyadic; + let shift = exponent.checked_sub(unit_exponent)?.unsigned_abs(); + let coefficient = multiply_by_power_of_two(significand, shift)?; + if negative { + negative_sum = negative_sum.checked_add(coefficient)?; + } else { + positive_sum = positive_sum.checked_add(coefficient)?; + } + square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; + } + + let sample_count = u128::try_from(residuals.len()).ok()?; + let scaled_square_sum = Wide256::multiply_u128(sample_count, square_sum); + let signed_sum_magnitude = positive_sum.abs_diff(negative_sum); + let squared_sum = Wide256::multiply_u128(signed_sum_magnitude, signed_sum_magnitude); + let numerator = scaled_square_sum.checked_sub(squared_sum)?.to_u128()?; + Some((numerator, unit_exponent)) +} + +fn exact_pair_distance_standard_error( + truth: &[f64], + recovered: &[f64], +) -> Option> { + // Keep the exact proof deliberately bounded. n=2 and n=3 have cheaper exact + // identities in `bias.rs`; four through sixteen observations are the smallest + // remaining sample sizes with demonstrated one-ULP errors in translated + // floating moment/sqrt paths. + if truth.len() != recovered.len() || !(4..=16).contains(&truth.len()) { + return None; + } + let sample_count = truth.len(); + + let mut residuals = Vec::with_capacity(sample_count); + for index in 0..sample_count { + let truth_value = truth[index]; + let recovered_value = recovered[index]; + if !truth_value.is_finite() || !recovered_value.is_finite() { + return None; + } + let residual = recovered_value - truth_value; + if !residual.is_finite() + || subtraction_roundoff(recovered_value, truth_value, residual) != 0.0 + { + return None; + } + residuals.push(residual); + } + + // Preserve the pairwise-f64 proof as the first authority. If one represented + // non-anchor pair subtraction rounds, search every represented residual as an + // exact translation anchor, choose the smallest exact dynamic range with a + // represented-value tie-break, and recover the same translation-invariant + // pair numerator through n*Σc_i²-(Σc_i)² using two-limb cancellation products. + let (pair_square_sum, unit_exponent) = exact_pairwise_pair_square_sum(&residuals) + .or_else(|| exact_anchor_linear_pair_square_sum(&residuals))?; if pair_square_sum == 0 { return Some(Ok(0.0)); } @@ -368,10 +467,11 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- through sixteen-observation samples whose represented residuals and -/// pairwise differences are exact use the exact pair-distance identity when its -/// reduced dyadic ratio fits the bounded integer proof. All other samples retain -/// the established bias implementation and its existing fail-closed behavior. +/// Four- through sixteen-observation samples whose represented residuals admit +/// either the exact pairwise-difference proof or a deterministic exact anchor +/// translation use the exact pair-distance identity when its reduced dyadic ratio +/// fits the bounded integer proof. All other samples retain the established bias +/// implementation and its existing fail-closed behavior. pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result { if let Some(result) = exact_pair_distance_standard_error(truth, recovered) { return result; @@ -383,7 +483,8 @@ pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result Date: Sun, 6 Sep 2026 17:07:54 +0900 Subject: [PATCH 542/576] docs(validation): record exact anchor production repair --- ...validation-bias-exact-proof-budget-characterization.md | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md index 1fadd37fe..9e97c82a1 100644 --- a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md +++ b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md @@ -4,8 +4,10 @@ - Distinguish the current minimum-shifted O(n) `u128` intermediate envelope (`n=2,047` at aligned diameter `2^53`) from the wider exact pair-square numerator envelope (`n=4,095` for the same diameter); neither is a production sample-count budget. - Normalize the shared power-of-two dyadic unit before judging checked O(n) intermediates. The former `D=2^58, n=65` refusal was a characterization artifact: factoring the common `2^58` unit reduces the aligned coefficients to zero/one and preserves the exact restored numerator `2^122` without pair fallback. - Preserve the non-equivalence finding with a normalized counterexample rather than the raw-scale artifact. With one zero and the remaining coefficients at odd diameter `D=2^58+1`, the common dyadic unit is one; both pair and narrow O(n) kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits `u128` but the narrow O(n) products require 129-bit intermediates before cancellation. -- Record the canonical accumulator bound `Σc_i <= Σc_i² <= Σ(i Wide256 O(n) -> buffered pair fail-closed fallback`. RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` requires the missing route; repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` implements it and records both wide-product selection and pairwise fallback separately in CSV. - The corrected harness keeps the existing narrow-to-pair hybrid for comparison. On odd `D=2^58+1, n=65`, that predecessor hybrid still allocates the pair buffer, while the new narrow-to-wide-to-pair candidate must recover the same exact numerator through `Wide256` with `used_wide_product=true` and `used_pairwise_fallback=false`. Power-of-two-normalized `n=65` and odd `n=64` remain narrow-path admissions. -- Represented-input equivalence now also distinguishes proof admission from public numerical correctness. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` uses residuals `[0,1,2^-54,2]`: every minimum-anchor subtraction is exact, but the non-anchor subtraction `1 - 2^-54` rounds in binary64, so the current O(n²) pairwise-f64 proof refuses. On common unit `2^-54`, integer coordinates `[0,2^54,1,2^55]` give the same exact pair numerator `3569704090242693886528325169446915` through direct integer pair distances and `n*Σc_i²-(Σc_i)²`. The public metric already returns the correctly represented result through its generic fallback, so production O(n) work must treat exact non-anchor pair subtraction as a reference-path condition, not a scientific prerequisite for anchor-linear admission. -- Keep production `bias_standard_error` admission unchanged at `n=4..=16`; all O(n)/Wide256 hybrids remain characterization tooling only. A production change still requires canonical anchor-relative normalization in the owning module, explicit equivalence where pairwise and linear routes both admit, explicit coverage of intended anchor-linear-only admissions, recorded Rust 1.98.0 release-mode CPU/raw CSV and allocator/RSS evidence, applicable buyer-path p95 evidence, exact-head CI/security/documentation GREEN, and qualifying independent current-head review. +- Represented-input equivalence distinguishes proof admission from public numerical correctness. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` uses residuals `[0,1,2^-54,2]`: every minimum-anchor subtraction is exact, but `1 - 2^-54` rounds in binary64, so the O(n²) pairwise-f64 proof refuses. On common unit `2^-54`, integer coordinates `[0,2^54,1,2^55]` give exact pair numerator `3569704090242693886528325169446915`; the generic fallback happens to return the correct public value. +- Add source-level RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` for a stronger represented geometry, residuals `[0,1,2,-2^53]`. The minimum residual cannot exactly translate `1`, but anchor `0` preserves every coordinate. Signed unit-one coordinates give `P=243388915243820099130562543878155`, so `SE(mean)^2=P/48`; the predecessor translated floating-moment path returns `0x4320000000000000` while exact rounding requires adjacent `0x4320000000000001`. The RED workflows were cancelled by the immediate successor push and are not hosted RED evidence. +- Repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` keeps pairwise exact accumulation first, then searches all represented residuals for an exact translation anchor, chooses the smallest exact translated dynamic range with a represented-value tie-break, builds signed dyadic coordinates, computes `n*Σc_i²-(Σc_i)²` with `Wide256` cancellation products, and reuses the exact candidate/midpoint tie-to-even rounder. This also promotes `[0,1,2^-54,2]` from generic fallback to exact anchor admission without widening the sample-count budget. +- Keep production `bias_standard_error` sample admission unchanged at `n=4..=16`. A budget change beyond 16 still requires pair/anchor same-domain equivalence plus intended anchor-only admissions, recorded Rust 1.98.0 release-mode CPU/raw CSV and allocator/RSS evidence, applicable buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch/security/documentation GREEN, current TRACEABILITY/research/operator doctoring, and qualifying independent current-head review. From b84b7fe80caa530990f9725d1aa1573792a7cf52 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 17:08:44 +0900 Subject: [PATCH 543/576] docs(research): trace deterministic exact-anchor bias-SE repair --- ...ndard-error-wide-linear-admission-bound.md | 100 +++++++++--------- 1 file changed, 49 insertions(+), 51 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index a60ea49f5..63c2ccc7d 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -2,99 +2,93 @@ ## Problem -Issue #491 previously required a fixture where the two-limb O(n) exact pair-numerator reference would refuse because the normalized coefficient sum or normalized square sum overflowed `u128` while the O(n²) pair numerator still fit. That search target is inconsistent with the canonical anchor-relative integer representation used by the characterization. +Issue #491 originally treated the production `n=4..=16` exact bias-standard-error proof as a sample-count staircase. The accumulated evidence shows that three different questions must remain separate: whether represented data admit an exact proof, whether the arithmetic representation is wide enough to carry that proof, and whether the resource cost is acceptable for production. None is resolved by incrementing `n` alone. -Let `c_i` be canonical nonnegative integer coefficients after subtracting the minimum represented value and removing the greatest common power-of-two unit. At least one `c_i` is zero. Define +For an earlier characterization that subtracts the represented minimum and produces nonnegative integer coefficients `c_i` on a common exact dyadic unit, define -`P = sum_{i Wide256 O(n) -> buffered pair fail-closed fallback` and records `used_wide_product` separately from `used_pairwise_fallback`. A correct numerator alone does not establish which resource path ran. ## Represented-input reachability -The odd `D = 2^58 + 1` integer boundary is useful for arithmetic width, but by itself it does not establish that the wider route is needed by a residual set that passes the represented binary64 subtraction gates. A separate characterization supplies such a case without weakening those gates. +Characterization `5a19b6334487b43fb630abba7e487d7cf4c49960` makes Wide256 recovery reachable from represented binary64 inputs rather than synthetic integer-only coefficients. At `n=4096`, represented residual classes `{0,1,2^53}` have exact residual construction and exact distinct pair differences. The common dyadic shift is zero, narrow products overflow, and Wide256 recovers -At `n = 4096`, take represented residuals with three distinct values: one `0`, one `1`, and 4094 copies of `2^53`. With truth fixed at represented zero, every residual construction is exact. The only distinct pairwise subtractions are `1`, `2^53`, and `2^53 - 1`; all three are exactly representable in binary64, so the production pair-subtraction roundoff predicate accepts the fixture's distinct subtraction classes. Because coefficient `1` is present, the canonical common power-of-two shift is zero rather than an artifact that removes the width pressure. +`P = 664289479338799435974172876300357631`. -The canonical sums `S1` and `S2` still fit `u128`, but both narrow cancellation products `n*S2` and `S1^2` exceed `u128`. The exact pair numerator remains only 119 bits: +The unreduced denominator is `68_702_699_520`. This establishes reachability, not production admission. -`P = 664289479338799435974172876300357631`. +## Exact-rounding width is a separate boundary -The two-limb product/subtraction recovers that value exactly. The unreduced scientific denominator is `4096^2 * 4095 = 68702699520`, which is also below the current `2^53` exact-denominator gate. This closes the narrower question of whether Wide256 recovery is reachable from represented residuals; it does not authorize changing the production sample-count admission. +At represented `n=2050`, characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d` reaches a second boundary. The exact pair numerator is -## Exact-rounding width is a second boundary +`P = 332306998946228931332463617650984961`, -Widening only the O(n) numerator identity is not sufficient for end-to-end exact admission. Before repair `e4a85f53a611922be7492fe906d62ce65787c18e`, the exact candidate/midpoint proof in `bias_se.rs` formed scaled `u128` products when comparing the exact rational target against a binary64 candidate square and the adjacent midpoint square. +with denominator `8_610_922_500`. The normal binary64 ratio/square-root seed is `0x4296998e1aff78de`; exact candidate-square comparison needs 136-bit operands and the adjacent upward midpoint comparison needs 140-bit operands. -A smaller represented fixture with the same three residual classes at `n = 2050` reaches that boundary. Its residual and pairwise-subtraction classes remain exact and its common dyadic shift is zero. Both narrow O(n) products require 129 bits, while the exact pair numerator is still only 118 bits: +Characterization `aab9fe9115cee97225f2aa81e54a55ceafb23336` shows that arbitrary precision is unnecessary for these comparisons: represent each nonzero operand as a `Wide256` mantissa plus signed dyadic exponent, compare absolute top-bit positions, then aligned significand bits only if the top positions tie. It also covers extreme exponent metadata `-2148/-2149` and `2046/2047` without materializing `2^k`. -`P = 332306998946228931332463617650984961`. +RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` → repair `e4a85f53a611922be7492fe906d62ce65787c18e` moves the signed-exponent two-limb comparison into `crates/validation_core/src/bias_se.rs`. `1240ace8eb41a01fa72a4bb99df842fd550a1288` fixes both exact-midpoint tie-to-even parity directions. The binary64 division/sqrt remains only a candidate seed; the returned value is authorized by the exact candidate/midpoint comparison. -The unreduced denominator is `8610922500`, below `2^53`. The normal binary64 ratio/square-root seed is `0x4296998e1aff78de`. Its compact dyadic significand/exponent are `3180642552495215 * 2^-9`. Exact candidate-square comparison therefore needs both `P * 2^18` and `denominator * significand^2`; each is 136 bits. Comparing with the exact midpoint to the upward neighbor requires 140-bit operands. Wide256 comparison shows the exact target is above the candidate square and below the midpoint square, proving that the original candidate is the nearest binary64 result. +The standards basis remains IEEE 754-2019 and ISO/IEC 60559:2020. IEEE P754 is an active revision project, not a published replacement. -This is the causal resource finding that motivated the production primitive repair: exact-rounding comparison width must cover the same represented cases as the wider numerator path. Pairwise proof remains the fail-closed comparison authority until the whole represented-input route is proven admission-equivalent. +## Pair versus Wide256 represented equivalence -Executable evidence for both represented boundaries is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`: reachability was introduced at `5a19b6334487b43fb630abba7e487d7cf4c49960`, and the exact-midpoint width characterization at `a8423173188fa53a26a16d3afdafeb76e114cc1d`. +Characterization `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` compares an actual two-pass O(n²) pairwise authority with an independent Wide256 O(n) identity for represented residual classes `{0,1,2^53}` at `n=4,16,17,65,257,2050`. Wherever both admit, they must produce the same common unit exponent, exact pair numerator, and GCD-reduced `(numerator, denominator, unit_exponent)` tuple presented to the exact rounder. At `n=2050`, both narrow products overflow while the two exact routes agree on the 118-bit `P` above. -## Exponent-safe exact comparison +That test originally used the represented minimum as anchor because its characterized input family made the minimum exact. It is not a universal production anchor rule. -The 136/140-bit finding does not require an arbitrary-precision integer for the comparison itself. Characterization `aab9fe9115cee97225f2aa81e54a55ceafb23336` adds a two-limb comparison reference that keeps each nonzero integer mantissa in `Wide256` and keeps its power-of-two scale as a signed exponent. It first compares the absolute top-bit positions. Only when those positions tie does it compare significand bits aligned from the common top bit. No `2^k` factor is materialized. +## Pairwise-f64 admission is broader than neither science nor exact-anchor admission -For represented `n = 2050`, that reference orders the 136-bit candidate-square operands as target greater than candidate square and the 140-bit upward-midpoint operands as target less than midpoint square, reproducing the exact nearest-binary64 decision from the earlier characterization. It also verifies equality and strict ordering across exponent pairs `-2148/-2149` and `2046/2047`, where a direct `u128` shift-factor construction is not a viable representation. The full-width edge `(2^128 - 1)^2` remains exactly represented as high limb `2^128 - 2`, low limb `1`. +Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` isolates a distinction inside the existing `n=4` budget. Residuals `[0,1,2^-54,2]` have exact minimum-anchor coordinates, but non-anchor subtraction `1-2^-54` rounds in binary64. The O(n²) pairwise-f64 proof therefore refuses. On common unit `2^-54`, integer coordinates `[0,2^54,1,2^55]` give -RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` moved this finding onto the authoritative path by requiring `correctly_rounded_scaled_sqrt_ratio` to return `0x4296998e1aff78de` for the represented `n=2050` reduced ratio; the previous bounded comparator returned `None` before it could make the exact decision. Repair `e4a85f53a611922be7492fe906d62ce65787c18e` integrates the two-limb product and signed-exponent ordering into `crates/validation_core/src/bias_se.rs`. Candidate and adjacent-midpoint denominator products are now formed as exact `Wide256` values and compared without materializing an oversized power-of-two factor. +`P = 3569704090242693886528325169446915` -This is a production primitive integration, not a sample-count admission change. `exact_pair_distance_standard_error` remains deliberately bounded to `n=4..=16`; the `n=2050` ratio is a private exact-rounding contract that prevents a future wider numerator route from inheriting the former false refusal. Exact-head Rust/rustdoc/line+branch/security/documentation GREEN and represented-input route equivalence remain required before admission can widen. +through both direct integer pair distances and `n*S2-S1^2`. The generic translated fallback happens to return the same correctly represented public result `0x3fdea33e2c83c140`. This showed that exact non-anchor pair subtraction is a sufficient reference-path condition, not a scientific prerequisite. -The standards basis remains the current published floating-point standards, IEEE 754-2019 and ISO/IEC 60559:2020. IEEE currently lists 754-2019 as an active standard and ISO lists ISO/IEC 60559:2020 as the published international standard; IEEE P754 is an active revision project and is not treated as a published replacement in this decision. +## Non-minimum exact anchor is a public correctness requirement -## Represented route-equivalence slice +A stronger represented fixture found after that characterization proves that selecting the represented minimum as a mandatory anchor is also too strict. Fix truth at represented zero and use residuals -Characterization `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` adds an actual two-pass O(n²) pairwise authority comparison against the two-limb O(n) identity for represented residual classes `{0, 1, 2^53}` at `n = 4, 16, 17, 65, 257, 2050`. It does not use the earlier analytic pair-numerator formula for the comparison: represented pair differences are checked for exact binary64 subtraction, normalized to a common dyadic unit, squared, and accumulated by the pairwise route. Independently, the wide-linear route selects the represented minimum as anchor, verifies every anchor-relative subtraction, derives the same dyadic unit, and computes `n*S2 - S1^2` with full-width products and subtraction. +`[0,1,2,-2^53]`. -For every characterized sample count, both routes must produce the same unit exponent, exact pair numerator, and GCD-reduced `(numerator, denominator, unit_exponent)` tuple presented to the exact rounder. The `n = 2050` fixture is the resource boundary rather than a small-only identity check: both narrow cancellation products must overflow, while the pairwise and Wide256 routes must agree on `P = 332306998946228931332463617650984961`, denominator `8610922500`, divisor `1`, and unit exponent `0`. The characterization also reverses the `n = 65` represented sequence to verify that selecting the minimum exact anchor makes the wide-linear result independent of observation order. +The represented minimum is `-2^53`. Mathematical difference `1-(-2^53)=2^53+1` is not representable in binary64, so neither the pairwise-f64 proof nor a minimum-anchor-only linear proof can preserve this geometry exactly. Anchor `0`, however, gives exact signed coordinates `[0,1,2,-2^53]` on unit `1`. -This closes a deterministic represented-input arithmetic-equivalence slice, not the whole production admission decision. The test still carries a test-only copy of the candidate arithmetic and does not route `bias_standard_error` samples above `n=16` through the wider implementation. Production integration therefore still requires one implementation of canonical represented normalization and Wide256 numerator routing in the owning module, same-head exact-rounding/tie-to-even evidence, and comparison against the pairwise authority before the fallback can be demoted or the cutoff widened. +Their translation-invariant exact pair numerator is -## Anchor-exact admission is broader than pairwise-f64 admission +`P = 243388915243820099130562543878155`, -Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` isolates a proof-admission distinction inside the existing `n=4` production sample budget. Use represented residuals `[0, 1, 2^-54, 2]` with truth fixed at represented zero. Every residual and every subtraction from the minimum anchor `0` is exact. The non-anchor subtraction `1 - 2^-54`, however, rounds in binary64; the current O(n²) pair proof therefore refuses before exact integer pair accumulation. +so `SE(mean)^2 = P/48`. Correct binary64 rounding is `0x4320000000000001`. The predecessor translated floating-moment fallback returns the adjacent lower `0x4320000000000000`; this is a public one-ULP defect rather than only a resource/admission observation. -That refusal is a limitation of the reference path, not evidence that the represented geometry lacks an exact pair-distance proof. With common unit `2^-54`, the anchor-relative integer coefficients are `[0, 2^54, 1, 2^55]`. Direct integer pair distances and the linear identity agree exactly on +Source-level RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` adds forward and reversed public contracts for `0x4320000000000001`. Its Actions runs were cancelled by the immediate successor push, so it is not claimed as hosted RED evidence. -`P = 3569704090242693886528325169446915`. +Production repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` keeps exact pairwise accumulation as the first authority. When pair subtraction cannot be proven exact, it searches every represented residual as a candidate translation anchor, requires every anchor-relative coordinate to be error-free, chooses the candidate with the smallest exact maximum translated magnitude and a represented-value tie-break, and converts the signed translated coordinates to a common dyadic grid. Positive and negative coefficient mass are accumulated separately; `n*Σc_i²` and `(Σc_i)²` are formed in `Wide256`, subtracted exactly, and downcast only if the final numerator fits the existing bounded `u128` rounder contract. The same exact candidate/midpoint/tie-to-even rounder then authorizes the result. -The public metric already returns the correctly represented standard error `0x3fdea33e2c83c140` through its generic translated fallback, so this fixture is not a public numerical defect. It demonstrates that requiring every non-anchor pair subtraction to be exact in binary64 is sufficient for the current O(n²) authority but is not a scientific prerequisite for canonical anchor-linear exact admission. A production O(n) route should therefore be evaluated for two properties separately: equality with the pair authority wherever both admit, and intentional strictly broader admission for anchor-exact geometries such as this one. The pairwise path remains fail-closed comparison evidence during that migration; the finding does not justify widening `n=4..=16`. +This anchor policy intentionally mirrors the permutation-invariant principle already used by `bias.rs`: observation arrival order is not scientific evidence, and the represented minimum is not privileged when it cannot translate the geometry exactly. Unsupported coordinate accumulation, Wide256 subtraction/downcast, denominator, or exact-rounding cases continue to fail closed to the established generic implementation. -## Consequence for #491 +The repair also promotes `[0,1,2^-54,2]` from generic fallback to exact anchor admission. It does **not** widen the production sample-count budget: `exact_pair_distance_standard_error` remains bounded to `n=4..=16`. -The candidate numerator route remains `narrow O(n) -> Wide256 O(n) -> buffered pair fail-closed fallback`, introduced by RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` and repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5`. The predecessor narrow-to-pair hybrid remains a comparison baseline. +## Resource budget remains unresolved -For odd `D = 2^58 + 1`, `n = 65`, the predecessor hybrid must still select the pair fallback because its narrow products overflow. The newer candidate must select the wider-product route, recover the same exact restored numerator as both O(n²) references, and report `used_wide_product=true` with `used_pairwise_fallback=false`. The power-of-two-normalized `D=2^58,n=65` geometry and odd `n=64` geometry remain narrow-path admissions. Separate route observability matters because a correct exact numerator does not by itself show whether the candidate avoided O(n²) allocation. +Exact pair records are 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2048`, and 4,997,541 at `n=3162`. For the older aligned nonnegative diameter characterization `D=2^53`, narrow checked products fit through `n=2047`, the exact pair-numerator extremum fits through `n=4095`, and unreduced `n²(n-1)` stays at or below `2^53` through `n=208064`. Because production reduces the denominator by GCD and represented geometry varies, none is a universal refusal count or production budget. -The product-width theorem makes a post-Wide256 pair fallback look redundant for the numerator identity within the canonical `u128` coefficient domain. The represented `n=2050` midpoint finding showed why this did not extend automatically to the whole exact-rounding proof. The exponent-safe comparison is now integrated into the authoritative rounding primitive, `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` demonstrates equality of the pairwise and wide-linear exact-ratio inputs across a deterministic represented-input slice, and `2bc1d2284d75154e020640adb573c1cfadf005fb` demonstrates an intended anchor-linear-only admission slice. The remaining equivalence work moves outward to production canonicalization/routing and fail-closed decisions across the represented domain, not to another sample-count staircase. +The production anchor repair changes the question from “can O(n) replace O(n²)?” to “what exact represented geometries can be admitted deterministically and at what measured cost?” Same-domain pair equivalence, intended anchor-only admission, exact rounder behavior, and fail-closed refusal all need to survive on one current head before any pair fallback can be demoted or the sample cutoff can move. -The executable accumulator characterization is `crates/validation_core/tests/bias_standard_error_wide_linear_admission_bound_characterization.rs`. The represented-input width characterization is `crates/validation_core/tests/bias_standard_error_represented_wide_recovery_characterization.rs`. The represented route-equivalence characterization is `crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs`. The anchor-linear admission characterization is `crates/validation_core/tests/bias_standard_error_anchor_linear_admission_characterization.rs`. The exponent-safe comparison characterization is `crates/validation_core/tests/bias_standard_error_wide_scaled_comparison_characterization.rs`. The timing/layout vehicle is `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records `used_wide_product` and `used_pairwise_fallback` independently so route selection can be audited from raw CSV. +The timing/layout vehicle remains `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records wide-product and pair-fallback selection separately. No Rust 1.98.0 `--release` CPU/raw CSV, allocator/RSS, or applicable buyer-path p95 evidence is claimed yet. ## Decision -Do not widen production `validation_core::bias_standard_error` solely because the exact-rounding primitive now supports wider comparison products, because the deterministic represented route-equivalence slice passes, or because anchor-linear proof admission is strictly broader than pairwise-f64 subtraction admission. Production admission remains `n=4..=16`. A budget change requires production canonical anchor-relative normalization and Wide256 routing, equality against the pairwise authority wherever both admit, explicit tests for intended anchor-linear-only admissions, exact tie-to-even preservation, recorded Rust 1.98.0 `--release` raw CSV, CPU/OS/build metadata, allocator/RSS evidence, applicable full buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch coverage/security/documentation GREEN, and qualifying independent current-head review. +Keep production `validation_core::bias_standard_error` admission at `n=4..=16`. Accept the deterministic exact-anchor/Wide256 repair within that existing budget because the non-minimum-anchor fixture is a public one-ULP correctness defect, not a speculative performance optimization. Do not infer from that repair that a larger sample budget is safe. + +Before any budget change beyond 16, require: exact-head Rust 1.98.0 fmt/clippy/nextest/rustdoc and owned production 100% line/branch coverage; same-head security/documentation GREEN and qualifying independent review; pair/anchor equality wherever both admit; explicit intended anchor-only admissions including `[0,1,2^-54,2]` and `[0,1,2,-2^53]`; exact candidate stepping/midpoint/tie-to-even and permutation invariance; fail-closed overflow/range behavior; recorded release-mode raw CPU/allocator/RSS and applicable buyer-path p95 evidence; and current CHANGELOG/TRACEABILITY/TEST_STRATEGY/OPERABILITY/operator baseline. ## Traceability @@ -108,10 +102,14 @@ Do not widen production `validation_core::bias_standard_error` solely because th - Exponent-safe scaled comparison characterization: `aab9fe9115cee97225f2aa81e54a55ceafb23336` - Production scaled-comparison RED: `f7717361ad8c5f0592688c1514c104cc1b4adabe` - Production scaled-comparison repair: `e4a85f53a611922be7492fe906d62ce65787c18e` -- Represented pair/wide exact-ratio equivalence: `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` -- Anchor-linear-only represented admission: `2bc1d2284d75154e020640adb573c1cfadf005fb` +- Exact midpoint tie-to-even edge contract: `1240ace8eb41a01fa72a4bb99df842fd550a1288` +- Represented pair/Wide256 exact-ratio equivalence: `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` +- Anchor-linear-only represented admission characterization: `2bc1d2284d75154e020640adb573c1cfadf005fb` +- Non-minimum-anchor public RED: `fd9f9ff2c5c395e4cc13042232f4deef018adb48` +- Deterministic exact-anchor production repair: `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` - Narrow-wide-pair RED: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` - Narrow-wide-pair repair: `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` - CHANGELOG fragment: `CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md` -- Production module under decision: `crates/validation_core/src/bias_se.rs` +- Production module: `crates/validation_core/src/bias_se.rs` +- Public regression: `crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs` - Exact-proof budget harness: `crates/validation_core/examples/bias_se_exact_proof_budget.rs` From bc0ed672bd2721fdf49bcb602fd91293b4ffbe7d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 17:09:46 +0900 Subject: [PATCH 544/576] docs(test): cover non-minimum exact-anchor bias-SE regression --- docs/TEST_STRATEGY.md | 43 ++++++++++++++++++++++--------------------- 1 file changed, 22 insertions(+), 21 deletions(-) diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index 7cd3a593b..d8e7a1776 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -68,26 +68,27 @@ Simulation thresholds account for Monte Carlo standard error and interval uncert Validation Evidence numerical proofs that add asymptotic work or material allocation require a measured resource contract before a production admission boundary is widened. For the bias-standard-error exact pair-distance path tracked by issue #491: - retain realistic represented-input counterexamples and permutation/sign-mirror contracts for scientific correctness; -- keep the existing exact residual/pairwise-subtraction, checked-integer, GCD-reduction, and exact midpoint authority fail-closed; -- compare the buffered O(n²) pair-record path, an allocation-free two-pass O(n²) reference, an algebraically equivalent narrow O(n) exact accumulator under a proved sufficient admission condition, a dependency-free two-limb wider-product O(n) characterization reference, the predecessor narrow-O(n)-fast-path/buffered-pair-fallback hybrid, and the implemented `narrow O(n) -> Wide256 O(n) -> pairwise fail-closed fallback` candidate; -- normalize the largest common power-of-two dyadic unit from exact anchor-relative coefficients before checked O(n) intermediates are judged. Raw-scale overflow is not a scientific or resource refusal when exact dyadic rescaling removes it; -- require an admitted/refused-set contract for any O(n) candidate. The predecessor `D=2^58, n=65` refusal was invalid because canonical normalization reduces the coefficients to zero/one and preserves the restored pair numerator `2^122`. The corrected narrow-path strict-subset boundary uses odd `D=2^58+1`: both pair and normalized narrow linear kernels fit at `n=64`, while at `n=65` the exact pair numerator still fits `u128` but the normalized narrow O(n) products require 129 bits before cancellation; -- retain the wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`: on odd `D=2^58+1, n=65`, exact two-limb products and subtraction recover the same 123-bit pair numerator after the two 129-bit intermediates cancel. This proves that narrow-intermediate refusal is not scientific refusal; -- retain accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb`: after canonical minimum anchoring, all coefficients are nonnegative integers and at least one is zero, so `Σc_i <= Σc_i² <= Σ(i>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence separately; field-width estimates alone are not allocation evidence; -- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing. RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` required the missing narrow→Wide256→pair route and repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` implements it. On odd `D=2^58+1, n=65`, the predecessor hybrid remains a pair-allocation comparison baseline while the new candidate must recover through `Wide256` with `used_wide_product=true` and `used_pairwise_fallback=false`; the harness records both route flags explicitly; -- keep RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3`, normalization repair `d423b57797b6f7f127e61e0679f9ee9841525c77`, restoration hardening `96f17c02edba0792f61e0e92167703a6ae4e40d0`, wider-product characterization `081000289f5a52e94863026d55696ee2a4daf923`, wider-reference harness `0bd805d4b0304cf1f76344ae14b7f079b3dade17`, accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb`, product-width RED/repair `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993`/`e9a7dee29afb97542bfe2965f850c8ab5a34368e`, represented-input reachability `5a19b6334487b43fb630abba7e487d7cf4c49960`, exact-rounding-width characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d`, narrow-wide-pair RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d`, and narrow-wide-pair repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` in the exact evidence lineage; -- run the characterization harness in release mode on a recorded CPU/OS/Rust 1.98.0 toolchain and retain raw CSV plus p95; unexecuted harness code is not performance evidence; -- if a service/API buyer path is affected, measure the full applicable path and retain the `p95 <= 20 ms` target without shrinking samples, omitting proof work, or relying on unrealistic warm-cache setup; -- arithmetic representability alone does not authorize a production sample-count budget. - -Until those measurements and exact-head gates exist, the `n<=16` production bias-SE exact pair-distance admission remains unchanged even when a larger represented-input counterexample is known. +- keep the existing exact represented-residual gate, GCD reduction, exact candidate/midpoint authorization, and fail-closed fallback; +- treat exact pairwise-f64 subtraction as a first reference authority, not a scientific prerequisite. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` proves `[0,1,2^-54,2]` has exact anchor coordinates even though a non-anchor pair subtraction rounds; +- do not require the represented minimum to be the exact anchor. Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` uses `[0,1,2,-2^53]`: minimum anchor `-2^53` cannot exactly translate `1`, while anchor `0` preserves all coordinates. The exact pair numerator is `243388915243820099130562543878155`, denominator `48`, and the correctly rounded public value is `0x4320000000000001`; the predecessor translated floating-moment fallback returns adjacent lower `0x4320000000000000`; +- retain repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f`: production exact admission searches every represented residual as a candidate anchor, requires exact anchor-relative coordinates, chooses the smallest exact maximum translated magnitude with represented-value tie-break, accumulates signed dyadic coordinates, forms `n*Σc_i²-(Σc_i)²` with `Wide256`, downcasts only a bounded final numerator, and reuses exact candidate/midpoint tie-to-even rounding. Test forward and reversed observation order bit-for-bit; +- preserve pair-versus-anchor equality wherever both admit and explicitly test intended anchor-only admissions. A broader exact-anchor set must not narrow existing pair admissions or depend on row arrival order; +- compare buffered O(n²), allocation-free two-pass O(n²), normalized narrow O(n), dependency-free Wide256 O(n), predecessor narrow→pair hybrid, and narrow→Wide256→pair candidate resource shapes before changing the sample budget; +- normalize the common power-of-two dyadic unit before narrow checked O(n) intermediates are judged. Raw `D=2^58,n=65` refusal is invalid after normalization; odd `D=2^58+1,n=65` remains the narrow 129-bit product witness; +- retain the earlier nonnegative minimum-anchor accumulator theorem `b7e4da353ac58069afd73ee7c0e8427d49993fdb`, `Σc_i <= Σc_i² <= P`, only within that characterized representation. Production exact-anchor coordinates may be signed, so positive and negative coefficient mass and the resulting signed sum must be exercised separately; +- retain RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` → repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e`: canonical `u128` cancellation operands require no more than 256-bit products; +- retain represented reachability `5a19b6334487b43fb630abba7e487d7cf4c49960`: at `n=4096`, `{0,1,2^53}` reaches narrow-product overflow while Wide256 recovers exact 119-bit `P=664_289_479_338_799_435_974_172_876_300_357_631`; +- retain exact-rounding-width characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d`: at represented `n=2050`, `P=332_306_998_946_228_931_332_463_617_650_984_961`, denominator `8_610_922_500`, candidate-square comparison requires 136 bits and the upward midpoint 140 bits; +- retain exponent-safe comparison characterization `aab9fe9115cee97225f2aa81e54a55ceafb23336`, production comparison RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` → repair `e4a85f53a611922be7492fe906d62ce65787c18e`, and tie-to-even edge contract `1240ace8eb41a01fa72a4bb99df842fd550a1288`; +- retain represented pair/Wide256 exact-ratio equivalence `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` at `n=4,16,17,65,257,2050`, while recognizing that its minimum-anchor rule is scoped to that input family rather than universal production canonicalization; +- keep the normalized O(n) distribution-independent intermediate envelope distinct from the exact pair-numerator and denominator envelopes. At aligned diameter `2^53`, `n<=2_047`, `n<=4_095`, and `n<=208_064` are arithmetic evidence points, not production budgets; +- record exact pair counts, target `size_of::>()`, scratch `Vec` capacity/payload, and allocator/RSS evidence separately; field-width estimates are not allocation evidence; +- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing and record `used_wide_product` and `used_pairwise_fallback` independently; +- run the characterization harness in release mode on recorded CPU/OS/Rust 1.98.0 and retain raw CSV plus p95. Unexecuted harness code is not performance evidence; +- if a service/API buyer path is affected, measure the complete applicable path against `p95 <= 20 ms` without shrinking samples, omitting proof work, or using unrealistic warm-cache-only setup; +- arithmetic representability or one successful anchor does not authorize a production sample-count budget. + +Until same-head correctness, exact-head gates, independent review, and resource measurements exist, the production bias-SE exact admission remains `n=4..=16`. ## Release acceptance @@ -95,4 +96,4 @@ A release requires one integrated protected head with all relevant scientific, n ## References -The full APA 7th register is [`docs/research/standards-and-literature.md`](research/standards-and-literature.md). Method names used above cite Allen (1983) for interval algebra and Asparouhov & Muthén (2009), Asparouhov et al. (2018), and Marsh et al. (2014) for ESEM/DSEM. +The full APA 7th register is [`docs/research/standards-and-literature.md`](research/standards-and-literature.md). Method names used above cite Allen (1983) for interval algebra and Asparouhov & Muthén (2009), Asparouhov et al. (2018), and Marsh et al. (2014) for ESEM/DSEM. \ No newline at end of file From fbd7c548a265d0e7c7abf9f8720a255bcdfe83d4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 17:10:28 +0900 Subject: [PATCH 545/576] docs(ops): operate deterministic exact-anchor bias-SE proof --- docs/OPERABILITY.md | 46 +++++++++++++++++---------------------------- 1 file changed, 17 insertions(+), 29 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index 0bb9b7ec4..1c5f64e3b 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -60,43 +60,31 @@ Before PostgreSQL becomes production state, prove migrations and rollback, tenan A numerical proof boundary is an operational resource contract when it changes asymptotic work, allocation, or buyer-path latency. It is not determined by the next sample count that happens to expose a rounding defect. -Issue #491 owns the current bias-standard-error exact-proof budget. Production exact pair-distance admission stays bounded to `n<=16` while the current implementation is O(n²) in pair enumeration and stores `n(n-1)/2` pair records. The characterization on PR #488 records a seventeen-observation counterexample and algebraically equivalent O(n) checked-integer numerators, but arithmetic representability alone does not authorize a wider production budget. +Issue #491 owns the current bias-standard-error exact-proof budget. Production exact admission remains `n<=16`; larger counts are characterization evidence only. The current work separates represented-input exactness, arithmetic width, exact-rounding width, and measured resource cost rather than treating one integer cutoff as all four. -The current characterization distinguishes three bounds that must not be collapsed into one cutoff. For a canonical aligned coefficient diameter `D=2^53` **after removing the largest common power-of-two dyadic unit**, the normalized narrow O(n) distribution-independent intermediate envelope `n^2D^2` fits `u128` through `n=2_047`, while the exact aligned pair-square numerator extremal bound `floor(n^2/4)D^2` fits through `n=4_095`. Separately, the unreduced scientific denominator `n^2(n-1)` exceeds `2^53` after `n=208_064`; production uses the reduced denominator after GCD, so that threshold is only an envelope marker. None of these values is a latency or memory budget. +The old nonnegative minimum-anchor characterization established `Σc_i <= Σc_i² <= P` and showed why raw-scale `D=2^58,n=65` refusal disappears after common dyadic-unit normalization. Odd `D=2^58+1,n=65` remains a real narrow-width witness: pair numerator fits in 123 bits while cancellation products require 129 bits. `Wide256` characterization `081000289f5a52e94863026d55696ee2a4daf923` and product-width RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` → repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e` show the characterized cancellation products need no more than two `u128` limbs. -The shared dyadic unit is a proof obligation. The predecessor characterization judged the O(n) candidate on raw values with one zero and the rest at `D=2^58`, and therefore reported a refusal at `n=65`. That refusal was representation-dependent: canonical normalization divides every nonzero coefficient by the exact common unit `2^58`, leaving one zero and sixty-four ones. The normalized intermediates are only `4_160` and `4_096`; their difference `64` restores to the exact pair numerator `2^122`. RED `4f1bd2c343cf2d54905a07c257a570a89dc575d3` fixes this requirement, and characterization repair `d423b57797b6f7f127e61e0679f9ee9841525c77` evaluates checked O(n) admission on the normalized dyadic grid. +Represented-input reachability `5a19b6334487b43fb630abba7e487d7cf4c49960` reaches the wider numerator route at `n=4096` on residual classes `{0,1,2^53}`. Exact-rounding characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d` then shows that represented `n=2050` needs 136-bit candidate-square and 140-bit adjacent-midpoint comparison operands even though its exact pair numerator is only 118 bits. `aab9fe9115cee97225f2aa81e54a55ceafb23336` shows those comparisons can remain bounded as `Wide256` mantissa plus signed dyadic exponent. RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` → repair `e4a85f53a611922be7492fe906d62ce65787c18e` integrates that comparison into the production exact rounder, and `1240ace8eb41a01fa72a4bb99df842fd550a1288` fixes both tie-to-even parity directions. -The corrected narrow O(n) accumulator is still a strict sufficient subset of the current pair reference under `u128`; the valid boundary uses odd diameter `D=2^58+1`, whose common dyadic unit is one. Both kernels fit at `n=64`. At `n=65`, the exact pair numerator `64D^2` is a 123-bit `u128`, while both O(n) products require 129 bits before cancellation. A normalized narrow O(n) refusal therefore still cannot become a scientific refusal. +`7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` proves represented pair/Wide256 exact-ratio equivalence for one family where the represented minimum is an exact anchor. That is not a universal anchor policy. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` demonstrates `[0,1,2^-54,2]`, where non-anchor pair subtraction rounds but anchor `0` remains exact. -Wider-intermediate characterization `081000289f5a52e94863026d55696ee2a4daf923` makes that distinction executable without introducing a production dependency. A test-only two-limb `Wide256` performs exact `u128 × u128` products and checked cancellation. On odd `D=2^58+1,n=65`, it recovers the same exact 123-bit pair numerator after the two 129-bit products cancel, while the narrow checked-`u128` O(n) path refuses. The characterization also fixes `(2^128-1)^2` as high limb `2^128-2`, low limb `1`. +The current production repair closes a stronger correctness defect. Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` uses represented residuals `[0,1,2,-2^53]`. The minimum residual cannot exactly translate `1` because `2^53+1` is not representable; anchor `0` preserves every coordinate. Signed unit-one coordinates give exact pair numerator `243388915243820099130562543878155`, denominator `48`, and correctly rounded result `0x4320000000000001`. The predecessor translated floating-moment fallback produces adjacent lower `0x4320000000000000`. -Accumulator-bound characterization `b7e4da353ac58069afd73ee7c0e8427d49993fdb` removes a further false resource boundary. After canonical minimum anchoring, every coefficient `c_i` is a nonnegative integer and at least one coefficient is zero. Therefore `Σc_i <= Σc_i²`; the zero-anchor pair terms contain every `c_i²`, while all other pair-square terms are nonnegative, so `Σc_i² <= Σ(i Wide256 O(n) -> pairwise fail-closed fallback` kernel before it existed. Repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` implements that candidate in `crates/validation_core/examples/bias_se_exact_proof_budget.rs` without changing production arithmetic. The predecessor narrow→buffered-pair hybrid remains in the same harness so the cost of unnecessary pair allocation is measurable rather than inferred. +- do not diagnose pairwise-f64 or minimum-anchor refusal as scientific invalidity when another deterministic represented anchor is exact; +- do not make row order part of anchor selection. Forward/reversed fixtures must be bit-identical; +- treat failure of signed coordinate accumulation, Wide256 subtraction/downcast, denominator reduction, or exact rounding as a fail-closed proof refusal and use the established generic fallback rather than weakening arithmetic checks; +- keep pairwise exact proof as the first authority while production exact-anchor coverage matures; +- separate route observability from numerical equality. The resource harness records `used_wide_product` and `used_pairwise_fallback` independently; +- treat `n=2_047`, `4_095`, and `208_064` only as arithmetic envelope markers from older aligned characterizations, not service limits; +- before widening beyond 16, retain raw Rust 1.98.0 `--release` timing CSV, CPU/OS/build flags, p95, actual scratch capacity/payload, allocator/RSS, and any applicable buyer-path `p95<=20 ms` evidence without sample shrinkage or omitted proof work. -Represented-input characterization `5a19b6334487b43fb630abba7e487d7cf4c49960` proves that wider numerator recovery is reachable through the same binary64 subtraction gates rather than only on synthetic integer coefficients. At `n=4096`, residual classes `{0,1,2^53}` with represented zero truth make residual construction exact and limit distinct pairwise subtraction magnitudes to exact values `{1,2^53,2^53-1}`. Coefficient `1` forces the canonical common dyadic shift to zero. Both narrow products overflow, while the exact pair numerator remains the 119-bit value `664_289_479_338_799_435_974_172_876_300_357_631`; `Wide256` recovers it exactly. The unreduced denominator `68_702_699_520` is below `2^53`. +Exact pair-record counts remain 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2048`, and 4,997,541 at `n=3162`. A two-pass O(n²) implementation may remove pair-record storage but still requires exact-head scientific and resource evidence. A Wide256 O(n) implementation may remove both pair storage and pair enumeration for admitted geometries but still requires pair-equivalence, anchor-only admission, exact-rounding, refusal, and resource evidence on one surviving production head. -Exact-rounding-width characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d` exposes the next causal boundary at represented `n=2050`. The exact pair numerator `332_306_998_946_228_931_332_463_617_650_984_961` is only 118 bits and is recovered by `Wide256`; denominator `8_610_922_500` remains below `2^53`. The binary64 ratio/square-root seed is `0x4296998e1aff78de`. The existing exact candidate-square comparison would need 136-bit scaled operands, and the upward-adjacent midpoint comparison needs 140-bit operands. Test-only `Wide256` comparisons show the exact target lies above the candidate square but below the midpoint square, proving that seed is nearest. A production route that widens only the O(n) numerator would therefore still falsely refuse this represented exact-proof case at the midpoint-comparison layer. - -Operationally, the wider resource path is not one arithmetic substitution. Before production admission changes, the exact candidate-square and adjacent-midpoint comparisons must become width-safe or receive a separate proved bound while preserving exact tie-to-even behavior. The pairwise path remains a fail-closed comparison authority until represented residual conversion, canonical normalization, full-width cancellation, dyadic restoration, rational reduction, candidate/midpoint proof, current-head verification, and measured resource behavior are demonstrated together. - -The corrected harness compares six numerator-resource shapes: buffered O(n²), allocation-free two-pass O(n²), normalized narrow checked O(n), the two-limb wider-product O(n) reference, the predecessor narrow→pair hybrid, and the new narrow→Wide256→pair hybrid. Before timing it requires exact restored-numerator equality. It records `used_wide_product` and `used_pairwise_fallback` independently. `D=2^58,n=65` must normalize and remain on the narrow route; odd `D=2^58+1,n=64` must remain narrow; odd `D=2^58+1,n=65` makes the predecessor hybrid use pair fallback while the new candidate must recover through `Wide256` with no pair allocation. This is route characterization, not proof that the new hybrid is production-ready. - -Before changing the production boundary, retain: - -- release-mode raw timing samples and p95 from `crates/validation_core/examples/bias_se_exact_proof_budget.rs`, with exact commit, CPU, OS, Rust 1.98.0 toolchain, build flags, timing sample count, and cold/warm procedure; -- side-by-side buffered O(n²), allocation-free two-pass O(n²), normalized narrow O(n), two-limb wider-product O(n), predecessor narrow→pair hybrid, and narrow→Wide256→pair candidate evidence, with exact equality of restored pair-square numerators before timing; -- route-aware timing covering common-power normalized admission and odd-diameter admitted/refused boundaries, including normalized unit exponent, `used_wide_product`, and `used_pairwise_fallback`; -- target `size_of::>()`, actual scratch `Vec` capacity, scratch payload bytes, and allocator/RSS evidence rather than byte estimates inferred from field widths; -- admitted/refused-set comparison at the actual represented-input boundary across sample count, canonical aligned coefficient diameter/exponent spread, and coefficient distribution; verify the accumulator and product-width theorems against conversion into canonical coefficients and classify any remaining refusal by scale restoration, denominator handling, exact candidate/midpoint comparison, or upstream exact-residual admission; -- width-safe exact candidate-square and both adjacent-midpoint comparisons over represented cases including ordinary nearest-neighbor selection and exact midpoint/tie-to-even cases; -- full service/API p95 when a buyer-facing path is affected, preserving the TEPP `p95 <= 20 ms` target without shrinking input, omitting proof work, or using an unrealistic cache-only setup. - -The exact pair-record count is `n(n-1)/2`; the current characterization locks 120 records at `n=16`, 136 at `n=17`, 2,096,128 at `n=2,048`, and 4,997,541 at `n=3,162`. A two-pass O(n²) implementation may remove pair-record storage while preserving pair-enumeration proof shape, but that optimization still requires exact-head Rust/rustdoc/coverage evidence before it replaces the current implementation. A stronger O(n) admission is not accepted merely because its checked arithmetic fits a wider intermediate, and a narrow checked O(n) refusal is not allowed to narrow the current exact pairwise admission set. - -No release-mode timing numbers are currently authoritative. The current execution environment does not provide the required Rust 1.98.0 toolchain, and hosted exact-head jobs have not produced a benchmark artifact. Unexecuted timing harnesses and branch-only resource characterization remain supporting evidence only. +No release-mode resource numbers are authoritative yet. Current #488 exact-head workflows after the source repair and subsequent doctoring remain pending/queued until a surviving head completes. The source-level RED workflows were cancelled by the immediate repair push and are not counted as hosted RED. Predecessor Rustfmt artifact `9982621569` from `1f765a...` is not current-head formatting evidence. ## Model release/cutover @@ -112,4 +100,4 @@ GitHub Actions registry identities survive YAML deletion. After any bootstrap, d ## Release gate -A software release requires exact protected-head CI/security/review, 100% production coverage/docs, validated migrations/rollback where present, scientific benchmark artifacts, SBOM/provenance, reproducible packages/images, operator runbooks, accessibility for product UI, CHANGELOG/version/tag consistency, and post-publish verification. TEPP has not reached that integrated release state merely because individual foundation PRs merge. Unexecuted timing harnesses and branch-only resource characterization are not release evidence. +A software release requires exact protected-head CI/security/review, 100% production coverage/docs, validated migrations/rollback where present, scientific benchmark artifacts, SBOM/provenance, reproducible packages/images, operator runbooks, accessibility for product UI, CHANGELOG/version/tag consistency, and post-publish verification. TEPP has not reached that integrated release state merely because individual foundation PRs merge. Unexecuted timing harnesses and branch-only resource characterization are not release evidence. \ No newline at end of file From 9f403194a2ec1636531c2dfe9229cfb34b73d747 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 17:13:35 +0900 Subject: [PATCH 546/576] test(validation): expose missing zero-anchor exact route --- ...nminimum_anchor_exact_rounding_contract.rs | 32 ++++++++++++++----- 1 file changed, 24 insertions(+), 8 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs index 410817486..8725238f0 100644 --- a/crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs @@ -1,12 +1,12 @@ -//! Regression contract for exact bias-SE recovery when the minimum residual is not an exact anchor. +//! Regression contracts for exact bias-SE recovery beyond pairwise-f64 admission. //! -//! Issue #491 previously characterized a minimum-anchor linear proof. This fixture -//! shows why production admission must instead search deterministic exact anchors: -//! subtracting the minimum residual `-2^53` from `1` rounds, while anchor `0` -//! preserves every translated coordinate exactly. The exact pair numerator is -//! `243388915243820099130562543878155`, so `SE(mean)^2 = P / 48` and the -//! correctly rounded binary64 result is one ULP above the translated floating -//! moment fallback. Observation order must not change that scientific result. +//! Issue #491 previously characterized a minimum-anchor linear proof. The first +//! fixture shows why production admission must search represented anchors rather +//! than force the minimum. The second shows that represented anchors alone are +//! still incomplete: no observed residual is an exact universal anchor, while +//! the neutral dyadic anchor `0` preserves every residual exactly. Both fixtures +//! have exact pair numerators that fit the bounded proof and both differ by one +//! ULP from the predecessor translated floating-moment fallback. use validation_core::bias_standard_error; @@ -25,3 +25,19 @@ fn exact_nonminimum_anchor_recovers_correctly_rounded_four_observation_bias_se() .expect("permutation must preserve the exact represented geometry"); assert_eq!(reverse.to_bits(), forward.to_bits()); } + +#[test] +fn exact_zero_anchor_recovers_when_no_observed_residual_is_a_universal_anchor() { + let tiny = 2.0_f64.powi(-54); + let truth = [0.0; 4]; + let recovered = [1.0, tiny, 2.0, 3.0]; + + let forward = bias_standard_error(&truth, &recovered) + .expect("neutral-anchor exact geometry is scientifically computable"); + assert_eq!(forward.to_bits(), 0x3fe4_a7e9_cb8a_3491); + + let permuted = [recovered[2], recovered[0], recovered[3], recovered[1]]; + let permuted_result = bias_standard_error(&truth, &permuted) + .expect("neutral-anchor proof must remain permutation invariant"); + assert_eq!(permuted_result.to_bits(), forward.to_bits()); +} From 6cf30eeb549c0df0377bda1111cf46396e8282a3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 17:18:34 +0900 Subject: [PATCH 547/576] fix(validation): include neutral exact anchor in bias SE proof --- crates/validation_core/src/bias_se.rs | 81 +++++++++++++++++++++------ 1 file changed, 65 insertions(+), 16 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index ac9cfca8a..105258701 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -111,7 +111,11 @@ impl Wide256 { } const fn to_u128(self) -> Option { - if self.high == 0 { Some(self.low) } else { None } + if self.high == 0 { + Some(self.low) + } else { + None + } } const fn is_zero(self) -> bool { @@ -329,8 +333,13 @@ fn exact_pairwise_pair_square_sum(residuals: &[f64]) -> Option<(u128, i32)> { } fn exact_anchor_linear_pair_square_sum(residuals: &[f64]) -> Option<(u128, i32)> { + // Zero is a neutral dyadic translation anchor for every finite represented + // residual, even when no observed residual can translate every other value + // without rounding. Keep represented residuals in the candidate set so an + // exact observed anchor with a smaller dynamic range still wins. The final + // total-order tie-break makes selection independent of observation order. let mut best: Option<(f64, f64, Vec)> = None; - for &anchor in residuals { + for anchor in core::iter::once(0.0).chain(residuals.iter().copied()) { let mut translated = Vec::with_capacity(residuals.len()); let mut max_magnitude = 0.0_f64; let mut exact = true; @@ -430,10 +439,10 @@ fn exact_pair_distance_standard_error( } // Preserve the pairwise-f64 proof as the first authority. If one represented - // non-anchor pair subtraction rounds, search every represented residual as an - // exact translation anchor, choose the smallest exact dynamic range with a - // represented-value tie-break, and recover the same translation-invariant - // pair numerator through n*Σc_i²-(Σc_i)² using two-limb cancellation products. + // non-anchor pair subtraction rounds, compare the neutral zero anchor with + // every represented residual anchor and choose the exact translation with the + // smallest dynamic range. Recover the same translation-invariant numerator + // through n*Σc_i²-(Σc_i)² using two-limb cancellation products. let (pair_square_sum, unit_exponent) = exact_pairwise_pair_square_sum(&residuals) .or_else(|| exact_anchor_linear_pair_square_sum(&residuals))?; if pair_square_sum == 0 { @@ -470,8 +479,10 @@ fn exact_pair_distance_standard_error( /// Four- through sixteen-observation samples whose represented residuals admit /// either the exact pairwise-difference proof or a deterministic exact anchor /// translation use the exact pair-distance identity when its reduced dyadic ratio -/// fits the bounded integer proof. All other samples retain the established bias -/// implementation and its existing fail-closed behavior. +/// fits the bounded integer proof. The anchor candidates include neutral zero and +/// every represented residual so proof admission does not depend on a minimum or +/// observed residual being universally subtractable. All other samples retain the +/// established bias implementation and its existing fail-closed behavior. pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result { if let Some(result) = exact_pair_distance_standard_error(truth, recovered) { return result; @@ -558,12 +569,30 @@ mod tests { assert!(!one.bit(128)); assert_eq!(compare_scaled_wide(zero, 0, zero, 0), Some(Ordering::Equal)); - assert_eq!(compare_scaled_wide(zero, -2_148, one, 2_047), Some(Ordering::Less)); - assert_eq!(compare_scaled_wide(one, 2_047, zero, -2_148), Some(Ordering::Greater)); - assert_eq!(compare_scaled_wide(one, -2_148, two, -2_149), Some(Ordering::Equal)); - assert_eq!(compare_scaled_wide(one, -2_148, three, -2_149), Some(Ordering::Less)); - assert_eq!(compare_scaled_wide(three, 2_046, one, 2_047), Some(Ordering::Greater)); - assert_eq!(compare_scaled_wide(one, 1, one, 0), Some(Ordering::Greater)); + assert_eq!( + compare_scaled_wide(zero, -2_148, one, 2_047), + Some(Ordering::Less) + ); + assert_eq!( + compare_scaled_wide(one, 2_047, zero, -2_148), + Some(Ordering::Greater) + ); + assert_eq!( + compare_scaled_wide(one, -2_148, two, -2_149), + Some(Ordering::Equal) + ); + assert_eq!( + compare_scaled_wide(one, -2_148, three, -2_149), + Some(Ordering::Less) + ); + assert_eq!( + compare_scaled_wide(three, 2_046, one, 2_047), + Some(Ordering::Greater) + ); + assert_eq!( + compare_scaled_wide(one, 1, one, 0), + Some(Ordering::Greater) + ); assert_eq!(compare_scaled_wide(one, 0, one, 1), Some(Ordering::Less)); } @@ -619,7 +648,7 @@ mod tests { } #[test] - fn anchor_linear_route_recovers_rounded_non_anchor_pair_and_nonminimum_anchor() { + fn anchor_linear_route_recovers_observed_and_neutral_anchor_geometries() { let tiny = 2.0_f64.powi(-54); let small = [0.0, 1.0, tiny, 2.0]; assert_eq!(exact_pairwise_pair_square_sum(&small), None); @@ -631,7 +660,20 @@ mod tests { let (numerator, unit_exponent) = exact_anchor_linear_pair_square_sum(&wide) .expect("zero is an exact non-minimum translation anchor"); assert_eq!(unit_exponent, 0); - assert_eq!(numerator, 243_388_915_243_820_099_130_562_543_878_155_u128); + assert_eq!( + numerator, + 243_388_915_243_820_099_130_562_543_878_155_u128 + ); + + let no_observed_anchor = [1.0, tiny, 2.0, 3.0]; + assert_eq!(exact_pairwise_pair_square_sum(&no_observed_anchor), None); + let (numerator, unit_exponent) = exact_anchor_linear_pair_square_sum(&no_observed_anchor) + .expect("neutral zero is exact when no observed residual is a universal anchor"); + assert_eq!(unit_exponent, -54); + assert_eq!( + numerator, + 6_490_371_073_168_534_319_490_338_297_741_315_u128 + ); } #[test] @@ -782,6 +824,13 @@ mod tests { .to_bits(), 0x3fde_a33e_2c83_c140 ); + assert_eq!( + exact_pair_distance_standard_error(&truth, &[1.0, tiny, 2.0, 3.0]) + .expect("neutral zero anchor admits the represented geometry") + .expect("represented result") + .to_bits(), + 0x3fe4_a7e9_cb8a_3491 + ); assert_eq!( exact_pair_distance_standard_error(&[1.0, 0.0, 0.0, 0.0], &[tiny, 0.0, 0.0, 0.0]), None From dc83dbf73987b6d5c526a6d546dc8abfac52cd93 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 17:19:01 +0900 Subject: [PATCH 548/576] docs(validation): trace neutral-anchor follow-up repair --- .../validation-bias-exact-proof-budget-characterization.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md index 9e97c82a1..69216b367 100644 --- a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md +++ b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md @@ -8,6 +8,7 @@ - Add a dependency-free two-limb `Wide256` O(n) reference and a measured candidate route `narrow O(n) -> Wide256 O(n) -> buffered pair fail-closed fallback`. RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` requires the missing route; repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` implements it and records both wide-product selection and pairwise fallback separately in CSV. - The corrected harness keeps the existing narrow-to-pair hybrid for comparison. On odd `D=2^58+1, n=65`, that predecessor hybrid still allocates the pair buffer, while the new narrow-to-wide-to-pair candidate must recover the same exact numerator through `Wide256` with `used_wide_product=true` and `used_pairwise_fallback=false`. Power-of-two-normalized `n=65` and odd `n=64` remain narrow-path admissions. - Represented-input equivalence distinguishes proof admission from public numerical correctness. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` uses residuals `[0,1,2^-54,2]`: every minimum-anchor subtraction is exact, but `1 - 2^-54` rounds in binary64, so the O(n²) pairwise-f64 proof refuses. On common unit `2^-54`, integer coordinates `[0,2^54,1,2^55]` give exact pair numerator `3569704090242693886528325169446915`; the generic fallback happens to return the correct public value. -- Add source-level RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` for a stronger represented geometry, residuals `[0,1,2,-2^53]`. The minimum residual cannot exactly translate `1`, but anchor `0` preserves every coordinate. Signed unit-one coordinates give `P=243388915243820099130562543878155`, so `SE(mean)^2=P/48`; the predecessor translated floating-moment path returns `0x4320000000000000` while exact rounding requires adjacent `0x4320000000000001`. The RED workflows were cancelled by the immediate successor push and are not hosted RED evidence. -- Repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` keeps pairwise exact accumulation first, then searches all represented residuals for an exact translation anchor, chooses the smallest exact translated dynamic range with a represented-value tie-break, builds signed dyadic coordinates, computes `n*Σc_i²-(Σc_i)²` with `Wide256` cancellation products, and reuses the exact candidate/midpoint tie-to-even rounder. This also promotes `[0,1,2^-54,2]` from generic fallback to exact anchor admission without widening the sample-count budget. +- Add source-level RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` for residuals `[0,1,2,-2^53]`. The minimum residual cannot exactly translate `1`, but represented anchor `0` preserves every coordinate. Signed unit-one coordinates give `P=243388915243820099130562543878155`, so `SE(mean)^2=P/48`; the predecessor translated floating-moment path returns `0x4320000000000000` while exact rounding requires adjacent `0x4320000000000001`. Its Actions runs were cancelled by the immediate repair push and are not hosted RED evidence. +- Initial production repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` keeps pairwise exact accumulation first and searches represented residual anchors. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` shows that observed anchors alone are incomplete: residuals `[1,2^-54,2,3]` have no observed universal exact anchor, while neutral dyadic anchor `0` preserves all coordinates. On unit `2^-54`, `P=6490371073168534319490338297741315`; exact rounding requires `0x3fe4a7e9cb8a3491`, while the predecessor translated floating-moment fallback returns adjacent `0x3fe4a7e9cb8a3492`. +- Repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` includes neutral zero plus every represented residual in the deterministic exact-anchor candidate set, keeps the exact candidate with the smallest maximum translated magnitude and represented-value tie-break, accumulates signed dyadic coordinates, computes `n*Σc_i²-(Σc_i)²` with `Wide256` cancellation products, and reuses the exact candidate/midpoint tie-to-even rounder. Forward and permuted public fixtures require bit-identical results. - Keep production `bias_standard_error` sample admission unchanged at `n=4..=16`. A budget change beyond 16 still requires pair/anchor same-domain equivalence plus intended anchor-only admissions, recorded Rust 1.98.0 release-mode CPU/raw CSV and allocator/RSS evidence, applicable buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch/security/documentation GREEN, current TRACEABILITY/research/operator doctoring, and qualifying independent current-head review. From a6cceb6e7dad8bfd835e813fa5e66e69f9b6c918 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 17:19:45 +0900 Subject: [PATCH 549/576] docs(research): extend exact-anchor proof to neutral zero --- ...ndard-error-wide-linear-admission-bound.md | 105 +++++------------- 1 file changed, 30 insertions(+), 75 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index 63c2ccc7d..23192d9f8 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -2,113 +2,68 @@ ## Problem -Issue #491 originally treated the production `n=4..=16` exact bias-standard-error proof as a sample-count staircase. The accumulated evidence shows that three different questions must remain separate: whether represented data admit an exact proof, whether the arithmetic representation is wide enough to carry that proof, and whether the resource cost is acceptable for production. None is resolved by incrementing `n` alone. +Issue #491 originally treated the production `n=4..=16` exact bias-standard-error proof as a sample-count staircase. The accumulated evidence separates three questions: whether represented data admit an exact proof, whether the arithmetic representation is wide enough to carry that proof, and whether the resource cost is acceptable for production. None is resolved by incrementing `n` alone. -For an earlier characterization that subtracts the represented minimum and produces nonnegative integer coefficients `c_i` on a common exact dyadic unit, define +For an earlier characterization that subtracts the represented minimum and produces nonnegative integer coefficients `c_i` on a common exact dyadic unit, define `P = sum_{i Wide256 O(n) -> pair` route telemetry. -## Full-width product bound +Represented-input reachability `5a19b6334487b43fb630abba7e487d7cf4c49960` reaches the wider numerator route at `n=4096` on `{0,1,2^53}` and recovers `P=664289479338799435974172876300357631`. At represented `n=2050`, characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d` finds `P=332306998946228931332463617650984961`, denominator `8_610_922_500`, but 136-bit candidate-square and 140-bit adjacent-midpoint comparison operands. `aab9fe9115cee97225f2aa81e54a55ceafb23336` shows those comparisons can remain bounded as `Wide256` mantissa plus signed dyadic exponent. RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` → repair `e4a85f53a611922be7492fe906d62ce65787c18e` integrates that comparator into production; `1240ace8eb41a01fa72a4bb99df842fd550a1288` fixes both exact tie-to-even parity directions. -The narrow O(n) identity can still refuse while `P` fits because `n*S2` and `S1^2` may exceed 128 bits before cancellation. Odd diameter `D=2^58+1,n=65` is the canonical arithmetic witness: both products require 129 bits while the exact pair numerator requires 123 bits. +IEEE 754-2019 and ISO/IEC 60559:2020 remain the published floating-point basis; IEEE P754 is an active revision project rather than a published replacement. -RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` → repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e` makes the capacity theorem executable. With `S1,S2,n` each bounded by `u128`, every cancellation product fits below `2^256`; dependency-free two-limb `Wide256` is therefore sufficient for the product width in the characterized domain. This theorem does not by itself prove end-to-end production admission. +## Represented proof equivalence and anchor admission -RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` → repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` adds a measured characterization route `narrow O(n) -> Wide256 O(n) -> buffered pair fail-closed fallback` and records `used_wide_product` separately from `used_pairwise_fallback`. A correct numerator alone does not establish which resource path ran. +Characterization `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` compares an actual O(n²) pair authority with an independent Wide256 O(n) identity for represented residual classes `{0,1,2^53}` at `n=4,16,17,65,257,2050`. Wherever both admit, they produce the same common unit, exact pair numerator, and reduced ratio. Its minimum-anchor rule is scoped to that input family, not a universal production policy. -## Represented-input reachability +Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` shows why: residuals `[0,1,2^-54,2]` have exact coordinates from anchor `0`, while non-anchor subtraction `1-2^-54` rounds. On common unit `2^-54`, coordinates `[0,2^54,1,2^55]` give `P=3569704090242693886528325169446915`. The generic fallback happens to return the same public result, so this is an admission distinction rather than a public defect. -Characterization `5a19b6334487b43fb630abba7e487d7cf4c49960` makes Wide256 recovery reachable from represented binary64 inputs rather than synthetic integer-only coefficients. At `n=4096`, represented residual classes `{0,1,2^53}` have exact residual construction and exact distinct pair differences. The common dyadic shift is zero, narrow products overflow, and Wide256 recovers +Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` then shows a public defect with `[0,1,2,-2^53]`. The represented minimum `-2^53` cannot exactly translate `1`, but represented anchor `0` preserves every coordinate. Signed unit-one coordinates give `P=243388915243820099130562543878155`, denominator `48`, and correctly rounded result `0x4320000000000001`; the predecessor translated floating-moment fallback returns `0x4320000000000000`. The RED Actions runs were cancelled by the immediate successor and are not hosted RED evidence. -`P = 664289479338799435974172876300357631`. +Initial repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` searched every represented residual as a candidate exact anchor. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` proves that set is still incomplete. With represented residuals `[1,2^-54,2,3]`, no observed residual is a universal exact anchor: each nonzero candidate loses the tiny represented component in at least one subtraction. Neutral dyadic anchor `0`, although not an observed residual in this fixture, preserves every coordinate exactly. -The unreduced denominator is `68_702_699_520`. This establishes reachability, not production admission. +On common unit `2^-54`, those coordinates are `[2^54,1,2^55,3*2^54]` and -## Exact-rounding width is a separate boundary +`P = 6490371073168534319490338297741315`. -At represented `n=2050`, characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d` reaches a second boundary. The exact pair numerator is +The exact scientific denominator is `48 * 2^108`, equivalently the exact-rounding route receives numerator `P`, denominator `48`, unit exponent `-54`. Correct binary64 rounding is `0x3fe4a7e9cb8a3491`. The predecessor translated floating-moment fallback returns adjacent upper `0x3fe4a7e9cb8a3492`; this is a second public one-ULP defect and demonstrates that “search observed anchors” is not a complete scientific admission policy. -`P = 332306998946228931332463617650984961`, +Repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` expands the production candidate set to neutral `0` plus every represented residual. Every candidate must preserve all translated coordinates exactly. Among admitted candidates, the implementation chooses the smallest maximum translated magnitude and breaks ties by represented anchor value, preserving permutation invariance while preferring a smaller exact dynamic range when an observed anchor is useful. Signed dyadic coordinates keep positive and negative coefficient mass separately; `n*Σc_i²` and `(Σc_i)²` are formed in `Wide256`, subtracted exactly, and downcast only if the final numerator fits the bounded exact-rounder contract. Unsupported coordinate construction, integer accumulation, Wide256 subtraction/downcast, denominator, or exact-rounding cases fail closed to the established generic implementation. -with denominator `8_610_922_500`. The normal binary64 ratio/square-root seed is `0x4296998e1aff78de`; exact candidate-square comparison needs 136-bit operands and the adjacent upward midpoint comparison needs 140-bit operands. +Neutral zero is not synthetic evidence. It is a deterministic translation origin for the translation-invariant pair-distance identity; subtracting `0` from a finite binary64 residual reproduces that represented residual exactly. The source observations remain unchanged. The repair still does not prove that zero plus observed residuals is a globally resource-optimal anchor set for every representable geometry; it closes the demonstrated correctness gaps without turning an unproved optimization claim into admission policy. -Characterization `aab9fe9115cee97225f2aa81e54a55ceafb23336` shows that arbitrary precision is unnecessary for these comparisons: represent each nonzero operand as a `Wide256` mantissa plus signed dyadic exponent, compare absolute top-bit positions, then aligned significand bits only if the top positions tie. It also covers extreme exponent metadata `-2148/-2149` and `2046/2047` without materializing `2^k`. - -RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` → repair `e4a85f53a611922be7492fe906d62ce65787c18e` moves the signed-exponent two-limb comparison into `crates/validation_core/src/bias_se.rs`. `1240ace8eb41a01fa72a4bb99df842fd550a1288` fixes both exact-midpoint tie-to-even parity directions. The binary64 division/sqrt remains only a candidate seed; the returned value is authorized by the exact candidate/midpoint comparison. - -The standards basis remains IEEE 754-2019 and ISO/IEC 60559:2020. IEEE P754 is an active revision project, not a published replacement. - -## Pair versus Wide256 represented equivalence - -Characterization `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` compares an actual two-pass O(n²) pairwise authority with an independent Wide256 O(n) identity for represented residual classes `{0,1,2^53}` at `n=4,16,17,65,257,2050`. Wherever both admit, they must produce the same common unit exponent, exact pair numerator, and GCD-reduced `(numerator, denominator, unit_exponent)` tuple presented to the exact rounder. At `n=2050`, both narrow products overflow while the two exact routes agree on the 118-bit `P` above. - -That test originally used the represented minimum as anchor because its characterized input family made the minimum exact. It is not a universal production anchor rule. - -## Pairwise-f64 admission is broader than neither science nor exact-anchor admission - -Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` isolates a distinction inside the existing `n=4` budget. Residuals `[0,1,2^-54,2]` have exact minimum-anchor coordinates, but non-anchor subtraction `1-2^-54` rounds in binary64. The O(n²) pairwise-f64 proof therefore refuses. On common unit `2^-54`, integer coordinates `[0,2^54,1,2^55]` give - -`P = 3569704090242693886528325169446915` - -through both direct integer pair distances and `n*S2-S1^2`. The generic translated fallback happens to return the same correctly represented public result `0x3fdea33e2c83c140`. This showed that exact non-anchor pair subtraction is a sufficient reference-path condition, not a scientific prerequisite. - -## Non-minimum exact anchor is a public correctness requirement - -A stronger represented fixture found after that characterization proves that selecting the represented minimum as a mandatory anchor is also too strict. Fix truth at represented zero and use residuals - -`[0,1,2,-2^53]`. - -The represented minimum is `-2^53`. Mathematical difference `1-(-2^53)=2^53+1` is not representable in binary64, so neither the pairwise-f64 proof nor a minimum-anchor-only linear proof can preserve this geometry exactly. Anchor `0`, however, gives exact signed coordinates `[0,1,2,-2^53]` on unit `1`. - -Their translation-invariant exact pair numerator is - -`P = 243388915243820099130562543878155`, - -so `SE(mean)^2 = P/48`. Correct binary64 rounding is `0x4320000000000001`. The predecessor translated floating-moment fallback returns the adjacent lower `0x4320000000000000`; this is a public one-ULP defect rather than only a resource/admission observation. - -Source-level RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` adds forward and reversed public contracts for `0x4320000000000001`. Its Actions runs were cancelled by the immediate successor push, so it is not claimed as hosted RED evidence. - -Production repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` keeps exact pairwise accumulation as the first authority. When pair subtraction cannot be proven exact, it searches every represented residual as a candidate translation anchor, requires every anchor-relative coordinate to be error-free, chooses the candidate with the smallest exact maximum translated magnitude and a represented-value tie-break, and converts the signed translated coordinates to a common dyadic grid. Positive and negative coefficient mass are accumulated separately; `n*Σc_i²` and `(Σc_i)²` are formed in `Wide256`, subtracted exactly, and downcast only if the final numerator fits the existing bounded `u128` rounder contract. The same exact candidate/midpoint/tie-to-even rounder then authorizes the result. - -This anchor policy intentionally mirrors the permutation-invariant principle already used by `bias.rs`: observation arrival order is not scientific evidence, and the represented minimum is not privileged when it cannot translate the geometry exactly. Unsupported coordinate accumulation, Wide256 subtraction/downcast, denominator, or exact-rounding cases continue to fail closed to the established generic implementation. - -The repair also promotes `[0,1,2^-54,2]` from generic fallback to exact anchor admission. It does **not** widen the production sample-count budget: `exact_pair_distance_standard_error` remains bounded to `n=4..=16`. +Production sample admission remains `n=4..=16`. ## Resource budget remains unresolved -Exact pair records are 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2048`, and 4,997,541 at `n=3162`. For the older aligned nonnegative diameter characterization `D=2^53`, narrow checked products fit through `n=2047`, the exact pair-numerator extremum fits through `n=4095`, and unreduced `n²(n-1)` stays at or below `2^53` through `n=208064`. Because production reduces the denominator by GCD and represented geometry varies, none is a universal refusal count or production budget. - -The production anchor repair changes the question from “can O(n) replace O(n²)?” to “what exact represented geometries can be admitted deterministically and at what measured cost?” Same-domain pair equivalence, intended anchor-only admission, exact rounder behavior, and fail-closed refusal all need to survive on one current head before any pair fallback can be demoted or the sample cutoff can move. +Exact pair records are 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2048`, and 4,997,541 at `n=3162`. For the older aligned nonnegative diameter characterization `D=2^53`, narrow checked products fit through `n=2047`, exact pair-numerator extremum through `n=4095`, and unreduced `n²(n-1)` stays at or below `2^53` through `n=208064`. GCD reduction and represented geometry mean none is a universal production cutoff. -The timing/layout vehicle remains `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records wide-product and pair-fallback selection separately. No Rust 1.98.0 `--release` CPU/raw CSV, allocator/RSS, or applicable buyer-path p95 evidence is claimed yet. +The timing vehicle remains `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records wide-product and pair-fallback selection separately. No Rust 1.98.0 `--release` CPU/raw CSV, allocator/RSS, or applicable buyer-path p95 evidence is authoritative yet. ## Decision -Keep production `validation_core::bias_standard_error` admission at `n=4..=16`. Accept the deterministic exact-anchor/Wide256 repair within that existing budget because the non-minimum-anchor fixture is a public one-ULP correctness defect, not a speculative performance optimization. Do not infer from that repair that a larger sample budget is safe. +Keep production `validation_core::bias_standard_error` at `n=4..=16`. Accept neutral-zero-plus-observed-anchor exact proof inside that existing budget because `9f403194... -> 6cf30eeb...` repairs a demonstrated public one-ULP defect. Do not infer that a larger sample budget is safe or that pairwise authority can yet be removed. -Before any budget change beyond 16, require: exact-head Rust 1.98.0 fmt/clippy/nextest/rustdoc and owned production 100% line/branch coverage; same-head security/documentation GREEN and qualifying independent review; pair/anchor equality wherever both admit; explicit intended anchor-only admissions including `[0,1,2^-54,2]` and `[0,1,2,-2^53]`; exact candidate stepping/midpoint/tie-to-even and permutation invariance; fail-closed overflow/range behavior; recorded release-mode raw CPU/allocator/RSS and applicable buyer-path p95 evidence; and current CHANGELOG/TRACEABILITY/TEST_STRATEGY/OPERABILITY/operator baseline. +Before widening beyond 16, require exact-head Rust 1.98.0 fmt/clippy/nextest/rustdoc and owned production 100% line/branch coverage, same-head security/documentation GREEN and qualifying independent review, pair/anchor equality wherever both admit, explicit anchor-only fixtures `[0,1,2^-54,2]`, `[0,1,2,-2^53]`, and `[1,2^-54,2,3]`, exact candidate stepping/midpoint/tie-to-even and permutation invariance, fail-closed overflow/range behavior, recorded release-mode raw CPU/allocator/RSS and applicable buyer-path p95 evidence, and current CHANGELOG/TRACEABILITY/TEST_STRATEGY/OPERABILITY/operator baseline. ## Traceability - Issue: #491 - Landing PR: #488 - Accumulator-bound characterization: `b7e4da353ac58069afd73ee7c0e8427d49993fdb` -- Wide-product capacity RED: `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` -- Wide-product capacity repair: `e9a7dee29afb97542bfe2965f850c8ab5a34368e` +- Wide-product capacity RED / repair: `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` / `e9a7dee29afb97542bfe2965f850c8ab5a34368e` - Represented-input Wide256 reachability: `5a19b6334487b43fb630abba7e487d7cf4c49960` -- Represented exact-midpoint width characterization: `a8423173188fa53a26a16d3afdafeb76e114cc1d` -- Exponent-safe scaled comparison characterization: `aab9fe9115cee97225f2aa81e54a55ceafb23336` -- Production scaled-comparison RED: `f7717361ad8c5f0592688c1514c104cc1b4adabe` -- Production scaled-comparison repair: `e4a85f53a611922be7492fe906d62ce65787c18e` -- Exact midpoint tie-to-even edge contract: `1240ace8eb41a01fa72a4bb99df842fd550a1288` +- Represented exact-midpoint width: `a8423173188fa53a26a16d3afdafeb76e114cc1d` +- Exponent-safe scaled comparison: `aab9fe9115cee97225f2aa81e54a55ceafb23336` +- Production scaled-comparison RED / repair: `f7717361ad8c5f0592688c1514c104cc1b4adabe` / `e4a85f53a611922be7492fe906d62ce65787c18e` +- Exact midpoint tie-to-even: `1240ace8eb41a01fa72a4bb99df842fd550a1288` - Represented pair/Wide256 exact-ratio equivalence: `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` -- Anchor-linear-only represented admission characterization: `2bc1d2284d75154e020640adb573c1cfadf005fb` -- Non-minimum-anchor public RED: `fd9f9ff2c5c395e4cc13042232f4deef018adb48` -- Deterministic exact-anchor production repair: `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` -- Narrow-wide-pair RED: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` -- Narrow-wide-pair repair: `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` +- Pairwise-f64-strict characterization: `2bc1d2284d75154e020640adb573c1cfadf005fb` +- Non-minimum represented-anchor RED / repair: `fd9f9ff2c5c395e4cc13042232f4deef018adb48` / `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` +- Neutral-anchor RED / repair: `9f403194a2ec1636531c2dfe9229cfb34b73d747` / `6cf30eeb549c0df0377bda1111cf46396e8282a3` +- Narrow-wide-pair RED / repair: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` / `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` - CHANGELOG fragment: `CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md` - Production module: `crates/validation_core/src/bias_se.rs` - Public regression: `crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs` From c8159c2dde1cb49d771c03cae2a5bb98aee1acae Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 17:20:24 +0900 Subject: [PATCH 550/576] docs(test): require neutral-anchor exact bias-SE coverage --- docs/TEST_STRATEGY.md | 14 ++++++++------ 1 file changed, 8 insertions(+), 6 deletions(-) diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index d8e7a1776..37aab2064 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -68,27 +68,29 @@ Simulation thresholds account for Monte Carlo standard error and interval uncert Validation Evidence numerical proofs that add asymptotic work or material allocation require a measured resource contract before a production admission boundary is widened. For the bias-standard-error exact pair-distance path tracked by issue #491: - retain realistic represented-input counterexamples and permutation/sign-mirror contracts for scientific correctness; -- keep the existing exact represented-residual gate, GCD reduction, exact candidate/midpoint authorization, and fail-closed fallback; +- keep the exact represented-residual gate, GCD reduction, exact candidate/midpoint authorization, and fail-closed fallback; - treat exact pairwise-f64 subtraction as a first reference authority, not a scientific prerequisite. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` proves `[0,1,2^-54,2]` has exact anchor coordinates even though a non-anchor pair subtraction rounds; -- do not require the represented minimum to be the exact anchor. Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` uses `[0,1,2,-2^53]`: minimum anchor `-2^53` cannot exactly translate `1`, while anchor `0` preserves all coordinates. The exact pair numerator is `243388915243820099130562543878155`, denominator `48`, and the correctly rounded public value is `0x4320000000000001`; the predecessor translated floating-moment fallback returns adjacent lower `0x4320000000000000`; -- retain repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f`: production exact admission searches every represented residual as a candidate anchor, requires exact anchor-relative coordinates, chooses the smallest exact maximum translated magnitude with represented-value tie-break, accumulates signed dyadic coordinates, forms `n*Σc_i²-(Σc_i)²` with `Wide256`, downcasts only a bounded final numerator, and reuses exact candidate/midpoint tie-to-even rounding. Test forward and reversed observation order bit-for-bit; +- do not require the represented minimum to be the exact anchor. Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` uses `[0,1,2,-2^53]`: minimum `-2^53` cannot exactly translate `1`, while represented anchor `0` preserves all coordinates. Exact `P=243388915243820099130562543878155`, denominator `48`, and public result `0x4320000000000001` differ by one ULP from the predecessor fallback; +- do not restrict production exact anchors to observed residuals. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` uses `[1,2^-54,2,3]`, where no observed residual exactly translates every other residual, but neutral dyadic anchor `0` preserves all. On unit `2^-54`, exact `P=6490371073168534319490338297741315`; exact result `0x3fe4a7e9cb8a3491` differs by one ULP from predecessor fallback `0x3fe4a7e9cb8a3492`; +- retain production repair `6cf30eeb549c0df0377bda1111cf46396e8282a3`: pairwise proof stays first; fallback exact-anchor candidates are neutral `0` plus every represented residual; every candidate must preserve all translated coordinates; choose the smallest exact maximum translated magnitude with represented-value tie-break; accumulate signed dyadic coordinates; form `n*Σc_i²-(Σc_i)²` with `Wide256`; downcast only a bounded exact numerator; reuse exact candidate/midpoint tie-to-even rounding. Test forward and permuted observation order bit-for-bit; - preserve pair-versus-anchor equality wherever both admit and explicitly test intended anchor-only admissions. A broader exact-anchor set must not narrow existing pair admissions or depend on row arrival order; +- neutral zero is a translation origin, not synthetic evidence: the observed residual values are unchanged. Do not claim zero-plus-observed anchors are globally resource-optimal without a separate proof; - compare buffered O(n²), allocation-free two-pass O(n²), normalized narrow O(n), dependency-free Wide256 O(n), predecessor narrow→pair hybrid, and narrow→Wide256→pair candidate resource shapes before changing the sample budget; - normalize the common power-of-two dyadic unit before narrow checked O(n) intermediates are judged. Raw `D=2^58,n=65` refusal is invalid after normalization; odd `D=2^58+1,n=65` remains the narrow 129-bit product witness; -- retain the earlier nonnegative minimum-anchor accumulator theorem `b7e4da353ac58069afd73ee7c0e8427d49993fdb`, `Σc_i <= Σc_i² <= P`, only within that characterized representation. Production exact-anchor coordinates may be signed, so positive and negative coefficient mass and the resulting signed sum must be exercised separately; +- retain the earlier nonnegative minimum-anchor accumulator theorem `b7e4da353ac58069afd73ee7c0e8427d49993fdb`, `Σc_i <= Σc_i² <= P`, only within that characterized representation. Production exact-anchor coordinates may be signed, so positive and negative coefficient mass and resulting signed sum must be exercised separately; - retain RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` → repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e`: canonical `u128` cancellation operands require no more than 256-bit products; - retain represented reachability `5a19b6334487b43fb630abba7e487d7cf4c49960`: at `n=4096`, `{0,1,2^53}` reaches narrow-product overflow while Wide256 recovers exact 119-bit `P=664_289_479_338_799_435_974_172_876_300_357_631`; - retain exact-rounding-width characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d`: at represented `n=2050`, `P=332_306_998_946_228_931_332_463_617_650_984_961`, denominator `8_610_922_500`, candidate-square comparison requires 136 bits and the upward midpoint 140 bits; - retain exponent-safe comparison characterization `aab9fe9115cee97225f2aa81e54a55ceafb23336`, production comparison RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` → repair `e4a85f53a611922be7492fe906d62ce65787c18e`, and tie-to-even edge contract `1240ace8eb41a01fa72a4bb99df842fd550a1288`; - retain represented pair/Wide256 exact-ratio equivalence `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` at `n=4,16,17,65,257,2050`, while recognizing that its minimum-anchor rule is scoped to that input family rather than universal production canonicalization; -- keep the normalized O(n) distribution-independent intermediate envelope distinct from the exact pair-numerator and denominator envelopes. At aligned diameter `2^53`, `n<=2_047`, `n<=4_095`, and `n<=208_064` are arithmetic evidence points, not production budgets; +- keep normalized O(n) intermediate, exact pair-numerator, and denominator envelopes separate. At aligned diameter `2^53`, `n<=2_047`, `n<=4_095`, and `n<=208_064` are arithmetic evidence points, not production budgets; - record exact pair counts, target `size_of::>()`, scratch `Vec` capacity/payload, and allocator/RSS evidence separately; field-width estimates are not allocation evidence; - use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing and record `used_wide_product` and `used_pairwise_fallback` independently; - run the characterization harness in release mode on recorded CPU/OS/Rust 1.98.0 and retain raw CSV plus p95. Unexecuted harness code is not performance evidence; - if a service/API buyer path is affected, measure the complete applicable path against `p95 <= 20 ms` without shrinking samples, omitting proof work, or using unrealistic warm-cache-only setup; - arithmetic representability or one successful anchor does not authorize a production sample-count budget. -Until same-head correctness, exact-head gates, independent review, and resource measurements exist, the production bias-SE exact admission remains `n=4..=16`. +Until same-head correctness, exact-head gates, independent review, and resource measurements exist, production bias-SE exact admission remains `n=4..=16`. ## Release acceptance From 103768fc3f2fa2f05e8c6e7314b325ceeaf804d4 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 17:21:00 +0900 Subject: [PATCH 551/576] docs(ops): cover neutral-anchor bias-SE recovery route --- docs/OPERABILITY.md | 43 +++++++++++++++---------------------------- 1 file changed, 15 insertions(+), 28 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index 1c5f64e3b..da3487bee 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -20,23 +20,7 @@ The Rust domain packages are embedded/library boundaries. They require no produc ## Planned service SLIs -When corresponding services exist, track: - -- source ingest success/rejection and exact error class; -- evidence/span count and lineage completeness; -- future-evidence exclusion count at each knowledge cutoff; -- temporal contradiction/path-consistency and budget exhaustion counts; -- event/link/tracking confidence and calibration; -- semantic-unit unknown/abstention rate by language; -- model convergence/ELBO/objective and posterior diagnostics; -- true-recovery/validation drift against release benchmark; -- CPU/GPU parity and fallback count; -- VRAM/RSS/transfer/kernel time; -- model/LLM provider failures and evidence-verifier rejection; -- artifact/export provenance completeness; -- tenant authorization/audit anomalies. - -Do not expose raw PII/source text in ordinary metrics/logs merely to gain observability. +When corresponding services exist, track source ingest success/rejection and exact error class; evidence/span count and lineage completeness; future-evidence exclusion at each knowledge cutoff; temporal contradiction/path-consistency and budget exhaustion; event/link/tracking confidence and calibration; semantic-unit unknown/abstention by language; model convergence/objective/posterior diagnostics; true-recovery drift; CPU/GPU parity and fallback; VRAM/RSS/transfer/kernel time; model/LLM provider failures and evidence-verifier rejection; artifact/export provenance completeness; and tenant authorization/audit anomalies. Do not expose raw PII/source text in ordinary metrics/logs merely to gain observability. ## Data snapshot and replay @@ -60,31 +44,34 @@ Before PostgreSQL becomes production state, prove migrations and rollback, tenan A numerical proof boundary is an operational resource contract when it changes asymptotic work, allocation, or buyer-path latency. It is not determined by the next sample count that happens to expose a rounding defect. -Issue #491 owns the current bias-standard-error exact-proof budget. Production exact admission remains `n<=16`; larger counts are characterization evidence only. The current work separates represented-input exactness, arithmetic width, exact-rounding width, and measured resource cost rather than treating one integer cutoff as all four. +Issue #491 owns the current bias-standard-error exact-proof budget. Production exact admission remains `n<=16`; larger counts are characterization evidence only. The work separates represented-input exactness, arithmetic width, exact-rounding width, and measured resource cost rather than treating one integer cutoff as all four. -The old nonnegative minimum-anchor characterization established `Σc_i <= Σc_i² <= P` and showed why raw-scale `D=2^58,n=65` refusal disappears after common dyadic-unit normalization. Odd `D=2^58+1,n=65` remains a real narrow-width witness: pair numerator fits in 123 bits while cancellation products require 129 bits. `Wide256` characterization `081000289f5a52e94863026d55696ee2a4daf923` and product-width RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` → repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e` show the characterized cancellation products need no more than two `u128` limbs. +The old nonnegative minimum-anchor characterization established `Σc_i <= Σc_i² <= P` and showed why raw-scale `D=2^58,n=65` refusal disappears after common dyadic-unit normalization. Odd `D=2^58+1,n=65` remains a narrow-width witness: pair numerator fits in 123 bits while cancellation products require 129 bits. `Wide256` characterization `081000289f5a52e94863026d55696ee2a4daf923` and product-width RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` → repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e` show the characterized cancellation products need no more than two `u128` limbs. -Represented-input reachability `5a19b6334487b43fb630abba7e487d7cf4c49960` reaches the wider numerator route at `n=4096` on residual classes `{0,1,2^53}`. Exact-rounding characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d` then shows that represented `n=2050` needs 136-bit candidate-square and 140-bit adjacent-midpoint comparison operands even though its exact pair numerator is only 118 bits. `aab9fe9115cee97225f2aa81e54a55ceafb23336` shows those comparisons can remain bounded as `Wide256` mantissa plus signed dyadic exponent. RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` → repair `e4a85f53a611922be7492fe906d62ce65787c18e` integrates that comparison into the production exact rounder, and `1240ace8eb41a01fa72a4bb99df842fd550a1288` fixes both tie-to-even parity directions. +Represented-input reachability `5a19b6334487b43fb630abba7e487d7cf4c49960` reaches the wider numerator route at `n=4096` on `{0,1,2^53}`. Exact-rounding characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d` shows represented `n=2050` needs 136-bit candidate-square and 140-bit adjacent-midpoint comparison operands even though its exact pair numerator is 118 bits. `aab9fe9115cee97225f2aa81e54a55ceafb23336` shows those comparisons can remain bounded as `Wide256` mantissa plus signed dyadic exponent. RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` → repair `e4a85f53a611922be7492fe906d62ce65787c18e` integrates that comparison into the production exact rounder, and `1240ace8eb41a01fa72a4bb99df842fd550a1288` fixes both tie-to-even parity directions. `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` proves represented pair/Wide256 exact-ratio equivalence for one family where the represented minimum is an exact anchor. That is not a universal anchor policy. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` demonstrates `[0,1,2^-54,2]`, where non-anchor pair subtraction rounds but anchor `0` remains exact. -The current production repair closes a stronger correctness defect. Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` uses represented residuals `[0,1,2,-2^53]`. The minimum residual cannot exactly translate `1` because `2^53+1` is not representable; anchor `0` preserves every coordinate. Signed unit-one coordinates give exact pair numerator `243388915243820099130562543878155`, denominator `48`, and correctly rounded result `0x4320000000000001`. The predecessor translated floating-moment fallback produces adjacent lower `0x4320000000000000`. +Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` establishes the first public anchor defect with `[0,1,2,-2^53]`: minimum `-2^53` cannot exactly translate `1`, represented anchor `0` preserves every coordinate, exact `P=243388915243820099130562543878155`, denominator `48`, and exact result `0x4320000000000001` while the predecessor translated floating-moment path returns `0x4320000000000000`. Initial repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` searched represented residual anchors. -Repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` therefore keeps pairwise exact proof first but, on pairwise refusal, searches every represented residual as an exact anchor. It chooses the candidate with the smallest exact maximum translated magnitude and represented-value tie-break, builds signed coordinates on a common dyadic unit, accumulates positive/negative coefficient mass and square mass with checked `u128`, performs `n*Σc_i²-(Σc_i)²` with exact `Wide256` products/subtraction, downcasts only a bounded final numerator, and reuses the exact candidate/midpoint tie-to-even rounder. This is a correctness repair within the existing sample budget, not permission to widen the budget. +Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` shows that observed anchors alone are operationally incomplete. Residuals `[1,2^-54,2,3]` have no observed universal exact anchor, but neutral dyadic anchor `0` preserves every residual exactly. On unit `2^-54`, exact `P=6490371073168534319490338297741315`; exact result `0x3fe4a7e9cb8a3491` differs by one ULP from the predecessor fallback `0x3fe4a7e9cb8a3492`. + +Repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` keeps pairwise exact proof first, then considers neutral zero plus every represented residual as exact-anchor candidates. It requires error-free coordinates, chooses the smallest exact maximum translated magnitude with represented-value tie-break, builds a common dyadic grid, tracks positive and negative coefficient mass separately, performs `n*Σc_i²-(Σc_i)²` with exact `Wide256` products/subtraction, downcasts only a bounded final numerator, and reuses exact candidate/midpoint tie-to-even rounding. Neutral zero is a deterministic translation origin, not a synthetic observation; source residuals remain unchanged. Operator implications: -- do not diagnose pairwise-f64 or minimum-anchor refusal as scientific invalidity when another deterministic represented anchor is exact; -- do not make row order part of anchor selection. Forward/reversed fixtures must be bit-identical; -- treat failure of signed coordinate accumulation, Wide256 subtraction/downcast, denominator reduction, or exact rounding as a fail-closed proof refusal and use the established generic fallback rather than weakening arithmetic checks; -- keep pairwise exact proof as the first authority while production exact-anchor coverage matures; +- do not diagnose pairwise-f64, minimum-anchor, or observed-anchor refusal as scientific invalidity when the neutral zero translation remains exact and the bounded dyadic proof admits it; +- do not make row order part of anchor selection; forward/reversed/permuted fixtures must be bit-identical; +- keep observed anchors because they can reduce exact dynamic range relative to zero; zero is the deterministic completeness fallback for the demonstrated represented-domain defect, not a claim of global resource-optimality; +- treat signed-coordinate accumulation, Wide256 subtraction/downcast, denominator reduction, or exact-rounding failure as a fail-closed proof refusal and use the established generic fallback rather than weakening checks; +- keep pairwise exact proof as the first authority while exact-anchor coverage matures; - separate route observability from numerical equality. The resource harness records `used_wide_product` and `used_pairwise_fallback` independently; - treat `n=2_047`, `4_095`, and `208_064` only as arithmetic envelope markers from older aligned characterizations, not service limits; - before widening beyond 16, retain raw Rust 1.98.0 `--release` timing CSV, CPU/OS/build flags, p95, actual scratch capacity/payload, allocator/RSS, and any applicable buyer-path `p95<=20 ms` evidence without sample shrinkage or omitted proof work. -Exact pair-record counts remain 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2048`, and 4,997,541 at `n=3162`. A two-pass O(n²) implementation may remove pair-record storage but still requires exact-head scientific and resource evidence. A Wide256 O(n) implementation may remove both pair storage and pair enumeration for admitted geometries but still requires pair-equivalence, anchor-only admission, exact-rounding, refusal, and resource evidence on one surviving production head. +Exact pair-record counts remain 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2048`, and 4,997,541 at `n=3162`. A two-pass O(n²) implementation may remove pair-record storage but still requires exact-head scientific/resource evidence. A Wide256 O(n) implementation may remove both pair storage and pair enumeration for admitted geometries but still requires pair-equivalence, anchor-only admission, exact-rounding, refusal, and resource evidence on one surviving production head. -No release-mode resource numbers are authoritative yet. Current #488 exact-head workflows after the source repair and subsequent doctoring remain pending/queued until a surviving head completes. The source-level RED workflows were cancelled by the immediate repair push and are not counted as hosted RED. Predecessor Rustfmt artifact `9982621569` from `1f765a...` is not current-head formatting evidence. +No release-mode resource numbers are authoritative yet. Source-level RED runs that were superseded or cancelled are not counted as hosted RED. Predecessor Rustfmt artifact `9982621569` from `1f765a...` is not current-head formatting evidence. ## Model release/cutover From e0b324864e48a503e2aba0d2a487a0b95f5276ed Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 17:57:55 +0900 Subject: [PATCH 552/576] test(validation): require neutral-zero linear proof first --- ...error_neutral_zero_route_order_contract.rs | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) create mode 100644 crates/validation_core/tests/bias_standard_error_neutral_zero_route_order_contract.rs diff --git a/crates/validation_core/tests/bias_standard_error_neutral_zero_route_order_contract.rs b/crates/validation_core/tests/bias_standard_error_neutral_zero_route_order_contract.rs new file mode 100644 index 000000000..0633578d6 --- /dev/null +++ b/crates/validation_core/tests/bias_standard_error_neutral_zero_route_order_contract.rs @@ -0,0 +1,32 @@ +#![forbid(unsafe_code)] +//! Guards the exact bias-SE proof route against reintroducing quadratic work ahead +//! of the neutral-zero linear kernel. + +const SOURCE: &str = include_str!("../src/bias_se.rs"); + +#[test] +fn neutral_zero_linear_proof_precedes_quadratic_fallbacks() { + let start = SOURCE + .find("fn exact_pair_distance_standard_error(") + .expect("production exact-proof entrypoint must exist"); + let end = SOURCE[start..] + .find("pub fn bias_standard_error(") + .map(|offset| start + offset) + .expect("public bias-SE entrypoint must follow the exact-proof helper"); + let route = &SOURCE[start..end]; + + let neutral_zero = route + .find("exact_neutral_zero_linear_pair_square_sum(&residuals)") + .expect("neutral-zero O(n) proof must be a production route"); + let conditioned_anchor = route + .find("exact_anchor_linear_pair_square_sum(&residuals)") + .expect("conditioned observed-anchor fallback must remain available"); + let pairwise = route + .find("exact_pairwise_pair_square_sum(&residuals)") + .expect("pairwise reference fallback must remain available"); + + assert!( + neutral_zero < conditioned_anchor && conditioned_anchor < pairwise, + "production proof order must be neutral-zero linear -> conditioned anchor -> pairwise reference" + ); +} From 2b62bd46eb0c391327d2285c2244a76f5a1e0449 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 18:00:08 +0900 Subject: [PATCH 553/576] fix(validation): run neutral-zero exact proof before quadratic fallbacks --- crates/validation_core/src/bias_se.rs | 119 +++++++++++++++++++------- 1 file changed, 87 insertions(+), 32 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 105258701..0aa882304 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -2,10 +2,10 @@ //! //! The general bias implementation remains the fallback authority. This module //! admits a bounded small-sample exact pair-distance identity when represented -//! residuals either have error-free pairwise differences or admit a deterministic -//! exact anchor translation whose dyadic integer numerator fits the bounded proof; -//! the exact rational square root is then rounded against binary64 midpoints -//! without first rounding the ratio under the square root. +//! residuals admit a neutral-zero linear proof, a conditioned exact observed +//! anchor, or error-free pairwise differences; the exact rational square root is +//! then rounded against binary64 midpoints without first rounding the ratio under +//! the square root. use crate::ValidationError; use core::cmp::Ordering; @@ -332,14 +332,56 @@ fn exact_pairwise_pair_square_sum(residuals: &[f64]) -> Option<(u128, i32)> { Some((pair_square_sum, unit_exponent)) } +fn exact_neutral_zero_linear_pair_square_sum(residuals: &[f64]) -> Option<(u128, i32)> { + // Every finite represented residual is already an exact coordinate relative + // to neutral zero. First choose the common dyadic unit, then rescan to build + // the signed first moment and square sum. This keeps the proof O(n) with O(1) + // proof storage and avoids materializing pair distances. + let mut unit_exponent = i32::MAX; + for &coordinate in residuals { + if coordinate == 0.0 { + continue; + } + let (_, exponent) = positive_dyadic(coordinate.abs())?; + unit_exponent = unit_exponent.min(exponent); + } + if unit_exponent == i32::MAX { + return Some((0, 0)); + } + + let mut positive_sum = 0_u128; + let mut negative_sum = 0_u128; + let mut square_sum = 0_u128; + for &coordinate in residuals { + if coordinate == 0.0 { + continue; + } + let (significand, exponent) = positive_dyadic(coordinate.abs())?; + let shift = exponent.checked_sub(unit_exponent)?.unsigned_abs(); + let coefficient = multiply_by_power_of_two(significand, shift)?; + if coordinate.is_sign_negative() { + negative_sum = negative_sum.checked_add(coefficient)?; + } else { + positive_sum = positive_sum.checked_add(coefficient)?; + } + square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; + } + + let sample_count = u128::try_from(residuals.len()).ok()?; + let scaled_square_sum = Wide256::multiply_u128(sample_count, square_sum); + let signed_sum_magnitude = positive_sum.abs_diff(negative_sum); + let squared_sum = Wide256::multiply_u128(signed_sum_magnitude, signed_sum_magnitude); + let numerator = scaled_square_sum.checked_sub(squared_sum)?.to_u128()?; + Some((numerator, unit_exponent)) +} + fn exact_anchor_linear_pair_square_sum(residuals: &[f64]) -> Option<(u128, i32)> { - // Zero is a neutral dyadic translation anchor for every finite represented - // residual, even when no observed residual can translate every other value - // without rounding. Keep represented residuals in the candidate set so an - // exact observed anchor with a smaller dynamic range still wins. The final - // total-order tie-break makes selection independent of observation order. + // Neutral zero is handled by the O(n) fast route. Search represented residuals + // only when translating by an observed anchor can reduce the dyadic dynamic + // range enough to recover a bounded proof that neutral zero refused. The final + // total-order tie-break keeps selection independent of observation order. let mut best: Option<(f64, f64, Vec)> = None; - for anchor in core::iter::once(0.0).chain(residuals.iter().copied()) { + for anchor in residuals.iter().copied() { let mut translated = Vec::with_capacity(residuals.len()); let mut max_magnitude = 0.0_f64; let mut exact = true; @@ -438,13 +480,15 @@ fn exact_pair_distance_standard_error( residuals.push(residual); } - // Preserve the pairwise-f64 proof as the first authority. If one represented - // non-anchor pair subtraction rounds, compare the neutral zero anchor with - // every represented residual anchor and choose the exact translation with the - // smallest dynamic range. Recover the same translation-invariant numerator - // through n*Σc_i²-(Σc_i)² using two-limb cancellation products. - let (pair_square_sum, unit_exponent) = exact_pairwise_pair_square_sum(&residuals) - .or_else(|| exact_anchor_linear_pair_square_sum(&residuals))?; + // Attempt the neutral-zero two-pass proof first: finite represented residuals + // are exact coordinates around zero, so this is O(n) with O(1) proof storage. + // If its bounded integer coordinate range refuses, search exact observed anchors + // that may reduce that range. Keep pairwise O(n²) last as a comparison and + // fail-closed reference while admission equivalence and release-mode budgets are + // still being characterized. + let (pair_square_sum, unit_exponent) = exact_neutral_zero_linear_pair_square_sum(&residuals) + .or_else(|| exact_anchor_linear_pair_square_sum(&residuals)) + .or_else(|| exact_pairwise_pair_square_sum(&residuals))?; if pair_square_sum == 0 { return Some(Ok(0.0)); } @@ -476,13 +520,12 @@ fn exact_pair_distance_standard_error( /// Standard error of mean signed bias. /// -/// Four- through sixteen-observation samples whose represented residuals admit -/// either the exact pairwise-difference proof or a deterministic exact anchor -/// translation use the exact pair-distance identity when its reduced dyadic ratio -/// fits the bounded integer proof. The anchor candidates include neutral zero and -/// every represented residual so proof admission does not depend on a minimum or -/// observed residual being universally subtractable. All other samples retain the -/// established bias implementation and its existing fail-closed behavior. +/// Four- through sixteen-observation samples first attempt an exact neutral-zero +/// linear proof, then a conditioned exact observed-anchor translation, and finally +/// the pairwise-difference reference when the earlier bounded proofs refuse. Each +/// admitted route uses the same exact pair-distance identity and exact dyadic +/// midpoint rounding. All other samples retain the established bias implementation +/// and its existing fail-closed behavior. pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result { if let Some(result) = exact_pair_distance_standard_error(truth, recovered) { return result; @@ -494,9 +537,9 @@ pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result Date: Sun, 6 Sep 2026 18:03:46 +0900 Subject: [PATCH 554/576] docs(validation): trace neutral-zero linear production route --- ...ndard-error-wide-linear-admission-bound.md | 30 ++++++++++++++----- 1 file changed, 22 insertions(+), 8 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index 23192d9f8..1d5748263 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -22,7 +22,7 @@ Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` shows why: residuals Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` then shows a public defect with `[0,1,2,-2^53]`. The represented minimum `-2^53` cannot exactly translate `1`, but represented anchor `0` preserves every coordinate. Signed unit-one coordinates give `P=243388915243820099130562543878155`, denominator `48`, and correctly rounded result `0x4320000000000001`; the predecessor translated floating-moment fallback returns `0x4320000000000000`. The RED Actions runs were cancelled by the immediate successor and are not hosted RED evidence. -Initial repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` searched every represented residual as a candidate exact anchor. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` proves that set is still incomplete. With represented residuals `[1,2^-54,2,3]`, no observed residual is a universal exact anchor: each nonzero candidate loses the tiny represented component in at least one subtraction. Neutral dyadic anchor `0`, although not an observed residual in this fixture, preserves every coordinate exactly. +Initial repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` searched every represented residual as a candidate exact anchor. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` proves that set is still incomplete. With represented residuals `[1,2^-54,2,3]`, no observed residual is a universal exact anchor: each nonzero candidate loses the tiny represented component in at least one subtraction. Neutral dyadic anchor `0`, although not an observed residual in this fixture, preserves every represented residual exactly. On common unit `2^-54`, those coordinates are `[2^54,1,2^55,3*2^54]` and @@ -30,9 +30,21 @@ On common unit `2^-54`, those coordinates are `[2^54,1,2^55,3*2^54]` and The exact scientific denominator is `48 * 2^108`, equivalently the exact-rounding route receives numerator `P`, denominator `48`, unit exponent `-54`. Correct binary64 rounding is `0x3fe4a7e9cb8a3491`. The predecessor translated floating-moment fallback returns adjacent upper `0x3fe4a7e9cb8a3492`; this is a second public one-ULP defect and demonstrates that “search observed anchors” is not a complete scientific admission policy. -Repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` expands the production candidate set to neutral `0` plus every represented residual. Every candidate must preserve all translated coordinates exactly. Among admitted candidates, the implementation chooses the smallest maximum translated magnitude and breaks ties by represented anchor value, preserving permutation invariance while preferring a smaller exact dynamic range when an observed anchor is useful. Signed dyadic coordinates keep positive and negative coefficient mass separately; `n*Σc_i²` and `(Σc_i)²` are formed in `Wide256`, subtracted exactly, and downcast only if the final numerator fits the bounded exact-rounder contract. Unsupported coordinate construction, integer accumulation, Wide256 subtraction/downcast, denominator, or exact-rounding cases fail closed to the established generic implementation. +Repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` expanded the production candidate set to neutral `0` plus every represented residual. Every candidate had to preserve all translated coordinates exactly; the implementation chose the smallest maximum translated magnitude with a represented-value tie-break. This closed the demonstrated correctness defects but still evaluated pairwise proof first and then scanned the full sample for each anchor candidate, so the production proof path remained O(n²). -Neutral zero is not synthetic evidence. It is a deterministic translation origin for the translation-invariant pair-distance identity; subtracting `0` from a finite binary64 residual reproduces that represented residual exactly. The source observations remain unchanged. The repair still does not prove that zero plus observed residuals is a globally resource-optimal anchor set for every representable geometry; it closes the demonstrated correctness gaps without turning an unproved optimization claim into admission policy. +Neutral zero is not synthetic evidence. It is a deterministic translation origin for the translation-invariant pair-distance identity; subtracting `0` from a finite binary64 residual reproduces that represented residual exactly. The source observations remain unchanged. + +## Production route repair + +The resource finding above is now repaired on active PR #488, without widening the production sample budget. + +Source-level RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` adds a contract requiring the production proof order `neutral_zero_linear -> conditioned_observed_anchor -> pairwise_reference`. The RED commit was superseded before hosted failure evidence completed: its non-Rust Actions runs were cancelled and the Rust run had not produced a failing result. It is therefore source-level TDD evidence, not hosted RED evidence. + +Repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` adds `exact_neutral_zero_linear_pair_square_sum`. It scans represented residuals once to select the common dyadic exponent and a second time to accumulate positive/negative integer coefficient mass and `Σc_i²`. It then evaluates `n*Σc_i²-(Σc_i)²` with exact `Wide256` products and subtraction. The kernel is O(n) time and O(1) proof storage after the residual vector; it allocates no pair records. + +If the zero-origin bounded integer representation refuses, `exact_anchor_linear_pair_square_sum` now searches only represented residual anchors. That O(n²) fallback remains scientifically useful because an exact translated origin can reduce dyadic dynamic range. `exact_pairwise_pair_square_sum` is evaluated last as the O(n²) comparison/fail-closed reference while broader represented-input equality remains under test. + +The production module now includes a common-domain equality unit test for neutral-zero versus pairwise proof and retains the exact anchor-only regression geometries. This is not yet a proof of full bounded-domain equivalence. Required follow-up evidence includes broader deterministic common-domain equality, permutation/reversal bit identity, a fixture where conditioned observed anchoring recovers a zero-origin bounded refusal, and route-specific production execution evidence. Production sample admission remains `n=4..=16`. @@ -40,13 +52,13 @@ Production sample admission remains `n=4..=16`. Exact pair records are 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2048`, and 4,997,541 at `n=3162`. For the older aligned nonnegative diameter characterization `D=2^53`, narrow checked products fit through `n=2047`, exact pair-numerator extremum through `n=4095`, and unreduced `n²(n-1)` stays at or below `2^53` through `n=208064`. GCD reduction and represented geometry mean none is a universal production cutoff. -The timing vehicle remains `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records wide-product and pair-fallback selection separately. No Rust 1.98.0 `--release` CPU/raw CSV, allocator/RSS, or applicable buyer-path p95 evidence is authoritative yet. +The timing vehicle remains `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records wide-product and pair-fallback selection separately. It is still characterization tooling, not production route telemetry. No Rust 1.98.0 `--release` CPU/raw CSV, allocator/RSS, or applicable buyer-path p95 evidence is authoritative yet. ## Decision -Keep production `validation_core::bias_standard_error` at `n=4..=16`. Accept neutral-zero-plus-observed-anchor exact proof inside that existing budget because `9f403194... -> 6cf30eeb...` repairs a demonstrated public one-ULP defect. Do not infer that a larger sample budget is safe or that pairwise authority can yet be removed. +Keep production `validation_core::bias_standard_error` at `n=4..=16`. Use the neutral-zero two-pass exact proof before quadratic proof work inside that existing budget, retain conditioned observed anchors when they can reduce bounded coordinate range, and keep pairwise O(n²) as comparison/fail-closed reference until broader equivalence and measured production-route evidence support consolidation. -Before widening beyond 16, require exact-head Rust 1.98.0 fmt/clippy/nextest/rustdoc and owned production 100% line/branch coverage, same-head security/documentation GREEN and qualifying independent review, pair/anchor equality wherever both admit, explicit anchor-only fixtures `[0,1,2^-54,2]`, `[0,1,2,-2^53]`, and `[1,2^-54,2,3]`, exact candidate stepping/midpoint/tie-to-even and permutation invariance, fail-closed overflow/range behavior, recorded release-mode raw CPU/allocator/RSS and applicable buyer-path p95 evidence, and current CHANGELOG/TRACEABILITY/TEST_STRATEGY/OPERABILITY/operator baseline. +Before widening beyond 16, require exact-head Rust 1.98.0 fmt/clippy/nextest/rustdoc and owned production 100% line/branch coverage, same-head security/documentation GREEN and qualifying independent review, broad pair/neutral-zero equality wherever both admit, explicit anchor-only fixtures `[0,1,2^-54,2]`, `[0,1,2,-2^53]`, and `[1,2^-54,2,3]`, exact candidate stepping/midpoint/tie-to-even and permutation invariance, fail-closed overflow/range behavior, truthful production route telemetry, recorded release-mode raw CPU/allocator/RSS and applicable buyer-path p95 evidence, and current CHANGELOG/TRACEABILITY/TEST_STRATEGY/OPERABILITY/operator baseline. ## Traceability @@ -62,9 +74,11 @@ Before widening beyond 16, require exact-head Rust 1.98.0 fmt/clippy/nextest/rus - Represented pair/Wide256 exact-ratio equivalence: `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` - Pairwise-f64-strict characterization: `2bc1d2284d75154e020640adb573c1cfadf005fb` - Non-minimum represented-anchor RED / repair: `fd9f9ff2c5c395e4cc13042232f4deef018adb48` / `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` -- Neutral-anchor RED / repair: `9f403194a2ec1636531c2dfe9229cfb34b73d747` / `6cf30eeb549c0df0377bda1111cf46396e8282a3` -- Narrow-wide-pair RED / repair: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` / `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` +- Neutral-anchor RED / correctness repair: `9f403194a2ec1636531c2dfe9229cfb34b73d747` / `6cf30eeb549c0df0377bda1111cf46396e8282a3` +- Neutral-zero route-order RED / production resource repair: `e0b324864e48a503e2aba0d2a487a0b95f5276ed` / `2b62bd46eb0c391327d2285c2244a76f5a1e0449` +- Narrow-wide-pair characterization RED / repair: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` / `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` - CHANGELOG fragment: `CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md` - Production module: `crates/validation_core/src/bias_se.rs` +- Route-order contract: `crates/validation_core/tests/bias_standard_error_neutral_zero_route_order_contract.rs` - Public regression: `crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs` - Exact-proof budget harness: `crates/validation_core/examples/bias_se_exact_proof_budget.rs` From 763eba8ff72c5469ef77511c993f9fdd8724e922 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 18:04:14 +0900 Subject: [PATCH 555/576] docs(changelog): record neutral-zero linear resource repair --- .../validation-bias-exact-proof-budget-characterization.md | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md index 69216b367..4b651aedc 100644 --- a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md +++ b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md @@ -11,4 +11,6 @@ - Add source-level RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` for residuals `[0,1,2,-2^53]`. The minimum residual cannot exactly translate `1`, but represented anchor `0` preserves every coordinate. Signed unit-one coordinates give `P=243388915243820099130562543878155`, so `SE(mean)^2=P/48`; the predecessor translated floating-moment path returns `0x4320000000000000` while exact rounding requires adjacent `0x4320000000000001`. Its Actions runs were cancelled by the immediate repair push and are not hosted RED evidence. - Initial production repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` keeps pairwise exact accumulation first and searches represented residual anchors. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` shows that observed anchors alone are incomplete: residuals `[1,2^-54,2,3]` have no observed universal exact anchor, while neutral dyadic anchor `0` preserves all coordinates. On unit `2^-54`, `P=6490371073168534319490338297741315`; exact rounding requires `0x3fe4a7e9cb8a3491`, while the predecessor translated floating-moment fallback returns adjacent `0x3fe4a7e9cb8a3492`. - Repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` includes neutral zero plus every represented residual in the deterministic exact-anchor candidate set, keeps the exact candidate with the smallest maximum translated magnitude and represented-value tie-break, accumulates signed dyadic coordinates, computes `n*Σc_i²-(Σc_i)²` with `Wide256` cancellation products, and reuses the exact candidate/midpoint tie-to-even rounder. Forward and permuted public fixtures require bit-identical results. -- Keep production `bias_standard_error` sample admission unchanged at `n=4..=16`. A budget change beyond 16 still requires pair/anchor same-domain equivalence plus intended anchor-only admissions, recorded Rust 1.98.0 release-mode CPU/raw CSV and allocator/RSS evidence, applicable buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch/security/documentation GREEN, current TRACEABILITY/research/operator doctoring, and qualifying independent current-head review. +- Source-level route-order RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` requires production to attempt `neutral_zero_linear` before conditioned observed-anchor and pairwise O(n²) proofs. Its superseding repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` adds a two-pass neutral-zero signed-dyadic/Wide256 kernel, O(n) in time with O(1) proof storage after the residual vector, and moves observed-anchor search and pairwise accumulation to fallbacks. The superseded RED did not finish a hosted failing run and is not claimed as hosted RED evidence. +- The resource repair keeps a common-domain neutral-zero/pairwise equality unit and the existing anchor-only regression geometries, but it does not yet promote full represented-input equivalence or a larger sample budget. The checked-in timing harness is still characterization tooling and does not yet constitute production route telemetry. +- Keep production `bias_standard_error` sample admission unchanged at `n=4..=16`. A budget change beyond 16 still requires broad pair/neutral-zero same-domain equivalence plus intended anchor-only admissions, truthful production route evidence, recorded Rust 1.98.0 release-mode CPU/raw CSV and allocator/RSS evidence, applicable buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch/security/documentation GREEN, current TRACEABILITY/research/operator doctoring, and qualifying independent current-head review. From 6ec2d31c963d3494380ab10ae8cf56950cc39a18 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 18:04:57 +0900 Subject: [PATCH 556/576] docs(test): require linear-first exact-proof route evidence --- docs/TEST_STRATEGY.md | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index 37aab2064..1a5cee063 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -69,12 +69,14 @@ Validation Evidence numerical proofs that add asymptotic work or material alloca - retain realistic represented-input counterexamples and permutation/sign-mirror contracts for scientific correctness; - keep the exact represented-residual gate, GCD reduction, exact candidate/midpoint authorization, and fail-closed fallback; -- treat exact pairwise-f64 subtraction as a first reference authority, not a scientific prerequisite. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` proves `[0,1,2^-54,2]` has exact anchor coordinates even though a non-anchor pair subtraction rounds; +- treat exact pairwise-f64 subtraction as a comparison/reference authority, not a scientific prerequisite or an unconditional first production route. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` proves `[0,1,2^-54,2]` has exact anchor coordinates even though a non-anchor pair subtraction rounds; - do not require the represented minimum to be the exact anchor. Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` uses `[0,1,2,-2^53]`: minimum `-2^53` cannot exactly translate `1`, while represented anchor `0` preserves all coordinates. Exact `P=243388915243820099130562543878155`, denominator `48`, and public result `0x4320000000000001` differ by one ULP from the predecessor fallback; - do not restrict production exact anchors to observed residuals. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` uses `[1,2^-54,2,3]`, where no observed residual exactly translates every other residual, but neutral dyadic anchor `0` preserves all. On unit `2^-54`, exact `P=6490371073168534319490338297741315`; exact result `0x3fe4a7e9cb8a3491` differs by one ULP from predecessor fallback `0x3fe4a7e9cb8a3492`; -- retain production repair `6cf30eeb549c0df0377bda1111cf46396e8282a3`: pairwise proof stays first; fallback exact-anchor candidates are neutral `0` plus every represented residual; every candidate must preserve all translated coordinates; choose the smallest exact maximum translated magnitude with represented-value tie-break; accumulate signed dyadic coordinates; form `n*Σc_i²-(Σc_i)²` with `Wide256`; downcast only a bounded exact numerator; reuse exact candidate/midpoint tie-to-even rounding. Test forward and permuted observation order bit-for-bit; -- preserve pair-versus-anchor equality wherever both admit and explicitly test intended anchor-only admissions. A broader exact-anchor set must not narrow existing pair admissions or depend on row arrival order; -- neutral zero is a translation origin, not synthetic evidence: the observed residual values are unchanged. Do not claim zero-plus-observed anchors are globally resource-optimal without a separate proof; +- retain the correctness lineage through repair `6cf30eeb549c0df0377bda1111cf46396e8282a3`, but do not retain its quadratic route order as production policy. Source-level route-order RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` requires `neutral_zero_linear -> conditioned_observed_anchor -> pairwise_reference`; repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` implements a two-pass neutral-zero signed-dyadic/Wide256 proof first, keeps observed-anchor search only as a bounded dynamic-range fallback, and moves pairwise accumulation last. The superseded RED did not finish a hosted failing run and must not be cited as hosted RED evidence; +- test the neutral-zero kernel as O(n) in loop structure and O(1) in *proof* storage after the already-required residual vector. Do not describe the whole public call as O(1) space while it still materializes residuals; +- preserve pair-versus-neutral-zero equality wherever both admit and explicitly test intended anchor-only admissions. Broaden deterministic represented-input equality beyond one common-domain unit test before promotion. Test forward/reversed/permuted observation order bit-for-bit and include signed coordinates; +- retain conditioned observed-anchor fallback tests that demonstrate an exact translated origin can recover a bounded zero-origin refusal; do not keep O(n²) anchor search merely by assumption; +- neutral zero is a translation origin, not synthetic evidence: the observed residual values are unchanged. Do not claim zero or observed anchors are globally resource-optimal without a separate proof; - compare buffered O(n²), allocation-free two-pass O(n²), normalized narrow O(n), dependency-free Wide256 O(n), predecessor narrow→pair hybrid, and narrow→Wide256→pair candidate resource shapes before changing the sample budget; - normalize the common power-of-two dyadic unit before narrow checked O(n) intermediates are judged. Raw `D=2^58,n=65` refusal is invalid after normalization; odd `D=2^58+1,n=65` remains the narrow 129-bit product witness; - retain the earlier nonnegative minimum-anchor accumulator theorem `b7e4da353ac58069afd73ee7c0e8427d49993fdb`, `Σc_i <= Σc_i² <= P`, only within that characterized representation. Production exact-anchor coordinates may be signed, so positive and negative coefficient mass and resulting signed sum must be exercised separately; @@ -85,12 +87,12 @@ Validation Evidence numerical proofs that add asymptotic work or material alloca - retain represented pair/Wide256 exact-ratio equivalence `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` at `n=4,16,17,65,257,2050`, while recognizing that its minimum-anchor rule is scoped to that input family rather than universal production canonicalization; - keep normalized O(n) intermediate, exact pair-numerator, and denominator envelopes separate. At aligned diameter `2^53`, `n<=2_047`, `n<=4_095`, and `n<=208_064` are arithmetic evidence points, not production budgets; - record exact pair counts, target `size_of::>()`, scratch `Vec` capacity/payload, and allocator/RSS evidence separately; field-width estimates are not allocation evidence; -- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing and record `used_wide_product` and `used_pairwise_fallback` independently; -- run the characterization harness in release mode on recorded CPU/OS/Rust 1.98.0 and retain raw CSV plus p95. Unexecuted harness code is not performance evidence; +- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing, but do not mislabel that characterization harness as production route telemetry. Production evidence must distinguish at least `neutral_zero_linear`, `conditioned_observed_anchor`, `pairwise_reference`, and `generic_fallback`; +- run the characterization and surviving production-route harness in release mode on recorded CPU/OS/Rust 1.98.0 and retain raw CSV plus p95. Unexecuted harness code is not performance evidence; - if a service/API buyer path is affected, measure the complete applicable path against `p95 <= 20 ms` without shrinking samples, omitting proof work, or using unrealistic warm-cache-only setup; -- arithmetic representability or one successful anchor does not authorize a production sample-count budget. +- arithmetic representability, one successful anchor, or one common-domain equality fixture does not authorize a production sample-count budget. -Until same-head correctness, exact-head gates, independent review, and resource measurements exist, production bias-SE exact admission remains `n=4..=16`. +Until broad same-head correctness, exact-head gates, independent review, and resource measurements exist, production bias-SE exact admission remains `n=4..=16`. ## Release acceptance From 5a27c64dcac6d37bb1e576351c52144922bc1f4d Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 18:05:39 +0900 Subject: [PATCH 557/576] docs(operability): adopt linear-first exact-proof route --- docs/OPERABILITY.md | 22 ++++++++++++++-------- 1 file changed, 14 insertions(+), 8 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index da3487bee..84bc7568c 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -56,20 +56,26 @@ Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` establishes the first publ Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` shows that observed anchors alone are operationally incomplete. Residuals `[1,2^-54,2,3]` have no observed universal exact anchor, but neutral dyadic anchor `0` preserves every residual exactly. On unit `2^-54`, exact `P=6490371073168534319490338297741315`; exact result `0x3fe4a7e9cb8a3491` differs by one ULP from the predecessor fallback `0x3fe4a7e9cb8a3492`. -Repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` keeps pairwise exact proof first, then considers neutral zero plus every represented residual as exact-anchor candidates. It requires error-free coordinates, chooses the smallest exact maximum translated magnitude with represented-value tie-break, builds a common dyadic grid, tracks positive and negative coefficient mass separately, performs `n*Σc_i²-(Σc_i)²` with exact `Wide256` products/subtraction, downcasts only a bounded final numerator, and reuses exact candidate/midpoint tie-to-even rounding. Neutral zero is a deterministic translation origin, not a synthetic observation; source residuals remain unchanged. +Correctness repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` established neutral zero as an admissible exact translation origin, but its production order still paid pairwise O(n²) first and then O(n²) anchor search. Source-level route-order RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` requires `neutral_zero_linear -> conditioned_observed_anchor -> pairwise_reference`. Repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` implements that sequence. + +The neutral-zero kernel scans residuals once to determine the common dyadic exponent and once more to accumulate positive/negative coefficient mass and `Σc_i²`; it then computes `n*Σc_i²-(Σc_i)²` with exact `Wide256` products/subtraction. This is O(n) time with O(1) proof storage after the already-required residual vector and allocates no pair records. If the bounded zero-origin integer representation refuses, observed-anchor search remains an O(n²) conditioned fallback that may reduce coordinate dynamic range. Pairwise O(n²) now runs last as comparison/fail-closed authority while broader represented-input equivalence remains under validation. + +The superseded route-order RED did not finish a hosted failing run, so it is source-level TDD evidence only. The current source adds a common-domain neutral-zero/pairwise equality unit and preserves the anchor-only public regressions; that is not yet full bounded-domain equivalence. Operator implications: -- do not diagnose pairwise-f64, minimum-anchor, or observed-anchor refusal as scientific invalidity when the neutral zero translation remains exact and the bounded dyadic proof admits it; -- do not make row order part of anchor selection; forward/reversed/permuted fixtures must be bit-identical; -- keep observed anchors because they can reduce exact dynamic range relative to zero; zero is the deterministic completeness fallback for the demonstrated represented-domain defect, not a claim of global resource-optimality; -- treat signed-coordinate accumulation, Wide256 subtraction/downcast, denominator reduction, or exact-rounding failure as a fail-closed proof refusal and use the established generic fallback rather than weakening checks; -- keep pairwise exact proof as the first authority while exact-anchor coverage matures; -- separate route observability from numerical equality. The resource harness records `used_wide_product` and `used_pairwise_fallback` independently; +- attempt the neutral-zero linear proof before quadratic proof work for the current bounded production route; +- do not diagnose pairwise-f64, minimum-anchor, or observed-anchor refusal as scientific invalidity when neutral-zero or a conditioned exact anchor admits the bounded dyadic proof; +- keep observed anchors only as a demonstrated dynamic-range recovery fallback and require a fixture that proves such recovery; do not retain O(n²) search merely by assumption; +- do not make row order part of proof semantics; forward/reversed/permuted fixtures must be bit-identical; +- treat signed-coordinate accumulation, Wide256 subtraction/downcast, denominator reduction, or exact-rounding failure as a fail-closed proof refusal and use later proof/fallback routes rather than weakening checks; +- keep pairwise proof as the comparison/fail-closed authority, not as unconditional first work; +- separate route observability from numerical equality. The existing resource harness records `used_wide_product` and `used_pairwise_fallback`, but production evidence still must distinguish `neutral_zero_linear`, `conditioned_observed_anchor`, `pairwise_reference`, and `generic_fallback`; +- describe storage precisely: the new kernel uses O(1) proof storage after the residual vector, while the public exact path still materializes O(n) residual storage; - treat `n=2_047`, `4_095`, and `208_064` only as arithmetic envelope markers from older aligned characterizations, not service limits; - before widening beyond 16, retain raw Rust 1.98.0 `--release` timing CSV, CPU/OS/build flags, p95, actual scratch capacity/payload, allocator/RSS, and any applicable buyer-path `p95<=20 ms` evidence without sample shrinkage or omitted proof work. -Exact pair-record counts remain 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2048`, and 4,997,541 at `n=3162`. A two-pass O(n²) implementation may remove pair-record storage but still requires exact-head scientific/resource evidence. A Wide256 O(n) implementation may remove both pair storage and pair enumeration for admitted geometries but still requires pair-equivalence, anchor-only admission, exact-rounding, refusal, and resource evidence on one surviving production head. +Exact pair-record counts remain 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2048`, and 4,997,541 at `n=3162`. A two-pass O(n²) implementation may remove pair-record storage but still requires exact-head scientific/resource evidence. The neutral-zero Wide256 O(n) implementation removes pair enumeration for admitted geometries but still requires broad pair-equivalence, conditioned-anchor admission, exact-rounding, refusal, route, and resource evidence on one surviving production head. No release-mode resource numbers are authoritative yet. Source-level RED runs that were superseded or cancelled are not counted as hosted RED. Predecessor Rustfmt artifact `9982621569` from `1f765a...` is not current-head formatting evidence. From 3c699267916c182e8a0c1b2bded57b51192bb09a Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 19:04:09 +0900 Subject: [PATCH 558/576] test(validation): document extreme decision contracts --- .../tests/match_count_extreme_decision_contract.rs | 5 +++++ ...ll_covered_exact_count_extreme_scale_rounding_contract.rs | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/crates/validation_core/tests/match_count_extreme_decision_contract.rs b/crates/validation_core/tests/match_count_extreme_decision_contract.rs index cd44033fc..2d917701b 100644 --- a/crates/validation_core/tests/match_count_extreme_decision_contract.rs +++ b/crates/validation_core/tests/match_count_extreme_decision_contract.rs @@ -1,3 +1,8 @@ +//! Regression contract for tolerance decisions when the represented residual itself overflows. +//! +//! `match_count` must decide directly from finite represented endpoints and tolerance instead of +//! requiring `absolute_residuals` to materialize an unrepresentable magnitude. + use validation_core::{ValidationError, absolute_residuals, match_count}; #[test] diff --git a/crates/validation_core/tests/wilson_all_covered_exact_count_extreme_scale_rounding_contract.rs b/crates/validation_core/tests/wilson_all_covered_exact_count_extreme_scale_rounding_contract.rs index 30f8ff6f5..468380a86 100644 --- a/crates/validation_core/tests/wilson_all_covered_exact_count_extreme_scale_rounding_contract.rs +++ b/crates/validation_core/tests/wilson_all_covered_exact_count_extreme_scale_rounding_contract.rs @@ -1,3 +1,8 @@ +//! Regression contract for all-covered Wilson evidence at an extreme finite critical value. +//! +//! A finite-count correction below the final binary64 half-ULP must not force the lower endpoint +//! away from the correctly rounded represented-input quotient. + use validation_core::wilson_coverage_interval; #[test] From 456a475264356046954b614233e3cb20c600c651 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 20:06:49 +0900 Subject: [PATCH 559/576] test(validation): document subnormal and Wilson evidence contracts --- ...e_sign_subnormal_double_rounding_contract.rs | 12 +++++++++++- .../wilson_coverage_evidence_v1_contract.rs | 17 +++++++++++++++-- 2 files changed, 26 insertions(+), 3 deletions(-) diff --git a/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs b/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs index 15f053651..57aaf0d12 100644 --- a/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs +++ b/crates/validation_core/tests/bias_same_sign_subnormal_double_rounding_contract.rs @@ -1,3 +1,10 @@ +//! Contracts exact binary64 mean-bias rounding for same-sign subnormal inputs. +//! +//! These cases guard the final represented-value decision against double rounding: +//! accumulation may cross the normal/subnormal boundary or land exactly halfway +//! between subnormal units, so the public metric must round once at the final scale +//! with IEEE 754 ties-to-even semantics. + use validation_core::mean_bias; #[test] @@ -19,7 +26,10 @@ fn mean_bias_does_not_double_round_same_sign_subnormal_mean() { let mirrored: Vec<_> = recovered.iter().map(|value| -*value).collect(); let mirrored_bias = mean_bias(&truth, &mirrored).expect("mirrored subnormal mean bias"); - assert_eq!(mirrored_bias.to_bits(), (1_u64 << 63) | (minimum_normal_units - 21)); + assert_eq!( + mirrored_bias.to_bits(), + (1_u64 << 63) | (minimum_normal_units - 21) + ); } #[test] diff --git a/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs b/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs index e5f571edf..d216bd060 100644 --- a/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs +++ b/crates/validation_core/tests/wilson_coverage_evidence_v1_contract.rs @@ -1,3 +1,10 @@ +//! Contracts the versioned Wilson coverage-evidence envelope used for validation receipts. +//! +//! The v1 schema binds sample and covered counts, the configured standard-normal +//! critical value, empirical coverage, and both Wilson endpoints. Deserialization +//! and re-validation must fail closed when those fields disagree, when unsupported +//! semantics are introduced, or when count/probability domains are impossible. + use validation_core::{ValidationError, WilsonCoverageEvidenceV1}; fn canonical_evidence() -> WilsonCoverageEvidenceV1 { @@ -57,7 +64,10 @@ fn tampered_denominator_critical_value_or_endpoint_fails_closed() { let mut wrong_coverage = evidence; wrong_coverage.empirical_coverage = 0.5; - assert_eq!(wrong_coverage.validate(), Err(ValidationError::InvalidInput)); + assert_eq!( + wrong_coverage.validate(), + Err(ValidationError::InvalidInput) + ); } #[test] @@ -90,7 +100,10 @@ fn impossible_counts_and_numeric_domains_fail_closed() { let mut impossible_counts = evidence; impossible_counts.covered_count = impossible_counts.sample_count + 1; - assert_eq!(impossible_counts.validate(), Err(ValidationError::InvalidInput)); + assert_eq!( + impossible_counts.validate(), + Err(ValidationError::InvalidInput) + ); for invalid_z in [0.0, -1.0, f64::NAN, f64::INFINITY, 1e200] { let mut invalid = evidence; From 69b8e8e8cd6a2b34353ae8e39d732d87abce84df Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 21:01:22 +0900 Subject: [PATCH 560/576] test(validation): document rational and extreme Wilson contracts --- ...error_two_level_rational_scale_rounding_contract.rs | 10 ++++++++-- ...son_all_covered_inexact_count_extreme_z_contract.rs | 7 +++++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_two_level_rational_scale_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_two_level_rational_scale_rounding_contract.rs index 84f1f061c..9150debec 100644 --- a/crates/validation_core/tests/bias_standard_error_two_level_rational_scale_rounding_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_two_level_rational_scale_rounding_contract.rs @@ -1,3 +1,9 @@ +//! Contracts exact rounding for two-level represented residuals with a rational count factor. +//! +//! The 2-versus-8 split at n=10 has SE(mean)^2 = 4*gap^2/225, so the public result +//! depends on preserving the exact rational square through the final sqrt/rounding decision. +//! The contract also fixes permutation and sign symmetry and keeps underflow fail-closed. + use validation_core::{ValidationError, bias_standard_error}; #[test] @@ -7,8 +13,8 @@ fn bias_standard_error_preserves_exact_rational_square_two_level_geometry() { 0.0, 0.0, repeated, repeated, repeated, repeated, repeated, repeated, repeated, repeated, ]; - let standard_error = bias_standard_error(&[0.0; 10], &recovered) - .expect("represented-input standard error"); + let standard_error = + bias_standard_error(&[0.0; 10], &recovered).expect("represented-input standard error"); // With two observations at one exact residual level and eight at the other, // m(n-m)/(n^2(n-1)) = 2*8/(10^2*9) = 4/225. Therefore SE(mean) is exactly // 2*|gap|/15. GAP-103 admits only reciprocal-integer-square count factors, diff --git a/crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs b/crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs index 387725dd3..923186007 100644 --- a/crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs +++ b/crates/validation_core/tests/wilson_all_covered_inexact_count_extreme_z_contract.rs @@ -1,3 +1,10 @@ +//! Contracts all-covered Wilson evidence when durable counts exceed exact binary64 integers. +//! +//! A u64 sample count above 2^53 must retain its exact provenance while endpoint arithmetic +//! remains correctly rounded at extreme critical-value scales. The contract prevents +//! complementary-mass cancellation from erasing a positive lower endpoint and prevents +//! subnormal squared-z scales from inventing non-representable uncertainty. + use validation_core::WilsonCoverageEvidenceV1; #[test] From 123af90a31775e0e86ad392e857431fb117b7f76 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 22:06:12 +0900 Subject: [PATCH 561/576] test(validation): document repeated-level and percentile support contracts --- ..._standard_error_repeated_level_rounding_contract.rs | 10 ++++++++-- ...e_carlo_percentile_joint_moment_support_contract.rs | 6 ++++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_repeated_level_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_repeated_level_rounding_contract.rs index d42c87aa7..f210f8505 100644 --- a/crates/validation_core/tests/bias_standard_error_repeated_level_rounding_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_repeated_level_rounding_contract.rs @@ -1,3 +1,9 @@ +//! Contracts exact bias-standard-error rounding for a repeated represented residual level. +//! +//! The three-observation geometry fixes the exact pairwise second moment when two observations +//! share one residual level. Forward, permuted, and sign-mirrored inputs must therefore produce +//! the same correctly rounded public standard error rather than depend on accumulation order. + use validation_core::bias_standard_error; #[test] @@ -5,8 +11,8 @@ fn bias_standard_error_preserves_three_observation_repeated_level_identity() { let repeated = f64::from_bits(0x3fef_ffff_ffff_ffff); let recovered = [0.0, repeated, repeated]; - let standard_error = bias_standard_error(&[0.0; 3], &recovered) - .expect("represented-input standard error"); + let standard_error = + bias_standard_error(&[0.0; 3], &recovered).expect("represented-input standard error"); // For exactly represented residuals [0, a, a], the three-observation // standard error simplifies algebraically to |a| / 3. The predecessor // squared the normalized a values, formed the second moment, and then took diff --git a/crates/validation_core/tests/monte_carlo_percentile_joint_moment_support_contract.rs b/crates/validation_core/tests/monte_carlo_percentile_joint_moment_support_contract.rs index 903315a9e..49071dbda 100644 --- a/crates/validation_core/tests/monte_carlo_percentile_joint_moment_support_contract.rs +++ b/crates/validation_core/tests/monte_carlo_percentile_joint_moment_support_contract.rs @@ -1,3 +1,9 @@ +//! Contracts joint moment support for serialized Monte Carlo percentile evidence. +//! +//! Distinct empirical percentile endpoints consume the same finite squared-deviation budget +//! recorded by the sample standard deviation; validating each endpoint independently is not +//! sufficient. Equal endpoints may refer to one retained observation and must not be charged twice. + use validation_core::{MonteCarloSummary, ValidationError}; fn summary(percentile_lower: f64, percentile_upper: f64) -> MonteCarloSummary { From d40bfbf98b36562164d15d505f8f8825fd1c1349 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 22:39:46 +0900 Subject: [PATCH 562/576] test(validation): require evidence before conditioned anchor fallback --- ...error_neutral_zero_route_order_contract.rs | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_neutral_zero_route_order_contract.rs b/crates/validation_core/tests/bias_standard_error_neutral_zero_route_order_contract.rs index 0633578d6..df1e6ece7 100644 --- a/crates/validation_core/tests/bias_standard_error_neutral_zero_route_order_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_neutral_zero_route_order_contract.rs @@ -1,11 +1,11 @@ #![forbid(unsafe_code)] -//! Guards the exact bias-SE proof route against reintroducing quadratic work ahead -//! of the neutral-zero linear kernel. +//! Guards the exact bias-SE proof route against quadratic work without demonstrated +//! represented-input admission value. const SOURCE: &str = include_str!("../src/bias_se.rs"); #[test] -fn neutral_zero_linear_proof_precedes_quadratic_fallbacks() { +fn neutral_zero_linear_proof_precedes_only_the_pairwise_reference() { let start = SOURCE .find("fn exact_pair_distance_standard_error(") .expect("production exact-proof entrypoint must exist"); @@ -18,15 +18,16 @@ fn neutral_zero_linear_proof_precedes_quadratic_fallbacks() { let neutral_zero = route .find("exact_neutral_zero_linear_pair_square_sum(&residuals)") .expect("neutral-zero O(n) proof must be a production route"); - let conditioned_anchor = route - .find("exact_anchor_linear_pair_square_sum(&residuals)") - .expect("conditioned observed-anchor fallback must remain available"); let pairwise = route .find("exact_pairwise_pair_square_sum(&residuals)") - .expect("pairwise reference fallback must remain available"); + .expect("pairwise fail-closed reference must remain available"); assert!( - neutral_zero < conditioned_anchor && conditioned_anchor < pairwise, - "production proof order must be neutral-zero linear -> conditioned anchor -> pairwise reference" + !route.contains("exact_anchor_linear_pair_square_sum(&residuals)"), + "do not retain an O(n²) conditioned-anchor scan without a represented fixture that uniquely recovers a neutral-zero refusal" + ); + assert!( + neutral_zero < pairwise, + "production proof order must be neutral-zero linear -> pairwise reference" ); } From 14e7862f4ddccce54f3b93d4dac89adbf047ba77 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Sun, 6 Sep 2026 22:48:47 +0900 Subject: [PATCH 563/576] fix(validation): remove unproven conditioned anchor fallback --- crates/validation_core/src/bias_se.rs | 153 +++++--------------------- 1 file changed, 28 insertions(+), 125 deletions(-) diff --git a/crates/validation_core/src/bias_se.rs b/crates/validation_core/src/bias_se.rs index 0aa882304..87dc38331 100644 --- a/crates/validation_core/src/bias_se.rs +++ b/crates/validation_core/src/bias_se.rs @@ -2,8 +2,8 @@ //! //! The general bias implementation remains the fallback authority. This module //! admits a bounded small-sample exact pair-distance identity when represented -//! residuals admit a neutral-zero linear proof, a conditioned exact observed -//! anchor, or error-free pairwise differences; the exact rational square root is +//! residuals admit a neutral-zero linear proof or error-free pairwise differences; +//! the exact rational square root is //! then rounded against binary64 midpoints without first rounding the ratio under //! the square root. @@ -111,11 +111,7 @@ impl Wide256 { } const fn to_u128(self) -> Option { - if self.high == 0 { - Some(self.low) - } else { - None - } + if self.high == 0 { Some(self.low) } else { None } } const fn is_zero(self) -> bool { @@ -155,10 +151,8 @@ fn compare_scaled_wide( let left_bits = left.bit_len(); let right_bits = right.bit_len(); - let left_top = left_exponent - .checked_add(i32::try_from(left_bits.checked_sub(1)?).ok()?)?; - let right_top = right_exponent - .checked_add(i32::try_from(right_bits.checked_sub(1)?).ok()?)?; + let left_top = left_exponent.checked_add(i32::try_from(left_bits.checked_sub(1)?).ok()?)?; + let right_top = right_exponent.checked_add(i32::try_from(right_bits.checked_sub(1)?).ok()?)?; match left_top.cmp(&right_top) { Ordering::Less => return Some(Ordering::Less), Ordering::Greater => return Some(Ordering::Greater), @@ -298,7 +292,10 @@ fn correctly_rounded_scaled_sqrt_ratio( } fn exact_pairwise_pair_square_sum(residuals: &[f64]) -> Option<(u128, i32)> { - let pair_count = residuals.len().checked_mul(residuals.len().checked_sub(1)?)? / 2; + let pair_count = residuals + .len() + .checked_mul(residuals.len().checked_sub(1)?)? + / 2; let mut pair_dyadics = Vec::with_capacity(pair_count); let mut unit_exponent = i32::MAX; for left in 0..residuals.len() { @@ -375,82 +372,6 @@ fn exact_neutral_zero_linear_pair_square_sum(residuals: &[f64]) -> Option<(u128, Some((numerator, unit_exponent)) } -fn exact_anchor_linear_pair_square_sum(residuals: &[f64]) -> Option<(u128, i32)> { - // Neutral zero is handled by the O(n) fast route. Search represented residuals - // only when translating by an observed anchor can reduce the dyadic dynamic - // range enough to recover a bounded proof that neutral zero refused. The final - // total-order tie-break keeps selection independent of observation order. - let mut best: Option<(f64, f64, Vec)> = None; - for anchor in residuals.iter().copied() { - let mut translated = Vec::with_capacity(residuals.len()); - let mut max_magnitude = 0.0_f64; - let mut exact = true; - for &residual in residuals { - let coordinate = residual - anchor; - if !coordinate.is_finite() - || subtraction_roundoff(residual, anchor, coordinate) != 0.0 - { - exact = false; - break; - } - max_magnitude = max_magnitude.max(coordinate.abs()); - translated.push(coordinate); - } - if !exact { - continue; - } - - let should_replace = match &best { - None => true, - Some((best_max_magnitude, best_anchor, _)) => max_magnitude - .total_cmp(best_max_magnitude) - .then_with(|| anchor.total_cmp(best_anchor)) - .is_lt(), - }; - if should_replace { - best = Some((max_magnitude, anchor, translated)); - } - } - let (_, _, translated) = best?; - - let mut dyadics = Vec::with_capacity(translated.len()); - let mut unit_exponent = i32::MAX; - for &coordinate in &translated { - if coordinate == 0.0 { - dyadics.push(None); - continue; - } - let dyadic = positive_dyadic(coordinate.abs())?; - unit_exponent = unit_exponent.min(dyadic.1); - dyadics.push(Some((coordinate.is_sign_negative(), dyadic))); - } - if unit_exponent == i32::MAX { - return Some((0, 0)); - } - - let mut positive_sum = 0_u128; - let mut negative_sum = 0_u128; - let mut square_sum = 0_u128; - for dyadic in dyadics.into_iter().flatten() { - let (negative, (significand, exponent)) = dyadic; - let shift = exponent.checked_sub(unit_exponent)?.unsigned_abs(); - let coefficient = multiply_by_power_of_two(significand, shift)?; - if negative { - negative_sum = negative_sum.checked_add(coefficient)?; - } else { - positive_sum = positive_sum.checked_add(coefficient)?; - } - square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; - } - - let sample_count = u128::try_from(residuals.len()).ok()?; - let scaled_square_sum = Wide256::multiply_u128(sample_count, square_sum); - let signed_sum_magnitude = positive_sum.abs_diff(negative_sum); - let squared_sum = Wide256::multiply_u128(signed_sum_magnitude, signed_sum_magnitude); - let numerator = scaled_square_sum.checked_sub(squared_sum)?.to_u128()?; - Some((numerator, unit_exponent)) -} - fn exact_pair_distance_standard_error( truth: &[f64], recovered: &[f64], @@ -482,12 +403,11 @@ fn exact_pair_distance_standard_error( // Attempt the neutral-zero two-pass proof first: finite represented residuals // are exact coordinates around zero, so this is O(n) with O(1) proof storage. - // If its bounded integer coordinate range refuses, search exact observed anchors - // that may reduce that range. Keep pairwise O(n²) last as a comparison and - // fail-closed reference while admission equivalence and release-mode budgets are - // still being characterized. + // Keep pairwise O(n²) only as a fail-closed comparison reference while broader + // represented-input equivalence and release-mode budgets are characterized. + // The former conditioned observed-anchor scan was removed because no production + // fixture demonstrated unique admission after the neutral-zero proof refused. let (pair_square_sum, unit_exponent) = exact_neutral_zero_linear_pair_square_sum(&residuals) - .or_else(|| exact_anchor_linear_pair_square_sum(&residuals)) .or_else(|| exact_pairwise_pair_square_sum(&residuals))?; if pair_square_sum == 0 { return Some(Ok(0.0)); @@ -510,21 +430,18 @@ fn exact_pair_distance_standard_error( } let reduced_numerator = pair_square_sum / divisor_left; let reduced_denominator = denominator / divisor_left; - let standard_error = correctly_rounded_scaled_sqrt_ratio( - reduced_numerator, - reduced_denominator, - unit_exponent, - )?; + let standard_error = + correctly_rounded_scaled_sqrt_ratio(reduced_numerator, reduced_denominator, unit_exponent)?; Some(Ok(standard_error)) } /// Standard error of mean signed bias. /// /// Four- through sixteen-observation samples first attempt an exact neutral-zero -/// linear proof, then a conditioned exact observed-anchor translation, and finally -/// the pairwise-difference reference when the earlier bounded proofs refuse. Each -/// admitted route uses the same exact pair-distance identity and exact dyadic -/// midpoint rounding. All other samples retain the established bias implementation +/// linear proof and then the pairwise-difference reference when the bounded linear +/// proof refuses. Each admitted route uses the same exact pair-distance identity +/// and exact dyadic midpoint rounding. All other samples retain the established +/// bias implementation /// and its existing fail-closed behavior. pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result { if let Some(result) = exact_pair_distance_standard_error(truth, recovered) { @@ -536,10 +453,10 @@ pub fn bias_standard_error(truth: &[f64], recovered: &[f64]) -> Result Date: Sun, 6 Sep 2026 23:02:13 +0900 Subject: [PATCH 564/576] test(validation): retire stale anchor terminology --- .../bias_standard_error_anchor_conditioning_contract.rs | 8 +++++++- .../bias_standard_error_anchor_permutation_contract.rs | 8 +++++++- 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_anchor_conditioning_contract.rs b/crates/validation_core/tests/bias_standard_error_anchor_conditioning_contract.rs index 0fcc5fb08..ee9a0e58c 100644 --- a/crates/validation_core/tests/bias_standard_error_anchor_conditioning_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_anchor_conditioning_contract.rs @@ -1,3 +1,9 @@ +//! Regression contract for correctly rounded three-observation bias standard error. +//! +//! The historical filename predates removal of the conditioned-anchor production +//! route. The durable contract is order and sign invariance for represented +//! three-observation inputs handled by the established small-sample authority. + use validation_core::bias_standard_error; const EXPECTED_STANDARD_ERROR_BITS: u64 = 0x3fd1_7a99_c875_b980; @@ -11,7 +17,7 @@ fn represented_sample() -> [f64; 3] { } #[test] -fn exact_translated_anchor_conditioning_preserves_correct_rounding() { +fn represented_three_observation_rounding_is_order_and_sign_invariant() { let [middle, low, high] = represented_sample(); let truth = [0.0; 3]; let permutations = [ diff --git a/crates/validation_core/tests/bias_standard_error_anchor_permutation_contract.rs b/crates/validation_core/tests/bias_standard_error_anchor_permutation_contract.rs index 9c309a80c..5f9a54412 100644 --- a/crates/validation_core/tests/bias_standard_error_anchor_permutation_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_anchor_permutation_contract.rs @@ -1,7 +1,13 @@ +//! Regression contract for permutation and sign invariance of the three-sample metric. +//! +//! The historical filename references an earlier anchor implementation. The +//! scientific invariant is that reordering or mirroring the represented sample +//! cannot change the correctly rounded standard error. + use validation_core::bias_standard_error; #[test] -fn bias_standard_error_is_invariant_to_the_exact_translation_anchor() { +fn three_observation_standard_error_is_permutation_and_sign_invariant() { let low = f64::from_bits(0x4194_f788_9184_b980); let middle = f64::from_bits(0x420c_409f_fce3_8390); let high = f64::from_bits(0x4222_70c4_634c_c6b6); From c4ccda1e4c36fe63d3786f8d7646c315ef79c207 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 00:03:19 +0900 Subject: [PATCH 565/576] test(validation): document reduced-ratio rounding contracts --- ...ror_four_observation_reduced_ratio_contract.rs | 8 ++++++++ ...hree_level_rational_scale_rounding_contract.rs | 15 +++++++++++++-- 2 files changed, 21 insertions(+), 2 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_four_observation_reduced_ratio_contract.rs b/crates/validation_core/tests/bias_standard_error_four_observation_reduced_ratio_contract.rs index 2a3932c34..628f47227 100644 --- a/crates/validation_core/tests/bias_standard_error_four_observation_reduced_ratio_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_four_observation_reduced_ratio_contract.rs @@ -1,3 +1,11 @@ +//! Exact reduced-ratio contract for the four-observation bias standard error. +//! +//! This geometry makes the exact pair-distance numerator and denominator share a +//! factor of four. Reducing that ratio before the bounded square-root proof is +//! required to reach the correctly rounded binary64 result; the unreduced +//! fallback lands one ULP low. Permutation and sign changes must preserve the +//! same public metric bits. + use validation_core::bias_standard_error; fn assert_reduced_ratio_contract(recovered: [f64; 4]) { diff --git a/crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs b/crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs index ff4b35a97..9eed3618c 100644 --- a/crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_three_level_rational_scale_rounding_contract.rs @@ -1,3 +1,10 @@ +//! Correct-rounding contract for a represented three-level rational scale. +//! +//! The sample has an exact dyadic spacing whose standard-error scale reduces to +//! a non-dyadic rational before the final square root. The public result must be +//! rounded once to the expected binary64 value and remain bit-identical under +//! every permutation and sign reflection of the represented observations. + use validation_core::bias_standard_error; const EXPECTED_STANDARD_ERROR_BITS: u64 = 0x3f79_5555_5555_5555; @@ -24,12 +31,16 @@ fn exact_three_level_rational_scale_preserves_correct_rounding() { ]; for recovered in permutations { - let standard_error = bias_standard_error(&truth, &recovered).expect("finite standard error"); + let standard_error = + bias_standard_error(&truth, &recovered).expect("finite standard error"); assert_eq!(standard_error.to_bits(), EXPECTED_STANDARD_ERROR_BITS); let mirrored = recovered.map(|value| -value); let mirrored_standard_error = bias_standard_error(&truth, &mirrored).expect("finite mirrored standard error"); - assert_eq!(mirrored_standard_error.to_bits(), EXPECTED_STANDARD_ERROR_BITS); + assert_eq!( + mirrored_standard_error.to_bits(), + EXPECTED_STANDARD_ERROR_BITS + ); } } From da703fc50e136ed79e12262909c9f400a3945621 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 00:15:31 +0900 Subject: [PATCH 566/576] test(validation): align route equivalence with neutral-zero production proof --- ...nted_route_equivalence_characterization.rs | 126 ++++++++++++------ 1 file changed, 85 insertions(+), 41 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs b/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs index 593e2e2cc..547e831b7 100644 --- a/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs +++ b/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs @@ -1,9 +1,12 @@ -//! Characterizes represented-input equivalence between pairwise and wide-linear bias-SE proofs. +//! Characterizes represented-input equivalence between pairwise and production neutral-zero bias-SE proofs. //! -//! This is test-only evidence for issue #491. It deliberately leaves production -//! admission at `n=4..=16`: the pairwise O(n²) proof remains authoritative until -//! represented-input route equivalence, exact rounding, resource evidence, and -//! protected-head quality gates are all satisfied. +//! This is test-only evidence for issue #491. Production admission remains +//! `n=4..=16`: the neutral-zero O(n) proof is primary, while pairwise O(n²) +//! remains a fail-closed comparison/reference path until represented-input +//! equivalence, exact rounding, resource evidence, and protected-head quality +//! gates are all satisfied. + +use validation_core::bias_standard_error; #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] struct Wide256 { @@ -144,45 +147,51 @@ fn pairwise_exact_numerator(values: &[f64]) -> Option<(u128, i32)> { Some((pair_square_sum, unit_exponent)) } -fn wide_linear_exact_numerator(values: &[f64]) -> Option<(u128, i32, u128, u128)> { - let anchor = values - .iter() - .copied() - .min_by(f64::total_cmp)?; - let mut dyadics = Vec::with_capacity(values.len()); +fn neutral_zero_linear_exact_numerator(values: &[f64]) -> Option<(u128, i32, u128, u128)> { let mut unit_exponent = i32::MAX; - - for value in values.iter().copied() { - let difference = value - anchor; - if !difference.is_finite() || subtraction_roundoff(value, anchor, difference) != 0.0 { - return None; - } - if difference == 0.0 { - dyadics.push(None); + for &coordinate in values { + if coordinate == 0.0 { continue; } - let dyadic = positive_dyadic(difference)?; - unit_exponent = unit_exponent.min(dyadic.1); - dyadics.push(Some(dyadic)); + let (_, exponent) = positive_dyadic(coordinate.abs())?; + unit_exponent = unit_exponent.min(exponent); } if unit_exponent == i32::MAX { return Some((0, 0, 0, 0)); } - let mut coefficient_sum = 0_u128; + let mut positive_sum = 0_u128; + let mut negative_sum = 0_u128; let mut square_sum = 0_u128; - for dyadic in dyadics.into_iter().flatten() { - let shift = dyadic.1.checked_sub(unit_exponent)?.unsigned_abs(); - let coefficient = multiply_by_power_of_two(dyadic.0, shift)?; - coefficient_sum = coefficient_sum.checked_add(coefficient)?; + for &coordinate in values { + if coordinate == 0.0 { + continue; + } + let (significand, exponent) = positive_dyadic(coordinate.abs())?; + let shift = exponent.checked_sub(unit_exponent)?.unsigned_abs(); + let coefficient = multiply_by_power_of_two(significand, shift)?; + if coordinate.is_sign_negative() { + negative_sum = negative_sum.checked_add(coefficient)?; + } else { + positive_sum = positive_sum.checked_add(coefficient)?; + } square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; } + let signed_sum_magnitude = positive_sum.abs_diff(negative_sum); let sample_count = u128::try_from(values.len()).ok()?; let numerator = Wide256::multiply_u128(sample_count, square_sum) - .checked_sub(Wide256::multiply_u128(coefficient_sum, coefficient_sum))? + .checked_sub(Wide256::multiply_u128( + signed_sum_magnitude, + signed_sum_magnitude, + ))? .as_u128()?; - Some((numerator, unit_exponent, coefficient_sum, square_sum)) + Some(( + numerator, + unit_exponent, + signed_sum_magnitude, + square_sum, + )) } fn gcd(mut left: u128, mut right: u128) -> u128 { @@ -195,7 +204,7 @@ fn gcd(mut left: u128, mut right: u128) -> u128 { } #[test] -fn represented_pairwise_and_wide_linear_routes_share_the_same_exact_ratio() { +fn represented_pairwise_and_neutral_zero_routes_share_the_same_exact_ratio() { for sample_count in [4_usize, 16, 17, 65, 257, 2_050] { let values = represented_values(sample_count); for value in values.iter().copied() { @@ -209,12 +218,16 @@ fn represented_pairwise_and_wide_linear_routes_share_the_same_exact_ratio() { let (pair_numerator, pair_exponent) = pairwise_exact_numerator(&values).expect("pairwise represented-input authority"); - let (wide_numerator, wide_exponent, coefficient_sum, square_sum) = - wide_linear_exact_numerator(&values).expect("wide-linear represented-input candidate"); - assert_eq!(wide_exponent, pair_exponent, "common exact unit must agree"); + let (linear_numerator, linear_exponent, signed_sum_magnitude, square_sum) = + neutral_zero_linear_exact_numerator(&values) + .expect("neutral-zero represented-input candidate"); + assert_eq!( + linear_exponent, pair_exponent, + "common exact unit must agree" + ); assert_eq!( - wide_numerator, pair_numerator, - "wide O(n) identity must preserve the O(n²) exact pair numerator for n={sample_count}" + linear_numerator, pair_numerator, + "neutral-zero O(n) identity must preserve the O(n²) exact pair numerator for n={sample_count}" ); let sample_count_u128 = u128::try_from(sample_count).expect("sample count fits u128"); @@ -224,8 +237,16 @@ fn represented_pairwise_and_wide_linear_routes_share_the_same_exact_ratio() { .expect("scientific denominator fits u128"); let divisor = gcd(pair_numerator, denominator); assert_eq!( - (wide_numerator / divisor, denominator / divisor, wide_exponent), - (pair_numerator / divisor, denominator / divisor, pair_exponent), + ( + linear_numerator / divisor, + denominator / divisor, + linear_exponent, + ), + ( + pair_numerator / divisor, + denominator / divisor, + pair_exponent, + ), "both routes must present the exact rounder with the same reduced ratio" ); @@ -235,7 +256,9 @@ fn represented_pairwise_and_wide_linear_routes_share_the_same_exact_ratio() { "n=2050 must exercise the wider cancellation product" ); assert!( - coefficient_sum.checked_mul(coefficient_sum).is_none(), + signed_sum_magnitude + .checked_mul(signed_sum_magnitude) + .is_none(), "n=2050 must exercise the wider squared-sum product" ); assert_eq!( @@ -255,11 +278,11 @@ fn represented_pairwise_and_wide_linear_routes_share_the_same_exact_ratio() { } #[test] -fn wide_linear_route_is_order_invariant_when_the_exact_anchor_moves() { +fn neutral_zero_linear_route_is_order_invariant() { let mut values = represented_values(65); - let forward = wide_linear_exact_numerator(&values).expect("forward route"); + let forward = neutral_zero_linear_exact_numerator(&values).expect("forward route"); values.reverse(); - let reversed = wide_linear_exact_numerator(&values).expect("reversed route"); + let reversed = neutral_zero_linear_exact_numerator(&values).expect("reversed route"); assert_eq!(forward.0, reversed.0); assert_eq!(forward.1, reversed.1); assert_eq!( @@ -269,3 +292,24 @@ fn wide_linear_route_is_order_invariant_when_the_exact_anchor_moves() { .0 ); } + +#[test] +fn neutral_zero_route_admits_mixed_sign_geometry_when_pairwise_refuses() { + let diameter = (1_u64 << 53) as f64; + let values = [-diameter, 0.0, 1.0, diameter]; + assert!( + pairwise_exact_numerator(&values).is_none(), + "pairwise subtraction must refuse the represented -2^53 versus +1 difference" + ); + + let (numerator, unit_exponent, signed_sum_magnitude, square_sum) = + neutral_zero_linear_exact_numerator(&values).expect("neutral-zero exact proof"); + assert_eq!(unit_exponent, 0); + assert_eq!(signed_sum_magnitude, 1); + assert_eq!(square_sum, (1_u128 << 107) + 1); + assert_eq!(numerator, (1_u128 << 109) + 3); + + let truth = [0.0; 4]; + let standard_error = bias_standard_error(&truth, &values).expect("finite standard error"); + assert_eq!(standard_error.to_bits(), 0x432a_20bd_700c_2c3e); +} From f5ca12cea099e0f98d0f790c7e6e0ff9ab6c84ad Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 00:17:15 +0900 Subject: [PATCH 567/576] docs(research): align bias-SE proof trace with production route --- ...ndard-error-wide-linear-admission-bound.md | 47 +++++++++---------- 1 file changed, 21 insertions(+), 26 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index 1d5748263..67f387611 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -4,47 +4,39 @@ Issue #491 originally treated the production `n=4..=16` exact bias-standard-error proof as a sample-count staircase. The accumulated evidence separates three questions: whether represented data admit an exact proof, whether the arithmetic representation is wide enough to carry that proof, and whether the resource cost is acceptable for production. None is resolved by incrementing `n` alone. -For an earlier characterization that subtracts the represented minimum and produces nonnegative integer coefficients `c_i` on a common exact dyadic unit, define `P = sum_{i Wide256 O(n) -> pair` route telemetry. +Odd diameter `D=2^58+1,n=65` is the canonical narrow-width witness: both `n*S2` and `S1^2` require 129 bits while the exact pair numerator requires 123 bits. RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` → repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e` makes the two-limb capacity theorem executable. RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` → repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` adds measured `narrow O(n) -> Wide256 O(n) -> pair` route telemetry to the characterization harness. Represented-input reachability `5a19b6334487b43fb630abba7e487d7cf4c49960` reaches the wider numerator route at `n=4096` on `{0,1,2^53}` and recovers `P=664289479338799435974172876300357631`. At represented `n=2050`, characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d` finds `P=332306998946228931332463617650984961`, denominator `8_610_922_500`, but 136-bit candidate-square and 140-bit adjacent-midpoint comparison operands. `aab9fe9115cee97225f2aa81e54a55ceafb23336` shows those comparisons can remain bounded as `Wide256` mantissa plus signed dyadic exponent. RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` → repair `e4a85f53a611922be7492fe906d62ce65787c18e` integrates that comparator into production; `1240ace8eb41a01fa72a4bb99df842fd550a1288` fixes both exact tie-to-even parity directions. -IEEE 754-2019 and ISO/IEC 60559:2020 remain the published floating-point basis; IEEE P754 is an active revision project rather than a published replacement. +IEEE 754-2019 and ISO/IEC 60559:2020 remain the published floating-point basis. Rechecked on 2026-09-07, ISO lists ISO/IEC 60559:2020 at published stage 60.60, while IEEE lists P754 as an Active PAR approved 2024-06-06 that supersedes 754-2019 when completed; P754 is therefore a revision project, not a published replacement. -## Represented proof equivalence and anchor admission +## Represented proof equivalence and admission -Characterization `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` compares an actual O(n²) pair authority with an independent Wide256 O(n) identity for represented residual classes `{0,1,2^53}` at `n=4,16,17,65,257,2050`. Wherever both admit, they produce the same common unit, exact pair numerator, and reduced ratio. Its minimum-anchor rule is scoped to that input family, not a universal production policy. +Historical characterization `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` compared an O(n²) pair authority with a minimum-anchor Wide256 O(n) identity for represented residual classes `{0,1,2^53}` at `n=4,16,17,65,257,2050`. Wherever both admitted, they produced the same common unit, exact pair numerator, and reduced ratio. That helper ceased to represent production after the conditioned-anchor route was removed. -Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` shows why: residuals `[0,1,2^-54,2]` have exact coordinates from anchor `0`, while non-anchor subtraction `1-2^-54` rounds. On common unit `2^-54`, coordinates `[0,2^54,1,2^55]` give `P=3569704090242693886528325169446915`. The generic fallback happens to return the same public result, so this is an admission distinction rather than a public defect. +Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` shows why pairwise-f64 exactness cannot define represented-input admission. Residuals `[0,1,2^-54,2]` have exact coordinates from neutral zero, while non-anchor subtraction `1-2^-54` rounds. On common unit `2^-54`, coordinates `[0,2^54,1,2^55]` give `P=3569704090242693886528325169446915`. The generic fallback happens to return the same public result, so this fixture is an admission distinction rather than a public defect. -Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` then shows a public defect with `[0,1,2,-2^53]`. The represented minimum `-2^53` cannot exactly translate `1`, but represented anchor `0` preserves every coordinate. Signed unit-one coordinates give `P=243388915243820099130562543878155`, denominator `48`, and correctly rounded result `0x4320000000000001`; the predecessor translated floating-moment fallback returns `0x4320000000000000`. The RED Actions runs were cancelled by the immediate successor and are not hosted RED evidence. +Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` then shows a public defect with `[0,1,2,-2^53]`. The represented minimum `-2^53` cannot exactly translate `1`, but neutral zero preserves every represented residual exactly. Signed unit-one coordinates give `P=243388915243820099130562543878155`, denominator `48`, and correctly rounded result `0x4320000000000001`; the predecessor translated floating-moment fallback returns `0x4320000000000000`. The RED Actions runs were cancelled by the immediate successor and are not hosted RED evidence. -Initial repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` searched every represented residual as a candidate exact anchor. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` proves that set is still incomplete. With represented residuals `[1,2^-54,2,3]`, no observed residual is a universal exact anchor: each nonzero candidate loses the tiny represented component in at least one subtraction. Neutral dyadic anchor `0`, although not an observed residual in this fixture, preserves every represented residual exactly. +Initial repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` searched every represented residual as a candidate exact anchor. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` proved that set incomplete: with represented residuals `[1,2^-54,2,3]`, no observed residual is a universal exact anchor, while neutral zero preserves every represented residual exactly. On common unit `2^-54`, the coordinates are `[2^54,1,2^55,3*2^54]` and `P=6490371073168534319490338297741315`; correctly rounded public bits are `0x3fe4a7e9cb8a3491`, while the predecessor translated floating-moment fallback returned adjacent upper `0x3fe4a7e9cb8a3492`. -On common unit `2^-54`, those coordinates are `[2^54,1,2^55,3*2^54]` and +Repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` therefore expanded the candidate set to neutral zero plus represented residual anchors. That fixed the demonstrated correctness defects but still evaluated pairwise proof first and then scanned the sample for every candidate, leaving production O(n²). -`P = 6490371073168534319490338297741315`. - -The exact scientific denominator is `48 * 2^108`, equivalently the exact-rounding route receives numerator `P`, denominator `48`, unit exponent `-54`. Correct binary64 rounding is `0x3fe4a7e9cb8a3491`. The predecessor translated floating-moment fallback returns adjacent upper `0x3fe4a7e9cb8a3492`; this is a second public one-ULP defect and demonstrates that “search observed anchors” is not a complete scientific admission policy. - -Repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` expanded the production candidate set to neutral `0` plus every represented residual. Every candidate had to preserve all translated coordinates exactly; the implementation chose the smallest maximum translated magnitude with a represented-value tie-break. This closed the demonstrated correctness defects but still evaluated pairwise proof first and then scanned the full sample for each anchor candidate, so the production proof path remained O(n²). - -Neutral zero is not synthetic evidence. It is a deterministic translation origin for the translation-invariant pair-distance identity; subtracting `0` from a finite binary64 residual reproduces that represented residual exactly. The source observations remain unchanged. +Neutral zero is not synthetic evidence. It is a deterministic coordinate origin for the translation-invariant pair-distance identity; subtracting `0` from a finite binary64 residual reproduces that represented residual exactly. The source observations remain unchanged. ## Production route repair -The resource finding above is now repaired on active PR #488, without widening the production sample budget. - -Source-level RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` adds a contract requiring the production proof order `neutral_zero_linear -> conditioned_observed_anchor -> pairwise_reference`. The RED commit was superseded before hosted failure evidence completed: its non-Rust Actions runs were cancelled and the Rust run had not produced a failing result. It is therefore source-level TDD evidence, not hosted RED evidence. +The production resource defect is repaired on active PR #488 without widening the sample budget. -Repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` adds `exact_neutral_zero_linear_pair_square_sum`. It scans represented residuals once to select the common dyadic exponent and a second time to accumulate positive/negative integer coefficient mass and `Σc_i²`. It then evaluates `n*Σc_i²-(Σc_i)²` with exact `Wide256` products and subtraction. The kernel is O(n) time and O(1) proof storage after the residual vector; it allocates no pair records. +Source-level RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` required a linear neutral-zero proof before quadratic work. Repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` added `exact_neutral_zero_linear_pair_square_sum`. It scans represented residuals once to select the common dyadic exponent and a second time to accumulate positive/negative integer coefficient mass and `S2`. It then evaluates `n*S2-S1^2` with exact `Wide256` products and subtraction. The proof kernel is O(n) time and O(1) proof storage after the residual vector and allocates no pair records. -If the zero-origin bounded integer representation refuses, `exact_anchor_linear_pair_square_sum` now searches only represented residual anchors. That O(n²) fallback remains scientifically useful because an exact translated origin can reduce dyadic dynamic range. `exact_pairwise_pair_square_sum` is evaluated last as the O(n²) comparison/fail-closed reference while broader represented-input equality remains under test. +A later retention criterion required the conditioned observed-anchor fallback to demonstrate unique bounded admission after neutral-zero refusal. The checked-in production and unit-test corpus supplied no such represented fixture. Source-level RED `d40bfbf98b36562164d15d505f8f8825fd1c1349` rejected continued production reliance on the unsupported route, and repair `14e7862f4ddccce54f3b93d4dac89adbf047ba77` removed `exact_anchor_linear_pair_square_sum`. Production order is now `neutral_zero_linear -> pairwise_reference -> generic_fallback`. This is a consolidation decision under current evidence, not a theorem that pairwise is globally redundant. -The production module now includes a common-domain equality unit test for neutral-zero versus pairwise proof and retains the exact anchor-only regression geometries. This is not yet a proof of full bounded-domain equivalence. Required follow-up evidence includes broader deterministic common-domain equality, permutation/reversal bit identity, a fixture where conditioned observed anchoring recovers a zero-origin bounded refusal, and route-specific production execution evidence. +Commit `da703fc50e136ed79e12262909c9f400a3945621` repairs the remaining equivalence characterization so it mirrors the production signed neutral-zero arithmetic rather than the removed minimum-anchor helper. For the common represented family `{0,1,2^53}`, it retains exact numerator/reduced-ratio equality against pairwise at `n=4,16,17,65,257,2050` and order invariance. It also adds the mixed-sign represented geometry `[-2^53,0,1,2^53]`: pairwise subtraction correctly refuses the inexact `-2^53` versus `+1` difference, while neutral-zero coordinates admit unit exponent `0`, `S1` magnitude `1`, `S2=2^107+1`, `P=2^109+3`, and the public exact route rounds to `0x432a20bd700c2c3e`. This is production-route admission evidence, not permission to widen `n`. Production sample admission remains `n=4..=16`. @@ -52,13 +44,13 @@ Production sample admission remains `n=4..=16`. Exact pair records are 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2048`, and 4,997,541 at `n=3162`. For the older aligned nonnegative diameter characterization `D=2^53`, narrow checked products fit through `n=2047`, exact pair-numerator extremum through `n=4095`, and unreduced `n²(n-1)` stays at or below `2^53` through `n=208064`. GCD reduction and represented geometry mean none is a universal production cutoff. -The timing vehicle remains `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records wide-product and pair-fallback selection separately. It is still characterization tooling, not production route telemetry. No Rust 1.98.0 `--release` CPU/raw CSV, allocator/RSS, or applicable buyer-path p95 evidence is authoritative yet. +The timing vehicle remains `crates/validation_core/examples/bias_se_exact_proof_budget.rs`; it records characterization routes rather than authoritative production execution. No Rust 1.98.0 `--release` raw CPU timing, allocator/RSS, or applicable buyer-path p95 evidence is authoritative yet. Production route telemetry must distinguish `neutral_zero_linear`, `pairwise_reference`, and `generic_fallback`; identical output bits are not route evidence. ## Decision -Keep production `validation_core::bias_standard_error` at `n=4..=16`. Use the neutral-zero two-pass exact proof before quadratic proof work inside that existing budget, retain conditioned observed anchors when they can reduce bounded coordinate range, and keep pairwise O(n²) as comparison/fail-closed reference until broader equivalence and measured production-route evidence support consolidation. +Keep production `validation_core::bias_standard_error` at `n=4..=16`. Use the neutral-zero two-pass exact proof before quadratic proof work inside that budget, keep pairwise O(n²) as the fail-closed comparison/reference path, and fall back to the established general implementation when bounded exact proof refuses. Do not reintroduce observed-anchor scanning without a represented fixture that proves unique scientific admission value. -Before widening beyond 16, require exact-head Rust 1.98.0 fmt/clippy/nextest/rustdoc and owned production 100% line/branch coverage, same-head security/documentation GREEN and qualifying independent review, broad pair/neutral-zero equality wherever both admit, explicit anchor-only fixtures `[0,1,2^-54,2]`, `[0,1,2,-2^53]`, and `[1,2^-54,2,3]`, exact candidate stepping/midpoint/tie-to-even and permutation invariance, fail-closed overflow/range behavior, truthful production route telemetry, recorded release-mode raw CPU/allocator/RSS and applicable buyer-path p95 evidence, and current CHANGELOG/TRACEABILITY/TEST_STRATEGY/OPERABILITY/operator baseline. +Before widening beyond 16, require exact-head Rust 1.98.0 fmt/clippy/nextest/rustdoc and owned-production 100% line/branch coverage, same-head security/documentation GREEN and qualifying independent review, broad pair/neutral-zero equality wherever both admit, explicit neutral-zero-only represented fixtures, exact candidate stepping/midpoint/tie-to-even and permutation invariance, fail-closed overflow/range behavior, truthful production route telemetry, recorded release-mode raw CPU/allocator/RSS and applicable buyer-path p95 evidence, and current CHANGELOG/TRACEABILITY/TEST_STRATEGY/OPERABILITY/operator baseline. ## Traceability @@ -71,14 +63,17 @@ Before widening beyond 16, require exact-head Rust 1.98.0 fmt/clippy/nextest/rus - Exponent-safe scaled comparison: `aab9fe9115cee97225f2aa81e54a55ceafb23336` - Production scaled-comparison RED / repair: `f7717361ad8c5f0592688c1514c104cc1b4adabe` / `e4a85f53a611922be7492fe906d62ce65787c18e` - Exact midpoint tie-to-even: `1240ace8eb41a01fa72a4bb99df842fd550a1288` -- Represented pair/Wide256 exact-ratio equivalence: `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` +- Historical minimum-anchor pair/Wide256 equivalence: `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` - Pairwise-f64-strict characterization: `2bc1d2284d75154e020640adb573c1cfadf005fb` - Non-minimum represented-anchor RED / repair: `fd9f9ff2c5c395e4cc13042232f4deef018adb48` / `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` - Neutral-anchor RED / correctness repair: `9f403194a2ec1636531c2dfe9229cfb34b73d747` / `6cf30eeb549c0df0377bda1111cf46396e8282a3` - Neutral-zero route-order RED / production resource repair: `e0b324864e48a503e2aba0d2a487a0b95f5276ed` / `2b62bd46eb0c391327d2285c2244a76f5a1e0449` +- Unsupported conditioned-anchor retention RED / removal: `d40bfbf98b36562164d15d505f8f8825fd1c1349` / `14e7862f4ddccce54f3b93d4dac89adbf047ba77` +- Production-aligned neutral-zero equivalence and mixed-sign admission: `da703fc50e136ed79e12262909c9f400a3945621` - Narrow-wide-pair characterization RED / repair: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` / `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` - CHANGELOG fragment: `CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md` - Production module: `crates/validation_core/src/bias_se.rs` - Route-order contract: `crates/validation_core/tests/bias_standard_error_neutral_zero_route_order_contract.rs` +- Production-aligned equivalence characterization: `crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs` - Public regression: `crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs` - Exact-proof budget harness: `crates/validation_core/examples/bias_se_exact_proof_budget.rs` From 2cd38bc67828c455c7a3fdf1cb810a37c49df4b2 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 00:17:51 +0900 Subject: [PATCH 568/576] docs(changelog): record neutral-zero route consolidation evidence --- ...on-bias-exact-proof-budget-characterization.md | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) diff --git a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md index 4b651aedc..ca50cdc80 100644 --- a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md +++ b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md @@ -7,10 +7,11 @@ - Record the canonical accumulator bound `Σc_i <= Σc_i² <= Σ(i Wide256 O(n) -> buffered pair fail-closed fallback`. RED `3136739460ef0c8e13c044a7e5b04891e4f4e23d` requires the missing route; repair `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` implements it and records both wide-product selection and pairwise fallback separately in CSV. - The corrected harness keeps the existing narrow-to-pair hybrid for comparison. On odd `D=2^58+1, n=65`, that predecessor hybrid still allocates the pair buffer, while the new narrow-to-wide-to-pair candidate must recover the same exact numerator through `Wide256` with `used_wide_product=true` and `used_pairwise_fallback=false`. Power-of-two-normalized `n=65` and odd `n=64` remain narrow-path admissions. -- Represented-input equivalence distinguishes proof admission from public numerical correctness. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` uses residuals `[0,1,2^-54,2]`: every minimum-anchor subtraction is exact, but `1 - 2^-54` rounds in binary64, so the O(n²) pairwise-f64 proof refuses. On common unit `2^-54`, integer coordinates `[0,2^54,1,2^55]` give exact pair numerator `3569704090242693886528325169446915`; the generic fallback happens to return the correct public value. -- Add source-level RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` for residuals `[0,1,2,-2^53]`. The minimum residual cannot exactly translate `1`, but represented anchor `0` preserves every coordinate. Signed unit-one coordinates give `P=243388915243820099130562543878155`, so `SE(mean)^2=P/48`; the predecessor translated floating-moment path returns `0x4320000000000000` while exact rounding requires adjacent `0x4320000000000001`. Its Actions runs were cancelled by the immediate repair push and are not hosted RED evidence. -- Initial production repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` keeps pairwise exact accumulation first and searches represented residual anchors. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` shows that observed anchors alone are incomplete: residuals `[1,2^-54,2,3]` have no observed universal exact anchor, while neutral dyadic anchor `0` preserves all coordinates. On unit `2^-54`, `P=6490371073168534319490338297741315`; exact rounding requires `0x3fe4a7e9cb8a3491`, while the predecessor translated floating-moment fallback returns adjacent `0x3fe4a7e9cb8a3492`. -- Repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` includes neutral zero plus every represented residual in the deterministic exact-anchor candidate set, keeps the exact candidate with the smallest maximum translated magnitude and represented-value tie-break, accumulates signed dyadic coordinates, computes `n*Σc_i²-(Σc_i)²` with `Wide256` cancellation products, and reuses the exact candidate/midpoint tie-to-even rounder. Forward and permuted public fixtures require bit-identical results. -- Source-level route-order RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` requires production to attempt `neutral_zero_linear` before conditioned observed-anchor and pairwise O(n²) proofs. Its superseding repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` adds a two-pass neutral-zero signed-dyadic/Wide256 kernel, O(n) in time with O(1) proof storage after the residual vector, and moves observed-anchor search and pairwise accumulation to fallbacks. The superseded RED did not finish a hosted failing run and is not claimed as hosted RED evidence. -- The resource repair keeps a common-domain neutral-zero/pairwise equality unit and the existing anchor-only regression geometries, but it does not yet promote full represented-input equivalence or a larger sample budget. The checked-in timing harness is still characterization tooling and does not yet constitute production route telemetry. -- Keep production `bias_standard_error` sample admission unchanged at `n=4..=16`. A budget change beyond 16 still requires broad pair/neutral-zero same-domain equivalence plus intended anchor-only admissions, truthful production route evidence, recorded Rust 1.98.0 release-mode CPU/raw CSV and allocator/RSS evidence, applicable buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch/security/documentation GREEN, current TRACEABILITY/research/operator doctoring, and qualifying independent current-head review. +- Represented-input equivalence distinguishes proof admission from public numerical correctness. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` uses residuals `[0,1,2^-54,2]`: every neutral-zero coordinate is exact, but `1 - 2^-54` rounds in binary64, so the O(n²) pairwise-f64 proof refuses. On common unit `2^-54`, integer coordinates `[0,2^54,1,2^55]` give exact pair numerator `3569704090242693886528325169446915`; the generic fallback happens to return the correct public value. +- Add source-level RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` for residuals `[0,1,2,-2^53]`. The minimum residual cannot exactly translate `1`, while neutral zero preserves every coordinate. Signed unit-one coordinates give `P=243388915243820099130562543878155`, so `SE(mean)^2=P/48`; the predecessor translated floating-moment path returns `0x4320000000000000` while exact rounding requires adjacent `0x4320000000000001`. Its Actions runs were cancelled by the immediate repair push and are not hosted RED evidence. +- Initial production repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` kept pairwise exact accumulation first and searched represented residual anchors. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` showed that observed anchors alone are incomplete: residuals `[1,2^-54,2,3]` have no observed universal exact anchor, while neutral dyadic anchor `0` preserves all coordinates. On unit `2^-54`, `P=6490371073168534319490338297741315`; exact rounding requires `0x3fe4a7e9cb8a3491`, while the predecessor translated floating-moment fallback returns adjacent `0x3fe4a7e9cb8a3492`. +- Repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` temporarily included neutral zero plus represented residuals in a deterministic exact-anchor set and reused signed dyadic/Wide256 cancellation plus the exact midpoint rounder. That closed the demonstrated correctness defects but retained O(n²) anchor scanning. +- Source-level route-order RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` required production to attempt `neutral_zero_linear` before quadratic proofs. Repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` added a two-pass neutral-zero signed-dyadic/Wide256 kernel, O(n) in time with O(1) proof storage after the residual vector. The superseded RED did not finish a hosted failing run and is not claimed as hosted RED evidence. +- A later retention criterion required the conditioned observed-anchor fallback to prove unique bounded admission after neutral-zero refusal. The checked-in corpus supplied no such represented fixture. RED `d40bfbf98b36562164d15d505f8f8825fd1c1349` and repair `14e7862f4ddccce54f3b93d4dac89adbf047ba77` therefore remove that unsupported O(n²) route. Production order is now `neutral_zero_linear -> pairwise_reference -> generic_fallback`; this is evidence-based consolidation, not a theorem that pairwise is globally redundant. +- Commit `da703fc50e136ed79e12262909c9f400a3945621` replaces the stale minimum-anchor equivalence helper with the production signed neutral-zero arithmetic. The common `{0,1,2^53}` family retains pair/linear exact-numerator and reduced-ratio equality at `n=4,16,17,65,257,2050` plus order invariance. A mixed-sign fixture `[-2^53,0,1,2^53]` additionally proves neutral-zero admission when pairwise subtraction refuses: unit exponent `0`, signed-sum magnitude `1`, `Σc_i²=2^107+1`, `P=2^109+3`, and correctly rounded public bits `0x432a20bd700c2c3e`. +- Keep production `bias_standard_error` sample admission unchanged at `n=4..=16`. A budget change beyond 16 still requires broad pair/neutral-zero same-domain equivalence, truthful production route evidence, recorded Rust 1.98.0 release-mode CPU/raw CSV and allocator/RSS evidence, applicable buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch/security/documentation GREEN, current TRACEABILITY/research/operator doctoring, and qualifying independent current-head review. From bcf7b3df5b4c0549ad1f1c427907ac865e8f6268 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 00:28:05 +0900 Subject: [PATCH 569/576] docs(test): align bias-SE validation with surviving routes --- docs/TEST_STRATEGY.md | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/docs/TEST_STRATEGY.md b/docs/TEST_STRATEGY.md index 1a5cee063..768d7edd5 100644 --- a/docs/TEST_STRATEGY.md +++ b/docs/TEST_STRATEGY.md @@ -1,7 +1,7 @@ # TEPP Test and Scientific Validation Strategy **Status:** Accepted quality baseline aligned to PRD v0.4 -**Last reviewed:** 2026-09-06 +**Last reviewed:** 2026-09-07 ## Mandatory repository gates @@ -69,28 +69,28 @@ Validation Evidence numerical proofs that add asymptotic work or material alloca - retain realistic represented-input counterexamples and permutation/sign-mirror contracts for scientific correctness; - keep the exact represented-residual gate, GCD reduction, exact candidate/midpoint authorization, and fail-closed fallback; -- treat exact pairwise-f64 subtraction as a comparison/reference authority, not a scientific prerequisite or an unconditional first production route. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` proves `[0,1,2^-54,2]` has exact anchor coordinates even though a non-anchor pair subtraction rounds; -- do not require the represented minimum to be the exact anchor. Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` uses `[0,1,2,-2^53]`: minimum `-2^53` cannot exactly translate `1`, while represented anchor `0` preserves all coordinates. Exact `P=243388915243820099130562543878155`, denominator `48`, and public result `0x4320000000000001` differ by one ULP from the predecessor fallback; -- do not restrict production exact anchors to observed residuals. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` uses `[1,2^-54,2,3]`, where no observed residual exactly translates every other residual, but neutral dyadic anchor `0` preserves all. On unit `2^-54`, exact `P=6490371073168534319490338297741315`; exact result `0x3fe4a7e9cb8a3491` differs by one ULP from predecessor fallback `0x3fe4a7e9cb8a3492`; -- retain the correctness lineage through repair `6cf30eeb549c0df0377bda1111cf46396e8282a3`, but do not retain its quadratic route order as production policy. Source-level route-order RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` requires `neutral_zero_linear -> conditioned_observed_anchor -> pairwise_reference`; repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` implements a two-pass neutral-zero signed-dyadic/Wide256 proof first, keeps observed-anchor search only as a bounded dynamic-range fallback, and moves pairwise accumulation last. The superseded RED did not finish a hosted failing run and must not be cited as hosted RED evidence; +- treat exact pairwise-f64 subtraction as a comparison/reference authority, not a scientific prerequisite or an unconditional first production route. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` proves `[0,1,2^-54,2]` has exact neutral-zero coordinates even though a non-anchor pair subtraction rounds; +- do not require the represented minimum to be the exact anchor. Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` uses `[0,1,2,-2^53]`: minimum `-2^53` cannot exactly translate `1`, while neutral zero preserves all coordinates. Exact `P=243388915243820099130562543878155`, denominator `48`, and public result `0x4320000000000001` differ by one ULP from the predecessor fallback; +- do not restrict exact translation origins to observed residuals. Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` uses `[1,2^-54,2,3]`, where no observed residual exactly translates every other residual, but neutral dyadic origin `0` preserves all. On unit `2^-54`, exact `P=6490371073168534319490338297741315`; exact result `0x3fe4a7e9cb8a3491` differs by one ULP from predecessor fallback `0x3fe4a7e9cb8a3492`; +- retain the correctness lineage through repair `6cf30eeb549c0df0377bda1111cf46396e8282a3`, but do not retain its quadratic route order as production policy. Source-level route-order RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` required a linear neutral-zero proof before quadratic work; repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` implemented it. A later retention criterion required conditioned observed-anchor search to prove unique bounded admission after neutral-zero refusal. The checked-in corpus supplied no such fixture, so RED `d40bfbf98b36562164d15d505f8f8825fd1c1349` → repair `14e7862f4ddccce54f3b93d4dac89adbf047ba77` removed that unsupported O(n²) route. Production order is now `neutral_zero_linear -> pairwise_reference -> generic_fallback`. Neither superseded RED finished a hosted failing run and neither is cited as hosted RED evidence; - test the neutral-zero kernel as O(n) in loop structure and O(1) in *proof* storage after the already-required residual vector. Do not describe the whole public call as O(1) space while it still materializes residuals; -- preserve pair-versus-neutral-zero equality wherever both admit and explicitly test intended anchor-only admissions. Broaden deterministic represented-input equality beyond one common-domain unit test before promotion. Test forward/reversed/permuted observation order bit-for-bit and include signed coordinates; -- retain conditioned observed-anchor fallback tests that demonstrate an exact translated origin can recover a bounded zero-origin refusal; do not keep O(n²) anchor search merely by assumption; -- neutral zero is a translation origin, not synthetic evidence: the observed residual values are unchanged. Do not claim zero or observed anchors are globally resource-optimal without a separate proof; +- preserve pair-versus-neutral-zero equality wherever both admit and explicitly test neutral-zero-only admissions. Commit `da703fc50e136ed79e12262909c9f400a3945621` mirrors the production signed neutral-zero arithmetic, keeps exact pair/linear numerator and reduced-ratio equality for represented `{0,1,2^53}` at `n=4,16,17,65,257,2050`, preserves order invariance, and adds mixed-sign `[-2^53,0,1,2^53]`, where pairwise refuses but neutral-zero admits `P=2^109+3` and public bits `0x432a20bd700c2c3e`. Broaden deterministic represented-input equality beyond these fixtures before promotion; +- do not reintroduce conditioned observed-anchor scanning without a represented fixture that proves unique scientific admission value after neutral-zero bounded refusal; O(n²) search is not retained by assumption; +- neutral zero is a translation origin, not synthetic evidence: the observed residual values are unchanged. Do not claim it is globally resource-optimal without a separate proof; - compare buffered O(n²), allocation-free two-pass O(n²), normalized narrow O(n), dependency-free Wide256 O(n), predecessor narrow→pair hybrid, and narrow→Wide256→pair candidate resource shapes before changing the sample budget; - normalize the common power-of-two dyadic unit before narrow checked O(n) intermediates are judged. Raw `D=2^58,n=65` refusal is invalid after normalization; odd `D=2^58+1,n=65` remains the narrow 129-bit product witness; -- retain the earlier nonnegative minimum-anchor accumulator theorem `b7e4da353ac58069afd73ee7c0e8427d49993fdb`, `Σc_i <= Σc_i² <= P`, only within that characterized representation. Production exact-anchor coordinates may be signed, so positive and negative coefficient mass and resulting signed sum must be exercised separately; +- retain the earlier nonnegative minimum-anchor accumulator theorem `b7e4da353ac58069afd73ee7c0e8427d49993fdb`, `Σc_i <= Σc_i² <= P`, only within that characterized representation. Production neutral-zero coordinates may be signed, so positive and negative coefficient mass and resulting signed sum must be exercised separately; - retain RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` → repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e`: canonical `u128` cancellation operands require no more than 256-bit products; - retain represented reachability `5a19b6334487b43fb630abba7e487d7cf4c49960`: at `n=4096`, `{0,1,2^53}` reaches narrow-product overflow while Wide256 recovers exact 119-bit `P=664_289_479_338_799_435_974_172_876_300_357_631`; - retain exact-rounding-width characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d`: at represented `n=2050`, `P=332_306_998_946_228_931_332_463_617_650_984_961`, denominator `8_610_922_500`, candidate-square comparison requires 136 bits and the upward midpoint 140 bits; - retain exponent-safe comparison characterization `aab9fe9115cee97225f2aa81e54a55ceafb23336`, production comparison RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` → repair `e4a85f53a611922be7492fe906d62ce65787c18e`, and tie-to-even edge contract `1240ace8eb41a01fa72a4bb99df842fd550a1288`; -- retain represented pair/Wide256 exact-ratio equivalence `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` at `n=4,16,17,65,257,2050`, while recognizing that its minimum-anchor rule is scoped to that input family rather than universal production canonicalization; +- retain historical minimum-anchor pair/Wide256 exact-ratio equivalence `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` only within its characterized input family; use `da703fc50e136ed79e12262909c9f400a3945621` as the production-aligned signed neutral-zero equivalence evidence; - keep normalized O(n) intermediate, exact pair-numerator, and denominator envelopes separate. At aligned diameter `2^53`, `n<=2_047`, `n<=4_095`, and `n<=208_064` are arithmetic evidence points, not production budgets; - record exact pair counts, target `size_of::>()`, scratch `Vec` capacity/payload, and allocator/RSS evidence separately; field-width estimates are not allocation evidence; -- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing, but do not mislabel that characterization harness as production route telemetry. Production evidence must distinguish at least `neutral_zero_linear`, `conditioned_observed_anchor`, `pairwise_reference`, and `generic_fallback`; +- use `crates/validation_core/examples/bias_se_exact_proof_budget.rs` to prove exact restored-numerator equality before timing, but do not mislabel that characterization harness as production route telemetry. Production evidence must distinguish at least `neutral_zero_linear`, `pairwise_reference`, and `generic_fallback`; - run the characterization and surviving production-route harness in release mode on recorded CPU/OS/Rust 1.98.0 and retain raw CSV plus p95. Unexecuted harness code is not performance evidence; - if a service/API buyer path is affected, measure the complete applicable path against `p95 <= 20 ms` without shrinking samples, omitting proof work, or using unrealistic warm-cache-only setup; -- arithmetic representability, one successful anchor, or one common-domain equality fixture does not authorize a production sample-count budget. +- arithmetic representability, one successful exact origin, or one common-domain equality fixture does not authorize a production sample-count budget. Until broad same-head correctness, exact-head gates, independent review, and resource measurements exist, production bias-SE exact admission remains `n=4..=16`. From 6730235228a0121681e7730bd4a57466011641d3 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 00:29:08 +0900 Subject: [PATCH 570/576] docs(ops): align bias-SE resource route with production --- docs/OPERABILITY.md | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/docs/OPERABILITY.md b/docs/OPERABILITY.md index 84bc7568c..b257b7a06 100644 --- a/docs/OPERABILITY.md +++ b/docs/OPERABILITY.md @@ -1,7 +1,7 @@ # TEPP Operability, Recovery, and Release Guide **Status:** Accepted target operating baseline with current maturity explicit. -**Last reviewed:** 2026-09-06 +**Last reviewed:** 2026-09-07 TEPP is still an implementation-stage research/product platform. Protected main currently contains the Rust workspace/evidence foundation plus implemented-main temporal primitives (merged PRs #8 and #9). Superseded PRs #5 and #6 are historical lineage only. Database adapters are partial; model fitting, GPU, services, visual analytics, and production deployment are later targets. This guide defines the operating evidence those stages must satisfy rather than claiming they already exist. Unmerged or draft PRs are not implemented-main claims. @@ -44,40 +44,40 @@ Before PostgreSQL becomes production state, prove migrations and rollback, tenan A numerical proof boundary is an operational resource contract when it changes asymptotic work, allocation, or buyer-path latency. It is not determined by the next sample count that happens to expose a rounding defect. -Issue #491 owns the current bias-standard-error exact-proof budget. Production exact admission remains `n<=16`; larger counts are characterization evidence only. The work separates represented-input exactness, arithmetic width, exact-rounding width, and measured resource cost rather than treating one integer cutoff as all four. +Issue #491 owns the current bias-standard-error exact-proof budget. Production exact admission remains `n=4..=16`; larger counts are characterization evidence only. The work separates represented-input exactness, arithmetic width, exact-rounding width, and measured resource cost rather than treating one integer cutoff as all four. The old nonnegative minimum-anchor characterization established `Σc_i <= Σc_i² <= P` and showed why raw-scale `D=2^58,n=65` refusal disappears after common dyadic-unit normalization. Odd `D=2^58+1,n=65` remains a narrow-width witness: pair numerator fits in 123 bits while cancellation products require 129 bits. `Wide256` characterization `081000289f5a52e94863026d55696ee2a4daf923` and product-width RED `f74d9ac11cb0acf3eb8fdd9ad79ac3d2e9180993` → repair `e9a7dee29afb97542bfe2965f850c8ab5a34368e` show the characterized cancellation products need no more than two `u128` limbs. Represented-input reachability `5a19b6334487b43fb630abba7e487d7cf4c49960` reaches the wider numerator route at `n=4096` on `{0,1,2^53}`. Exact-rounding characterization `a8423173188fa53a26a16d3afdafeb76e114cc1d` shows represented `n=2050` needs 136-bit candidate-square and 140-bit adjacent-midpoint comparison operands even though its exact pair numerator is 118 bits. `aab9fe9115cee97225f2aa81e54a55ceafb23336` shows those comparisons can remain bounded as `Wide256` mantissa plus signed dyadic exponent. RED `f7717361ad8c5f0592688c1514c104cc1b4adabe` → repair `e4a85f53a611922be7492fe906d62ce65787c18e` integrates that comparison into the production exact rounder, and `1240ace8eb41a01fa72a4bb99df842fd550a1288` fixes both tie-to-even parity directions. -`7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` proves represented pair/Wide256 exact-ratio equivalence for one family where the represented minimum is an exact anchor. That is not a universal anchor policy. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` demonstrates `[0,1,2^-54,2]`, where non-anchor pair subtraction rounds but anchor `0` remains exact. +Historical characterization `7a2ab0a1ef7a72d8cc9b9253d7f92c493e578943` proves represented pair/Wide256 exact-ratio equivalence for one family where the represented minimum is an exact anchor. That is not a universal production policy. Characterization `2bc1d2284d75154e020640adb573c1cfadf005fb` demonstrates `[0,1,2^-54,2]`, where a non-anchor pair subtraction rounds but neutral zero remains exact. -Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` establishes the first public anchor defect with `[0,1,2,-2^53]`: minimum `-2^53` cannot exactly translate `1`, represented anchor `0` preserves every coordinate, exact `P=243388915243820099130562543878155`, denominator `48`, and exact result `0x4320000000000001` while the predecessor translated floating-moment path returns `0x4320000000000000`. Initial repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` searched represented residual anchors. +Source RED `fd9f9ff2c5c395e4cc13042232f4deef018adb48` establishes the first public anchor defect with `[0,1,2,-2^53]`: minimum `-2^53` cannot exactly translate `1`, neutral zero preserves every coordinate, exact `P=243388915243820099130562543878155`, denominator `48`, and exact result `0x4320000000000001` while the predecessor translated floating-moment path returns `0x4320000000000000`. Initial repair `81ba770cc4812c8fbeb4b3529f0a73b41abbed0f` searched represented residual anchors. -Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` shows that observed anchors alone are operationally incomplete. Residuals `[1,2^-54,2,3]` have no observed universal exact anchor, but neutral dyadic anchor `0` preserves every residual exactly. On unit `2^-54`, exact `P=6490371073168534319490338297741315`; exact result `0x3fe4a7e9cb8a3491` differs by one ULP from the predecessor fallback `0x3fe4a7e9cb8a3492`. +Follow-up RED `9f403194a2ec1636531c2dfe9229cfb34b73d747` shows that observed anchors alone are operationally incomplete. Residuals `[1,2^-54,2,3]` have no observed universal exact anchor, but neutral dyadic origin `0` preserves every residual exactly. On unit `2^-54`, exact `P=6490371073168534319490338297741315`; exact result `0x3fe4a7e9cb8a3491` differs by one ULP from the predecessor fallback `0x3fe4a7e9cb8a3492`. -Correctness repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` established neutral zero as an admissible exact translation origin, but its production order still paid pairwise O(n²) first and then O(n²) anchor search. Source-level route-order RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` requires `neutral_zero_linear -> conditioned_observed_anchor -> pairwise_reference`. Repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` implements that sequence. +Correctness repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` established neutral zero as an admissible exact translation origin, but its production order still paid pairwise O(n²) first and then O(n²) anchor search. Source-level route-order RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` required a linear neutral-zero proof before quadratic work; repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` added that two-pass path. A later retention criterion required conditioned observed-anchor search to demonstrate unique bounded admission after neutral-zero refusal. The checked-in corpus supplied no such represented fixture, so RED `d40bfbf98b36562164d15d505f8f8825fd1c1349` → repair `14e7862f4ddccce54f3b93d4dac89adbf047ba77` removed the unsupported O(n²) anchor route. Production order is now `neutral_zero_linear -> pairwise_reference -> generic_fallback`. -The neutral-zero kernel scans residuals once to determine the common dyadic exponent and once more to accumulate positive/negative coefficient mass and `Σc_i²`; it then computes `n*Σc_i²-(Σc_i)²` with exact `Wide256` products/subtraction. This is O(n) time with O(1) proof storage after the already-required residual vector and allocates no pair records. If the bounded zero-origin integer representation refuses, observed-anchor search remains an O(n²) conditioned fallback that may reduce coordinate dynamic range. Pairwise O(n²) now runs last as comparison/fail-closed authority while broader represented-input equivalence remains under validation. +The neutral-zero kernel scans residuals once to determine the common dyadic exponent and once more to accumulate positive/negative coefficient mass and `Σc_i²`; it then computes `n*Σc_i²-(Σc_i)²` with exact `Wide256` products/subtraction. This is O(n) time with O(1) proof storage after the already-required residual vector and allocates no pair records. If the bounded neutral-zero proof refuses, pairwise O(n²) is the comparison/fail-closed authority before the established generic fallback. -The superseded route-order RED did not finish a hosted failing run, so it is source-level TDD evidence only. The current source adds a common-domain neutral-zero/pairwise equality unit and preserves the anchor-only public regressions; that is not yet full bounded-domain equivalence. +The superseded route-order and conditioned-anchor-retention REDs did not finish hosted failing runs, so they are source-level TDD evidence only. Commit `da703fc50e136ed79e12262909c9f400a3945621` aligns the equivalence characterization with production signed neutral-zero arithmetic: represented `{0,1,2^53}` families retain exact pair/linear numerator and reduced-ratio equality at `n=4,16,17,65,257,2050` plus order invariance. Mixed-sign `[-2^53,0,1,2^53]` proves a neutral-zero-only admission: pairwise subtraction refuses, while neutral-zero yields unit exponent `0`, signed-sum magnitude `1`, `Σc_i²=2^107+1`, `P=2^109+3`, and public bits `0x432a20bd700c2c3e`. Operator implications: - attempt the neutral-zero linear proof before quadratic proof work for the current bounded production route; -- do not diagnose pairwise-f64, minimum-anchor, or observed-anchor refusal as scientific invalidity when neutral-zero or a conditioned exact anchor admits the bounded dyadic proof; -- keep observed anchors only as a demonstrated dynamic-range recovery fallback and require a fixture that proves such recovery; do not retain O(n²) search merely by assumption; +- do not diagnose pairwise-f64 or minimum/observed-anchor refusal as scientific invalidity when neutral zero admits the bounded dyadic proof; +- do not reintroduce conditioned observed-anchor scanning without a represented fixture that proves unique scientific admission after neutral-zero bounded refusal; O(n²) search is not retained by assumption; - do not make row order part of proof semantics; forward/reversed/permuted fixtures must be bit-identical; - treat signed-coordinate accumulation, Wide256 subtraction/downcast, denominator reduction, or exact-rounding failure as a fail-closed proof refusal and use later proof/fallback routes rather than weakening checks; - keep pairwise proof as the comparison/fail-closed authority, not as unconditional first work; -- separate route observability from numerical equality. The existing resource harness records `used_wide_product` and `used_pairwise_fallback`, but production evidence still must distinguish `neutral_zero_linear`, `conditioned_observed_anchor`, `pairwise_reference`, and `generic_fallback`; -- describe storage precisely: the new kernel uses O(1) proof storage after the residual vector, while the public exact path still materializes O(n) residual storage; +- separate route observability from numerical equality. The existing resource harness records characterization-specific `used_wide_product` and `used_pairwise_fallback`, but production evidence still must distinguish `neutral_zero_linear`, `pairwise_reference`, and `generic_fallback`; +- describe storage precisely: the neutral-zero kernel uses O(1) proof storage after the residual vector, while the public exact path still materializes O(n) residual storage; - treat `n=2_047`, `4_095`, and `208_064` only as arithmetic envelope markers from older aligned characterizations, not service limits; - before widening beyond 16, retain raw Rust 1.98.0 `--release` timing CSV, CPU/OS/build flags, p95, actual scratch capacity/payload, allocator/RSS, and any applicable buyer-path `p95<=20 ms` evidence without sample shrinkage or omitted proof work. -Exact pair-record counts remain 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2048`, and 4,997,541 at `n=3162`. A two-pass O(n²) implementation may remove pair-record storage but still requires exact-head scientific/resource evidence. The neutral-zero Wide256 O(n) implementation removes pair enumeration for admitted geometries but still requires broad pair-equivalence, conditioned-anchor admission, exact-rounding, refusal, route, and resource evidence on one surviving production head. +Exact pair-record counts remain 120 at `n=16`, 136 at `n=17`, 2,096,128 at `n=2048`, and 4,997,541 at `n=3162`. A two-pass O(n²) implementation may remove pair-record storage but still requires exact-head scientific/resource evidence. The neutral-zero Wide256 O(n) implementation removes pair enumeration for admitted geometries but still requires broad pair-equivalence, neutral-zero-only admission, exact-rounding, refusal, route, and resource evidence on one surviving production head. -No release-mode resource numbers are authoritative yet. Source-level RED runs that were superseded or cancelled are not counted as hosted RED. Predecessor Rustfmt artifact `9982621569` from `1f765a...` is not current-head formatting evidence. +No release-mode resource numbers are authoritative yet. Source-level RED runs that were superseded or cancelled are not counted as hosted RED. Pinned Rustfmt Evidence `34037910108` for predecessor `206c1789ea13759591c308705fc1f57b75c96b28` produced artifact `9991468416`, digest `sha256:cd03e7c35be9d398d86494b01306df08af94dc117b38482578d652d9e2b2283b`, with 36 formatted Rust files; it is predecessor-only evidence, not current-head formatting GREEN. ## Model release/cutover From 43d208383b765a3424019de45530af77ca484d78 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 00:42:10 +0900 Subject: [PATCH 571/576] test(validation): remove copied exact-route implementation from oracle --- ...nted_route_equivalence_characterization.rs | 370 +++++------------- 1 file changed, 90 insertions(+), 280 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs b/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs index 547e831b7..79a65f6f4 100644 --- a/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs +++ b/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs @@ -1,315 +1,125 @@ -//! Characterizes represented-input equivalence between pairwise and production neutral-zero bias-SE proofs. +//! Characterizes represented-input bias-SE behavior without copying production arithmetic. //! -//! This is test-only evidence for issue #491. Production admission remains -//! `n=4..=16`: the neutral-zero O(n) proof is primary, while pairwise O(n²) -//! remains a fail-closed comparison/reference path until represented-input -//! equivalence, exact rounding, resource evidence, and protected-head quality -//! gates are all satisfied. +//! Issue #491 requires an independent oracle for the admitted production domain. +//! These fixtures use exactly represented integer residuals, compute the pair-distance +//! numerator directly in integer arithmetic, and then assert the public API's audited +//! binary64 result and permutation invariance. Production admission remains `n=4..=16`. use validation_core::bias_standard_error; -#[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)] -struct Wide256 { - high: u128, - low: u128, -} - -impl Wide256 { - fn multiply_u128(left: u128, right: u128) -> Self { - let mask = u128::from(u64::MAX); - let left_limbs = [ - u64::try_from(left & mask).expect("masked low limb fits u64"), - u64::try_from(left >> 64).expect("high limb fits u64"), - ]; - let right_limbs = [ - u64::try_from(right & mask).expect("masked low limb fits u64"), - u64::try_from(right >> 64).expect("high limb fits u64"), - ]; - let mut limbs = [0_u64; 4]; - - for (left_index, left_limb) in left_limbs.iter().copied().enumerate() { - let mut carry = 0_u128; - for (right_index, right_limb) in right_limbs.iter().copied().enumerate() { - let limb_index = left_index + right_index; - let accumulator = u128::from(left_limb) - .checked_mul(u128::from(right_limb)) - .expect("64-bit limb product fits u128") - .checked_add(u128::from(limbs[limb_index])) - .expect("schoolbook partial sum fits u128") - .checked_add(carry) - .expect("schoolbook carry sum fits u128"); - limbs[limb_index] = - u64::try_from(accumulator & mask).expect("masked schoolbook limb fits u64"); - carry = accumulator >> 64; - } - limbs[left_index + 2] = - u64::try_from(carry).expect("schoolbook multiplication carry fits u64"); - } +const DIAMETER_INTEGER: i128 = 1_i128 << 53; +const DIAMETER: f64 = (1_u64 << 53) as f64; - Self { - high: u128::from(limbs[2]) | (u128::from(limbs[3]) << 64), - low: u128::from(limbs[0]) | (u128::from(limbs[1]) << 64), - } - } - - fn checked_sub(self, right: Self) -> Option { - let (low, borrow) = self.low.overflowing_sub(right.low); - let high = self - .high - .checked_sub(right.high)? - .checked_sub(u128::from(u8::from(borrow)))?; - Some(Self { high, low }) - } - - fn as_u128(self) -> Option { - (self.high == 0).then_some(self.low) - } -} - -fn subtraction_roundoff(recovered: f64, truth: f64, residual: f64) -> f64 { - let negated_truth = -truth; - let truth_virtual = residual - recovered; - let recovered_virtual = residual - truth_virtual; - let recovered_roundoff = recovered - recovered_virtual; - let truth_roundoff = negated_truth - truth_virtual; - recovered_roundoff + truth_roundoff -} - -fn positive_dyadic(value: f64) -> Option<(u128, i32)> { - if !value.is_finite() || value <= 0.0 { - return None; - } - let bits = value.to_bits(); - let exponent_bits = i32::try_from((bits >> 52) & 0x7ff).ok()?; - let fraction = bits & 0x000f_ffff_ffff_ffff; - let (mut significand, mut exponent) = if exponent_bits == 0 { - (u128::from(fraction), -1074) - } else { - ( - u128::from((1_u64 << 52) | fraction), - exponent_bits - 1023 - 52, - ) - }; - if significand == 0 { - return None; - } - let trailing = significand.trailing_zeros(); - significand >>= trailing; - exponent += i32::try_from(trailing).ok()?; - Some((significand, exponent)) -} - -fn multiply_by_power_of_two(value: u128, shift: u32) -> Option { - value.checked_mul(1_u128.checked_shl(shift)?) -} - -fn represented_values(sample_count: usize) -> Vec { - assert!(sample_count >= 3); - let diameter = (1_u64 << 53) as f64; +fn represented_integer_family(sample_count: usize) -> Vec { + assert!((4..=16).contains(&sample_count)); let mut values = Vec::with_capacity(sample_count); - values.extend([0.0, 1.0]); - values.extend((2..sample_count).map(|_| diameter)); + values.extend([0, 1]); + values.extend((2..sample_count).map(|_| DIAMETER_INTEGER)); values } -fn pairwise_exact_numerator(values: &[f64]) -> Option<(u128, i32)> { - let mut unit_exponent = i32::MAX; - for left in 0..values.len() { - for right in left + 1..values.len() { - let difference = values[left] - values[right]; - if !difference.is_finite() - || subtraction_roundoff(values[left], values[right], difference) != 0.0 - { - return None; - } - if difference != 0.0 { - unit_exponent = unit_exponent.min(positive_dyadic(difference.abs())?.1); - } - } - } - if unit_exponent == i32::MAX { - return Some((0, 0)); - } +fn represented_f64_family(sample_count: usize) -> Vec { + represented_integer_family(sample_count) + .into_iter() + .map(|value| value as f64) + .collect() +} +fn exact_integer_pair_square_sum(values: &[i128]) -> u128 { let mut pair_square_sum = 0_u128; for left in 0..values.len() { for right in left + 1..values.len() { - let difference = values[left] - values[right]; - if difference == 0.0 { - continue; - } - let (significand, exponent) = positive_dyadic(difference.abs())?; - let shift = exponent.checked_sub(unit_exponent)?.unsigned_abs(); - let coefficient = multiply_by_power_of_two(significand, shift)?; - pair_square_sum = pair_square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; - } - } - Some((pair_square_sum, unit_exponent)) -} - -fn neutral_zero_linear_exact_numerator(values: &[f64]) -> Option<(u128, i32, u128, u128)> { - let mut unit_exponent = i32::MAX; - for &coordinate in values { - if coordinate == 0.0 { - continue; - } - let (_, exponent) = positive_dyadic(coordinate.abs())?; - unit_exponent = unit_exponent.min(exponent); - } - if unit_exponent == i32::MAX { - return Some((0, 0, 0, 0)); - } - - let mut positive_sum = 0_u128; - let mut negative_sum = 0_u128; - let mut square_sum = 0_u128; - for &coordinate in values { - if coordinate == 0.0 { - continue; + let distance = (values[left] - values[right]).unsigned_abs(); + pair_square_sum = pair_square_sum + .checked_add( + distance + .checked_mul(distance) + .expect("bounded fixture square fits u128"), + ) + .expect("bounded fixture pair numerator fits u128"); } - let (significand, exponent) = positive_dyadic(coordinate.abs())?; - let shift = exponent.checked_sub(unit_exponent)?.unsigned_abs(); - let coefficient = multiply_by_power_of_two(significand, shift)?; - if coordinate.is_sign_negative() { - negative_sum = negative_sum.checked_add(coefficient)?; - } else { - positive_sum = positive_sum.checked_add(coefficient)?; - } - square_sum = square_sum.checked_add(coefficient.checked_mul(coefficient)?)?; } - - let signed_sum_magnitude = positive_sum.abs_diff(negative_sum); - let sample_count = u128::try_from(values.len()).ok()?; - let numerator = Wide256::multiply_u128(sample_count, square_sum) - .checked_sub(Wide256::multiply_u128( - signed_sum_magnitude, - signed_sum_magnitude, - ))? - .as_u128()?; - Some(( - numerator, - unit_exponent, - signed_sum_magnitude, - square_sum, - )) -} - -fn gcd(mut left: u128, mut right: u128) -> u128 { - while right != 0 { - let remainder = left % right; - left = right; - right = remainder; - } - left + pair_square_sum } #[test] -fn represented_pairwise_and_neutral_zero_routes_share_the_same_exact_ratio() { - for sample_count in [4_usize, 16, 17, 65, 257, 2_050] { - let values = represented_values(sample_count); - for value in values.iter().copied() { - let residual = value - 0.0; - assert_eq!( - subtraction_roundoff(value, 0.0, residual), - 0.0, - "represented residual construction must remain exact for n={sample_count}" - ); - } - - let (pair_numerator, pair_exponent) = - pairwise_exact_numerator(&values).expect("pairwise represented-input authority"); - let (linear_numerator, linear_exponent, signed_sum_magnitude, square_sum) = - neutral_zero_linear_exact_numerator(&values) - .expect("neutral-zero represented-input candidate"); +fn admitted_represented_families_match_independent_integer_pair_oracles() { + let cases = [ + ( + 4_usize, + 324_518_553_658_426_690_754_359_001_612_291_u128, + 0x4322_79a7_4590_331c_u64, + ), + ( + 16_usize, + 2_271_629_875_608_986_835_280_513_011_286_031_u128, + 0x4305_dc33_8d87_81a3_u64, + ), + ]; + + for (sample_count, expected_pair_numerator, expected_bits) in cases { + let integer_values = represented_integer_family(sample_count); assert_eq!( - linear_exponent, pair_exponent, - "common exact unit must agree" + exact_integer_pair_square_sum(&integer_values), + expected_pair_numerator, + "independent exact pair-distance oracle changed for n={sample_count}" ); + + let values = represented_f64_family(sample_count); + let truth = vec![0.0; sample_count]; assert_eq!( - linear_numerator, pair_numerator, - "neutral-zero O(n) identity must preserve the O(n²) exact pair numerator for n={sample_count}" + bias_standard_error(&truth, &values) + .expect("represented family has a finite standard error") + .to_bits(), + expected_bits, + "public exact route must preserve the audited correctly-rounded result for n={sample_count}" ); + } +} - let sample_count_u128 = u128::try_from(sample_count).expect("sample count fits u128"); - let denominator = sample_count_u128 - .checked_mul(sample_count_u128) - .and_then(|value| value.checked_mul(sample_count_u128 - 1)) - .expect("scientific denominator fits u128"); - let divisor = gcd(pair_numerator, denominator); +#[test] +fn admitted_neutral_zero_route_is_permutation_and_reversal_invariant() { + let sample_count = 16_usize; + let expected_bits = 0x4305_dc33_8d87_81a3_u64; + let truth = vec![0.0; sample_count]; + let values = represented_f64_family(sample_count); + + let mut reversed = values.clone(); + reversed.reverse(); + let mut rotated = values.clone(); + rotated.rotate_left(5); + + for candidate in [&values, &reversed, &rotated] { assert_eq!( - ( - linear_numerator / divisor, - denominator / divisor, - linear_exponent, - ), - ( - pair_numerator / divisor, - denominator / divisor, - pair_exponent, - ), - "both routes must present the exact rounder with the same reduced ratio" + bias_standard_error(&truth, candidate) + .expect("permuted represented family has a finite standard error") + .to_bits(), + expected_bits ); - - if sample_count == 2_050 { - assert!( - sample_count_u128.checked_mul(square_sum).is_none(), - "n=2050 must exercise the wider cancellation product" - ); - assert!( - signed_sum_magnitude - .checked_mul(signed_sum_magnitude) - .is_none(), - "n=2050 must exercise the wider squared-sum product" - ); - assert_eq!( - pair_numerator, - 332_306_998_946_228_931_332_463_617_650_984_961_u128 - ); - assert_eq!(denominator, 8_610_922_500); - assert_eq!(divisor, 1); - assert_eq!(pair_exponent, 0); - assert_eq!( - ((pair_numerator as f64) / (denominator as f64)).sqrt().to_bits(), - 0x4296_998e_1aff_78de, - "the shared exact ratio must reach the already-authoritative n=2050 exact-rounding fixture" - ); - } } } #[test] -fn neutral_zero_linear_route_is_order_invariant() { - let mut values = represented_values(65); - let forward = neutral_zero_linear_exact_numerator(&values).expect("forward route"); - values.reverse(); - let reversed = neutral_zero_linear_exact_numerator(&values).expect("reversed route"); - assert_eq!(forward.0, reversed.0); - assert_eq!(forward.1, reversed.1); +fn neutral_zero_route_recovers_a_represented_geometry_that_direct_pair_subtraction_loses() { + let represented = [-DIAMETER, 0.0, 1.0, DIAMETER]; + let exact_integers = [-DIAMETER_INTEGER, 0, 1, DIAMETER_INTEGER]; + assert_eq!( - forward.0, - pairwise_exact_numerator(&values) - .expect("reversed pair authority") - .0 + -DIAMETER - 1.0, + -DIAMETER, + "direct binary64 pair subtraction rounds away the unit difference at -2^53" ); -} - -#[test] -fn neutral_zero_route_admits_mixed_sign_geometry_when_pairwise_refuses() { - let diameter = (1_u64 << 53) as f64; - let values = [-diameter, 0.0, 1.0, diameter]; - assert!( - pairwise_exact_numerator(&values).is_none(), - "pairwise subtraction must refuse the represented -2^53 versus +1 difference" + assert_eq!( + exact_integer_pair_square_sum(&exact_integers), + (1_u128 << 109) + 3, + "independent integer oracle must retain the unit contribution" ); - let (numerator, unit_exponent, signed_sum_magnitude, square_sum) = - neutral_zero_linear_exact_numerator(&values).expect("neutral-zero exact proof"); - assert_eq!(unit_exponent, 0); - assert_eq!(signed_sum_magnitude, 1); - assert_eq!(square_sum, (1_u128 << 107) + 1); - assert_eq!(numerator, (1_u128 << 109) + 3); - let truth = [0.0; 4]; - let standard_error = bias_standard_error(&truth, &values).expect("finite standard error"); - assert_eq!(standard_error.to_bits(), 0x432a_20bd_700c_2c3e); + assert_eq!( + bias_standard_error(&truth, &represented) + .expect("neutral-zero exact proof recovers the represented geometry") + .to_bits(), + 0x432a_20bd_700c_2c3e + ); } From 3ddba8db8ffa489d6f648a3ad3be766af6d92c96 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 00:43:16 +0900 Subject: [PATCH 572/576] docs(research): make bias SE route evidence independent --- ...bias-standard-error-wide-linear-admission-bound.md | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/docs/research/bias-standard-error-wide-linear-admission-bound.md b/docs/research/bias-standard-error-wide-linear-admission-bound.md index 67f387611..e2bd7ec4f 100644 --- a/docs/research/bias-standard-error-wide-linear-admission-bound.md +++ b/docs/research/bias-standard-error-wide-linear-admission-bound.md @@ -36,7 +36,9 @@ Source-level RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` required a linear ne A later retention criterion required the conditioned observed-anchor fallback to demonstrate unique bounded admission after neutral-zero refusal. The checked-in production and unit-test corpus supplied no such represented fixture. Source-level RED `d40bfbf98b36562164d15d505f8f8825fd1c1349` rejected continued production reliance on the unsupported route, and repair `14e7862f4ddccce54f3b93d4dac89adbf047ba77` removed `exact_anchor_linear_pair_square_sum`. Production order is now `neutral_zero_linear -> pairwise_reference -> generic_fallback`. This is a consolidation decision under current evidence, not a theorem that pairwise is globally redundant. -Commit `da703fc50e136ed79e12262909c9f400a3945621` repairs the remaining equivalence characterization so it mirrors the production signed neutral-zero arithmetic rather than the removed minimum-anchor helper. For the common represented family `{0,1,2^53}`, it retains exact numerator/reduced-ratio equality against pairwise at `n=4,16,17,65,257,2050` and order invariance. It also adds the mixed-sign represented geometry `[-2^53,0,1,2^53]`: pairwise subtraction correctly refuses the inexact `-2^53` versus `+1` difference, while neutral-zero coordinates admit unit exponent `0`, `S1` magnitude `1`, `S2=2^107+1`, `P=2^109+3`, and the public exact route rounds to `0x432a20bd700c2c3e`. This is production-route admission evidence, not permission to widen `n`. +Commit `da703fc50e136ed79e12262909c9f400a3945621` was an intermediate correction that replaced the removed minimum-anchor helper in the equivalence characterization with a neutral-zero formulation. Review of that test exposed a second methodological defect: the integration test copied `Wide256`, dyadic parsing, scaling, and the neutral-zero kernel from production, so it was not an independent scientific oracle and it described `n=17..2050` as production-aligned even though production exact admission stops at 16. + +Repair `43d208383b765a3424019de45530af77ca484d78` removes the copied exact-route implementation from the integration test. The production-equivalence contract is now restricted to admitted `n=4` and `n=16` represented families and uses direct `i128/u128` integer pair-distance arithmetic as an independent oracle plus audited binary64 output bits. Forward, reversed, and rotated `n=16` inputs must return identical bits. The mixed-sign represented geometry `[-2^53,0,1,2^53]` remains an admission witness: direct binary64 subtraction of `-2^53-1` rounds away the unit contribution, while the independent integer pair oracle retains `P=2^109+3` and the public exact route returns `0x432a20bd700c2c3e`. Wider `n>16` arithmetic remains characterization evidence only and is not called a production-route test. Production sample admission remains `n=4..=16`. @@ -48,7 +50,7 @@ The timing vehicle remains `crates/validation_core/examples/bias_se_exact_proof_ ## Decision -Keep production `validation_core::bias_standard_error` at `n=4..=16`. Use the neutral-zero two-pass exact proof before quadratic proof work inside that budget, keep pairwise O(n²) as the fail-closed comparison/reference path, and fall back to the established general implementation when bounded exact proof refuses. Do not reintroduce observed-anchor scanning without a represented fixture that proves unique scientific admission value. +Keep production `validation_core::bias_standard_error` at `n=4..=16`. Use the neutral-zero two-pass exact proof before quadratic proof work inside that budget, keep pairwise O(n²) as the fail-closed comparison/reference path, and fall back to the established general implementation when bounded exact proof refuses. Do not reintroduce observed-anchor scanning without a represented fixture that proves unique scientific admission value. Scientific acceptance tests must use independent oracles or private production-unit tests; do not copy production exact arithmetic into an integration test and then treat agreement between the copies as independent evidence. Before widening beyond 16, require exact-head Rust 1.98.0 fmt/clippy/nextest/rustdoc and owned-production 100% line/branch coverage, same-head security/documentation GREEN and qualifying independent review, broad pair/neutral-zero equality wherever both admit, explicit neutral-zero-only represented fixtures, exact candidate stepping/midpoint/tie-to-even and permutation invariance, fail-closed overflow/range behavior, truthful production route telemetry, recorded release-mode raw CPU/allocator/RSS and applicable buyer-path p95 evidence, and current CHANGELOG/TRACEABILITY/TEST_STRATEGY/OPERABILITY/operator baseline. @@ -69,11 +71,12 @@ Before widening beyond 16, require exact-head Rust 1.98.0 fmt/clippy/nextest/rus - Neutral-anchor RED / correctness repair: `9f403194a2ec1636531c2dfe9229cfb34b73d747` / `6cf30eeb549c0df0377bda1111cf46396e8282a3` - Neutral-zero route-order RED / production resource repair: `e0b324864e48a503e2aba0d2a487a0b95f5276ed` / `2b62bd46eb0c391327d2285c2244a76f5a1e0449` - Unsupported conditioned-anchor retention RED / removal: `d40bfbf98b36562164d15d505f8f8825fd1c1349` / `14e7862f4ddccce54f3b93d4dac89adbf047ba77` -- Production-aligned neutral-zero equivalence and mixed-sign admission: `da703fc50e136ed79e12262909c9f400a3945621` +- Intermediate copied neutral-zero characterization: `da703fc50e136ed79e12262909c9f400a3945621` +- Independent production-domain pair oracle repair: `43d208383b765a3424019de45530af77ca484d78` - Narrow-wide-pair characterization RED / repair: `3136739460ef0c8e13c044a7e5b04891e4f4e23d` / `ce4ed2722e160eb0ca0ee2d636a5eca55e3ff2d5` - CHANGELOG fragment: `CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md` - Production module: `crates/validation_core/src/bias_se.rs` - Route-order contract: `crates/validation_core/tests/bias_standard_error_neutral_zero_route_order_contract.rs` -- Production-aligned equivalence characterization: `crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs` +- Independent production-domain equivalence characterization: `crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs` - Public regression: `crates/validation_core/tests/bias_standard_error_nonminimum_anchor_exact_rounding_contract.rs` - Exact-proof budget harness: `crates/validation_core/examples/bias_se_exact_proof_budget.rs` From 4521418dddabcf02d331b4581cc89e346d61641e Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 00:43:46 +0900 Subject: [PATCH 573/576] docs(changelog): correct bias SE oracle authority --- .../validation-bias-exact-proof-budget-characterization.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md index ca50cdc80..6a2d7b22b 100644 --- a/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md +++ b/CHANGELOG.d/validation-bias-exact-proof-budget-characterization.md @@ -13,5 +13,5 @@ - Repair `6cf30eeb549c0df0377bda1111cf46396e8282a3` temporarily included neutral zero plus represented residuals in a deterministic exact-anchor set and reused signed dyadic/Wide256 cancellation plus the exact midpoint rounder. That closed the demonstrated correctness defects but retained O(n²) anchor scanning. - Source-level route-order RED `e0b324864e48a503e2aba0d2a487a0b95f5276ed` required production to attempt `neutral_zero_linear` before quadratic proofs. Repair `2b62bd46eb0c391327d2285c2244a76f5a1e0449` added a two-pass neutral-zero signed-dyadic/Wide256 kernel, O(n) in time with O(1) proof storage after the residual vector. The superseded RED did not finish a hosted failing run and is not claimed as hosted RED evidence. - A later retention criterion required the conditioned observed-anchor fallback to prove unique bounded admission after neutral-zero refusal. The checked-in corpus supplied no such represented fixture. RED `d40bfbf98b36562164d15d505f8f8825fd1c1349` and repair `14e7862f4ddccce54f3b93d4dac89adbf047ba77` therefore remove that unsupported O(n²) route. Production order is now `neutral_zero_linear -> pairwise_reference -> generic_fallback`; this is evidence-based consolidation, not a theorem that pairwise is globally redundant. -- Commit `da703fc50e136ed79e12262909c9f400a3945621` replaces the stale minimum-anchor equivalence helper with the production signed neutral-zero arithmetic. The common `{0,1,2^53}` family retains pair/linear exact-numerator and reduced-ratio equality at `n=4,16,17,65,257,2050` plus order invariance. A mixed-sign fixture `[-2^53,0,1,2^53]` additionally proves neutral-zero admission when pairwise subtraction refuses: unit exponent `0`, signed-sum magnitude `1`, `Σc_i²=2^107+1`, `P=2^109+3`, and correctly rounded public bits `0x432a20bd700c2c3e`. +- Intermediate commit `da703fc50e136ed79e12262909c9f400a3945621` replaced the stale minimum-anchor helper but copied production `Wide256`, dyadic parsing, scaling, and neutral-zero arithmetic into the integration characterization and described `n=17..2050` as production-aligned despite the production `n=4..=16` admission. Repair `43d208383b765a3424019de45530af77ca484d78` removes that source copy: admitted `n=4` and `n=16` families now use an independent `i128/u128` direct pair-distance oracle plus audited output bits, forward/reversed/rotated order invariance is checked through the public API, and `[-2^53,0,1,2^53]` remains the neutral-zero-only admission witness with `P=2^109+3` and bits `0x432a20bd700c2c3e`. Wider `n>16` evidence remains characterization-only rather than a production-route test. - Keep production `bias_standard_error` sample admission unchanged at `n=4..=16`. A budget change beyond 16 still requires broad pair/neutral-zero same-domain equivalence, truthful production route evidence, recorded Rust 1.98.0 release-mode CPU/raw CSV and allocator/RSS evidence, applicable buyer-path p95 evidence, exact-head Rust/rustdoc/100% line+branch/security/documentation GREEN, current TRACEABILITY/research/operator doctoring, and qualifying independent current-head review. From eae187fcfc98e77a126907a06c7917fef90a5e2f Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 01:05:18 +0900 Subject: [PATCH 574/576] test(validation): cover every admitted bias-SE sample count --- ...nted_route_equivalence_characterization.rs | 118 +++++++++++++----- 1 file changed, 87 insertions(+), 31 deletions(-) diff --git a/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs b/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs index 79a65f6f4..a7322993e 100644 --- a/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs +++ b/crates/validation_core/tests/bias_standard_error_represented_route_equivalence_characterization.rs @@ -10,6 +10,74 @@ use validation_core::bias_standard_error; const DIAMETER_INTEGER: i128 = 1_i128 << 53; const DIAMETER: f64 = (1_u64 << 53) as f64; +const ADMITTED_CASES: [(usize, u128, u64); 13] = [ + ( + 4, + 324_518_553_658_426_690_754_359_001_612_291, + 0x4322_79a7_4590_331c, + ), + ( + 5, + 486_777_830_487_640_036_131_538_502_418_436, + 0x431f_5a7c_ecdb_684a, + ), + ( + 6, + 649_037_107_316_853_381_508_718_003_224_581, + 0x431a_fc19_d860_6169, + ), + ( + 7, + 811_296_384_146_066_726_885_897_504_030_726, + 0x4317_9b54_5654_ce5c, + ), + ( + 8, + 973_555_660_975_280_072_263_077_004_836_871, + 0x4314_f2ec_413c_b52a, + ), + ( + 9, + 1_135_814_937_804_493_417_640_256_505_643_016, + 0x4312_d071_7a82_a45e, + ), + ( + 10, + 1_298_074_214_633_706_763_017_436_006_449_161, + 0x4311_1111_1111_1111, + ), + ( + 11, + 1_460_333_491_462_920_108_394_615_507_255_306, + 0x430f_3940_7aa2_d4ec, + ), + ( + 12, + 1_622_592_768_292_133_453_771_795_008_061_451, + 0x430c_c40f_740a_8d6c, + ), + ( + 13, + 1_784_852_045_121_346_799_148_974_508_867_596, + 0x430a_a9db_d5af_20e5, + ), + ( + 14, + 1_947_111_321_950_560_144_526_154_009_673_741, + 0x4308_d86b_b06d_a1c7, + ), + ( + 15, + 2_109_370_598_779_773_489_903_333_510_479_886, + 0x4307_4208_c3da_686f, + ), + ( + 16, + 2_271_629_875_608_986_835_280_513_011_286_031, + 0x4305_dc33_8d87_81a3, + ), +]; + fn represented_integer_family(sample_count: usize) -> Vec { assert!((4..=16).contains(&sample_count)); let mut values = Vec::with_capacity(sample_count); @@ -43,21 +111,8 @@ fn exact_integer_pair_square_sum(values: &[i128]) -> u128 { } #[test] -fn admitted_represented_families_match_independent_integer_pair_oracles() { - let cases = [ - ( - 4_usize, - 324_518_553_658_426_690_754_359_001_612_291_u128, - 0x4322_79a7_4590_331c_u64, - ), - ( - 16_usize, - 2_271_629_875_608_986_835_280_513_011_286_031_u128, - 0x4305_dc33_8d87_81a3_u64, - ), - ]; - - for (sample_count, expected_pair_numerator, expected_bits) in cases { +fn every_admitted_sample_count_matches_an_independent_integer_pair_oracle() { + for (sample_count, expected_pair_numerator, expected_bits) in ADMITTED_CASES { let integer_values = represented_integer_family(sample_count); assert_eq!( exact_integer_pair_square_sum(&integer_values), @@ -78,24 +133,25 @@ fn admitted_represented_families_match_independent_integer_pair_oracles() { } #[test] -fn admitted_neutral_zero_route_is_permutation_and_reversal_invariant() { - let sample_count = 16_usize; - let expected_bits = 0x4305_dc33_8d87_81a3_u64; - let truth = vec![0.0; sample_count]; - let values = represented_f64_family(sample_count); +fn every_admitted_sample_count_is_permutation_and_reversal_invariant() { + for (sample_count, _, expected_bits) in ADMITTED_CASES { + let truth = vec![0.0; sample_count]; + let values = represented_f64_family(sample_count); - let mut reversed = values.clone(); - reversed.reverse(); - let mut rotated = values.clone(); - rotated.rotate_left(5); + let mut reversed = values.clone(); + reversed.reverse(); + let mut rotated = values.clone(); + rotated.rotate_left((sample_count / 3).max(1)); - for candidate in [&values, &reversed, &rotated] { - assert_eq!( - bias_standard_error(&truth, candidate) - .expect("permuted represented family has a finite standard error") - .to_bits(), - expected_bits - ); + for candidate in [&values, &reversed, &rotated] { + assert_eq!( + bias_standard_error(&truth, candidate) + .expect("permuted represented family has a finite standard error") + .to_bits(), + expected_bits, + "permutation changed the public result for n={sample_count}" + ); + } } } From 9634c5b97209f01b14041dd730b953d1325698af Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 02:01:16 +0900 Subject: [PATCH 575/576] test(validation): document exact-count rounding contracts --- ...standard_error_exact_residual_mean_roundoff_contract.rs | 7 +++++++ .../tests/wilson_sample_count_rounding_contract.rs | 7 +++++++ 2 files changed, 14 insertions(+) diff --git a/crates/validation_core/tests/bias_standard_error_exact_residual_mean_roundoff_contract.rs b/crates/validation_core/tests/bias_standard_error_exact_residual_mean_roundoff_contract.rs index 9f66ff697..62b14e587 100644 --- a/crates/validation_core/tests/bias_standard_error_exact_residual_mean_roundoff_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_exact_residual_mean_roundoff_contract.rs @@ -1,3 +1,10 @@ +//! Correctly rounded bias standard error from exact represented residual geometry. +//! +//! The contract fixes a three-observation case where rounding the residual mean +//! before dispersion changes the binary64 result. It also checks sign symmetry, +//! so uncertainty must be derived from the represented residual geometry rather +//! than from a prematurely rounded residual mean. + use validation_core::bias_standard_error; #[test] diff --git a/crates/validation_core/tests/wilson_sample_count_rounding_contract.rs b/crates/validation_core/tests/wilson_sample_count_rounding_contract.rs index b4f635c0c..96d9b7ca7 100644 --- a/crates/validation_core/tests/wilson_sample_count_rounding_contract.rs +++ b/crates/validation_core/tests/wilson_sample_count_rounding_contract.rs @@ -1,3 +1,10 @@ +//! Durable Wilson coverage evidence retains exact integer sample-count provenance. +//! +//! Counts above 2^53 are not exactly representable in binary64. These contracts +//! require Wilson endpoint reconstruction to use the versioned `u64` counts rather +//! than a pre-rounded floating denominator, including all-covered cases whose +//! positive miss mass remains representable immediately below one. + use validation_core::WilsonCoverageEvidenceV1; #[test] From a6cd5bd8d405428e1861ad4830adf24d6cacc883 Mon Sep 17 00:00:00 2001 From: Seongho Bae Date: Mon, 7 Sep 2026 03:06:14 +0900 Subject: [PATCH 576/576] test(validation): document additional rounding contracts --- ...ias_standard_error_common_high_mean_roundoff_contract.rs | 6 ++++++ .../bias_standard_error_distinct_high_roundoff_contract.rs | 6 ++++++ ...andard_error_eight_observation_pair_distance_contract.rs | 6 ++++++ ...ndard_error_eleven_observation_pair_distance_contract.rs | 6 ++++++ 4 files changed, 24 insertions(+) diff --git a/crates/validation_core/tests/bias_standard_error_common_high_mean_roundoff_contract.rs b/crates/validation_core/tests/bias_standard_error_common_high_mean_roundoff_contract.rs index e57f7225b..b886d5165 100644 --- a/crates/validation_core/tests/bias_standard_error_common_high_mean_roundoff_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_common_high_mean_roundoff_contract.rs @@ -1,3 +1,9 @@ +//! Preserves low-order represented dispersion when rounded residual highs coincide. +//! +//! The contract fixes a three-observation geometry where all binary64 residual +//! highs round to one while an exact subtraction low term changes the correctly +//! rounded standard error. Sign mirroring must leave that uncertainty unchanged. + use validation_core::bias_standard_error; #[test] diff --git a/crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs b/crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs index e7b849f9f..8ad719a0b 100644 --- a/crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_distinct_high_roundoff_contract.rs @@ -1,3 +1,9 @@ +//! Preserves subtraction low terms when represented residual high parts differ. +//! +//! The contract distinguishes exact represented-input dispersion from the larger +//! result obtained after prematurely rounding pairwise residuals, while retaining +//! sign symmetry and the established exact-residual control geometry. + use validation_core::bias_standard_error; #[test] diff --git a/crates/validation_core/tests/bias_standard_error_eight_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_eight_observation_pair_distance_contract.rs index 09d40ba7c..479aeed21 100644 --- a/crates/validation_core/tests/bias_standard_error_eight_observation_pair_distance_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_eight_observation_pair_distance_contract.rs @@ -1,3 +1,9 @@ +//! Eight-observation represented pair-distance rounding contract. +//! +//! Three orderings and their sign mirrors must preserve the audited exact +//! pair-distance ratio and its correctly rounded binary64 standard error, rather +//! than falling back to a translated floating-moment result one ULP higher. + use validation_core::bias_standard_error; fn assert_eight_observation_pair_distance_contract(recovered: [f64; 8]) { diff --git a/crates/validation_core/tests/bias_standard_error_eleven_observation_pair_distance_contract.rs b/crates/validation_core/tests/bias_standard_error_eleven_observation_pair_distance_contract.rs index a95a6f82b..5b39d22fb 100644 --- a/crates/validation_core/tests/bias_standard_error_eleven_observation_pair_distance_contract.rs +++ b/crates/validation_core/tests/bias_standard_error_eleven_observation_pair_distance_contract.rs @@ -1,3 +1,9 @@ +//! Eleven-observation represented pair-distance rounding contract. +//! +//! Audited forward, reversed, and permuted samples and their sign mirrors must +//! preserve the exact represented pair-distance ratio and the same correctly +//! rounded standard error instead of the one-ULP-low floating-moment fallback. + use validation_core::bias_standard_error; fn assert_eleven_observation_pair_distance_contract(recovered: [f64; 11]) {