pub const AC_LM_R2_TRAIN_FLOOR: f32 = 0.0;
pub const AC_LM_R2_PERFECT_TOLERANCE: f32 = 1e-3;
pub const AC_LM_PROB_SUM_TOLERANCE: f32 = 1e-6;
pub const AC_LM_PROB_SATURATION_EPSILON: f32 = 1e-30;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LmVerdict {
Pass,
Fail,
}
#[must_use]
pub fn verdict_from_r2_nonneg_train(r2_train: f32) -> LmVerdict {
if !r2_train.is_finite() {
return LmVerdict::Fail;
}
if r2_train >= AC_LM_R2_TRAIN_FLOOR {
LmVerdict::Pass
} else {
LmVerdict::Fail
}
}
#[must_use]
pub fn verdict_from_predict_deterministic(call_a: &[f32], call_b: &[f32]) -> LmVerdict {
if call_a.is_empty() || call_b.is_empty() || call_a.len() != call_b.len() {
return LmVerdict::Fail;
}
for (x, y) in call_a.iter().zip(call_b.iter()) {
if x.to_bits() != y.to_bits() {
return LmVerdict::Fail;
}
}
LmVerdict::Pass
}
#[must_use]
pub fn verdict_from_perfect_fit_r2(r2: f32) -> LmVerdict {
if !r2.is_finite() {
return LmVerdict::Fail;
}
if (r2 - 1.0).abs() <= AC_LM_R2_PERFECT_TOLERANCE {
LmVerdict::Pass
} else {
LmVerdict::Fail
}
}
#[must_use]
pub fn verdict_from_logistic_bounded(probs: &[f32]) -> LmVerdict {
if probs.is_empty() {
return LmVerdict::Fail;
}
for &p in probs {
if !p.is_finite() || p <= 0.0 || p >= 1.0 {
return LmVerdict::Fail;
}
}
LmVerdict::Pass
}
#[must_use]
pub fn verdict_from_probability_sums_to_one(p0: &[f32], p1: &[f32]) -> LmVerdict {
if p0.is_empty() || p1.is_empty() || p0.len() != p1.len() {
return LmVerdict::Fail;
}
for (a, b) in p0.iter().zip(p1.iter()) {
if !a.is_finite() || !b.is_finite() {
return LmVerdict::Fail;
}
let s = a + b;
if (s - 1.0).abs() > AC_LM_PROB_SUM_TOLERANCE {
return LmVerdict::Fail;
}
}
LmVerdict::Pass
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn provenance_constants() {
assert_eq!(AC_LM_R2_TRAIN_FLOOR, 0.0);
assert_eq!(AC_LM_R2_PERFECT_TOLERANCE, 1e-3);
assert_eq!(AC_LM_PROB_SUM_TOLERANCE, 1e-6);
assert_eq!(AC_LM_PROB_SATURATION_EPSILON, 1e-30);
}
#[test]
fn flm001_pass_zero_r2() {
let v = verdict_from_r2_nonneg_train(0.0);
assert_eq!(v, LmVerdict::Pass);
}
#[test]
fn flm001_pass_perfect() {
let v = verdict_from_r2_nonneg_train(1.0);
assert_eq!(v, LmVerdict::Pass);
}
#[test]
fn flm001_fail_negative_r2() {
let v = verdict_from_r2_nonneg_train(-0.001);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm001_fail_nan() {
let v = verdict_from_r2_nonneg_train(f32::NAN);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm002_pass_identical_calls() {
let v = verdict_from_predict_deterministic(&[1.0, 2.0, 3.0], &[1.0, 2.0, 3.0]);
assert_eq!(v, LmVerdict::Pass);
}
#[test]
fn flm002_fail_one_ulp_drift() {
let bumped = f32::from_bits(2.0_f32.to_bits() + 1);
let v = verdict_from_predict_deterministic(&[1.0, 2.0, 3.0], &[1.0, bumped, 3.0]);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm002_fail_length_mismatch() {
let v = verdict_from_predict_deterministic(&[1.0, 2.0], &[1.0, 2.0, 3.0]);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm002_fail_empty() {
let v = verdict_from_predict_deterministic(&[], &[]);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm003_pass_exact_one() {
let v = verdict_from_perfect_fit_r2(1.0);
assert_eq!(v, LmVerdict::Pass);
}
#[test]
fn flm003_pass_within_tolerance() {
let v = verdict_from_perfect_fit_r2(0.9995);
assert_eq!(v, LmVerdict::Pass);
}
#[test]
fn flm003_fail_below_tolerance() {
let v = verdict_from_perfect_fit_r2(0.95);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm003_fail_above_tolerance() {
let v = verdict_from_perfect_fit_r2(1.1);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm004_pass_typical_probs() {
let v = verdict_from_logistic_bounded(&[0.1, 0.5, 0.9, 0.99]);
assert_eq!(v, LmVerdict::Pass);
}
#[test]
fn flm004_fail_exact_zero() {
let v = verdict_from_logistic_bounded(&[0.5, 0.0, 0.7]);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm004_fail_exact_one() {
let v = verdict_from_logistic_bounded(&[0.5, 1.0, 0.7]);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm004_fail_negative() {
let v = verdict_from_logistic_bounded(&[-0.1]);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm004_fail_nan() {
let v = verdict_from_logistic_bounded(&[f32::NAN]);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm004_fail_empty() {
let v = verdict_from_logistic_bounded(&[]);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm005_pass_typical() {
let v = verdict_from_probability_sums_to_one(&[0.3, 0.2, 0.7], &[0.7, 0.8, 0.3]);
assert_eq!(v, LmVerdict::Pass);
}
#[test]
fn flm005_fail_sum_drift() {
let v = verdict_from_probability_sums_to_one(&[0.3], &[0.6]); assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm005_pass_within_tolerance() {
let v = verdict_from_probability_sums_to_one(&[0.5], &[0.5000001]);
assert_eq!(v, LmVerdict::Pass);
}
#[test]
fn flm005_fail_length_mismatch() {
let v = verdict_from_probability_sums_to_one(&[0.3], &[0.7, 0.5]);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn flm005_fail_nan() {
let v = verdict_from_probability_sums_to_one(&[f32::NAN], &[0.5]);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn mutation_survey_001_r2_around_zero() {
for r2_x100 in [-1_i32, 0, 1, 10, 50, 100] {
let r2 = r2_x100 as f32 / 100.0;
let v = verdict_from_r2_nonneg_train(r2);
let want = if r2 >= 0.0 {
LmVerdict::Pass
} else {
LmVerdict::Fail
};
assert_eq!(v, want, "r2={r2}");
}
}
#[test]
fn mutation_survey_004_probability_band() {
let probs = [0.001_f32, 0.01, 0.5, 0.99, 0.999];
let v = verdict_from_logistic_bounded(&probs);
assert_eq!(v, LmVerdict::Pass);
let bad = [0.001_f32, 0.0, 0.5, 0.99];
let v = verdict_from_logistic_bounded(&bad);
assert_eq!(v, LmVerdict::Fail);
}
#[test]
fn realistic_healthy_passes_all_5() {
let v1 = verdict_from_r2_nonneg_train(0.85);
let v2 = verdict_from_predict_deterministic(&[1.5, 2.5, 3.5], &[1.5, 2.5, 3.5]);
let v3 = verdict_from_perfect_fit_r2(0.99995);
let v4 = verdict_from_logistic_bounded(&[0.1, 0.5, 0.9]);
let v5 = verdict_from_probability_sums_to_one(&[0.3, 0.7], &[0.7, 0.3]);
assert_eq!(v1, LmVerdict::Pass);
assert_eq!(v2, LmVerdict::Pass);
assert_eq!(v3, LmVerdict::Pass);
assert_eq!(v4, LmVerdict::Pass);
assert_eq!(v5, LmVerdict::Pass);
}
#[test]
fn realistic_pre_fix_all_5_failures() {
let v1 = verdict_from_r2_nonneg_train(-0.01);
let bumped = f32::from_bits(2.0_f32.to_bits() + 1);
let v2 = verdict_from_predict_deterministic(&[1.0, 2.0], &[1.0, bumped]);
let v3 = verdict_from_perfect_fit_r2(0.85);
let v4 = verdict_from_logistic_bounded(&[0.5, 1.0]);
let v5 = verdict_from_probability_sums_to_one(&[0.3], &[0.7 - 0.01]);
assert_eq!(v1, LmVerdict::Fail);
assert_eq!(v2, LmVerdict::Fail);
assert_eq!(v3, LmVerdict::Fail);
assert_eq!(v4, LmVerdict::Fail);
assert_eq!(v5, LmVerdict::Fail);
}
}