use burn_tensor::{Tensor, TensorData};
use crate::Backend;
#[derive(Debug, Clone, Copy)]
pub struct Burn<const D: usize>;
impl<const D: usize> Burn<D> {
fn device() -> burn_tensor::Device {
burn_tensor::Device::default().autodiff()
}
}
impl<const D: usize> Backend for Burn<D> {
type Tensor = Tensor<D>;
fn name() -> &'static str {
"burn"
}
fn from_slice(data: &[f64], shape: &[usize]) -> Self::Tensor {
assert_eq!(
shape.len(),
D,
"gradcheck/burn: shape {shape:?} has rank {} but this adapter is Burn<{D}>",
shape.len()
);
let values: Vec<f32> = data.iter().map(|&v| v as f32).collect();
Tensor::from_data(TensorData::new(values, shape.to_vec()), &Self::device())
}
fn to_vec(t: &Self::Tensor) -> Vec<f64> {
t.clone()
.to_data()
.to_vec::<f32>()
.expect("gradcheck/burn: could not read tensor data as f32")
.into_iter()
.map(|v| v as f64)
.collect()
}
fn forward_sum(f: &dyn Fn(Self::Tensor) -> Self::Tensor, x: &Self::Tensor) -> f64 {
let out: Vec<f32> = f(x.clone())
.sum()
.to_data()
.to_vec::<f32>()
.expect("gradcheck/burn: could not read the forward result");
out[0] as f64
}
fn analytic_grad(f: &dyn Fn(Self::Tensor) -> Self::Tensor, x: &Self::Tensor) -> Vec<f64> {
let tracked = x.clone().require_grad();
let grads = f(tracked.clone()).sum().backward();
let g = tracked.grad(&grads).expect(
"gradcheck/burn: no gradient was recorded for the input. The tensor was marked \
require_grad, so this usually means the function under test detached it (for \
example by going through `into_data` or a non-differentiable op).",
);
g.to_data()
.to_vec::<f32>()
.expect("gradcheck/burn: could not read the gradient as f32")
.into_iter()
.map(|v| v as f64)
.collect()
}
}