Skip to main content

tt-rl

CI License: MIT

Reinforcement learning on Tenstorrent hardware, built directly on TT-NN. Networks, gradients, the Adam step and the device trace all run on the chip through ttnn. Environments and replay buffers stay on the host.

Pong
Pong, DQN
Breakout
Breakout, DQN
Boxing
Boxing, DQN
Hopper
Hopper, SAC
  • Algorithms: DQN (double, n-step, prioritized replay), PPO (GAE, clipping, optional GRU memory) and SAC (twin critics, learned temperature).
  • Environments: Atari pixels, MuJoCo state vectors, and any environment you write against a small protocol.
  • Single or multiple chips: a run trains on one chip, or data-parallel across several with one replica per chip.
  • Deploy anywhere: export a trained policy to numpy or ONNX and run it on any CPU.

Results

Measured on one Blackhole chip. Atari scores are the mean over 30 evaluation episodes with sticky actions (0.25), which is harder than the NoFrameskip-v4 setting most published numbers use. A step is one agent action, four Atari frames. Each score is the best evaluation of its run.

Task Algorithm Steps Score Weights
Pong DQN 10M 17.8 ttrl-dqn-pong
Breakout DQN 10M 144.8 ttrl-dqn-breakout
Boxing DQN 10M 91.0 ttrl-dqn-boxing
Hopper-v5 SAC 1M 3604 ttrl-sac-hopper

Each config in configs/ records how it was measured in its docstring. The trained policies are on Hugging Face as ONNX and numpy weights, with a play.py that runs them on CPU.

Learning

From an untrained agent to the final one, at fixed checkpoints of the same run (Atari clips play at 3x speed):

Pong learning

Hopper learning

Breakout and Boxing

Breakout learning

Boxing learning

Requirements

  • Linux and Python 3.12. ttnn 0.80 ships wheels for 3.10 and 3.12, and 3.10 is dropped because current Pillow and filelock no longer support it.
  • A Tenstorrent Blackhole chip with its driver and firmware installed. Developed on a TT-QuietBox with four Blackhole chips.

Install

ttrl installs from source for now. A PyPI release as tt-rl is on its way, after which pip install tt-rl will be enough.

git clone https://github.com/scaleresearch/tt-rl && cd tt-rl
pip install -e '.[atari]'     # Atari
pip install -e '.[mujoco]'    # MuJoCo
pip install -e '.[dev]'       # both, plus the test suite's dependencies

ttnn installs from PyPI as a dependency. Then check the machine:

ttrl doctor

It reports versions, which chips exist and who holds each one, and opens a free chip to confirm a trace captures and replays.

Train

A run is a Python config. Arguments after the path override any field by its dotted name:

ttrl train configs/pong_dqn.py device.chip=0
ttrl train configs/pong_dqn.py total_frames=3000 algo_cfg.learning_starts=500 log_dir=runs/smoke

The same from Python:

import ttrl

config = ttrl.load_config("configs/pong_dqn.py", overrides=["device.chip=0"])
result = ttrl.run(config)

A run writes metrics, checkpoints and checkpoint-best.npz to its log_dir. ttrl train --resume best <config> continues from the best one; DQN and SAC runs also need checkpoint_replay=True so the replay buffer is saved with them. examples/atari-dqn and examples/mujoco-sac are standalone projects that use ttrl as a library.

A chip is exclusive to one process. device.chip=N names a physical chip, the N in /dev/tenstorrent/N; leave it unset to take the lowest free one.

Multiple chips

device.chips trains one replica per chip. Each replica steps its own environments and runs its own updates, and every device.sync_every updates (50 by default) the replicas average their parameters, so they stay one model.

ttrl train configs/pong_ppo.py device.chips=4          # any 4 chips
ttrl train configs/pong_ppo.py device.chips=all        # every chip on the machine
ttrl train configs/pong_ppo.py 'device.chips=[0,1]'    # these physical chips
config = ttrl.load_config("configs/pong_ppo.py", overrides=["device.chips=4"])
ttrl.run(config)

N replicas collect N times the experience per update round, so a multi-chip run is a different recipe from its single-chip config, and learning rates or batch sizes may need retuning. PPO, which already runs many environments at once, is the natural fit.

Deploy without a chip

import ttrl

artifact = ttrl.export_policy("runs/pong/checkpoint-best.npz", "pong.policy.npz")
ttrl.export_onnx(artifact, "pong.onnx")      # needs the [onnx] extra

policy = ttrl.load_policy(artifact)          # numpy only
action = policy(obs)                         # obs: one raw observation

The ONNX graph has one input, obs, and two outputs, action and head (the Q-values or action distribution). It runs with onnxruntime, and the Atari DQN graph also compiles unchanged with Tenstorrent's TT-Forge-ONNX.

Your own environment

An environment is any object with obs_spec, action_spec, episode_bound, reset, step and close. Nothing needs subclassing:

import numpy as np
import ttrl
from ttrl.envs import check_env
from ttrl.envs.registry import register
from ttrl.envs.wrappers import RecordEpisodeStatistics


class Slider:
    """Move a point to the origin. Reward is the step's reduction in |x|."""

    obs_spec = ttrl.ObsSpec.vector(2)
    action_spec = ttrl.ActionSpec.discrete(3)
    episode_bound = 64

    def reset(self, *, seed=None):
        self.rng = np.random.default_rng(seed)
        self.x, self.t = float(self.rng.uniform(-1.0, 1.0)), 0
        return self._obs(), {}

    def _obs(self):
        return np.array([self.x, self.t / self.episode_bound], dtype=np.float32)

    def step(self, action):
        before = abs(self.x)
        self.x = float(np.clip(self.x + (int(action) - 1) * 0.05, -1.0, 1.0))
        self.t += 1
        return self._obs(), before - abs(self.x), False, self.t >= self.episode_bound, {}

    def close(self):
        pass


register("slider", lambda **kw: RecordEpisodeStatistics(Slider(**kw)))
check_env(RecordEpisodeStatistics(Slider()))

config = ttrl.TrainConfig(
    algo="dqn",
    total_frames=4_000,
    eval_interval=4_000,
    log_dir="runs/slider",
    env=ttrl.EnvConfig(name="slider", n_envs=1),
    algo_cfg=ttrl.DQNConfig(batch_size=64, learning_starts=500, buffer_capacity=4_000),
)
ttrl.run(config)

check_env raises on the first contract violation with a message saying what was expected. RecordEpisodeStatistics is required: evaluation reads each episode's score from it.

Tests

pytest                 # host suite, no chip needed
pytest -m tt           # hardware suite, needs a chip

License

MIT

Metadata

Release files for tt-rl 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for tt-rl 0.1.0
File Size Uploaded Provenance Actions
tt_rl-0.1.0.tar.gz 452.5 kB With provenance Details for tt_rl-0.1.0.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for tt-rl 0.1.0
File Interpreter ABI Platform Actions
tt_rl-0.1.0-py3-none-any.whl Python 3 none any Details for tt_rl-0.1.0-py3-none-any.whl

Total release size: 983.1 kB

Release files / tt_rl-0.1.0.tar.gz

Download URL tt_rl-0.1.0.tar.gz
Size 452.5 kB
Tags Source
SHA-256 checksum
How to use checksums
864c516857866c2c3d44b2eabfc22476a74e417627a41ceea41124612ad3722f
BLAKE2b-256 checksum
How to use checksums
175c2df433c6367d43de32a8443f3aff5dd08cd3cfeb45e94f5360429ae96a8d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Release files / tt_rl-0.1.0-py3-none-any.whl

Download URL tt_rl-0.1.0-py3-none-any.whl
Size 530.5 kB
Tags Python 3
SHA-256 checksum
How to use checksums
fc41119d4ff0bb1ec59904b7f89f3d9f124ae4698022f107f7f8caef8e4152dc
BLAKE2b-256 checksum
How to use checksums
5f86268730888c1fd61ee8180e81ee3eea63cad0b9dba88d539f4df5af57d213
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/7.0.0 CPython/3.13.16

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

Security for tt-rl 0.1.0

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

Both files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for tt_rl-0.1.0.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Nov 6, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page