JAX backend#
This example samples the Himmelblau target described in the array backends overview.
In run_jax.py, the callbacks use jax.numpy. The script runs DE and
BlackJAX NUTS in turn, starting each run from the same seed. Both use
the same functional execution path. Split the initial key so prior
initialization and sampling use separate random draws:
particle_key, random_state = jax.random.split(jax.random.key(seed))
initial_state = ParticleState(
particles=jax.random.uniform(
particle_key,
shape=(n_particles, n_dimensions),
minval=lower_bound,
maxval=upper_bound,
)
)
The DE configuration matches the eager examples. For BlackJAX, configure NUTS through the sampler’s mutation options:
mutation = "blackjax"
mutation_kwargs = {
"algorithm": "nuts",
"algorithm_kwargs": {
"step_size": 0.02,
"inverse_mass_matrix": jnp.ones(n_dimensions),
},
}
Pass these as mutation=mutation and mutation_kwargs=mutation_kwargs
to make_standard_bayesian_smc, keeping mutation_n_steps=10. For NUTS,
this means ten transitions per particle per tempering stage; each transition
can involve several leapfrog steps. NUTS uses gradients of the log target,
whereas DE uses differences between complementary particles. These runs do
not have equal computational costs.
The JAX script prints Total likelihood evaluations for either method,
using result.evaluation_counts.log_likelihood_evaluations to compare
their recorded likelihood work.
BlackJAX evaluates and differentiates one particle at a time. The JAX
callbacks therefore use particles[..., 0] and particles[..., 1] so
they accept both an individual vector and a particle batch.
The NUTS step size and inverse mass matrix are fixed in the affine-transformed coordinates; this example does not run BlackJAX warm-up or tune them. The box prior has a hard boundary, and NUTS moves are local, so inspect mode coverage across seeds rather than assuming it will move between separated peaks.
For either choice, set logging_frequency=0 to keep Python logging outside
the compiled iteration. compile_functional_iteration uses JAX to compile
the weighting, resampling, and mutation steps of one SMC iteration together.
Passing the resulting callable to run_functional reuses that compiled
computation at each tempering stage, reducing Python overhead within the
iteration. The outer loop stays in Python, where Sequince checks when to
stop and collects results.
The helper initializes the functional state and context before compiling, and checks that the iteration preserves their structure, shapes, and dtypes. Compilation does not consume a sampling transition. Compile the iteration, then execute it through the functional API:
iteration = compile_functional_iteration(
smc, initial_state, random_state, metadata=metadata
)
result, final_random_state = smc.run_functional(
initial_state,
random_state=random_state,
metadata=metadata,
iteration=iteration,
)
jax.block_until_ready(result.final_state.particles)
JAX can dispatch computations asynchronously: Python may receive an array
before the device has finished computing its values.
jax.block_until_ready(result.final_state.particles) waits for the final
particle array to be ready before the script reports results and plots them.
It synchronizes with the device without copying the particles to NumPy.
If you time sampling, put the end of the timing interval after this call so
the measurement includes device execution rather than only dispatch. See
JAX’s asynchronous dispatch guide for more detail.
Retain final_random_state for subsequent random operations. The script
reports the device used by its final particles.
Running the example#
Run from examples/array_backends with the repository’s jax Pixi
environment, which includes JAX on CPU and BlackJAX:
python run_jax.py
For another Python environment, install Sequince’s blackjax extra to run
both methods. To run only DE, remove "blackjax" from the loop in main.
The script selects CPU with platform = "cpu" at the top of the file.
For a GPU run, use an environment with GPU-enabled JAX and change that line
to platform = "cuda". The script applies this setting through
jax.config.update("jax_platforms", platform), overriding the
repository environment’s JAX_PLATFORMS=cpu selection.
Outputs go to outdir/jax/differential-evolution and
outdir/jax/blackjax, so the plots can be compared without overwriting one
another.
Edit n_particles, seed, and outdir at the top of the script. See the
overview for shared settings and guidance on
interpreting the plots.