Skip to content

← JAX Autodiff step 9 of 25

Medium Primitives

HVP via jvp-of-grad

Jump to the task

How a gradient changes

A gradient measures how a scalar loss changes with its inputs. The Hessian measures how that gradient changes. For d inputs it is a d by d matrix of second derivatives.

Often we only need its product with a vector, H @ v: the rate at which the gradient changes along x + epsilon*v. This avoids constructing a dense Hessian with d*d entries. It does not guarantee total memory use of O(d), since the differentiated computation can store other arrays.

Take a JVP of the gradient

jax.jvp returns a function's value and its directional derivative. Apply it to the gradient function: the value is the gradient, and the directional derivative is the Hessian-vector product.

import jax
import jax.numpy as jnp

A = jnp.array([[2.0, 1.0], [1.0, 3.0]])
x = jnp.array([1.0, 2.0])
v = jnp.array([2.0, -1.0])

def f(x):
    return 0.5 * x @ A @ x

grad_f = jax.grad(f)
gradient, hvp = jax.jvp(grad_f, (x,), (v,))
# gradient = A @ x = [4.0, 7.0]
# hvp = A @ v = [3.0, -1.0]

The two results are different: the first describes the loss's slope at x, while the second describes how that slope changes along v. The Jacobian of grad_f is the Hessian, which explains why its JVP is H @ v. Creating grad_f constructs a callable; it does not yet evaluate a gradient at x.

This combines forward-mode differentiation with a reverse-mode gradient, often called forward-over-reverse. It can use less memory than nesting two reverse-mode derivatives, but it is not guaranteed to be faster for every function or backend. The two approaches compute the same mathematical product where the Hessian is symmetric; floating-point results can differ slightly.

Pass the single input and direction as (x,) and (v,). Matching lists are also supported. The real floating-point arrays x and v must have the same shape and dtype. Return the second result, not the gradient.

Problem

Implement hvp_jvp_of_grad(x, A, v) using a JVP of jax.grad(f) for f(x) = 0.5 * x @ A @ x.

All inputs are real floating-point JAX arrays. x and v have shape (d,), and A has shape (d, d) and is symmetric. Hold A fixed. Return a JAX array with shape (d,), containing H @ v.

For this quadratic the Hessian is A, so the answer is A @ v, independent of x. Use it to check your result while practicing the mixed differentiation method. Output tests cannot enforce API choice.