Contents · Matrix Calculus & Autodiff
Part 0 · Matrix Calculus
Part 1 · Automatic Differentiation
Part 2 · Backpropagation
Part 3 · Differentiable Simulation
- Differentiating Through a Simulator
Part 3 · Differentiable Simulation
Differentiating Through a Simulator
A simulator is a function iterated. One step maps state to state, , with parameters (controls, masses, gains) held fixed across the rollout, and a scalar loss reads off the final state. To improve you want . Running reverse-mode autodiff through the unrolled trajectory is not a separate trick: it is exactly the adjoint method, and it returns the full gradient in a single backward pass whose cost does not depend on how many parameters holds.
The unrolled trajectory
A rollout of steps is the composition . The loss depends on through every step, so the chain rule threads a product of per-step Jacobians:
Forming those products left to right (forward mode) costs one pass per input direction, so needs passes. The adjoint reorders the sum to sweep once from the right.
Reverse mode is the adjoint
Define the adjoint , the sensitivity of the loss to the state at time . Seed it at the end with the loss gradient, then propagate backward with the transposed state Jacobian, accumulating a parameter term at each step:
One backward traversal produces every entry of the gradient at once, so the cost is independent of : a million-parameter controller and a one-parameter one cost the same backward sweep. That asymmetry, cheap gradient regardless of input dimension, is why reverse mode drives differentiable physics1.
A scalar sim, by hand
Take the smallest nontrivial case: a scalar linear sim with loss . The two partials are constant,
so the seed is , the recurrence is , and every accumulation term is just . Summing the geometric chain gives a closed form:
Now plug in , , , , . The forward rollout is
with . The backward sweep seeds , then
and the parameter gradient is the running sum
The closed form agrees: . And so does a central finite difference, which never sees the adjoint at all:
The demo runs the same recurrence for : drag and to set the forward trajectory, then step the adjoint backward and watch each get folded into , ending at the finite-difference value.
The forward pass (top, left to right) builds the trajectory. The adjoint pass (bottom, right to left) starts from the seed λ_8 and multiplies by a at every step; each λ it visits is added into dL/dθ. One backward sweep produces the exact gradient, which matches the finite difference.
The continuous limit and solver layers
As the step size the backward recurrence becomes an ODE for integrated in reverse time, the continuous adjoint behind neural ordinary differential equations2. And when a step is not an explicit update but an equation solve, say a contact quadratic program, you need not unroll the solver at all: the implicit function theorem differentiates its solution directly from the optimality conditions, giving an exact gradient through a fixed number of solver iterations3.
One caveat carries over from the contact-cliff view of differentiable simulation: when contact makes nonsmooth, jumps or spikes, and the adjoint faithfully propagates a gradient that is biased or high-variance. The machinery is exact for the model it is given; it cannot repair a discontinuous .
Footnotes
-
Hu et al., DiffTaichi: Differentiable Programming for Physical Simulation (2020). ↩
-
Chen et al., Neural Ordinary Differential Equations (2018). ↩
-
Amos & Kolter, OptNet: Differentiable Optimization as a Layer in Neural Networks (2017). ↩