Topological order
Apply the chain rule through a graph and implement the core of a scalar reverse-mode autodiff engine.
THE IDEA
Why the order is not optional
The forward pass must compute a node only after all of its inputs. The backward pass is the mirror image: a node may send its gradient to its inputs only after every node that uses it has already sent its share. Otherwise it passes along an incomplete number, and the error spreads to everything upstream.
A topological order is any ordering of the nodes where every node comes after its inputs. Run it forwards to compute values, and in reverse to send gradients.
Forward pass. Click nodes to compute them (blue = ready).
Backward pass. Click the operation nodes in the order you want to push gradients back. A node that goes too early pushes an incomplete gradient and turns red.
Predict first. Why is an arbitrary node order unsafe for the backward pass?
Chapter context
Backpropagation organizes repeated applications of the chain rule. The graph stores how values depend on earlier values. Reverse traversal accumulates how the final loss changes with each intermediate quantity.
TRY IT YOURSELF
Part 1. Do it hands-on
The graph has two nodes that are used twice, which is what makes the backward order matter.
Forward pass. Click nodes to compute them (blue = ready).
Backward pass. Click the operation nodes in the order you want to push gradients back. A node that goes too early pushes an incomplete gradient and turns red.
- To do: Run the forward pass: click the nodes in an order where every node comes after its inputs
- To do: Run the backward pass: click the four operation nodes in an order that gives the same gradients as the correct engine
- To do: Type the final value of ∂L/∂a that the correct backward pass produces
Part 2. Do it in code
- Nodes are names. parents maps a name to the list of names it was computed from (leaves may be missing or empty). Write topo_order(out, parents): a list of every node that out depends on (and out itself, last) in which each node appears once, after all of its parents. A node shared by several users must appear only once.
- Write is_topological(order, parents): True only if every node appears exactly once and after all of its parents (all parents must be in the order).
- Write backward_order(out, parents): the order to visit nodes in the backward pass, i.e. the reverse of topo_order.
Stuck, or something not working? You can come back to it any time.
CHECK YOUR UNDERSTANDING
Why is arbitrary node order unsafe?
Chapter reference: math, worked example & code
This section belongs to the whole chapter, so it is the same on every lesson in it and may cover a different lesson's topic. The code for the experiment you just ran is inside the experiment itself.
Math and notation
If and , then
At a shared node x, add all downstream contributions: .
Worked example
Take a=2,b=3,c=1. Then u=7 and L=49. The upstream gradient into u is 14, giving dL/da=14×3=42, dL/db=14×2=28 and dL/dc=14. These are sensitivities, not new parameter values; an optimizer still needs a step size.
Reference code
The following example uses the Python standard library and does not call a remote service.
from math import isclose
a,b,c = 2.,3.,1.
u = a*b+c
assert u*u == 49
assert (2*u*b,2*u*a,2*u) == (42,28,14)
h = 1e-5
f = lambda x: (x*b+c)**2
assert isclose((f(a+h)-f(a-h))/(2*h), 42, rel_tol=1e-7)
Python examples are provided for study and local execution; this page does not execute Python.
Common mistakes
Forward values and gradients are different objects. Shared branches require addition of gradient contributions. Backpropagation computes derivatives; the optimizer changes parameters.