Skip to content

← JAX Numerical Computing step 22 of 25

Medium Primitives

Numerically Stable logsumexp

Jump to the task

Log-sum-exp computes the natural logarithm of a sum of exponentials:

logsumexp(x) = log(sum(exp(x)))

It appears in the normalization term for softmax and in calculations that combine probabilities represented by their logarithms. For a vector input, reducing across all entries produces one scalar.

Why the direct expression can fail

In float32, exp(100) overflows to infinity. So directly evaluating log(sum(exp([100, 100, 100]))) gives infinity, even though the answer is 100 + log(3), about 101.098612.

Very negative inputs cause the opposite problem: exp(-1000) underflows to zero, so taking the logarithm afterwards gives negative infinity.

Shift before exponentiating

For finite inputs, let m = max(x). Then use:

m + log(sum(exp(x - m)))

The shifted exponents are nonpositive, and at least one equals zero. This avoids overflowing those exponentials and keeps their sum nonzero. Adding m back restores the original scale. The identity is exact in real arithmetic; floating-point evaluation still rounds.

import jax
import jax.numpy as jnp

x = jnp.array([100.0, 100.0, 100.0], dtype=jnp.float32)
print(jax.nn.logsumexp(x))  # approximately 101.09861
x = jnp.array([-1000.0, -1000.0], dtype=jnp.float32)
print(jax.nn.logsumexp(x))  # approximately -999.3068

Your task

Implement stable_logsumexp(x) using jax.nn.logsumexp. The input is a nonempty one-dimensional array of finite float32 values. Return a scalar, not one value per input element. This exercise allows the dedicated JAX operation.

For [0.0, 0.0, 0.0, 0.0], return log(4), approximately 1.386294. Unlike softmax, adding a constant c to every input adds c to logsumexp.

The explorer below shows softmax probabilities, which use the same exponential normalization. It does not display the scalar returned by this function.

Loading visualization…