Computes the gradient of the targets with respect to self. ```python exec="true" source="above" session="tensor" result="python" x = Tensor.eye(3) y = Tensor([[2.0,0,-2.0]]) z = y.matmul(x).sum() dx, dy = z.gradient(x, y) print(dx.tolist()) # dz/dx print(dy.tolist
(self, *targets:Tensor, gradient:Tensor|None=None)
| 825 | # ***** toposort and backward pass ***** |
| 826 | |
| 827 | def gradient(self, *targets:Tensor, gradient:Tensor|None=None) -> list[Tensor]: |
| 828 | """ |
| 829 | Computes the gradient of the targets with respect to self. |
| 830 | |
| 831 | ```python exec="true" source="above" session="tensor" result="python" |
| 832 | x = Tensor.eye(3) |
| 833 | y = Tensor([[2.0,0,-2.0]]) |
| 834 | z = y.matmul(x).sum() |
| 835 | dx, dy = z.gradient(x, y) |
| 836 | |
| 837 | print(dx.tolist()) # dz/dx |
| 838 | print(dy.tolist()) # dz/dy |
| 839 | ``` |
| 840 | """ |
| 841 | assert gradient is not None or self.shape == tuple(), "when no gradient is provided, backward must be called on a scalar tensor" |
| 842 | if not (self.is_floating_point() and all(t.is_floating_point() for t in targets)): raise RuntimeError("only float Tensors have gradient") |
| 843 | if gradient is None: gradient = Tensor(1.0, dtype=self.dtype, device=self.device) |
| 844 | target_uops = [x.uop for x in targets] |
| 845 | grads = compute_gradient(self.uop, gradient.uop, set(target_uops)) |
| 846 | ret:list[Tensor] = [] |
| 847 | for x in target_uops: |
| 848 | if (y:=grads.get(x)) is None: y = x.const_like(0) |
| 849 | ret.append(Tensor(y)) |
| 850 | return ret |
| 851 | |
| 852 | def backward(self, gradient:Tensor|None=None) -> Tensor: |
| 853 | """ |