pub use crate::tensor::BasicAutodiffOps;
use crate::api::{Tensor, TensorPrimitive, backend::AutodiffBackend};
macro_rules! value_autodiff_context {
($kind:ty, $get:ident) => {
impl<const D: usize, B: crate::Backend> Tensor<B, D, $kind> {
pub fn autodiff_context(&self) -> crate::AutodiffContext { B::$get(&self.primitive) }
pub fn is_autodiff(&self) -> bool { self.autodiff_context() != crate::AutodiffContext::Disabled }
pub fn autodiff(self) -> Self {
if self.is_autodiff() { return self; }
self.with_autodiff_context(crate::AutodiffContext::Enabled(crate::GradientCheckpointingStrategy::Disabled))
}
pub fn without_autodiff(self) -> Self { self.with_autodiff_context(crate::AutodiffContext::Disabled) }
pub fn into_autodiff<A: AutodiffBackend<InnerBackend = B>>(self) -> Tensor<A, D, $kind> {
Tensor::<A, D, $kind>::from_inner(self)
}
pub fn gradient_checkpointing_strategy(&self) -> Option<crate::GradientCheckpointingStrategy> {
match self.autodiff_context() {
crate::AutodiffContext::Disabled => None,
crate::AutodiffContext::Enabled(policy) => Some(policy),
}
}
pub fn with_gradient_checkpointing_strategy(self, strategy: crate::GradientCheckpointingStrategy) -> Self {
assert!(self.is_autodiff(), "Tensor::with_gradient_checkpointing_strategy requires autodiff; call Tensor::autodiff first");
self.with_autodiff_context(crate::AutodiffContext::Enabled(strategy))
}
}
};
}
value_autodiff_context!(crate::api::Int, int_autodiff_context);
value_autodiff_context!(crate::api::Bool, bool_autodiff_context);
impl<const D: usize, B: AutodiffBackend> Tensor<B, D> {
pub fn backward(&self) -> B::Gradients {
B::backward(self.primitive.clone().tensor())
}
pub fn grad(&self, grads: &B::Gradients) -> Option<Tensor<B::InnerBackend, D>> {
match &self.primitive {
TensorPrimitive::Float(tensor) => B::grad(tensor, grads)
.map(TensorPrimitive::Float)
.map(Tensor::new),
TensorPrimitive::QFloat(tensor) => B::q_grad(tensor, grads)
.map(TensorPrimitive::Float)
.map(Tensor::new),
}
}
pub fn grad_remove(&self, grads: &mut B::Gradients) -> Option<Tensor<B::InnerBackend, D>> {
match &self.primitive {
TensorPrimitive::Float(tensor) => B::grad_remove(tensor, grads)
.map(TensorPrimitive::Float)
.map(Tensor::new),
TensorPrimitive::QFloat(tensor) => {
B::q_grad_remove(tensor, grads)
.map(TensorPrimitive::Float)
.map(Tensor::new)
}
}
}
pub fn grad_replace(&self, grads: &mut B::Gradients, grad: Tensor<B::InnerBackend, D>) {
match &self.primitive {
TensorPrimitive::Float(tensor) => {
B::grad_replace(tensor, grads, grad.primitive.tensor())
}
TensorPrimitive::QFloat(tensor) => B::q_grad_replace(
tensor,
grads,
grad.primitive.tensor(),
),
}
}
}
impl<const D: usize, B: AutodiffBackend, K: BasicAutodiffOps<B>> Tensor<B, D, K> {
pub fn inner(self) -> Tensor<B::InnerBackend, D, K::InnerKind> {
Tensor::new(K::inner(self.primitive))
}
pub fn from_inner(inner: Tensor<B::InnerBackend, D, K::InnerKind>) -> Self {
Self::new(K::from_inner(inner.primitive))
}
}