use std::cell::Cell;
use crate::tensor::Tensor;
thread_local! {
static ENABLED: Cell<bool> = const { Cell::new(false) };
}
pub fn is_detecting() -> bool {
ENABLED.with(|flag| flag.get())
}
pub fn detect_anomaly<T>(f: impl FnOnce() -> T) -> T {
let _guard = Enabled::new();
f()
}
pub(crate) fn check(op: &str, grads: &[Tensor]) {
for (index, grad) in grads.iter().enumerate() {
if let Some(report) = first_bad(grad) {
panic!("anomaly: {op} produced {report} in the gradient for input {index}");
}
}
}
fn first_bad(t: &Tensor) -> Option<String> {
let data = t.to_vec();
let (slot, value) = data.iter().enumerate().find(|(_, v)| !v.is_finite())?;
let kind = if value.is_nan() { "NaN" } else { "inf" };
Some(format!("{kind} at element {slot} of {:?}", t.shape()))
}
struct Enabled(bool);
impl Enabled {
fn new() -> Self {
Enabled(ENABLED.with(|flag| flag.replace(true)))
}
}
impl Drop for Enabled {
fn drop(&mut self) {
ENABLED.with(|flag| flag.set(self.0));
}
}
impl Tensor {
pub fn is_finite(&self) -> bool {
self.to_vec().iter().all(|v| v.is_finite())
}
}