Skip to content

Jaxnasiumยค

A Lightweight Utility Library for JAX-based Reinforcement Learning Projects.

Jaxnasium is not an environment suite, and not a framework that locks you in. Rather, it is your one-stop shop for your JAX RL code: a single environment API that existing suites are wrapped into, a set of general algorithms you can either import or copy into your project, and the tooling to train, evaluate and sweep them.

  1. ๐Ÿ•น๏ธ One environment API. Import environments from Gymnax, Jumanji, Brax, Crax, Pgx, JaxMARL, xMinigrid, Navix, Playground, PopJym, or Craftax through jym.make(...), automatically wrapped to a common standard.
  2. ๐Ÿค– Readable cross-suite algorithms. Various algorithms that can operate on any of those environments in near-single-file philosophy, built in Equinox with a familiar Stable-Baselines-like API and end-to-end JIT training in the spirit of PureJaxRL.
  3. ๐Ÿ‘ฅ Multi-agent for free. Single-agent algorithm code transparently upgrades to multi-agent environments through PyTrees and function transformations.
  4. ๐Ÿ“Š Sweeps and evaluation. Grid, random, Sobol and one-at-a-time searches over many seeds, locally or across a Slurm job array.
  5. ๐Ÿš€ Project scaffolding. Bootstrap a complete codebase with a single CLI command.

Installationยค

pip install "jaxnasium[algs]"   # [algs] pulls in optax + distrax, needed for jaxnasium.algorithms

Third-party environment suites are not dependencies; install the ones you want to use (e.g. pip install gymnax). For a brand-new project, let the CLI set everything up for you:

uvx jaxnasium init <projectname>   # or: pipx run jaxnasium init <projectname>
cd <projectname> && uv run train.py

Quickstartยค

import jax
import jaxnasium as jym
from jaxnasium.algorithms import PPO

env = jym.make("CartPole-v1")
key = jax.random.PRNGKey(0)

algorithm = PPO(total_timesteps=500_000, learning_rate_start=2.5e-3)
agent, metrics = algorithm.train(key, env)
returns = agent.evaluate(key, env, num_eval_episodes=10)

train returns the trained agent alongside its training metrics: by default the mean return of the episodes that finished in each training iteration, one scalar per iteration (iterations without a finished episode yield NaN).

๐Ÿ  Environmentsยค

Jaxnasium does not aim to deliver a full environment suite. Instead, jym.make(...) imports environments from existing suites (provided they are installed) and wraps them to the Jaxnasium API standard:

env = jym.make("Breakout-MinAtar")  # from Gymnax
env = jym.FlattenObservationWrapper(env)

algorithm = PPO(**some_good_hyperparameters)
agent, metrics = algorithm.train(jax.random.PRNGKey(0), env)

# > Using an environment from Gymnax via gymnax.make(Breakout-MinAtar).
# > Wrapping Gymnax environment with GymnaxWrapper
# >  Control this behavior by passing wrappers=[...] to jym.make
# > Wrapping environment in VecEnvWrapper
# > ... training results

By default make applies the adapter wrapper for the environment's library and a LogWrapper for episode statistics; the algorithms add a VecEnvWrapper themselves when training.

Info

For convenience, Jaxnasium bundles the 5 classic-control environments, which need no external dependencies.

See Available Environments for a complete list of available environments.

Environment APIยค

The Jaxnasium API stays close to the somewhat established Gymnax API for the reset() and step() functions, but allows for truncated episodes in a manner closer to Gymnasium.

obs, env_state = env.reset(key)  # <-- Mirroring Gymnax

# env.step(): Gymnasium Timestep tuple with state information
(obs, reward, terminated, truncated, info), env_state = env.step(key, env_state, action)

Writing your own environment means subclassing jym.Environment and implementing step_env, reset_env, observation_space and action_space.

๐Ÿค– Algorithmsยค

Algorithms in jaxnasium.algorithms follow a near-single-file implementation philosophy. In contrast to implementations in CleanRL or PureJaxRL, Jaxnasium algorithms are built in Equinox and follow a class-based design with a familiar Stable-Baselines API.

Each algorithm is split into an algorithm, which holds the hyperparameters and the training logic, and the agent, holding the trainable state along with the trainer that produced it.

agent, metrics = PPO(**some_good_hyperparameters).train(key, env)

agent.get_action(key, obs)                          # act
agent, more_metrics = agent.train(key, env)         # keep training
agent.save("ppo_agent.eqx")                         # checkpoint, trainer included

Networks are configurable without touching the algorithm, observation and action spaces may be arbitrary PyTrees of spaces, and observation/reward normalization is built in and checkpointed with the agent. See Algorithms for the details.

Algorithm Multi-Agent1 Observation Spaces Action Spaces Composite (nested) Spaces2
PPO โœ… Box, Discrete, MultiDiscrete Box, Discrete, MultiDiscrete โœ…
DQN โœ… Box, Discrete, MultiDiscrete Discrete, MultiDiscrete3 โœ…
PQN โœ… Box, Discrete, MultiDiscrete Discrete, MultiDiscrete3 โœ…
SAC โœ… Box, Discrete, MultiDiscrete Box, Discrete, MultiDiscrete โœ…
1 All algorithms support automatic multi-agent transformation through the auto_upgrade_multi_agent parameter. See Multi-Agent for more information.
2 Algorithms support composite (nested) spaces. See Spaces for more information.
3 MultiDiscrete action spaces in PQN and DQN are only supported when flattening to a Discrete action space. E.g. via the FlattenActionSpaceWrapper.

๐Ÿ‘ฅ Multi-agentยค

Multi-agent support is a core pillar of Jaxnasium's design rather than a separate API. Multi-agent Environments simply expose a PyTree of spaces whose first level is the agent dimension, and algorithms build one agent per entry automatically:

env = jym.make("MPE_simple_spread_v3")  # a JaxMARL environment
agent, metrics = PPO().train(key, env)  # one PPO agent per agent in the environment

agent.get_action(key, obs)  # {"agent_0": ..., "agent_1": ..., ...}

Hyperparameters may even differ per agent, e.g. PPO(gamma={"agent_0": 0.9, "agent_1": 0.99}). See Multi-Agent.

๐Ÿ“Š Sweeps and evaluationยค

jaxnasium.eval turns any function into a parameter sweep, and provides a standard train-and-evaluate trial for Jaxnasium algorithms. Jobs are addressed by index, so the same script runs locally in a loop or as a Slurm job array:

from jaxnasium.eval import AlgorithmEvaluation, GridSearch, SobolSearch, Sweep

sweep = Sweep(
    AlgorithmEvaluation(jax.random.split(key, 10), batch_size=5, save_path="results/"),
    GridSearch({"env": ["CartPole-v1", "Acrobot-v1"], "algorithm": ["PPO", "DQN"]}),
    SobolSearch({"learning_rate_start": (1e-4, 1e-2, "log")}, num_samples=32, seed=key),
)

len(sweep)          # 2 envs * 2 algorithms * 32 learning rates
result = sweep[0]()  # run one job

See Sweeps and Searches.

Where to go nextยค

Environment ยท Spaces ยท Wrappers Build or adapt an environment
Algorithms ยท Multi-Agent Train agents
Networks ยท Architectures Swap in your own model
Sweeps ยท Searches Run experiments at scale
Compilation Ahead-of-time compilation and caching
Checkpointing ยท Tree utilities ยท CLI Everything else