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,825 @@
1
+ """Triton Kernel Wrappers — triton mode execution layer.
2
+
3
+ Each wrapper:
4
+ 1. Receives NBXTensor
5
+ 2. Allocates output via NBXTensor.empty (cudaMalloc)
6
+ 3. Launches @triton.jit kernel
7
+ 4. Returns NBXTensor
8
+
9
+ Dependencies: triton, NBXTensor. Used exclusively by dispatch.py.
10
+ """
11
+
12
+ import os
13
+ import triton
14
+ # The bucket of a request-dependent key dimension (kernels/autotune_bucket.py): the key
15
+ # carries the bucket's top, the kernel runs the true size (the owner's decision, 2026-09-21).
16
+ from nbxtensor.ops.autotune_bucket import bucket_of as _bucket_of
17
+ from nbxtensor.ops import launch_keys as _lk
18
+
19
+ from nbxtensor.tensor import NBXTensor
20
+ from nbxtensor.dtype.nbx_dtype import NBXDtype, dtype_size
21
+ from nbxtensor.memory.allocator import DeviceAllocator
22
+ from nbxtensor.backend.runtime import set_device
23
+ from nbxtensor.shape import MAX_NDIM as _NBX_MAX_NDIM, broadcast_shapes, max_linear_offset
24
+ from nbxtensor.dtype.itemsize import canonical_name
25
+ from nbxtensor.dtype.promotion import binary_result, float_math_input
26
+ from nbxtensor.dtype.nbx_dtype import parse_dtype
27
+ from nbxtensor.memory.allocator import DeviceOOMError
28
+ from nbxtensor.backend.capabilities import fa_min_tile as _fa_min_tile
29
+ from nbxtensor.backend.capabilities import reduce_tile_max as _reduce_tile_max
30
+ from nbxtensor.backend.faults import device_fault_buffer, device_fault_code_cached, fault_channel
31
+ from nbxtensor.tensor import _upload_int64_array as _nbx_upload_int64, _saturating_cast as _nbx_saturating_cast
32
+ from nbxtensor.ops._configs import sdpa_block_ceiling as _sdpa_block_ceiling
33
+ from nbxtensor.ops._configs import sdpa_launch_meta as _sdpa_launch_meta
34
+ from nbxtensor.ops._configs import matrix_unit as _matrix_unit, matrix_unit_mm_tile as _matrix_unit_mm_tile
35
+ from nbxtensor.ops._configs import largest_tile_within_smem as _largest_tile_within_smem
36
+ from nbxtensor.backend import launcher as _nbx_launcher
37
+
38
+ # Route this module's kernel[grid] sites through the engine's launcher.
39
+ #
40
+ # `kernels/dispatch.py` already calls install(), and for a full engine run
41
+ # that is enough. It is not enough for anything that reaches a wrapper
42
+ # WITHOUT going through dispatch — a unit test, a tool, a probe — which then
43
+ # silently runs upstream Triton's launch path instead of the engine's, and
44
+ # on a backend whose upstream driver cannot load our artifacts it simply
45
+ # fails. Worse, whether it happened depended on whether some earlier import
46
+ # in the same process had pulled dispatch: nine kernels tests changed state
47
+ # on 2026-09-06 because an unrelated import reordered.
48
+ #
49
+ # install() is idempotent and honours NBX_LAUNCHER=triton, so the
50
+ # differential arm is unaffected. The launch sites live in this module; the
51
+ # routing of those sites belongs with them.
52
+ from nbxtensor.backend.launcher import install as _install_launcher
53
+
54
+ _install_launcher()
55
+
56
+
57
+ _FLOOR_CONFLICTS_SAID = set()
58
+
59
+
60
+ def _announce_floor_over_ceiling(clamped, taken, floor) -> None:
61
+ """Say, once per (clamped, floor) pair, that a profile row lost to the floor.
62
+
63
+ Once per pair and not per call: attention wrappers run thousands of times
64
+ a decode, and a line repeated thousands of times is a line the next
65
+ reader has learned to skip — the same rule as the autotune exclusions.
66
+ """
67
+ token = (clamped, floor)
68
+ if token in _FLOOR_CONFLICTS_SAID:
69
+ return
70
+ _FLOOR_CONFLICTS_SAID.add(token)
71
+ print(f"[SDPA_TILE] the profile ceiling proposed {clamped} but this "
72
+ f"backend's attention lowering refuses tile dimensions under "
73
+ f"{floor}; using {taken}. The profile row promises a tile the "
74
+ f"backend will not lower — the row should be raised or scoped to "
75
+ f"the template path that measured it.", flush=True)
76
+
77
+
78
+ def _autotune_headroom_guard(launch):
79
+ """Wrap an autotuned kernel launch (`kernel[grid]`) against the ONE
80
+ device allocation the engine does not own: the Triton autotuner's
81
+ benchmark buffer (its L2-flush cache, a 256 MiB torch allocation,
82
+ taken once per never-seen key). On an edge row the allocator pool
83
+ and the deferred dead-tensor queues may hold the headroom that
84
+ allocation needs — the pool flushes on OUR malloc failures only.
85
+ On an out-of-memory failure of the launch, return the pool to the
86
+ driver, drain the registered reclaimers, say so once, and retry the
87
+ launch ONCE; a second failure raises unchanged. Zero cost on the
88
+ cached-key path (one try/except around the call). 2026-09-02,
89
+ xlong int4 row: the first attention chunk's autotune bench found
90
+ 121 MiB free on a 32 GB card."""
91
+ def _run(*args, **kwargs):
92
+ try:
93
+ return launch(*args, **kwargs)
94
+ except RuntimeError as exc:
95
+ if "out of memory" not in str(exc).lower():
96
+ raise
97
+ freed = DeviceAllocator.empty_cache_pool()
98
+ reclaimed = 0
99
+ try:
100
+ dev = DeviceAllocator.get_device()
101
+ except Exception:
102
+ dev = None
103
+ for hook in list(getattr(DeviceAllocator, "_oom_reclaim_hooks", ())):
104
+ try:
105
+ reclaimed += int(hook(dev, 0) or 0)
106
+ except Exception as hook_err:
107
+ print(f"[AUTOTUNE_HEADROOM] reclaim hook error: {hook_err}", flush=True)
108
+ print(f"[AUTOTUNE_HEADROOM] the autotune benchmark buffer was refused "
109
+ f"({str(exc).splitlines()[0][:90]}); pool flushed {freed / 2**20:.0f}MB, "
110
+ f"reclaimers freed {reclaimed / 2**20:.0f}MB — retrying the launch once",
111
+ flush=True)
112
+ return launch(*args, **kwargs)
113
+ return _run
114
+
115
+ # === Activations ===
116
+
117
+ from nbxtensor.ops.relu import relu_forward_kernel
118
+ from nbxtensor.ops.silu import silu_forward_kernel
119
+ from nbxtensor.ops.gelu import gelu_forward_kernel
120
+ from nbxtensor.ops.sigmoid import sigmoid_forward_kernel
121
+ from nbxtensor.ops.tanh import tanh_forward_kernel
122
+ from nbxtensor.ops.hardsigmoid import hardsigmoid_forward_kernel
123
+ from nbxtensor.ops.hardswish import hardswish_forward_kernel
124
+ from nbxtensor.ops.leaky_relu import leaky_relu_forward_kernel
125
+ from nbxtensor.ops.elu import elu_forward_kernel
126
+ from nbxtensor.ops.mish import mish_forward_kernel
127
+ from nbxtensor.ops.selu import selu_forward_kernel
128
+
129
+ # === Unary element-wise ===
130
+
131
+ from nbxtensor.ops.neg import neg_forward_kernel
132
+ from nbxtensor.ops.exp import exp_forward_kernel
133
+ from nbxtensor.ops.sin import sin_forward_kernel
134
+ from nbxtensor.ops.cos import cos_forward_kernel
135
+ from nbxtensor.ops.rsqrt import rsqrt_forward_kernel
136
+ from nbxtensor.ops.sqrt import sqrt_forward_kernel
137
+ from nbxtensor.ops.abs import abs_forward_kernel
138
+ from nbxtensor.ops.log import log_forward_kernel
139
+ from nbxtensor.ops.reciprocal import reciprocal_forward_kernel
140
+ from nbxtensor.ops.pow import pow_forward_kernel
141
+ from nbxtensor.ops.clamp import clamp_forward_kernel
142
+ from nbxtensor.ops.erf import erf_forward_kernel
143
+ from nbxtensor.ops.copy import copy_forward_kernel
144
+
145
+ # === Binary element-wise ===
146
+
147
+ from nbxtensor.ops.add import add_forward_kernel, add_scalar_kernel, add_scalar_dev_kernel, add_bias_broadcast_kernel
148
+ from nbxtensor.ops.binary_strided import add_strided_nd_kernel, mul_strided_nd_kernel, sub_strided_nd_kernel, div_strided_nd_kernel, add_scalar_strided_nd_kernel, mul_scalar_strided_nd_kernel
149
+ from nbxtensor.ops.mul import mul_forward_kernel, mul_scalar_kernel, mul_scalar_dev_kernel
150
+ from nbxtensor.ops.div import div_forward_kernel, div_scalar_kernel, div_scalar_dev_kernel, int_div_kernel
151
+ from nbxtensor.ops.sub import sub_forward_kernel, rsub_forward_kernel
152
+ from nbxtensor.ops.where import where_forward_kernel
153
+ from nbxtensor.ops.maximum import maximum_forward_kernel
154
+ from nbxtensor.ops.minimum import minimum_forward_kernel
155
+ from nbxtensor.ops.masked_fill import masked_fill_forward_kernel
156
+
157
+ # === Comparisons ===
158
+
159
+ from nbxtensor.ops.gt import gt_forward_kernel, gt_scalar_kernel
160
+ from nbxtensor.ops.ge import ge_forward_kernel, ge_scalar_kernel
161
+ from nbxtensor.ops.lt import lt_forward_kernel, lt_scalar_kernel
162
+ from nbxtensor.ops.eq import eq_forward_kernel, eq_scalar_kernel
163
+ from nbxtensor.ops.ne import ne_forward_kernel, ne_scalar_kernel
164
+
165
+ # === Normalization ===
166
+
167
+ from nbxtensor.ops.layernorm import layer_norm_forward_kernel
168
+ from nbxtensor.ops.rmsnorm import rms_norm_forward_kernel
169
+
170
+ # === Softmax ===
171
+
172
+ from nbxtensor.ops.softmax import softmax_forward_kernel, softmax_forward_looped_kernel
173
+
174
+ # === Matmul ===
175
+
176
+ from nbxtensor.ops.matmul import matmul_kernel, addmm_kernel
177
+ from nbxtensor.ops._autotune_policy import NBX_FORCE_FP32_ACCUM
178
+
179
+ # === Fused MoE ===
180
+
181
+ from nbxtensor.ops.fused_moe import fused_moe_kernel, silu_and_mul_kernel, silu_mul_split_kernel
182
+ from nbxtensor.ops.dtype_convert import bf16_to_fp16_kernel
183
+
184
+ # === Embedding ===
185
+
186
+ from nbxtensor.ops.embedding import embedding_kernel, EMBEDDING_OOB
187
+
188
+ # === Reductions ===
189
+
190
+ from nbxtensor.ops.sum import sum_forward_kernel
191
+ from nbxtensor.ops.mean import mean_forward_kernel
192
+ from nbxtensor.ops.amax import amax_forward_kernel
193
+
194
+ # === GLU ===
195
+
196
+ from nbxtensor.ops.glu import glu_forward_kernel
197
+
198
+ # === New kernels (Phase 2) ===
199
+
200
+ from nbxtensor.ops.le import le_forward_kernel, le_scalar_kernel
201
+ from nbxtensor.ops.softplus import softplus_forward_kernel
202
+ from nbxtensor.ops.dropout import dropout_inference_kernel
203
+ from nbxtensor.ops.upsample_nearest2d import upsample_nearest2d_kernel
204
+ from nbxtensor.ops.groupnorm import group_norm_forward_kernel
205
+ from nbxtensor.ops.index_select import index_select_kernel, INDEX_SELECT_OOB
206
+ from nbxtensor.ops.triu import triu_kernel, triu_batch_kernel
207
+ from nbxtensor.ops.tril import tril_kernel, tril_batch_kernel
208
+ from nbxtensor.ops.argmax import argmax_kernel_1, argmax_kernel_2, argmax_kernel_inner
209
+ from nbxtensor.ops.argmin import argmin_kernel_1, argmin_kernel_2, argmin_kernel
210
+ from nbxtensor.ops.conv2d import conv2d_forward_kernel
211
+ from nbxtensor.ops.depthwise_conv2d import depthwise_conv2d_kernel
212
+ from nbxtensor.ops.batch_norm import batch_norm_forward_kernel
213
+ from nbxtensor.ops.cumsum import scan_part_sum_kernel, add_base_sum_kernel
214
+ from nbxtensor.ops.scatter_op import scatter_kernel, scatter_add_kernel, scatter_reduce_amax_kernel, scatter_reduce_amin_kernel
215
+ from nbxtensor.ops.gather_op import gather_kernel
216
+ from nbxtensor.ops.avg_pool2d import avg_pool2d_forward_kernel
217
+ from nbxtensor.ops.max_pool2d import max_pool2d_forward_kernel
218
+ from nbxtensor.ops.max_pool3d import max_pool3d_forward_kernel
219
+ from nbxtensor.ops.cross_entropy import cross_entropy_loss_forward_kernel
220
+
221
+ # === Simple element-wise (Phase 3) ===
222
+
223
+ from nbxtensor.ops.exp2 import exp2_forward_kernel
224
+ from nbxtensor.ops.tan import tan_forward_kernel
225
+ from nbxtensor.ops.celu import celu_forward_kernel
226
+ from nbxtensor.ops.log_sigmoid import log_sigmoid_forward_kernel
227
+ from nbxtensor.ops.isfinite import isfinite_forward_kernel
228
+ from nbxtensor.ops.isinf import isinf_forward_kernel
229
+ from nbxtensor.ops.isnan import isnan_forward_kernel
230
+ from nbxtensor.ops.nan_to_num import nan_to_num_forward_kernel
231
+ from nbxtensor.ops.threshold import threshold_forward_kernel
232
+ from nbxtensor.ops.floor import floor_forward_kernel, ceil_forward_kernel, round_forward_kernel, trunc_forward_kernel
233
+
234
+ # === Phase 4: Remaining kernels ===
235
+
236
+ from nbxtensor.ops.addcdiv import addcdiv_forward_kernel
237
+ from nbxtensor.ops.addcmul import addcmul_forward_kernel
238
+ from nbxtensor.ops.all_reduce import all_kernel_mid, all_kernel_result, all_kernel_dim
239
+ from nbxtensor.ops.any_reduce import any_kernel_mid, any_kernel_result, any_kernel_dim
240
+ from nbxtensor.ops.bitwise_and import bitwise_and_forward_kernel
241
+ from nbxtensor.ops.bitwise_or import bitwise_or_forward_kernel
242
+ from nbxtensor.ops.bitwise_not import bitwise_not_forward_kernel
243
+ from nbxtensor.ops.logical_and import logical_and_forward_kernel
244
+ from nbxtensor.ops.logical_or import logical_or_forward_kernel
245
+ from nbxtensor.ops.logical_not import logical_not_forward_kernel
246
+ from nbxtensor.ops.lerp import lerp_tensor_forward_kernel, lerp_scalar_head_kernel, lerp_scalar_tail_kernel
247
+ from nbxtensor.ops.dot_op import dot_kernel_small, dot_kernel_partial, dot_kernel_reduce
248
+ from nbxtensor.ops.flip_op import flip_1d_kernel, flip_strided_kernel
249
+ from nbxtensor.ops.cat_op import cat_copy_kernel_4
250
+ from nbxtensor.ops.fill_op import fill_kernel
251
+ from nbxtensor.ops.arange_op import arange_kernel
252
+ from nbxtensor.ops.prod import prod_kernel_mid, prod_kernel_result, prod_kernel
253
+ from nbxtensor.ops.min_reduce import min_kernel_mid, min_kernel_result, min_kernel
254
+ from nbxtensor.ops.max_reduce import max_kernel_mid, max_kernel_result, max_kernel
255
+ from nbxtensor.ops.baddbmm_op import baddbmm_kernel
256
+ from nbxtensor.ops.addmv_op import addmv_kernel
257
+ from nbxtensor.ops.mse_loss import mse_loss_partial_kernel, mse_loss_reduce_kernel, mse_loss_none_kernel
258
+ from nbxtensor.ops.nllloss import nll_loss_forward_kernel
259
+ from nbxtensor.ops.std import std_map_kernel, std_reduce_kernel, std_dim_kernel
260
+ from nbxtensor.ops.var import var_kernel_1, var_kernel_2, var_welford_kernel
261
+ from nbxtensor.ops.index_add import index_add_gather_kernel
262
+ from nbxtensor.ops.index_put_op import index_put_kernel, INDEX_PUT_OOB
263
+ from nbxtensor.ops.sort_op import (radix_sort_histogram_kernel,
264
+ radix_sort_scatter_kernel,
265
+ radix_sort_tile_counts_kernel,
266
+ radix_sort_tile_prefix_kernel)
267
+
268
+ # === Phase 5: RoPE, spatial, RNG, remaining ===
269
+
270
+ from nbxtensor.ops.rope import rope_forward_kernel
271
+ from nbxtensor.ops.pixel_shuffle import (
272
+ pixel_shuffle_kernel,
273
+ pixel_shuffle_broadcast_aware_kernel,
274
+ )
275
+ from nbxtensor.ops.pixel_unshuffle import pixel_unshuffle_kernel
276
+ from nbxtensor.ops.upsample_bilinear2d import upsample_bilinear2d_kernel
277
+ from nbxtensor.ops.adaptive_avg_pool2d import adaptive_avg_pool2d_kernel
278
+ from nbxtensor.ops.conv_depthwise2d import conv_depthwise2d_kernel
279
+ from nbxtensor.ops.scaled_softmax import scaled_softmax_kernel
280
+ from nbxtensor.ops.vector_norm import (
281
+ l2_norm_kernel, l1_norm_kernel, linf_norm_kernel, l0_norm_kernel, lp_norm_kernel,
282
+ l2_norm_pass1_kernel, l2_norm_pass2_kernel,
283
+ linf_norm_pass1_kernel, linf_norm_pass2_kernel,
284
+ )
285
+ from nbxtensor.ops.addr import addr_kernel
286
+ from nbxtensor.ops.logical_xor import logical_xor_kernel
287
+ from nbxtensor.ops.bitwise_left_shift import bitwise_left_shift_kernel
288
+ from nbxtensor.ops.bitwise_right_shift import bitwise_right_shift_kernel
289
+ from nbxtensor.ops.rand_op import rand_kernel, randn_kernel
290
+ from nbxtensor.ops.clamp_min import clamp_min_forward_kernel
291
+ from nbxtensor.ops.conv_transpose2d import conv_transpose2d_kernel
292
+ from nbxtensor.ops.cummax import scan_part_max_kernel
293
+ from nbxtensor.ops.cummin import scan_part_min_kernel
294
+ from nbxtensor.ops.count_nonzero import count_nonzero_kernel
295
+ from nbxtensor.ops.trace_op import trace_kernel
296
+ from nbxtensor.ops.linspace_op import linspace_kernel
297
+ from nbxtensor.ops.full_op import full_kernel
298
+ from nbxtensor.ops.stack_op import stack_copy_kernel
299
+ from nbxtensor.ops.repeat_op import repeat_1d_kernel
300
+
301
+ # === Phase 6: Video + Audio ops ===
302
+
303
+ from nbxtensor.ops.upsample_nearest3d import upsample_nearest3d_kernel
304
+ from nbxtensor.ops.weight_norm import weight_norm_kernel_first, weight_norm_kernel_last
305
+ from nbxtensor.ops.repeat_interleave import repeat_interleave_tensor_kernel
306
+
307
+ # An element-wise launch's tile and warps: its element count's class, certified (`ops/elementwise_launch.py`).
308
+ from nbxtensor.ops.elementwise_launch import ew_meta as _ew_meta, ew_grid as _ew_grid
309
+
310
+
311
+ # ---------------------------------------------------------------------------
312
+ # Hardware capability surface — single source of truth for the Triton side.
313
+ #
314
+ # Default is True (modern hardware). The CLI / serving layer calls
315
+ # set_hardware_profile(profile) once at executor construction to override
316
+ # with the actual PrismProfile.has_native_bf16. Kernel wrappers then read
317
+ # _NBX_HAS_NATIVE_BF16 for runtime dtype decisions (e.g. forcing fp32
318
+ # output on fp16 mm/bmm/addmm when bf16 is unavailable).
319
+ #
320
+ # Rationale: NeuroBrix is universal-data-driven. The fp16 overflow
321
+ # protection mirrors what the native DtypeEngine's AMP rules already do
322
+ # on pre-Ampere GPUs — so it's an existing policy, not a V100 hardcode.
323
+ # On Ampere+ (has_native_bf16=True) the protection path is a no-op and
324
+ # all kernel wrappers behave identically to before this was introduced.
325
+ # ---------------------------------------------------------------------------
326
+ _NBX_HAS_NATIVE_BF16 = True
327
+ _NBX_HW_PROFILE = None
328
+
329
+
330
+ def set_hardware_profile(profile) -> None:
331
+ """Configure Triton kernel wrappers from a PrismProfile.
332
+
333
+ Called once per process, typically by the CLI and serving entry
334
+ points right before constructing RuntimeExecutor. Plumbs the
335
+ hardware capability into a module-level flag so kernel wrappers
336
+ can make dtype-safety decisions without carrying a profile through
337
+ every call site. The full profile is also stashed so the weight
338
+ loader can query per-device VRAM when deciding whether to
339
+ bind-time upcast fp16 weights to fp32 on pre-Ampere hardware.
340
+
341
+ Accepts any object with `has_native_bf16` and `devices` (each with `brand` and
342
+ `architecture`), duck-typed so tests can pass a mock without importing the full Prism
343
+ stack; the kernels' float64 capability is the backend's fact, declared to the capabilities
344
+ (`set_backend_has_fp64`).
345
+ """
346
+ global _NBX_HAS_NATIVE_BF16, _NBX_HW_PROFILE
347
+ if profile is None:
348
+ return
349
+ _NBX_HW_PROFILE = profile
350
+ _NBX_HAS_NATIVE_BF16 = bool(getattr(profile, "has_native_bf16", True))
351
+ # The kernels' float64 capability, the backend's own fact (`device_facts.kernels_carry_fp64`), declared to the
352
+ # capabilities the integer-division widening and the cast door ask; an application that states it otherwise sets
353
+ # it after this call (`capabilities.set_backend_has_fp64`, NeuroBrix: `triton.dtype.declare_hardware_profile`).
354
+ from nbxtensor.backend.device_facts import kernels_carry_fp64
355
+ from nbxtensor.backend.capabilities import set_backend_has_fp64
356
+ set_backend_has_fp64(kernels_carry_fp64())
357
+
358
+
359
+ def has_native_bf16() -> bool:
360
+ """Expose the cached hardware capability flag for non-kernel callers."""
361
+ return _NBX_HAS_NATIVE_BF16
362
+
363
+
364
+ def get_hardware_profile():
365
+ """Return the PrismProfile stashed by set_hardware_profile, or None."""
366
+ return _NBX_HW_PROFILE
367
+
368
+
369
+ # ---------------------------------------------------------------------------
370
+ # Per-component runtime context: compute_dtype + activations_fp16_safe flag.
371
+ # These globals are set by TritonSequence.run() at hot-loop entry and
372
+ # restored at exit (try/finally). Single-threaded NeuroBrix runtime by
373
+ # convention (sequence.py:325 docstring), so module-level state is safe.
374
+ #
375
+ # - _NBX_COMPUTE_DTYPE: the per-component compute dtype Prism allocated
376
+ # for this sequence. Read by self-managed wrappers (conv2d_wrapper)
377
+ # to decide output dtype, mirroring what cuDNN does in compiled mode.
378
+ # - _NBX_ACTIVATIONS_FP16_SAFE: per-component flag of the precision
379
+ # contract (calibration record). Read under an fp16 compute dtype only:
380
+ # when True, ops in AMP_FP32_OPS that produce fp32 internally cast their
381
+ # output back to compute_dtype (rms_norm, div, etc.) — VRAM-preserving for
382
+ # models whose activations are confirmed within fp16 range by the
383
+ # calibration record (neurobrix calibrate); False keeps the conservative
384
+ # fp32 output. Under bf16 the output is cast back whatever the flag says
385
+ # (triton/dtype.py `amp_fp32_output_dtype`).
386
+ # ---------------------------------------------------------------------------
387
+ _NBX_COMPUTE_DTYPE = None
388
+ _NBX_ACTIVATIONS_FP16_SAFE: bool = False
389
+
390
+
391
+ def set_compute_dtype(dt) -> None:
392
+ """Set the active per-component compute dtype. Called by TritonSequence.run."""
393
+ global _NBX_COMPUTE_DTYPE
394
+ _NBX_COMPUTE_DTYPE = dt
395
+
396
+
397
+ def get_compute_dtype():
398
+ """Return the active per-component compute dtype, or None if not set."""
399
+ return _NBX_COMPUTE_DTYPE
400
+
401
+
402
+ def set_activations_fp16_safe(safe: bool) -> None:
403
+ """Set the activations-fp16-safe opt-in flag for the active component."""
404
+ global _NBX_ACTIVATIONS_FP16_SAFE
405
+ _NBX_ACTIVATIONS_FP16_SAFE = bool(safe)
406
+
407
+
408
+ def get_activations_fp16_safe() -> bool:
409
+ """Return the activations-fp16-safe flag for the active component."""
410
+ return _NBX_ACTIVATIONS_FP16_SAFE
411
+
412
+
413
+ # --- Fixed block sizes for kernels without @triton.autotune ---
414
+ _MM_BM, _MM_BN, _MM_BK = 64, 64, 32 # matmul / addmm / baddbmm
415
+ _MM_GROUP = 8 # matmul GROUP_M
416
+ _CONV_BHW, _CONV_OUTF, _CONV_INF = 64, 64, 32 # conv2d
417
+ _BILINEAR_BX, _BILINEAR_BY = 16, 16 # upsample_bilinear2d
418
+
419
+
420
+ # ===========================================================================
421
+ # UNIVERSAL LAUNCH LAYER
422
+ #
423
+ # Handles broadcasting, device context, scalar tensors, and contiguity
424
+ # for ALL Triton kernel wrappers. One place, all concerns, all ops.
425
+ # ===========================================================================
426
+
427
+ def _to_scalar(x):
428
+ """Extract Python scalar from 0-d tensor or scalar."""
429
+ if isinstance(x, (int, float, bool)):
430
+ return x
431
+ if hasattr(x, 'ndim') and x.ndim == 0:
432
+ if os.environ.get("NBX_SCALAR_DIAG") == "1":
433
+ import traceback as _tb
434
+ site = next((f"{_f.name}@{_f.lineno}" for _f in
435
+ reversed(_tb.extract_stack()[:-1])
436
+ if "wrappers.py" in _f.filename), "?")
437
+ print(f"[SCALAR_DIAG] device-0d .item() via {site} "
438
+ f"dtype={getattr(x, 'nbx_dtype', '?')}")
439
+ return x.item()
440
+ if hasattr(x, 'numel') and x.numel() == 1:
441
+ if os.environ.get("NBX_SCALAR_DIAG") == "1":
442
+ import traceback as _tb
443
+ site = next((f"{_f.name}@{_f.lineno}" for _f in
444
+ reversed(_tb.extract_stack()[:-1])
445
+ if "wrappers.py" in _f.filename), "?")
446
+ print(f"[SCALAR_DIAG] device-1el .item() via {site} "
447
+ f"shape={getattr(x, 'shape', '?')}")
448
+ return x.item()
449
+ return x
450
+
451
+
452
+ def _is_scalar(x):
453
+ """Check if x is a scalar (Python number or 0-d tensor)."""
454
+ if isinstance(x, (int, float, bool)):
455
+ return True
456
+ if hasattr(x, 'ndim') and x.ndim == 0:
457
+ return True
458
+ return False
459
+
460
+
461
+ def _ensure_cuda(t):
462
+ """Ensure CUDA runtime device matches tensor device."""
463
+ set_device(t)
464
+ return t
465
+
466
+
467
+ def _transfer_to_device(tensor, target_device_idx: int):
468
+ """Transfer NBXTensor to a different GPU via cudaMemcpy D2D."""
469
+ if not hasattr(tensor, '_device_idx') or tensor._device_idx == target_device_idx:
470
+ return tensor
471
+ src = tensor.contiguous()
472
+ dst = NBXTensor.empty(src._shape, src._dtype, f"cuda:{target_device_idx}")
473
+ DeviceAllocator.memcpy(dst.data_ptr(), src.data_ptr(), src._nbytes, kind=3)
474
+ return dst
475
+
476
+
477
+ def _wider_dtype(a_dtype, b_dtype):
478
+ """The dtype a binary element-wise op computes in: nbxtensor's one rule (`dtype.promotion.binary_result`)."""
479
+ if a_dtype == b_dtype:
480
+ return a_dtype
481
+ return parse_dtype(binary_result(canonical_name(a_dtype), canonical_name(b_dtype)))
482
+
483
+
484
+ def _prepare_unary(x):
485
+ """Prepare a single tensor for kernel launch."""
486
+ x = x.contiguous()
487
+ set_device(x)
488
+ return x, x.numel(), None
489
+
490
+
491
+ def _prepare_binary(a, b):
492
+ """Prepare two tensors for a binary element-wise kernel.
493
+
494
+ UNIVERSAL CONTRACT:
495
+ - Scalar position normalized: tensor ALWAYS in `a`, scalar ALWAYS in `b`
496
+ - Dtype aligned: both upcast to wider dtype
497
+ - Device aligned: both on same GPU
498
+ - Shape broadcast: expanded to common shape
499
+ - Contiguous: both made contiguous before kernel launch
500
+
501
+ Returns: (a, b_or_scalar, output, n_elements, device_ctx, is_scalar)
502
+ When is_scalar=True: a is tensor, b is Python scalar (int/float/bool).
503
+ """
504
+ # --- Scalar path: normalize so tensor=a, scalar=b ---
505
+ # Device-resident 0-d operands stay ON DEVICE (is_scalar="dev"):
506
+ # the host .item() here was 1,196 syncs per Ming request and made
507
+ # the denoiser bucket replay-ineligible (per-step timestep baked
508
+ # into launch scalars). The *_scalar_dev kernels load the scalar
509
+ # in-register with bit-exact host-path arithmetic (2026-08-15).
510
+ def _dev_scalar(x):
511
+ # NBXTensor.device returns self (its own device context) — the
512
+ # canonical device string lives in _device ('cuda' / 'cpu').
513
+ return (isinstance(x, NBXTensor) and getattr(x, 'ndim', None) == 0
514
+ and str(getattr(x, '_device', 'cpu')).startswith('cuda'))
515
+
516
+ if _is_scalar(b):
517
+ a = a.contiguous()
518
+ set_device(a)
519
+ output = NBXTensor.empty_like(a)
520
+ if _dev_scalar(b):
521
+ return a, b, output, a.numel(), None, "dev"
522
+ return a, _to_scalar(b), output, a.numel(), None, True
523
+
524
+ if _is_scalar(a):
525
+ # Swap: tensor goes to `a`, scalar goes to `b`
526
+ b = b.contiguous()
527
+ set_device(b)
528
+ output = NBXTensor.empty_like(b)
529
+ if _dev_scalar(a):
530
+ return b, a, output, b.numel(), None, "dev"
531
+ return b, _to_scalar(a), output, b.numel(), None, True
532
+
533
+ # --- Two-tensor path ---
534
+
535
+ # 1) Align dtypes (upcast to wider)
536
+ common_dtype = _wider_dtype(a._dtype, b._dtype)
537
+ if a._dtype != common_dtype:
538
+ a = a.to(common_dtype)
539
+ if b._dtype != common_dtype:
540
+ b = b.to(common_dtype)
541
+
542
+ # 2) Align devices
543
+ if hasattr(a, '_device_idx') and hasattr(b, '_device_idx') and a._device_idx != b._device_idx:
544
+ b = _transfer_to_device(b, a._device_idx)
545
+
546
+ # 3) Broadcast shapes
547
+ if a.shape != b.shape:
548
+ out_shape = broadcast_shapes(a.shape, b.shape)
549
+ a = a.expand(out_shape).contiguous()
550
+ b = b.expand(out_shape).contiguous()
551
+ else:
552
+ a = a.contiguous()
553
+ b = b.contiguous()
554
+
555
+ set_device(a)
556
+ output = NBXTensor.empty_like(a)
557
+ return a, b, output, a.numel(), None, False
558
+
559
+
560
+ def _prepare_binary_strided(a, b):
561
+ """The two-tensor case read by strides: (a, b, output, n, shape_buf, a_strides_buf,
562
+ b_strides_buf, NDIM) with both operands broadcast to the common shape as VIEWS —
563
+ no `expand(...).contiguous()` transient. Returns (None, a, b) when the flat path
564
+ applies (both contiguous at the same shape: the flat kernel's indexing is cheaper),
565
+ with a and b ALREADY aligned in dtype and device so the caller's `_prepare_binary`
566
+ casts nothing twice (the first form did: 45 casts a token on TinyLlama, 2026-09-08).
567
+ The dtype cast stays a copy (an in-kernel widen moves the compiler's contraction —
568
+ the RoPE lesson, 2026-09-07)."""
569
+ if _is_scalar(a) or _is_scalar(b) or not (hasattr(a, "_dtype") and hasattr(b, "_dtype")):
570
+ return None, a, b
571
+ common_dtype = _wider_dtype(a._dtype, b._dtype)
572
+ if a._dtype != common_dtype:
573
+ a = a.to(common_dtype)
574
+ if b._dtype != common_dtype:
575
+ b = b.to(common_dtype)
576
+ if hasattr(a, '_device_idx') and hasattr(b, '_device_idx') and a._device_idx != b._device_idx:
577
+ b = _transfer_to_device(b, a._device_idx)
578
+ if a.shape == b.shape and a.is_contiguous() and b.is_contiguous():
579
+ return None, a, b
580
+ out_shape = broadcast_shapes(a.shape, b.shape) if a.shape != b.shape else tuple(a.shape)
581
+ if tuple(a.shape) != tuple(out_shape):
582
+ a = a.expand(*out_shape)
583
+ if tuple(b.shape) != tuple(out_shape):
584
+ b = b.expand(*out_shape)
585
+ ndim = max(len(out_shape), 1)
586
+ if ndim > _NBX_MAX_NDIM:
587
+ return None, a, b
588
+ shape = tuple(out_shape) or (1,)
589
+ a_st = tuple(a._strides) or (0,)
590
+ b_st = tuple(b._strides) or (0,)
591
+ set_device(a)
592
+ output = NBXTensor.empty(tuple(out_shape), a._dtype, f"cuda:{a._device_idx}")
593
+ n = output.numel()
594
+ return (a, b, output, n,
595
+ _nbx_upload_int64(shape, a._device_idx), _nbx_upload_int64(a_st, a._device_idx),
596
+ _nbx_upload_int64(b_st, a._device_idx), ndim)
597
+
598
+
599
+ def _prepare_scalar_strided(a, b):
600
+ """The tensor-and-host-scalar case with a strided tensor: (tensor, scalar, output, n,
601
+ shape_buf, strides_buf, NDIM), the tensor read in place by the strided scalar kernel —
602
+ or None when the flat path applies (a contiguous tensor, or a device-resident 0-d
603
+ scalar, whose kernel reads it in-register). A chunk of an adaLN vector, `1 + scale` on a
604
+ (2, 1, 1152) slice of (2, 6, 1152), used to be copied contiguous per modulation."""
605
+ if _is_scalar(a) and not _is_scalar(b):
606
+ a, b = b, a
607
+ if not (_is_scalar(b) and hasattr(a, "_strides")) or a.is_contiguous():
608
+ return None
609
+ if isinstance(b, NBXTensor): # a device-resident 0-d scalar: the *_scalar_dev kernels' path
610
+ return None
611
+ ndim = max(a.ndim, 1)
612
+ if ndim > _NBX_MAX_NDIM:
613
+ return None
614
+ shape = tuple(a.shape) or (1,)
615
+ st = tuple(a._strides) or (0,)
616
+ set_device(a)
617
+ output = NBXTensor.empty(tuple(a.shape), a._dtype, f"cuda:{a._device_idx}")
618
+ return (a, _to_scalar(b), output, output.numel(),
619
+ _nbx_upload_int64(shape, a._device_idx), _nbx_upload_int64(st, a._device_idx), ndim)
620
+
621
+
622
+ def _prepare_comparison(a, b):
623
+ """Prepare two tensors for a comparison kernel (output is bool).
624
+
625
+ Same guards as _prepare_binary (dtype align, device align, broadcast,
626
+ contiguous) but output tensor is always bool dtype.
627
+
628
+ CONTRACT: when is_scalar=True, tensor is in `a`, scalar is in `b`.
629
+ """
630
+ if _is_scalar(b):
631
+ a = a.contiguous()
632
+ set_device(a)
633
+ output = NBXTensor.empty(a.shape, NBXDtype.bool_, device=a.device)
634
+ return a, _to_scalar(b), output, a.numel(), None, True
635
+
636
+ if _is_scalar(a):
637
+ # Swap: tensor goes to `a`, scalar goes to `b`
638
+ b = b.contiguous()
639
+ set_device(b)
640
+ output = NBXTensor.empty(b.shape, NBXDtype.bool_, device=b.device)
641
+ return b, _to_scalar(a), output, b.numel(), None, True
642
+
643
+ # Align dtypes for comparison (both must be same type for the kernel)
644
+ common_dtype = _wider_dtype(a._dtype, b._dtype)
645
+ if a._dtype != common_dtype:
646
+ a = a.to(common_dtype)
647
+ if b._dtype != common_dtype:
648
+ b = b.to(common_dtype)
649
+
650
+ # Align devices
651
+ if hasattr(a, '_device_idx') and hasattr(b, '_device_idx') and a._device_idx != b._device_idx:
652
+ b = _transfer_to_device(b, a._device_idx)
653
+
654
+ # Broadcast
655
+ if a.shape != b.shape:
656
+ out_shape = broadcast_shapes(a.shape, b.shape)
657
+ a = a.expand(out_shape).contiguous()
658
+ b = b.expand(out_shape).contiguous()
659
+ else:
660
+ a = a.contiguous()
661
+ b = b.contiguous()
662
+
663
+ set_device(a)
664
+ output = NBXTensor.empty(a.shape, NBXDtype.bool_, device=a.device)
665
+ return a, b, output, a.numel(), None, False
666
+
667
+
668
+ def _is_integer_operand(x):
669
+ if isinstance(x, bool):
670
+ return False
671
+ if isinstance(x, int):
672
+ return True
673
+ dt = getattr(x, "dtype", None)
674
+ return hasattr(dt, "is_floating") and not dt.is_floating() and dt != NBXDtype.bool_
675
+
676
+
677
+ def _int_division_widening_dtype(a, b):
678
+ """The float dtype an integer division widens through: float64 where the backend has it
679
+ (exact below 2^53), float32 where it has none (Metal; exact below 2^24, and REFUSED by name
680
+ above, because a float64 element there is misread, not computed — `backend_has_fp64`,
681
+ 2026-09-28). The magnitude check is one reduction per integer division, a rare op."""
682
+ from nbxtensor.backend.capabilities import backend_has_fp64
683
+ if backend_has_fp64():
684
+ return NBXDtype.float64
685
+ limit = 1 << 24
686
+ for t in (a, b):
687
+ if hasattr(t, "to") and hasattr(t, "numel") and t.numel() > 0:
688
+ # measured through float32 (exact below 2^24, and anything at or above it reads as such)
689
+ m = int(_red.amax_wrapper(_ew.abs_wrapper(t.to(NBXDtype.float32))).item())
690
+ if m >= limit:
691
+ raise RuntimeError(
692
+ f"ZERO FALLBACK: an integer division with |values| up to {m} on a backend with no "
693
+ f"float64 ('{__import__('nbxtensor.backend.runtime', fromlist=['gpu_backend']).gpu_backend()}'): "
694
+ f"the float32 widening is exact only below 2^24, and float64 is misread there. "
695
+ f"An integer division kernel is the extension that lifts this.")
696
+ return NBXDtype.float32
697
+
698
+
699
+ def _nbx_to_numpy(t):
700
+ """Generic NBXTensor → numpy (D2H). Zero torch."""
701
+ import numpy as np
702
+ nb_dtype_to_np = {
703
+ NBXDtype.float32: np.float32,
704
+ NBXDtype.float16: np.float16,
705
+ NBXDtype.int32: np.int32,
706
+ NBXDtype.int64: np.int64,
707
+ }
708
+ dt = t.dtype
709
+ if dt == NBXDtype.bfloat16:
710
+ t = t.to(NBXDtype.float32)
711
+ dt = NBXDtype.float32
712
+ np_dtype = nb_dtype_to_np.get(dt)
713
+ if np_dtype is None:
714
+ t = t.to(NBXDtype.float32)
715
+ np_dtype = np.float32
716
+ arr = np.empty(t.shape, dtype=np_dtype)
717
+ import ctypes as _ct # noqa: F401 — force DeviceAllocator path
718
+ DeviceAllocator.memcpy(arr.ctypes.data, t.data_ptr(), arr.nbytes, kind=2)
719
+ return arr
720
+
721
+
722
+ def _reduction_dtype(op, dtype):
723
+ """The `dtype` keyword of aten::sum / aten::mean as an NBXDtype (None when absent). ATen casts the INPUT to it
724
+ before reducing, and the result carries it (`sum(x, -1, keepdim=True, dtype=torch.float32)` over a bf16 router's
725
+ top-k weights, Ming-Lite-Omni-1.5's language model, 2026-10-14). A string the NBX dtypes do not know is refused."""
726
+ if dtype is None or isinstance(dtype, NBXDtype):
727
+ return dtype
728
+ if isinstance(dtype, dict) and dtype.get("type") == "dtype":
729
+ dtype = dtype.get("value")
730
+ name = str(dtype).replace("torch.", "")
731
+ nbx = getattr(NBXDtype, name, None)
732
+ if nbx is None:
733
+ raise RuntimeError(f"ZERO FALLBACK: {op} dtype {dtype!r} is not a known NBXDtype.")
734
+ return nbx
735
+
736
+
737
+ # ===========================================================================
738
+ # CONV2D WRAPPER — Extracted from FlagGems
739
+ # ===========================================================================
740
+
741
+ # Spatial band-streaming threshold for conv2d. When the per-launch output
742
+ # tensor would exceed `tiling_sizes.conv2d_band_bytes()` (`conv2d.band_bytes` in
743
+ # nbxtensor/ops/tiling.yml), conv2d_wrapper transparently splits along the H dimension
744
+ # and streams band-by-band. This is the kernel-level tiling lever
745
+ # (P-SANA-4KPX-RUNTIME, Step 1 — internal to the wrapper, not a Prism op-level
746
+ # interceptor). The TilingEngine's sizing half is the one definition Prism, the
747
+ # launch keys and this wrapper share.
748
+ from nbxtensor.ops import tiling_sizes as _ts
749
+
750
+
751
+ import math
752
+
753
+
754
+ def _arch_param(section: str, key: str, default):
755
+ """One `<section>.<key>` value of the executing device's description — its device file merged with the
756
+ application's half (`device_files.param`) under the application's hardware profile, the device file of the
757
+ launch target alone without one; `default` when no description holds the key, or the loader fails (R23: hardware
758
+ params come from the yml, never from code, and never from a driver query in a hot path).
759
+
760
+ Shared reader for every per-architecture knob — the memory policy
761
+ (`memory.sdpa_math_min_chunk_rows`, `memory.alloc_pool_parked_cap_fraction`,
762
+ …) and the pipelining budget (`pipelining.max_num_stages`)."""
763
+ prof = get_hardware_profile()
764
+ devices = getattr(prof, "devices", None) if prof is not None else None
765
+ if not devices:
766
+ # the library alone, no application profile: the device the launch target names, read from its device file
767
+ # (`device_files.in_force`) — the facts the kernels read are the library's own (the supervisor 2026-11-04 06:00:
768
+ # an application's profile only lies over them); the conformance suite's reductions were refused here on CUDA
769
+ from nbxtensor.backend.device_files import in_force
770
+ sec = in_force().get(section)
771
+ if isinstance(sec, dict) and key in sec:
772
+ return sec[key]
773
+ return default
774
+ try:
775
+ from nbxtensor.backend.device_files import param as vendor_param # one key of the device, both halves
776
+ _brand = devices[0].brand
777
+ _vendor = getattr(_brand, "value", _brand)
778
+ return vendor_param(_vendor, devices[0].architecture, section, key, default)
779
+ except Exception:
780
+ return default
781
+
782
+
783
+ def _arch_dispatch_param(key: str, default):
784
+ """One `dispatch.<key>` value for the executing hardware profile.
785
+
786
+ The launch path's own knobs live here — how many command buffers a queue
787
+ may hold in flight, and anything else about how work reaches the device.
788
+ They belong to the device, so they are read from its profile rather than
789
+ written into the driver."""
790
+ return _arch_param("dispatch", key, default)
791
+
792
+
793
+ def _arch_memory_param(key: str, default):
794
+ """One `memory.<key>` value for the executing hardware profile.
795
+ Thin alias of `_arch_param("memory", ...)`, kept because the memory
796
+ knobs are read from many call sites."""
797
+ return _arch_param("memory", key, default)
798
+
799
+ # the families cut out of this module (M2), reached through their module
800
+ from nbxtensor.ops import elementwise_wrappers as _ew # noqa: E402 — the cut's families (M2)
801
+ from nbxtensor.ops import reduction_wrappers as _red # noqa: E402 — the cut's families (M2)
802
+
803
+
804
+ #: the family modules of the wrappers, in the order a name is looked up (`function`)
805
+ _FAMILY_MODULES = ("elementwise_wrappers", "shape_wrappers", "conv_wrappers", "linalg_wrappers", "reduction_wrappers",
806
+ "attention_wrappers", "spectral_wrappers", "index_wrappers", "norm_wrappers", "random_wrappers",
807
+ "loss_wrappers", "moe_wrappers")
808
+ _MISSING = object()
809
+
810
+
811
+ def function(name: str, default=_MISSING):
812
+ """The wrapper called `name`, wherever the split put it (this module or one of its families): the lookup for a
813
+ wrapper NAMED BY DATA (an op table, an in-place map, a candidate list). A name no module defines is refused by name,
814
+ unless a `default` is given."""
815
+ import importlib
816
+ if name in globals() and callable(globals()[name]):
817
+ return globals()[name]
818
+ for fam in _FAMILY_MODULES:
819
+ mod = importlib.import_module(f"nbxtensor.ops.{fam}")
820
+ fn = getattr(mod, name, None)
821
+ if fn is not None:
822
+ return fn
823
+ if default is not _MISSING:
824
+ return default
825
+ raise AttributeError(f"no wrapper named {name!r} in nbxtensor.ops.wrappers or its families ({', '.join(_FAMILY_MODULES)})")