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,1033 @@
1
+ # Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """This module defines an emitter for the universal sparse tensor (UST)."""
6
+
7
+ __all__ = []
8
+
9
+ import re
10
+ from enum import Enum
11
+ from io import StringIO
12
+ from itertools import permutations
13
+ from sys import stdout
14
+
15
+ from nvmath.sparse.ust.tensor_format import (
16
+ Add,
17
+ Dimension,
18
+ Divide,
19
+ LevelExpr,
20
+ LevelFormat,
21
+ Modulo,
22
+ Sequence,
23
+ Subtract,
24
+ is_unique,
25
+ )
26
+
27
+ from ._cpp import prolog_decl
28
+ from ._utils import as_external_tensor, type_str
29
+
30
+ ###############
31
+ # Definitions #
32
+ ###############
33
+
34
+
35
+ class Backend(Enum):
36
+ CUDA = 1
37
+ OMP = 2
38
+
39
+
40
+ class Index(Enum):
41
+ LEVEL = 1
42
+ LEVEL_A = 2
43
+ LEVEL_B = 3
44
+ FREE = 4
45
+ FREE_A = 5
46
+ FREE_B = 6
47
+
48
+
49
+ ID = {Index.LEVEL: "l", Index.LEVEL_A: "ll", Index.LEVEL_B: "l", Index.FREE: "i", Index.FREE_A: "ii", Index.FREE_B: "i"}
50
+
51
+ CYCLIC = 1024 # cyclic scheduled coarsening
52
+
53
+ INDEX_DONE = 999
54
+
55
+
56
+ ##################
57
+ # HELPER METHODS #
58
+ ##################
59
+
60
+
61
+ def _id0(list):
62
+ return f"{[('i' + str(i)) for i in list]}"
63
+
64
+
65
+ def _id1(list):
66
+ return f"{[(ID[t] + str(i)) for t, i in list]}"
67
+
68
+
69
+ def _base(C):
70
+ return f"idxC{len(C) - 1}" if len(C) > 0 else "0"
71
+
72
+
73
+ def _fmt(fmt):
74
+ if isinstance(fmt, tuple):
75
+ fmt, _ = fmt
76
+ return fmt
77
+
78
+
79
+ def _is_unique(fmt):
80
+ if isinstance(fmt, Sequence):
81
+ _, prop = fmt
82
+ return is_unique(prop)
83
+ return True
84
+
85
+
86
+ def _collapse_type(ftypes, A, fmtA, k):
87
+ """Returns loop type 'par' or 'red' depending on indexing."""
88
+ if isinstance(k, Dimension):
89
+ return ftypes[A[fmtA.dimensions.index(k)]]
90
+ elif isinstance(k, LevelExpr) and (
91
+ _collapse_type(ftypes, A, fmtA, k.expression1) == "red" or _collapse_type(ftypes, A, fmtA, k.expression2) == "red"
92
+ ):
93
+ return "red"
94
+ return "par"
95
+
96
+
97
+ def _max_grid(indices, fmts):
98
+ """Returns the maximum grid (up to 3-D) that can be supported."""
99
+ maxg = 0
100
+ for t, i in indices:
101
+ if t == Index.FREE or _fmt(fmts[i]) in (LevelFormat.DENSE, LevelFormat.BATCH, LevelFormat.COMPRESSED):
102
+ maxg += 1
103
+ continue
104
+ break
105
+ return min(3, maxg)
106
+
107
+
108
+ def _lvl2dim(fmtA, A):
109
+ """Returns the mapping of assignments that will implement lvl2dim."""
110
+
111
+ def _mapi(k):
112
+ return A[fmtA.dimensions.index(k)]
113
+
114
+ def _find_range(litems, expr1, expr2):
115
+ for lvl, (k, v) in enumerate(litems):
116
+ if _fmt(v) == LevelFormat.RANGE and k in (expr1, expr2):
117
+ return lvl, k
118
+ raise AssertionError(f"Cannot find {expr1} or {expr2} in levels")
119
+
120
+ lmap = []
121
+ litems = fmtA.levels.items()
122
+ for lvl, (k, _) in enumerate(litems):
123
+ if isinstance(k, Dimension):
124
+ i = _mapi(k)
125
+ stmt = f"const CRD i{i} = l{lvl};"
126
+ need, gen = [lvl], [i]
127
+ elif isinstance(k.operator, Add):
128
+ rl, re = _find_range(litems, k.expression1, k.expression2)
129
+ if re == k.expression2:
130
+ i = _mapi(k.expression1)
131
+ stmt = f"const CRD i{i} = l{lvl} - l{rl};"
132
+ need, gen = [lvl, rl], [i]
133
+ else:
134
+ assert re == k.expression1
135
+ i = _mapi(k.expression2)
136
+ stmt = f"const CRD i{i} = l{lvl} - l{rl};"
137
+ need, gen = [lvl, rl], [i]
138
+ elif isinstance(k.operator, Subtract):
139
+ rl, re = _find_range(litems, k.expression1, k.expression2)
140
+ if re == k.expression2:
141
+ i = _mapi(k.expression1)
142
+ stmt = f"const CRD i{i} = l{rl} + l{lvl};"
143
+ need, gen = [rl, lvl], [i]
144
+ else:
145
+ assert re == k.expression1
146
+ i = _mapi(k.expression2)
147
+ stmt = f"const CRD i{i} = l{rl} - l{lvl};"
148
+ need, gen = [rl, lvl], [i]
149
+ elif isinstance(k.operator, Divide):
150
+ i = _mapi(k.expression1)
151
+ stmt = f"const CRD b{i} = l{lvl} * {k.expression2};"
152
+ need, gen = [lvl], [] # i not ready yet!
153
+ elif isinstance(k.operator, Modulo):
154
+ i = _mapi(k.expression1)
155
+ stmt = f"const CRD i{i} = b{i} + l{lvl};"
156
+ need, gen = [lvl], [i] # i ready!
157
+ else:
158
+ raise AssertionError(f"Unsupported: {k}")
159
+
160
+ lmap.append((need, stmt, gen))
161
+
162
+ return lmap
163
+
164
+
165
+ ###################
166
+ # EMITTER METHODS #
167
+ ###################
168
+
169
+
170
+ def _emit_linearize(name, subs, geni, stream=stdout, indent=0):
171
+ """Emits linearized address computations for a dense tensor."""
172
+ for n, i in enumerate(subs):
173
+ if i == INDEX_DONE:
174
+ continue
175
+ elif i in geni:
176
+ sub = f"(idx{name}{n - 1} + i{i})" if n > 0 else f"i{i}"
177
+ if n == len(subs) - 1: # last one
178
+ print(f"{'':>{indent}}const CRD idx{name}{n} = {sub};", file=stream)
179
+ if name == "C":
180
+ print(f"{'':>{indent}}CTP vC = 0;", file=stream)
181
+ else:
182
+ assert name == "B"
183
+ print(f"{'':>{indent}}const CTP vB = prolog_b(static_cast<CTP>(B[idxB{n}]));", file=stream)
184
+ else:
185
+ print(f"{'':>{indent}}const CRD idx{name}{n} = {sub} * N{subs[n + 1]};", file=stream)
186
+ subs[n] = INDEX_DONE
187
+ else:
188
+ return # next subscript must wait
189
+
190
+
191
+ def _emit_ready(lmap, A, B, C, P, genl, geni, stream=stdout, indent=0):
192
+ """Emits all variables that have their required inputs generated."""
193
+ # Indices ready?
194
+ for m in range(len(lmap)):
195
+ if lmap[m] is not None:
196
+ need, stmt, gen = lmap[m]
197
+ if all(n in genl for n in need):
198
+ print(f"{'':>{indent}}{stmt}", file=stream)
199
+ geni.extend(gen)
200
+ lmap[m] = None
201
+ # A ready? Note that linearization is already done by sparse emitter.
202
+ if all(i in geni for i in A):
203
+ print(f"{'':>{indent}}const CTP vA = prolog_a(static_cast<CTP>(Aval[{P}]));", file=stream)
204
+ A[0] = INDEX_DONE
205
+ # B, C ready?
206
+ _emit_linearize("B", B, geni, stream=stream, indent=indent)
207
+ _emit_linearize("C", C, geni, stream=stream, indent=indent)
208
+
209
+
210
+ def _emit_types(ctp, vtp, itp, gtp, stream=stdout, backend=Backend.CUDA):
211
+ """Emits type definitions with required includes."""
212
+ if backend == Backend.CUDA:
213
+ if vtp in ["__nv_fp8_e4m3", "__nv_fp8_e5m2"]:
214
+ print("#include <cuda_fp8.h>\n", file=stream)
215
+ elif vtp in ["__half"]:
216
+ print("#include <cuda_fp16.h>\n", file=stream)
217
+ elif vtp in ["__nv_bfloat16"]:
218
+ print("#include <cuda_bf16.h>\n", file=stream)
219
+ elif vtp.startswith("cuda::std::complex"):
220
+ print("#include <cuda/std/complex>\n", file=stream)
221
+ elif backend == Backend.OMP:
222
+ if vtp in ["__nv_fp8_e4m3", "__nv_fp8_e5m2", "__half", "__nv_bfloat16"]:
223
+ raise NotImplementedError()
224
+ else:
225
+ raise AssertionError(f"Unsupported backend: {backend}")
226
+ print(f"using CTP = {ctp};", file=stream)
227
+ print(f"using VAL = {vtp};", file=stream)
228
+ print(f"using POS = {itp};", file=stream)
229
+ print(f"using CRD = {itp};", file=stream)
230
+ print(f"using GRD = {gtp};\n", file=stream)
231
+
232
+
233
+ def _emit_atomic(C, vtp, stream=stdout, indent=0, backend=Backend.CUDA):
234
+ """Emits atomic operation."""
235
+ if backend == Backend.CUDA:
236
+ if not vtp.startswith("cuda::std::complex"):
237
+ print(f"{'':>{indent}}atomicAdd(&C[{_base(C)}], static_cast<VAL>(vC));", file=stream)
238
+ else:
239
+ atp = re.search(r"<(.+?)>", vtp).group(1)
240
+ print(f"{'':>{indent}}{atp}* addr = reinterpret_cast<{atp}*>(&C[{_base(C)}]);", file=stream)
241
+ print(f"{'':>{indent}}const VAL c = static_cast<VAL>(vC);", file=stream)
242
+ print(f"{'':>{indent}}atomicAdd(addr + 0, c.real());", file=stream)
243
+ print(f"{'':>{indent}}atomicAdd(addr + 1, c.imag());", file=stream)
244
+ elif backend == Backend.OMP:
245
+ print(f"{'':>{indent}}omp_atomic_add(&C[{_base(C)}], static_cast<VAL>(vC));", file=stream)
246
+ else:
247
+ raise AssertionError(f"Unsupported backend: {backend}")
248
+
249
+
250
+ def _emit_shuffle_down(C, vtp, stream=stdout, indent=0, backend=Backend.CUDA):
251
+ if backend != Backend.CUDA:
252
+ raise NotImplementedError(f"Unsupported backend for shuffle down: {backend}")
253
+ print(f"{'':>{indent}}// WARP REDUCTION (to reduce number of atomics)", file=stream)
254
+ print(f"{'':>{indent}}assert((blockDim.x & 31) == 0);", file=stream)
255
+ print(f"{'':>{indent}}#pragma unroll", file=stream)
256
+ print(f"{'':>{indent}}for (int offset = 16; offset > 0; offset >>= 1) {{", file=stream)
257
+ print(f"{'':>{indent}} vC += __shfl_down_sync(0xffffffffu, vC, offset);", file=stream)
258
+ print(f"{'':>{indent}}}}", file=stream)
259
+ print(f"{'':>{indent}}int lane = threadIdx.x & 31;", file=stream)
260
+ print(f"{'':>{indent}}if (lane == 0) {{", file=stream)
261
+ _emit_atomic(C, vtp, stream=stream, indent=indent + 2, backend=backend)
262
+ print(f"{'':>{indent}}}}", file=stream)
263
+
264
+
265
+ def _emit_accumulate(C, vtp, reduc, stream=stdout, indent=0, backend=Backend.CUDA):
266
+ isAtomicRed, isWarpRed, _ = reduc
267
+ if isAtomicRed:
268
+ if isWarpRed:
269
+ _emit_shuffle_down(C, vtp, stream=stream, indent=indent, backend=backend)
270
+ else:
271
+ _emit_atomic(C, vtp, stream=stream, indent=indent, backend=backend)
272
+ else:
273
+ print(f"{'':>{indent}}C[{_base(C)}] += static_cast<VAL>(vC);", file=stream)
274
+
275
+
276
+ def _emit_cleanup(N, C, vtp, reduc, isGrid=True, stream=stdout, indent=0, backend=Backend.CUDA):
277
+ # OMP uses tightly nested parallel loops for grid set up, so the accumulation
278
+ # must end and the inner loop nest level.
279
+ _, _, cd = reduc
280
+ if isGrid and backend == Backend.OMP and cd is not None:
281
+ cd = N - 1
282
+ # Loop nest closing.
283
+ for i in range(N - 1, -1, -1):
284
+ if cd == i:
285
+ _emit_accumulate(C, vtp, reduc, stream=stream, indent=indent, backend=backend)
286
+ indent -= 2
287
+ print(f"{'':>{indent}}}}", file=stream)
288
+
289
+
290
+ def _emit_thread_grid_setup(
291
+ indices, types, A, B, C, fmtA, lmap, genl, geni, endian, stream=stdout, indent=0, backend=Backend.CUDA
292
+ ):
293
+ """Emits a G-dim grid for a kernel."""
294
+ G = len(indices)
295
+ assert len(types) == G
296
+
297
+ print(f"{'':>{indent}}// {G}-D GRID: {_id1(indices)} {types}", file=stream)
298
+
299
+ # Grid setup.
300
+ if backend == Backend.CUDA:
301
+ if G == 1:
302
+ print(f"{'':>{indent}}const GRD x = blockIdx.x * blockDim.x + threadIdx.x;", file=stream)
303
+ # print(f"{'':>{indent}}const POS t = x;", file=stream)
304
+ grid = ["x"] # any endian
305
+ elif G == 2:
306
+ print(f"{'':>{indent}}const GRD x = blockIdx.x * blockDim.x + threadIdx.x;", file=stream)
307
+ print(f"{'':>{indent}}const GRD y = blockIdx.y * blockDim.y + threadIdx.y;", file=stream)
308
+ # print(f"{'':>{indent}}const GRD w = gridDim.x * blockDim.x;", file=stream)
309
+ # print(f"{'':>{indent}}const GRD t = y * w + x;", file=stream)
310
+ grid = ["x", "y"] if endian else ["y", "x"]
311
+ elif G == 3:
312
+ print(f"{'':>{indent}}const GRD x = blockIdx.x * blockDim.x + threadIdx.x;", file=stream)
313
+ print(f"{'':>{indent}}const GRD y = blockIdx.y * blockDim.y + threadIdx.y;", file=stream)
314
+ print(f"{'':>{indent}}const GRD z = blockIdx.z * blockDim.z + threadIdx.z;", file=stream)
315
+ # print(f"{'':>{indent}}const GRD w = gridDim.x * blockDim.x;", file=stream)
316
+ # print(f"{'':>{indent}}const GRD h = gridDim.y * blockDim.y;", file=stream)
317
+ # print(f"{'':>{indent}}const GRD t = z * w * h + y * w + x;", file=stream)
318
+ grid = ["x", "y", "z"] if endian else ["z", "y", "x"]
319
+ else:
320
+ raise AssertionError(f"unexpected {G}-D grid")
321
+ elif backend == Backend.OMP:
322
+ assert endian
323
+ grid = ["x", "y", "z"][:G]
324
+ print(f"{'':>{indent}}#pragma omp parallel for", file=stream)
325
+ for i in range(G):
326
+ grid_index = grid[i]
327
+ grid_bound = f"G{grid_index}"
328
+ print(f"{'':>{indent}}for (GRD {grid_index} = 0; {grid_index} < {grid_bound}; {grid_index}++) {{", file=stream)
329
+ indent += 2
330
+ else:
331
+ raise AssertionError(f"Unsupported backend: {backend}")
332
+
333
+ # Grid translation into loops.
334
+ P = "0"
335
+ isAtomicRed = False
336
+ cd = None
337
+ fmts = list(fmtA.levels.values())
338
+ for g in range(G):
339
+ t, a = indices[g]
340
+ if t == Index.FREE or t == Index.FREE_A:
341
+ if t == Index.FREE:
342
+ ii, cnst = f"i{a}", "const "
343
+ geni.append(a)
344
+ else:
345
+ ii, cnst = f"ii{a}", ""
346
+ print(f"{'':>{indent}}{cnst}CRD {ii} = {grid[g]};", file=stream)
347
+ if backend == Backend.CUDA:
348
+ print(f"{'':>{indent}}if ({ii} < N{a}) {{", file=stream)
349
+ indent += 2
350
+ else:
351
+ print(f"{'':>{indent}}if ({ii} >= N{a}) continue;", file=stream)
352
+ elif t == Index.LEVEL or t == Index.LEVEL_A:
353
+ isAtomicRed = isAtomicRed or types[g] == "red" or not _is_unique(fmts[a])
354
+ if t == Index.LEVEL:
355
+ pp, ll = f"p{a}", f"l{a}"
356
+ genl.append(a)
357
+ else:
358
+ pp, ll = f"pp{a}", f"ll{a}"
359
+ if _fmt(fmts[a]) in (LevelFormat.DENSE, LevelFormat.BATCH):
360
+ print(f"{'':>{indent}}const POS {pp} = {P} * L{a} + {grid[g]};", file=stream)
361
+ print(f"{'':>{indent}}const CRD {ll} = {grid[g]};", file=stream)
362
+ if backend == Backend.CUDA:
363
+ print(f"{'':>{indent}}if ({ll} < L{a}) {{", file=stream)
364
+ indent += 2
365
+ else:
366
+ print(f"{'':>{indent}}if ({ll} >= L{a}) continue;", file=stream)
367
+ elif _fmt(fmts[a]) == LevelFormat.COMPRESSED:
368
+ # TODO: batching?
369
+ print(f"{'':>{indent}}const POS lo{a} = Apos{a}[{P}];", file=stream)
370
+ print(f"{'':>{indent}}const POS hi{a} = Apos{a}[{P} + 1];", file=stream)
371
+ print(f"{'':>{indent}}const POS {pp} = lo{a} + {grid[g]};", file=stream)
372
+ if backend == Backend.CUDA:
373
+ print(f"{'':>{indent}}if ({pp} < hi{a}) {{", file=stream)
374
+ indent += 2
375
+ else:
376
+ print(f"{'':>{indent}}if ({pp} >= hi{a}) continue;", file=stream)
377
+ if t == Index.LEVEL:
378
+ print(f"{'':>{indent}}const CRD {ll} = Acrd{a}[{pp}];", file=stream)
379
+ else:
380
+ raise AssertionError(f"Unknown level format for {t}: {_fmt(fmts[a])}")
381
+ P = pp
382
+ else:
383
+ raise AssertionError(f"Unexpected index type: {t}")
384
+ _emit_ready(lmap, A, B, C, P, genl, geni, stream=stream, indent=indent)
385
+ if cd is None and all(i == INDEX_DONE for i in C):
386
+ cd = g
387
+
388
+ return P, isAtomicRed, cd
389
+
390
+
391
+ def _emit_loop_structure(
392
+ indices, types, A, B, C, fmtA, vtp, N, lmap, genl, geni, P, reduc, stream=stdout, indent=0, backend=Backend.CUDA
393
+ ):
394
+ """Emits computational loop nest for a kernel."""
395
+ D = len(indices)
396
+ assert D < N and len(types) == D
397
+
398
+ def _find_add_sub(litems, r):
399
+ for lvl, (k, _) in enumerate(litems):
400
+ if isinstance(k, LevelExpr) and isinstance(k.operator, (Add, Subtract)):
401
+ if k.expression1 == r:
402
+ return lvl, k, False, k.expression2
403
+ if k.expression2 == r:
404
+ return lvl, k, True, k.expression1
405
+ raise AssertionError(f"Cannot find {r} in levels")
406
+
407
+ print(f"{'':>{indent}}// LOOPS: {_id1(indices)} {types}", file=stream)
408
+
409
+ # Loop nest opening.
410
+ isAtomicRed, isWarpRed, cd = reduc
411
+ cd = None if cd is None else -1
412
+ fmts = list(fmtA.levels.values())
413
+ litems = fmtA.levels.items()
414
+ batch = sum(_fmt(f) == LevelFormat.BATCH for f in fmts)
415
+ for d in range(D):
416
+ t, a = indices[d]
417
+ if t == Index.FREE:
418
+ print(f"{'':>{indent}}for (CRD i{a} = 0; i{a} < N{a}; i{a}++) {{", file=stream)
419
+ geni.append(a)
420
+ elif t == Index.FREE_B:
421
+ print(f"{'':>{indent}}for (CRD i{a} = ii{a}; i{a} < N{a}; i{a}+={CYCLIC}) {{", file=stream)
422
+ geni.append(a)
423
+ elif t == Index.LEVEL:
424
+ if _fmt(fmts[a]) in (LevelFormat.DENSE, LevelFormat.BATCH):
425
+ print(f"{'':>{indent}}POS p{a} = {P} * L{a};", file=stream)
426
+ print(f"{'':>{indent}}for (CRD l{a} = 0; l{a} < L{a}; l{a}++, p{a}++) {{", file=stream)
427
+ P = f"p{a}"
428
+ genl.append(a)
429
+ elif _fmt(fmts[a]) == LevelFormat.COMPRESSED:
430
+ if batch > 0:
431
+ # Batched uncompressed.
432
+ cidx = f"p{batch - 1} * (L{batch} + 1) + l{a - 1}"
433
+ hidx = f"p{batch - 1} * (L{batch} + 1) + L{batch}"
434
+ print(f"{'':>{indent}}const POS lo{a} = Apos{a}[{cidx}];", file=stream)
435
+ print(f"{'':>{indent}}const POS hi{a} = Apos{a}[{cidx} + 1];", file=stream)
436
+ print(f"{'':>{indent}}const POS no{a} = p{a - 2} * Apos{a}[{hidx}];", file=stream)
437
+ print(f"{'':>{indent}}for (POS p{a} = lo{a} + no{a}; p{a} < hi{a} + no{a}; p{a}++) {{", file=stream)
438
+ batch = 0
439
+ else:
440
+ # Regular uncompressed.
441
+ print(f"{'':>{indent}}const POS lo{a} = Apos{a}[{P}];", file=stream)
442
+ print(f"{'':>{indent}}const POS hi{a} = Apos{a}[{P} + 1];", file=stream)
443
+ print(f"{'':>{indent}}for (POS p{a} = lo{a}; p{a} < hi{a}; p{a}++) {{", file=stream)
444
+ print(f"{'':>{indent}} const CRD l{a} = Acrd{a}[p{a}];", file=stream)
445
+ P = f"p{a}"
446
+ genl.append(a)
447
+ elif _fmt(fmts[a]) == LevelFormat.SINGLETON:
448
+ print(f"{'':>{indent}}const CRD l{a} = Acrd{a}[{P}];", file=stream)
449
+ print(f"{'':>{indent}}{{", file=stream)
450
+ genl.append(a)
451
+ elif _fmt(fmts[a]) == LevelFormat.RANGE:
452
+ q, _ = list(litems)[a]
453
+ la, add, isI, r = _find_add_sub(litems, q)
454
+ di, dj = fmtA.dimensions.index(q), fmtA.dimensions.index(r)
455
+ szi, szj = f"N{di}", f"N{dj}"
456
+ if isinstance(add.operator, Add):
457
+ print(f"{'':>{indent}}const CRD of{a} = l{la};", file=stream)
458
+ print(f"{'':>{indent}}const CRD lo{a} = llmax(0, of{a} - {szj} + 1);", file=stream)
459
+ print(f"{'':>{indent}}const CRD hi{a} = llmin({szi}, of{a} + 1);", file=stream)
460
+ else:
461
+ print(f"{'':>{indent}}const CRD of{a} = {'-' if isI else ''}l{la};", file=stream)
462
+ print(f"{'':>{indent}}const CRD lo{a} = llmax(0, of{a});", file=stream)
463
+ print(f"{'':>{indent}}const CRD hi{a} = llmin({szi}, {szj} + of{a});", file=stream)
464
+ print(f"{'':>{indent}}CRD p{a} = {P} * {szi} + lo{a};", file=stream)
465
+ print(f"{'':>{indent}}for (CRD l{a} = lo{a}; l{a} < hi{a}; l{a}++, p{a}++) {{", file=stream)
466
+ P = f"p{a}"
467
+ genl.append(a)
468
+ elif _fmt(fmts[a]) == LevelFormat.DELTA:
469
+ print(f"{'':>{indent}}const POS lo{a} = Apos{a}[{P}];", file=stream)
470
+ print(f"{'':>{indent}}const POS hi{a} = Apos{a}[{P} + 1];", file=stream)
471
+ print(f"{'':>{indent}}CRD l{a} = 0; // start running sequence", file=stream)
472
+ print(f"{'':>{indent}}for (POS p{a} = lo{a}; p{a} < hi{a}; p{a}++, l{a}++) {{", file=stream)
473
+ print(f"{'':>{indent}} l{a} += Acrd{a}[p{a}]; // adjust by delta", file=stream)
474
+ P = f"p{a}"
475
+ genl.append(a)
476
+ elif _fmt(fmts[a]) == LevelFormat.STRUCTURED:
477
+ n = fmts[a][1]
478
+ print(f"{'':>{indent}}const POS lo{a} = {n} * {P};", file=stream)
479
+ print(f"{'':>{indent}}const POS hi{a} = lo{a} + {n};", file=stream)
480
+ print(f"{'':>{indent}}for (POS p{a} = lo{a}; p{a} < hi{a}; p{a}++) {{", file=stream)
481
+ print(f"{'':>{indent}} const CRD l{a} = Acrd{a}[p{a}];", file=stream)
482
+ P = f"p{a}"
483
+ genl.append(a)
484
+ else:
485
+ raise AssertionError(f"Unknown level format for {t}: {_fmt(fmts[a])}")
486
+ elif t == Index.LEVEL_B:
487
+ if _fmt(fmts[a]) in (LevelFormat.DENSE, LevelFormat.BATCH):
488
+ print(f"{'':>{indent}}POS p{a} = {P};", file=stream)
489
+ print(f"{'':>{indent}}for (CRD l{a} = ll{a}; l{a} < L{a}; l{a}+={CYCLIC}, p{a}+={CYCLIC}) {{", file=stream)
490
+ P = f"p{a}"
491
+ genl.append(a)
492
+ elif _fmt(fmts[a]) == LevelFormat.COMPRESSED:
493
+ assert batch == 0 # TODO: batching?
494
+ print(f"{'':>{indent}}for (POS p{a} = {P}; p{a} < hi{a}; p{a}+={CYCLIC}) {{", file=stream)
495
+ print(f"{'':>{indent}} const CRD l{a} = Acrd{a}[p{a}];", file=stream)
496
+ P = f"p{a}"
497
+ genl.append(a)
498
+ else:
499
+ raise AssertionError(f"Unknown level format for {t}: {_fmt(fmts[a])}")
500
+ else:
501
+ raise AssertionError(f"Unexpected index type: {t}")
502
+ indent += 2
503
+ _emit_ready(lmap, A, B, C, P, genl, geni, stream=stream, indent=indent)
504
+ if cd is None and all(i == INDEX_DONE for i in C):
505
+ cd = d
506
+
507
+ # Loop body.
508
+ if B == [] and C == []:
509
+ pars = ", ".join([f"i{i}" for i in range(fmtA.num_dimensions)])
510
+ print(f"{'':>{indent}}Aval[{P}] = static_cast<VAL>(apply(vA, {pars}));", file=stream)
511
+ elif C == []:
512
+ print(f"{'':>{indent}}const CTP vC = vA * vB;", file=stream)
513
+ else:
514
+ print(f"{'':>{indent}}vC += vA * vB;", file=stream)
515
+
516
+ # Loop nest closing.
517
+ reduc = isAtomicRed, isWarpRed, cd
518
+ _emit_cleanup(D, C, vtp, reduc, isGrid=False, stream=stream, indent=indent, backend=backend)
519
+
520
+
521
+ def _emit_A_parameters(fmtA, constA=True, stream=stdout, indent=0, backend=Backend.CUDA):
522
+ """Emit kernel parameters for a UST."""
523
+ for lvl, (_, v) in enumerate(fmtA.levels.items()):
524
+ # Handle level format.
525
+ if _fmt(v) in (LevelFormat.DENSE, LevelFormat.BATCH, LevelFormat.RANGE):
526
+ pass
527
+ elif _fmt(v) in (LevelFormat.COMPRESSED, LevelFormat.DELTA):
528
+ print(f"{'':>{indent}}const POS* __restrict__ Apos{lvl},", file=stream)
529
+ print(f"{'':>{indent}}const CRD* __restrict__ Acrd{lvl},", file=stream)
530
+ elif _fmt(v) in (LevelFormat.SINGLETON, LevelFormat.STRUCTURED):
531
+ print(f"{'':>{indent}}const CRD* __restrict__ Acrd{lvl},", file=stream)
532
+ else:
533
+ raise AssertionError(f"Unsupported: {_fmt(v)}")
534
+ cnst = "const " if constA else ""
535
+ print(f"{'':>{indent}}{cnst}VAL* __restrict__ Aval,", file=stream)
536
+
537
+
538
+ def _emit_BC_parameters(stream=stdout, indent=0):
539
+ """Emits kernel parameters for dense B and C (shortcut for UST)."""
540
+ print(f"{'':>{indent}}const VAL* __restrict__ B,", file=stream)
541
+ print(f"{'':>{indent}}VAL* __restrict__ C,", file=stream)
542
+
543
+
544
+ def _emit_size_parameters(Q, L, stream=stdout, indent=0):
545
+ """Emits kernel parameters for dimension and level sizes."""
546
+ pars = ",\n ".join([f"const GRD N{i}" for i in range(Q)] + [f"const GRD L{i}" for i in range(L)])
547
+ print(f"{'':>{indent}}{pars}", file=stream)
548
+
549
+
550
+ def _emit_apply_kernel(
551
+ indices, fmtA, endian, ctp, vtp, itp, gtp, with_indices, G, stream=stdout, indent=0, backend=Backend.CUDA
552
+ ):
553
+ """Emits a G-dim "apply" kernel for the given tensor format."""
554
+ if backend != Backend.CUDA:
555
+ raise NotImplementedError(f"Unsupported backend for apply kernel: {backend}")
556
+ Q = fmtA.num_dimensions
557
+ L = fmtA.num_levels
558
+
559
+ print(f"\n// TENSOR FORMAT : {fmtA.name}\n", file=stream)
560
+
561
+ _emit_types(ctp, vtp, itp, gtp, stream=stream, backend=backend)
562
+ print("#define prolog_a(a) (a)\n", file=stream)
563
+
564
+ dim = Q if with_indices else 0
565
+ print(f'{"":>{indent}}extern "C" __device__ CTP apply(CTP{", CRD" * dim});\n', file=stream)
566
+ print(f'{"":>{indent}}extern "C" __global__ void apply_kernel(', file=stream)
567
+ indent += 2
568
+
569
+ if with_indices:
570
+ _emit_A_parameters(fmtA, constA=False, stream=stream, indent=indent)
571
+ _emit_size_parameters(Q, L, stream=stream, indent=indent)
572
+ print(") {", file=stream)
573
+ types = ["par" for i in range(L)]
574
+ A = list(range(Q)) # in terms of dim
575
+ lmap, genl, geni = _lvl2dim(fmtA, A), [], []
576
+ P, _, _ = _emit_thread_grid_setup(
577
+ indices[:G], types[:G], A, [], [], fmtA, lmap, genl, geni, endian, stream=stream, indent=indent, backend=backend
578
+ )
579
+ indent += G * 2
580
+ reduc = False, False, -1
581
+ _emit_loop_structure(
582
+ indices[G:], types[G:], A, [], [], fmtA, vtp, L, lmap, genl, geni, P, reduc, stream=stream, indent=indent
583
+ )
584
+ _emit_cleanup(G, [], vtp, reduc, isGrid=True, stream=stream, indent=indent, backend=backend)
585
+ indent -= G * 2
586
+ else:
587
+ print(f"{'':>{indent}}VAL* __restrict__ Aval,\n{'':>{indent}}POS Anse) {{", file=stream)
588
+ print(f"{'':>{indent}}const GRD x = blockIdx.x * blockDim.x + threadIdx.x;", file=stream)
589
+ print(f"{'':>{indent}}if (x < Anse) {{", file=stream)
590
+ print(f"{'':>{indent}} const CTP vA = prolog_a(static_cast<CTP>(Aval[x]));", file=stream)
591
+ print(f"{'':>{indent}} Aval[x] = static_cast<VAL>(apply(vA));", file=stream)
592
+ print(f"{'':>{indent}}}}", file=stream)
593
+
594
+ indent -= 2
595
+ print(f"{'':>{indent}}}}", file=stream)
596
+
597
+ return indices[:G], endian
598
+
599
+
600
+ def _emit_matmul_kernel(
601
+ indices, types, Q, G, A, B, C, fmtA, endian, ctp, vtp, itp, gtp, stream=stdout, indent=0, backend=Backend.CUDA
602
+ ):
603
+ """Emits a G-dim "matmul" kernel for the given tensor index expression."""
604
+ N = len(indices)
605
+ L = fmtA.num_levels
606
+ assert len(types) == N and G <= N and L <= N
607
+
608
+ print(f"// TENSOR ITERATION : {_id0(list(range(Q)))}", file=stream)
609
+ print(f"// TENSOR EXPRESSION : C{_id0(C)} += A{_id0(A)} * B{_id0(B)}", file=stream)
610
+ print(f"// TENSOR FORMAT : {fmtA.name}\n", file=stream)
611
+
612
+ _emit_types(ctp, vtp, itp, gtp, stream=stream, backend=backend)
613
+ if backend == Backend.CUDA:
614
+ print(prolog_decl, file=stream)
615
+ else:
616
+ # OMP does not inject user-functions.
617
+ print("#define prolog_a(a) (a)", file=stream)
618
+ print("#define prolog_b(b) (b)\n", file=stream)
619
+
620
+ print(f"{'':>{indent}}// MATMUL KERNEL: {_id1(indices)} {types}", file=stream)
621
+ if backend == Backend.CUDA:
622
+ print(f'{"":>{indent}}extern "C" __global__ void matmul_kernel(', file=stream)
623
+ elif backend == Backend.OMP:
624
+ print(f'{"":>{indent}}extern "C" void matmul_kernel(', file=stream)
625
+ else:
626
+ raise AssertionError(f"Unsupported backend for matmul kernel: {backend}")
627
+ _emit_A_parameters(fmtA, stream=stream, indent=indent + 2)
628
+ _emit_BC_parameters(stream=stream, indent=indent + 2)
629
+ _emit_size_parameters(Q, L, stream=stream, indent=indent + 2)
630
+ if backend == Backend.OMP:
631
+ # For OMP, grid dimensions are passed as parameters
632
+ print(f"{'':>{indent + 2}}, GRD Gx, GRD Gy, GRD Gz", file=stream)
633
+ print(f"{'':>{indent}}) {{", file=stream)
634
+ indent += 2
635
+
636
+ lmap, genl, geni = _lvl2dim(fmtA, A), [], []
637
+ P, isAtomicRed, cd = _emit_thread_grid_setup(
638
+ indices[:G], types[:G], A, B, C, fmtA, lmap, genl, geni, endian, stream=stream, indent=indent, backend=backend
639
+ )
640
+ isWarpRed = not endian and types[G:] == [] and (types[:G] == ["par", "red"] or types[:G] == ["par", "par", "red"])
641
+ reduc = isAtomicRed, isWarpRed, cd
642
+ indent += G * 2
643
+ _emit_loop_structure(
644
+ indices[G:], types[G:], A, B, C, fmtA, vtp, N, lmap, genl, geni, P, reduc, stream=stream, indent=indent, backend=backend
645
+ )
646
+ _emit_cleanup(G, C, vtp, reduc, isGrid=True, stream=stream, indent=indent, backend=backend)
647
+ indent -= G * 2
648
+
649
+ indent -= 2
650
+ print(f"{'':>{indent}}}}", file=stream)
651
+
652
+ assert all(i is None for i in lmap) # all consumed
653
+
654
+ return indices[:G], endian
655
+
656
+
657
+ def _emit_search_space(A, B, C, fmtA, ctp, vtp, itp, gtp, kernel=None, stream=stdout, backend=Backend.CUDA):
658
+ """Searches the state space of kernels for the given tensor index expression."""
659
+ Q = len(set(A) | set(B) | set(C))
660
+ L = fmtA.num_levels
661
+
662
+ def _filter_order(indices):
663
+ """Returns `True` when index order satisfies constraints."""
664
+ level = 0
665
+ for t, i in indices:
666
+ if t == Index.LEVEL or t == Index.LEVEL_A or t == Index.LEVEL_B:
667
+ if i < level:
668
+ return False
669
+ elif t == Index.LEVEL_A:
670
+ level = i
671
+ else:
672
+ level = i + 1
673
+ return True # all seen, no conflict
674
+
675
+ # Convert iteration space [i0,i1,...] into iteration space [l0,l1,..,free].
676
+ ftypes = ["par" if i in C else "red" for i in range(Q)]
677
+ levels = [(Index.LEVEL, i) for i in range(L)]
678
+ free = [(Index.FREE, i) for i in range(Q) if i not in A]
679
+ fmts = list(fmtA.levels.values())
680
+ llist = list(fmtA.levels)
681
+
682
+ # Generate all valid iteration space permutations.
683
+ count = 0
684
+ space1 = levels + free
685
+ for perm1 in permutations(space1):
686
+ indices1 = list(perm1)
687
+ if _filter_order(indices1):
688
+ maxg = _max_grid(indices1, fmts)
689
+ # Generate all legal 1D, 2D, 3D grid versions.
690
+ for G in range(1, maxg + 1):
691
+ E = [True] if (G == 1 or backend == Backend.OMP) else [True, False]
692
+ # Generate the version for this grid without thread coarsening.
693
+ for endian in E:
694
+ if count == kernel:
695
+ types1 = [
696
+ ftypes[i]
697
+ if t == Index.FREE or t == Index.FREE_A or t == Index.FREE_B
698
+ else _collapse_type(ftypes, A, fmtA, llist[i])
699
+ for t, i in indices1
700
+ ]
701
+ print(f"// UNCOARSENED KERNEL: K={kernel} G={G} E={endian}", file=stream)
702
+ return _emit_matmul_kernel(
703
+ indices1, types1, Q, G, A, B, C, fmtA, endian, ctp, vtp, itp, gtp, stream=stream, backend=backend
704
+ )
705
+ count += 1
706
+ # Construct valid thread coarsening of the level and free indices.
707
+ for gg in range(G):
708
+ if gg < G - 1 and all(t != Index.FREE for t, i in indices1[gg : G - 1]):
709
+ continue
710
+ prefix = []
711
+ suffix = []
712
+ for g in range(0, gg):
713
+ prefix.append(indices1[g])
714
+ for g in range(gg, G):
715
+ t, i = indices1[g]
716
+ if t == Index.LEVEL:
717
+ if g == G - 1:
718
+ prefix.append((Index.LEVEL_A, i))
719
+ suffix.append((Index.LEVEL_B, i))
720
+ else:
721
+ prefix.append((Index.LEVEL, i))
722
+ elif t == Index.FREE:
723
+ prefix.append((Index.FREE_A, i))
724
+ suffix.append((Index.FREE_B, i))
725
+ else:
726
+ raise AssertionError(f"Unexpected coarsening: {t}")
727
+ assert len(suffix) != 0
728
+ # Generate the other versions for this grid with thread coarsening.
729
+ space2 = suffix + indices1[G:]
730
+ for perm2 in permutations(space2):
731
+ indices2 = prefix + list(perm2)
732
+ if _filter_order(indices2):
733
+ for endian in E:
734
+ if count == kernel:
735
+ types2 = [
736
+ ftypes[i]
737
+ if t == Index.FREE or t == Index.FREE_A or t == Index.FREE_B
738
+ else _collapse_type(ftypes, A, fmtA, llist[i])
739
+ for t, i in indices2
740
+ ]
741
+ print(f"// COARSENED KERNEL : K={kernel} G={G} E={endian}", file=stream)
742
+ return _emit_matmul_kernel(
743
+ indices2,
744
+ types2,
745
+ Q,
746
+ G,
747
+ A,
748
+ B,
749
+ C,
750
+ fmtA,
751
+ endian,
752
+ ctp,
753
+ vtp,
754
+ itp,
755
+ gtp,
756
+ stream=stream,
757
+ backend=backend,
758
+ )
759
+ count += 1
760
+
761
+ assert kernel is None
762
+ return count
763
+
764
+
765
+ ##############
766
+ # POPULATION #
767
+ ##############
768
+
769
+
770
+ #
771
+ # NVIDIA RTX A6000
772
+ #
773
+ # Threads per warp : 32
774
+ # Max threads per block : 1024
775
+ # Max thread dimensions : (1024, 1024, 64)
776
+ # Max grid dimensions : (2147483647, 65535, 65535)
777
+ #
778
+ # thread dims == per axis maxima, and joint product cap 1024
779
+ # grid dims == per axis maxima, but no joint product cap
780
+ #
781
+ # NOTE
782
+ # grid ( gridDim.xyz ) -> blockIdx.xyz
783
+ # block ( blockDim.xyz ) -> threadIdx.xyz
784
+ #
785
+ def _compute_grid_block(grid_xyz, endian):
786
+ """Computes grid and block dimension for given grid.
787
+
788
+ Note that we always keep X a multiple of 32 to get full warps,
789
+ which means that X = min(x, 32) simply become X=32.
790
+ """
791
+ if len(grid_xyz) == 1:
792
+ x = grid_xyz[0] # any endian
793
+ # X = min(x, 1024) -> 1024 max
794
+ X = (min(x, 1024) + 31) // 32 * 32 # keep multiple of 32
795
+ grid_dim = ((x + X - 1) // X,)
796
+ block_dim = (X,)
797
+ elif len(grid_xyz) == 2:
798
+ x, y = (grid_xyz[0], grid_xyz[1]) if endian else (grid_xyz[1], grid_xyz[0])
799
+ # X, Y = min(x, 32), min(y, 32) -> 32*32=1024 max
800
+ X, Y = 32, min(y, 32)
801
+ grid_dim = ((x + X - 1) // X, (y + Y - 1) // Y)
802
+ block_dim = (X, Y)
803
+ else: # truncate to first three
804
+ x, y, z = (grid_xyz[0], grid_xyz[1], grid_xyz[2]) if endian else (grid_xyz[2], grid_xyz[1], grid_xyz[0])
805
+ # X, Y, Z = min(x, 32), min(y, 4), min(z, 8) -> 32*4*8=1024 max
806
+ X, Y, Z = 32, min(y, 4), min(z, 8)
807
+ grid_dim = ((x + X - 1) // X, (y + Y - 1) // Y, (z + Z - 1) // Z)
808
+ block_dim = (X, Y, Z)
809
+ # print(f"{grid_xyz} -> {grid_dim} {block_dim}")
810
+ return grid_dim, block_dim
811
+
812
+
813
+ def _max_pos_diff(pos_tensor, stream_holder):
814
+ pos_tensor = as_external_tensor(pos_tensor, stream_holder)
815
+ return (pos_tensor[1:] - pos_tensor[:-1]).max().item()
816
+
817
+
818
+ def _populate_grid(tensorA, ti_grid_iter, sizes, stream_holder):
819
+ """Populates grid x,y,z values given the tensor format and grid iteration."""
820
+ fmts = list(tensorA.tensor_format.levels.values())
821
+ grid_xyz = []
822
+ for t, i in ti_grid_iter:
823
+ if t == Index.FREE_A or t == Index.LEVEL_A:
824
+ grid_xyz.append(CYCLIC)
825
+ elif t == Index.FREE:
826
+ grid_xyz.append(sizes[i])
827
+ elif t == Index.LEVEL:
828
+ if _fmt(fmts[i]) == LevelFormat.DENSE or _fmt(fmts[i]) == LevelFormat.BATCH:
829
+ grid_xyz.append(tensorA.levels[i])
830
+ elif _fmt(fmts[i]) == LevelFormat.COMPRESSED:
831
+ pos_tensor = tensorA._pos[i].tensor
832
+ grid_xyz.append(_max_pos_diff(pos_tensor, stream_holder))
833
+ else:
834
+ raise AssertionError(f"Unknown level format for {t}: {_fmt(fmts[i])}")
835
+ else:
836
+ raise AssertionError(f"Unexpected index type: {t}")
837
+ return grid_xyz
838
+
839
+
840
+ def _populate_data_parameters(tensor):
841
+ """Populates parameters for given UST."""
842
+ params = []
843
+ for lvl, (_, fmt) in enumerate(tensor.tensor_format.levels.items()):
844
+ f = _fmt(fmt)
845
+ if f in (LevelFormat.DENSE, LevelFormat.BATCH, LevelFormat.RANGE):
846
+ pass
847
+ elif f in (LevelFormat.COMPRESSED, LevelFormat.DELTA):
848
+ params.append(tensor._pos.get(lvl).data_ptr)
849
+ params.append(tensor._crd.get(lvl).data_ptr)
850
+ elif f in (LevelFormat.SINGLETON, LevelFormat.STRUCTURED):
851
+ params.append(tensor._crd.get(lvl).data_ptr)
852
+ else:
853
+ raise AssertionError(f"Unknown level format: {f}")
854
+ params.append(tensor._val.data_ptr)
855
+ return params
856
+
857
+
858
+ #################
859
+ # ENTRY METHODS #
860
+ #################
861
+
862
+
863
+ def emit_apply_kernel(tensorA, with_indices):
864
+ """
865
+ Emits the kernel for an apply operation (with or without indices).
866
+
867
+ Returns source_code, grid_iter
868
+ """
869
+ stream = StringIO()
870
+ fmtA = tensorA.tensor_format
871
+ ctp = type_str(tensorA.dtype)
872
+ vtp = ctp
873
+ itp = type_str(tensorA.index_type)
874
+ gtp = "int"
875
+ indices = [(Index.LEVEL, i) for i in range(tensorA.num_levels)]
876
+ fmts = list(fmtA.levels.values())
877
+ G = _max_grid(indices, fmts) # always max, with endian=True
878
+ grid_iter = _emit_apply_kernel(indices, fmtA, True, ctp, vtp, itp, gtp, with_indices, G, stream=stream)
879
+ return stream.getvalue(), grid_iter
880
+
881
+
882
+ def populate_apply_parameters(tensorA, with_indices, grid_iter, stream_holder):
883
+ """
884
+ Populates the parameters for an apply operation (with or without indices).
885
+
886
+ Returns (grid_dim, block_dim), params
887
+ """
888
+ with stream_holder.ctx:
889
+ ti_grid_iter, endian = grid_iter
890
+ if with_indices:
891
+ # Set grid and parameters.
892
+ grid_xyz = _populate_grid(tensorA, ti_grid_iter, [], stream_holder)
893
+ params = _populate_data_parameters(tensorA)
894
+ for i in range(tensorA.num_dimensions):
895
+ params.append(tensorA.extents[i])
896
+ for i in range(tensorA.num_levels):
897
+ params.append(tensorA.levels[i])
898
+ else:
899
+ # Set trivial grid and parameters.
900
+ grid_xyz = [tensorA.nse]
901
+ params = [tensorA._val.data_ptr, tensorA.nse]
902
+ return _compute_grid_block(grid_xyz, endian), params
903
+
904
+
905
+ def emit_matmul_kernel(tensorA, tensorB, tensorC, ctp, transpose_a=False, transpose_b=False, kernel=0, backend=Backend.CUDA):
906
+ """
907
+ Emits the kernel for a generic C += AxB matmul operation.
908
+
909
+ Returns source_code, grid_iter
910
+ """
911
+ stream = StringIO()
912
+ vtp = type_str(tensorA.dtype)
913
+ itp = type_str(tensorA.index_type)
914
+ gtp = "int"
915
+
916
+ # Restrictions on dense formats of B and C.
917
+ if tensorB.tensor_format.name not in ["DenseVector", "DensedRight", "DensedLeft", "Dense3D-0-1-2", "Dense4D-0-1-2-3"]:
918
+ raise NotImplementedError(f"Unsupported format {tensorB.tensor_format.name} for B operand.")
919
+ if tensorC.tensor_format.name not in ["Scalar", "DenseVector", "DensedRight", "Dense3D-0-1-2", "Dense4D-0-1-2-3"]:
920
+ raise NotImplementedError(f"Unsupported format {tensorC.tensor_format.name} for C operand.")
921
+
922
+ # Construct tensor index expression.
923
+ A, B, C = None, None, None
924
+ if tensorA.num_dimensions == 0:
925
+ # A is a scalar.
926
+ raise NotImplementedError("Unsupported matmul scalar A")
927
+ elif tensorA.num_dimensions == 1:
928
+ # A is a vector.
929
+ if tensorB.num_dimensions == 1 and tensorC.num_dimensions == 0:
930
+ A, B, C = [0], [0], [] # DOT: c = a(i) b(i)
931
+ elif tensorB.num_dimensions == 2 and tensorC.num_dimensions == 1:
932
+ A, B, C = [0], [0, 1], [1] # VM: c(j) = a(i) B(i,j)
933
+ else:
934
+ # A is a matrix or tensor.
935
+ if tensorA.num_dimensions == 2 and tensorB.num_dimensions == 1 and tensorC.num_dimensions == 1:
936
+ A, B, C = [0, 1], [1], [0] # MV: c(i) = A(i,j) b(j)
937
+ elif (
938
+ tensorB.num_dimensions == 2 or tensorB.num_dimensions == tensorA.num_dimensions
939
+ ) and tensorC.num_dimensions == tensorA.num_dimensions:
940
+ bi = tensorA.num_dimensions - 2 # (B)M(B)M: C([bi*],i,k) = A([bi*],i,j) B({[bi*]},j,k)
941
+ prefix = list(range(bi))
942
+ prefixb = [] if tensorB.num_dimensions == 2 else prefix
943
+ A, B, C = (
944
+ prefix + [bi, bi + 1],
945
+ prefixb + [bi + 1, bi + 2],
946
+ prefix + [bi, bi + 2],
947
+ )
948
+
949
+ if A is None or B is None or C is None:
950
+ raise NotImplementedError("Unsupported matmul situation in sparse emitter")
951
+
952
+ # Transposition is on last two dimensions.
953
+ if tensorB.tensor_format.name == "DensedLeft":
954
+ transpose_b = not transpose_b
955
+ if transpose_a:
956
+ A[-2], A[-1] = A[-1], A[-2]
957
+ if transpose_b:
958
+ B[-2], B[-1] = B[-1], B[-2]
959
+
960
+ grid_iter = _emit_search_space(
961
+ A, B, C, tensorA.tensor_format, ctp, vtp, itp, gtp, kernel=kernel, stream=stream, backend=backend
962
+ )
963
+ return stream.getvalue(), grid_iter
964
+
965
+
966
+ def count_matmul_kernels(tensorA, tensorB, tensorC, ctp, transpose_a=False, transpose_b=False, backend=Backend.CUDA):
967
+ """Returns the number of kernels for a generic C += AxB matmul operation."""
968
+ _, G = emit_matmul_kernel(tensorA, tensorB, tensorC, ctp, transpose_a, transpose_b, kernel=None, backend=backend)
969
+ return G
970
+
971
+
972
+ def populate_matmul_parameters(
973
+ tensorA,
974
+ tensorB,
975
+ tensorC,
976
+ grid_iter,
977
+ stream_holder,
978
+ transpose_a=False,
979
+ transpose_b=False,
980
+ backend=Backend.CUDA,
981
+ ):
982
+ """
983
+ Populates the parameters for a generic C += AxB matmul operation.
984
+
985
+ Returns (grid_dim, block_dim), params
986
+ """
987
+ with stream_holder.ctx:
988
+ # Handle transposition. Because transposition can be either forced by caller
989
+ # or due to "Left" layout of B, make sure to test "levels", not "extents".
990
+ if tensorB.tensor_format.name == "DensedLeft":
991
+ transpose_b = not transpose_b
992
+ ai, aj = (1, 0) if transpose_a else (0, 1)
993
+ bi, bj = (1, 0) if transpose_b else (0, 1)
994
+ # Validate and extract sizes from tensor index expression.
995
+ if tensorA.num_dimensions == 1:
996
+ if tensorB.num_dimensions == 1 and tensorC.num_dimensions == 0:
997
+ assert tensorA.extents[0] == tensorB.extents[0]
998
+ sizes = [tensorA.extents[0]] # DOT: c = a(i) b(i)
999
+ else:
1000
+ assert tensorA.extents[0] == tensorB.levels[bi]
1001
+ assert tensorB.levels[bj] == tensorC.extents[0]
1002
+ sizes = [tensorA.extents[0], tensorC.extents[0]] # VM: c(j) = a(i) B(i,j)
1003
+ elif tensorA.num_dimensions == 2 and tensorB.num_dimensions == 1 and tensorC.num_dimensions == 1:
1004
+ assert tensorA.extents[ai] == tensorC.extents[0]
1005
+ assert tensorA.extents[aj] == tensorB.extents[0]
1006
+ sizes = [tensorA.extents[ai], tensorA.extents[aj]] # MV: c(i) = A(i,j) b(j)
1007
+ else:
1008
+ b = tensorA.num_dimensions - 2 # (B)M(B)M: C([bi*],i,k) = A([bi*],i,j) B({[bi*]},j,k)
1009
+ bb = 0 if tensorB.num_dimensions == 2 else b
1010
+ sizes = []
1011
+ for i in range(b):
1012
+ assert tensorA.extents[i] == tensorC.extents[i]
1013
+ assert bb == 0 or tensorA.extents[i] == tensorB.extents[i]
1014
+ sizes.append(tensorA.extents[i])
1015
+ assert tensorA.extents[b + ai] == tensorC.extents[b]
1016
+ assert tensorA.extents[b + aj] == tensorB.levels[bb + bi]
1017
+ assert tensorB.levels[bb + bj] == tensorC.extents[b + 1]
1018
+ sizes.append(tensorA.extents[b + ai])
1019
+ sizes.append(tensorA.extents[b + aj])
1020
+ sizes.append(tensorC.extents[b + 1])
1021
+ # Set grid and parameters.
1022
+ ti_grid_iter, endian = grid_iter
1023
+ grid_xyz = _populate_grid(tensorA, ti_grid_iter, sizes, stream_holder)
1024
+ params = _populate_data_parameters(tensorA)
1025
+ params.append(tensorB._val.data_ptr)
1026
+ params.append(tensorC._val.data_ptr)
1027
+ params.extend(sizes)
1028
+ for i in range(tensorA.num_levels):
1029
+ params.append(tensorA.levels[i])
1030
+ if backend == Backend.OMP:
1031
+ # For OMP, grid dimensions are passed as parameters
1032
+ params.extend(grid_xyz)
1033
+ return _compute_grid_block(grid_xyz, endian), params