Interactive explainer
Why backpropagation needs an order
A gradient can move through a node only after every downstream path has contributed to that node. Topological sorting gives us exactly that safe schedule.
Step through one real scalar graph. Watch the recursive calls build a dependency-first list, then reverse it once for backpropagation.
- 01Follow each saved ParentLink to an operand.
- 02Append a node only after its parents return.
- 03Reverse the finished list for backward.
Interactive trace · exact notebook graph
Watch dependency_safe_order(L) build its list
Each step is one action performed by the recursive helper. The graph is fixed;
only the call stack, seen, and safe_order change.
append_after_parents(L).The moving dashed edge is a saved ParentLink being followed from an output back to one direct operand. On a phone, scroll the graph sideways.
Dependency-first output being built
A Value enters this list only after every recursive parent call has returned.
What the current structures mean
stack answers “which calls are waiting?” · seen prevents scheduling one shared Value twice · safe_order is the list we will reverse.
Reverse once · backward schedule
Why every node is ready: reversing puts an output before the operands it can update.
Therefore every later output that can contribute to a node's .grad has already run before
that node sends its completed gradient to its own parents.
Reverse for backward: L → e → y → a → b → m → x → w
Each operand appears before the output that uses it; reversing makes each output ready before it sends gradient contributions to its operands.
The invariant
Wait until the gradient is complete
The forward graph points from operands to outputs. The traversal follows stored parent links in the opposite direction and appends on the way back. Reversing that list puts every output before the operands whose gradient buffers it updates.