nbxtensor 0.0.1__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.
Files changed (291) hide show
  1. nbxtensor/THIRD_PARTY_NOTICES.md +287 -0
  2. nbxtensor/__init__.py +17 -0
  3. nbxtensor/backend/__init__.py +1 -0
  4. nbxtensor/backend/capabilities.py +207 -0
  5. nbxtensor/backend/cpu/__init__.py +1 -0
  6. nbxtensor/backend/cpu/blas.py +102 -0
  7. nbxtensor/backend/cpu/pool.py +204 -0
  8. nbxtensor/backend/cpu_backend.py +162 -0
  9. nbxtensor/backend/device_facts.py +105 -0
  10. nbxtensor/backend/device_files.py +129 -0
  11. nbxtensor/backend/device_names.py +18 -0
  12. nbxtensor/backend/devices/amd/cdna.yml +228 -0
  13. nbxtensor/backend/devices/amd/cdna2.yml +228 -0
  14. nbxtensor/backend/devices/amd/cdna3.yml +227 -0
  15. nbxtensor/backend/devices/apple/apple_m1.yml +266 -0
  16. nbxtensor/backend/devices/apple/apple_m1_max.yml +266 -0
  17. nbxtensor/backend/devices/apple/apple_m1_pro.yml +266 -0
  18. nbxtensor/backend/devices/apple/apple_m1_ultra.yml +266 -0
  19. nbxtensor/backend/devices/apple/apple_m2.yml +266 -0
  20. nbxtensor/backend/devices/apple/apple_m2_max.yml +266 -0
  21. nbxtensor/backend/devices/apple/apple_m2_pro.yml +266 -0
  22. nbxtensor/backend/devices/apple/apple_m2_ultra.yml +266 -0
  23. nbxtensor/backend/devices/apple/apple_m3.yml +266 -0
  24. nbxtensor/backend/devices/apple/apple_m3_max.yml +266 -0
  25. nbxtensor/backend/devices/apple/apple_m3_pro.yml +266 -0
  26. nbxtensor/backend/devices/apple/apple_m3_ultra.yml +266 -0
  27. nbxtensor/backend/devices/apple/apple_m4.yml +266 -0
  28. nbxtensor/backend/devices/apple/apple_m4_max.yml +266 -0
  29. nbxtensor/backend/devices/apple/apple_m4_pro.yml +308 -0
  30. nbxtensor/backend/devices/apple/apple_m5.yml +269 -0
  31. nbxtensor/backend/devices/apple/apple_m5_max.yml +269 -0
  32. nbxtensor/backend/devices/apple/apple_m5_pro.yml +269 -0
  33. nbxtensor/backend/devices/apple/apple_m5_ultra.yml +269 -0
  34. nbxtensor/backend/devices/apple/apple_m6.yml +269 -0
  35. nbxtensor/backend/devices/apple/apple_silicon.yml +263 -0
  36. nbxtensor/backend/devices/nvidia/ampere.yml +214 -0
  37. nbxtensor/backend/devices/nvidia/hopper.yml +213 -0
  38. nbxtensor/backend/devices/nvidia/volta.yml +411 -0
  39. nbxtensor/backend/faults.py +151 -0
  40. nbxtensor/backend/host_memory.py +185 -0
  41. nbxtensor/backend/launcher.py +2350 -0
  42. nbxtensor/backend/launcher_contract.py +429 -0
  43. nbxtensor/backend/metal_backend.py +661 -0
  44. nbxtensor/backend/metal_device.py +1274 -0
  45. nbxtensor/backend/process_memory.py +73 -0
  46. nbxtensor/backend/replay_graph.py +322 -0
  47. nbxtensor/backend/runtime.py +409 -0
  48. nbxtensor/backend/triton_ext_driver.py +735 -0
  49. nbxtensor/dtype/__init__.py +1 -0
  50. nbxtensor/dtype/convert.py +107 -0
  51. nbxtensor/dtype/dtypes.yml +70 -0
  52. nbxtensor/dtype/itemsize.py +166 -0
  53. nbxtensor/dtype/nbx_dtype.py +72 -0
  54. nbxtensor/dtype/operands.py +158 -0
  55. nbxtensor/dtype/outputs.py +64 -0
  56. nbxtensor/dtype/policy.py +155 -0
  57. nbxtensor/dtype/policy.yml +51 -0
  58. nbxtensor/dtype/promotion.py +112 -0
  59. nbxtensor/host.py +55 -0
  60. nbxtensor/io/__init__.py +1 -0
  61. nbxtensor/io/read_rate.py +197 -0
  62. nbxtensor/io/safetensors.py +314 -0
  63. nbxtensor/memory/__init__.py +9 -0
  64. nbxtensor/memory/allocator.py +1988 -0
  65. nbxtensor/memory/arena.py +75 -0
  66. nbxtensor/memory/device_memory.py +150 -0
  67. nbxtensor/memory/device_transfer.py +154 -0
  68. nbxtensor/memory/layout.py +43 -0
  69. nbxtensor/memory/memory_pool.py +191 -0
  70. nbxtensor/memory/metal.py +96 -0
  71. nbxtensor/memory/oom_advice.py +91 -0
  72. nbxtensor/memory/pointer_tables.py +323 -0
  73. nbxtensor/memory/pool.py +507 -0
  74. nbxtensor/memory/shadow.py +43 -0
  75. nbxtensor/memory/slab.py +273 -0
  76. nbxtensor/memory/trace_replay.py +122 -0
  77. nbxtensor/memory/traces/sana-video-2b-720p.16g.2026-11-02.tsv +13610 -0
  78. nbxtensor/memory/vmm.py +118 -0
  79. nbxtensor/ops/__init__.py +1 -0
  80. nbxtensor/ops/_autotune_policy.py +90 -0
  81. nbxtensor/ops/_common.py +32 -0
  82. nbxtensor/ops/_configs.py +1041 -0
  83. nbxtensor/ops/_cost.py +231 -0
  84. nbxtensor/ops/abs.py +17 -0
  85. nbxtensor/ops/adaptive_avg_pool2d.py +161 -0
  86. nbxtensor/ops/add.py +96 -0
  87. nbxtensor/ops/addcdiv.py +29 -0
  88. nbxtensor/ops/addcmul.py +29 -0
  89. nbxtensor/ops/addmv_op.py +64 -0
  90. nbxtensor/ops/addr.py +68 -0
  91. nbxtensor/ops/all_reduce.py +83 -0
  92. nbxtensor/ops/amax.py +40 -0
  93. nbxtensor/ops/any_reduce.py +83 -0
  94. nbxtensor/ops/arange_op.py +27 -0
  95. nbxtensor/ops/argmax.py +107 -0
  96. nbxtensor/ops/argmin.py +93 -0
  97. nbxtensor/ops/atan2.py +71 -0
  98. nbxtensor/ops/attention_wrappers.py +1648 -0
  99. nbxtensor/ops/autotune_bucket.py +137 -0
  100. nbxtensor/ops/autotune_cache.py +430 -0
  101. nbxtensor/ops/autotune_certified.py +1262 -0
  102. nbxtensor/ops/autotune_refusals.py +380 -0
  103. nbxtensor/ops/autotune_settings.py +58 -0
  104. nbxtensor/ops/avg_pool2d.py +73 -0
  105. nbxtensor/ops/baddbmm_op.py +132 -0
  106. nbxtensor/ops/batch_norm.py +163 -0
  107. nbxtensor/ops/binary_strided.py +196 -0
  108. nbxtensor/ops/bitwise_and.py +26 -0
  109. nbxtensor/ops/bitwise_left_shift.py +47 -0
  110. nbxtensor/ops/bitwise_not.py +24 -0
  111. nbxtensor/ops/bitwise_or.py +26 -0
  112. nbxtensor/ops/bitwise_right_shift.py +47 -0
  113. nbxtensor/ops/bucketize.py +37 -0
  114. nbxtensor/ops/cat_op.py +87 -0
  115. nbxtensor/ops/celu.py +20 -0
  116. nbxtensor/ops/clamp.py +24 -0
  117. nbxtensor/ops/clamp_min.py +22 -0
  118. nbxtensor/ops/conv2d.py +265 -0
  119. nbxtensor/ops/conv2d_m8n8k4.py +165 -0
  120. nbxtensor/ops/conv_depthwise2d.py +154 -0
  121. nbxtensor/ops/conv_transpose2d.py +148 -0
  122. nbxtensor/ops/conv_wrappers.py +1482 -0
  123. nbxtensor/ops/copy.py +18 -0
  124. nbxtensor/ops/copy_op.py +39 -0
  125. nbxtensor/ops/cos.py +17 -0
  126. nbxtensor/ops/count_nonzero.py +64 -0
  127. nbxtensor/ops/cross_entropy.py +67 -0
  128. nbxtensor/ops/cummax.py +109 -0
  129. nbxtensor/ops/cummin.py +105 -0
  130. nbxtensor/ops/cumsum.py +244 -0
  131. nbxtensor/ops/decode_attn_vec.py +162 -0
  132. nbxtensor/ops/depthwise_conv2d.py +132 -0
  133. nbxtensor/ops/dequant_gemv.py +190 -0
  134. nbxtensor/ops/dft.py +67 -0
  135. nbxtensor/ops/dispatch.py +1228 -0
  136. nbxtensor/ops/div.py +75 -0
  137. nbxtensor/ops/dot_op.py +67 -0
  138. nbxtensor/ops/dropout.py +23 -0
  139. nbxtensor/ops/dtype_convert.py +42 -0
  140. nbxtensor/ops/elementwise_launch.py +81 -0
  141. nbxtensor/ops/elementwise_wrappers.py +2315 -0
  142. nbxtensor/ops/elu.py +20 -0
  143. nbxtensor/ops/embedding.py +69 -0
  144. nbxtensor/ops/eq.py +32 -0
  145. nbxtensor/ops/erf.py +18 -0
  146. nbxtensor/ops/exp.py +17 -0
  147. nbxtensor/ops/exp2.py +18 -0
  148. nbxtensor/ops/fft_op.py +125 -0
  149. nbxtensor/ops/fill_op.py +20 -0
  150. nbxtensor/ops/flash_attention.py +307 -0
  151. nbxtensor/ops/flash_attention_m8n8k4.py +384 -0
  152. nbxtensor/ops/flash_decode.py +78 -0
  153. nbxtensor/ops/flip_op.py +60 -0
  154. nbxtensor/ops/floor.py +90 -0
  155. nbxtensor/ops/full_op.py +24 -0
  156. nbxtensor/ops/fused_moe.py +417 -0
  157. nbxtensor/ops/fused_moe_m8n8k4.py +99 -0
  158. nbxtensor/ops/fused_upsample_conv.py +413 -0
  159. nbxtensor/ops/gather_op.py +52 -0
  160. nbxtensor/ops/ge.py +32 -0
  161. nbxtensor/ops/gelu.py +31 -0
  162. nbxtensor/ops/gemv_vec.py +80 -0
  163. nbxtensor/ops/glu.py +43 -0
  164. nbxtensor/ops/grid_sampler.py +145 -0
  165. nbxtensor/ops/group_norm_tiled.py +67 -0
  166. nbxtensor/ops/groupnorm.py +123 -0
  167. nbxtensor/ops/gt.py +32 -0
  168. nbxtensor/ops/hardsigmoid.py +18 -0
  169. nbxtensor/ops/hardswish.py +19 -0
  170. nbxtensor/ops/im2col.py +57 -0
  171. nbxtensor/ops/index_add.py +95 -0
  172. nbxtensor/ops/index_put_op.py +92 -0
  173. nbxtensor/ops/index_select.py +121 -0
  174. nbxtensor/ops/index_wrappers.py +770 -0
  175. nbxtensor/ops/isfinite.py +20 -0
  176. nbxtensor/ops/isinf.py +20 -0
  177. nbxtensor/ops/isnan.py +18 -0
  178. nbxtensor/ops/launch_keys.py +1054 -0
  179. nbxtensor/ops/launch_observer.py +54 -0
  180. nbxtensor/ops/layernorm.py +71 -0
  181. nbxtensor/ops/le.py +35 -0
  182. nbxtensor/ops/leaky_relu.py +19 -0
  183. nbxtensor/ops/lerp.py +76 -0
  184. nbxtensor/ops/linalg_wrappers.py +1370 -0
  185. nbxtensor/ops/linspace_op.py +47 -0
  186. nbxtensor/ops/log.py +17 -0
  187. nbxtensor/ops/log_sigmoid.py +23 -0
  188. nbxtensor/ops/logical_and.py +26 -0
  189. nbxtensor/ops/logical_not.py +24 -0
  190. nbxtensor/ops/logical_or.py +26 -0
  191. nbxtensor/ops/logical_xor.py +37 -0
  192. nbxtensor/ops/loss_wrappers.py +282 -0
  193. nbxtensor/ops/lt.py +32 -0
  194. nbxtensor/ops/masked_fill.py +21 -0
  195. nbxtensor/ops/matmul.py +377 -0
  196. nbxtensor/ops/matmul_m8n8k4.py +235 -0
  197. nbxtensor/ops/max_pool2d.py +74 -0
  198. nbxtensor/ops/max_pool3d.py +93 -0
  199. nbxtensor/ops/max_reduce.py +91 -0
  200. nbxtensor/ops/maximum.py +18 -0
  201. nbxtensor/ops/mean.py +40 -0
  202. nbxtensor/ops/min_reduce.py +91 -0
  203. nbxtensor/ops/minimum.py +18 -0
  204. nbxtensor/ops/mish.py +22 -0
  205. nbxtensor/ops/mma_m8n8k4.py +152 -0
  206. nbxtensor/ops/moe_align.py +195 -0
  207. nbxtensor/ops/moe_decode_vec.py +374 -0
  208. nbxtensor/ops/moe_device_residency.py +141 -0
  209. nbxtensor/ops/moe_wrappers.py +345 -0
  210. nbxtensor/ops/mse_loss.py +79 -0
  211. nbxtensor/ops/mul.py +51 -0
  212. nbxtensor/ops/nan_to_num.py +23 -0
  213. nbxtensor/ops/ne.py +32 -0
  214. nbxtensor/ops/neg.py +17 -0
  215. nbxtensor/ops/nllloss.py +124 -0
  216. nbxtensor/ops/nonzero.py +33 -0
  217. nbxtensor/ops/nonzero_op.py +62 -0
  218. nbxtensor/ops/norm_wrappers.py +579 -0
  219. nbxtensor/ops/ones_op.py +20 -0
  220. nbxtensor/ops/oracles/__init__.py +6 -0
  221. nbxtensor/ops/oracles/conv2d_fp64.py +450 -0
  222. nbxtensor/ops/pad_op.py +159 -0
  223. nbxtensor/ops/paged_decode_attn.py +215 -0
  224. nbxtensor/ops/pixel_shuffle.py +144 -0
  225. nbxtensor/ops/pixel_unshuffle.py +72 -0
  226. nbxtensor/ops/pow.py +137 -0
  227. nbxtensor/ops/prod.py +81 -0
  228. nbxtensor/ops/rand_op.py +52 -0
  229. nbxtensor/ops/random_wrappers.py +366 -0
  230. nbxtensor/ops/reciprocal.py +17 -0
  231. nbxtensor/ops/reduction_wrappers.py +845 -0
  232. nbxtensor/ops/relu.py +18 -0
  233. nbxtensor/ops/remainder.py +46 -0
  234. nbxtensor/ops/repeat_interleave.py +48 -0
  235. nbxtensor/ops/repeat_op.py +132 -0
  236. nbxtensor/ops/residual_chain.py +374 -0
  237. nbxtensor/ops/rmsnorm.py +68 -0
  238. nbxtensor/ops/rope.py +142 -0
  239. nbxtensor/ops/rsqrt.py +17 -0
  240. nbxtensor/ops/scaled_softmax.py +96 -0
  241. nbxtensor/ops/scatter_op.py +139 -0
  242. nbxtensor/ops/screen_oracle.py +479 -0
  243. nbxtensor/ops/selu.py +22 -0
  244. nbxtensor/ops/shape_wrappers.py +1032 -0
  245. nbxtensor/ops/sigmoid.py +19 -0
  246. nbxtensor/ops/silu.py +27 -0
  247. nbxtensor/ops/sin.py +17 -0
  248. nbxtensor/ops/softmax.py +169 -0
  249. nbxtensor/ops/softplus.py +27 -0
  250. nbxtensor/ops/sort_op.py +304 -0
  251. nbxtensor/ops/spectral_wrappers.py +481 -0
  252. nbxtensor/ops/sqrt.py +17 -0
  253. nbxtensor/ops/stack_op.py +84 -0
  254. nbxtensor/ops/std.py +113 -0
  255. nbxtensor/ops/strided_copy.py +200 -0
  256. nbxtensor/ops/sub.py +35 -0
  257. nbxtensor/ops/sum.py +40 -0
  258. nbxtensor/ops/tan.py +19 -0
  259. nbxtensor/ops/tanh.py +21 -0
  260. nbxtensor/ops/threshold.py +19 -0
  261. nbxtensor/ops/tiling.yml +91 -0
  262. nbxtensor/ops/tiling_policy.py +43 -0
  263. nbxtensor/ops/tiling_sizes.py +370 -0
  264. nbxtensor/ops/topk.py +267 -0
  265. nbxtensor/ops/trace_op.py +64 -0
  266. nbxtensor/ops/tril.py +64 -0
  267. nbxtensor/ops/triu.py +62 -0
  268. nbxtensor/ops/unfold_op.py +37 -0
  269. nbxtensor/ops/unique_op.py +97 -0
  270. nbxtensor/ops/upsample_bilinear2d.py +106 -0
  271. nbxtensor/ops/upsample_nearest2d.py +52 -0
  272. nbxtensor/ops/upsample_nearest3d.py +61 -0
  273. nbxtensor/ops/var.py +136 -0
  274. nbxtensor/ops/vector_norm.py +216 -0
  275. nbxtensor/ops/weight_norm.py +104 -0
  276. nbxtensor/ops/where.py +39 -0
  277. nbxtensor/ops/wrappers.py +825 -0
  278. nbxtensor/ops/zeros_op.py +20 -0
  279. nbxtensor/quant/__init__.py +1 -0
  280. nbxtensor/quant/quantized_tensor.py +187 -0
  281. nbxtensor/random/__init__.py +1 -0
  282. nbxtensor/random/mt_randn.py +154 -0
  283. nbxtensor/random/rng_pin.py +59 -0
  284. nbxtensor/random/rng_stream.py +118 -0
  285. nbxtensor/random/seeded_draw.py +223 -0
  286. nbxtensor/shape/__init__.py +157 -0
  287. nbxtensor/tensor.py +1867 -0
  288. nbxtensor-0.0.1.dist-info/METADATA +379 -0
  289. nbxtensor-0.0.1.dist-info/RECORD +291 -0
  290. nbxtensor-0.0.1.dist-info/WHEEL +4 -0
  291. nbxtensor-0.0.1.dist-info/licenses/LICENSE +189 -0
@@ -0,0 +1,19 @@
1
+ """Sigmoid — pure @triton.jit kernel."""
2
+
3
+ import triton
4
+ import triton.language as tl
5
+
6
+ @triton.jit
7
+ def sigmoid_forward_kernel(
8
+ input_ptr, output_ptr,
9
+ n_elements,
10
+ BLOCK_SIZE: tl.constexpr,
11
+ ):
12
+ pid = tl.program_id(0).to(tl.int64)
13
+ offset = pid.to(tl.int64) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) # 64-bit from the program id: the product itself wraps past 2^31 elements (register 58)
14
+ mask = offset < n_elements
15
+
16
+ x = tl.load(input_ptr + offset, mask=mask)
17
+ x_fp32 = x.to(tl.float32)
18
+ out = 1.0 / (1.0 + tl.exp(-x_fp32))
19
+ tl.store(output_ptr + offset, out, mask=mask)
nbxtensor/ops/silu.py ADDED
@@ -0,0 +1,27 @@
1
+ """SiLU (Swish) — pure @triton.jit kernel."""
2
+
3
+ import triton
4
+ import triton.language as tl
5
+
6
+ from ._common import sigmoid
7
+
8
+ @triton.jit
9
+ def silu_forward_kernel(
10
+ input_ptr, output_ptr,
11
+ n_elements,
12
+ BLOCK_SIZE: tl.constexpr,
13
+ ):
14
+ pid = tl.program_id(0).to(tl.int64)
15
+ # Cast offset to int64 so that mask `offset < n_elements`
16
+ # works correctly when n_elements >= 2^31 (e.g. Sana 4Kpx VAE
17
+ # silu input 1*128*4096*4096 = 2^31 elements). Without this,
18
+ # int32 n_elements wraps to negative and the mask is all-False,
19
+ # silently skipping every element and leaving output as
20
+ # uninitialized memory (= garbage).
21
+ offset = pid.to(tl.int64) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) # 64-bit from the program id: the product itself wraps past 2^31 elements (register 58)
22
+ mask = offset < n_elements
23
+
24
+ x = tl.load(input_ptr + offset, mask=mask)
25
+ x_fp32 = x.to(tl.float32)
26
+ out = x_fp32 * sigmoid(x_fp32)
27
+ tl.store(output_ptr + offset, out, mask=mask)
nbxtensor/ops/sin.py ADDED
@@ -0,0 +1,17 @@
1
+ """Sine — pure @triton.jit kernel."""
2
+
3
+ import triton
4
+ import triton.language as tl
5
+
6
+ @triton.jit
7
+ def sin_forward_kernel(
8
+ input_ptr, output_ptr,
9
+ n_elements,
10
+ BLOCK_SIZE: tl.constexpr,
11
+ ):
12
+ pid = tl.program_id(0).to(tl.int64)
13
+ offset = pid.to(tl.int64) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) # 64-bit from the program id: the product itself wraps past 2^31 elements (register 58)
14
+ mask = offset < n_elements
15
+
16
+ x = tl.load(input_ptr + offset, mask=mask)
17
+ tl.store(output_ptr + offset, tl.sin(x.to(tl.float32)), mask=mask)
@@ -0,0 +1,169 @@
1
+ """Softmax — pure @triton.jit kernel. Ported from attorch (MIT)."""
2
+
3
+ import triton
4
+ import triton.language as tl
5
+ from triton import next_power_of_2
6
+
7
+ from nbxtensor.ops._configs import nbx_autotune
8
+
9
+
10
+ def _softmax_configs():
11
+ """The candidates the certifier proves (certified per key since 2026-10-19 instead of a hand table): rows per program x warps. The
12
+ one the kernel ran before certification is among them (1 row, 4 warps for a row of 64 or more; up to 128 rows for
13
+ a short row) — a certified winner equal to it changes no bit. Rows per program partition independent rows and change
14
+ no bit; the warps split a row's reduction and can (V100, 4096 columns: 0.001 % of the elements)."""
15
+ return [triton.Config({'BLOCK_SIZE_BATCH': rows}, num_warps=warps)
16
+ for rows in (1, 2, 4, 8, 16, 32, 64, 128) for warps in (1, 2, 4, 8)]
17
+
18
+
19
+ def _prune_softmax(configs, named_args, **kwargs):
20
+ """A program holds rows x the padded row: at most what the kernel already holds for ONE row — the longest row it
21
+ accepts (`launch_keys.SOFTMAX_FUSED_MAX_FEAT`, beyond which the looped kernel runs) — and at most the backend's
22
+ reduce-tile cap (`nbx_tensor.reduce_tile_max`, the profile's, where the lowering refuses beyond); never more rows
23
+ than exist. (The cap alone is far above the register budget on CUDA: 128 rows of a 2048-padded row compiled for
24
+ minutes per candidate, 2026-10-20.)"""
25
+ from nbxtensor.backend.capabilities import reduce_tile_max
26
+ from nbxtensor.ops.launch_keys import SOFTMAX_FUSED_MAX_FEAT
27
+ feat = next_power_of_2(int(named_args['feat_dim']))
28
+ rows = int(named_args['batch_dim'])
29
+ budget = min(reduce_tile_max(), SOFTMAX_FUSED_MAX_FEAT)
30
+ from nbxtensor.ops._configs import fits_thread_registers
31
+ keep = [c for c in configs
32
+ if c.kwargs['BLOCK_SIZE_BATCH'] * feat <= max(budget, feat) and c.kwargs['BLOCK_SIZE_BATCH'] <= max(1, rows)
33
+ and fits_thread_registers(c.kwargs['BLOCK_SIZE_BATCH'] * feat, c.num_warps)]
34
+ return keep or [max(configs, key=lambda c: c.num_warps)]
35
+
36
+
37
+ @nbx_autotune(configs=_softmax_configs(), key=['BATCH_BUCKET', 'FEAT_BUCKET', 'log'],
38
+ prune_configs_by={'early_config_prune': _prune_softmax}, cache_results=True)
39
+ @triton.heuristics({
40
+ 'BLOCK_SIZE_FEAT': lambda args: next_power_of_2(args['feat_dim']),
41
+ })
42
+ @triton.jit
43
+ def softmax_forward_kernel(
44
+ input_ptr, output_ptr,
45
+ batch_dim, feat_dim, BATCH_BUCKET, FEAT_BUCKET,
46
+ input_batch_stride, input_feat_stride,
47
+ output_batch_stride, output_feat_stride,
48
+ log: tl.constexpr,
49
+ BLOCK_SIZE_BATCH: tl.constexpr,
50
+ BLOCK_SIZE_FEAT: tl.constexpr,
51
+ ):
52
+ """Softmax forward (or log_softmax if log=True).
53
+
54
+ input: [batch_dim, feat_dim]
55
+ output: [batch_dim, feat_dim]
56
+ """
57
+ batch_pid = tl.program_id(0).to(tl.int64)
58
+
59
+ batch_offset = batch_pid * BLOCK_SIZE_BATCH + tl.arange(0, BLOCK_SIZE_BATCH)
60
+ feat_offset = tl.arange(0, BLOCK_SIZE_FEAT)
61
+
62
+ batch_mask = batch_offset < batch_dim
63
+ feat_mask = feat_offset < feat_dim
64
+
65
+ input_ptr += (input_batch_stride * batch_offset[:, None] +
66
+ input_feat_stride * feat_offset[None, :])
67
+ output_ptr += (output_batch_stride * batch_offset[:, None] +
68
+ output_feat_stride * feat_offset[None, :])
69
+
70
+ inp = tl.load(input_ptr,
71
+ mask=batch_mask[:, None] & feat_mask[None, :],
72
+ other=-float('inf')).to(tl.float32)
73
+
74
+ # Numerical stability: subtract max
75
+ inp -= tl.max(inp, axis=1)[:, None]
76
+ numerator = tl.exp(inp)
77
+ denominator = tl.sum(numerator, axis=1)[:, None]
78
+
79
+ if log:
80
+ output = inp - tl.log(denominator)
81
+ else:
82
+ output = numerator / denominator
83
+
84
+ tl.store(output_ptr, output,
85
+ mask=batch_mask[:, None] & feat_mask[None, :])
86
+
87
+
88
+ def _looped_softmax_configs():
89
+ """The candidates the certifier proves for the looped kernel (chantier 1): the tile a row is scanned by x warps.
90
+ The one it ran before certification is among them (4 096 columns, 4 warps) — a certified winner equal to it changes
91
+ no bit; another tile or warp count changes the order of a row's running max and sum."""
92
+ return [triton.Config({'TILE_FEAT': tile}, num_warps=warps)
93
+ for tile in (1024, 2048, 4096, 8192, 16384) for warps in (2, 4, 8)]
94
+
95
+
96
+ def _prune_looped_softmax(configs, named_args, **kwargs):
97
+ """A tile no wider than the backend's reduce-tile cap (`nbx_tensor.reduce_tile_max`, the profile's, where the
98
+ lowering refuses beyond) nor than the padded row it scans (a wider tile only masks)."""
99
+ from nbxtensor.backend.capabilities import reduce_tile_max
100
+ cap = min(reduce_tile_max(), next_power_of_2(int(named_args['feat_dim'])))
101
+ keep = [c for c in configs if c.kwargs['TILE_FEAT'] <= cap]
102
+ return keep or configs[:1]
103
+
104
+
105
+ @nbx_autotune(configs=_looped_softmax_configs(), key=['BATCH_BUCKET', 'FEAT_BUCKET', 'log'],
106
+ prune_configs_by={'early_config_prune': _prune_looped_softmax}, cache_results=True)
107
+ @triton.jit
108
+ def softmax_forward_looped_kernel(
109
+ input_ptr, output_ptr,
110
+ feat_dim, BATCH_BUCKET, FEAT_BUCKET,
111
+ input_batch_stride, input_feat_stride,
112
+ output_batch_stride, output_feat_stride,
113
+ log: tl.constexpr,
114
+ TILE_FEAT: tl.constexpr,
115
+ ):
116
+ """Online two-pass softmax for rows too large for one register tile.
117
+
118
+ One program per row; the row is scanned in TILE_FEAT chunks with a
119
+ running max / rescaled running sum (pass 1), then normalized and
120
+ stored (pass 2). Register footprint is bounded by TILE_FEAT
121
+ regardless of feat_dim.
122
+
123
+ Why this exists: the single-tile kernel above sizes its tile as
124
+ next_power_of_2(feat_dim). At vocab-scale rows (e.g. 151936 →
125
+ 262144-element tile) it spills to CUDA local memory, and the driver
126
+ allocates the local pool DEVICE-WIDE at launch — per-thread frame ×
127
+ max resident threads (163,840 on V100) ≈ 2 GiB, measured. The launch
128
+ itself then fails with CUDA_ERROR_OUT_OF_MEMORY whenever the device
129
+ cannot serve that hidden allocation (D9 warm-daemon incident,
130
+ 2026-07-27). Bounded tiles keep the frame register-resident, so the
131
+ launch has no local-memory cost at any feat_dim.
132
+ """
133
+ row = tl.program_id(0).to(tl.int64)
134
+ in_row = input_ptr + row * input_batch_stride
135
+ out_row = output_ptr + row * output_batch_stride
136
+
137
+ running_max = tl.full((1,), -float('inf'), tl.float32)
138
+ running_sum = tl.zeros((1,), tl.float32)
139
+ for start in range(0, feat_dim, TILE_FEAT):
140
+ offs = start + tl.arange(0, TILE_FEAT)
141
+ mask = offs < feat_dim
142
+ x = tl.load(in_row + offs * input_feat_stride, mask=mask,
143
+ other=-float('inf')).to(tl.float32)
144
+ new_max = tl.maximum(running_max, tl.max(x, axis=0))
145
+ # -inf guard (online-softmax idiom): while every element seen so
146
+ # far is -inf (top-k/top-p masking routinely -inf-fills whole
147
+ # leading tiles of an unsorted vocab row), new_max stays -inf and
148
+ # both exp arguments would be -inf - (-inf) = NaN, poisoning the
149
+ # running sum for the entire row. Pin alpha to 1 and the shifted
150
+ # tile to -inf (exp → 0) until the first finite value arrives.
151
+ # Fully -inf rows still yield NaN in pass 2 — parity with the
152
+ # single-tile kernel, guarded downstream by the samplers.
153
+ seen_only_neginf = new_max == -float('inf')
154
+ alpha = tl.where(seen_only_neginf, 1.0,
155
+ tl.exp(running_max - new_max))
156
+ shifted = tl.where(seen_only_neginf, -float('inf'), x - new_max)
157
+ running_sum = running_sum * alpha + tl.sum(tl.exp(shifted), axis=0)
158
+ running_max = new_max
159
+
160
+ for start in range(0, feat_dim, TILE_FEAT):
161
+ offs = start + tl.arange(0, TILE_FEAT)
162
+ mask = offs < feat_dim
163
+ x = tl.load(in_row + offs * input_feat_stride, mask=mask,
164
+ other=-float('inf')).to(tl.float32)
165
+ if log:
166
+ y = x - running_max - tl.log(running_sum)
167
+ else:
168
+ y = tl.exp(x - running_max) / running_sum
169
+ tl.store(out_row + offs * output_feat_stride, y, mask=mask)
@@ -0,0 +1,27 @@
1
+ """Softplus activation — pure @triton.jit kernel.
2
+
3
+ Logic from FlagGems softplus.py: softplus(x) = log(1 + exp(beta * x)) / beta.
4
+ For large beta*x, returns x directly (numerical stability).
5
+ """
6
+
7
+ import triton
8
+ import triton.language as tl
9
+
10
+ @triton.jit
11
+ def softplus_forward_kernel(
12
+ x_ptr, output_ptr,
13
+ n_elements,
14
+ beta,
15
+ threshold,
16
+ BLOCK_SIZE: tl.constexpr,
17
+ ):
18
+ pid = tl.program_id(0).to(tl.int64)
19
+ offset = pid.to(tl.int64) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) # 64-bit from the program id: the product itself wraps past 2^31 elements (register 58)
20
+ mask = offset < n_elements
21
+
22
+ x = tl.load(x_ptr + offset, mask=mask)
23
+ x_fp = x.to(tl.float32)
24
+ z = x_fp * beta
25
+ soft_z = tl.where(z > threshold, z, tl.log(1.0 + tl.exp(z)))
26
+ result = soft_z / beta
27
+ tl.store(output_ptr + offset, result, mask=mask)
@@ -0,0 +1,304 @@
1
+ """Sort — pure @triton.jit radix sort helper kernels.
2
+
3
+ Ported from FlagGems sort (Apache-2.0 license).
4
+ Extracts the uint-conversion helpers and the two main radix sort kernels
5
+ (global histogram + sweep). The small-N bitonic sort kernel is also included.
6
+
7
+ The radix sort orchestration (multi-pass loop, buffer allocation) must be
8
+ done in the wrapper since it requires memory allocation.
9
+ """
10
+
11
+ import triton
12
+ import triton.language as tl
13
+
14
+
15
+ # --------------------------------------------------------------------------- #
16
+ # Uint conversion helpers — preserve sort order across signed/float types
17
+ # --------------------------------------------------------------------------- #
18
+
19
+ # COMPILE-TIME helpers — `@tl.constexpr`, NEVER `@triton.jit`. Upstream
20
+ # FlagGems has carried them as constexpr functions since the one-sweep
21
+ # sort's birth commit (e669aee, PR #694); our vendoring delta hardened
22
+ # them into `@triton.jit`, which Triton >= 3.4 structurally rejects (the
23
+ # `_builder`→`_semantic` frontend refactor: a JIT callee compiles as a
24
+ # real typed function, so it can no longer return a dtype object, and
25
+ # `tl.core.get_int_dtype` — a plain undecorated python function — no
26
+ # longer resolves from inside JIT). `@tl.constexpr` runs them as plain
27
+ # compile-time python (legal 3.1→3.6), exactly upstream's shape.
28
+ def _unwrap_constexpr(o):
29
+ return o.value if isinstance(o, tl.constexpr) else o
30
+
31
+
32
+ @tl.constexpr
33
+ def _get_int_t(num_bits: tl.constexpr, signed: tl.constexpr):
34
+ num_bits = _unwrap_constexpr(num_bits)
35
+ signed = _unwrap_constexpr(signed)
36
+ return tl.core.get_int_dtype(num_bits, signed)
37
+
38
+
39
+ @tl.constexpr
40
+ def _one_zeros(num_bits: tl.constexpr):
41
+ """1 followed by (num_bits-1) zeros, e.g. 0x80 for 8-bit."""
42
+ num_bits = _unwrap_constexpr(num_bits)
43
+ return 1 << (num_bits - 1)
44
+
45
+
46
+ @tl.constexpr
47
+ def _zero_ones(num_bits: tl.constexpr):
48
+ """0 followed by (num_bits-1) ones, e.g. 0x7F for 8-bit."""
49
+ num_bits = _unwrap_constexpr(num_bits)
50
+ return (1 << (num_bits - 1)) - 1
51
+
52
+
53
+ @triton.jit
54
+ def uint_to_uint(x, descending: tl.constexpr = False):
55
+ """Unsigned → unsigned: negate bits if descending."""
56
+ out = ~x if descending else x
57
+ return out
58
+
59
+
60
+ @triton.jit
61
+ def int_to_uint(x, descending: tl.constexpr = False):
62
+ """Signed int → unsigned, preserving sort order."""
63
+ num_bits: tl.constexpr = x.dtype.primitive_bitwidth
64
+ udtype = _get_int_t(num_bits, False)
65
+ ux = tl.cast(x, udtype, bitcast=True)
66
+ if descending:
67
+ bit_mask: tl.constexpr = _zero_ones(num_bits)
68
+ bit_mask_tensor = tl.full((), value=bit_mask, dtype=udtype)
69
+ out = ux ^ bit_mask_tensor
70
+ else:
71
+ sign_bit_mask: tl.constexpr = _one_zeros(num_bits)
72
+ sign_bit_mask_tensor = tl.full((), value=sign_bit_mask, dtype=udtype)
73
+ out = ux ^ sign_bit_mask_tensor
74
+ return out
75
+
76
+
77
+ @triton.jit
78
+ def floating_to_uint(x, descending: tl.constexpr = False):
79
+ """Float → unsigned, preserving sort order (handles sign bit correctly)."""
80
+ num_bits: tl.constexpr = x.dtype.primitive_bitwidth
81
+ sdtype = _get_int_t(num_bits, True)
82
+ udtype = _get_int_t(num_bits, False)
83
+ sx = x.to(sdtype, bitcast=True)
84
+ ux = x.to(udtype, bitcast=True)
85
+
86
+ sign_bit_mask_v: tl.constexpr = _one_zeros(num_bits)
87
+ sign_bit_mask = tl.full((), value=sign_bit_mask_v, dtype=udtype)
88
+ rshift_bits = tl.full((), value=num_bits - 1, dtype=sdtype)
89
+ mask = sign_bit_mask | (sx >> rshift_bits).to(udtype, bitcast=True)
90
+ if descending:
91
+ out = ux ^ (~mask)
92
+ else:
93
+ out = ux ^ mask
94
+ return out.to(udtype, bitcast=True)
95
+
96
+
97
+ @triton.jit
98
+ def convert_to_uint_preserve_order(x, descending: tl.constexpr = False):
99
+ """Convert any numeric type to unsigned, preserving sort order."""
100
+ if x.dtype.is_floating():
101
+ out = floating_to_uint(x, descending)
102
+ elif x.dtype.is_int_signed():
103
+ out = int_to_uint(x, descending)
104
+ elif x.dtype.is_int_unsigned():
105
+ out = uint_to_uint(x, descending)
106
+ return out
107
+
108
+
109
+ # --------------------------------------------------------------------------- #
110
+ # Global histogram kernel — counts per-bin occurrences for radix sort
111
+ # --------------------------------------------------------------------------- #
112
+
113
+ @triton.jit
114
+ def radix_sort_histogram_kernel(
115
+ arr_ptr,
116
+ out_ptr,
117
+ num_passes,
118
+ m,
119
+ n,
120
+ tiles_n_per_cta,
121
+ TILE_N: tl.constexpr,
122
+ TILE_R: tl.constexpr,
123
+ num_bits_per_pass: tl.constexpr,
124
+ descending: tl.constexpr,
125
+ ):
126
+ """Compute per-bin histograms for radix sort.
127
+
128
+ arr_ptr: (m, n) — input data
129
+ out_ptr: (m, num_passes, r) — histogram output, r = 2^num_bits_per_pass
130
+ """
131
+ pid = tl.program_id(0).to(tl.int64)
132
+ pid_n = pid // m
133
+ pid_m = pid % m
134
+
135
+ r: tl.constexpr = 2 ** num_bits_per_pass
136
+ bfe_mask: tl.constexpr = (1 << num_bits_per_pass) - 1
137
+ CTA_TILE_N: tl.constexpr = TILE_N * tiles_n_per_cta
138
+ cta_n_start = (CTA_TILE_N * pid_n).to(tl.int32) # loop bounds are 32-bit counts: the Metal lowering refuses a 64-bit scf.for bound (addressing stays 64-bit through the program ids)
139
+ cta_n_end = (tl.minimum(cta_n_start + CTA_TILE_N, n)).to(tl.int32)
140
+
141
+ for p in range(0, num_passes):
142
+ bit_offset = p * num_bits_per_pass
143
+ for r_start in range(0, r, TILE_R):
144
+ bin_indices = r_start + tl.arange(0, TILE_R)
145
+ acc = tl.zeros((TILE_R, TILE_N), dtype=tl.int64)
146
+ for n_start in range(cta_n_start, cta_n_end, TILE_N):
147
+ n_offsets = n_start + tl.arange(0, TILE_N)
148
+ mask = n_offsets < cta_n_end
149
+ arr = tl.load(arr_ptr + pid_m * n + n_offsets, mask=mask)
150
+ arr = convert_to_uint_preserve_order(arr, descending)
151
+ key = (arr >> bit_offset) & bfe_mask
152
+ matches = tl.where(mask, (bin_indices[:, None] == key), False)
153
+ acc += matches
154
+ local_sum = tl.sum(acc, axis=1)
155
+ tl.atomic_add(
156
+ out_ptr + pid_m * num_passes * r + p * r + bin_indices,
157
+ local_sum,
158
+ sem="relaxed",
159
+ )
160
+
161
+
162
+ # --------------------------------------------------------------------------- #
163
+ # Sweep kernel — scatter elements to sorted positions using decoupled lookback
164
+ # --------------------------------------------------------------------------- #
165
+
166
+ @triton.jit
167
+ def radix_sort_tile_counts_kernel(
168
+ arr_ptr,
169
+ counts_ptr,
170
+ m,
171
+ N,
172
+ OUT_N,
173
+ TILE_N: tl.constexpr,
174
+ TILE_R: tl.constexpr,
175
+ k_bits: tl.constexpr,
176
+ bit_offset,
177
+ descending: tl.constexpr,
178
+ ):
179
+ """How many elements of each bin this TILE holds. Stage 1 of three.
180
+
181
+ This and `radix_sort_scatter_kernel` replace a single kernel that carried a
182
+ DECOUPLED LOOKBACK: each tile published its count and then spun on its
183
+ predecessors' flags to learn how many of its bin came before it.
184
+
185
+ That construction needs forward progress across CTAs and cross-CTA
186
+ visibility for a `.cg` store, and Triton promises neither — inside a kernel
187
+ it promises nothing about other programs at all. Measured 2026-09-17 on
188
+ triton-ext: correct to exactly 2048 elements (one tile) and wrong from 2049,
189
+ with elements LOST rather than misordered (9 absent at n=2049, 1726 at
190
+ n=4096) — two tiles computing the same destinations and overwriting each
191
+ other, which is what a lookback reading stale flags produces.
192
+
193
+ The replacement is not a new design. `cumsum_wrapper` in this same tree does
194
+ its cross-tile scan as SEPARATE LAUNCHES (`scan_part_sum_kernel`, then
195
+ `add_base_sum_kernel` when there is more than one part) and is correct at
196
+ eight tiles, measured the same day. Triton guarantees ordering BETWEEN
197
+ launches; that is the guarantee this needs.
198
+ """
199
+ pid = tl.program_id(0).to(tl.int64)
200
+ pid_m = pid % m
201
+ pid_n = pid // m
202
+ pid_r = tl.program_id(1).to(tl.int64)
203
+
204
+ bfe_mask: tl.constexpr = (1 << k_bits) - 1
205
+ r: tl.constexpr = 2 ** k_bits
206
+ cta_r_start = (pid_r * TILE_R).to(tl.int32)
207
+ cta_r_end = (tl.minimum(cta_r_start + TILE_R, r)).to(tl.int32)
208
+
209
+ n_offsets = pid_n * TILE_N + tl.arange(0, TILE_N)
210
+ mask = n_offsets < N
211
+ arr = tl.load(arr_ptr + pid_m * N + n_offsets, mask=mask)
212
+ arr_u = convert_to_uint_preserve_order(arr, descending)
213
+ key = (arr_u >> bit_offset) & bfe_mask
214
+
215
+ for bin_index in range(cta_r_start, cta_r_end):
216
+ matches = tl.where(mask, key == bin_index, False)
217
+ local_sum = tl.sum(matches.to(tl.uint32), axis=0)
218
+ tl.store(counts_ptr + pid_m * (r * OUT_N) + bin_index * OUT_N + pid_n,
219
+ local_sum)
220
+
221
+
222
+ @triton.jit
223
+ def radix_sort_tile_prefix_kernel(
224
+ counts_ptr,
225
+ prefix_ptr,
226
+ num_tiles,
227
+ OUT_N,
228
+ TILES_POW2: tl.constexpr,
229
+ ):
230
+ """Exclusive scan of the per-tile counts, along the TILE axis. Stage 2.
231
+
232
+ One program per (batch, bin) row — the rows are independent, and the row is
233
+ `num_tiles` long, which is `cdiv(n, 2048)`: small. This is the step the
234
+ lookback was doing inside the scatter, done where Triton actually orders it.
235
+ """
236
+ row = tl.program_id(0).to(tl.int64)
237
+ offs = tl.arange(0, TILES_POW2)
238
+ mask = offs < num_tiles
239
+ counts = tl.load(counts_ptr + row * OUT_N + offs, mask=mask, other=0)
240
+ prefix = tl.cumsum(counts, axis=0) - counts # exclusive
241
+ tl.store(prefix_ptr + row * OUT_N + offs, prefix, mask=mask)
242
+
243
+
244
+ @triton.jit
245
+ def radix_sort_scatter_kernel(
246
+ arr_ptr,
247
+ associate_arr_ptr,
248
+ out_ptr,
249
+ associate_out_ptr,
250
+ excumsum_bins_ptr,
251
+ tile_prefix_ptr,
252
+ n_passes,
253
+ pass_id,
254
+ bit_offset,
255
+ m,
256
+ N,
257
+ OUT_N,
258
+ TILE_N: tl.constexpr,
259
+ TILE_R: tl.constexpr,
260
+ k_bits: tl.constexpr,
261
+ descending: tl.constexpr,
262
+ ):
263
+ """Scatter elements to their sorted positions. Stage 3.
264
+
265
+ Identical to the old `radix_sort_sweep_kernel` except that
266
+ `exclusive_prefix` — how many of this bin lie in EARLIER tiles — is READ
267
+ from the scan of stage 2 instead of being discovered by spinning on other
268
+ programs' flags.
269
+ """
270
+ pid = tl.program_id(0).to(tl.int64)
271
+ pid_m = pid % m
272
+ pid_n = pid // m
273
+ pid_r = tl.program_id(1).to(tl.int64)
274
+
275
+ bfe_mask: tl.constexpr = (1 << k_bits) - 1
276
+ r: tl.constexpr = 2 ** k_bits
277
+ cta_r_start = (pid_r * TILE_R).to(tl.int32)
278
+ cta_r_end = (tl.minimum(cta_r_start + TILE_R, r)).to(tl.int32)
279
+
280
+ n_offsets = pid_n * TILE_N + tl.arange(0, TILE_N)
281
+ mask = n_offsets < N
282
+ arr = tl.load(arr_ptr + pid_m * N + n_offsets, mask=mask)
283
+ arr_u = convert_to_uint_preserve_order(arr, descending)
284
+ key = (arr_u >> bit_offset) & bfe_mask
285
+
286
+ for bin_index in range(cta_r_start, cta_r_end):
287
+ matches = tl.where(mask, key == bin_index, False)
288
+ exclusive_prefix = tl.load(
289
+ tile_prefix_ptr + pid_m * (r * OUT_N) + bin_index * OUT_N + pid_n)
290
+
291
+ local_ex_cumsum = tl.cumsum(matches.to(tl.uint32), axis=0) - matches
292
+ ex_cumsum_in_bin = exclusive_prefix + local_ex_cumsum
293
+
294
+ ex_cumsum_bins = tl.load(
295
+ excumsum_bins_ptr + pid_m * (n_passes * r) + pass_id * r + bin_index
296
+ )
297
+ pos = ex_cumsum_bins + ex_cumsum_in_bin
298
+
299
+ tl.store(out_ptr + pid_m * N + pos, arr, mask=matches)
300
+ if associate_arr_ptr is not None:
301
+ associate_arr = tl.load(
302
+ associate_arr_ptr + pid_m * N + n_offsets, mask=mask
303
+ )
304
+ tl.store(associate_out_ptr + pid_m * N + pos, associate_arr, mask=matches)