class Node: def __init__(self, value): self.value = value self.grad = 0 self._backward = lambda: None # this is defined as the forward mode is done based on the computation graph. self._prev = set() def __add__(self, other): other = other if isinstance(other, Node) else Node(other) out = Node(self.value + other.value) out._prev = {self, other} def _backward(): self.grad += out.grad other.grad += out.grad out._backward = _backward return out def __mul__(self, other): other = other if isinstance(other, Node) else Node(other) out = Node(self.value * other.value) out._prev = {self, other} def _backward(): self.grad += other.value * out.grad other.grad += self.value * out.grad out._backward = _backward return out def __pow__(self, n): out = Node(self.value ** n) out._prev = {self} def _backward(): self.grad += n * (self.value ** (n-1)) * out.grad out._backward = _backward return outdef backward(node): topo = [] visited = set() def build_topo(v): if v not in visited: visited.add(v) for child in v._prev: build_topo(child) topo.append(v) build_topo(node) node.grad = 1 for node in reversed(topo): node._backward()# Example usagex = Node(2.0)y = x**4 + 3*x**2 + 2*xbackward(y)print(f"f(2) = {y.value}, f'(2) = {x.grad}")# Output: f(2) = 42.0, f'(2) = 58.0