nbxtensor 0.0.3__tar.gz → 0.0.4__tar.gz
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-0.0.3 → nbxtensor-0.0.4}/CHANGELOG.md +21 -1
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/PKG-INFO +19 -11
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/README.md +16 -9
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/pyproject.toml +6 -2
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/__init__.py +1 -1
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/__main__.py +15 -5
- nbxtensor-0.0.4/src/nbxtensor/backend/install_metal.py +107 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/metal_backend.py +25 -17
- nbxtensor-0.0.4/tests/test_install_metal_fetches_only_what_it_names.py +117 -0
- nbxtensor-0.0.4/tests/test_it_installs_on_a_mac.py +23 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/.github/PULL_REQUEST_TEMPLATE/certificate.md +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/CONTRIBUTING.md +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/LICENSE +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/THIRD_PARTY_NOTICES.md +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/assets/logo.png +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/__init__.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/capabilities.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/cpu/__init__.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/cpu/blas.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/cpu/pool.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/cpu_backend.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/device_facts.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/device_files.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/device_names.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/amd/cdna.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/amd/cdna2.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/amd/cdna3.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/amd/cdna4.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/amd/rdna3.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/amd/rdna4.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m1.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m1_max.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m1_pro.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m1_ultra.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m2.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m2_max.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m2_pro.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m2_ultra.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m3.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m3_max.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m3_pro.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m3_ultra.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m4.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m4_max.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m4_pro.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m5.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m5_max.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m5_pro.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m5_ultra.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_m6.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/apple/apple_silicon.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/intel/bmg.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/intel/dg2.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/intel/lnl.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/intel/pvc.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/nvidia/ampere.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/nvidia/hopper.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/devices/nvidia/volta.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/faults.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/host_memory.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/install_help.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/install_steps.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/launcher.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/launcher_contract.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/level_zero.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/level_zero_driver.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/metal_device.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/process_memory.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/replay_graph.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/runtime.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/backend/triton_ext_driver.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/dtype/__init__.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/dtype/convert.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/dtype/dtypes.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/dtype/itemsize.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/dtype/nbx_dtype.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/dtype/operands.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/dtype/outputs.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/dtype/policy.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/dtype/policy.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/dtype/promotion.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/host.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/io/__init__.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/io/read_rate.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/io/safetensors.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/__init__.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/allocator.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/arena.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/device_memory.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/device_transfer.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/layout.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/memory_pool.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/metal.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/oom_advice.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/pointer_tables.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/pool.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/shadow.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/slab.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/trace_replay.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/traces/sana-video-2b-720p.16g.2026-11-02.tsv +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/memory/vmm.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/__init__.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/_autotune_policy.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/_common.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/_configs.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/_cost.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/abs.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/adaptive_avg_pool2d.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/add.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/addcdiv.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/addcmul.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/addmv_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/addr.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/all_reduce.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/amax.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/any_reduce.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/arange_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/argmax.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/argmin.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/atan2.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/attention_wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/autotune_bucket.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/autotune_cache.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/autotune_certified.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/autotune_refusals.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/autotune_settings.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/avg_pool2d.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/baddbmm_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/batch_norm.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/binary_strided.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/bitwise_and.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/bitwise_left_shift.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/bitwise_not.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/bitwise_or.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/bitwise_right_shift.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/bucketize.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/cat_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/celu.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/certified_index.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/clamp.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/clamp_min.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/conv2d.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/conv2d_m8n8k4.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/conv_depthwise2d.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/conv_transpose2d.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/conv_wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/copy.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/copy_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/cos.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/count_nonzero.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/cross_entropy.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/cummax.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/cummin.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/cumsum.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/decode_attn_vec.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/depthwise_conv2d.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/dequant_gemv.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/dft.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/dispatch.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/div.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/dot_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/dropout.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/dtype_convert.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/elementwise_launch.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/elementwise_wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/elu.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/embedding.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/eq.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/erf.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/exp.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/exp2.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/fft_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/fill_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/flash_attention.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/flash_attention_m8n8k4.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/flash_decode.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/flip_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/floor.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/full_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/fused_moe.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/fused_moe_m8n8k4.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/fused_upsample_conv.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/gather_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/ge.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/gelu.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/gemv_vec.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/glu.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/grid_sampler.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/group_norm_tiled.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/groupnorm.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/gt.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/hardsigmoid.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/hardswish.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/im2col.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/index_add.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/index_put_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/index_select.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/index_wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/isfinite.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/isinf.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/isnan.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/launch_keys.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/launch_observer.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/layernorm.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/le.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/leaky_relu.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/lerp.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/linalg_wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/linspace_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/log.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/log_sigmoid.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/logical_and.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/logical_not.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/logical_or.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/logical_xor.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/loss_wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/lt.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/masked_fill.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/matmul.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/matmul_m8n8k4.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/max_pool2d.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/max_pool3d.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/max_reduce.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/maximum.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/mean.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/min_reduce.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/minimum.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/mish.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/mma_m8n8k4.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/moe_align.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/moe_decode_vec.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/moe_device_residency.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/moe_wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/mse_loss.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/mul.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/nan_to_num.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/ne.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/neg.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/nllloss.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/nonzero.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/nonzero_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/norm_wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/ones_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/oracles/__init__.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/oracles/conv2d_fp64.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/pad_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/paged_decode_attn.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/pixel_shuffle.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/pixel_unshuffle.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/pow.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/prod.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/rand_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/random_wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/reciprocal.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/reduction_wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/relu.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/remainder.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/repeat_interleave.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/repeat_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/residual_chain.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/rmsnorm.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/rope.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/rsqrt.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/scaled_softmax.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/scatter_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/screen_oracle.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/selu.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/shape_wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/sigmoid.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/silu.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/sin.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/softmax.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/softplus.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/sort_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/spectral_wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/sqrt.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/stack_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/std.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/strided_copy.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/sub.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/sum.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/tan.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/tanh.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/threshold.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/tiling.yml +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/tiling_policy.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/tiling_sizes.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/topk.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/trace_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/tril.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/triu.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/unfold_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/unique_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/upsample_bilinear2d.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/upsample_nearest2d.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/upsample_nearest3d.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/var.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/vector_norm.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/weight_norm.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/where.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/wrappers.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/ops/zeros_op.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/quant/__init__.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/quant/quantized_tensor.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/random/__init__.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/random/mt_randn.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/random/rng_pin.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/random/rng_stream.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/random/seeded_draw.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/shape/__init__.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/src/nbxtensor/tensor.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_a_small_tensor_never_pins_a_large_block.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_a_sweep_refused_everywhere_says_so.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_a_triton_kernel_compiles_for_amd_and_launches_through_hip.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_allocator_interceptor.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_array_api_promotion.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_cpu_pool.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_device_facts.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_device_files.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_device_memory.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_dtype_convert.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_dtype_outputs.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_dtype_promotion.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_host.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_io_bf16_split.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_io_read_rate.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_nbx_dtype.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_no_environment_switch_changes_a_kernel.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_ops_cost.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_policy.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_readme.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_shape.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_the_backend_is_the_card_on_the_machine.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_the_compiler_key_is_prewarmed.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_the_hip_driver_keeps_the_launch_contract.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_the_launcher_refuses_a_backend_it_has_no_driver_for.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_the_level_zero_backend_keeps_its_contracts.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_the_malloc_trace_names_the_wrapper.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_the_metal_dispatch_facts_are_declared_or_refused.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_third_party_notices.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tests/test_version.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tools/device_key_census.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tools/device_key_trace.py +0 -0
- {nbxtensor-0.0.3 → nbxtensor-0.0.4}/tools/scatter_scan.py +0 -0
|
@@ -7,6 +7,25 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
|
|
|
7
7
|
|
|
8
8
|
## [Unreleased]
|
|
9
9
|
|
|
10
|
+
## [0.0.4] - 2026-11-07
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- **Triton for the Apple GPU in one command**: `python -m nbxtensor install-metal` installs the Metal wheels of the
|
|
14
|
+
project's fork NeuroBrix/triton-ext (Triton built with triton-ext, and imperatormk's AppleGPU backend as
|
|
15
|
+
`nbx-triton-apple`, triton-lang/triton-ext#126), each by the release URL and sha256 written in the library,
|
|
16
|
+
through pip with `--no-deps --require-hashes --no-index` — never an extra index nor a package name resolved on PyPI. The
|
|
17
|
+
wheels are for Python 3.12 and macOS 15 or later (release apple-v0.1.0+triton3.8.0); it refuses by name off an
|
|
18
|
+
Apple-silicon Mac, on another Python, or below the macOS the wheels are built for. `python -m nbxtensor doctor` on a Mac
|
|
19
|
+
without it says so and names the command; the Metal backend's refusal names it too. On a Mac, nbxtensor depends on
|
|
20
|
+
`pyobjc-framework-Metal`, through which it opens the Metal device; without it the run fell to a CPU profile and was
|
|
21
|
+
refused for triton-cpu, it is now refused by name with its install line.
|
|
22
|
+
|
|
23
|
+
### Fixed
|
|
24
|
+
- **nbxtensor installs on a Mac**: it depended on `triton` on every platform, and PyPI publishes no Triton for macOS, so
|
|
25
|
+
`pip install nbxtensor` — and with it the NeuroBrix engine, even its PyTorch engine on MPS — could not resolve on a
|
|
26
|
+
clean macOS environment. Triton is now a dependency where PyPI has it; on a Mac it is built from source with its Metal
|
|
27
|
+
backend (the README's install), and the library says by name what to build when it is absent.
|
|
28
|
+
|
|
10
29
|
## [0.0.3] - 2026-11-06
|
|
11
30
|
|
|
12
31
|
### Fixed
|
|
@@ -143,7 +162,8 @@ memory; the fix is in test).
|
|
|
143
162
|
- **`flash_decode_split_kernel` and its route**: armed only by an environment switch (`NBX_FLASH_DECODE`, with `NBX_FD_BLOCK_N` and `NBX_FD_SEG`) and measured slower than the vector decode kernels; `flash_decode.py` keeps the fixed-order reduce they share.
|
|
144
163
|
- **`conv1d_forward_kernel`**: launched by no route (a 1-D convolution runs through `conv2d`).
|
|
145
164
|
|
|
146
|
-
[Unreleased]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.
|
|
165
|
+
[Unreleased]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.4...HEAD
|
|
166
|
+
[0.0.4]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.3...v0.0.4
|
|
147
167
|
[0.0.3]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.2...v0.0.3
|
|
148
168
|
[0.0.2]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.1...v0.0.2
|
|
149
169
|
[0.0.1]: https://github.com/NeuroBrix/nbxtensor/releases/tag/v0.0.1
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.5
|
|
2
2
|
Name: nbxtensor
|
|
3
|
-
Version: 0.0.
|
|
3
|
+
Version: 0.0.4
|
|
4
4
|
Summary: Run AI models without PyTorch: tensors, Triton kernels, planned memory and certified tuning, wherever Triton runs — NVIDIA, AMD, Intel and Apple GPUs.
|
|
5
5
|
Project-URL: Homepage, https://github.com/NeuroBrix/nbxtensor
|
|
6
6
|
Project-URL: Source, https://github.com/NeuroBrix/nbxtensor
|
|
@@ -12,8 +12,9 @@ License-File: LICENSE
|
|
|
12
12
|
Requires-Python: >=3.10
|
|
13
13
|
Requires-Dist: numpy
|
|
14
14
|
Requires-Dist: nvidia-cuda-runtime-cu12; platform_system == 'Linux'
|
|
15
|
+
Requires-Dist: pyobjc-framework-metal; platform_system == 'Darwin'
|
|
15
16
|
Requires-Dist: pyyaml
|
|
16
|
-
Requires-Dist: triton
|
|
17
|
+
Requires-Dist: triton; platform_system != 'Darwin'
|
|
17
18
|
Provides-Extra: test
|
|
18
19
|
Requires-Dist: pytest; extra == 'test'
|
|
19
20
|
Description-Content-Type: text/markdown
|
|
@@ -137,7 +138,7 @@ What changed from one version to the next — added, changed, fixed, removed —
|
|
|
137
138
|
nbxtensor never needs torch: it is not a dependency, not even an optional one, and the library and its kernels run in
|
|
138
139
|
an environment where torch is not installed (proven on Metal, 2026-10-29: `tools/torch_free_proof.py` records every
|
|
139
140
|
attempt to import torch, caught ones included, and recorded none). Python 3.10 or later; its dependencies are numpy,
|
|
140
|
-
pyyaml and Triton.
|
|
141
|
+
pyyaml and Triton (on a Mac, `pyobjc-framework-Metal` and the Triton `install-metal` brings).
|
|
141
142
|
|
|
142
143
|
```
|
|
143
144
|
pip install nbxtensor
|
|
@@ -155,14 +156,21 @@ install commands when one is not.
|
|
|
155
156
|
|
|
156
157
|
**From the NeuroBrix repository** (contributors): `pip install -e nbxtensor`.
|
|
157
158
|
|
|
158
|
-
**macOS, Apple silicon (Metal).** PyPI publishes no Triton for macOS
|
|
159
|
-
|
|
159
|
+
**macOS, Apple silicon (Metal).** PyPI publishes no Triton for macOS. `install-metal` installs it from the releases of
|
|
160
|
+
the project's fork of triton-ext, [NeuroBrix/triton-ext](https://github.com/NeuroBrix/triton-ext): Triton built with
|
|
161
|
+
`TRITON_EXT_ENABLED=1`, and the AppleGPU backend (the `nbx-triton-apple` wheel) written by imperatormk
|
|
162
|
+
([triton-lang/triton-ext#126](https://github.com/triton-lang/triton-ext/pull/126)); each wheel is checked against the
|
|
163
|
+
sha256 written in the library (Python 3.12, macOS 15 or later):
|
|
160
164
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
165
|
+
```
|
|
166
|
+
pip install nbxtensor
|
|
167
|
+
python -m nbxtensor install-metal
|
|
168
|
+
```
|
|
169
|
+
|
|
170
|
+
Xcode's Metal toolchain is the other requirement (`xcrun metal` must find the real compiler, not Xcode's placeholder:
|
|
171
|
+
`TOOLCHAINS=Metal`); `python -m nbxtensor doctor` says what is missing. To build Triton and its Metal backend yourself
|
|
172
|
+
instead: Triton (github.com/triton-lang/triton) built with `TRITON_EXT_ENABLED=1`, then triton-ext's `backend/AppleGPU`
|
|
173
|
+
installed into the same environment.
|
|
166
174
|
|
|
167
175
|
`nbxtensor.dtype` and `nbxtensor.shape` need neither a device nor Triton.
|
|
168
176
|
|
|
@@ -492,7 +500,7 @@ with the tensor class (API.md §4).
|
|
|
492
500
|
|
|
493
501
|
## Versions
|
|
494
502
|
|
|
495
|
-
The first publication was **0.0.1**; this is **0.0.
|
|
503
|
+
The first publication was **0.0.1**; this is **0.0.4**. Each fix published after it increments the number (0.0.5,
|
|
496
504
|
…), with no alpha or beta label. The rewrite of the core in Rust will be **2.0.0**. `nbxtensor.__version__` is the
|
|
497
505
|
installed version.
|
|
498
506
|
|
|
@@ -117,7 +117,7 @@ What changed from one version to the next — added, changed, fixed, removed —
|
|
|
117
117
|
nbxtensor never needs torch: it is not a dependency, not even an optional one, and the library and its kernels run in
|
|
118
118
|
an environment where torch is not installed (proven on Metal, 2026-10-29: `tools/torch_free_proof.py` records every
|
|
119
119
|
attempt to import torch, caught ones included, and recorded none). Python 3.10 or later; its dependencies are numpy,
|
|
120
|
-
pyyaml and Triton.
|
|
120
|
+
pyyaml and Triton (on a Mac, `pyobjc-framework-Metal` and the Triton `install-metal` brings).
|
|
121
121
|
|
|
122
122
|
```
|
|
123
123
|
pip install nbxtensor
|
|
@@ -135,14 +135,21 @@ install commands when one is not.
|
|
|
135
135
|
|
|
136
136
|
**From the NeuroBrix repository** (contributors): `pip install -e nbxtensor`.
|
|
137
137
|
|
|
138
|
-
**macOS, Apple silicon (Metal).** PyPI publishes no Triton for macOS
|
|
139
|
-
|
|
138
|
+
**macOS, Apple silicon (Metal).** PyPI publishes no Triton for macOS. `install-metal` installs it from the releases of
|
|
139
|
+
the project's fork of triton-ext, [NeuroBrix/triton-ext](https://github.com/NeuroBrix/triton-ext): Triton built with
|
|
140
|
+
`TRITON_EXT_ENABLED=1`, and the AppleGPU backend (the `nbx-triton-apple` wheel) written by imperatormk
|
|
141
|
+
([triton-lang/triton-ext#126](https://github.com/triton-lang/triton-ext/pull/126)); each wheel is checked against the
|
|
142
|
+
sha256 written in the library (Python 3.12, macOS 15 or later):
|
|
140
143
|
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
```
|
|
145
|
+
pip install nbxtensor
|
|
146
|
+
python -m nbxtensor install-metal
|
|
147
|
+
```
|
|
148
|
+
|
|
149
|
+
Xcode's Metal toolchain is the other requirement (`xcrun metal` must find the real compiler, not Xcode's placeholder:
|
|
150
|
+
`TOOLCHAINS=Metal`); `python -m nbxtensor doctor` says what is missing. To build Triton and its Metal backend yourself
|
|
151
|
+
instead: Triton (github.com/triton-lang/triton) built with `TRITON_EXT_ENABLED=1`, then triton-ext's `backend/AppleGPU`
|
|
152
|
+
installed into the same environment.
|
|
146
153
|
|
|
147
154
|
`nbxtensor.dtype` and `nbxtensor.shape` need neither a device nor Triton.
|
|
148
155
|
|
|
@@ -472,7 +479,7 @@ with the tensor class (API.md §4).
|
|
|
472
479
|
|
|
473
480
|
## Versions
|
|
474
481
|
|
|
475
|
-
The first publication was **0.0.1**; this is **0.0.
|
|
482
|
+
The first publication was **0.0.1**; this is **0.0.4**. Each fix published after it increments the number (0.0.5,
|
|
476
483
|
…), with no alpha or beta label. The rewrite of the core in Rust will be **2.0.0**. `nbxtensor.__version__` is the
|
|
477
484
|
installed version.
|
|
478
485
|
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "nbxtensor"
|
|
7
|
-
version = "0.0.
|
|
7
|
+
version = "0.0.4"
|
|
8
8
|
description = "Run AI models without PyTorch: tensors, Triton kernels, planned memory and certified tuning, wherever Triton runs — NVIDIA, AMD, Intel and Apple GPUs."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "Apache-2.0" }
|
|
@@ -16,7 +16,11 @@ authors = [{ name = "NeuroBrix" }]
|
|
|
16
16
|
# On Linux the CUDA runtime library its CUDA backend opens (libcudart.so.12), which the NVIDIA driver does not ship: a clean
|
|
17
17
|
# machine with only the driver found no GPU runtime and the engine read the card as CPU-only (the stable's install test,
|
|
18
18
|
# 2026-11-05). On a machine without an NVIDIA card it is a library nothing opens.
|
|
19
|
-
|
|
19
|
+
# Triton on every platform PyPI publishes it for; macOS has none (the Metal Triton is built from source, with triton-ext's
|
|
20
|
+
# AppleGPU backend — the README's install), so a plain "triton" made the library uninstallable on a Mac (2026-11-06).
|
|
21
|
+
# On a Mac the Metal device is opened through pyobjc's Metal framework; Triton for Metal comes from `install-metal`.
|
|
22
|
+
dependencies = ["numpy", "triton; platform_system != 'Darwin'", "pyyaml", "nvidia-cuda-runtime-cu12; platform_system == 'Linux'",
|
|
23
|
+
"pyobjc-framework-Metal; platform_system == 'Darwin'"]
|
|
20
24
|
|
|
21
25
|
[project.urls]
|
|
22
26
|
Homepage = "https://github.com/NeuroBrix/nbxtensor"
|
|
@@ -1,11 +1,17 @@
|
|
|
1
1
|
"""`python -m nbxtensor doctor`: which GPU backend this machine runs, and for every NVIDIA, AMD or Intel GPU on its PCI
|
|
2
2
|
bus whether the runtime library and the Triton backend it needs are installed — with the vendor's install steps when
|
|
3
|
-
one is missing (`backend.install_help`).
|
|
3
|
+
one is missing (`backend.install_help`); on an Apple-silicon Mac, whether Triton for Metal is installed.
|
|
4
|
+
`python -m nbxtensor install-metal [--dry-run]`: install Triton for the Apple GPU from this version's release
|
|
5
|
+
(`backend.install_metal`)."""
|
|
4
6
|
import sys
|
|
5
7
|
|
|
6
8
|
|
|
7
9
|
def doctor() -> int:
|
|
8
10
|
from nbxtensor.backend import install_help, runtime
|
|
11
|
+
from nbxtensor.backend.metal_backend import is_apple_silicon, triton_metal_available
|
|
12
|
+
if is_apple_silicon() and not triton_metal_available():
|
|
13
|
+
print("Apple GPU found; Triton for Metal not installed — run: python -m nbxtensor install-metal")
|
|
14
|
+
return 0
|
|
9
15
|
try:
|
|
10
16
|
backend = runtime.gpu_backend()
|
|
11
17
|
print(f"backend in force: {backend}" + (" (the Apple GPU, through Metal)" if backend == "metal" else ""))
|
|
@@ -19,10 +25,14 @@ def doctor() -> int:
|
|
|
19
25
|
|
|
20
26
|
def main(argv=None) -> int:
|
|
21
27
|
argv = sys.argv[1:] if argv is None else argv
|
|
22
|
-
if argv[:1]
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
28
|
+
if argv[:1] == ["doctor"]:
|
|
29
|
+
return doctor()
|
|
30
|
+
if argv[:1] == ["install-metal"] and set(argv[1:]) <= {"--dry-run"}:
|
|
31
|
+
import nbxtensor
|
|
32
|
+
from nbxtensor.backend import install_metal
|
|
33
|
+
return install_metal.main(nbxtensor.__version__, dry_run="--dry-run" in argv[1:])
|
|
34
|
+
print("usage: python -m nbxtensor doctor | install-metal [--dry-run]", file=sys.stderr)
|
|
35
|
+
return 2
|
|
26
36
|
|
|
27
37
|
|
|
28
38
|
if __name__ == "__main__":
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
"""`python -m nbxtensor install-metal`: install Triton for the Apple GPU — the project's own wheels (Triton built with
|
|
2
|
+
TRITON_EXT_ENABLED=1, and triton-ext's AppleGPU backend as `nbx-triton-apple`), downloaded from the release of the
|
|
3
|
+
project's fork, github.com/NeuroBrix/triton-ext. The AppleGPU backend is imperatormk's (triton-lang/triton-ext#126).
|
|
4
|
+
|
|
5
|
+
PyPI publishes no Triton for macOS, so a Mac has no Triton after `pip install nbxtensor`. The wheels are fetched from
|
|
6
|
+
the URLs written below, each checked against the sha256 written beside it (`pip --require-hashes`), with `--no-deps`:
|
|
7
|
+
never through an extra index and never by a package name pip would look up on PyPI — anyone can publish a package
|
|
8
|
+
named "triton-apple-backend" there (dependency confusion). A machine other than an Apple-silicon Mac, or a Python the
|
|
9
|
+
table does not cover, is refused by name.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import os
|
|
14
|
+
import platform
|
|
15
|
+
import re
|
|
16
|
+
import subprocess
|
|
17
|
+
import sys
|
|
18
|
+
import tempfile
|
|
19
|
+
from typing import List, Tuple
|
|
20
|
+
|
|
21
|
+
#: the only place the wheels are fetched from: the releases of the project's fork of triton-ext
|
|
22
|
+
RELEASES = "https://github.com/NeuroBrix/triton-ext/releases/download/"
|
|
23
|
+
|
|
24
|
+
#: the Metal wheels per CPython tag: (URL of the release asset, sha256 of the file), Triton first; a tag absent here is
|
|
25
|
+
#: refused by name. The fork's release apple-v0.1.0+triton3.8.0 (2026-11-07): Triton 3.8.0 (git 4a15f415) built with
|
|
26
|
+
#: TRITON_EXT_ENABLED=1 and nbx-triton-apple 0.1.0, both macOS 15.0 arm64 (the prebuilt LLVM Triton links is minos 15.0);
|
|
27
|
+
#: sha256 read on the downloaded assets.
|
|
28
|
+
METAL_WHEELS: dict = {
|
|
29
|
+
"cp312": (
|
|
30
|
+
(RELEASES + "apple-v0.1.0%2Btriton3.8.0/triton-3.8.0%2Bgit4a15f415.nbx1-cp312-cp312-macosx_15_0_arm64.whl",
|
|
31
|
+
"b9084dc93ae7f1468062a03e713d49c9add9a1c9a609fae753a7b32a13b7a223"),
|
|
32
|
+
(RELEASES + "apple-v0.1.0%2Btriton3.8.0/nbx_triton_apple-0.1.0-cp312-cp312-macosx_15_0_arm64.whl",
|
|
33
|
+
"b9ed95c983b220b1fbf1300481b1e2f52a0435c10ee618ef7ccf60a132fe263c"),
|
|
34
|
+
),
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class InstallMetalRefused(RuntimeError):
|
|
39
|
+
"""This machine or this Python has no Metal wheels in this version's table."""
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _python_tag() -> str:
|
|
43
|
+
return f"cp{sys.version_info.major}{sys.version_info.minor}"
|
|
44
|
+
|
|
45
|
+
|
|
46
|
+
def wheels(version: str) -> List[Tuple[str, str]]:
|
|
47
|
+
"""[(url, sha256)] of the wheels this machine installs, or a named refusal."""
|
|
48
|
+
if not (platform.system() == "Darwin" and platform.machine() == "arm64"):
|
|
49
|
+
raise InstallMetalRefused(
|
|
50
|
+
f"install-metal installs Triton for the Apple GPU and runs on an Apple-silicon Mac only (this machine: "
|
|
51
|
+
f"{platform.system()} {platform.machine()}). Elsewhere Triton comes from PyPI with nbxtensor.")
|
|
52
|
+
tag = _python_tag()
|
|
53
|
+
if tag not in METAL_WHEELS:
|
|
54
|
+
covered = ", ".join(sorted(METAL_WHEELS)) or "none yet"
|
|
55
|
+
raise InstallMetalRefused(
|
|
56
|
+
f"nbxtensor {version} carries no Metal wheels for Python {sys.version_info.major}.{sys.version_info.minor} "
|
|
57
|
+
f"({tag}); covered: {covered}.")
|
|
58
|
+
entries = list(METAL_WHEELS[tag])
|
|
59
|
+
need = max(_min_macos(url) for url, _sha in entries)
|
|
60
|
+
have = _macos_version()
|
|
61
|
+
if have < need:
|
|
62
|
+
raise InstallMetalRefused(
|
|
63
|
+
f"macOS {'.'.join(map(str, need))} required for now: the Metal wheels of nbxtensor {version} are built for "
|
|
64
|
+
f"macOS {'.'.join(map(str, need))} and later (this Mac: macOS {'.'.join(map(str, have))}).")
|
|
65
|
+
return entries
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
def _min_macos(url: str) -> Tuple[int, int]:
|
|
69
|
+
"""The lowest macOS a wheel installs on, read from its platform tag (`macosx_15_0_arm64` -> (15, 0))."""
|
|
70
|
+
m = re.search(r"-macosx_(\d+)_(\d+)_arm64\.whl$", url)
|
|
71
|
+
if m is None:
|
|
72
|
+
raise InstallMetalRefused(f"not a macOS arm64 wheel: {url}")
|
|
73
|
+
return int(m.group(1)), int(m.group(2))
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def _macos_version() -> Tuple[int, int]:
|
|
77
|
+
parts = [int(p) for p in (platform.mac_ver()[0] or "0").split(".")[:2]]
|
|
78
|
+
return (parts + [0])[0], (parts + [0])[1]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
def pip_command(requirements: str) -> List[str]:
|
|
82
|
+
"""pip on this interpreter, from a requirements file of URLs and hashes only: no index is read, nothing else."""
|
|
83
|
+
return [sys.executable, "-m", "pip", "install", "--no-deps", "--require-hashes", "--no-index", "-r", requirements]
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
def requirements_text(entries: List[Tuple[str, str]]) -> str:
|
|
87
|
+
return "".join(f"{url} --hash=sha256:{sha}\n" for url, sha in entries)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
def main(version: str, dry_run: bool = False) -> int:
|
|
91
|
+
try:
|
|
92
|
+
entries = wheels(version)
|
|
93
|
+
except InstallMetalRefused as exc:
|
|
94
|
+
print(f"install-metal: {exc}", file=sys.stderr)
|
|
95
|
+
return 1
|
|
96
|
+
text = requirements_text(entries)
|
|
97
|
+
if dry_run:
|
|
98
|
+
print(text, end="")
|
|
99
|
+
print(" ".join(pip_command("<these lines>")))
|
|
100
|
+
return 0
|
|
101
|
+
fd, path = tempfile.mkstemp(prefix="nbxtensor-metal-", suffix=".txt")
|
|
102
|
+
try:
|
|
103
|
+
with os.fdopen(fd, "w") as f:
|
|
104
|
+
f.write(text)
|
|
105
|
+
return subprocess.call(pip_command(path))
|
|
106
|
+
finally:
|
|
107
|
+
os.unlink(path)
|
|
@@ -139,6 +139,16 @@ class TritonMetalShaderCompilerMissingError(TritonMetalNotInstalledError):
|
|
|
139
139
|
"""
|
|
140
140
|
|
|
141
141
|
|
|
142
|
+
class TritonMetalBindingMissingError(TritonMetalNotInstalledError):
|
|
143
|
+
"""Raised when Triton for Metal is installed but pyobjc's Metal binding, through which the device is opened, is
|
|
144
|
+
not: the device cannot be opened, and the run would otherwise fall to a CPU profile."""
|
|
145
|
+
|
|
146
|
+
|
|
147
|
+
def metal_binding_available() -> bool:
|
|
148
|
+
"""pyobjc's Metal binding, through which the device is opened, is installed (asked without importing it)."""
|
|
149
|
+
return importlib.util.find_spec("Metal") is not None
|
|
150
|
+
|
|
151
|
+
|
|
142
152
|
def is_apple_silicon() -> bool:
|
|
143
153
|
"""True on an Apple-Silicon Mac."""
|
|
144
154
|
return platform.system() == "Darwin" and platform.machine() == "arm64"
|
|
@@ -573,27 +583,25 @@ def ensure_triton_metal_or_raise() -> None:
|
|
|
573
583
|
|
|
574
584
|
if not triton_metal_available():
|
|
575
585
|
raise TritonMetalNotInstalledError(
|
|
576
|
-
"The --triton engine needs
|
|
577
|
-
"none is installed
|
|
578
|
-
"
|
|
579
|
-
"
|
|
580
|
-
"exists:\n"
|
|
581
|
-
" pip install triton-msl (MIT; needs Triton 3.7+ built "
|
|
582
|
-
"with TRITON_EXT_ENABLED=1)\n"
|
|
586
|
+
"The --triton engine needs Triton for the Apple GPU (Triton with "
|
|
587
|
+
"triton-ext's AppleGPU backend), and none is installed. PyPI publishes "
|
|
588
|
+
"no Triton for macOS; nbxtensor's release carries it:\n"
|
|
589
|
+
" python -m nbxtensor install-metal\n"
|
|
583
590
|
"\n"
|
|
584
|
-
"
|
|
585
|
-
"
|
|
586
|
-
"supply-chain surface you did not consent to, and it fails "
|
|
587
|
-
"confusingly on\n"
|
|
588
|
-
"air-gapped machines.\n"
|
|
591
|
+
"A run does not install it for you: a download at run time is a "
|
|
592
|
+
"supply-chain surface you did not consent to.\n"
|
|
589
593
|
"\n"
|
|
590
|
-
"
|
|
591
|
-
"
|
|
592
|
-
"on Apple GPUs through PyTorch MPS with no extra install.\n"
|
|
593
|
-
"\n"
|
|
594
|
-
"Status and known gaps: docs/internal/metal_adoption_plan_2026_09_03.md"
|
|
594
|
+
"Alternative: the PyTorch engine (--compiled) runs on the Apple GPU "
|
|
595
|
+
"through MPS (pip install torch \"neurobrix[pytorch]\")."
|
|
595
596
|
)
|
|
596
597
|
|
|
598
|
+
if not metal_binding_available():
|
|
599
|
+
# the device is opened through pyobjc's Metal binding: without it the run found no device and fell to a CPU
|
|
600
|
+
# profile, refused there for triton-cpu with not a word about Metal (the Mac's clean venv, 2026-11-07 00:51)
|
|
601
|
+
raise TritonMetalBindingMissingError(
|
|
602
|
+
"The --triton engine opens the Apple GPU through pyobjc's Metal binding, and it is not installed:\n"
|
|
603
|
+
" pip install pyobjc-framework-Metal")
|
|
604
|
+
|
|
597
605
|
if not metal_shader_compiler_available():
|
|
598
606
|
raise TritonMetalShaderCompilerMissingError(
|
|
599
607
|
"A Triton Metal backend is installed, but Apple's offline shader "
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
"""`python -m nbxtensor install-metal` installs Triton for the Apple GPU from the release of the project's fork
|
|
2
|
+
(github.com/NeuroBrix/triton-ext), each wheel by the URL and sha256 written in the code, through pip with --no-deps --require-hashes --no-index — never an extra index,
|
|
3
|
+
never a package name pip would resolve on PyPI (anyone can publish "triton-apple-backend" there: dependency
|
|
4
|
+
confusion). Off an Apple-silicon Mac, or on a Python the table does not cover, it refuses by name. `python -m nbxtensor
|
|
5
|
+
doctor` on a Mac without Triton names the command (the owner via the supervisor, 2026-11-07 00:45).
|
|
6
|
+
|
|
7
|
+
What this test would do if the code were wrong: a run on Linux that tries to install (red), a covered Python missing
|
|
8
|
+
from the table that installs anyway (red), a pip command reading an index or resolving dependencies (red), an entry
|
|
9
|
+
whose URL leaves the release or whose hash is not a sha256 (red), the table changed without this file (red)."""
|
|
10
|
+
from __future__ import annotations
|
|
11
|
+
|
|
12
|
+
import re
|
|
13
|
+
import sys
|
|
14
|
+
|
|
15
|
+
import pytest
|
|
16
|
+
|
|
17
|
+
from nbxtensor.backend import install_metal as M
|
|
18
|
+
|
|
19
|
+
#: the table as published — this file changes with it, by hand, from the files the release carries
|
|
20
|
+
_R = "https://github.com/NeuroBrix/triton-ext/releases/download/apple-v0.1.0%2Btriton3.8.0/"
|
|
21
|
+
EXPECTED_METAL_WHEELS: dict = {
|
|
22
|
+
"cp312": (
|
|
23
|
+
(_R + "triton-3.8.0%2Bgit4a15f415.nbx1-cp312-cp312-macosx_15_0_arm64.whl",
|
|
24
|
+
"b9084dc93ae7f1468062a03e713d49c9add9a1c9a609fae753a7b32a13b7a223"),
|
|
25
|
+
(_R + "nbx_triton_apple-0.1.0-cp312-cp312-macosx_15_0_arm64.whl",
|
|
26
|
+
"b9ed95c983b220b1fbf1300481b1e2f52a0435c10ee618ef7ccf60a132fe263c"),
|
|
27
|
+
),
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
def _mac(monkeypatch, macos="15.4"):
|
|
32
|
+
monkeypatch.setattr(M.platform, "system", lambda: "Darwin")
|
|
33
|
+
monkeypatch.setattr(M.platform, "machine", lambda: "arm64")
|
|
34
|
+
monkeypatch.setattr(M.platform, "mac_ver", lambda: (macos, ("", "", ""), "arm64"))
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def test_the_table_is_the_published_one():
|
|
38
|
+
assert M.METAL_WHEELS == EXPECTED_METAL_WHEELS
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
def test_every_entry_is_a_release_url_of_the_fork_with_a_sha256():
|
|
42
|
+
for tag, entries in M.METAL_WHEELS.items():
|
|
43
|
+
assert re.fullmatch(r"cp3\d+", tag), tag
|
|
44
|
+
names = [url.rsplit("/", 1)[1] for url, _ in entries]
|
|
45
|
+
assert [n.split("-")[0] for n in names] == ["triton", "nbx_triton_apple"], names # Triton first, then the backend
|
|
46
|
+
for (url, sha), name in zip(entries, names):
|
|
47
|
+
assert url.startswith(M.RELEASES) and url.count("/") == M.RELEASES.count("/") + 1, url
|
|
48
|
+
assert re.fullmatch(r"[A-Za-z0-9_.]+-[^-]+-%s-%s-macosx_\d+_\d+_arm64\.whl" % (tag, tag), name), name
|
|
49
|
+
assert re.fullmatch(r"[0-9a-f]{64}", sha), (name, sha)
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
@pytest.mark.parametrize("system, machine", [("Linux", "x86_64"), ("Darwin", "x86_64"), ("Windows", "AMD64")])
|
|
53
|
+
def test_off_an_apple_silicon_mac_it_refuses_by_name(monkeypatch, system, machine):
|
|
54
|
+
monkeypatch.setattr(M.platform, "system", lambda: system)
|
|
55
|
+
monkeypatch.setattr(M.platform, "machine", lambda: machine)
|
|
56
|
+
with pytest.raises(M.InstallMetalRefused, match="Apple-silicon Mac only"):
|
|
57
|
+
M.wheels("0.0.4")
|
|
58
|
+
calls = []
|
|
59
|
+
monkeypatch.setattr(M.subprocess, "call", lambda *a, **k: calls.append(a) or 0)
|
|
60
|
+
assert M.main("0.0.4") == 1 and not calls
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def test_a_python_the_table_does_not_cover_is_refused_by_name(monkeypatch):
|
|
64
|
+
_mac(monkeypatch)
|
|
65
|
+
monkeypatch.setattr(M, "METAL_WHEELS", {"cp399": (("triton-3.8.0-cp399-cp399-macosx_15_0_arm64.whl", "0" * 64),)})
|
|
66
|
+
with pytest.raises(M.InstallMetalRefused, match=r"no Metal wheels for Python .*covered: cp399"):
|
|
67
|
+
M.wheels("0.0.4")
|
|
68
|
+
|
|
69
|
+
|
|
70
|
+
def test_a_covered_python_installs_by_url_and_hash_only(monkeypatch):
|
|
71
|
+
_mac(monkeypatch)
|
|
72
|
+
tag = f"cp{sys.version_info.major}{sys.version_info.minor}"
|
|
73
|
+
base = M.RELEASES + "apple-v0.1.0+triton3.8.0/"
|
|
74
|
+
t = f"{base}triton-3.8.0-{tag}-{tag}-macosx_15_0_arm64.whl"
|
|
75
|
+
b = f"{base}nbx_triton_apple-0.1.0-{tag}-{tag}-macosx_15_0_arm64.whl"
|
|
76
|
+
monkeypatch.setattr(M, "METAL_WHEELS", {tag: ((t, "a" * 64), (b, "b" * 64))})
|
|
77
|
+
seen = {}
|
|
78
|
+
|
|
79
|
+
def call(cmd):
|
|
80
|
+
seen["cmd"] = cmd
|
|
81
|
+
seen["req"] = open(cmd[-1]).read()
|
|
82
|
+
return 0
|
|
83
|
+
monkeypatch.setattr(M.subprocess, "call", call)
|
|
84
|
+
assert M.main("0.0.4") == 0
|
|
85
|
+
assert seen["req"] == f"{t} --hash=sha256:{'a' * 64}\n{b} --hash=sha256:{'b' * 64}\n"
|
|
86
|
+
cmd = seen["cmd"]
|
|
87
|
+
assert cmd[:4] == [sys.executable, "-m", "pip", "install"]
|
|
88
|
+
assert {"--no-deps", "--require-hashes", "--no-index"} <= set(cmd), cmd
|
|
89
|
+
assert not any(a.startswith(("--index-url", "--extra-index-url", "-i", "--find-links", "-f")) for a in cmd), cmd
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def test_doctor_on_a_mac_without_triton_names_the_command(monkeypatch, capsys):
|
|
93
|
+
import nbxtensor.__main__ as main_mod
|
|
94
|
+
from nbxtensor.backend import metal_backend
|
|
95
|
+
monkeypatch.setattr(metal_backend, "is_apple_silicon", lambda: True)
|
|
96
|
+
monkeypatch.setattr(metal_backend, "triton_metal_available", lambda: False)
|
|
97
|
+
assert main_mod.main(["doctor"]) == 0
|
|
98
|
+
assert capsys.readouterr().out.strip() == (
|
|
99
|
+
"Apple GPU found; Triton for Metal not installed — run: python -m nbxtensor install-metal")
|
|
100
|
+
|
|
101
|
+
|
|
102
|
+
def test_the_command_line_names_install_metal(capsys):
|
|
103
|
+
import nbxtensor.__main__ as main_mod
|
|
104
|
+
assert main_mod.main(["nothing"]) == 2
|
|
105
|
+
assert "install-metal" in capsys.readouterr().err
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def test_a_mac_older_than_the_wheels_tag_is_refused_by_name(monkeypatch):
|
|
109
|
+
# the fork's first wheels were built for macOS 27 (macosx_27_0): pip refuses them on macOS 14-26 with no word of why
|
|
110
|
+
_mac(monkeypatch, macos="15.4")
|
|
111
|
+
tag = f"cp{sys.version_info.major}{sys.version_info.minor}"
|
|
112
|
+
url = f"{M.RELEASES}apple-v0.1.0+triton3.8.0/triton-3.8.0-{tag}-{tag}-macosx_27_0_arm64.whl"
|
|
113
|
+
monkeypatch.setattr(M, "METAL_WHEELS", {tag: ((url, "a" * 64),)})
|
|
114
|
+
with pytest.raises(M.InstallMetalRefused, match=r"macOS 27\.0 required for now.*this Mac: macOS 15\.4"):
|
|
115
|
+
M.wheels("0.0.4")
|
|
116
|
+
_mac(monkeypatch, macos="27.1")
|
|
117
|
+
assert M.wheels("0.0.4") == [(url, "a" * 64)]
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"""nbxtensor installs on a Mac. PyPI publishes no Triton for macOS (triton 3.8.0: two manylinux wheels, no sdist): the
|
|
2
|
+
Metal Triton is built from source (Triton with TRITON_EXT_ENABLED=1 and triton-ext's AppleGPU backend), so a plain
|
|
3
|
+
"triton" dependency made `pip install nbxtensor` — and `pip install "neurobrix[pytorch]"`, the PyTorch engine on MPS —
|
|
4
|
+
ResolutionImpossible on a clean macOS venv (the Mac, 2026-11-06 23:03). The engine's own pyproject already carries the
|
|
5
|
+
marker. Where Triton is absent the library refuses by name with what to build (backend/metal_backend).
|
|
6
|
+
|
|
7
|
+
What this test would do if the packaging were wrong: a triton dependency that macOS must resolve from PyPI (red)."""
|
|
8
|
+
from __future__ import annotations
|
|
9
|
+
|
|
10
|
+
from pathlib import Path
|
|
11
|
+
|
|
12
|
+
try:
|
|
13
|
+
import tomllib
|
|
14
|
+
except ModuleNotFoundError: # python 3.10
|
|
15
|
+
import tomli as tomllib
|
|
16
|
+
|
|
17
|
+
PYPROJECT = Path(__file__).resolve().parents[1] / "pyproject.toml"
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def test_triton_is_not_resolved_from_pypi_on_macos():
|
|
21
|
+
deps = tomllib.loads(PYPROJECT.read_text())["project"]["dependencies"]
|
|
22
|
+
triton = [d.replace(" ", "").replace('"', "'") for d in deps if d.split(";")[0].strip() == "triton"]
|
|
23
|
+
assert triton == ["triton;platform_system!='Darwin'"], triton
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|