#[must_use]
pub fn sigmoid(x: f32) -> f32 {
if x >= 0.0 {
1.0 / (1.0 + (-x).exp())
} else {
let e = x.exp();
e / (1.0 + e)
}
}
#[must_use]
pub fn silu(x: f32) -> f32 {
x * sigmoid(x)
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Si001Verdict { Pass, Fail }
#[must_use]
pub fn verdict_from_zero_preservation() -> Si001Verdict {
let v = silu(0.0);
if v.to_bits() == 0.0_f32.to_bits() { Si001Verdict::Pass } else { Si001Verdict::Fail }
}
pub const AC_SI_002_LOWER_BOUND: f32 = -0.279;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Si002Verdict { Pass, Fail }
#[must_use]
pub fn verdict_from_lower_bound(probes: &[f32]) -> Si002Verdict {
if probes.is_empty() { return Si002Verdict::Fail; }
for &x in probes {
if !x.is_finite() { return Si002Verdict::Fail; }
let s = silu(x);
if !s.is_finite() { return Si002Verdict::Fail; }
if s <= AC_SI_002_LOWER_BOUND { return Si002Verdict::Fail; }
}
Si002Verdict::Pass
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Si003Verdict { Pass, Fail }
#[must_use]
pub fn verdict_from_positive_monotonicity(probes: &[f32]) -> Si003Verdict {
if probes.len() < 2 { return Si003Verdict::Fail; }
let mut prev_x = f32::NEG_INFINITY;
let mut prev_silu = f32::NEG_INFINITY;
for &x in probes {
if !x.is_finite() || x <= 0.0 { return Si003Verdict::Fail; }
if x <= prev_x { return Si003Verdict::Fail; } let s = silu(x);
if !s.is_finite() { return Si003Verdict::Fail; }
if s <= prev_silu { return Si003Verdict::Fail; } prev_x = x;
prev_silu = s;
}
Si003Verdict::Pass
}
pub const AC_SI_004_ULP_TOLERANCE: u32 = 8;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Si004Verdict { Pass, Fail }
#[must_use]
pub fn ulp_distance(a: f32, b: f32) -> u32 {
if !a.is_finite() || !b.is_finite() { return u32::MAX; }
if a == b { return 0; }
let ai = a.to_bits() as i32;
let bi = b.to_bits() as i32;
let ord_a = if ai < 0 { i32::MIN.wrapping_sub(ai).wrapping_add(1) } else { ai };
let ord_b = if bi < 0 { i32::MIN.wrapping_sub(bi).wrapping_add(1) } else { bi };
ord_a.wrapping_sub(ord_b).unsigned_abs()
}
#[must_use]
pub fn verdict_from_simd_parity(scalar: &[f32], simd: &[f32]) -> Si004Verdict {
if scalar.is_empty() || simd.is_empty() { return Si004Verdict::Fail; }
if scalar.len() != simd.len() { return Si004Verdict::Fail; }
for (&s, &v) in scalar.iter().zip(simd.iter()) {
if !s.is_finite() || !v.is_finite() { return Si004Verdict::Fail; }
if ulp_distance(s, v) > AC_SI_004_ULP_TOLERANCE { return Si004Verdict::Fail; }
}
Si004Verdict::Pass
}
pub const AC_SI_005_TOLERANCE: f32 = 0.01;
pub const AC_SI_005_THRESHOLD: f32 = 10.0;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Si005Verdict { Pass, Fail }
#[must_use]
pub fn verdict_from_asymptotic_linearity(probes: &[f32]) -> Si005Verdict {
if probes.is_empty() { return Si005Verdict::Fail; }
for &x in probes {
if !x.is_finite() { return Si005Verdict::Fail; }
if x <= AC_SI_005_THRESHOLD { return Si005Verdict::Fail; } let s = silu(x);
if !s.is_finite() { return Si005Verdict::Fail; }
if (s - x).abs() >= AC_SI_005_TOLERANCE { return Si005Verdict::Fail; }
}
Si005Verdict::Pass
}
pub const AC_SI_006_TOLERANCE: f32 = 0.01;
pub const AC_SI_006_THRESHOLD: f32 = -10.0;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Si006Verdict { Pass, Fail }
#[must_use]
pub fn verdict_from_large_negative_boundary(probes: &[f32]) -> Si006Verdict {
if probes.is_empty() { return Si006Verdict::Fail; }
for &x in probes {
if !x.is_finite() { return Si006Verdict::Fail; }
if x >= AC_SI_006_THRESHOLD { return Si006Verdict::Fail; } let s = silu(x);
if !s.is_finite() { return Si006Verdict::Fail; }
if s.abs() >= AC_SI_006_TOLERANCE { return Si006Verdict::Fail; }
}
Si006Verdict::Pass
}
#[cfg(test)]
mod tests {
use super::*;
#[test] fn si001_pass() {
assert_eq!(verdict_from_zero_preservation(), Si001Verdict::Pass);
}
#[test] fn silu_at_zero_is_exactly_zero() {
assert_eq!(silu(0.0).to_bits(), 0.0_f32.to_bits());
}
#[test] fn si002_pass_canonical_range() {
let probes: Vec<f32> = (-100..100)
.step_by(5)
.map(|i| i as f32 / 10.0)
.collect();
assert_eq!(verdict_from_lower_bound(&probes), Si002Verdict::Pass);
}
#[test] fn si002_pass_at_global_minimum() {
let probes = [-1.2784_f32, -1.0, -1.5, -2.0];
assert_eq!(verdict_from_lower_bound(&probes), Si002Verdict::Pass);
}
#[test] fn si002_pass_at_extremes_within_finite_range() {
let probes = [-100.0_f32, -50.0, -10.0, 50.0, 100.0];
assert_eq!(verdict_from_lower_bound(&probes), Si002Verdict::Pass);
}
#[test] fn si002_fail_nan() {
assert_eq!(verdict_from_lower_bound(&[f32::NAN]), Si002Verdict::Fail);
}
#[test] fn si002_fail_inf() {
assert_eq!(verdict_from_lower_bound(&[f32::INFINITY]), Si002Verdict::Fail);
}
#[test] fn si002_fail_empty() {
assert_eq!(verdict_from_lower_bound(&[]), Si002Verdict::Fail);
}
#[test] fn si003_pass_sorted_positive() {
let probes = [0.1_f32, 0.5, 1.0, 2.0, 5.0, 10.0, 50.0];
assert_eq!(verdict_from_positive_monotonicity(&probes), Si003Verdict::Pass);
}
#[test] fn si003_fail_unsorted() {
let probes = [1.0_f32, 0.5, 2.0]; assert_eq!(verdict_from_positive_monotonicity(&probes), Si003Verdict::Fail);
}
#[test] fn si003_fail_zero() {
let probes = [0.0_f32, 1.0];
assert_eq!(verdict_from_positive_monotonicity(&probes), Si003Verdict::Fail);
}
#[test] fn si003_fail_negative() {
let probes = [-1.0_f32, 1.0];
assert_eq!(verdict_from_positive_monotonicity(&probes), Si003Verdict::Fail);
}
#[test] fn si003_fail_single_probe() {
let probes = [1.0_f32];
assert_eq!(verdict_from_positive_monotonicity(&probes), Si003Verdict::Fail);
}
#[test] fn si004_pass_identical() {
let a = vec![0.5_f32, 1.0, 2.0];
assert_eq!(verdict_from_simd_parity(&a, &a), Si004Verdict::Pass);
}
#[test] fn si004_pass_within_ulp() {
let a = vec![1.0_f32];
let b = vec![f32::from_bits(1.0_f32.to_bits() + 4)];
assert_eq!(verdict_from_simd_parity(&a, &b), Si004Verdict::Pass);
}
#[test] fn si004_fail_above_8_ulp() {
let a = vec![1.0_f32];
let b = vec![f32::from_bits(1.0_f32.to_bits() + 100)];
assert_eq!(verdict_from_simd_parity(&a, &b), Si004Verdict::Fail);
}
#[test] fn si005_pass_above_threshold() {
let probes = [11.0_f32, 15.0, 20.0, 50.0, 100.0];
assert_eq!(verdict_from_asymptotic_linearity(&probes), Si005Verdict::Pass);
}
#[test] fn si005_fail_at_threshold() {
let probes = [10.0_f32];
assert_eq!(verdict_from_asymptotic_linearity(&probes), Si005Verdict::Fail);
}
#[test] fn si005_fail_below_threshold() {
let probes = [5.0_f32];
assert_eq!(verdict_from_asymptotic_linearity(&probes), Si005Verdict::Fail);
}
#[test] fn si005_fail_nan() {
assert_eq!(verdict_from_asymptotic_linearity(&[f32::NAN]), Si005Verdict::Fail);
}
#[test] fn si006_pass_below_threshold() {
let probes = [-11.0_f32, -15.0, -20.0, -50.0, -100.0];
assert_eq!(verdict_from_large_negative_boundary(&probes), Si006Verdict::Pass);
}
#[test] fn si006_fail_at_threshold() {
let probes = [-10.0_f32];
assert_eq!(verdict_from_large_negative_boundary(&probes), Si006Verdict::Fail);
}
#[test] fn si006_fail_above_threshold() {
let probes = [-5.0_f32];
assert_eq!(verdict_from_large_negative_boundary(&probes), Si006Verdict::Fail);
}
#[test] fn silu_at_one_is_canonical() {
let s = silu(1.0);
assert!((s - 0.7310586).abs() < 1e-5);
}
#[test] fn provenance_constants() {
assert!((AC_SI_002_LOWER_BOUND - (-0.279)).abs() < 1e-9);
assert_eq!(AC_SI_004_ULP_TOLERANCE, 8);
assert!((AC_SI_005_TOLERANCE - 0.01).abs() < 1e-9);
assert!((AC_SI_005_THRESHOLD - 10.0).abs() < 1e-9);
assert!((AC_SI_006_TOLERANCE - 0.01).abs() < 1e-9);
assert!((AC_SI_006_THRESHOLD - (-10.0)).abs() < 1e-9);
}
}