How to use spexial with jit, vmap and grad¤
spexial functions are ordinary JAX functions, so the transforms apply directly. Three things about this library change how you reach for them.
>>> import jax
>>> jax.config.update("jax_enable_x64", True)
>>> import jax.numpy as jnp
>>> import spexial as sp
Evaluate over an array¤
Most functions already broadcast over their evaluation point, so you need no transform at all:
>>> sp.eval_gegenbauer(3, 0.5, jnp.linspace(-1.0, 1.0, 5))
Array([-1. , 0.4375, -0. , -0.4375, 1. ], dtype=float64)
If you want vmap anyway — to map over an axis of a larger computation — it gives the same answer.
Evaluate polylog over an array¤
polylog broadcasts over z like the rest of the library, so call it directly:
>>> sp.polylog(2, jnp.array([0.25, 0.5]))
Array([0.26765264, 0.58224053], dtype=float64)
vmap gives the same answer, to the last bit, if you want it for an axis of a larger computation:
>>> jax.vmap(lambda z: sp.polylog(2, z))(jnp.array([0.25, 0.5]))
Array([0.26765264, 0.58224053], dtype=float64)
Do not reach for a Python loop here. See Accuracy and domains for which functions carry restrictions.
Hold an integer parameter fixed¤
Degrees and orders — the n of \(C_n^{(\alpha)}\), the n of \(\mathrm{Li}_n\) — are Python ints that change the shape of the computation. They are static: you cannot pass a traced array where one is expected, and each distinct value compiles separately.
To map over the continuous arguments while holding the degree fixed, pin it with in_axes=None:
>>> f = lambda n, x: sp.eval_gegenbauer(n, 0.5, x)
>>> jax.vmap(f, in_axes=(None, 0))(3, jnp.linspace(-1.0, 1.0, 5))
Array([-1. , 0.4375, -0. , -0.4375, 1. ], dtype=float64)
If you need many degrees at once, prefer a function that returns them all in one call — eval_gegenbauers returns every order up to n — over sweeping n in a Python loop, which pays a compilation per iteration.
Differentiate¤
jax.grad works directly:
>>> float(jax.grad(sp.zeta)(4.0))
-0.06891126589612538
One trap: zeta is only differentiable in a meaningful sense for \(n > 1\). On the negative line its value comes from a Bernoulli-number table, and jax.grad will return a finite number there that is not \(\zeta'\). It will not warn you. See About domain edges.
Compile¤
>>> float(jax.jit(sp.zeta)(4.0))
1.0823232337111384
Compose the transforms as you would anywhere else in JAX — jax.jit(jax.vmap(jax.grad(f))) is fine.