Quant Memo
Advanced

Adjoint Algorithmic Differentiation

Bumping each of a thousand risk factors one at a time and rerunning the pricer a thousand times is unusable in real time. Running the calculation backwards once gets every single sensitivity in about the same time as one forward pass.

Prerequisites: Monte Carlo Greeks: Pathwise And Likelihood Ratio, Backpropagation

A book with a thousand risk factors — one volatility per strike and expiry, one rate per tenor — needs a thousand Greeks. The bump-and-reprice approach, nudge one input, rerun the whole pricer, repeat for every factor, costs roughly a thousand full pricing runs. If one run takes even a second, that is over 16 minutes for a single risk snapshot — unusable intraday. Adjoint algorithmic differentiation (AAD) gets every one of those thousand sensitivities from roughly the cost of one extra pricing run.

Retracing a hike backwards to find every fork's effect

Imagine hiking a trail with a thousand forks, and you want to know: for each fork, how much would taking the other path have changed your final elevation? Walking the whole trail again a thousand times, once per fork, is bump-and-reprice. A cleverer way: walk the trail forward once as normal, then walk backwards from the end exactly once, at each fork combining how sensitive the final elevation was to everything downstream with how much that fork could have changed the path. One forward walk, one backward walk, and every fork's sensitivity falls out.

Forward mode versus reverse mode

Any calculation, including a pricing model, is a sequence of simple operations chained together. Forward-mode differentiation propagates a sensitivity with the calculation — cheap per input, but needs one full pass per input, the "bump and reprice" cost structure in disguise. Reverse-mode (the "adjoint") instead computes the output first, then propagates sensitivities backwards to every input in a single pass, using the chain rule at each step:

Vxi  =  Vyyxi,\frac{\partial V}{\partial x_i} \;=\; \frac{\partial V}{\partial y} \cdot \frac{\partial y}{\partial x_i},

applied repeatedly, step by step, from the final output VV back through every intermediate quantity yy to every original input xix_i. In plain English: once you know how sensitive the price is to the last intermediate step, its sensitivity to the previous step is just one more multiplication, backwards through the whole calculation — and this sweep computes the sensitivity to every input at once, since each backward sensitivity is reused for every input that fed into it.

Worked example 1: a tiny three-step calculation, forward vs. adjoint

Compute V=(x1+x2)×x3V = (x_1 + x_2) \times x_3 with x1=2,x2=3,x3=4x_1=2, x_2=3, x_3=4. Forward pass: y=x1+x2=5y = x_1+x_2 = 5, then V=y×x3=5×4=20V = y \times x_3 = 5 \times 4 = 20.

Bump-and-reprice for V/x1\partial V/\partial x_1: recompute with x1=2.001x_1 = 2.001: y=5.001y=5.001, V=20.004V=20.004, so V/x1(20.00420)/0.001=4\partial V/\partial x_1 \approx (20.004-20)/0.001 = 4. Repeat separately for x2x_2 and x3x_3 — three recomputations for three sensitivities.

Adjoint, one backward pass: start from V/V=1\partial V/\partial V = 1. Since V=y×x3V = y \times x_3: V/y=x3=4\partial V/\partial y = x_3 = 4 and V/x3=y=5\partial V/\partial x_3 = y = 5. Since y=x1+x2y = x_1 + x_2: V/x1=V/y×1=4\partial V/\partial x_1 = \partial V/\partial y \times 1 = 4, and V/x2=4\partial V/\partial x_2 = 4 likewise. All three sensitivities came from one forward pass plus one backward pass — no repeated recomputation, matching the bump-and-reprice answers exactly.

Worked example 2: the cost comparison at realistic scale

A Monte Carlo pricer with N=1,000N=1{,}000 risk factors takes T=1T=1 second per full pricing run.

Bump-and-reprice: one baseline run plus one bumped run per factor: (1+1,000)×1s=1,001(1 + 1{,}000) \times 1\text{s} = 1{,}001 seconds, about 16.7 minutes.

Adjoint: one forward pass plus one backward pass, where the backward pass typically costs a small constant multiple (commonly cited as roughly 2-4x) of the forward pass, say 3×3\times here: 1+3=41 + 3 = 4 seconds total, for all 1,000 sensitivities.

That is a speedup of roughly 1,001/4250×1{,}001 / 4 \approx 250\times for this factor count, and the gap only widens as the number of risk factors grows, since bump-and-reprice cost scales linearly with NN while adjoint cost stays roughly constant.

number of risk factors compute time bump-and-reprice: linear in N adjoint: roughly flat
Bump-and-reprice cost grows linearly with the number of risk factors; adjoint differentiation's cost barely grows at all, because one backward pass yields every sensitivity at once.
forward pass (left to right) x1, x2 y=x1+x2 V=y·x3 backward pass (right to left, one sweep) ∂V/∂V=1 ∂V/∂y=4 ∂V/∂x1=∂V/∂x2=4
One forward pass computes the answer; one backward pass, reusing the same intermediate values, produces every input's sensitivity at once — the sensitivities to x1 and x2 both fall out of the single ∂V/∂y computed one step earlier.

What this means in practice

Every major bank's front-office risk engine uses adjoint differentiation for full Greeks sets across books with thousands of risk factors, because it is the only approach that makes intraday, full-revaluation risk computationally feasible — the same core idea as Backpropagation in neural network training, reverse-mode differentiation through a network's layers instead of a pricer's calculation steps. It generalises the pathwise method to compute all Greeks from one simulation, not just delta.

Adjoint differentiation gives exact derivatives of the code as written, not automatically the derivative of the underlying financial quantity if the code contains a non-differentiable step — an if branch, a sort, a hard cutoff in a payoff. The common mistake is assuming AAD "just works" without checking for these; at a genuine kink, the derivative can be technically correct for that exact input yet wildly unrepresentative nearby, needing the same care that pathwise vs. likelihood-ratio methods require for non-smooth payoffs.

Reverse-mode (adjoint) differentiation computes sensitivities to every input from a single backward pass through the calculation, at roughly constant cost regardless of how many inputs there are — the opposite scaling from bump-and-reprice, which pays one full recomputation per input.

Related concepts

Practice in interviews

Further reading

  • Giles & Glasserman (2006), Smoking Adjoints: Fast Monte Carlo Greeks
  • Griewank & Walther, Evaluating Derivatives (Ch. 3-4)
ShareTwitterLinkedIn