Skip to content

JAX Transformations¤

Every ripple waveform is a pure JAX function of (axis, params), so it composes directly with jax.grad, jax.jit, and jax.vmap. This page shows the common patterns; see the FAQ for troubleshooting compile times and precision issues.

Float precision¤

JAX defaults to 32-bit floats. Enable 64-bit precision before doing anything else — without it you may see inaccurate waveforms, particularly at high frequencies or for long signals:

import jax
jax.config.update("jax_enable_x64", True)

Gradients with jax.grad¤

Construct the waveform once, outside the function you differentiate — the model object is reused across every gradient evaluation rather than rebuilt each call:

import jax
import jax.numpy as jnp
import ripplegw

jax.config.update("jax_enable_x64", True)

frequency = jnp.arange(20.0, 1024.0, 0.25)
params = {
    "M_c": 28.3, "eta": 0.247, "s1_z": 0.0, "s2_z": 0.0,
    "d_L": 440.0, "phase_c": 0.0, "iota": 0.0,
}
wf = ripplegw.waveform("IMRPhenomD", f_ref=20.0)

def total_power(M_c):
    h = wf(frequency, {**params, "M_c": M_c})
    return jnp.sum(jnp.abs(h["p"]) ** 2)

grad_Mc = jax.grad(total_power)(params["M_c"])

This same pattern differentiates with respect to any parameter, or several at once via jax.grad on a function that takes a pytree of parameters.

Fast repeated evaluation with jax.jit¤

fast_wf = jax.jit(wf)

_ = fast_wf(frequency, params)  # first call traces and compiles — this is slow
h = fast_wf(frequency, params)  # subsequent calls with the same shapes are fast
h["p"].block_until_ready()      # JAX dispatch is async; block before timing

The first call to a jit-compiled waveform triggers XLA compilation, which will be slow for large models. If you're timing ripple, always discard the first call and call .block_until_ready() before stopping your timer — see Benchmarking for a ready-made CLI that already does this correctly.

Batch evaluation with jax.vmap¤

Mc_values = jnp.linspace(20.0, 40.0, 1000)

def h_plus(M_c):
    return wf(frequency, {**params, "M_c": M_c})["p"]

hp_batch = jax.jit(jax.vmap(h_plus))(Mc_values)   # shape (1000, n_freq)

Prefer jax.vmap over a Python loop when evaluating many parameter sets — a Python loop retraces (or re-dispatches) once per iteration, while vmap compiles a single batched kernel.

Compilation time for large models¤

Some models (particularly the higher-mode and precessing families) have long JIT compile times on first call because of their size. If you only need to evaluate once, jax.config.update("jax_disable_jit", True) skips compilation entirely at some runtime cost; if you need many evaluations, jax.jit still wins overall.