nvmath-python 1.0.0__cp314-cp314t-win_amd64.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (332) hide show
  1. nvmath/__init__.pxd +0 -0
  2. nvmath/__init__.py +45 -0
  3. nvmath/_internal/__init__.py +0 -0
  4. nvmath/_internal/attribute_ifc_factory.py +330 -0
  5. nvmath/_internal/layout.py +70 -0
  6. nvmath/_internal/templates.py +130 -0
  7. nvmath/_internal/threadsafe.py +106 -0
  8. nvmath/_internal/utils.py +43 -0
  9. nvmath/_internal/workspace.py +490 -0
  10. nvmath/_utils.py +147 -0
  11. nvmath/bindings/__init__.py +60 -0
  12. nvmath/bindings/_internal/__init__.pxd +0 -0
  13. nvmath/bindings/_internal/__init__.py +0 -0
  14. nvmath/bindings/_internal/common_types.pxd +31 -0
  15. nvmath/bindings/_internal/cublas.cp314t-win_amd64.pyd +0 -0
  16. nvmath/bindings/_internal/cublas.pxd +530 -0
  17. nvmath/bindings/_internal/cublasLt.cp314t-win_amd64.pyd +0 -0
  18. nvmath/bindings/_internal/cublasLt.pxd +59 -0
  19. nvmath/bindings/_internal/cublasMp.pxd +52 -0
  20. nvmath/bindings/_internal/cudss.cp314t-win_amd64.pyd +0 -0
  21. nvmath/bindings/_internal/cudss.pxd +54 -0
  22. nvmath/bindings/_internal/cufft.cp314t-win_amd64.pyd +0 -0
  23. nvmath/bindings/_internal/cufft.pxd +70 -0
  24. nvmath/bindings/_internal/cufftMp.pxd +77 -0
  25. nvmath/bindings/_internal/curand.cp314t-win_amd64.pyd +0 -0
  26. nvmath/bindings/_internal/curand.pxd +42 -0
  27. nvmath/bindings/_internal/cusolver.cp314t-win_amd64.pyd +0 -0
  28. nvmath/bindings/_internal/cusolver.pxd +15 -0
  29. nvmath/bindings/_internal/cusolverDn.cp314t-win_amd64.pyd +0 -0
  30. nvmath/bindings/_internal/cusolverDn.pxd +406 -0
  31. nvmath/bindings/_internal/cusolverMp.pxd +71 -0
  32. nvmath/bindings/_internal/cusolverSp.cp314t-win_amd64.pyd +0 -0
  33. nvmath/bindings/_internal/cusolverSp.pxd +75 -0
  34. nvmath/bindings/_internal/cusparse.cp314t-win_amd64.pyd +0 -0
  35. nvmath/bindings/_internal/cusparse.pxd +471 -0
  36. nvmath/bindings/_internal/cusparseLt.cp314t-win_amd64.pyd +0 -0
  37. nvmath/bindings/_internal/cusparseLt.pxd +48 -0
  38. nvmath/bindings/_internal/cutensor.cp314t-win_amd64.pyd +0 -0
  39. nvmath/bindings/_internal/cutensor.pxd +58 -0
  40. nvmath/bindings/_internal/mathdx.cp314t-win_amd64.pyd +0 -0
  41. nvmath/bindings/_internal/mathdx.pxd +116 -0
  42. nvmath/bindings/_internal/nvshmem.pxd +29 -0
  43. nvmath/bindings/_internal/utils.cp314t-win_amd64.pyd +0 -0
  44. nvmath/bindings/_internal/utils.pxd +174 -0
  45. nvmath/bindings/_internal/utils.pyi +10 -0
  46. nvmath/bindings/cublas.cp314t-win_amd64.pyd +0 -0
  47. nvmath/bindings/cublas.pxd +558 -0
  48. nvmath/bindings/cublas.pyi +812 -0
  49. nvmath/bindings/cublasLt.cp314t-win_amd64.pyd +0 -0
  50. nvmath/bindings/cublasLt.pxd +109 -0
  51. nvmath/bindings/cublasLt.pyi +1461 -0
  52. nvmath/bindings/cublasMp.pxd +85 -0
  53. nvmath/bindings/cublasMp.pyi +267 -0
  54. nvmath/bindings/cudss.cp314t-win_amd64.pyd +0 -0
  55. nvmath/bindings/cudss.pxd +98 -0
  56. nvmath/bindings/cudss.pyi +443 -0
  57. nvmath/bindings/cufft.cp314t-win_amd64.pyd +0 -0
  58. nvmath/bindings/cufft.pxd +118 -0
  59. nvmath/bindings/cufft.pyi +301 -0
  60. nvmath/bindings/cufftMp.pxd +124 -0
  61. nvmath/bindings/cufftMp.pyi +326 -0
  62. nvmath/bindings/curand.cp314t-win_amd64.pyd +0 -0
  63. nvmath/bindings/curand.pxd +71 -0
  64. nvmath/bindings/curand.pyi +189 -0
  65. nvmath/bindings/cusolver.cp314t-win_amd64.pyd +0 -0
  66. nvmath/bindings/cusolver.pxd +62 -0
  67. nvmath/bindings/cusolver.pyi +320 -0
  68. nvmath/bindings/cusolverDn.cp314t-win_amd64.pyd +0 -0
  69. nvmath/bindings/cusolverDn.pxd +430 -0
  70. nvmath/bindings/cusolverDn.pyi +422 -0
  71. nvmath/bindings/cusolverMp.pxd +98 -0
  72. nvmath/bindings/cusolverMp.pyi +114 -0
  73. nvmath/bindings/cusolverSp.cp314t-win_amd64.pyd +0 -0
  74. nvmath/bindings/cusolverSp.pxd +95 -0
  75. nvmath/bindings/cusolverSp.pyi +70 -0
  76. nvmath/bindings/cusparse.cp314t-win_amd64.pyd +0 -0
  77. nvmath/bindings/cusparse.pxd +546 -0
  78. nvmath/bindings/cusparse.pyi +1017 -0
  79. nvmath/bindings/cusparseLt.cp314t-win_amd64.pyd +0 -0
  80. nvmath/bindings/cusparseLt.pxd +99 -0
  81. nvmath/bindings/cusparseLt.pyi +252 -0
  82. nvmath/bindings/cutensor.cp314t-win_amd64.pyd +0 -0
  83. nvmath/bindings/cutensor.pxd +98 -0
  84. nvmath/bindings/cutensor.pyi +324 -0
  85. nvmath/bindings/cycublas.cp314t-win_amd64.pyd +0 -0
  86. nvmath/bindings/cycublas.pxd +664 -0
  87. nvmath/bindings/cycublasLt.cp314t-win_amd64.pyd +0 -0
  88. nvmath/bindings/cycublasLt.pxd +1045 -0
  89. nvmath/bindings/cycublasMp.pxd +171 -0
  90. nvmath/bindings/cycudss.cp314t-win_amd64.pyd +0 -0
  91. nvmath/bindings/cycudss.pxd +277 -0
  92. nvmath/bindings/cycufft.cp314t-win_amd64.pyd +0 -0
  93. nvmath/bindings/cycufft.pxd +333 -0
  94. nvmath/bindings/cycufftMp.pxd +342 -0
  95. nvmath/bindings/cycurand.cp314t-win_amd64.pyd +0 -0
  96. nvmath/bindings/cycurand.pxd +141 -0
  97. nvmath/bindings/cycusolver.cp314t-win_amd64.pyd +0 -0
  98. nvmath/bindings/cycusolver.pxd +137 -0
  99. nvmath/bindings/cycusolverDn.cp314t-win_amd64.pyd +0 -0
  100. nvmath/bindings/cycusolverDn.pxd +443 -0
  101. nvmath/bindings/cycusolverMp.pxd +107 -0
  102. nvmath/bindings/cycusolverSp.cp314t-win_amd64.pyd +0 -0
  103. nvmath/bindings/cycusolverSp.pxd +93 -0
  104. nvmath/bindings/cycusparse.cp314t-win_amd64.pyd +0 -0
  105. nvmath/bindings/cycusparse.pxd +679 -0
  106. nvmath/bindings/cycusparseLt.cp314t-win_amd64.pyd +0 -0
  107. nvmath/bindings/cycusparseLt.pxd +135 -0
  108. nvmath/bindings/cycutensor.cp314t-win_amd64.pyd +0 -0
  109. nvmath/bindings/cycutensor.pxd +189 -0
  110. nvmath/bindings/cymathdx.cp314t-win_amd64.pyd +0 -0
  111. nvmath/bindings/cymathdx.pxd +552 -0
  112. nvmath/bindings/cynvshmem.pxd +118 -0
  113. nvmath/bindings/mathdx.cp314t-win_amd64.pyd +0 -0
  114. nvmath/bindings/mathdx.pxd +182 -0
  115. nvmath/bindings/mathdx.pyi +1562 -0
  116. nvmath/bindings/nvpl/__init__.pxd +0 -0
  117. nvmath/bindings/nvpl/__init__.py +13 -0
  118. nvmath/bindings/nvpl/_internal/__init__.pxd +0 -0
  119. nvmath/bindings/nvpl/_internal/__init__.py +0 -0
  120. nvmath/bindings/nvpl/_internal/blas.cp314t-win_amd64.pyd +0 -0
  121. nvmath/bindings/nvpl/_internal/blas.pxd +237 -0
  122. nvmath/bindings/nvpl/_internal/fft.cp314t-win_amd64.pyd +0 -0
  123. nvmath/bindings/nvpl/_internal/fft.pxd +36 -0
  124. nvmath/bindings/nvpl/blas.cp314t-win_amd64.pyd +0 -0
  125. nvmath/bindings/nvpl/blas.pxd +131 -0
  126. nvmath/bindings/nvpl/blas.pyi +168 -0
  127. nvmath/bindings/nvpl/cyblas.cp314t-win_amd64.pyd +0 -0
  128. nvmath/bindings/nvpl/cyblas.pxd +280 -0
  129. nvmath/bindings/nvpl/cyfft.cp314t-win_amd64.pyd +0 -0
  130. nvmath/bindings/nvpl/cyfft.pxd +93 -0
  131. nvmath/bindings/nvpl/fft.cp314t-win_amd64.pyd +0 -0
  132. nvmath/bindings/nvpl/fft.pxd +100 -0
  133. nvmath/bindings/nvpl/fft.pyi +168 -0
  134. nvmath/bindings/nvshmem.pxd +54 -0
  135. nvmath/bindings/nvshmem.pyi +191 -0
  136. nvmath/device/__init__.py +38 -0
  137. nvmath/device/_deprecated.py +33 -0
  138. nvmath/device/common.py +315 -0
  139. nvmath/device/common_backend.py +131 -0
  140. nvmath/device/common_cuda.py +201 -0
  141. nvmath/device/common_numba.py +300 -0
  142. nvmath/device/common_numba_cuda_mlir.py +202 -0
  143. nvmath/device/common_opaque_tensor.py +201 -0
  144. nvmath/device/cublasdx.py +1606 -0
  145. nvmath/device/cublasdx_backend.py +860 -0
  146. nvmath/device/cublasdx_numba.py +1534 -0
  147. nvmath/device/cublasdx_numba_cuda_mlir.py +208 -0
  148. nvmath/device/cufftdx.py +373 -0
  149. nvmath/device/cufftdx_backend.py +220 -0
  150. nvmath/device/cufftdx_numba.py +140 -0
  151. nvmath/device/cufftdx_numba_cuda_mlir.py +79 -0
  152. nvmath/device/curand_kernel.py +9147 -0
  153. nvmath/device/cusolverdx.py +2708 -0
  154. nvmath/device/cusolverdx_backend.py +440 -0
  155. nvmath/device/cusolverdx_numba.py +567 -0
  156. nvmath/device/cusolverdx_numba_cuda_mlir.py +604 -0
  157. nvmath/device/cusolverdx_overload_backend.py +1029 -0
  158. nvmath/device/llvm_array.py +29 -0
  159. nvmath/device/random.py +441 -0
  160. nvmath/device/random_helpers.py +23 -0
  161. nvmath/device/random_states.py +187 -0
  162. nvmath/device/types.py +138 -0
  163. nvmath/device/vector_types_numba.py +259 -0
  164. nvmath/distributed/__init__.py +200 -0
  165. nvmath/distributed/_internal/__init__.py +0 -0
  166. nvmath/distributed/_internal/nccl.py +86 -0
  167. nvmath/distributed/_internal/nvshmem.py +307 -0
  168. nvmath/distributed/_internal/symmetric_memory.py +35 -0
  169. nvmath/distributed/_internal/tensor_ifc.py +70 -0
  170. nvmath/distributed/_internal/tensor_ifc_cupy.py +68 -0
  171. nvmath/distributed/_internal/tensor_ifc_host_device.py +172 -0
  172. nvmath/distributed/_internal/tensor_ifc_numpy.py +46 -0
  173. nvmath/distributed/_internal/tensor_ifc_torch.py +162 -0
  174. nvmath/distributed/_internal/tensor_wrapper.py +81 -0
  175. nvmath/distributed/_utils.py +167 -0
  176. nvmath/distributed/distribution/__init__.py +30 -0
  177. nvmath/distributed/distribution/_configuration.py +39 -0
  178. nvmath/distributed/distribution/distributions.py +1024 -0
  179. nvmath/distributed/distribution/redistribute.py +1284 -0
  180. nvmath/distributed/fft/__init__.py +7 -0
  181. nvmath/distributed/fft/_configuration.py +82 -0
  182. nvmath/distributed/fft/fft.py +2742 -0
  183. nvmath/distributed/linalg/__init__.py +22 -0
  184. nvmath/distributed/linalg/_internal/__init__.py +3 -0
  185. nvmath/distributed/linalg/_internal/epilog_protocol.py +586 -0
  186. nvmath/distributed/linalg/_internal/matmul_desc_ifc.py +28 -0
  187. nvmath/distributed/linalg/advanced/__init__.py +8 -0
  188. nvmath/distributed/linalg/advanced/_configuration.py +171 -0
  189. nvmath/distributed/linalg/advanced/matmulmod.py +3573 -0
  190. nvmath/distributed/linalg/generic/__init__.py +8 -0
  191. nvmath/distributed/linalg/generic/_caching.py +66 -0
  192. nvmath/distributed/linalg/generic/_configuration.py +61 -0
  193. nvmath/distributed/linalg/generic/_factorization.py +172 -0
  194. nvmath/distributed/linalg/generic/_initialization.py +966 -0
  195. nvmath/distributed/linalg/generic/_problem_spec.py +511 -0
  196. nvmath/distributed/linalg/generic/solvermod.py +1368 -0
  197. nvmath/distributed/process_group.py +408 -0
  198. nvmath/fft/__init__.py +7 -0
  199. nvmath/fft/_configuration.py +189 -0
  200. nvmath/fft/_exec_utils.py +82 -0
  201. nvmath/fft/_helpers.py +237 -0
  202. nvmath/fft/fft.py +3122 -0
  203. nvmath/internal/__init__.pxd +3 -0
  204. nvmath/internal/__init__.py +10 -0
  205. nvmath/internal/_bindings.cp314t-win_amd64.pyd +0 -0
  206. nvmath/internal/_bindings.pxd +18 -0
  207. nvmath/internal/_device_utils.py +45 -0
  208. nvmath/internal/_layout/__init__.pxd +3 -0
  209. nvmath/internal/_layout/__init__.py +7 -0
  210. nvmath/internal/_layout/_layout.cp314t-win_amd64.pyd +0 -0
  211. nvmath/internal/_layout/_layout.pxd +1303 -0
  212. nvmath/internal/_layout/_layout.pyi +1145 -0
  213. nvmath/internal/enum_utils.py +142 -0
  214. nvmath/internal/formatters.py +87 -0
  215. nvmath/internal/mem_limit.py +51 -0
  216. nvmath/internal/memory.cp314t-win_amd64.pyd +0 -0
  217. nvmath/internal/memory.pxd +13 -0
  218. nvmath/internal/memory.pyi +50 -0
  219. nvmath/internal/ndbuffer/__init__.pxd +3 -0
  220. nvmath/internal/ndbuffer/__init__.py +9 -0
  221. nvmath/internal/ndbuffer/_copy_kernel.cp314t-win_amd64.pyd +0 -0
  222. nvmath/internal/ndbuffer/_copy_kernel.pxd +10 -0
  223. nvmath/internal/ndbuffer/_jit.cp314t-win_amd64.pyd +0 -0
  224. nvmath/internal/ndbuffer/_jit.pxd +7 -0
  225. nvmath/internal/ndbuffer/_ndbuffer.cp314t-win_amd64.pyd +0 -0
  226. nvmath/internal/ndbuffer/_ndbuffer.pxd +40 -0
  227. nvmath/internal/ndbuffer/_ndbuffer.pyi +463 -0
  228. nvmath/internal/ndbuffer/copy_kernel/args.h +34 -0
  229. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/array_view.h +52 -0
  230. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/elementwise.h +68 -0
  231. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/grid_indexer.h +69 -0
  232. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/transposed.h +242 -0
  233. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/type_utils.h +39 -0
  234. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/utils.h +132 -0
  235. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/vec.h +159 -0
  236. nvmath/internal/ndbuffer/copy_kernel/elementwise.h +53 -0
  237. nvmath/internal/ndbuffer/copy_kernel/transposed.h +58 -0
  238. nvmath/internal/package_ifc.py +168 -0
  239. nvmath/internal/package_ifc_cuda.py +57 -0
  240. nvmath/internal/package_ifc_cupy.py +67 -0
  241. nvmath/internal/package_ifc_torch.py +69 -0
  242. nvmath/internal/package_wrapper.py +14 -0
  243. nvmath/internal/tensor_ifc.py +179 -0
  244. nvmath/internal/tensor_ifc_cupy.py +234 -0
  245. nvmath/internal/tensor_ifc_ndbuffer.py +147 -0
  246. nvmath/internal/tensor_ifc_numpy.py +184 -0
  247. nvmath/internal/tensor_ifc_torch.py +178 -0
  248. nvmath/internal/tensor_wrapper.py +160 -0
  249. nvmath/internal/typemaps.py +113 -0
  250. nvmath/internal/utils.py +805 -0
  251. nvmath/linalg/__init__.py +56 -0
  252. nvmath/linalg/_internal/__init__.py +3 -0
  253. nvmath/linalg/_internal/algo_cap_ifc.py +82 -0
  254. nvmath/linalg/_internal/algo_config_ifc.py +43 -0
  255. nvmath/linalg/_internal/batch.py +234 -0
  256. nvmath/linalg/_internal/enum_to_tuples.py +64 -0
  257. nvmath/linalg/_internal/epilog_protocol.py +766 -0
  258. nvmath/linalg/_internal/layout.py +624 -0
  259. nvmath/linalg/_internal/matmul_desc_ifc.py +28 -0
  260. nvmath/linalg/_internal/matmul_pref_ifc.py +27 -0
  261. nvmath/linalg/_internal/matrix_layout_ifc.py +26 -0
  262. nvmath/linalg/_internal/solver_utils.py +432 -0
  263. nvmath/linalg/_internal/typemaps.py +144 -0
  264. nvmath/linalg/_internal/utils.py +157 -0
  265. nvmath/linalg/advanced/__init__.py +8 -0
  266. nvmath/linalg/advanced/_algorithmmod.py +170 -0
  267. nvmath/linalg/advanced/_configuration.py +351 -0
  268. nvmath/linalg/advanced/helpers/__init__.py +5 -0
  269. nvmath/linalg/advanced/helpers/matmul.py +1316 -0
  270. nvmath/linalg/advanced/matmulmod.py +3734 -0
  271. nvmath/linalg/generic/__init__.py +53 -0
  272. nvmath/linalg/generic/_configuration/__init__.py +39 -0
  273. nvmath/linalg/generic/_configuration/layout.py +263 -0
  274. nvmath/linalg/generic/_configuration/match.py +734 -0
  275. nvmath/linalg/generic/_configuration/qualifiers.py +493 -0
  276. nvmath/linalg/generic/_configuration/solver_configuration.py +59 -0
  277. nvmath/linalg/generic/_configuration/wrap.py +217 -0
  278. nvmath/linalg/generic/_dtype.py +15 -0
  279. nvmath/linalg/generic/matmulmod.py +2094 -0
  280. nvmath/linalg/generic/solvermod.py +1301 -0
  281. nvmath/memory.py +279 -0
  282. nvmath/sparse/__init__.py +38 -0
  283. nvmath/sparse/_internal/__init__.py +21 -0
  284. nvmath/sparse/_internal/common_utils.py +147 -0
  285. nvmath/sparse/_internal/cudss_config_ifc.py +702 -0
  286. nvmath/sparse/_internal/cudss_data_ifc.py +399 -0
  287. nvmath/sparse/_internal/cudss_utils.py +506 -0
  288. nvmath/sparse/_internal/cusparse_utils.py +382 -0
  289. nvmath/sparse/_internal/sparse_bsc_ifc.py +303 -0
  290. nvmath/sparse/_internal/sparse_bsr_ifc.py +305 -0
  291. nvmath/sparse/_internal/sparse_coo_ifc.py +256 -0
  292. nvmath/sparse/_internal/sparse_csc_ifc.py +268 -0
  293. nvmath/sparse/_internal/sparse_csr_ifc.py +288 -0
  294. nvmath/sparse/_internal/sparse_dia_ifc.py +242 -0
  295. nvmath/sparse/_internal/sparse_format_helpers.py +601 -0
  296. nvmath/sparse/_internal/sparse_tensor_ifc.py +133 -0
  297. nvmath/sparse/_internal/sparse_ust_ifc.py +141 -0
  298. nvmath/sparse/_internal/utils.py +56 -0
  299. nvmath/sparse/advanced/__init__.py +7 -0
  300. nvmath/sparse/advanced/_configuration.py +227 -0
  301. nvmath/sparse/advanced/direct_solver.py +2069 -0
  302. nvmath/sparse/generic/__init__.py +7 -0
  303. nvmath/sparse/generic/_configuration.py +129 -0
  304. nvmath/sparse/generic/_helpers.py +137 -0
  305. nvmath/sparse/generic/_thunks.py +21 -0
  306. nvmath/sparse/generic/matmulmod.py +2353 -0
  307. nvmath/sparse/ust/__init__.py +7 -0
  308. nvmath/sparse/ust/_converters.py +422 -0
  309. nvmath/sparse/ust/_cpp.py +28 -0
  310. nvmath/sparse/ust/_drawer.py +565 -0
  311. nvmath/sparse/ust/_emitter.py +1033 -0
  312. nvmath/sparse/ust/_jit.py +188 -0
  313. nvmath/sparse/ust/_kernel.py +282 -0
  314. nvmath/sparse/ust/_utils.py +149 -0
  315. nvmath/sparse/ust/interfaces/__init__.py +0 -0
  316. nvmath/sparse/ust/interfaces/torch_interface.py +476 -0
  317. nvmath/sparse/ust/tensor.py +1016 -0
  318. nvmath/sparse/ust/tensor_format.py +957 -0
  319. nvmath/tensor/__init__.py +6 -0
  320. nvmath/tensor/_configuration.py +120 -0
  321. nvmath/tensor/_internal/__init__.py +3 -0
  322. nvmath/tensor/_internal/cutensor_config_ifc.py +279 -0
  323. nvmath/tensor/_internal/cutensor_utils.py +230 -0
  324. nvmath/tensor/_internal/data.py +43 -0
  325. nvmath/tensor/_internal/einsum_parser.py +444 -0
  326. nvmath/tensor/_internal/typemaps.py +96 -0
  327. nvmath/tensor/contract.py +1900 -0
  328. nvmath_python-1.0.0.dist-info/METADATA +134 -0
  329. nvmath_python-1.0.0.dist-info/RECORD +332 -0
  330. nvmath_python-1.0.0.dist-info/WHEEL +5 -0
  331. nvmath_python-1.0.0.dist-info/licenses/LICENSE +177 -0
  332. nvmath_python-1.0.0.dist-info/top_level.txt +2 -0
nvmath/__init__.pxd ADDED
File without changes
nvmath/__init__.py ADDED
@@ -0,0 +1,45 @@
1
+ # Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import importlib.metadata
6
+
7
+
8
+ # Attempt to preload libraries. Fail silently if preload fails.
9
+ def _force_lib_load():
10
+ from nvmath._utils import module_init_force_cupy_lib_load
11
+
12
+ module_init_force_cupy_lib_load()
13
+
14
+
15
+ _force_lib_load()
16
+
17
+ from nvmath import ( # noqa: E402
18
+ bindings, # noqa: E402
19
+ fft,
20
+ linalg,
21
+ sparse,
22
+ tensor,
23
+ )
24
+ from nvmath._utils import ( # noqa: E402
25
+ ComputeType,
26
+ CudaDataType,
27
+ LibraryPropertyType,
28
+ )
29
+ from nvmath.memory import BaseCUDAMemoryManager, BaseCUDAMemoryManagerAsync, MemoryPointer # noqa: E402
30
+
31
+ __all__ = [
32
+ "BaseCUDAMemoryManager",
33
+ "BaseCUDAMemoryManagerAsync",
34
+ "bindings",
35
+ "ComputeType",
36
+ "CudaDataType",
37
+ "fft",
38
+ "LibraryPropertyType",
39
+ "linalg",
40
+ "MemoryPointer",
41
+ "sparse",
42
+ "tensor",
43
+ ]
44
+
45
+ __version__ = importlib.metadata.version("nvmath-python")
File without changes
@@ -0,0 +1,330 @@
1
+ # Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Generic factory that builds a Python class with one ``property`` per
7
+ enum member, plus optional ``set_<attr>_unchecked`` methods, from
8
+ caller-supplied closures.
9
+
10
+ The low-level :func:`_make_attribute_interface_impl` is generic:
11
+ it receives *make_getter* / *make_setter* / *make_setter_unchecked*
12
+ callables that produce the per-attribute closures, then attaches them
13
+ as properties.
14
+
15
+ Library-specific wrappers (e.g. :func:`make_cublas_attribute_interface`)
16
+ supply the closure factories that encode a particular C API convention
17
+ (argument order, ``size_written`` type, logging, etc.).
18
+ """
19
+
20
+ __all__ = ["make_cublas_attribute_interface"]
21
+
22
+ import ctypes
23
+ import enum
24
+ import logging
25
+ import sys
26
+ from collections.abc import Callable
27
+ from typing import TYPE_CHECKING, Any
28
+
29
+ import numpy as np
30
+
31
+ logger = logging.getLogger()
32
+
33
+ # A closure factory accepted by _impl. Called once per attribute name;
34
+ # returns the bound method to attach as a getter, setter, etc.
35
+ _ClosureFactory = Callable[[str], Callable]
36
+
37
+
38
+ def _make_attribute_interface_impl(
39
+ *,
40
+ class_module: str,
41
+ class_name: str,
42
+ attribute_names: list[str],
43
+ make_getter: _ClosureFactory,
44
+ make_setter: _ClosureFactory | None = None,
45
+ make_setter_unchecked: _ClosureFactory | None = None,
46
+ ) -> type:
47
+ """Build and return an attribute interface class.
48
+
49
+ This is the low-level implementation behind library-specific wrappers
50
+ such as :func:`make_cublas_attribute_interface`. It performs no
51
+ caller-site validation and is completely agnostic to the underlying
52
+ C API convention -- all library-specific logic lives in the closure
53
+ factories passed by the caller.
54
+
55
+ Args:
56
+ class_module: ``__module__`` for the generated class.
57
+ class_name: ``__name__`` and ``__qualname__`` for the generated
58
+ class.
59
+ attribute_names: Uppercase attribute names (e.g.
60
+ ``["POINTER_MODE", "EPILOGUE"]``).
61
+ make_getter: ``(name) -> getter`` factory that returns a
62
+ property getter closure.
63
+ make_setter: ``(name) -> setter`` factory that returns a
64
+ property setter closure, or ``None`` for read-only
65
+ interfaces.
66
+ make_setter_unchecked: ``(name) -> setter`` factory for
67
+ hot-path setters without logging, or ``None`` to skip.
68
+ """
69
+
70
+ class AttributeInterface: # noqa: D101
71
+ if TYPE_CHECKING:
72
+
73
+ def __getattr__(self, name: str) -> Any: ...
74
+ def __setattr__(self, name: str, value: Any) -> None: ...
75
+
76
+ def __init__(self, obj: Any) -> None:
77
+ self._handle = obj
78
+
79
+ AttributeInterface.__name__ = class_name
80
+ AttributeInterface.__qualname__ = class_name
81
+ AttributeInterface.__module__ = class_module
82
+
83
+ for _name in attribute_names:
84
+ _lower = _name.lower()
85
+
86
+ getter = make_getter(_name)
87
+
88
+ if make_setter is not None:
89
+ setter = make_setter(_name)
90
+ if make_setter_unchecked is not None:
91
+ setattr(
92
+ AttributeInterface,
93
+ f"set_{_lower}_unchecked",
94
+ make_setter_unchecked(_name),
95
+ )
96
+ else:
97
+ setter = None
98
+ setattr(AttributeInterface, _lower, property(getter, setter))
99
+
100
+ return AttributeInterface
101
+
102
+
103
+ # ---------------------------------------------------------------------------
104
+ # cuBLAS-specific wrapper
105
+ # ---------------------------------------------------------------------------
106
+
107
+
108
+ def _build_enum_to_ctype_map(
109
+ attribute_enum: type[enum.IntEnum],
110
+ get_attribute_dtype_fn: Callable[[enum.IntEnum], np.dtype],
111
+ exclude: set[str] | None = None,
112
+ ) -> dict[str, tuple[int, type, int]]:
113
+ """
114
+ Return ``{NAME: (enum_int_value, ctypes_type, sizeof)}`` for every
115
+ non-deprecated enum member whose name is not in *exclude*.
116
+ """
117
+ result: dict[str, tuple[int, type, int]] = {}
118
+ for member in attribute_enum:
119
+ name: str = member.name
120
+
121
+ if exclude and name in exclude:
122
+ continue
123
+
124
+ try:
125
+ dtype: np.dtype = get_attribute_dtype_fn(member)
126
+ except ValueError:
127
+ continue
128
+
129
+ try:
130
+ ctype = np.ctypeslib.as_ctypes_type(dtype)
131
+ except NotImplementedError as exc:
132
+ raise TypeError(
133
+ f"get_attribute_dtype_fn returned a dtype for {name!r} that cannot be converted to a ctypes type"
134
+ ) from exc
135
+
136
+ result[name] = (member.value, ctype, ctypes.sizeof(ctype))
137
+
138
+ return result
139
+
140
+
141
+ def _cublas_interface_class_doc(
142
+ *,
143
+ attribute_enum: type[enum.IntEnum],
144
+ get_attribute_fn: Callable[..., None],
145
+ set_attribute_fn: Callable[..., None] | None,
146
+ with_unchecked: bool,
147
+ attribute_names: list[str],
148
+ ) -> str:
149
+ enum_qualname = f"{attribute_enum.__module__}.{attribute_enum.__qualname__}"
150
+ getter_qualname = f"{get_attribute_fn.__module__}.{get_attribute_fn.__qualname__}"
151
+
152
+ names_sorted = sorted(attribute_names)
153
+ if names_sorted:
154
+ example_upper = names_sorted[0]
155
+ example_lower = example_upper.lower()
156
+ else:
157
+ example_upper = "UPPER_CASE"
158
+ example_lower = "lower_case"
159
+
160
+ properties_extra = (
161
+ " (and their ``set_<attr>_unchecked`` variants)" if set_attribute_fn is not None and with_unchecked else ""
162
+ )
163
+
164
+ lines = [
165
+ f"Property-based access to attributes enumerated in\n``{enum_qualname}``.",
166
+ "",
167
+ f"Properties{properties_extra} are generated \ndynamically at import time by ``make_cublas_attribute_interface``.",
168
+ "",
169
+ "This avoids hand-writing a property for every entry in "
170
+ f"``{enum_qualname}``, \nkeeps the interface in sync automatically when new\n"
171
+ "attributes are added to the binding, and normalises the UPPER_CASE enum\n"
172
+ "names to lowercase property names "
173
+ f"(e.g. ``{example_upper}`` becomes ``{example_lower}``).",
174
+ "",
175
+ ]
176
+
177
+ if set_attribute_fn is not None:
178
+ set_qualname = f"{set_attribute_fn.__module__}.{set_attribute_fn.__qualname__}"
179
+ delegates = f"``{getter_qualname}`` / ``{set_qualname}``"
180
+ else:
181
+ delegates = f"``{getter_qualname}``"
182
+ lines.append(
183
+ "Each generated property delegates to the corresponding\n"
184
+ f"{delegates} \n"
185
+ "call with the correct enum value and ctypes type\n"
186
+ "already baked in via closure."
187
+ )
188
+
189
+ if set_attribute_fn is not None and with_unchecked:
190
+ lines.extend(
191
+ [
192
+ "",
193
+ "The ``set_<attr>_unchecked`` methods are identical to the property\n"
194
+ "setters but skip debug logging. They are useful in low-overhead code paths \n"
195
+ "where the attribute type has already been validated during initialization.",
196
+ ]
197
+ )
198
+
199
+ return "\n".join(lines)
200
+
201
+
202
+ def make_cublas_attribute_interface(
203
+ *,
204
+ class_module: str,
205
+ class_name: str,
206
+ attribute_enum: type[enum.IntEnum],
207
+ get_attribute_dtype_fn: Callable[[enum.IntEnum], np.dtype],
208
+ get_attribute_fn: Callable[..., None],
209
+ set_attribute_fn: Callable[..., None] | None = None,
210
+ with_unchecked: bool = False,
211
+ exclude: set[str] | None = None,
212
+ ) -> type:
213
+ """
214
+ Build and return an attribute interface class for a cuBLAS-style
215
+ descriptor whose attributes are accessed through a uniform
216
+ ``*_get_attribute`` / ``*_set_attribute`` C API pattern.
217
+
218
+ The returned class's docstring is generated automatically from the
219
+ enum and the getter/setter callables (see *Returns*).
220
+
221
+ This is the public entry point for all cublasLt and cublasMp
222
+ descriptor / algorithm interface modules.
223
+ For example, the entire ``MatmulDescInterface`` for
224
+ ``nvmath.linalg`` is reduced to::
225
+
226
+ from nvmath.bindings import cublasLt as cublaslt
227
+
228
+ MatmulDescInterface = make_cublas_attribute_interface(
229
+ class_module=__name__,
230
+ class_name="MatmulDescInterface",
231
+ attribute_enum=cublaslt.MatmulDescAttribute,
232
+ get_attribute_dtype_fn=cublaslt.get_matmul_desc_attribute_dtype,
233
+ get_attribute_fn=cublaslt.matmul_desc_get_attribute,
234
+ set_attribute_fn=cublaslt.matmul_desc_set_attribute,
235
+ )
236
+
237
+ The opaque library handle passed to ``__init__`` is stored as
238
+ ``self._handle``. Subclasses that need direct access to it (e.g.
239
+ for variable-length attribute queries) should use ``self._handle``.
240
+
241
+ Args:
242
+ class_module: The ``__module__`` to assign to the generated
243
+ class. Callers should pass ``__name__``.
244
+ class_name: The ``__name__`` and ``__qualname__`` to assign to
245
+ the generated class. Must match the module-level variable
246
+ name the caller assigns the result to.
247
+ attribute_enum: The binding's enum type whose members enumerate
248
+ all gettable/settable attributes.
249
+ get_attribute_dtype_fn: Callable that maps an enum member to its
250
+ numpy dtype.
251
+ get_attribute_fn: Callable invoked as
252
+ ``(handle, enum_value, buf_addr, buf_size, size_written_addr)``
253
+ that must store the attribute's value into *buf_addr*.
254
+ set_attribute_fn: Callable invoked as
255
+ ``(handle, enum_value, buf_addr, buf_size)`` that must apply
256
+ the new value from *buf_addr*. ``None`` for read-only
257
+ interfaces.
258
+ with_unchecked: If ``True``, generate ``set_<attr>_unchecked``
259
+ methods (no debug logging). Requires *set_attribute_fn*.
260
+ exclude: Enum member names to skip.
261
+
262
+ Returns:
263
+ A class with one *lowercase* property per enum member.
264
+ Its ``__doc__`` is built automatically from the enum
265
+ and the getter/setter callables.
266
+
267
+ .. note::
268
+ This function **must** be called at module scope (i.e. as a
269
+ top-level statement in the module body). A ``RuntimeError`` is
270
+ raised otherwise. This constraint ensures that the generated
271
+ class receives a correct ``__qualname__``.
272
+ """
273
+
274
+ if with_unchecked and set_attribute_fn is None:
275
+ raise ValueError("with_unchecked=True requires set_attribute_fn")
276
+
277
+ caller_frame = sys._getframe(1)
278
+ if caller_frame.f_code.co_name != "<module>":
279
+ raise RuntimeError("make_cublas_attribute_interface must be called at module level")
280
+
281
+ attr_info = _build_enum_to_ctype_map(attribute_enum, get_attribute_dtype_fn, exclude)
282
+
283
+ def _cublas_make_getter(name):
284
+ enum_value, ctype, sizeof = attr_info[name]
285
+
286
+ def getter(self):
287
+ logger.debug("Getting attribute %s.", name)
288
+ buf = ctype()
289
+ sw = ctypes.c_uint64()
290
+ get_attribute_fn(self._handle, enum_value, ctypes.addressof(buf), sizeof, ctypes.addressof(sw))
291
+ return buf.value
292
+
293
+ return getter
294
+
295
+ def _cublas_make_setter(name):
296
+ enum_value, ctype, sizeof = attr_info[name]
297
+
298
+ def setter(self, value):
299
+ logger.debug("Setting attribute %s to %s.", name, value)
300
+ v = ctype(value)
301
+ set_attribute_fn(self._handle, enum_value, ctypes.addressof(v), sizeof)
302
+
303
+ return setter
304
+
305
+ def _cublas_make_setter_unchecked(name):
306
+ enum_value, ctype, sizeof = attr_info[name]
307
+
308
+ def setter_unc(self, value):
309
+ v = ctype(value)
310
+ set_attribute_fn(self._handle, enum_value, ctypes.addressof(v), sizeof)
311
+
312
+ return setter_unc
313
+
314
+ attr_names = list(attr_info)
315
+ iface_cls = _make_attribute_interface_impl(
316
+ class_module=class_module,
317
+ class_name=class_name,
318
+ attribute_names=attr_names,
319
+ make_getter=_cublas_make_getter,
320
+ make_setter=_cublas_make_setter if set_attribute_fn is not None else None,
321
+ make_setter_unchecked=(_cublas_make_setter_unchecked if (set_attribute_fn is not None and with_unchecked) else None),
322
+ )
323
+ iface_cls.__doc__ = _cublas_interface_class_doc(
324
+ attribute_enum=attribute_enum,
325
+ get_attribute_fn=get_attribute_fn,
326
+ set_attribute_fn=set_attribute_fn,
327
+ with_unchecked=with_unchecked,
328
+ attribute_names=attr_names,
329
+ )
330
+ return iface_cls
@@ -0,0 +1,70 @@
1
+ # Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ A collection of (internal use) helper functions for shape/stride
7
+ validation and manipulation.
8
+ """
9
+
10
+ from collections.abc import Sequence
11
+
12
+
13
+ def is_contiguous_layout(sorted_shape: Sequence[int], sorted_strides: Sequence[int]) -> bool:
14
+ return all(sorted_shape[s - 1] * sorted_strides[s - 1] == sorted_strides[s] for s in range(1, len(sorted_strides)))
15
+
16
+
17
+ def is_contiguous_in_memory(shape: Sequence[int], strides: Sequence[int]) -> bool:
18
+ """
19
+ Check if the provided (shape, strides) result in a contiguous memory layout.
20
+ """
21
+ sorted_strides, sorted_shape = zip(*sorted(zip(strides, shape, strict=True)), strict=True)
22
+ return is_contiguous_layout(sorted_shape, sorted_strides)
23
+
24
+
25
+ def is_contiguous_and_dense(shape: Sequence[int], strides: Sequence[int]) -> bool:
26
+ """
27
+ Check if the provided (shape, strides) result in a contiguous memory layout
28
+ with no extra stride in least strided dimension.
29
+ """
30
+ if not shape and not strides:
31
+ return True
32
+ sorted_strides, sorted_shape = zip(*sorted(zip(strides, shape, strict=True)), strict=True)
33
+ if len(sorted_strides) > 0 and sorted_strides[0] != 1:
34
+ return False
35
+ return is_contiguous_layout(sorted_shape, sorted_strides)
36
+
37
+
38
+ def is_overlapping_layout(shape: Sequence[int], strides: Sequence[int]) -> bool:
39
+ """
40
+ For a tensor `t`, if `not is_overlapping_layout(t.shape, t.strides)`,
41
+ there are no two different valid nd-indices `idxs` such that
42
+ `t[idxs_0]` and `t[idxs_1]` map to the same offset in the memory.
43
+ Checks that the strides:
44
+ 1. are positive
45
+ 2. any n - 1 extents maximal offset is smaller than the stride
46
+ of the n-th extent.
47
+ The check should return False for contiguous
48
+ or contiguous and sliced tensors.
49
+ """
50
+ if not shape and not strides:
51
+ return False
52
+ sorted_strides, sorted_shape = zip(*sorted(zip(strides, shape, strict=True)), strict=True)
53
+ cur_max_offset = 0
54
+ for s in range(1, len(sorted_strides)):
55
+ stride = sorted_strides[s - 1]
56
+ extent = sorted_shape[s - 1]
57
+ if stride <= 0:
58
+ return True
59
+ cur_max_offset += stride * (extent - 1)
60
+ if cur_max_offset >= sorted_strides[s]:
61
+ return True
62
+ return False
63
+
64
+
65
+ def check_monotonic_strides(strides, reverse: bool):
66
+ """
67
+ Check if the strides are monotonically increasing (reverse=False) or
68
+ decreasing (reverse=True).
69
+ """
70
+ return sorted(strides, reverse=reverse) == list(strides)
@@ -0,0 +1,130 @@
1
+ # Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import dataclasses
6
+ import logging
7
+ from logging import Logger
8
+ from typing import ClassVar, Literal
9
+
10
+ from nvmath import memory
11
+ from nvmath.internal import utils
12
+
13
+
14
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
15
+ class ExecutionCUDA:
16
+ """
17
+ A data class for providing GPU execution options.
18
+
19
+ Attributes:
20
+ device_id: CUDA device ordinal (only used if the operand resides on the CPU). The
21
+ default value is 0.
22
+
23
+ .. seealso::
24
+ :class:`ExecutionCPU`
25
+ """
26
+
27
+ name: ClassVar[Literal["cuda"]] = "cuda"
28
+ device_id: int = 0
29
+
30
+
31
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
32
+ class ExecutionCPU:
33
+ """
34
+ A data class for providing CPU execution options.
35
+
36
+ Attributes:
37
+ num_threads: The number of CPU threads used to execute the operation.
38
+ If not specified, defaults to the number of CPU cores available to the
39
+ process.
40
+
41
+ .. seealso::
42
+ :class:`ExecutionCUDA`
43
+ """
44
+
45
+ name: ClassVar[Literal["cpu"]] = "cpu"
46
+ num_threads: int | None = None
47
+
48
+
49
+ def copy_operand_perhaps(
50
+ internal_operand: utils.TensorHolder | None,
51
+ operand: utils.TensorHolder,
52
+ stream_holder: utils.StreamHolder | None,
53
+ execution_device_id: int | Literal["cpu"],
54
+ operands_device_id: int | Literal["cpu"],
55
+ ) -> tuple[utils.TensorHolder, utils.TensorHolder | None]:
56
+ """Private implementation of memory space management for tensor operands.
57
+
58
+ The `copy_operand_perhaps` function facilitates transitions of tensor operands between
59
+ different memory spaces, ensuring compatibility with execution requirements. Its role is
60
+ to determine whether a tensor operand needs to be copied to accommodate differing
61
+ execution and operand memory spaces, while preserving the original operand for cases
62
+ requiring in-place operations.
63
+
64
+ Args:
65
+ internal_operand: Represents an internal tensor for in-place
66
+ memory operations, or `None` if not applicable.
67
+
68
+ operand: Tensor to possibly copied to the execution memory space.
69
+
70
+ stream_holder: Manages the CUDA stream for device operations.
71
+
72
+ execution_device_id: Specifies the target execution space.
73
+
74
+ operands_device_id: Specifies the current operand memory space.
75
+
76
+ Returns:
77
+ A tuple containing:
78
+ - The operand copied to the execution space, or the original operand if
79
+ no copy is necessary.
80
+ - The original operand, or `None` if no copy occurred.
81
+
82
+ """
83
+ if execution_device_id == operands_device_id:
84
+ return operand, None
85
+ else:
86
+ # Copy the `operand` to memory that matches the exec space
87
+ # and keep the original `operand` to handle `options.inplace=True`
88
+ if internal_operand is None:
89
+ exec_space_copy = operand.to(execution_device_id, stream_holder)
90
+ return exec_space_copy, operand
91
+ else:
92
+ # In-place copy to existing pointer
93
+ internal_operand.copy_(src=operand, stream_holder=stream_holder)
94
+ return internal_operand, operand
95
+
96
+
97
+ @dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
98
+ class StatefulAPIOptions:
99
+ """A dataclass for providing options to a stateful API object.
100
+
101
+ Attributes:
102
+ allocator: An object that supports the :class:`BaseCUDAMemoryManager` protocol, used
103
+ to draw device memory. If an allocator is not provided, a memory allocator from
104
+ the library package will be used (:func:`torch.cuda.caching_allocator_alloc` for
105
+ PyTorch operands, :func:`cupy.cuda.alloc` otherwise).
106
+
107
+ blocking: A flag specifying the behavior of the stream-ordered functions and
108
+ methods. When ``blocking`` is `True`, the stream-ordered methods do not return
109
+ until the operation is complete. When ``blocking`` is ``"auto"``, the methods
110
+ return immediately when the inputs are on the GPU. The stream-ordered methods
111
+ always block when the operands are on the CPU to ensure that the user doesn't
112
+ inadvertently use the result before it becomes available. The default is
113
+ ``"auto"``.
114
+
115
+ logger: Python Logger object. The root logger will be used if a
116
+ logger object is not provided.
117
+ """
118
+
119
+ allocator: memory.BaseCUDAMemoryManager | memory.BaseCUDAMemoryManagerAsync | None = None
120
+ blocking: Literal[True, "auto"] = "auto"
121
+ logger: Logger = dataclasses.field(default_factory=logging.getLogger)
122
+
123
+ def __post_init__(self):
124
+ if self.blocking not in (True, "auto"):
125
+ raise ValueError("The value specified for blocking must be either True or 'auto'.")
126
+
127
+ if self.allocator is not None and not isinstance(
128
+ self.allocator, memory.BaseCUDAMemoryManager | memory.BaseCUDAMemoryManagerAsync
129
+ ):
130
+ raise TypeError("The allocator must be an object of type that fulfills the BaseCUDAMemoryManager protocol.")
@@ -0,0 +1,106 @@
1
+ # Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """Reusable per-thread data structures."""
6
+
7
+ import sys
8
+ import threading
9
+ import typing
10
+ import weakref
11
+
12
+ __all__ = ["HandleCache"]
13
+
14
+ Key = typing.TypeVar("Key")
15
+
16
+
17
+ class _CachedHandle(typing.Generic[Key]):
18
+ # A raw integer handle cannot be the referent of `weakref.finalize`, so we wrap it in
19
+ # this object and attach the finalizer to the wrapper. The finalizer fires when the
20
+ # wrapper is garbage-collected — i.e. when the owning thread's `threading.local` storage
21
+ # is dropped, or at interpreter shutdown.
22
+
23
+ __slots__ = ("handle", "__weakref__")
24
+
25
+ handle: typing.Final[int]
26
+
27
+ def __init__(self, key: Key, create: typing.Callable[[Key], int], destroy: typing.Callable[[int], None]):
28
+ self.handle = create(key)
29
+
30
+ def finalize(handle: int) -> None:
31
+ # `destroy` is the binding-agnostic callable injected by the consumer. It is
32
+ # expected to be safe to run on any thread regardless of the current device:
33
+ # e.g. `cublasDestroy` / `cublasLtDestroy` each internally call
34
+ # `cudaDeviceSynchronize()` on the handle's own device. That matters because the
35
+ # finalizer may run on whichever thread garbage-collects the cached handle, not
36
+ # necessarily the one that created it.
37
+ try:
38
+ destroy(handle)
39
+ except Exception:
40
+ # Tolerate destroy failures only at interpreter shutdown, where the bindings
41
+ # module may already be partially torn down. Outside shutdown a destroy
42
+ # failure is a real bug; re-raising surfaces it as the standard "Exception
43
+ # ignored in:" message from the finalizer rather than silently leaking the
44
+ # handle.
45
+ if not sys.is_finalizing():
46
+ raise
47
+
48
+ weakref.finalize(self, finalize, self.handle)
49
+
50
+
51
+ class HandleCache(typing.Generic[Key]):
52
+ """Per-thread cache of integer library handles, keyed by an opaque key.
53
+
54
+ The key is any hashable the caller chooses: a bare ``device_id``, or a tuple such as
55
+ ``(device_id, ...)`` for libraries that need to distinguish handles by more than device.
56
+ The cache never interprets the key — only the injected ``create`` callable does. Because
57
+ the caching behavior is per-thread, keys only have to be unique per-thread.
58
+
59
+ Args:
60
+
61
+ create: ``create(key) -> int`` makes a new handle for ``key``. The
62
+ callable must perform any device-context handling it needs.
63
+
64
+ destroy: ``destroy(handle) -> None`` destroys a handle. It is invoked
65
+ from ``weakref.finalize`` when the owning thread or the interpreter exits, so
66
+ must be able to run on a different thread than the one that created the handle.
67
+
68
+ Examples:
69
+
70
+ .. code-block:: python
71
+
72
+ from nvmath._internal import threadsafe
73
+
74
+
75
+ def create_handle(device_id: int) -> int:
76
+ with utils.device_ctx(device_id):
77
+ return cublas.create()
78
+
79
+
80
+ threadsafe.HandleCache[int](
81
+ create=create_handle,
82
+ destroy=cublas.destroy,
83
+ )
84
+
85
+ """
86
+
87
+ __slots__ = ("_create", "_destroy", "_tls")
88
+
89
+ def __init__(self, create: typing.Callable[[Key], int], destroy: typing.Callable[[int], None]):
90
+ self._create = create
91
+ self._destroy = destroy
92
+ # Per-thread storage; each thread mutates only its own `cache` dict, so
93
+ # no lock is required.
94
+ self._tls = threading.local()
95
+
96
+ def get(self, key: Key) -> int:
97
+ """Return the cached handle for ``key`` on the current thread."""
98
+ try:
99
+ cache = self._tls.cache
100
+ except AttributeError:
101
+ cache = self._tls.cache = {}
102
+ item = cache.get(key)
103
+ if item is None:
104
+ item = _CachedHandle(key, self._create, self._destroy)
105
+ cache[key] = item
106
+ return item.handle