pygpgpu 0.2.0__tar.gz → 0.2.2__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 (473) hide show
  1. {pygpgpu-0.2.0/pygpgpu.egg-info → pygpgpu-0.2.2}/PKG-INFO +386 -39
  2. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/README.md +937 -591
  3. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/README.zh-CN.md +891 -576
  4. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/01_basic_kernel.py +5 -2
  5. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/05_gnp_zero_copy_and_atomics.py +115 -111
  6. pygpgpu-0.2.2/examples/06_handwritten_kernels.py +76 -0
  7. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/07_low_level_oop.py +66 -66
  8. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/09_reduction.py +159 -160
  9. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/11_python_backend.py +83 -84
  10. pygpgpu-0.2.2/examples/11_returns_and_scope.py +109 -0
  11. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/13_image.py +1 -1
  12. pygpgpu-0.2.2/examples/14_heterogeneous_data.py +341 -0
  13. pygpgpu-0.2.2/examples/15_kernel_import.py +129 -0
  14. pygpgpu-0.2.2/examples/16_scientific_math.py +63 -0
  15. pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle1_Map.py +35 -0
  16. pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle2_Zip.py +37 -0
  17. pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle3_Guards.py +37 -0
  18. pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle4_Map2D.py +37 -0
  19. pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle5_Broadcast.py +40 -0
  20. pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle6_Blocks.py +37 -0
  21. pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle7_Blocks2D.py +38 -0
  22. pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle8_Shared.py +48 -0
  23. pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle1_Map.cu +21 -0
  24. pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle2_Zip.cu +22 -0
  25. pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle3_Guards.cu +26 -0
  26. pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle4_Map2D.cu +29 -0
  27. pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle5_Broadcast.cu +29 -0
  28. pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle6_Blocks.cu +26 -0
  29. pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle7_Blocks2D.cu +29 -0
  30. pygpgpu-0.2.2/examples/GPU-Puzzles/__cucache__/pykernels/Puzzle8_Shared.cu +34 -0
  31. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/README.md +82 -75
  32. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/README.zh-CN.md +66 -59
  33. pygpgpu-0.2.2/examples/digital_image_processing/01_image_sampling.py +12 -0
  34. pygpgpu-0.2.2/examples/digital_image_processing/02_image_quantify.py +11 -0
  35. pygpgpu-0.2.2/examples/digital_image_processing/03_image_brightness_adjustment.py +11 -0
  36. pygpgpu-0.2.2/examples/digital_image_processing/04_image_contrast_adjustment.py +11 -0
  37. pygpgpu-0.2.2/examples/digital_image_processing/05_swap_channels.py +10 -0
  38. pygpgpu-0.2.2/examples/digital_image_processing/06_merge_images.py +12 -0
  39. pygpgpu-0.2.2/examples/digital_image_processing/07_gamma.py +10 -0
  40. pygpgpu-0.2.2/examples/digital_image_processing/__clcache__/pykernels/image_processing.cl +45 -0
  41. pygpgpu-0.2.2/examples/digital_image_processing/__cucache__/pykernels/image_processing.cu +46 -0
  42. pygpgpu-0.2.2/examples/digital_image_processing/__hipcache__/pykernels/__hipcache__/AMD Radeon(TM) 610M gfx1036/image_processing.hip_5f2fc99ee13d66d4837d4cafdf514803_clean.hip +44 -0
  43. pygpgpu-0.2.2/examples/digital_image_processing/__hipcache__/pykernels/image_processing.hip +43 -0
  44. pygpgpu-0.2.2/examples/digital_image_processing/image_processing.py +154 -0
  45. pygpgpu-0.2.2/examples/kernel_import/README.md +45 -0
  46. pygpgpu-0.2.2/examples/kernel_import/__hipcache__/AMD Radeon(TM) 610M gfx1036/vecops.hip_2cec05837e5f9def95fd37d804cdd3eb_clean.hip +51 -0
  47. pygpgpu-0.2.2/examples/kernel_import/__init__.py +17 -0
  48. pygpgpu-0.2.2/examples/kernel_import/vecops.cl +58 -0
  49. pygpgpu-0.2.2/examples/kernel_import/vecops.cu +45 -0
  50. pygpgpu-0.2.2/examples/kernel_import/vecops.hip +47 -0
  51. pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/01_first_image.py +26 -0
  52. pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/camera.py +89 -0
  53. pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/hittable.py +27 -0
  54. pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/interval.py +31 -0
  55. pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/main.py +16 -0
  56. pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/ray.py +11 -0
  57. pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/scene.py +41 -0
  58. pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/sphere.py +44 -0
  59. pygpgpu-0.2.2/examples/ray_tarcing_in_one_weekend/utils.py +20 -0
  60. pygpgpu-0.2.2/pygpgpu/__init__.py +190 -0
  61. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/ts_parser.py +155 -33
  62. pygpgpu-0.2.2/pygpgpu/cuda/__init__.py +10 -0
  63. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/__init__.py +70 -70
  64. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/cuda.py +73 -14
  65. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/cuinfo.py +891 -853
  66. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/cutypes.py +610 -610
  67. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/nvrtc.py +90 -13
  68. pygpgpu-0.2.2/pygpgpu/cuda/libraries/__init__.py +117 -0
  69. pygpgpu-0.2.2/pygpgpu/cuda/libraries/_base.py +375 -0
  70. pygpgpu-0.2.2/pygpgpu/cuda/libraries/cublas.py +425 -0
  71. pygpgpu-0.2.2/pygpgpu/cuda/libraries/cublaslt.py +356 -0
  72. pygpgpu-0.2.2/pygpgpu/cuda/libraries/cudart.py +403 -0
  73. pygpgpu-0.2.2/pygpgpu/cuda/libraries/cufft.py +219 -0
  74. pygpgpu-0.2.2/pygpgpu/cuda/libraries/cusolver.py +373 -0
  75. pygpgpu-0.2.2/pygpgpu/cuda/libraries/cusparse.py +343 -0
  76. pygpgpu-0.2.2/pygpgpu/cuda/libraries/wrappers.py +818 -0
  77. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/buffer.py +26 -14
  78. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/build_options.py +5 -1
  79. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/kernel.py +774 -682
  80. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/nvrtc_program.py +4 -1
  81. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/program_info.py +223 -173
  82. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/program_parser.py +93 -8
  83. pygpgpu-0.2.2/pygpgpu/hip/__init__.py +9 -0
  84. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/driver/__init__.py +28 -28
  85. pygpgpu-0.2.2/pygpgpu/hip/driver/hip.py +264 -0
  86. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/driver/hipinfo.py +6 -0
  87. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/buffer.py +26 -14
  88. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/hip_compiler.py +238 -209
  89. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/kernel.py +88 -6
  90. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/program_info.py +58 -5
  91. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/program_parser.py +93 -8
  92. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/image.py +562 -552
  93. pygpgpu-0.2.2/pygpgpu/kernel/__init__.py +23 -0
  94. pygpgpu-0.2.2/pygpgpu/kernel/builtin_registry.py +42 -0
  95. pygpgpu-0.2.2/pygpgpu/kernel/builtins.py +580 -0
  96. pygpgpu-0.2.2/pygpgpu/kernel/decorator.py +3263 -0
  97. pygpgpu-0.2.2/pygpgpu/kernel/device_builtins.py +547 -0
  98. pygpgpu-0.2.2/pygpgpu/kernel/device_library.py +216 -0
  99. pygpgpu-0.2.2/pygpgpu/kernel/host_runtime.py +130 -0
  100. pygpgpu-0.2.2/pygpgpu/kernel/include/quat.h +276 -0
  101. pygpgpu-0.2.2/pygpgpu/kernel/metal_codegen.py +113 -0
  102. pygpgpu-0.2.2/pygpgpu/kernel/objects.py +1576 -0
  103. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/kernel/python_backend.py +638 -714
  104. pygpgpu-0.2.2/pygpgpu/kernel/quaternion.py +210 -0
  105. pygpgpu-0.2.2/pygpgpu/kernel/sci_math.py +608 -0
  106. pygpgpu-0.2.2/pygpgpu/kernel/source_paths.py +640 -0
  107. pygpgpu-0.2.2/pygpgpu/kernel/structs.py +644 -0
  108. pygpgpu-0.2.2/pygpgpu/kernel/translator.py +8494 -0
  109. pygpgpu-0.2.2/pygpgpu/kernel_import.py +642 -0
  110. pygpgpu-0.2.2/pygpgpu/kernel_import_stub.py +172 -0
  111. pygpgpu-0.2.2/pygpgpu/metal/__init__.py +8 -0
  112. pygpgpu-0.2.2/pygpgpu/metal/driver/__init__.py +2 -0
  113. pygpgpu-0.2.2/pygpgpu/metal/driver/metal.py +193 -0
  114. pygpgpu-0.2.2/pygpgpu/metal/driver/objc.py +148 -0
  115. pygpgpu-0.2.2/pygpgpu/metal/oop/__init__.py +3 -0
  116. pygpgpu-0.2.2/pygpgpu/metal/oop/buffer.py +196 -0
  117. pygpgpu-0.2.2/pygpgpu/metal/oop/context.py +99 -0
  118. pygpgpu-0.2.2/pygpgpu/metal/oop/devices.py +145 -0
  119. pygpgpu-0.2.2/pygpgpu/metal/oop/event.py +44 -0
  120. pygpgpu-0.2.2/pygpgpu/metal/oop/kernel.py +308 -0
  121. pygpgpu-0.2.2/pygpgpu/metal/oop/metalobject.py +69 -0
  122. pygpgpu-0.2.2/pygpgpu/metal/oop/program.py +123 -0
  123. pygpgpu-0.2.2/pygpgpu/metal/oop/program_info.py +163 -0
  124. pygpgpu-0.2.2/pygpgpu/metal/oop/program_parser.py +199 -0
  125. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/__init__.py +7 -6
  126. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/__init__.py +1 -1
  127. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/cl.py +59 -5
  128. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/__init__.py +0 -1
  129. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/buffer.py +29 -13
  130. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/kernel.py +112 -26
  131. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/platforms.py +4 -0
  132. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/program_info.py +32 -2
  133. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/program_parser.py +66 -8
  134. pygpgpu-0.2.2/pygpgpu/opencl/oop/struct_layout.py +122 -0
  135. pygpgpu-0.2.2/pygpgpu/py.typed +0 -0
  136. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/utils.py +88 -0
  137. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/__init__.py +66 -51
  138. pygpgpu-0.2.2/pygpgpu/vec_types/funcs.py +229 -0
  139. pygpgpu-0.2.2/pygpgpu/vec_types/layout.py +481 -0
  140. {pygpgpu-0.2.0 → pygpgpu-0.2.2/pygpgpu.egg-info}/PKG-INFO +386 -39
  141. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu.egg-info/SOURCES.txt +148 -26
  142. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu.egg-info/requires.txt +3 -3
  143. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pyproject.toml +41 -13
  144. pygpgpu-0.2.2/tests/test_builtins.py +282 -0
  145. pygpgpu-0.2.2/tests/test_cuda_libraries.py +672 -0
  146. pygpgpu-0.2.2/tests/test_device_library.py +125 -0
  147. pygpgpu-0.2.2/tests/test_driver_background_init.py +127 -0
  148. pygpgpu-0.2.2/tests/test_kernel_aliases_and_ranks.py +345 -0
  149. pygpgpu-0.2.2/tests/test_kernel_argument_errors.py +338 -0
  150. pygpgpu-0.2.2/tests/test_kernel_atomic_augassign.py +242 -0
  151. pygpgpu-0.2.2/tests/test_kernel_atomics.py +202 -0
  152. pygpgpu-0.2.2/tests/test_kernel_body_arrays.py +151 -0
  153. pygpgpu-0.2.2/tests/test_kernel_boolean_ops.py +92 -0
  154. pygpgpu-0.2.2/tests/test_kernel_carried_scalar.py +153 -0
  155. pygpgpu-0.2.2/tests/test_kernel_clip_where.py +191 -0
  156. pygpgpu-0.2.2/tests/test_kernel_comparison_builtins.py +200 -0
  157. pygpgpu-0.2.2/tests/test_kernel_copy_policy.py +81 -0
  158. pygpgpu-0.2.2/tests/test_kernel_creation.py +367 -0
  159. pygpgpu-0.2.2/tests/test_kernel_creation_functions.py +296 -0
  160. pygpgpu-0.2.2/tests/test_kernel_ctypes_structures.py +332 -0
  161. pygpgpu-0.2.2/tests/test_kernel_ctypes_unions_and_enums.py +320 -0
  162. pygpgpu-0.2.2/tests/test_kernel_declared_domain.py +146 -0
  163. pygpgpu-0.2.2/tests/test_kernel_generated_layout.py +174 -0
  164. pygpgpu-0.2.2/tests/test_kernel_generated_source_cache.py +165 -0
  165. pygpgpu-0.2.2/tests/test_kernel_generated_spacing.py +155 -0
  166. pygpgpu-0.2.2/tests/test_kernel_global_array.py +100 -0
  167. pygpgpu-0.2.2/tests/test_kernel_histogram.py +180 -0
  168. pygpgpu-0.2.2/tests/test_kernel_host_region_names.py +80 -0
  169. pygpgpu-0.2.2/tests/test_kernel_host_regions.py +110 -0
  170. pygpgpu-0.2.2/tests/test_kernel_host_write_roundtrip.py +49 -0
  171. pygpgpu-0.2.2/tests/test_kernel_import.py +231 -0
  172. pygpgpu-0.2.2/tests/test_kernel_inplace_and_containment.py +264 -0
  173. pygpgpu-0.2.2/tests/test_kernel_local_memory.py +306 -0
  174. pygpgpu-0.2.2/tests/test_kernel_magic_methods.py +198 -0
  175. pygpgpu-0.2.2/tests/test_kernel_math_builtins.py +140 -0
  176. pygpgpu-0.2.2/tests/test_kernel_method_calls.py +187 -0
  177. pygpgpu-0.2.2/tests/test_kernel_narrow_arguments.py +78 -0
  178. pygpgpu-0.2.2/tests/test_kernel_narrow_types.py +113 -0
  179. pygpgpu-0.2.2/tests/test_kernel_ndindex_forms.py +141 -0
  180. pygpgpu-0.2.2/tests/test_kernel_nested_launch.py +275 -0
  181. pygpgpu-0.2.2/tests/test_kernel_object_arguments.py +529 -0
  182. pygpgpu-0.2.2/tests/test_kernel_outer_scope.py +366 -0
  183. pygpgpu-0.2.2/tests/test_kernel_partial_write.py +101 -0
  184. pygpgpu-0.2.2/tests/test_kernel_quaternion.py +346 -0
  185. pygpgpu-0.2.2/tests/test_kernel_rank_mismatch.py +125 -0
  186. pygpgpu-0.2.2/tests/test_kernel_returns.py +389 -0
  187. pygpgpu-0.2.2/tests/test_kernel_scalar_calls.py +260 -0
  188. pygpgpu-0.2.2/tests/test_kernel_serial_loop_above_dispatch.py +68 -0
  189. pygpgpu-0.2.2/tests/test_kernel_shared_scalar.py +198 -0
  190. pygpgpu-0.2.2/tests/test_kernel_struct_name_hints.py +88 -0
  191. pygpgpu-0.2.2/tests/test_kernel_struct_types.py +276 -0
  192. pygpgpu-0.2.2/tests/test_kernel_structure_construction.py +225 -0
  193. pygpgpu-0.2.2/tests/test_kernel_tail_region.py +62 -0
  194. pygpgpu-0.2.2/tests/test_kernel_translation_switch.py +141 -0
  195. pygpgpu-0.2.2/tests/test_kernel_typing.py +204 -0
  196. pygpgpu-0.2.2/tests/test_kernel_vector3.py +178 -0
  197. pygpgpu-0.2.2/tests/test_kernel_vector_arguments.py +190 -0
  198. pygpgpu-0.2.2/tests/test_kernel_vector_layout.py +307 -0
  199. pygpgpu-0.2.2/tests/test_metal_codegen.py +427 -0
  200. pygpgpu-0.2.2/tests/test_metal_launch_path.py +302 -0
  201. pygpgpu-0.2.2/tests/test_sci_math.py +235 -0
  202. pygpgpu-0.2.2/tests/test_sci_math_hip.py +205 -0
  203. pygpgpu-0.2.2/tests/test_vector_array_construction.py +114 -0
  204. pygpgpu-0.2.2/tests/test_work_size_bracket.py +152 -0
  205. pygpgpu-0.2.0/examples/06_handwritten_kernels.py +0 -60
  206. pygpgpu-0.2.0/examples/11_texture2d.py +0 -220
  207. pygpgpu-0.2.0/examples/vecadd_cuda.cu +0 -11
  208. pygpgpu-0.2.0/pygpgpu/__init__.py +0 -135
  209. pygpgpu-0.2.0/pygpgpu/cuda/__init__.py +0 -7
  210. pygpgpu-0.2.0/pygpgpu/cuda/cuimport.py +0 -201
  211. pygpgpu-0.2.0/pygpgpu/hip/__init__.py +0 -6
  212. pygpgpu-0.2.0/pygpgpu/hip/driver/hip.py +0 -150
  213. pygpgpu-0.2.0/pygpgpu/hip/hipimport.py +0 -179
  214. pygpgpu-0.2.0/pygpgpu/kernel/__init__.py +0 -22
  215. pygpgpu-0.2.0/pygpgpu/kernel/builtins.py +0 -251
  216. pygpgpu-0.2.0/pygpgpu/kernel/decorator.py +0 -1025
  217. pygpgpu-0.2.0/pygpgpu/kernel/translator.py +0 -2963
  218. pygpgpu-0.2.0/pygpgpu/opencl/climport.py +0 -130
  219. pygpgpu-0.2.0/pygpgpu/opencl/common.py +0 -76
  220. pygpgpu-0.2.0/pygpgpu/opencl/oop/kernel_wrapper.py +0 -90
  221. pygpgpu-0.2.0/pygpgpu/opencl/oop/kernel_wrapper.pyi +0 -45
  222. pygpgpu-0.2.0/pygpgpu/opencl/oop/program_wrapper.py +0 -109
  223. pygpgpu-0.2.0/pygpgpu/vec_types/double2.py +0 -14
  224. pygpgpu-0.2.0/pygpgpu/vec_types/double2.pyi +0 -320
  225. pygpgpu-0.2.0/pygpgpu/vec_types/double4.py +0 -16
  226. pygpgpu-0.2.0/pygpgpu/vec_types/double4.pyi +0 -3652
  227. pygpgpu-0.2.0/pygpgpu/vec_types/float2.py +0 -15
  228. pygpgpu-0.2.0/pygpgpu/vec_types/float2.pyi +0 -320
  229. pygpgpu-0.2.0/pygpgpu/vec_types/float4.py +0 -16
  230. pygpgpu-0.2.0/pygpgpu/vec_types/float4.pyi +0 -3652
  231. pygpgpu-0.2.0/pygpgpu/vec_types/funcs.py +0 -316
  232. pygpgpu-0.2.0/pygpgpu/vec_types/int2.py +0 -14
  233. pygpgpu-0.2.0/pygpgpu/vec_types/int2.pyi +0 -320
  234. pygpgpu-0.2.0/pygpgpu/vec_types/int4.py +0 -16
  235. pygpgpu-0.2.0/pygpgpu/vec_types/int4.pyi +0 -3652
  236. pygpgpu-0.2.0/pygpgpu/vec_types/uint2.py +0 -14
  237. pygpgpu-0.2.0/pygpgpu/vec_types/uint2.pyi +0 -320
  238. pygpgpu-0.2.0/pygpgpu/vec_types/uint4.py +0 -16
  239. pygpgpu-0.2.0/pygpgpu/vec_types/uint4.pyi +0 -3652
  240. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/LICENSE +0 -0
  241. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/MANIFEST.in +0 -0
  242. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/02_backend_device_selection.py +0 -0
  243. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/03_nd_loop_and_class_method.py +0 -0
  244. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/04_vector_types.py +0 -0
  245. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/08_compare_backends.py +0 -0
  246. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/10_async_launch.py +0 -0
  247. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/12_print_in_kernel.py +0 -0
  248. /pygpgpu-0.2.0/pygpgpu/py.typed → /pygpgpu-0.2.2/examples/GPU-Puzzles/Puzzle9_Pooling.py +0 -0
  249. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/examples/vecadd.cl +0 -0
  250. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/constants.py +0 -0
  251. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/ShaderBuiltins.py +0 -0
  252. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/__init__.py +0 -0
  253. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/cparser.py +0 -0
  254. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/cpreprocessor.py +0 -0
  255. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/csyntaxtree.py +0 -0
  256. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/ctokens.py +0 -0
  257. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/generate_code.py +0 -0
  258. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/minifyc.py +0 -0
  259. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/__init__.py +0 -0
  260. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/evaluator.py +0 -0
  261. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/lextab.py +0 -0
  262. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/parser.py +0 -0
  263. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/parsetab.py +0 -0
  264. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/pcmd.py +0 -0
  265. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/ply/__init__.py +0 -0
  266. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/ply/cpp.py +0 -0
  267. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/ply/ctokens.py +0 -0
  268. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/ply/lex.py +0 -0
  269. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/ply/yacc.py +0 -0
  270. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/ply/ygen.py +0 -0
  271. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cparser/pcpp/preprocessor.py +0 -0
  272. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/cuda.pyi +0 -0
  273. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/cudaSurfaceObject_t.py +0 -0
  274. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/cudaTextureObject_t.py +0 -0
  275. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/image_shape.py +0 -0
  276. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/nvrtc.pyi +0 -0
  277. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/nvrtcinfo.py +0 -0
  278. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/nvrtctypes.py +0 -0
  279. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/__init__.py +0 -0
  280. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/char3.py +0 -0
  281. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/char3.pyi +0 -0
  282. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/double3.py +0 -0
  283. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/double3.pyi +0 -0
  284. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/float3.py +0 -0
  285. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/float3.pyi +0 -0
  286. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/int3.py +0 -0
  287. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/int3.pyi +0 -0
  288. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/long2.py +0 -0
  289. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/long2.pyi +0 -0
  290. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/long3.py +0 -0
  291. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/long3.pyi +0 -0
  292. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/long4.py +0 -0
  293. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/long4.pyi +0 -0
  294. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/short3.py +0 -0
  295. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/short3.pyi +0 -0
  296. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/uchar3.py +0 -0
  297. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/uchar3.pyi +0 -0
  298. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/uint3.py +0 -0
  299. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/uint3.pyi +0 -0
  300. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ulong2.py +0 -0
  301. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ulong2.pyi +0 -0
  302. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ulong3.py +0 -0
  303. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ulong3.pyi +0 -0
  304. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ulong4.py +0 -0
  305. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ulong4.pyi +0 -0
  306. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ushort3.py +0 -0
  307. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/driver/vec_types/ushort3.pyi +0 -0
  308. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/__init__.py +0 -0
  309. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/array.py +0 -0
  310. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/context.py +0 -0
  311. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/cuobject.py +0 -0
  312. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/device.py +0 -0
  313. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/device.pyi +0 -0
  314. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/devices.py +0 -0
  315. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/event.py +0 -0
  316. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/mem_object.py +0 -0
  317. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/program.py +0 -0
  318. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/stream.py +0 -0
  319. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/surface.py +0 -0
  320. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/cuda/oop/texture.py +0 -0
  321. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/exceptions.py +0 -0
  322. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/driver/hiptypes.py +0 -0
  323. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/__init__.py +0 -0
  324. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/build_options.py +0 -0
  325. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/context.py +0 -0
  326. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/device.py +0 -0
  327. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/devices.py +0 -0
  328. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/event.py +0 -0
  329. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/hipobject.py +0 -0
  330. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/mem_object.py +0 -0
  331. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/program.py +0 -0
  332. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/hip/oop/stream.py +0 -0
  333. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/numpy/__init__.py +0 -0
  334. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/numpy/array_creation.py +0 -0
  335. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/numpy/ndarray.py +0 -0
  336. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/cl.pyi +0 -0
  337. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/clinfo.py +0 -0
  338. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/cltypes.py +0 -0
  339. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/__init__.py +0 -0
  340. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/image1d_array_t.py +0 -0
  341. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/image1d_t.py +0 -0
  342. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/image2d_array_t.py +0 -0
  343. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/image2d_t.py +0 -0
  344. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/image3d_t.py +0 -0
  345. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/imagend_t.py +0 -0
  346. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/image_types/sampler_t.py +0 -0
  347. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/__init__.py +0 -0
  348. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/char3.py +0 -0
  349. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/char3.pyi +0 -0
  350. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/double3.py +0 -0
  351. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/double3.pyi +0 -0
  352. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/float3.py +0 -0
  353. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/float3.pyi +0 -0
  354. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/int3.py +0 -0
  355. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/int3.pyi +0 -0
  356. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long16.py +0 -0
  357. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long16.pyi +0 -0
  358. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long2.py +0 -0
  359. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long2.pyi +0 -0
  360. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long3.py +0 -0
  361. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long3.pyi +0 -0
  362. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long4.py +0 -0
  363. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long4.pyi +0 -0
  364. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long8.py +0 -0
  365. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/long8.pyi +0 -0
  366. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/short3.py +0 -0
  367. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/short3.pyi +0 -0
  368. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/uchar3.py +0 -0
  369. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/uchar3.pyi +0 -0
  370. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/uint3.py +0 -0
  371. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/uint3.pyi +0 -0
  372. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong16.py +0 -0
  373. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong16.pyi +0 -0
  374. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong2.py +0 -0
  375. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong2.pyi +0 -0
  376. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong3.py +0 -0
  377. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong3.pyi +0 -0
  378. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong4.py +0 -0
  379. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong4.pyi +0 -0
  380. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong8.py +0 -0
  381. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ulong8.pyi +0 -0
  382. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ushort3.py +0 -0
  383. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/driver/vec_types/ushort3.pyi +0 -0
  384. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/build_options.py +0 -0
  385. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/clobject.py +0 -0
  386. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/command_queue.py +0 -0
  387. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/command_queue.pyi +0 -0
  388. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/context.py +0 -0
  389. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/context.pyi +0 -0
  390. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/device.py +0 -0
  391. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/device.pyi +0 -0
  392. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/event.py +0 -0
  393. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/event.pyi +0 -0
  394. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/image1d.py +0 -0
  395. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/image1d_array.py +0 -0
  396. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/image2d.py +0 -0
  397. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/image2d_array.py +0 -0
  398. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/image3d.py +0 -0
  399. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/imagend.py +0 -0
  400. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/kernel.pyi +0 -0
  401. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/mem_object.py +0 -0
  402. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/mem_object.pyi +0 -0
  403. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/pipe.py +0 -0
  404. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/platform.py +0 -0
  405. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/platform.pyi +0 -0
  406. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/program.py +0 -0
  407. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/program.pyi +0 -0
  408. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/sampler.py +0 -0
  409. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/sampler.pyi +0 -0
  410. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/opencl/oop/svm_buffer.py +0 -0
  411. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char16.py +0 -0
  412. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char16.pyi +0 -0
  413. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char2.py +0 -0
  414. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char2.pyi +0 -0
  415. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char4.py +0 -0
  416. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char4.pyi +0 -0
  417. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char8.py +0 -0
  418. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/char8.pyi +0 -0
  419. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/double16.py +0 -0
  420. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/double16.pyi +0 -0
  421. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/double8.py +0 -0
  422. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/double8.pyi +0 -0
  423. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/float16.py +0 -0
  424. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/float16.pyi +0 -0
  425. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/float8.py +0 -0
  426. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/float8.pyi +0 -0
  427. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec.py +0 -0
  428. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec16.py +0 -0
  429. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec16.pyi.in +0 -0
  430. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec2.py +0 -0
  431. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec2.pyi.in +0 -0
  432. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec3.py +0 -0
  433. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec3.pyi.in +0 -0
  434. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec4.py +0 -0
  435. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec4.pyi.in +0 -0
  436. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec8.py +0 -0
  437. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/genVec8.pyi.in +0 -0
  438. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/helper.py +0 -0
  439. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/int16.py +0 -0
  440. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/int16.pyi +0 -0
  441. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/int8.py +0 -0
  442. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/int8.pyi +0 -0
  443. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short16.py +0 -0
  444. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short16.pyi +0 -0
  445. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short2.py +0 -0
  446. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short2.pyi +0 -0
  447. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short4.py +0 -0
  448. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short4.pyi +0 -0
  449. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short8.py +0 -0
  450. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/short8.pyi +0 -0
  451. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar16.py +0 -0
  452. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar16.pyi +0 -0
  453. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar2.py +0 -0
  454. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar2.pyi +0 -0
  455. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar4.py +0 -0
  456. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar4.pyi +0 -0
  457. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar8.py +0 -0
  458. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uchar8.pyi +0 -0
  459. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uint16.py +0 -0
  460. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uint16.pyi +0 -0
  461. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uint8.py +0 -0
  462. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/uint8.pyi +0 -0
  463. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort16.py +0 -0
  464. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort16.pyi +0 -0
  465. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort2.py +0 -0
  466. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort2.pyi +0 -0
  467. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort4.py +0 -0
  468. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort4.pyi +0 -0
  469. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort8.py +0 -0
  470. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu/vec_types/ushort8.pyi +0 -0
  471. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu.egg-info/dependency_links.txt +0 -0
  472. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/pygpgpu.egg-info/top_level.txt +0 -0
  473. {pygpgpu-0.2.0 → pygpgpu-0.2.2}/setup.cfg +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pygpgpu
3
- Version: 0.2.0
3
+ Version: 0.2.2
4
4
  Summary: Run Python functions as GPU kernels with the @kernel decorator (OpenCL / CUDA / HIP); handwritten .cl/.cu/.hip kernels import like Python modules
5
5
  Author-email: "王炳辉 (Bing-Hui WANG)" <binghui.wang@foxmail.com>
6
6
  License: MIT License
@@ -32,6 +32,7 @@ Classifier: Intended Audience :: Developers
32
32
  Classifier: Intended Audience :: Science/Research
33
33
  Classifier: License :: OSI Approved :: MIT License
34
34
  Classifier: Operating System :: Microsoft :: Windows
35
+ Classifier: Operating System :: POSIX :: Linux
35
36
  Classifier: Programming Language :: Python :: 3
36
37
  Classifier: Programming Language :: Python :: 3.7
37
38
  Classifier: Programming Language :: Python :: 3.8
@@ -50,9 +51,9 @@ License-File: LICENSE
50
51
  Requires-Dist: numpy>=1.21
51
52
  Requires-Dist: imageio>=2.30
52
53
  Requires-Dist: tree-sitter>=0.20.0
53
- Requires-Dist: tree-sitter-opencl>=0.0.2
54
- Requires-Dist: tree-sitter-cuda37>=0.21.2
55
- Requires-Dist: pygf>=0.1.4
54
+ Requires-Dist: tree-sitter-opencl>=0.0.3
55
+ Requires-Dist: tree-sitter-cuda37>=0.21.3
56
+ Requires-Dist: pygf>=0.2.0
56
57
  Requires-Dist: typing_extensions>=4.4.0
57
58
  Provides-Extra: dev
58
59
  Requires-Dist: pytest>=8; extra == "dev"
@@ -64,7 +65,7 @@ Dynamic: license-file
64
65
 
65
66
  **English** | [Chinese](README.zh-CN.md)
66
67
 
67
- Decorate a Python function and it runs on the GPU — one codebase, three backends (**OpenCL / CUDA / HIP**).
68
+ Decorate a Python function and it runs on the GPU — one codebase, four backends (**OpenCL / CUDA / HIP / Metal**).
68
69
 
69
70
  ```python
70
71
  import numpy as np
@@ -78,7 +79,7 @@ def saxpy(x: np.ndarray, y: np.ndarray, out: np.ndarray, alpha: float) -> None:
78
79
  x = np.arange(1_000_000, dtype=np.float32)
79
80
  out = np.zeros_like(x)
80
81
 
81
- saxpy(x, x, out, 2.0) # no backend given: probe cuda -> hip -> opencl, take the first available
82
+ saxpy(x, x, out, 2.0) # no backend given: probe cuda -> hip -> metal -> opencl, take the first available
82
83
  saxpy["cuda"](x, x, out, 2.0) # bracket switches the backend
83
84
  saxpy["hip:gfx1036"](x, x, out, 2.0) # backend:device
84
85
  saxpy["python"](x, x, out, 2.0) # the same function, serially in the interpreter (no GPU)
@@ -90,25 +91,26 @@ No C code to write, no build system to configure; the first call translates, com
90
91
 
91
92
  - **`@kernel` decorator** — decorate a Python function directly (class methods and static methods included); at call time it is specialized per argument type, translated to backend source and compiled (monomorphization).
92
93
  - **Plain-Python helper functions** — a helper called from a kernel needs no decorator, and its annotations are optional: it is translated into a device function and monomorphized from the call site, recursively for helpers that call helpers. The same holds for helpers reached through a name: `module.func(...)`, `SomeClass.static_method(...)`, `self.static_method(...)`, and `self.other_method(...)` between methods of one class.
93
- - **All three backends in one process** — the bracket chooses the backend per call, so a single process (and even a single kernel object) can mix OpenCL, CUDA and HIP; each backend/device pair is specialized and cached separately.
94
+ - **Every backend in one process** — the bracket chooses the backend per call, so a single process (and even a single kernel object) can mix OpenCL, CUDA, HIP and Metal; each backend/device pair is specialized and cached separately.
94
95
  - **Pure-Python backend** — `k["python"]` runs the same `@kernel` body in the interpreter, serially on the CPU: no GPU, no compiler, no runtime. Useful for testing, for small inputs and as a reference for what the GPU semantics mean.
95
- - **Handwritten kernel file import** — `.cl` / `.cu` / `.hip` files import like Python modules: kernels and struct types become module attributes, and a `.pyi` type stub is generated automatically.
96
+ - **Handwritten kernel file import** — `.cl` / `.cu` / `.hip` / `.metal` files import like Python modules: kernels and struct types become module attributes, and a `.pyi` type stub is generated automatically.
96
97
  - **Low-level OOP API** — Device/Context/Buffer/Program/Kernel, for when you need full control.
97
98
  - **One bracket syntax** for backend/device/work size (identical to handwritten kernels, order does not matter):
98
99
  ```python
99
100
  k(args) # fully automatic
100
- k["cuda"](args) # backend (opencl/cuda/hip/python, aliases ocl/cu/rocm/amd/py)
101
+ k["cuda"](args) # backend (opencl/cuda/hip/metal/python, aliases ocl/cu/rocm/amd/mtl/py)
101
102
  k["opencl:gfx1036"](args) # backend:device
102
- k[(1024, 256)](args) # work size: (total, per group), None = automatic
103
- k["cuda"][(1024, 256)](args) # combined
103
+ k[(4, 256)](args) # work size: (blocks per grid, threads per block)
104
+ k["cuda"][(4, 256)](args) # combined
104
105
  ```
105
- - **Total-work semantics** (OpenCL/SYCL style): in `(global, local)`, `global` is the **total number of work items**; CUDA/HIP grid dimensions are derived as `global / local`, so all three backends behave exactly the same.
106
+ - **CUDA work-size brackets on every backend**: (blocks_per_grid, threads_per_block), the spelling a CUDA or HIP reader already writes. k[(4, 256)] launches 4 blocks of 256 threads (1024 work items); k[(n,)] is a bare item count with an automatic block size, and None means automatic. The same bracket works on every backend, in @kernel and in the low-level oop API, and a block count never has to divide anything: the total is locks * threads by construction.
106
107
  - **Static read/write analysis**: the kernel body is parsed to infer whether each pointer argument is read-only, write-only or read-write — write-only arguments skip the upload, read-only arguments skip the read-back, and data stays on the device across calls (zero intermediate copies for gnp arrays).
107
108
  - **Vector types**: 50 vector types (float4/int3/...) with `.x/.y/.z/.w` members, swizzles, construction, arithmetic (scalar broadcast), comparisons and component-wise math.
108
109
  - **Dual-sided function library** `pygpgpu.funcs` (aka `pygpgpu.vec_types.funcs`): `dot/cross/normalize/clamp/...` run as-is in host Python and are translated to device builtins inside `@kernel`.
109
- - **Device builtin namespace** `gpu.*`: `get_global_id/barrier/atomic_add/dot/...` covers GPU builtins that have no Python counterpart, mapped per backend.
110
+ - **Device builtins, exported at the top level**: `get_global_id`, `barrier`, `atomic_add`, `dot`, ... are plain functions imported from `pygpgpu`. Inside a `@kernel` body the translator recognizes each one by the object it resolves to and maps it to the call of every backend, so the same kernel body runs on OpenCL, CUDA, HIP and in the interpreter.
110
111
  - **Multi-dimensional iteration**: `np.ndindex(shape)` / `np.ndenumerate(arr)` magic loops (2D/3D), `enumerate(arr)` for a 1D index plus value snapshot.
111
112
  - **Three cache layers**: parsed metadata / compiled artifacts / generated sources, all invalidated by mtime — unchanged sources mean zero recompilation.
113
+ - **Host-side CUDA libraries bound with ctypes** — cuBLAS, cuBLASLt, cuSOLVER, cuFFT, cuSPARSE and the CUDA runtime, in the same shape as the driver (lazy DLL, signature table, the library's own error messages), with a numpy-friendly layer over them (`gemm`, `axpy`, `scal`, `dot`, `rfft`, `csrmv`). See "Host-side CUDA libraries" below.
112
114
 
113
115
  ## Installation
114
116
 
@@ -122,31 +124,32 @@ Python ≥ 3.7. Bring your own GPU runtime (any one of them is enough):
122
124
  |---|---|---|
123
125
  | OpenCL | GPU driver (usually already installed) | Nothing extra to install; works across NVIDIA/AMD/Intel |
124
126
  | CUDA | NVIDIA driver (includes NVRTC) | The Toolkit is not required — the driver-provided nvrtc is enough |
125
- | HIP | AMD HIP SDK | On Windows compilation runs through the SDK's clang; if MSVC/Windows SDK headers are not on the default paths, point `PYGPGPU_HIP_INCLUDES` at the include directories (os.pathsep-separated) |
127
+ | HIP | AMD HIP SDK | Compilation runs through the SDK's clang: on Windows that is `<ROCm>/bin/clang.exe` with the MSVC/Windows SDK headers auto-discovered (point `PYGPGPU_HIP_INCLUDES` at the include directories, os.pathsep-separated, if they are not on the default paths); on Linux it is `<ROCm>/bin/clang` (or `hipcc` on `PATH`), which finds the system headers itself |
128
+ | Metal | macOS on Apple hardware | The Metal backend compiles through the framework's own runtime compiler, so no Xcode command-line tools are needed. It requires a real Apple GPU: a Mac virtual machine has no Metal device, so this backend is unavailable there |
126
129
  | Python | nothing | Runs the `@kernel` body serially in the interpreter; selected explicitly with `k["python"]` and never probed for |
127
130
 
128
- When `@kernel` has no explicit `backend`, backends are probed in **cuda -> hip -> opencl** order and the first one that enumerates a device wins.
131
+ When `@kernel` has no explicit `backend`, backends are probed in **cuda -> hip -> metal -> opencl** order and the first one that enumerates a device wins. The order puts Metal ahead of OpenCL on macOS, where OpenCL is deprecated and only exposes CPU devices.
129
132
 
130
133
  ## Quick start
131
134
 
132
135
  Every commonly used API is importable straight from the `pygpgpu` top level — no need to remember submodule names:
133
136
 
134
137
  ```python
135
- from pygpgpu import kernel, gpu, funcs, float4 # @kernel / device builtins / dual-sided funcs / vector types
138
+ from pygpgpu import kernel, funcs, float4 # @kernel / dual-sided funcs / vector types
136
139
  from pygpgpu import list_devices, get_device
137
- from pygpgpu import climport, cuimport, hipimport # import hooks for handwritten .cl/.cu/.hip files
140
+ from pygpgpu import kernel_import # the import hook for handwritten .cl/.cu/.hip files
138
141
  ```
139
142
 
140
143
  The submodule paths (`pygpgpu.kernel` / `pygpgpu.vec_types` / `pygpgpu.opencl` / ...) still work. Note that top-level `pygpgpu.kernel` is the `@kernel` decorator; to reach things inside the submodule write `from pygpgpu.kernel import ...`.
141
144
 
142
- The three import hooks load lazily: the corresponding backend is only loaded when you actually use the hook, so `import pygpgpu` itself needs no GPU runtime at all.
145
+ `kernel_import` needs no GPU runtime to be imported or installed, and a merged kernel loads only its own backend's runtime when it is first called.
143
146
 
144
147
  ### The @kernel decorator
145
148
 
146
149
  ```python
147
150
  import math
148
151
  import numpy as np
149
- from pygpgpu import kernel, gpu, funcs, float4
152
+ from pygpgpu import kernel, funcs, float4
150
153
 
151
154
  def norm2(v: float4) -> float:
152
155
  return funcs.dot(v, v)
@@ -161,18 +164,69 @@ def matrix_scale(m: np.ndarray, out: np.ndarray, k: float) -> None:
161
164
  for y, x in np.ndindex(out.shape): # 2D NDRange
162
165
  out[y, x] = m[y, x] * k
163
166
 
164
- @kernel(backend="cuda")
167
+ @kernel(backend="cuda", domain="data")
165
168
  def histogram(data: np.ndarray, counts: np.ndarray) -> None:
166
- gpu.work_size(data) # iteration domain for a gid-style kernel
167
- i = gpu.get_global_id(0)
168
- gpu.atomic_add(counts, data[i], 1) # atomics, results read back automatically
169
+ i = get_global_id(0)
170
+ atomic_add(counts, data[i], 1) # atomics, results read back automatically
169
171
  ```
170
172
 
171
173
  Supported: scalar/array/vector arguments, if/while/for-range, arithmetic/comparison/bit ops, `math.*` and `np.*` math functions, class methods (`self.xxx` attributes become arguments automatically), static methods, and calling helpers through a name — a module-level function, `module.func(...)`, `SomeClass.static_method(...)`, and `self.method(...)` for another method of the same class. Unsupported syntax raises a `SyntaxError` carrying the line number.
172
174
 
175
+ ### `+=` into an array element
176
+
177
+ `arr[i] op= value` follows Python's semantics even when several work items meet at the same element: `bins[image[i, j, k]] += 1` accumulates every pixel, without writing `atomic_add` by hand. The translator emits a device atomic whenever it cannot prove the element belongs to one work item — the proof being that the subscript is built from exactly the magic loop's own index variables, so `out[i] += x[i]` stays a plain (faster) read-modify-write.
178
+
179
+ ```python
180
+ @kernel
181
+ def histogram(image: np.ndarray):
182
+ bins: np.ndarray = np.zeros((3, 256), dtype=np.int32) # created on the host
183
+ for i, j, k in np.ndindex(image.shape):
184
+ bins[k, image[i, j, k]] += 1 # one atomic add per pixel: no lost counts
185
+ return bins
186
+ ```
187
+
188
+ Only operators with a device atomic counterpart are automated (`+=`, `-=`, `&=`, `|=`, `^=`). A racy `*=`/`/=`/`**=`, or a racy accumulation into a float element, raises a `SyntaxError` instead of losing data: there is no portable float atomic on the OpenCL backend, so use an integer buffer or accumulate per work item and reduce afterwards (`examples/09_reduction.py`).
189
+
190
+ ### Accumulating into a single scalar
191
+
192
+ A handwritten kernel takes a pointer to one element (`__global int* total`) and passes it to `atomicAdd`. There are three equivalent shapes here:
193
+
194
+ ```python
195
+ from pygpgpu import atomic_add, kernel
196
+
197
+ # 1) a 1-element array IS that pointer
198
+ @kernel
199
+ def count(x: np.ndarray, total: np.ndarray):
200
+ for i in range(x.shape[0]):
201
+ if x[i] > 0.0:
202
+ atomic_add(total, 0, 1) # or simply: total[0] += 1
203
+
204
+ # 2) an outer scalar declared nonlocal/global becomes a device pointer,
205
+ # and the result is written back to the enclosing scope after the call
206
+ @kernel
207
+ def count_global(x: np.ndarray):
208
+ global TOTAL
209
+ for i in range(x.shape[0]):
210
+ if x[i] > 0.0:
211
+ TOTAL += 1 # atomic on the device, native in the interpreter
212
+
213
+ # 3) a counter created in the body can be returned as the result
214
+ @kernel
215
+ def count_and_return(x: np.ndarray):
216
+ total: np.ndarray = np.zeros(1, dtype=np.int32)
217
+ for i in range(x.shape[0]):
218
+ if x[i] > 0.0:
219
+ total[0] += 1
220
+ return total # a 1-element array: the caller reads [0]
221
+ ```
222
+
223
+ All three are exact on every backend — thousands of work items incrementing one location lose nothing. An outer scalar is always a *shared* location, so `TOTAL += 1` there is always atomic; `atomic_add(scalar, 0, 1)` is refused with a message pointing at that spelling, since an atomic needs an array to index.
224
+
225
+
226
+
173
227
  Type annotations are optional: the snippets in this README carry them for readability, but the device types come from the arguments at call time, so the same code works unannotated (`self` is not a kernel argument, so annotating it changes nothing).
174
228
 
175
- A kernel needs an iteration domain for its work size to be derived. Either use a magic loop (`for i in range(x.shape[0])`, whose bound is the domain) or, when dispatching on `gpu.get_global_id`, declare `gpu.work_size(x)` — that statement is host-side only, erased by the translator, and contributes all of `x`'s dimensions. A `k[(total, local)]` bracket also works. Without any of these the call raises instead of guessing.
229
+ A kernel needs an iteration domain for its work size to be derived. Either use a magic loop (`for i in range(x.shape[0])`, whose bound is the domain) or, when dispatching on `get_global_id`, name the array in the decorator: `@kernel(domain="x")`. The domain contributes all of `x`'s dimensions, and a `k[(blocks, threads)]` bracket also works. Without any of these the call raises instead of guessing.
176
230
 
177
231
  ### Printing from a kernel
178
232
 
@@ -245,14 +299,30 @@ The two kernel styles keep their meanings, which is where the interpreter differ
245
299
 
246
300
  | Kernel style | What `k["python"](...)` does |
247
301
  |---|---|
248
- | magic loop (`for i in range(x.shape[0])`, `enumerate(x)`, `np.ndindex`/`np.ndenumerate`) | runs the function once: the loop already covers every element. A `(total, local)` bracket changes nothing here, because the loop, not the launch, decides the extent |
249
- | gid-style (`gpu.get_global_id`), domain from `gpu.work_size(x)` | runs the body once per work item of the declared domain (`x`'s full shape), with `gpu.get_global_id` reporting the current index — one serial step per element, in `np.ndindex` order |
250
- | gid-style, domain from a `k[(total, local)]` bracket | same, one step per work item of the bracket, so the body must clamp itself against the real element count exactly as it does on a GPU |
302
+ | magic loop (`for i in range(x.shape[0])`, `enumerate(x)`, `np.ndindex`/`np.ndenumerate`) | runs the function once: the loop already covers every element. A `(blocks, threads)` bracket changes nothing here, because the loop, not the launch, decides the extent |
303
+ | gid-style (`get_global_id`), domain from `@kernel(domain="x")` | runs the body once per work item of the declared domain (`x`'s full shape), with `get_global_id` reporting the current index — one serial step per element, in `np.ndindex` order |
304
+ | gid-style, domain from a `k[(blocks, threads)]` bracket | same, one step per work item of the bracket, so the body must clamp itself against the real element count exactly as it does on a GPU |
251
305
 
252
- Inside such a body `gpu.*` resolves to host implementations: the indexing builtins, the atomics (plain read-modify-writes — serial execution makes them exact), `gpu.barrier()` (a no-op) and the vector math family, which uses `pygpgpu.funcs`' host implementation. Two things cannot be emulated and raise instead: `gpu.local` (group-local memory is shared by parallel work items) and a `gpu.get_global_id` call inside a magic-loop body, where there is no current work item — use the loop variable there.
306
+ Inside such a body the same functions resolve to their host implementations: the indexing builtins, the atomics (plain read-modify-writes — serial execution makes them exact), `barrier()` (a no-op) and the vector math family, which uses `pygpgpu.funcs`' host implementation. Two things cannot be emulated and raise instead: `local` (group-local memory is shared by parallel work items) and a `get_global_id` call inside a magic-loop body, where there is no current work item — use the loop variable there.
253
307
 
254
308
  Vector-typed arrays work unchanged: reading `v = points[i]` yields a vector instance, and `out[i] = v.xyzy * 2.0` writes its components.
255
309
 
310
+ ### One body, two behaviours: `KERNEL_TRANSLATING`
311
+
312
+ The interpreter runs the body as written, so a kernel that needs *different* code on each side has nowhere to put the difference — and a plain `if` on a value the device cannot see would be refused. `KERNEL_TRANSLATING` is the escape hatch, borrowed from `typing.TYPE_CHECKING`: it is `False` in Python and `True` while the body is being translated, so the condition is decided at **translation time** and exactly one branch is emitted:
313
+
314
+ ```python
315
+ @kernel(domain="out")
316
+ def measure(data: np.ndarray, out: np.ndarray):
317
+ i = get_global_id(0)
318
+ if KERNEL_TRANSLATING:
319
+ out[i] = data[i] * 2.0 # device: the real computation
320
+ else:
321
+ out[i] = len(str(i)) # python: a host-only expression the device could not run
322
+ ```
323
+
324
+ The branch is inlined as a macro — no `if` survives into the generated source — and the dropped branch is never translated, so Python-only work costs the device nothing. Only the name is recognized, so a kernel may declare its own `KERNEL_TRANSLATING` instead of importing it.
325
+
256
326
  ### Handwritten kernel files
257
327
 
258
328
  Already have C-style kernels? `.cl` / `.cu` / `.hip` files (native syntax of the matching backend) import like Python modules. Write a `vecadd.cl`:
@@ -268,14 +338,16 @@ __kernel void vecadd(__global const float* a, __global const float* b,
268
338
  ```
269
339
 
270
340
  ```python
271
- from pygpgpu import climport # cuimport / hipimport work the same way
341
+ from pygpgpu import kernel_import
272
342
  import numpy as np
273
- import vecadd # just like importing a Python module
343
+
344
+ kernel_import.install()
345
+ import vecadd # one .cl file = a module with one backend
274
346
 
275
347
  a = np.random.rand(1_000_000).astype(np.float32)
276
348
  out = np.zeros_like(a)
277
- vecadd.vecadd(a, a, out, np.int32(len(a))) # work size inferred
278
- vecadd.vecadd[(1_000_192, 256)](a, a, out, np.int32(len(a))) # explicit (total, per group)
349
+ vecadd.vecadd(a, a, out, np.int32(len(a))) # work size inferred
350
+ vecadd.vecadd[(3907, 256)](a, a, out, np.int32(len(a))) # (blocks, threads)
279
351
  ```
280
352
 
281
353
  Note the `n` argument: a handwritten kernel must clamp against the real element count, because the
@@ -284,6 +356,114 @@ size, not the buffer length, so it is not a usable bound.
284
356
 
285
357
  Struct definitions map to ctypes types; a `.pyi` stub is generated next to the kernel so IDE completion works.
286
358
 
359
+ ### One kernel, several backends
360
+
361
+ Same-named `.cl` / `.cu` / `.hip` files are ONE module. Put `vecops.cl`, `vecops.cu` and
362
+ `vecops.hip` in a package directory (with an `__init__.py`), then:
363
+
364
+ ```python
365
+ from pygpgpu import kernel_import
366
+
367
+ kernel_import.install()
368
+ from kernel_import import vecops # the package that holds the three sources
369
+
370
+ vecops.add_offset(a, b, out, offset, n) # default device, like a @kernel call
371
+ vecops.add_offset["cuda"](a, b, out, ...) # a bracket names the backend
372
+ vecops.add_offset["opencl:1"][(blocks, threads)](...) # device + work size, either order
373
+ ```
374
+
375
+ What makes one module out of three files:
376
+
377
+ * a kernel declared with the **same parameter list** in several files is one function with one
378
+ implementation per backend -- the bracket picks the implementation, the call keeps the kernel's
379
+ own signature;
380
+ * declared with **different parameter lists** it is a set of overloads -- the stub declares one
381
+ `@overload` per list, and the argument types pick the implementation at call time (a bare call
382
+ walks the same `cuda -> hip -> metal -> opencl` probe a `@kernel` uses);
383
+ * declared in only one file it still exists, and a bracket naming a backend without an
384
+ implementation raises `KeyError` at the bracket.
385
+
386
+ A `.pyi` stub is generated for the merged module: one `class Kernel_<name>` per kernel, one
387
+ `__call__` per distinct parameter list, and the structs from the first file that declares each.
388
+
389
+ ### The scientific-computing vocabulary
390
+
391
+ The device-side math functions are plain names, like every other builtin:
392
+
393
+ ```python
394
+ from pygpgpu import erf, tgamma, fast_exp, normal_cdf
395
+
396
+ @kernel
397
+ def integrate(x: np.ndarray, out: np.ndarray):
398
+ for i in range(out.shape[0]):
399
+ out[i] = erf(x[i]) * tgamma(x[i] + 1.0)
400
+ ```
401
+
402
+ Three groups, and the difference matters:
403
+
404
+ * the functions **all three backends have** -- `erf`, `erfc`, `tgamma`, `lgamma`, `rint`,
405
+ `remainder`, `fmin`, `fmax`, `fdim`, `copysign`, `nextafter`, `ldexp`, `fract`, `sign`,
406
+ and the `native_*` family. These run on OpenCL, CUDA and HIP alike.
407
+ * the functions **CUDA and HIP have** and OpenCL does not -- the fast approximations
408
+ (`fast_exp`, `fast_log`, `fast_sin`, ...), the correctly-rounded forms (`exact_recip`,
409
+ `exact_divide`, `exact_sqrt`, ...), the special functions (`erf_inverse`, `normal_cdf`,
410
+ `reciprocal_hypot`, `sin_pi`, `cos_pi`, the Bessel functions) and the conversions
411
+ (`float_to_int_nearest`, `int_to_float`, ...). A kernel that uses one of these is a
412
+ **CUDA or HIP kernel**: on OpenCL the translator refuses it *by name*, so the message
413
+ says which function has no OpenCL counterpart rather than leaving you with an
414
+ "undeclared identifier" from the compiler.
415
+ * the `mad` / `fma` family and the vector operations, which pygf already publishes and
416
+ which are pygf's own objects here (see `pygpgpu.funcs`).
417
+
418
+ CUDA and HIP answer the same thing by the same name, but they are not the same device and
419
+ the difference is worth knowing:
420
+
421
+ * HIP has no `__exp2f`, so `native_exp2` is spelled `exp2f` there -- the ordinary name,
422
+ which HIP does have.
423
+ * HIP has **none** of the round-toward-zero intrinsics (`__frcp_rz`, `__fdiv_rz`,
424
+ `__fsqrt_rz`), so `exact_recip_rz`, `exact_divide_rz` and `exact_sqrt_rz` are computed
425
+ on HIP by a device helper the package emits: it takes the correctly rounded value, uses
426
+ the fused multiply-add to learn which side of it the exact result is on, and steps one
427
+ unit toward zero when it is the other way. The result agrees with CUDA's own intrinsic
428
+ bit for bit (a division is the one exception, because the two hardware divide units
429
+ round differently in the last bit -- the rounding *direction* is the same either way).
430
+
431
+ Two names are worth a warning, because the device's documentation is the authority and
432
+ the spelling does not tell you:
433
+
434
+ * CUDA's `rhypotf` is the **reciprocal** of the hypotenuse (`1/sqrt(x²+y²)`), so the name
435
+ here is `reciprocal_hypot`; the plain hypotenuse is `hypot`.
436
+ * CUDA's `sinpif` is `sin(πx)`, **not** the sinc function -- so the name here is `sin_pi`.
437
+
438
+ ### Naming a device function that already exists
439
+
440
+ A device function that is not one of the builtins -- a `__device__` function in a header
441
+ you ship, or a compiler intrinsic with a spelling of your own -- is registered:
442
+
443
+ ```python
444
+ from pygpgpu import device_function
445
+
446
+ @device_function(cuda="my_fast_rcp", opencl="native_recip",
447
+ headers={"cuda": "myclib.h"})
448
+ def my_fast_rcp(x: float) -> float:
449
+ """The host's implementation; the device gets the symbol the registry names."""
450
+ return 1.0 / x
451
+
452
+ @kernel
453
+ def use_it(x: np.ndarray, out: np.ndarray):
454
+ for i in range(out.shape[0]):
455
+ out[i] = my_fast_rcp(x[i])
456
+ ```
457
+
458
+ The device calls the symbol the table names; the header is copied into the cache and
459
+ included in the generated source, so you do not have to know where the cache is (a header
460
+ given as a full `#include` line is carried into the source verbatim instead, which is what
461
+ a system header such as `#include <cuda_fp16.h>` needs). A backend with no symbol for the
462
+ function is refused by name, the same way the CUDA-only builtins are.
463
+
464
+ The body is the **host's** implementation -- the `python` backend runs it -- so the two
465
+ sides have to agree, exactly as they do for the builtins.
466
+
287
467
  ### Class methods
288
468
 
289
469
  ```python
@@ -459,7 +639,7 @@ wp.launch(saxpy, dim=n, inputs=[x, y, out, 2.0])
459
639
  | Input data | numpy arrays directly | wrap in `ti.field`, explicit `from_numpy`/`to_numpy` | `cuda.to_device` + `copy_to_host` by hand | wrap in `wp.array` |
460
640
  | Helper functions | **plain Python — no decorator, annotations optional** | `@ti.func` + annotated args | `@cuda.jit(device=True)` | `@wp.func` + annotated args |
461
641
  | Backends in one process | **all three, mixed freely** | one, fixed by `ti.init(arch=...)` | CUDA only | CUDA only |
462
- | Launch size | inferred from the loop bound (or `gpu.work_size(x)` / a bracket) | inferred from `range` | blocks × threads by hand, plus a guard | pass `dim=` |
642
+ | Launch size | inferred from the loop bound (or `domain="x"` / a bracket) | inferred from `range` | blocks × threads by hand, plus a guard | pass `dim=` |
463
643
  | Bounds guard | emitted for you | emitted for you | you write it | you write it |
464
644
  | Backends | OpenCL, CUDA, HIP | CPU, CUDA, Vulkan and others | CPU, CUDA | CPU, CUDA |
465
645
  | AMD GPU | HIP (native AMD codegen) and OpenCL | via Vulkan | no | no |
@@ -484,7 +664,7 @@ apply_all["cuda"](x, out) # NVIDIA
484
664
  apply_all["hip"](x, out) # AMD
485
665
  apply_all["opencl"](x, out) # whichever device OpenCL enumerates
486
666
  apply_all["python"](x, out) # the interpreter, on the CPU (no device needed)
487
- apply_all(x, out) # unqualified: probe cuda -> hip -> opencl
667
+ apply_all(x, out) # unqualified: probe cuda -> hip -> metal -> opencl
488
668
  ```
489
669
 
490
670
  Each backend/device pair is specialized and cached separately, so alternating between them is just
@@ -496,11 +676,86 @@ Where the others are ahead — worth knowing before you pick:
496
676
  - **Automatic differentiation**: Taichi and Warp can differentiate kernels; this library cannot.
497
677
  - **Sparse and irregular data structures**: Taichi has sparse grids and SNode trees; here you pass dense numpy arrays (or handle sparsity yourself).
498
678
  - **CPU fallback**: all three can run on the CPU; this library runs on the CPU only through the explicit `k["python"]` interpreter backend, which is serial and meant for testing rather than throughput.
499
- - **Minimum interpreter version**: this library runs on Python 3.7 and newer, on both 64- and 32-bit Windows builds; the others require a newer interpreter.
679
+ - **Minimum interpreter version**: this library runs on Python 3.7 and newer, on 64- and 32-bit Windows and on Linux x86-64; the others require a newer interpreter. Linux is exercised end to end under WSL (CUDA through the `/usr/lib/wsl` driver passthrough, OpenCL through a runtime the distribution ships), so the driver-name and library-discovery paths there are the real ones rather than an untested branch.
500
680
 
501
681
  This table was written from each project's documentation and the snippets above; their APIs move
502
682
  fast, so confirm against current docs before relying on a specific cell.
503
683
 
684
+ ### Return values and outer variables
685
+
686
+ A kernel runs once per work item, so there is no single C return value to hand back. The
687
+ translator gives every returned position a device parameter of its own — a one-element
688
+ buffer for a scalar or vector, the array object itself for an array — and the first work
689
+ item to reach a `return` claims the result with an atomic compare-and-swap. The host reads
690
+ the slots back after the launch and returns exactly what the winning work item returned:
691
+
692
+ ```python
693
+ @kernel
694
+ def first_peak(x: np.ndarray):
695
+ for i in range(x.shape[0]):
696
+ if x[i] > 10.0:
697
+ return x[i], i # a tuple
698
+ return None # a launch where nothing returns is None
699
+
700
+ value, index = first_peak[backend](x)
701
+ ```
702
+
703
+ The supported return types are exactly the supported parameter types, every `return`
704
+ statement may have its own shape (`return x[i]` from one branch, `return x[i], i` from
705
+ another), and a bare `return` gives `None`. `submit()` and `async_call()` deliver the same
706
+ value once their future resolves, after read-back. A value-returning `return` is refused
707
+ below the magic loop: there it would return one work item's local, not the function's
708
+ result.
709
+
710
+ Variables the body reads from outside become parameters too, so a changed outer value is
711
+ visible on the next call — inlining it would leave the kernel stale:
712
+
713
+ ```python
714
+ SCALE = 2.0
715
+
716
+ @kernel
717
+ def scaled(x: np.ndarray, out: np.ndarray):
718
+ for i in range(out.shape[0]):
719
+ out[i] = x[i] * SCALE
720
+
721
+ scaled[backend](x, out) # SCALE is passed as an argument
722
+ SCALE = 100.0 # the next call uses 100.0
723
+ scaled[backend](x, out)
724
+ ```
725
+
726
+ A `nonlocal`/`global` declaration makes the outer scalar writable: it travels as a pointer,
727
+ every use inside is a dereference, and the value is copied back into its scope after the
728
+ call — a closure cell for `nonlocal`, the module globals for `global`. Outer arrays are
729
+ pointer parameters as well, and are read back when the kernel writes them. Rebinding an
730
+ outer array (as opposed to writing its elements) is refused, because the object is shared
731
+ with the caller. A helper function gets the same treatment as the kernel that calls it.
732
+
733
+ Statements above the magic loop may also *create* data with numpy — `np.zeros`, `np.ones`,
734
+ `np.empty`, `np.full`, `np.arange`, `np.linspace`, `*_like`, `np.float32(...)` and the rest
735
+ of the allocating constructors. The interpreter runs those statements, so the array has
736
+ exactly the shape and dtype numpy gives it, and it reaches the kernel as a parameter:
737
+
738
+ ```python
739
+ @kernel
740
+ def normalized(x: np.ndarray, out: np.ndarray):
741
+ total = np.zeros(1, dtype=np.float32) # built on the host
742
+ for i in range(x.shape[0]):
743
+ total[0] = total[0] + x[i]
744
+ for i in range(out.shape[0]):
745
+ out[i] = x[i] / total[0]
746
+
747
+ @kernel
748
+ def doubled(x: np.ndarray):
749
+ result = np.zeros_like(x) # created, then returned as itself
750
+ for i in range(x.shape[0]):
751
+ result[i] = x[i] * 2.0
752
+ return result
753
+ ```
754
+
755
+ A created object the body returns reuses the parameter it already travels through, so the
756
+ caller receives the host's own object rather than a copy. Creation below the magic loop is
757
+ refused: it would build one object per work item instead of one.
758
+
504
759
  ### Images
505
760
 
506
761
  `Image` is one image type for every backend. A kernel indexes it like an array, and the
@@ -561,6 +816,97 @@ The HIP backend refuses images: it reuses CUDA code generation, which has no tex
561
816
  object type, and the driver rejects `hipTexObjectCreate` on hardware without texture
562
817
  support.
563
818
 
819
+ ## Host-side CUDA libraries
820
+
821
+ The CUDA *driver* (`pygpgpu/cuda/driver`) is the low-level API: contexts, streams,
822
+ modules, memory. The CUDA *math libraries* ship as separate DLLs next to it and do
823
+ the actual mathematics. They are bound with ctypes under `pygpgpu/cuda/libraries`, in
824
+ the same shape as the driver — a lazily loaded DLL, a signature table per library, and
825
+ an error code turned into a `RuntimeError` carrying the library's own message:
826
+
827
+ ```python
828
+ from pygpgpu.cuda.libraries import driver_for
829
+
830
+ CUBLAS = driver_for("cublas") # the cuBLAS entry points
831
+ CUFFT = driver_for("cufft") # the cuFFT entry points
832
+ ```
833
+
834
+ | Library | Bound operations |
835
+ |---|---|
836
+ | **cuBLAS** | `gemm` (float32/float64), `axpy`, `scal`, `dot`, `GemmEx`, handle and stream management |
837
+ | **cuBLASLt** | the generic `matmul` with a heuristically chosen algorithm, matrix layouts, epilogues |
838
+ | **cuFFT** | `plan1d`, `exec` (R2C / C2R / C2C), plan destruction, stream binding |
839
+ | **cuSPARSE** | `csrmv` (createCsr / createDnVec / SpMV), dense and sparse descriptors |
840
+ | **cuSOLVER** | `potrf` (Cholesky), `gesvd` (SVD), `getrf` (LU), with their workspace queries |
841
+ | **CUDA runtime** | `cudaMalloc`, `cudaFree`, `cudaMemcpy`, `cudaMemset`, the device count and properties |
842
+
843
+ A thin, numpy-friendly layer sits on top of the raw entry points, so a call takes
844
+ arrays rather than device pointers:
845
+
846
+ ```python
847
+ import numpy as np
848
+ from pygpgpu.cuda_libraries import gemm, axpy, dot, rfft, csrmv
849
+
850
+ a = np.random.rand(3, 4).astype(np.float32)
851
+ b = np.random.rand(4, 2).astype(np.float32)
852
+
853
+ c = gemm(a, b) # a @ b, on the GPU
854
+ d = axpy(2.0, a[0], b[0]) # elementwise
855
+ e = dot(a[0], b[0]) # a scalar
856
+ f = rfft(a[0]) # n/2+1 complex numbers, like np.fft.rfft
857
+ g = csrmv(row_offsets, column_indices, values, x) # a sparse product
858
+ ```
859
+
860
+ The same names are reachable as attributes of the module, so both spellings work:
861
+
862
+ ```python
863
+ from pygpgpu import cuda_libraries
864
+ cuda_libraries.gemm(a, b)
865
+
866
+ from pygpgpu.cuda_libraries import gemm
867
+ gemm(a, b)
868
+ ```
869
+
870
+ The wrappers take numpy's **row-major** layout and return the same layout; cuBLAS
871
+ reads column-major, and the difference is handled inside, so `gemm(a, b)` is just
872
+ `a @ b`. The 1-D cases follow numpy's own rules for `@`, so a matrix and a vector
873
+ multiply without the caller reshaping anything.
874
+
875
+ **A CUDA context has to be current.** A library handle belongs to the context that
876
+ was current when it was created, so the wrappers make one current if none is — but
877
+ if you use the raw entry points, make one current yourself first:
878
+
879
+ ```python
880
+ from pygpgpu import get_device
881
+
882
+ device = get_device("cuda:0")
883
+ device.default_context.make_current() # before the first library call
884
+ ```
885
+
886
+ Without it the call fails with `CUDA_ERROR_INVALID_CONTEXT`, and the message says
887
+ so.
888
+
889
+ **These are host-side operations, not device builtins.** A `gemm` moves whole arrays
890
+ to the device and runs one library call; a `@kernel` body is per-thread arithmetic.
891
+ That is why a `@kernel` cannot call `cuda_libraries.gemm` — the two are different
892
+ layers, and mixing them would mean a kernel launching a library call per work item.
893
+ The device-side counterparts (a per-element multiply, a per-element dot) are the
894
+ device builtins the top level already exports.
895
+
896
+ The libraries are **loaded lazily**: `import pygpgpu` loads no CUDA library at all,
897
+ and each library's DLL is loaded on the first call that needs it. A machine without
898
+ the CUDA toolkit still imports the package, and the failure surfaces at the call with
899
+ the loader's own error.
900
+
901
+ **cuSOLVER needs kernels the consumer cards do not have.** NVIDIA does not ship
902
+ cuSOLVER's dense factorization kernels for every architecture, and a card without
903
+ them fails at execution rather than at setup: the call returns a failure code and the
904
+ context reports an illegal memory access. Measured on the machine this was developed
905
+ on (an RTX 4070 Laptop, compute capability 8.9, CUDA 12.6), `potrf`, `getrf`,
906
+ `gesvd`, `gesvdj` and `syevj` all fail that way, with `compute-sanitizer` putting the
907
+ fault inside cuSOLVER's own DLL. The bindings are correct and the test skips with
908
+ that reason instead of failing.
909
+
564
910
  ## Device selection
565
911
 
566
912
  Three forms of device identifier inside the brackets (same on every backend):
@@ -583,7 +929,7 @@ dev = get_device("cuda:0") # the Device object of that backend, directly
583
929
 
584
930
  ### Choosing the default device
585
931
 
586
- A call without a bracket runs on the first usable backend in `cuda -> hip -> opencl`.
932
+ A call without a bracket runs on the first usable backend in `cuda -> hip -> metal -> opencl`.
587
933
  `set_default_device()` replaces that order:
588
934
 
589
935
  ```python
@@ -609,6 +955,7 @@ is. A kernel decorated with an explicit `@kernel(backend=...)` keeps that backen
609
955
  | OpenCL | `__clcache__/` | parsed meta + per-device binaries |
610
956
  | CUDA | `__cucache__/` | parsed meta + per-device cubin/ptx |
611
957
  | HIP | `__hipcache__/` | parsed meta + per-device code objects |
958
+ | Metal | `__metalcache__/` | generated MSL source, compiled at first launch |
612
959
  | Python | none | nothing is generated or compiled; the body runs as-is |
613
960
 
614
961
  Caches are invalidated by the mtime of the source file (and every `#include`); sources generated by `@kernel` are keyed by a hash of the Python source. These directories should be in `.gitignore`.
@@ -637,15 +984,15 @@ set_print_info(True) # verbose again (the default)
637
984
 
638
985
  ## Known limitations
639
986
 
640
- - `@kernel` does not support: strings/lists/dicts, closures, recursion, kernel return values, dynamic multi-dimensional shape indexing;
987
+ - `@kernel` does not support: strings/lists/dicts, recursion, dynamic multi-dimensional shape indexing. Closures and module globals *are* supported: an outer variable becomes a kernel parameter, and one declared `nonlocal`/`global` is written back after the call (see "Return values and outer variables" above);
641
988
  - `print()` inside a kernel prints one `True`/`False` pair, integer, real or vector at a time. `sep` and `end` are translated, but must be string literals (the format string is fixed in the device code); `file` and `flush` are refused; it also cannot print an array (print an element instead). A line containing many floats expands into a `switch` over their digit counts, so an extremely long line can hit the code-size guard and should be split into several calls. The HIP backend rejects `print()` outright: its driver drops the device on such kernels on the tested runtime;
642
989
  - vector comparison, writing through a swizzle, and nesting beyond three operands are restricted in favour of cross-backend consistency (the error message suggests a workaround);
643
- - `Image` is unsupported on the HIP backend: HIP reuses CUDA code generation, which has no texture object type, and `hipTexObjectCreate` is rejected with "operation not supported" on hardware without texture support (an integrated Radeon under ROCm 7.2 Windows, for instance). The translator raises a `PyKernelSyntaxError` naming the backend; use cuda, opencl or python for image kernels;
990
+ - `Image` is unsupported on the HIP backend: HIP reuses CUDA code generation, which has no texture object type, and `hipTexObjectCreate` is rejected with "operation not supported" on hardware without texture support (an integrated Radeon under ROCm 7.2 Windows, for instance). The translator raises a `SyntaxError` naming the backend; use cuda, opencl or python for image kernels;
644
991
  - type annotations on a `@kernel` function are accepted and ignored: the device type comes from the actual argument at call time, since one kernel is specialized per argument-type signature. Annotations are there for editors and type checkers, not for translation;
645
992
  - a `@kernel` signature may carry default values, and a call may pass arguments by keyword: the call is bound to positional order before launch, with omitted defaults filled in. `*args`, `**kwargs` and keyword-only parameters remain unsupported;
646
993
  - fp16 (`np.float16`) arguments are unavailable on the CUDA/HIP backends (use float32);
647
994
  - the HIP backend compiles with the clang shipped by the AMD HIP SDK; hipRTC on this machine (ROCm 7.2 Windows) fails on any input, so it is not used;
648
- - the pure-Python backend (`k["python"]`) does not emulate `gpu.local` (group-shared memory): it runs serially and has no parallel work items to share it.
995
+ - the pure-Python backend (`k["python"]`) does not emulate `local` (group-shared memory): it runs serially and has no parallel work items to share it.
649
996
 
650
997
  On CUDA, the array APIs (`Array` upload/read-back through `cuMemcpy2D`) work on every supported interpreter. `cuSurfObjectCreate` and `cuTexObjectCreate` occasionally return `CUDA_ERROR_INVALID_VALUE` on a descriptor that is byte-for-byte valid; the failures cluster in the first few calls of a process and then stop. Measured on the machine this was developed on (driver 13.4, RTX 4070): about one in a hundred creations fails, retrying an already-failed descriptor does not recover it, and a fresh descriptor for a newly created array usually does. Because an `Image` binds one surface and one texture per launch, a scenario that launches several image kernels in one process can therefore lose a few launches. The `python` and OpenCL backends are unaffected: OpenCL images are created per launch on a code path that has not shown the failure, and the `python` backend touches no device at all. `examples/11_texture2d.py` reports a driver refusal instead of raising.
651
998
  ## License