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
@@ -0,0 +1,1085 @@
1
+ # Copyright (c) 2024-2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ __all__ = ["matmul", "TransposeMode", "BlasOptions"]
6
+
7
+ from functools import cached_property
8
+ import itertools
9
+ from collections.abc import Sequence
10
+ import math
11
+ import re
12
+ from typing import overload
13
+ from warnings import warn
14
+
15
+ from .common import (
16
+ Layout,
17
+ make_binary_tempfile,
18
+ check_in,
19
+ SHARED_DEVICE_DOCSTRINGS,
20
+ pad_or_truncate,
21
+ )
22
+ from .common_backend import MATHDX_TYPES_TO_NP, get_isa_version, get_lto
23
+ from .common_cuda import MAX_SUPPORTED_CC, get_default_code_type, ComputeCapability, Code, CodeType, Dim3
24
+ from .common_numba import NP_TYPES_TO_NUMBA_FE_TYPES
25
+ from .cublasdx_backend import (
26
+ Alignment,
27
+ Arrangement,
28
+ Precision,
29
+ generate_MM,
30
+ generate_code,
31
+ generate_code_tensors,
32
+ generate_copy_wait_lto,
33
+ generate_tensors,
34
+ get_str_trait,
35
+ get_int_traits,
36
+ get_tensor_int_traits,
37
+ validate,
38
+ LeadingDimension,
39
+ TransposeMode,
40
+ validate_alignment,
41
+ validate_execute_api,
42
+ validate_tensor_types,
43
+ MAX_ALIGNMENT, # noqa: F401
44
+ )
45
+ from ._deprecated import deprecated
46
+ from nvmath.internal.utils import docstring_decorator
47
+
48
+ from nvmath.bindings import mathdx
49
+ import numpy
50
+
51
+ CUBLASDX_DOCSTRING = SHARED_DEVICE_DOCSTRINGS.copy()
52
+ CUBLASDX_DOCSTRING.update(
53
+ {
54
+ "size": """\
55
+ A sequence of integers denoting the three dimensions ``(m, n, k)`` for the matrix multiplication
56
+ problem.""".replace("\n", " "),
57
+ #
58
+ "data_type": """\
59
+ The data type of the input matrices, can be either ``'real'`` or ``'complex'``.""".replace("\n", " "),
60
+ #
61
+ "block_size": """\
62
+ The total block size, optional. If not provided or set to ``'suggested'``, will be set to a suggested value for 1D block
63
+ dim. """.replace("\n", " "),
64
+ #
65
+ "block_dim": """\
66
+ The block dimension for launching the CUDA kernel, optional. If not provided or set to ``'suggested'``, will be set to a
67
+ suggested value. Cannot be used when `block_size` is explicitly specified.""".replace("\n", " "),
68
+ #
69
+ "leading_dimension": """\
70
+ The leading dimensions for the input matrices, optional. If not provided, will be set to match the matrix row/column
71
+ dimension. Alternatively, if provided as ``'suggested'``, will be set to a suggested value for optimal performance.
72
+ """.replace("\n", " "),
73
+ #
74
+ "transpose_mode": """\
75
+ The transpose mode for all input matrices ;
76
+ transpose_mode or arrangement must be provided.""".replace("\n", " "),
77
+ #
78
+ "arrangement": """\
79
+ The arrangement for all input matrices ;
80
+ transpose_mode or arrangement must be provided.""".replace("\n", " "),
81
+ #
82
+ "alignment": """\
83
+ The alignment for the input matrices in shared memory.
84
+ Defines the alignments (in bytes) of the input matrices A, B, and C
85
+ (either arrays or wrapped in opaque tensors) that are passed to the
86
+ execute(...) method. Default alignment is equal to an element size of the
87
+ matrix unless used suggested layout. In that case alignment is greater or equal
88
+ than the element size.""".replace("\n", " "),
89
+ #
90
+ "global_memory_alignment": """\
91
+ Same as alignment, but for the global memory. Used to optimize copying between
92
+ shared and global memory.
93
+ """.replace("\n", " "),
94
+ #
95
+ "function": """\
96
+ A string specifying the name of the function. Currently supports ``'MM'`` (default) for matrix
97
+ multiplication.""".replace("\n", " "),
98
+ #
99
+ "execute_api": """\
100
+ A string specifying the signature of the function that handles problems with default or custom/dynamic leading dimensions.
101
+ Could be ``'static_leading_dimensions'`` or ``'dynamic_leading_dimensions'``.""".replace("\n", " "),
102
+ "tensor_types": """\
103
+ A list of strings specifying the tensors being used at execute signature.""".replace("\n", " "),
104
+ }
105
+ )
106
+
107
+ #
108
+ # A set of knobs, potentially in-complete (ie not sufficient to generate a device functions)
109
+ #
110
+
111
+
112
+ class SharedStorageCalc:
113
+ """
114
+ Helper class to calculate shared storage size.
115
+
116
+ For further details, please refer to `cuBLASDx documentation
117
+ <https://docs.nvidia.com/cuda/cublasdx/>`_.
118
+ """
119
+
120
+ _memory: int = 0
121
+
122
+ @overload
123
+ def add(self, alignment: int, matrix_size_bytes: int) -> None: ...
124
+ @overload
125
+ def add(self, alignment: int, elem_size: int, num_elements: int) -> None: ...
126
+ @overload
127
+ def add(self, alignment: int, elem_size: int, layout: Layout) -> None: ...
128
+ def add(self, *args):
129
+ assert len(args) in {2, 3}
130
+
131
+ if len(args) == 2:
132
+ [alignment, matrix_size_bytes] = args
133
+
134
+ assert matrix_size_bytes > 0
135
+ else:
136
+ [alignment, elem_size, num_elements] = args
137
+
138
+ if isinstance(num_elements, Layout):
139
+ num_elements = num_elements.cosize
140
+
141
+ assert elem_size > 0
142
+ assert num_elements > 0
143
+
144
+ matrix_size_bytes = elem_size * num_elements
145
+
146
+ assert alignment > 0
147
+
148
+ self._memory = ((self._memory + alignment - 1) // alignment) * alignment + matrix_size_bytes
149
+
150
+ def get(self):
151
+ return self._memory
152
+
153
+
154
+ @docstring_decorator(CUBLASDX_DOCSTRING, skip_missing=False)
155
+ class BlasOptions:
156
+ """
157
+ A class that encapsulates a partial BLAS device function. A partial device function can
158
+ be queried for available or optimal values for some knobs (such as `leading_dimension`
159
+ or `block_dim`). It does not contain a compiled, ready-to-use, device function until
160
+ finalized using :meth:`create`.
161
+
162
+ Args:
163
+ size: {size}
164
+
165
+ precision: {precision}
166
+
167
+ data_type: {data_type}
168
+
169
+ code_type (CodeType): {code_type}
170
+
171
+ block_size (int): {block_size}
172
+
173
+ block_dim (Dim3): {block_dim}
174
+
175
+ leading_dimension (LeadingDimension): {leading_dimension}
176
+
177
+ transpose_mode (TransposeMode): {transpose_mode}
178
+
179
+ arrangement (Arrangement): {arrangement}
180
+
181
+ alignment (Alignment): {alignment}
182
+
183
+ function (str): {function}
184
+
185
+ execution (str): {execution}
186
+
187
+ execute_api:
188
+ .. versionchanged:: 0.5.0
189
+ execute_api is not part of the Blas type. Pass this argument to
190
+ :py:func:`nvmath.device.matmul` instead.
191
+
192
+ tensor_types:
193
+ .. versionchanged:: 0.5.0
194
+ tensor_types is not part of the Blas type. Pass this argument to
195
+ :py:func:`nvmath.device.matmul` instead.
196
+
197
+ See Also:
198
+ The attributes of this class provide a 1:1 mapping with the CUDA C++ cuBLASDx APIs.
199
+ For further details, please refer to `cuBLASDx documentation
200
+ <https://docs.nvidia.com/cuda/cublasdx/>`_.
201
+ """
202
+
203
+ def __init__(
204
+ self,
205
+ size,
206
+ precision,
207
+ data_type,
208
+ *,
209
+ code_type=None,
210
+ block_size=None,
211
+ block_dim=None,
212
+ leading_dimension=None,
213
+ transpose_mode=None,
214
+ arrangement=None,
215
+ alignment=None,
216
+ function="MM",
217
+ static_block_dim=False,
218
+ execution="Block",
219
+ ):
220
+ if not isinstance(code_type, Sequence) or len(code_type) != 2:
221
+ raise ValueError(f"code_type should be an instance of CodeType or a 2-tuple ; got code_type = {code_type}")
222
+ code_type = CodeType(code_type[0], ComputeCapability(*code_type[1]))
223
+ if code_type.cc.major < 7:
224
+ raise RuntimeError(
225
+ f"Minimal compute capability 7.0 is required by cuBLASDx, got {code_type.cc.major}.{code_type.cc.minor}"
226
+ )
227
+ if (code_type.cc.major, code_type.cc.minor) > MAX_SUPPORTED_CC:
228
+ raise RuntimeError(
229
+ "The maximum compute capability currently supported by device "
230
+ f"APIs is {MAX_SUPPORTED_CC}, "
231
+ f"got {code_type.cc.major}.{code_type.cc.minor}"
232
+ )
233
+
234
+ if transpose_mode is not None:
235
+ warn(
236
+ "transpose_mode is deprecated and may be removed in future versions. User arrangement instead",
237
+ category=DeprecationWarning,
238
+ )
239
+ if not isinstance(transpose_mode, Sequence) or len(transpose_mode) != 2:
240
+ raise ValueError(
241
+ "transpose_mode should be an instance of TransposeMode or a 2-tuple ; "
242
+ f"got transpose_mode = {transpose_mode}"
243
+ )
244
+ transpose_mode = TransposeMode(*transpose_mode)
245
+ if arrangement is not None:
246
+ if not isinstance(arrangement, Sequence) or len(arrangement) != 3:
247
+ raise ValueError(
248
+ f"arrangement should be an instance of Arrangement or a 3-tuple ; got arrangement = {arrangement}"
249
+ )
250
+ arrangement = Arrangement(*arrangement)
251
+
252
+ if alignment is not None:
253
+ if not isinstance(alignment, Sequence) or len(alignment) != 3:
254
+ raise ValueError(f"alignment should be an instance of Alignment or a 3-tuple ; got alignment = {alignment}")
255
+ alignment = Alignment(*alignment)
256
+
257
+ if leading_dimension is not None and leading_dimension != "suggested":
258
+ if not isinstance(leading_dimension, Sequence) or len(leading_dimension) != 3:
259
+ raise ValueError(
260
+ "leading_dimension should be a 3-tuple, an instance of LeadingDimension, 'suggested' or None ; "
261
+ f"got leading_dimension = {leading_dimension}"
262
+ )
263
+ else:
264
+ leading_dimension = LeadingDimension(*leading_dimension)
265
+
266
+ if isinstance(precision, Sequence):
267
+ if len(precision) != 3:
268
+ raise ValueError(
269
+ "precision should be a 3-len sequence, an instance of Precision, or a single value; "
270
+ f"got precision = {precision}"
271
+ )
272
+ else:
273
+ precision = (precision, precision, precision)
274
+ precision = Precision(*precision)
275
+
276
+ #
277
+ # Check that the knobs are, individually, valid
278
+ #
279
+
280
+ if block_size is not None and block_dim is not None:
281
+ raise ValueError(f"Both block_size ({block_size}) and block_dim ({block_dim}) cannot be specified.")
282
+ if block_size is not None:
283
+ check_in("block_dim", block_dim, [None])
284
+ if block_size == "suggested":
285
+ block_dim = "suggested"
286
+ else:
287
+ block_dim = Dim3(block_size, 1, 1)
288
+ if block_dim is not None and isinstance(block_dim, Sequence) and block_dim != "suggested":
289
+ if len(block_dim) != 3:
290
+ raise ValueError(
291
+ f"block_dim should be a 3-tuple, an instance of Dim3, 'suggested' or None ; got block_dim = {block_dim}"
292
+ )
293
+ else:
294
+ block_dim = Dim3(*block_dim)
295
+
296
+ validate(
297
+ size=size,
298
+ precision=precision,
299
+ data_type=data_type,
300
+ transpose_mode=transpose_mode,
301
+ arrangement=arrangement,
302
+ alignment=alignment,
303
+ code_type=code_type,
304
+ leading_dimension=leading_dimension,
305
+ block_dim=block_dim,
306
+ function=function,
307
+ execution=execution,
308
+ static_block_dim=static_block_dim,
309
+ )
310
+
311
+ #
312
+ # Traits set by input
313
+ #
314
+
315
+ self._size = size
316
+ self._precision = precision
317
+ self._data_type = data_type
318
+ self._transpose_mode = transpose_mode
319
+ self._arrangement = arrangement
320
+ self._alignment = alignment
321
+ self._code_type = code_type
322
+ self._block_dim = block_dim
323
+ self._function = function
324
+ self._execution = execution
325
+ self._leading_dimension = leading_dimension
326
+ self._static_block_dim = static_block_dim
327
+
328
+ #
329
+ # Update suggested traits
330
+ #
331
+
332
+ if leading_dimension == "suggested":
333
+ self._leading_dimension = self._suggested_leading_dimension
334
+
335
+ if block_dim == "suggested":
336
+ self._block_dim = self._suggested_block_dim
337
+
338
+ @property
339
+ def precision(self) -> Precision:
340
+ return self._precision
341
+
342
+ @property
343
+ def data_type(self) -> str:
344
+ return self._data_type
345
+
346
+ @property
347
+ def size(self) -> tuple[int, int, int]:
348
+ return self._size
349
+
350
+ @property
351
+ def execution(self) -> str:
352
+ return self._execution
353
+
354
+ @property
355
+ @deprecated("transpose_mode trait is deprecated and may be removed in future versions. Use arrangement instead")
356
+ def transpose_mode(self) -> TransposeMode:
357
+ return self._transpose_mode
358
+
359
+ @property
360
+ def arrangement(self) -> Arrangement:
361
+ return self._arrangement
362
+
363
+ @property
364
+ def alignment(self) -> Alignment:
365
+ return self._alignment
366
+
367
+ @property
368
+ def code_type(self):
369
+ return self._code_type
370
+
371
+ @property
372
+ def function(self) -> str:
373
+ return self._function
374
+
375
+ @property
376
+ def block_size(self) -> int:
377
+ return self._block_dim[0] * self._block_dim[1] * self._block_dim[2]
378
+
379
+ @property
380
+ def block_dim(self) -> Dim3:
381
+ return self._block_dim
382
+
383
+ @property
384
+ def static_block_dim(self) -> bool:
385
+ return self._static_block_dim
386
+
387
+ @property
388
+ def leading_dimension(self) -> LeadingDimension:
389
+ return self._leading_dimension
390
+
391
+ #
392
+ # Extensions
393
+ #
394
+
395
+ def valid(self, *knobs):
396
+ return itertools.product(*[self._valid(knob) for knob in knobs])
397
+
398
+ def definition(self):
399
+ dd = {
400
+ "size": self.size,
401
+ "precision": self.precision,
402
+ "data_type": self.data_type,
403
+ "transpose_mode": self.transpose_mode,
404
+ "arrangement": self.arrangement,
405
+ "alignment": self.alignment,
406
+ "code_type": self.code_type,
407
+ "block_dim": self.block_dim,
408
+ "static_block_dim": self.static_block_dim,
409
+ "function": self.function,
410
+ "execution": self.execution,
411
+ "leading_dimension": self.leading_dimension,
412
+ }
413
+ return dd
414
+
415
+ def create(self, **kwargs):
416
+ dd = self.definition()
417
+ dd.update(**kwargs)
418
+ return matmul(**dd)
419
+
420
+ #
421
+ # Private implementations
422
+ #
423
+
424
+ def _valid(self, knob):
425
+ if knob == "block_dim":
426
+ return [self._suggested_block_dim]
427
+ else:
428
+ raise ValueError("Unsupported knob")
429
+
430
+ @cached_property
431
+ def _suggested_leading_dimension(self):
432
+ if self.code_type is None:
433
+ raise ValueError("leading_dimension='suggested' require code_type to be set.")
434
+ if self.execution != "Block":
435
+ raise ValueError("leading_dimension='suggested' require execution to be 'Block'.")
436
+ # Generate special PTX for suggested_leading_dimension_of
437
+ descriptor = generate_MM(
438
+ size=self.size,
439
+ function=self.function,
440
+ precision=self.precision,
441
+ data_type=self.data_type,
442
+ code_type=self.code_type,
443
+ transpose_mode=self._transpose_mode,
444
+ arrangement=self._arrangement,
445
+ alignment=self._alignment,
446
+ block_dim=None,
447
+ static_block_dim=self._static_block_dim,
448
+ leading_dimension=None,
449
+ execution=self.execution,
450
+ )
451
+
452
+ return LeadingDimension(*get_int_traits(descriptor.descriptor, mathdx.CublasdxTraitType.SUGGESTED_LEADING_DIMENSION, 3))
453
+
454
+ @cached_property
455
+ def _suggested_block_dim(self):
456
+ if self.code_type is None:
457
+ raise ValueError("block_dim='suggested' require code_type to be set.")
458
+ if self.execution != "Block":
459
+ raise ValueError("block_dim='suggested' require execution to be 'Block'.")
460
+ # Generate full PTX
461
+ descriptor = generate_MM(
462
+ size=self.size,
463
+ function=self.function,
464
+ precision=self.precision,
465
+ data_type=self.data_type,
466
+ code_type=self.code_type,
467
+ transpose_mode=self._transpose_mode,
468
+ arrangement=self._arrangement,
469
+ alignment=self._alignment,
470
+ block_dim=None,
471
+ static_block_dim=self._static_block_dim,
472
+ leading_dimension=None,
473
+ execution=self.execution,
474
+ )
475
+
476
+ return Dim3(*get_int_traits(descriptor.descriptor, mathdx.CublasdxTraitType.SUGGESTED_BLOCK_DIM, 3))
477
+
478
+
479
+ #
480
+ # A complete set of knobs, ie sufficient to generate a device functions and query all traits
481
+ # Not exposed to end users
482
+ #
483
+ class BlasOptionsComplete(BlasOptions):
484
+ def __init__(self, **kwargs):
485
+ super().__init__(**kwargs)
486
+
487
+ if self.code_type is None:
488
+ raise NotImplementedError(f"code_type should be set, but got code_type = {self.code_type}")
489
+ if self.execution != "Block":
490
+ raise NotImplementedError(f"Only execution=Block is implemented ; got execution = {self.execution}")
491
+
492
+ (m, n, k) = self.size
493
+
494
+ h = generate_MM(
495
+ size=self.size,
496
+ function=self.function,
497
+ precision=self.precision,
498
+ data_type=self.data_type,
499
+ code_type=self.code_type,
500
+ transpose_mode=self._transpose_mode,
501
+ arrangement=self.arrangement,
502
+ alignment=self._alignment,
503
+ block_dim=self.block_dim,
504
+ static_block_dim=self._static_block_dim,
505
+ leading_dimension=self._leading_dimension,
506
+ execution=self.execution,
507
+ ).descriptor
508
+
509
+ self._value_types = tuple(MATHDX_TYPES_TO_NP[vt] for vt in get_int_traits(h, mathdx.CublasdxTraitType.VALUE_TYPE, 3))
510
+ self._leading_dimension = LeadingDimension(*get_int_traits(h, mathdx.CublasdxTraitType.LEADING_DIMENSION, 3))
511
+ self._block_dim = Dim3(*get_int_traits(h, mathdx.CublasdxTraitType.BLOCK_DIM, 3))
512
+ self._alignment = Alignment(*get_int_traits(h, mathdx.CublasdxTraitType.ALIGNMENT, 3))
513
+
514
+ self._a_dim = (m, k)
515
+ self._b_dim = (k, n)
516
+ self._c_dim = (m, n)
517
+
518
+ if self._transpose_mode is not None:
519
+ if self._transpose_mode.a in {"transposed", "conj_transposed"}:
520
+ self._a_dim = self._a_dim[::-1]
521
+ if self._transpose_mode.b in {"transposed", "conj_transposed"}:
522
+ self._b_dim = self._b_dim[::-1]
523
+
524
+ [self._a_size, self._b_size, self._c_size] = self._calculate_abc_sizes(self._leading_dimension)
525
+
526
+ self._max_threads_per_block = self._block_dim.x * self._block_dim.y * self._block_dim.z
527
+
528
+ def _calculate_abc_sizes(self, ld: LeadingDimension) -> tuple[int, int, int]:
529
+ assert isinstance(ld, LeadingDimension)
530
+ if self._transpose_mode:
531
+ non_ld = (self._a_dim[1], self._b_dim[1], self._c_dim[1])
532
+ elif self._arrangement:
533
+ non_ld = (
534
+ self._a_dim[1 if self._arrangement.a == "col_major" else 0],
535
+ self._b_dim[1 if self._arrangement.b == "col_major" else 0],
536
+ self._c_dim[1 if self._arrangement.c == "col_major" else 0],
537
+ )
538
+
539
+ return tuple(x * y for x, y in zip(ld, non_ld, strict=True))
540
+
541
+ @property
542
+ def a_value_type(self):
543
+ return self._value_types[0]
544
+
545
+ @property
546
+ def b_value_type(self):
547
+ return self._value_types[1]
548
+
549
+ @property
550
+ def c_value_type(self):
551
+ return self._value_types[2]
552
+
553
+ @property
554
+ @deprecated("value_type trait is deprecated. Please use {a|b|c}_value_type instead")
555
+ def value_type(self):
556
+ if not all(vt == self.a_value_type for vt in self._value_types):
557
+ raise RuntimeError("value_type may be used only if all {a|b|c}_value_type have the same type")
558
+ return self.a_value_type
559
+
560
+ @property
561
+ @deprecated("input_type trait is deprecated. Please use {a|b}_value_type instead")
562
+ def input_type(self):
563
+ if self.a_value_type != self.b_value_type:
564
+ raise RuntimeError("input_type may be used only if A and B input matrix have the same type")
565
+ return self.a_value_type
566
+
567
+ @property
568
+ @deprecated("output_type trait is deprecated. Please use c_value_type instead")
569
+ def output_type(self):
570
+ return self.c_value_type
571
+
572
+ @property
573
+ def a_dim(self):
574
+ return self._a_dim
575
+
576
+ @property
577
+ def b_dim(self):
578
+ return self._b_dim
579
+
580
+ @property
581
+ def c_dim(self):
582
+ return self._c_dim
583
+
584
+ @property
585
+ def leading_dimension(self):
586
+ return self._leading_dimension
587
+
588
+ @property
589
+ def a_size(self):
590
+ return self._a_size
591
+
592
+ @property
593
+ def b_size(self):
594
+ return self._b_size
595
+
596
+ @property
597
+ def c_size(self):
598
+ return self._c_size
599
+
600
+ @property
601
+ @deprecated(
602
+ "shared_memory_size trait is deprecated and will be removed in "
603
+ "future versions. Use get_shared_storage_size instead. Don't "
604
+ "use with Opaque Tensors. Use get_shared_storage_size(...) or"
605
+ "SharedStorageCalc instead"
606
+ )
607
+ def shared_memory_size(self):
608
+ return self.get_shared_storage_size()
609
+
610
+ @property
611
+ def max_threads_per_block(self):
612
+ return self._max_threads_per_block
613
+
614
+ def _get_shared_storage_size(self, *args, ab=False) -> int | None: # type: ignore
615
+ # Complex will be over-aligned (eg: f32x2 complex is aligned on 8B) with
616
+ # this logic (which is what we want - for performance and vectorization)
617
+ item_sizes = tuple(numpy.dtype(vt).itemsize for vt in self._value_types)
618
+
619
+ alignment = self.alignment
620
+ sizes = None
621
+
622
+ if len(args) == 0:
623
+ sizes = (self._a_size, self._b_size, self._c_size)
624
+ elif all(isinstance(arg, int) for arg in args):
625
+ sizes = self._calculate_abc_sizes(LeadingDimension(*pad_or_truncate(list(args), 3)))
626
+ elif all(isinstance(arg, Layout) for arg in args):
627
+ sizes = tuple(arg.cosize for arg in args)
628
+
629
+ if sizes is None:
630
+ return None
631
+
632
+ smem_calc = SharedStorageCalc()
633
+ smem_calc.add(alignment[0], item_sizes[0], sizes[0])
634
+ smem_calc.add(alignment[1], item_sizes[1], sizes[1])
635
+ if not ab:
636
+ smem_calc.add(alignment[2], item_sizes[2], sizes[2])
637
+ return smem_calc.get()
638
+
639
+ @overload
640
+ def get_shared_storage_size(self) -> int: ...
641
+ @overload
642
+ def get_shared_storage_size(self, lda: int, ldb: int, ldc: int) -> int: ...
643
+ @overload
644
+ def get_shared_storage_size(self, matrix_a_layout: Layout, matrix_b_layout: Layout, matrix_c_layout: Layout) -> int: ...
645
+ def get_shared_storage_size(self, *args) -> int: # type: ignore
646
+ value_error = ValueError(
647
+ "get_shared_storage_size() takes either 0 or 3 arguments. If 3 "
648
+ "arguments are provided, they must be either all integers or "
649
+ "all Layout objects."
650
+ )
651
+ if len(args) not in {0, 3}:
652
+ raise value_error
653
+ if any(not isinstance(arg, Layout) for arg in args) and any(not isinstance(arg, int) for arg in args):
654
+ raise value_error
655
+ size = self._get_shared_storage_size(*args, ab=False)
656
+ if size is None:
657
+ raise value_error
658
+ return size
659
+
660
+ @overload
661
+ def get_shared_storage_size_ab(self) -> int: ...
662
+ @overload
663
+ def get_shared_storage_size_ab(self, lda: int, ldb: int) -> int: ...
664
+ @overload
665
+ def get_shared_storage_size_ab(self, matrix_a_layout: Layout, matrix_b_layout: Layout) -> int: ...
666
+ def get_shared_storage_size_ab(self, *args) -> int: # type: ignore
667
+ value_error = ValueError(
668
+ "get_shared_storage_size_ab() takes either 0 or 2 arguments. "
669
+ "If 2 arguments are provided, they must be either all integers "
670
+ "or all Layout objects."
671
+ )
672
+ if len(args) not in {0, 2}:
673
+ raise value_error
674
+ if any(not isinstance(arg, Layout) for arg in args) and any(not isinstance(arg, int) for arg in args):
675
+ raise value_error
676
+ size = self._get_shared_storage_size(*args, ab=True)
677
+ if size is None:
678
+ raise value_error
679
+ return size
680
+
681
+ def get_layout_gmem_a(self, leading_dimension: int | None = None) -> Layout:
682
+ return _BlasLayout(self, "get_layout_gmem_a", leading_dimension)
683
+
684
+ def get_layout_gmem_b(self, leading_dimension: int | None = None) -> Layout:
685
+ return _BlasLayout(self, "get_layout_gmem_b", leading_dimension)
686
+
687
+ def get_layout_gmem_c(self, leading_dimension: int | None = None) -> Layout:
688
+ return _BlasLayout(self, "get_layout_gmem_c", leading_dimension)
689
+
690
+ def get_layout_smem_a(self) -> Layout:
691
+ return _BlasLayout(self, "get_layout_smem_a")
692
+
693
+ def get_layout_smem_b(self) -> Layout:
694
+ return _BlasLayout(self, "get_layout_smem_b")
695
+
696
+ def get_layout_smem_c(self) -> Layout:
697
+ return _BlasLayout(self, "get_layout_smem_c")
698
+
699
+ def suggest_layout_smem_a(self) -> Layout:
700
+ return _BlasLayout(self, "suggest_layout_smem_a")
701
+
702
+ def suggest_layout_smem_b(self) -> Layout:
703
+ return _BlasLayout(self, "suggest_layout_smem_b")
704
+
705
+ def suggest_layout_smem_c(self) -> Layout:
706
+ return _BlasLayout(self, "suggest_layout_smem_c")
707
+
708
+ def suggest_layout_rmem_c(self) -> Layout:
709
+ return _BlasLayout(self, "suggest_layout_rmem_c")
710
+
711
+
712
+ #
713
+ # A compiled BLAS device function, with knobs and device function
714
+ #
715
+ class BlasCompiled(BlasOptionsComplete):
716
+ def __init__(self, **kwargs):
717
+ execute_api = kwargs.pop("execute_api", "static_leading_dimensions")
718
+ tensor_types = kwargs.pop("tensor_types", None)
719
+ global_memory_alignment = kwargs.pop("global_memory_alignment", None)
720
+
721
+ # Build set of knobs
722
+ super().__init__(**kwargs)
723
+
724
+ if global_memory_alignment is not None:
725
+ if not isinstance(global_memory_alignment, Sequence) or len(global_memory_alignment) != 3:
726
+ raise ValueError(
727
+ "global_memory_alignment should be an instance of Alignment"
728
+ "or a 3-tuple ; "
729
+ "got global_memory_alignment = {global_memory_alignment}"
730
+ )
731
+ global_memory_alignment = Alignment(*global_memory_alignment)
732
+
733
+ validate_alignment(
734
+ global_memory_alignment,
735
+ self.precision,
736
+ self.data_type,
737
+ gmem=True,
738
+ )
739
+
740
+ validate_execute_api(execute_api)
741
+ tensors_api = execute_api == "tensors"
742
+ if tensors_api:
743
+ validate_tensor_types(tensor_types)
744
+
745
+ self._execute_api = execute_api
746
+ self._tensor_types = tensor_types
747
+ self._global_memory_alignment = global_memory_alignment
748
+
749
+ handle = generate_MM(
750
+ size=self.size,
751
+ function=self.function,
752
+ precision=self.precision,
753
+ data_type=self.data_type,
754
+ code_type=self.code_type,
755
+ transpose_mode=self._transpose_mode,
756
+ arrangement=self.arrangement,
757
+ alignment=self._alignment,
758
+ block_dim=self.block_dim,
759
+ static_block_dim=self._static_block_dim,
760
+ # TODO: find better way to exclude ld operator for dynamic_leading_dimensions
761
+ leading_dimension=self._leading_dimension if self._execute_api == "static_leading_dimensions" else None,
762
+ execution=self._execution,
763
+ execute_api=self._execute_api,
764
+ tensor_types=self._tensor_types,
765
+ )
766
+
767
+ # TODO: remove once MM.files is deprecated
768
+ self._handle = handle
769
+
770
+ # Now compile the LTO device function
771
+ h = handle.descriptor
772
+
773
+ if tensors_api:
774
+ self._declare_tensors(h)
775
+
776
+ code, self._tensor_api_symbols = generate_code_tensors(
777
+ h, self.code_type.cc, self._gmem_tensors, self._target_tensors, rmem_c="rmem" in self._tensor_types[2]
778
+ )
779
+ else:
780
+ code = generate_code(h, self.code_type.cc)
781
+
782
+ # Compile
783
+ lto_fn = get_lto(code.descriptor)
784
+ isa_version = get_isa_version(code.descriptor)
785
+
786
+ self._ltos = [Code(self.code_type, isa_version, lto_fn)]
787
+ self._symbol = get_str_trait(h, mathdx.CublasdxTraitType.SYMBOL_NAME)
788
+
789
+ if self._tensor_types:
790
+ _, copy_wait_lto = generate_copy_wait_lto(self.code_type.cc)
791
+ self._ltos += [Code(self.code_type, isa_version, copy_wait_lto)]
792
+
793
+ def _declare_tensors(self, h):
794
+ # Complex will be over-aligned (eg: f32x2 complex is aligned on 8B) with
795
+ # this logic (which is what we want - for performance and vectorization)
796
+ item_sizes = tuple(numpy.dtype(vt).itemsize for vt in self._value_types)
797
+
798
+ self._gmem_tensors, self._target_tensors = generate_tensors(h, self._tensor_types, self._global_memory_alignment)
799
+ self._target_tensor_sizes = get_tensor_int_traits(self._target_tensors, mathdx.CublasdxTensorTrait.STORAGE_BYTES)
800
+ for ts, _is in zip(self._target_tensor_sizes, item_sizes, strict=True):
801
+ assert ts % _is == 0
802
+ self._target_tensor_sizes = tuple(ts // item_sizes[i] for i, ts in enumerate(self._target_tensor_sizes))
803
+ self._gmem_tensor_uids = get_tensor_int_traits(self._gmem_tensors, mathdx.CublasdxTensorTrait.UID)
804
+ self._target_tensor_uids = get_tensor_int_traits(self._target_tensors, mathdx.CublasdxTensorTrait.UID)
805
+
806
+ def definition(self):
807
+ dd = super().definition()
808
+ dd.update(execute_api=self.execute_api)
809
+ if self.execute_api == "tensors":
810
+ dd.update(tensor_types=self.tensor_types)
811
+ return dd
812
+
813
+ @cached_property
814
+ def _tempfiles(self):
815
+ """
816
+ Create temporary files for the LTO functions.
817
+ """
818
+ return [make_binary_tempfile(lto.data, ".ltoir") for lto in self._ltos]
819
+
820
+ @property
821
+ def files(self):
822
+ """The list of binary files for the lto functions."""
823
+ return [v.name for v in self._tempfiles]
824
+
825
+ @property
826
+ def codes(self):
827
+ """A list of :class:`Code` objects for all lto functions."""
828
+ return self._ltos
829
+
830
+ @property
831
+ def symbol(self):
832
+ """The name of the device function."""
833
+ return self._symbol
834
+
835
+ @property
836
+ def execute_api(self) -> str:
837
+ """
838
+ The API used to execute the function. It defines the signature of the
839
+ LTO function.
840
+ """
841
+ return self._execute_api
842
+
843
+ @property
844
+ def tensor_types(self) -> tuple[str, str, str]:
845
+ """
846
+ The tensor types used in the function. Defines types of the tensors for
847
+ the tensors API.
848
+ """
849
+ if self.execute_api != "tensors":
850
+ raise RuntimeError("tensor_types is only available when execute_api is 'tensors'")
851
+ return self._tensor_types
852
+
853
+
854
+ #
855
+ # A compiled BLAS device function, with knobs and device function, to be used with Numba
856
+ #
857
+ class BlasNumba(BlasCompiled):
858
+ """
859
+ A class that encapsulates a compiled BLAS device function compatible with Numba.
860
+
861
+
862
+ """
863
+
864
+ def __init__(self, **kwargs):
865
+ if "code_type" not in kwargs:
866
+ kwargs["code_type"] = get_default_code_type()
867
+
868
+ # Build LTO device functions
869
+ super().__init__(**kwargs)
870
+
871
+ self._numba_value_types = tuple(NP_TYPES_TO_NUMBA_FE_TYPES[vt] for vt in self._value_types)
872
+
873
+ @property
874
+ def a_value_type(self):
875
+ return self._numba_value_types[0]
876
+
877
+ @property
878
+ def b_value_type(self):
879
+ return self._numba_value_types[1]
880
+
881
+ @property
882
+ def c_value_type(self):
883
+ return self._numba_value_types[2]
884
+
885
+ @deprecated("Calling MM(...) directly is deprecated, please use MM.execute(...) method instead.")
886
+ def __call__(self, *args):
887
+ raise RuntimeError("__call__ should not be called directly outside of a numba.cuda.jit(...) kernel.")
888
+
889
+ def execute(self, *args):
890
+ raise RuntimeError("execute should not be called directly outside of a numba.cuda.jit(...) kernel.")
891
+
892
+ @cached_property
893
+ def _copy_symbols_map(self):
894
+ if self.execute_api != "tensors":
895
+ return {}
896
+
897
+ return {
898
+ (self._gmem_tensor_uids[0], self._target_tensor_uids[0]): self._tensor_api_symbols.copy_a,
899
+ (self._gmem_tensor_uids[1], self._target_tensor_uids[1]): self._tensor_api_symbols.copy_b,
900
+ (self._gmem_tensor_uids[2], self._target_tensor_uids[2]): self._tensor_api_symbols.copy_c,
901
+ (self._target_tensor_uids[2], self._gmem_tensor_uids[2]): self._tensor_api_symbols.copy_c_back,
902
+ }
903
+
904
+
905
+ @docstring_decorator(CUBLASDX_DOCSTRING, skip_missing=False)
906
+ def matmul(*, compiler=None, **kwargs):
907
+ """
908
+ Create an :class:`BlasOptions` object that encapsulates a compiled and ready-to-use
909
+ device function for matrix multiplication.
910
+
911
+ Args:
912
+ size: {size}
913
+
914
+ precision: {precision}
915
+
916
+ data_type: {data_type}
917
+
918
+ compiler: {compiler}
919
+
920
+ code_type (CodeType): {code_type}
921
+
922
+ block_size (int): {block_size}
923
+
924
+ block_dim (Dim3): {block_dim}
925
+
926
+ leading_dimension (LeadingDimension): {leading_dimension}
927
+
928
+ transpose_mode (TransposeMode): {transpose_mode}
929
+
930
+ arrangement (Arrangement): {arrangement}
931
+
932
+ alignment (Alignment): {alignment}
933
+
934
+ function (str): {function}
935
+
936
+ execution (str): {execution}
937
+
938
+ execute_api (str): {execute_api}
939
+
940
+ tensor_types (str): {tensor_types}
941
+
942
+ global_memory_alignment (Alignment): {global_memory_alignment}
943
+
944
+ See Also:
945
+ The attributes of :class:`BlasOptions` provide a 1:1 mapping with the CUDA C++
946
+ cuBLASDx APIs. For further details, please refer to `cuBLASDx documentation
947
+ <https://docs.nvidia.com/cuda/cublasdx/>`_.
948
+
949
+ Examples:
950
+
951
+ >>> from numba import cuda
952
+ >>> from nvmath.device import matmul
953
+ >>> import numpy as np
954
+ >>> m, n, k = 32, 16, 64
955
+ >>> block_size = 256
956
+
957
+ Use :func:`nvmath.device.matmul` to create the compiled matrix multiplication
958
+ object:
959
+
960
+ >>> MM = matmul(
961
+ ... size=(m, n, k),
962
+ ... precision=np.float32,
963
+ ... data_type="real",
964
+ ... transpose_mode=("non_transposed", "transposed"),
965
+ ... execution="Block",
966
+ ... block_size=block_size,
967
+ ... compiler="numba",
968
+ ... )
969
+
970
+ Pass ``link=MM.files`` to the :func:`numba.cuda.jit` decorator when defining your
971
+ kernel to link with the compiled code.
972
+
973
+ cuBLASDx works on shared memory arrays. It requires column-major (F order) arrays
974
+ but :class:`cuda.shared.array` creates row-major (C order) arrays only. You can
975
+ emulate a column-major array by flipping dimensions. With your shared memory arrays
976
+ ready and filled with actual data, you can run the matrix multiplication by calling
977
+ `MM`
978
+
979
+ >>> a_dim, b_dim, c_dim = MM.a_dim, MM.b_dim, MM.c_dim
980
+ >>> @cuda.jit(link=MM.files)
981
+ ... def f():
982
+ ... a = cuda.shared.array(shape=(a_dim[1], a_dim[0]), dtype=np.float32)
983
+ ... b = cuda.shared.array(shape=(b_dim[1], b_dim[0]), dtype=np.float32)
984
+ ... c = cuda.shared.array(shape=(c_dim[1], c_dim[0]), dtype=np.float32)
985
+ ... # TODO: Populate the arrays with actual data.
986
+ ... alpha, beta = 1.0, 0.0
987
+ ... MM(alpha, a, b, beta, c)
988
+ ... cuda.syncthreads()
989
+ ... # TODO: Copy the result (c) from the shared memory
990
+ >>> f[1, block_size]()
991
+
992
+ Further examples can be found in the `nvmath/examples/device
993
+ <https://github.com/NVIDIA/nvmath-python/tree/main/examples/device>`_ directory.
994
+ """
995
+ check_in("compiler", compiler, [None, "numba"])
996
+ if compiler is None:
997
+ return BlasCompiled(**kwargs)
998
+ elif compiler == "numba":
999
+ return BlasNumba(**kwargs)
1000
+
1001
+
1002
+ def _parse_layout(layout: str) -> tuple[bool, str, str]:
1003
+ """Parse layout string to extract tensor type and memory type.
1004
+
1005
+ Returns: tuple of (suggest, memory, tensor)
1006
+ suggest: bool, True if the layout is a suggested layout
1007
+ memory: str, memory type ('s' for shared, 'g' for global, 'r' for register)
1008
+ tensor: str, tensor type ('a', 'b', 'c')
1009
+ """
1010
+ # extracting tensor type from layout
1011
+ pattern = re.compile(r"^(?:(suggest|get)_)?layout_([srg])mem_([abc])$")
1012
+
1013
+ match = pattern.match(layout)
1014
+
1015
+ assert match is not None
1016
+
1017
+ suggest, memory, tensor = match.group(1, 2, 3)
1018
+
1019
+ return suggest == "suggest", memory, tensor
1020
+
1021
+
1022
+ class _BlasLayout(Layout):
1023
+ """BlasLayout for the OpaqueTensor"""
1024
+
1025
+ _size: int
1026
+ _cosize: int
1027
+
1028
+ # Runtime fields for the opaque tensor
1029
+ _uid: int
1030
+ _leading_dimension: int | None
1031
+
1032
+ # Internal fields to recreate the numba layout type
1033
+ _MM: BlasNumba | None
1034
+ _layout: str
1035
+
1036
+ # Cached fields to avoid recomputing
1037
+ _is_register: bool
1038
+ _tensor_index: int
1039
+
1040
+ def __init__(self, MM: BlasOptionsComplete, layout: str, leading_dimension: int | None = None):
1041
+ if not isinstance(MM, BlasCompiled):
1042
+ raise ValueError("MM should be an instance of BlasCompiled, support for BlasOptionsComplete is in progress")
1043
+ if MM.execute_api != "tensors":
1044
+ raise ValueError(f"{layout} is only available for execute_api='tensors'")
1045
+
1046
+ assert MM._tensor_types is not None
1047
+
1048
+ suggested, memory, tensor = _parse_layout(layout)
1049
+ self._tensor_index = ["a", "b", "c"].index(tensor)
1050
+
1051
+ self._size = math.prod((MM.a_dim, MM.b_dim, MM.c_dim)[self._tensor_index])
1052
+
1053
+ if memory == "g":
1054
+ self._uid = MM._gmem_tensor_uids[self._tensor_index]
1055
+ self._cosize = self._size
1056
+ else:
1057
+ tensor_type = f"{memory}mem_{tensor}"
1058
+
1059
+ if suggested:
1060
+ tensor_type = "suggested_" + tensor_type
1061
+
1062
+ if tensor_type not in set(MM._tensor_types):
1063
+ raise ValueError(f"Invalid layout {layout} for tensor {tensor_type}. Available layouts are {MM._tensor_types}")
1064
+ self._uid = MM._target_tensor_uids[self._tensor_index]
1065
+ self._cosize = MM._target_tensor_sizes[self._tensor_index]
1066
+
1067
+ if memory == "r":
1068
+ # for register memory, we are using fragment so it does not have
1069
+ # any gaps and contain only small chank, so dimension production
1070
+ # does not apply
1071
+ self._size = self._cosize
1072
+
1073
+ self._is_register = memory == "r"
1074
+ self._dynamic_ld = memory == "g" # dynamic ld only global memory
1075
+ self._MM = MM if isinstance(MM, BlasNumba) else None
1076
+ self._layout = layout
1077
+ self._leading_dimension = leading_dimension
1078
+
1079
+ @property
1080
+ def size(self) -> int:
1081
+ return self._size
1082
+
1083
+ @property
1084
+ def cosize(self) -> int:
1085
+ return self._cosize