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
@@ -0,0 +1,408 @@
1
+ # Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import logging
6
+ import math
7
+ import pickle
8
+ from abc import ABC, abstractmethod
9
+ from collections.abc import Callable
10
+ from enum import IntEnum
11
+ from typing import Literal, TypeAlias, TypeVar
12
+
13
+ import numpy as np
14
+
15
+ __all__ = [
16
+ "ReductionOp",
17
+ "ProcessGroup",
18
+ "MPIProcessGroup",
19
+ "TorchProcessGroup",
20
+ ]
21
+
22
+
23
+ class ReductionOp(IntEnum):
24
+ SUM = 0
25
+ MIN = 1
26
+ MAX = 2
27
+
28
+
29
+ T = TypeVar("T")
30
+ Reducer: TypeAlias = Callable[[T, T], T]
31
+
32
+
33
+ class ProcessGroup(ABC):
34
+ """A ProcessGroup represents a set of processes collectively running
35
+ ``nvmath.distributed`` operations. A ProcessGroup implements a small set of
36
+ communication collectives used by nvmath-python to initialize the distributed
37
+ runtime and set up distributed math operations. It is not used for compute
38
+ (each math library uses its chosen communication backend(s), like NCCL or NVSHMEM).
39
+ """
40
+
41
+ @property
42
+ @abstractmethod
43
+ def rank(self) -> int:
44
+ "Index of this process in the group."
45
+ raise NotImplementedError
46
+
47
+ @property
48
+ @abstractmethod
49
+ def nranks(self) -> int:
50
+ """Number of processes in the group."""
51
+ raise NotImplementedError
52
+
53
+ @abstractmethod
54
+ def broadcast_buffer(self, array: np.ndarray, *, root: int = 0) -> None:
55
+ """Broadcast an array from one process to every process.
56
+
57
+ Args:
58
+ array: input (on root) and output of the collective.
59
+
60
+ root: rank of sending process.
61
+ """
62
+ raise NotImplementedError
63
+
64
+ @abstractmethod
65
+ def allreduce_buffer(self, array: np.ndarray, *, op: ReductionOp) -> None:
66
+ """Allreduce an array.
67
+
68
+ Args:
69
+ array: Input and output of the collective. The function operates in-place.
70
+
71
+ op: One of the values from :class:`ReduceOp` enum. Specifies an operation for
72
+ element-wise reductions.
73
+ """
74
+ raise NotImplementedError
75
+
76
+ @abstractmethod
77
+ def allreduce_object(self, obj: T, *, op: Reducer[T]) -> T:
78
+ """Reduces all Python objects contributed by members of the group. The result
79
+ is a single reduced object which is returned on every process.
80
+
81
+ Args:
82
+ obj: object contributed by this process.
83
+
84
+ op: A Python function that takes two objects and returns a single (reduced)
85
+ object.
86
+ """
87
+ raise NotImplementedError
88
+
89
+
90
+ class MPIProcessGroup(ProcessGroup):
91
+ """ProcessGroup implemented on mpi4py.
92
+
93
+ Args:
94
+ mpi_comm: mpi4py communicator.
95
+ """
96
+
97
+ # Map from mpi4py reduction ops to this module's ReductionOp.
98
+ _reduction_op_map = {} # type: ignore
99
+
100
+ def __init__(self, mpi_comm):
101
+ if not MPIProcessGroup._reduction_op_map:
102
+ from mpi4py import MPI
103
+
104
+ MPIProcessGroup._reduction_op_map = {
105
+ ReductionOp.MIN: MPI.MIN,
106
+ ReductionOp.MAX: MPI.MAX,
107
+ ReductionOp.SUM: MPI.SUM,
108
+ }
109
+ self._mpi_comm = mpi_comm
110
+
111
+ @property
112
+ def rank(self) -> int:
113
+ return self._mpi_comm.Get_rank()
114
+
115
+ @property
116
+ def nranks(self) -> int:
117
+ return self._mpi_comm.Get_size()
118
+
119
+ def broadcast_buffer(self, array: np.ndarray, *, root: int = 0) -> None:
120
+ self._mpi_comm.Bcast(array, root=root)
121
+
122
+ def allreduce_buffer(self, array: np.ndarray, *, op: ReductionOp) -> None:
123
+ mpi_reduction_op = MPIProcessGroup._reduction_op_map[op]
124
+ from mpi4py import MPI
125
+
126
+ self._mpi_comm.Allreduce(MPI.IN_PLACE, array, mpi_reduction_op)
127
+
128
+ def allreduce_object(self, obj: T, *, op: Reducer[T]) -> T:
129
+ return self._mpi_comm.allreduce(obj, op=op)
130
+
131
+
132
+ def _init_binomial_tree(rank: int, nranks: int) -> tuple[int | None, list[int]]:
133
+ """Return parent and children of process with given rank in a binomial
134
+ tree of nranks processes."""
135
+ max_height = math.ceil(math.log2(nranks))
136
+ max_size = 1 << max_height # max number of nodes in tree (2^max_height)
137
+
138
+ # Compute order/height of this subtree (in a full binomial tree of max_size nodes).
139
+ # Relabel this node for the computation (so 0 is deepest leaf instead of root).
140
+ p = label = max_size - 1 - rank
141
+ order = 0
142
+ while p > 0:
143
+ if p % 2 == 0:
144
+ break
145
+ p //= 2
146
+ order += 1
147
+
148
+ if rank == 0:
149
+ parent = None
150
+ else:
151
+ parent = label + (1 << order) # parent = label + 2^order
152
+ parent = max_size - 1 - parent # convert back to original numbering scheme
153
+
154
+ children = []
155
+ # Max number of children is the order of this subtree.
156
+ for i in range(order):
157
+ child = label - (1 << i) # child = label - 2^i
158
+ child = max_size - 1 - child # convert back to original numbering scheme
159
+ if child <= nranks - 1:
160
+ children.append(child)
161
+
162
+ return parent, children
163
+
164
+
165
+ class MsgSizeError(Exception):
166
+ def __init__(self, size: int):
167
+ self.size = size
168
+
169
+
170
+ class TorchProcessGroup(ProcessGroup):
171
+ """ProcessGroup implemented on ``torch.distributed``.
172
+
173
+ Args:
174
+ device_id: Device used by the ``torch.distributed`` process group backend.
175
+
176
+ torch_process_group: ``torch.distributed`` process group handle (e.g. returned
177
+ by ``torch.distributed.new_group()``), or None to use the default torch
178
+ process group.
179
+ """
180
+
181
+ # Map from torch.distributed reduction ops to this module's ReductionOp.
182
+ _reduction_op_map = {} # type: ignore
183
+ # The minimum buffer size (in bytes) for allreduce_object()
184
+ MIN_ALL_REDUCE_OBJ_BUFFER_SIZE = 128
185
+
186
+ def __init__(
187
+ self,
188
+ *,
189
+ device_id: int | Literal["cpu"],
190
+ torch_process_group=None,
191
+ logger=None,
192
+ ):
193
+ import torch
194
+ import torch.distributed as dist
195
+
196
+ if not isinstance(device_id, int) and device_id != "cpu":
197
+ raise ValueError("device_id must be int or 'cpu'")
198
+
199
+ if not dist.is_initialized():
200
+ raise RuntimeError("torch.distributed is not initialized")
201
+
202
+ if logger is None:
203
+ logger = logging.getLogger()
204
+ self.logger = logger
205
+
206
+ # Get communication backend used by this torch process group.
207
+ backend = dist.get_backend(torch_process_group)
208
+ if backend in ("nccl", "xccl") and device_id == "cpu":
209
+ raise ValueError(f"This process group is using a GPU-only backend ({backend}), but the provided device_id is 'cpu'")
210
+ if backend == "mpi":
211
+ logger.warning(
212
+ "You're using torch.distributed with MPI backend. In this case it's "
213
+ "recommended to initialize nvmath.distributed with "
214
+ "nvmath.distributed.process_group.MPIProcessGroup instead of "
215
+ "TorchProcessGroup."
216
+ )
217
+
218
+ self._device_id = device_id
219
+ self._device = torch.device(f"cuda:{device_id}") if device_id != "cpu" else torch.device("cpu")
220
+ self._torch_process_group = torch_process_group
221
+
222
+ if not TorchProcessGroup._reduction_op_map:
223
+ TorchProcessGroup._reduction_op_map = {
224
+ ReductionOp.MIN: dist.ReduceOp.MIN,
225
+ ReductionOp.MAX: dist.ReduceOp.MAX,
226
+ ReductionOp.SUM: dist.ReduceOp.SUM,
227
+ }
228
+
229
+ if backend == "nccl":
230
+ # Ensure a NCCL communicator is formed for the whole group. Otherwise if a
231
+ # parent NCCL communicator is not yet formed when batched P2P operations are
232
+ # called on a subset of ranks (see allreduce_object() below), they could fail.
233
+ with torch.cuda.device(self._device):
234
+ a = torch.ones(1, device=self._device)
235
+ dist.all_reduce(a, op=dist.ReduceOp.SUM, group=self._torch_process_group)
236
+ if a[0] != self.nranks:
237
+ raise RuntimeError("torch.distributed incorrect all_reduce output")
238
+
239
+ # A serialized MsgSizeError must fit in MIN_ALL_REDUCE_OBJ_BUFFER_SIZE
240
+ assert len(pickle.dumps(MsgSizeError(1 << 30))) < self.MIN_ALL_REDUCE_OBJ_BUFFER_SIZE, "Internal error."
241
+ # Initial buffer size for allreduce_object (in bytes)
242
+ self.allreduce_obj_buffer_size = 2048
243
+
244
+ # Determine my parent and children for allreduce_object() implementation.
245
+ # A binomial tree is good for small message sizes.
246
+ self._parent, self._children = _init_binomial_tree(self.rank, self.nranks)
247
+
248
+ @property
249
+ def device_id(self) -> int | Literal["cpu"]:
250
+ """Device used by the communication backend of this torch process group."""
251
+ return self._device_id
252
+
253
+ @property
254
+ def rank(self) -> int:
255
+ import torch.distributed as dist
256
+
257
+ return dist.get_rank(group=self._torch_process_group)
258
+
259
+ @property
260
+ def nranks(self) -> int:
261
+ import torch.distributed as dist
262
+
263
+ return dist.get_world_size(group=self._torch_process_group)
264
+
265
+ def broadcast_buffer(self, array: np.ndarray, *, root: int = 0) -> None:
266
+ import torch
267
+
268
+ if self._device_id == "cpu":
269
+ torch.distributed.broadcast(torch.from_numpy(array), group=self._torch_process_group, group_src=root)
270
+ else:
271
+ tensor_gpu = torch.from_numpy(array).to(self._device)
272
+ torch.distributed.broadcast(tensor_gpu, group=self._torch_process_group, group_src=root)
273
+ array[:] = tensor_gpu.cpu().numpy()
274
+
275
+ def allreduce_buffer(self, array: np.ndarray, *, op: ReductionOp) -> None:
276
+ import torch
277
+
278
+ torch_reduction_op = TorchProcessGroup._reduction_op_map[op]
279
+ if self._device_id == "cpu":
280
+ torch.distributed.all_reduce(torch.from_numpy(array), op=torch_reduction_op, group=self._torch_process_group)
281
+ else:
282
+ tensor_gpu = torch.from_numpy(array).to(self._device)
283
+ torch.distributed.all_reduce(tensor_gpu, op=torch_reduction_op, group=self._torch_process_group)
284
+ array[:] = tensor_gpu.cpu().numpy()
285
+
286
+ @property
287
+ def allreduce_obj_buffer_size(self):
288
+ """Current buffer size for allreduce_object() (in bytes)"""
289
+ return self._allreduce_obj_buffer_size
290
+
291
+ @allreduce_obj_buffer_size.setter
292
+ def allreduce_obj_buffer_size(self, value):
293
+ """**NOTE: Same size needs to be set on every process**"""
294
+ if not isinstance(value, int) or value < self.MIN_ALL_REDUCE_OBJ_BUFFER_SIZE:
295
+ raise ValueError(f"Buffer size must be >= {self.MIN_ALL_REDUCE_OBJ_BUFFER_SIZE}")
296
+ self._allreduce_obj_buffer_size = value
297
+
298
+ def allreduce_object(self, obj: T, *, op: Reducer[T]) -> T:
299
+ import torch
300
+
301
+ NUM_TRIES = 5
302
+ for _ in range(NUM_TRIES):
303
+ try:
304
+ if self._device_id != "cpu":
305
+ # It's recommended to set torch cuda device for batch_isend_irecv
306
+ with torch.cuda.device(self._device):
307
+ return self._allreduce_object(obj, op)
308
+ else:
309
+ return self._allreduce_object(obj, op)
310
+ except MsgSizeError as e:
311
+ new_size = int(e.size * 1.2)
312
+ self.logger.warning(
313
+ "allreduce_obj_buffer_size is too small for this allreduce: "
314
+ f"current size is {self.allreduce_obj_buffer_size} and the required "
315
+ f"size is {e.size}. Increasing to {new_size}. "
316
+ "You can set the size manually with 'allreduce_obj_buffer_size' property "
317
+ " to avoid dynamic resizing."
318
+ )
319
+ self.allreduce_obj_buffer_size = new_size
320
+ raise RuntimeError(
321
+ f"Could not find buffer size for allreduce_object() after {NUM_TRIES} tries. "
322
+ "Please set the limit manually with 'allreduce_obj_buffer_size' property."
323
+ )
324
+
325
+ def _allreduce_object(self, obj: T, reducer: Reducer[T]) -> T:
326
+ import torch
327
+ import torch.distributed as dist
328
+
329
+ def to_tensor(obj, out_tensor):
330
+ """Serializes Python object into out_tensor buffer.
331
+ If the tensor buffer is not large enough to accommodate the serialized object,
332
+ insert a serialized MsgSizeError (with required size) instead."""
333
+ pickled_obj = pickle.dumps(obj)
334
+ pickled_obj_arr = np.frombuffer(pickled_obj, dtype=np.uint8).copy()
335
+ if pickled_obj_arr.nbytes > out_tensor.nbytes:
336
+ pickled_obj = pickle.dumps(MsgSizeError(pickled_obj_arr.nbytes))
337
+ pickled_obj_arr = np.frombuffer(pickled_obj, dtype=np.uint8).copy()
338
+ out_tensor[: pickled_obj_arr.nbytes] = torch.from_numpy(pickled_obj_arr)
339
+ return out_tensor
340
+
341
+ def from_tensor(tensor):
342
+ """Deserializes Python object from tensor buffer."""
343
+ return pickle.loads(tensor.cpu().numpy().tobytes())
344
+
345
+ num_children = len(self._children)
346
+
347
+ if num_children > 0:
348
+ # 1) Get partially reduced objects from children.
349
+
350
+ # Post a recv for each child.
351
+ # Use batch_isend_irecv because, when using the NCCL backend, individual
352
+ # (unbatched) point-to-point (P2P) send or receive operations can trigger
353
+ # the creation of new, separate NCCL communicators for each pair of
354
+ # communicating processes, which can have significant overhead or use
355
+ # large amounts of GPU memory. Instead, we want the p2p operations to use
356
+ # the parent NCCL communicator whenever possible.
357
+ data = [
358
+ torch.empty(self._allreduce_obj_buffer_size, dtype=torch.uint8, device=self._device)
359
+ for _ in range(num_children)
360
+ ]
361
+ recv_ops = []
362
+ for i, child in enumerate(self._children):
363
+ # The tag is to match the recv against the remote send (in case the sender
364
+ # is sending multiple different messages).
365
+ recv_op = dist.P2POp(dist.irecv, data[i], group=self._torch_process_group, tag=2355, group_peer=child)
366
+ recv_ops.append(recv_op)
367
+
368
+ reqs = dist.batch_isend_irecv(recv_ops)
369
+ for req in reqs:
370
+ req.wait()
371
+
372
+ # 2) Apply the reducer on my object and objects received from children.
373
+ reduced_obj = obj
374
+ errors = []
375
+ for i in range(num_children):
376
+ child_obj = from_tensor(data[i])
377
+ if isinstance(child_obj, MsgSizeError):
378
+ errors.append(child_obj)
379
+ elif not errors:
380
+ reduced_obj = reducer(reduced_obj, child_obj)
381
+ if errors:
382
+ # Max reduction of all received errors to get the maximum required buffer
383
+ # size for this subtree.
384
+ reduced_obj = MsgSizeError(max(e.size for e in errors)) # type: ignore
385
+ else:
386
+ reduced_obj = obj
387
+
388
+ data = [torch.empty(self._allreduce_obj_buffer_size, dtype=torch.uint8, device=self._device)]
389
+ if self._parent is not None:
390
+ # Send reduced object to parent.
391
+ send_op = dist.P2POp(
392
+ dist.isend, to_tensor(reduced_obj, data[0]), group=self._torch_process_group, tag=2355, group_peer=self._parent
393
+ )
394
+ dist.batch_isend_irecv([send_op])[0].wait()
395
+
396
+ # Wait for broadcast of reduction from root.
397
+ dist.broadcast(data[0], group=self._torch_process_group, group_src=0)
398
+ result = from_tensor(data[0])
399
+ else:
400
+ # This is the root: broadcast the result.
401
+ assert self.rank == 0
402
+ dist.broadcast(to_tensor(reduced_obj, data[0]), group=self._torch_process_group, group_src=0)
403
+ result = reduced_obj
404
+
405
+ if isinstance(result, MsgSizeError):
406
+ raise result
407
+
408
+ return result
nvmath/fft/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ # Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from ._configuration import * # noqa: F403
6
+ from ._helpers import * # noqa: F403
7
+ from .fft import * # noqa: F403
@@ -0,0 +1,189 @@
1
+ # Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ __all__ = ["FFTDirection", "FFTOptions", "DeviceCallable", "ExecutionCUDA", "ExecutionCPU"]
6
+
7
+ from dataclasses import dataclass
8
+ from enum import IntEnum
9
+ from logging import Logger
10
+ from typing import Literal
11
+
12
+ from nvmath._internal import templates
13
+ from nvmath.memory import BaseCUDAMemoryManager
14
+
15
+
16
+ @dataclass(frozen=True, slots=True, kw_only=True)
17
+ class ExecutionCUDA(templates.ExecutionCUDA):
18
+ """
19
+ A data class for providing GPU execution options to the :class:`FFT` object and the
20
+ family of wrapper functions :func:`fft`, :func:`ifft`, :func:`rfft`, and :func:`irfft`.
21
+
22
+ Attributes:
23
+ device_id: CUDA device ordinal (only used if the operand resides on the CPU). The
24
+ default value is 0.
25
+
26
+ .. seealso::
27
+ :class:`ExecutionCPU`, :class:`FFT`, :func:`fft`, :func:`ifft`, :func:`rfft`, and
28
+ :func:`irfft`.
29
+ """
30
+
31
+
32
+ @dataclass(frozen=True, slots=True, kw_only=True)
33
+ class ExecutionCPU(templates.ExecutionCPU):
34
+ """
35
+ A data class for providing CPU execution options to the :class:`FFT` object and the
36
+ family of wrapper functions :func:`fft`, :func:`ifft`, :func:`rfft`, and :func:`irfft`.
37
+
38
+ Attributes:
39
+ num_threads: The number of CPU threads used to execute the plan.
40
+ If not specified, defaults to the number of CPU cores available to the
41
+ process.
42
+
43
+ .. seealso::
44
+ :class:`ExecutionCUDA`, :class:`FFT`, :func:`fft`, :func:`ifft`, :func:`rfft`, and
45
+ :func:`irfft`.
46
+ """
47
+
48
+
49
+ @dataclass
50
+ class FFTOptions:
51
+ """
52
+ A data class for providing options to the :class:`FFT` object and the family of wrapper
53
+ functions :func:`fft`, :func:`ifft`, :func:`rfft`, and :func:`irfft`.
54
+
55
+ Attributes:
56
+ fft_type: The type of FFT to perform, available options include ``'C2C'``,
57
+ ``'C2R'``, and ``'R2C'``. The default is ``'C2C'`` for complex input and
58
+ ``'R2C'`` for real input.
59
+
60
+ inplace: Specify if the operation is in-place (`True` or `False`). The operand is
61
+ overwritten by the result if ``inplace`` is `True`. The default is `False`.
62
+
63
+ .. experimental:: attribute
64
+
65
+ last_axis_parity: For complex-to-real FFT (corresponding to ``fft_type='C2R'``),
66
+ specify whether the size of the last axis in the result should be even or odd.
67
+ The even size is calculated as :math:`2 * (m - 1)`, where :math:`m` is the
68
+ size of the last axis of the operand, and the odd size is calculated as :math:`2
69
+ * (m - 1) + 1`. The specified value should be either ``'even'`` or ``'odd'``,
70
+ with the default being ``'even'``.
71
+
72
+ result_layout: The layout to use for the result, either ``'natural'`` or
73
+ ``'optimized'``. For the ``'natural'`` option, the result layout is the same as
74
+ that of the operand. The default is ``'optimized'``, which generally provides
75
+ much better performance and should be used if the user doesn't care about the
76
+ result layout matching the operand layout. However in rare cases, depending on
77
+ the device type, shape and strides of the operand, and the FFT dimensions, the
78
+ ``'natural'`` layout may perform better. This option is ignored if ``inplace``
79
+ is specified to be True.
80
+
81
+ logger (logging.Logger): Python Logger object. The root logger will be used if a
82
+ logger object is not provided.
83
+
84
+ blocking: A flag specifying the behavior of the execution functions and methods,
85
+ such as :func:`fft` and :meth:`FFT.execute`. When ``blocking`` is `True`, the
86
+ execution methods do not return until the operation is complete. When
87
+ ``blocking`` is ``"auto"``, the methods return immediately when the input tensor
88
+ is on the GPU and ``execution`` is set to 'CUDA'. The execution methods always
89
+ block when the input tensor is on the CPU or ``execution`` is specified to 'CPU'
90
+ to ensure that the user doesn't inadvertently use the result before it becomes
91
+ available. The default is ``"auto"``.
92
+
93
+ allocator: An object that supports the :class:`BaseCUDAMemoryManager` protocol, used
94
+ to draw device memory. If an allocator is not provided, a memory allocator from
95
+ the library package will be used (:func:`torch.cuda.caching_allocator_alloc` for
96
+ PyTorch operands, :func:`cupy.cuda.alloc` otherwise). Currently, CPU execution
97
+ does not allow specifying a memory pool.
98
+
99
+ .. seealso::
100
+ :class:`ExecutionCUDA`, :class:`ExecutionCPU`, :class:`FFT`, :func:`fft`,
101
+ :func:`ifft`, :func:`rfft`, and :func:`irfft`.
102
+ """
103
+
104
+ fft_type: Literal["C2C", "C2R", "R2C"] | None = None
105
+ inplace: bool = False
106
+ last_axis_parity: Literal["even", "odd"] = "even"
107
+ result_layout: Literal["natural", "optimized"] = "optimized"
108
+ logger: Logger | None = None
109
+ blocking: Literal[True, "auto"] = "auto"
110
+ allocator: BaseCUDAMemoryManager | None = None
111
+
112
+ def __post_init__(self):
113
+ valid_fft_types = [None, "C2C", "C2R", "R2C"]
114
+ if self.fft_type not in valid_fft_types:
115
+ raise ValueError(f"The value specified for 'fft_type' must be one of {valid_fft_types}.")
116
+
117
+ if not isinstance(self.inplace, bool):
118
+ raise ValueError("The value specified for 'inplace' must be of type bool (True or False).")
119
+
120
+ valid_last_axis_parity = ["even", "odd"]
121
+ if self.last_axis_parity not in valid_last_axis_parity:
122
+ raise ValueError(f"The value specified for 'last_axis_parity' must be one of {valid_last_axis_parity}.")
123
+
124
+ valid_result_layout_options = ["natural", "optimized"]
125
+ if self.result_layout not in valid_result_layout_options:
126
+ raise ValueError(f"The value specified for 'result_layout' must be one of {valid_result_layout_options}.")
127
+
128
+ if self.blocking not in (True, "auto"):
129
+ raise ValueError("The value specified for 'blocking' must be either True or 'auto'.")
130
+
131
+
132
+ @dataclass
133
+ class DeviceCallable:
134
+ """A data class capturing LTO-IR callables.
135
+
136
+ Attributes:
137
+ ltoir: A device-callable function in LTO-IR format, which can be provided as as
138
+ either as a :class:`bytes` object or as a pointer to the LTO-IR as Python
139
+ :class:`int`.
140
+
141
+ size: The size of the LTO-IR callable. If not specified and a :class:`bytes` object
142
+ is passed for ``ltoir``, the size is calculated from it. If a pointer is
143
+ provided for the LTO-IR, `size` must be specified.
144
+
145
+ data: A device pointer to user data used in the callback. The default is None,
146
+ which means a null pointer will be used in the callback.
147
+
148
+ .. seealso::
149
+ :meth:`FFT.plan`, :func:`fft`, :func:`ifft`, :func:`rfft`, and :func:`irfft`.
150
+ """
151
+
152
+ ltoir: int | bytes | None = None
153
+ size: int | None = None
154
+ data: int | None = None
155
+
156
+ def __post_init__(self):
157
+ if self.ltoir is None:
158
+ return
159
+ if not isinstance(self.ltoir, int | bytes):
160
+ raise ValueError(
161
+ "The LTO-IR code must be provided as a bytes object or as a Python int "
162
+ "representing the pointer to the LTO-IR code."
163
+ )
164
+ if isinstance(self.ltoir, int) and self.size is None:
165
+ raise ValueError(
166
+ "The size of the LTO-IR code specified as a pointer must be explicitly provided using the 'size' option."
167
+ )
168
+ if isinstance(self.ltoir, bytes):
169
+ self.size = len(self.ltoir)
170
+ if not isinstance(self.size, int):
171
+ raise ValueError(f"Invalid size value: {self.size}.")
172
+ if self.data is None:
173
+ self.data = 0
174
+ if not isinstance(self.data, int):
175
+ raise ValueError("The 'data' attribute must be a Python int representing a device pointer to user data.")
176
+
177
+
178
+ KeywordArgType = dict
179
+
180
+
181
+ class FFTDirection(IntEnum):
182
+ """An IntEnum class specifying the direction of the transform.
183
+
184
+ .. seealso::
185
+ :meth:`FFT.execute`, :func:`fft`
186
+ """
187
+
188
+ FORWARD = -1
189
+ INVERSE = 1
@@ -0,0 +1,82 @@
1
+ # Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import os
6
+
7
+ from nvmath.bindings import cufft # type: ignore
8
+ from nvmath.bindings._internal import utils as _bindings_utils # type: ignore
9
+
10
+ IS_EXEC_GPU_AVAILABLE = False
11
+ IS_EXEC_CPU_AVAILABLE = False
12
+ NUM_THREADS_DEFAULT = None
13
+
14
+
15
+ def _get_num_threads_default():
16
+ global NUM_THREADS_DEFAULT
17
+ if NUM_THREADS_DEFAULT is None:
18
+ if os.name == "posix":
19
+ num_threads = len(os.sched_getaffinity(0))
20
+ else:
21
+ # `sched_getaffinity` is not supported on Windows
22
+ num_threads = os.cpu_count() or 1
23
+ num_threads = max(1, num_threads // 2)
24
+ NUM_THREADS_DEFAULT = num_threads
25
+ return NUM_THREADS_DEFAULT
26
+
27
+
28
+ def _check_init_cufft():
29
+ global IS_EXEC_GPU_AVAILABLE
30
+ if not IS_EXEC_GPU_AVAILABLE:
31
+ try:
32
+ cufft.get_version()
33
+ except (
34
+ _bindings_utils.FunctionNotFoundError,
35
+ _bindings_utils.NotSupportedError,
36
+ RuntimeError,
37
+ ) as e:
38
+ raise RuntimeError(
39
+ "The FFT CUDA execution is not available. "
40
+ "Please check if CUDA toolkit and cuFFT are installed and visible to nvmath."
41
+ ) from e
42
+
43
+ IS_EXEC_GPU_AVAILABLE = True
44
+
45
+
46
+ def _check_init_fftw():
47
+ global IS_EXEC_CPU_AVAILABLE
48
+ if not IS_EXEC_CPU_AVAILABLE:
49
+ from nvmath.bindings.nvpl import fft as _fftw
50
+ from nvmath.bindings.nvpl._internal import fft as _internal_fftw
51
+
52
+ env_lib_name = os.environ.get("NVMATH_FFT_CPU_LIBRARY")
53
+
54
+ if env_lib_name:
55
+ _internal_fftw._set_lib_so_names((env_lib_name,))
56
+ try:
57
+ _internal_fftw._inspect_function_pointers()
58
+ loaded_lib_name = _internal_fftw._get_current_lib_so_name()
59
+ except (
60
+ _bindings_utils.FunctionNotFoundError,
61
+ _bindings_utils.NotSupportedError,
62
+ RuntimeError,
63
+ ) as e:
64
+ raise RuntimeError(
65
+ "The FFT CPU execution is not available, because no FFTW-compatible "
66
+ "library was found. Please make sure to install optional dependency required "
67
+ "for FFT execution (NVPL on aarch64 platforms or MKL on x86_64 platforms). "
68
+ "Users can use other FFTW3-compatible libraries. In order to do that, please "
69
+ "specify the library name as "
70
+ "`NVMATH_FFT_CPU_LIBRARY=some_fftw3_compatible_lib.so` and make sure "
71
+ "the library can be found within current `LD_LIBRARY_PATH`"
72
+ ) from e
73
+
74
+ if env_lib_name:
75
+ assert env_lib_name == loaded_lib_name
76
+
77
+ lib_nvpl = "libnvpl_fftw.so"
78
+ if not loaded_lib_name.startswith(lib_nvpl):
79
+ _fftw.init_threads_float()
80
+ _fftw.init_threads_double()
81
+
82
+ IS_EXEC_CPU_AVAILABLE = True