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,624 @@
1
+ # Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Defines dataclasses for tracking matrix layout and traits specifically for wrangling inputs
7
+ into a form accepted by matrix multiplication operations in BLAS APIs.
8
+
9
+ The module defines three classes: BLASMatrixTraits which represents a single matrix,
10
+ InputMMTraits which represents a collection of BLASMatrixTraits to be used together in a
11
+ matrix multiplication, and BLASMMTraitsView which represents a view of the InputMMTraits
12
+ that is compatible with the BLAS API.
13
+ """
14
+
15
+ from __future__ import annotations
16
+
17
+ import itertools
18
+ import typing
19
+ from collections.abc import Sequence
20
+ from dataclasses import dataclass
21
+
22
+ import nvmath.bindings.cublas as cublas
23
+ import nvmath.bindings.cublasLt as cublaslt
24
+ from nvmath._internal.utils import LoggerLike
25
+ from nvmath.internal import typemaps
26
+
27
+
28
+ def check_extents(shape: Sequence[int], name: str):
29
+ """Raises an error if any element in shape is non-positive.
30
+
31
+ 0D tensors are expected to have shape () and stride ().
32
+ """
33
+ if any(e <= 0 for e in shape):
34
+ message = f"The specified extents {shape} for operand {name} are not valid. The extents must be strictly positive."
35
+ raise ValueError(message)
36
+
37
+
38
+ def check_strides(strides: Sequence[int], name: str):
39
+ """Raises an error if any element in strides is negative.
40
+
41
+ 0D tensors are expected to have shape () and stride().
42
+ Strides may be 0 or positive.
43
+ BLAS disallows negative strides, but the cuBLAS dgmm extension allows negative strides.
44
+ """
45
+ if any(s < 0 for s in strides):
46
+ message = f"The specified strides {strides} for operand {name} are not valid. The strides must be non-negative."
47
+ raise ValueError(message)
48
+
49
+
50
+ @dataclass(slots=True)
51
+ class BLASMatrixTraits:
52
+ """Manages a tensor's layout and its compatibility with BLAS API matmuls.
53
+
54
+ :class:`BLASMatrixTraits` encapsulates attributes and methods to handle single matrix
55
+ layouts, including memory order, operations (e.g., transpose, conjugation), and
56
+ dimensions. It provides functionality to transform matrices into compatible formats for
57
+ BLAS matrix multiplication.
58
+
59
+ Diagonal matrices should be described as the equivalent square matrix, with equal
60
+ strides in both dimensions.
61
+
62
+ Attributes:
63
+ dtype : The CUDA data type representing the matrix element.
64
+
65
+ shape: The number of elements along each dimension.
66
+
67
+ strides: The number of elements in memory to move between elements along the
68
+ corresponding dimension. We expect a stride of 0 for singleton dimensions.
69
+
70
+ is_conjugate : A flag indicating if the matrix elements should be conjugated.
71
+
72
+ is_transpose : A flag indicating if the matrix should be transposed.
73
+
74
+ is_lower : A flag indicating if the matrix is a lower (true) or upper (false)
75
+ triangular matrix. We track this by default even if the matrix is full because
76
+ we can always ignore this parameter.
77
+ """
78
+
79
+ dtype: typemaps.cudaDataType
80
+ shape: Sequence[int]
81
+ strides: Sequence[int]
82
+ is_conjugate: bool
83
+ is_transpose: bool
84
+ is_lower: bool
85
+
86
+ def __post_init__(self):
87
+ assert len(self.shape) < 3, "Internal Error: BLASMatrixTraits supports only 0..2D matrices."
88
+ assert len(self.strides) == len(self.shape), "Internal Error: BLASMatrixTraits strides and shape must have same length."
89
+ assert all(extent > 0 for extent in self.shape), "Internal Error: BLASMatrixTraits supports only positive shapes."
90
+ assert all(stride >= 0 for stride in self.strides), (
91
+ "Internal Error: BLASMatrixTraits supports only non-negative strides."
92
+ )
93
+
94
+ @property
95
+ def order(self) -> cublaslt.Order:
96
+ """The indexing order of the matrix in memory."""
97
+ if len(self.shape) < 2:
98
+ # For vectors and scalars we return COL-order because COL is the default.
99
+ return cublaslt.Order.COL
100
+ msg = f"Unsupported layout for shape: {self.shape} strides: {self.strides}. At least one dimension must be contiguous."
101
+ if (strides_contiguous_perhaps := self.strides_contiguous_perhaps) is None:
102
+ raise ValueError(msg)
103
+ if self.shape[0] * strides_contiguous_perhaps[0] <= strides_contiguous_perhaps[1]:
104
+ return cublaslt.Order.COL
105
+ if self.shape[1] * strides_contiguous_perhaps[1] <= strides_contiguous_perhaps[0]:
106
+ return cublaslt.Order.ROW
107
+ raise ValueError(msg)
108
+
109
+ @property
110
+ def ld(self) -> int:
111
+ """The leading dimension of the matrix without operations applied."""
112
+ assert self.order == cublaslt.Order.COL, (
113
+ f"Internal Error: {self.__class__.__name__}.ld should only be accessed if the matrix is COL-order."
114
+ )
115
+ assert len(self.shape) == 2, (
116
+ f"Internal Error: {self.__class__.__name__}.ld should only be accessed if the matrix is 2D."
117
+ )
118
+ if self.shape == (1, 1):
119
+ return 1 # A special case where the strides don't matter
120
+ if self.strides[0] == 0 and self.shape[0] != 1 or self.strides[1] == 0 and self.shape[1] != 1:
121
+ raise ValueError("The leading dimension cannot be 0 for a non-singleton dimension.")
122
+ if (strides_contiguous_perhaps := self.strides_contiguous_perhaps) is None:
123
+ msg = (
124
+ f"Unsupported layout for shape: {self.shape} strides: {self.strides}. "
125
+ "At least one dimension must be contiguous."
126
+ )
127
+ raise ValueError(msg)
128
+ return strides_contiguous_perhaps[1]
129
+
130
+ @property
131
+ def operation(self) -> cublas.Operation:
132
+ """The operation to be applied to the matrix before multiplication."""
133
+ match (self.is_conjugate, self.is_transpose):
134
+ case (False, False):
135
+ return cublas.Operation.N
136
+ case (False, True):
137
+ return cublas.Operation.T
138
+ case (True, True):
139
+ return cublas.Operation.C
140
+ case (True, False):
141
+ raise NotImplementedError("Conjugate non-transpose operation is not supported.")
142
+ case _:
143
+ raise NotImplementedError("Conjugate and transpose flags must be python booleans.")
144
+
145
+ @property
146
+ def is_contiguous(self) -> bool:
147
+ """Whether the matrix is contiguous in memory."""
148
+ return 1 in self.strides or 0 in self.strides or len(self.shape) == 0
149
+
150
+ @property
151
+ def has_contiguous_view(self) -> bool:
152
+ """Whether the matrix can be viewed as contiguous in memory.
153
+
154
+ A view/slice of a non-contiguous matrix can be contiguous if the matrix is a vector
155
+ or a scalar.
156
+ """
157
+ return self.is_contiguous or 1 in self.shape or 0 in self.shape
158
+
159
+ @property
160
+ def strides_contiguous_perhaps(self) -> Sequence[int] | None:
161
+ """A contiguous view of the matrix's strides if the matrix can be viewed as
162
+ contiguous in memory, otherwise None.
163
+ """
164
+ if self.is_contiguous:
165
+ return self.strides
166
+ if self.has_contiguous_view:
167
+ new_strides = tuple(
168
+ 0 if shape in (0, 1) else stride for shape, stride in zip(self.shape, self.strides, strict=True)
169
+ )
170
+ return new_strides
171
+ return None
172
+
173
+ @property
174
+ def has_a_contiguous_dim(self) -> bool:
175
+ """Whether the matrix has at least one contiguous dimension in memory."""
176
+ return 1 in self.strides or 0 in self.strides or len(self.shape) == 0
177
+
178
+ @property
179
+ def has_singleton_dim(self) -> bool:
180
+ """Whether the matrix has a dimension that can be viewed as contiguous in memory.
181
+
182
+ A view/slice of a non-contiguous matrix can be contiguous if the matrix is a vector
183
+ or a scalar.
184
+ """
185
+ return 1 in self.shape or 0 in self.shape
186
+
187
+ def transpose_and_reorder(self, logger: LoggerLike) -> BLASMatrixTraits:
188
+ """Return a new :class:`BLASMatrixTraits` that has been transposed, reordered,
189
+ and the transpose flag flipped.
190
+
191
+ Simultaneous transpose and reorder is a useful operation because the data does not
192
+ need to move, but we can change the layout order of the matrix. Flipping the
193
+ transpose flag makes the matrix the same shape as the original.
194
+ """
195
+ assert self.has_contiguous_view, "Internal Error: transpose_and_reorder only supports for contiguous matrices."
196
+ new = BLASMatrixTraits(
197
+ dtype=self.dtype,
198
+ shape=self.shape[::-1],
199
+ strides=self.strides[::-1],
200
+ is_conjugate=self.is_conjugate,
201
+ is_transpose=not self.is_transpose,
202
+ is_lower=not self.is_lower,
203
+ )
204
+ logger.debug("The matrix was transposed and reordered from %s to %s.", self.order.name, new.order.name)
205
+ return new
206
+
207
+ def flip_transpose_flag(self, logger: LoggerLike) -> BLASMatrixTraits:
208
+ """Return a new :class:`BLASMatrixTraits` with the transpose flag flipped."""
209
+ new = BLASMatrixTraits(
210
+ dtype=self.dtype,
211
+ shape=self.shape,
212
+ strides=self.strides,
213
+ is_conjugate=self.is_conjugate,
214
+ is_transpose=not self.is_transpose,
215
+ is_lower=self.is_lower,
216
+ )
217
+ logger.debug("The matrix was transposed.")
218
+ return new
219
+
220
+ def promote_left(self, logger: LoggerLike, ndim: int = 2) -> BLASMatrixTraits:
221
+ """Return a new :class:`BLASMatrixTraits` with new singleton dimensions added to
222
+ the left side of `shape` until the matrix has at least `ndim` dimensions."""
223
+ add = max(ndim - len(self.shape), 0)
224
+ promoted = BLASMatrixTraits(
225
+ dtype=self.dtype,
226
+ shape=(*[1] * add, *self.shape),
227
+ strides=(*[0] * add, *self.strides),
228
+ is_conjugate=self.is_conjugate,
229
+ is_transpose=self.is_transpose,
230
+ is_lower=self.is_lower,
231
+ )
232
+ if add > 0:
233
+ logger.debug("The matrix was promoted from shape %s to shape %s", self.shape, promoted.shape)
234
+ return promoted
235
+
236
+ def promote_right(self, logger: LoggerLike, ndim: int = 2) -> BLASMatrixTraits:
237
+ """Return a new :class:`BLASMatrixTraits` with new singleton dimensions added to
238
+ the right side of `shape` until the matrix has at least `ndim` dimensions."""
239
+ add = max(ndim - len(self.shape), 0)
240
+ promoted = BLASMatrixTraits(
241
+ dtype=self.dtype,
242
+ shape=(*self.shape, *[1] * add),
243
+ strides=(*self.strides, *[0] * add),
244
+ is_conjugate=self.is_conjugate,
245
+ is_transpose=self.is_transpose,
246
+ is_lower=self.is_lower,
247
+ )
248
+ if add > 0:
249
+ logger.debug("The matrix was promoted from shape %s to shape %s", self.shape, promoted.shape)
250
+ return promoted
251
+
252
+ def __str__(self):
253
+ match self.is_conjugate, self.is_transpose:
254
+ case (True, True):
255
+ operation = "CONJ-TRAN"
256
+ case (True, False):
257
+ operation = "CONJ"
258
+ case (False, True):
259
+ operation = "TRAN"
260
+ case (False, False):
261
+ operation = "NONE"
262
+ return f"shape {self.shape} strides {self.strides} order {self.order.name} and operation {operation}"
263
+
264
+
265
+ @dataclass(slots=True, frozen=True)
266
+ class InputMMTraits:
267
+ """
268
+ InputMMTraits represents the traits of the input and output matrices for BLAS-compatible
269
+ matrix multiplications, including operand A, operand B, an optional operand C, and the
270
+ result matrix, D. It ensures that the operands are compatible with each other.
271
+
272
+ Attributes:
273
+
274
+ M : The number of rows in the resulting matrix multiplication.
275
+
276
+ N : The number of columns in the resulting matrix multiplication.
277
+
278
+ K : The shared dimension between operands A and B for matrix multiplication.
279
+
280
+ a_layout_traits : Layout traits of operand A.
281
+
282
+ b_layout_traits : Layout traits of operand B.
283
+
284
+ c_layout_traits : Layout traits of operand C.
285
+
286
+ d_layout_traits : Layout traits of the result tensor or operand C if inplace.
287
+
288
+ inplace: whether the result will overwrite operand C or the result will be copied
289
+ into a new memory-compact array, D, the same shape as C, but potentially
290
+ different strides.
291
+ """
292
+
293
+ M: int | None
294
+ N: int | None
295
+ K: int | None
296
+ a_layout_traits: BLASMatrixTraits
297
+ b_layout_traits: BLASMatrixTraits
298
+ c_layout_traits: BLASMatrixTraits
299
+ d_layout_traits: BLASMatrixTraits
300
+ inplace: bool
301
+
302
+ @staticmethod
303
+ def from_layouts(
304
+ a_layout: BLASMatrixTraits,
305
+ b_layout: BLASMatrixTraits,
306
+ c_layout: BLASMatrixTraits | None,
307
+ inplace: bool,
308
+ logger: LoggerLike,
309
+ ):
310
+ """Create a `InputMMTraits` from 2 or 3 `BLASMatrixTraits`.
311
+
312
+ See nvmath.linalg.advanced.matmulmod semantics docstring for matrix promotion and
313
+ broadcasting rules.
314
+ """
315
+ logger.debug("Checking Matmul operands for layout compatibility.")
316
+ match len(a_layout.shape):
317
+ case 0:
318
+ M0, K0 = None, None
319
+ case 1:
320
+ M0, K0 = None, a_layout.shape[0]
321
+ case _:
322
+ M0, K0 = a_layout.shape[::-1] if a_layout.is_transpose else a_layout.shape
323
+ match len(b_layout.shape):
324
+ case 0:
325
+ K1, N0 = None, None
326
+ case 1:
327
+ K1, N0 = b_layout.shape[0], None
328
+ case _:
329
+ K1, N0 = b_layout.shape[::-1] if b_layout.is_transpose else b_layout.shape
330
+ if inplace:
331
+ assert c_layout is not None, "Internal Error: Cannot have inplace operation without C."
332
+ d_layout = c_layout
333
+ else:
334
+ shape: Sequence[int]
335
+ strides: Sequence[int]
336
+ if M0 is None and N0 is not None:
337
+ shape = (N0,)
338
+ strides = (1,)
339
+ elif M0 is not None and N0 is None:
340
+ shape = (M0,)
341
+ strides = (1,)
342
+ elif M0 is not None and N0 is not None:
343
+ shape = (M0, N0)
344
+ # Match order of input C by default.
345
+ # If input C is None, ROW if all ROW else COL
346
+ order = (
347
+ (a_layout.order == cublaslt.Order.ROW and b_layout.order == cublaslt.Order.ROW)
348
+ if c_layout is None
349
+ else c_layout.order
350
+ )
351
+ strides = (N0, 1) if order == cublaslt.Order.ROW else (1, M0)
352
+ else: # both are None
353
+ shape = ()
354
+ strides = ()
355
+ d_layout = BLASMatrixTraits(
356
+ dtype=a_layout.dtype,
357
+ shape=shape,
358
+ strides=strides,
359
+ is_transpose=False,
360
+ is_conjugate=False,
361
+ is_lower=True,
362
+ )
363
+ logger.debug("Out-of-place result has traits of %s", d_layout)
364
+ if c_layout is None:
365
+ c_layout = d_layout
366
+ match len(c_layout.shape):
367
+ case 0:
368
+ M1, N1 = None, None
369
+ case 1:
370
+ if len(a_layout.shape) <= 1:
371
+ M1, N1 = c_layout.shape[0], None
372
+ else:
373
+ M1, N1 = None, c_layout.shape[0]
374
+ case _:
375
+ M1, N1 = c_layout.shape[::-1] if c_layout.is_transpose else c_layout.shape
376
+ if K0 != K1:
377
+ raise ValueError(
378
+ f"The 'K' extent must match for the operands: K={K0} in operand A is not equal to K={K1} in operand B."
379
+ )
380
+ if M0 is None and N0 is None:
381
+ # Both a,b are vectors; c must be a scalar
382
+ # NOTE: Since BLAS does not support broadcasting c, the shape of c should match
383
+ # a@b exactly not shape (1, 1) or (1,)
384
+ if (M1, N1) != (None, None):
385
+ raise ValueError(f"When both operands A,B are vectors, operand C must be scalar-like, not shape {(M1, N1)}.")
386
+ elif (M0 is not None and N0 is None) or (M0 is None and N0 is not None):
387
+ # One of a,b is a vector; c must be a vector.
388
+ if (M1 or N1) not in [M0, N0]:
389
+ raise ValueError(
390
+ f"When one of operands A,B is a vector, operand C must be a vector with shape ({M0 or N0},), "
391
+ f"not shape ({M1}, {N1})."
392
+ )
393
+ else:
394
+ # Both a,b are matrices; c must have shape (M0, N0)
395
+ if M0 != M1:
396
+ raise ValueError(
397
+ f"The 'M' extent must match for the operands: M={M0} in operand A is not equal to M={M1} in operand C."
398
+ )
399
+ if N0 != N1:
400
+ raise ValueError(
401
+ f"The 'N' extent must match for the operands: N={N0} in operand B is not equal to N={N1} in operand C."
402
+ )
403
+ logger.debug("Matmul operands are layout compatible.")
404
+
405
+ if not (a_layout.has_singleton_dim or a_layout.has_a_contiguous_dim):
406
+ raise ValueError("Unsupported layout: Operand A is not contiguous.")
407
+ if not (b_layout.has_singleton_dim or b_layout.has_a_contiguous_dim):
408
+ raise ValueError("Unsupported layout: Operand B is not contiguous.")
409
+ if not (c_layout.has_singleton_dim or c_layout.has_a_contiguous_dim):
410
+ raise ValueError("Unsupported layout: Operand C is not contiguous.")
411
+
412
+ return InputMMTraits(
413
+ M=M0,
414
+ N=N0,
415
+ K=K0,
416
+ a_layout_traits=a_layout,
417
+ b_layout_traits=b_layout,
418
+ c_layout_traits=c_layout,
419
+ d_layout_traits=d_layout,
420
+ inplace=inplace,
421
+ )
422
+
423
+
424
+ @dataclass(slots=True, frozen=True)
425
+ class BLASMMTraitsView:
426
+ """
427
+ BLASMMTraitsView represents a view of the InputMMTraits that is compatible with the BLAS
428
+ API. i.e. one where all operands are COL order.
429
+
430
+ A series of no-op (no data movement required) transformations are applied to the
431
+ InputMMTraits to create a BLASMMTraitsView.
432
+
433
+ Attributes:
434
+
435
+ M : The number of rows in the resulting matrix multiplication.
436
+
437
+ N : The number of columns in the resulting matrix multiplication.
438
+
439
+ K : The shared dimension between operands A and B for matrix multiplication.
440
+
441
+ a_layout_traits : A BLAS compatible view of operand A.
442
+
443
+ b_layout_traits : A BLAS compatible view of operand B.
444
+
445
+ c_layout_traits : A BLAS compatible view of operand C.
446
+
447
+ is_swapped_AB : Indicates if operands A and B were swapped in order to ensure
448
+ compatibility.
449
+ """
450
+
451
+ M: int
452
+ N: int
453
+ K: int
454
+ a_layout_traits: BLASMatrixTraits
455
+ b_layout_traits: BLASMatrixTraits
456
+ c_layout_traits: BLASMatrixTraits
457
+ is_swapped_AB: bool
458
+
459
+ @staticmethod
460
+ def from_input_traits(
461
+ input_traits: InputMMTraits,
462
+ layout_checker: MMLayoutChecker,
463
+ logger: LoggerLike,
464
+ lookup_table_table: dict[MMLayoutChecker, MMLayoutCheckerLookupTable] | None = None,
465
+ ) -> BLASMMTraitsView:
466
+ logger.debug("Creating a BLAS compatible view of Matmul operands.")
467
+
468
+ a_layout_traits = input_traits.a_layout_traits.promote_left(logger)
469
+ b_layout_traits = input_traits.b_layout_traits.promote_right(logger)
470
+ if input_traits.M is None:
471
+ c_layout_traits = input_traits.d_layout_traits.promote_left(logger)
472
+ else:
473
+ c_layout_traits = input_traits.d_layout_traits.promote_right(logger)
474
+ M0, K0 = a_layout_traits.shape[::-1] if a_layout_traits.is_transpose else a_layout_traits.shape
475
+ K1, N0 = b_layout_traits.shape[::-1] if b_layout_traits.is_transpose else b_layout_traits.shape
476
+ M1, N1 = c_layout_traits.shape[::-1] if c_layout_traits.is_transpose else c_layout_traits.shape
477
+ assert M0 == M1, "Internal Error: M must match."
478
+ assert K0 == K1, "Internal Error: K must match."
479
+ assert N0 == N1, "Internal Error: N must match."
480
+
481
+ supported_layout = BLASMMTraitsView(
482
+ M=M0,
483
+ N=N0,
484
+ K=K0,
485
+ a_layout_traits=a_layout_traits,
486
+ b_layout_traits=b_layout_traits,
487
+ c_layout_traits=c_layout_traits,
488
+ is_swapped_AB=False,
489
+ ).lookup_supported_layout(layout_checker, lookup_table_table, logger)
490
+ logger.debug("A BLAS compatible view of the operands was created.")
491
+ return supported_layout # type: ignore[return-value]
492
+
493
+ def swap_AB_and_transpose_ABC(self, logger: LoggerLike) -> BLASMMTraitsView:
494
+ """Return a new :class:`BLASMMTraits` with operands A and B swapped."""
495
+ logger.debug("Operands A, B will be swapped and transposed in order to transpose C.")
496
+ return BLASMMTraitsView(
497
+ M=self.N,
498
+ N=self.M,
499
+ K=self.K,
500
+ a_layout_traits=self.b_layout_traits.flip_transpose_flag(logger),
501
+ b_layout_traits=self.a_layout_traits.flip_transpose_flag(logger),
502
+ c_layout_traits=self.c_layout_traits.flip_transpose_flag(logger),
503
+ is_swapped_AB=not self.is_swapped_AB,
504
+ )
505
+
506
+ def transpose_and_reorder_A(self, logger: LoggerLike) -> BLASMMTraitsView:
507
+ """Return a new :class:`BLASMMTraits` with operand A transposed and reordered."""
508
+ logger.debug("Operand A was transposed and reordered.")
509
+ return BLASMMTraitsView(
510
+ M=self.M,
511
+ N=self.N,
512
+ K=self.K,
513
+ a_layout_traits=self.a_layout_traits.transpose_and_reorder(logger),
514
+ b_layout_traits=self.b_layout_traits,
515
+ c_layout_traits=self.c_layout_traits,
516
+ is_swapped_AB=self.is_swapped_AB,
517
+ )
518
+
519
+ def transpose_and_reorder_B(self, logger: LoggerLike) -> BLASMMTraitsView:
520
+ """Return a new :class:`BLASMMTraits` with operand B transposed and reordered."""
521
+ logger.debug("Operand B was transposed and reordered.")
522
+ return BLASMMTraitsView(
523
+ M=self.M,
524
+ N=self.N,
525
+ K=self.K,
526
+ a_layout_traits=self.a_layout_traits,
527
+ b_layout_traits=self.b_layout_traits.transpose_and_reorder(logger),
528
+ c_layout_traits=self.c_layout_traits,
529
+ is_swapped_AB=self.is_swapped_AB,
530
+ )
531
+
532
+ def transpose_and_reorder_C(self, logger: LoggerLike) -> BLASMMTraitsView:
533
+ """Return a new :class:`BLASMMTraits` with operand C transposed and reordered."""
534
+ logger.debug("Operand C was transposed and reordered.")
535
+ return BLASMMTraitsView(
536
+ M=self.M,
537
+ N=self.N,
538
+ K=self.K,
539
+ a_layout_traits=self.a_layout_traits,
540
+ b_layout_traits=self.b_layout_traits,
541
+ c_layout_traits=self.c_layout_traits.transpose_and_reorder(logger),
542
+ is_swapped_AB=self.is_swapped_AB,
543
+ )
544
+
545
+ def find_supported_layout(self, layout_checker: MMLayoutChecker, logger: LoggerLike) -> BLASMMTraitsView:
546
+ # If we assume that A, B, C are all non-transposed, but may be conjugate and may be
547
+ # either ROW or COL, then the input space for the matmul is 2 * 2 = 4 for EACH
548
+ # operand (two layouts and either conjugate or not). That makes the total input
549
+ # space 4 * 4 * 4 = 64 possible input combinations which is too large to enumerate.
550
+ #
551
+ # The operations we are allowed to apply to the operands are:
552
+ # - Transpose and reorder A
553
+ # - Transpose and reorder B
554
+ # - Transpose and reorder C
555
+ # - Swap A, B and transpose A, B, C
556
+ #
557
+ # The order in which the operations are applied does not matter, and applying each
558
+ # operation twice is a no-op. Therefore we only care about whether to apply each
559
+ # operation or not.
560
+ #
561
+ # That makes the possible search space of applying each of these operations 2
562
+ # * 2 * 2 * 2 = 16 which is much smaller than 64. We just need check whether the
563
+ # result of applying each of the 16 combinations results in a supported layout.
564
+ allowed_operations = [
565
+ "transpose_and_reorder_A",
566
+ "transpose_and_reorder_B",
567
+ "transpose_and_reorder_C",
568
+ "swap_AB_and_transpose_ABC",
569
+ ]
570
+ # Prioritize the simplest cases: when operands are all COL (apply no operations) or
571
+ # all are ROW (apply all operations)
572
+ for search_depth in [0, 4, 1, 2, 3]:
573
+ for combination in itertools.combinations(allowed_operations, search_depth):
574
+ new_traits = self
575
+ logger.debug("Trying combination: %s", combination)
576
+ for operation in combination:
577
+ new_traits = getattr(new_traits, operation)(logger)
578
+ if layout_checker(new_traits):
579
+ return new_traits # type: ignore[return-value]
580
+ raise ValueError("No BLAS compatible view of the operands was found.")
581
+
582
+ def lookup_supported_layout(
583
+ self,
584
+ layout_checker: MMLayoutChecker,
585
+ lookup_table_table: dict[MMLayoutChecker, MMLayoutCheckerLookupTable] | None,
586
+ logger: LoggerLike,
587
+ ) -> BLASMMTraitsView:
588
+ if lookup_table_table is None or layout_checker not in lookup_table_table:
589
+ logger.debug("No cached layout lookup table found for layout checker; performing exhaustive search.")
590
+ return self.find_supported_layout(layout_checker, logger)
591
+ lookup_table = lookup_table_table[layout_checker]
592
+ key = (
593
+ self.a_layout_traits.order,
594
+ self.b_layout_traits.order,
595
+ self.c_layout_traits.order,
596
+ self.a_layout_traits.is_conjugate,
597
+ self.b_layout_traits.is_conjugate,
598
+ self.c_layout_traits.is_conjugate,
599
+ )
600
+ assert not self.a_layout_traits.is_transpose, "Internal Error: A must be non-transpose."
601
+ assert not self.b_layout_traits.is_transpose, "Internal Error: B must be non-transpose."
602
+ assert not self.c_layout_traits.is_transpose, "Internal Error: C must be non-transpose."
603
+ try:
604
+ operations = lookup_table[key]
605
+ logger.debug("Layout lookup table contains a mapping.")
606
+ except KeyError:
607
+ # We have to fall back to the exhaustive search because ambigously ordered
608
+ # matrices (vectors/scalars) miss the cache
609
+ logger.debug("Layout lookup table does not contain a mapping; performing exhaustive search.")
610
+ return self.find_supported_layout(layout_checker, logger)
611
+ new_traits = self
612
+ for operation in operations:
613
+ new_traits = getattr(new_traits, operation)(logger)
614
+ assert layout_checker(new_traits), "Internal Error: Layout checker returned False for a supported layout."
615
+ return new_traits # type: ignore[return-value]
616
+
617
+
618
+ """A function which checks if a layout is supported for a BLAS matrix multiplication."""
619
+ MMLayoutChecker: typing.TypeAlias = typing.Callable[[BLASMMTraitsView], bool]
620
+
621
+ """Maps from input traits to the operations needed to get a supported layout."""
622
+ MMLayoutCheckerLookupTable: typing.TypeAlias = dict[
623
+ tuple[cublaslt.Order, cublaslt.Order, cublaslt.Order, bool, bool, bool], list[str]
624
+ ]
@@ -0,0 +1,28 @@
1
+ # Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Interface class to encapsulate low-level calls to get and set matmul descriptor attributes.
7
+ """
8
+
9
+ __all__ = ["MatmulDescInterface"]
10
+
11
+ from nvmath._internal.attribute_ifc_factory import make_cublas_attribute_interface
12
+ from nvmath.bindings import cublasLt as cublaslt
13
+
14
+ # Create a class, MatmulDescInterface, such that each enum member in
15
+ # MatmulDescAttribute is exposed as a lowercase property (getter + setter)
16
+ # and a ``set_<attr>_unchecked`` method (setter without debug logging).
17
+ # For example, if the class instance is stored as ``desc_ifc``,
18
+ # then the enum member ``POINTER_MODE`` becomes the property
19
+ # ``desc_ifc.pointer_mode``.
20
+ MatmulDescInterface = make_cublas_attribute_interface(
21
+ class_module=__name__,
22
+ class_name="MatmulDescInterface",
23
+ attribute_enum=cublaslt.MatmulDescAttribute,
24
+ get_attribute_dtype_fn=cublaslt.get_matmul_desc_attribute_dtype,
25
+ get_attribute_fn=cublaslt.matmul_desc_get_attribute,
26
+ set_attribute_fn=cublaslt.matmul_desc_set_attribute,
27
+ with_unchecked=True, # Used by Matmul.reset_operands_unchecked hot paths.
28
+ )
@@ -0,0 +1,27 @@
1
+ # Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Interface class to encapsulate low-level calls to get and set matmul plan preference
7
+ attributes.
8
+ """
9
+
10
+ __all__ = ["MatmulPreferenceInterface"]
11
+
12
+ from nvmath._internal.attribute_ifc_factory import make_cublas_attribute_interface
13
+ from nvmath.bindings import cublasLt as cublaslt
14
+
15
+ # Create a class, MatmulPreferenceInterface, such that each enum member in
16
+ # MatmulPreferenceAttribute is exposed as a lowercase property (getter + setter).
17
+ # For example, if the class instance is stored as ``pref_ifc``,
18
+ # then the enum member ``POINTER_MODE_MASK`` becomes the property
19
+ # ``pref_ifc.pointer_mode_mask``.
20
+ MatmulPreferenceInterface = make_cublas_attribute_interface(
21
+ class_module=__name__,
22
+ class_name="MatmulPreferenceInterface",
23
+ attribute_enum=cublaslt.MatmulPreferenceAttribute,
24
+ get_attribute_dtype_fn=cublaslt.get_matmul_preference_attribute_dtype,
25
+ get_attribute_fn=cublaslt.matmul_preference_get_attribute,
26
+ set_attribute_fn=cublaslt.matmul_preference_set_attribute,
27
+ )