1use crate::Tensor;
10use ferric_core::Context;
11use std::cell::RefCell;
12use std::rc::Rc;
13use std::sync::Arc;
14
15type BackFn = dyn Fn(&Tensor, &[Var]);
16
17struct Inner {
18 value: Tensor,
19 grad: RefCell<Option<Tensor>>,
20 parents: Vec<Var>,
21 backward: Option<Box<BackFn>>,
22}
23
24#[derive(Clone)]
25pub struct Var(Rc<Inner>);
26
27impl Var {
28 pub fn leaf(t: Tensor) -> Var {
30 Var(Rc::new(Inner { value: t, grad: RefCell::new(None), parents: vec![], backward: None }))
31 }
32 fn node(value: Tensor, parents: Vec<Var>, backward: Box<BackFn>) -> Var {
33 Var(Rc::new(Inner { value, grad: RefCell::new(None), parents, backward: Some(backward) }))
34 }
35 pub fn value(&self) -> &Tensor { &self.0.value }
36 pub fn grad(&self) -> Option<Tensor> { self.0.grad.borrow().clone() }
37
38 fn ctx(&self) -> Arc<Context> { self.0.value.ctx_arc() }
39 fn accumulate(&self, g: &Tensor) {
40 let g = unbroadcast(g, &self.0.value.shape);
41 let mut slot = self.0.grad.borrow_mut();
42 *slot = Some(match slot.take() {
43 Some(prev) => prev.add(&g),
44 None => g,
45 });
46 }
47
48 pub fn backward(&self) {
50 let mut topo: Vec<Var> = vec![];
52 let mut seen: Vec<*const Inner> = vec![];
53 fn visit(v: &Var, topo: &mut Vec<Var>, seen: &mut Vec<*const Inner>) {
54 let p = Rc::as_ptr(&v.0);
55 if seen.contains(&p) { return; }
56 seen.push(p);
57 for par in &v.0.parents { visit(par, topo, seen); }
58 topo.push(v.clone());
59 }
60 visit(self, &mut topo, &mut seen);
61 let ones = Tensor::from_vec(&self.ctx(), &vec![1.0; self.0.value.numel()], &self.0.value.shape);
62 *self.0.grad.borrow_mut() = Some(ones);
63 for v in topo.iter().rev() {
64 if let Some(bw) = &v.0.backward {
65 let g = v.0.grad.borrow().clone().expect("grad set before backward");
66 bw(&g, &v.0.parents);
67 }
68 }
69 }
70
71 pub fn add(&self, o: &Var) -> Var {
73 let out = self.0.value.add(&o.0.value);
74 Var::node(out, vec![self.clone(), o.clone()], Box::new(|g, p| { p[0].accumulate(g); p[1].accumulate(g); }))
75 }
76 pub fn sub(&self, o: &Var) -> Var {
77 let out = self.0.value.sub(&o.0.value);
78 Var::node(out, vec![self.clone(), o.clone()], Box::new(|g, p| { p[0].accumulate(g); p[1].accumulate(&g.neg()); }))
79 }
80 pub fn mul(&self, o: &Var) -> Var {
81 let out = self.0.value.mul(&o.0.value);
82 let (a, b) = (self.0.value.clone(), o.0.value.clone());
83 Var::node(out, vec![self.clone(), o.clone()], Box::new(move |g, p| {
84 p[0].accumulate(&g.mul(&b));
85 p[1].accumulate(&g.mul(&a));
86 }))
87 }
88 pub fn matmul(&self, o: &Var) -> Var {
90 let out = self.0.value.matmul(&o.0.value);
91 let (a, b) = (self.0.value.clone(), o.0.value.clone());
92 Var::node(out, vec![self.clone(), o.clone()], Box::new(move |g, p| {
93 let r = a.rank();
94 p[0].accumulate(&g.matmul(&b.transpose(r - 1, r - 2))); p[1].accumulate(&a.transpose(r - 1, r - 2).matmul(g)); }))
97 }
98 pub fn relu(&self) -> Var {
99 let out = self.0.value.relu();
100 let x = self.0.value.clone();
101 Var::node(out, vec![self.clone()], Box::new(move |g, p| {
102 let mask = x.relu_mask();
104 p[0].accumulate(&g.mul(&mask));
105 }))
106 }
107 pub fn neg(&self) -> Var {
108 let out = self.0.value.neg();
109 Var::node(out, vec![self.clone()], Box::new(|g, p| { p[0].accumulate(&g.neg()); }))
110 }
111 pub fn transpose(&self, a: usize, b: usize) -> Var {
113 let out = self.0.value.transpose(a, b);
114 Var::node(out, vec![self.clone()], Box::new(move |g, p| { p[0].accumulate(&g.transpose(a, b)); }))
115 }
116 pub fn div(&self, o: &Var) -> Var {
117 let out = self.0.value.div(&o.0.value);
118 let (a, b) = (self.0.value.clone(), o.0.value.clone());
119 Var::node(out, vec![self.clone(), o.clone()], Box::new(move |g, p| {
120 p[0].accumulate(&g.div(&b)); p[1].accumulate(&g.mul(&a).neg().div(&b.mul(&b))); }))
123 }
124 pub fn exp(&self) -> Var {
125 let out = self.0.value.exp();
126 let o2 = out.clone();
127 Var::node(out, vec![self.clone()], Box::new(move |g, p| { p[0].accumulate(&g.mul(&o2)); }))
128 }
129 pub fn log(&self) -> Var {
130 let out = self.0.value.log();
131 let x = self.0.value.clone();
132 Var::node(out, vec![self.clone()], Box::new(move |g, p| { p[0].accumulate(&g.div(&x)); }))
133 }
134 pub fn sum(&self, axes: &[usize]) -> Var {
136 let out = self.0.value.sum(axes, true);
137 let ishape = self.0.value.shape.clone();
138 Var::node(out, vec![self.clone()], Box::new(move |g, p| {
139 p[0].accumulate(&g.broadcast_to(&ishape));
140 }))
141 }
142 pub fn mean(&self, axes: &[usize]) -> Var {
143 let n: f32 = axes.iter().map(|&d| self.0.value.shape[d]).product::<usize>() as f32;
144 let s = self.sum(axes);
145 s.mul(&Var::leaf(s.0.value.scalar(1.0 / n)))
146 }
147 pub fn detach(&self) -> Var { Var::leaf(self.0.value.clone()) }
149 pub fn softmax(&self, axis: usize) -> Var {
151 let m = Var::leaf(self.0.value.max(&[axis], true)); let e = self.sub(&m).exp();
153 e.div(&e.sum(&[axis]))
154 }
155 pub fn sum_all(&self) -> Var {
156 let axes: Vec<usize> = (0..self.0.value.rank()).collect();
157 let out = self.0.value.sum(&axes, false);
158 let shape = self.0.value.shape.clone();
159 Var::node(out, vec![self.clone()], Box::new(move |g, p| {
160 let full = Tensor::from_vec(&p[0].ctx(), &vec![0.0; numel(&shape)], &shape);
162 p[0].accumulate(&full.add(g)); }))
164 }
165 pub fn mean_all(&self) -> Var {
166 let n = self.0.value.numel() as f32;
167 let s = self.sum_all();
168 let inv = Var::leaf(Tensor::from_vec(&self.ctx(), &[1.0 / n], &[1]));
169 s.mul(&inv)
170 }
171}
172
173fn numel(s: &[usize]) -> usize { s.iter().product() }
174
175fn unbroadcast(g: &Tensor, shape: &[usize]) -> Tensor {
177 let mut out = g.clone();
178 while out.rank() > shape.len() {
180 out = out.sum(&[0], false);
181 }
182 let axes: Vec<usize> = (0..shape.len()).filter(|&d| shape[d] == 1 && out.shape[d] != 1).collect();
184 if !axes.is_empty() {
185 out = out.sum(&axes, true);
186 }
187 if out.shape != shape {
188 out = out.reshape(shape);
189 }
190 out
191}