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.
- nbxtensor/THIRD_PARTY_NOTICES.md +287 -0
- nbxtensor/__init__.py +17 -0
- nbxtensor/backend/__init__.py +1 -0
- nbxtensor/backend/capabilities.py +207 -0
- nbxtensor/backend/cpu/__init__.py +1 -0
- nbxtensor/backend/cpu/blas.py +102 -0
- nbxtensor/backend/cpu/pool.py +204 -0
- nbxtensor/backend/cpu_backend.py +162 -0
- nbxtensor/backend/device_facts.py +105 -0
- nbxtensor/backend/device_files.py +129 -0
- nbxtensor/backend/device_names.py +18 -0
- nbxtensor/backend/devices/amd/cdna.yml +228 -0
- nbxtensor/backend/devices/amd/cdna2.yml +228 -0
- nbxtensor/backend/devices/amd/cdna3.yml +227 -0
- nbxtensor/backend/devices/apple/apple_m1.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m1_max.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m1_pro.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m1_ultra.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m2.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m2_max.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m2_pro.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m2_ultra.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m3.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m3_max.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m3_pro.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m3_ultra.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m4.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m4_max.yml +266 -0
- nbxtensor/backend/devices/apple/apple_m4_pro.yml +308 -0
- nbxtensor/backend/devices/apple/apple_m5.yml +269 -0
- nbxtensor/backend/devices/apple/apple_m5_max.yml +269 -0
- nbxtensor/backend/devices/apple/apple_m5_pro.yml +269 -0
- nbxtensor/backend/devices/apple/apple_m5_ultra.yml +269 -0
- nbxtensor/backend/devices/apple/apple_m6.yml +269 -0
- nbxtensor/backend/devices/apple/apple_silicon.yml +263 -0
- nbxtensor/backend/devices/nvidia/ampere.yml +214 -0
- nbxtensor/backend/devices/nvidia/hopper.yml +213 -0
- nbxtensor/backend/devices/nvidia/volta.yml +411 -0
- nbxtensor/backend/faults.py +151 -0
- nbxtensor/backend/host_memory.py +185 -0
- nbxtensor/backend/launcher.py +2350 -0
- nbxtensor/backend/launcher_contract.py +429 -0
- nbxtensor/backend/metal_backend.py +661 -0
- nbxtensor/backend/metal_device.py +1274 -0
- nbxtensor/backend/process_memory.py +73 -0
- nbxtensor/backend/replay_graph.py +322 -0
- nbxtensor/backend/runtime.py +409 -0
- nbxtensor/backend/triton_ext_driver.py +735 -0
- nbxtensor/dtype/__init__.py +1 -0
- nbxtensor/dtype/convert.py +107 -0
- nbxtensor/dtype/dtypes.yml +70 -0
- nbxtensor/dtype/itemsize.py +166 -0
- nbxtensor/dtype/nbx_dtype.py +72 -0
- nbxtensor/dtype/operands.py +158 -0
- nbxtensor/dtype/outputs.py +64 -0
- nbxtensor/dtype/policy.py +155 -0
- nbxtensor/dtype/policy.yml +51 -0
- nbxtensor/dtype/promotion.py +112 -0
- nbxtensor/host.py +55 -0
- nbxtensor/io/__init__.py +1 -0
- nbxtensor/io/read_rate.py +197 -0
- nbxtensor/io/safetensors.py +314 -0
- nbxtensor/memory/__init__.py +9 -0
- nbxtensor/memory/allocator.py +1988 -0
- nbxtensor/memory/arena.py +75 -0
- nbxtensor/memory/device_memory.py +150 -0
- nbxtensor/memory/device_transfer.py +154 -0
- nbxtensor/memory/layout.py +43 -0
- nbxtensor/memory/memory_pool.py +191 -0
- nbxtensor/memory/metal.py +96 -0
- nbxtensor/memory/oom_advice.py +91 -0
- nbxtensor/memory/pointer_tables.py +323 -0
- nbxtensor/memory/pool.py +507 -0
- nbxtensor/memory/shadow.py +43 -0
- nbxtensor/memory/slab.py +273 -0
- nbxtensor/memory/trace_replay.py +122 -0
- nbxtensor/memory/traces/sana-video-2b-720p.16g.2026-11-02.tsv +13610 -0
- nbxtensor/memory/vmm.py +118 -0
- nbxtensor/ops/__init__.py +1 -0
- nbxtensor/ops/_autotune_policy.py +90 -0
- nbxtensor/ops/_common.py +32 -0
- nbxtensor/ops/_configs.py +1041 -0
- nbxtensor/ops/_cost.py +231 -0
- nbxtensor/ops/abs.py +17 -0
- nbxtensor/ops/adaptive_avg_pool2d.py +161 -0
- nbxtensor/ops/add.py +96 -0
- nbxtensor/ops/addcdiv.py +29 -0
- nbxtensor/ops/addcmul.py +29 -0
- nbxtensor/ops/addmv_op.py +64 -0
- nbxtensor/ops/addr.py +68 -0
- nbxtensor/ops/all_reduce.py +83 -0
- nbxtensor/ops/amax.py +40 -0
- nbxtensor/ops/any_reduce.py +83 -0
- nbxtensor/ops/arange_op.py +27 -0
- nbxtensor/ops/argmax.py +107 -0
- nbxtensor/ops/argmin.py +93 -0
- nbxtensor/ops/atan2.py +71 -0
- nbxtensor/ops/attention_wrappers.py +1648 -0
- nbxtensor/ops/autotune_bucket.py +137 -0
- nbxtensor/ops/autotune_cache.py +430 -0
- nbxtensor/ops/autotune_certified.py +1262 -0
- nbxtensor/ops/autotune_refusals.py +380 -0
- nbxtensor/ops/autotune_settings.py +58 -0
- nbxtensor/ops/avg_pool2d.py +73 -0
- nbxtensor/ops/baddbmm_op.py +132 -0
- nbxtensor/ops/batch_norm.py +163 -0
- nbxtensor/ops/binary_strided.py +196 -0
- nbxtensor/ops/bitwise_and.py +26 -0
- nbxtensor/ops/bitwise_left_shift.py +47 -0
- nbxtensor/ops/bitwise_not.py +24 -0
- nbxtensor/ops/bitwise_or.py +26 -0
- nbxtensor/ops/bitwise_right_shift.py +47 -0
- nbxtensor/ops/bucketize.py +37 -0
- nbxtensor/ops/cat_op.py +87 -0
- nbxtensor/ops/celu.py +20 -0
- nbxtensor/ops/clamp.py +24 -0
- nbxtensor/ops/clamp_min.py +22 -0
- nbxtensor/ops/conv2d.py +265 -0
- nbxtensor/ops/conv2d_m8n8k4.py +165 -0
- nbxtensor/ops/conv_depthwise2d.py +154 -0
- nbxtensor/ops/conv_transpose2d.py +148 -0
- nbxtensor/ops/conv_wrappers.py +1482 -0
- nbxtensor/ops/copy.py +18 -0
- nbxtensor/ops/copy_op.py +39 -0
- nbxtensor/ops/cos.py +17 -0
- nbxtensor/ops/count_nonzero.py +64 -0
- nbxtensor/ops/cross_entropy.py +67 -0
- nbxtensor/ops/cummax.py +109 -0
- nbxtensor/ops/cummin.py +105 -0
- nbxtensor/ops/cumsum.py +244 -0
- nbxtensor/ops/decode_attn_vec.py +162 -0
- nbxtensor/ops/depthwise_conv2d.py +132 -0
- nbxtensor/ops/dequant_gemv.py +190 -0
- nbxtensor/ops/dft.py +67 -0
- nbxtensor/ops/dispatch.py +1228 -0
- nbxtensor/ops/div.py +75 -0
- nbxtensor/ops/dot_op.py +67 -0
- nbxtensor/ops/dropout.py +23 -0
- nbxtensor/ops/dtype_convert.py +42 -0
- nbxtensor/ops/elementwise_launch.py +81 -0
- nbxtensor/ops/elementwise_wrappers.py +2315 -0
- nbxtensor/ops/elu.py +20 -0
- nbxtensor/ops/embedding.py +69 -0
- nbxtensor/ops/eq.py +32 -0
- nbxtensor/ops/erf.py +18 -0
- nbxtensor/ops/exp.py +17 -0
- nbxtensor/ops/exp2.py +18 -0
- nbxtensor/ops/fft_op.py +125 -0
- nbxtensor/ops/fill_op.py +20 -0
- nbxtensor/ops/flash_attention.py +307 -0
- nbxtensor/ops/flash_attention_m8n8k4.py +384 -0
- nbxtensor/ops/flash_decode.py +78 -0
- nbxtensor/ops/flip_op.py +60 -0
- nbxtensor/ops/floor.py +90 -0
- nbxtensor/ops/full_op.py +24 -0
- nbxtensor/ops/fused_moe.py +417 -0
- nbxtensor/ops/fused_moe_m8n8k4.py +99 -0
- nbxtensor/ops/fused_upsample_conv.py +413 -0
- nbxtensor/ops/gather_op.py +52 -0
- nbxtensor/ops/ge.py +32 -0
- nbxtensor/ops/gelu.py +31 -0
- nbxtensor/ops/gemv_vec.py +80 -0
- nbxtensor/ops/glu.py +43 -0
- nbxtensor/ops/grid_sampler.py +145 -0
- nbxtensor/ops/group_norm_tiled.py +67 -0
- nbxtensor/ops/groupnorm.py +123 -0
- nbxtensor/ops/gt.py +32 -0
- nbxtensor/ops/hardsigmoid.py +18 -0
- nbxtensor/ops/hardswish.py +19 -0
- nbxtensor/ops/im2col.py +57 -0
- nbxtensor/ops/index_add.py +95 -0
- nbxtensor/ops/index_put_op.py +92 -0
- nbxtensor/ops/index_select.py +121 -0
- nbxtensor/ops/index_wrappers.py +770 -0
- nbxtensor/ops/isfinite.py +20 -0
- nbxtensor/ops/isinf.py +20 -0
- nbxtensor/ops/isnan.py +18 -0
- nbxtensor/ops/launch_keys.py +1054 -0
- nbxtensor/ops/launch_observer.py +54 -0
- nbxtensor/ops/layernorm.py +71 -0
- nbxtensor/ops/le.py +35 -0
- nbxtensor/ops/leaky_relu.py +19 -0
- nbxtensor/ops/lerp.py +76 -0
- nbxtensor/ops/linalg_wrappers.py +1370 -0
- nbxtensor/ops/linspace_op.py +47 -0
- nbxtensor/ops/log.py +17 -0
- nbxtensor/ops/log_sigmoid.py +23 -0
- nbxtensor/ops/logical_and.py +26 -0
- nbxtensor/ops/logical_not.py +24 -0
- nbxtensor/ops/logical_or.py +26 -0
- nbxtensor/ops/logical_xor.py +37 -0
- nbxtensor/ops/loss_wrappers.py +282 -0
- nbxtensor/ops/lt.py +32 -0
- nbxtensor/ops/masked_fill.py +21 -0
- nbxtensor/ops/matmul.py +377 -0
- nbxtensor/ops/matmul_m8n8k4.py +235 -0
- nbxtensor/ops/max_pool2d.py +74 -0
- nbxtensor/ops/max_pool3d.py +93 -0
- nbxtensor/ops/max_reduce.py +91 -0
- nbxtensor/ops/maximum.py +18 -0
- nbxtensor/ops/mean.py +40 -0
- nbxtensor/ops/min_reduce.py +91 -0
- nbxtensor/ops/minimum.py +18 -0
- nbxtensor/ops/mish.py +22 -0
- nbxtensor/ops/mma_m8n8k4.py +152 -0
- nbxtensor/ops/moe_align.py +195 -0
- nbxtensor/ops/moe_decode_vec.py +374 -0
- nbxtensor/ops/moe_device_residency.py +141 -0
- nbxtensor/ops/moe_wrappers.py +345 -0
- nbxtensor/ops/mse_loss.py +79 -0
- nbxtensor/ops/mul.py +51 -0
- nbxtensor/ops/nan_to_num.py +23 -0
- nbxtensor/ops/ne.py +32 -0
- nbxtensor/ops/neg.py +17 -0
- nbxtensor/ops/nllloss.py +124 -0
- nbxtensor/ops/nonzero.py +33 -0
- nbxtensor/ops/nonzero_op.py +62 -0
- nbxtensor/ops/norm_wrappers.py +579 -0
- nbxtensor/ops/ones_op.py +20 -0
- nbxtensor/ops/oracles/__init__.py +6 -0
- nbxtensor/ops/oracles/conv2d_fp64.py +450 -0
- nbxtensor/ops/pad_op.py +159 -0
- nbxtensor/ops/paged_decode_attn.py +215 -0
- nbxtensor/ops/pixel_shuffle.py +144 -0
- nbxtensor/ops/pixel_unshuffle.py +72 -0
- nbxtensor/ops/pow.py +137 -0
- nbxtensor/ops/prod.py +81 -0
- nbxtensor/ops/rand_op.py +52 -0
- nbxtensor/ops/random_wrappers.py +366 -0
- nbxtensor/ops/reciprocal.py +17 -0
- nbxtensor/ops/reduction_wrappers.py +845 -0
- nbxtensor/ops/relu.py +18 -0
- nbxtensor/ops/remainder.py +46 -0
- nbxtensor/ops/repeat_interleave.py +48 -0
- nbxtensor/ops/repeat_op.py +132 -0
- nbxtensor/ops/residual_chain.py +374 -0
- nbxtensor/ops/rmsnorm.py +68 -0
- nbxtensor/ops/rope.py +142 -0
- nbxtensor/ops/rsqrt.py +17 -0
- nbxtensor/ops/scaled_softmax.py +96 -0
- nbxtensor/ops/scatter_op.py +139 -0
- nbxtensor/ops/screen_oracle.py +479 -0
- nbxtensor/ops/selu.py +22 -0
- nbxtensor/ops/shape_wrappers.py +1032 -0
- nbxtensor/ops/sigmoid.py +19 -0
- nbxtensor/ops/silu.py +27 -0
- nbxtensor/ops/sin.py +17 -0
- nbxtensor/ops/softmax.py +169 -0
- nbxtensor/ops/softplus.py +27 -0
- nbxtensor/ops/sort_op.py +304 -0
- nbxtensor/ops/spectral_wrappers.py +481 -0
- nbxtensor/ops/sqrt.py +17 -0
- nbxtensor/ops/stack_op.py +84 -0
- nbxtensor/ops/std.py +113 -0
- nbxtensor/ops/strided_copy.py +200 -0
- nbxtensor/ops/sub.py +35 -0
- nbxtensor/ops/sum.py +40 -0
- nbxtensor/ops/tan.py +19 -0
- nbxtensor/ops/tanh.py +21 -0
- nbxtensor/ops/threshold.py +19 -0
- nbxtensor/ops/tiling.yml +91 -0
- nbxtensor/ops/tiling_policy.py +43 -0
- nbxtensor/ops/tiling_sizes.py +370 -0
- nbxtensor/ops/topk.py +267 -0
- nbxtensor/ops/trace_op.py +64 -0
- nbxtensor/ops/tril.py +64 -0
- nbxtensor/ops/triu.py +62 -0
- nbxtensor/ops/unfold_op.py +37 -0
- nbxtensor/ops/unique_op.py +97 -0
- nbxtensor/ops/upsample_bilinear2d.py +106 -0
- nbxtensor/ops/upsample_nearest2d.py +52 -0
- nbxtensor/ops/upsample_nearest3d.py +61 -0
- nbxtensor/ops/var.py +136 -0
- nbxtensor/ops/vector_norm.py +216 -0
- nbxtensor/ops/weight_norm.py +104 -0
- nbxtensor/ops/where.py +39 -0
- nbxtensor/ops/wrappers.py +825 -0
- nbxtensor/ops/zeros_op.py +20 -0
- nbxtensor/quant/__init__.py +1 -0
- nbxtensor/quant/quantized_tensor.py +187 -0
- nbxtensor/random/__init__.py +1 -0
- nbxtensor/random/mt_randn.py +154 -0
- nbxtensor/random/rng_pin.py +59 -0
- nbxtensor/random/rng_stream.py +118 -0
- nbxtensor/random/seeded_draw.py +223 -0
- nbxtensor/shape/__init__.py +157 -0
- nbxtensor/tensor.py +1867 -0
- nbxtensor-0.0.1.dist-info/METADATA +379 -0
- nbxtensor-0.0.1.dist-info/RECORD +291 -0
- nbxtensor-0.0.1.dist-info/WHEEL +4 -0
- nbxtensor-0.0.1.dist-info/licenses/LICENSE +189 -0
nbxtensor/ops/relu.py
ADDED
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"""ReLU — pure @triton.jit kernel."""
|
|
2
|
+
|
|
3
|
+
import triton
|
|
4
|
+
import triton.language as tl
|
|
5
|
+
|
|
6
|
+
@triton.jit
|
|
7
|
+
def relu_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
|
+
out = tl.maximum(0, x)
|
|
18
|
+
tl.store(output_ptr + offset, out, mask=mask)
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
"""Remainder (modulo) — pure @triton.jit kernel.
|
|
2
|
+
|
|
3
|
+
torch.remainder / Python `%` define the result to follow the sign of the
|
|
4
|
+
DIVISOR: remainder(a, b) = a - floor(a / b) * b, so the result lies in [0, b)
|
|
5
|
+
for b > 0 (e.g. remainder(-0.1, 1) = 0.9). Triton's `%` operator on floats is C
|
|
6
|
+
`fmod` (trunc division), which follows the sign of the DIVIDEND (fmod(-0.1, 1) =
|
|
7
|
+
-0.1). Using `%` silently broke any negative-input modulo — surfaced by the
|
|
8
|
+
Kokoro SineGen phase wrap `(f0*harmonics/sr) % 1`, where the predictor's
|
|
9
|
+
unconstrained (sometimes negative) F0 produced negative phases that never
|
|
10
|
+
wrapped into [0,1), degrading the harmonic source. The floor formula matches
|
|
11
|
+
torch for floats and for integers (the result is integer-valued, cast back
|
|
12
|
+
cleanly); x/y promotes to float so `floor` is well-defined for int inputs too.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
import triton
|
|
16
|
+
import triton.language as tl
|
|
17
|
+
|
|
18
|
+
@triton.jit
|
|
19
|
+
def remainder_forward_kernel(
|
|
20
|
+
x_ptr, y_ptr, output_ptr,
|
|
21
|
+
n_elements,
|
|
22
|
+
BLOCK_SIZE: tl.constexpr,
|
|
23
|
+
):
|
|
24
|
+
pid = tl.program_id(0).to(tl.int64)
|
|
25
|
+
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)
|
|
26
|
+
mask = offset < n_elements
|
|
27
|
+
|
|
28
|
+
x = tl.load(x_ptr + offset, mask=mask)
|
|
29
|
+
y = tl.load(y_ptr + offset, mask=mask)
|
|
30
|
+
result = x - tl.math.floor((x / y).to(tl.float32)).to(x.dtype) * y
|
|
31
|
+
tl.store(output_ptr + offset, result, mask=mask)
|
|
32
|
+
|
|
33
|
+
@triton.jit
|
|
34
|
+
def remainder_scalar_kernel(
|
|
35
|
+
x_ptr, output_ptr,
|
|
36
|
+
n_elements,
|
|
37
|
+
divisor,
|
|
38
|
+
BLOCK_SIZE: tl.constexpr,
|
|
39
|
+
):
|
|
40
|
+
pid = tl.program_id(0).to(tl.int64)
|
|
41
|
+
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)
|
|
42
|
+
mask = offset < n_elements
|
|
43
|
+
|
|
44
|
+
x = tl.load(x_ptr + offset, mask=mask)
|
|
45
|
+
result = x - tl.math.floor((x / divisor).to(tl.float32)).to(x.dtype) * divisor
|
|
46
|
+
tl.store(output_ptr + offset, result, mask=mask)
|
|
@@ -0,0 +1,48 @@
|
|
|
1
|
+
"""repeat_interleave — pure @triton.jit kernel.
|
|
2
|
+
|
|
3
|
+
Handles the tensor-repeats case: given a 1D repeats tensor and its cumsum,
|
|
4
|
+
writes indices so that element i is repeated repeats[i] times.
|
|
5
|
+
|
|
6
|
+
Example: repeats=[2,3,1], cumsum=[2,5,6] -> output=[0,0,1,1,1,2]
|
|
7
|
+
|
|
8
|
+
The self-int case (scalar repeats) is a stride trick handled in the wrapper.
|
|
9
|
+
|
|
10
|
+
Ported from FlagGems repeat_interleave.py. Stripped FlagGems decorators.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
import triton
|
|
14
|
+
import triton.language as tl
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
@triton.jit
|
|
18
|
+
def repeat_interleave_tensor_kernel(
|
|
19
|
+
repeats_ptr,
|
|
20
|
+
cumsum_ptr,
|
|
21
|
+
out_ptr,
|
|
22
|
+
size,
|
|
23
|
+
BLOCK_SIZE: tl.constexpr,
|
|
24
|
+
):
|
|
25
|
+
"""Write source indices for tensor-based repeat_interleave.
|
|
26
|
+
|
|
27
|
+
Grid: (size,) — one program per input element.
|
|
28
|
+
Each program writes its index `pid` into repeats[pid] consecutive
|
|
29
|
+
positions starting at cumsum[pid] - repeats[pid].
|
|
30
|
+
"""
|
|
31
|
+
pid = tl.program_id(0).to(tl.int64)
|
|
32
|
+
mask = pid < size
|
|
33
|
+
cumsum = tl.load(cumsum_ptr + pid, mask, other=0)
|
|
34
|
+
# The loop bound below must be 32-bit: the Metal induction lowering does
|
|
35
|
+
# not terminate on a 64-bit `scf.for` bound (it refuses rather than hang).
|
|
36
|
+
# `repeats` arrives i64 by torch default. The cast is safe -- a per-element
|
|
37
|
+
# repeat count of 2**31 would need an 8 GB output for ONE element, which
|
|
38
|
+
# the allocator refuses long before this loop -- and numerically inert on
|
|
39
|
+
# CUDA: same iterations, narrower type. Addressing stays wide: `out_offset`
|
|
40
|
+
# promotes back through `cumsum`.
|
|
41
|
+
repeats = tl.load(repeats_ptr + pid, mask, other=0).to(tl.int32)
|
|
42
|
+
out_offset = cumsum - repeats
|
|
43
|
+
|
|
44
|
+
base_ptr = out_ptr + out_offset
|
|
45
|
+
for start_k in range(0, repeats, BLOCK_SIZE):
|
|
46
|
+
offsets_k = start_k + tl.arange(0, BLOCK_SIZE)
|
|
47
|
+
mask_k = offsets_k < repeats
|
|
48
|
+
tl.store(base_ptr + offsets_k, pid, mask=mask_k)
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
"""Repeat (tile) — pure @triton.jit kernel.
|
|
2
|
+
|
|
3
|
+
Copies input data into output with tiling via modular indexing.
|
|
4
|
+
For each output element, the source index is computed as:
|
|
5
|
+
src[i] = (output_idx[i] % input_shape[i]) for each dimension.
|
|
6
|
+
|
|
7
|
+
This is a rank-generic kernel that handles up to 6 dimensions.
|
|
8
|
+
Grid: (ceil(num_output_elements / BLOCK_SIZE),)
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
import triton
|
|
12
|
+
import triton.language as tl
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@triton.jit
|
|
16
|
+
def repeat_1d_kernel(
|
|
17
|
+
in_ptr, out_ptr,
|
|
18
|
+
in_stride0,
|
|
19
|
+
out_stride0,
|
|
20
|
+
out_shape0,
|
|
21
|
+
in_shape0,
|
|
22
|
+
num_tasks,
|
|
23
|
+
BLOCK_SIZE: tl.constexpr,
|
|
24
|
+
):
|
|
25
|
+
"""Repeat kernel for 1D tensors."""
|
|
26
|
+
pid = tl.program_id(0).to(tl.int64)
|
|
27
|
+
tid = 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)
|
|
28
|
+
mask = tid < num_tasks
|
|
29
|
+
|
|
30
|
+
i0 = tid
|
|
31
|
+
|
|
32
|
+
# Compute source index via modular indexing
|
|
33
|
+
in_offset = (i0 % in_shape0) * in_stride0
|
|
34
|
+
out_offset = i0 * out_stride0
|
|
35
|
+
|
|
36
|
+
val = tl.load(in_ptr + in_offset, mask=mask)
|
|
37
|
+
tl.store(out_ptr + out_offset, val, mask=mask)
|
|
38
|
+
|
|
39
|
+
|
|
40
|
+
@triton.jit
|
|
41
|
+
def repeat_2d_kernel(
|
|
42
|
+
in_ptr, out_ptr,
|
|
43
|
+
in_stride0, in_stride1,
|
|
44
|
+
out_stride0, out_stride1,
|
|
45
|
+
out_shape0, out_shape1,
|
|
46
|
+
in_shape0, in_shape1,
|
|
47
|
+
num_tasks,
|
|
48
|
+
BLOCK_SIZE: tl.constexpr,
|
|
49
|
+
):
|
|
50
|
+
"""Repeat kernel for 2D tensors."""
|
|
51
|
+
pid = tl.program_id(0).to(tl.int64)
|
|
52
|
+
tid = 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)
|
|
53
|
+
mask = tid < num_tasks
|
|
54
|
+
|
|
55
|
+
# Decompose flat index into multi-index
|
|
56
|
+
i1 = tid % out_shape1
|
|
57
|
+
i0 = tid // out_shape1
|
|
58
|
+
|
|
59
|
+
# Source via modular indexing
|
|
60
|
+
in_offset = (i0 % in_shape0) * in_stride0 + (i1 % in_shape1) * in_stride1
|
|
61
|
+
out_offset = i0 * out_stride0 + i1 * out_stride1
|
|
62
|
+
|
|
63
|
+
val = tl.load(in_ptr + in_offset, mask=mask)
|
|
64
|
+
tl.store(out_ptr + out_offset, val, mask=mask)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
@triton.jit
|
|
68
|
+
def repeat_3d_kernel(
|
|
69
|
+
in_ptr, out_ptr,
|
|
70
|
+
in_stride0, in_stride1, in_stride2,
|
|
71
|
+
out_stride0, out_stride1, out_stride2,
|
|
72
|
+
out_shape0, out_shape1, out_shape2,
|
|
73
|
+
in_shape0, in_shape1, in_shape2,
|
|
74
|
+
num_tasks,
|
|
75
|
+
BLOCK_SIZE: tl.constexpr,
|
|
76
|
+
):
|
|
77
|
+
"""Repeat kernel for 3D tensors."""
|
|
78
|
+
pid = tl.program_id(0).to(tl.int64)
|
|
79
|
+
tid = 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)
|
|
80
|
+
mask = tid < num_tasks
|
|
81
|
+
|
|
82
|
+
i2 = tid % out_shape2
|
|
83
|
+
tmp = tid // out_shape2
|
|
84
|
+
i1 = tmp % out_shape1
|
|
85
|
+
i0 = tmp // out_shape1
|
|
86
|
+
|
|
87
|
+
in_offset = (
|
|
88
|
+
(i0 % in_shape0) * in_stride0
|
|
89
|
+
+ (i1 % in_shape1) * in_stride1
|
|
90
|
+
+ (i2 % in_shape2) * in_stride2
|
|
91
|
+
)
|
|
92
|
+
out_offset = i0 * out_stride0 + i1 * out_stride1 + i2 * out_stride2
|
|
93
|
+
|
|
94
|
+
val = tl.load(in_ptr + in_offset, mask=mask)
|
|
95
|
+
tl.store(out_ptr + out_offset, val, mask=mask)
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
@triton.jit
|
|
99
|
+
def repeat_4d_kernel(
|
|
100
|
+
in_ptr, out_ptr,
|
|
101
|
+
in_stride0, in_stride1, in_stride2, in_stride3,
|
|
102
|
+
out_stride0, out_stride1, out_stride2, out_stride3,
|
|
103
|
+
out_shape0, out_shape1, out_shape2, out_shape3,
|
|
104
|
+
in_shape0, in_shape1, in_shape2, in_shape3,
|
|
105
|
+
num_tasks,
|
|
106
|
+
BLOCK_SIZE: tl.constexpr,
|
|
107
|
+
):
|
|
108
|
+
"""Repeat kernel for 4D tensors."""
|
|
109
|
+
pid = tl.program_id(0).to(tl.int64)
|
|
110
|
+
tid = 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)
|
|
111
|
+
mask = tid < num_tasks
|
|
112
|
+
|
|
113
|
+
i3 = tid % out_shape3
|
|
114
|
+
tmp = tid // out_shape3
|
|
115
|
+
i2 = tmp % out_shape2
|
|
116
|
+
tmp = tmp // out_shape2
|
|
117
|
+
i1 = tmp % out_shape1
|
|
118
|
+
i0 = tmp // out_shape1
|
|
119
|
+
|
|
120
|
+
in_offset = (
|
|
121
|
+
(i0 % in_shape0) * in_stride0
|
|
122
|
+
+ (i1 % in_shape1) * in_stride1
|
|
123
|
+
+ (i2 % in_shape2) * in_stride2
|
|
124
|
+
+ (i3 % in_shape3) * in_stride3
|
|
125
|
+
)
|
|
126
|
+
out_offset = (
|
|
127
|
+
i0 * out_stride0 + i1 * out_stride1
|
|
128
|
+
+ i2 * out_stride2 + i3 * out_stride3
|
|
129
|
+
)
|
|
130
|
+
|
|
131
|
+
val = tl.load(in_ptr + in_offset, mask=mask)
|
|
132
|
+
tl.store(out_ptr + out_offset, val, mask=mask)
|
|
@@ -0,0 +1,374 @@
|
|
|
1
|
+
"""
|
|
2
|
+
S5 — DC-AE residual chain band-streamed execution.
|
|
3
|
+
|
|
4
|
+
Implements the runtime wrapper that absorbs a long residual chain
|
|
5
|
+
(detected by `OpLevelTilingEngine._detect_residual_chains`) into a
|
|
6
|
+
single band-streamed compute. Reduces per-band intermediate footprint
|
|
7
|
+
from 3× full-resolution buffers to 1× band-sized buffer.
|
|
8
|
+
|
|
9
|
+
The chain pattern (validated on Sana 4Kpx VAE DC-AE blocks):
|
|
10
|
+
|
|
11
|
+
T_base (fork output)
|
|
12
|
+
|
|
|
13
|
+
|--- (chain): conv -> silu -> conv -> permute -> rms_norm -> add -> permute
|
|
14
|
+
|
|
|
15
|
+
+--- merge_add: chain_output + T_base = output
|
|
16
|
+
|
|
17
|
+
For each band of H rows:
|
|
18
|
+
1. Slice T_base [h_in_start:h_in_end] with halo for conv kernels
|
|
19
|
+
2. Run the chain ops on the band (PyTorch ATen)
|
|
20
|
+
3. Trim halo from chain output
|
|
21
|
+
4. Add T_base band → write to output[h_start:h_end]
|
|
22
|
+
|
|
23
|
+
Live tensor footprint per band: T_base (full) + output (full, fill in
|
|
24
|
+
place) + ~1 band intermediate (small) → fits 16 GiB on Sana 4Kpx VAE
|
|
25
|
+
4096² peak.
|
|
26
|
+
|
|
27
|
+
R34 conformant: pattern detection is structural (`fork → linear≥3 →
|
|
28
|
+
merge`), no model-name lookup. R30 dual-branch: compiled path uses
|
|
29
|
+
PyTorch ATen here; triton path port will add NBX equivalent.
|
|
30
|
+
|
|
31
|
+
S5 P-PRISM-NEVER-REFUSE v2 2026-05-13.
|
|
32
|
+
"""
|
|
33
|
+
from __future__ import annotations
|
|
34
|
+
|
|
35
|
+
from typing import Any, Dict, List, Optional
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
class ChainSentinel:
|
|
40
|
+
"""Zero-allocation sentinel returned by chain intermediate
|
|
41
|
+
interceptors. Carries no real data — the merge interceptor
|
|
42
|
+
reads the precomputed merge output from the chain registry.
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
__slots__ = ("chain_id", "shape", "dtype", "device")
|
|
46
|
+
|
|
47
|
+
def __init__(self, chain_id, shape, dtype, device):
|
|
48
|
+
self.chain_id = chain_id
|
|
49
|
+
self.shape = tuple(shape)
|
|
50
|
+
self.dtype = dtype
|
|
51
|
+
self.device = device
|
|
52
|
+
|
|
53
|
+
@property
|
|
54
|
+
def ndim(self):
|
|
55
|
+
return len(self.shape)
|
|
56
|
+
|
|
57
|
+
@property
|
|
58
|
+
def _nbytes(self):
|
|
59
|
+
return 0
|
|
60
|
+
|
|
61
|
+
def __repr__(self):
|
|
62
|
+
return f"ChainSentinel(chain_id={self.chain_id}, shape={self.shape})"
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def resolve_chain_weights(
|
|
66
|
+
chain_spec: Dict[str, Any],
|
|
67
|
+
dag: Dict[str, Any],
|
|
68
|
+
weights: Dict[str, Any],
|
|
69
|
+
) -> Dict[str, Any]:
|
|
70
|
+
"""Look up the weight tensors each chain op needs.
|
|
71
|
+
|
|
72
|
+
Parses the chain ops' `input_tensor_ids` from the DAG, identifies
|
|
73
|
+
`param::*` references, and pulls the matching entries from the
|
|
74
|
+
executor's `_weights` dict.
|
|
75
|
+
|
|
76
|
+
Returns a dict shaped for `band_streamed_chain_torch`:
|
|
77
|
+
{
|
|
78
|
+
"conv1_weight": tensor, "conv1_bias": tensor or None,
|
|
79
|
+
"conv1_padding": [1, 1], "conv1_stride": [1, 1],
|
|
80
|
+
"conv1_dilation": [1, 1], "conv1_groups": 1,
|
|
81
|
+
"conv2_weight": tensor, "conv2_bias": tensor or None,
|
|
82
|
+
"conv2_padding": ..., (same fields)
|
|
83
|
+
"norm_weight": tensor, "norm_eps": 1e-6,
|
|
84
|
+
"post_norm_bias": tensor or None,
|
|
85
|
+
"permute_forward": [0, 2, 3, 1],
|
|
86
|
+
"permute_backward": [0, 3, 1, 2],
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
Returns None if any required weight cannot be resolved.
|
|
90
|
+
"""
|
|
91
|
+
ops = dag.get("ops", {})
|
|
92
|
+
chain_uids = chain_spec["chain_uids"]
|
|
93
|
+
out: Dict[str, Any] = {
|
|
94
|
+
"conv1_weight": None, "conv1_bias": None,
|
|
95
|
+
"conv1_padding": [1, 1], "conv1_stride": [1, 1],
|
|
96
|
+
"conv1_dilation": [1, 1], "conv1_groups": 1,
|
|
97
|
+
"conv2_weight": None, "conv2_bias": None,
|
|
98
|
+
"conv2_padding": [1, 1], "conv2_stride": [1, 1],
|
|
99
|
+
"conv2_dilation": [1, 1], "conv2_groups": 1,
|
|
100
|
+
"norm_weight": None, "norm_eps": 1e-6,
|
|
101
|
+
"post_norm_bias": None,
|
|
102
|
+
"permute_forward": [0, 2, 3, 1],
|
|
103
|
+
"permute_backward": [0, 3, 1, 2],
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
conv_idx = 0
|
|
107
|
+
for uid in chain_uids:
|
|
108
|
+
op = ops.get(uid, {})
|
|
109
|
+
op_type = op.get("op_type", "")
|
|
110
|
+
in_tids = op.get("input_tensor_ids", [])
|
|
111
|
+
if op_type == "aten::convolution":
|
|
112
|
+
# input_tensor_ids: [activation, weight, optional bias]
|
|
113
|
+
if len(in_tids) >= 2:
|
|
114
|
+
w_tid = in_tids[1]
|
|
115
|
+
if isinstance(w_tid, str) and w_tid.startswith("param::"):
|
|
116
|
+
w = weights.get(w_tid[len("param::"):])
|
|
117
|
+
if w is None:
|
|
118
|
+
return {}
|
|
119
|
+
out[f"conv{conv_idx + 1}_weight"] = w
|
|
120
|
+
if len(in_tids) >= 3:
|
|
121
|
+
b_tid = in_tids[2]
|
|
122
|
+
if isinstance(b_tid, str) and b_tid.startswith("param::"):
|
|
123
|
+
out[f"conv{conv_idx + 1}_bias"] = weights.get(
|
|
124
|
+
b_tid[len("param::"):])
|
|
125
|
+
# Conv attrs from args: [act, w, b, stride, padding, dil,
|
|
126
|
+
# transposed, output_padding, groups]
|
|
127
|
+
attrs = op.get("attributes", {})
|
|
128
|
+
args = attrs.get("args", [])
|
|
129
|
+
|
|
130
|
+
def _get_list(arg):
|
|
131
|
+
if isinstance(arg, dict) and arg.get("type") == "list":
|
|
132
|
+
return [int(v) if isinstance(v, int) else int(
|
|
133
|
+
v.get("value", 1)) for v in arg.get("value", [])]
|
|
134
|
+
return None
|
|
135
|
+
|
|
136
|
+
def _get_scalar(arg):
|
|
137
|
+
if isinstance(arg, dict) and arg.get("type") == "scalar":
|
|
138
|
+
return arg.get("value")
|
|
139
|
+
return None
|
|
140
|
+
|
|
141
|
+
if len(args) >= 4:
|
|
142
|
+
v = _get_list(args[3])
|
|
143
|
+
if v:
|
|
144
|
+
out[f"conv{conv_idx + 1}_stride"] = v
|
|
145
|
+
if len(args) >= 5:
|
|
146
|
+
v = _get_list(args[4])
|
|
147
|
+
if v:
|
|
148
|
+
out[f"conv{conv_idx + 1}_padding"] = v
|
|
149
|
+
if len(args) >= 6:
|
|
150
|
+
v = _get_list(args[5])
|
|
151
|
+
if v:
|
|
152
|
+
out[f"conv{conv_idx + 1}_dilation"] = v
|
|
153
|
+
if len(args) >= 9:
|
|
154
|
+
v = _get_scalar(args[8])
|
|
155
|
+
if isinstance(v, int):
|
|
156
|
+
out[f"conv{conv_idx + 1}_groups"] = v
|
|
157
|
+
conv_idx += 1
|
|
158
|
+
elif op_type == "custom::rms_norm":
|
|
159
|
+
if len(in_tids) >= 2:
|
|
160
|
+
w_tid = in_tids[1]
|
|
161
|
+
if isinstance(w_tid, str) and w_tid.startswith("param::"):
|
|
162
|
+
w = weights.get(w_tid[len("param::"):])
|
|
163
|
+
if w is None:
|
|
164
|
+
return {}
|
|
165
|
+
out["norm_weight"] = w
|
|
166
|
+
attrs = op.get("attributes", {})
|
|
167
|
+
for arg in attrs.get("args", []):
|
|
168
|
+
if isinstance(arg, dict) and arg.get("type") == "scalar":
|
|
169
|
+
v = arg.get("value")
|
|
170
|
+
if isinstance(v, float):
|
|
171
|
+
out["norm_eps"] = v
|
|
172
|
+
break
|
|
173
|
+
elif op_type == "aten::add":
|
|
174
|
+
# Post-rms_norm bias-add: a learnable shift.
|
|
175
|
+
# input_tensor_ids: [activation, bias_param]
|
|
176
|
+
if len(in_tids) >= 2:
|
|
177
|
+
b_tid = in_tids[1]
|
|
178
|
+
if isinstance(b_tid, str) and b_tid.startswith("param::"):
|
|
179
|
+
out["post_norm_bias"] = weights.get(
|
|
180
|
+
b_tid[len("param::"):])
|
|
181
|
+
elif op_type == "aten::permute":
|
|
182
|
+
attrs = op.get("attributes", {})
|
|
183
|
+
args = attrs.get("args", [])
|
|
184
|
+
if len(args) >= 2 and isinstance(args[1], dict) \
|
|
185
|
+
and args[1].get("type") == "list":
|
|
186
|
+
perm = [int(v.get("value", 0)) if isinstance(v, dict)
|
|
187
|
+
else int(v) for v in args[1].get("value", [])]
|
|
188
|
+
if perm == [0, 2, 3, 1]:
|
|
189
|
+
out["permute_forward"] = perm
|
|
190
|
+
elif perm == [0, 3, 1, 2]:
|
|
191
|
+
out["permute_backward"] = perm
|
|
192
|
+
|
|
193
|
+
if out["conv1_weight"] is None or out["conv2_weight"] is None:
|
|
194
|
+
return {}
|
|
195
|
+
return out
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
|
|
199
|
+
# ---------------------------------------------------------------------------
|
|
200
|
+
# NBX-PURE TRITON variant — R33 zero-torch port of band_streamed_chain_torch.
|
|
201
|
+
# ---------------------------------------------------------------------------
|
|
202
|
+
# Same band-streaming algorithm, same correctness invariant (halo_carry),
|
|
203
|
+
# same memory shape. The only difference is the backend: every ATen op
|
|
204
|
+
# is replaced by its NBX wrapper from `nbxtensor.ops.wrappers`.
|
|
205
|
+
#
|
|
206
|
+
# P-TRITON-LIVE-WATERMARK-AUDIT 2026-05-14 L4: closes the R30 chain wrapper
|
|
207
|
+
# gap left open by commit c9d2581 ("future band_streamed_chain_nbx would
|
|
208
|
+
# close this gap"). On Sana 4Kpx 16g triton the L2 LIVE_DUMP showed two
|
|
209
|
+
# 4 GiB tensors at (1, 128, 4096, 4096) co-resident at add::89 because
|
|
210
|
+
# the chain wrapper was SKIP'd on triton — natural NBX dispatch keeps
|
|
211
|
+
# all chain intermediates fully materialized. With this wrapper the
|
|
212
|
+
# chain runs band-by-band on the triton arena tensors, peak transient
|
|
213
|
+
# drops to 1/tile_factor of the chain intermediate.
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
def band_streamed_chain_nbx(
|
|
217
|
+
t_base: "Any",
|
|
218
|
+
chain_weights: Dict[str, Any],
|
|
219
|
+
tile_factor: int,
|
|
220
|
+
halo: int,
|
|
221
|
+
) -> "Any":
|
|
222
|
+
"""NBXTensor band-streamed residual chain (R33-pure mirror of
|
|
223
|
+
`band_streamed_chain_torch`).
|
|
224
|
+
|
|
225
|
+
Args:
|
|
226
|
+
t_base: NBXTensor, the fork tensor (residual base). Mutated in
|
|
227
|
+
place to hold the merge output. Returned for caller convenience.
|
|
228
|
+
chain_weights: dict from `resolve_chain_weights` — same shape as
|
|
229
|
+
for the torch variant, but weight values are NBXTensor when
|
|
230
|
+
the executor is in triton mode.
|
|
231
|
+
tile_factor: number of bands along H.
|
|
232
|
+
halo: rows of halo on each side per band.
|
|
233
|
+
|
|
234
|
+
Returns: t_base (mutated).
|
|
235
|
+
|
|
236
|
+
R33: no `torch.*`, no `F.*`. All ops via
|
|
237
|
+
`nbxtensor.ops.wrappers` and NBXTensor methods.
|
|
238
|
+
|
|
239
|
+
Correctness invariant: halo_carry is a clone of the rows the NEXT
|
|
240
|
+
band needs as its top halo, captured BEFORE the current band's
|
|
241
|
+
`add_` overwrites them. The first band uses t_base directly because
|
|
242
|
+
no top halo has been overwritten yet.
|
|
243
|
+
"""
|
|
244
|
+
from nbxtensor.tensor import NBXTensor
|
|
245
|
+
from nbxtensor.backend.runtime import set_device
|
|
246
|
+
from nbxtensor.ops.conv_wrappers import conv2d_wrapper
|
|
247
|
+
from nbxtensor.ops.elementwise_wrappers import silu as silu_w, add as add_w
|
|
248
|
+
from nbxtensor.ops.norm_wrappers import rms_norm as rms_norm_w
|
|
249
|
+
|
|
250
|
+
# P-TRITON-LIVE-WATERMARK-AUDIT L4b: explicit device sync at
|
|
251
|
+
# entry. P-TRITON-CHAIN-CPU-POINTER C1 2026-05-14: diagnostic
|
|
252
|
+
# NBX_CHAIN_DIAG showed 1024² and 2048² chains succeed but
|
|
253
|
+
# 4096² chains fail with "Pointer argument (at 0) cannot be
|
|
254
|
+
# accessed from Triton (cpu tensor?)" while t_base+weights all
|
|
255
|
+
# report dev=0 contig=True at chain entry. The failure must
|
|
256
|
+
# therefore happen at an intermediate allocation (slice +
|
|
257
|
+
# contiguous → 1 GiB band, conv2d output → 1 GiB) where the
|
|
258
|
+
# device-context drifts. C3 fix: call `set_device(t_base)`
|
|
259
|
+
# before EVERY conv2d_wrapper / rms_norm_w / add_w in the
|
|
260
|
+
# band loop — overhead is tiny (single cudaSetDevice per op)
|
|
261
|
+
# vs the chain crash.
|
|
262
|
+
set_device(t_base)
|
|
263
|
+
|
|
264
|
+
N, C, H, W = t_base.shape
|
|
265
|
+
from nbxtensor.ops.tiling_sizes import residual_chain_bands # the one band geometry (the census derives by it)
|
|
266
|
+
bands = residual_chain_bands(H, tile_factor, halo)
|
|
267
|
+
band_h = (H + tile_factor - 1) // tile_factor
|
|
268
|
+
# P-TRITON-CHAIN-CPU-POINTER C3c 2026-05-14: log halo / tile_factor /
|
|
269
|
+
# band_h / H so we can confirm the band_h+halo math matches what the
|
|
270
|
+
# diag CHAIN_STEP shows (e.g. 1030 = 1024 + 6 implies halo=6 not 2).
|
|
271
|
+
import os as _os_cd3
|
|
272
|
+
if _os_cd3.environ.get("NBX_CHAIN_DIAG", "0") == "1":
|
|
273
|
+
print(f" [CHAIN_GEOM H={H} band_h={band_h} halo={halo} "
|
|
274
|
+
f"tile_factor={tile_factor}]", flush=True)
|
|
275
|
+
|
|
276
|
+
halo_carry = None # type: Optional[NBXTensor]
|
|
277
|
+
|
|
278
|
+
for i, (h_start, h_end, h_in_start, h_in_end) in enumerate(bands):
|
|
279
|
+
|
|
280
|
+
# Build the input band.
|
|
281
|
+
# If we have a halo_carry AND the top-halo region [h_in_start,
|
|
282
|
+
# h_start) has been overwritten by the previous band's add_,
|
|
283
|
+
# take the top from halo_carry and the rest from t_base.
|
|
284
|
+
if halo_carry is not None and h_in_start < h_start:
|
|
285
|
+
top_size = h_start - h_in_start
|
|
286
|
+
top_band = halo_carry[:, :, -top_size:, :]
|
|
287
|
+
rest_band = t_base[:, :, h_start:h_in_end, :]
|
|
288
|
+
band = NBXTensor.cat([top_band, rest_band], dim=2).contiguous()
|
|
289
|
+
else:
|
|
290
|
+
band = t_base[:, :, h_in_start:h_in_end, :].contiguous()
|
|
291
|
+
|
|
292
|
+
# Save halo for next band BEFORE overwriting (clone detaches
|
|
293
|
+
# from t_base storage).
|
|
294
|
+
if i + 1 < tile_factor:
|
|
295
|
+
halo_save_size = min(halo, h_end - h_start)
|
|
296
|
+
halo_carry = t_base[:, :,
|
|
297
|
+
h_end - halo_save_size:h_end, :].clone()
|
|
298
|
+
|
|
299
|
+
# conv1 — anchor device per op (the band size at 4096² is
|
|
300
|
+
# 1 GiB; the allocator path inside NBXTensor.empty cudaSetDevice
|
|
301
|
+
# can drift Triton's active device).
|
|
302
|
+
set_device(band)
|
|
303
|
+
w1 = chain_weights["conv1_weight"]
|
|
304
|
+
b1 = chain_weights["conv1_bias"]
|
|
305
|
+
import os as _os_cd2
|
|
306
|
+
if _os_cd2.environ.get("NBX_CHAIN_DIAG", "0") == "1":
|
|
307
|
+
_bp = getattr(band, "_data_ptr", "?")
|
|
308
|
+
_bd = getattr(band, "_device_idx", "?")
|
|
309
|
+
_bc = band.is_contiguous() if hasattr(
|
|
310
|
+
band, 'is_contiguous') else "?"
|
|
311
|
+
print(f" [CHAIN_STEP i={i} h_start={h_start} h_end={h_end} "
|
|
312
|
+
f"h_in_start={h_in_start} h_in_end={h_in_end} "
|
|
313
|
+
f"conv1_pre band ptr={_bp} dev={_bd} "
|
|
314
|
+
f"contig={_bc} shape={tuple(band.shape)}]",
|
|
315
|
+
flush=True)
|
|
316
|
+
band = conv2d_wrapper(
|
|
317
|
+
band, w1, b1,
|
|
318
|
+
stride=tuple(chain_weights["conv1_stride"]),
|
|
319
|
+
padding=tuple(chain_weights["conv1_padding"]),
|
|
320
|
+
dilation=tuple(chain_weights["conv1_dilation"]),
|
|
321
|
+
groups=chain_weights["conv1_groups"],
|
|
322
|
+
)
|
|
323
|
+
|
|
324
|
+
# silu
|
|
325
|
+
set_device(band)
|
|
326
|
+
band = silu_w(band)
|
|
327
|
+
|
|
328
|
+
# conv2
|
|
329
|
+
set_device(band)
|
|
330
|
+
w2 = chain_weights["conv2_weight"]
|
|
331
|
+
b2 = chain_weights["conv2_bias"]
|
|
332
|
+
band = conv2d_wrapper(
|
|
333
|
+
band, w2, b2,
|
|
334
|
+
stride=tuple(chain_weights["conv2_stride"]),
|
|
335
|
+
padding=tuple(chain_weights["conv2_padding"]),
|
|
336
|
+
dilation=tuple(chain_weights["conv2_dilation"]),
|
|
337
|
+
groups=chain_weights["conv2_groups"],
|
|
338
|
+
)
|
|
339
|
+
|
|
340
|
+
# permute NCHW → NHWC (R33-pure NBXTensor.permute)
|
|
341
|
+
band = band.permute(*chain_weights["permute_forward"]).contiguous()
|
|
342
|
+
|
|
343
|
+
# rms_norm over last (feature) dim
|
|
344
|
+
set_device(band)
|
|
345
|
+
norm_w = chain_weights["norm_weight"]
|
|
346
|
+
eps = float(chain_weights.get("norm_eps", 1e-6))
|
|
347
|
+
band = rms_norm_w(band, norm_w, eps)
|
|
348
|
+
|
|
349
|
+
# post-norm bias shift (if any)
|
|
350
|
+
post_bias = chain_weights.get("post_norm_bias")
|
|
351
|
+
if post_bias is not None:
|
|
352
|
+
set_device(band)
|
|
353
|
+
band = add_w(band, post_bias)
|
|
354
|
+
|
|
355
|
+
# permute NHWC → NCHW
|
|
356
|
+
band = band.permute(*chain_weights["permute_backward"]).contiguous()
|
|
357
|
+
|
|
358
|
+
# Trim halo from chain output, then add to t_base in place via
|
|
359
|
+
# __setitem__ + add. NBX add_inplace_nbx requires identical
|
|
360
|
+
# shapes AND contiguous target, but t_base[:, :, h_start:h_end, :]
|
|
361
|
+
# is a non-contig view (slicing on dim 2) — so we use the
|
|
362
|
+
# `add_w` + setitem pattern: materialize the band-sized sum,
|
|
363
|
+
# write it back. The setitem path uses _strided_scatter for
|
|
364
|
+
# the non-contig dst and a single contiguous src copy.
|
|
365
|
+
trim_top = h_start - h_in_start
|
|
366
|
+
trim_h = h_end - h_start
|
|
367
|
+
band_out = band[:, :, trim_top:trim_top + trim_h, :]
|
|
368
|
+
t_base_slice = t_base[:, :, h_start:h_end, :]
|
|
369
|
+
# add returns a new contiguous tensor; setitem writes back.
|
|
370
|
+
set_device(t_base)
|
|
371
|
+
t_base[:, :, h_start:h_end, :] = add_w(t_base_slice, band_out)
|
|
372
|
+
|
|
373
|
+
return t_base
|
|
374
|
+
|
nbxtensor/ops/rmsnorm.py
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
"""RMSNorm — pure @triton.jit kernel. Ported from attorch (MIT)."""
|
|
2
|
+
|
|
3
|
+
import triton
|
|
4
|
+
import triton.language as tl
|
|
5
|
+
from triton import next_power_of_2
|
|
6
|
+
|
|
7
|
+
from nbxtensor.ops._configs import nbx_autotune, row_program_configs, prune_row_programs
|
|
8
|
+
|
|
9
|
+
|
|
10
|
+
# rows per program and warps: the certified configuration of the key (workstream 1), the key the derived census forms
|
|
11
|
+
# (`launch_keys.rms_norm_launches`)
|
|
12
|
+
@nbx_autotune(configs=row_program_configs(), key=['BATCH_BUCKET', 'FEAT_BUCKET', 'scale_by_weight', 'SATURATE_F16'],
|
|
13
|
+
prune_configs_by={'early_config_prune': prune_row_programs}, cache_results=True)
|
|
14
|
+
@triton.heuristics({
|
|
15
|
+
'BLOCK_SIZE_FEAT': lambda args: next_power_of_2(args['feat_dim']),
|
|
16
|
+
})
|
|
17
|
+
@triton.jit
|
|
18
|
+
def rms_norm_forward_kernel(
|
|
19
|
+
input_ptr, weight_ptr,
|
|
20
|
+
output_ptr,
|
|
21
|
+
batch_dim, feat_dim, BATCH_BUCKET, FEAT_BUCKET,
|
|
22
|
+
input_batch_stride, input_feat_stride,
|
|
23
|
+
output_batch_stride, output_feat_stride,
|
|
24
|
+
eps,
|
|
25
|
+
scale_by_weight: tl.constexpr,
|
|
26
|
+
BLOCK_SIZE_BATCH: tl.constexpr,
|
|
27
|
+
BLOCK_SIZE_FEAT: tl.constexpr,
|
|
28
|
+
SATURATE_F16: tl.constexpr = False, # the protected fp16 store: finite values clamp to ±65504
|
|
29
|
+
):
|
|
30
|
+
"""RMS normalization forward.
|
|
31
|
+
|
|
32
|
+
input: [batch_dim, feat_dim]
|
|
33
|
+
weight: [feat_dim] (optional)
|
|
34
|
+
output: [batch_dim, feat_dim]
|
|
35
|
+
"""
|
|
36
|
+
batch_pid = tl.program_id(0).to(tl.int64)
|
|
37
|
+
|
|
38
|
+
batch_offset = batch_pid * BLOCK_SIZE_BATCH + tl.arange(0, BLOCK_SIZE_BATCH)
|
|
39
|
+
feat_offset = tl.arange(0, BLOCK_SIZE_FEAT)
|
|
40
|
+
|
|
41
|
+
batch_mask = batch_offset < batch_dim
|
|
42
|
+
feat_mask = feat_offset < feat_dim
|
|
43
|
+
|
|
44
|
+
input_ptr += (input_batch_stride * batch_offset[:, None] +
|
|
45
|
+
input_feat_stride * feat_offset[None, :])
|
|
46
|
+
output_ptr += (output_batch_stride * batch_offset[:, None] +
|
|
47
|
+
output_feat_stride * feat_offset[None, :])
|
|
48
|
+
|
|
49
|
+
inp = tl.load(input_ptr,
|
|
50
|
+
mask=batch_mask[:, None] & feat_mask[None, :]).to(tl.float32)
|
|
51
|
+
inv_rms = tl.rsqrt(tl.sum(inp * inp, axis=1) / feat_dim + eps)
|
|
52
|
+
output = inp * inv_rms[:, None]
|
|
53
|
+
|
|
54
|
+
if scale_by_weight:
|
|
55
|
+
weight = tl.load(weight_ptr + feat_offset, mask=feat_mask)
|
|
56
|
+
output *= weight
|
|
57
|
+
|
|
58
|
+
if SATURATE_F16:
|
|
59
|
+
# The store narrows to fp16: the same protected conversion the dtype
|
|
60
|
+
# engine's cast copy applied when this kernel stored fp32 and the wrap
|
|
61
|
+
# cast the result (copy_kernel's expression) — an fp16 stream with
|
|
62
|
+
# outliers past 65504 (VibeVoice's Qwen2, 2026-09-08) clamps instead
|
|
63
|
+
# of overflowing to inf.
|
|
64
|
+
is_finite = (output == output) & (output != float("inf")) & (output != float("-inf"))
|
|
65
|
+
clamped = tl.minimum(tl.maximum(output, -65504.0), 65504.0)
|
|
66
|
+
output = tl.where(is_finite, clamped, output)
|
|
67
|
+
tl.store(output_ptr, output,
|
|
68
|
+
mask=batch_mask[:, None] & feat_mask[None, :])
|