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,408 @@
1
+ # Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ import logging
6
+ import math
7
+ import pickle
8
+ from abc import ABC, abstractmethod
9
+ from collections.abc import Callable
10
+ from enum import IntEnum
11
+ from typing import Literal, TypeAlias, TypeVar
12
+
13
+ import numpy as np
14
+
15
+ __all__ = [
16
+ "ReductionOp",
17
+ "ProcessGroup",
18
+ "MPIProcessGroup",
19
+ "TorchProcessGroup",
20
+ ]
21
+
22
+
23
+ class ReductionOp(IntEnum):
24
+ SUM = 0
25
+ MIN = 1
26
+ MAX = 2
27
+
28
+
29
+ T = TypeVar("T")
30
+ Reducer: TypeAlias = Callable[[T, T], T]
31
+
32
+
33
+ class ProcessGroup(ABC):
34
+ """A ProcessGroup represents a set of processes collectively running
35
+ ``nvmath.distributed`` operations. A ProcessGroup implements a small set of
36
+ communication collectives used by nvmath-python to initialize the distributed
37
+ runtime and set up distributed math operations. It is not used for compute
38
+ (each math library uses its chosen communication backend(s), like NCCL or NVSHMEM).
39
+ """
40
+
41
+ @property
42
+ @abstractmethod
43
+ def rank(self) -> int:
44
+ "Index of this process in the group."
45
+ raise NotImplementedError
46
+
47
+ @property
48
+ @abstractmethod
49
+ def nranks(self) -> int:
50
+ """Number of processes in the group."""
51
+ raise NotImplementedError
52
+
53
+ @abstractmethod
54
+ def broadcast_buffer(self, array: np.ndarray, *, root: int = 0) -> None:
55
+ """Broadcast an array from one process to every process.
56
+
57
+ Args:
58
+ array: input (on root) and output of the collective.
59
+
60
+ root: rank of sending process.
61
+ """
62
+ raise NotImplementedError
63
+
64
+ @abstractmethod
65
+ def allreduce_buffer(self, array: np.ndarray, *, op: ReductionOp) -> None:
66
+ """Allreduce an array.
67
+
68
+ Args:
69
+ array: Input and output of the collective. The function operates in-place.
70
+
71
+ op: One of the values from :class:`ReduceOp` enum. Specifies an operation for
72
+ element-wise reductions.
73
+ """
74
+ raise NotImplementedError
75
+
76
+ @abstractmethod
77
+ def allreduce_object(self, obj: T, *, op: Reducer[T]) -> T:
78
+ """Reduces all Python objects contributed by members of the group. The result
79
+ is a single reduced object which is returned on every process.
80
+
81
+ Args:
82
+ obj: object contributed by this process.
83
+
84
+ op: A Python function that takes two objects and returns a single (reduced)
85
+ object.
86
+ """
87
+ raise NotImplementedError
88
+
89
+
90
+ class MPIProcessGroup(ProcessGroup):
91
+ """ProcessGroup implemented on mpi4py.
92
+
93
+ Args:
94
+ mpi_comm: mpi4py communicator.
95
+ """
96
+
97
+ # Map from mpi4py reduction ops to this module's ReductionOp.
98
+ _reduction_op_map = {} # type: ignore
99
+
100
+ def __init__(self, mpi_comm):
101
+ if not MPIProcessGroup._reduction_op_map:
102
+ from mpi4py import MPI
103
+
104
+ MPIProcessGroup._reduction_op_map = {
105
+ ReductionOp.MIN: MPI.MIN,
106
+ ReductionOp.MAX: MPI.MAX,
107
+ ReductionOp.SUM: MPI.SUM,
108
+ }
109
+ self._mpi_comm = mpi_comm
110
+
111
+ @property
112
+ def rank(self) -> int:
113
+ return self._mpi_comm.Get_rank()
114
+
115
+ @property
116
+ def nranks(self) -> int:
117
+ return self._mpi_comm.Get_size()
118
+
119
+ def broadcast_buffer(self, array: np.ndarray, *, root: int = 0) -> None:
120
+ self._mpi_comm.Bcast(array, root=root)
121
+
122
+ def allreduce_buffer(self, array: np.ndarray, *, op: ReductionOp) -> None:
123
+ mpi_reduction_op = MPIProcessGroup._reduction_op_map[op]
124
+ from mpi4py import MPI
125
+
126
+ self._mpi_comm.Allreduce(MPI.IN_PLACE, array, mpi_reduction_op)
127
+
128
+ def allreduce_object(self, obj: T, *, op: Reducer[T]) -> T:
129
+ return self._mpi_comm.allreduce(obj, op=op)
130
+
131
+
132
+ def _init_binomial_tree(rank: int, nranks: int) -> tuple[int | None, list[int]]:
133
+ """Return parent and children of process with given rank in a binomial
134
+ tree of nranks processes."""
135
+ max_height = math.ceil(math.log2(nranks))
136
+ max_size = 1 << max_height # max number of nodes in tree (2^max_height)
137
+
138
+ # Compute order/height of this subtree (in a full binomial tree of max_size nodes).
139
+ # Relabel this node for the computation (so 0 is deepest leaf instead of root).
140
+ p = label = max_size - 1 - rank
141
+ order = 0
142
+ while p > 0:
143
+ if p % 2 == 0:
144
+ break
145
+ p //= 2
146
+ order += 1
147
+
148
+ if rank == 0:
149
+ parent = None
150
+ else:
151
+ parent = label + (1 << order) # parent = label + 2^order
152
+ parent = max_size - 1 - parent # convert back to original numbering scheme
153
+
154
+ children = []
155
+ # Max number of children is the order of this subtree.
156
+ for i in range(order):
157
+ child = label - (1 << i) # child = label - 2^i
158
+ child = max_size - 1 - child # convert back to original numbering scheme
159
+ if child <= nranks - 1:
160
+ children.append(child)
161
+
162
+ return parent, children
163
+
164
+
165
+ class MsgSizeError(Exception):
166
+ def __init__(self, size: int):
167
+ self.size = size
168
+
169
+
170
+ class TorchProcessGroup(ProcessGroup):
171
+ """ProcessGroup implemented on ``torch.distributed``.
172
+
173
+ Args:
174
+ device_id: Device used by the ``torch.distributed`` process group backend.
175
+
176
+ torch_process_group: ``torch.distributed`` process group handle (e.g. returned
177
+ by ``torch.distributed.new_group()``), or None to use the default torch
178
+ process group.
179
+ """
180
+
181
+ # Map from torch.distributed reduction ops to this module's ReductionOp.
182
+ _reduction_op_map = {} # type: ignore
183
+ # The minimum buffer size (in bytes) for allreduce_object()
184
+ MIN_ALL_REDUCE_OBJ_BUFFER_SIZE = 128
185
+
186
+ def __init__(
187
+ self,
188
+ *,
189
+ device_id: int | Literal["cpu"],
190
+ torch_process_group=None,
191
+ logger=None,
192
+ ):
193
+ import torch
194
+ import torch.distributed as dist
195
+
196
+ if not isinstance(device_id, int) and device_id != "cpu":
197
+ raise ValueError("device_id must be int or 'cpu'")
198
+
199
+ if not dist.is_initialized():
200
+ raise RuntimeError("torch.distributed is not initialized")
201
+
202
+ if logger is None:
203
+ logger = logging.getLogger()
204
+ self.logger = logger
205
+
206
+ # Get communication backend used by this torch process group.
207
+ backend = dist.get_backend(torch_process_group)
208
+ if backend in ("nccl", "xccl") and device_id == "cpu":
209
+ raise ValueError(f"This process group is using a GPU-only backend ({backend}), but the provided device_id is 'cpu'")
210
+ if backend == "mpi":
211
+ logger.warning(
212
+ "You're using torch.distributed with MPI backend. In this case it's "
213
+ "recommended to initialize nvmath.distributed with "
214
+ "nvmath.distributed.process_group.MPIProcessGroup instead of "
215
+ "TorchProcessGroup."
216
+ )
217
+
218
+ self._device_id = device_id
219
+ self._device = torch.device(f"cuda:{device_id}") if device_id != "cpu" else torch.device("cpu")
220
+ self._torch_process_group = torch_process_group
221
+
222
+ if not TorchProcessGroup._reduction_op_map:
223
+ TorchProcessGroup._reduction_op_map = {
224
+ ReductionOp.MIN: dist.ReduceOp.MIN,
225
+ ReductionOp.MAX: dist.ReduceOp.MAX,
226
+ ReductionOp.SUM: dist.ReduceOp.SUM,
227
+ }
228
+
229
+ if backend == "nccl":
230
+ # Ensure a NCCL communicator is formed for the whole group. Otherwise if a
231
+ # parent NCCL communicator is not yet formed when batched P2P operations are
232
+ # called on a subset of ranks (see allreduce_object() below), they could fail.
233
+ with torch.cuda.device(self._device):
234
+ a = torch.ones(1, device=self._device)
235
+ dist.all_reduce(a, op=dist.ReduceOp.SUM, group=self._torch_process_group)
236
+ if a[0] != self.nranks:
237
+ raise RuntimeError("torch.distributed incorrect all_reduce output")
238
+
239
+ # A serialized MsgSizeError must fit in MIN_ALL_REDUCE_OBJ_BUFFER_SIZE
240
+ assert len(pickle.dumps(MsgSizeError(1 << 30))) < self.MIN_ALL_REDUCE_OBJ_BUFFER_SIZE, "Internal error."
241
+ # Initial buffer size for allreduce_object (in bytes)
242
+ self.allreduce_obj_buffer_size = 2048
243
+
244
+ # Determine my parent and children for allreduce_object() implementation.
245
+ # A binomial tree is good for small message sizes.
246
+ self._parent, self._children = _init_binomial_tree(self.rank, self.nranks)
247
+
248
+ @property
249
+ def device_id(self) -> int | Literal["cpu"]:
250
+ """Device used by the communication backend of this torch process group."""
251
+ return self._device_id
252
+
253
+ @property
254
+ def rank(self) -> int:
255
+ import torch.distributed as dist
256
+
257
+ return dist.get_rank(group=self._torch_process_group)
258
+
259
+ @property
260
+ def nranks(self) -> int:
261
+ import torch.distributed as dist
262
+
263
+ return dist.get_world_size(group=self._torch_process_group)
264
+
265
+ def broadcast_buffer(self, array: np.ndarray, *, root: int = 0) -> None:
266
+ import torch
267
+
268
+ if self._device_id == "cpu":
269
+ torch.distributed.broadcast(torch.from_numpy(array), group=self._torch_process_group, group_src=root)
270
+ else:
271
+ tensor_gpu = torch.from_numpy(array).to(self._device)
272
+ torch.distributed.broadcast(tensor_gpu, group=self._torch_process_group, group_src=root)
273
+ array[:] = tensor_gpu.cpu().numpy()
274
+
275
+ def allreduce_buffer(self, array: np.ndarray, *, op: ReductionOp) -> None:
276
+ import torch
277
+
278
+ torch_reduction_op = TorchProcessGroup._reduction_op_map[op]
279
+ if self._device_id == "cpu":
280
+ torch.distributed.all_reduce(torch.from_numpy(array), op=torch_reduction_op, group=self._torch_process_group)
281
+ else:
282
+ tensor_gpu = torch.from_numpy(array).to(self._device)
283
+ torch.distributed.all_reduce(tensor_gpu, op=torch_reduction_op, group=self._torch_process_group)
284
+ array[:] = tensor_gpu.cpu().numpy()
285
+
286
+ @property
287
+ def allreduce_obj_buffer_size(self):
288
+ """Current buffer size for allreduce_object() (in bytes)"""
289
+ return self._allreduce_obj_buffer_size
290
+
291
+ @allreduce_obj_buffer_size.setter
292
+ def allreduce_obj_buffer_size(self, value):
293
+ """**NOTE: Same size needs to be set on every process**"""
294
+ if not isinstance(value, int) or value < self.MIN_ALL_REDUCE_OBJ_BUFFER_SIZE:
295
+ raise ValueError(f"Buffer size must be >= {self.MIN_ALL_REDUCE_OBJ_BUFFER_SIZE}")
296
+ self._allreduce_obj_buffer_size = value
297
+
298
+ def allreduce_object(self, obj: T, *, op: Reducer[T]) -> T:
299
+ import torch
300
+
301
+ NUM_TRIES = 5
302
+ for _ in range(NUM_TRIES):
303
+ try:
304
+ if self._device_id != "cpu":
305
+ # It's recommended to set torch cuda device for batch_isend_irecv
306
+ with torch.cuda.device(self._device):
307
+ return self._allreduce_object(obj, op)
308
+ else:
309
+ return self._allreduce_object(obj, op)
310
+ except MsgSizeError as e:
311
+ new_size = int(e.size * 1.2)
312
+ self.logger.warning(
313
+ "allreduce_obj_buffer_size is too small for this allreduce: "
314
+ f"current size is {self.allreduce_obj_buffer_size} and the required "
315
+ f"size is {e.size}. Increasing to {new_size}. "
316
+ "You can set the size manually with 'allreduce_obj_buffer_size' property "
317
+ " to avoid dynamic resizing."
318
+ )
319
+ self.allreduce_obj_buffer_size = new_size
320
+ raise RuntimeError(
321
+ f"Could not find buffer size for allreduce_object() after {NUM_TRIES} tries. "
322
+ "Please set the limit manually with 'allreduce_obj_buffer_size' property."
323
+ )
324
+
325
+ def _allreduce_object(self, obj: T, reducer: Reducer[T]) -> T:
326
+ import torch
327
+ import torch.distributed as dist
328
+
329
+ def to_tensor(obj, out_tensor):
330
+ """Serializes Python object into out_tensor buffer.
331
+ If the tensor buffer is not large enough to accommodate the serialized object,
332
+ insert a serialized MsgSizeError (with required size) instead."""
333
+ pickled_obj = pickle.dumps(obj)
334
+ pickled_obj_arr = np.frombuffer(pickled_obj, dtype=np.uint8).copy()
335
+ if pickled_obj_arr.nbytes > out_tensor.nbytes:
336
+ pickled_obj = pickle.dumps(MsgSizeError(pickled_obj_arr.nbytes))
337
+ pickled_obj_arr = np.frombuffer(pickled_obj, dtype=np.uint8).copy()
338
+ out_tensor[: pickled_obj_arr.nbytes] = torch.from_numpy(pickled_obj_arr)
339
+ return out_tensor
340
+
341
+ def from_tensor(tensor):
342
+ """Deserializes Python object from tensor buffer."""
343
+ return pickle.loads(tensor.cpu().numpy().tobytes())
344
+
345
+ num_children = len(self._children)
346
+
347
+ if num_children > 0:
348
+ # 1) Get partially reduced objects from children.
349
+
350
+ # Post a recv for each child.
351
+ # Use batch_isend_irecv because, when using the NCCL backend, individual
352
+ # (unbatched) point-to-point (P2P) send or receive operations can trigger
353
+ # the creation of new, separate NCCL communicators for each pair of
354
+ # communicating processes, which can have significant overhead or use
355
+ # large amounts of GPU memory. Instead, we want the p2p operations to use
356
+ # the parent NCCL communicator whenever possible.
357
+ data = [
358
+ torch.empty(self._allreduce_obj_buffer_size, dtype=torch.uint8, device=self._device)
359
+ for _ in range(num_children)
360
+ ]
361
+ recv_ops = []
362
+ for i, child in enumerate(self._children):
363
+ # The tag is to match the recv against the remote send (in case the sender
364
+ # is sending multiple different messages).
365
+ recv_op = dist.P2POp(dist.irecv, data[i], group=self._torch_process_group, tag=2355, group_peer=child)
366
+ recv_ops.append(recv_op)
367
+
368
+ reqs = dist.batch_isend_irecv(recv_ops)
369
+ for req in reqs:
370
+ req.wait()
371
+
372
+ # 2) Apply the reducer on my object and objects received from children.
373
+ reduced_obj = obj
374
+ errors = []
375
+ for i in range(num_children):
376
+ child_obj = from_tensor(data[i])
377
+ if isinstance(child_obj, MsgSizeError):
378
+ errors.append(child_obj)
379
+ elif not errors:
380
+ reduced_obj = reducer(reduced_obj, child_obj)
381
+ if errors:
382
+ # Max reduction of all received errors to get the maximum required buffer
383
+ # size for this subtree.
384
+ reduced_obj = MsgSizeError(max(e.size for e in errors)) # type: ignore
385
+ else:
386
+ reduced_obj = obj
387
+
388
+ data = [torch.empty(self._allreduce_obj_buffer_size, dtype=torch.uint8, device=self._device)]
389
+ if self._parent is not None:
390
+ # Send reduced object to parent.
391
+ send_op = dist.P2POp(
392
+ dist.isend, to_tensor(reduced_obj, data[0]), group=self._torch_process_group, tag=2355, group_peer=self._parent
393
+ )
394
+ dist.batch_isend_irecv([send_op])[0].wait()
395
+
396
+ # Wait for broadcast of reduction from root.
397
+ dist.broadcast(data[0], group=self._torch_process_group, group_src=0)
398
+ result = from_tensor(data[0])
399
+ else:
400
+ # This is the root: broadcast the result.
401
+ assert self.rank == 0
402
+ dist.broadcast(to_tensor(reduced_obj, data[0]), group=self._torch_process_group, group_src=0)
403
+ result = reduced_obj
404
+
405
+ if isinstance(result, MsgSizeError):
406
+ raise result
407
+
408
+ return result
@@ -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 .reshape import * # noqa: F403
@@ -0,0 +1,39 @@
1
+ # Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ __all__ = ["ReshapeOptions"]
6
+
7
+ from dataclasses import dataclass
8
+ from logging import Logger
9
+ from typing import Literal
10
+
11
+
12
+ @dataclass
13
+ class ReshapeOptions:
14
+ """
15
+ A data class for providing options to the :class:`Reshape` object and the wrapper
16
+ function :func:`reshape`.
17
+
18
+ Attributes:
19
+ logger (logging.Logger): Python Logger object. The root logger will be used if a
20
+ logger object is not provided.
21
+
22
+ blocking: A flag specifying the behavior of the execution functions and methods,
23
+ such as :func:`reshape` and :meth:`Reshape.execute`. When ``blocking`` is
24
+ `True`, the execution methods do not return until the operation is complete.
25
+ When ``blocking`` is ``"auto"``, the methods return immediately when the input
26
+ tensor is on the GPU. The execution methods always block when the input tensor
27
+ is on the CPU to ensure that the user doesn't inadvertently use the result
28
+ before it becomes available. The default is ``"auto"``.
29
+
30
+ .. seealso::
31
+ :class:`Reshape` and :func:`reshape`.
32
+ """
33
+
34
+ logger: Logger | None = None
35
+ blocking: Literal[True, "auto"] = "auto"
36
+
37
+ def __post_init__(self):
38
+ if self.blocking not in (True, "auto"):
39
+ raise ValueError("The value specified for 'blocking' must be either True or 'auto'.")