nbxtensor 0.0.3__tar.gz → 0.0.5__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.
Files changed (347) hide show
  1. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/CHANGELOG.md +40 -1
  2. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/PKG-INFO +20 -12
  3. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/README.md +17 -10
  4. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/pyproject.toml +6 -2
  5. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/__init__.py +1 -1
  6. nbxtensor-0.0.5/src/nbxtensor/__main__.py +39 -0
  7. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/cpu/blas.py +11 -0
  8. nbxtensor-0.0.5/src/nbxtensor/backend/install_metal.py +107 -0
  9. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/launcher.py +3 -2
  10. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/level_zero.py +2 -2
  11. nbxtensor-0.0.5/src/nbxtensor/backend/libnames.py +26 -0
  12. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/metal_backend.py +25 -17
  13. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/replay_graph.py +2 -1
  14. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/runtime.py +9 -6
  15. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/io/read_rate.py +14 -3
  16. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/io/safetensors.py +35 -2
  17. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/autotune_certified.py +18 -8
  18. nbxtensor-0.0.5/tests/test_a_setting_not_served_says_why.py +32 -0
  19. nbxtensor-0.0.5/tests/test_install_metal_fetches_only_what_it_names.py +117 -0
  20. nbxtensor-0.0.5/tests/test_it_installs_on_a_mac.py +23 -0
  21. nbxtensor-0.0.3/src/nbxtensor/__main__.py +0 -29
  22. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/.github/PULL_REQUEST_TEMPLATE/certificate.md +0 -0
  23. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/CONTRIBUTING.md +0 -0
  24. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/LICENSE +0 -0
  25. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/THIRD_PARTY_NOTICES.md +0 -0
  26. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/assets/logo.png +0 -0
  27. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/__init__.py +0 -0
  28. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/capabilities.py +0 -0
  29. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/cpu/__init__.py +0 -0
  30. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/cpu/pool.py +0 -0
  31. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/cpu_backend.py +0 -0
  32. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/device_facts.py +0 -0
  33. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/device_files.py +0 -0
  34. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/device_names.py +0 -0
  35. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/amd/cdna.yml +0 -0
  36. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/amd/cdna2.yml +0 -0
  37. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/amd/cdna3.yml +0 -0
  38. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/amd/cdna4.yml +0 -0
  39. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/amd/rdna3.yml +0 -0
  40. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/amd/rdna4.yml +0 -0
  41. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m1.yml +0 -0
  42. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m1_max.yml +0 -0
  43. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m1_pro.yml +0 -0
  44. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m1_ultra.yml +0 -0
  45. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m2.yml +0 -0
  46. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m2_max.yml +0 -0
  47. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m2_pro.yml +0 -0
  48. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m2_ultra.yml +0 -0
  49. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m3.yml +0 -0
  50. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m3_max.yml +0 -0
  51. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m3_pro.yml +0 -0
  52. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m3_ultra.yml +0 -0
  53. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m4.yml +0 -0
  54. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m4_max.yml +0 -0
  55. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m4_pro.yml +0 -0
  56. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m5.yml +0 -0
  57. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m5_max.yml +0 -0
  58. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m5_pro.yml +0 -0
  59. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m5_ultra.yml +0 -0
  60. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m6.yml +0 -0
  61. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_silicon.yml +0 -0
  62. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/intel/bmg.yml +0 -0
  63. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/intel/dg2.yml +0 -0
  64. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/intel/lnl.yml +0 -0
  65. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/intel/pvc.yml +0 -0
  66. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/nvidia/ampere.yml +0 -0
  67. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/nvidia/hopper.yml +0 -0
  68. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/nvidia/volta.yml +0 -0
  69. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/faults.py +0 -0
  70. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/host_memory.py +0 -0
  71. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/install_help.py +0 -0
  72. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/install_steps.yml +0 -0
  73. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/launcher_contract.py +0 -0
  74. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/level_zero_driver.py +0 -0
  75. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/metal_device.py +0 -0
  76. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/process_memory.py +0 -0
  77. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/backend/triton_ext_driver.py +0 -0
  78. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/dtype/__init__.py +0 -0
  79. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/dtype/convert.py +0 -0
  80. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/dtype/dtypes.yml +0 -0
  81. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/dtype/itemsize.py +0 -0
  82. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/dtype/nbx_dtype.py +0 -0
  83. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/dtype/operands.py +0 -0
  84. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/dtype/outputs.py +0 -0
  85. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/dtype/policy.py +0 -0
  86. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/dtype/policy.yml +0 -0
  87. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/dtype/promotion.py +0 -0
  88. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/host.py +0 -0
  89. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/io/__init__.py +0 -0
  90. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/__init__.py +0 -0
  91. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/allocator.py +0 -0
  92. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/arena.py +0 -0
  93. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/device_memory.py +0 -0
  94. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/device_transfer.py +0 -0
  95. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/layout.py +0 -0
  96. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/memory_pool.py +0 -0
  97. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/metal.py +0 -0
  98. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/oom_advice.py +0 -0
  99. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/pointer_tables.py +0 -0
  100. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/pool.py +0 -0
  101. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/shadow.py +0 -0
  102. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/slab.py +0 -0
  103. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/trace_replay.py +0 -0
  104. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/traces/sana-video-2b-720p.16g.2026-11-02.tsv +0 -0
  105. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/memory/vmm.py +0 -0
  106. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/__init__.py +0 -0
  107. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/_autotune_policy.py +0 -0
  108. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/_common.py +0 -0
  109. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/_configs.py +0 -0
  110. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/_cost.py +0 -0
  111. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/abs.py +0 -0
  112. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/adaptive_avg_pool2d.py +0 -0
  113. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/add.py +0 -0
  114. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/addcdiv.py +0 -0
  115. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/addcmul.py +0 -0
  116. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/addmv_op.py +0 -0
  117. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/addr.py +0 -0
  118. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/all_reduce.py +0 -0
  119. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/amax.py +0 -0
  120. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/any_reduce.py +0 -0
  121. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/arange_op.py +0 -0
  122. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/argmax.py +0 -0
  123. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/argmin.py +0 -0
  124. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/atan2.py +0 -0
  125. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/attention_wrappers.py +0 -0
  126. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/autotune_bucket.py +0 -0
  127. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/autotune_cache.py +0 -0
  128. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/autotune_refusals.py +0 -0
  129. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/autotune_settings.py +0 -0
  130. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/avg_pool2d.py +0 -0
  131. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/baddbmm_op.py +0 -0
  132. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/batch_norm.py +0 -0
  133. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/binary_strided.py +0 -0
  134. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/bitwise_and.py +0 -0
  135. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/bitwise_left_shift.py +0 -0
  136. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/bitwise_not.py +0 -0
  137. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/bitwise_or.py +0 -0
  138. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/bitwise_right_shift.py +0 -0
  139. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/bucketize.py +0 -0
  140. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/cat_op.py +0 -0
  141. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/celu.py +0 -0
  142. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/certified_index.py +0 -0
  143. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/clamp.py +0 -0
  144. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/clamp_min.py +0 -0
  145. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/conv2d.py +0 -0
  146. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/conv2d_m8n8k4.py +0 -0
  147. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/conv_depthwise2d.py +0 -0
  148. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/conv_transpose2d.py +0 -0
  149. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/conv_wrappers.py +0 -0
  150. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/copy.py +0 -0
  151. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/copy_op.py +0 -0
  152. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/cos.py +0 -0
  153. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/count_nonzero.py +0 -0
  154. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/cross_entropy.py +0 -0
  155. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/cummax.py +0 -0
  156. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/cummin.py +0 -0
  157. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/cumsum.py +0 -0
  158. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/decode_attn_vec.py +0 -0
  159. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/depthwise_conv2d.py +0 -0
  160. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/dequant_gemv.py +0 -0
  161. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/dft.py +0 -0
  162. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/dispatch.py +0 -0
  163. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/div.py +0 -0
  164. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/dot_op.py +0 -0
  165. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/dropout.py +0 -0
  166. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/dtype_convert.py +0 -0
  167. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/elementwise_launch.py +0 -0
  168. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/elementwise_wrappers.py +0 -0
  169. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/elu.py +0 -0
  170. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/embedding.py +0 -0
  171. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/eq.py +0 -0
  172. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/erf.py +0 -0
  173. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/exp.py +0 -0
  174. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/exp2.py +0 -0
  175. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/fft_op.py +0 -0
  176. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/fill_op.py +0 -0
  177. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/flash_attention.py +0 -0
  178. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/flash_attention_m8n8k4.py +0 -0
  179. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/flash_decode.py +0 -0
  180. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/flip_op.py +0 -0
  181. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/floor.py +0 -0
  182. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/full_op.py +0 -0
  183. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/fused_moe.py +0 -0
  184. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/fused_moe_m8n8k4.py +0 -0
  185. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/fused_upsample_conv.py +0 -0
  186. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/gather_op.py +0 -0
  187. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/ge.py +0 -0
  188. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/gelu.py +0 -0
  189. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/gemv_vec.py +0 -0
  190. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/glu.py +0 -0
  191. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/grid_sampler.py +0 -0
  192. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/group_norm_tiled.py +0 -0
  193. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/groupnorm.py +0 -0
  194. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/gt.py +0 -0
  195. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/hardsigmoid.py +0 -0
  196. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/hardswish.py +0 -0
  197. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/im2col.py +0 -0
  198. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/index_add.py +0 -0
  199. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/index_put_op.py +0 -0
  200. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/index_select.py +0 -0
  201. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/index_wrappers.py +0 -0
  202. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/isfinite.py +0 -0
  203. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/isinf.py +0 -0
  204. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/isnan.py +0 -0
  205. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/launch_keys.py +0 -0
  206. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/launch_observer.py +0 -0
  207. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/layernorm.py +0 -0
  208. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/le.py +0 -0
  209. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/leaky_relu.py +0 -0
  210. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/lerp.py +0 -0
  211. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/linalg_wrappers.py +0 -0
  212. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/linspace_op.py +0 -0
  213. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/log.py +0 -0
  214. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/log_sigmoid.py +0 -0
  215. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/logical_and.py +0 -0
  216. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/logical_not.py +0 -0
  217. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/logical_or.py +0 -0
  218. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/logical_xor.py +0 -0
  219. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/loss_wrappers.py +0 -0
  220. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/lt.py +0 -0
  221. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/masked_fill.py +0 -0
  222. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/matmul.py +0 -0
  223. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/matmul_m8n8k4.py +0 -0
  224. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/max_pool2d.py +0 -0
  225. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/max_pool3d.py +0 -0
  226. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/max_reduce.py +0 -0
  227. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/maximum.py +0 -0
  228. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/mean.py +0 -0
  229. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/min_reduce.py +0 -0
  230. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/minimum.py +0 -0
  231. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/mish.py +0 -0
  232. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/mma_m8n8k4.py +0 -0
  233. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/moe_align.py +0 -0
  234. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/moe_decode_vec.py +0 -0
  235. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/moe_device_residency.py +0 -0
  236. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/moe_wrappers.py +0 -0
  237. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/mse_loss.py +0 -0
  238. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/mul.py +0 -0
  239. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/nan_to_num.py +0 -0
  240. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/ne.py +0 -0
  241. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/neg.py +0 -0
  242. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/nllloss.py +0 -0
  243. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/nonzero.py +0 -0
  244. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/nonzero_op.py +0 -0
  245. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/norm_wrappers.py +0 -0
  246. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/ones_op.py +0 -0
  247. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/oracles/__init__.py +0 -0
  248. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/oracles/conv2d_fp64.py +0 -0
  249. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/pad_op.py +0 -0
  250. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/paged_decode_attn.py +0 -0
  251. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/pixel_shuffle.py +0 -0
  252. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/pixel_unshuffle.py +0 -0
  253. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/pow.py +0 -0
  254. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/prod.py +0 -0
  255. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/rand_op.py +0 -0
  256. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/random_wrappers.py +0 -0
  257. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/reciprocal.py +0 -0
  258. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/reduction_wrappers.py +0 -0
  259. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/relu.py +0 -0
  260. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/remainder.py +0 -0
  261. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/repeat_interleave.py +0 -0
  262. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/repeat_op.py +0 -0
  263. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/residual_chain.py +0 -0
  264. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/rmsnorm.py +0 -0
  265. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/rope.py +0 -0
  266. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/rsqrt.py +0 -0
  267. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/scaled_softmax.py +0 -0
  268. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/scatter_op.py +0 -0
  269. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/screen_oracle.py +0 -0
  270. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/selu.py +0 -0
  271. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/shape_wrappers.py +0 -0
  272. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/sigmoid.py +0 -0
  273. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/silu.py +0 -0
  274. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/sin.py +0 -0
  275. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/softmax.py +0 -0
  276. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/softplus.py +0 -0
  277. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/sort_op.py +0 -0
  278. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/spectral_wrappers.py +0 -0
  279. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/sqrt.py +0 -0
  280. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/stack_op.py +0 -0
  281. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/std.py +0 -0
  282. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/strided_copy.py +0 -0
  283. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/sub.py +0 -0
  284. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/sum.py +0 -0
  285. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/tan.py +0 -0
  286. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/tanh.py +0 -0
  287. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/threshold.py +0 -0
  288. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/tiling.yml +0 -0
  289. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/tiling_policy.py +0 -0
  290. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/tiling_sizes.py +0 -0
  291. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/topk.py +0 -0
  292. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/trace_op.py +0 -0
  293. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/tril.py +0 -0
  294. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/triu.py +0 -0
  295. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/unfold_op.py +0 -0
  296. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/unique_op.py +0 -0
  297. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/upsample_bilinear2d.py +0 -0
  298. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/upsample_nearest2d.py +0 -0
  299. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/upsample_nearest3d.py +0 -0
  300. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/var.py +0 -0
  301. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/vector_norm.py +0 -0
  302. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/weight_norm.py +0 -0
  303. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/where.py +0 -0
  304. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/wrappers.py +0 -0
  305. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/ops/zeros_op.py +0 -0
  306. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/quant/__init__.py +0 -0
  307. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/quant/quantized_tensor.py +0 -0
  308. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/random/__init__.py +0 -0
  309. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/random/mt_randn.py +0 -0
  310. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/random/rng_pin.py +0 -0
  311. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/random/rng_stream.py +0 -0
  312. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/random/seeded_draw.py +0 -0
  313. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/shape/__init__.py +0 -0
  314. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/src/nbxtensor/tensor.py +0 -0
  315. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_a_small_tensor_never_pins_a_large_block.py +0 -0
  316. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_a_sweep_refused_everywhere_says_so.py +0 -0
  317. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_a_triton_kernel_compiles_for_amd_and_launches_through_hip.py +0 -0
  318. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_allocator_interceptor.py +0 -0
  319. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_array_api_promotion.py +0 -0
  320. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_cpu_pool.py +0 -0
  321. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_device_facts.py +0 -0
  322. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_device_files.py +0 -0
  323. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_device_memory.py +0 -0
  324. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_dtype_convert.py +0 -0
  325. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_dtype_outputs.py +0 -0
  326. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_dtype_promotion.py +0 -0
  327. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_host.py +0 -0
  328. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_io_bf16_split.py +0 -0
  329. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_io_read_rate.py +0 -0
  330. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_nbx_dtype.py +0 -0
  331. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_no_environment_switch_changes_a_kernel.py +0 -0
  332. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_ops_cost.py +0 -0
  333. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_policy.py +0 -0
  334. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_readme.py +0 -0
  335. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_shape.py +0 -0
  336. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_the_backend_is_the_card_on_the_machine.py +0 -0
  337. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_the_compiler_key_is_prewarmed.py +0 -0
  338. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_the_hip_driver_keeps_the_launch_contract.py +0 -0
  339. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_the_launcher_refuses_a_backend_it_has_no_driver_for.py +0 -0
  340. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_the_level_zero_backend_keeps_its_contracts.py +0 -0
  341. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_the_malloc_trace_names_the_wrapper.py +0 -0
  342. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_the_metal_dispatch_facts_are_declared_or_refused.py +0 -0
  343. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_third_party_notices.py +0 -0
  344. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tests/test_version.py +0 -0
  345. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tools/device_key_census.py +0 -0
  346. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tools/device_key_trace.py +0 -0
  347. {nbxtensor-0.0.3 → nbxtensor-0.0.5}/tools/scatter_scan.py +0 -0
@@ -7,6 +7,43 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
7
7
 
8
8
  ## [Unreleased]
9
9
 
10
+ ## [0.0.5] - 2026-11-07
11
+
12
+ ### Fixed
13
+ - **The library runs on Windows**: weights are read at their offset without `os.preadv` (Windows' `ReadFile` with an
14
+ OVERLAPPED offset), the BLAS's thread control is found beside numpy (`numpy.libs`) instead of through /proc, and the
15
+ GPU libraries are opened by their Windows names (nvcuda.dll, cudart64_12.dll from the runtime wheel's `bin`, nvml.dll,
16
+ amdhip64_6.dll, ze_loader.dll, OpenCL.dll). Written and tested with Windows simulated: the project has no Windows
17
+ machine yet.
18
+ - **`python -m nbxtensor doctor` on a Mac without Triton exits 1**: it named the fix and exited 0, so a script could
19
+ not tell that nothing could run yet.
20
+ - **The library imports on Windows**: its page size came from `os.sysconf`, which Windows does not have; and the code
21
+ generator's identity now reads a backend's files as `/` paths with its `.pyd` and `.dll` binaries, so the same code
22
+ has the same identity on every system.
23
+ - **A certified setting that is not served says why**: where the directory holds a key proven under another code
24
+ generator than the running one, the line named a memory class that matched ("certified for 18 GB, this card is
25
+ 18 GB, not served"); it now names both generators and the re-proof command. A setting proven on another memory class
26
+ is still said as such.
27
+
28
+ ## [0.0.4] - 2026-11-07
29
+
30
+ ### Added
31
+ - **Triton for the Apple GPU in one command**: `python -m nbxtensor install-metal` installs the Metal wheels of the
32
+ project's fork NeuroBrix/triton-ext (Triton built with triton-ext, and imperatormk's AppleGPU backend as
33
+ `nbx-triton-apple`, triton-lang/triton-ext#126), each by the release URL and sha256 written in the library,
34
+ through pip with `--no-deps --require-hashes --no-index` — never an extra index nor a package name resolved on PyPI. The
35
+ 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
36
+ Apple-silicon Mac, on another Python, or below the macOS the wheels are built for. `python -m nbxtensor doctor` on a Mac
37
+ without it says so and names the command; the Metal backend's refusal names it too. On a Mac, nbxtensor depends on
38
+ `pyobjc-framework-Metal`, through which it opens the Metal device; without it the run fell to a CPU profile and was
39
+ refused for triton-cpu, it is now refused by name with its install line.
40
+
41
+ ### Fixed
42
+ - **nbxtensor installs on a Mac**: it depended on `triton` on every platform, and PyPI publishes no Triton for macOS, so
43
+ `pip install nbxtensor` — and with it the NeuroBrix engine, even its PyTorch engine on MPS — could not resolve on a
44
+ clean macOS environment. Triton is now a dependency where PyPI has it; on a Mac it is built from source with its Metal
45
+ backend (the README's install), and the library says by name what to build when it is absent.
46
+
10
47
  ## [0.0.3] - 2026-11-06
11
48
 
12
49
  ### Fixed
@@ -143,7 +180,9 @@ memory; the fix is in test).
143
180
  - **`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
181
  - **`conv1d_forward_kernel`**: launched by no route (a 1-D convolution runs through `conv2d`).
145
182
 
146
- [Unreleased]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.3...HEAD
183
+ [Unreleased]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.5...HEAD
184
+ [0.0.5]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.4...v0.0.5
185
+ [0.0.4]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.3...v0.0.4
147
186
  [0.0.3]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.2...v0.0.3
148
187
  [0.0.2]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.1...v0.0.2
149
188
  [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
3
+ Version: 0.0.5
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
@@ -24,7 +25,7 @@ Description-Content-Type: text/markdown
24
25
 
25
26
  Run AI models without PyTorch, on any GPU Triton supports: tensors, Triton kernels, planned memory and certified tuning.
26
27
 
27
- Runs wherever Triton runs — NVIDIA, AMD, Intel and Apple Silicon GPUs. Inference first; training is not in this
28
+ Runs wherever Triton runs — NVIDIA, AMD, Intel and Apple Silicon GPUs, on Linux, Windows and macOS. Inference first; training is not in this
28
29
  release. Found a bug on your hardware? [Open an issue](https://github.com/NeuroBrix/nbxtensor/issues) — we fix fast.
29
30
 
30
31
  [![PyPI](https://img.shields.io/pypi/v/nbxtensor)](https://pypi.org/project/nbxtensor/)
@@ -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, so Triton and its Metal backend are built from
159
- source, then nbxtensor is installed into that environment:
159
+ **macOS, Apple silicon (Metal).** PyPI publishes no Triton for macOS. `install-metal` installs NeuroBrix's Triton
160
+ build for Apple GPUs, [NeuroBrix/triton-ext](https://github.com/NeuroBrix/triton-ext) (Triton built with
161
+ `TRITON_EXT_ENABLED=1` and the `nbx-triton-apple` backend; upstream:
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
- 1. Triton (github.com/triton-lang/triton) built with `TRITON_EXT_ENABLED=1`, which lets out-of-tree backends plug in;
162
- 2. the AppleGPU backend of triton-ext (github.com/triton-lang/triton-ext, `backend/AppleGPU`), installed into the same
163
- environment;
164
- 3. `pyobjc-framework-Metal` (the library opens the Metal device through it);
165
- 4. Xcode's Metal toolchain (`xcrun metal` must find the real compiler, not Xcode's placeholder: `TOOLCHAINS=Metal`).
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.3**. Each fix published after it increments the number (0.0.4,
503
+ The first publication was **0.0.1**; this is **0.0.5**. Each fix published after it increments the number (0.0.6,
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
 
@@ -4,7 +4,7 @@
4
4
 
5
5
  Run AI models without PyTorch, on any GPU Triton supports: tensors, Triton kernels, planned memory and certified tuning.
6
6
 
7
- Runs wherever Triton runs — NVIDIA, AMD, Intel and Apple Silicon GPUs. Inference first; training is not in this
7
+ Runs wherever Triton runs — NVIDIA, AMD, Intel and Apple Silicon GPUs, on Linux, Windows and macOS. Inference first; training is not in this
8
8
  release. Found a bug on your hardware? [Open an issue](https://github.com/NeuroBrix/nbxtensor/issues) — we fix fast.
9
9
 
10
10
  [![PyPI](https://img.shields.io/pypi/v/nbxtensor)](https://pypi.org/project/nbxtensor/)
@@ -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, so Triton and its Metal backend are built from
139
- source, then nbxtensor is installed into that environment:
138
+ **macOS, Apple silicon (Metal).** PyPI publishes no Triton for macOS. `install-metal` installs NeuroBrix's Triton
139
+ build for Apple GPUs, [NeuroBrix/triton-ext](https://github.com/NeuroBrix/triton-ext) (Triton built with
140
+ `TRITON_EXT_ENABLED=1` and the `nbx-triton-apple` backend; upstream:
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
- 1. Triton (github.com/triton-lang/triton) built with `TRITON_EXT_ENABLED=1`, which lets out-of-tree backends plug in;
142
- 2. the AppleGPU backend of triton-ext (github.com/triton-lang/triton-ext, `backend/AppleGPU`), installed into the same
143
- environment;
144
- 3. `pyobjc-framework-Metal` (the library opens the Metal device through it);
145
- 4. Xcode's Metal toolchain (`xcrun metal` must find the real compiler, not Xcode's placeholder: `TOOLCHAINS=Metal`).
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.3**. Each fix published after it increments the number (0.0.4,
482
+ The first publication was **0.0.1**; this is **0.0.5**. Each fix published after it increments the number (0.0.6,
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.3"
7
+ version = "0.0.5"
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
- dependencies = ["numpy", "triton", "pyyaml", "nvidia-cuda-runtime-cu12; platform_system == 'Linux'"]
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"
@@ -13,5 +13,5 @@ first publication is 0.0.1, each fix after it increments, the Rust rewrite is
13
13
  """
14
14
 
15
15
  #: the one version number; `pyproject.toml` states the same (tests/test_version.py)
16
- __version__ = "0.0.3"
16
+ __version__ = "0.0.5"
17
17
  __all__: list = []
@@ -0,0 +1,39 @@
1
+ """`python -m nbxtensor doctor`: which GPU backend this machine runs, and for every NVIDIA, AMD or Intel GPU on its PCI
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`); 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`)."""
6
+ import sys
7
+
8
+
9
+ def doctor() -> int:
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 1 # nothing can run yet: a script reads it from the exit status
15
+ try:
16
+ backend = runtime.gpu_backend()
17
+ print(f"backend in force: {backend}" + (" (the Apple GPU, through Metal)" if backend == "metal" else ""))
18
+ except RuntimeError as exc:
19
+ backend = None
20
+ print(f"backend in force: none — {str(exc).splitlines()[0]}")
21
+ if backend != "metal" or install_help.pci_gpus(): # a Mac has no PCI GPU to report: nothing to install there
22
+ print(install_help.report())
23
+ return 0
24
+
25
+
26
+ def main(argv=None) -> int:
27
+ argv = sys.argv[1:] if argv is None else argv
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
36
+
37
+
38
+ if __name__ == "__main__":
39
+ raise SystemExit(main())
@@ -20,6 +20,7 @@ from __future__ import annotations
20
20
  import ctypes
21
21
  import functools
22
22
  import platform
23
+ import sys
23
24
  from pathlib import Path
24
25
  from typing import Optional, Tuple
25
26
 
@@ -33,6 +34,16 @@ _OPENBLAS_GET = ("scipy_openblas_get_num_threads64_", "scipy_openblas_get_num_th
33
34
 
34
35
 
35
36
  def _loaded_openblas() -> Optional[str]:
37
+ if sys.platform == "win32":
38
+ # no /proc on Windows: numpy's wheel ships its OpenBLAS beside the package (`numpy.libs`, older wheels
39
+ # `numpy/.libs`), loaded with numpy; ctypes opening the same path returns the module already in the process
40
+ import numpy
41
+ root = Path(numpy.__file__).resolve().parent
42
+ for d in (root.parent / "numpy.libs", root / ".libs"):
43
+ hits = sorted(d.glob("*openblas*.dll")) if d.is_dir() else []
44
+ if hits:
45
+ return str(hits[0])
46
+ return None
36
47
  maps = Path("/proc/self/maps")
37
48
  if not maps.is_file():
38
49
  return None
@@ -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)
@@ -488,14 +488,15 @@ class CudaDriver(_ModuleDriver):
488
488
 
489
489
  def __init__(self) -> None:
490
490
  lib = None
491
- for name in ("libcuda.so.1", "libcuda.so"):
491
+ from nbxtensor.backend.libnames import CUDA_DRIVER
492
+ for name in CUDA_DRIVER:
492
493
  try:
493
494
  lib = ctypes.CDLL(name)
494
495
  break
495
496
  except OSError:
496
497
  continue
497
498
  if lib is None:
498
- raise RuntimeError("NeuroBrix launcher: libcuda not found")
499
+ raise RuntimeError(f"NeuroBrix launcher: the CUDA driver library not found (tried {CUDA_DRIVER})")
499
500
  self.lib = lib
500
501
  self._check(lib.cuInit(0), "cuInit")
501
502
  # ctypes infers the type of every argument at every call unless the signature is
@@ -27,7 +27,7 @@ import threading
27
27
  from typing import Dict, Optional, Tuple
28
28
 
29
29
  #: the Level Zero loader (`libze1` on Ubuntu), tried in order
30
- LIBS = ["libze_loader.so.1", "libze_loader.so"]
30
+ from nbxtensor.backend.libnames import LEVEL_ZERO as LIBS # noqa: E402
31
31
 
32
32
  # ZE_STRUCTURE_TYPE_* (ze.py)
33
33
  _ST_DRIVER_PROPERTIES, _ST_DEVICE_COMPUTE, _ST_QUEUE_GROUP, _ST_DEVICE_MEMORY = 0x1, 0x4, 0x6, 0x7
@@ -558,7 +558,7 @@ def runtime() -> LevelZeroRuntime:
558
558
 
559
559
 
560
560
  #: the OpenCL ICD loader (`ocl-icd-libopencl1`; Intel's runtime registers itself in it with intel-opencl-icd)
561
- OPENCL_LIBS = ["libOpenCL.so.1", "libOpenCL.so"]
561
+ from nbxtensor.backend.libnames import OPENCL as OPENCL_LIBS # noqa: E402
562
562
  _CL_DEVICE_TYPE_GPU, _CL_DEVICE_VENDOR_ID, _CL_DEVICE_EXTENSIONS, _CL_DEVICE_ID_INTEL = 4, 0x1001, 0x1030, 0x4251
563
563
 
564
564
  #: target facts Intel's compiler reads (intel/backend/compiler.py `parse_target`) -> the OpenCL extension that states
@@ -0,0 +1,26 @@
1
+ """The vendor libraries the backends open through ctypes, named as each system names them: a shared object on Linux
2
+ (`libcuda.so.1`), a DLL on Windows (`nvcuda.dll`). Read by the backend tables (`backend.runtime`), the launch driver
3
+ (`backend.launcher`), the graph replay (`backend.replay_graph`), the expandable pool (`memory.vmm`) and Level Zero
4
+ (`backend.level_zero`). Windows' names are the vendors' (NVIDIA: nvcuda.dll in System32 with the driver, cudart64_12.dll
5
+ in the CUDA runtime wheel's `bin`, nvml.dll; AMD HIP SDK: amdhip64_6.dll; Intel: ze_loader.dll, OpenCL.dll).
6
+ """
7
+ from __future__ import annotations
8
+
9
+ import sys
10
+
11
+ WINDOWS = sys.platform == "win32"
12
+
13
+ #: the CUDA runtime, after the one the environment's wheel carries (`runtime._environment_runtime_libs`)
14
+ CUDA_RUNTIME = ["cudart64_12.dll", "cudart64_110.dll"] if WINDOWS else ["libcudart.so", "libcudart.so.12", "libcudart.so.11.0"]
15
+ #: the CUDA runtime file inside the nvidia-cuda-runtime wheel, and the wheel's directory that holds it
16
+ CUDA_RUNTIME_IN_WHEEL = ("bin", "cudart64_12.dll") if WINDOWS else ("lib", "libcudart.so.12")
17
+ #: the CUDA driver API
18
+ CUDA_DRIVER = ["nvcuda.dll"] if WINDOWS else ["libcuda.so.1", "libcuda.so"]
19
+ #: NVIDIA's management library
20
+ NVML = ["nvml.dll"] if WINDOWS else ["libnvidia-ml.so.1", "libnvidia-ml.so"]
21
+ #: the HIP runtime
22
+ HIP_RUNTIME = ["amdhip64_6.dll", "amdhip64.dll"] if WINDOWS else ["libamdhip64.so", "libamdhip64.so.5"]
23
+ #: the Level Zero loader
24
+ LEVEL_ZERO = ["ze_loader.dll"] if WINDOWS else ["libze_loader.so.1", "libze_loader.so"]
25
+ #: the OpenCL ICD loader
26
+ OPENCL = ["OpenCL.dll"] if WINDOWS else ["libOpenCL.so.1", "libOpenCL.so"]
@@ -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 a Triton backend that targets Metal, and "
577
- "none is installed.\n"
578
- "\n"
579
- "Upstream Triton has no Apple GPU target. An out-of-tree backend "
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
- "NeuroBrix does not install it for you: fetching a wheel at runtime "
585
- "is a\n"
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
- "Working alternative today: drop --triton. The default compiled "
591
- "engine runs\n"
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 "
@@ -70,7 +70,8 @@ class _CudaGraphAPI:
70
70
  _inst = None
71
71
 
72
72
  def __init__(self) -> None:
73
- lib = ctypes.CDLL("libcuda.so.1")
73
+ from nbxtensor.backend.libnames import CUDA_DRIVER
74
+ lib = ctypes.CDLL(CUDA_DRIVER[0])
74
75
 
75
76
  def bind(name, argtypes):
76
77
  fn = getattr(lib, name)
@@ -39,23 +39,26 @@ def _environment_runtime_libs(package: str, names) -> list:
39
39
  return []
40
40
  import os
41
41
  root = list(spec.submodule_search_locations)[0]
42
+ from nbxtensor.backend.libnames import CUDA_RUNTIME_IN_WHEEL, WINDOWS
42
43
  out = []
43
44
  for n in names:
44
- cand = os.path.join(root, "lib", n)
45
+ cand = os.path.join(root, CUDA_RUNTIME_IN_WHEEL[0], n)
45
46
  if os.path.exists(cand):
46
47
  out.append(cand)
48
+ if WINDOWS: # the DLL's own directory searched for what it loads
49
+ os.add_dll_directory(os.path.dirname(cand))
47
50
  return out
48
51
 
49
52
 
50
53
  # Runtime API mapping per backend
54
+ from nbxtensor.backend import libnames as _LIB # noqa: E402 the libraries as this system names them
51
55
  _GPU_BACKENDS = {
52
56
  "cuda": {
53
- "rt_libs": _environment_runtime_libs("nvidia.cuda_runtime", ["libcudart.so.12"])
54
- + ["libcudart.so", "libcudart.so.12", "libcudart.so.11.0"],
57
+ "rt_libs": _environment_runtime_libs("nvidia.cuda_runtime", [_LIB.CUDA_RUNTIME_IN_WHEEL[1]]) + _LIB.CUDA_RUNTIME,
55
58
  # the driver API's library: the virtual memory primitives of the expandable pool (`memory.vmm`, R16)
56
- "driver_libs": ["libcuda.so.1", "libcuda.so"],
59
+ "driver_libs": _LIB.CUDA_DRIVER,
57
60
  # the management library: what the driver attributes to THIS process on a device (`backend.process_memory`)
58
- "management_libs": ["libnvidia-ml.so.1", "libnvidia-ml.so"],
61
+ "management_libs": _LIB.NVML,
59
62
  "pci_bus_id": "cudaDeviceGetPCIBusId",
60
63
  "malloc": "cudaMalloc", "free": "cudaFree",
61
64
  "memcpy": "cudaMemcpy", "memset": "cudaMemset",
@@ -100,7 +103,7 @@ _GPU_BACKENDS = {
100
103
  "device_enable_peer_access": "cudaDeviceEnablePeerAccess",
101
104
  },
102
105
  "hip": {
103
- "rt_libs": ["libamdhip64.so", "libamdhip64.so.5"],
106
+ "rt_libs": _LIB.HIP_RUNTIME,
104
107
  # the same three facts as cuda's, declared ABSENT: the expandable pool speaks the CUDA driver's VMM calls only
105
108
  # (`memory.vmm`; HIP's hipMemCreate family is not wired), so a HIP device keeps its driver segments; and no
106
109
  # per-process accounting is read on ROCm (`backend.process_memory` answers None, the runtime's growth is not
@@ -11,7 +11,8 @@ import time
11
11
  from concurrent.futures import ThreadPoolExecutor
12
12
  from typing import Dict, List, Optional, Sequence
13
13
 
14
- __all__ = ["PIECE", "WITNESS_BYTES", "START_BYTES", "mount_of", "pieces", "trial", "choose", "measure", "workers_for"]
14
+ __all__ = ["PIECE", "WITNESS_BYTES", "START_BYTES", "mount_of", "pieces", "trial", "choose", "measure", "workers_for",
15
+ "can_bypass_the_cache"]
15
16
 
16
17
  PIECE = 16 << 20 # the loaders' read piece
17
18
 
@@ -63,14 +64,21 @@ def pieces(files, max_bytes: int):
63
64
  return out, total
64
65
 
65
66
 
67
+ def can_bypass_the_cache() -> bool:
68
+ """Whether this system lets a measure read past its page cache (macOS F_NOCACHE + msync, Linux posix_fadvise);
69
+ Windows offers neither through Python, and a rate read from the cache is not the storage's."""
70
+ return sys.platform == "darwin" or hasattr(os, "posix_fadvise")
71
+
72
+
66
73
  def trial(pieces, workers: int) -> float:
67
74
  """Bytes per second reading `pieces` with `workers` threads, the cache bypassed."""
75
+ from nbxtensor.io.safetensors import _read_at
68
76
  for path in {p for p, _, _ in pieces}:
69
77
  _evict(path, os.path.getsize(path))
70
78
  fds = {}
71
79
  for path, _, _ in pieces:
72
80
  if path not in fds:
73
- fd = os.open(path, os.O_RDONLY)
81
+ fd = os.open(path, os.O_RDONLY | getattr(os, "O_BINARY", 0))
74
82
  _no_cache(fd)
75
83
  fds[path] = fd
76
84
  bufs = [bytearray(PIECE) for _ in range(workers)]
@@ -80,7 +88,7 @@ def trial(pieces, workers: int) -> float:
80
88
  buf = memoryview(bufs[i % workers])[:n]
81
89
  got = 0
82
90
  while got < n:
83
- got += os.preadv(fds[path], [buf[got:]], off + got)
91
+ got += _read_at(fds[path], buf[got:], off + got)
84
92
  return n
85
93
 
86
94
  t = time.perf_counter()
@@ -146,6 +154,9 @@ def workers_for(directory, witness_dir=None, max_workers: Optional[int] = None,
146
154
  up to everything the files hold. When no size gives a stable measure, refused by name: never a silent None, never
147
155
  a default."""
148
156
  from pathlib import Path
157
+ if not can_bypass_the_cache():
158
+ raise RuntimeError(f"ZERO FALLBACK: no uncached read-rate measure on this system ({sys.platform}: no way to "
159
+ f"read past the page cache) — no worker count is assumed")
149
160
  root = Path(directory)
150
161
  max_workers = max_workers or os.cpu_count() or 1
151
162
  files = [str(p) for p in sorted(root.glob(pattern), key=lambda p: -p.stat().st_size)[:4]]