flashmap 0.1.0__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,457 @@
1
+ # coding=utf-8
2
+ """Triton kernels of the FlashMap sglang draft (see :mod:`.model`).
3
+
4
+ They reproduce the rounding of the reference modules they replace: the RMSNorm
5
+ multiplies the bf16 input by its bf16-rounded scale ``weight * rsqrt(mean(x^2) +
6
+ eps)``, and the interleaved-pair RoPE rotates in float32 and rounds back to
7
+ bf16. The ops are registered as custom ops so torch.compile keeps them whole
8
+ inside the draft step it compiles.
9
+ """
10
+
11
+ from __future__ import annotations
12
+
13
+ import torch
14
+ import triton
15
+ import triton.language as tl
16
+
17
+
18
+ @triton.jit
19
+ def _block_self_attention_kernel(
20
+ qkv_ptr,
21
+ q_norm_ptr,
22
+ k_norm_ptr,
23
+ freqs_ptr,
24
+ out_ptr,
25
+ eps,
26
+ sm_scale,
27
+ T: tl.constexpr,
28
+ H: tl.constexpr,
29
+ D: tl.constexpr,
30
+ ):
31
+ # One program per (block, head): the block's T queries attend to its T keys.
32
+ b = tl.program_id(0) // H
33
+ h = tl.program_id(0) % H
34
+ HALF: tl.constexpr = D // 2
35
+ t = tl.arange(0, T)[:, None]
36
+ i = tl.arange(0, HALF)[None, :]
37
+ d = tl.arange(0, D)[None, :]
38
+ # qkv is (blocks, T, 3, H, D); RoPE pairs dims (2i, 2i + 1).
39
+ row = qkv_ptr + (b * T + t) * (3 * H * D) + h * D
40
+ freqs = freqs_ptr + (b * T + t) * D + 2 * i # (blocks, T, D / 2, [cos, sin])
41
+ cos = tl.load(freqs)
42
+ sin = tl.load(freqs + 1)
43
+
44
+ q_even = tl.load(row + 2 * i).to(tl.float32)
45
+ q_odd = tl.load(row + 2 * i + 1).to(tl.float32)
46
+ k_even = tl.load(row + H * D + 2 * i).to(tl.float32)
47
+ k_odd = tl.load(row + H * D + 2 * i + 1).to(tl.float32)
48
+
49
+ # RMSNorm: x * bf16(weight * rsqrt(mean(x^2) + eps)), in bf16.
50
+ q_rms = tl.rsqrt((tl.sum(q_even * q_even, 1) + tl.sum(q_odd * q_odd, 1)) / D + eps)[
51
+ :, None
52
+ ]
53
+ k_rms = tl.rsqrt((tl.sum(k_even * k_even, 1) + tl.sum(k_odd * k_odd, 1)) / D + eps)[
54
+ :, None
55
+ ]
56
+ q_even = _scale(q_even, tl.load(q_norm_ptr + 2 * i) * q_rms)
57
+ q_odd = _scale(q_odd, tl.load(q_norm_ptr + 2 * i + 1) * q_rms)
58
+ k_even = _scale(k_even, tl.load(k_norm_ptr + 2 * i) * k_rms)
59
+ k_odd = _scale(k_odd, tl.load(k_norm_ptr + 2 * i + 1) * k_rms)
60
+
61
+ # RoPE, rounded to bf16 as the attention receives it.
62
+ q_rot_even = (q_even * cos - q_odd * sin).to(tl.bfloat16)
63
+ q_rot_odd = (q_odd * cos + q_even * sin).to(tl.bfloat16)
64
+ k_rot_even = (k_even * cos - k_odd * sin).to(tl.bfloat16)
65
+ k_rot_odd = (k_odd * cos + k_even * sin).to(tl.bfloat16)
66
+
67
+ scores = tl.dot(q_rot_even, tl.trans(k_rot_even)) + tl.dot(
68
+ q_rot_odd, tl.trans(k_rot_odd)
69
+ )
70
+ scores = scores * sm_scale
71
+ probs = tl.exp(scores - tl.max(scores, 1)[:, None])
72
+ denom = tl.sum(probs, 1)[:, None]
73
+ values = tl.load(row + 2 * H * D + d)
74
+ out = tl.dot(probs.to(tl.bfloat16), values) / denom
75
+ tl.store(out_ptr + (b * T + t) * (H * D) + h * D + d, out.to(tl.bfloat16))
76
+
77
+
78
+ @triton.jit
79
+ def _scale(x, scale):
80
+ """``x`` (bf16 values in float32) times the bf16-rounded ``scale``, in bf16."""
81
+ return (x * scale.to(tl.bfloat16).to(tl.float32)).to(tl.bfloat16).to(tl.float32)
82
+
83
+
84
+ @torch.library.custom_op("flashmap_sglang::block_self_attention", mutates_args=())
85
+ def block_self_attention(
86
+ qkv: torch.Tensor,
87
+ q_norm: torch.Tensor,
88
+ k_norm: torch.Tensor,
89
+ freqs: torch.Tensor,
90
+ eps: float,
91
+ ) -> torch.Tensor:
92
+ """Self-attention within each draft block, from its projected ``qkv
93
+ (blocks, T, 3, heads, head_dim)``: q/k RMSNorms with float32 weights, RoPE
94
+ by ``freqs (blocks, T, head_dim / 2, [cos, sin])``, then unmasked softmax
95
+ attention. Returns ``(blocks, T, heads, head_dim)``."""
96
+ blocks, block_size, _, heads, head_dim = qkv.shape
97
+ assert qkv.is_contiguous() and freqs.is_contiguous()
98
+ assert freqs.dtype == torch.float32
99
+ out = torch.empty(
100
+ (blocks, block_size, heads, head_dim), dtype=qkv.dtype, device=qkv.device
101
+ )
102
+ _block_self_attention_kernel[(blocks * heads,)](
103
+ qkv,
104
+ q_norm,
105
+ k_norm,
106
+ freqs,
107
+ out,
108
+ eps,
109
+ head_dim**-0.5,
110
+ T=block_size,
111
+ H=heads,
112
+ D=head_dim,
113
+ )
114
+ return out
115
+
116
+
117
+ @block_self_attention.register_fake
118
+ def _(qkv, q_norm, k_norm, freqs, eps):
119
+ blocks, block_size, _, heads, head_dim = qkv.shape
120
+ return qkv.new_empty((blocks, block_size, heads, head_dim))
121
+
122
+
123
+ @triton.jit
124
+ def _rms_norm_kernel(x_ptr, weight_ptr, out_ptr, n_cols, eps, BLOCK: tl.constexpr):
125
+ row = tl.program_id(0).to(tl.int64)
126
+ cols = tl.arange(0, BLOCK)
127
+ mask = cols < n_cols
128
+ x = tl.load(x_ptr + row * n_cols + cols, mask=mask, other=0.0).to(tl.float32)
129
+ rms = tl.rsqrt(tl.sum(x * x, 0) / n_cols + eps)
130
+ scale = tl.load(weight_ptr + cols, mask=mask, other=0.0) * rms
131
+ scale = scale.to(tl.bfloat16).to(tl.float32)
132
+ tl.store(out_ptr + row * n_cols + cols, (x * scale).to(tl.bfloat16), mask=mask)
133
+
134
+
135
+ def rms_norm(x: torch.Tensor, weight: torch.Tensor, eps: float) -> torch.Tensor:
136
+ """RMSNorm of a contiguous bf16 ``x (..., C)`` with a float32 ``weight``,
137
+ in one launch."""
138
+ assert x.dtype == torch.bfloat16 and x.is_contiguous()
139
+ assert weight.dtype == torch.float32
140
+ out = torch.empty_like(x)
141
+ n_cols = x.shape[-1]
142
+ if x.numel():
143
+ _rms_norm_kernel[(x.numel() // n_cols,)](
144
+ x, weight, out, n_cols, eps, BLOCK=triton.next_power_of_2(n_cols)
145
+ )
146
+ return out
147
+
148
+
149
+ @triton.jit
150
+ def _rms_norm_rope_kernel(
151
+ x_ptr, weight_ptr, freqs_ptr, out_ptr, eps, H: tl.constexpr, D: tl.constexpr
152
+ ):
153
+ # One program per (token, head) of a contiguous (tokens, H, D) input.
154
+ token = tl.program_id(0) // H
155
+ head = tl.program_id(0) % H
156
+ HALF: tl.constexpr = D // 2
157
+ i = tl.arange(0, HALF)
158
+ row = x_ptr + (token * H + head) * D
159
+ even = tl.load(row + 2 * i).to(tl.float32)
160
+ odd = tl.load(row + 2 * i + 1).to(tl.float32)
161
+ rms = tl.rsqrt((tl.sum(even * even, 0) + tl.sum(odd * odd, 0)) / D + eps)
162
+ even = _scale(even, tl.load(weight_ptr + 2 * i) * rms)
163
+ odd = _scale(odd, tl.load(weight_ptr + 2 * i + 1) * rms)
164
+ cos = tl.load(freqs_ptr + token * D + 2 * i)
165
+ sin = tl.load(freqs_ptr + token * D + 2 * i + 1)
166
+ out = out_ptr + (token * H + head) * D
167
+ tl.store(out + 2 * i, (even * cos - odd * sin).to(tl.bfloat16))
168
+ tl.store(out + 2 * i + 1, (odd * cos + even * sin).to(tl.bfloat16))
169
+
170
+
171
+ @torch.library.custom_op("flashmap_sglang::rms_norm_rope", mutates_args=())
172
+ def rms_norm_rope(
173
+ x: torch.Tensor, weight: torch.Tensor, freqs: torch.Tensor, eps: float
174
+ ) -> torch.Tensor:
175
+ """RoPE of RMSNorm(x) for ``x (..., T, heads, head_dim)`` and ``freqs (...,
176
+ T, head_dim / 2, [cos, sin])``, in one launch."""
177
+ *_, heads, head_dim = x.shape
178
+ assert x.is_contiguous() and freqs.is_contiguous()
179
+ assert freqs.dtype == torch.float32
180
+ out = torch.empty_like(x)
181
+ _rms_norm_rope_kernel[(x.numel() // head_dim,)](
182
+ x, weight, freqs, out, eps, H=heads, D=head_dim
183
+ )
184
+ return out
185
+
186
+
187
+ @rms_norm_rope.register_fake
188
+ def _(x, weight, freqs, eps):
189
+ return torch.empty_like(x)
190
+
191
+
192
+ __all__ = ["block_self_attention", "rms_norm", "rms_norm_rope"]
193
+
194
+
195
+ @triton.jit
196
+ def _sample_tiles_kernel(
197
+ logits_ptr,
198
+ temperatures_ptr,
199
+ greedy_ptr,
200
+ seed_ptr,
201
+ best_ptr,
202
+ best_index_ptr,
203
+ V,
204
+ n_tiles,
205
+ BLOCK_V: tl.constexpr,
206
+ ):
207
+ # One program per (row, tile of the vocabulary): the tile's best key and
208
+ # its first index. A greedy row's key is its logit; a sampled row's is its
209
+ # logit over the temperature plus Gumbel noise from in-kernel Philox
210
+ # (Gumbel-max: the argmax is a sample of softmax(logits / T)).
211
+ row = tl.program_id(0)
212
+ tile = tl.program_id(1)
213
+ offs = tile * BLOCK_V + tl.arange(0, BLOCK_V)
214
+ mask = offs < V
215
+ key = tl.load(logits_ptr + row * V + offs, mask=mask, other=float("-inf"))
216
+ key = key.to(tl.float32)
217
+ if tl.load(greedy_ptr + row) == 0:
218
+ uniform = tl.rand(tl.load(seed_ptr), row * V + offs)
219
+ key = key / tl.load(temperatures_ptr + row) - tl.log(-tl.log(uniform))
220
+ key = tl.where(mask, key, float("-inf"))
221
+ best = tl.max(key, axis=0)
222
+ index = tl.min(tl.where(key == best, offs, V), axis=0)
223
+ tl.store(best_ptr + row * n_tiles + tile, best)
224
+ tl.store(best_index_ptr + row * n_tiles + tile, index)
225
+
226
+
227
+ def sample_rows(
228
+ logits: torch.Tensor,
229
+ temperatures: torch.Tensor,
230
+ greedy: torch.Tensor,
231
+ seed: torch.Tensor,
232
+ block_v: int = 4096,
233
+ ) -> torch.Tensor:
234
+ """A token per row of contiguous ``logits (rows, V)``: the argmax for
235
+ ``greedy`` rows, else a sample of ``softmax(logits / temperature)``, with
236
+ Philox noise seeded by the int64 scalar tensor ``seed`` (advance it between
237
+ calls, in-graph, for fresh noise). Reads the logits once; no noise buffer."""
238
+ rows, vocab = logits.shape
239
+ assert logits.is_contiguous()
240
+ n_tiles = triton.cdiv(vocab, block_v)
241
+ best = torch.empty((rows, n_tiles), dtype=torch.float32, device=logits.device)
242
+ best_index = torch.empty((rows, n_tiles), dtype=torch.int64, device=logits.device)
243
+ _sample_tiles_kernel[(rows, n_tiles)](
244
+ logits, temperatures, greedy, seed, best, best_index, vocab, n_tiles,
245
+ BLOCK_V=block_v,
246
+ ) # fmt: skip
247
+ return best_index.gather(1, best.argmax(dim=1, keepdim=True)).squeeze(1)
248
+
249
+
250
+ __all__ += ["sample_rows"]
251
+
252
+
253
+ # The largest e4m3 magnitude.
254
+ _FP8_MAX = 448.0
255
+
256
+
257
+ def quantize_weight_fp8(weight: torch.Tensor):
258
+ """``weight (N, K)`` -> ``(e4m3 weight (N, K), float32 scale (1, N))``, one
259
+ scale per output channel."""
260
+ scale = weight.float().abs().amax(dim=1, keepdim=True).clamp_min(1e-12) / _FP8_MAX
261
+ quantized = (weight.float() / scale).to(torch.float8_e4m3fn)
262
+ return quantized, scale.t().contiguous()
263
+
264
+
265
+ def fp8_linear(x, weight, scale, bias=None) -> torch.Tensor:
266
+ """``x @ W^T + b`` with ``W`` from :func:`quantize_weight_fp8` and ``x``
267
+ quantized per row on the fly (dynamic per-token scales): one FP8 GEMM with
268
+ row-wise scales (Hopper), bf16 out. Elementwise parts fuse under compile."""
269
+ rows = x.reshape(-1, x.shape[-1])
270
+ x_scale = rows.float().abs().amax(dim=1, keepdim=True).clamp_min(1e-12) / _FP8_MAX
271
+ quantized = (rows.float() / x_scale).to(torch.float8_e4m3fn)
272
+ out = torch._scaled_mm(
273
+ quantized, weight.t(), scale_a=x_scale, scale_b=scale, out_dtype=torch.bfloat16
274
+ )
275
+ if bias is not None:
276
+ out = out + bias
277
+ return out.view(*x.shape[:-1], -1)
278
+
279
+
280
+ __all__ += ["fp8_linear", "quantize_weight_fp8"]
281
+
282
+
283
+ @triton.jit
284
+ def _kv_norm_rope_write_fp8_kernel(
285
+ kv_ptr,
286
+ meta_ptr,
287
+ knw_ptr,
288
+ cos_sin_ptr,
289
+ pos_ptr,
290
+ loc_ptr,
291
+ commit_lens_ptr,
292
+ locs_row_width,
293
+ k_scale,
294
+ v_scale,
295
+ KV: tl.constexpr,
296
+ D: tl.constexpr,
297
+ NH: tl.constexpr,
298
+ L: tl.constexpr,
299
+ EPS: tl.constexpr,
300
+ HAS_COMMIT_LENS: tl.constexpr,
301
+ ):
302
+ # sglang's fused_kv_norm_rope_write (interleaved RoPE), storing e4m3 with
303
+ # static per-tensor scales instead of bf16.
304
+ t = tl.program_id(0).to(tl.int64)
305
+ layer = tl.program_id(1).to(tl.int64)
306
+ if HAS_COMMIT_LENS:
307
+ row_b = t // locs_row_width
308
+ if t - row_b * locs_row_width >= tl.load(commit_lens_ptr + row_b).to(tl.int64):
309
+ return
310
+ loc = tl.load(loc_ptr + t).to(tl.int64)
311
+ if loc < 0:
312
+ return
313
+ pos = tl.load(pos_ptr + t).to(tl.int64)
314
+ HALF: tl.constexpr = D // 2
315
+ half = tl.arange(0, HALF)
316
+ d = tl.arange(0, D)
317
+ even, odd = 2 * half, 2 * half + 1
318
+ cos = tl.load(cos_sin_ptr + pos * D + half).to(tl.float32)
319
+ sin = tl.load(cos_sin_ptr + pos * D + HALF + half).to(tl.float32)
320
+ w_even = tl.load(knw_ptr + layer * D + even).to(tl.float32)
321
+ w_odd = tl.load(knw_ptr + layer * D + odd).to(tl.float32)
322
+ k_buf = tl.load(meta_ptr + layer * 4 + 0).to(tl.pointer_type(tl.float8e4nv))
323
+ v_buf = tl.load(meta_ptr + layer * 4 + 1).to(tl.pointer_type(tl.float8e4nv))
324
+ k_stride = tl.load(meta_ptr + layer * 4 + 2)
325
+ v_stride = tl.load(meta_ptr + layer * 4 + 3)
326
+ row = kv_ptr + t * (L * 2 * KV) + layer * (2 * KV)
327
+ for h in tl.static_range(NH):
328
+ k = tl.load(row + h * D + d).to(tl.float32)
329
+ inv = 1.0 / tl.sqrt(tl.sum(k * k, 0) / D + EPS)
330
+ k1 = (tl.load(row + h * D + even).to(tl.float32) * inv * w_even).to(tl.bfloat16)
331
+ k2 = (tl.load(row + h * D + odd).to(tl.float32) * inv * w_odd).to(tl.bfloat16)
332
+ k1, k2 = k1.to(tl.float32), k2.to(tl.float32)
333
+ o1 = (k1 * cos - k2 * sin) / k_scale
334
+ o2 = (k2 * cos + k1 * sin) / k_scale
335
+ tl.store(k_buf + loc * k_stride + h * D + even, o1.to(tl.float8e4nv))
336
+ tl.store(k_buf + loc * k_stride + h * D + odd, o2.to(tl.float8e4nv))
337
+ v = tl.load(row + KV + h * D + d).to(tl.float32) / v_scale
338
+ tl.store(v_buf + loc * v_stride + h * D + d, v.to(tl.float8e4nv))
339
+
340
+
341
+ def kv_norm_rope_write_fp8(
342
+ kv, meta, k_norm_weights, cos_sin, positions, locs, num_layers, kv_size,
343
+ head_dim, eps, *, k_scale=1.0, v_scale=1.0, commit_lens=None, locs_row_width=None,
344
+ ): # fmt: skip
345
+ """``fused_kv_norm_rope_write`` (same arguments; interleaved RoPE) for an
346
+ e4m3 pool: keys k-normed and rotated, both divided by their static scale."""
347
+ if kv.shape[0] == 0:
348
+ return
349
+ has_commit = commit_lens is not None
350
+ _kv_norm_rope_write_fp8_kernel[(kv.shape[0], num_layers)](
351
+ kv, meta, k_norm_weights, cos_sin,
352
+ positions.to(torch.int64).contiguous(), locs.to(torch.int64).contiguous(),
353
+ commit_lens.contiguous() if has_commit else locs, locs_row_width or 1,
354
+ k_scale, v_scale,
355
+ KV=kv_size, D=head_dim, NH=kv_size // head_dim, L=num_layers, EPS=eps,
356
+ HAS_COMMIT_LENS=has_commit,
357
+ ) # fmt: skip
358
+
359
+
360
+ __all__ += ["kv_norm_rope_write_fp8"]
361
+
362
+
363
+ @triton.jit
364
+ def _tree_commit_kernel(
365
+ accept_ptr, # (bs, D1) int32: global node ids of the accepted path, root first; -1 past it
366
+ num_correct_ptr, # (bs,) int32: accepted drafts
367
+ window_ptr, # (bs, W) int64: the verify window's KV slots, node order
368
+ predict_ptr, # (bs * W,) int32: the target's token after each node on the path
369
+ req_pool_ptr, # (bs,) int64
370
+ prefix_ptr, # (bs,) int64: committed length before this step
371
+ req_to_token_ptr, # (rows, row_stride) int32
372
+ row_stride,
373
+ out_tokens_ptr, # (bs, D1) int64
374
+ bonus_ptr, # (bs,) int64
375
+ commit_ptr, # (bs,) int32
376
+ new_seq_lens_ptr, # (bs,) int64
377
+ inject_loc_ptr, # (bs * W,) int64
378
+ W: tl.constexpr,
379
+ D1: tl.constexpr,
380
+ BLOCK: tl.constexpr,
381
+ ):
382
+ # One program per request: the tree path's commit in one launch.
383
+ b = tl.program_id(0)
384
+ n = tl.load(num_correct_ptr + b)
385
+ j = tl.arange(0, BLOCK) # path positions
386
+ i = tl.arange(0, BLOCK) # tree nodes
387
+ on_path_j = j <= n
388
+ node = tl.load(accept_ptr + b * D1 + j, mask=(j < D1) & on_path_j, other=0) - b * W
389
+ node = tl.where(on_path_j & (j < D1), node, -1)
390
+ # Which nodes the path takes (node i at path position j).
391
+ hit = (node[:, None] == i[None, :]) & (node[:, None] >= 0)
392
+ on_path = tl.sum(hit.to(tl.int32), axis=0) > 0
393
+ in_tree = i < W
394
+ window = tl.load(window_ptr + b * W + i, mask=in_tree, other=0)
395
+ # Reorder the window's req_to_token entries: the path first (where its KV
396
+ # is), then every other node in index order.
397
+ off_path = (on_path == 0) & in_tree
398
+ rank = tl.cumsum(off_path.to(tl.int32), axis=0) - 1
399
+ row = tl.load(req_pool_ptr + b)
400
+ prefix = tl.load(prefix_ptr + b)
401
+ base = req_to_token_ptr + row * row_stride + prefix
402
+ path_slot = tl.load(window_ptr + b * W + node, mask=node >= 0, other=0)
403
+ tl.store(base + j, path_slot.to(tl.int32), mask=node >= 0)
404
+ tl.store(base + n + 1 + rank, window.to(tl.int32), mask=off_path)
405
+ # The path's tokens (the drafts, then the target's next one) and the bonus.
406
+ token = tl.load(predict_ptr + b * W + node, mask=node >= 0, other=0).to(tl.int64)
407
+ tl.store(out_tokens_ptr + b * D1 + j, token, mask=j < D1)
408
+ tl.store(bonus_ptr + b, tl.sum(tl.where(j == n, token, 0), axis=0))
409
+ tl.store(commit_ptr + b, n + 1)
410
+ tl.store(new_seq_lens_ptr + b, prefix + n + 1)
411
+ # Draft context goes where the path's KV is; other rows to dummy slot 0.
412
+ tl.store(inject_loc_ptr + b * W + i, tl.where(on_path, window, 0), mask=in_tree)
413
+
414
+
415
+ def tree_commit(
416
+ accept_index,
417
+ num_correct,
418
+ window,
419
+ predict,
420
+ req_pool_indices,
421
+ prefix_lens,
422
+ req_to_token,
423
+ ):
424
+ """The tree path's commit after verify_tree_greedy, one launch for the batch.
425
+ Reorders each request's verify window in req_to_token so the accepted path
426
+ comes first (no KV moves) and returns (out_tokens (bs, D1) int64, bonus,
427
+ commit_lens int32, new_seq_lens, inject_locs (bs * W) int64)."""
428
+ bs, d1 = accept_index.shape
429
+ width = window.shape[1]
430
+ device = accept_index.device
431
+ out_tokens = torch.empty((bs, d1), dtype=torch.int64, device=device)
432
+ bonus = torch.empty((bs,), dtype=torch.int64, device=device)
433
+ commit = torch.empty((bs,), dtype=torch.int32, device=device)
434
+ new_seq_lens = torch.empty((bs,), dtype=prefix_lens.dtype, device=device)
435
+ inject_locs = torch.empty((bs * width,), dtype=torch.int64, device=device)
436
+ _tree_commit_kernel[(bs,)](
437
+ accept_index,
438
+ num_correct,
439
+ window,
440
+ predict,
441
+ req_pool_indices,
442
+ prefix_lens,
443
+ req_to_token,
444
+ req_to_token.stride(0),
445
+ out_tokens,
446
+ bonus,
447
+ commit,
448
+ new_seq_lens,
449
+ inject_locs,
450
+ W=width,
451
+ D1=d1,
452
+ BLOCK=triton.next_power_of_2(max(width, d1)),
453
+ )
454
+ return out_tokens, bonus, commit, new_seq_lens, inject_locs
455
+
456
+
457
+ __all__ += ["tree_commit"]