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,20 @@
1
+ """Zeros — pure @triton.jit kernel.
2
+
3
+ Fills a tensor with zeros. Specialization of fill.
4
+ """
5
+
6
+ import triton
7
+ import triton.language as tl
8
+
9
+ @triton.jit
10
+ def zeros_kernel(
11
+ output_ptr,
12
+ n_elements,
13
+ BLOCK_SIZE: tl.constexpr,
14
+ ):
15
+ """Fill output with zeros."""
16
+ pid = tl.program_id(0).to(tl.int64)
17
+ 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)
18
+ mask = offset < n_elements
19
+ zero = tl.zeros([BLOCK_SIZE], dtype=output_ptr.type.element_ty)
20
+ tl.store(output_ptr + offset, zero, mask=mask)
@@ -0,0 +1 @@
1
+ """Quantized tensors."""
@@ -0,0 +1,187 @@
1
+ """QuantizedTensor — the engine-side handle for an encoded weight.
2
+
3
+ A weight stored under the int4-g128-asym encoding rides the container
4
+ as a triplet (`X.qweight` int32 [K//8, N], `X.scales` / `X.qmins`
5
+ fp16 [K//group, N], K = in_features). At weight-load the triplet is
6
+ assembled into ONE QuantizedTensor registered under the graph's
7
+ expected key (`X.weight`), so binding, placement accounting and
8
+ lifecycle stay untouched — compute wrappers detect the type and route:
9
+
10
+ aten::t -> returns self with the view flag flipped. The packed
11
+ layout is ALREADY the transposed orientation [K, N]
12
+ (the dequant-GEMV family's natural layout), so the
13
+ graph's `t(weight [N, K])` is a zero-cost marker.
14
+ aten::mm -> M == 1: fused dequant-GEMV (byte-gated family);
15
+ M > 1: dequantize to dense fp32 via the family's
16
+ standalone kernel, then the normal mm path (prefill is
17
+ compute-bound; the transient dense buffer frees at op
18
+ end).
19
+
20
+ Lives in kernels/ beside NBXTensor (wrappers route on this type; triton/ consumes it). R33: NBXTensor members only, zero torch. The logical metadata
21
+ (shape/dtype) mirrors what the dense weight would expose so generic
22
+ binding/accounting code needs no special cases.
23
+ """
24
+
25
+ from typing import Tuple
26
+
27
+ from nbxtensor.tensor import NBXTensor
28
+ from nbxtensor.dtype.nbx_dtype import NBXDtype
29
+
30
+ ENCODING = "int4-g128-asym"
31
+ GROUP = 128
32
+ # The leaves an encoded `X.weight` is stored under, in fold order
33
+ # (packed values, per-group scales, per-group minimums).
34
+ STORAGE_LEAVES = ("qweight", "scales", "qmins")
35
+ PACK = 8
36
+
37
+
38
+ class QuantizedTensor:
39
+ """int4-g128-asym weight triplet under one graph-facing handle.
40
+
41
+ `logical_shape` is the DENSE weight's shape as the graph traced it
42
+ ([out_features, in_features]); `transposed=True` marks the
43
+ graph-side aten::t view ([in, out] — the packed layout's natural
44
+ orientation)."""
45
+
46
+ __slots__ = ("qweight", "scales", "qmins", "logical_shape",
47
+ "transposed")
48
+
49
+ def __init__(self, qweight: NBXTensor, scales: NBXTensor,
50
+ qmins: NBXTensor, logical_shape: Tuple[int, int],
51
+ transposed: bool = False):
52
+ self.qweight = qweight
53
+ self.scales = scales
54
+ self.qmins = qmins
55
+ self.logical_shape = tuple(logical_shape)
56
+ self.transposed = transposed
57
+
58
+ # ── graph-facing metadata (mirrors the dense weight) ──
59
+ @property
60
+ def shape(self) -> Tuple[int, int]:
61
+ n, k = self.logical_shape
62
+ return (k, n) if self.transposed else (n, k)
63
+
64
+ @property
65
+ def ndim(self) -> int:
66
+ return 2
67
+
68
+ @property
69
+ def dtype(self):
70
+ return NBXDtype.float16 # the dense weight's traced dtype class
71
+
72
+ @property
73
+ def _dtype(self):
74
+ return NBXDtype.float16
75
+
76
+ @property
77
+ def _shape(self) -> Tuple[int, int]:
78
+ return self.shape
79
+
80
+ @property
81
+ def nbx_dtype(self):
82
+ return NBXDtype.float16
83
+
84
+ @property
85
+ def device(self):
86
+ return self.qweight.device
87
+
88
+ @property
89
+ def _device(self):
90
+ return self.qweight._device
91
+
92
+ @property
93
+ def _device_idx(self):
94
+ return self.qweight._device_idx
95
+
96
+ @property
97
+ def _nbytes(self) -> int:
98
+ return (self.qweight._nbytes + self.scales._nbytes
99
+ + self.qmins._nbytes)
100
+
101
+ def numel(self) -> int:
102
+ n, k = self.logical_shape
103
+ return n * k
104
+
105
+ def data_ptr(self) -> int:
106
+ """Identity pointer for cache fingerprints (the qweight buffer
107
+ — stable for the triplet's lifetime)."""
108
+ return self.qweight.data_ptr()
109
+
110
+ def pin_host(self) -> "QuantizedTensor":
111
+ """The three host parts pinned (zero3 offload of an int4 weight)."""
112
+ return QuantizedTensor(self.qweight.pin_host(), self.scales.pin_host(), self.qmins.pin_host(),
113
+ self.logical_shape, self.transposed)
114
+
115
+ # ── the movers: a weight is moved by ASKING THE WEIGHT ──
116
+ #
117
+ # Every placement path in the engine moves a weight by calling a mover on
118
+ # the tensor itself (`strategies/base.py`, `strategies/zero3.py`,
119
+ # `strategies/triton/base.py`): dense weights answer, and an encoded one
120
+ # that cannot answer breaks the first time a plan streams or offloads.
121
+ # Measured 2026-09-16 on the vitrine's first LLM request:
122
+ # Qwen3-Coder-30B-A3B-Instruct-int4g128 died at
123
+ # `'QuantizedTensor' object has no attribute 'to_cuda_async'` — the plan
124
+ # was right, the encoding was right, the handle was missing three methods.
125
+ # The triplet moves as a triplet; the metadata (logical shape, view flag)
126
+ # is not a property of where the bytes live, so it rides unchanged.
127
+
128
+ def to_cuda(self, device_idx: int = 0) -> "QuantizedTensor":
129
+ """The three parts on the given card, the triplet rebuilt."""
130
+ return QuantizedTensor(self.qweight.to_cuda(device_idx),
131
+ self.scales.to_cuda(device_idx),
132
+ self.qmins.to_cuda(device_idx),
133
+ self.logical_shape, self.transposed)
134
+
135
+ def to_cuda_async(self, device_idx: int = 0, stream: int = 0) -> "QuantizedTensor":
136
+ """The three parts enqueued on `stream` (the streaming strategies'
137
+ mover). Sequencing is the caller's, exactly as for a dense weight:
138
+ the returned triplet is readable once the stream is waited on."""
139
+ return QuantizedTensor(self.qweight.to_cuda_async(device_idx, stream=stream),
140
+ self.scales.to_cuda_async(device_idx, stream=stream),
141
+ self.qmins.to_cuda_async(device_idx, stream=stream),
142
+ self.logical_shape, self.transposed)
143
+
144
+ def to_cpu(self, pinned: bool = False) -> "QuantizedTensor":
145
+ """The three parts back on the host (offload), pinned if asked."""
146
+ return QuantizedTensor(self.qweight.to_cpu(pinned=pinned),
147
+ self.scales.to_cpu(pinned=pinned),
148
+ self.qmins.to_cpu(pinned=pinned),
149
+ self.logical_shape, self.transposed)
150
+
151
+ @property
152
+ def _pinned(self) -> bool:
153
+ return bool(getattr(self.qweight, "_pinned", False))
154
+
155
+ def t(self) -> "QuantizedTensor":
156
+ return QuantizedTensor(self.qweight, self.scales, self.qmins,
157
+ self.logical_shape,
158
+ transposed=not self.transposed)
159
+
160
+ def __repr__(self) -> str:
161
+ return (f"QuantizedTensor({ENCODING}, logical={self.logical_shape}, "
162
+ f"transposed={self.transposed}, "
163
+ f"packed={tuple(self.qweight.shape)})")
164
+
165
+
166
+ def assemble_quantized(weights: dict) -> int:
167
+ """Fold `.qweight`/`.scales`/`.qmins` triplets in a loaded weights
168
+ dict into QuantizedTensor entries under the graph key `<base>.weight`.
169
+ Mutates the dict in place; returns the number of assembled weights.
170
+ ZERO FALLBACK: an incomplete triplet raises."""
171
+ q_leaf, s_leaf, m_leaf = STORAGE_LEAVES
172
+ bases = [k[: -len("." + q_leaf)] for k in weights
173
+ if k.endswith("." + q_leaf)]
174
+ for base in bases:
175
+ qw = weights.pop(f"{base}.{q_leaf}")
176
+ try:
177
+ sc = weights.pop(f"{base}.{s_leaf}")
178
+ mn = weights.pop(f"{base}.{m_leaf}")
179
+ except KeyError as e:
180
+ raise RuntimeError(
181
+ f"ZERO FALLBACK: incomplete quantized triplet for "
182
+ f"'{base}' — missing {e}. The build gate should have "
183
+ f"refused this artifact; re-build the variant.") from e
184
+ kp, n = qw.shape
185
+ weights[base + ".weight"] = QuantizedTensor(
186
+ qw, sc, mn, logical_shape=(n, kp * PACK))
187
+ return len(bases)
@@ -0,0 +1 @@
1
+ """Random: seeded streams (the vendor CPU generator's MT19937) and torch-free draws."""
@@ -0,0 +1,154 @@
1
+ """ONE draw for both engines: the vendor's CPU draw, torch-free.
2
+
3
+ The owner's rule (2026-10-13 02:05): a model's initial noise is one draw, data-driven (the request's
4
+ seed), identical between the compiled and the Triton branch and to the vendor at equal seed. Diffusers
5
+ draws its initial latents on torch's CPU generator — `torch.Generator("cpu").manual_seed(seed)` then
6
+ `randn` — which is MT19937 seeded by `init_genrand(seed)` (the low 32 bits), uniforms made of the 24
7
+ high mantissa bits of a 32-bit word, and ATen's `normal_fill`: Box-Muller over blocks of 16 (the first
8
+ eight uniforms of a block are the radii, the last eight the angles), the tail of `n % 16` values
9
+ recomputed from 16 FRESH uniforms. This module reproduces that stream without torch.
10
+
11
+ What is bit-exact: the uniform stream (every word of MT19937, every 24-bit mantissa) and the pairing.
12
+ What is within 1 ulp: the transcendentals. ATen computes `sqrt(-2 log u1)`, `cos`, `sin` through its
13
+ vectorised Sleef kernels (`logf_u10`, `sinf/cosf_u35`, AVX512 here, NEON on Apple), which are not
14
+ correctly rounded and differ per architecture; this module computes them in float64 and rounds once,
15
+ which is correctly rounded everywhere. Measured against torch 2.14 on an AVX512 Xeon over 100 000 values
16
+ per seed (seeds 7, 42, 2^31-1; 2026-10-13): the uniform stream bit-equal, the normals at most 3 ulp
17
+ apart (Sleef's u35 bound is 3.5), 98.8 % within 1 ulp, 61.5 % bit-equal, max |diff| 4.8e-7. The draw is
18
+ therefore identical between our two branches and on every platform, and within 4 ulp of the vendor's.
19
+
20
+ No torch, no device: numpy only (R33 holds; the compiled branch wraps the array in a tensor).
21
+ """
22
+ from __future__ import annotations
23
+
24
+ import math
25
+ from typing import Iterable, Tuple
26
+
27
+ import numpy as np
28
+
29
+ _N, _M = 624, 397
30
+ _MATRIX_A = np.uint32(0x9908B0DF)
31
+ _UPPER, _LOWER = np.uint32(0x80000000), np.uint32(0x7FFFFFFF)
32
+
33
+
34
+ class MT19937:
35
+ """The generator as torch's CPU `Generator` runs it: `manual_seed(s)` = `init_genrand(s & 0xffffffff)`."""
36
+
37
+ def __init__(self, seed: int) -> None:
38
+ mt = np.zeros(_N, dtype=np.uint32)
39
+ mt[0] = np.uint32(int(seed) & 0xFFFFFFFF)
40
+ for i in range(1, _N): # the recurrence is sequential by definition; 624 steps once per run
41
+ prev = int(mt[i - 1])
42
+ mt[i] = np.uint32((1812433253 * (prev ^ (prev >> 30)) + i) & 0xFFFFFFFF)
43
+ self._mt = mt
44
+ self._i = _N
45
+ self._cached = None # the second Box-Muller sample of the scalar path, as torch caches it
46
+
47
+ def _twist(self) -> None:
48
+ """The reference twist, vectorised in the order it reads: `mt[k] = mt[(k + M) % N] ^ f(y_k)` with
49
+ `y_k` over the ORIGINAL `mt[k], mt[k+1]` for every k but the last. The second segment reads entries
50
+ rewritten earlier in the same pass, so it runs in chunks of N-M, each reading only chunks already
51
+ produced (a single vector expression read uninitialised values — the stream departed at word 454,
52
+ 2026-10-13)."""
53
+ mt = self._mt
54
+ y = (mt & _UPPER) | (np.roll(mt, -1) & _LOWER)
55
+ shifted = (y >> np.uint32(1)) ^ np.where(y & np.uint32(1), _MATRIX_A, np.uint32(0))
56
+ new = np.empty_like(mt)
57
+ s = _N - _M # 227: the stride a segment reads behind itself
58
+ new[:s] = mt[_M:] ^ shifted[:s]
59
+ k = s
60
+ while k < _N - 1:
61
+ e = min(k + s, _N - 1)
62
+ new[k:e] = new[k - s : e - s] ^ shifted[k:e]
63
+ k = e
64
+ y_last = (mt[_N - 1] & _UPPER) | (new[0] & _LOWER)
65
+ new[_N - 1] = new[_M - 1] ^ ((y_last >> np.uint32(1)) ^ (_MATRIX_A if int(y_last) & 1 else np.uint32(0)))
66
+ self._mt = new
67
+ self._i = 0
68
+
69
+ def words(self, n: int) -> np.ndarray:
70
+ """`n` tempered 32-bit words, in stream order."""
71
+ out = np.empty(n, dtype=np.uint32)
72
+ k = 0
73
+ while k < n:
74
+ if self._i >= _N:
75
+ self._twist()
76
+ take = min(n - k, _N - self._i)
77
+ y = self._mt[self._i : self._i + take].copy()
78
+ y ^= y >> np.uint32(11)
79
+ y ^= (y << np.uint32(7)) & np.uint32(0x9D2C5680)
80
+ y ^= (y << np.uint32(15)) & np.uint32(0xEFC60000)
81
+ y ^= y >> np.uint32(18)
82
+ out[k : k + take] = y
83
+ self._i += take
84
+ k += take
85
+ return out
86
+
87
+ def uniform_f32(self, n: int) -> np.ndarray:
88
+ """ATen `uniform_real_distribution<float>(0, 1)`: the 24 low bits of each word over 2^24."""
89
+ return ((self.words(n) & np.uint32((1 << 24) - 1)).astype(np.float64) * (1.0 / (1 << 24))).astype(np.float32)
90
+
91
+ def randn(self, n: int) -> np.ndarray:
92
+ """`n` standard normals as torch's CPU `randn` draws them (ATen `normal_fill`, blocks of 16; a tail of
93
+ `n % 16` recomputed from 16 fresh uniforms), the transcendentals in float64 rounded once."""
94
+ if n < 16:
95
+ return self._randn_scalar(n)
96
+ u = self.uniform_f32(n)
97
+ out = np.empty(n, dtype=np.float32)
98
+ m = n - n % 16
99
+ if m:
100
+ _fill16(out, 0, u[:m])
101
+ if n % 16:
102
+ _fill16(out, n - 16, self.uniform_f32(16))
103
+ return out
104
+
105
+ def _randn_scalar(self, n: int) -> np.ndarray:
106
+ """ATen's path for fewer than 16 values (`normal_kernel`'s serial branch): `normal_distribution<double>`
107
+ over 53-bit uniforms (two words per uniform, `random64`), Box-Muller in double with the second sample
108
+ of each pair cached in the generator and served next — exactly the vendor's small draws."""
109
+ out = np.empty(n, dtype=np.float32)
110
+ i = 0
111
+ while i < n:
112
+ if self._cached is not None:
113
+ out[i] = np.float32(self._cached)
114
+ self._cached = None
115
+ i += 1
116
+ continue
117
+ # ATen normal_distribution<double>: u1 then u2; r = sqrt(-2 log1p(-u2)); theta = 2 pi u1;
118
+ # the sine sample cached for the next call, the cosine returned.
119
+ u1 = self._uniform_f64()
120
+ u2 = self._uniform_f64()
121
+ radius = math.sqrt(-2.0 * math.log1p(-u2))
122
+ theta = 2.0 * math.pi * u1
123
+ self._cached = radius * math.sin(theta)
124
+ out[i] = np.float32(radius * math.cos(theta))
125
+ i += 1
126
+ return out
127
+
128
+ def _uniform_f64(self) -> float:
129
+ """ATen `uniform_real_distribution<double>(0, 1)`: the low 53 bits of a 64-bit word made of two 32-bit
130
+ words (`random64()` = `make64BitsFrom32Bits(random(), random())`: the first word high, the second low)."""
131
+ hi, lo = (int(x) for x in self.words(2))
132
+ v = ((hi << 32) | lo) & ((1 << 53) - 1)
133
+ return v * (1.0 / (1 << 53))
134
+
135
+
136
+ def _fill16(dst: np.ndarray, base: int, u: np.ndarray) -> None:
137
+ b = u.reshape(-1, 16)
138
+ u1 = (np.float32(1.0) - b[:, :8]).astype(np.float64)
139
+ u2 = b[:, 8:].astype(np.float32)
140
+ radius = np.sqrt(-2.0 * np.log(u1))
141
+ theta = (np.float32(2.0) * np.float32(math.pi) * u2).astype(np.float64) # ATen: 2.0f * pi<double> * u2 → float
142
+ view = dst[base : base + b.size].reshape(-1, 16)
143
+ view[:, :8] = (radius * np.cos(theta)).astype(np.float32)
144
+ view[:, 8:] = (radius * np.sin(theta)).astype(np.float32)
145
+
146
+
147
+ def mt_randn(seed: int, shape: Iterable[int]) -> np.ndarray:
148
+ """A fresh generator seeded with `seed`, its first `prod(shape)` normals reshaped — the vendor's
149
+ `randn_tensor(shape, generator=torch.Generator("cpu").manual_seed(seed))`, torch-free."""
150
+ shape: Tuple[int, ...] = tuple(int(s) for s in shape)
151
+ n = 1
152
+ for s in shape:
153
+ n *= s
154
+ return MT19937(seed).randn(n).reshape(shape)
@@ -0,0 +1,59 @@
1
+ """Gated cross-engine deterministic RNG for noise pinning.
2
+
3
+ `NBX_FORCE_RAND_SEED=<int>` makes every `rand`/`randn`/`*_like` op draw from a
4
+ single `numpy.RandomState`, so the PyTorch and Triton engines produce IDENTICAL
5
+ noise when they execute the same DAG (same op order + shapes). Two uses:
6
+
7
+ 1. Diagnostic — pin the noise so an op-by-op cross-engine diff is meaningful
8
+ past the first random op (otherwise every downstream op "diverges" purely
9
+ because each engine drew different noise, masking real kernel bugs).
10
+ 2. Shared-seeded determinism — the production way to make a stochastic vocoder
11
+ (e.g. chatterbox s3gen CFM + NSF/SineGen source) reproducible and equal
12
+ across all four execution modes, same discipline as the openaudio dual_ar /
13
+ VibeVoice diffusion seeded noise.
14
+
15
+ Zero torch: this module returns numpy arrays. The Triton side wraps them as
16
+ `NBXTensor.from_numpy`; the PyTorch side wraps them as `torch.from_numpy`. The
17
+ draw order/shape must match across engines for the arrays to be bit-identical;
18
+ both run the same component DAG, so they do.
19
+ """
20
+
21
+ import os
22
+ import numpy as np
23
+
24
+ _STATE = None
25
+ _SEED_USED = "<unset>"
26
+
27
+
28
+ def pinned_seed():
29
+ """Return the configured seed (int) or None when pinning is disabled."""
30
+ v = os.environ.get("NBX_FORCE_RAND_SEED")
31
+ if v is None or v == "":
32
+ return None
33
+ try:
34
+ return int(v)
35
+ except ValueError:
36
+ return 1234
37
+
38
+
39
+ def _state():
40
+ global _STATE, _SEED_USED
41
+ s = pinned_seed()
42
+ if _STATE is None or _SEED_USED != s:
43
+ _STATE = np.random.RandomState(s if s is not None else 1234)
44
+ _SEED_USED = s
45
+ return _STATE
46
+
47
+
48
+ def _shape(shape):
49
+ return tuple(int(d) for d in shape)
50
+
51
+
52
+ def pinned_normal(shape):
53
+ """Standard-normal N(0,1) float32 array of the given shape."""
54
+ return _state().standard_normal(size=_shape(shape)).astype(np.float32)
55
+
56
+
57
+ def pinned_uniform(shape):
58
+ """Uniform [0,1) float32 array of the given shape."""
59
+ return _state().random_sample(size=_shape(shape)).astype(np.float32)
@@ -0,0 +1,118 @@
1
+ """Run-scoped seeded RNG stream for the Triton random wrappers.
2
+
3
+ The random wrappers (`rand/randn/normal/*_like` in `kernels/wrappers.py`)
4
+ historically drew each call's Philox seed from Python's `random.randint`,
5
+ which `--seed` never seeds: Triton-mode stochastic sampling (EulerAncestral
6
+ noise, DDIM eta>0 variance) was NON-reproducible by construction, and no
7
+ run-scoped stream contract existed at all.
8
+
9
+ This module is the Triton-engine mirror of the compiled engine's run-scoped
10
+ `torch.Generator` (see `VariableResolver.sampling_generator`): ONE seeded
11
+ stream drives every stochastic draw of a run, in consumption order. The flow
12
+ handler arms it at execution start from `defaults.seed` (the same data-driven
13
+ source both engines read); each wrapper call then derives an independent
14
+ per-draw kernel seed as splitmix64(run_seed, draw_counter). Draws are
15
+ reproducible per (seed, draw order) and statistically independent across the
16
+ counter. Cross-ENGINE bit-equality is NOT the contract here (the two engines
17
+ use different RNG algorithms); when a bit-identical cross-engine noise diff
18
+ is needed, the `rng_pin` module (NBX_FORCE_RAND_SEED) remains the tool and
19
+ takes precedence in the wrappers.
20
+
21
+ Zero torch, zero device state: pure Python integers.
22
+
23
+ THE ONE DRAW (owner, 2026-10-13 02:05): a model's initial noise is ONE draw, data-driven (the run's
24
+ seed), identical between the compiled and the Triton branch and to the vendor at equal seed. Both
25
+ engines take it from `init_draw(shape)` below — the vendor's CPU draw reproduced torch-free
26
+ (`nbxtensor/random/mt_randn.py`: MT19937 + ATen's normal_fill, within 4 ulp of `torch.randn(generator=cpu)`)
27
+ — as the first draw of the run's seeded stream, then the next for every further `init: randn`
28
+ variable, in consumption order. The per-draw Philox seeds above still serve the Triton random
29
+ kernels of the hot loop (scheduler noise); the compiled branch's scheduler draws keep its generator.
30
+ Cross-engine bit-equality of the INITIAL draw is the contract from here on.
31
+
32
+ EVERY DRAW OF A SEEDED RUN (2026-10-14): the vendor threads ONE generator through every draw of a run — an
33
+ image encoder's posterior sample (`retrieve_latents(vae.encode(x), generator)`), the initial noise, each
34
+ ancestral step's noise (`EulerAncestralDiscreteScheduler.step(..., generator)`). Measured on Allegro-TI2V at
35
+ its gate request: the two engines rendered the same clip at one step (mean |a - b| under 1 of 255 per frame)
36
+ and different content at ten, the ancestral noise being the only difference (torch's device generator in the
37
+ compiled branch, Philox in the Triton branch); on CogVideoX-5b-I2V the first op that departs between the
38
+ kernel oracle and the Triton branch is the posterior sample `vae_encoder/aten.randn_like::0`. So every draw of a
39
+ seeded run — graph random ops and scheduler noise, in both engines — is the run stream's next normal
40
+ (`next_normal`) or uniform (`next_uniform`) values, in consumption order. The per-draw Philox seeds
41
+ (`next_seed`) serve only what is not a seeded run's draw.
42
+ """
43
+
44
+ _RUN_SEED = None
45
+ _COUNTER = 0
46
+ _MT = None # the run's MT19937, armed with the seed; the init draws come from it in order
47
+
48
+
49
+ def set_run_seed(seed):
50
+ """Arm (or disarm with None) the run stream; resets the draw counter and the init-draw generator."""
51
+ global _RUN_SEED, _COUNTER, _MT
52
+ _RUN_SEED = int(seed) if seed is not None else None
53
+ _COUNTER = 0
54
+ _MT = None
55
+
56
+
57
+ def _mt():
58
+ global _MT
59
+ if _RUN_SEED is None:
60
+ raise RuntimeError("rng_stream: a draw while inactive: the run has no seed")
61
+ if _MT is None:
62
+ from nbxtensor.random.mt_randn import MT19937
63
+ _MT = MT19937(_RUN_SEED)
64
+ return _MT
65
+
66
+
67
+ def _count(shape):
68
+ n = 1
69
+ for x in shape:
70
+ n *= int(x)
71
+ return n
72
+
73
+
74
+ def next_normal(shape):
75
+ """The run stream's next standard normals as a float32 numpy array of `shape` (torch's CPU `randn` for the
76
+ run's seed, continued): the draw of every seeded random op of either engine."""
77
+ shape = tuple(int(x) for x in shape)
78
+ return _mt().randn(_count(shape)).reshape(shape)
79
+
80
+
81
+ def next_uniform(shape):
82
+ """The run stream's next [0, 1) uniforms as a float32 numpy array of `shape` (torch's CPU `rand`, continued)."""
83
+ shape = tuple(int(x) for x in shape)
84
+ return _mt().uniform_f32(_count(shape)).reshape(shape)
85
+
86
+
87
+ def init_draw(shape):
88
+ """The run's next initial-noise draw as a float32 numpy array of `shape` — the vendor's CPU draw for
89
+ the run's seed (`nbxtensor/random/mt_randn.MT19937`), the same bytes in both engines. Refused when no seed is
90
+ armed: a seedless run draws unseeded in each engine (the historical behaviour), and that is said."""
91
+ global _MT
92
+ if _RUN_SEED is None:
93
+ raise RuntimeError("rng_stream.init_draw() called while inactive: the run has no seed")
94
+ if _MT is None:
95
+ from nbxtensor.random.mt_randn import MT19937
96
+ _MT = MT19937(_RUN_SEED)
97
+ shape = tuple(int(x) for x in shape)
98
+ n = 1
99
+ for x in shape:
100
+ n *= x
101
+ return _MT.randn(n).reshape(shape)
102
+
103
+
104
+ def active() -> bool:
105
+ return _RUN_SEED is not None
106
+
107
+
108
+ def next_seed() -> int:
109
+ """Next per-draw kernel seed: splitmix64 of (run_seed, counter), 31-bit."""
110
+ global _COUNTER
111
+ if _RUN_SEED is None:
112
+ raise RuntimeError("rng_stream.next_seed() called while inactive")
113
+ x = (_RUN_SEED * 0x9E3779B97F4A7C15 + _COUNTER * 0xBF58476D1CE4E5B9) & 0xFFFFFFFFFFFFFFFF
114
+ _COUNTER += 1
115
+ x = ((x ^ (x >> 30)) * 0xBF58476D1CE4E5B9) & 0xFFFFFFFFFFFFFFFF
116
+ x = ((x ^ (x >> 27)) * 0x94D049BB133111EB) & 0xFFFFFFFFFFFFFFFF
117
+ x = x ^ (x >> 31)
118
+ return int(x & 0x7FFFFFFF)