nvmath-python 0.5.0__cp313-cp313-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 (174) hide show
  1. nvmath/__init__.pxd +0 -0
  2. nvmath/__init__.py +37 -0
  3. nvmath/_internal/__init__.py +0 -0
  4. nvmath/_internal/layout.py +58 -0
  5. nvmath/_utils.py +363 -0
  6. nvmath/bindings/__init__.pxd +0 -0
  7. nvmath/bindings/__init__.py +44 -0
  8. nvmath/bindings/_internal/__init__.pxd +0 -0
  9. nvmath/bindings/_internal/__init__.py +0 -0
  10. nvmath/bindings/_internal/cublas.cp313-win_amd64.pyd +0 -0
  11. nvmath/bindings/_internal/cublas.pxd +518 -0
  12. nvmath/bindings/_internal/cublasLt.cp313-win_amd64.pyd +0 -0
  13. nvmath/bindings/_internal/cublasLt.pxd +54 -0
  14. nvmath/bindings/_internal/cudss.cp313-win_amd64.pyd +0 -0
  15. nvmath/bindings/_internal/cudss.pxd +44 -0
  16. nvmath/bindings/_internal/cufft.cp313-win_amd64.pyd +0 -0
  17. nvmath/bindings/_internal/cufft.pxd +69 -0
  18. nvmath/bindings/_internal/cufftMp.pxd +77 -0
  19. nvmath/bindings/_internal/curand.cp313-win_amd64.pyd +0 -0
  20. nvmath/bindings/_internal/curand.pxd +42 -0
  21. nvmath/bindings/_internal/cusolver.cp313-win_amd64.pyd +0 -0
  22. nvmath/bindings/_internal/cusolver.pxd +15 -0
  23. nvmath/bindings/_internal/cusolverDn.cp313-win_amd64.pyd +0 -0
  24. nvmath/bindings/_internal/cusolverDn.pxd +386 -0
  25. nvmath/bindings/_internal/cusparse.cp313-win_amd64.pyd +0 -0
  26. nvmath/bindings/_internal/cusparse.pxd +270 -0
  27. nvmath/bindings/_internal/mathdx.cp313-win_amd64.pyd +0 -0
  28. nvmath/bindings/_internal/mathdx.pxd +79 -0
  29. nvmath/bindings/_internal/nvshmem.pxd +29 -0
  30. nvmath/bindings/_internal/utils.cp313-win_amd64.pyd +0 -0
  31. nvmath/bindings/_internal/utils.pxd +186 -0
  32. nvmath/bindings/cublas.cp313-win_amd64.pyd +0 -0
  33. nvmath/bindings/cublas.pxd +545 -0
  34. nvmath/bindings/cublas.pyi +18 -0
  35. nvmath/bindings/cublasLt.cp313-win_amd64.pyd +0 -0
  36. nvmath/bindings/cublasLt.pxd +99 -0
  37. nvmath/bindings/cudss.cp313-win_amd64.pyd +0 -0
  38. nvmath/bindings/cudss.pxd +83 -0
  39. nvmath/bindings/cudss.pyi +249 -0
  40. nvmath/bindings/cufft.cp313-win_amd64.pyd +0 -0
  41. nvmath/bindings/cufft.pxd +114 -0
  42. nvmath/bindings/cufftMp.pxd +124 -0
  43. nvmath/bindings/curand.cp313-win_amd64.pyd +0 -0
  44. nvmath/bindings/curand.pxd +71 -0
  45. nvmath/bindings/cusolver.cp313-win_amd64.pyd +0 -0
  46. nvmath/bindings/cusolver.pxd +60 -0
  47. nvmath/bindings/cusolverDn.cp313-win_amd64.pyd +0 -0
  48. nvmath/bindings/cusolverDn.pxd +410 -0
  49. nvmath/bindings/cusparse.cp313-win_amd64.pyd +0 -0
  50. nvmath/bindings/cusparse.pxd +329 -0
  51. nvmath/bindings/cycublas.cp313-win_amd64.pyd +0 -0
  52. nvmath/bindings/cycublas.pxd +657 -0
  53. nvmath/bindings/cycublasLt.cp313-win_amd64.pyd +0 -0
  54. nvmath/bindings/cycublasLt.pxd +1010 -0
  55. nvmath/bindings/cycudss.cp313-win_amd64.pyd +0 -0
  56. nvmath/bindings/cycudss.pxd +191 -0
  57. nvmath/bindings/cycufft.cp313-win_amd64.pyd +0 -0
  58. nvmath/bindings/cycufft.pxd +320 -0
  59. nvmath/bindings/cycufftMp.pxd +333 -0
  60. nvmath/bindings/cycurand.cp313-win_amd64.pyd +0 -0
  61. nvmath/bindings/cycurand.pxd +146 -0
  62. nvmath/bindings/cycusolver.cp313-win_amd64.pyd +0 -0
  63. nvmath/bindings/cycusolver.pxd +143 -0
  64. nvmath/bindings/cycusolverDn.cp313-win_amd64.pyd +0 -0
  65. nvmath/bindings/cycusolverDn.pxd +414 -0
  66. nvmath/bindings/cycusparse.cp313-win_amd64.pyd +0 -0
  67. nvmath/bindings/cycusparse.pxd +476 -0
  68. nvmath/bindings/cymathdx.cp313-win_amd64.pyd +0 -0
  69. nvmath/bindings/cymathdx.pxd +363 -0
  70. nvmath/bindings/cynvshmem.pxd +126 -0
  71. nvmath/bindings/mathdx.cp313-win_amd64.pyd +0 -0
  72. nvmath/bindings/mathdx.pxd +131 -0
  73. nvmath/bindings/mathdx.pyi +686 -0
  74. nvmath/bindings/nvpl/__init__.pxd +0 -0
  75. nvmath/bindings/nvpl/__init__.py +11 -0
  76. nvmath/bindings/nvpl/_internal/__init__.pxd +0 -0
  77. nvmath/bindings/nvpl/_internal/__init__.py +0 -0
  78. nvmath/bindings/nvpl/_internal/fft.pxd +36 -0
  79. nvmath/bindings/nvpl/cyfft.pxd +87 -0
  80. nvmath/bindings/nvpl/fft.pxd +100 -0
  81. nvmath/bindings/nvshmem.pxd +54 -0
  82. nvmath/device/__init__.py +23 -0
  83. nvmath/device/_deprecated.py +33 -0
  84. nvmath/device/caching.py +51 -0
  85. nvmath/device/common.py +131 -0
  86. nvmath/device/common_backend.py +125 -0
  87. nvmath/device/common_cuda.py +156 -0
  88. nvmath/device/common_mathdx.py +97 -0
  89. nvmath/device/common_numba.py +159 -0
  90. nvmath/device/common_opaque_tensor.py +128 -0
  91. nvmath/device/cublasdx.py +1085 -0
  92. nvmath/device/cublasdx_backend.py +552 -0
  93. nvmath/device/cublasdx_numba.py +528 -0
  94. nvmath/device/cufftdx.py +551 -0
  95. nvmath/device/cufftdx_backend.py +194 -0
  96. nvmath/device/cufftdx_numba.py +123 -0
  97. nvmath/device/curand_kernel.py +9147 -0
  98. nvmath/device/nvrtc.py +95 -0
  99. nvmath/device/patch.py +35 -0
  100. nvmath/device/random.py +417 -0
  101. nvmath/device/random_helpers.py +23 -0
  102. nvmath/device/random_states.py +189 -0
  103. nvmath/device/types.py +11 -0
  104. nvmath/device/vector_types_numba.py +203 -0
  105. nvmath/distributed/__init__.py +117 -0
  106. nvmath/distributed/_internal/__init__.py +0 -0
  107. nvmath/distributed/_internal/nvshmem.py +243 -0
  108. nvmath/distributed/_internal/tensor_ifc_cupy.py +111 -0
  109. nvmath/distributed/_internal/tensor_ifc_numpy.py +53 -0
  110. nvmath/distributed/_internal/tensor_ifc_torch.py +108 -0
  111. nvmath/distributed/_internal/tensor_wrapper.py +59 -0
  112. nvmath/distributed/_utils.py +152 -0
  113. nvmath/distributed/fft/__init__.py +6 -0
  114. nvmath/distributed/fft/_configuration.py +86 -0
  115. nvmath/distributed/fft/fft.py +1737 -0
  116. nvmath/distributed/reshape/__init__.py +6 -0
  117. nvmath/distributed/reshape/_configuration.py +39 -0
  118. nvmath/distributed/reshape/reshape.py +1249 -0
  119. nvmath/fft/__init__.py +7 -0
  120. nvmath/fft/_configuration.py +208 -0
  121. nvmath/fft/_exec_utils.py +116 -0
  122. nvmath/fft/_helpers.py +260 -0
  123. nvmath/fft/fft.py +2346 -0
  124. nvmath/internal/__init__.py +5 -0
  125. nvmath/internal/enum_utils.py +142 -0
  126. nvmath/internal/formatters.py +87 -0
  127. nvmath/internal/mem_limit.py +51 -0
  128. nvmath/internal/package_ifc.py +119 -0
  129. nvmath/internal/package_ifc_cuda.py +43 -0
  130. nvmath/internal/package_ifc_cupy.py +49 -0
  131. nvmath/internal/package_ifc_torch.py +31 -0
  132. nvmath/internal/package_wrapper.py +14 -0
  133. nvmath/internal/tensor_ifc.py +143 -0
  134. nvmath/internal/tensor_ifc_cupy.py +185 -0
  135. nvmath/internal/tensor_ifc_numpy.py +157 -0
  136. nvmath/internal/tensor_ifc_torch.py +115 -0
  137. nvmath/internal/tensor_wrapper.py +160 -0
  138. nvmath/internal/typemaps.py +154 -0
  139. nvmath/internal/utils.py +634 -0
  140. nvmath/linalg/__init__.py +11 -0
  141. nvmath/linalg/_internal/__init__.py +3 -0
  142. nvmath/linalg/_internal/algo_cap_ifc.py +86 -0
  143. nvmath/linalg/_internal/algo_config_ifc.py +87 -0
  144. nvmath/linalg/_internal/enum_to_tuples.py +64 -0
  145. nvmath/linalg/_internal/epilog_protocol.py +738 -0
  146. nvmath/linalg/_internal/matmul_desc_ifc.py +72 -0
  147. nvmath/linalg/_internal/matmul_pref_ifc.py +65 -0
  148. nvmath/linalg/_internal/matrix_layout_ifc.py +59 -0
  149. nvmath/linalg/_internal/typemaps.py +135 -0
  150. nvmath/linalg/_internal/utils.py +96 -0
  151. nvmath/linalg/advanced/__init__.py +8 -0
  152. nvmath/linalg/advanced/_algorithmmod.py +142 -0
  153. nvmath/linalg/advanced/_configuration.py +320 -0
  154. nvmath/linalg/advanced/helpers/__init__.py +5 -0
  155. nvmath/linalg/advanced/helpers/matmul.py +173 -0
  156. nvmath/linalg/advanced/matmulmod.py +2855 -0
  157. nvmath/memory.py +266 -0
  158. nvmath/sparse/__init__.py +18 -0
  159. nvmath/sparse/_internal/__init__.py +21 -0
  160. nvmath/sparse/_internal/common_utils.py +81 -0
  161. nvmath/sparse/_internal/cudss_config_ifc.py +584 -0
  162. nvmath/sparse/_internal/cudss_data_ifc.py +291 -0
  163. nvmath/sparse/_internal/cudss_utils.py +398 -0
  164. nvmath/sparse/_internal/sparse_csr_ifc.py +154 -0
  165. nvmath/sparse/_internal/sparse_format_helpers.py +88 -0
  166. nvmath/sparse/_internal/sparse_tensor_ifc.py +80 -0
  167. nvmath/sparse/advanced/__init__.py +7 -0
  168. nvmath/sparse/advanced/_configuration.py +150 -0
  169. nvmath/sparse/advanced/direct_solver.py +1705 -0
  170. nvmath_python-0.5.0.dist-info/METADATA +122 -0
  171. nvmath_python-0.5.0.dist-info/RECORD +174 -0
  172. nvmath_python-0.5.0.dist-info/WHEEL +5 -0
  173. nvmath_python-0.5.0.dist-info/licenses/LICENSE +177 -0
  174. nvmath_python-0.5.0.dist-info/top_level.txt +1 -0
nvmath/fft/__init__.py ADDED
@@ -0,0 +1,7 @@
1
+ # Copyright (c) 2024-2025, 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,208 @@
1
+ # Copyright (c) 2024-2025, 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, field
8
+ from enum import IntEnum
9
+ from logging import Logger
10
+ from typing import Literal
11
+ import warnings
12
+
13
+ from nvmath.memory import BaseCUDAMemoryManager
14
+
15
+
16
+ @dataclass
17
+ class 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
+ See Also:
27
+ :class:`ExecutionCPU`, :class:`FFT`, :func:`fft`, :func:`ifft`, :func:`rfft`, and
28
+ :func:`irfft`.
29
+ """
30
+
31
+ name: Literal["cuda"] = field(default="cuda", init=False)
32
+ # If not specified, it defaults to the deprecated FFTOptions.device_id or 0
33
+ # Keep None as an option to differentiate between the user-provided 0 and the default 0.
34
+ device_id: int | None = None
35
+
36
+
37
+ @dataclass
38
+ class ExecutionCPU:
39
+ """
40
+ A data class for providing CPU execution options to the :class:`FFT` object and the
41
+ family of wrapper functions :func:`fft`, :func:`ifft`, :func:`rfft`, and :func:`irfft`.
42
+
43
+ Attributes:
44
+ num_threads: The number of CPU threads used to execute the plan.
45
+ If not specified, defaults to the number of CPU cores available to the
46
+ process.
47
+
48
+ See Also:
49
+ :class:`ExecutionCUDA`, :class:`FFT`, :func:`fft`, :func:`ifft`, :func:`rfft`, and
50
+ :func:`irfft`.
51
+ """
52
+
53
+ name: Literal["cpu"] = field(default="cpu", init=False)
54
+ num_threads: int | None = None
55
+
56
+
57
+ @dataclass
58
+ class FFTOptions:
59
+ """
60
+ A data class for providing options to the :class:`FFT` object and the family of wrapper
61
+ functions :func:`fft`, :func:`ifft`, :func:`rfft`, and :func:`irfft`.
62
+
63
+ Attributes:
64
+ fft_type: The type of FFT to perform, available options include ``'C2C'``,
65
+ ``'C2R'``, and ``'R2C'``. The default is ``'C2C'`` for complex input and
66
+ ``'R2C'`` for real input.
67
+
68
+ inplace: Specify if the operation is in-place (`True` or `False`). The operand is
69
+ overwritten by the result if ``inplace`` is `True`. The default is `False`.
70
+
71
+ last_axis_parity: For complex-to-real FFT (corresponding to ``fft_type='C2R'``),
72
+ specify whether the size of the last axis in the result should be even or odd.
73
+ The even size is calculated as :math:`2 * (m - 1)`, where :math:`m` is the the
74
+ size of the last axis of the operand, and the odd size is calculated as :math:`2
75
+ * (m - 1) + 1`. The specified value should be either ``'even'`` or ``'odd'``,
76
+ with the default being ``'even'``.
77
+
78
+ result_layout: The layout to use for the result, either ``'natural'`` or
79
+ ``'optimized'``. For the ``'natural'`` option, the result layout is the same as
80
+ that of the operand. The default is ``'optimized'``, which generally provides
81
+ much better performance and should be used if the user doesn't care about the
82
+ result layout matching the operand layout. However in rare cases, depending on
83
+ the device type, shape and strides of the operand, and the FFT dimensions, the
84
+ ``'natural'`` layout may perform better. This option is ignored if ``inplace``
85
+ is specified to be True.
86
+
87
+ device_id: CUDA device ordinal (used if the operand resides on the CPU).
88
+ Device 0 will be used if not specified.
89
+
90
+ .. deprecated:: 0.2.0
91
+ The ``device_id`` should be specified as ``execution`` option,
92
+ see :attr:`ExecutionCUDA.device_id`.
93
+
94
+
95
+ logger (logging.Logger): Python Logger object. The root logger will be used if a
96
+ logger object is not provided.
97
+
98
+ blocking: A flag specifying the behavior of the execution functions and methods,
99
+ such as :func:`fft` and :meth:`FFT.execute`. When ``blocking`` is `True`, the
100
+ execution methods do not return until the operation is complete. When
101
+ ``blocking`` is ``"auto"``, the methods return immediately when the input tensor
102
+ is on the GPU and ``execution`` is set to 'CUDA'. The execution methods always
103
+ block when the input tensor is on the CPU or ``execution`` is specified to 'CPU'
104
+ to ensure that the user doesn't inadvertently use the result before it becomes
105
+ available. The default is ``"auto"``.
106
+
107
+ allocator: An object that supports the :class:`BaseCUDAMemoryManager` protocol, used
108
+ to draw device memory. If an allocator is not provided, a memory allocator from
109
+ the library package will be used (:func:`torch.cuda.caching_allocator_alloc` for
110
+ PyTorch operands, :func:`cupy.cuda.alloc` otherwise). Currently, CPU execution
111
+ does not allow specifying a memory pool.
112
+
113
+ See Also:
114
+ :class:`ExecutionCUDA`, :class:`ExecutionCPU`, :class:`FFT`, :func:`fft`,
115
+ :func:`ifft`, :func:`rfft`, and :func:`irfft`.
116
+ """
117
+
118
+ fft_type: Literal["C2C", "C2R", "R2C"] | None = None
119
+ inplace: bool = False
120
+ last_axis_parity: Literal["even", "odd"] = "even"
121
+ result_layout: Literal["natural", "optimized"] = "optimized"
122
+ device_id: int | None = None
123
+ logger: Logger | None = None
124
+ blocking: Literal[True, "auto"] = "auto"
125
+ allocator: BaseCUDAMemoryManager | None = None
126
+
127
+ def __post_init__(self):
128
+ valid_fft_types = [None, "C2C", "C2R", "R2C"]
129
+ if self.fft_type not in valid_fft_types:
130
+ raise ValueError(f"The value specified for 'fft_type' must be one of {valid_fft_types}.")
131
+
132
+ if not isinstance(self.inplace, bool):
133
+ raise ValueError("The value specified for 'inplace' must be of type bool (True or False).")
134
+
135
+ valid_last_axis_parity = ["even", "odd"]
136
+ if self.last_axis_parity not in valid_last_axis_parity:
137
+ raise ValueError(f"The value specified for 'last_axis_parity' must be one of {valid_last_axis_parity}.")
138
+
139
+ valid_result_layout_options = ["natural", "optimized"]
140
+ if self.result_layout not in valid_result_layout_options:
141
+ raise ValueError(f"The value specified for 'result_layout' must be one of {valid_result_layout_options}.")
142
+
143
+ if self.blocking not in (True, "auto"):
144
+ raise ValueError("The value specified for 'blocking' must be either True or 'auto'.")
145
+
146
+ if self.device_id is not None:
147
+ msg = "`FFTOptions.device_id` is deprecated. Use `ExecutionCUDA.device_id` instead."
148
+ warnings.warn(msg, DeprecationWarning)
149
+
150
+
151
+ @dataclass
152
+ class DeviceCallable:
153
+ """A data class capturing LTO-IR callables.
154
+
155
+ Attributes:
156
+ ltoir: A device-callable function in LTO-IR format, which can be provided as as
157
+ either as a :class:`bytes` object or as a pointer to the LTO-IR as Python
158
+ :class:`int`.
159
+
160
+ size: The size of the LTO-IR callable. If not specified and a :class:`bytes` object
161
+ is passed for ``ltoir``, the size is calculated from it. If a pointer is
162
+ provided for the LTO-IR, `size` must be specified.
163
+
164
+ data: A device pointer to user data used in the callback. The default is None,
165
+ which means a null pointer will be used in the callback.
166
+
167
+ See Also:
168
+ :meth:`FFT.plan`, :func:`fft`, :func:`ifft`, :func:`rfft`, and :func:`irfft`.
169
+ """
170
+
171
+ ltoir: int | bytes | None = None
172
+ size: int | None = None
173
+ data: int | None = None
174
+
175
+ def __post_init__(self):
176
+ if self.ltoir is None:
177
+ return
178
+ if not isinstance(self.ltoir, int | bytes):
179
+ raise ValueError(
180
+ "The LTO-IR code must be provided as a bytes object or as a Python int "
181
+ "representing the pointer to the LTO-IR code."
182
+ )
183
+ if isinstance(self.ltoir, int) and self.size is None:
184
+ raise ValueError(
185
+ "The size of the LTO-IR code specified as a pointer must be explicitly provided using the 'size' option."
186
+ )
187
+ if isinstance(self.ltoir, bytes):
188
+ self.size = len(self.ltoir)
189
+ if not isinstance(self.size, int):
190
+ raise ValueError(f"Invalid size value: {self.size}.")
191
+ if self.data is None:
192
+ self.data = 0
193
+ if not isinstance(self.data, int):
194
+ raise ValueError("The 'data' attribute must be a Python int representing a device pointer to user data.")
195
+
196
+
197
+ KeywordArgType = dict
198
+
199
+
200
+ class FFTDirection(IntEnum):
201
+ """An IntEnum class specifying the direction of the transform.
202
+
203
+ See Also:
204
+ :meth:`FFT.execute`, :func:`fft`
205
+ """
206
+
207
+ FORWARD = -1
208
+ INVERSE = 1
@@ -0,0 +1,116 @@
1
+ # Copyright (c) 2024-2025, 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
+ from nvmath.fft import _configuration
11
+
12
+
13
+ IS_EXEC_GPU_AVAILABLE = False
14
+ IS_EXEC_CPU_AVAILABLE = False
15
+
16
+
17
+ def _check_init_cufft():
18
+ global IS_EXEC_GPU_AVAILABLE
19
+ if not IS_EXEC_GPU_AVAILABLE:
20
+ try:
21
+ cufft.get_version()
22
+ except (
23
+ _bindings_utils.FunctionNotFoundError,
24
+ _bindings_utils.NotSupportedError,
25
+ RuntimeError,
26
+ ) as e:
27
+ raise RuntimeError(
28
+ "The FFT CUDA execution is not available. "
29
+ "Please check if CUDA toolkit and cuFFT are installed and visible to nvmath."
30
+ ) from e
31
+
32
+ try:
33
+ import cupy # noqa: F401
34
+ except ImportError as e:
35
+ raise RuntimeError("Currently, the FFT CUDA execution requires cupy. Please make sure cupy is installed.") from e
36
+
37
+ IS_EXEC_GPU_AVAILABLE = True
38
+
39
+
40
+ def _check_init_fftw():
41
+ global IS_EXEC_CPU_AVAILABLE
42
+ if not IS_EXEC_CPU_AVAILABLE:
43
+ try:
44
+ from nvmath.bindings.nvpl import fft as _fftw
45
+ from nvmath.bindings.nvpl._internal import fft as _internal_fftw
46
+ except ImportError as e:
47
+ import sys
48
+
49
+ if sys.platform.startswith("linux"):
50
+ raise
51
+ else:
52
+ raise RuntimeError(
53
+ "The FFT CPU execution is not available. Currently, it is supported only "
54
+ "with Linux. To process the CPU samples on GPU, please specify `execution='cuda'` "
55
+ "when calling fft."
56
+ ) from e
57
+
58
+ lib_nvpl = "libnvpl_fftw.so"
59
+ env_lib_name = os.environ.get("NVMATH_FFT_CPU_LIBRARY")
60
+
61
+ if env_lib_name:
62
+ _internal_fftw._set_lib_so_names((env_lib_name,))
63
+ try:
64
+ _internal_fftw._inspect_function_pointers()
65
+ loaded_lib_name = _internal_fftw._get_current_lib_so_name()
66
+ except (
67
+ _bindings_utils.FunctionNotFoundError,
68
+ _bindings_utils.NotSupportedError,
69
+ RuntimeError,
70
+ ) as e:
71
+ raise RuntimeError(
72
+ "The FFT CPU execution is not available, because no FFTW-compatible "
73
+ "library was found. Please make sure to install optional dependency required "
74
+ "for FFT execution (NVPL on aarch64 platforms or MKL on x86_64 platforms). "
75
+ "Users can use other FFTW3-compatible libraries. In order to do that, please "
76
+ "specify the library name as "
77
+ "`NVMATH_FFT_CPU_LIBRARY=some_fftw3_compatible_lib.so` and make sure "
78
+ "the library can be found within current `LD_LIBRARY_PATH`"
79
+ ) from e
80
+
81
+ if env_lib_name:
82
+ assert env_lib_name == loaded_lib_name
83
+
84
+ if not loaded_lib_name.startswith(lib_nvpl):
85
+ _fftw.init_threads_float()
86
+ _fftw.init_threads_double()
87
+
88
+ IS_EXEC_CPU_AVAILABLE = True
89
+
90
+
91
+ def _cross_setup_execution_and_options(
92
+ options: _configuration.FFTOptions,
93
+ execution: _configuration.ExecutionCUDA | _configuration.ExecutionCPU,
94
+ ):
95
+ if execution.name == "cpu":
96
+ _check_init_fftw()
97
+ if options.device_id is not None:
98
+ raise ValueError("The 'device_id' is not a valid option when 'execution' is specified to be 'cpu'.")
99
+ if execution.num_threads is None:
100
+ # `sched_getaffinity` is not supported on Windows, it must be adjusted
101
+ # once the support for Windows is enabled
102
+ execution.num_threads = len(os.sched_getaffinity(0))
103
+ if not isinstance(execution.num_threads, int) or execution.num_threads <= 0:
104
+ raise ValueError("The 'num_threads' must be a positive integer")
105
+ else:
106
+ assert execution.name == "cuda"
107
+ _check_init_cufft()
108
+ if execution.device_id is None:
109
+ execution.device_id = options.device_id or 0
110
+ elif options.device_id is not None and execution.device_id != options.device_id:
111
+ raise ValueError(
112
+ f"Got conflicting 'device_id' passed in 'execution' ({execution.device_id}) "
113
+ f"and 'options' ({options.device_id}). It should be passed as 'execution' "
114
+ f"configuration only."
115
+ )
116
+ return options, execution
nvmath/fft/_helpers.py ADDED
@@ -0,0 +1,260 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ __all__ = ["compile_prolog", "compile_epilog"]
6
+
7
+ import functools
8
+
9
+ from nvmath.bindings import cufft as _cufft # type: ignore
10
+
11
+
12
+ # Currently supported element data types for FFT callback functions.
13
+ VALID_DTYPES = {"float32": "Real", "float64": "DoubleReal", "complex64": "Complex", "complex128": "DoubleComplex"}
14
+
15
+ # Python <> CUDA magled types.
16
+ TYPE_MAP = {
17
+ "float32": "f", # float
18
+ "float64": "d", # double
19
+ "complex64": "6float2", # float2
20
+ "complex128": "7double2", # double2
21
+ }
22
+
23
+
24
+ def _check_numba_available():
25
+ try:
26
+ import numba # noqa: F401
27
+ except ModuleNotFoundError as e:
28
+ raise RuntimeError("Numba is required to compile FFT prolog and epilog functions.") from e
29
+
30
+
31
+ def _get_compiler():
32
+ import numba
33
+ import numba.cuda
34
+
35
+ @numba.core.compiler_lock.global_compiler_lock
36
+ def compile_to(function, sig, name, *, compute_capability=None, representation="ltoir"):
37
+ if compute_capability is None:
38
+ compute_capability = numba.cuda.get_current_device().compute_capability
39
+ else:
40
+ if not isinstance(compute_capability, str):
41
+ raise ValueError(
42
+ f"The compute capability must be specified as a string ('80', '89', ...). "
43
+ f"The provided value {compute_capability} is invalid."
44
+ )
45
+ cc_number = int(compute_capability)
46
+ compute_capability = (cc_number // 10, cc_number % 10)
47
+
48
+ if hasattr(numba.cuda.cudadrv.nvrtc, "get_arch_option"):
49
+ # numba-cuda >=0.16.0
50
+ get_arch_option = numba.cuda.cudadrv.nvrtc.get_arch_option
51
+ elif hasattr(numba.cuda.cudadrv.nvvm, "get_arch_option"):
52
+ get_arch_option = numba.cuda.cudadrv.nvvm.get_arch_option
53
+ nvvm_options = {"opt": 3, "arch": get_arch_option(*compute_capability)}
54
+
55
+ if representation == "ltoir":
56
+ nvvm_options["gen-lto"] = None
57
+
58
+ debug = False
59
+ lineinfo = False
60
+
61
+ args, return_type = numba.core.sigutils.normalize_signature(sig)
62
+
63
+ cres = numba.cuda.compiler.compile_cuda(
64
+ function,
65
+ return_type,
66
+ args,
67
+ debug=debug,
68
+ lineinfo=lineinfo,
69
+ nvvm_options=nvvm_options,
70
+ cc=compute_capability,
71
+ )
72
+
73
+ lib = numba.cuda.compiler.cabi_wrap_function(cres.target_context, cres.library, cres.fndesc, name, nvvm_options)
74
+
75
+ return numba.cuda.cudadrv.nvvm.compile_ir(lib.llvm_strs, **nvvm_options)
76
+
77
+ return compile_to
78
+
79
+
80
+ def _compile(
81
+ function, element_dtype, user_info_dtype, *, name=None, phase=None, representation="ltoir", compute_capability=None
82
+ ):
83
+ _check_numba_available()
84
+
85
+ import numba
86
+ import numba.cuda
87
+
88
+ assert phase in ["Load", "Store"], "Internal error."
89
+
90
+ if element_dtype not in VALID_DTYPES:
91
+ raise ValueError(
92
+ f"The specified operand data type '{element_dtype}' is not currently supported. "
93
+ f"It must be one of {VALID_DTYPES.keys()}."
94
+ )
95
+
96
+ data_type = getattr(numba.cuda.types.types, element_dtype)
97
+
98
+ if user_info_dtype in VALID_DTYPES:
99
+ info_type = getattr(numba.cuda.types.types, user_info_dtype)
100
+ elif isinstance(user_info_dtype, numba.types.Type):
101
+ info_type = user_info_dtype
102
+ else:
103
+ raise ValueError(
104
+ f"The specified user information data type '{user_info_dtype}' is not supported. "
105
+ f"It must be a Numba custom type or one of {VALID_DTYPES.keys()}."
106
+ )
107
+
108
+ dataptr_type = numba.types.CPointer(data_type)
109
+ infoptr_type = numba.types.CPointer(info_type)
110
+ offset_type = numba.types.uint64
111
+ smemptr_type = dataptr_type
112
+
113
+ if phase == "Load":
114
+ insert = ""
115
+ return_type = data_type
116
+ signature = numba.core.typing.signature(return_type, dataptr_type, offset_type, infoptr_type, smemptr_type)
117
+ else:
118
+ insert = TYPE_MAP[element_dtype]
119
+ return_type = numba.types.void
120
+ signature = numba.core.typing.signature(return_type, dataptr_type, offset_type, data_type, infoptr_type, smemptr_type)
121
+
122
+ if name is None:
123
+ snippet = VALID_DTYPES[element_dtype]
124
+ length = 16 + len(phase) + len(snippet)
125
+ # the signature of callbacks differs - the offset argument
126
+ # changed type from size_t to unsigned long long int
127
+ offset_dtype = "m" if _cufft.get_version() < 11300 else "y"
128
+ name = f"_Z{length}cufftJITCallback{phase}{snippet}Pv{offset_dtype}{insert}S_S_"
129
+
130
+ compile_to = _get_compiler()
131
+ return compile_to(function, signature, name, representation=representation, compute_capability=compute_capability)
132
+
133
+
134
+ compile_prolog = functools.wraps(_compile)(functools.partial(_compile, phase="Load"))
135
+ compile_epilog = functools.wraps(_compile)(functools.partial(_compile, phase="Store"))
136
+
137
+ SHARED_FFT_HELPER_DOCUMENTATION = {
138
+ "element_dtype": """The data type of the ``data_in`` argument, one of ``['float32', 'float64', 'complex64',
139
+ 'complex128']``. It must have the same data type as that of the FFT operand for prolog functions or the FFT
140
+ result for epilog functions.""",
141
+ "user_info_dtype": """The data type of the ``user_info`` argument. It must be one of ``['float32', 'float64',
142
+ 'complex64', 'complex128']`` or an object of type :class:`numba.types.Type`. The offset is computed based on
143
+ the memory layout (shape and strides) of the operand (input for prolog, output for epilog). If the user
144
+ would like to pass additional tensor as `user_info` and access it based on the offset, it is crucial to know
145
+ memory layout of the operand. Please note, the actual layout of the input tensor may differ from the layout
146
+ of the tensor passed to fft call. To learn the memory layout of the input or output, please use stateful FFT
147
+ API and :meth:`nvmath.fft.FFT.get_input_layout` :meth:`nvmath.fft.FFT.get_output_layout` respectively.
148
+
149
+ .. note::
150
+ Currently, in the callback, the position of the element in the input and output operands are described
151
+ with a single flat offset, even if the original operand is multi-dimensional tensor.
152
+
153
+ """,
154
+ "compute_capability": """The target compute capability, specified as a string (``'80'``, ``'89'``, ...). The
155
+ default is the compute capability of the current device.""",
156
+ }
157
+
158
+ compile_prolog.__doc__ = """
159
+ compile_prolog(prolog_fn, element_dtype, user_info_dtype, *, compute_capability=None)
160
+
161
+ Compile a Python function to LTO-IR to provide as a prolog function for
162
+ :func:`~nvmath.fft.fft` and :meth:`~nvmath.fft.FFT.plan`.
163
+
164
+ Args:
165
+ prolog_fn: The prolog function to be compiled to LTO-IR. It must have the signature:
166
+ ``prolog_fn(data_in, offset, user_info, reserved_for_future_use)``, and it
167
+ essentially returns transformed ``data_in`` at ``offset``.
168
+
169
+ element_dtype: {element_dtype}
170
+
171
+ user_info_dtype: {user_info_dtype}
172
+
173
+ compute_capability: {compute_capability}
174
+
175
+ Returns:
176
+ The function compiled to LTO-IR as `bytes` object.
177
+
178
+ See Also:
179
+ :func:`~nvmath.fft.fft`, :meth:`~nvmath.fft.FFT.plan`,
180
+ :meth:`~nvmath.fft.compile_epilog`.
181
+
182
+ Notes:
183
+ - The user must ensure that the specified argument types meet the requirements
184
+ listed above.
185
+ """.format(**SHARED_FFT_HELPER_DOCUMENTATION)
186
+ compile_prolog.__name__ = "compile_prolog"
187
+
188
+ compile_epilog.__doc__ = """
189
+ compile_epilog(epilog_fn, element_dtype, user_info_dtype, *, compute_capability=None)
190
+
191
+ Compile a Python function to LTO-IR to provide as an epilog function for
192
+ :func:`~nvmath.fft.fft` and :meth:`~nvmath.fft.FFT.plan`.
193
+
194
+ Args:
195
+ epilog_fn: The epilog function to be compiled to LTO-IR. It must have the signature:
196
+ ``epilog_fn(data_out, offset, data, user_info, reserved_for_future_use)``, and
197
+ it essentially stores transformed ``data`` into ``data_out`` at ``offset``.
198
+
199
+ element_dtype: {element_dtype}
200
+
201
+ user_info_dtype: {user_info_dtype}
202
+
203
+ compute_capability: {compute_capability}
204
+
205
+ Returns:
206
+ The function compiled to LTO-IR as `bytes` object.
207
+
208
+ See Also:
209
+ :func:`~nvmath.fft.fft`, :meth:`~nvmath.fft.FFT.plan`,
210
+ :meth:`~nvmath.fft.compile_prolog`.
211
+
212
+ Examples:
213
+
214
+ The cuFFT library expects the end user to manage scaling of the outputs, so in order
215
+ to replicate the ``norm`` option found in `other Python FFT libraries
216
+ <https://numpy.org/doc/stable/reference/routines.fft.html#normalization>`_ we can
217
+ define an epilog which performs the scaling.
218
+
219
+ >>> import cupy as cp
220
+ >>> import nvmath
221
+ >>> import math
222
+
223
+ Create the data for a batched 1-D FFT.
224
+
225
+ >>> B, N = 256, 1024
226
+ >>> a = cp.random.rand(B, N, dtype=cp.float64) + 1j * cp.random.rand(B, N, dtype=cp.float64)
227
+
228
+ Compute a normalization factor that will create unitary transforms.
229
+
230
+ >>> norm_factor = 1.0 / math.sqrt(N)
231
+
232
+ Define the epilog function for the FFT.
233
+
234
+ >>> def rescale(data_out, offset, data, user_info, unused):
235
+ ... data_out[offset] = data * norm_factor
236
+
237
+ Compile the epilog to LTO-IR. In a system with GPUs that have different compute
238
+ capability, the `compute_capability` option must be specified to the
239
+ `compile_prolog` or `compile_epilog` helpers. Alternatively, the epilog can be
240
+ compiled in the context of the device where the FFT to which the epilog is provided
241
+ is executed. In this case we use the current device context, where the operands have
242
+ been created.
243
+
244
+ >>> with cp.cuda.Device():
245
+ ... epilog = nvmath.fft.compile_epilog(rescale, "complex128", "complex128")
246
+
247
+ Perform the forward FFT, applying the rescaling as a epilog.
248
+
249
+ >>> r = nvmath.fft.fft(a, axes=[-1], epilog=dict(ltoir=epilog))
250
+
251
+ Test that the fused FFT run result matches the result of other libraries.
252
+
253
+ >>> s = cp.fft.fftn(a, axes=[-1], norm="ortho")
254
+ >>> assert cp.allclose(r, s)
255
+
256
+ Notes:
257
+ - The user must ensure that the specified argument types meet the requirements
258
+ listed above.
259
+ """.format(**SHARED_FFT_HELPER_DOCUMENTATION)
260
+ compile_prolog.__name__ = "compile_prolog"