use burn_backend::{
Backend, TensorMetadata, TensorPrimitive,
tensor::{FloatTensor, TensorContainer},
};
use crate::{
NodeId,
graph::{NodeRef, Requirement},
tensor::AutodiffTensor,
};
pub type GradID = u64;
pub struct Gradients {
container: TensorContainer<GradID>,
}
impl Gradients {
pub fn new<B: Backend>(root_node: NodeRef, root_tensor: FloatTensor<B>) -> Self {
let mut gradients = Self {
container: TensorContainer::new(),
};
gradients.register::<B>(
root_node.id,
B::float_ones(
root_tensor.shape(),
&B::float_device(&root_tensor),
root_tensor.dtype().into(),
),
);
gradients
}
pub fn consume<B: Backend>(&mut self, node: &NodeRef) -> FloatTensor<B> {
match node.requirement {
Requirement::Grad => self
.container
.get::<B>(&node.id.value)
.map(|tensor| tensor.tensor())
.expect("Can't consume the gradients before they are registered at least once."),
Requirement::GradInBackward => self
.container
.remove::<B>(&node.id.value)
.map(|tensor| tensor.tensor())
.expect("Can't consume the gradients before they are registered at least once."),
Requirement::None => panic!("Trying to consume the gradients for an untracked tensor"),
}
}
pub fn remove<B: Backend>(&mut self, tensor: &AutodiffTensor<B>) -> Option<FloatTensor<B>> {
self.container
.remove::<B>(&tensor.node.id.value)
.map(|tensor| tensor.tensor())
}
pub fn get<B: Backend>(&self, tensor: &AutodiffTensor<B>) -> Option<FloatTensor<B>> {
self.container
.get::<B>(&tensor.node.id.value)
.map(|tensor| tensor.tensor())
}
pub fn register<B: Backend>(&mut self, node_id: NodeId, value: FloatTensor<B>) {
if let Some(tensor_old) = self.container.remove::<B>(&node_id.value) {
self.container.register::<B>(
node_id.value,
TensorPrimitive::Float(B::float_add(value, tensor_old.tensor())),
);
} else {
self.container
.register::<B>(node_id.value, TensorPrimitive::Float(value));
}
}
}