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,314 @@
1
+ """safetensors -> NBXTensor primitives (moved from the engine's Triton weight loader, M2): the dtype table of the
2
+ format, the parallel read of a tensor's bytes, the host-side conversions to the dtype it is held at, its placement in a
3
+ device arena or in page-locked host memory. The loading POLICY — which weight goes to which device or stays on the host,
4
+ the dtype a stored weight takes under a compute dtype, whether an fp16 target is widened — is the application's: it
5
+ passes each decision in. So are the I/O workers: every function that splits its work takes `io = (workers, executor)`,
6
+ the application's pool (its size is a fact of the machine the application owns).
7
+
8
+ Torch-free.
9
+ """
10
+ from __future__ import annotations
11
+
12
+ import ctypes
13
+ import math
14
+ import os
15
+ from typing import Optional
16
+
17
+ import numpy as np
18
+
19
+ from nbxtensor.tensor import NBXTensor
20
+ from nbxtensor.dtype.nbx_dtype import NBXDtype
21
+ from nbxtensor.memory.allocator import DeviceAllocator
22
+ from nbxtensor.dtype.convert import bf16_bits_to_f16, f32_to_bf16_bits
23
+ from nbxtensor.shape import contiguous_strides
24
+ from nbxtensor.memory.memory_pool import ComponentArena, HostPinnedArena
25
+
26
+ PAGE = os.sysconf("SC_PAGE_SIZE")
27
+
28
+
29
+ def spans(n: int, unit: int, align: int, workers: int):
30
+ """[start, end) element spans of an n-element run, one per worker, each a multiple of `align`
31
+ elements; a run under one page per worker stays whole (a split costs more than it shares)."""
32
+ if workers < 2 or n * unit < workers * PAGE:
33
+ return [(0, n)]
34
+ step = -(-n // workers)
35
+ step = -(-step // align) * align
36
+ return [(a, min(a + step, n)) for a in range(0, n, step)]
37
+
38
+
39
+ def read_bytes(fd: int, offset: int, nbytes: int, io) -> np.ndarray:
40
+ """nbytes of the file at offset, read by the loader's workers into one buffer (page-aligned
41
+ spans; `preadv` may return short, so each span reads until it is full)."""
42
+ buf = np.empty(nbytes, dtype=np.uint8)
43
+ view = memoryview(buf)
44
+
45
+ def _one(span):
46
+ a, b = span
47
+ while a < b:
48
+ got = os.preadv(fd, [view[a:b]], offset + a)
49
+ if got <= 0:
50
+ raise RuntimeError(f"weight read: {nbytes} bytes at offset {offset} of fd {fd} ended "
51
+ f"{b - a} bytes short (file truncated?)")
52
+ a += got
53
+
54
+ list(io[1].map(_one, spans(nbytes, 1, PAGE, io[0])))
55
+ return buf
56
+
57
+
58
+ def split_across_workers(src: np.ndarray, out: np.ndarray, convert, io) -> np.ndarray:
59
+ """`convert(src[a:b], out=flat_out[a:b])` over the loader's worker spans: one conversion of the library run in
60
+ parallel on a host-held weight — the widening ran on one thread for every host-held bf16 weight (a third of
61
+ Qwen3-30B-A3B's 447 s load, py-spy 2026-10-27). The split is the loader's; the conversion is nbxtensor's."""
62
+ flat_src = np.ascontiguousarray(src).reshape(-1)
63
+ flat = out.reshape(-1)
64
+
65
+ def _one(span):
66
+ a, b = span
67
+ convert(flat_src[a:b], out=flat[a:b])
68
+
69
+ list(io[1].map(_one, spans(flat_src.size, flat_src.itemsize, 1, io[0])))
70
+ return out
71
+
72
+
73
+ def cast(arr: np.ndarray, want, io) -> np.ndarray:
74
+ """`arr.astype(want)`, C-contiguous, the elements split across the loader's workers."""
75
+ src = np.ascontiguousarray(arr).reshape(-1)
76
+ out = np.empty(arr.shape, dtype=want)
77
+ flat = out.reshape(-1)
78
+
79
+ def _one(span):
80
+ np.copyto(flat[span[0]:span[1]], src[span[0]:span[1]], casting="unsafe")
81
+
82
+ list(io[1].map(_one, spans(src.size, src.itemsize, 1, io[0])))
83
+ return out
84
+
85
+
86
+ def to_bf16_bits(arr: np.ndarray, io) -> np.ndarray:
87
+ """float32 or float16 `arr` rounded to bfloat16 (`f32_to_bf16_bits`, uint16 patterns), the elements split across
88
+ the loader's workers like `cast`: each span is widened (float16 -> float32 is exact) and rounded into its slice."""
89
+ src = np.ascontiguousarray(arr).reshape(-1)
90
+ out = np.empty(arr.shape, dtype=np.uint16)
91
+ flat = out.reshape(-1)
92
+
93
+ def _one(span):
94
+ f32_to_bf16_bits(src[span[0]:span[1]].astype(np.float32, copy=False), out=flat[span[0]:span[1]])
95
+
96
+ list(io[1].map(_one, spans(src.size, src.itemsize, 1, io[0])))
97
+ return out
98
+
99
+
100
+ # safetensors dtype → (numpy dtype for reading, NBXDtype, bytes per element)
101
+ SF_DTYPE_INFO = {
102
+ "F16": (np.float16, NBXDtype.float16, 2),
103
+ "BF16": (None, NBXDtype.bfloat16, 2),
104
+ "F32": (np.float32, NBXDtype.float32, 4),
105
+ "F64": (np.float64, NBXDtype.float64, 8),
106
+ "I8": (np.int8, NBXDtype.int8, 1),
107
+ "I16": (np.int16, NBXDtype.int16, 2),
108
+ "I32": (np.int32, NBXDtype.int32, 4),
109
+ "I64": (np.int64, NBXDtype.int64, 8),
110
+ "U8": (np.uint8, NBXDtype.uint8, 1),
111
+ "BOOL": (np.bool_, NBXDtype.bool_, 1),
112
+ }
113
+
114
+
115
+ def half_in_place(ptr: int, count: int, device_idx: int):
116
+ """The loader's raw bf16 weight buffer at `ptr` rewritten in place as float16 by nbxtensor's one device
117
+ conversion (`ops.wrappers.bf16_to_fp16_gpu` with `out=` the same buffer)."""
118
+ from nbxtensor.ops.elementwise_wrappers import bf16_to_fp16_gpu
119
+
120
+ DeviceAllocator.set_device(device_idx)
121
+ DeviceAllocator.ensure_triton_device(device_idx)
122
+ flat = (count,)
123
+ strides = contiguous_strides(flat)
124
+ bits = NBXTensor(ptr, flat, strides, NBXDtype.int16, 'cuda', owns_data=False, device_idx=device_idx)
125
+ half = NBXTensor(ptr, flat, strides, NBXDtype.float16, 'cuda', owns_data=False, device_idx=device_idx)
126
+ bf16_to_fp16_gpu(bits, out=half)
127
+
128
+
129
+ def load_to_pinned_host(
130
+ raw: bytes,
131
+ shape: tuple,
132
+ source_dtype: NBXDtype,
133
+ target_dtype: NBXDtype,
134
+ io,
135
+ host_arena: Optional[HostPinnedArena] = None,
136
+ ) -> NBXTensor:
137
+ """Decode safetensors raw bytes into a pinned-host NBXTensor.
138
+
139
+ Applies the standard dtype remap chain (bf16→fp16, fp16→bf16) via
140
+ numpy before copying to pinned memory. bf16→fp16 uses the same
141
+ uint16-bit reinterpret path the GPU arena loader uses, so the
142
+ resulting values match exactly.
143
+ """
144
+ if source_dtype == NBXDtype.bfloat16 and target_dtype == NBXDtype.float16:
145
+ # The widening written straight into the destination by the workers: no per-weight temporary (each one over
146
+ # the host mmap threshold an mmap, its page faults and an munmap) and no copy after it — 18 864 of
147
+ # Qwen3-30B-A3B's weights, 57 GB, take this path on a card without bfloat16 (2026-10-30).
148
+ dst = _host_destination(shape, target_dtype, host_arena)
149
+ if len(raw) != dst.nbytes():
150
+ raise RuntimeError(f"weight staging would overflow: {len(raw)} bfloat16 bytes for shape {tuple(shape)} "
151
+ f"into a {dst.nbytes()}-byte float16 buffer")
152
+ if dst.numel():
153
+ out = np.ctypeslib.as_array((ctypes.c_uint16 * dst.numel()).from_address(dst.data_ptr())).view(np.float16)
154
+ split_across_workers(np.frombuffer(raw, dtype=np.uint16), out, bf16_bits_to_f16, io)
155
+ return dst
156
+
157
+ # Decode source bytes to a numpy array matching target_dtype.
158
+ if source_dtype == NBXDtype.bfloat16:
159
+ # Raw bf16 bits stored as uint16. Stays uint16 if target is
160
+ # bf16, or gets bit-shifted to fp32 then cast to fp16 if target
161
+ # is fp16.
162
+ raw_u16 = np.frombuffer(raw, dtype=np.uint16).reshape(shape)
163
+ if target_dtype == NBXDtype.float16:
164
+ arr = split_across_workers(raw_u16, np.empty(raw_u16.shape, dtype=np.float16), bf16_bits_to_f16, io)
165
+ else:
166
+ arr = np.ascontiguousarray(raw_u16)
167
+ else:
168
+ np_dtype = {
169
+ NBXDtype.float16: np.float16,
170
+ NBXDtype.float32: np.float32,
171
+ NBXDtype.float64: np.float64,
172
+ NBXDtype.int64: np.int64,
173
+ NBXDtype.int32: np.int32,
174
+ NBXDtype.int16: np.int16,
175
+ NBXDtype.int8: np.int8,
176
+ NBXDtype.uint8: np.uint8,
177
+ NBXDtype.bool_: np.bool_,
178
+ }[source_dtype]
179
+ arr = np.frombuffer(raw, dtype=np_dtype).reshape(shape)
180
+ arr = np.ascontiguousarray(arr)
181
+ if target_dtype == NBXDtype.bfloat16 and source_dtype == NBXDtype.float16:
182
+ # fp16 → bf16, rounded to nearest even (fp16 → fp32 is exact)
183
+ arr = to_bf16_bits(arr, io)
184
+ elif target_dtype == NBXDtype.bfloat16 and source_dtype == NBXDtype.float32:
185
+ # fp32 → bf16 rounded to nearest even, the SAME transform the
186
+ # GPU arena placement uses (`place_in_arena`), so the zero3
187
+ # host-staged copy of a weight is bit-identical to its arena copy.
188
+ # An fp32 encoder (PixArt / CogVideoX T5) staged to a bf16 compute
189
+ # target reached here still 4 bytes wide and overflowed the 2-byte
190
+ # pinned buffer — _NUMPY_FOR has no bf16 entry (numpy cannot
191
+ # represent it). The target itself is decided by
192
+ # stored_dtype_in_compute, the one rule shared with the census
193
+ # shadow, so completing this pair keeps the shadow's dtypes and the
194
+ # real run's identical rather than diverging.
195
+ arr = to_bf16_bits(arr, io)
196
+
197
+ # Any remaining source->target pair: cast through numpy.
198
+ #
199
+ # The two bit-level paths above cover bf16, which numpy cannot represent.
200
+ # Everything else is an ordinary cast, and it MUST happen: before
201
+ # 2026-09-03 only the two bf16 pairs were converted, so a float32 weight
202
+ # with a float16 target reached the copy still 4 bytes wide and wrote
203
+ # `arr.nbytes` into a buffer sized for the TARGET dtype — 1024 bytes into
204
+ # 512. Kokoro-82M's decoder is exactly that case (a (256,) fp32 tensor),
205
+ # and the full-zoo gate recorded it as a 240 s TIMEOUT rather than as the
206
+ # overflow it is, because the run never got far enough to fail.
207
+ _NUMPY_FOR = {
208
+ NBXDtype.float16: np.float16, NBXDtype.float32: np.float32,
209
+ NBXDtype.float64: np.float64, NBXDtype.int64: np.int64,
210
+ NBXDtype.int32: np.int32, NBXDtype.int16: np.int16,
211
+ NBXDtype.int8: np.int8, NBXDtype.uint8: np.uint8,
212
+ NBXDtype.bool_: np.bool_,
213
+ }
214
+ want = _NUMPY_FOR.get(target_dtype)
215
+ if want is not None and arr.dtype != want:
216
+ arr = cast(arr, want, io)
217
+
218
+ dst = _host_destination(shape, target_dtype, host_arena)
219
+
220
+ # The copy is sized from the SOURCE and the buffer from the TARGET, so a
221
+ # dtype pair nobody converted is a write past the end of a cudaMallocHost
222
+ # region. That is only caught at all because a pinned range is registered
223
+ # with the driver; the same defect on ordinary host memory corrupts the
224
+ # heap in silence. Refuse here, naming the pair, rather than rely on the
225
+ # driver noticing.
226
+ if arr.nbytes != dst.nbytes():
227
+ raise RuntimeError(
228
+ f"weight staging would overflow: {source_dtype!r} -> "
229
+ f"{target_dtype!r} for shape {tuple(shape)} produced "
230
+ f"{arr.nbytes} bytes for a {dst.nbytes()}-byte buffer. "
231
+ f"This dtype pair has no conversion in load_to_pinned_host — add "
232
+ f"one; never widen the buffer to match."
233
+ )
234
+ DeviceAllocator.memcpy(dst.data_ptr(), arr.ctypes.data,
235
+ arr.nbytes, kind=0)
236
+ return dst
237
+
238
+
239
+ def _host_destination(shape: tuple, target_dtype: NBXDtype, host_arena: Optional[HostPinnedArena]) -> NBXTensor:
240
+ """A host weight's destination: a slice of the component's one block when the load sized it, else its own
241
+ page-locked block."""
242
+ if host_arena is not None:
243
+ from nbxtensor.dtype.nbx_dtype import dtype_size as _dsize
244
+ ptr = host_arena.alloc((math.prod(shape) if shape else 1) * _dsize(target_dtype))
245
+ return NBXTensor(ptr, tuple(shape), contiguous_strides(tuple(shape)), target_dtype, 'cpu',
246
+ owns_data=False, device_idx=0, base=host_arena, pinned=True)
247
+ return NBXTensor.empty_cpu(shape, target_dtype, pinned=True)
248
+
249
+
250
+ def host_weight_layout(arr: np.ndarray, krsc: bool):
251
+ """The host array to copy to the device and the element strides of the logical tensor over
252
+ it. A KRSC weight (`launch_keys.conv_weight_krsc`) is written channels-innermost, once, here,
253
+ and stays a logical (K, C, R, S) tensor: the original order is never on the device."""
254
+ if not krsc:
255
+ return arr, contiguous_strides(arr.shape)
256
+ K, C, R, S = arr.shape
257
+ return np.ascontiguousarray(arr.transpose(0, 2, 3, 1)), (R * S * C, 1, S * C, C)
258
+
259
+
260
+ def place_in_arena(raw, shape: tuple, nbx_dtype: NBXDtype, np_dtype, target_dtype: NBXDtype, krsc: bool,
261
+ arena: ComponentArena, target_dev: int, io) -> NBXTensor:
262
+ """One safetensors tensor's raw bytes (stored as `nbx_dtype`, read through `np_dtype`) placed in `arena` on
263
+ `target_dev` at `target_dtype` — channels-innermost when `krsc` (`host_weight_layout`) — and the NBXTensor over
264
+ the arena's sub-allocation. The current device is the caller's to set."""
265
+ nbytes = len(raw)
266
+ # GPU-accelerated bf16→fp16 path (same buffer, 2 bytes/elem).
267
+ if nbx_dtype == NBXDtype.bfloat16 and target_dtype == NBXDtype.float16:
268
+ ptr = arena.alloc(nbytes)
269
+ arr_u16, strides = host_weight_layout(
270
+ np.frombuffer(raw, dtype=np.uint16).reshape(shape), krsc)
271
+ DeviceAllocator.memcpy(ptr, arr_u16.ctypes.data, nbytes, kind=1)
272
+ half_in_place(ptr, arr_u16.size, target_dev)
273
+ return NBXTensor(ptr, shape, strides, NBXDtype.float16, 'cuda',
274
+ owns_data=False, device_idx=target_dev)
275
+
276
+ # Standard path: numpy → H2D.
277
+ if nbx_dtype == NBXDtype.bfloat16:
278
+ # bf16 bits in uint16 containers, kept as they are
279
+ arr = np.ascontiguousarray(np.frombuffer(raw, dtype=np.uint16).reshape(shape))
280
+ final_dtype = target_dtype
281
+ else:
282
+ arr = np.frombuffer(raw, dtype=np_dtype).reshape(shape)
283
+ arr = np.ascontiguousarray(arr)
284
+ if (nbx_dtype == NBXDtype.float32
285
+ and target_dtype == NBXDtype.float16):
286
+ # fp32 → fp16 downcast. Required when an encoder ships
287
+ # fp32 on disk (PixArt T5, SDXL text_encoder_2, ...)
288
+ # but the compute path wants fp16. Native's
289
+ # WeightLoader(torch_dtype=fp16) does this implicitly;
290
+ # triton needs to do it explicitly so arena sizing
291
+ # and the kernel-side dtype agree.
292
+ arr = cast(arr, np.float16, io)
293
+ final_dtype = NBXDtype.float16
294
+ elif (nbx_dtype == NBXDtype.float32
295
+ and target_dtype == NBXDtype.bfloat16):
296
+ # fp32 → bf16, rounded to nearest even.
297
+ arr = to_bf16_bits(arr, io)
298
+ final_dtype = NBXDtype.bfloat16
299
+ elif target_dtype != nbx_dtype:
300
+ # fp16 → bf16, rounded to nearest even (fp16 → fp32 is exact).
301
+ arr = to_bf16_bits(arr, io)
302
+ final_dtype = target_dtype
303
+ else:
304
+ final_dtype = target_dtype
305
+
306
+ arr, strides = host_weight_layout(arr, krsc)
307
+ arr_bytes = arr.nbytes
308
+ ptr = arena.alloc(arr_bytes)
309
+ DeviceAllocator.memcpy(ptr, arr.ctypes.data, arr_bytes, kind=1) # H2D
310
+
311
+ # Create NBXTensor wrapping the arena sub-allocation (KRSC: the logical shape over
312
+ # the channels-innermost memory)
313
+ return NBXTensor(ptr, shape, strides, final_dtype, 'cuda',
314
+ owns_data=False, device_idx=target_dev)
@@ -0,0 +1,9 @@
1
+ """Memory: slot storage, and (as the move proceeds) allocators, pools and transfers."""
2
+
3
+
4
+ def set_limit(nbytes, device=None) -> None:
5
+ """Hold this process to a card of `nbytes` bytes on `device` (an ordinal; None: every device), or lift the limit
6
+ (`nbytes` None): the allocator refuses what such a card refuses and its pool parks a fraction of the limit
7
+ (`DeviceAllocator.set_limit`). Optional; absent, the physical card is the limit."""
8
+ from nbxtensor.memory.allocator import DeviceAllocator
9
+ DeviceAllocator.set_limit(nbytes, device)