Skip to content

← JAX Autodiff step 21 of 25

Medium Primitives

jax.linearize Primitive

Jump to the task

Reuse a derivative at one point

Suppose you want to see how a function responds to several input directions, all starting at the same x. jax.linearize(f, x) returns two things: the value f(x) and a function that applies the derivative at that x to a direction.

import jax
import jax.numpy as jnp

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

x = jnp.array([1.0, 2.0])
value, linear = jax.linearize(f, x)
# value = 5.0
linear(jnp.array([1.0, 0.0]))   # 2.0
linear(jnp.array([0.0, 1.0]))   # 4.0
linear(jnp.array([-1.0, 2.0]))  # 6.0

The gradient at x=[1, 2] is [2, 4]. Each call takes its dot product with the supplied direction. More generally, the returned function computes J(x) @ v, a Jacobian-vector product. Its output has the same shape as the function's output.

A direction is not a new evaluation point

The returned function is linear in v, but f itself need not be linear. For small epsilon, the first-order estimate is f(x + epsilon*v) ≈ value + epsilon*linear(v). The derivative function does not return f(x + v) or include the original value automatically.

For example, with x=[1, 2] and v=[1, 0], the estimate for f(x+v) is 5 + 2 = 7, while its actual value is 8. The difference comes from the quadratic term. To compute derivatives at a different input point, call jax.linearize again at that point.

Reuse has a memory cost

jax.jvp(f, (x,), (v,)) returns the value and one tangent immediately. jax.linearize instead returns a callable for later tangents. It keeps intermediate information needed by that callable, rather than building and caching a full Jacobian matrix. Storage can grow with the original computation, and each application still performs derivative operations.

This can be useful when directions arrive one at a time. If you already have all directions, batching JVPs with jax.vmap is another option that can avoid linearize's stored-intermediate cost. Choose based on the workload rather than assuming every reused call is cheap.

Problem

Implement linearize_at_point(x, v) using jax.linearize for the scalar function f(x) = sum(x**2). Both inputs are one-dimensional real floating-point JAX arrays with the same shape and dtype.

Apply the returned derivative function to v. Return a JAX array with shape (2,), containing [value, tangent] in that order. Here the value is sum(x**2) and the tangent is sum(2*x*v).

For x=[2.0, -1.0] and v=[-1.0, 3.0], return [5.0, -10.0]. The exercise applies one tangent to practice the two-step API. The explanation above shows how the same derivative function can be reused.