Overview
Overview
All kernels in rl-triton share a single architectural idea: express the RL recurrence as a linear recurrence of the form
and solve it in \(O(\log N)\) parallel steps using an associative scan on the GPU, rather than the \(O(N)\) sequential loop a naïve implementation requires. The scan runs entirely inside Streaming Multiprocessor SRAM, avoiding repeated round-trips to High Bandwidth Memory (HBM).
For sequences that fit in a single thread block (seq_len ≤ 131072), a
fully-fused kernel computes every intermediate quantity - TD errors, IS
ratios, decay products - without materialising any intermediate tensors.
Longer sequences fall back to a chunked scan that stitches results across
blocks.
Choosing the Right Kernel
| Situation | Kernel |
|---|---|
| On-policy PPO / A2C advantage estimation | compute_gae |
| Off-policy actor-critic (IMPALA, APPO) | compute_vtrace |
| Off-policy Q-learning, discrete actions | compute_retrace |
| Critic targets with bias-variance control | compute_lambda_returns |
| Simple reward-to-go baseline | compute_discounted_returns |
| TD(λ) parameter updates with traces | compute_eligibility_traces |
| Episode-scoped cumulative statistics | compute_episodic_prefix_sum |
API Reference
rl_triton.ops.gae.compute_gae(rewards, values, terminateds, truncateds=None, gamma=0.99, lambda_=0.95, bootstrap_values=None, last_value=None)
Compute Generalized Advantage Estimation via a backward associative scan.
Recurrence:
- A[t] = δ[t] + β[t] * A[t+1], A[T] = 0
- δ[t] = r[t] + γ·(1-terminated[t]) * v_next[t] - V(s_t)
- β[t] = γ·λ·(1-done[t]) (scan decay coefficient)
- done[t] = terminated[t] | truncated[t]
terminated[t] gates the one-step bootstrap inside δ[t]: set to 1 for true
episode ends (s_{t+1} is a reset state with no meaningful value).
truncated[t] stops trace propagation and injects the true continuation
value bootstrap_values[env, t] = V(s_{t+1}^true) into δ[t].
bootstrap_values supplies the true continuation value V(s_{t+1}) wherever the stored values[t+1] is invalid. Two situations require this, under one rule: (a) truncated steps, where values[t+1] belongs to the next episode; and (b) the final column t=T-1, where values[t+1] lies past the buffer. In both cases set bootstrap_values to the true continuation value; leave it zero everywhere else.
The final column feeds delta[T-1] as the next-state value ONLY. The scan's additive boundary carry A[T] is always 0: an advantage carry represents trace mass from steps past the buffer, and there is none there. (β[T-1], the multiplicative decay coefficient, is unaffected by this -- only the additive A[T] term changes.) A single entry in bootstrap_values[:, -1] is sufficient -- set it to V(s_T) if the episode continues past the window, or 0 if it terminated at T-1.
Most users have no interior truncations and should use the last_value
argument (shape [num_envs]) instead, which populates the boundary column
automatically.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rewards
|
Tensor
|
Per-step rewards, [num_envs, seq_len], float32, CUDA. |
required |
values
|
Tensor
|
V(s_t), [num_envs, seq_len], float32, CUDA. |
required |
terminateds
|
Tensor
|
True termination flags (1.0=terminated), [num_envs, seq_len], float32, CUDA. |
required |
truncateds
|
Tensor | None
|
Time-limit truncation flags (1.0=truncated), [num_envs, seq_len], float32, CUDA. If None, terminateds is used for both gating roles (conservative: treats all boundaries as terminations). |
None
|
gamma
|
float
|
Discount factor (default 0.99). |
0.99
|
lambda_
|
float
|
GAE trace parameter in [0, 1] (default 0.95). |
0.95
|
bootstrap_values
|
Tensor | None
|
True continuation values V(s_{t+1}^true), [num_envs, seq_len], float32, CUDA. Set bootstrap_values[env, t] = V(s_{t+1}^true) at every truncated step and at t=T-1 if the window ends mid-episode. Zero elsewhere. If None, defaults to all zeros. Mutually exclusive with last_value. |
None
|
last_value
|
Tensor | None
|
Convenience arg for the common case of no interior truncations: V(s_T) per environment, shape [num_envs], float32, CUDA. Populates bootstrap_values[:, -1] automatically. Mutually exclusive with bootstrap_values. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
advantages |
Tensor
|
A[t], shape [num_envs, seq_len], float32. |
Source code in src/rl_triton/ops/gae.py
34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 | |
rl_triton.ops.vtrace.compute_vtrace(log_pi_target, log_pi_behavior, values, rewards, terminateds, truncateds=None, gamma=0.99, rho_bar=1.0, c_bar=1.0, bootstrap_values=None, last_value=None)
Compute V-Trace targets and advantages via a backward associative scan.
V-Trace (Espeholt et al. 2018, IMPALA) corrects for off-policy data using clipped importance sampling ratios ρ and c.
Recurrence:
- Δ[t] = δ[t] + β[t] * Δ[t+1], Δ[T] = 0
- δ[t] = ρ[t] * (r[t] + γ * V(s_{t+1}) * (1 - terminated[t]) - V(s_t))
- β[t] = γ * c[t] * (1 - done[t]), done[t] = terminated[t] | truncated[t] (scan decay coefficient)
- ρ[t] = min(ρ_bar, π_target[t] / π_behavior[t])
- c[t] = min(c_bar, π_target[t] / π_behavior[t])
Outputs:
- vs[t] = Δ[t] + V(s_t) (critic targets)
- A[t] = ρ[t] * (r[t] + γ * vs[t+1] * (1 - terminated[t]) - V(s_t)) (actor advantages)
bootstrap_values supplies the true continuation value V(s_{t+1}) wherever the stored values[t+1] is invalid. Two situations require this, under one rule: (a) truncated steps, where values[t+1] belongs to the next episode; and (b) the final column t=T-1, where values[t+1] lies past the buffer. In both cases set bootstrap_values to the true continuation value; leave it zero everywhere else.
The final column feeds delta[T-1] as the next-state value ONLY. The scan's additive boundary carry Δ[T] is always 0: an advantage carry represents trace mass from steps past the buffer, and there is none there. (β[T-1], the multiplicative decay coefficient above, is unaffected by this -- only the additive Δ[T] term changes.) A single entry in bootstrap_values[:, -1] is sufficient -- set it to V(s_T) if the episode continues past the window, or 0 if it terminated at T-1. (The same bootstrap_values[:, -1] is also used directly, not as a carry, when computing next_vtrace_targets[:, -1] for the advantage formula -- that use is single-counted and correct as-is.)
Most users have no interior truncations and should use the last_value
argument (shape [num_envs]) instead, which populates the boundary column
automatically.
terminated[t] gates the one-step bootstrap in δ[t]: set to 1 at true episode
ends. truncated[t] stops trace propagation and injects bootstrap_values[env, t]
= V(s_{t+1}^true), correcting for stale reset observations in the values buffer
under Gymnasium next-step autoreset.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
log_pi_target
|
Tensor
|
Log probabilities under target policy, [num_envs, seq_len], float32, CUDA. |
required |
log_pi_behavior
|
Tensor
|
Log probabilities under behavior policy, [num_envs, seq_len], float32, CUDA. |
required |
values
|
Tensor
|
V(s_t), [num_envs, seq_len], float32, CUDA. |
required |
rewards
|
Tensor
|
Per-step rewards, [num_envs, seq_len], float32, CUDA. |
required |
terminateds
|
Tensor
|
True termination flags (1.0=terminated), [num_envs, seq_len], float32, CUDA. |
required |
truncateds
|
Tensor | None
|
Time-limit truncation flags (1.0=truncated), [num_envs, seq_len], float32, CUDA. If None, terminateds is used for both gating roles. |
None
|
gamma
|
float
|
Discount factor (default 0.99). |
0.99
|
rho_bar
|
float
|
IS ratio clip for δ (default 1.0). |
1.0
|
c_bar
|
float
|
IS ratio clip for decay (default 1.0). |
1.0
|
bootstrap_values
|
Tensor | None
|
True continuation values V(s_{t+1}^true), [num_envs, seq_len], float32, CUDA. Set bootstrap_values[env, t] = V(s_{t+1}^true) at every truncated step and at t=T-1 if the window ends mid-episode. Zero elsewhere. If None, defaults to all zeros. Mutually exclusive with last_value. |
None
|
last_value
|
Tensor | None
|
Convenience arg for the common case of no interior truncations: V(s_T) per environment, shape [num_envs], float32, CUDA. Populates bootstrap_values[:, -1] automatically. Mutually exclusive with bootstrap_values. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
vtrace_targets |
Tensor
|
vs[t], shape [num_envs, seq_len], float32. |
vtrace_advantages |
Tensor
|
A[t], shape [num_envs, seq_len], float32. |
Source code in src/rl_triton/ops/vtrace.py
7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 | |
rl_triton.ops.retrace.compute_retrace(action_probs_target, action_probs_behavior, q_values, next_q_values_all, actions, rewards, terminateds, truncateds, gamma, lambda_=1.0, c_bar=1.0, rho_bar=1.0)
Compute Retrace(λ) Q-value targets and advantages via a backward associative scan.
Retrace(λ) (Munos et al. 2016) corrects off-policy Q-value estimates using truncated IS traces applied only to the decay factor, not the TD error. Discrete actions only -- use compute_vtrace for continuous action spaces.
Recurrence:
- Δ[t] = δ[t] + decay[t] * Δ[t+1], Δ[T] = 0
- δ[t] = r[t] + γ · E_π[Q(s_{t+1},a)] · (1-terminated[t]) - Q(s_t,a_t)
- decay[t] = γ · c[t+1] · (1-done[t]), done[t] = terminated[t] | truncated[t]
- c[t] = λ · min(c_bar, π(a_t|s_t) / μ(a_t|s_t))
Q-value targets: Q_ret[t] = Q(s_t, a_t) + Δ[t] Advantages: A[t] = ρ[t] · (r[t] + γ · Q_ret[t+1] · (1-terminated[t]) - Q(s_t,a_t)) ρ[t] = min(rho_bar, π(a_t|s_t) / μ(a_t|s_t))
The Q-bootstrap γ·E_π[Q(s_{t+1},·)] is folded into δ[t] via
next_q_values_all, so no separate bootstrap_values argument is needed.
terminated gates the one-step bootstrap in δ[t]; terminated | truncated
gates trace decay in β[t].
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
action_probs_target
|
Tensor
|
Target policy probabilities over all actions, [num_envs, seq_len, num_actions], float32, CUDA. |
required |
action_probs_behavior
|
Tensor
|
Behavior policy probability of the taken action, [num_envs, seq_len], float32, CUDA. |
required |
q_values
|
Tensor
|
Q(s_t, a_t) for the taken action, [num_envs, seq_len], float32, CUDA. |
required |
next_q_values_all
|
Tensor
|
Q(s_{t+1}, a) for all actions, [num_envs, seq_len, num_actions], float32, CUDA. |
required |
actions
|
Tensor
|
Indices of the taken action, [num_envs, seq_len], int64, CUDA. |
required |
rewards
|
Tensor
|
Per-step rewards, [num_envs, seq_len], float32, CUDA. |
required |
terminateds
|
Tensor
|
True termination flags (1.0 = terminated). Zeros the bootstrap γ·E_π[Q(s_{t+1},·)] in δ[t]. [num_envs, seq_len], float32, CUDA. |
required |
truncateds
|
Tensor
|
Time-limit truncation flags (1.0 = truncated). Keeps the bootstrap in δ[t] but severs the trace. [num_envs, seq_len], float32, CUDA. |
required |
gamma
|
float
|
Discount factor. |
required |
lambda_
|
float
|
Trace decay parameter (default 1.0). |
1.0
|
c_bar
|
float
|
IS ratio clip for trace weights (default 1.0). |
1.0
|
rho_bar
|
float
|
IS ratio clip for advantage scaling (default 1.0). |
1.0
|
Returns:
| Name | Type | Description |
|---|---|---|
retrace_targets |
Tensor
|
Q_ret[t], shape [num_envs, seq_len], float32. |
advantages |
Tensor
|
A[t], shape [num_envs, seq_len], float32. |
Source code in src/rl_triton/ops/retrace.py
52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 | |
rl_triton.ops.returns.compute_lambda_returns(rewards, next_values, terminateds, truncateds=None, gamma=0.99, lambda_=0.95, bootstrap_values=None)
Compute TD(λ) targets (λ-returns) via a backward associative scan.
Recurrence:
- G[t] = r[t] + γ(1-done[t]) * [(1-λ)V(s_{t+1}) + λG[t+1]] + γtruncated[t]*bootstrap[t]
- done[t] = terminated[t] | truncated[t]
Special cases: λ=0 reduces to one-step TD; λ=1 reduces to discounted returns.
terminated[t] stops both the value bootstrap and trace propagation.
truncated[t] stops trace propagation and injects bootstrap_values[env, t]
= V(s_{t+1}^true) as the continuation value. The caller must zero
next_values[env, t] at truncated steps to avoid double-counting.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rewards
|
Tensor
|
Per-step rewards, [num_envs, seq_len], float32, CUDA. |
required |
next_values
|
Tensor
|
V(s_{t+1}), [num_envs, seq_len], float32, CUDA. Must be zeroed by caller at truncated steps. |
required |
terminateds
|
Tensor
|
True termination flags (1.0=terminated), [num_envs, seq_len], float32, CUDA. |
required |
truncateds
|
Tensor | None
|
Time-limit truncation flags (1.0=truncated), [num_envs, seq_len], float32, CUDA. If None, terminateds is used for both gating roles. |
None
|
gamma
|
float
|
Discount factor (default 0.99). |
0.99
|
lambda_
|
float
|
Trace parameter in [0, 1] (default 0.95). |
0.95
|
bootstrap_values
|
Tensor | None
|
True continuation values V(s_{t+1}^true), [num_envs, seq_len], float32, CUDA. Nonzero at truncated steps and at t=T-1 when the window ends mid-episode; zero elsewhere. If None, defaults to all zeros. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
lambda_returns |
Tensor
|
G[t], shape [num_envs, seq_len], float32. |
Source code in src/rl_triton/ops/returns.py
33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 | |
rl_triton.ops.returns.compute_discounted_returns(rewards, terminateds, truncateds=None, gamma=0.99, bootstrap_values=None)
Compute discounted returns (reward-to-go) via a backward associative scan.
Recurrence:
- G[t] = r[t] + γ(1-done[t])G[t+1] + γtruncated[t]bootstrap[t]
- done[t] = terminated[t] | truncated[t]
terminated[t] stops the return propagation (episode ends, G[t+1]=0).
truncated[t] also stops propagation but injects bootstrap_values[env, t]
= V(s_{t+1}^true) as the continuation value instead of zero.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
rewards
|
Tensor
|
Per-step rewards, [num_envs, seq_len], float32, CUDA. |
required |
terminateds
|
Tensor
|
True termination flags (1.0=terminated), [num_envs, seq_len], float32, CUDA. |
required |
truncateds
|
Tensor | None
|
Time-limit truncation flags (1.0=truncated), [num_envs, seq_len], float32, CUDA. If None, terminateds is used for both gating roles. |
None
|
gamma
|
float
|
Discount factor (default 0.99). |
0.99
|
bootstrap_values
|
Tensor | None
|
True continuation values V(s_{t+1}^true), [num_envs, seq_len], float32, CUDA. Nonzero at truncated steps and at t=T-1 when the window ends mid-episode; zero elsewhere. If None, defaults to all zeros. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
returns |
Tensor
|
G[t], shape [num_envs, seq_len], float32. |
Source code in src/rl_triton/ops/returns.py
155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 | |
rl_triton.ops.returns.compute_eligibility_traces(gradients, dones, gamma, lambda_, seed_values=None)
Compute accumulating eligibility traces via a forward associative scan.
Recurrence:
- z[t] = g[t] + γ·λ·(1 - done[t-1]) * z[t-1], z[-1] = seed, done[-1] := 0
g[t] is the per-step input: the value-function gradient ∇_w V̂(s_t) for general function approximation, or the feature vector x(s_t) in the linear case. Unlike all other kernels, this scan runs forward in time.
done uses the same convention as every other function in this package
(done[t]=1 means the episode ends AT t) -- callers pass the same raw done
array used elsewhere, unshifted. Internally, the trace carried into t is
severed whenever the PRECEDING step ended an episode (done[t-1]=1), i.e.
whenever t is the first step of a new episode; gating on done[t] instead
would sever the carry at the old episode's own last step (still that
episode's data) and fail to sever it at the new episode's first step.
Limited to seq_len <= 131072.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
gradients
|
Tensor
|
g[t] -- value-function gradients or feature vectors, [num_envs, seq_len], float32, CUDA. |
required |
dones
|
Tensor
|
Episode termination flags (1.0=done), [num_envs, seq_len], float32, CUDA. done[t]=1 means episode ends at t (same convention as compute_gae etc.); do not pre-shift this array. |
required |
gamma
|
float
|
Discount factor. |
required |
lambda_
|
float
|
Trace decay parameter. |
required |
seed_values
|
Tensor | None
|
Initial trace z[-1] per environment, shape [num_envs]. Defaults to zeros. |
None
|
Returns:
| Name | Type | Description |
|---|---|---|
traces |
Tensor
|
z[t], shape [num_envs, seq_len], float32. |
Source code in src/rl_triton/ops/returns.py
266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 | |
rl_triton.ops.prefix_sum.compute_episodic_prefix_sum(inputs, dones, seed_values=None, boundary='ends_at')
Cumulative sum that resets to zero at episode/segment boundaries.
Two mutually-exclusive boundary conventions, selected by boundary:
- "ends_at" (default): done[t]=1 means the segment ENDS at t; the reset lands at t+1. C[t] = x[t] + (1 - done[t-1]) * C[t-1], C[-1] = seed, done[-1] := 0 This is the Gymnasium-next-step / GAE-canonical convention used identically by every other kernel in this library (compute_gae, compute_lambda_returns, compute_discounted_returns, compute_vtrace, compute_retrace, and compute_eligibility_traces). An RL user computing advantages, returns, and a reset-aware timestep counter from one rollout buffer's terminated/truncated flags gets boundary-consistent results across every kernel by default.
- "starts_at": done[t]=1 means the segment STARTS at t; the reset lands at t itself, immediately. C[t] = x[t] + (1 - done[t]) * C[t-1], C[-1] = seed This is the convention sequence-packing callers want: a document- boundary flag marks a new document's first token, and a RoPE local position counter must reset exactly there, not one token later. Pass this explicitly for that use case -- it is not the default.
In both modes: done[t]=1 accumulator resets (C[t]=x[t], modulo which index the reset is keyed to); done[t]=0 accumulator continues.
Limited to seq_len <= 131072.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
inputs
|
Tensor
|
Values to accumulate x[t], [num_envs, seq_len], float32, CUDA. |
required |
dones
|
Tensor
|
Episode/segment boundary flags (1.0=flagged), [num_envs, seq_len],
float32, CUDA. Meaning depends on |
required |
seed_values
|
Tensor | None
|
Initial carry C[-1] per environment, shape [num_envs]. Defaults to zeros. |
None
|
boundary
|
str
|
"ends_at" (default) or "starts_at" -- see above. |
'ends_at'
|
Returns:
| Name | Type | Description |
|---|---|---|
prefix_sums |
Tensor
|
C[t], shape [num_envs, seq_len], float32. |