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
|
@@ -0,0 +1,770 @@
|
|
|
1
|
+
"""Indexing wrappers: gather, scatter, index_select/add/put, embedding, masked fill, where, nonzero, bucketize. Cut out of wrappers.py by family (M2, 2026-10-29), unchanged; shared launch layer and run context stay in wrappers.py."""
|
|
2
|
+
|
|
3
|
+
from __future__ import annotations
|
|
4
|
+
|
|
5
|
+
import os
|
|
6
|
+
import triton
|
|
7
|
+
from nbxtensor.ops.autotune_bucket import bucket_of as _bucket_of
|
|
8
|
+
from nbxtensor.ops import launch_keys as _lk
|
|
9
|
+
from nbxtensor.tensor import NBXTensor
|
|
10
|
+
from nbxtensor.dtype.nbx_dtype import NBXDtype, dtype_size
|
|
11
|
+
from nbxtensor.memory.allocator import DeviceAllocator
|
|
12
|
+
from nbxtensor.backend.runtime import set_device
|
|
13
|
+
from nbxtensor.shape import MAX_NDIM as _NBX_MAX_NDIM, broadcast_shapes, max_linear_offset
|
|
14
|
+
from nbxtensor.dtype.itemsize import canonical_name
|
|
15
|
+
from nbxtensor.dtype.promotion import binary_result, float_math_input
|
|
16
|
+
from nbxtensor.dtype.nbx_dtype import parse_dtype
|
|
17
|
+
from nbxtensor.memory.allocator import DeviceOOMError
|
|
18
|
+
from nbxtensor.backend.capabilities import fa_min_tile as _fa_min_tile
|
|
19
|
+
from nbxtensor.backend.capabilities import reduce_tile_max as _reduce_tile_max
|
|
20
|
+
from nbxtensor.backend.faults import device_fault_buffer, device_fault_code_cached, fault_channel
|
|
21
|
+
from nbxtensor.tensor import _upload_int64_array as _nbx_upload_int64, _saturating_cast as _nbx_saturating_cast
|
|
22
|
+
from nbxtensor.ops._configs import sdpa_block_ceiling as _sdpa_block_ceiling
|
|
23
|
+
from nbxtensor.ops._configs import sdpa_launch_meta as _sdpa_launch_meta
|
|
24
|
+
from nbxtensor.ops._configs import matrix_unit as _matrix_unit, matrix_unit_mm_tile as _matrix_unit_mm_tile
|
|
25
|
+
from nbxtensor.ops._configs import largest_tile_within_smem as _largest_tile_within_smem
|
|
26
|
+
from nbxtensor.backend import launcher as _nbx_launcher
|
|
27
|
+
from nbxtensor.backend.launcher import install as _install_launcher
|
|
28
|
+
from nbxtensor.ops.relu import relu_forward_kernel
|
|
29
|
+
from nbxtensor.ops.silu import silu_forward_kernel
|
|
30
|
+
from nbxtensor.ops.gelu import gelu_forward_kernel
|
|
31
|
+
from nbxtensor.ops.sigmoid import sigmoid_forward_kernel
|
|
32
|
+
from nbxtensor.ops.tanh import tanh_forward_kernel
|
|
33
|
+
from nbxtensor.ops.hardsigmoid import hardsigmoid_forward_kernel
|
|
34
|
+
from nbxtensor.ops.hardswish import hardswish_forward_kernel
|
|
35
|
+
from nbxtensor.ops.leaky_relu import leaky_relu_forward_kernel
|
|
36
|
+
from nbxtensor.ops.elu import elu_forward_kernel
|
|
37
|
+
from nbxtensor.ops.mish import mish_forward_kernel
|
|
38
|
+
from nbxtensor.ops.selu import selu_forward_kernel
|
|
39
|
+
from nbxtensor.ops.neg import neg_forward_kernel
|
|
40
|
+
from nbxtensor.ops.exp import exp_forward_kernel
|
|
41
|
+
from nbxtensor.ops.sin import sin_forward_kernel
|
|
42
|
+
from nbxtensor.ops.cos import cos_forward_kernel
|
|
43
|
+
from nbxtensor.ops.rsqrt import rsqrt_forward_kernel
|
|
44
|
+
from nbxtensor.ops.sqrt import sqrt_forward_kernel
|
|
45
|
+
from nbxtensor.ops.abs import abs_forward_kernel
|
|
46
|
+
from nbxtensor.ops.log import log_forward_kernel
|
|
47
|
+
from nbxtensor.ops.reciprocal import reciprocal_forward_kernel
|
|
48
|
+
from nbxtensor.ops.pow import pow_forward_kernel
|
|
49
|
+
from nbxtensor.ops.clamp import clamp_forward_kernel
|
|
50
|
+
from nbxtensor.ops.erf import erf_forward_kernel
|
|
51
|
+
from nbxtensor.ops.copy import copy_forward_kernel
|
|
52
|
+
from nbxtensor.ops.add import add_forward_kernel, add_scalar_kernel, add_scalar_dev_kernel, add_bias_broadcast_kernel
|
|
53
|
+
from nbxtensor.ops.binary_strided import add_strided_nd_kernel, mul_strided_nd_kernel, sub_strided_nd_kernel, div_strided_nd_kernel, add_scalar_strided_nd_kernel, mul_scalar_strided_nd_kernel
|
|
54
|
+
from nbxtensor.ops.mul import mul_forward_kernel, mul_scalar_kernel, mul_scalar_dev_kernel
|
|
55
|
+
from nbxtensor.ops.div import div_forward_kernel, div_scalar_kernel, div_scalar_dev_kernel, int_div_kernel
|
|
56
|
+
from nbxtensor.ops.sub import sub_forward_kernel, rsub_forward_kernel
|
|
57
|
+
from nbxtensor.ops.where import where_forward_kernel
|
|
58
|
+
from nbxtensor.ops.maximum import maximum_forward_kernel
|
|
59
|
+
from nbxtensor.ops.minimum import minimum_forward_kernel
|
|
60
|
+
from nbxtensor.ops.masked_fill import masked_fill_forward_kernel
|
|
61
|
+
from nbxtensor.ops.gt import gt_forward_kernel, gt_scalar_kernel
|
|
62
|
+
from nbxtensor.ops.ge import ge_forward_kernel, ge_scalar_kernel
|
|
63
|
+
from nbxtensor.ops.lt import lt_forward_kernel, lt_scalar_kernel
|
|
64
|
+
from nbxtensor.ops.eq import eq_forward_kernel, eq_scalar_kernel
|
|
65
|
+
from nbxtensor.ops.ne import ne_forward_kernel, ne_scalar_kernel
|
|
66
|
+
from nbxtensor.ops.layernorm import layer_norm_forward_kernel
|
|
67
|
+
from nbxtensor.ops.rmsnorm import rms_norm_forward_kernel
|
|
68
|
+
from nbxtensor.ops.softmax import softmax_forward_kernel, softmax_forward_looped_kernel
|
|
69
|
+
from nbxtensor.ops.matmul import matmul_kernel, addmm_kernel
|
|
70
|
+
from nbxtensor.ops._autotune_policy import NBX_FORCE_FP32_ACCUM
|
|
71
|
+
from nbxtensor.ops.fused_moe import fused_moe_kernel, silu_and_mul_kernel, silu_mul_split_kernel
|
|
72
|
+
from nbxtensor.ops.dtype_convert import bf16_to_fp16_kernel
|
|
73
|
+
from nbxtensor.ops.embedding import embedding_kernel, EMBEDDING_OOB
|
|
74
|
+
from nbxtensor.ops.sum import sum_forward_kernel
|
|
75
|
+
from nbxtensor.ops.mean import mean_forward_kernel
|
|
76
|
+
from nbxtensor.ops.amax import amax_forward_kernel
|
|
77
|
+
from nbxtensor.ops.glu import glu_forward_kernel
|
|
78
|
+
from nbxtensor.ops.le import le_forward_kernel, le_scalar_kernel
|
|
79
|
+
from nbxtensor.ops.softplus import softplus_forward_kernel
|
|
80
|
+
from nbxtensor.ops.dropout import dropout_inference_kernel
|
|
81
|
+
from nbxtensor.ops.upsample_nearest2d import upsample_nearest2d_kernel
|
|
82
|
+
from nbxtensor.ops.groupnorm import group_norm_forward_kernel
|
|
83
|
+
from nbxtensor.ops.index_select import index_select_kernel, INDEX_SELECT_OOB
|
|
84
|
+
from nbxtensor.ops.triu import triu_kernel, triu_batch_kernel
|
|
85
|
+
from nbxtensor.ops.tril import tril_kernel, tril_batch_kernel
|
|
86
|
+
from nbxtensor.ops.argmax import argmax_kernel_1, argmax_kernel_2, argmax_kernel_inner
|
|
87
|
+
from nbxtensor.ops.argmin import argmin_kernel_1, argmin_kernel_2, argmin_kernel
|
|
88
|
+
from nbxtensor.ops.conv2d import conv2d_forward_kernel
|
|
89
|
+
from nbxtensor.ops.depthwise_conv2d import depthwise_conv2d_kernel
|
|
90
|
+
from nbxtensor.ops.batch_norm import batch_norm_forward_kernel
|
|
91
|
+
from nbxtensor.ops.cumsum import scan_part_sum_kernel, add_base_sum_kernel
|
|
92
|
+
from nbxtensor.ops.scatter_op import scatter_kernel, scatter_add_kernel, scatter_reduce_amax_kernel, scatter_reduce_amin_kernel
|
|
93
|
+
from nbxtensor.ops.gather_op import gather_kernel
|
|
94
|
+
from nbxtensor.ops.avg_pool2d import avg_pool2d_forward_kernel
|
|
95
|
+
from nbxtensor.ops.max_pool2d import max_pool2d_forward_kernel
|
|
96
|
+
from nbxtensor.ops.max_pool3d import max_pool3d_forward_kernel
|
|
97
|
+
from nbxtensor.ops.cross_entropy import cross_entropy_loss_forward_kernel
|
|
98
|
+
from nbxtensor.ops.exp2 import exp2_forward_kernel
|
|
99
|
+
from nbxtensor.ops.tan import tan_forward_kernel
|
|
100
|
+
from nbxtensor.ops.celu import celu_forward_kernel
|
|
101
|
+
from nbxtensor.ops.log_sigmoid import log_sigmoid_forward_kernel
|
|
102
|
+
from nbxtensor.ops.isfinite import isfinite_forward_kernel
|
|
103
|
+
from nbxtensor.ops.isinf import isinf_forward_kernel
|
|
104
|
+
from nbxtensor.ops.isnan import isnan_forward_kernel
|
|
105
|
+
from nbxtensor.ops.nan_to_num import nan_to_num_forward_kernel
|
|
106
|
+
from nbxtensor.ops.threshold import threshold_forward_kernel
|
|
107
|
+
from nbxtensor.ops.floor import floor_forward_kernel, ceil_forward_kernel, round_forward_kernel, trunc_forward_kernel
|
|
108
|
+
from nbxtensor.ops.addcdiv import addcdiv_forward_kernel
|
|
109
|
+
from nbxtensor.ops.addcmul import addcmul_forward_kernel
|
|
110
|
+
from nbxtensor.ops.all_reduce import all_kernel_mid, all_kernel_result, all_kernel_dim
|
|
111
|
+
from nbxtensor.ops.any_reduce import any_kernel_mid, any_kernel_result, any_kernel_dim
|
|
112
|
+
from nbxtensor.ops.bitwise_and import bitwise_and_forward_kernel
|
|
113
|
+
from nbxtensor.ops.bitwise_or import bitwise_or_forward_kernel
|
|
114
|
+
from nbxtensor.ops.bitwise_not import bitwise_not_forward_kernel
|
|
115
|
+
from nbxtensor.ops.logical_and import logical_and_forward_kernel
|
|
116
|
+
from nbxtensor.ops.logical_or import logical_or_forward_kernel
|
|
117
|
+
from nbxtensor.ops.logical_not import logical_not_forward_kernel
|
|
118
|
+
from nbxtensor.ops.lerp import lerp_tensor_forward_kernel, lerp_scalar_head_kernel, lerp_scalar_tail_kernel
|
|
119
|
+
from nbxtensor.ops.dot_op import dot_kernel_small, dot_kernel_partial, dot_kernel_reduce
|
|
120
|
+
from nbxtensor.ops.flip_op import flip_1d_kernel, flip_strided_kernel
|
|
121
|
+
from nbxtensor.ops.cat_op import cat_copy_kernel_4
|
|
122
|
+
from nbxtensor.ops.fill_op import fill_kernel
|
|
123
|
+
from nbxtensor.ops.arange_op import arange_kernel
|
|
124
|
+
from nbxtensor.ops.prod import prod_kernel_mid, prod_kernel_result, prod_kernel
|
|
125
|
+
from nbxtensor.ops.min_reduce import min_kernel_mid, min_kernel_result, min_kernel
|
|
126
|
+
from nbxtensor.ops.max_reduce import max_kernel_mid, max_kernel_result, max_kernel
|
|
127
|
+
from nbxtensor.ops.baddbmm_op import baddbmm_kernel
|
|
128
|
+
from nbxtensor.ops.addmv_op import addmv_kernel
|
|
129
|
+
from nbxtensor.ops.mse_loss import mse_loss_partial_kernel, mse_loss_reduce_kernel, mse_loss_none_kernel
|
|
130
|
+
from nbxtensor.ops.nllloss import nll_loss_forward_kernel
|
|
131
|
+
from nbxtensor.ops.std import std_map_kernel, std_reduce_kernel, std_dim_kernel
|
|
132
|
+
from nbxtensor.ops.var import var_kernel_1, var_kernel_2, var_welford_kernel
|
|
133
|
+
from nbxtensor.ops.index_add import index_add_gather_kernel
|
|
134
|
+
from nbxtensor.ops.index_put_op import index_put_kernel, INDEX_PUT_OOB
|
|
135
|
+
from nbxtensor.ops.sort_op import (radix_sort_histogram_kernel,
|
|
136
|
+
radix_sort_scatter_kernel,
|
|
137
|
+
radix_sort_tile_counts_kernel,
|
|
138
|
+
radix_sort_tile_prefix_kernel)
|
|
139
|
+
from nbxtensor.ops.rope import rope_forward_kernel
|
|
140
|
+
from nbxtensor.ops.pixel_shuffle import (
|
|
141
|
+
pixel_shuffle_kernel,
|
|
142
|
+
pixel_shuffle_broadcast_aware_kernel,
|
|
143
|
+
)
|
|
144
|
+
from nbxtensor.ops.pixel_unshuffle import pixel_unshuffle_kernel
|
|
145
|
+
from nbxtensor.ops.upsample_bilinear2d import upsample_bilinear2d_kernel
|
|
146
|
+
from nbxtensor.ops.adaptive_avg_pool2d import adaptive_avg_pool2d_kernel
|
|
147
|
+
from nbxtensor.ops.conv_depthwise2d import conv_depthwise2d_kernel
|
|
148
|
+
from nbxtensor.ops.scaled_softmax import scaled_softmax_kernel
|
|
149
|
+
from nbxtensor.ops.vector_norm import (
|
|
150
|
+
l2_norm_kernel, l1_norm_kernel, linf_norm_kernel, l0_norm_kernel, lp_norm_kernel,
|
|
151
|
+
l2_norm_pass1_kernel, l2_norm_pass2_kernel,
|
|
152
|
+
linf_norm_pass1_kernel, linf_norm_pass2_kernel,
|
|
153
|
+
)
|
|
154
|
+
from nbxtensor.ops.addr import addr_kernel
|
|
155
|
+
from nbxtensor.ops.logical_xor import logical_xor_kernel
|
|
156
|
+
from nbxtensor.ops.bitwise_left_shift import bitwise_left_shift_kernel
|
|
157
|
+
from nbxtensor.ops.bitwise_right_shift import bitwise_right_shift_kernel
|
|
158
|
+
from nbxtensor.ops.rand_op import rand_kernel, randn_kernel
|
|
159
|
+
from nbxtensor.ops.clamp_min import clamp_min_forward_kernel
|
|
160
|
+
from nbxtensor.ops.conv_transpose2d import conv_transpose2d_kernel
|
|
161
|
+
from nbxtensor.ops.cummax import scan_part_max_kernel
|
|
162
|
+
from nbxtensor.ops.cummin import scan_part_min_kernel
|
|
163
|
+
from nbxtensor.ops.count_nonzero import count_nonzero_kernel
|
|
164
|
+
from nbxtensor.ops.trace_op import trace_kernel
|
|
165
|
+
from nbxtensor.ops.linspace_op import linspace_kernel
|
|
166
|
+
from nbxtensor.ops.full_op import full_kernel
|
|
167
|
+
from nbxtensor.ops.stack_op import stack_copy_kernel
|
|
168
|
+
from nbxtensor.ops.repeat_op import repeat_1d_kernel
|
|
169
|
+
from nbxtensor.ops.upsample_nearest3d import upsample_nearest3d_kernel
|
|
170
|
+
from nbxtensor.ops.weight_norm import weight_norm_kernel_first, weight_norm_kernel_last
|
|
171
|
+
from nbxtensor.ops.repeat_interleave import repeat_interleave_tensor_kernel
|
|
172
|
+
from nbxtensor.ops.elementwise_launch import ew_meta as _ew_meta, ew_grid as _ew_grid
|
|
173
|
+
from nbxtensor.ops import tiling_sizes as _ts
|
|
174
|
+
import math
|
|
175
|
+
from nbxtensor.ops import elementwise_wrappers as _ew
|
|
176
|
+
from nbxtensor.ops import reduction_wrappers as _red
|
|
177
|
+
from nbxtensor.ops import wrappers as _core
|
|
178
|
+
|
|
179
|
+
|
|
180
|
+
def where_wrapper(cond, x, y) :
|
|
181
|
+
# Broadcast all three to same shape
|
|
182
|
+
if cond.shape != x.shape or x.shape != y.shape:
|
|
183
|
+
out_shape = broadcast_shapes(cond.shape, x.shape, y.shape)
|
|
184
|
+
cond = cond.expand(out_shape).contiguous()
|
|
185
|
+
x = x.expand(out_shape).contiguous()
|
|
186
|
+
y = y.expand(out_shape).contiguous()
|
|
187
|
+
else:
|
|
188
|
+
cond, x, y = cond.contiguous(), x.contiguous(), y.contiguous()
|
|
189
|
+
output = NBXTensor.empty_like(x)
|
|
190
|
+
set_device(cond)
|
|
191
|
+
where_forward_kernel[_ew._1d_grid(x.numel())](cond, x, y, output, x.numel(), **_ew_meta(x.numel()))
|
|
192
|
+
return output
|
|
193
|
+
|
|
194
|
+
|
|
195
|
+
def masked_fill(x, mask, value: float) :
|
|
196
|
+
# Broadcast mask to match x shape
|
|
197
|
+
if x.shape != mask.shape:
|
|
198
|
+
out_shape = broadcast_shapes(x.shape, mask.shape)
|
|
199
|
+
x = x.expand(out_shape).contiguous()
|
|
200
|
+
mask = mask.expand(out_shape).contiguous()
|
|
201
|
+
else:
|
|
202
|
+
x, mask = x.contiguous(), mask.contiguous()
|
|
203
|
+
value = _core._to_scalar(value) if isinstance(value, NBXTensor) else value
|
|
204
|
+
output = NBXTensor.empty_like(x)
|
|
205
|
+
set_device(x)
|
|
206
|
+
masked_fill_forward_kernel[_ew._1d_grid(x.numel())](
|
|
207
|
+
x, mask, output, x.numel(), float(value),
|
|
208
|
+
**_ew_meta(x.numel()))
|
|
209
|
+
return output
|
|
210
|
+
|
|
211
|
+
|
|
212
|
+
def is_nonzero_wrapper(x):
|
|
213
|
+
"""aten::is_nonzero — returns the scalar truthiness of a 1-element tensor.
|
|
214
|
+
|
|
215
|
+
PyTorch's native impl returns a Python bool, but traced graphs feed
|
|
216
|
+
the output back through tensor ops, so we return a 0-d NBXTensor
|
|
217
|
+
(uint8) to match the graph's expected dtype surface.
|
|
218
|
+
"""
|
|
219
|
+
import numpy as np
|
|
220
|
+
val = x.item()
|
|
221
|
+
return NBXTensor.from_numpy(np.array(bool(val), dtype=np.uint8))
|
|
222
|
+
|
|
223
|
+
|
|
224
|
+
# ===========================================================================
|
|
225
|
+
# EMBEDDING WRAPPER
|
|
226
|
+
# ===========================================================================
|
|
227
|
+
|
|
228
|
+
def embedding(weight, indices, padding_idx=-1, **kwargs) :
|
|
229
|
+
"""Embedding lookup: output[i] = weight[indices[i]].
|
|
230
|
+
|
|
231
|
+
Indices must be integer. Diffusion timestep embeddings sometimes arrive
|
|
232
|
+
as fp32 (timestep is a scalar float in the scheduler); cast to int64 so
|
|
233
|
+
the Triton kernel's pointer arithmetic (`weight_ptr += row_idx * N`)
|
|
234
|
+
sees an integer operand.
|
|
235
|
+
"""
|
|
236
|
+
if indices.dtype not in (NBXDtype.int64, NBXDtype.int32, NBXDtype.int16,
|
|
237
|
+
NBXDtype.int8, NBXDtype.uint8, NBXDtype.bool_):
|
|
238
|
+
indices = indices.to(NBXDtype.int64)
|
|
239
|
+
indices = indices.contiguous()
|
|
240
|
+
weight = weight.contiguous()
|
|
241
|
+
M = indices.numel()
|
|
242
|
+
N = weight.shape[-1]
|
|
243
|
+
BLOCK_SIZE = triton.next_power_of_2(N)
|
|
244
|
+
|
|
245
|
+
# Allocate output on same device as weight (all args must be same device)
|
|
246
|
+
dev = f"cuda:{weight._device_idx}" if hasattr(weight, '_device_idx') else 'cuda'
|
|
247
|
+
# Move indices to weight's device if needed
|
|
248
|
+
if hasattr(indices, '_device_idx') and hasattr(weight, '_device_idx') and indices._device_idx != weight._device_idx:
|
|
249
|
+
# Cross-device: copy indices to weight's device
|
|
250
|
+
new_indices = NBXTensor.empty(indices._shape, indices._dtype, dev)
|
|
251
|
+
DeviceAllocator.memcpy(new_indices.data_ptr(), indices.data_ptr(), indices._nbytes)
|
|
252
|
+
indices = new_indices
|
|
253
|
+
output = NBXTensor.empty((*indices.shape, N), dtype=weight.nbx_dtype if hasattr(weight, 'nbx_dtype') else weight.dtype, device=dev)
|
|
254
|
+
set_device(weight)
|
|
255
|
+
# weight.shape[0] = the id bound the kernel traps on (OOB parity with torch).
|
|
256
|
+
embedding_kernel[M,](
|
|
257
|
+
output, indices, weight, weight.shape[0],
|
|
258
|
+
*fault_channel(EMBEDDING_OOB, output),
|
|
259
|
+
N, BLOCK_SIZE)
|
|
260
|
+
return output
|
|
261
|
+
|
|
262
|
+
|
|
263
|
+
def index_select_wrapper(x, dim: int, index) :
|
|
264
|
+
"""Index select along dimension. Wrapper from FlagGems."""
|
|
265
|
+
# aten::index / aten::index_select REQUIRE integer indices. The triton path
|
|
266
|
+
# can present an integer-VALUED index tagged float32 (e.g. the whisper
|
|
267
|
+
# decoder position_ids reaching aten::index via _meta_index). The kernel
|
|
268
|
+
# computes a pointer offset `inp + (rows*N + indices)`; a float `indices`
|
|
269
|
+
# makes that pointer arithmetic illegal (Triton compile error
|
|
270
|
+
# "pointer<fp16> and float32"). Enforce the integer contract at this single
|
|
271
|
+
# choke point both modes funnel through — symmetric across triton /
|
|
272
|
+
# triton_sequential (R30), and matches torch (index_select needs a Long).
|
|
273
|
+
if hasattr(index, "is_floating_point") and index.is_floating_point():
|
|
274
|
+
index = index.to(NBXDtype.int64)
|
|
275
|
+
# Complex tensors: the kernel copies one real value per element, so it
|
|
276
|
+
# mishandles the interleaved real/imag storage of complex64/128 (reads half
|
|
277
|
+
# the bytes / wrong stride -> garbage, e.g. the Wan rotary-embedding freqs
|
|
278
|
+
# constant indexed per grid axis). Route through the real view: view_as_real
|
|
279
|
+
# appends the [.,2] real/imag axis AFTER all original dims, so the select
|
|
280
|
+
# `dim` (resolved on the complex rank) indexes the same axis untouched.
|
|
281
|
+
if x.is_complex():
|
|
282
|
+
ndc = x.ndim
|
|
283
|
+
dd = dim % ndc
|
|
284
|
+
outr = index_select_wrapper(x.view_as_real(), dd, index)
|
|
285
|
+
return outr.view_as_complex()
|
|
286
|
+
assert dim >= -x.ndim and dim < x.ndim
|
|
287
|
+
if index.ndim == 0:
|
|
288
|
+
index = index.unsqueeze(0)
|
|
289
|
+
dim = dim % x.ndim
|
|
290
|
+
inp_shape = list(x.shape)
|
|
291
|
+
index_len = index.numel()
|
|
292
|
+
|
|
293
|
+
# A gather along a middle axis of a contiguous input reads it as (outer, N, inner) and
|
|
294
|
+
# writes the output in its final layout: no movedim copy, no permute copy (the copy lever,
|
|
295
|
+
# 2026-09-07). A non-contiguous input is materialised once first — the line that justifies
|
|
296
|
+
# it: the kernels index a flat buffer.
|
|
297
|
+
if dim != x.ndim - 1:
|
|
298
|
+
x = x.contiguous()
|
|
299
|
+
N = inp_shape[dim]
|
|
300
|
+
outer = 1
|
|
301
|
+
for d_ in inp_shape[:dim]:
|
|
302
|
+
outer *= d_
|
|
303
|
+
inner = 1
|
|
304
|
+
for d_ in inp_shape[dim + 1:]:
|
|
305
|
+
inner *= d_
|
|
306
|
+
out_shape = list(inp_shape); out_shape[dim] = index_len
|
|
307
|
+
out = NBXTensor.empty(out_shape, dtype=x.dtype, device=x.device)
|
|
308
|
+
total = outer * index_len * inner
|
|
309
|
+
if total > 0:
|
|
310
|
+
from nbxtensor.ops.index_select import index_select_mid_kernel
|
|
311
|
+
set_device(x)
|
|
312
|
+
_m = _ew_meta(total)
|
|
313
|
+
index_select_mid_kernel[_ew_grid(total, _m)](
|
|
314
|
+
x, out, outer, N, inner, index, index_len,
|
|
315
|
+
*fault_channel(INDEX_SELECT_OOB, out),
|
|
316
|
+
BLOCK=_m['BLOCK_SIZE'], num_warps=_m['num_warps'])
|
|
317
|
+
return out
|
|
318
|
+
x = x.contiguous()
|
|
319
|
+
N = inp_shape[dim]
|
|
320
|
+
M = x.numel() // N
|
|
321
|
+
out_shape = list(x.shape)
|
|
322
|
+
out_shape[-1] = index_len
|
|
323
|
+
out = NBXTensor.empty(out_shape, dtype=x.dtype, device=x.device)
|
|
324
|
+
# Diagnostic (NBX_INDEX_SELECT_SENTINEL=1, §8): pre-fill the output with a
|
|
325
|
+
# sentinel and count what the kernel leaves unwritten — the kernel stores
|
|
326
|
+
# under `index_valid_mask`, so an out-of-range index leaves its output
|
|
327
|
+
# element as whatever the allocation held (pool gate 2026-09-02).
|
|
328
|
+
_sentinel = os.environ.get("NBX_INDEX_SELECT_SENTINEL") == "1"
|
|
329
|
+
if _sentinel:
|
|
330
|
+
out.fill_(float("nan") if x.dtype in (NBXDtype.float16, NBXDtype.float32, NBXDtype.bfloat16) else -7777)
|
|
331
|
+
|
|
332
|
+
BLOCK_M = min(64, triton.next_power_of_2(M))
|
|
333
|
+
BLOCK_N = min(64, triton.next_power_of_2(index_len))
|
|
334
|
+
grid = (triton.cdiv(M, BLOCK_M), triton.cdiv(index_len, BLOCK_N))
|
|
335
|
+
set_device(x)
|
|
336
|
+
index_select_kernel[grid](
|
|
337
|
+
x, out, M, N, index, index_len,
|
|
338
|
+
*fault_channel(INDEX_SELECT_OOB, out),
|
|
339
|
+
BLOCK_M, BLOCK_N)
|
|
340
|
+
if _sentinel:
|
|
341
|
+
import numpy as _np
|
|
342
|
+
_o = out.numpy()
|
|
343
|
+
_unwritten = int(_np.isnan(_o).sum()) if _o.dtype.kind == "f" else int((_o == -7777).sum())
|
|
344
|
+
if _unwritten:
|
|
345
|
+
_ix = index.numpy()
|
|
346
|
+
print(f"[INDEX_SELECT_SENTINEL] {_unwritten}/{_o.size} output elements UNWRITTEN "
|
|
347
|
+
f"(index dtype {index.dtype} len {index_len} min {int(_ix.min())} max {int(_ix.max())} N={N} M={M})", flush=True)
|
|
348
|
+
|
|
349
|
+
return out
|
|
350
|
+
|
|
351
|
+
|
|
352
|
+
def gather_wrapper(input, dim: int, index) :
|
|
353
|
+
"""Gather along dimension."""
|
|
354
|
+
input = input.contiguous()
|
|
355
|
+
index = index.contiguous()
|
|
356
|
+
dim = dim % input.ndim
|
|
357
|
+
|
|
358
|
+
# Decompose into (outer, dim, inner) strides
|
|
359
|
+
outer_size = 1
|
|
360
|
+
for i in range(dim):
|
|
361
|
+
outer_size *= input.shape[i]
|
|
362
|
+
inner_size = 1
|
|
363
|
+
for i in range(dim + 1, input.ndim):
|
|
364
|
+
inner_size *= input.shape[i]
|
|
365
|
+
dim_size = index.shape[dim]
|
|
366
|
+
|
|
367
|
+
out = NBXTensor.empty_like(index, dtype=input.dtype)
|
|
368
|
+
N = index.numel()
|
|
369
|
+
|
|
370
|
+
# Pre-compute strides for the kernel.
|
|
371
|
+
# gather_kernel's outer_idx is a ROW-MAJOR FLATTENING of every dim before
|
|
372
|
+
# `dim` (range [0, outer_size)). For a contiguous tensor that whole outer
|
|
373
|
+
# block collapses to a single stride = product of the sizes from `dim`
|
|
374
|
+
# onward = shape[dim] * inner_size (== stride(dim-1)). Using stride(0) is the
|
|
375
|
+
# stride of dim 0 = prod(shape[1:]); it equals the correct value ONLY when
|
|
376
|
+
# there is exactly one outer dim (dim == 1). With >= 2 outer dims it
|
|
377
|
+
# over-counts by prod(shape[1:dim]) and drives `outer_idx * stride` past the
|
|
378
|
+
# buffer end -> destructive OOB store/load -> CUDA 700 at the next sync
|
|
379
|
+
# (e.g. chatterbox s3gen T5 rel-pos gather [1,8,180,359] idx dim=3). input,
|
|
380
|
+
# index and out are all contiguous here, so the flat collapse is exact.
|
|
381
|
+
inp_dim_stride = input.stride(dim)
|
|
382
|
+
idx_stride_outer = dim_size * inner_size if dim > 0 else 0
|
|
383
|
+
idx_stride_dim = index.stride(dim)
|
|
384
|
+
idx_stride_inner = index.stride(-1) if dim < input.ndim - 1 else 0
|
|
385
|
+
inp_stride_outer = input.shape[dim] * inner_size if dim > 0 else 0
|
|
386
|
+
inp_stride_inner = input.stride(-1) if dim < input.ndim - 1 else 0
|
|
387
|
+
out_stride_outer = dim_size * inner_size if dim > 0 else 0
|
|
388
|
+
out_stride_dim = out.stride(dim)
|
|
389
|
+
out_stride_inner = out.stride(-1) if dim < input.ndim - 1 else 0
|
|
390
|
+
|
|
391
|
+
set_device(input)
|
|
392
|
+
gather_kernel[_ew._1d_grid(N)](
|
|
393
|
+
input, index, out,
|
|
394
|
+
N,
|
|
395
|
+
inp_dim_stride,
|
|
396
|
+
idx_stride_outer, idx_stride_dim, idx_stride_inner,
|
|
397
|
+
inp_stride_outer, inp_stride_inner,
|
|
398
|
+
out_stride_outer, out_stride_dim, out_stride_inner,
|
|
399
|
+
outer_size, dim_size, inner_size,
|
|
400
|
+
**_ew_meta(N),
|
|
401
|
+
)
|
|
402
|
+
return out
|
|
403
|
+
|
|
404
|
+
|
|
405
|
+
def masked_scatter_wrapper(x, mask, source) :
|
|
406
|
+
"""aten::masked_scatter — fill mask-True elements of `x` from the
|
|
407
|
+
flat `source` buffer in row-major order (torch semantics).
|
|
408
|
+
|
|
409
|
+
R33-pure composition over existing Triton bricks (no dedicated scan
|
|
410
|
+
kernel): the running index of each True element is the inclusive
|
|
411
|
+
cumsum of the flat mask minus one; False lanes gather a clamped
|
|
412
|
+
dummy and are discarded by the final `where`.
|
|
413
|
+
|
|
414
|
+
idx = cumsum(mask.float32) - 1 (exclusive True count)
|
|
415
|
+
gathered = source_flat[clamp(idx, 0, n_src-1).int64]
|
|
416
|
+
out = where(mask, gathered, x)
|
|
417
|
+
|
|
418
|
+
The count arithmetic runs in float32 (the well-trodden elementwise
|
|
419
|
+
path) — exact for counts < 2^24, guarded below; a dedicated scan
|
|
420
|
+
kernel is the extension past that ceiling (and the optimisation path
|
|
421
|
+
if this op ever lands on a hot loop beyond the twice-per-forward
|
|
422
|
+
splice — the flat single-row cumsum walks its partial-sum chain
|
|
423
|
+
host-side, numel/4096 launches).
|
|
424
|
+
|
|
425
|
+
First consumer: the MiniCPM-o llm.model graph (in-graph bool-mask
|
|
426
|
+
modality splice — expanded [B, S, H] mask, flattened [n, H] source).
|
|
427
|
+
v1 contract: `mask` must match `x`'s shape exactly (the traced form);
|
|
428
|
+
broadcasting masks raise a named error.
|
|
429
|
+
"""
|
|
430
|
+
if tuple(mask.shape) != tuple(x.shape):
|
|
431
|
+
raise RuntimeError(
|
|
432
|
+
"ZERO FALLBACK: masked_scatter v1 requires mask.shape == "
|
|
433
|
+
f"self.shape (got mask {tuple(mask.shape)} vs self "
|
|
434
|
+
f"{tuple(x.shape)}); broadcast-mask support lands with its "
|
|
435
|
+
"first consumer graph.")
|
|
436
|
+
n = x.numel()
|
|
437
|
+
n_src = source.numel()
|
|
438
|
+
x_flat = x.contiguous().reshape(n)
|
|
439
|
+
if n_src == 0:
|
|
440
|
+
# torch semantics: valid only when the mask has no True element;
|
|
441
|
+
# the splice flows feed a 1-row stub instead, so an empty source
|
|
442
|
+
# here means an all-False mask — the input passes through.
|
|
443
|
+
return x_flat.reshape(*x.shape)
|
|
444
|
+
if n >= (1 << 24):
|
|
445
|
+
raise RuntimeError(
|
|
446
|
+
"ZERO FALLBACK: masked_scatter float32 count path is exact "
|
|
447
|
+
f"only below 2^24 elements (got {n}); a dedicated int scan "
|
|
448
|
+
"kernel is the extension for larger splices.")
|
|
449
|
+
mask_flat = mask.contiguous().reshape(n)
|
|
450
|
+
src_flat = source.contiguous().reshape(n_src)
|
|
451
|
+
if src_flat.dtype != x_flat.dtype:
|
|
452
|
+
src_flat = src_flat.to(x_flat.dtype)
|
|
453
|
+
idx = _red.cumsum_wrapper(mask_flat, dim=0, dtype=NBXDtype.float32)
|
|
454
|
+
idx = _ew.sub(idx, 1.0)
|
|
455
|
+
idx = _ew.clamp(idx, min_val=0.0, max_val=float(n_src - 1))
|
|
456
|
+
gathered = gather_wrapper(src_flat, 0, idx.to(NBXDtype.int64))
|
|
457
|
+
out = where_wrapper(mask_flat, gathered, x_flat)
|
|
458
|
+
return out.reshape(*x.shape)
|
|
459
|
+
|
|
460
|
+
|
|
461
|
+
# ---------------------------------------------------------------------------
|
|
462
|
+
# Scatter / Scatter Add
|
|
463
|
+
# ---------------------------------------------------------------------------
|
|
464
|
+
|
|
465
|
+
def scatter_wrapper(input, dim: int, index,
|
|
466
|
+
src) :
|
|
467
|
+
"""Scatter: out[outer][index_val][inner] = src[outer][dim][inner]."""
|
|
468
|
+
input = input.contiguous()
|
|
469
|
+
src = src.contiguous()
|
|
470
|
+
index = index.contiguous()
|
|
471
|
+
dim = dim % input.ndim
|
|
472
|
+
|
|
473
|
+
out = input.clone()
|
|
474
|
+
N = index.numel()
|
|
475
|
+
|
|
476
|
+
outer_size = 1
|
|
477
|
+
for i in range(dim):
|
|
478
|
+
outer_size *= index.shape[i]
|
|
479
|
+
inner_size = 1
|
|
480
|
+
for i in range(dim + 1, index.ndim):
|
|
481
|
+
inner_size *= index.shape[i]
|
|
482
|
+
dim_size = index.shape[dim]
|
|
483
|
+
|
|
484
|
+
set_device(src)
|
|
485
|
+
scatter_kernel[_ew._1d_grid(N)](
|
|
486
|
+
src, index, out,
|
|
487
|
+
N,
|
|
488
|
+
out.stride(dim),
|
|
489
|
+
src.stride(0) if dim > 0 else 0, src.stride(dim), src.stride(-1) if dim < src.ndim - 1 else 0,
|
|
490
|
+
index.stride(0) if dim > 0 else 0, index.stride(dim), index.stride(-1) if dim < index.ndim - 1 else 0,
|
|
491
|
+
out.stride(0) if dim > 0 else 0, out.stride(-1) if dim < out.ndim - 1 else 0,
|
|
492
|
+
outer_size, dim_size, inner_size,
|
|
493
|
+
**_ew_meta(N),
|
|
494
|
+
)
|
|
495
|
+
return out
|
|
496
|
+
|
|
497
|
+
|
|
498
|
+
def scatter_add_wrapper(input, dim: int, index,
|
|
499
|
+
src) :
|
|
500
|
+
"""Scatter add: out[outer][index_val][inner] += src[outer][dim][inner]."""
|
|
501
|
+
input = input.contiguous()
|
|
502
|
+
src = src.contiguous()
|
|
503
|
+
index = index.contiguous()
|
|
504
|
+
dim = dim % input.ndim
|
|
505
|
+
|
|
506
|
+
out = input.clone()
|
|
507
|
+
N = index.numel()
|
|
508
|
+
|
|
509
|
+
outer_size = 1
|
|
510
|
+
for i in range(dim):
|
|
511
|
+
outer_size *= index.shape[i]
|
|
512
|
+
inner_size = 1
|
|
513
|
+
for i in range(dim + 1, index.ndim):
|
|
514
|
+
inner_size *= index.shape[i]
|
|
515
|
+
dim_size = index.shape[dim]
|
|
516
|
+
|
|
517
|
+
set_device(src)
|
|
518
|
+
scatter_add_kernel[_ew._1d_grid(N)](
|
|
519
|
+
src, index, out,
|
|
520
|
+
N,
|
|
521
|
+
out.stride(dim),
|
|
522
|
+
src.stride(0) if dim > 0 else 0, src.stride(dim), src.stride(-1) if dim < src.ndim - 1 else 0,
|
|
523
|
+
index.stride(0) if dim > 0 else 0, index.stride(dim), index.stride(-1) if dim < index.ndim - 1 else 0,
|
|
524
|
+
out.stride(0) if dim > 0 else 0, out.stride(-1) if dim < out.ndim - 1 else 0,
|
|
525
|
+
outer_size, dim_size, inner_size,
|
|
526
|
+
**_ew_meta(N),
|
|
527
|
+
)
|
|
528
|
+
return out
|
|
529
|
+
|
|
530
|
+
|
|
531
|
+
# ---------------------------------------------------------------------------
|
|
532
|
+
# Index Add
|
|
533
|
+
# ---------------------------------------------------------------------------
|
|
534
|
+
|
|
535
|
+
def index_add_wrapper(x, dim: int, index,
|
|
536
|
+
source, alpha: float = 1.0) :
|
|
537
|
+
"""Index add: out[..., index[i], ...] += alpha * source[..., i, ...]."""
|
|
538
|
+
x = x.contiguous()
|
|
539
|
+
source = source.contiguous()
|
|
540
|
+
index = index.contiguous()
|
|
541
|
+
dim = dim % x.ndim
|
|
542
|
+
|
|
543
|
+
outer_size = 1
|
|
544
|
+
for i in range(dim):
|
|
545
|
+
outer_size *= source.shape[i]
|
|
546
|
+
inner_size = 1
|
|
547
|
+
for i in range(dim + 1, source.ndim):
|
|
548
|
+
inner_size *= source.shape[i]
|
|
549
|
+
dim_size = source.shape[dim]
|
|
550
|
+
inp_shape_dim = x.shape[dim]
|
|
551
|
+
|
|
552
|
+
if x.numel() == 0 or dim_size == 0: # nothing to add -> out = x
|
|
553
|
+
return x.clone()
|
|
554
|
+
|
|
555
|
+
out = NBXTensor.empty_like(x)
|
|
556
|
+
|
|
557
|
+
src_stride_outer = source.stride(0) if dim > 0 else 0
|
|
558
|
+
src_stride_dim = source.stride(dim)
|
|
559
|
+
src_stride_inner = source.stride(-1) if dim < source.ndim - 1 else 0
|
|
560
|
+
|
|
561
|
+
# Deterministic output-owner gather: one program per
|
|
562
|
+
# (outer, dest, inner-tile) output cell, ascending-j sequential
|
|
563
|
+
# fp32 fold, zero atomics, no sort dependency. See
|
|
564
|
+
# ops/index_add.py for the rationale and state-of-the-art notes.
|
|
565
|
+
_inner = _ew_meta(inner_size)
|
|
566
|
+
n_inner_tiles = (inner_size + _inner['BLOCK_SIZE'] - 1) // _inner['BLOCK_SIZE']
|
|
567
|
+
grid = (outer_size * inp_shape_dim * n_inner_tiles,)
|
|
568
|
+
set_device(x)
|
|
569
|
+
index_add_gather_kernel[grid](
|
|
570
|
+
x, source, index, out,
|
|
571
|
+
dim_size, inp_shape_dim, inner_size, alpha,
|
|
572
|
+
src_stride_outer, src_stride_dim, src_stride_inner,
|
|
573
|
+
INNER_BLOCK=_inner['BLOCK_SIZE'], num_warps=_inner['num_warps'],
|
|
574
|
+
)
|
|
575
|
+
return out
|
|
576
|
+
|
|
577
|
+
|
|
578
|
+
# ---------------------------------------------------------------------------
|
|
579
|
+
# Index Put
|
|
580
|
+
# ---------------------------------------------------------------------------
|
|
581
|
+
|
|
582
|
+
def nonzero_1d_wrapper(mask, expected=None) -> "NBXTensor":
|
|
583
|
+
"""Ascending int64 indices of the True elements of a 1-D mask.
|
|
584
|
+
|
|
585
|
+
Deterministic single-program compaction (kernels/ops/nonzero.py) —
|
|
586
|
+
no atomics, indices come out in order, bit-reproducible. The output
|
|
587
|
+
length is data-dependent: worst-case buffer + one count readback
|
|
588
|
+
(the host-boundary sync every data-dependent-shape op requires),
|
|
589
|
+
narrowed to [n] (a leading-dim narrow of a contiguous buffer stays
|
|
590
|
+
contiguous — no guard needed).
|
|
591
|
+
|
|
592
|
+
`expected`: the count the caller's own contract states (a masked scatter's values have exactly one row per True
|
|
593
|
+
element). A census shadow launches nothing and reads no value, so it takes that count as the extent — the read it
|
|
594
|
+
replaced returned a count the shadow never computed (Qwen3-VL's DeepStack write, `values` [196, 2048], formed one
|
|
595
|
+
row: every Qwen3-VL coverage cell rc 1 since the shadow existed). A real run reads the count and refuses by name a
|
|
596
|
+
mask that disagrees with the caller's contract."""
|
|
597
|
+
from nbxtensor.ops.nonzero import nonzero_1d_kernel
|
|
598
|
+
assert mask.ndim == 1, f"nonzero_1d expects 1-D, got {mask.ndim}-D"
|
|
599
|
+
m = mask.contiguous()
|
|
600
|
+
n = int(m.shape[0])
|
|
601
|
+
if expected is not None:
|
|
602
|
+
from nbxtensor.ops.launch_observer import launch_observer
|
|
603
|
+
if launch_observer().active():
|
|
604
|
+
return NBXTensor.empty([int(expected)], dtype=NBXDtype.int64, device=m.device)
|
|
605
|
+
out = NBXTensor.empty([max(n, 1)], dtype=NBXDtype.int64, device=m.device)
|
|
606
|
+
count = NBXTensor.zeros([1], dtype=NBXDtype.int32, device=m.device)
|
|
607
|
+
if n > 0:
|
|
608
|
+
BLOCK = min(1024, triton.next_power_of_2(n))
|
|
609
|
+
set_device(m)
|
|
610
|
+
nonzero_1d_kernel[(1,)](m, out, count, n, BLOCK)
|
|
611
|
+
k = int(count.numpy()[0])
|
|
612
|
+
else:
|
|
613
|
+
k = 0
|
|
614
|
+
if expected is not None and k != int(expected):
|
|
615
|
+
raise RuntimeError(f"nonzero: the mask holds {k} True elements and the caller's contract states {int(expected)} "
|
|
616
|
+
f"(a masked scatter's values have one row per True element)")
|
|
617
|
+
return out[:k]
|
|
618
|
+
|
|
619
|
+
|
|
620
|
+
def bucketize_wrapper(x, boundaries, out_int32: bool = False,
|
|
621
|
+
right: bool = False) -> "NBXTensor":
|
|
622
|
+
"""aten::bucketize.Tensor — per-element boundary index (linear scan
|
|
623
|
+
kernel, kernels/ops/bucketize.py). Boundary vectors on this path
|
|
624
|
+
are decode-control scale (one per temporal frame / packed segment),
|
|
625
|
+
so the O(n_boundaries) scan is exact, branch-free and
|
|
626
|
+
bit-reproducible."""
|
|
627
|
+
from nbxtensor.ops.bucketize import bucketize_kernel
|
|
628
|
+
xc = x.contiguous()
|
|
629
|
+
bc = boundaries.contiguous()
|
|
630
|
+
n = 1
|
|
631
|
+
for d in xc.shape:
|
|
632
|
+
n *= int(d)
|
|
633
|
+
nb = int(bc.shape[0]) if bc.ndim >= 1 else 1
|
|
634
|
+
if nb == 0:
|
|
635
|
+
# No boundary is below any element: every index is 0, and the empty
|
|
636
|
+
# boundary vector has no buffer to bind (a one-image Qwen3-VL request
|
|
637
|
+
# buckets over cu_seqlens[1:-1], which is empty).
|
|
638
|
+
out = NBXTensor.zeros(list(xc.shape) or [1], dtype=NBXDtype.int64,
|
|
639
|
+
device=xc.device)
|
|
640
|
+
return out.to(NBXDtype.int32) if out_int32 else out
|
|
641
|
+
out = NBXTensor.empty(list(xc.shape) or [1], dtype=NBXDtype.int64,
|
|
642
|
+
device=xc.device)
|
|
643
|
+
if n > 0:
|
|
644
|
+
BLOCK = min(1024, triton.next_power_of_2(n))
|
|
645
|
+
grid = ((n + BLOCK - 1) // BLOCK,)
|
|
646
|
+
set_device(xc)
|
|
647
|
+
bucketize_kernel[grid](xc, bc, out, n, nb,
|
|
648
|
+
RIGHT=bool(right), BLOCK_SIZE=BLOCK)
|
|
649
|
+
if out_int32:
|
|
650
|
+
out = out.to(NBXDtype.int32)
|
|
651
|
+
return out
|
|
652
|
+
|
|
653
|
+
|
|
654
|
+
def index_put_wrapper(x, indices, values, accumulate: bool = False):
|
|
655
|
+
"""aten::index_put / index_put_ — scatter-write `values` into `x`.
|
|
656
|
+
|
|
657
|
+
Functional: returns a fresh tensor (the executor reassigns the op's
|
|
658
|
+
output slot, exactly like `index_add_wrapper`; the `_` in-place
|
|
659
|
+
variant is realised by the graph, not by mutating the input here).
|
|
660
|
+
|
|
661
|
+
Scope = the decomposed-ATen norm: exactly ONE non-None *integer*
|
|
662
|
+
index tensor on the leading dim (covers MoE-v2 expert aggregation,
|
|
663
|
+
KV-cache indexed writes, post-`aten::nonzero` masked scatter).
|
|
664
|
+
k>=2 advanced indices, non-leading-dim indexing, boolean masks, and
|
|
665
|
+
un-broadcastable `values` raise NotImplementedError with a named
|
|
666
|
+
follow-up rather than silently mis-scattering (ZERO-FALLBACK — a
|
|
667
|
+
visible crash beats the previous identity-lambda silent data loss).
|
|
668
|
+
"""
|
|
669
|
+
from nbxtensor.dtype.nbx_dtype import NBXDtype
|
|
670
|
+
|
|
671
|
+
if not isinstance(indices, (list, tuple)):
|
|
672
|
+
indices = [indices]
|
|
673
|
+
non_none = [(d, ix) for d, ix in enumerate(indices) if ix is not None]
|
|
674
|
+
if len(non_none) != 1 or non_none[0][0] != 0:
|
|
675
|
+
raise NotImplementedError(
|
|
676
|
+
"aten::index_put supports exactly one leading-dim index "
|
|
677
|
+
f"tensor; got non-None at positions "
|
|
678
|
+
f"{[d for d, _ in non_none]} of {len(indices)} — k>=2 / "
|
|
679
|
+
"non-leading advanced indexing is unwired (follow-up "
|
|
680
|
+
"P-INDEX-PUT-ADVANCED-GENERAL).")
|
|
681
|
+
idx = non_none[0][1]
|
|
682
|
+
# Boolean-mask index_put = masked_fill semantics: out[mask] = scalar, the
|
|
683
|
+
# mask broadcasting over x's TRAILING dims. The attention masked_fill
|
|
684
|
+
# (scores.masked_fill(~mask, value)) functionalises to exactly this form —
|
|
685
|
+
# a 1-byte mask whose shape is a leading-dim PREFIX of x, with a scalar value.
|
|
686
|
+
# Route it to the masked_fill kernel instead of the 1-D integer-index path
|
|
687
|
+
# (which read the mask as garbage indices and wrote far out of bounds → the
|
|
688
|
+
# OpenAudio DualAR-decode triton CUDA-700). Match the mask dtype by NAME,
|
|
689
|
+
# robust to whether the index was typed NBXDtype.{bool_,uint8} or left as a
|
|
690
|
+
# raw triton dtype (tl.uint8) by an upstream bitwise_not/isin. The shape-prefix
|
|
691
|
+
# + scalar-value test keeps genuine 1-D integer indices on the integer path.
|
|
692
|
+
_idx_dtn = str(idx.dtype).lower()
|
|
693
|
+
if (("uint8" in _idx_dtn or "bool" in _idx_dtn)
|
|
694
|
+
and idx.ndim < x.ndim
|
|
695
|
+
and list(idx.shape) == list(x.shape[:idx.ndim])
|
|
696
|
+
and values.numel() == 1):
|
|
697
|
+
mask_b = idx.reshape([*list(idx.shape),
|
|
698
|
+
*([1] * (x.ndim - idx.ndim))])
|
|
699
|
+
fill_val = _core._to_scalar(values) if isinstance(values, NBXTensor) else float(values)
|
|
700
|
+
return masked_fill(x, mask_b, fill_val)
|
|
701
|
+
if (("bool" in _idx_dtn or "uint8" in _idx_dtn)
|
|
702
|
+
and list(idx.shape) == list(x.shape[:idx.ndim])):
|
|
703
|
+
# General boolean-mask scatter with tensor values: x[mask] = vals
|
|
704
|
+
# where mask is a leading-dim PREFIX of x and vals is [n, ...]
|
|
705
|
+
# (n = mask True-count) — the DeepStack injection write
|
|
706
|
+
# (hidden[visual_pos_masks, :] = local_this). NBX stores bool
|
|
707
|
+
# masks as uint8 (bool-repr doctrine), hence the same name+
|
|
708
|
+
# shape-prefix conjunction as the masked_fill route above.
|
|
709
|
+
# Lower to the integer path: flatten the masked leading dims,
|
|
710
|
+
# compact the mask to ascending row indices (deterministic
|
|
711
|
+
# nonzero), scatter through the proven single-index route,
|
|
712
|
+
# restore the shape.
|
|
713
|
+
xc = x.contiguous()
|
|
714
|
+
flat_rows = 1
|
|
715
|
+
for _s in idx.shape:
|
|
716
|
+
flat_rows *= int(_s)
|
|
717
|
+
tail = list(x.shape[idx.ndim:])
|
|
718
|
+
x_flat = xc.reshape([flat_rows] + tail)
|
|
719
|
+
# x[mask] = vals: vals holds one row per True element (its leading dim) unless it broadcasts a single row
|
|
720
|
+
expected = (int(values.shape[0]) if isinstance(values, NBXTensor) and values.ndim == 1 + len(tail)
|
|
721
|
+
and int(values.shape[0]) != 1 else None)
|
|
722
|
+
rows = nonzero_1d_wrapper(idx.reshape([flat_rows]), expected=expected)
|
|
723
|
+
if int(rows.shape[0]) == 0:
|
|
724
|
+
# All-False mask (text-mode empty-visual stubs): zero writes —
|
|
725
|
+
# a fresh copy per the functional contract, same as the
|
|
726
|
+
# integer path's clone-then-scatter with an empty scatter.
|
|
727
|
+
return x.clone()
|
|
728
|
+
out_flat = index_put_wrapper(x_flat, [rows], values, accumulate)
|
|
729
|
+
return out_flat.reshape(list(x.shape))
|
|
730
|
+
if idx.dtype in (NBXDtype.float16, NBXDtype.bfloat16,
|
|
731
|
+
NBXDtype.float32, NBXDtype.float64):
|
|
732
|
+
raise NotImplementedError(
|
|
733
|
+
f"aten::index_put index tensor must be integer, got "
|
|
734
|
+
f"{idx.dtype} — follow-up P-INDEX-PUT-ADVANCED-GENERAL.")
|
|
735
|
+
|
|
736
|
+
x = x.contiguous()
|
|
737
|
+
idx = idx.contiguous()
|
|
738
|
+
out = x.clone()
|
|
739
|
+
|
|
740
|
+
T = 1
|
|
741
|
+
for i in range(1, x.ndim):
|
|
742
|
+
T *= x.shape[i]
|
|
743
|
+
Sn = idx.numel()
|
|
744
|
+
N = Sn * T
|
|
745
|
+
if N == 0:
|
|
746
|
+
return out
|
|
747
|
+
|
|
748
|
+
vnumel = values.numel()
|
|
749
|
+
if vnumel == 1:
|
|
750
|
+
val_scalar = True
|
|
751
|
+
vbuf = values.contiguous()
|
|
752
|
+
elif vnumel == N:
|
|
753
|
+
val_scalar = False
|
|
754
|
+
vbuf = values.contiguous()
|
|
755
|
+
else:
|
|
756
|
+
raise NotImplementedError(
|
|
757
|
+
f"aten::index_put values numel {vnumel} != idx*tail {N} "
|
|
758
|
+
"and not scalar — value broadcasting unwired (follow-up "
|
|
759
|
+
"P-INDEX-PUT-ADVANCED-GENERAL).")
|
|
760
|
+
|
|
761
|
+
set_device(out)
|
|
762
|
+
index_put_kernel[_ew._1d_grid(N)](
|
|
763
|
+
out, idx, vbuf,
|
|
764
|
+
T, N, out.shape[0], # R: the row bound the kernel traps on (OOB parity with torch)
|
|
765
|
+
*fault_channel(INDEX_PUT_OOB, out),
|
|
766
|
+
VAL_SCALAR=val_scalar,
|
|
767
|
+
ACCUMULATE=bool(accumulate),
|
|
768
|
+
**_ew_meta(N),
|
|
769
|
+
)
|
|
770
|
+
return out
|