Specifications¶
Written by benchmarks/reference/refcmp_publish.py specs from
benchmarks/reference/refcmp_specs.py, which a test keeps it equal to.
Each comparison is set before its runs and changed only as a new
version (see the protocol).
PPO, version 2¶
Ajax's PPO against stable-baselines3's PPO, MlpPolicy.
- Environment: gymnax CartPole-v1, a port of Gymnasium's CartPole-v1 (dynamics, termination, 500-step limit); SB3 steps it through refcmp_sb3.gymnax_vec_env.
- Budget: 200,704 steps per seed.
- Evaluation: 10 episodes of the deterministic policy, every 14,336 steps; a seed's score is the last one's mean return.
- AdaStop: n = 4, K = 24, alpha = 0.05.
- Margin: ±25.
- Spec hash:
ab1379d3e1e9ebb6. - Reproduce:
uv run python benchmarks/reference/refcmp_protocol.py run PPO --cache DIR
| Reference setting | Value | Ajax setting | Value |
|---|---|---|---|
n_envs |
8 |
n_envs |
8 |
n_steps |
256 |
n_steps |
256 |
batch_size |
256 |
- | |
n_epochs |
10 |
n_epochs |
10 |
learning_rate |
0.0003 |
actor_learning_rate, critic_learning_rate |
0.0003, 0.0003 |
gamma |
0.99 |
gamma |
0.99 |
gae_lambda |
0.95 |
gae_lambda |
0.95 |
clip_range |
0.2 |
clip_range |
0.2 |
ent_coef |
0.0 |
ent_coef |
0.0 |
vf_coef |
0.5 |
vf_coef |
1.0 |
max_grad_norm |
0.5 |
max_grad_norm |
0.5 |
net_arch |
{'pi': [64, 64], 'vf': [64, 64]} |
- | |
activation |
'tanh' |
- |
Ajax settings without a reference counterpart: num_minibatches=8, actor_architecture=('64', 'tanh', '64', 'tanh'), critic_architecture=('64', 'tanh', '64', 'tanh'), adam_eps=1e-05, encoder_kernel_init='orthogonal(1.4142135623730951)'.
Not matched:
PPO-inverted_pendulum, version 2¶
Ajax's PPO against brax.training.agents.ppo, with Brax's settings for inverted_pendulum (notebooks/training.ipynb) but the length.
- Environment: Brax's inverted_pendulum (generalized pipeline, 1000-step episodes), a MuJoCo InvertedPendulum simulated by Brax: the same Env on both sides.
- Budget: 819,200 steps per seed.
- Evaluation: 128 episodes of the deterministic policy, every 163,840 steps; a seed's score is the last one's mean return.
- AdaStop: n = 4, K = 24, alpha = 0.05.
- Margin: ±50.
- Spec hash:
5f421eaf04254d20. - Reproduce:
uv run python benchmarks/reference/refcmp_protocol.py run PPO-inverted_pendulum --cache DIR
| Reference setting | Value | Ajax setting | Value |
|---|---|---|---|
num_envs |
2048 |
n_envs |
2048 |
batch_size |
1024 |
- | |
num_minibatches |
32 |
num_minibatches |
32 |
unroll_length |
5 |
unroll_length |
5 |
num_updates_per_batch |
4 |
n_epochs |
4 |
learning_rate |
0.0003 |
actor_learning_rate, critic_learning_rate |
0.0003, 0.0003 |
entropy_cost |
0.01 |
ent_coef |
0.01 |
discounting |
0.97 |
gamma |
0.97 |
gae_lambda |
0.95 |
gae_lambda |
0.95 |
clipping_epsilon |
0.3 |
clip_range |
0.3 |
vf_loss_coefficient |
0.5 |
vf_coef |
0.5 |
max_grad_norm |
None |
max_grad_norm |
None |
normalize_advantage |
True |
normalize_advantage |
True |
normalize_observations |
True |
normalize_observations |
True |
reward_scaling |
10 |
- | |
episode_length |
1000 |
episode_length |
1000 |
action_repeat |
1 |
action_repeat |
1 |
Ajax settings without a reference counterpart: n_steps=80, adam_eps=1e-08, actor_architecture=('32', 'swish', '32', 'swish', '32', 'swish', '32', 'swish'), critic_architecture=('256', 'swish', '256', 'swish', '256', 'swish', '256', 'swish', '256', 'swish'), encoder_kernel_init='lecun_uniform', mean_kernel_init='lecun_uniform', squash=True, log_std_state_independent=False, log_std_init=-0.3651.
Not matched:
- rewards: Brax scales them by 10 (reward_scaling); Ajax's PPO cannot
- time limits: Brax leaves a truncated step out of the advantage (bootstrap_on_timeout off: this env marks every fall as a time out, so it would bootstrap falls); Ajax bootstraps it
- auto-reset: Brax restarts each env from its own first state; Ajax draws a new one
- std, computed from the observation on both sides: softplus(x) + 0.001 in Brax, exp(x) clipped to [-20, 2] in Ajax; its weights start lecun_uniform in Brax, zero in Ajax
- the value head's initialisation (lecun_uniform in Brax)
DQN, version 1¶
Ajax's DQN against stable-baselines3's DQN, MlpPolicy.
- Environment: gymnax CartPole-v1, a port of Gymnasium's CartPole-v1 (dynamics, termination, 500-step limit); SB3 steps it through refcmp_sb3.gymnax_vec_env.
- Budget: 100,000 steps per seed.
- Evaluation: 10 episodes of the deterministic policy, every 5,000 steps; a seed's score is the last one's mean return.
- AdaStop: n = 4, K = 24, alpha = 0.05.
- Margin: none registered yet.
- Spec hash:
74aef7221269ae79. - Reproduce:
uv run python benchmarks/reference/refcmp_protocol.py run DQN --cache DIR
| Reference setting | Value | Ajax setting | Value |
|---|---|---|---|
learning_rate |
0.001 |
learning_rate |
0.001 |
buffer_size |
100000 |
buffer_size |
100000 |
learning_starts |
1000 |
learning_starts |
1000 |
batch_size |
64 |
batch_size |
64 |
gamma |
0.99 |
gamma |
0.99 |
train_freq |
1 |
- | |
gradient_steps |
1 |
n_gradient_steps |
1 |
target_update_interval |
500 |
target_update_interval |
500 |
exploration_fraction |
0.2 |
epsilon_decay_frac |
0.2 |
exploration_initial_eps |
1.0 |
epsilon_start |
1.0 |
exploration_final_eps |
0.05 |
epsilon_end |
0.05 |
max_grad_norm |
10.0 |
max_grad_norm |
10.0 |
net_arch |
[128, 128] |
- |
Ajax settings without a reference counterpart: n_envs=1, tau=1.0, architecture=('128', 'relu', '128', 'relu'), td_loss='huber'.
Not matched:
- weight initialisation (torch default vs Ajax's)
- Adam eps: 1e-5 in Ajax, 1e-8 in the reference
SAC, version 1¶
Ajax's SAC against stable-baselines3's SAC, MlpPolicy.
- Environment: gymnax Pendulum-v1, a port of Gymnasium's Pendulum-v1 (200-step episodes); SB3 steps it through refcmp_sb3.gymnax_vec_env.
- Budget: 20,000 steps per seed.
- Evaluation: 10 episodes of the deterministic policy, every 1,000 steps; a seed's score is the last one's mean return.
- AdaStop: n = 4, K = 24, alpha = 0.05.
- Margin: none registered yet.
- Spec hash:
3639b4ebc6ea4944. - Reproduce:
uv run python benchmarks/reference/refcmp_protocol.py run SAC --cache DIR
| Reference setting | Value | Ajax setting | Value |
|---|---|---|---|
learning_rate |
0.001 |
actor_learning_rate, critic_learning_rate, alpha_learning_rate |
0.001, 0.001, 0.001 |
buffer_size |
20000 |
buffer_size |
20000 |
learning_starts |
100 |
learning_starts |
100 |
batch_size |
256 |
batch_size |
256 |
tau |
0.005 |
tau |
0.005 |
gamma |
0.99 |
gamma |
0.99 |
train_freq |
1 |
- | |
gradient_steps |
1 |
- | |
ent_coef |
'auto' |
- | |
net_arch |
[256, 256] |
- |
Ajax settings without a reference counterpart: n_envs=1, alpha_init=1.0, target_entropy_per_dim=-1.0, policy_update_start=0, alpha_update_start=0, actor_architecture=('256', 'relu', '256', 'relu'), critic_architecture=('256', 'relu', '256', 'relu').
Not matched:
- weight initialisation
- initial policy std: exp(-1) in Ajax, about 1 in SB3
- Adam eps: 1e-5 in Ajax, 1e-8 in the reference
TD3, version 1¶
Ajax's TD3 against stable-baselines3's TD3, MlpPolicy.
- Environment: gymnax Pendulum-v1, a port of Gymnasium's Pendulum-v1 (200-step episodes); SB3 steps it through refcmp_sb3.gymnax_vec_env.
- Budget: 20,000 steps per seed.
- Evaluation: 10 episodes of the deterministic policy, every 1,000 steps; a seed's score is the last one's mean return.
- AdaStop: n = 4, K = 24, alpha = 0.05.
- Margin: none registered yet.
- Spec hash:
7d9a3c8804e712ba. - Reproduce:
uv run python benchmarks/reference/refcmp_protocol.py run TD3 --cache DIR
| Reference setting | Value | Ajax setting | Value |
|---|---|---|---|
learning_rate |
0.001 |
actor_learning_rate, critic_learning_rate |
0.001, 0.001 |
buffer_size |
20000 |
buffer_size |
20000 |
learning_starts |
100 |
learning_starts |
100 |
batch_size |
256 |
batch_size |
256 |
tau |
0.005 |
tau |
0.005 |
gamma |
0.99 |
gamma |
0.99 |
train_freq |
1 |
- | |
gradient_steps |
1 |
- | |
policy_delay |
2 |
policy_delay |
2 |
target_policy_noise |
0.2 |
target_policy_noise |
0.2 |
target_noise_clip |
0.5 |
target_noise_clip |
0.5 |
action_noise_std |
0.1 |
exploration_noise |
0.1 |
net_arch |
[400, 300] |
- |
Ajax settings without a reference counterpart: n_envs=1, actor_architecture=('400', 'relu', '300', 'relu'), critic_architecture=('400', 'relu', '300', 'relu').
Not matched:
- weight initialisation
- Adam eps: 1e-5 in Ajax, 1e-8 in the reference
PQN, version 2¶
Ajax's PQN against purejaxql 47af6d7: vendor/purejaxql/pqn_gymnax.py.
- Environment: gymnax CartPole-v1, a port of Gymnasium's CartPole-v1.
- Budget: 491,520 steps per seed.
- Evaluation: 128 episodes of the deterministic policy, every 24,576 steps; a seed's score is the last one's mean return.
- AdaStop: n = 4, K = 24, alpha = 0.05.
- Margin: ±25.
- Spec hash:
100ff8e434eecb1a. - Reproduce:
uv run python benchmarks/reference/refcmp_protocol.py run PQN --cache DIR
| Reference setting | Value | Ajax setting | Value |
|---|---|---|---|
TOTAL_TIMESTEPS |
491520 |
- | |
TOTAL_TIMESTEPS_DECAY |
491520 |
- | |
NUM_ENVS |
32 |
n_envs |
32 |
NUM_STEPS |
64 |
n_steps |
64 |
EPS_START |
1.0 |
epsilon_start |
1.0 |
EPS_FINISH |
0.2 |
epsilon_end |
0.2 |
EPS_DECAY |
0.2 |
epsilon_decay_frac |
0.2 |
NUM_MINIBATCHES |
16 |
num_minibatches |
16 |
NUM_EPOCHS |
4 |
n_epochs |
4 |
NORM_INPUT |
False |
- | |
HIDDEN_SIZE |
256 |
- | |
NUM_LAYERS |
2 |
- | |
NORM_TYPE |
'layer_norm' |
- | |
LR |
0.0001 |
learning_rate |
0.0001 |
MAX_GRAD_NORM |
10 |
max_grad_norm |
10.0 |
LR_LINEAR_DECAY |
True |
- | |
REW_SCALE |
0.1 |
reward_scale |
0.1 |
GAMMA |
0.99 |
gamma |
0.99 |
LAMBDA |
0.95 |
q_lambda |
0.95 |
ENV_NAME |
'CartPole-v1' |
- | |
ENV_KWARGS |
{} |
- | |
TEST_DURING_TRAINING |
True |
- | |
TEST_INTERVAL |
0.05 |
- | |
TEST_NUM_ENVS |
128 |
- | |
EPS_TEST |
0.0 |
- | |
WANDB_MODE |
'disabled' |
- |
Ajax settings without a reference counterpart: architecture=('256', 'relu', '256', 'relu'), lr_decay=('linear', 1e-20, 15360), td_loss='half_squared'.
Not matched:
- optimiser: RAdam (eps 1e-8) in purejaxql, Adam (eps 1e-5) in Ajax
- weight initialisation (lecun_normal vs Ajax's)
- evaluation: purejaxql scores the last episode each of its 128 test envs ends within one 500-step window, Ajax one episode per env
PQN-Asterix, version 2¶
Ajax's PQN against purejaxql 47af6d7: vendor/purejaxql/pqn_gymnax.py with config/alg/pqn_minatar.yaml but the length. That config is for pqn_minatar.py, whose CNN Ajax's CNN encoder cannot build (VALID padding, LayerNorm after the convolution); both sides use pqn_gymnax.py's MLP on the flattened image instead.
- Environment: gymnax Asterix-MinAtar, a port of MinAtar's Asterix with a 1000-step limit and no sticky actions.
- Budget: 1,228,800 steps per seed.
- Evaluation: 128 episodes of the deterministic policy, every 61,440 steps; a seed's score is the last one's mean return.
- AdaStop: n = 4, K = 24, alpha = 0.05.
- Margin: ±5% of the reference's mean score.
- Spec hash:
c2753ca9a0052abe. - Reproduce:
uv run python benchmarks/reference/refcmp_protocol.py run PQN-Asterix --cache DIR
| Reference setting | Value | Ajax setting | Value |
|---|---|---|---|
TOTAL_TIMESTEPS |
1228800 |
- | |
TOTAL_TIMESTEPS_DECAY |
1228800 |
- | |
NUM_ENVS |
128 |
n_envs |
128 |
NUM_STEPS |
32 |
n_steps |
32 |
EPS_START |
1.0 |
epsilon_start |
1.0 |
EPS_FINISH |
0.05 |
epsilon_end |
0.05 |
EPS_DECAY |
0.1 |
epsilon_decay_frac |
0.1 |
NUM_MINIBATCHES |
32 |
num_minibatches |
32 |
NUM_EPOCHS |
2 |
n_epochs |
2 |
NORM_TYPE |
'layer_norm' |
- | |
LR |
0.0005 |
learning_rate |
0.0005 |
MAX_GRAD_NORM |
10 |
max_grad_norm |
10.0 |
LR_LINEAR_DECAY |
True |
- | |
REW_SCALE |
1.0 |
reward_scale |
1.0 |
GAMMA |
0.99 |
gamma |
0.99 |
LAMBDA |
0.65 |
q_lambda |
0.65 |
ENV_NAME |
'Asterix-MinAtar' |
- | |
ENV_KWARGS |
{} |
- | |
TEST_DURING_TRAINING |
True |
- | |
TEST_INTERVAL |
0.05 |
- | |
TEST_NUM_ENVS |
128 |
- | |
EPS_TEST |
0.0 |
- | |
WANDB_MODE |
'disabled' |
- |
Ajax settings without a reference counterpart: architecture=('128', 'relu', '128', 'relu'), lr_decay=('linear', 1e-20, 19200), td_loss='half_squared'.
Not matched:
- optimiser: RAdam (eps 1e-8) in purejaxql, Adam (eps 1e-5) in Ajax
- weight initialisation (lecun_normal vs Ajax's)
- evaluation: purejaxql scores the last episode each of its 128 test envs ends within one 1000-step window, Ajax one episode per env
APG, version 1¶
Ajax's APG against brax.training.agents.apg, on refcmp_brax.GymnaxAsBrax.
- Environment: gymnax Pendulum-v1 with transition gradients, a port of Gymnasium's Pendulum-v1.
- Budget: 2,048,000 steps per seed.
- Evaluation: 10 episodes of the deterministic policy, once, at the end; a seed's score is the last one's mean return.
- AdaStop: n = 4, K = 24, alpha = 0.05.
- Margin: none registered yet.
- Spec hash:
c74efa6c58e75716. - Reproduce:
uv run python benchmarks/reference/refcmp_protocol.py run APG --cache DIR
| Reference setting | Value | Ajax setting | Value |
|---|---|---|---|
episode_length |
200 |
- | |
policy_updates |
1000 |
- | |
horizon_length |
32 |
horizon |
32 |
num_envs |
64 |
n_envs |
64 |
learning_rate |
0.0001 |
learning_rate |
0.0001 |
Ajax settings without a reference counterpart: beta_1=0.7, beta_2=0.95, max_grad_norm=None, lr_decay=('exponential', 0.997), actor_architecture=('32', 'relu', '32', 'relu', '32', 'relu', '32', 'relu').
Not matched:
- episodes: Brax continues them across updates from scrambled start times and resets each env to its own first state (64 starts); Ajax restarts them from fresh states at every update
- Brax trains on sampled actions (tanh-Gaussian, var_scale 0.1); Ajax's controller is deterministic
- Brax clips each gradient entry to [-1, 1]; its loss is the mean reward over the horizon, Ajax's the summed return
- network: ELU with LayerNorm and orthogonal(0.01) in Brax, ReLU in Ajax
- Adam eps: 1e-5 in Ajax, 1e-8 in the reference