use crate::Backend;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Precision {
F32,
F64,
}
pub fn realized_step_with(x: f64, h: f64, p: Precision) -> f64 {
match p {
Precision::F32 => {
let up = (x + h) as f32 as f64;
let down = (x - h) as f32 as f64;
(up - down) / 2.0
}
Precision::F64 => ((x + h) - (x - h)) / 2.0,
}
}
pub fn realized_step(x: f64, h: f64) -> f64 {
realized_step_with(x, h, Precision::F32)
}
pub fn numerical_gradient<B, F>(
f: &F,
data: &[f64],
shape: &[usize],
step: f64,
precision: Precision,
) -> Vec<f64>
where
B: Backend,
F: Fn(B::Tensor) -> B::Tensor,
{
assert!(step > 0.0, "gradcheck: step must be positive, got {step}");
let mut out = Vec::with_capacity(data.len());
let mut probe = data.to_vec();
for i in 0..data.len() {
let original = probe[i];
let realized = realized_step_with(original, step, precision);
probe[i] = original + step;
let up = B::forward_sum(f, &B::from_slice(&probe, shape));
probe[i] = original - step;
let down = B::forward_sum(f, &B::from_slice(&probe, shape));
probe[i] = original;
if realized == 0.0 {
out.push(f64::NAN);
} else {
out.push((up - down) / (2.0 * realized));
}
}
out
}