Using Hyperoptax#
Installation#
uv pip install hyperoptax
Hyperoptax requires Python 3.10 or newer. Install accelerator-specific JAX wheels by following the JAX installation guide.
Choosing an optimizer#
Optimizer |
Best suited to |
Constraints |
|---|---|---|
Cheap baseline and broad exploration |
Stateless; does not retain observations. |
|
Small, explicitly enumerated grids |
Every leaf must be a numeric
|
|
Expensive, smooth objectives |
Stores observations in a fixed |
Iterations, batches, and buffers#
n_parallel is the number of configurations evaluated in each optimizer
iteration. Therefore n_iterations * n_parallel is the number of objective
evaluations.
Bayesian search uses fixed-size buffers so its state has static JAX shapes.
n_max is the number of observations that fit in the buffer, not the number
of iterations. When n_iterations is omitted, Bayesian search runs until the
buffer is full. Keep n_max divisible by n_parallel so the last batch
fits exactly.
Grid search builds the full Cartesian product during init. If the grid size
is not divisible by n_parallel, its incomplete final batch is omitted.
Loop APIs#
optimize() runs the outer loop in Python. It
returns params_history and results_history as lists indexed by
iteration. The parameter PyTrees have a leading n_parallel axis and each
result array has shape (n_parallel,).
optimize_scan() uses jax.lax.scan and
requires a JAX-traceable objective. It returns stacked parameter leaves with
shape (n_iterations, n_parallel, ...) and results with shape
(n_iterations, n_parallel).
Both APIs evaluate the batch with jax.vmap. The objective must accept one
unbatched configuration and return a scalar.
External evaluation loop#
Use get_next_params() and
update_state() directly when a job scheduler,
service, or other external system evaluates configurations.
import jax
key = jax.random.PRNGKey(0)
previous_params = None
previous_results = None
for _ in range(20):
key, ask_key, eval_key, tell_key = jax.random.split(key, 4)
params = optimizer.get_next_params(
state, ask_key, previous_params, previous_results
)
eval_keys = jax.random.split(eval_key, optimizer.n_parallel)
results = jax.vmap(objective)(eval_keys, params)
state = optimizer.update_state(state, tell_key, results, params)
previous_params, previous_results = params, results
JAX constraints#
Search-space leaves and objective results must be representable as JAX arrays. String categories are not supported; encode them numerically.
Data-dependent Python control flow, variable-shaped outputs, and side effects are not compatible with
optimize_scan.Parameters that change evaluation shape or duration may not batch cleanly. Use
n_parallel=1when evaluations cannot be synchronized.