nbxtensor 0.0.4__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 (346) hide show
  1. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/CHANGELOG.md +20 -1
  2. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/PKG-INFO +7 -7
  3. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/README.md +6 -6
  4. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/pyproject.toml +1 -1
  5. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/__init__.py +1 -1
  6. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/__main__.py +1 -1
  7. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/cpu/blas.py +11 -0
  8. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/launcher.py +3 -2
  9. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/level_zero.py +2 -2
  10. nbxtensor-0.0.5/src/nbxtensor/backend/libnames.py +26 -0
  11. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/replay_graph.py +2 -1
  12. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/runtime.py +9 -6
  13. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/io/read_rate.py +14 -3
  14. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/io/safetensors.py +35 -2
  15. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/autotune_certified.py +18 -8
  16. nbxtensor-0.0.5/tests/test_a_setting_not_served_says_why.py +32 -0
  17. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_install_metal_fetches_only_what_it_names.py +1 -1
  18. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/.github/PULL_REQUEST_TEMPLATE/certificate.md +0 -0
  19. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/CONTRIBUTING.md +0 -0
  20. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/LICENSE +0 -0
  21. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/THIRD_PARTY_NOTICES.md +0 -0
  22. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/assets/logo.png +0 -0
  23. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/__init__.py +0 -0
  24. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/capabilities.py +0 -0
  25. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/cpu/__init__.py +0 -0
  26. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/cpu/pool.py +0 -0
  27. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/cpu_backend.py +0 -0
  28. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/device_facts.py +0 -0
  29. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/device_files.py +0 -0
  30. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/device_names.py +0 -0
  31. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/amd/cdna.yml +0 -0
  32. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/amd/cdna2.yml +0 -0
  33. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/amd/cdna3.yml +0 -0
  34. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/amd/cdna4.yml +0 -0
  35. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/amd/rdna3.yml +0 -0
  36. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/amd/rdna4.yml +0 -0
  37. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m1.yml +0 -0
  38. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m1_max.yml +0 -0
  39. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m1_pro.yml +0 -0
  40. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m1_ultra.yml +0 -0
  41. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m2.yml +0 -0
  42. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m2_max.yml +0 -0
  43. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m2_pro.yml +0 -0
  44. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m2_ultra.yml +0 -0
  45. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m3.yml +0 -0
  46. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m3_max.yml +0 -0
  47. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m3_pro.yml +0 -0
  48. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m3_ultra.yml +0 -0
  49. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m4.yml +0 -0
  50. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m4_max.yml +0 -0
  51. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m4_pro.yml +0 -0
  52. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m5.yml +0 -0
  53. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m5_max.yml +0 -0
  54. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m5_pro.yml +0 -0
  55. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m5_ultra.yml +0 -0
  56. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_m6.yml +0 -0
  57. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/apple/apple_silicon.yml +0 -0
  58. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/intel/bmg.yml +0 -0
  59. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/intel/dg2.yml +0 -0
  60. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/intel/lnl.yml +0 -0
  61. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/intel/pvc.yml +0 -0
  62. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/nvidia/ampere.yml +0 -0
  63. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/nvidia/hopper.yml +0 -0
  64. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/devices/nvidia/volta.yml +0 -0
  65. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/faults.py +0 -0
  66. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/host_memory.py +0 -0
  67. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/install_help.py +0 -0
  68. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/install_metal.py +0 -0
  69. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/install_steps.yml +0 -0
  70. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/launcher_contract.py +0 -0
  71. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/level_zero_driver.py +0 -0
  72. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/metal_backend.py +0 -0
  73. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/metal_device.py +0 -0
  74. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/process_memory.py +0 -0
  75. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/backend/triton_ext_driver.py +0 -0
  76. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/dtype/__init__.py +0 -0
  77. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/dtype/convert.py +0 -0
  78. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/dtype/dtypes.yml +0 -0
  79. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/dtype/itemsize.py +0 -0
  80. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/dtype/nbx_dtype.py +0 -0
  81. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/dtype/operands.py +0 -0
  82. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/dtype/outputs.py +0 -0
  83. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/dtype/policy.py +0 -0
  84. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/dtype/policy.yml +0 -0
  85. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/dtype/promotion.py +0 -0
  86. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/host.py +0 -0
  87. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/io/__init__.py +0 -0
  88. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/__init__.py +0 -0
  89. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/allocator.py +0 -0
  90. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/arena.py +0 -0
  91. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/device_memory.py +0 -0
  92. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/device_transfer.py +0 -0
  93. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/layout.py +0 -0
  94. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/memory_pool.py +0 -0
  95. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/metal.py +0 -0
  96. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/oom_advice.py +0 -0
  97. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/pointer_tables.py +0 -0
  98. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/pool.py +0 -0
  99. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/shadow.py +0 -0
  100. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/slab.py +0 -0
  101. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/trace_replay.py +0 -0
  102. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/traces/sana-video-2b-720p.16g.2026-11-02.tsv +0 -0
  103. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/memory/vmm.py +0 -0
  104. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/__init__.py +0 -0
  105. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/_autotune_policy.py +0 -0
  106. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/_common.py +0 -0
  107. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/_configs.py +0 -0
  108. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/_cost.py +0 -0
  109. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/abs.py +0 -0
  110. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/adaptive_avg_pool2d.py +0 -0
  111. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/add.py +0 -0
  112. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/addcdiv.py +0 -0
  113. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/addcmul.py +0 -0
  114. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/addmv_op.py +0 -0
  115. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/addr.py +0 -0
  116. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/all_reduce.py +0 -0
  117. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/amax.py +0 -0
  118. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/any_reduce.py +0 -0
  119. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/arange_op.py +0 -0
  120. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/argmax.py +0 -0
  121. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/argmin.py +0 -0
  122. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/atan2.py +0 -0
  123. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/attention_wrappers.py +0 -0
  124. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/autotune_bucket.py +0 -0
  125. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/autotune_cache.py +0 -0
  126. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/autotune_refusals.py +0 -0
  127. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/autotune_settings.py +0 -0
  128. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/avg_pool2d.py +0 -0
  129. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/baddbmm_op.py +0 -0
  130. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/batch_norm.py +0 -0
  131. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/binary_strided.py +0 -0
  132. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/bitwise_and.py +0 -0
  133. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/bitwise_left_shift.py +0 -0
  134. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/bitwise_not.py +0 -0
  135. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/bitwise_or.py +0 -0
  136. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/bitwise_right_shift.py +0 -0
  137. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/bucketize.py +0 -0
  138. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/cat_op.py +0 -0
  139. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/celu.py +0 -0
  140. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/certified_index.py +0 -0
  141. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/clamp.py +0 -0
  142. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/clamp_min.py +0 -0
  143. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/conv2d.py +0 -0
  144. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/conv2d_m8n8k4.py +0 -0
  145. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/conv_depthwise2d.py +0 -0
  146. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/conv_transpose2d.py +0 -0
  147. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/conv_wrappers.py +0 -0
  148. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/copy.py +0 -0
  149. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/copy_op.py +0 -0
  150. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/cos.py +0 -0
  151. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/count_nonzero.py +0 -0
  152. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/cross_entropy.py +0 -0
  153. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/cummax.py +0 -0
  154. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/cummin.py +0 -0
  155. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/cumsum.py +0 -0
  156. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/decode_attn_vec.py +0 -0
  157. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/depthwise_conv2d.py +0 -0
  158. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/dequant_gemv.py +0 -0
  159. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/dft.py +0 -0
  160. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/dispatch.py +0 -0
  161. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/div.py +0 -0
  162. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/dot_op.py +0 -0
  163. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/dropout.py +0 -0
  164. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/dtype_convert.py +0 -0
  165. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/elementwise_launch.py +0 -0
  166. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/elementwise_wrappers.py +0 -0
  167. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/elu.py +0 -0
  168. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/embedding.py +0 -0
  169. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/eq.py +0 -0
  170. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/erf.py +0 -0
  171. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/exp.py +0 -0
  172. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/exp2.py +0 -0
  173. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/fft_op.py +0 -0
  174. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/fill_op.py +0 -0
  175. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/flash_attention.py +0 -0
  176. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/flash_attention_m8n8k4.py +0 -0
  177. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/flash_decode.py +0 -0
  178. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/flip_op.py +0 -0
  179. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/floor.py +0 -0
  180. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/full_op.py +0 -0
  181. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/fused_moe.py +0 -0
  182. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/fused_moe_m8n8k4.py +0 -0
  183. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/fused_upsample_conv.py +0 -0
  184. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/gather_op.py +0 -0
  185. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/ge.py +0 -0
  186. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/gelu.py +0 -0
  187. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/gemv_vec.py +0 -0
  188. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/glu.py +0 -0
  189. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/grid_sampler.py +0 -0
  190. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/group_norm_tiled.py +0 -0
  191. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/groupnorm.py +0 -0
  192. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/gt.py +0 -0
  193. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/hardsigmoid.py +0 -0
  194. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/hardswish.py +0 -0
  195. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/im2col.py +0 -0
  196. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/index_add.py +0 -0
  197. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/index_put_op.py +0 -0
  198. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/index_select.py +0 -0
  199. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/index_wrappers.py +0 -0
  200. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/isfinite.py +0 -0
  201. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/isinf.py +0 -0
  202. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/isnan.py +0 -0
  203. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/launch_keys.py +0 -0
  204. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/launch_observer.py +0 -0
  205. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/layernorm.py +0 -0
  206. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/le.py +0 -0
  207. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/leaky_relu.py +0 -0
  208. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/lerp.py +0 -0
  209. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/linalg_wrappers.py +0 -0
  210. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/linspace_op.py +0 -0
  211. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/log.py +0 -0
  212. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/log_sigmoid.py +0 -0
  213. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/logical_and.py +0 -0
  214. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/logical_not.py +0 -0
  215. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/logical_or.py +0 -0
  216. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/logical_xor.py +0 -0
  217. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/loss_wrappers.py +0 -0
  218. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/lt.py +0 -0
  219. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/masked_fill.py +0 -0
  220. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/matmul.py +0 -0
  221. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/matmul_m8n8k4.py +0 -0
  222. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/max_pool2d.py +0 -0
  223. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/max_pool3d.py +0 -0
  224. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/max_reduce.py +0 -0
  225. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/maximum.py +0 -0
  226. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/mean.py +0 -0
  227. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/min_reduce.py +0 -0
  228. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/minimum.py +0 -0
  229. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/mish.py +0 -0
  230. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/mma_m8n8k4.py +0 -0
  231. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/moe_align.py +0 -0
  232. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/moe_decode_vec.py +0 -0
  233. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/moe_device_residency.py +0 -0
  234. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/moe_wrappers.py +0 -0
  235. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/mse_loss.py +0 -0
  236. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/mul.py +0 -0
  237. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/nan_to_num.py +0 -0
  238. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/ne.py +0 -0
  239. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/neg.py +0 -0
  240. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/nllloss.py +0 -0
  241. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/nonzero.py +0 -0
  242. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/nonzero_op.py +0 -0
  243. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/norm_wrappers.py +0 -0
  244. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/ones_op.py +0 -0
  245. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/oracles/__init__.py +0 -0
  246. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/oracles/conv2d_fp64.py +0 -0
  247. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/pad_op.py +0 -0
  248. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/paged_decode_attn.py +0 -0
  249. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/pixel_shuffle.py +0 -0
  250. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/pixel_unshuffle.py +0 -0
  251. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/pow.py +0 -0
  252. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/prod.py +0 -0
  253. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/rand_op.py +0 -0
  254. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/random_wrappers.py +0 -0
  255. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/reciprocal.py +0 -0
  256. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/reduction_wrappers.py +0 -0
  257. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/relu.py +0 -0
  258. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/remainder.py +0 -0
  259. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/repeat_interleave.py +0 -0
  260. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/repeat_op.py +0 -0
  261. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/residual_chain.py +0 -0
  262. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/rmsnorm.py +0 -0
  263. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/rope.py +0 -0
  264. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/rsqrt.py +0 -0
  265. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/scaled_softmax.py +0 -0
  266. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/scatter_op.py +0 -0
  267. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/screen_oracle.py +0 -0
  268. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/selu.py +0 -0
  269. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/shape_wrappers.py +0 -0
  270. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/sigmoid.py +0 -0
  271. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/silu.py +0 -0
  272. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/sin.py +0 -0
  273. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/softmax.py +0 -0
  274. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/softplus.py +0 -0
  275. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/sort_op.py +0 -0
  276. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/spectral_wrappers.py +0 -0
  277. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/sqrt.py +0 -0
  278. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/stack_op.py +0 -0
  279. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/std.py +0 -0
  280. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/strided_copy.py +0 -0
  281. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/sub.py +0 -0
  282. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/sum.py +0 -0
  283. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/tan.py +0 -0
  284. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/tanh.py +0 -0
  285. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/threshold.py +0 -0
  286. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/tiling.yml +0 -0
  287. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/tiling_policy.py +0 -0
  288. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/tiling_sizes.py +0 -0
  289. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/topk.py +0 -0
  290. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/trace_op.py +0 -0
  291. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/tril.py +0 -0
  292. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/triu.py +0 -0
  293. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/unfold_op.py +0 -0
  294. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/unique_op.py +0 -0
  295. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/upsample_bilinear2d.py +0 -0
  296. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/upsample_nearest2d.py +0 -0
  297. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/upsample_nearest3d.py +0 -0
  298. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/var.py +0 -0
  299. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/vector_norm.py +0 -0
  300. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/weight_norm.py +0 -0
  301. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/where.py +0 -0
  302. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/wrappers.py +0 -0
  303. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/ops/zeros_op.py +0 -0
  304. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/quant/__init__.py +0 -0
  305. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/quant/quantized_tensor.py +0 -0
  306. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/random/__init__.py +0 -0
  307. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/random/mt_randn.py +0 -0
  308. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/random/rng_pin.py +0 -0
  309. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/random/rng_stream.py +0 -0
  310. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/random/seeded_draw.py +0 -0
  311. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/shape/__init__.py +0 -0
  312. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/src/nbxtensor/tensor.py +0 -0
  313. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_a_small_tensor_never_pins_a_large_block.py +0 -0
  314. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_a_sweep_refused_everywhere_says_so.py +0 -0
  315. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_a_triton_kernel_compiles_for_amd_and_launches_through_hip.py +0 -0
  316. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_allocator_interceptor.py +0 -0
  317. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_array_api_promotion.py +0 -0
  318. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_cpu_pool.py +0 -0
  319. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_device_facts.py +0 -0
  320. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_device_files.py +0 -0
  321. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_device_memory.py +0 -0
  322. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_dtype_convert.py +0 -0
  323. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_dtype_outputs.py +0 -0
  324. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_dtype_promotion.py +0 -0
  325. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_host.py +0 -0
  326. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_io_bf16_split.py +0 -0
  327. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_io_read_rate.py +0 -0
  328. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_it_installs_on_a_mac.py +0 -0
  329. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_nbx_dtype.py +0 -0
  330. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_no_environment_switch_changes_a_kernel.py +0 -0
  331. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_ops_cost.py +0 -0
  332. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_policy.py +0 -0
  333. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_readme.py +0 -0
  334. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_shape.py +0 -0
  335. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_the_backend_is_the_card_on_the_machine.py +0 -0
  336. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_the_compiler_key_is_prewarmed.py +0 -0
  337. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_the_hip_driver_keeps_the_launch_contract.py +0 -0
  338. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_the_launcher_refuses_a_backend_it_has_no_driver_for.py +0 -0
  339. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_the_level_zero_backend_keeps_its_contracts.py +0 -0
  340. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_the_malloc_trace_names_the_wrapper.py +0 -0
  341. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_the_metal_dispatch_facts_are_declared_or_refused.py +0 -0
  342. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_third_party_notices.py +0 -0
  343. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tests/test_version.py +0 -0
  344. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tools/device_key_census.py +0 -0
  345. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tools/device_key_trace.py +0 -0
  346. {nbxtensor-0.0.4 → nbxtensor-0.0.5}/tools/scatter_scan.py +0 -0
@@ -7,6 +7,24 @@ 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
+
10
28
  ## [0.0.4] - 2026-11-07
11
29
 
12
30
  ### Added
@@ -162,7 +180,8 @@ memory; the fix is in test).
162
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.
163
181
  - **`conv1d_forward_kernel`**: launched by no route (a 1-D convolution runs through `conv2d`).
164
182
 
165
- [Unreleased]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.4...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
166
185
  [0.0.4]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.3...v0.0.4
167
186
  [0.0.3]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.2...v0.0.3
168
187
  [0.0.2]: https://github.com/NeuroBrix/nbxtensor/compare/v0.0.1...v0.0.2
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: nbxtensor
3
- Version: 0.0.4
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
@@ -25,7 +25,7 @@ Description-Content-Type: text/markdown
25
25
 
26
26
  Run AI models without PyTorch, on any GPU Triton supports: tensors, Triton kernels, planned memory and certified tuning.
27
27
 
28
- 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
29
29
  release. Found a bug on your hardware? [Open an issue](https://github.com/NeuroBrix/nbxtensor/issues) — we fix fast.
30
30
 
31
31
  [![PyPI](https://img.shields.io/pypi/v/nbxtensor)](https://pypi.org/project/nbxtensor/)
@@ -156,10 +156,10 @@ install commands when one is not.
156
156
 
157
157
  **From the NeuroBrix repository** (contributors): `pip install -e nbxtensor`.
158
158
 
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
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
163
  sha256 written in the library (Python 3.12, macOS 15 or later):
164
164
 
165
165
  ```
@@ -500,7 +500,7 @@ with the tensor class (API.md §4).
500
500
 
501
501
  ## Versions
502
502
 
503
- The first publication was **0.0.1**; this is **0.0.4**. Each fix published after it increments the number (0.0.5,
503
+ The first publication was **0.0.1**; this is **0.0.5**. Each fix published after it increments the number (0.0.6,
504
504
  …), with no alpha or beta label. The rewrite of the core in Rust will be **2.0.0**. `nbxtensor.__version__` is the
505
505
  installed version.
506
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/)
@@ -135,10 +135,10 @@ 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. `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
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
142
  sha256 written in the library (Python 3.12, macOS 15 or later):
143
143
 
144
144
  ```
@@ -479,7 +479,7 @@ with the tensor class (API.md §4).
479
479
 
480
480
  ## Versions
481
481
 
482
- The first publication was **0.0.1**; this is **0.0.4**. Each fix published after it increments the number (0.0.5,
482
+ The first publication was **0.0.1**; this is **0.0.5**. Each fix published after it increments the number (0.0.6,
483
483
  …), with no alpha or beta label. The rewrite of the core in Rust will be **2.0.0**. `nbxtensor.__version__` is the
484
484
  installed version.
485
485
 
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "nbxtensor"
7
- version = "0.0.4"
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" }
@@ -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.4"
16
+ __version__ = "0.0.5"
17
17
  __all__: list = []
@@ -11,7 +11,7 @@ def doctor() -> int:
11
11
  from nbxtensor.backend.metal_backend import is_apple_silicon, triton_metal_available
12
12
  if is_apple_silicon() and not triton_metal_available():
13
13
  print("Apple GPU found; Triton for Metal not installed — run: python -m nbxtensor install-metal")
14
- return 0
14
+ return 1 # nothing can run yet: a script reads it from the exit status
15
15
  try:
16
16
  backend = runtime.gpu_backend()
17
17
  print(f"backend in force: {backend}" + (" (the Apple GPU, through Metal)" if backend == "metal" else ""))
@@ -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
@@ -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"]
@@ -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]]
@@ -11,6 +11,7 @@ from __future__ import annotations
11
11
 
12
12
  import ctypes
13
13
  import math
14
+ import mmap
14
15
  import os
15
16
  from typing import Optional
16
17
 
@@ -23,7 +24,7 @@ from nbxtensor.dtype.convert import bf16_bits_to_f16, f32_to_bf16_bits
23
24
  from nbxtensor.shape import contiguous_strides
24
25
  from nbxtensor.memory.memory_pool import ComponentArena, HostPinnedArena
25
26
 
26
- PAGE = os.sysconf("SC_PAGE_SIZE")
27
+ PAGE = mmap.PAGESIZE # os.sysconf does not exist on Windows (its import stopped every plan there)
27
28
 
28
29
 
29
30
  def spans(n: int, unit: int, align: int, workers: int):
@@ -36,6 +37,38 @@ def spans(n: int, unit: int, align: int, workers: int):
36
37
  return [(a, min(a + step, n)) for a in range(0, n, step)]
37
38
 
38
39
 
40
+ def _read_at(fd: int, view: memoryview, offset: int) -> int:
41
+ """Read into `view` from the file's byte `offset`, at that offset and never through a shared file position (the
42
+ workers read one file at once): `os.preadv` where the system has it (Linux, macOS); on Windows, which has neither
43
+ preadv nor pread, `ReadFile` with an OVERLAPPED offset on the descriptor's handle — a positioned read. Returns the
44
+ bytes read (0 at the end of the file)."""
45
+ if hasattr(os, "preadv"):
46
+ return os.preadv(fd, [view], offset)
47
+ if hasattr(os, "pread"):
48
+ data = os.pread(fd, len(view), offset)
49
+ view[:len(data)] = data
50
+ return len(data)
51
+ import msvcrt
52
+ from ctypes import wintypes
53
+
54
+ class _Overlapped(ctypes.Structure):
55
+ _fields_ = [("Internal", ctypes.c_void_p), ("InternalHigh", ctypes.c_void_p), ("Offset", wintypes.DWORD),
56
+ ("OffsetHigh", wintypes.DWORD), ("hEvent", wintypes.HANDLE)]
57
+
58
+ n = min(len(view), 0x7FFFFFFF) # one ReadFile reads at most a DWORD's worth
59
+ ov = _Overlapped(0, 0, offset & 0xFFFFFFFF, offset >> 32, None)
60
+ got = wintypes.DWORD(0)
61
+ buf = (ctypes.c_char * n).from_buffer(view)
62
+ ok = ctypes.windll.kernel32.ReadFile(wintypes.HANDLE(msvcrt.get_osfhandle(fd)), buf, n, ctypes.byref(got),
63
+ ctypes.byref(ov))
64
+ if not ok:
65
+ err = ctypes.windll.kernel32.GetLastError()
66
+ if err == 38: # ERROR_HANDLE_EOF: a read at or past the end
67
+ return 0
68
+ raise OSError(err, f"ReadFile at offset {offset} of fd {fd}")
69
+ return int(got.value)
70
+
71
+
39
72
  def read_bytes(fd: int, offset: int, nbytes: int, io) -> np.ndarray:
40
73
  """nbytes of the file at offset, read by the loader's workers into one buffer (page-aligned
41
74
  spans; `preadv` may return short, so each span reads until it is full)."""
@@ -45,7 +78,7 @@ def read_bytes(fd: int, offset: int, nbytes: int, io) -> np.ndarray:
45
78
  def _one(span):
46
79
  a, b = span
47
80
  while a < b:
48
- got = os.preadv(fd, [view[a:b]], offset + a)
81
+ got = _read_at(fd, view[a:b], offset + a)
49
82
  if got <= 0:
50
83
  raise RuntimeError(f"weight read: {nbytes} bytes at offset {offset} of fd {fd} ended "
51
84
  f"{b - a} bytes short (file truncated?)")
@@ -444,13 +444,13 @@ def _backend_content_hash(backend) -> str:
444
444
  h = hashlib.sha256()
445
445
  for name in getattr(type(backend), "CODEGEN_ENV", ()) or ():
446
446
  h.update(f"{name}={name in os.environ}:{os.environ.get(name, '')}".encode())
447
- files = sorted(f for f in pkg.rglob("*") if f.is_file() and f.suffix in (".py", ".so", ".dylib")
447
+ files = sorted(f for f in pkg.rglob("*") if f.is_file() and f.suffix in (".py", ".so", ".dylib", ".pyd", ".dll")
448
448
  and "__pycache__" not in f.parts) if pkg.is_dir() else []
449
449
  plugin = getattr(mod, "PLUGIN_LIBRARY", None)
450
450
  if plugin is not None and Path(plugin).is_file() and Path(plugin) not in files:
451
451
  files.append(Path(plugin))
452
452
  for f in files:
453
- h.update(str(f.relative_to(pkg) if pkg in f.parents else f.name).encode())
453
+ h.update((f.relative_to(pkg).as_posix() if pkg in f.parents else f.name).encode()) # "/" on every OS
454
454
  h.update(hashlib.sha256(f.read_bytes()).digest())
455
455
  return f"content-{h.hexdigest()[:16]}"
456
456
 
@@ -1115,6 +1115,20 @@ def _explain_miss(kernel_qual: str, key: tuple, memory_gb: Optional[int]) -> str
1115
1115
  return f" (the application's explanation of the miss failed: {type(exc).__name__}: {exc})."
1116
1116
 
1117
1117
 
1118
+ def _not_served_because(raw: Dict[str, Any], here: Optional[int]) -> str:
1119
+ """Why an entry the directory holds for this key is not served here: proven under another code generator than the
1120
+ running one (for this card's memory class), or on another memory class (register 56). The Mac's new-user run on the
1121
+ published Metal wheels (2026-11-07): every Apple entry carried the dev environment's generator and the line said
1122
+ "certified for 18 GB, this card is 18 GB, not served" — a memory class that matched, the cause unnamed."""
1123
+ covered = sorted(covered_memory_gbs(raw))
1124
+ gens, gen = sorted(proof_backends(raw)), running_generator()
1125
+ if here is not None and here in covered and gen is not None and gen not in gens:
1126
+ return (f"certified for this card's {here} GB under {', '.join(gens) or 'no named generator'}, and this machine "
1127
+ f"runs {gen}: not served — re-proven under it by `neurobrix autotune certify --reprove-generator`")
1128
+ return (f"certified for {', '.join(f'{c} GB' for c in covered) or 'no known memory class'}, this card is "
1129
+ f"{f'{here} GB' if here is not None else 'of unknown memory'}, not served (register 56)")
1130
+
1131
+
1118
1132
  def refuse_missing(kernel_qual: str, tuner, key: tuple) -> None:
1119
1133
  """The certified-only answer to a miss: raised before any bench, naming the key, the profile,
1120
1134
  why a directory entry (if any) is not served, and what the census says."""
@@ -1129,9 +1143,7 @@ def refuse_missing(kernel_qual: str, tuner, key: tuple) -> None:
1129
1143
  try:
1130
1144
  raw = lookup(kernel_qual, tuner, key, ignore_switch=True, any_class=True)
1131
1145
  if raw is not None:
1132
- covered = sorted(covered_memory_gbs(raw))
1133
- why = (f"; the directory certifies it for {', '.join(f'{c} GB' for c in covered) or 'no known memory class'} "
1134
- f"and this card is {f'{here} GB' if here is not None else 'of unknown memory'} (register 56)")
1146
+ why = f"; the directory holds it: {_not_served_because(raw, here)}"
1135
1147
  except Exception as exc: # noqa: BLE001 — the reason failed to compute: said, the refusal stands
1136
1148
  why = f"; (could not read what the directory holds for it: {exc})"
1137
1149
  raise KeyNotCertified(
@@ -1159,10 +1171,8 @@ def announce_missing(kernel_qual: str, tuner, key: tuple) -> None:
1159
1171
  try:
1160
1172
  raw = lookup(kernel_qual, tuner, key, ignore_switch=True, any_class=True)
1161
1173
  if raw is not None:
1162
- covered = sorted(covered_memory_gbs(raw))
1163
1174
  here = executing_memory_gb(list((getattr(tuner, "nargs", None) or {}).values()) or None)
1164
- why = (f" — certified for {', '.join(f'{c} GB' for c in covered) or 'no known memory class'}, "
1165
- f"this card is {f'{here} GB' if here is not None else 'of unknown memory'}, not served (register 56)")
1175
+ why = f" — {_not_served_because(raw, here)}"
1166
1176
  except Exception as exc: # the reason failed to compute: say that, never an empty reason
1167
1177
  why = f" — (could not read what the directory holds for it: {exc})"
1168
1178
  print(f"[autotune] no certified setting for {kernel_short(kernel_qual)} {output_dtype(tuner, key)} "
@@ -0,0 +1,32 @@
1
+ """A certified setting the directory holds but does not serve says why: proven under another code generator than the
2
+ running one, or on another memory class. The Mac's new-user run on the published Metal wheels (2026-11-07): every Apple
3
+ entry was proven under the development environment's generator, none was served, and the line said "certified for
4
+ 18 GB, this card is 18 GB, not served (register 56)" — the memory class matched and the cause was not named.
5
+
6
+ What this test would do if the code were wrong: a generator mismatch reported as a memory class (red), a memory-class
7
+ miss reported as a generator (red)."""
8
+ from __future__ import annotations
9
+
10
+ from nbxtensor.ops import autotune_certified as C
11
+
12
+
13
+ def _entry(gen_version: str, memory_mb: int):
14
+ return {"proof": {"backend": {"triton": gen_version, "name": "metal"},
15
+ "machine": {"device": {"memory_mb": memory_mb}}}}
16
+
17
+
18
+ def test_a_generator_mismatch_on_the_right_memory_class_names_the_generators(monkeypatch):
19
+ monkeypatch.setattr(C, "running_generator", lambda: "triton 3.8.0+git4a15f415.nbx1 metal")
20
+ raw = _entry("3.8.0+git4a15f415", 18432)
21
+ gens = sorted(C.proof_backends(raw))
22
+ why = C._not_served_because(raw, 18)
23
+ assert why.startswith("certified for this card's 18 GB under "), why
24
+ assert gens[0] in why and "triton 3.8.0+git4a15f415.nbx1 metal" in why and "--reprove-generator" in why, why
25
+ assert "register 56" not in why
26
+
27
+
28
+ def test_another_memory_class_is_said_as_such(monkeypatch):
29
+ raw = _entry("3.8.0", 32768)
30
+ monkeypatch.setattr(C, "running_generator", lambda: sorted(C.proof_backends(raw))[0])
31
+ why = C._not_served_because(raw, 16)
32
+ assert "certified for 32 GB, this card is 16 GB, not served (register 56)" == why, why
@@ -94,7 +94,7 @@ def test_doctor_on_a_mac_without_triton_names_the_command(monkeypatch, capsys):
94
94
  from nbxtensor.backend import metal_backend
95
95
  monkeypatch.setattr(metal_backend, "is_apple_silicon", lambda: True)
96
96
  monkeypatch.setattr(metal_backend, "triton_metal_available", lambda: False)
97
- assert main_mod.main(["doctor"]) == 0
97
+ assert main_mod.main(["doctor"]) == 1 # nothing can run yet: said by the exit status too (the Mac, 04:21)
98
98
  assert capsys.readouterr().out.strip() == (
99
99
  "Apple GPU found; Triton for Metal not installed — run: python -m nbxtensor install-metal")
100
100
 
File without changes
File without changes
File without changes