#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Dtype {
F64,
F32,
Tf32,
F16,
Bf16,
}
impl Dtype {
#[must_use]
pub const fn precision(self) -> i32 {
match self {
Self::F64 => 53,
Self::F32 => 24,
Self::Tf32 | Self::F16 => 11,
Self::Bf16 => 8,
}
}
#[must_use]
pub fn unit_roundoff(self) -> f64 {
2f64.powi(-self.precision())
}
#[must_use]
pub fn min_normal(self) -> f64 {
match self {
Self::F64 => f64::MIN_POSITIVE,
Self::F32 | Self::Tf32 | Self::Bf16 => 2f64.powi(-126),
Self::F16 => 2f64.powi(-14),
}
}
#[must_use]
pub fn gamma(self, n: usize) -> Option<f64> {
#[allow(clippy::cast_precision_loss)] let nu = n as f64 * self.unit_roundoff();
(nu < 1.0).then(|| nu / (1.0 - nu))
}
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum ErrorModel {
Dot {
k: usize,
acc: Dtype,
out: Dtype,
ftz: bool,
},
Sum {
n: usize,
acc: Dtype,
out: Dtype,
ftz: bool,
},
Max,
Dequant,
Elementwise,
Softmax,
Attention,
Rope,
}
#[derive(Debug, Clone, PartialEq)]
pub enum Refusal {
Vacuous {
id: &'static str,
n: usize,
acc: Dtype,
},
NotImplemented { id: &'static str },
LengthMismatch {
yhat: usize,
oracle: usize,
bound: usize,
},
}
impl std::fmt::Display for Refusal {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
Self::Vacuous { id, n, acc } => write!(
f,
"{id}: n = {n} with a {acc:?} accumulator gives n·u ≥ 1, so no bound holds (K1b)"
),
Self::NotImplemented { id } => {
write!(
f,
"{id}: no implemented error bound; refusing to guess one (S-1)"
)
},
Self::LengthMismatch {
yhat,
oracle,
bound,
} => write!(
f,
"length mismatch: {yhat} outputs, {oracle} oracle values, {bound} bounds"
),
}
}
}
impl std::error::Error for Refusal {}
#[derive(Debug, Clone, Copy, PartialEq)]
pub struct Bound {
pub scale: f64,
pub absolute: f64,
}
impl Bound {
#[must_use]
pub fn at(self, magnitude: f64) -> f64 {
self.scale * magnitude + self.absolute
}
#[must_use]
pub fn over(self, magnitudes: &[f64]) -> Vec<f64> {
magnitudes.iter().map(|&m| self.at(m)).collect()
}
}
fn oracle_slack(n: usize) -> f64 {
Dtype::F64.gamma(n.max(1)).unwrap_or(f64::INFINITY)
}
fn dot_like(
id: &'static str,
n: usize,
acc: Dtype,
out: Dtype,
ftz: bool,
) -> Result<Bound, Refusal> {
let gamma = acc.gamma(n).ok_or(Refusal::Vacuous { id, n, acc })?;
let store = if out.precision() < acc.precision() {
out.unit_roundoff()
} else {
0.0
};
#[allow(clippy::cast_precision_loss)] let absolute = if ftz {
n as f64 * acc.min_normal()
} else {
0.0
};
Ok(Bound {
scale: gamma + store + oracle_slack(n),
absolute,
})
}
impl ErrorModel {
#[must_use]
pub const fn id(&self) -> &'static str {
match self {
Self::Dot { .. } => "EM-DOT",
Self::Sum { .. } | Self::Max => "EM-RED",
Self::Dequant => "EM-DEQ",
Self::Elementwise => "EM-ELEM",
Self::Softmax => "EM-SMX",
Self::Attention => "EM-ATT",
Self::Rope => "EM-ROPE",
}
}
pub fn bound(&self) -> Result<Bound, Refusal> {
let id = self.id();
match *self {
Self::Dot { k, acc, out, ftz } => dot_like(id, k, acc, out, ftz),
Self::Sum { n, acc, out, ftz } => dot_like(id, n, acc, out, ftz),
Self::Max | Self::Dequant => Ok(Bound {
scale: 0.0,
absolute: 0.0,
}),
Self::Elementwise | Self::Softmax | Self::Attention | Self::Rope => {
Err(Refusal::NotImplemented { id })
},
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn k1b_vacuity_matches_the_lean_lemmas() {
assert_eq!(Dtype::F16.gamma(4096), None);
assert_eq!(Dtype::F16.gamma(2048), None, "K·u = 1 is already vacuous");
assert!(Dtype::F16.gamma(2047).is_some());
assert_eq!(Dtype::Bf16.gamma(256), None);
assert!(Dtype::Bf16.gamma(255).is_some());
assert_eq!(Dtype::Tf32.gamma(4096), None, "TF32 is not f32 for K1b");
let g = Dtype::F32
.gamma(1 << 20)
.expect("f32 is meaningful up to 2^20");
assert!((g - 1.0 / 15.0).abs() < 1e-15, "γ_(2^20) = 1/15, got {g}");
assert!(Dtype::F32.gamma(4096).is_some());
}
#[test]
fn f16_ftz_drift_quarter() {
let eta = 4096.0 * Dtype::F16.min_normal();
assert!((eta - 0.25).abs() < f64::EPSILON);
}
#[test]
fn a_vacuous_accumulator_is_refused_not_loosened() {
let m = ErrorModel::Dot {
k: 4096,
acc: Dtype::F16,
out: Dtype::F16,
ftz: false,
};
assert_eq!(
m.bound(),
Err(Refusal::Vacuous {
id: "EM-DOT",
n: 4096,
acc: Dtype::F16
})
);
}
#[test]
fn unimplemented_models_refuse_s1() {
for m in [
ErrorModel::Elementwise,
ErrorModel::Softmax,
ErrorModel::Attention,
ErrorModel::Rope,
] {
assert!(
matches!(m.bound(), Err(Refusal::NotImplemented { id }) if id == m.id()),
"{} must refuse, not guess",
m.id()
);
}
}
#[test]
fn dot_bound_terms() {
let f32_dot = |out, ftz| {
ErrorModel::Dot {
k: 1024,
acc: Dtype::F32,
out,
ftz,
}
.bound()
.expect("f32 at K = 1024 has a bound")
};
let g = Dtype::F32.gamma(1024).expect("finite");
let plain = f32_dot(Dtype::F32, false);
assert!(plain.scale >= g && plain.scale < g * 1.000_001);
assert!(plain.absolute.abs() < f64::MIN_POSITIVE);
let to_f16 = f32_dot(Dtype::F16, false);
assert!((to_f16.scale - plain.scale - 2f64.powi(-11)).abs() < 1e-18);
let ftz = f32_dot(Dtype::F32, true);
assert!((ftz.absolute - 1024.0 * 2f64.powi(-126)).abs() < 1e-45);
assert!((plain.at(2.0) - 2.0 * plain.scale).abs() < f64::MIN_POSITIVE);
let exact = ErrorModel::Dequant.bound().expect("bit-exact bound");
assert!(exact.at(123.0).abs() < f64::MIN_POSITIVE);
}
}