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
nbxtensor/ops/std.py ADDED
@@ -0,0 +1,113 @@
1
+ """Standard deviation — pure @triton.jit kernels.
2
+
3
+ Global reduction via map-reduce (sum + sum_sq) and dim-specific two-pass
4
+ (mean, then variance). Extracted from FlagGems (Apache-2.0).
5
+ """
6
+
7
+ import triton
8
+ import triton.language as tl
9
+
10
+
11
+ # ---------------------------------------------------------------------------
12
+ # Global reduction: map-reduce
13
+ # ---------------------------------------------------------------------------
14
+
15
+ @triton.jit
16
+ def std_map_kernel(X, Tmp_sum, Tmp_sum_sq, N, BLOCK_N: tl.constexpr):
17
+ """Pass 1: compute partial sum and sum-of-squares per block."""
18
+ pid = tl.program_id(0).to(tl.int64)
19
+ offset = pid * BLOCK_N + tl.arange(0, BLOCK_N)
20
+ mask = offset < N
21
+ x = tl.load(X + offset, mask=mask, other=0.0).to(tl.float32)
22
+ sum_val = tl.sum(x, axis=0)
23
+ sum_sq_val = tl.sum(x * x, axis=0)
24
+ tl.store(Tmp_sum + pid, sum_val)
25
+ tl.store(Tmp_sum_sq + pid, sum_sq_val)
26
+
27
+
28
+ @triton.jit
29
+ def std_reduce_kernel(
30
+ Tmp_sum, Tmp_sum_sq, Out, N, correction, BLOCK_NUM, BLOCK_SIZE: tl.constexpr
31
+ ):
32
+ """Pass 2: combine blocks and compute std = sqrt(var)."""
33
+ total_sum_acc = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
34
+ total_sum_sq_acc = tl.zeros((BLOCK_SIZE,), dtype=tl.float32)
35
+ for off in range(0, BLOCK_NUM, BLOCK_SIZE):
36
+ offset = off + tl.arange(0, BLOCK_SIZE)
37
+ mask = offset < BLOCK_NUM
38
+ tmp_sum_vals = tl.load(Tmp_sum + offset, mask=mask, other=0.0).to(tl.float32)
39
+ tmp_sum_sq_vals = tl.load(Tmp_sum_sq + offset, mask=mask, other=0.0).to(
40
+ tl.float32
41
+ )
42
+ total_sum_acc += tmp_sum_vals
43
+ total_sum_sq_acc += tmp_sum_sq_vals
44
+ total_sum = tl.sum(total_sum_acc, axis=0)
45
+ total_sum_sq = tl.sum(total_sum_sq_acc, axis=0)
46
+ mean = total_sum / N
47
+ var = (total_sum_sq / N) - (mean * mean)
48
+ var = var * N / tl.maximum(N - correction, 1.0)
49
+ safe_var = tl.maximum(var, 0.0)
50
+ std_dev = tl.sqrt(safe_var)
51
+ tl.store(Out, std_dev.to(Out.dtype.element_ty))
52
+
53
+
54
+ # ---------------------------------------------------------------------------
55
+ # Dim-specific: two-pass (mean, then variance)
56
+ # ---------------------------------------------------------------------------
57
+
58
+ @triton.jit
59
+ def std_dim_kernel(
60
+ X,
61
+ Out,
62
+ stride_x_row,
63
+ stride_x_col,
64
+ M,
65
+ N,
66
+ correction,
67
+ BLOCK_M: tl.constexpr,
68
+ BLOCK_N: tl.constexpr,
69
+ ):
70
+ """Compute std along compressed inner dim N.
71
+
72
+ X: [M, N] (dim-compressed, strided) → Out: [M]
73
+ Two-pass: first compute mean, then sum of squared deviations.
74
+ """
75
+ pid_group = tl.program_id(0).to(tl.int64)
76
+ start_row = pid_group * BLOCK_M
77
+ row_offsets = start_row + tl.arange(0, BLOCK_M)
78
+ row_mask = row_offsets < M
79
+
80
+ # Pass 1: compute mean
81
+ mean_acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
82
+ x_row_ptrs = X + row_offsets[:, None] * stride_x_row
83
+
84
+ for off in range(0, N, BLOCK_N):
85
+ col_offsets = off + tl.arange(0, BLOCK_N)
86
+ col_mask = col_offsets < N
87
+ x_ptrs = x_row_ptrs + col_offsets[None, :] * stride_x_col
88
+ final_mask = row_mask[:, None] & col_mask[None, :]
89
+ x = tl.load(x_ptrs, mask=final_mask, other=0.0)
90
+ mean_acc += x.to(tl.float32)
91
+
92
+ mean = tl.sum(mean_acc, axis=1) / N
93
+
94
+ # Pass 2: compute variance
95
+ var_acc = tl.zeros((BLOCK_M, BLOCK_N), dtype=tl.float32)
96
+ for off in range(0, N, BLOCK_N):
97
+ col_offsets = off + tl.arange(0, BLOCK_N)
98
+ col_mask = col_offsets < N
99
+ x_ptrs = x_row_ptrs + col_offsets[None, :] * stride_x_col
100
+ final_mask = row_mask[:, None] & col_mask[None, :]
101
+ x = tl.load(x_ptrs, mask=final_mask, other=0.0)
102
+ diff = x.to(tl.float32) - mean[:, None]
103
+ var_acc += tl.where(final_mask, diff * diff, 0.0)
104
+
105
+ var = tl.sum(var_acc, axis=1)
106
+
107
+ denom = N - correction
108
+ var = var / tl.maximum(denom, 1e-12)
109
+ safe_var = tl.maximum(var, 0.0)
110
+ std_dev = tl.sqrt(safe_var)
111
+
112
+ out_ptrs = Out + row_offsets
113
+ tl.store(out_ptrs, std_dev.to(Out.dtype.element_ty), mask=row_mask)
@@ -0,0 +1,200 @@
1
+ """Strided ↔ contiguous copy kernels — N-D scalable, Triton-JIT.
2
+
3
+ Two operations:
4
+
5
+ * `strided_copy_kernel` — read from a non-contiguous (strided) source,
6
+ write to a contiguous destination. Used by
7
+ `NBXTensor.clone` / `NBXTensor.contiguous`
8
+ whenever the source is a view (permute,
9
+ transpose, narrow, expand, ...).
10
+
11
+ * `strided_scatter_kernel` — read from a contiguous source, write to a
12
+ non-contiguous destination. Used by
13
+ `NBXTensor.__setitem__` (KV cache indexed
14
+ writes into a narrow view).
15
+
16
+ * `strided_copy_nd_kernel` — read from a strided source, write to a
17
+ strided destination, converting the dtype on
18
+ the store with `copy_kernel`'s exact protected
19
+ fp16 rounding. One launch where `__setitem__`,
20
+ `copy_` and `.to()` used to take a contiguous
21
+ copy of the source, a cast of that copy and a
22
+ scatter of the cast (three launches and two
23
+ transients per KV-cache write, 2026-09-07).
24
+
25
+ Both kernels are parameterised by `NDIM: tl.constexpr`. Triton
26
+ specialises and caches one compiled kernel per distinct ndim
27
+ encountered at runtime: the per-dimension index decomposition loop is
28
+ unrolled at compile time via `tl.static_range(NDIM)`.
29
+
30
+ The shape and stride vectors are passed as GPU pointers to small int64
31
+ arrays (NDIM elements each). The kernel loads them inside the JIT body,
32
+ so callers can handle any tensor up to PyTorch's 25-D hard limit
33
+ without recompiling the code. A 6-D PixArt patchify clone, an 8-D
34
+ SANA-Video VAE reshape, and a hypothetical 10-12-D multi-view 4D-scene
35
+ permute all route through the same kernel text.
36
+
37
+ Why this matters: the previous version was hardcoded to 5 dims
38
+ (`d0..d4`, `s0..s4`). The wrapper silently dropped higher dims via
39
+ `[1] * (5 - ndim)` returning `[]` for ndim > 5. 6-D PixArt clones
40
+ produced out-of-bounds reads past the source allocation — silently
41
+ corrupted data (Alpha) or `cudaErrorIllegalAddress` (Sigma), and
42
+ 8-D SANA-Video VAE clones had been unreachable entirely.
43
+ """
44
+
45
+ import triton
46
+ import triton.language as tl
47
+
48
+
49
+ @triton.jit
50
+ def strided_copy_kernel(
51
+ src_ptr, dst_ptr,
52
+ n_elements,
53
+ shape_ptr, # GPU pointer → NDIM int64 shape values (source)
54
+ stride_ptr, # GPU pointer → NDIM int64 stride values (source)
55
+ BLOCK_SIZE: tl.constexpr,
56
+ NDIM: tl.constexpr,
57
+ WIDE: tl.constexpr = True,
58
+ ):
59
+ """Copy strided src → contiguous dst. Dimensions are unrolled at
60
+ compile time via the NDIM constexpr; shape/strides are loaded from
61
+ GPU scratch buffers so the kernel text is identical for every
62
+ ndim Triton specialises.
63
+
64
+ Args:
65
+ src_ptr: Source tensor data pointer (may be non-contiguous).
66
+ dst_ptr: Destination tensor data pointer (contiguous).
67
+ n_elements: Total elements to copy (= src._numel).
68
+ shape_ptr: Pointer to NDIM int64 shape values (source shape).
69
+ stride_ptr: Pointer to NDIM int64 stride values (source strides).
70
+ BLOCK_SIZE: Elements per thread block (constexpr).
71
+ NDIM: Rank of the tensors (constexpr; triggers
72
+ specialisation + kernel cache entry).
73
+ """
74
+ if WIDE:
75
+ offsets = tl.program_id(0).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)
76
+ else:
77
+ offsets = tl.program_id(0) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
78
+ mask = offsets < n_elements
79
+
80
+ # Decompose the flat offset into multi-dim indices, walking dims
81
+ # from the innermost (NDIM-1) outward. At each step, `remaining %
82
+ # d` gives the current-dim index and `remaining // d` feeds the
83
+ # next outer dim. The src_offsets accumulator multiplies each
84
+ # per-dim index by the source's stride for that dim — this is
85
+ # what lets the kernel read from a non-contiguous layout while
86
+ # writing contiguously.
87
+ remaining = offsets
88
+ src_offsets = tl.zeros_like(offsets)
89
+ for i in tl.static_range(NDIM):
90
+ dim = NDIM - 1 - i
91
+ d = tl.load(shape_ptr + dim)
92
+ s = tl.load(stride_ptr + dim)
93
+ if not WIDE:
94
+ d = d.to(tl.int32)
95
+ s = s.to(tl.int32)
96
+ idx = remaining % d
97
+ remaining = remaining // d
98
+ src_offsets = src_offsets + idx * s
99
+
100
+ vals = tl.load(src_ptr + src_offsets, mask=mask)
101
+ tl.store(dst_ptr + offsets, vals, mask=mask)
102
+
103
+
104
+ @triton.jit
105
+ def strided_scatter_kernel(
106
+ src_ptr, dst_ptr,
107
+ n_elements,
108
+ shape_ptr, # GPU pointer → NDIM int64 shape values (destination)
109
+ stride_ptr, # GPU pointer → NDIM int64 stride values (destination)
110
+ BLOCK_SIZE: tl.constexpr,
111
+ NDIM: tl.constexpr,
112
+ WIDE: tl.constexpr = True,
113
+ ):
114
+ """Scatter contiguous src → strided dst. Inverse of
115
+ `strided_copy_kernel`. Same N-D specialisation strategy.
116
+
117
+ Args:
118
+ src_ptr: Source tensor data pointer (contiguous).
119
+ dst_ptr: Destination tensor data pointer (may be non-
120
+ contiguous — e.g. a narrow view into a KV cache).
121
+ n_elements: Total elements to scatter (= dst._numel).
122
+ shape_ptr: Pointer to NDIM int64 shape values (dst shape).
123
+ stride_ptr: Pointer to NDIM int64 stride values (dst strides).
124
+ BLOCK_SIZE: Elements per thread block.
125
+ NDIM: Rank of the tensors (constexpr).
126
+ """
127
+ if WIDE:
128
+ offsets = tl.program_id(0).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)
129
+ else:
130
+ offsets = tl.program_id(0) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
131
+ mask = offsets < n_elements
132
+
133
+ remaining = offsets
134
+ dst_offsets = tl.zeros_like(offsets)
135
+ for i in tl.static_range(NDIM):
136
+ dim = NDIM - 1 - i
137
+ d = tl.load(shape_ptr + dim)
138
+ s = tl.load(stride_ptr + dim)
139
+ if not WIDE:
140
+ d = d.to(tl.int32)
141
+ s = s.to(tl.int32)
142
+ idx = remaining % d
143
+ remaining = remaining // d
144
+ dst_offsets = dst_offsets + idx * s
145
+
146
+ vals = tl.load(src_ptr + offsets, mask=mask)
147
+ tl.store(dst_ptr + dst_offsets, vals, mask=mask)
148
+
149
+
150
+ @triton.jit
151
+ def strided_copy_nd_kernel(
152
+ src_ptr, dst_ptr,
153
+ n_elements,
154
+ shape_ptr, # GPU pointer → NDIM int64 extents (shared by src and dst)
155
+ src_stride_ptr, # GPU pointer → NDIM int64 source strides (0 on a broadcast dim)
156
+ dst_stride_ptr, # GPU pointer → NDIM int64 destination strides
157
+ BLOCK_SIZE: tl.constexpr,
158
+ NDIM: tl.constexpr,
159
+ WIDE: tl.constexpr = True,
160
+ SATURATE_F16: tl.constexpr = False,
161
+ ):
162
+ """Copy strided src → strided dst, casting on the store.
163
+
164
+ The flat element index is decomposed once into the multi-index and
165
+ each side's offset accumulates its own strides, so any pair of
166
+ layouts of the same extents copies in one pass — a broadcast source
167
+ (stride 0) included. SATURATE_F16 is the protected float16 downcast
168
+ of `copy_kernel`, the same expression, so a cast through this kernel
169
+ is bit-identical to a cast through `copy_kernel` after `contiguous()`.
170
+ """
171
+ if WIDE:
172
+ offsets = tl.program_id(0).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)
173
+ else:
174
+ offsets = tl.program_id(0) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE)
175
+ mask = offsets < n_elements
176
+
177
+ remaining = offsets
178
+ src_offsets = tl.zeros_like(offsets)
179
+ dst_offsets = tl.zeros_like(offsets)
180
+ for i in tl.static_range(NDIM):
181
+ dim = NDIM - 1 - i
182
+ d = tl.load(shape_ptr + dim)
183
+ ss = tl.load(src_stride_ptr + dim)
184
+ ds = tl.load(dst_stride_ptr + dim)
185
+ if not WIDE:
186
+ d = d.to(tl.int32)
187
+ ss = ss.to(tl.int32)
188
+ ds = ds.to(tl.int32)
189
+ idx = remaining % d
190
+ remaining = remaining // d
191
+ src_offsets = src_offsets + idx * ss
192
+ dst_offsets = dst_offsets + idx * ds
193
+
194
+ vals = tl.load(src_ptr + src_offsets, mask=mask)
195
+ if SATURATE_F16:
196
+ f = vals.to(tl.float32)
197
+ is_finite = (f == f) & (f != float("inf")) & (f != float("-inf"))
198
+ clamped = tl.minimum(tl.maximum(f, -65504.0), 65504.0)
199
+ vals = tl.where(is_finite, clamped, f)
200
+ tl.store(dst_ptr + dst_offsets, vals, mask=mask)
nbxtensor/ops/sub.py ADDED
@@ -0,0 +1,35 @@
1
+ """Subtraction — pure @triton.jit kernel."""
2
+
3
+ import triton
4
+ import triton.language as tl
5
+
6
+ @triton.jit
7
+ def sub_forward_kernel(
8
+ x_ptr, y_ptr, output_ptr,
9
+ n_elements,
10
+ alpha,
11
+ BLOCK_SIZE: tl.constexpr,
12
+ ):
13
+ """out = x - alpha * y"""
14
+ pid = tl.program_id(0).to(tl.int64)
15
+ 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)
16
+ mask = offset < n_elements
17
+
18
+ x = tl.load(x_ptr + offset, mask=mask)
19
+ y = tl.load(y_ptr + offset, mask=mask)
20
+ tl.store(output_ptr + offset, x - alpha * y, mask=mask)
21
+
22
+ @triton.jit
23
+ def rsub_forward_kernel(
24
+ x_ptr, output_ptr,
25
+ n_elements,
26
+ scalar,
27
+ BLOCK_SIZE: tl.constexpr,
28
+ ):
29
+ """out = scalar - x (reverse subtraction)"""
30
+ pid = tl.program_id(0).to(tl.int64)
31
+ 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)
32
+ mask = offset < n_elements
33
+
34
+ x = tl.load(x_ptr + offset, mask=mask)
35
+ tl.store(output_ptr + offset, scalar - x, mask=mask)
nbxtensor/ops/sum.py ADDED
@@ -0,0 +1,40 @@
1
+ """Sum reduction — pure @triton.jit kernel, the row walked in tiles of the certified width."""
2
+
3
+ import triton
4
+ import triton.language as tl
5
+
6
+ from nbxtensor.ops._configs import nbx_autotune, row_reduce_configs, prune_row_reduce
7
+
8
+
9
+ @nbx_autotune(configs=row_reduce_configs(), key=['BATCH_BUCKET', 'FEAT_BUCKET'],
10
+ prune_configs_by={'early_config_prune': prune_row_reduce}, cache_results=True)
11
+ @triton.jit
12
+ def sum_forward_kernel(
13
+ input_ptr, output_ptr,
14
+ batch_dim, feat_dim, BATCH_BUCKET, FEAT_BUCKET,
15
+ input_batch_stride, input_feat_stride,
16
+ BLOCK_SIZE_BATCH: tl.constexpr,
17
+ BLOCK_SIZE_FEAT: tl.constexpr,
18
+ ):
19
+ """Sum reduction over the last dimension: input [batch_dim, feat_dim] -> output [batch_dim].
20
+
21
+ The first tile is reduced alone and every further tile combined into it, so a row that fits one
22
+ tile is reduced by exactly the instructions it was before the loop existed.
23
+ """
24
+ batch_pid = tl.program_id(0).to(tl.int64)
25
+ batch_offset = batch_pid * BLOCK_SIZE_BATCH + tl.arange(0, BLOCK_SIZE_BATCH)
26
+ feat_offset = tl.arange(0, BLOCK_SIZE_FEAT)
27
+ batch_mask = batch_offset < batch_dim
28
+ row_ptr = input_ptr + input_batch_stride * batch_offset[:, None]
29
+
30
+ feat_mask = feat_offset < feat_dim
31
+ inp = tl.load(row_ptr + input_feat_stride * feat_offset[None, :],
32
+ mask=batch_mask[:, None] & feat_mask[None, :], other=0.0).to(tl.float32)
33
+ result = tl.sum(inp, axis=1)
34
+ for start in range(BLOCK_SIZE_FEAT, feat_dim, BLOCK_SIZE_FEAT):
35
+ offs = start + feat_offset
36
+ feat_mask = offs < feat_dim
37
+ inp = tl.load(row_ptr + input_feat_stride * offs[None, :].to(tl.int64),
38
+ mask=batch_mask[:, None] & feat_mask[None, :], other=0.0).to(tl.float32)
39
+ result += tl.sum(inp, axis=1)
40
+ tl.store(output_ptr + batch_offset, result, mask=batch_mask)
nbxtensor/ops/tan.py ADDED
@@ -0,0 +1,19 @@
1
+ """Tan (tangent) — pure @triton.jit kernel. Logic from FlagGems."""
2
+
3
+ import triton
4
+ import triton.language as tl
5
+
6
+ @triton.jit
7
+ def tan_forward_kernel(
8
+ input_ptr, output_ptr,
9
+ n_elements,
10
+ BLOCK_SIZE: tl.constexpr,
11
+ ):
12
+ pid = tl.program_id(0).to(tl.int64)
13
+ offset = pid.to(tl.int64) * BLOCK_SIZE + tl.arange(0, BLOCK_SIZE) # 64-bit from the program id: the product itself wraps past 2^31 elements (register 58)
14
+ mask = offset < n_elements
15
+
16
+ x = tl.load(input_ptr + offset, mask=mask)
17
+ x_fp32 = x.to(tl.float32)
18
+ out = tl.sin(x_fp32) / tl.cos(x_fp32)
19
+ tl.store(output_ptr + offset, out, mask=mask)
nbxtensor/ops/tanh.py ADDED
@@ -0,0 +1,21 @@
1
+ """Tanh — pure @triton.jit kernel."""
2
+
3
+ import triton
4
+ import triton.language as tl
5
+
6
+ from ._common import sigmoid
7
+
8
+ @triton.jit
9
+ def tanh_forward_kernel(
10
+ input_ptr, output_ptr,
11
+ n_elements,
12
+ BLOCK_SIZE: tl.constexpr,
13
+ ):
14
+ pid = tl.program_id(0).to(tl.int64)
15
+ 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)
16
+ mask = offset < n_elements
17
+
18
+ x = tl.load(input_ptr + offset, mask=mask)
19
+ x_fp32 = x.to(tl.float32)
20
+ out = 2.0 * sigmoid(2.0 * x_fp32) - 1.0
21
+ tl.store(output_ptr + offset, out, mask=mask)
@@ -0,0 +1,19 @@
1
+ """Threshold activation — pure @triton.jit kernel. Logic from FlagGems."""
2
+
3
+ import triton
4
+ import triton.language as tl
5
+
6
+ @triton.jit
7
+ def threshold_forward_kernel(
8
+ input_ptr, output_ptr,
9
+ n_elements,
10
+ threshold_val, value,
11
+ BLOCK_SIZE: tl.constexpr,
12
+ ):
13
+ pid = tl.program_id(0).to(tl.int64)
14
+ 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)
15
+ mask = offset < n_elements
16
+
17
+ x = tl.load(input_ptr + offset, mask=mask)
18
+ out = tl.where(x > threshold_val, x, value)
19
+ tl.store(output_ptr + offset, out, mask=mask)
@@ -0,0 +1,91 @@
1
+ # The TilingEngine's sizes — every engine-wide value a memory split is cut by.
2
+ #
3
+ # Read by the TilingEngine's sizing half and nowhere else — `nbxtensor/ops/tiling_sizes.py`, for
4
+ # both engine branches; a missing key is refused by name. Values that belong to an ARCHITECTURE stay in its hardware
5
+ # profile (config/vendors/<vendor>/<arch>.yml): memory.sdpa_math_max_scores_bytes,
6
+ # memory.sdpa_math_scores_device_fraction, memory.sdpa_math_min_chunk_rows,
7
+ # memory.sdpa_math_max_chunks, tiling.extent_lattice.
8
+ #
9
+ # Each value carries its SOURCE: the reasoning, measurement or workstream that set it.
10
+
11
+ conv3d:
12
+ # 1 GiB. The per-transient bound of the chunked temporal path, and the fold size above which
13
+ # the one-shot path's peak is priced at all: small and validated convolutions never chunk
14
+ # (P-TRITON-LIVE-SET-AUDIT; Allegro-TI2V vae_encoder at 720x1280x12f fp32, 5.66 GB a plane ->
15
+ # 30.4 GB live on a 32 GB V100 in triton only; the decision made Prism's, 2026-09-29).
16
+ chunk_bytes: 1073741824
17
+ # 256 MiB. The fixed slack added to the one-shot path's priced peak (conv3d_need).
18
+ slack_bytes: 268435456
19
+
20
+ conv2d:
21
+ # 4 GiB. A convolution whose output exceeds it streams in bands along H; it leaves headroom for
22
+ # weights, activations and arena overhead on a V100 32 GB (P-SANA-4KPX-RUNTIME Step 1). A
23
+ # one-shot conv3d whose conv2d output exceeds it holds one band's worth more.
24
+ band_bytes: 4294967296
25
+ # Bands of about half the band budget: the other half covers the transient input slice and the
26
+ # kernel's intermediate (P-SANA-4KPX-RUNTIME Step 1, `_conv2d_band_streamed`).
27
+ band_budget_divisor: 2
28
+
29
+ sdpa:
30
+ # 2 GiB. The fp32 scores bound a non-power-of-two head dim routes on when the architecture
31
+ # declares no `memory.sdpa_math_max_scores_bytes` (the scores budget LLM-class prefills chunk
32
+ # under a handful of times: 4k context -> 2 chunks, 16k -> 16; P-NONDET-LONG-ROW).
33
+ non_pow2_head_scores_bytes: 2147483648
34
+
35
+ op_level:
36
+ # 0.85 of the card, minus the component's resident weights: the budget an op's transient is
37
+ # measured against — overflow detection, the op-level plan and the conv3d chunk decision share
38
+ # it, or they disagree about which upsamples are free (`_tiling_aware_zero_alloc_uids`).
39
+ op_budget_card_fraction: 0.85
40
+ # 0.65 of the card, minus the conv output that stays full-resolution allocated: one band's
41
+ # budget for an upsample->conv fusion pair or a standalone convolution (S5, P-SANA-4KPX).
42
+ conv_band_card_fraction: 0.65
43
+ # 256 MB: the smallest per-band budget a band factor is computed against (a budget under it,
44
+ # "< 256 MB usable", is raised to it).
45
+ band_budget_floor_bytes: 268435456
46
+ # A tiled op runs in at least two bands.
47
+ min_band_factor: 2
48
+ # Band factors round up to a power of two for halo alignment, at most 64.
49
+ max_band_factor: 64
50
+ # An upsample whose output exceeds 25 % of the card is worth fusing into its conv even when the
51
+ # conv does not overflow (typical: 16 GB on a 32 GB card).
52
+ upsample_overflow_card_fraction: 0.25
53
+ rms_norm:
54
+ # 20 % of the card: an rms_norm whose output exceeds it runs in H bands — a stricter bound
55
+ # skipped the exactly-8 GB tensors of Sana 4Kpx on a 32 GB card.
56
+ overflow_card_fraction: 0.20
57
+ # One band's budget: 0.10 of the card (tightened from 0.65 because S5 chains carry 5-7 GiB of
58
+ # state into the rms_norm dispatch and each band's fp32 cast must fit beside it; the comment
59
+ # beside the code said 0.20, the code has carried 0.10).
60
+ band_card_fraction: 0.10
61
+ # At least four bands.
62
+ min_band_factor: 4
63
+ # At most eight: sixteen produced a numerically wrong output at Sana 4Kpx's rms_norm::27
64
+ # (post-chain wrapper, NHWC view of non-contiguous storage); eight was validated on 32 GB.
65
+ max_band_factor: 8
66
+ residual_chain:
67
+ # A residual chain (fork -> >=3 ops -> merge) streams in bands of at most 1 GiB at runtime
68
+ # (P-PRISM-NEVER-REFUSE v2 S5, 2026-05-13).
69
+ band_target_bytes: 1073741824
70
+ # A chain streams in at least two bands (the solver's chain rule carried the conv band floor,
71
+ # 2, so a change of that floor moved this one silently; it is its own value now).
72
+ min_band_factor: 2
73
+ # Its band factor rounds up to a power of two, at most 32.
74
+ max_band_factor: 32
75
+ # 1.6 GB: a chain is detected only when its base tensor (fp32, conservative) reaches it —
76
+ # 1.6 GB at fp32, ~0.8 GB at bf16 (S5).
77
+ min_base_bytes_fp32: 1600000000
78
+
79
+ overlap:
80
+ # A tile's overlap is its extent // 8, at least 4 to ensure some blending (TilingEngine's
81
+ # data-driven init; Prism's decode and encode tilings follow the same proportional law).
82
+ spatial_divisor: 8
83
+ spatial_min: 4
84
+ # A temporal tile overlaps its neighbour by t_tile // 4 frames (Prism's linear-class tiling).
85
+ temporal_divisor: 4
86
+
87
+ inplace:
88
+ # 1 GiB. A residual add or an element-wise unary writes into the buffer of its dead input when
89
+ # its output reaches it (P-SANA-4KPX-RUNTIME 2026-05-05, multi-branch fusion fix vector B; the
90
+ # unary rewrite uses the same figure, real-esrgan-x8 at 1024x1024 on a 16 GB V100).
91
+ min_bytes: 1073741824
@@ -0,0 +1,43 @@
1
+ """The tiling policy's default: the split sizes the tiling rules read (`tiling.yml`, beside this module).
2
+
3
+ The library owns the file and reads it itself, so the rules run with no application around them. A caller may set
4
+ its own table (`set_tiling`, plain data; `None` restores the library's); NeuroBrix reads this same file through the
5
+ library's `tiling_sizes`. A missing file is refused by name: no split is ever cut by a size the data
6
+ does not state.
7
+ """
8
+ from __future__ import annotations
9
+
10
+ import copy
11
+ import functools
12
+ from pathlib import Path
13
+ from typing import Any, Dict, Optional
14
+
15
+ _FILE = Path(__file__).resolve().parent / "tiling.yml"
16
+
17
+ __all__ = ["tiling_table", "set_tiling"]
18
+
19
+ _SET: Dict[str, Any] = {} # {"table": dict} once a caller set one
20
+
21
+
22
+ @functools.lru_cache(maxsize=1)
23
+ def _raw() -> Dict[str, Any]:
24
+ import yaml
25
+ if not _FILE.is_file():
26
+ raise FileNotFoundError(f"ZERO FALLBACK: the tiling policy is not found: {_FILE}")
27
+ return yaml.safe_load(_FILE.read_text()) or {}
28
+
29
+
30
+ def tiling_table() -> Dict[str, Any]:
31
+ """The policy in force: the table a caller set, else the library's file (a copy: the caller may not edit it)."""
32
+ return copy.deepcopy(_SET["table"] if "table" in _SET else _raw())
33
+
34
+
35
+ def set_tiling(table: Optional[Dict[str, Any]]) -> None:
36
+ """Set the split sizes the tiling rules read (a whole table, as `tiling.yml` writes it); `None` restores the
37
+ library's own."""
38
+ if table is None:
39
+ _SET.clear()
40
+ elif not isinstance(table, dict):
41
+ raise TypeError(f"set_tiling: a table is a dict as tiling.yml writes it, not {type(table).__name__}")
42
+ else:
43
+ _SET["table"] = copy.deepcopy(table)