jax-ml/jax · error · ValueError

expn does not support complex-valued inputs.

Error message

expn does not support complex-valued inputs.

What it means

jax.scipy.special.expn (generalized exponential integral En) rejects complex x after promotion via promote_args_inexact. Its piecewise branch structure (small-x series vs continued fraction) is implemented only for real floats.

Source

Thrown at jax/_src/scipy/special.py:2965

  .. math::

     \mathrm{expn}(n, x) = E_n(x) = x^{n-1}\int_x^\infty\frac{e^{-t}}{t^n}\mathrm{d}t

  Args:
    n: arraylike, real-valued
    x: arraylike, real-valued

  Returns:
    array of expn values

  See also:
    - :func:`jax.scipy.special.expi`
    - :func:`jax.scipy.special.exp1`
  """
  n, x = promote_args_inexact("expn", n, x)
  if dtypes.issubdtype(x.dtype, np.complexfloating):
    raise ValueError("expn does not support complex-valued inputs.")
  _c = _lax_const
  zero = _c(x, 0)
  one = _c(x, 1)
  conds = [
    (n < _c(n, 0)) | (x < zero),
    (x == zero) & (n < _c(n, 2)),
    (x == zero) & (n >= _c(n, 2)),
    (n == _c(n, 0)) & (x >= zero),
    (n >= _c(n, 5000)),
    (x > one),
  ]
  n1 = jnp.where(n == _c(n, 1), n + n, n)
  vals = [
    np.nan,
    np.inf,
    one / n1,  # prevent div by zero
    jnp.exp(-x) / x,
    _expn3,

View on GitHub (pinned to 1e1c6a8fc0)

Solutions

  1. For E1 of complex argument use mpmath.e1 or scipy on the host via jax.pure_callback.
  2. For real pipelines, ensure upstream ops (fft, casts) have not promoted x to complex (e.g. use jnp.real(x) if imaginary parts are numerically zero).
  3. Check x.dtype in debug runs to find where the promotion to complex happens.

Example fix

# before (raises)
y = jax.scipy.special.expn(2, x)  # x complex

# after: strip spurious imaginary part or use host SciPy
y = jax.scipy.special.expn(2, jnp.real(x)) if jnp.allclose(x.imag, 0) else \
    jax.pure_callback(lambda v: scipy.special.expn(2, v), x.real.dtype, x)
Defensive patterns

Strategy: type-guard

Validate before calling

x = jnp.asarray(x)
if dtypes.issubdtype(x.dtype, jnp.complexfloating):
    if jnp.allclose(x.imag, 0):
        x = x.real
    else:
        raise TypeError('expn is real-only in JAX')

Type guard

def coerce_real_if_imaginary_zero(x):
    x = jnp.asarray(x)
    if dtypes.issubdtype(x.dtype, jnp.complexfloating) and np.allclose(x.imag, 0):
        return x.real
    return x

Try / catch

try:
    y = jax.scipy.special.expn(n, x)
except ValueError:
    y = jax.pure_callback(lambda a: scipy.special.expn(n, a), x.real.dtype, x)

Prevention

When it happens

Trigger: Calling jax.scipy.special.expn(n, x) with complex x; transitively, any complex input to exp1 (which calls expn(1, x)) also raises here.

Common situations: Complex-argument E_n integrals in physics code; passing complex values to exp1 expecting SciPy semantics where scipy.special.expn supports complex inputs.

Related errors


AI-assisted analysis of jax-ml/jax@1e1c6a8fc0 (2026-08-27). Data as JSON: /api/errors/bcbf6fe062cf1400. Report an issue: GitHub.