Mathematical foundations
Matrix Calculus, JVPs, VJPs, and Hessian Products
Use differentials, Jacobian–vector products, vector–Jacobian products, and trace identities to reason about derivatives in modern machine-learning systems.
By the end you can
- Treat multivariable derivatives as linear maps before choosing an array layout
- Distinguish Jacobians, JVPs, VJPs, and Hessian–vector products
- Use differentials and shape checks to derive matrix gradients
- Apply trace identities without assuming arbitrary matrix commutation
Differentials keep shapes visible
The differential of a scalar f(x) can be written df = ∇f(x)ᵀdx. A scalar change is produced by pairing the gradient with an input perturbation. For y = Ax, the differential is dy = A dx. For the quadratic form f(x) = xᵀAx, the differential does work a memorised table cannot. Form df = f(x+dx) − f(x), drop the second-order term, read off ∇f = (A + Aᵀ)x. Nothing in that derivation assumes A is symmetric. It is Example 10 of Edelman and Johnson's 2023 MIT 18.S096 matrix-calculus notes. The Matrix Cookbook (2012 edition) records the same result with a linear term added: ∇x(xᵀAx + bᵀx) = (A + Aᵀ)x + b, whose Hessian is A + Aᵀ.
The notes compute that gradient the slow way first, component by component, and then warn the reader off the detour: “However, this approach is awkward, error-prone, labor-intensive, and quickly becomes worse as we move on to more complicated functions.” The differential method leaves you less to memorise. It also makes a transpose mistake easier to see.
Modern derivatives are linear maps before they are arrays
A derivative maps a small input perturbation to a first-order output perturbation. Coordinates represent that linear map as a gradient, Jacobian, or higher-order tensor. Matrix calculus becomes confusing when notation hides whether vectors are rows or columns, which variable is differentiated, and whether a derivative acts forward or backward. A reliable approach starts with shapes and differentials. The final array formula should be the last step, not the first.
Treating derivatives as operators rather than as stored arrays pays off, and the payoff has a date on it. Multiplying a stored Hessian by a vector “takes O(n2) time when there are n weights”. Barak Pearlmutter's R-operator technique instead “finds this product in O(n) time and space”, and it makes no approximations: the product is exact, “which takes about as much computation, and is about as local, as a gradient evaluation”. Neural Computation published that in 1994. The 2018 JMLR autodiff survey reports the same result for the reverse-on-forward construction, which “computes Hv with O(n) complexity, even though H is a n × n matrix.” The same n² that makes storage impossible is the n² the operator never touches.
Derivative notation is safe only when the input and output spaces are explicit.
Comparison
Numerator layout, denominator layout, and operator notation
Different books arrange Jacobians differently, and the books themselves say so. Parr and Howard declare their choice partway through their 2018 deep-learning matrix-calculus tutorial: “We are using the so-called numerator layout but many papers and software will use the denominator layout.” The other convention, they add, is “just transpose of the numerator layout Jacobian (flip it around its diagonal)”.
The disagreement is not folklore. It is visible in the printed identities. Minka's 2000 statistics notes declare their own rule — partials with respect to the numerator laid out by the shape of Y, partials with respect to the denominator by the transpose of X — and on that rule his equation 14 gives d/dX tr(AXB) = BA. The Matrix Cookbook prints ∂Tr(AXB)/∂X = AᵀBᵀ as its equation 101. Same derivative, transposed answer. The mathematics can agree while the displayed arrays are transposed. Declare the convention before you copy the formula.
Numerator layout
Rows follow output coordinates and columns follow input coordinates.
- Common in modern ML texts
- Jv has a natural shape
- Matches many autodiff explanations
- Still requires declared column-vector convention
Denominator layout
Rows follow input coordinates and columns follow output coordinates.
- Appears in some statistics texts
- Can make gradients row vectors
- Equivalent after consistent transposes
- Dangerous when conventions are mixed
Linear-operator view
Treat the derivative as a map independent of coordinates.
- Clarifies what is intrinsic
- Explains JVP and VJP directly
- Reduces layout disputes
- Requires comfort with abstract maps
Example
Shape tracing a softmax–cross-entropy gradient
A familiar derivative illustrates why the composed loss is simpler than separate Jacobians suggest. Track the shape at every line and the cancellation appears on its own.
- Logits: z has shape [K] for K classes.
- Softmax: p = softmax(z) also has shape [K].
- Target: y is a one-hot or probability vector with shape [K].
- Loss: L = −Σ yᵢ log pᵢ is scalar.
- Gradient: ∂L/∂z = p − y, a vector with the same shape as z.
- Lesson: Differentiating the composed expression avoids materializing the dense softmax Jacobian, which for K classes would be a K × K array formed only to be collapsed again.
Analogy
Sensitivity moves through a routing network in both directions
An input perturbation enters a routing network as a small shipment sent forward through connected routes. A JVP reports how that shipment changes every downstream output. A VJP starts from a downstream cost and sends responsibility backward through the same local connections. The network structure is shared, but the direction and computational cost differ.
How much they differ is measurable. Take a chain rule through n-sized intermediates, with n ≫ 1 inputs and a single output. Going left-to-right — reverse mode — costs Θ(n²) scalar operations. Going right-to-left — forward mode — costs Θ(n³). Reverse the shape of the problem, to one input and m ≫ 1 outputs, and the numbers swap: Θ(m³) for reverse mode against Θ(m²) for forward. Those counts are worked out in the MIT 18.S096 notes, and the rule that follows is arithmetic rather than taste: “If you have a lot of inputs and few outputs (the usual case in machine learning and optimization), compute the chain rule left-to-right (reverse mode). If you have a lot of outputs and few inputs, compute the chain rule right-to-left (forward mode).” The JMLR survey states the same asymmetry: forward mode needs n evaluations for f: Rⁿ→R, and reverse mode performs better when m ≪ n.
Shipments in this network can also cancel, because derivatives are linear approximations that carry negative sensitivities as readily as positive ones. Full Jacobians remain unnecessary most of the time.
JVPs move perturbations forward; VJPs move sensitivities backward.
Visual
Four derivative products used in ML systems
Most frameworks avoid constructing full Jacobians by applying them to vectors, and the cost model behind that choice is published rather than assumed. The 2018 JMLR autodiff survey prices the m × n Jacobian at n·c·ops(f) in forward mode and m·c·ops(f) in reverse mode, “where c is a constant guaranteed to be c < 6 and typically c ∼ [2, 3] (Griewank and Walther, 2008)”. A single direction costs far less than either: “Thus, we can compute the Jacobian–vector product in just one forward pass.” JAX's own documentation puts the same constant at about 3, describing a JVP as “about 3x the cost of just evaluating the function” and a VJP as “only about three times the cost of evaluating f”.
When a full Jacobian really is wanted, the direction is a shape decision, and both frameworks document the same threshold. JAX's Autodiff Cookbook states it directly: “jacfwd uses forward-mode automatic differentiation, which is more efficient for “tall” Jacobian matrices (more outputs than inputs), while jacrev uses reverse-mode, which is more efficient for “wide” Jacobian matrices (more inputs than outputs).” PyTorch's functorch tutorial gives the rule of thumb with letters attached: for f: R^N → R^M, prefer jacfwd when M > N and jacrev otherwise. Reverse mode builds the Jacobian row by row, forward mode column by column.
The HVP is not a matrix in either framework. It is a composition of the other two products. JAX's Autodiff Cookbook builds hvp from grad-of-grad and then from forward-over-reverse, jvp(grad(f), x, v). PyTorch's functorch tutorial defines hvp(f, primals, tangents) as jvp(grad(f), primals, tangents)[1], noting that “it turns out we don't need to materialize the full Hessian to do this”. The MIT 18.S096 notes give the same forward-over-reverse construction and say it can “incur computational cost proportional only to that of a single function evaluation”. The reason to compose rather than store is stated without hedging: “The trick is not to instantiate the full Hessian matrix: if n is large, perhaps in the millions or billions in the context of neural networks, then that might be impossible to store.”
- 1
Jacobian J
Captures the local linear relationship between perturbations of inputs and outputs.
- 2
JVP: Jv
Pushes one input direction forward through the derivative.
- 3
VJP: vᵀJ
Pulls an output sensitivity backward toward the inputs.
- 4
HVP: Hv
Applies Hessian curvature to a direction without forming the full Hessian.
Key idea
The trace trick is a tool, not a law of nature
Trace identities can turn scalar expressions into forms that expose matrix derivatives. xᵀAx equals tr(xᵀAx), and inside the trace the factors can be cyclically rearranged. That rearrangement is all the standard references license. The Matrix Cookbook lists Tr(AB) = Tr(BA) and Tr(ABC) = Tr(BCA) = Tr(CAB) — the cyclic permutations, and nothing beyond them. Minka builds his whole matrix-derivative recipe on the two-factor case: “One particularly helpful identity is: tr(AB) = tr(BA)”, and uses it to obtain d/dX tr(AXB) = BA. Cyclic permutation is still not arbitrary reordering. In general tr(ABC) = tr(BCA), but tr(ABC) need not equal tr(ACB). Shape-check every trace manipulation.
Cyclic trace invariance permits rotation of factors, not free commutation.
Steps
Differentiate a matrix expression without guessing
A table of derivative identities can wait until after these six steps. It should also wait until you have checked the table's layout convention against your own, because Minka's equation 14 and the Cookbook's equation 101 print transposes of the same derivative. The steps below are the route Edelman and Johnson take to ∇f = (A + Aᵀ)x: declare shapes, choose the variable, form the differential, collect the perturbation, read the gradient, and verify it numerically.
1. Declare shapes
Write the dimensions and orientation of every variable.
2. Choose the variable
State which object changes and which remain constant.
3. Form the differential
Compute the first-order change using product and chain rules.
4. Collect the perturbation
Rearrange the expression so dx or dX appears once.
5. Read the gradient
Match the coefficient under the chosen inner product convention.
6. Verify numerically
Use a directional finite-difference or autodiff check.
Key takeaways
- A derivative is intrinsically a linear map, while a Jacobian is one coordinate representation — and the array that represents it depends on a declared layout. Parr and Howard choose numerator layout over the denominator layout used by many papers and software.
- JVPs push perturbations forward and VJPs pull sensitivities backward, and the choice is priced: n·c·ops(f) forward against m·c·ops(f) reverse, with c < 6 and typically 2–3 in the JMLR survey's model.
- Differentials expose shape and transpose requirements more reliably than memorized formulas. Edelman and Johnson call the component-by-component alternative awkward, error-prone and labor-intensive, then derive ∇f = (A + Aᵀ)x in two lines.
- Trace identities allow cyclic rotation — Tr(AB) = Tr(BA) and Tr(ABC) = Tr(BCA) = Tr(CAB) in The Matrix Cookbook — but not arbitrary reordering of matrix factors.
- Composed derivatives can simplify dramatically without materializing full Jacobians: softmax cross-entropy collapses to p − y, and Pearlmutter's 1994 result turns an O(n²) Hessian product into an exact O(n) one.
- Numerical directional checks are valuable safeguards for matrix-calculus derivations, and both JAX and PyTorch expose the products (jvp, vjp, hvp = jvp∘grad) needed to run them.