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,2708 @@
1
+ # Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ __all__ = [
6
+ "Solver",
7
+ "CholeskySolver",
8
+ "LUSolver",
9
+ "LUPivotSolver",
10
+ "TriangularSolver",
11
+ "QRFactorize",
12
+ "LQFactorize",
13
+ "QRMultiply",
14
+ "LQMultiply",
15
+ "LeastSquaresSolver",
16
+ "compile_solver_execute",
17
+ ]
18
+
19
+ from collections.abc import Sequence
20
+ from functools import cached_property
21
+ from typing import Any, Literal
22
+
23
+ import numpy as np
24
+
25
+ from nvmath._utils import get_nvrtc_version
26
+ from nvmath.bindings import mathdx
27
+ from nvmath.internal.utils import docstring_decorator
28
+
29
+ from .common import SHARED_DEVICE_DOCSTRINGS, check_code_type, parse_code_type, parse_sm
30
+ from .common_backend import get_isa_version, get_lto
31
+ from .common_cuda import (
32
+ Code,
33
+ ComputeCapability,
34
+ )
35
+ from .cusolverdx_backend import (
36
+ _ENABLE_CUSOLVERDX_0_3,
37
+ ALLOWED_ARRANGEMENT,
38
+ ALLOWED_CUSOLVERDX_FUNCTIONS,
39
+ ALLOWED_DATA_TYPE,
40
+ ALLOWED_DIAG,
41
+ ALLOWED_EXECUTION,
42
+ ALLOWED_FILL_MODE,
43
+ ALLOWED_REAL_NP_TYPES,
44
+ ALLOWED_SIDE,
45
+ ALLOWED_TRANSPOSE_MODE,
46
+ CUSOLVERDX_0_3_ALLOWED_FUNCTIONS,
47
+ DIAG_SUPPORTED_FUNCTIONS,
48
+ FILL_MODE_SUPPORTED_FUNCTIONS,
49
+ JOB_SUPPORT_MAP,
50
+ JOB_SUPPORTED_FUNCTIONS,
51
+ SIDE_SUPPORTED_FUNCTIONS,
52
+ generate_code,
53
+ generate_SOLVER,
54
+ get_int_traits,
55
+ get_str_trait,
56
+ validate,
57
+ )
58
+ from .types import Complex, complex64, complex128
59
+
60
+ # ==========================
61
+ # docs
62
+ # ==========================
63
+
64
+ SOLVER_DOCSTRING_SIZE_BASE = """Problem size specified as a sequence of 1 to 3 elements:
65
+ ``(M,)`` (treated as ``(M, M, 1)``), ``(M, N)`` (treated as ``(M, N, 1)``), or ``(M, N, K)``.""".replace("\n", " ")
66
+
67
+ SOLVER_DOCSTRING = {
68
+ "function": f"""Solver function to be executed by the execute() method. List of available options:
69
+ {", ".join(f"``'{k}'`` ({v})" for k, v in ALLOWED_CUSOLVERDX_FUNCTIONS.items())}.
70
+ Functions {", ".join(f"``'{k}'``" for k in CUSOLVERDX_0_3_ALLOWED_FUNCTIONS)} require libmathdx 0.3.2 or later.""".replace(
71
+ "\n", " "
72
+ ),
73
+ #
74
+ "size": f"""{SOLVER_DOCSTRING_SIZE_BASE} Please refer to cuSOLVERDx functionalities for detailed meaning.
75
+ """.replace("\n", " "),
76
+ #
77
+ "arrangement": f"""Storage layout for matrices A and B, specified as a sequence of 1 or 2 elements ``(arr_A, arr_B)``.
78
+ Each element can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_ARRANGEMENT)}.
79
+ Defaults to ``("col_major", "col_major")``.""".replace("\n", " "),
80
+ #
81
+ "transpose_mode": f"""Transpose mode of matrix A. Refer to cuSOLVERDx documentation,
82
+ as some functions do not support this option.
83
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_TRANSPOSE_MODE)}.
84
+ Defaults to ``'non_transposed'``.""".replace("\n", " "),
85
+ #
86
+ "side": f"""Side of matrix A in a multiplication operation.
87
+ Required and supported only by functions: {", ".join(f"``'{v}'``" for v in SIDE_SUPPORTED_FUNCTIONS)}.
88
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_SIDE)}.""".replace("\n", " "),
89
+ #
90
+ "diag": f"""Indicates whether the diagonal elements of matrix A are ones or not.
91
+ Required and supported only by {", ".join(f"``'{v}'``" for v in DIAG_SUPPORTED_FUNCTIONS)} functions.
92
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_DIAG)}.""".replace("\n", " "),
93
+ #
94
+ "fill_mode": f"""Indicates which part of matrix A is filled and should be used by function.
95
+ Required and supported only by functions: {", ".join(f"``'{v}'``" for v in FILL_MODE_SUPPORTED_FUNCTIONS)}.
96
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_FILL_MODE)}.""".replace("\n", " "),
97
+ #
98
+ "job": f"""Job type for eigenvalue computation.
99
+ Required and supported only by functions: {", ".join(f"``'{v}'``" for v in JOB_SUPPORTED_FUNCTIONS)}.
100
+ For ``'htev'``: {", ".join(f"``'{v}'``" for v in JOB_SUPPORT_MAP["htev"])}.
101
+ For ``'heev'``: {", ".join(f"``'{v}'``" for v in JOB_SUPPORT_MAP["heev"])}.
102
+ Requires libmathdx 0.3.2 or later.""".replace("\n", " "),
103
+ #
104
+ "batches_per_block": """Number of batches to compute in parallel in a single CUDA block.
105
+ Can be a positive integer or the string ``'suggested'`` for automatic selection of an optimal value.
106
+ We recommend using 1 for matrix A size larger than or equal to 16 x 16,
107
+ and using ``'suggested'`` for smaller sizes to achieve optimal performance.
108
+ Defaults to 1.""".replace("\n", " "),
109
+ #
110
+ "data_type": f"""The data type of the input matrices,
111
+ can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_DATA_TYPE)}.
112
+ Defaults to ``'real'``.""".replace("\n", " "),
113
+ #
114
+ "leading_dimensions": """The leading dimensions for input matrices A and B,
115
+ specified as a sequence of 1 or 2 elements (``lda``, ``ldb``) or ``None``.
116
+ If not provided, it will be automatically deduced from ``size`` and ``arrangement``.
117
+ """.replace("\n", " "),
118
+ #
119
+ "block_dim": """The block dimension for launching the CUDA kernel,
120
+ specified as a 1 to 3 integer sequence (x, y, z) where missing dimensions are assumed to be 1.
121
+ Can be a sequence of 1 to 3 positive integers, the string ``'suggested'``
122
+ for optimal value selection, or ``None`` for the default value.""".replace("\n", " "),
123
+ #
124
+ "execution": f"""A string specifying the execution method.
125
+ Supported values: {", ".join(f"``'{v}'``" for v in ALLOWED_EXECUTION)}.""".replace("\n", " "),
126
+ #
127
+ "precision": f"""The computation precision specified as a numpy float dtype.
128
+ Currently supports: {", ".join(f"``numpy.{v.__name__}``" for v in ALLOWED_REAL_NP_TYPES)}.""".replace("\n", " "),
129
+ #
130
+ "sm": SHARED_DEVICE_DOCSTRINGS["sm"],
131
+ }
132
+
133
+ # ==========================
134
+ # Solver class
135
+ # ==========================
136
+
137
+
138
+ @docstring_decorator(SOLVER_DOCSTRING, skip_missing=False)
139
+ class Solver:
140
+ """
141
+ A class that encapsulates a partial dense matrix factorization and solve
142
+ device function.
143
+
144
+ **Memory Layout Requirements:**
145
+
146
+ Matrices must be stored in shared memory according
147
+ to their arrangement and leading dimension (ld):
148
+
149
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, rows, cols)``
150
+ with strides ``(ld * cols, 1, ld)``
151
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, rows, cols)``
152
+ with strides ``(ld * rows, ld, 1)``
153
+
154
+ Args:
155
+ function (str): {function}
156
+
157
+ size (Sequence[int]): {size}
158
+
159
+ precision (type[np.floating]): {precision}
160
+
161
+ execution (str): {execution}
162
+
163
+ sm (ComputeCapability): {sm}
164
+
165
+ arrangement (Sequence[str], optional): {arrangement}
166
+
167
+ transpose_mode (str, optional): {transpose_mode}
168
+
169
+ side (str, optional): {side}
170
+
171
+ diag (str, optional): {diag}
172
+
173
+ fill_mode (str, optional): {fill_mode}
174
+
175
+ batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
176
+
177
+ data_type (str, optional): {data_type}
178
+
179
+ leading_dimensions (Sequence[int], optional): {leading_dimensions}
180
+
181
+ block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
182
+
183
+ job (str, optional): {job}
184
+
185
+ See Also:
186
+ The attributes of this class provide a 1:1
187
+ mapping with the CUDA C++ cuSOLVERDx APIs.
188
+ For further details, please refer to :cusolverdx_doc:`cuSOLVERDx documentation
189
+ <index.html>`.
190
+ """
191
+
192
+ def __init__(
193
+ self,
194
+ function: str,
195
+ size: Sequence[int],
196
+ precision: type[np.floating],
197
+ execution: str,
198
+ *,
199
+ sm=None,
200
+ arrangement: Sequence[str] | None = None,
201
+ transpose_mode: str | None = None,
202
+ side: str | None = None,
203
+ diag: str | None = None,
204
+ fill_mode: str | None = None,
205
+ batches_per_block: int | Literal["suggested"] | None = None,
206
+ data_type: str | None = None,
207
+ leading_dimensions: Sequence[int] | None = None,
208
+ block_dim: Sequence[int] | Literal["suggested"] | None = None,
209
+ job: str | None = None,
210
+ ):
211
+ if get_nvrtc_version() < (12, 6, 85):
212
+ raise RuntimeError("cuSOLVERDx requires CUDA Toolkit 12.6 Update 3 or later.")
213
+
214
+ sm = parse_sm(sm)
215
+
216
+ validate(
217
+ function=function,
218
+ size=size,
219
+ precision=precision,
220
+ execution=execution,
221
+ sm=sm,
222
+ arrangement=arrangement,
223
+ transpose_mode=transpose_mode,
224
+ side=side,
225
+ diag=diag,
226
+ fill_mode=fill_mode,
227
+ batches_per_block=batches_per_block,
228
+ data_type=data_type,
229
+ leading_dimensions=leading_dimensions,
230
+ block_dim=block_dim,
231
+ job=job,
232
+ )
233
+
234
+ if len(size) == 1:
235
+ size = (size[0], size[0], 1)
236
+ elif len(size) == 2:
237
+ size = (size[0], size[1], 1)
238
+ else:
239
+ size = (size[0], size[1], size[2])
240
+
241
+ if block_dim is not None and block_dim != "suggested":
242
+ if len(block_dim) == 1:
243
+ block_dim = (block_dim[0], 1, 1)
244
+ elif len(block_dim) == 2:
245
+ block_dim = (block_dim[0], block_dim[1], 1)
246
+ else:
247
+ block_dim = (block_dim[0], block_dim[1], block_dim[2])
248
+
249
+ self._function = function
250
+ self._size = size
251
+ self._precision = precision
252
+ self._execution = execution
253
+ self._sm = sm
254
+ self._arrangement = tuple(arrangement) if arrangement is not None else None
255
+ self._transpose_mode = transpose_mode
256
+ self._side = side
257
+ self._diag = diag
258
+ self._fill_mode = fill_mode
259
+ self._data_type = data_type
260
+ self._job = job
261
+
262
+ self._batches_per_block = batches_per_block if batches_per_block != "suggested" else None
263
+ self._leading_dimensions = tuple(leading_dimensions) if leading_dimensions is not None else None
264
+ self._block_dim = block_dim if block_dim != "suggested" and block_dim is not None else None
265
+
266
+ if batches_per_block == "suggested" or block_dim == "suggested":
267
+ # Update suggested fields
268
+ traits = _SolverTraits(self)
269
+
270
+ if batches_per_block == "suggested":
271
+ self._batches_per_block = traits.suggested_batches_per_block
272
+
273
+ if block_dim == "suggested":
274
+ self._block_dim = traits.suggested_block_dim
275
+
276
+ # ==========================
277
+ # Operator properties
278
+ # ==========================
279
+
280
+ @property
281
+ def function(self) -> str:
282
+ return self._function
283
+
284
+ @property
285
+ def size(self) -> tuple[int, int, int]:
286
+ return self._size
287
+
288
+ @property
289
+ def m(self) -> int:
290
+ m, _, _ = self.size
291
+ return m
292
+
293
+ @property
294
+ def n(self) -> int:
295
+ _, n, _ = self.size
296
+ return n
297
+
298
+ @property
299
+ def k(self) -> int:
300
+ _, _, k = self.size
301
+ return k
302
+
303
+ @property
304
+ def precision(self) -> type[np.floating]:
305
+ return self._precision
306
+
307
+ @property
308
+ def execution(self) -> str:
309
+ return self._execution
310
+
311
+ @property
312
+ def sm(self) -> ComputeCapability:
313
+ return self._sm
314
+
315
+ @property
316
+ def arrangement(self) -> Sequence[str]:
317
+ if self._arrangement is None:
318
+ # TODO: replace with libmathdx trait when supported
319
+ return ("col_major", "col_major")
320
+ if len(self._arrangement) == 1:
321
+ return (self._arrangement[0], "col_major")
322
+ return self._arrangement
323
+
324
+ @property
325
+ def a_arrangement(self) -> str:
326
+ arr_a, _ = self.arrangement
327
+ return arr_a
328
+
329
+ @property
330
+ def b_arrangement(self) -> str:
331
+ _, arr_b = self.arrangement
332
+ return arr_b
333
+
334
+ @property
335
+ def transpose_mode(self) -> str | None:
336
+ return self._transpose_mode
337
+
338
+ @property
339
+ def side(self) -> str | None:
340
+ return self._side
341
+
342
+ @property
343
+ def diag(self) -> str | None:
344
+ return self._diag
345
+
346
+ @property
347
+ def fill_mode(self) -> str | None:
348
+ return self._fill_mode
349
+
350
+ @property
351
+ def batches_per_block(self) -> int:
352
+ if self._batches_per_block is None:
353
+ # TODO: replace with libmathdx trait when supported
354
+ return 1
355
+ return self._batches_per_block
356
+
357
+ @property
358
+ def data_type(self) -> str:
359
+ if self._data_type is None:
360
+ # TODO: replace with libmathdx trait when supported
361
+ return "real"
362
+ return self._data_type
363
+
364
+ @property
365
+ def leading_dimensions(self) -> tuple:
366
+ if self._leading_dimensions is None:
367
+ # TODO: replace with libmathdx trait when supported
368
+ return self._calculate_default_leading_dimensions()
369
+
370
+ if len(self._leading_dimensions) == 1:
371
+ return (self._leading_dimensions[0], self._calculate_default_leading_dimensions()[1])
372
+ return self._leading_dimensions
373
+
374
+ @property
375
+ def lda(self) -> int:
376
+ lda, _ = self.leading_dimensions
377
+ return lda
378
+
379
+ @property
380
+ def ldb(self) -> int:
381
+ _, ldb = self.leading_dimensions
382
+ return ldb
383
+
384
+ @property
385
+ def block_dim(self) -> tuple:
386
+ if self._block_dim is None:
387
+ return self._traits.block_dim
388
+ return self._block_dim
389
+
390
+ @property
391
+ def block_size(self) -> int:
392
+ return self.block_dim[0] * self.block_dim[1] * self.block_dim[2]
393
+
394
+ @property
395
+ def job(self) -> str | None:
396
+ return self._job
397
+
398
+ # ==========================
399
+ # Trait properties
400
+ # ==========================
401
+
402
+ @cached_property
403
+ def _traits(self):
404
+ return _SolverTraits(self)
405
+
406
+ @property
407
+ def workspace_size(self) -> int:
408
+ """Workspace size in elements of :attr:`value_type`, not in bytes."""
409
+ if not _ENABLE_CUSOLVERDX_0_3:
410
+ raise RuntimeError("workspace size requires libmathdx 0.3.2 or later")
411
+
412
+ return self._traits.workspace_size
413
+
414
+ @property
415
+ def value_type(self) -> type[np.floating] | Complex:
416
+ if self.data_type == "complex":
417
+ return complex64 if self.precision == np.float32 else complex128
418
+ return self.precision
419
+
420
+ @property
421
+ def info_type(self) -> type[np.signedinteger]:
422
+ return np.int32
423
+
424
+ @property
425
+ def ipiv_type(self) -> type[np.signedinteger]:
426
+ return np.int32
427
+
428
+ @property
429
+ def tau_type(self) -> type[np.floating] | Complex:
430
+ return self.value_type
431
+
432
+ # ==========================
433
+ # execute()
434
+ # ==========================
435
+
436
+ def execute(*args):
437
+ raise RuntimeError("execute should not be called directly outside of a jitted kernel.")
438
+
439
+ # ==========================
440
+ # Private methods
441
+ # ==========================
442
+
443
+ def _generate_SOLVER(self, execution_api):
444
+ return generate_SOLVER(
445
+ function=self._function,
446
+ size=self._size,
447
+ precision=self._precision,
448
+ execution=self._execution,
449
+ sm=self._sm,
450
+ arrangement=self.arrangement, # TODO: remove property when libmathdx supports traits
451
+ transpose_mode=self._transpose_mode,
452
+ side=self._side,
453
+ diag=self._diag,
454
+ fill_mode=self._fill_mode,
455
+ batches_per_block=self.batches_per_block, # TODO: remove property when libmathdx supports traits
456
+ data_type=self.data_type, # TODO: remove property when libmathdx supports traits
457
+ leading_dimensions=self.leading_dimensions, # TODO: remove property when libmathdx supports traits
458
+ block_dim=self._block_dim,
459
+ execution_api=execution_api,
460
+ job=self._job,
461
+ )
462
+
463
+ def _calculate_default_leading_dimensions(self) -> tuple:
464
+ """
465
+ Calculates default leading dimensions based on problem
466
+ size (m, n, k) for different solver functions.
467
+ """
468
+
469
+ def calculate_ld(rows, cols, arr):
470
+ return rows if arr == "col_major" else cols
471
+
472
+ arr_a, arr_b = self.arrangement
473
+ m, n, k = self.size
474
+
475
+ match self.function:
476
+ case (
477
+ "potrf"
478
+ | "potrs"
479
+ | "posv"
480
+ | "getrf_no_pivot"
481
+ | "getrs_no_pivot"
482
+ | "gesv_no_pivot"
483
+ | "getrf_partial_pivot"
484
+ | "getrs_partial_pivot"
485
+ | "gesv_partial_pivot"
486
+ ):
487
+ return (calculate_ld(m, n, arr_a), calculate_ld(n, k, arr_b))
488
+ case "trsm":
489
+ assert self.side is not None
490
+ return (m if self.side == "left" else n, calculate_ld(m, n, arr_b))
491
+ case "geqrf" | "gelqf" | "htev" | "unglq" | "ungqr" | "heev":
492
+ return (calculate_ld(m, n, arr_a), 0)
493
+ case "unmqr":
494
+ return (calculate_ld(m if self.side == "left" else n, k, arr_a), calculate_ld(m, n, arr_b))
495
+ case "unmlq":
496
+ return (calculate_ld(k, m if self.side == "left" else n, arr_a), calculate_ld(m, n, arr_b))
497
+ case "gels":
498
+ return (calculate_ld(m, n, arr_a), calculate_ld(max(m, n), k, arr_b)) # mirrors cusolverdx behaviour
499
+ case "gtsv_no_pivot":
500
+ return (0, calculate_ld(n, k, arr_b))
501
+ case _:
502
+ raise NotImplementedError(f"Function {self.function} is not supported for leading dimension calculation")
503
+
504
+
505
+ # ==========================
506
+ # Internal traits class
507
+ # ==========================
508
+
509
+
510
+ class _SolverTraits:
511
+ def __init__(self, solver: Solver):
512
+ h = solver._generate_SOLVER("compiled_leading_dim")
513
+
514
+ try:
515
+ self.suggested_batches_per_block = int(
516
+ mathdx.cusolverdx_get_trait_int64(h.descriptor, mathdx.CusolverdxTraitType.SUGGESTED_BATCHES_PER_BLOCK)
517
+ )
518
+
519
+ self.suggested_block_dim = tuple(get_int_traits(h.descriptor, mathdx.CusolverdxTraitType.SUGGESTED_BLOCK_DIM, 3))
520
+ self.block_dim = tuple(get_int_traits(h.descriptor, mathdx.CusolverdxTraitType.BLOCK_DIM, 3))
521
+
522
+ if _ENABLE_CUSOLVERDX_0_3:
523
+ self.workspace_size = int(
524
+ mathdx.cusolverdx_get_trait_int64(h.descriptor, mathdx.CusolverdxTraitType.WORKSPACE_SIZE)
525
+ )
526
+
527
+ except mathdx.LibMathDxError as e:
528
+ raise RuntimeError(
529
+ "Failed to compile the solver. This may indicate incompatible "
530
+ f"parameter combination. Please refer to the cuSOLVERDx documentation. Details: {e}"
531
+ ) from e
532
+
533
+
534
+ # ==========================
535
+ # Compile function
536
+ # ==========================
537
+
538
+
539
+ def compile_solver_execute(
540
+ solver: Solver,
541
+ code_type: Any,
542
+ execution_api: str,
543
+ ) -> tuple[Code, str]:
544
+ """
545
+ Compiles the solver device function to LTO IR for the given code type.
546
+
547
+ Args:
548
+ solver (Solver): The solver device function to compile.
549
+
550
+ code_type (CodeType): The target code type, an instance of
551
+ :py:class:`nvmath.device.CodeType` or a compatible 2-tuple.
552
+
553
+ execution_api (str): The execution API, ``'compiled_leading_dim'`` or
554
+ ``'runtime_leading_dim'``.
555
+
556
+ Returns:
557
+ A tuple of the compiled :py:class:`nvmath.device.Code` and the mangled symbol name
558
+ of the device function.
559
+ """
560
+ code_type = parse_code_type(code_type)
561
+ check_code_type(code_type, "cuSOLVERDx")
562
+
563
+ h = solver._generate_SOLVER(execution_api).descriptor
564
+
565
+ try:
566
+ code = generate_code(h, code_type.cc)
567
+ except mathdx.LibMathDxError as e:
568
+ raise RuntimeError(
569
+ "Failed to compile the solver. This may indicate incompatible "
570
+ f"parameter combination. Please refer to the cuSOLVERDx documentation. Details: {e}"
571
+ ) from e
572
+
573
+ lto = get_lto(code.descriptor)
574
+ isa_version = get_isa_version(code.descriptor)
575
+ symbol = get_str_trait(h, mathdx.CusolverdxTraitType.SYMBOL_NAME)
576
+
577
+ return Code(code_type, isa_version, lto), symbol
578
+
579
+
580
+ # ==========================
581
+ # Pythonic Adapters
582
+ # ==========================
583
+
584
+
585
+ def _calculate_strides(shape: tuple[int, int], ld: int, arrangement: str) -> tuple[int, int, int]:
586
+ return (ld * shape[1], 1, ld) if arrangement == "col_major" else (ld * shape[0], ld, 1)
587
+
588
+
589
+ class _SolverProperties:
590
+ def __init__(self, source: Solver):
591
+ self._properties_source = source
592
+
593
+ @property
594
+ def size(self) -> tuple[int, int, int]:
595
+ return self._properties_source.size
596
+
597
+ @property
598
+ def m(self) -> int:
599
+ return self._properties_source.m
600
+
601
+ @property
602
+ def n(self) -> int:
603
+ return self._properties_source.n
604
+
605
+ @property
606
+ def k(self) -> int:
607
+ return self._properties_source.k
608
+
609
+ @property
610
+ def precision(self) -> type[np.floating]:
611
+ return self._properties_source.precision
612
+
613
+ @property
614
+ def execution(self) -> str:
615
+ return self._properties_source.execution
616
+
617
+ @property
618
+ def sm(self) -> ComputeCapability:
619
+ return self._properties_source.sm
620
+
621
+ @property
622
+ def arrangement(self) -> Sequence[str]:
623
+ return self._properties_source.arrangement
624
+
625
+ @property
626
+ def a_arrangement(self) -> str:
627
+ return self._properties_source.a_arrangement
628
+
629
+ @property
630
+ def b_arrangement(self) -> str:
631
+ return self._properties_source.b_arrangement
632
+
633
+ @property
634
+ def batches_per_block(self) -> int:
635
+ return self._properties_source.batches_per_block
636
+
637
+ @property
638
+ def data_type(self) -> str:
639
+ return self._properties_source.data_type
640
+
641
+ @property
642
+ def leading_dimensions(self) -> tuple:
643
+ return self._properties_source.leading_dimensions
644
+
645
+ @property
646
+ def lda(self) -> int:
647
+ return self._properties_source.lda
648
+
649
+ @property
650
+ def ldb(self) -> int:
651
+ return self._properties_source.ldb
652
+
653
+ @property
654
+ def block_dim(self) -> tuple:
655
+ return self._properties_source.block_dim
656
+
657
+ @property
658
+ def block_size(self) -> int:
659
+ return self._properties_source.block_size
660
+
661
+ @property
662
+ def value_type(self) -> type[np.floating] | Complex:
663
+ return self._properties_source.value_type
664
+
665
+
666
+ class _LinearSolverProperties(_SolverProperties):
667
+ def __init__(self, source: Solver):
668
+ super().__init__(source)
669
+
670
+ @property
671
+ def info_type(self) -> type[np.signedinteger]:
672
+ return self._properties_source.info_type
673
+
674
+ @property
675
+ def info_shape(self) -> tuple[int]:
676
+ return (self.batches_per_block,)
677
+
678
+ @property
679
+ def info_strides(self) -> tuple[int]:
680
+ return (1,)
681
+
682
+ @property
683
+ def a_shape(self) -> tuple[int, int, int]:
684
+ return (self.batches_per_block, self.m, self.n)
685
+
686
+ @property
687
+ def b_shape(self) -> tuple[int, int, int]:
688
+ return (self.batches_per_block, self.n, self.k)
689
+
690
+ def a_strides(self, *, lda: int | None = None) -> tuple[int, int, int]:
691
+ lda = self.lda if lda is None else lda
692
+ return _calculate_strides(self.a_shape[1:], lda, self.a_arrangement)
693
+
694
+ def b_strides(self, *, ldb: int | None = None) -> tuple[int, int, int]:
695
+ ldb = self.ldb if ldb is None else ldb
696
+ return _calculate_strides(self.b_shape[1:], ldb, self.b_arrangement)
697
+
698
+ def a_size(self, *, lda: int | None = None) -> int:
699
+ return self.a_strides(lda=lda)[0] * self.a_shape[0]
700
+
701
+ def b_size(self, *, ldb: int | None = None) -> int:
702
+ return self.b_strides(ldb=ldb)[0] * self.b_shape[0]
703
+
704
+
705
+ # ==========================
706
+ # Cholesky Solver
707
+ # ==========================
708
+
709
+ ADAPTERS_API_LD_DOCSTRING = """
710
+ Note: When provided in the constructor, leading dimensions are set at compile-time.
711
+ To use runtime leading dimensions (avoiding recompilation for different leading dimensions),
712
+ provide the leading dimension parameters directly to the device methods instead.
713
+ """.replace("\n", " ")
714
+
715
+ CHOLESKY_SOLVER_DOCSTRING = SOLVER_DOCSTRING.copy()
716
+ del CHOLESKY_SOLVER_DOCSTRING["function"]
717
+ del CHOLESKY_SOLVER_DOCSTRING["transpose_mode"]
718
+ del CHOLESKY_SOLVER_DOCSTRING["side"]
719
+ del CHOLESKY_SOLVER_DOCSTRING["diag"]
720
+ del CHOLESKY_SOLVER_DOCSTRING["job"]
721
+
722
+ CHOLESKY_SOLVER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
723
+ ``M`` represents the dimension of the square matrix A (``M`` x ``M``) used in factorization, ``N`` must be equal to ``M``.
724
+ ``K`` represents the number of columns in the right-hand side matrix B
725
+ (dimensions ``M`` x ``K``) for the solve operation.""".replace("\n", " ")
726
+ CHOLESKY_SOLVER_DOCSTRING["leading_dimensions"] = SOLVER_DOCSTRING["leading_dimensions"] + ADAPTERS_API_LD_DOCSTRING
727
+ CHOLESKY_SOLVER_DOCSTRING["fill_mode"] = f"""Indicates which part of matrix A is filled and should be used by function.
728
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_FILL_MODE)}.""".replace("\n", " ")
729
+
730
+
731
+ @docstring_decorator(CHOLESKY_SOLVER_DOCSTRING, skip_missing=False)
732
+ class CholeskySolver(_LinearSolverProperties):
733
+ """
734
+ A class that encapsulates Cholesky factorization and solve device functions
735
+ for Hermitian positive definite matrices.
736
+
737
+ **Available operations:**
738
+
739
+ * factorize: Computes the Cholesky factorization
740
+ A = L @ L^H (lower) or A = U^H @ U (upper),
741
+ where L is a lower triangular matrix and U is an upper triangular matrix.
742
+ The choice depends on the fill_mode parameter.
743
+ * solve: Solves the system Ax = B using a previously computed Cholesky factorization
744
+
745
+ **Memory Layout Requirements:**
746
+
747
+ Matrices must be stored in shared memory according
748
+ to their arrangement and leading dimension (ld):
749
+
750
+ **For matrix A (M x N):**
751
+
752
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
753
+ with strides ``(lda * N, 1, lda)``
754
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
755
+ with strides ``(lda * M, lda, 1)``
756
+
757
+ **For matrix B (N x K):**
758
+
759
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, N, K)``
760
+ with strides ``(ldb * K, 1, ldb)``
761
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, N, K)``
762
+ with strides ``(ldb * N, ldb, 1)``
763
+
764
+ Args:
765
+ size (Sequence[int]): {size}
766
+
767
+ precision (type[np.floating]): {precision}
768
+
769
+ execution (str): {execution}
770
+
771
+ fill_mode (str): {fill_mode}
772
+
773
+ sm (ComputeCapability): {sm}
774
+
775
+ arrangement (Sequence[str], optional): {arrangement}
776
+
777
+ batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
778
+
779
+ data_type (str, optional): {data_type}
780
+
781
+ leading_dimensions (Sequence[int], optional): {leading_dimensions}
782
+
783
+ block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
784
+
785
+ See Also:
786
+ For further details, please refer to the cuSOLVERDx documentation:
787
+
788
+ * :cusolverdx_doc:`factorize <get_started/functions/potrf.html>`
789
+ * :cusolverdx_doc:`solve <get_started/functions/potrs.html>`
790
+ """
791
+
792
+ # ==========================
793
+ # Constructor
794
+ # ==========================
795
+
796
+ def __init__(
797
+ self,
798
+ size: Sequence[int],
799
+ precision: type[np.floating],
800
+ execution: str,
801
+ fill_mode: str,
802
+ *,
803
+ sm=None,
804
+ arrangement: Sequence[str] | None = None,
805
+ batches_per_block: int | Literal["suggested"] | None = None,
806
+ data_type: str | None = None,
807
+ leading_dimensions: Sequence[int] | None = None,
808
+ block_dim: Sequence[int] | Literal["suggested"] | None = None,
809
+ ):
810
+ if fill_mode is None:
811
+ raise ValueError("fill_mode must be provided for CholeskySolver")
812
+
813
+ def construct(function):
814
+ return Solver(
815
+ function=function,
816
+ size=size,
817
+ precision=precision,
818
+ execution=execution,
819
+ sm=sm,
820
+ arrangement=arrangement,
821
+ fill_mode=fill_mode,
822
+ batches_per_block=batches_per_block,
823
+ data_type=data_type,
824
+ leading_dimensions=leading_dimensions,
825
+ block_dim=block_dim,
826
+ )
827
+
828
+ self._factorize = construct("potrf")
829
+ self._solve = construct("potrs")
830
+
831
+ super().__init__(self._solve)
832
+
833
+ if self.m != self.n:
834
+ raise ValueError("A must be a square matrix for CholeskySolver.")
835
+
836
+ # ==========================
837
+ # Property methods
838
+ # ==========================
839
+
840
+ @property
841
+ def fill_mode(self) -> str:
842
+ return self._solve.fill_mode
843
+
844
+ # ==========================
845
+ # Device function methods
846
+ # ==========================
847
+
848
+ def factorize(self, a, info, lda=None) -> None:
849
+ """
850
+ Computes the Cholesky factorization of a Hermitian positive definite matrix A.
851
+
852
+ This device function computes A = L @ L^H (if fill_mode = ``'lower'``)
853
+ or A = U^H @ U (if fill_mode = ``'upper'``). Uses cuSOLVERDx ``'potrf'``.
854
+
855
+ If ``lda`` is provided, uses runtime version with the specified
856
+ leading dimension. If ``lda`` is not provided (``None``),
857
+ uses compile-time version with
858
+ default or constructor-provided leading dimensions.
859
+
860
+ For more details, see: :cusolverdx_doc:`get_started/functions/potrf.html`
861
+
862
+ Args:
863
+ a: Pointer to an array in shared memory, storing
864
+ the matrix according to the specified
865
+ arrangement and leading dimension (see :class:`CholeskySolver`).
866
+ On entry, contains the Hermitian positive definite matrix.
867
+ On exit, contains the triangular factor L (lower) or U (upper).
868
+ info: Pointer to a 1D array of ``int32``. On exit, ``info[batch_id] = 0``
869
+ indicates success for that batch, ``info[batch_id] != 0``
870
+ indicates the matrix is not positive definite.
871
+ lda: Optional runtime leading dimension of matrix A.
872
+ If not specified, the compile-time ``lda`` is used.
873
+ """
874
+ raise RuntimeError("factorize should not be called directly outside of a jitted kernel.")
875
+
876
+ def solve(self, a, b, lda=None, ldb=None) -> None:
877
+ """
878
+ Solves a system of linear equations Ax = B using the Cholesky factorization.
879
+
880
+ This device function uses the previously computed
881
+ factorization A = L @ L^H (lower) or A = U^H @ U (upper)
882
+ to solve the system. Uses cuSOLVERDx ``'potrs'``.
883
+
884
+ If ``lda`` and ``ldb`` are provided, uses
885
+ runtime version with the specified leading dimensions.
886
+ If not provided (``None``), uses
887
+ compile-time version with default or constructor-provided leading dimensions.
888
+
889
+ For more details, see: :cusolverdx_doc:`get_started/functions/potrs.html`
890
+
891
+ Args:
892
+ a: Pointer to an array in shared memory, storing
893
+ the triangular factor L (lower) or U (upper)
894
+ from the Cholesky factorization, according
895
+ to the specified arrangement
896
+ and leading dimension (see :class:`CholeskySolver`).
897
+ b: Pointer to an array in shared memory, storing the
898
+ matrix according to the specified
899
+ arrangement and leading dimension (see :class:`CholeskySolver`).
900
+ The matrix is overwritten in place with the solution matrix x.
901
+ lda: Optional runtime leading dimension of matrix A.
902
+ The ``lda`` and ``ldb`` must be specified together.
903
+ If not specified, the compile-time ``lda`` is used.
904
+ ldb: Optional runtime leading dimension of matrix B.
905
+ The ``lda`` and ``ldb`` must be specified together.
906
+ If not specified, the compile-time ``ldb`` is used.
907
+ """
908
+ raise RuntimeError("solve should not be called directly outside of a jitted kernel.")
909
+
910
+
911
+ # ==========================
912
+ # LU Solver
913
+ # ==========================
914
+
915
+ LU_SOLVER_DOCSTRING = SOLVER_DOCSTRING.copy()
916
+ del LU_SOLVER_DOCSTRING["function"]
917
+ del LU_SOLVER_DOCSTRING["fill_mode"]
918
+ del LU_SOLVER_DOCSTRING["side"]
919
+ del LU_SOLVER_DOCSTRING["diag"]
920
+ del LU_SOLVER_DOCSTRING["job"]
921
+
922
+ LU_SOLVER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
923
+ ``M`` and ``N`` represent the dimensions of the matrix A used in factorization.
924
+ ``K`` represents the number of columns in the right-hand side matrix B (dimensions ``N`` x ``K``) for the ``solve`` operation.
925
+ To use :meth:`solve`, ``N`` must be equal to ``M``,
926
+ otherwise an exception will be thrown when ``solver.solve()`` is used.""".replace("\n", " ")
927
+ LU_SOLVER_DOCSTRING["leading_dimensions"] = SOLVER_DOCSTRING["leading_dimensions"] + ADAPTERS_API_LD_DOCSTRING
928
+ LU_SOLVER_DOCSTRING["transpose_mode"] = f"""Transpose mode of matrix A for the solve operation.
929
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_TRANSPOSE_MODE)}.
930
+ Defaults to ``'non_transposed'``.""".replace("\n", " ")
931
+
932
+
933
+ @docstring_decorator(LU_SOLVER_DOCSTRING, skip_missing=False)
934
+ class LUSolver(_LinearSolverProperties):
935
+ """
936
+ A class that encapsulates cuSOLVERDx LU factorization without pivoting
937
+ and linear solver for general matrices.
938
+
939
+ **Available operations:**
940
+
941
+ * factorize: Computes the LU factorization A = L @ U,
942
+ where L is a unit lower triangular matrix and U is an upper triangular matrix.
943
+ * solve: Solves the system Ax = B using a previously computed LU factorization.
944
+
945
+ **Memory Layout Requirements:**
946
+
947
+ Matrices must be stored in shared memory according
948
+ to their arrangement and leading dimension (ld):
949
+
950
+ **For matrix A (M x N):**
951
+
952
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
953
+ with strides ``(lda * N, 1, lda)``
954
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
955
+ with strides ``(lda * M, lda, 1)``
956
+
957
+ **For matrix B (N x K):**
958
+
959
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, N, K)``
960
+ with strides ``(ldb * K, 1, ldb)``
961
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, N, K)``
962
+ with strides ``(ldb * N, ldb, 1)``
963
+
964
+ .. note::
965
+ If a nonsingular matrix A is diagonally dominant, then it is safe to factorize
966
+ without pivoting. If a matrix is not diagonally dominant, then pivoting is usually
967
+ required to ensure numerical stability (see :class:`LUPivotSolver`).
968
+
969
+ Args:
970
+ size (Sequence[int]): {size}
971
+
972
+ precision (type[np.floating]): {precision}
973
+
974
+ execution (str): {execution}
975
+
976
+ sm (ComputeCapability): {sm}
977
+
978
+ transpose_mode (str, optional): {transpose_mode}
979
+
980
+ arrangement (Sequence[str], optional): {arrangement}
981
+
982
+ batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
983
+
984
+ data_type (str, optional): {data_type}
985
+
986
+ leading_dimensions (Sequence[int], optional): {leading_dimensions}
987
+
988
+ block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
989
+
990
+ See Also:
991
+ For further details, please refer to the cuSOLVERDx documentation:
992
+
993
+ * :cusolverdx_doc:`factorize <get_started/functions/getrf.html>`
994
+ * :cusolverdx_doc:`solve <get_started/functions/getrs.html>`
995
+
996
+ """
997
+
998
+ # ==========================
999
+ # Constructor
1000
+ # ==========================
1001
+
1002
+ def __init__(
1003
+ self,
1004
+ size: Sequence[int],
1005
+ precision: type[np.floating],
1006
+ execution: str,
1007
+ *,
1008
+ sm=None,
1009
+ transpose_mode: str = "non_transposed",
1010
+ arrangement: Sequence[str] | None = None,
1011
+ batches_per_block: int | Literal["suggested"] | None = None,
1012
+ data_type: str | None = None,
1013
+ leading_dimensions: Sequence[int] | None = None,
1014
+ block_dim: Sequence[int] | Literal["suggested"] | None = None,
1015
+ ):
1016
+ def construct(function, transpose_mode):
1017
+ return Solver(
1018
+ function=function,
1019
+ size=size,
1020
+ precision=precision,
1021
+ execution=execution,
1022
+ sm=sm,
1023
+ arrangement=arrangement,
1024
+ batches_per_block=batches_per_block,
1025
+ data_type=data_type,
1026
+ leading_dimensions=leading_dimensions,
1027
+ block_dim=block_dim,
1028
+ transpose_mode=transpose_mode,
1029
+ )
1030
+
1031
+ if transpose_mode is None:
1032
+ raise ValueError("transpose_mode must be provided for LUSolver")
1033
+
1034
+ self._factorize = construct("getrf_no_pivot", "non_transposed")
1035
+ self._transpose_mode = transpose_mode
1036
+ self._solve = construct("getrs_no_pivot", transpose_mode) if self._factorize.m == self._factorize.n else None
1037
+
1038
+ super().__init__(self._factorize)
1039
+
1040
+ # ==========================
1041
+ # Property methods
1042
+ # ==========================
1043
+
1044
+ @property
1045
+ def transpose_mode(self) -> str:
1046
+ return self._transpose_mode
1047
+
1048
+ # ==========================
1049
+ # Device function methods
1050
+ # ==========================
1051
+
1052
+ def factorize(self, a, info, lda=None) -> None:
1053
+ """
1054
+ Computes the LU factorization of a general matrix A without pivoting.
1055
+
1056
+ This device function computes A = L @ U, where L is a unit lower triangular matrix
1057
+ and U is an upper triangular matrix. This variant is suitable for diagonally
1058
+ dominant matrices or when pivoting is not required.
1059
+ Uses cuSOLVERDx ``'getrf_no_pivot'``.
1060
+
1061
+ If ``lda`` is provided, uses runtime version with the specified leading dimension.
1062
+ If ``lda`` is not provided (``None``), uses compile-time version with default
1063
+ or constructor-provided leading dimensions.
1064
+
1065
+ .. note::
1066
+ The ``transpose_mode`` parameter does not affect factorization.
1067
+ This operation always treats the input matrix as-is (non-transposed).
1068
+
1069
+ For more details, see: :cusolverdx_doc:`get_started/functions/getrf.html`
1070
+
1071
+ Args:
1072
+ a: Pointer to an array in shared memory, storing the batched matrix according
1073
+ to the specified arrangement and leading dimension (see :class:`LUSolver`).
1074
+ The matrix is overwritten in place.
1075
+ On exit, contains the factors L and U from the factorization A = L @ U.
1076
+ The unit diagonal elements of L are not stored.
1077
+ info: Pointer to a 1D array of ``int32``.
1078
+ On exit, ``info[batch_id] = 0`` indicates success for that batch,
1079
+ ``info[batch_id] = i > 0`` indicates ``U(i,i)`` is exactly zero,
1080
+ meaning the factorization has been completed but the factor U
1081
+ is singular and division by zero will occur if it is used to solve
1082
+ a system of equations.
1083
+ lda: Optional runtime leading dimension of matrix A.
1084
+ If not specified, the compile-time ``lda`` is used.
1085
+ """
1086
+ raise RuntimeError("factorize should not be called directly outside of a jitted kernel.")
1087
+
1088
+ def solve(self, a, b, lda=None, ldb=None) -> None:
1089
+ """
1090
+ Solves a system of linear equations Ax = B
1091
+ using the LU factorization without pivoting.
1092
+ The ``a`` operand must be a square matrix (``M == N``),
1093
+ otherwise this function will throw an exception.
1094
+
1095
+ This device function uses the previously computed factorization A = L @ U
1096
+ to solve the system. Uses cuSOLVERDx ``'getrs_no_pivot'``.
1097
+
1098
+ If ``lda`` and ``ldb`` are provided, uses runtime version with
1099
+ the specified leading dimensions. If not provided (``None``),
1100
+ uses compile-time version with default or constructor-provided
1101
+ leading dimensions.
1102
+
1103
+ .. note::
1104
+ The ``transpose_mode`` parameter (set in constructor) determines which
1105
+ system is solved: A*x=B (``'non_transposed'``), A^T*x=B (``'transposed'``),
1106
+ or A^H*x=B (``'conj_transposed'`` for complex matrices).
1107
+
1108
+ For more details, see: :cusolverdx_doc:`get_started/functions/getrs.html`
1109
+
1110
+ Args:
1111
+ a: Pointer to an array in shared memory, storing the batched factors L and U
1112
+ from the LU factorization, according to the specified arrangement
1113
+ and leading dimension (see :class:`LUSolver`).
1114
+ The unit diagonal elements of L are not stored.
1115
+ See the :meth:`factorize` documentation for details.
1116
+ b: Pointer to an array in shared memory, storing the batched matrix according
1117
+ to the specified arrangement and leading dimension (see :class:`LUSolver`).
1118
+ The matrix is overwritten in place with the solution matrix x.
1119
+ lda: Optional runtime leading dimension of matrix A.
1120
+ The ``lda`` and ``ldb`` must be specified together.
1121
+ If not specified, the compile-time ``lda`` is used.
1122
+ ldb: Optional runtime leading dimension of matrix B.
1123
+ The ``lda`` and ``ldb`` must be specified together.
1124
+ If not specified, the compile-time ``ldb`` is used.
1125
+ """
1126
+ raise RuntimeError("solve should not be called directly outside of a jitted kernel.")
1127
+
1128
+
1129
+ # ==========================
1130
+ # Triangular Solver
1131
+ # ==========================
1132
+
1133
+ TRIANGULAR_SOLVER_DOCSTRING = SOLVER_DOCSTRING.copy()
1134
+ del TRIANGULAR_SOLVER_DOCSTRING["function"]
1135
+ del TRIANGULAR_SOLVER_DOCSTRING["job"]
1136
+
1137
+ TRIANGULAR_SOLVER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
1138
+ ``M`` and ``N`` represent the dimensions of matrices A and B.
1139
+ When ``side='left'``, A is ``M`` x ``M``, otherwise when ``side='right'``, A is ``N`` x ``N``.
1140
+ B is always ``M`` x ``N``.""".replace("\n", " ")
1141
+
1142
+ TRIANGULAR_SOLVER_DOCSTRING["side"] = f"""Side of matrix A in the triangular solve operation (required for TRSM).
1143
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_SIDE)}.
1144
+ If ``side='left'``, solves op(A) * X = B where A is ``M`` x ``M``.
1145
+ If ``side='right'``, solves X * op(A) = B where A is ``N`` x ``N``.""".replace("\n", " ")
1146
+
1147
+ TRIANGULAR_SOLVER_DOCSTRING["fill_mode"] = f"""Indicates which part of triangular matrix A is filled and should be used.
1148
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_FILL_MODE)}.
1149
+ For lower fill mode, only the diagonal and lower triangular part of A is processed, the upper part is untouched.
1150
+ For upper fill mode, only the diagonal and upper triangular part of A is processed, the lower part is untouched.""".replace(
1151
+ "\n", " "
1152
+ )
1153
+
1154
+ TRIANGULAR_SOLVER_DOCSTRING["diag"] = f"""Indicates whether the diagonal elements of matrix A are unity or not.
1155
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_DIAG)}.
1156
+ For unit diagonal mode, the diagonal elements of A are unity and are not accessed.
1157
+ For non-unit diagonal mode, the diagonal elements of A are used in the computation.""".replace("\n", " ")
1158
+
1159
+ TRIANGULAR_SOLVER_DOCSTRING["transpose_mode"] = f"""Transpose mode for operation op(A) applied to matrix A.
1160
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_TRANSPOSE_MODE)}.
1161
+ Defaults to ``'non_transposed'``.""".replace("\n", " ")
1162
+
1163
+ TRIANGULAR_SOLVER_DOCSTRING["leading_dimensions"] = SOLVER_DOCSTRING["leading_dimensions"] + ADAPTERS_API_LD_DOCSTRING
1164
+
1165
+
1166
+ @docstring_decorator(TRIANGULAR_SOLVER_DOCSTRING, skip_missing=False)
1167
+ class TriangularSolver(_SolverProperties):
1168
+ """
1169
+ A class that encapsulates triangular matrix-matrix solve device function (``'trsm'``).
1170
+
1171
+ TRSM (TRiangular Solve for Matrix) solves a triangular linear system
1172
+ with multiple right-hand sides:
1173
+
1174
+ * op(A) * X = B (if ``side='left'``)
1175
+ * X * op(A) = B (if ``side='right'``)
1176
+
1177
+ where:
1178
+
1179
+ * A is the input batched triangular matrix stored in lower or upper mode
1180
+ * B is the batched right-hand side matrix, overwritten by the result X on exit
1181
+ * Operation op(A) indicates if matrix A is ``'non_transposed'``,
1182
+ ``'transposed'`` (for real data type),
1183
+ or ``'conj_transposed'`` (for complex data type)
1184
+
1185
+ **Memory Layout Requirements:**
1186
+
1187
+ Matrices must be stored in shared memory according
1188
+ to their arrangement and leading dimension (ld):
1189
+
1190
+ **For matrix A (M x M) with ``side='left'``:**
1191
+
1192
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, M, M)``
1193
+ with strides ``(lda * M, 1, lda)``
1194
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, M, M)``
1195
+ with strides ``(lda * M, lda, 1)``
1196
+
1197
+ **For matrix A (N x N) with ``side='right'``:**
1198
+
1199
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, N, N)``
1200
+ with strides ``(lda * N, 1, lda)``
1201
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, N, N)``
1202
+ with strides ``(lda * N, lda, 1)``
1203
+
1204
+ **For matrix B (M x N):**
1205
+
1206
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
1207
+ with strides ``(ldb * N, 1, ldb)``
1208
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
1209
+ with strides ``(ldb * M, ldb, 1)``
1210
+
1211
+ .. note::
1212
+ The TRSM function is temporarily exposed in cuSOLVERDx library
1213
+ and will be moved to cuBLASDx library in a future release.
1214
+
1215
+ Args:
1216
+ size (Sequence[int]): {size}
1217
+
1218
+ precision (type[np.floating]): {precision}
1219
+
1220
+ execution (str): {execution}
1221
+
1222
+ side (str): {side}
1223
+
1224
+ fill_mode (str): {fill_mode}
1225
+
1226
+ diag (str): {diag}
1227
+
1228
+ transpose_mode (str, optional): {transpose_mode}
1229
+
1230
+ sm (ComputeCapability): {sm}
1231
+
1232
+ arrangement (Sequence[str], optional): {arrangement}
1233
+
1234
+ batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
1235
+
1236
+ data_type (str, optional): {data_type}
1237
+
1238
+ leading_dimensions (Sequence[int], optional): {leading_dimensions}
1239
+
1240
+ block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
1241
+
1242
+ See Also:
1243
+ For further details, please refer to the cuSOLVERDx documentation:
1244
+
1245
+ * :cusolverdx_doc:`trsm <get_started/functions/trsm.html>`
1246
+ """
1247
+
1248
+ # ==========================
1249
+ # Constructor
1250
+ # ==========================
1251
+
1252
+ def __init__(
1253
+ self,
1254
+ size: Sequence[int],
1255
+ precision: type[np.floating],
1256
+ execution: str,
1257
+ side: str,
1258
+ fill_mode: str,
1259
+ diag: str,
1260
+ transpose_mode: str = "non_transposed",
1261
+ *,
1262
+ sm=None,
1263
+ arrangement: Sequence[str] | None = None,
1264
+ batches_per_block: int | Literal["suggested"] | None = None,
1265
+ data_type: str | None = None,
1266
+ leading_dimensions: Sequence[int] | None = None,
1267
+ block_dim: Sequence[int] | Literal["suggested"] | None = None,
1268
+ ):
1269
+ self._solve: Solver = Solver(
1270
+ function="trsm",
1271
+ size=size,
1272
+ precision=precision,
1273
+ execution=execution,
1274
+ sm=sm,
1275
+ side=side,
1276
+ fill_mode=fill_mode,
1277
+ diag=diag,
1278
+ transpose_mode=transpose_mode,
1279
+ arrangement=arrangement,
1280
+ batches_per_block=batches_per_block,
1281
+ data_type=data_type,
1282
+ leading_dimensions=leading_dimensions,
1283
+ block_dim=block_dim,
1284
+ )
1285
+
1286
+ super().__init__(self._solve)
1287
+
1288
+ if not isinstance(self._solve.side, str):
1289
+ raise ValueError("side must be provided for TriangularSolver")
1290
+
1291
+ if not isinstance(self._solve.fill_mode, str):
1292
+ raise ValueError("fill_mode must be provided for TriangularSolver")
1293
+
1294
+ if not isinstance(self._solve.diag, str):
1295
+ raise ValueError("diag must be provided for TriangularSolver")
1296
+
1297
+ # ==========================
1298
+ # Property methods
1299
+ # ==========================
1300
+
1301
+ @property
1302
+ def side(self) -> str:
1303
+ assert self._solve.side is not None
1304
+ return self._solve.side
1305
+
1306
+ @property
1307
+ def fill_mode(self) -> str:
1308
+ assert self._solve.fill_mode is not None
1309
+ return self._solve.fill_mode
1310
+
1311
+ @property
1312
+ def diag(self) -> str:
1313
+ assert self._solve.diag is not None
1314
+ return self._solve.diag
1315
+
1316
+ @property
1317
+ def transpose_mode(self) -> str:
1318
+ assert self._solve.transpose_mode is not None
1319
+ return self._solve.transpose_mode
1320
+
1321
+ @property
1322
+ def a_shape(self) -> tuple[int, int, int]:
1323
+ dim = self.m if self.side == "left" else self.n
1324
+ return (self.batches_per_block, dim, dim)
1325
+
1326
+ @property
1327
+ def b_shape(self) -> tuple[int, int, int]:
1328
+ return (self.batches_per_block, self.m, self.n)
1329
+
1330
+ def a_strides(self, *, lda: int | None = None) -> tuple[int, int, int]:
1331
+ lda = self.lda if lda is None else lda
1332
+ return _calculate_strides(self.a_shape[1:], lda, self.a_arrangement)
1333
+
1334
+ def b_strides(self, *, ldb: int | None = None) -> tuple[int, int, int]:
1335
+ ldb = self.ldb if ldb is None else ldb
1336
+ return _calculate_strides(self.b_shape[1:], ldb, self.b_arrangement)
1337
+
1338
+ def a_size(self, *, lda: int | None = None) -> int:
1339
+ return self.a_strides(lda=lda)[0] * self.a_shape[0]
1340
+
1341
+ def b_size(self, *, ldb: int | None = None) -> int:
1342
+ return self.b_strides(ldb=ldb)[0] * self.b_shape[0]
1343
+
1344
+ # ==========================
1345
+ # Device function methods
1346
+ # ==========================
1347
+
1348
+ def solve(self, a, b, lda=None, ldb=None) -> None:
1349
+ """
1350
+ Solves a triangular linear system with multiple right-hand sides:
1351
+ ``op(A) * X = B`` (if ``side='left'``)
1352
+ ``X * op(A) = B`` (if ``side='right'``)
1353
+
1354
+ This device function solves a triangular system where A is a triangular matrix.
1355
+ Uses cuSOLVERDx ``'trsm'``. The operation is in-place: result X overwrites B.
1356
+
1357
+ If ``lda`` and ``ldb`` are provided, uses runtime version with the
1358
+ specified leading dimensions. If not provided (``None``), uses compile-time
1359
+ version with default or constructor-provided leading dimensions.
1360
+
1361
+ For more details, see: :cusolverdx_doc:`get_started/functions/trsm.html`
1362
+
1363
+ Args:
1364
+ a: Pointer to an array in shared memory, storing the batched triangular matrix
1365
+ according to the specified arrangement
1366
+ and leading dimension (see :class:`TriangularSolver`).
1367
+ The ``fill_mode`` parameter denotes which
1368
+ part of the matrix is used (the other part is ignored).
1369
+ For unit diagonal mode (``diag='unit'``),
1370
+ diagonal elements are unity and not accessed.
1371
+ b: Pointer to an array in shared memory,
1372
+ storing the batched ``M`` x ``N`` right-hand side
1373
+ matrix according to the specified arrangement
1374
+ and leading dimension (see :class:`TriangularSolver`).
1375
+ The operation is in-place: result X overwrites B.
1376
+ lda: Optional runtime leading dimension for matrix A.
1377
+ The ``lda`` and ``ldb`` must be specified together.
1378
+ If not specified, the compile-time ``lda`` is used.
1379
+ ldb: Optional runtime leading dimension for matrix B.
1380
+ The ``lda`` and ``ldb`` must be specified together.
1381
+ If not specified, the compile-time ``ldb`` is used.
1382
+ """
1383
+ raise RuntimeError("solve should not be called directly outside of a jitted kernel.")
1384
+
1385
+
1386
+ # ==========================
1387
+ # LU Pivot Solver
1388
+ # ==========================
1389
+
1390
+
1391
+ @docstring_decorator(LU_SOLVER_DOCSTRING, skip_missing=False)
1392
+ class LUPivotSolver(_LinearSolverProperties):
1393
+ """
1394
+ A class that encapsulates cuSOLVERDx LU factorization with partial pivoting
1395
+ and linear solver for general matrices.
1396
+
1397
+ **Available operations:**
1398
+
1399
+ * factorize: Computes the LU factorization P @ A = L @ U with partial pivoting,
1400
+ where P is a permutation matrix, L is a
1401
+ unit lower triangular matrix and U is an upper triangular matrix.
1402
+ * solve: Solves the system Ax = B using a previously
1403
+ computed LU factorization with partial pivoting
1404
+
1405
+ **Memory Layout Requirements:**
1406
+
1407
+ Matrices must be stored in shared memory according
1408
+ to their arrangement and leading dimension (ld):
1409
+
1410
+ **For matrix A (M x N):**
1411
+
1412
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
1413
+ with strides ``(lda * N, 1, lda)``
1414
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
1415
+ with strides ``(lda * M, lda, 1)``
1416
+
1417
+ **For matrix B (N x K):**
1418
+
1419
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, N, K)``
1420
+ with strides ``(ldb * K, 1, ldb)``
1421
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, N, K)``
1422
+ with strides ``(ldb * N, ldb, 1)``
1423
+
1424
+ .. note::
1425
+ This solver uses partial pivoting for improved numerical stability and is
1426
+ suitable for general matrices. If your matrix is diagonally dominant,
1427
+ you may consider using :class:`LUSolver` which does not use pivoting
1428
+ and may be faster.
1429
+
1430
+ Args:
1431
+ size (Sequence[int]): {size}
1432
+
1433
+ precision (type[np.floating]): {precision}
1434
+
1435
+ execution (str): {execution}
1436
+
1437
+ sm (ComputeCapability): {sm}
1438
+
1439
+ transpose_mode (str, optional): {transpose_mode}
1440
+
1441
+ arrangement (Sequence[str], optional): {arrangement}
1442
+
1443
+ batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
1444
+
1445
+ data_type (str, optional): {data_type}
1446
+
1447
+ leading_dimensions (Sequence[int], optional): {leading_dimensions}
1448
+
1449
+ block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
1450
+
1451
+ See Also:
1452
+ For further details, please refer to the cuSOLVERDx documentation:
1453
+
1454
+ * :cusolverdx_doc:`factorize <get_started/functions/getrf.html>`
1455
+ * :cusolverdx_doc:`solve <get_started/functions/getrs.html>`
1456
+
1457
+ """
1458
+
1459
+ # ==========================
1460
+ # Constructor
1461
+ # ==========================
1462
+
1463
+ def __init__(
1464
+ self,
1465
+ size: Sequence[int],
1466
+ precision: type[np.floating],
1467
+ execution: str,
1468
+ *,
1469
+ sm=None,
1470
+ transpose_mode: str = "non_transposed",
1471
+ arrangement: Sequence[str] | None = None,
1472
+ batches_per_block: int | Literal["suggested"] | None = None,
1473
+ data_type: str | None = None,
1474
+ leading_dimensions: Sequence[int] | None = None,
1475
+ block_dim: Sequence[int] | Literal["suggested"] | None = None,
1476
+ ):
1477
+ def construct(function, transpose_mode):
1478
+ return Solver(
1479
+ function=function,
1480
+ size=size,
1481
+ precision=precision,
1482
+ execution=execution,
1483
+ sm=sm,
1484
+ arrangement=arrangement,
1485
+ batches_per_block=batches_per_block,
1486
+ data_type=data_type,
1487
+ leading_dimensions=leading_dimensions,
1488
+ block_dim=block_dim,
1489
+ transpose_mode=transpose_mode,
1490
+ )
1491
+
1492
+ if transpose_mode is None:
1493
+ raise ValueError("transpose_mode must be provided for LUPivotSolver")
1494
+
1495
+ self._factorize = construct("getrf_partial_pivot", "non_transposed")
1496
+ self._solve = construct("getrs_partial_pivot", transpose_mode) if self._factorize.m == self._factorize.n else None
1497
+ self._transpose_mode = transpose_mode
1498
+
1499
+ super().__init__(self._factorize)
1500
+
1501
+ # ==========================
1502
+ # Property methods
1503
+ # ==========================
1504
+
1505
+ @property
1506
+ def transpose_mode(self) -> str:
1507
+ return self._transpose_mode
1508
+
1509
+ @property
1510
+ def ipiv_type(self) -> type[np.signedinteger]:
1511
+ return self._factorize.ipiv_type
1512
+
1513
+ @property
1514
+ def ipiv_shape(self) -> tuple[int, int]:
1515
+ return (self.batches_per_block, min(self.m, self.n))
1516
+
1517
+ @property
1518
+ def ipiv_strides(self) -> tuple[int, int]:
1519
+ return (self.ipiv_shape[1], 1)
1520
+
1521
+ @property
1522
+ def ipiv_size(self) -> int:
1523
+ return self.ipiv_shape[0] * self.ipiv_shape[1]
1524
+
1525
+ # ==========================
1526
+ # Device function methods
1527
+ # ==========================
1528
+
1529
+ def factorize(self, a, ipiv, info, lda=None) -> None:
1530
+ """
1531
+ Computes the LU factorization of a general matrix A with partial pivoting.
1532
+
1533
+ This device function computes P @ A = L @ U, where P is a permutation matrix,
1534
+ L is a unit lower triangular matrix and U is an upper triangular matrix.
1535
+ This variant uses partial pivoting for improved numerical stability
1536
+ and is suitable for general matrices. Uses cuSOLVERDx ``'getrf_partial_pivot'``.
1537
+
1538
+ If ``lda`` is provided, uses runtime version with the specified leading dimension.
1539
+ If ``lda`` is not provided (``None``), uses compile-time version with default
1540
+ or constructor-provided leading dimensions.
1541
+
1542
+ .. note::
1543
+ The ``transpose_mode`` parameter does not affect factorization.
1544
+ This operation always treats the input matrix as-is (non-transposed).
1545
+
1546
+ For more details, see: :cusolverdx_doc:`get_started/functions/getrf.html`
1547
+
1548
+ Args:
1549
+ a: Pointer to an array in shared memory, storing the batched matrix according
1550
+ to the specified arrangement
1551
+ and leading dimension (see :class:`LUPivotSolver`).
1552
+ The matrix is overwritten in place.
1553
+ On exit, contains the factors L and U from the factorization P @ A = L @ U.
1554
+ The unit diagonal elements of L are not stored.
1555
+ ipiv: Pointer to a 1D array of ``int32``, storing pivot indices.
1556
+ The array has size min(M, N) for each batch.
1557
+ On exit, ``ipiv[batch_id * min(M, N) + i]`` indicates that row i
1558
+ was interchanged with row ``ipiv[batch_id * min(M, N) + i] - 1``
1559
+ in the batch_id-th batch of A.
1560
+ info: Pointer to a 1D array of ``int32``. On exit, ``info[batch_id] = 0``
1561
+ indicates success for that batch, ``info[batch_id] = i > 0``
1562
+ indicates ``U(i,i)`` is exactly zero,
1563
+ meaning the factorization has been completed but the factor U
1564
+ is singular and division by zero will occur if it is used to solve
1565
+ a system of equations.
1566
+ lda: Optional runtime leading dimension of matrix A.
1567
+ If not specified, the compile-time ``lda`` is used.
1568
+ """
1569
+ raise RuntimeError("factorize should not be called directly outside of a jitted kernel.")
1570
+
1571
+ def solve(self, a, ipiv, b, lda=None, ldb=None) -> None:
1572
+ """
1573
+ Solves a system of linear equations Ax = B
1574
+ using the LU factorization with partial pivoting.
1575
+ The ``a`` operand must be a square matrix (``M == N``),
1576
+ otherwise this function will throw an exception.
1577
+
1578
+ This device function uses the previously computed factorization P @ A = L @ U
1579
+ to solve the system. Uses cuSOLVERDx ``'getrs_partial_pivot'``.
1580
+
1581
+ If ``lda`` and ``ldb`` are provided, uses runtime version with
1582
+ the specified leading dimensions. If not provided (``None``),
1583
+ uses compile-time version with default or constructor-provided
1584
+ leading dimensions.
1585
+
1586
+ .. note::
1587
+ The ``transpose_mode`` parameter (set in constructor) determines which
1588
+ system is solved: A*x=B (``'non_transposed'``), A^T*x=B (``'transposed'``),
1589
+ or A^H*x=B (``'conj_transposed'`` for complex matrices).
1590
+
1591
+ For more details, see: :cusolverdx_doc:`get_started/functions/getrs.html`
1592
+
1593
+ Args:
1594
+ a: Pointer to an array in shared memory, storing the batched factors L and U
1595
+ from the LU factorization with partial pivoting, according
1596
+ to the specified arrangement
1597
+ and leading dimension (see :class:`LUPivotSolver`).
1598
+ The unit diagonal elements of L are not stored.
1599
+ See the :meth:`factorize` documentation for details.
1600
+ ipiv: Pointer to a 1D array of ``int32`` in shared or global memory
1601
+ storing pivot indices.
1602
+ The array has size min(M, N) for each batch. The ipiv array should contain
1603
+ the pivot information from the :meth:`factorize` call.
1604
+ ``ipiv[batch_id * min(M, N) + i]`` indicates that row i was interchanged
1605
+ with row ``ipiv[batch_id * min(M, N) + i] - 1``
1606
+ in the batch_id-th batch of A.
1607
+ b: Pointer to an array in shared memory, storing the batched matrix according
1608
+ to the specified arrangement
1609
+ and leading dimension (see :class:`LUPivotSolver`).
1610
+ The matrix is overwritten in place with the solution matrix x.
1611
+ lda: Optional runtime leading dimension of matrix A.
1612
+ The ``lda`` and ``ldb`` must be specified together.
1613
+ If not specified, the compile-time ``lda`` is used.
1614
+ ldb: Optional runtime leading dimension of matrix B.
1615
+ The ``lda`` and ``ldb`` must be specified together.
1616
+ If not specified, the compile-time ``ldb`` is used.
1617
+ """
1618
+ raise RuntimeError("solve should not be called directly outside of a jitted kernel.")
1619
+
1620
+
1621
+ # ==========================
1622
+ # QR/LQ Factorizers Base
1623
+ # ==========================
1624
+
1625
+ ORTOGHONAL_FACTORIZER_DOCSTRING = SOLVER_DOCSTRING.copy()
1626
+ del ORTOGHONAL_FACTORIZER_DOCSTRING["function"]
1627
+ del ORTOGHONAL_FACTORIZER_DOCSTRING["fill_mode"]
1628
+ del ORTOGHONAL_FACTORIZER_DOCSTRING["side"]
1629
+ del ORTOGHONAL_FACTORIZER_DOCSTRING["diag"]
1630
+ del ORTOGHONAL_FACTORIZER_DOCSTRING["transpose_mode"]
1631
+ del ORTOGHONAL_FACTORIZER_DOCSTRING["job"]
1632
+
1633
+ ORTOGHONAL_FACTORIZER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
1634
+ ``M`` and ``N`` represent the dimensions of the matrix A used in factorization.
1635
+ ``K`` is ignored if specified.""".replace("\n", " ")
1636
+
1637
+ ORTOGHONAL_FACTORIZER_DOCSTRING["arrangement"] = """Storage layout for matrix A.
1638
+ Can be one of: ``'col_major'``, ``'row_major'``. Defaults to ``'col_major'``.""".replace("\n", " ")
1639
+
1640
+ ORTOGHONAL_FACTORIZER_DOCSTRING["leading_dimension"] = (
1641
+ """The leading dimension for input matrix A, or ``None``.
1642
+ If not provided, it will be automatically deduced from ``size`` and ``arrangement``.""".replace("\n", " ")
1643
+ + " "
1644
+ + ADAPTERS_API_LD_DOCSTRING
1645
+ )
1646
+
1647
+
1648
+ class _OrthogonalFactorizerProperties:
1649
+ def __init__(self, source: Solver):
1650
+ self._properties_source = source
1651
+
1652
+ @property
1653
+ def size(self) -> tuple[int, int, int]:
1654
+ return self._properties_source.size
1655
+
1656
+ @property
1657
+ def m(self) -> int:
1658
+ return self._properties_source.m
1659
+
1660
+ @property
1661
+ def n(self) -> int:
1662
+ return self._properties_source.n
1663
+
1664
+ @property
1665
+ def precision(self) -> type[np.floating]:
1666
+ return self._properties_source.precision
1667
+
1668
+ @property
1669
+ def execution(self) -> str:
1670
+ return self._properties_source.execution
1671
+
1672
+ @property
1673
+ def sm(self) -> ComputeCapability:
1674
+ return self._properties_source.sm
1675
+
1676
+ @property
1677
+ def a_arrangement(self) -> str:
1678
+ return self._properties_source.a_arrangement
1679
+
1680
+ @property
1681
+ def batches_per_block(self) -> int:
1682
+ return self._properties_source.batches_per_block
1683
+
1684
+ @property
1685
+ def data_type(self) -> str:
1686
+ return self._properties_source.data_type
1687
+
1688
+ @property
1689
+ def lda(self) -> int:
1690
+ return self._properties_source.lda
1691
+
1692
+ @property
1693
+ def block_dim(self) -> tuple:
1694
+ return self._properties_source.block_dim
1695
+
1696
+ @property
1697
+ def block_size(self) -> int:
1698
+ return self._properties_source.block_size
1699
+
1700
+ @property
1701
+ def value_type(self) -> type[np.floating] | Complex:
1702
+ return self._properties_source.value_type
1703
+
1704
+ @property
1705
+ def tau_type(self) -> type[np.floating] | Complex:
1706
+ return self._properties_source.tau_type
1707
+
1708
+ @property
1709
+ def tau_shape(self) -> tuple[int, int]:
1710
+ return (self.batches_per_block, min(self.m, self.n))
1711
+
1712
+ @property
1713
+ def tau_strides(self) -> tuple[int, int]:
1714
+ return (self.tau_shape[1], 1)
1715
+
1716
+ @property
1717
+ def a_shape(self) -> tuple[int, int, int]:
1718
+ return (self.batches_per_block, self.m, self.n)
1719
+
1720
+ def a_strides(self, *, lda: int | None = None) -> tuple[int, int, int]:
1721
+ lda = self.lda if lda is None else lda
1722
+ return _calculate_strides(self.a_shape[1:], lda, self.a_arrangement)
1723
+
1724
+ def a_size(self, *, lda: int | None = None) -> int:
1725
+ return self.a_strides(lda=lda)[0] * self.a_shape[0]
1726
+
1727
+ @property
1728
+ def tau_size(self) -> int:
1729
+ return self.tau_shape[0] * self.tau_shape[1]
1730
+
1731
+
1732
+ # ==========================
1733
+ # QR Factorize
1734
+ # ==========================
1735
+
1736
+
1737
+ @docstring_decorator(ORTOGHONAL_FACTORIZER_DOCSTRING, skip_missing=False)
1738
+ class QRFactorize(_OrthogonalFactorizerProperties):
1739
+ """
1740
+ A class that encapsulates QR orthogonal factorization device function
1741
+ for general matrices using Householder reflections.
1742
+
1743
+ **Available operation:**
1744
+
1745
+ * factorize: Computes the QR factorization A = Q @ R,
1746
+ where Q is a unitary M x M matrix
1747
+ and R is an upper triangular matrix (if M >= N)
1748
+ or upper trapezoidal matrix (if M < N).
1749
+
1750
+ The factorization uses Householder reflection transformations and does not explicitly
1751
+ form the unitary matrix Q. Instead, Q is represented as a product of Householder vectors
1752
+ stored in the input matrix A along with the tau array.
1753
+
1754
+ **Memory Layout Requirements:**
1755
+
1756
+ Matrices must be stored in shared memory according
1757
+ to their arrangement and leading dimension (ld):
1758
+
1759
+ **For matrix A (M x N):**
1760
+
1761
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
1762
+ with strides ``(lda * N, 1, lda)``
1763
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
1764
+ with strides ``(lda * M, lda, 1)``
1765
+
1766
+ Args:
1767
+ size (Sequence[int]): {size}
1768
+
1769
+ precision (type[np.floating]): {precision}
1770
+
1771
+ execution (str): {execution}
1772
+
1773
+ sm (ComputeCapability): {sm}
1774
+
1775
+ arrangement (str, optional): {arrangement}
1776
+
1777
+ batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
1778
+
1779
+ data_type (str, optional): {data_type}
1780
+
1781
+ leading_dimension (int, optional): {leading_dimension}
1782
+
1783
+ block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
1784
+
1785
+ See Also:
1786
+ For further details, please refer to the cuSOLVERDx documentation:
1787
+
1788
+ * :cusolverdx_doc:`factorize (geqrf) <get_started/functions/geqrf.html>`
1789
+ """
1790
+
1791
+ # ==========================
1792
+ # Constructor
1793
+ # ==========================
1794
+
1795
+ def __init__(
1796
+ self,
1797
+ size: Sequence[int],
1798
+ precision: type[np.floating],
1799
+ execution: str,
1800
+ *,
1801
+ sm=None,
1802
+ arrangement: str | None = None,
1803
+ batches_per_block: int | Literal["suggested"] | None = None,
1804
+ data_type: str | None = None,
1805
+ leading_dimension: int | None = None,
1806
+ block_dim: Sequence[int] | Literal["suggested"] | None = None,
1807
+ ):
1808
+ self._factorize = Solver(
1809
+ function="geqrf",
1810
+ size=size,
1811
+ precision=precision,
1812
+ execution=execution,
1813
+ sm=sm,
1814
+ arrangement=(arrangement,) if arrangement is not None else None,
1815
+ batches_per_block=batches_per_block,
1816
+ data_type=data_type,
1817
+ leading_dimensions=(leading_dimension,) if leading_dimension is not None else None,
1818
+ block_dim=block_dim,
1819
+ )
1820
+ super().__init__(self._factorize)
1821
+
1822
+ # ==========================
1823
+ # Device function methods
1824
+ # ==========================
1825
+
1826
+ def factorize(self, a, tau, lda=None) -> None:
1827
+ """
1828
+ Computes the QR factorization of a general matrix A using Householder reflections.
1829
+
1830
+ This device function computes A = Q @ R, where Q is a unitary M x M matrix
1831
+ and R is an upper triangular matrix (if M >= N)
1832
+ or upper trapezoidal matrix (if M < N).
1833
+ Uses cuSOLVERDx ``'geqrf'``.
1834
+
1835
+ If ``lda`` is provided, uses runtime version with the
1836
+ specified leading dimension. If ``lda`` is not provided (``None``),
1837
+ uses compile-time version with
1838
+ default or constructor-provided leading dimensions.
1839
+
1840
+ Matrix Q is not explicitly formed. Instead, Q is represented as a product of
1841
+ min(M, N) Householder vectors: Q = H(0) * H(1) * ... * H(min(M, N) - 1).
1842
+
1843
+ Each Householder vector has the form H(i) = I - tau[i] * v * v^H, where:
1844
+
1845
+ * v is a vector of size M for each batch
1846
+ * v[0:i] = 0, v[i] = 1
1847
+ * v[i+1:M] is stored on exit in A[i+1:M, i]
1848
+
1849
+ For more details, see: :cusolverdx_doc:`get_started/functions/geqrf.html`
1850
+
1851
+ Args:
1852
+ a: Pointer to an array in shared memory, storing
1853
+ the batched matrix according
1854
+ to the specified arrangement
1855
+ and leading dimension (see :class:`QRFactorize`).
1856
+ The matrix is overwritten in place.
1857
+ On exit, the upper triangular or upper trapezoidal part (including diagonal)
1858
+ contains the matrix R. The elements below the diagonal, with the array tau,
1859
+ represent the unitary matrix Q as a product of Householder vectors.
1860
+ tau: Pointer to a 1D array of size min(M, N) for each batch.
1861
+ Contains the scalar factors of the Householder reflections.
1862
+ The tau array, together with the Householder vectors stored in A,
1863
+ defines the unitary matrix Q.
1864
+ lda: Optional runtime leading dimension of matrix A.
1865
+ If not specified, the compile-time ``lda`` is used.
1866
+ """
1867
+ raise RuntimeError("factorize should not be called directly outside of a jitted kernel.")
1868
+
1869
+
1870
+ # ==========================
1871
+ # LQ Factorize
1872
+ # ==========================
1873
+
1874
+
1875
+ @docstring_decorator(ORTOGHONAL_FACTORIZER_DOCSTRING, skip_missing=False)
1876
+ class LQFactorize(_OrthogonalFactorizerProperties):
1877
+ """
1878
+ A class that encapsulates LQ orthogonal factorization device function
1879
+ for general matrices using Householder reflections.
1880
+
1881
+ **Available operation:**
1882
+
1883
+ * factorize: Computes the LQ factorization A = L @ Q,
1884
+ where L is a lower triangular matrix (if M <= N)
1885
+ or lower trapezoidal matrix (if M > N)
1886
+ and Q is a unitary N x N matrix.
1887
+
1888
+ The factorization uses Householder reflection transformations and does not explicitly
1889
+ form the unitary matrix Q. Instead, Q is represented as a product of Householder vectors
1890
+ stored in the input matrix A along with the tau array.
1891
+
1892
+ **Memory Layout Requirements:**
1893
+
1894
+ Matrices must be stored in shared memory according
1895
+ to their arrangement and leading dimension (ld):
1896
+
1897
+ **For matrix A (M x N):**
1898
+
1899
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
1900
+ with strides ``(lda * N, 1, lda)``
1901
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
1902
+ with strides ``(lda * M, lda, 1)``
1903
+
1904
+ .. note::
1905
+ The LQ factorization is essentially equivalent to the
1906
+ QR factorization of A^T (or A^H for complex types).
1907
+
1908
+ Args:
1909
+ size (Sequence[int]): {size}
1910
+
1911
+ precision (type[np.floating]): {precision}
1912
+
1913
+ execution (str): {execution}
1914
+
1915
+ sm (ComputeCapability): {sm}
1916
+
1917
+ arrangement (str, optional): {arrangement}
1918
+
1919
+ batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
1920
+
1921
+ data_type (str, optional): {data_type}
1922
+
1923
+ leading_dimension (int, optional): {leading_dimension}
1924
+
1925
+ block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
1926
+
1927
+ See Also:
1928
+ For further details, please refer to the cuSOLVERDx documentation:
1929
+
1930
+ * :cusolverdx_doc:`factorize (gelqf) <get_started/functions/gelqf.html>`
1931
+ """
1932
+
1933
+ # ==========================
1934
+ # Constructor
1935
+ # ==========================
1936
+
1937
+ def __init__(
1938
+ self,
1939
+ size: Sequence[int],
1940
+ precision: type[np.floating],
1941
+ execution: str,
1942
+ *,
1943
+ sm=None,
1944
+ arrangement: str | None = None,
1945
+ batches_per_block: int | Literal["suggested"] | None = None,
1946
+ data_type: str | None = None,
1947
+ leading_dimension: int | None = None,
1948
+ block_dim: Sequence[int] | Literal["suggested"] | None = None,
1949
+ ):
1950
+ self._factorize = Solver(
1951
+ function="gelqf",
1952
+ size=size,
1953
+ precision=precision,
1954
+ execution=execution,
1955
+ sm=sm,
1956
+ arrangement=(arrangement,) if arrangement is not None else None,
1957
+ batches_per_block=batches_per_block,
1958
+ data_type=data_type,
1959
+ leading_dimensions=(leading_dimension,) if leading_dimension is not None else None,
1960
+ block_dim=block_dim,
1961
+ )
1962
+ super().__init__(self._factorize)
1963
+
1964
+ # ==========================
1965
+ # Device function methods
1966
+ # ==========================
1967
+
1968
+ def factorize(self, a, tau, lda=None) -> None:
1969
+ """
1970
+ Computes the LQ factorization of a general matrix A using Householder reflections.
1971
+
1972
+ This device function computes A = L @ Q, where L is a lower triangular matrix
1973
+ (if M <= N) or lower trapezoidal matrix (if M > N),
1974
+ and Q is a unitary N x N matrix.
1975
+ Uses cuSOLVERDx ``'gelqf'``.
1976
+
1977
+ If ``lda`` is provided, uses runtime version with the
1978
+ specified leading dimension. If ``lda`` is not provided (``None``),
1979
+ uses compile-time version with
1980
+ default or constructor-provided leading dimensions.
1981
+
1982
+ The LQ factorization is essentially the same as the QR factorization of A^T
1983
+ (or A^H for complex data types).
1984
+
1985
+ Matrix Q is not explicitly formed. Instead, Q is represented as a product of
1986
+ min(M, N) Householder vectors: Q = H(min(M, N) - 1)^H * ... * H(1)^H * H(0)^H.
1987
+
1988
+ Each Householder vector has the form H(i) = I - tau[i] * v * v^H, where:
1989
+
1990
+ * v is a vector of size N for each batch
1991
+ * v[0:i] = 0, v[i] = 1
1992
+ * conjugate(v[i+1:N]) is stored on exit in A[i, i+1:N]
1993
+
1994
+ For more details, see: :cusolverdx_doc:`get_started/functions/gelqf.html`
1995
+
1996
+ Args:
1997
+ a: Pointer to an array in shared memory, storing
1998
+ the batched M x N matrix according
1999
+ to the specified arrangement
2000
+ and leading dimension (see :class:`LQFactorize`).
2001
+ The matrix is overwritten in place.
2002
+ On exit, the lower triangular or lower trapezoidal part (including diagonal)
2003
+ contains the matrix L. The elements above the diagonal, with the array tau,
2004
+ represent the unitary matrix Q as a product of Householder vectors.
2005
+ tau: Pointer to a 1D array of size min(M, N) for each batch.
2006
+ Contains the scalar factors of the Householder reflections.
2007
+ The tau array, together with the Householder vectors stored in A,
2008
+ defines the unitary matrix Q.
2009
+ lda: Optional runtime leading dimension of matrix A.
2010
+ If not specified, the compile-time ``lda`` is used.
2011
+ """
2012
+ raise RuntimeError("factorize should not be called directly outside of a jitted kernel.")
2013
+
2014
+
2015
+ # ==========================
2016
+ # QR Multiplier (UNMQR)
2017
+ # ==========================
2018
+
2019
+ QR_MULTIPLIER_DOCSTRING = SOLVER_DOCSTRING.copy()
2020
+ del QR_MULTIPLIER_DOCSTRING["function"]
2021
+ del QR_MULTIPLIER_DOCSTRING["fill_mode"]
2022
+ del QR_MULTIPLIER_DOCSTRING["diag"]
2023
+ del QR_MULTIPLIER_DOCSTRING["job"]
2024
+
2025
+ QR_MULTIPLIER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
2026
+ ``M`` and ``N`` represent the dimensions of matrix C.
2027
+ ``K`` represents the number of Householder reflections from the QR factorization.
2028
+ If ``side='left'``, then ``K <= M`` and A is ``M`` x ``K``.
2029
+ If ``side='right'``, then ``K <= N`` and A is ``N`` x ``K``.""".replace("\n", " ")
2030
+
2031
+ QR_MULTIPLIER_DOCSTRING["side"] = f"""Side of matrix Q in the multiplication operation.
2032
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_SIDE)}.
2033
+ If ``side='left'``, computes op(Q) * C where Q is ``M`` x ``M``.
2034
+ If ``side='right'``, computes C * op(Q) where Q is ``N`` x ``N``.""".replace("\n", " ")
2035
+
2036
+ QR_MULTIPLIER_DOCSTRING["transpose_mode"] = f"""Transpose mode for operation op(Q) applied to matrix Q.
2037
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_TRANSPOSE_MODE)}.
2038
+ Defaults to ``'non_transposed'``.""".replace("\n", " ")
2039
+
2040
+ QR_MULTIPLIER_DOCSTRING["leading_dimensions"] = SOLVER_DOCSTRING["leading_dimensions"] + ADAPTERS_API_LD_DOCSTRING
2041
+
2042
+
2043
+ @docstring_decorator(QR_MULTIPLIER_DOCSTRING, skip_missing=False)
2044
+ class QRMultiply(_SolverProperties):
2045
+ """
2046
+ A class that encapsulates the multiplication of a matrix C by the unitary matrix Q
2047
+ from a QR factorization (UNMQR operation).
2048
+
2049
+ **Memory Layout Requirements:**
2050
+
2051
+ Matrices must be stored in shared memory according
2052
+ to their arrangement and leading dimension (ld):
2053
+
2054
+ **For matrix A (containing Householder vectors):**
2055
+
2056
+ * If ``side='left'``: A is ``M`` x ``K``
2057
+ * If ``side='right'``: A is ``N`` x ``K``
2058
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, rows, K)``
2059
+ with strides ``(lda * K, 1, lda)``
2060
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, rows, K)``
2061
+ with strides ``(lda * rows, lda, 1)``
2062
+
2063
+ **For matrix C (M x N):**
2064
+
2065
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
2066
+ with strides ``(ldc * N, 1, ldc)``
2067
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
2068
+ with strides ``(ldc * M, ldc, 1)``
2069
+
2070
+ Args:
2071
+ size (Sequence[int]): {size}
2072
+
2073
+ precision (type[np.floating]): {precision}
2074
+
2075
+ execution (str): {execution}
2076
+
2077
+ side (str): {side}
2078
+
2079
+ sm (ComputeCapability): {sm}
2080
+
2081
+ transpose_mode (str, optional): {transpose_mode}
2082
+
2083
+ arrangement (Sequence[str], optional): {arrangement}
2084
+
2085
+ batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
2086
+
2087
+ data_type (str, optional): {data_type}
2088
+
2089
+ leading_dimensions (Sequence[int], optional): {leading_dimensions}
2090
+
2091
+ block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
2092
+
2093
+ See Also:
2094
+ For further details, please refer to the cuSOLVERDx documentation:
2095
+
2096
+ * :cusolverdx_doc:`unmqr <get_started/functions/unmqr.html>`
2097
+ """
2098
+
2099
+ # ==========================
2100
+ # Constructor
2101
+ # ==========================
2102
+
2103
+ def __init__(
2104
+ self,
2105
+ size: Sequence[int],
2106
+ precision: type[np.floating],
2107
+ execution: str,
2108
+ side: str,
2109
+ *,
2110
+ sm=None,
2111
+ transpose_mode: str = "non_transposed",
2112
+ arrangement: Sequence[str] | None = None,
2113
+ batches_per_block: int | Literal["suggested"] | None = None,
2114
+ data_type: str | None = None,
2115
+ leading_dimensions: Sequence[int] | None = None,
2116
+ block_dim: Sequence[int] | Literal["suggested"] | None = None,
2117
+ ):
2118
+ self._multiply: Solver = Solver(
2119
+ function="unmqr",
2120
+ size=size,
2121
+ precision=precision,
2122
+ execution=execution,
2123
+ sm=sm,
2124
+ side=side,
2125
+ transpose_mode=transpose_mode,
2126
+ arrangement=arrangement,
2127
+ batches_per_block=batches_per_block,
2128
+ data_type=data_type,
2129
+ leading_dimensions=leading_dimensions,
2130
+ block_dim=block_dim,
2131
+ )
2132
+
2133
+ super().__init__(self._multiply)
2134
+
2135
+ if not isinstance(self._multiply.transpose_mode, str):
2136
+ raise ValueError("transpose_mode must be provided for QRMultiply")
2137
+
2138
+ if not isinstance(self._multiply.side, str):
2139
+ raise ValueError("side must be provided for QRMultiply")
2140
+
2141
+ # ==========================
2142
+ # Property methods
2143
+ # ==========================
2144
+
2145
+ @property
2146
+ def side(self) -> str:
2147
+ assert self._multiply.side is not None
2148
+ return self._multiply.side
2149
+
2150
+ @property
2151
+ def transpose_mode(self) -> str:
2152
+ assert self._multiply.transpose_mode is not None
2153
+ return self._multiply.transpose_mode
2154
+
2155
+ @property
2156
+ def tau_type(self) -> type[np.floating] | Complex:
2157
+ return self.value_type
2158
+
2159
+ @property
2160
+ def tau_shape(self) -> tuple[int, int]:
2161
+ return (self.batches_per_block, self.k)
2162
+
2163
+ @property
2164
+ def tau_strides(self) -> tuple[int, int]:
2165
+ return (self.tau_shape[1], 1)
2166
+
2167
+ @property
2168
+ def a_shape(self) -> tuple[int, int, int]:
2169
+ rows = self.m if self.side == "left" else self.n
2170
+ return (self.batches_per_block, rows, self.k)
2171
+
2172
+ @property
2173
+ def c_shape(self) -> tuple[int, int, int]:
2174
+ return (self.batches_per_block, self.m, self.n)
2175
+
2176
+ def a_strides(self, *, lda: int | None = None) -> tuple[int, int, int]:
2177
+ lda = self.lda if lda is None else lda
2178
+ return _calculate_strides(self.a_shape[1:], lda, self.a_arrangement)
2179
+
2180
+ def c_strides(self, *, ldc: int | None = None) -> tuple[int, int, int]:
2181
+ ldc = self.ldb if ldc is None else ldc
2182
+ return _calculate_strides(self.c_shape[1:], ldc, self.b_arrangement)
2183
+
2184
+ def a_size(self, *, lda: int | None = None) -> int:
2185
+ return self.a_strides(lda=lda)[0] * self.a_shape[0]
2186
+
2187
+ def c_size(self, *, ldc: int | None = None) -> int:
2188
+ return self.c_strides(ldc=ldc)[0] * self.c_shape[0]
2189
+
2190
+ @property
2191
+ def tau_size(self) -> int:
2192
+ return self.tau_shape[0] * self.tau_shape[1]
2193
+
2194
+ # ==========================
2195
+ # Device function methods
2196
+ # ==========================
2197
+
2198
+ def multiply(self, a, tau, c, lda=None, ldc=None) -> None:
2199
+ """
2200
+ Multiplies matrix C by the unitary matrix Q from a QR factorization.
2201
+
2202
+ This device function computes:
2203
+ ``op(Q) * C`` (if ``side='left'``)
2204
+ ``C * op(Q)`` (if ``side='right'``)
2205
+
2206
+ where Q is the unitary matrix from the QR factorization, represented
2207
+ by Householder vectors stored in A and the tau array.
2208
+ Uses cuSOLVERDx ``'unmqr'``. The result overwrites matrix C.
2209
+
2210
+ If ``lda`` and ``ldc`` are provided, uses runtime version with the
2211
+ specified leading dimensions. If not provided (``None``), uses compile-time
2212
+ version with default or constructor-provided leading dimensions.
2213
+
2214
+ For more details, see: :cusolverdx_doc:`get_started/functions/unmqr.html`
2215
+
2216
+ Args:
2217
+ a: Pointer to an array in shared memory, storing the batched matrix containing
2218
+ Householder vectors from the QR factorization, according to the specified
2219
+ arrangement and leading dimension (see :class:`QRMultiply`).
2220
+ The elements below the diagonal of A, with the array tau, represent the
2221
+ unitary matrix Q as a product of Householder reflections.
2222
+ If ``side='left'``, A is ``M`` x ``K``.
2223
+ If ``side='right'``, A is ``N`` x ``K``.
2224
+ tau: Pointer to a 1D array of size K for each batch, containing the scalar
2225
+ factors of the Householder reflections from the QR factorization.
2226
+ The tau array, together with the Householder vectors in A, defines Q.
2227
+ c: Pointer to an array in shared memory,
2228
+ storing the batched ``M`` x ``N`` matrix
2229
+ according to the specified arrangement
2230
+ and leading dimension (see :class:`QRMultiply`).
2231
+ The operation is in-place: result overwrites C.
2232
+ lda: Optional runtime leading dimension for matrix A.
2233
+ The ``lda`` and ``ldc`` must be specified together.
2234
+ If not specified, the compile-time ``lda`` is used.
2235
+ ldc: Optional runtime leading dimension for matrix C.
2236
+ The ``lda`` and ``ldc`` must be specified together.
2237
+ If not specified, the compile-time ``ldc`` is used.
2238
+ """
2239
+ raise RuntimeError("multiply should not be called directly outside of a jitted kernel.")
2240
+
2241
+
2242
+ # ==========================
2243
+ # LQ Multiplier (UNMLQ)
2244
+ # ==========================
2245
+
2246
+ LQ_MULTIPLIER_DOCSTRING = SOLVER_DOCSTRING.copy()
2247
+ del LQ_MULTIPLIER_DOCSTRING["function"]
2248
+ del LQ_MULTIPLIER_DOCSTRING["fill_mode"]
2249
+ del LQ_MULTIPLIER_DOCSTRING["diag"]
2250
+ del LQ_MULTIPLIER_DOCSTRING["job"]
2251
+
2252
+ LQ_MULTIPLIER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
2253
+ ``M`` and ``N`` represent the dimensions of matrix C.
2254
+ ``K`` represents the number of Householder reflections from the LQ factorization.
2255
+ If ``side='left'``, then ``K <= M`` and A is ``K`` x ``M``.
2256
+ If ``side='right'``, then ``K <= N`` and A is ``K`` x ``N``.""".replace("\n", " ")
2257
+
2258
+ LQ_MULTIPLIER_DOCSTRING["side"] = f"""Side of matrix Q in the multiplication operation.
2259
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_SIDE)}.
2260
+ If ``side='left'``, computes op(Q) * C where Q is ``M`` x ``M``.
2261
+ If ``side='right'``, computes C * op(Q) where Q is ``N`` x ``N``.""".replace("\n", " ")
2262
+
2263
+ LQ_MULTIPLIER_DOCSTRING["transpose_mode"] = f"""Transpose mode for operation op(Q) applied to matrix Q.
2264
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_TRANSPOSE_MODE)}.
2265
+ Defaults to ``'non_transposed'``.""".replace("\n", " ")
2266
+
2267
+ LQ_MULTIPLIER_DOCSTRING["leading_dimensions"] = SOLVER_DOCSTRING["leading_dimensions"] + ADAPTERS_API_LD_DOCSTRING
2268
+
2269
+
2270
+ @docstring_decorator(LQ_MULTIPLIER_DOCSTRING, skip_missing=False)
2271
+ class LQMultiply(_SolverProperties):
2272
+ """
2273
+ A class that encapsulates the multiplication of a matrix C by the unitary matrix Q
2274
+ from an LQ factorization (UNMLQ operation).
2275
+
2276
+ **Memory Layout Requirements:**
2277
+
2278
+ Matrices must be stored in shared memory according
2279
+ to their arrangement and leading dimension (ld):
2280
+
2281
+ **For matrix A (containing Householder vectors):**
2282
+
2283
+ * If ``side='left'``: A is ``K`` x ``M``
2284
+ * If ``side='right'``: A is ``K`` x ``N``
2285
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, K, cols)``
2286
+ with strides ``(lda * cols, 1, lda)``
2287
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, K, cols)``
2288
+ with strides ``(lda * K, lda, 1)``
2289
+
2290
+ **For matrix C (M x N):**
2291
+
2292
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
2293
+ with strides ``(ldc * N, 1, ldc)``
2294
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
2295
+ with strides ``(ldc * M, ldc, 1)``
2296
+
2297
+ Args:
2298
+ size (Sequence[int]): {size}
2299
+
2300
+ precision (type[np.floating]): {precision}
2301
+
2302
+ execution (str): {execution}
2303
+
2304
+ side (str): {side}
2305
+
2306
+ sm (ComputeCapability): {sm}
2307
+
2308
+ transpose_mode (str, optional): {transpose_mode}
2309
+
2310
+ arrangement (Sequence[str], optional): {arrangement}
2311
+
2312
+ batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
2313
+
2314
+ data_type (str, optional): {data_type}
2315
+
2316
+ leading_dimensions (Sequence[int], optional): {leading_dimensions}
2317
+
2318
+ block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
2319
+
2320
+ See Also:
2321
+ For further details, please refer to the cuSOLVERDx documentation:
2322
+
2323
+ * :cusolverdx_doc:`unmlq <get_started/functions/unmlq.html>`
2324
+ """
2325
+
2326
+ # ==========================
2327
+ # Constructor
2328
+ # ==========================
2329
+
2330
+ def __init__(
2331
+ self,
2332
+ size: Sequence[int],
2333
+ precision: type[np.floating],
2334
+ execution: str,
2335
+ side: str,
2336
+ *,
2337
+ sm=None,
2338
+ transpose_mode: str = "non_transposed",
2339
+ arrangement: Sequence[str] | None = None,
2340
+ batches_per_block: int | Literal["suggested"] | None = None,
2341
+ data_type: str | None = None,
2342
+ leading_dimensions: Sequence[int] | None = None,
2343
+ block_dim: Sequence[int] | Literal["suggested"] | None = None,
2344
+ ):
2345
+ self._multiply: Solver = Solver(
2346
+ function="unmlq",
2347
+ size=size,
2348
+ precision=precision,
2349
+ execution=execution,
2350
+ sm=sm,
2351
+ side=side,
2352
+ transpose_mode=transpose_mode,
2353
+ arrangement=arrangement,
2354
+ batches_per_block=batches_per_block,
2355
+ data_type=data_type,
2356
+ leading_dimensions=leading_dimensions,
2357
+ block_dim=block_dim,
2358
+ )
2359
+
2360
+ super().__init__(self._multiply)
2361
+
2362
+ if not isinstance(self._multiply.transpose_mode, str):
2363
+ raise ValueError("transpose_mode must be provided for LQMultiply")
2364
+
2365
+ if not isinstance(self._multiply.side, str):
2366
+ raise ValueError("side must be provided for LQMultiply")
2367
+
2368
+ # ==========================
2369
+ # Property methods
2370
+ # ==========================
2371
+
2372
+ @property
2373
+ def side(self) -> str:
2374
+ assert self._multiply.side is not None
2375
+ return self._multiply.side
2376
+
2377
+ @property
2378
+ def transpose_mode(self) -> str:
2379
+ assert self._multiply.transpose_mode is not None
2380
+ return self._multiply.transpose_mode
2381
+
2382
+ @property
2383
+ def tau_type(self) -> type[np.floating] | Complex:
2384
+ return self.value_type
2385
+
2386
+ @property
2387
+ def tau_shape(self) -> tuple[int, int]:
2388
+ return (self.batches_per_block, self.k)
2389
+
2390
+ @property
2391
+ def tau_strides(self) -> tuple[int, int]:
2392
+ return (self.tau_shape[1], 1)
2393
+
2394
+ @property
2395
+ def a_shape(self) -> tuple[int, int, int]:
2396
+ cols = self.m if self.side == "left" else self.n
2397
+ return (self.batches_per_block, self.k, cols)
2398
+
2399
+ @property
2400
+ def c_shape(self) -> tuple[int, int, int]:
2401
+ return (self.batches_per_block, self.m, self.n)
2402
+
2403
+ def a_strides(self, *, lda: int | None = None) -> tuple[int, int, int]:
2404
+ lda = self.lda if lda is None else lda
2405
+ return _calculate_strides(self.a_shape[1:], lda, self.a_arrangement)
2406
+
2407
+ def c_strides(self, *, ldc: int | None = None) -> tuple[int, int, int]:
2408
+ ldc = self.ldb if ldc is None else ldc
2409
+ return _calculate_strides(self.c_shape[1:], ldc, self.b_arrangement)
2410
+
2411
+ def a_size(self, *, lda: int | None = None) -> int:
2412
+ return self.a_strides(lda=lda)[0] * self.a_shape[0]
2413
+
2414
+ def c_size(self, *, ldc: int | None = None) -> int:
2415
+ return self.c_strides(ldc=ldc)[0] * self.c_shape[0]
2416
+
2417
+ @property
2418
+ def tau_size(self) -> int:
2419
+ return self.tau_shape[0] * self.tau_shape[1]
2420
+
2421
+ # ==========================
2422
+ # Device function methods
2423
+ # ==========================
2424
+
2425
+ def multiply(self, a, tau, c, lda=None, ldc=None) -> None:
2426
+ """
2427
+ Multiplies matrix C by the unitary matrix Q from an LQ factorization.
2428
+
2429
+ This device function computes:
2430
+ ``op(Q) * C`` (if ``side='left'``)
2431
+ ``C * op(Q)`` (if ``side='right'``)
2432
+
2433
+ where Q is the unitary matrix from the LQ factorization, represented
2434
+ by Householder vectors stored in A and the tau array.
2435
+ Uses cuSOLVERDx ``'unmlq'``. The result overwrites matrix C.
2436
+
2437
+ If ``lda`` and ``ldc`` are provided, uses runtime version with the
2438
+ specified leading dimensions. If not provided (``None``), uses compile-time
2439
+ version with default or constructor-provided leading dimensions.
2440
+
2441
+ For more details, see: :cusolverdx_doc:`get_started/functions/unmlq.html`
2442
+
2443
+ Args:
2444
+ a: Pointer to an array in shared memory, storing the batched matrix containing
2445
+ Householder vectors from the LQ factorization, according to the specified
2446
+ arrangement and leading dimension (see :class:`LQMultiply`).
2447
+ The elements above the diagonal of A, with the array tau, represent the
2448
+ unitary matrix Q as a product of Householder reflections.
2449
+ If ``side='left'``, A is ``K`` x ``M``.
2450
+ If ``side='right'``, A is ``K`` x ``N``.
2451
+ tau: Pointer to a 1D array of size K for each batch, containing the scalar
2452
+ factors of the Householder reflections from the LQ factorization.
2453
+ The tau array, together with the Householder vectors in A, defines Q.
2454
+ c: Pointer to an array in shared memory,
2455
+ storing the batched ``M`` x ``N`` matrix
2456
+ according to the specified arrangement
2457
+ and leading dimension (see :class:`LQMultiply`).
2458
+ The operation is in-place: result overwrites C.
2459
+ lda: Optional runtime leading dimension for matrix A.
2460
+ The ``lda`` and ``ldc`` must be specified together.
2461
+ If not specified, the compile-time ``lda`` is used.
2462
+ ldc: Optional runtime leading dimension for matrix C.
2463
+ The ``lda`` and ``ldc`` must be specified together.
2464
+ If not specified, the compile-time ``ldc`` is used.
2465
+ """
2466
+ raise RuntimeError("multiply should not be called directly outside of a jitted kernel.")
2467
+
2468
+
2469
+ # ==========================
2470
+ # Least Squares Solver
2471
+ # ==========================
2472
+
2473
+ LEAST_SQUARES_SOLVER_DOCSTRING = SOLVER_DOCSTRING.copy()
2474
+ del LEAST_SQUARES_SOLVER_DOCSTRING["function"]
2475
+ del LEAST_SQUARES_SOLVER_DOCSTRING["fill_mode"]
2476
+ del LEAST_SQUARES_SOLVER_DOCSTRING["side"]
2477
+ del LEAST_SQUARES_SOLVER_DOCSTRING["diag"]
2478
+
2479
+ LEAST_SQUARES_SOLVER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
2480
+ ``M`` and ``N`` represent the dimensions of matrix A (``M`` x ``N``).
2481
+ ``K`` represents the number of columns in the right-hand side matrix B.""".replace("\n", " ")
2482
+
2483
+ LEAST_SQUARES_SOLVER_DOCSTRING["transpose_mode"] = f"""Transpose mode for operation op(A) applied to matrix A.
2484
+ Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_TRANSPOSE_MODE)}.
2485
+ Defaults to ``'non_transposed'``.""".replace("\n", " ")
2486
+
2487
+ LEAST_SQUARES_SOLVER_DOCSTRING["leading_dimensions"] = SOLVER_DOCSTRING["leading_dimensions"] + ADAPTERS_API_LD_DOCSTRING
2488
+
2489
+
2490
+ @docstring_decorator(LEAST_SQUARES_SOLVER_DOCSTRING, skip_missing=False)
2491
+ class LeastSquaresSolver(_SolverProperties):
2492
+ """
2493
+ A class that encapsulates least squares solver device function (``'gels'``).
2494
+ GELS (GEneral Least Squares) solves overdetermined
2495
+ or underdetermined least squares problems:
2496
+
2497
+ .. math::
2498
+ \\min \\| op(A) * X - B \\|_2
2499
+
2500
+ using the QR or LQ factorization of A, and overwriting B with the solution X.
2501
+
2502
+ **Memory layout requirements:**
2503
+
2504
+ Matrices must be stored in shared memory according
2505
+ to their arrangement and leading dimension (ld):
2506
+
2507
+ **For matrix A (M x N):**
2508
+
2509
+ * **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
2510
+ with strides ``(lda * N, 1, lda)``
2511
+ * **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
2512
+ with strides ``(lda * M, lda, 1)``
2513
+
2514
+ **Matrix B and X** are stored in the same buffer (B is overwritten by X in place).
2515
+ The buffer has shape ``(batches_per_block, max(M, N), K)``. The second leading
2516
+ dimension ``ldb`` refers to this shared B/X buffer
2517
+ and must satisfy ``ldb >= max(M, N)``.
2518
+ Logical shapes differ by :attr:`transpose_mode`.
2519
+ Use :attr:`b_shape` and :attr:`x_shape` for B and X.
2520
+
2521
+ **Matrix B** (right-hand side):
2522
+
2523
+ * **Logical shape**: :attr:`b_shape` - ``(batches_per_block, M, K)`` if non-transposed,
2524
+ ``(batches_per_block, N, K)`` if transposed.
2525
+ * **Column-major arrangement**: strides ``(ldb * K, 1, ldb)``.
2526
+ * **Row-major arrangement**: strides ``(ldb * max(M, N), ldb, 1)``.
2527
+
2528
+ **Matrix X** (solution):
2529
+
2530
+ * **Logical shape**: :attr:`x_shape` - ``(batches_per_block, N, K)`` if non-transposed,
2531
+ ``(batches_per_block, M, K)`` if transposed.
2532
+ * **Column-major arrangement**: strides ``(ldb * K, 1, ldb)``.
2533
+ * **Row-major arrangement**: strides ``(ldb * max(M, N), ldb, 1)``.
2534
+
2535
+ .. note::
2536
+ GELS is an in-place function. Matrix A is overwritten by the QR or LQ factorization,
2537
+ and matrix B is overwritten by the solution X. Both B and X use the single buffer
2538
+ of shape ``(max(M, N), K)`` per batch.
2539
+
2540
+ Args:
2541
+ size (Sequence[int]): {size}
2542
+
2543
+ precision (type[np.floating]): {precision}
2544
+
2545
+ execution (str): {execution}
2546
+
2547
+ sm (ComputeCapability): {sm}
2548
+
2549
+ transpose_mode (str, optional): {transpose_mode}
2550
+
2551
+ arrangement (Sequence[str], optional): {arrangement}
2552
+
2553
+ batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
2554
+
2555
+ data_type (str, optional): {data_type}
2556
+
2557
+ leading_dimensions (Sequence[int], optional): {leading_dimensions}
2558
+
2559
+ block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
2560
+
2561
+ See Also:
2562
+ For further details, please refer to the cuSOLVERDx documentation:
2563
+
2564
+ * :cusolverdx_doc:`gels <get_started/functions/gels.html>`
2565
+ """
2566
+
2567
+ # ==========================
2568
+ # Constructor
2569
+ # ==========================
2570
+
2571
+ def __init__(
2572
+ self,
2573
+ size: Sequence[int],
2574
+ precision: type[np.floating],
2575
+ execution: str,
2576
+ *,
2577
+ sm=None,
2578
+ transpose_mode: str = "non_transposed",
2579
+ arrangement: Sequence[str] | None = None,
2580
+ batches_per_block: int | Literal["suggested"] | None = None,
2581
+ data_type: str | None = None,
2582
+ leading_dimensions: Sequence[int] | None = None,
2583
+ block_dim: Sequence[int] | Literal["suggested"] | None = None,
2584
+ ):
2585
+ self._solve: Solver = Solver(
2586
+ function="gels",
2587
+ size=size,
2588
+ precision=precision,
2589
+ execution=execution,
2590
+ sm=sm,
2591
+ transpose_mode=transpose_mode,
2592
+ arrangement=arrangement,
2593
+ batches_per_block=batches_per_block,
2594
+ data_type=data_type,
2595
+ leading_dimensions=leading_dimensions,
2596
+ block_dim=block_dim,
2597
+ )
2598
+
2599
+ super().__init__(self._solve)
2600
+
2601
+ # ==========================
2602
+ # Property methods
2603
+ # ==========================
2604
+
2605
+ @property
2606
+ def transpose_mode(self) -> str:
2607
+ assert self._solve.transpose_mode is not None
2608
+ return self._solve.transpose_mode
2609
+
2610
+ @property
2611
+ def tau_type(self) -> type[np.floating] | Complex:
2612
+ return self._solve.tau_type
2613
+
2614
+ @property
2615
+ def tau_shape(self) -> tuple[int, int]:
2616
+ return (self.batches_per_block, min(self.m, self.n))
2617
+
2618
+ @property
2619
+ def tau_strides(self) -> tuple[int, int]:
2620
+ return (self.tau_shape[1], 1)
2621
+
2622
+ @property
2623
+ def a_shape(self) -> tuple[int, int, int]:
2624
+ return (self.batches_per_block, self.m, self.n)
2625
+
2626
+ @property
2627
+ def b_shape(self) -> tuple[int, int, int]:
2628
+ return (self.batches_per_block, self.m if self.transpose_mode == "non_transposed" else self.n, self.k)
2629
+
2630
+ @property
2631
+ def x_shape(self) -> tuple[int, int, int]:
2632
+ return (self.batches_per_block, self.n if self.transpose_mode == "non_transposed" else self.m, self.k)
2633
+
2634
+ def a_strides(self, *, lda: int | None = None) -> tuple[int, int, int]:
2635
+ lda = self.lda if lda is None else lda
2636
+ return _calculate_strides(self.a_shape[1:], lda, self.a_arrangement)
2637
+
2638
+ def bx_strides(self, *, ldb: int | None = None) -> tuple[int, int, int]:
2639
+ ldb = self.ldb if ldb is None else ldb
2640
+ return _calculate_strides((max(self.m, self.n), self.k), ldb, self.b_arrangement)
2641
+
2642
+ def a_size(self, *, lda: int | None = None) -> int:
2643
+ return self.a_strides(lda=lda)[0] * self.a_shape[0]
2644
+
2645
+ def bx_size(self, *, ldb: int | None = None) -> int:
2646
+ return self.bx_strides(ldb=ldb)[0] * self.batches_per_block
2647
+
2648
+ @property
2649
+ def tau_size(self) -> int:
2650
+ return self.tau_shape[0] * self.tau_shape[1]
2651
+
2652
+ # ==========================
2653
+ # Device function methods
2654
+ # ==========================
2655
+
2656
+ def solve(self, a, tau, b, lda=None, ldb=None) -> None:
2657
+ """
2658
+ Solves a least squares problem using QR or LQ factorization.
2659
+
2660
+ This device function solves:
2661
+
2662
+ .. math::
2663
+ \\min \\| op(A) * X - B \\|_2
2664
+
2665
+ using the QR or LQ factorization of A, and overwrites B with the solution X.
2666
+ Uses cuSOLVERDx ``'gels'``. The operation is in-place: matrix A is overwritten
2667
+ by the factorization, and matrix B is overwritten by the solution X.
2668
+
2669
+ If ``lda`` and ``ldb`` are provided, uses runtime version with the
2670
+ specified leading dimensions. If not provided (``None``), uses compile-time
2671
+ version with default or constructor-provided leading dimensions.
2672
+
2673
+ .. note::
2674
+ The choice between QR and LQ factorization depends on the problem dimensions
2675
+ and transpose mode:
2676
+
2677
+ * If ``op(A)`` is ``'non_transposed'`` and ``M >= N``: uses QR factorization
2678
+ * If ``op(A)`` is ``'non_transposed'`` and ``M < N``: uses LQ factorization
2679
+ * If ``op(A)`` is ``'transposed'`` or ``'conj_transposed'``
2680
+ and ``M >= N``: uses LQ factorization
2681
+ * If ``op(A)`` is ``'transposed'`` or ``'conj_transposed'``
2682
+ and ``M < N``: uses QR factorization
2683
+
2684
+ For more details, see: :cusolverdx_doc:`get_started/functions/gels.html`
2685
+
2686
+ Args:
2687
+ a: Pointer to an array in shared memory, storing the batched matrix
2688
+ according to the specified arrangement
2689
+ and leading dimension (see :class:`LeastSquaresSolver`).
2690
+ The matrix is overwritten in place by the QR or LQ factorization.
2691
+ tau: Pointer to a 1D array of size min(M, N) for each batch.
2692
+ Contains the scalar factors of the Householder reflections.
2693
+ The tau array, together with the Householder vectors stored in A,
2694
+ defines the unitary matrix Q.
2695
+ b: Pointer to an array in shared memory,
2696
+ storing the batched right-hand side matrix
2697
+ according to the specified arrangement
2698
+ and leading dimension (see :class:`LeastSquaresSolver`).
2699
+ The storage size is ``max(M, N) x K`` per batch.
2700
+ The operation is in-place: result X overwrites B.
2701
+ lda: Optional runtime leading dimension for matrix A.
2702
+ The ``lda`` and ``ldb`` must be specified together.
2703
+ If not specified, the compile-time ``lda`` is used.
2704
+ ldb: Optional runtime leading dimension for matrix B.
2705
+ The ``lda`` and ``ldb`` must be specified together.
2706
+ If not specified, the compile-time ``ldb`` is used.
2707
+ """
2708
+ raise RuntimeError("solve should not be called directly outside of a jitted kernel.")