nvmath-python 0.9.0__cp314-cp314-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 (309) 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 +383 -0
  7. nvmath/_utils.py +150 -0
  8. nvmath/bindings/__init__.py +51 -0
  9. nvmath/bindings/_internal/__init__.pxd +0 -0
  10. nvmath/bindings/_internal/__init__.py +0 -0
  11. nvmath/bindings/_internal/cublas.cp314-win_amd64.pyd +0 -0
  12. nvmath/bindings/_internal/cublas.pxd +530 -0
  13. nvmath/bindings/_internal/cublasLt.cp314-win_amd64.pyd +0 -0
  14. nvmath/bindings/_internal/cublasLt.pxd +59 -0
  15. nvmath/bindings/_internal/cublasMp.pxd +52 -0
  16. nvmath/bindings/_internal/cudss.cp314-win_amd64.pyd +0 -0
  17. nvmath/bindings/_internal/cudss.pxd +47 -0
  18. nvmath/bindings/_internal/cufft.cp314-win_amd64.pyd +0 -0
  19. nvmath/bindings/_internal/cufft.pxd +70 -0
  20. nvmath/bindings/_internal/cufftMp.pxd +77 -0
  21. nvmath/bindings/_internal/curand.cp314-win_amd64.pyd +0 -0
  22. nvmath/bindings/_internal/curand.pxd +42 -0
  23. nvmath/bindings/_internal/cusolver.cp314-win_amd64.pyd +0 -0
  24. nvmath/bindings/_internal/cusolver.pxd +15 -0
  25. nvmath/bindings/_internal/cusolverDn.cp314-win_amd64.pyd +0 -0
  26. nvmath/bindings/_internal/cusolverDn.pxd +406 -0
  27. nvmath/bindings/_internal/cusparse.cp314-win_amd64.pyd +0 -0
  28. nvmath/bindings/_internal/cusparse.pxd +277 -0
  29. nvmath/bindings/_internal/cusparseLt.cp314-win_amd64.pyd +0 -0
  30. nvmath/bindings/_internal/cusparseLt.pxd +48 -0
  31. nvmath/bindings/_internal/cutensor.cp314-win_amd64.pyd +0 -0
  32. nvmath/bindings/_internal/cutensor.pxd +58 -0
  33. nvmath/bindings/_internal/mathdx.cp314-win_amd64.pyd +0 -0
  34. nvmath/bindings/_internal/mathdx.pxd +116 -0
  35. nvmath/bindings/_internal/nvshmem.pxd +29 -0
  36. nvmath/bindings/_internal/utils.cp314-win_amd64.pyd +0 -0
  37. nvmath/bindings/_internal/utils.pxd +179 -0
  38. nvmath/bindings/_internal/utils.pyi +10 -0
  39. nvmath/bindings/cublas.cp314-win_amd64.pyd +0 -0
  40. nvmath/bindings/cublas.pxd +558 -0
  41. nvmath/bindings/cublas.pyi +746 -0
  42. nvmath/bindings/cublasLt.cp314-win_amd64.pyd +0 -0
  43. nvmath/bindings/cublasLt.pxd +109 -0
  44. nvmath/bindings/cublasLt.pyi +1337 -0
  45. nvmath/bindings/cublasMp.pxd +85 -0
  46. nvmath/bindings/cublasMp.pyi +219 -0
  47. nvmath/bindings/cudss.cp314-win_amd64.pyd +0 -0
  48. nvmath/bindings/cudss.pxd +86 -0
  49. nvmath/bindings/cudss.pyi +282 -0
  50. nvmath/bindings/cufft.cp314-win_amd64.pyd +0 -0
  51. nvmath/bindings/cufft.pxd +118 -0
  52. nvmath/bindings/cufft.pyi +241 -0
  53. nvmath/bindings/cufftMp.pxd +124 -0
  54. nvmath/bindings/cufftMp.pyi +260 -0
  55. nvmath/bindings/curand.cp314-win_amd64.pyd +0 -0
  56. nvmath/bindings/curand.pxd +71 -0
  57. nvmath/bindings/curand.pyi +159 -0
  58. nvmath/bindings/cusolver.cp314-win_amd64.pyd +0 -0
  59. nvmath/bindings/cusolver.pxd +62 -0
  60. nvmath/bindings/cusolver.pyi +242 -0
  61. nvmath/bindings/cusolverDn.cp314-win_amd64.pyd +0 -0
  62. nvmath/bindings/cusolverDn.pxd +430 -0
  63. nvmath/bindings/cusolverDn.pyi +416 -0
  64. nvmath/bindings/cusparse.cp314-win_amd64.pyd +0 -0
  65. nvmath/bindings/cusparse.pxd +338 -0
  66. nvmath/bindings/cusparse.pyi +659 -0
  67. nvmath/bindings/cusparseLt.cp314-win_amd64.pyd +0 -0
  68. nvmath/bindings/cusparseLt.pxd +99 -0
  69. nvmath/bindings/cusparseLt.pyi +198 -0
  70. nvmath/bindings/cutensor.cp314-win_amd64.pyd +0 -0
  71. nvmath/bindings/cutensor.pxd +98 -0
  72. nvmath/bindings/cutensor.pyi +294 -0
  73. nvmath/bindings/cycublas.cp314-win_amd64.pyd +0 -0
  74. nvmath/bindings/cycublas.pxd +684 -0
  75. nvmath/bindings/cycublasLt.cp314-win_amd64.pyd +0 -0
  76. nvmath/bindings/cycublasLt.pxd +1055 -0
  77. nvmath/bindings/cycublasMp.pxd +183 -0
  78. nvmath/bindings/cycudss.cp314-win_amd64.pyd +0 -0
  79. nvmath/bindings/cycudss.pxd +224 -0
  80. nvmath/bindings/cycufft.cp314-win_amd64.pyd +0 -0
  81. nvmath/bindings/cycufft.pxd +326 -0
  82. nvmath/bindings/cycufftMp.pxd +334 -0
  83. nvmath/bindings/cycurand.cp314-win_amd64.pyd +0 -0
  84. nvmath/bindings/cycurand.pxd +146 -0
  85. nvmath/bindings/cycusolver.cp314-win_amd64.pyd +0 -0
  86. nvmath/bindings/cycusolver.pxd +154 -0
  87. nvmath/bindings/cycusolverDn.cp314-win_amd64.pyd +0 -0
  88. nvmath/bindings/cycusolverDn.pxd +446 -0
  89. nvmath/bindings/cycusparse.cp314-win_amd64.pyd +0 -0
  90. nvmath/bindings/cycusparse.pxd +470 -0
  91. nvmath/bindings/cycusparseLt.cp314-win_amd64.pyd +0 -0
  92. nvmath/bindings/cycusparseLt.pxd +150 -0
  93. nvmath/bindings/cycutensor.cp314-win_amd64.pyd +0 -0
  94. nvmath/bindings/cycutensor.pxd +192 -0
  95. nvmath/bindings/cymathdx.cp314-win_amd64.pyd +0 -0
  96. nvmath/bindings/cymathdx.pxd +552 -0
  97. nvmath/bindings/cynvshmem.pxd +126 -0
  98. nvmath/bindings/mathdx.cp314-win_amd64.pyd +0 -0
  99. nvmath/bindings/mathdx.pxd +182 -0
  100. nvmath/bindings/mathdx.pyi +1244 -0
  101. nvmath/bindings/nvpl/__init__.pxd +0 -0
  102. nvmath/bindings/nvpl/__init__.py +13 -0
  103. nvmath/bindings/nvpl/_internal/__init__.pxd +0 -0
  104. nvmath/bindings/nvpl/_internal/__init__.py +0 -0
  105. nvmath/bindings/nvpl/_internal/blas.cp314-win_amd64.pyd +0 -0
  106. nvmath/bindings/nvpl/_internal/blas.pxd +125 -0
  107. nvmath/bindings/nvpl/_internal/fft.cp314-win_amd64.pyd +0 -0
  108. nvmath/bindings/nvpl/_internal/fft.pxd +36 -0
  109. nvmath/bindings/nvpl/blas.cp314-win_amd64.pyd +0 -0
  110. nvmath/bindings/nvpl/blas.pxd +85 -0
  111. nvmath/bindings/nvpl/blas.pyi +122 -0
  112. nvmath/bindings/nvpl/cyblas.cp314-win_amd64.pyd +0 -0
  113. nvmath/bindings/nvpl/cyblas.pxd +166 -0
  114. nvmath/bindings/nvpl/cyfft.cp314-win_amd64.pyd +0 -0
  115. nvmath/bindings/nvpl/cyfft.pxd +92 -0
  116. nvmath/bindings/nvpl/fft.cp314-win_amd64.pyd +0 -0
  117. nvmath/bindings/nvpl/fft.pxd +100 -0
  118. nvmath/bindings/nvpl/fft.pyi +100 -0
  119. nvmath/bindings/nvshmem.pxd +54 -0
  120. nvmath/bindings/nvshmem.pyi +179 -0
  121. nvmath/device/__init__.py +21 -0
  122. nvmath/device/_deprecated.py +33 -0
  123. nvmath/device/common.py +313 -0
  124. nvmath/device/common_backend.py +131 -0
  125. nvmath/device/common_cuda.py +201 -0
  126. nvmath/device/common_numba.py +310 -0
  127. nvmath/device/common_opaque_tensor.py +201 -0
  128. nvmath/device/cublasdx.py +1805 -0
  129. nvmath/device/cublasdx_backend.py +807 -0
  130. nvmath/device/cublasdx_numba.py +1612 -0
  131. nvmath/device/cufftdx.py +510 -0
  132. nvmath/device/cufftdx_backend.py +196 -0
  133. nvmath/device/cufftdx_numba.py +196 -0
  134. nvmath/device/curand_kernel.py +9147 -0
  135. nvmath/device/cusolverdx.py +2690 -0
  136. nvmath/device/cusolverdx_backend.py +440 -0
  137. nvmath/device/cusolverdx_numba.py +1624 -0
  138. nvmath/device/llvm_array.py +29 -0
  139. nvmath/device/random.py +445 -0
  140. nvmath/device/random_helpers.py +23 -0
  141. nvmath/device/random_states.py +187 -0
  142. nvmath/device/types.py +147 -0
  143. nvmath/device/vector_types_numba.py +203 -0
  144. nvmath/distributed/__init__.py +205 -0
  145. nvmath/distributed/_internal/__init__.py +0 -0
  146. nvmath/distributed/_internal/nvshmem.py +302 -0
  147. nvmath/distributed/_internal/tensor_ifc.py +67 -0
  148. nvmath/distributed/_internal/tensor_ifc_cupy.py +62 -0
  149. nvmath/distributed/_internal/tensor_ifc_host_device.py +165 -0
  150. nvmath/distributed/_internal/tensor_ifc_numpy.py +41 -0
  151. nvmath/distributed/_internal/tensor_ifc_torch.py +141 -0
  152. nvmath/distributed/_internal/tensor_wrapper.py +78 -0
  153. nvmath/distributed/_utils.py +167 -0
  154. nvmath/distributed/distribution.py +770 -0
  155. nvmath/distributed/fft/__init__.py +7 -0
  156. nvmath/distributed/fft/_configuration.py +79 -0
  157. nvmath/distributed/fft/fft.py +2801 -0
  158. nvmath/distributed/linalg/__init__.py +12 -0
  159. nvmath/distributed/linalg/_internal/__init__.py +3 -0
  160. nvmath/distributed/linalg/_internal/epilog_protocol.py +496 -0
  161. nvmath/distributed/linalg/_internal/matmul_desc_ifc.py +28 -0
  162. nvmath/distributed/linalg/advanced/__init__.py +8 -0
  163. nvmath/distributed/linalg/advanced/_configuration.py +166 -0
  164. nvmath/distributed/linalg/advanced/matmulmod.py +2908 -0
  165. nvmath/distributed/process_group.py +408 -0
  166. nvmath/distributed/reshape/__init__.py +6 -0
  167. nvmath/distributed/reshape/_configuration.py +39 -0
  168. nvmath/distributed/reshape/reshape.py +1256 -0
  169. nvmath/fft/__init__.py +7 -0
  170. nvmath/fft/_configuration.py +208 -0
  171. nvmath/fft/_exec_utils.py +109 -0
  172. nvmath/fft/_helpers.py +237 -0
  173. nvmath/fft/fft.py +2903 -0
  174. nvmath/internal/__init__.pxd +3 -0
  175. nvmath/internal/__init__.py +10 -0
  176. nvmath/internal/_bindings.cp314-win_amd64.pyd +0 -0
  177. nvmath/internal/_bindings.pxd +12 -0
  178. nvmath/internal/_device_utils.py +54 -0
  179. nvmath/internal/enum_utils.py +142 -0
  180. nvmath/internal/formatters.py +87 -0
  181. nvmath/internal/mem_limit.py +51 -0
  182. nvmath/internal/memory.cp314-win_amd64.pyd +0 -0
  183. nvmath/internal/memory.pxd +10 -0
  184. nvmath/internal/memory.pyi +46 -0
  185. nvmath/internal/ndbuffer/__init__.pxd +3 -0
  186. nvmath/internal/ndbuffer/__init__.py +7 -0
  187. nvmath/internal/ndbuffer/copy_kernel/args.h +34 -0
  188. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/array_view.h +52 -0
  189. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/elementwise.h +68 -0
  190. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/grid_indexer.h +69 -0
  191. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/transposed.h +240 -0
  192. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/type_utils.h +39 -0
  193. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/utils.h +132 -0
  194. nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/vec.h +159 -0
  195. nvmath/internal/ndbuffer/copy_kernel/elementwise.h +53 -0
  196. nvmath/internal/ndbuffer/copy_kernel/transposed.h +58 -0
  197. nvmath/internal/ndbuffer/copy_kernel.cp314-win_amd64.pyd +0 -0
  198. nvmath/internal/ndbuffer/copy_kernel.pxd +9 -0
  199. nvmath/internal/ndbuffer/copy_kernel.pyi +9 -0
  200. nvmath/internal/ndbuffer/data_layout.cp314-win_amd64.pyd +0 -0
  201. nvmath/internal/ndbuffer/data_layout.pxd +72 -0
  202. nvmath/internal/ndbuffer/data_layout.pyi +23 -0
  203. nvmath/internal/ndbuffer/jit.cp314-win_amd64.pyd +0 -0
  204. nvmath/internal/ndbuffer/jit.pxd +7 -0
  205. nvmath/internal/ndbuffer/jit.pyi +6 -0
  206. nvmath/internal/ndbuffer/ndbuffer.cp314-win_amd64.pyd +0 -0
  207. nvmath/internal/ndbuffer/ndbuffer.pxd +36 -0
  208. nvmath/internal/ndbuffer/ndbuffer.pyi +41 -0
  209. nvmath/internal/ndbuffer/package_utils.cp314-win_amd64.pyd +0 -0
  210. nvmath/internal/ndbuffer/package_utils.pxd +11 -0
  211. nvmath/internal/ndbuffer/package_utils.pyi +12 -0
  212. nvmath/internal/package_ifc.py +147 -0
  213. nvmath/internal/package_ifc_cuda.py +49 -0
  214. nvmath/internal/package_ifc_cupy.py +49 -0
  215. nvmath/internal/package_ifc_torch.py +31 -0
  216. nvmath/internal/package_wrapper.py +14 -0
  217. nvmath/internal/tensor_ifc.py +194 -0
  218. nvmath/internal/tensor_ifc_cupy.py +241 -0
  219. nvmath/internal/tensor_ifc_ndbuffer.py +153 -0
  220. nvmath/internal/tensor_ifc_numpy.py +183 -0
  221. nvmath/internal/tensor_ifc_torch.py +176 -0
  222. nvmath/internal/tensor_wrapper.py +157 -0
  223. nvmath/internal/typemaps.py +162 -0
  224. nvmath/internal/utils.py +771 -0
  225. nvmath/linalg/__init__.py +46 -0
  226. nvmath/linalg/_internal/__init__.py +3 -0
  227. nvmath/linalg/_internal/algo_cap_ifc.py +82 -0
  228. nvmath/linalg/_internal/algo_config_ifc.py +43 -0
  229. nvmath/linalg/_internal/batch.py +217 -0
  230. nvmath/linalg/_internal/enum_to_tuples.py +64 -0
  231. nvmath/linalg/_internal/epilog_protocol.py +766 -0
  232. nvmath/linalg/_internal/layout.py +624 -0
  233. nvmath/linalg/_internal/matmul_desc_ifc.py +28 -0
  234. nvmath/linalg/_internal/matmul_pref_ifc.py +27 -0
  235. nvmath/linalg/_internal/matrix_layout_ifc.py +26 -0
  236. nvmath/linalg/_internal/typemaps.py +144 -0
  237. nvmath/linalg/_internal/utils.py +134 -0
  238. nvmath/linalg/advanced/__init__.py +8 -0
  239. nvmath/linalg/advanced/_algorithmmod.py +149 -0
  240. nvmath/linalg/advanced/_configuration.py +349 -0
  241. nvmath/linalg/advanced/helpers/__init__.py +5 -0
  242. nvmath/linalg/advanced/helpers/matmul.py +1348 -0
  243. nvmath/linalg/advanced/matmulmod.py +3937 -0
  244. nvmath/linalg/generic/__init__.py +43 -0
  245. nvmath/linalg/generic/_configuration/__init__.py +37 -0
  246. nvmath/linalg/generic/_configuration/layout.py +263 -0
  247. nvmath/linalg/generic/_configuration/match.py +609 -0
  248. nvmath/linalg/generic/_configuration/qualifiers.py +494 -0
  249. nvmath/linalg/generic/_configuration/wrap.py +217 -0
  250. nvmath/linalg/generic/_dtype.py +15 -0
  251. nvmath/linalg/generic/matmulmod.py +1060 -0
  252. nvmath/memory.py +282 -0
  253. nvmath/sparse/__init__.py +38 -0
  254. nvmath/sparse/_internal/__init__.py +21 -0
  255. nvmath/sparse/_internal/common_utils.py +144 -0
  256. nvmath/sparse/_internal/cudss_config_ifc.py +741 -0
  257. nvmath/sparse/_internal/cudss_data_ifc.py +399 -0
  258. nvmath/sparse/_internal/cudss_utils.py +445 -0
  259. nvmath/sparse/_internal/cusparse_utils.py +382 -0
  260. nvmath/sparse/_internal/sparse_bsc_ifc.py +300 -0
  261. nvmath/sparse/_internal/sparse_bsr_ifc.py +303 -0
  262. nvmath/sparse/_internal/sparse_coo_ifc.py +254 -0
  263. nvmath/sparse/_internal/sparse_csc_ifc.py +266 -0
  264. nvmath/sparse/_internal/sparse_csr_ifc.py +266 -0
  265. nvmath/sparse/_internal/sparse_dia_ifc.py +239 -0
  266. nvmath/sparse/_internal/sparse_format_helpers.py +601 -0
  267. nvmath/sparse/_internal/sparse_tensor_ifc.py +121 -0
  268. nvmath/sparse/_internal/sparse_ust_ifc.py +146 -0
  269. nvmath/sparse/_internal/utils.py +56 -0
  270. nvmath/sparse/advanced/__init__.py +7 -0
  271. nvmath/sparse/advanced/_configuration.py +230 -0
  272. nvmath/sparse/advanced/direct_solver.py +1884 -0
  273. nvmath/sparse/generic/__init__.py +7 -0
  274. nvmath/sparse/generic/_configuration.py +129 -0
  275. nvmath/sparse/generic/_helpers.py +137 -0
  276. nvmath/sparse/generic/_thunks.py +21 -0
  277. nvmath/sparse/generic/matmulmod.py +2426 -0
  278. nvmath/sparse/ust/__init__.py +7 -0
  279. nvmath/sparse/ust/_converters.py +378 -0
  280. nvmath/sparse/ust/_drawer.py +552 -0
  281. nvmath/sparse/ust/_emitter.py +553 -0
  282. nvmath/sparse/ust/_jit.py +212 -0
  283. nvmath/sparse/ust/_kernel.py +346 -0
  284. nvmath/sparse/ust/_semiring.py +71 -0
  285. nvmath/sparse/ust/_utils.py +102 -0
  286. nvmath/sparse/ust/interfaces/__init__.py +0 -0
  287. nvmath/sparse/ust/interfaces/torch_interface.py +476 -0
  288. nvmath/sparse/ust/semiring_ops.py +71 -0
  289. nvmath/sparse/ust/tensor.py +1027 -0
  290. nvmath/sparse/ust/tensor_drawer.py +552 -0
  291. nvmath/sparse/ust/tensor_emitter.py +558 -0
  292. nvmath/sparse/ust/tensor_format.py +935 -0
  293. nvmath/sparse/ust/tensor_jit.py +212 -0
  294. nvmath/sparse/ust/tensor_kernel.py +348 -0
  295. nvmath/sparse/ust/tensor_utils.py +380 -0
  296. nvmath/tensor/__init__.py +6 -0
  297. nvmath/tensor/_configuration.py +95 -0
  298. nvmath/tensor/_internal/__init__.py +3 -0
  299. nvmath/tensor/_internal/cutensor_config_ifc.py +247 -0
  300. nvmath/tensor/_internal/cutensor_utils.py +162 -0
  301. nvmath/tensor/_internal/data.py +43 -0
  302. nvmath/tensor/_internal/einsum_parser.py +444 -0
  303. nvmath/tensor/_internal/typemaps.py +96 -0
  304. nvmath/tensor/contract.py +1861 -0
  305. nvmath_python-0.9.0.dist-info/METADATA +127 -0
  306. nvmath_python-0.9.0.dist-info/RECORD +309 -0
  307. nvmath_python-0.9.0.dist-info/WHEEL +5 -0
  308. nvmath_python-0.9.0.dist-info/licenses/LICENSE +177 -0
  309. nvmath_python-0.9.0.dist-info/top_level.txt +2 -0
@@ -0,0 +1,380 @@
1
+ # Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ This module implements utilities for the Universal Sparse Tensor (UST).
7
+ """
8
+
9
+ __all__ = [
10
+ "TensorDecomposer",
11
+ "TensorComposer",
12
+ "TensorConverter",
13
+ ]
14
+
15
+ from collections.abc import Sequence
16
+
17
+ # from numba import njit
18
+ import numpy as np
19
+
20
+ from nvmath.internal import utils
21
+ from nvmath.internal.tensor_wrapper import wrap_operand
22
+
23
+ from ._utils import LevelMap, np_enveloping_type
24
+ from .tensor_format import Add, LevelExpr, LevelFormat, Subtract, is_unique
25
+
26
+
27
+ class TensorDecomposer:
28
+ """
29
+ A class that traverses a Universal Sparse Tensor and calls a functor for every
30
+ stored element. The traversal can be used for conversions and debugging but may
31
+ not necessarily provide the most efficient way of implementing a traversal.
32
+ Also see set_kernel and run_kernel.
33
+
34
+ Args:
35
+ tensor: the UST tensor for traversal
36
+ functor: a function of the form `def visit(dims, val)`
37
+ """
38
+
39
+ def __init__(self, tensor, functor):
40
+ self._tensor = tensor
41
+ self._functor = functor
42
+ self._flist = list(tensor.tensor_format.levels.items())
43
+ self._lvls = [0] * tensor.num_levels # pre-populate
44
+
45
+ def run(self):
46
+ self._iterate(0, 0, None)
47
+
48
+ def _iterate(self, idx, p, lasta):
49
+ # All levels exhausted?
50
+ if idx == self._tensor.num_levels:
51
+ assert lasta is None # consumed add/sub in range
52
+ val = self._tensor.val.tensor
53
+ self._functor(self._tensor.tensor_format.lvl2dim(self._lvls), val[p].item())
54
+ return
55
+ # Inspect level.
56
+ k, v = self._flist[idx]
57
+ if isinstance(v, Sequence):
58
+ fmt, prop = v
59
+ else:
60
+ fmt, prop = v, None
61
+ # Handle level format.
62
+ if fmt == LevelFormat.DENSE or fmt == LevelFormat.BATCH:
63
+ sz = self._tensor.levels[idx]
64
+ for i in range(sz):
65
+ self._lvls[idx] = i
66
+ self._iterate(idx + 1, p * sz + i, lasta)
67
+ elif fmt == LevelFormat.COMPRESSED:
68
+ if isinstance(k, LevelExpr) and isinstance(k.operator, (Add, Subtract)):
69
+ assert lasta is None # no add/sub nesting
70
+ lasta = idx # record last add/sub
71
+ pos = self._tensor.pos(idx).tensor
72
+ crd = self._tensor.crd(idx).tensor
73
+ assert pos.ndim == crd.ndim
74
+ adjust = 0
75
+ if pos.ndim > 1:
76
+ # This only happens for prior BATCH. Correct the higher-dimensions in
77
+ # both pos and crd buffers and adjust position by #nnz per batch.
78
+ assert pos.ndim == idx
79
+ for i in range(idx - 1):
80
+ assert self._flist[i][1] == LevelFormat.BATCH
81
+ pos = pos[self._lvls[i]]
82
+ crd = crd[self._lvls[i]]
83
+ adjust = pos[-1].item() * (p - self._lvls[idx - 1]) // self._tensor.levels[idx - 1]
84
+ p = self._lvls[idx - 1]
85
+ lo = pos[p].item()
86
+ hi = pos[p + 1].item()
87
+ for i in range(lo, hi):
88
+ self._lvls[idx] = crd[i].item()
89
+ self._iterate(idx + 1, i + adjust, lasta)
90
+ elif fmt == LevelFormat.SINGLETON:
91
+ crd = self._tensor.crd(idx).tensor
92
+ assert crd.ndim == 1
93
+ self._lvls[idx] = crd[p].item()
94
+ self._iterate(idx + 1, p, lasta)
95
+ elif fmt == LevelFormat.RANGE:
96
+ assert lasta is not None # single add/sub-range relation
97
+ add, _ = self._flist[lasta]
98
+ isI = k == add.expression2
99
+ di = self._tensor.tensor_format.dimensions.index(k)
100
+ dj = self._tensor.tensor_format.dimensions.index(add.expression1 if isI else add.expression2)
101
+ szi = self._tensor.extents[di]
102
+ szj = self._tensor.extents[dj]
103
+ lsz = self._lvls[lasta]
104
+ if isinstance(add.operator, Add):
105
+ off = lsz
106
+ lo = max(0, off - szj + 1)
107
+ hi = min(szi, off + 1)
108
+ else:
109
+ off = -lsz if isI else lsz
110
+ lo = max(0, off)
111
+ hi = min(szi, szj + off)
112
+ for i in range(lo, hi):
113
+ self._lvls[idx] = i
114
+ self._iterate(idx + 1, p * szi + i, None)
115
+ elif fmt == LevelFormat.DELTA:
116
+ assert is_unique(prop)
117
+ pos = self._tensor.pos(idx).tensor
118
+ crd = self._tensor.crd(idx).tensor
119
+ assert pos.ndim == 1 and crd.ndim == 1
120
+ corig = 0
121
+ lo = pos[p].item()
122
+ hi = pos[p + 1].item()
123
+ for i in range(lo, hi):
124
+ corig += crd[i].item()
125
+ self._lvls[idx] = corig
126
+ self._iterate(idx + 1, i, lasta)
127
+ corig += 1
128
+ else:
129
+ raise TypeError(f"Unsupported: {fmt}")
130
+
131
+
132
+ class TensorComposer:
133
+ """
134
+ A class to build a Universal Sparse Tensor from a coordinate list. This is a convenience
135
+ class for composing UST objects, expected to run on the host, and does not necessarily
136
+ provide the most efficient way of building a UST. Since the builder takes unordered
137
+ dimension indices together with values, it can also be function as intermediate step
138
+ for tensor format conversions.
139
+
140
+ Args:
141
+ source: source tensor
142
+ target: target tensor
143
+ indices: a 2-dim array of shape (dim,nse) with dimension indices (on CPU)
144
+ values: a 1-dim array of shape (nse,) with the stored numerical values (on CPU)
145
+ is_sorted: whether coordinate list is lexicographically sorted
146
+ """
147
+
148
+ def __init__(self, source, target, indices, values, is_sorted=False):
149
+ assert len(indices.shape) == 2 and len(values.shape) == 1, "Internal error."
150
+ fmt = target.tensor_format
151
+ dim, nse = indices.shape
152
+ assert dim == fmt.num_dimensions and nse == values.shape[0]
153
+ lvl = fmt.num_levels
154
+ # Translate dimension indices to level indices.
155
+ if not fmt.is_identity:
156
+ lindices = np.zeros((lvl, nse), dtype=indices.dtype)
157
+ for i in range(nse):
158
+ lindices[:, i] = fmt.dim2lvl(indices[:, i])
159
+ indices = lindices
160
+ is_sorted = False
161
+ # Apply lexicographical sort on level indices.
162
+ if not is_sorted:
163
+ sorted_indices = np.lexsort(indices[::-1])
164
+ indices = indices[:, sorted_indices]
165
+ values = values[sorted_indices]
166
+ # Bookkeeping.
167
+ self._source = source
168
+ self._target = target
169
+ self._lvl = lvl
170
+ self._flist = list(fmt.levels.values())
171
+ self._indices = indices
172
+ self._values = values
173
+ self._pos_sz = np.zeros((lvl,), dtype=indices.dtype)
174
+ self._crd_sz = np.zeros((lvl,), dtype=indices.dtype)
175
+ self._val_sz = 0
176
+
177
+ def run(self, stream=None):
178
+ dim, nse = self._indices.shape
179
+
180
+ # Scan data to find sizes. Note that for some forms (like COO and CSR), all sizes
181
+ # can easily be computed deterministically without a first scan. For simplicity,
182
+ # we use the scan for both analysis and insertion, however.
183
+ self._insert_builder(0, lo=0, hi=nse, is_insert=False)
184
+
185
+ # Pre-allocate and reset sizes.
186
+ package = self._source._dense_tensorholder_type
187
+ device_id = self._source.device_id # no device migration in convert()
188
+ stream_holder = None
189
+ if device_id != "cpu":
190
+ stream_holder = utils.get_or_create_stream(device_id, stream, package.name)
191
+ self._pos = LevelMap()
192
+ self._crd = LevelMap()
193
+ for idx in range(self._lvl):
194
+ self._pos[idx] = utils.create_empty_tensor(
195
+ package,
196
+ extents=(self._pos_sz[idx],),
197
+ dtype=self._target.index_type,
198
+ device_id=device_id,
199
+ stream_holder=stream_holder,
200
+ verify_strides=False,
201
+ )
202
+ self._pos_sz[idx] = 0
203
+ self._crd[idx] = utils.create_empty_tensor(
204
+ package,
205
+ extents=(self._crd_sz[idx],),
206
+ dtype=self._target.index_type,
207
+ device_id=device_id,
208
+ stream_holder=stream_holder,
209
+ verify_strides=False,
210
+ )
211
+ self._crd_sz[idx] = 0
212
+ self._val = utils.create_empty_tensor(
213
+ package,
214
+ extents=(self._val_sz,),
215
+ dtype=self._target.dtype,
216
+ device_id=device_id,
217
+ stream_holder=stream_holder,
218
+ verify_strides=False,
219
+ )
220
+ self._val_sz = 0
221
+
222
+ # Scan to perform the actual insertion.
223
+ self._insert_builder(0, lo=0, hi=nse, is_insert=True)
224
+ return (self._pos, self._crd, self._val)
225
+
226
+ def _insert_builder(self, idx, lo, hi, is_insert):
227
+ # All levels exhausted?
228
+ if idx == self._lvl:
229
+ assert lo < hi
230
+ self._append_val(self._values[lo].item(), 1, is_insert)
231
+ return
232
+ # Handle segments.
233
+ full = 0
234
+ while lo < hi:
235
+ # Find segment.
236
+ crd = self._indices[idx][lo]
237
+ seg = lo + 1
238
+ if is_unique(self._get_prop(idx)):
239
+ while seg < hi and self._indices[idx][seg] == crd:
240
+ seg += 1
241
+ # Handle level format.
242
+ fmt = self._get_format(idx)
243
+ if fmt == LevelFormat.DENSE or fmt == LevelFormat.RANGE:
244
+ assert crd >= full
245
+ if crd > full:
246
+ self._segment_builder(idx + 1, 0, crd - full, is_insert)
247
+ elif fmt == LevelFormat.COMPRESSED or fmt == LevelFormat.SINGLETON:
248
+ self._append_crd(idx, crd, 1, is_insert)
249
+ elif fmt == LevelFormat.DELTA:
250
+ d = self._get_prop(idx)
251
+ assert isinstance(d, int)
252
+ mDelta = (1 << d) - 1
253
+ delta = crd - full
254
+ while delta > mDelta:
255
+ self._append_crd(idx, mDelta, 1, is_insert)
256
+ self._append_val(0, 1, is_insert)
257
+ delta -= mDelta + 1
258
+ self._append_crd(idx, delta, 1, is_insert)
259
+ else:
260
+ raise TypeError(f"Unsupported: {fmt}")
261
+ full = crd + 1
262
+ self._insert_builder(idx + 1, lo, seg, is_insert)
263
+ lo = seg # next segment
264
+ # Done with segments.
265
+ self._segment_builder(idx, full, 1, is_insert)
266
+
267
+ def _segment_builder(self, idx, full, repeat, is_insert):
268
+ # All levels exhausted?
269
+ if idx == self._lvl:
270
+ self._append_val(0, repeat, is_insert)
271
+ return
272
+ # Handle level format.
273
+ fmt = self._get_format(idx)
274
+ if fmt == LevelFormat.DENSE or fmt == LevelFormat.RANGE:
275
+ sz = self._target.levels[idx]
276
+ assert sz >= full
277
+ self._segment_builder(idx + 1, 0, repeat * (sz - full), is_insert)
278
+ elif fmt == LevelFormat.COMPRESSED or fmt == LevelFormat.DELTA:
279
+ self._append_pos(idx, self._crd_sz[idx], repeat, is_insert)
280
+ elif fmt == LevelFormat.SINGLETON:
281
+ pass # nothing to do for singleton
282
+ else:
283
+ raise TypeError(f"Unsupported: {fmt}")
284
+
285
+ def _append_pos(self, idx, pos, repeat, is_insert):
286
+ if self._pos_sz[idx] == 0: # append initial 0 at start
287
+ if is_insert:
288
+ self._pos[idx].tensor[0] = 0
289
+ self._pos_sz[idx] = 1
290
+ if is_insert:
291
+ start = self._pos_sz[idx]
292
+ self._pos[idx].tensor[start : start + repeat] = pos
293
+ self._pos_sz[idx] += repeat
294
+
295
+ def _append_crd(self, idx, crd, repeat, is_insert):
296
+ if is_insert:
297
+ start = self._crd_sz[idx]
298
+ self._crd[idx].tensor[start : start + repeat] = crd
299
+ self._crd_sz[idx] += repeat
300
+
301
+ def _append_val(self, val, repeat, is_insert):
302
+ if is_insert:
303
+ start = self._val_sz
304
+ self._val.tensor[start : start + repeat] = val
305
+ self._val_sz += repeat
306
+
307
+ def _get_format(self, idx):
308
+ f = self._flist[idx]
309
+ return f[0] if isinstance(f, tuple) else f
310
+
311
+ def _get_prop(self, idx):
312
+ f = self._flist[idx]
313
+ return f[1] if isinstance(f, tuple) else None
314
+
315
+
316
+ class TensorConverter:
317
+ """
318
+ Converts a tensor from one format to another (using the [de]composer). The tensor
319
+ converter reduces the O(n^2) conversion problem into a O(n) problem by using a
320
+ [de]composer that converts to an intermediate coordinate list on the CPU first.
321
+ This class is provided to enable conversions between any UST formats. However,
322
+ this does not necessarily provide the most efficient way of implementing a
323
+ conversion. Fast paths (or library dispatches) can be used for efficiency.
324
+
325
+ CSR\\ //CSR
326
+ CSC \\ // CSC
327
+ UST ... > COO < ... UST
328
+ ELL // on \\ ELL
329
+ DIA// CPU \\DIA
330
+
331
+ Args:
332
+ source: source tensor
333
+ target: target tensor (partially constructed)
334
+ """
335
+
336
+ def __init__(self, source, target):
337
+ dim = source.num_dimensions
338
+ nse = source.nse
339
+ # Set fields.
340
+ self._source = source
341
+ self._target = target
342
+ self._indices = np.empty((dim, nse), dtype=target.index_type)
343
+ container_tp = np_enveloping_type(source.dtype)
344
+ self._values = np.empty((nse,), dtype=container_tp)
345
+ self._pos = 0
346
+
347
+ def run(self, stream=None):
348
+ # Intermediate COO is sorted if source format is sorted
349
+ # and uses an identity mapping. This may skip sorted
350
+ # if the target format has the same properties.
351
+ sformat = self._source.tensor_format
352
+ is_sorted = sformat.is_ordered and sformat.is_identity
353
+
354
+ # Run the decomposer and composer.
355
+ TensorDecomposer(self._source, self._visit).run()
356
+ pos, crd, val = TensorComposer(
357
+ self._source,
358
+ self._target,
359
+ self._indices[:, : self._pos],
360
+ self._values[: self._pos],
361
+ is_sorted,
362
+ ).run(stream=stream)
363
+
364
+ # Set directly (converter knows about UST conventions).
365
+ for idx in range(self._target.num_levels):
366
+ p = pos.get(idx)
367
+ if p is not None:
368
+ self._target._pos[idx] = wrap_operand(p)
369
+ c = crd.get(idx)
370
+ if c is not None:
371
+ self._target._crd[idx] = wrap_operand(c)
372
+ self._target._val = wrap_operand(val)
373
+
374
+ def _visit(self, dims, val):
375
+ if val == 0:
376
+ return # drop zeros (e.g. from dense or padding)
377
+ pos = self._pos
378
+ self._pos += 1
379
+ self._indices[:, pos] = dims[:]
380
+ self._values[pos] = val
@@ -0,0 +1,6 @@
1
+ # Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ from ._configuration import * # noqa: F403
6
+ from .contract import * # noqa: F403
@@ -0,0 +1,95 @@
1
+ # Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ __all__ = [
6
+ "ContractionAlgo",
7
+ "ContractionAutotuneMode",
8
+ "ContractionJitMode",
9
+ "ContractionCacheMode",
10
+ "ContractionOptions",
11
+ "ExecutionCUDA",
12
+ ]
13
+
14
+
15
+ from dataclasses import dataclass
16
+ from logging import Logger
17
+ from typing import ClassVar, Literal
18
+
19
+ from nvmath.bindings import cutensor
20
+ from nvmath.memory import BaseCUDAMemoryManager
21
+
22
+ ContractionAlgo = cutensor.Algo
23
+ ContractionAutotuneMode = cutensor.AutotuneMode
24
+ ContractionJitMode = cutensor.JitMode
25
+ ContractionCacheMode = cutensor.CacheMode
26
+
27
+
28
+ @dataclass
29
+ class ContractionOptions:
30
+ """
31
+ A data class for providing options to the :class:`BinaryContraction` and
32
+ :class:`TernaryContraction` objects, or the wrapper functions
33
+ :func:`binary_contraction`and :func:`ternary_contraction`.
34
+
35
+ Attributes:
36
+ compute_type: The compute type to use for the contraction.
37
+ See :class:`~nvmath.tensor.ComputeDesc` for available compute types.
38
+ logger (logging.Logger): Python Logger object. The root logger will be used if a
39
+ logger object is not provided.
40
+
41
+ blocking: A flag specifying the behavior of the execution functions and methods,
42
+ such as :func:`binary_contraction` and :meth:`TernaryContraction.execute`.
43
+ When ``blocking`` is `True`, the execution methods do not return until the
44
+ operation is complete. When
45
+ ``blocking`` is ``"auto"``, the methods return immediately when the input tensor
46
+ is on the GPU. The execution methods always block when the input tensor is
47
+ on the CPU to ensure that the user doesn't inadvertently use the result
48
+ before it becomes available. The default is ``"auto"``.
49
+
50
+ handle: cuTensor library handle. A handle will be created if one is not provided.
51
+
52
+ allocator: An object that supports the :class:`BaseCUDAMemoryManager` protocol, used
53
+ to draw device memory. If an allocator is not provided, a memory allocator from
54
+ the library package will be used (:func:`torch.cuda.caching_allocator_alloc` for
55
+ PyTorch operands, :func:`cupy.cuda.alloc` otherwise).
56
+
57
+ memory_limit: Maximum memory available to the contraction operation.
58
+ It can be specified as a value (with optional suffix like K[iB], M[iB],
59
+ G[iB]) or as a percentage. The default is 80% of the device memory.
60
+
61
+ .. seealso::
62
+ For supported compute types by data type, refer to the cuTENSOR documentation:
63
+
64
+ * `Binary contraction <https://docs.nvidia.com/cuda/cutensor/latest/api/cutensor.html#cutensorcreatecontraction>`_
65
+ * `Ternary contraction <https://docs.nvidia.com/cuda/cutensor/latest/api/cutensor.html#cutensorcreatecontractiontrinary>`_
66
+
67
+ """
68
+
69
+ compute_type: int | None = None
70
+ logger: Logger | None = None
71
+ blocking: Literal[True, "auto"] = "auto"
72
+ handle: int | None = None
73
+ allocator: BaseCUDAMemoryManager | None = None
74
+ memory_limit: int | str | None = r"80%"
75
+
76
+ def __post_init__(self):
77
+ if self.blocking not in (True, "auto"):
78
+ raise ValueError("The value specified for 'blocking' must be either True or 'auto'.")
79
+
80
+
81
+ @dataclass
82
+ class ExecutionCUDA:
83
+ """
84
+ A data class for providing GPU execution options to the :class:`BinaryContraction` and
85
+ :class:`TernaryContraction` objects, or the wrapper functions
86
+ :func:`binary_contraction`and :func:`ternary_contraction`.
87
+
88
+ Attributes:
89
+ device_id: CUDA device ordinal (only used if the operand resides on the CPU). The
90
+ default value is 0.
91
+
92
+ """
93
+
94
+ name: ClassVar[Literal["cuda"]] = "cuda"
95
+ device_id: int = 0
@@ -0,0 +1,3 @@
1
+ # Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0