use std::sync::{Arc, Mutex};
use crate::tensor::Tensor;
pub trait Backward: Send + Sync {
fn backward(&self, grad: &Tensor) -> Vec<Tensor>;
fn name(&self) -> &'static str;
}
pub struct Op {
pub rule: Box<dyn Backward>,
pub inputs: Vec<Tensor>,
}
#[derive(Clone)]
pub enum Node {
Leaf(GradSlot),
Op(Arc<Op>),
}
impl Node {
pub fn op(rule: impl Backward + 'static, inputs: Vec<Tensor>) -> Node {
Node::Op(Arc::new(Op {
rule: Box::new(rule),
inputs,
}))
}
pub fn id(&self) -> NodeId {
match self {
Node::Leaf(slot) => NodeId(Arc::as_ptr(&slot.0) as *const () as usize),
Node::Op(op) => NodeId(Arc::as_ptr(op) as *const () as usize),
}
}
}
#[derive(Clone, Copy, PartialEq, Eq, Hash, Debug)]
pub struct NodeId(usize);
#[derive(Clone, Default)]
pub struct GradSlot(Arc<Mutex<Option<Tensor>>>);
impl GradSlot {
pub fn new() -> Self {
GradSlot::default()
}
pub fn accumulate(&self, grad: Tensor) {
let mut slot = self.0.lock().unwrap();
*slot = Some(match slot.take() {
Some(existing) => existing.add(&grad),
None => grad,
});
}
pub fn get(&self) -> Option<Tensor> {
self.0.lock().unwrap().clone()
}
pub fn clear(&self) {
*self.0.lock().unwrap() = None;
}
}