#![cfg(feature = "burn")]
use burn_tensor::{Tensor, TensorData};
#[test]
fn first_derivative_is_the_baseline() {
let dev = burn_tensor::Device::default().autodiff();
let x = Tensor::<1>::from_data(TensorData::new(vec![2.0f32], vec![1]), &dev).require_grad();
let y = x.clone() * x.clone() * x.clone();
let g = y.sum().backward();
let d: Vec<f32> = x.grad(&g).unwrap().to_data().to_vec().unwrap();
println!("B5 f(x)=x^3 at x=2 -> f' = {:?} (expected [12.0])", d);
assert!((d[0] - 12.0).abs() < 1e-3);
}
#[test]
#[should_panic(expected = "Only first-order autodiff is supported")]
fn can_the_autodiff_device_be_nested() {
let once = burn_tensor::Device::default().autodiff();
let twice = once.clone().autodiff();
println!("B5 device.autodiff() = {once:?}");
println!("B5 device.autodiff().autodiff() = {twice:?}");
println!(
"B5 nesting changes the device = {}",
format!("{once:?}") != format!("{twice:?}")
);
}
#[test]
#[should_panic(expected = "Only first-order autodiff is supported")]
fn second_derivative_attempt() {
let dev = burn_tensor::Device::default().autodiff().autodiff();
let x = Tensor::<1>::from_data(TensorData::new(vec![2.0f32], vec![1]), &dev).require_grad();
let y = x.clone() * x.clone() * x.clone();
let g1 = y.sum().backward();
let d1 = x.grad(&g1).expect("first gradient must exist");
let d1v: Vec<f32> = d1.clone().to_data().to_vec().unwrap();
println!("B5 first grad = {d1v:?}");
println!("B5 grad.require_grad? -> attempting second backward");
let g2 = d1.sum().backward();
match x.grad(&g2) {
Some(t) => {
let v: Vec<f32> = t.to_data().to_vec().unwrap();
println!("B5 SECOND DERIVATIVE = {v:?} (expected [12.0])");
}
None => println!("B5 SECOND DERIVATIVE = None -- the returned gradient is not tracked"),
}
}