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,302 @@
1
+ # Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ __all__ = [
6
+ "initialize",
7
+ "finalize",
8
+ "is_initialized",
9
+ "nvshmem_empty_dlpack",
10
+ "free",
11
+ "NvshmemMemoryManager",
12
+ "NvshmemNDBufferAllocator",
13
+ ]
14
+
15
+ import atexit
16
+ import logging
17
+
18
+ import numpy as np
19
+
20
+ try:
21
+ from cuda.core import Buffer, Device, MemoryResource
22
+ except ImportError:
23
+ from cuda.core.experimental import Buffer, Device, MemoryResource
24
+
25
+ from nvmath import memory
26
+ from nvmath.bindings import nvshmem # type: ignore
27
+ from nvmath.distributed.process_group import ProcessGroup, ReductionOp
28
+ from nvmath.internal.utils import device_ctx
29
+
30
+ # Indicates if this module has initialized NVSHMEM
31
+ _nvshmem_initialized_here = False
32
+
33
+ _atexit_registered = False
34
+ _exiting = False
35
+
36
+
37
+ def initialize(device_id: int, process_group: ProcessGroup) -> None:
38
+ """Initialize NVSHMEM runtime if not initialized, otherwise do nothing.
39
+ NOTE: device_id must be set as the current device, and a CUDA context must have
40
+ been created for this device.
41
+ """
42
+
43
+ global _nvshmem_initialized_here
44
+
45
+ rank = process_group.rank
46
+ nranks = process_group.nranks
47
+
48
+ status = nvshmem.init_status()
49
+ if status > nvshmem.STATUS_IS_BOOTSTRAPPED:
50
+ # NVSHMEM is already initialized
51
+ # NOTE: We assume that the user has passed the same communicator and device_id
52
+ # on which NVSHMEM was initialized (we have no way of checking here).
53
+ assert nvshmem.n_pes() == nranks
54
+ return
55
+ elif status == nvshmem.STATUS_IS_BOOTSTRAPPED:
56
+ # A value of 0 indicates an initialization that is similar to when nvshmem_init
57
+ # is used.
58
+ nvshmem.hostlib_init_attr(0, 0)
59
+ assert nvshmem.n_pes() == nranks
60
+ # Sanity check, can eventually remove
61
+ assert nvshmem.init_status() > nvshmem.STATUS_IS_BOOTSTRAPPED
62
+ _nvshmem_initialized_here = True
63
+ return
64
+
65
+ attr = nvshmem.InitAttr()
66
+ unique_id = nvshmem.UniqueId()
67
+ # PE 0 queries the unique ID
68
+ if rank == 0:
69
+ nvshmem.get_uniqueid(unique_id.ptr)
70
+ # PE 0 broadcasts the unique ID
71
+ process_group.broadcast_buffer(unique_id._data.view(np.int8), root=0) # type: ignore[attr-defined]
72
+ nvshmem.set_attr_uniqueid_args(rank, nranks, unique_id.ptr, attr.ptr)
73
+ nvshmem.hostlib_init_attr(nvshmem.Flags.INIT_WITH_UNIQUEID, attr.ptr)
74
+
75
+ # sanity check
76
+ assert nvshmem.init_status() > nvshmem.STATUS_IS_BOOTSTRAPPED
77
+ _nvshmem_initialized_here = True
78
+ _register_atexit_maybe()
79
+
80
+
81
+ def _register_atexit_maybe() -> None:
82
+ global _atexit_registered
83
+ if not _atexit_registered:
84
+ atexit.register(_detect_exit)
85
+ _atexit_registered = True
86
+
87
+
88
+ def _detect_exit() -> None:
89
+ global _exiting
90
+ _exiting = True
91
+
92
+
93
+ def finalize(device_id: int) -> None:
94
+ """Finalize NVSHMEM runtime if initialized"""
95
+ global _nvshmem_initialized_here
96
+ if not _nvshmem_initialized_here or (nvshmem.init_status() < nvshmem.STATUS_IS_INITIALIZED):
97
+ return
98
+
99
+ with device_ctx(device_id):
100
+ nvshmem.hostlib_finalize()
101
+ _nvshmem_initialized_here = False
102
+
103
+
104
+ def is_initialized() -> bool:
105
+ return nvshmem.init_status() >= nvshmem.STATUS_IS_INITIALIZED
106
+
107
+
108
+ def _check_initialized():
109
+ if nvshmem.init_status() < nvshmem.STATUS_IS_INITIALIZED:
110
+ raise RuntimeError("NVSHMEM is not initialized. Please initialize nvmath.distributed with NVSHMEM backend")
111
+ _register_atexit_maybe()
112
+
113
+
114
+ # Keeps track of memory allocated with nvshmem_empty_dlpack. This is used to report memory
115
+ # leaks and double free errors. This functionality might be handled by nvshmem4py in the
116
+ # future. More precisely, how it works is that when a buffer allocated via
117
+ # nvshmem_empty_dlpack is garbage-collected and found to not have been freed explicitly
118
+ # with the `free` function in this module, we report it.
119
+ _resource_registry = {}
120
+
121
+
122
+ class _NvshmemResource(MemoryResource):
123
+ def __init__(self, device):
124
+ self.device = device
125
+
126
+ def allocate(self, size, stream=None) -> Buffer:
127
+ # NOTE: setting the device is left to the caller
128
+ ptr = nvshmem.malloc(size)
129
+ if ptr == 0 and size != 0:
130
+ raise MemoryError("nvshmem_malloc returned NULL")
131
+ self.freed = False
132
+ return Buffer.from_handle(ptr=ptr, size=size, mr=self)
133
+
134
+ def deallocate(self, ptr, size, stream=None, manual=False):
135
+ # NOTE: setting the device is left to the caller
136
+ if not manual:
137
+ if self.freed:
138
+ return # this is fine
139
+ else:
140
+ # We can't call nvshmem_free when deallocate is triggered by the GC, since
141
+ # the GC has non-deterministic behavior and nvshmem_free is a collective
142
+ # call.
143
+ if not _exiting:
144
+ logging.error("Symmetric heap memory needs to be deallocated explicitly")
145
+ else:
146
+ logging.error(
147
+ "Symmetric heap memory was not deallocated explicitly (you may have "
148
+ "forgotten to clean up before exit, or an unrelated exception "
149
+ "crashed the program)"
150
+ )
151
+ return
152
+ if self.freed:
153
+ raise RuntimeError("This memory resource was already deallocated")
154
+ nvshmem.free(ptr)
155
+ self.freed = True
156
+
157
+ @property
158
+ def is_device_accessible(self) -> bool:
159
+ return True
160
+
161
+ @property
162
+ def is_host_accessible(self) -> bool:
163
+ return False
164
+
165
+ @property
166
+ def device_id(self) -> int:
167
+ return self.device.device_id
168
+
169
+
170
+ def nvshmem_empty_dlpack(size, device_id, process_group, make_symmetric=False, skip_symmetric_check=False, logger=None):
171
+ """Return uninitialized DLPack buffer of given size in bytes, allocated using
172
+ nvshmem_malloc (which makes this a *collective* call). Note that the DLPack
173
+ buffer currently does not include any shape, dtype, or stride information.
174
+
175
+ IMPORTANT: device_id must be the one with which NVSHMEM was initialized, and
176
+ setting the device is left to the caller.
177
+ """
178
+
179
+ _check_initialized()
180
+
181
+ global _resource_registry
182
+
183
+ logger = logger if logger is not None else logging.getLogger()
184
+
185
+ if make_symmetric and skip_symmetric_check:
186
+ raise ValueError("skip_symmetric_check is incompatible with make_symmetric=True")
187
+
188
+ if not skip_symmetric_check:
189
+ max_size = np.array([-size, size], dtype=np.int64)
190
+ process_group.allreduce_buffer(max_size, op=ReductionOp.MAX)
191
+ if -max_size[0] != max_size[1]:
192
+ # The buffer size is not the same on all processes.
193
+ if make_symmetric:
194
+ logger.info(
195
+ "Symmetric memory allocator: the buffer will be padded on some processes to "
196
+ f"satisfy symmetric requirement (make_symmetric=True), size={size} max_size={max_size[1]}."
197
+ )
198
+ else:
199
+ raise ValueError(
200
+ "The buffer size for symmetric memory allocation is not the same on all processes. "
201
+ "Consider using make_symmetric=True if you have uneven data distribution."
202
+ )
203
+ else:
204
+ logger.info(f"Symmetric memory allocator: the requested buffer size ({size}) is the same on all processes.")
205
+ # Sizes are equal or make_symmetric=True.
206
+ size = max_size[1]
207
+
208
+ mem = _NvshmemResource(Device(device_id))
209
+ mem_buffer = mem.allocate(size)
210
+ pointer = int(mem_buffer.handle)
211
+ assert pointer not in _resource_registry
212
+ _resource_registry[pointer] = (mem, size)
213
+ return mem_buffer
214
+
215
+
216
+ def free(pointer):
217
+ """This is a *collective* call (invokes nvshmem_free), used to free buffers
218
+ allocated with nvshmem_empty_dlpack.
219
+ Setting the device is left to the caller"""
220
+
221
+ _check_initialized()
222
+
223
+ global _resource_registry
224
+
225
+ try:
226
+ resource, size = _resource_registry.pop(pointer)
227
+ except KeyError as e:
228
+ raise RuntimeError(
229
+ "Unknown pointer to free. Possible causes:\n"
230
+ " - This memory was not allocated with nvmath.distributed helpers.\n"
231
+ " - This memory was already freed. Possible causes:\n"
232
+ " - Free was called multiple times on the same tensor.\n"
233
+ " - You have multiple tensors sharing the same symmetric memory buffer,\n"
234
+ " e.g. as a result of inplace operations, or tensor operations that\n"
235
+ " result in views such as slicing."
236
+ ) from e
237
+ resource.deallocate(pointer, size, manual=True)
238
+
239
+
240
+ class NvshmemMemoryManager(memory.BaseCUDAMemoryManager):
241
+ """
242
+ Nvshmem memory allocator.
243
+
244
+ Args:
245
+ device_id: The ID (int) of the device on which memory is to be allocated.
246
+ logger (logging.Logger): Python Logger object.
247
+ """
248
+
249
+ def __init__(self, device_id, logger):
250
+ """
251
+ __init__(device_id)
252
+ """
253
+ _check_initialized()
254
+ self.device_id = device_id
255
+ self.logger = logger
256
+
257
+ def memalloc(self, size):
258
+ """This is a *collective* call (invokes nvshmem_malloc)"""
259
+ mem = _NvshmemResource(Device(self.device_id))
260
+ mem_buffer = mem.allocate(size)
261
+ pointer = int(mem_buffer.handle)
262
+ assert pointer not in _resource_registry
263
+ _resource_registry[pointer] = (mem, size)
264
+
265
+ self.logger.debug(
266
+ f"NvshmemMemoryManager (allocate memory): size = {size}, pointer = {pointer}, device_id = {self.device_id}"
267
+ )
268
+
269
+ return SymmetricMemoryPointer(mem_buffer)
270
+
271
+
272
+ class NvshmemNDBufferAllocator:
273
+ __slots__ = ("ctx", "make_symmetric", "skip_symmetric_check")
274
+
275
+ def __init__(self, device_id, ctx, make_symmetric, skip_symmetric_check):
276
+ assert ctx.device_id == device_id, (
277
+ "Internal error: attempting to allocate symmetric memory on a device not used "
278
+ "by the NVSHMEM runtime on this process"
279
+ )
280
+ self.ctx = ctx
281
+ self.make_symmetric = make_symmetric
282
+ self.skip_symmetric_check = skip_symmetric_check
283
+
284
+ def allocate(self, size, stream, logger=None):
285
+ return nvshmem_empty_dlpack(
286
+ size,
287
+ self.ctx.device_id,
288
+ self.ctx.process_group,
289
+ make_symmetric=self.make_symmetric,
290
+ skip_symmetric_check=self.skip_symmetric_check,
291
+ logger=logger,
292
+ )
293
+
294
+
295
+ class SymmetricMemoryPointer(memory.MemoryPointer):
296
+ def __init__(self, mem_buffer):
297
+ super().__init__(int(mem_buffer.handle), mem_buffer.size, finalizer=None)
298
+ self.mem_buffer = mem_buffer
299
+
300
+ def free(self):
301
+ """This is a *collective* call (invokes nvshmem_free)"""
302
+ free(self.device_ptr)
@@ -0,0 +1,67 @@
1
+ # Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Common distributed tensor interface.
7
+ """
8
+
9
+ from __future__ import annotations # allows typehint of class methods to return the self class
10
+
11
+ __all__ = ["DistributedTensor"]
12
+
13
+ from abc import abstractmethod
14
+ from collections.abc import Sequence
15
+ from typing import Literal
16
+
17
+ from nvmath.bindings import nvshmem # type: ignore
18
+ from nvmath.distributed._internal.nvshmem import free
19
+ from nvmath.distributed._internal.nvshmem import is_initialized as is_nvshmem_initialized
20
+ from nvmath.internal.package_ifc import StreamHolder
21
+ from nvmath.internal.tensor_ifc import Tensor, TensorHolder
22
+
23
+
24
+ class DistributedTensor(TensorHolder[Tensor]):
25
+ """Base class for distributed tensors.
26
+
27
+ Sets flag during construction to indicate if the tensor is on NVSHMEM symmetric memory
28
+ or not.
29
+ """
30
+
31
+ def __init__(self, tensor):
32
+ super().__init__(tensor)
33
+ self._is_symmetric_memory = False
34
+ if self.device == "cuda" and is_nvshmem_initialized():
35
+ self._is_symmetric_memory = nvshmem.ptr(self.data_ptr, nvshmem.my_pe()) != 0
36
+
37
+ @property
38
+ def is_symmetric_memory(self):
39
+ """True if the tensor is on NVSHMEM symmetric heap.
40
+
41
+ This doesn't apply to NCCL because window registrations in NCCL are
42
+ on a per-communicator basis, not global.
43
+ """
44
+ return self._is_symmetric_memory
45
+
46
+ @abstractmethod
47
+ def to(
48
+ self, device_id: int | Literal["cpu"], stream_holder: StreamHolder | None, symmetric_memory: bool = False
49
+ ) -> DistributedTensor:
50
+ """Copy the TensorHolder to a different device.
51
+
52
+ No copy is performed if the TensorHolder is already on the requested device.
53
+ """
54
+ raise NotImplementedError
55
+
56
+ @abstractmethod
57
+ def reshape(self, shape: Sequence[int], *, copy: bool | None = None) -> DistributedTensor:
58
+ raise NotImplementedError
59
+
60
+ def free_symmetric(self) -> None:
61
+ """
62
+ Release this tensor's allocation on NVSHMEM symmetric memory heap.
63
+ """
64
+ if not self._is_symmetric_memory:
65
+ raise TypeError("tensor is not on symmetric memory")
66
+
67
+ free(self.data_ptr)
@@ -0,0 +1,62 @@
1
+ # Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Interface to seamlessly use distributed Cupy ndarray objects.
7
+ """
8
+
9
+ from __future__ import annotations # allows typehint of class methods to return the self class
10
+
11
+ __all__ = ["CupyDistributedTensor", "HostDistributedTensor"]
12
+
13
+
14
+ try:
15
+ import cupy
16
+ except ImportError:
17
+ cupy = None
18
+
19
+
20
+ from nvmath.internal.ndbuffer import ndbuffer
21
+ from nvmath.internal.tensor_ifc_cupy import CupyTensor, HostTensor
22
+
23
+ from .tensor_ifc import DistributedTensor
24
+ from .tensor_ifc_host_device import CudaDistributedTensorMixIn, HostDistributedTensorMixIn
25
+
26
+
27
+ class HostDistributedTensor(HostDistributedTensorMixIn, HostTensor, DistributedTensor):
28
+ device_tensor_class: type[CupyDistributedTensor] # set once CupyDistributedTensor is defined
29
+
30
+
31
+ # Most methods aren't redefined, because they simply act on the local array
32
+ class CupyDistributedTensor(CudaDistributedTensorMixIn, CupyTensor, DistributedTensor):
33
+ """
34
+ Tensor wrapper for distributed cupy ndarrays.
35
+ """
36
+
37
+ host_tensor_class = HostDistributedTensor
38
+
39
+ @classmethod
40
+ def wrap_ndbuffer(cls, ndbuffer: ndbuffer.NDBuffer) -> CupyDistributedTensor:
41
+ """
42
+ Wraps NDBuffer into a cupy.ndarray, the method assumes the
43
+ NDBuffer is backed by CUDA device memory.
44
+ """
45
+ mem = cupy.cuda.UnownedMemory(
46
+ ndbuffer.data_ptr,
47
+ ndbuffer.size_in_bytes,
48
+ owner=ndbuffer.data,
49
+ device_id=ndbuffer.device_id,
50
+ )
51
+ memptr = cupy.cuda.MemoryPointer(mem, offset=0)
52
+ dtype = cls.name_to_dtype[ndbuffer.dtype_name]
53
+ tensor = cupy.ndarray(
54
+ ndbuffer.shape,
55
+ dtype=dtype,
56
+ strides=ndbuffer.strides_in_bytes,
57
+ memptr=memptr,
58
+ )
59
+ return cls(tensor)
60
+
61
+
62
+ HostDistributedTensor.device_tensor_class = CupyDistributedTensor
@@ -0,0 +1,165 @@
1
+ # Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Mixins for handling host <-> device symmetry for distributed tensors
7
+ for packages that support only host or device tensors.
8
+
9
+ Most of the symmetric-memory logic is not package-specific, but handling
10
+ regular allocations or local tensor operations is, and can be delegated
11
+ to the non-distributed tensor implementation.
12
+ For this reason, the functionality is implemented as mixins, so that
13
+ we can delegate the `super()` calls without specifying the parent class.
14
+ """
15
+
16
+ from abc import ABC, abstractmethod
17
+ from collections.abc import Sequence
18
+
19
+ __all__ = ["HostDistributedTensorMixIn", "CudaDistributedTensorMixIn"]
20
+
21
+ import nvmath.distributed
22
+ from nvmath.distributed._internal.nvshmem import NvshmemNDBufferAllocator
23
+ from nvmath.internal.ndbuffer import ndbuffer
24
+ from nvmath.internal.package_ifc import StreamHolder
25
+ from nvmath.internal.tensor_ifc import TensorHolder
26
+ from nvmath.internal.typemaps import NAME_TO_ITEM_SIZE
27
+ from nvmath.internal.utils import device_ctx
28
+
29
+
30
+ class HostDistributedTensorMixIn(ABC): # noqa: B024
31
+ """
32
+ Host counterpart for distributed tensor wrapping package that
33
+ does not support host memory space (e.g. cupy). The class is marked
34
+ as abstract, because the mixin is not meant to be instantiated directly.
35
+ """
36
+
37
+ def to(self, device_id, stream_holder, symmetric_memory: bool = False):
38
+ """
39
+ In addition to the base class semantics:
40
+ - If symmetric_memory=True, target device must be the one used to initialize
41
+ NVSHMEM on this process.
42
+ - Strides must be dense non-overlapping.
43
+ - Memory layout is preserved (if strides are dense non-overlapping,
44
+ the base class guarantees that)
45
+ """
46
+ if not symmetric_memory or device_id == "cpu":
47
+ tensor = super().to(device_id, stream_holder) # type: ignore
48
+ elif isinstance(device_id, int):
49
+ device_cls = self.device_tensor_class # type: ignore
50
+ tensor = device_cls.create_from_host(self, device_id, stream_holder, symmetric_memory)
51
+ else:
52
+ raise ValueError(f"The device must be specified as an integer or 'cpu', not '{device_id}'.")
53
+ assert tensor.is_symmetric_memory == symmetric_memory
54
+ return tensor
55
+
56
+
57
+ class CudaDistributedTensorMixIn(ABC):
58
+ @classmethod
59
+ def empty(
60
+ cls,
61
+ shape,
62
+ device_id="cpu",
63
+ *,
64
+ dtype="float32",
65
+ strides=None,
66
+ stream_holder: StreamHolder | None = None,
67
+ **context,
68
+ ):
69
+ """
70
+ Create an empty tensor of the specified shape and data type.
71
+
72
+ Note, that the strides, if specified, MUST correspond to a dense (possibly permuted)
73
+ tensor, otherwise the created tensor may be corrupted.
74
+ """
75
+ symmetric_memory = context.pop("symmetric_memory", False)
76
+ make_symmetric = context.pop("make_symmetric", False)
77
+ skip_symmetric_check = context.pop("skip_symmetric_check", False)
78
+
79
+ if not symmetric_memory:
80
+ if make_symmetric or skip_symmetric_check:
81
+ raise ValueError("Use of symmetric memory option with symmetric_memory=False")
82
+ return super().empty( # type: ignore
83
+ shape,
84
+ device_id=device_id,
85
+ dtype=dtype,
86
+ strides=strides,
87
+ stream_holder=stream_holder,
88
+ **context,
89
+ )
90
+
91
+ logger = context.get("logger")
92
+ with device_ctx(device_id):
93
+ ctx = nvmath.distributed.get_context()
94
+ assert ctx is not None, "nvmath.distributed has not been initialized"
95
+ allocator = NvshmemNDBufferAllocator(
96
+ device_id, ctx, make_symmetric=make_symmetric, skip_symmetric_check=skip_symmetric_check
97
+ )
98
+ nd_dst = ndbuffer.empty(
99
+ shape,
100
+ device_id=device_id,
101
+ dtype_name=dtype,
102
+ itemsize=NAME_TO_ITEM_SIZE[dtype],
103
+ strides=strides,
104
+ stream=stream_holder,
105
+ device_memory_pool=allocator,
106
+ logger=logger,
107
+ )
108
+ if nd_dst.cf_order() == "K":
109
+ raise ValueError("CudaDistributedTensor only supports 'C' or 'F' order")
110
+ return cls.wrap_ndbuffer(nd_dst)
111
+
112
+ @classmethod
113
+ def create_from_host(
114
+ cls,
115
+ tensor: TensorHolder,
116
+ device_id: int,
117
+ stream_holder: StreamHolder,
118
+ symmetric_memory: bool = False,
119
+ ):
120
+ if not symmetric_memory:
121
+ return super().create_from_host(tensor, device_id, stream_holder) # type: ignore
122
+ with device_ctx(device_id):
123
+ ctx = nvmath.distributed.get_context()
124
+ assert ctx is not None, "nvmath.distributed has not been initialized"
125
+ allocator = NvshmemNDBufferAllocator(device_id, ctx, make_symmetric=True, skip_symmetric_check=False)
126
+ src_ndbuffer = tensor.asndbuffer()
127
+ dst_ndbuffer = ndbuffer.empty_like(
128
+ src_ndbuffer,
129
+ device_id=device_id,
130
+ stream=stream_holder,
131
+ device_memory_pool=allocator,
132
+ )
133
+ if dst_ndbuffer.cf_order() == "K":
134
+ raise ValueError("CudaDistributedTensor only supports 'C' or 'F' order")
135
+ ndbuffer.copy_into(dst_ndbuffer, src_ndbuffer, stream_holder)
136
+ return cls.wrap_ndbuffer(dst_ndbuffer)
137
+
138
+ @classmethod
139
+ @abstractmethod
140
+ def wrap_ndbuffer(cls, ndbuffer: ndbuffer.NDBuffer):
141
+ """
142
+ Defines how to wrap NDBuffer into a distributed tensor instance.
143
+ The exact implementation depends on `self.tensor` type
144
+ expected by the TensorHolder implementation to be mixed in.
145
+ E.g. for NDBufferTensor, it suffices to call `cls(ndbuffer)`,
146
+ while for CupyTensor, we need to wrap the NDBuffer into a cupy.ndarray first.
147
+ """
148
+ raise NotImplementedError
149
+
150
+ def to(self, device_id, stream_holder, symmetric_memory: bool = False):
151
+ """
152
+ In addition to the base class semantics:
153
+ - Target device must be the one used to initialize NVSHMEM on this process.
154
+ - Strides must be dense non-overlapping.
155
+ - Memory layout is preserved (if strides are dense non-overlapping,
156
+ the base class guarantees that)
157
+ """
158
+ tensor = super().to(device_id, stream_holder) # type: ignore
159
+ assert tensor.is_symmetric_memory == symmetric_memory
160
+ return tensor
161
+
162
+ def reshape(self, shape: Sequence[int], *, copy: bool | None = None):
163
+ if copy:
164
+ raise NotImplementedError("reshape with copy=True is not supported for CUDA distributed tensor")
165
+ return super().reshape(shape, copy=copy) # type: ignore
@@ -0,0 +1,41 @@
1
+ # Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
2
+ #
3
+ # SPDX-License-Identifier: Apache-2.0
4
+
5
+ """
6
+ Interface to seamlessly use distributed Numpy ndarray objects.
7
+ """
8
+
9
+ from __future__ import annotations # allows typehint of class methods to return the self class
10
+
11
+ __all__ = ["NumpyDistributedTensor", "CudaDistributedTensor"]
12
+
13
+ from nvmath.internal.ndbuffer import ndbuffer
14
+ from nvmath.internal.tensor_ifc_numpy import CudaTensor, NumpyTensor
15
+
16
+ from .tensor_ifc import DistributedTensor
17
+ from .tensor_ifc_host_device import CudaDistributedTensorMixIn, HostDistributedTensorMixIn
18
+
19
+
20
+ class CudaDistributedTensor(CudaDistributedTensorMixIn, CudaTensor, DistributedTensor):
21
+ """
22
+ Tensor wrapper for distributed cuda ndarrays.
23
+ """
24
+
25
+ host_tensor_class: type[NumpyDistributedTensor] # set once NumpyDistributedTensor is defined
26
+
27
+ @classmethod
28
+ def wrap_ndbuffer(cls, ndbuffer: ndbuffer.NDBuffer) -> CudaDistributedTensor:
29
+ return cls(ndbuffer)
30
+
31
+
32
+ # Most methods aren't redefined, because they simply act on the local array
33
+ class NumpyDistributedTensor(HostDistributedTensorMixIn, NumpyTensor, DistributedTensor):
34
+ """
35
+ Tensor wrapper for distributed numpy ndarrays.
36
+ """
37
+
38
+ device_tensor_class = CudaDistributedTensor
39
+
40
+
41
+ CudaDistributedTensor.host_tensor_class = NumpyDistributedTensor