Skip to content

← JAX Autodiff step 4 of 25

Easy Primitives

vjp Basics

Jump to the task

Send an output weight backward

For a scalar loss, multiplying the loss by a constant also multiplies its gradient by that constant. jax.vjp lets you supply that weight when running the derivative calculation backward. The weight is called a cotangent.

import jax
import jax.numpy as jnp

def loss(x):
    return jnp.sum(x ** 2)

x = jnp.array([1.0, 2.0, 3.0])
value, pullback = jax.vjp(loss, x)
# value is 14.0
pullback(1.0)[0]   # [2.0, 4.0, 6.0]
pullback(-2.0)[0]  # [-4.0, -8.0, -12.0]

value is the ordinary loss. pullback is a function that maps a weight on the output to weights on the inputs, using derivatives at this x. Here the loss derivative is 2*x, so each call scales that vector. The pullback returns a tuple with one entry per input argument. Since loss has one argument, [0] extracts its only entry.

What makes it a vector-Jacobian product?

For a function with several outputs, supply one weight per output. If J[i, j] is the derivative of output i with respect to input j, the result's entry j is sum_i cotangent[i] * J[i, j]. This is the derivative of a weighted sum of outputs, with those weights held fixed. JAX can compute this product without constructing the whole Jacobian matrix.

For example, if f(x) = [x[0]**2, 3*x[1]], then at x=[2, 4] the output weights [2, -1] produce input weights [8, -3].

For array outputs, the cotangent must have the output's shape and a compatible dtype. A scalar output has shape (): both 1.0 and jnp.array(1.0) work here, but jnp.array([1.0]) has the wrong shape. You can reuse a pullback with different cotangents at the same input. To evaluate derivatives at a new input, call jax.vjp again.

Problem

Implement vjp_quadratic(x, cotangent) using jax.vjp for the scalar function f(x) = sum(x**2). x is a one-dimensional real floating-point JAX array, and cotangent is a real floating-point scalar.

Return a JAX array with the same shape as x, containing the gradient scaled by cotangent. For x=[2.0, -3.0] and cotangent=-0.5, return [-2.0, 3.0]. A zero cotangent gives zeros for finite inputs.

The formula cotangent * 2*x is useful for checking your answer. Use jax.vjp to practice applying the pullback; output checks alone cannot enforce this choice.