Dense Backward Pass: Appending One Lowered Node #
This file proves the inductive step shared by the Graph and GraphData backward-link theorems.
Given a prefix tape t on which the dense reverse loop is already known to agree with some
reverse program bp, appending one lowered node yields a tape on which the dense reverse loop
computes: one vjp step for the new node, added into the prefix seed, followed by bp on the
prefix. The last gradient slot is never modified afterwards.
The proof is organised in three groups of lemmas.
- Push commutation:
addGradAll,backwardDenseFromStepandbackwardDenseFromLoopon the extended tape and an accumulator with one extra slot act on the prefix tape and accumulator, leaving the extra slot untouched, as long as all ids stay in the prefix range. - The last-node step: processing the freshly appended node adds its
vjpcontributions into the prefix, which isTensorPack.addafter shape erasure. backwardDenseFrom_addNode_lowerNode, which assembles the two.
Everything is stated for an arbitrary prefix tape characterised by its size, its requiresGrad
flags, its stored values and the BackwardPidsLt invariant, so it does not depend on which graph
representation produced the prefix, nor on the debug name of the appended node.
Appending a node does not change the nodes stored at existing ids.
addGradAll commutes with pushing an unused last slot.
On the tape extended by nd and an accumulator extended by v, accumulating into an id of the
prefix range acts exactly as on the prefix tape and accumulator, and v is carried along.
Folding addGradAll over a contribution list commutes with pushing an unused last slot, as long
as every contribution targets the prefix range.
backwardDenseFromStep commutes with pushing an unused last slot for ids in the prefix range.
The BackwardPidsLt invariant guarantees that all contributions emitted by the visited node also
stay in the prefix range.
backwardDenseFromLoop over the prefix range commutes with pushing an unused last slot.
The prefix of the extended tape is a well-formed dense accumulator for seed: every prefix id
stores a node that requires gradients and whose value has the shape of the seed entry.
This is the hypothesis of foldlM_addGradAll_toIndexedShapeErasedArray_eq_add with pref = #[].
The runtime step for a freshly appended lowered node adds the node's vjp contributions into the
prefix gradients, which is TensorPack.add after shape erasure, and leaves the node's own slot
untouched.
Inductive step of the backward link. Let t be a tape whose dense reverse loop agrees with the
reverse program bp on every seed (after shape erasure), and whose nodes have the sizes, flags,
values and backward-pointing invariant of a lowered prefix over the context ss. Appending the
lowered node node yields a tape whose dense reverse loop, seeded by snoc seedPrev seedOut,
returns snoc (bp (seedPrev + node.vjp ctx d seedOut)) seedOut.
This is the shared content of backwardDenseFrom_lowerGraphToTape_eq_backpropAllCtx and its
GraphData variant: the snoc cases of backpropAllCtx unfold to exactly this shape.