nvmath-python 1.0.0__cp314-cp314t-win_amd64.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- nvmath/__init__.pxd +0 -0
- nvmath/__init__.py +45 -0
- nvmath/_internal/__init__.py +0 -0
- nvmath/_internal/attribute_ifc_factory.py +330 -0
- nvmath/_internal/layout.py +70 -0
- nvmath/_internal/templates.py +130 -0
- nvmath/_internal/threadsafe.py +106 -0
- nvmath/_internal/utils.py +43 -0
- nvmath/_internal/workspace.py +490 -0
- nvmath/_utils.py +147 -0
- nvmath/bindings/__init__.py +60 -0
- nvmath/bindings/_internal/__init__.pxd +0 -0
- nvmath/bindings/_internal/__init__.py +0 -0
- nvmath/bindings/_internal/common_types.pxd +31 -0
- nvmath/bindings/_internal/cublas.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cublas.pxd +530 -0
- nvmath/bindings/_internal/cublasLt.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cublasLt.pxd +59 -0
- nvmath/bindings/_internal/cublasMp.pxd +52 -0
- nvmath/bindings/_internal/cudss.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cudss.pxd +54 -0
- nvmath/bindings/_internal/cufft.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cufft.pxd +70 -0
- nvmath/bindings/_internal/cufftMp.pxd +77 -0
- nvmath/bindings/_internal/curand.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/curand.pxd +42 -0
- nvmath/bindings/_internal/cusolver.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusolver.pxd +15 -0
- nvmath/bindings/_internal/cusolverDn.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusolverDn.pxd +406 -0
- nvmath/bindings/_internal/cusolverMp.pxd +71 -0
- nvmath/bindings/_internal/cusolverSp.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusolverSp.pxd +75 -0
- nvmath/bindings/_internal/cusparse.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusparse.pxd +471 -0
- nvmath/bindings/_internal/cusparseLt.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cusparseLt.pxd +48 -0
- nvmath/bindings/_internal/cutensor.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/cutensor.pxd +58 -0
- nvmath/bindings/_internal/mathdx.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/mathdx.pxd +116 -0
- nvmath/bindings/_internal/nvshmem.pxd +29 -0
- nvmath/bindings/_internal/utils.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/_internal/utils.pxd +174 -0
- nvmath/bindings/_internal/utils.pyi +10 -0
- nvmath/bindings/cublas.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cublas.pxd +558 -0
- nvmath/bindings/cublas.pyi +812 -0
- nvmath/bindings/cublasLt.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cublasLt.pxd +109 -0
- nvmath/bindings/cublasLt.pyi +1461 -0
- nvmath/bindings/cublasMp.pxd +85 -0
- nvmath/bindings/cublasMp.pyi +267 -0
- nvmath/bindings/cudss.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cudss.pxd +98 -0
- nvmath/bindings/cudss.pyi +443 -0
- nvmath/bindings/cufft.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cufft.pxd +118 -0
- nvmath/bindings/cufft.pyi +301 -0
- nvmath/bindings/cufftMp.pxd +124 -0
- nvmath/bindings/cufftMp.pyi +326 -0
- nvmath/bindings/curand.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/curand.pxd +71 -0
- nvmath/bindings/curand.pyi +189 -0
- nvmath/bindings/cusolver.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cusolver.pxd +62 -0
- nvmath/bindings/cusolver.pyi +320 -0
- nvmath/bindings/cusolverDn.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cusolverDn.pxd +430 -0
- nvmath/bindings/cusolverDn.pyi +422 -0
- nvmath/bindings/cusolverMp.pxd +98 -0
- nvmath/bindings/cusolverMp.pyi +114 -0
- nvmath/bindings/cusolverSp.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cusolverSp.pxd +95 -0
- nvmath/bindings/cusolverSp.pyi +70 -0
- nvmath/bindings/cusparse.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cusparse.pxd +546 -0
- nvmath/bindings/cusparse.pyi +1017 -0
- nvmath/bindings/cusparseLt.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cusparseLt.pxd +99 -0
- nvmath/bindings/cusparseLt.pyi +252 -0
- nvmath/bindings/cutensor.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cutensor.pxd +98 -0
- nvmath/bindings/cutensor.pyi +324 -0
- nvmath/bindings/cycublas.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycublas.pxd +664 -0
- nvmath/bindings/cycublasLt.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycublasLt.pxd +1045 -0
- nvmath/bindings/cycublasMp.pxd +171 -0
- nvmath/bindings/cycudss.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycudss.pxd +277 -0
- nvmath/bindings/cycufft.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycufft.pxd +333 -0
- nvmath/bindings/cycufftMp.pxd +342 -0
- nvmath/bindings/cycurand.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycurand.pxd +141 -0
- nvmath/bindings/cycusolver.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycusolver.pxd +137 -0
- nvmath/bindings/cycusolverDn.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycusolverDn.pxd +443 -0
- nvmath/bindings/cycusolverMp.pxd +107 -0
- nvmath/bindings/cycusolverSp.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycusolverSp.pxd +93 -0
- nvmath/bindings/cycusparse.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycusparse.pxd +679 -0
- nvmath/bindings/cycusparseLt.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycusparseLt.pxd +135 -0
- nvmath/bindings/cycutensor.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cycutensor.pxd +189 -0
- nvmath/bindings/cymathdx.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/cymathdx.pxd +552 -0
- nvmath/bindings/cynvshmem.pxd +118 -0
- nvmath/bindings/mathdx.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/mathdx.pxd +182 -0
- nvmath/bindings/mathdx.pyi +1562 -0
- nvmath/bindings/nvpl/__init__.pxd +0 -0
- nvmath/bindings/nvpl/__init__.py +13 -0
- nvmath/bindings/nvpl/_internal/__init__.pxd +0 -0
- nvmath/bindings/nvpl/_internal/__init__.py +0 -0
- nvmath/bindings/nvpl/_internal/blas.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/nvpl/_internal/blas.pxd +237 -0
- nvmath/bindings/nvpl/_internal/fft.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/nvpl/_internal/fft.pxd +36 -0
- nvmath/bindings/nvpl/blas.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/nvpl/blas.pxd +131 -0
- nvmath/bindings/nvpl/blas.pyi +168 -0
- nvmath/bindings/nvpl/cyblas.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/nvpl/cyblas.pxd +280 -0
- nvmath/bindings/nvpl/cyfft.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/nvpl/cyfft.pxd +93 -0
- nvmath/bindings/nvpl/fft.cp314t-win_amd64.pyd +0 -0
- nvmath/bindings/nvpl/fft.pxd +100 -0
- nvmath/bindings/nvpl/fft.pyi +168 -0
- nvmath/bindings/nvshmem.pxd +54 -0
- nvmath/bindings/nvshmem.pyi +191 -0
- nvmath/device/__init__.py +38 -0
- nvmath/device/_deprecated.py +33 -0
- nvmath/device/common.py +315 -0
- nvmath/device/common_backend.py +131 -0
- nvmath/device/common_cuda.py +201 -0
- nvmath/device/common_numba.py +300 -0
- nvmath/device/common_numba_cuda_mlir.py +202 -0
- nvmath/device/common_opaque_tensor.py +201 -0
- nvmath/device/cublasdx.py +1606 -0
- nvmath/device/cublasdx_backend.py +860 -0
- nvmath/device/cublasdx_numba.py +1534 -0
- nvmath/device/cublasdx_numba_cuda_mlir.py +208 -0
- nvmath/device/cufftdx.py +373 -0
- nvmath/device/cufftdx_backend.py +220 -0
- nvmath/device/cufftdx_numba.py +140 -0
- nvmath/device/cufftdx_numba_cuda_mlir.py +79 -0
- nvmath/device/curand_kernel.py +9147 -0
- nvmath/device/cusolverdx.py +2708 -0
- nvmath/device/cusolverdx_backend.py +440 -0
- nvmath/device/cusolverdx_numba.py +567 -0
- nvmath/device/cusolverdx_numba_cuda_mlir.py +604 -0
- nvmath/device/cusolverdx_overload_backend.py +1029 -0
- nvmath/device/llvm_array.py +29 -0
- nvmath/device/random.py +441 -0
- nvmath/device/random_helpers.py +23 -0
- nvmath/device/random_states.py +187 -0
- nvmath/device/types.py +138 -0
- nvmath/device/vector_types_numba.py +259 -0
- nvmath/distributed/__init__.py +200 -0
- nvmath/distributed/_internal/__init__.py +0 -0
- nvmath/distributed/_internal/nccl.py +86 -0
- nvmath/distributed/_internal/nvshmem.py +307 -0
- nvmath/distributed/_internal/symmetric_memory.py +35 -0
- nvmath/distributed/_internal/tensor_ifc.py +70 -0
- nvmath/distributed/_internal/tensor_ifc_cupy.py +68 -0
- nvmath/distributed/_internal/tensor_ifc_host_device.py +172 -0
- nvmath/distributed/_internal/tensor_ifc_numpy.py +46 -0
- nvmath/distributed/_internal/tensor_ifc_torch.py +162 -0
- nvmath/distributed/_internal/tensor_wrapper.py +81 -0
- nvmath/distributed/_utils.py +167 -0
- nvmath/distributed/distribution/__init__.py +30 -0
- nvmath/distributed/distribution/_configuration.py +39 -0
- nvmath/distributed/distribution/distributions.py +1024 -0
- nvmath/distributed/distribution/redistribute.py +1284 -0
- nvmath/distributed/fft/__init__.py +7 -0
- nvmath/distributed/fft/_configuration.py +82 -0
- nvmath/distributed/fft/fft.py +2742 -0
- nvmath/distributed/linalg/__init__.py +22 -0
- nvmath/distributed/linalg/_internal/__init__.py +3 -0
- nvmath/distributed/linalg/_internal/epilog_protocol.py +586 -0
- nvmath/distributed/linalg/_internal/matmul_desc_ifc.py +28 -0
- nvmath/distributed/linalg/advanced/__init__.py +8 -0
- nvmath/distributed/linalg/advanced/_configuration.py +171 -0
- nvmath/distributed/linalg/advanced/matmulmod.py +3573 -0
- nvmath/distributed/linalg/generic/__init__.py +8 -0
- nvmath/distributed/linalg/generic/_caching.py +66 -0
- nvmath/distributed/linalg/generic/_configuration.py +61 -0
- nvmath/distributed/linalg/generic/_factorization.py +172 -0
- nvmath/distributed/linalg/generic/_initialization.py +966 -0
- nvmath/distributed/linalg/generic/_problem_spec.py +511 -0
- nvmath/distributed/linalg/generic/solvermod.py +1368 -0
- nvmath/distributed/process_group.py +408 -0
- nvmath/fft/__init__.py +7 -0
- nvmath/fft/_configuration.py +189 -0
- nvmath/fft/_exec_utils.py +82 -0
- nvmath/fft/_helpers.py +237 -0
- nvmath/fft/fft.py +3122 -0
- nvmath/internal/__init__.pxd +3 -0
- nvmath/internal/__init__.py +10 -0
- nvmath/internal/_bindings.cp314t-win_amd64.pyd +0 -0
- nvmath/internal/_bindings.pxd +18 -0
- nvmath/internal/_device_utils.py +45 -0
- nvmath/internal/_layout/__init__.pxd +3 -0
- nvmath/internal/_layout/__init__.py +7 -0
- nvmath/internal/_layout/_layout.cp314t-win_amd64.pyd +0 -0
- nvmath/internal/_layout/_layout.pxd +1303 -0
- nvmath/internal/_layout/_layout.pyi +1145 -0
- nvmath/internal/enum_utils.py +142 -0
- nvmath/internal/formatters.py +87 -0
- nvmath/internal/mem_limit.py +51 -0
- nvmath/internal/memory.cp314t-win_amd64.pyd +0 -0
- nvmath/internal/memory.pxd +13 -0
- nvmath/internal/memory.pyi +50 -0
- nvmath/internal/ndbuffer/__init__.pxd +3 -0
- nvmath/internal/ndbuffer/__init__.py +9 -0
- nvmath/internal/ndbuffer/_copy_kernel.cp314t-win_amd64.pyd +0 -0
- nvmath/internal/ndbuffer/_copy_kernel.pxd +10 -0
- nvmath/internal/ndbuffer/_jit.cp314t-win_amd64.pyd +0 -0
- nvmath/internal/ndbuffer/_jit.pxd +7 -0
- nvmath/internal/ndbuffer/_ndbuffer.cp314t-win_amd64.pyd +0 -0
- nvmath/internal/ndbuffer/_ndbuffer.pxd +40 -0
- nvmath/internal/ndbuffer/_ndbuffer.pyi +463 -0
- nvmath/internal/ndbuffer/copy_kernel/args.h +34 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/array_view.h +52 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/elementwise.h +68 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/grid_indexer.h +69 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/transposed.h +242 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/type_utils.h +39 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/utils.h +132 -0
- nvmath/internal/ndbuffer/copy_kernel/copy_kernel_impl/vec.h +159 -0
- nvmath/internal/ndbuffer/copy_kernel/elementwise.h +53 -0
- nvmath/internal/ndbuffer/copy_kernel/transposed.h +58 -0
- nvmath/internal/package_ifc.py +168 -0
- nvmath/internal/package_ifc_cuda.py +57 -0
- nvmath/internal/package_ifc_cupy.py +67 -0
- nvmath/internal/package_ifc_torch.py +69 -0
- nvmath/internal/package_wrapper.py +14 -0
- nvmath/internal/tensor_ifc.py +179 -0
- nvmath/internal/tensor_ifc_cupy.py +234 -0
- nvmath/internal/tensor_ifc_ndbuffer.py +147 -0
- nvmath/internal/tensor_ifc_numpy.py +184 -0
- nvmath/internal/tensor_ifc_torch.py +178 -0
- nvmath/internal/tensor_wrapper.py +160 -0
- nvmath/internal/typemaps.py +113 -0
- nvmath/internal/utils.py +805 -0
- nvmath/linalg/__init__.py +56 -0
- nvmath/linalg/_internal/__init__.py +3 -0
- nvmath/linalg/_internal/algo_cap_ifc.py +82 -0
- nvmath/linalg/_internal/algo_config_ifc.py +43 -0
- nvmath/linalg/_internal/batch.py +234 -0
- nvmath/linalg/_internal/enum_to_tuples.py +64 -0
- nvmath/linalg/_internal/epilog_protocol.py +766 -0
- nvmath/linalg/_internal/layout.py +624 -0
- nvmath/linalg/_internal/matmul_desc_ifc.py +28 -0
- nvmath/linalg/_internal/matmul_pref_ifc.py +27 -0
- nvmath/linalg/_internal/matrix_layout_ifc.py +26 -0
- nvmath/linalg/_internal/solver_utils.py +432 -0
- nvmath/linalg/_internal/typemaps.py +144 -0
- nvmath/linalg/_internal/utils.py +157 -0
- nvmath/linalg/advanced/__init__.py +8 -0
- nvmath/linalg/advanced/_algorithmmod.py +170 -0
- nvmath/linalg/advanced/_configuration.py +351 -0
- nvmath/linalg/advanced/helpers/__init__.py +5 -0
- nvmath/linalg/advanced/helpers/matmul.py +1316 -0
- nvmath/linalg/advanced/matmulmod.py +3734 -0
- nvmath/linalg/generic/__init__.py +53 -0
- nvmath/linalg/generic/_configuration/__init__.py +39 -0
- nvmath/linalg/generic/_configuration/layout.py +263 -0
- nvmath/linalg/generic/_configuration/match.py +734 -0
- nvmath/linalg/generic/_configuration/qualifiers.py +493 -0
- nvmath/linalg/generic/_configuration/solver_configuration.py +59 -0
- nvmath/linalg/generic/_configuration/wrap.py +217 -0
- nvmath/linalg/generic/_dtype.py +15 -0
- nvmath/linalg/generic/matmulmod.py +2094 -0
- nvmath/linalg/generic/solvermod.py +1301 -0
- nvmath/memory.py +279 -0
- nvmath/sparse/__init__.py +38 -0
- nvmath/sparse/_internal/__init__.py +21 -0
- nvmath/sparse/_internal/common_utils.py +147 -0
- nvmath/sparse/_internal/cudss_config_ifc.py +702 -0
- nvmath/sparse/_internal/cudss_data_ifc.py +399 -0
- nvmath/sparse/_internal/cudss_utils.py +506 -0
- nvmath/sparse/_internal/cusparse_utils.py +382 -0
- nvmath/sparse/_internal/sparse_bsc_ifc.py +303 -0
- nvmath/sparse/_internal/sparse_bsr_ifc.py +305 -0
- nvmath/sparse/_internal/sparse_coo_ifc.py +256 -0
- nvmath/sparse/_internal/sparse_csc_ifc.py +268 -0
- nvmath/sparse/_internal/sparse_csr_ifc.py +288 -0
- nvmath/sparse/_internal/sparse_dia_ifc.py +242 -0
- nvmath/sparse/_internal/sparse_format_helpers.py +601 -0
- nvmath/sparse/_internal/sparse_tensor_ifc.py +133 -0
- nvmath/sparse/_internal/sparse_ust_ifc.py +141 -0
- nvmath/sparse/_internal/utils.py +56 -0
- nvmath/sparse/advanced/__init__.py +7 -0
- nvmath/sparse/advanced/_configuration.py +227 -0
- nvmath/sparse/advanced/direct_solver.py +2069 -0
- nvmath/sparse/generic/__init__.py +7 -0
- nvmath/sparse/generic/_configuration.py +129 -0
- nvmath/sparse/generic/_helpers.py +137 -0
- nvmath/sparse/generic/_thunks.py +21 -0
- nvmath/sparse/generic/matmulmod.py +2353 -0
- nvmath/sparse/ust/__init__.py +7 -0
- nvmath/sparse/ust/_converters.py +422 -0
- nvmath/sparse/ust/_cpp.py +28 -0
- nvmath/sparse/ust/_drawer.py +565 -0
- nvmath/sparse/ust/_emitter.py +1033 -0
- nvmath/sparse/ust/_jit.py +188 -0
- nvmath/sparse/ust/_kernel.py +282 -0
- nvmath/sparse/ust/_utils.py +149 -0
- nvmath/sparse/ust/interfaces/__init__.py +0 -0
- nvmath/sparse/ust/interfaces/torch_interface.py +476 -0
- nvmath/sparse/ust/tensor.py +1016 -0
- nvmath/sparse/ust/tensor_format.py +957 -0
- nvmath/tensor/__init__.py +6 -0
- nvmath/tensor/_configuration.py +120 -0
- nvmath/tensor/_internal/__init__.py +3 -0
- nvmath/tensor/_internal/cutensor_config_ifc.py +279 -0
- nvmath/tensor/_internal/cutensor_utils.py +230 -0
- nvmath/tensor/_internal/data.py +43 -0
- nvmath/tensor/_internal/einsum_parser.py +444 -0
- nvmath/tensor/_internal/typemaps.py +96 -0
- nvmath/tensor/contract.py +1900 -0
- nvmath_python-1.0.0.dist-info/METADATA +134 -0
- nvmath_python-1.0.0.dist-info/RECORD +332 -0
- nvmath_python-1.0.0.dist-info/WHEEL +5 -0
- nvmath_python-1.0.0.dist-info/licenses/LICENSE +177 -0
- nvmath_python-1.0.0.dist-info/top_level.txt +2 -0
nvmath/device/types.py
ADDED
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
# Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"Complex",
|
|
7
|
+
"Vector",
|
|
8
|
+
"complex32",
|
|
9
|
+
"complex64",
|
|
10
|
+
"complex128",
|
|
11
|
+
"half2",
|
|
12
|
+
"half4",
|
|
13
|
+
"np_float16x2",
|
|
14
|
+
"np_float16x4",
|
|
15
|
+
"REAL_NP_TYPES",
|
|
16
|
+
"INT_NP_TYPES",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
import warnings
|
|
20
|
+
|
|
21
|
+
import numpy as np
|
|
22
|
+
|
|
23
|
+
np_float16x2 = np.dtype([("x", np.float16), ("y", np.float16)], align=True)
|
|
24
|
+
np_float16x4 = np.dtype([("x", np.float16), ("y", np.float16), ("z", np.float16), ("w", np.float16)], align=True)
|
|
25
|
+
|
|
26
|
+
REAL_NP_TYPES: list = [np.float16, np.float32, np.float64]
|
|
27
|
+
INT_NP_TYPES: list = [np.int8, np.int16, np.int32, np.int64, np.uint8, np.uint16, np.uint32, np.uint64]
|
|
28
|
+
|
|
29
|
+
_alignment_warning_msg = (
|
|
30
|
+
"You are using the host counterpart of a dtype that is under-aligned "
|
|
31
|
+
"compared to what the device function expects. In most cases this will "
|
|
32
|
+
"work, since device memory is typically allocated with at least 256-byte "
|
|
33
|
+
"alignment. For details on device memory access and built-in type "
|
|
34
|
+
"alignment requirements, see "
|
|
35
|
+
"https://docs.nvidia.com/cuda/cuda-c-programming-guide/#device-memory-accesses "
|
|
36
|
+
"and "
|
|
37
|
+
"https://docs.nvidia.com/cuda/cuda-programming-guide/05-appendices/cpp-language-extensions.html#built-in-types"
|
|
38
|
+
)
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class Complex:
|
|
42
|
+
"""
|
|
43
|
+
Complex type that can be used to represent complex numbers both
|
|
44
|
+
on host and device side. Numpy does not provide a built-in complex type with
|
|
45
|
+
16-bit real and imaginary parts, so we define our own dtype for that case.
|
|
46
|
+
For 32-bit and 64-bit complex numbers, we can use the built-in numpy dtypes.
|
|
47
|
+
However on device side we expect those types to be aligned to the full size
|
|
48
|
+
of the complex type, so the array defined on host and device side will have
|
|
49
|
+
different type and alignment. :py:const:`np_float16x2`,
|
|
50
|
+
``numpy.dtype(numpy.complex64)`` and
|
|
51
|
+
``numpy.dtype(numpy.complex128)`` are the host side dtypes and
|
|
52
|
+
:py:const:`float16x2_type`, :py:const:`float32x2_type` and
|
|
53
|
+
:py:const:`float64x2_type` are the device side types.
|
|
54
|
+
"""
|
|
55
|
+
|
|
56
|
+
def __init__(self, real_dtype):
|
|
57
|
+
self._real_dtype = real_dtype
|
|
58
|
+
|
|
59
|
+
@property
|
|
60
|
+
def real_dtype(self):
|
|
61
|
+
return self._real_dtype
|
|
62
|
+
|
|
63
|
+
@property
|
|
64
|
+
def dtype(self):
|
|
65
|
+
warnings.warn(_alignment_warning_msg, UserWarning, stacklevel=2)
|
|
66
|
+
if self._real_dtype == np.float16:
|
|
67
|
+
return np_float16x2
|
|
68
|
+
elif self._real_dtype == np.float32:
|
|
69
|
+
return np.dtype(np.complex64)
|
|
70
|
+
assert self._real_dtype == np.float64
|
|
71
|
+
return np.dtype(np.complex128)
|
|
72
|
+
|
|
73
|
+
@property
|
|
74
|
+
def _numba_type(self):
|
|
75
|
+
from .vector_types_numba import float16x2_type, float32x2_type, float64x2_type
|
|
76
|
+
|
|
77
|
+
if self.real_dtype == np.float16:
|
|
78
|
+
return float16x2_type
|
|
79
|
+
if self.real_dtype == np.float32:
|
|
80
|
+
return float32x2_type
|
|
81
|
+
assert self.real_dtype == np.float64
|
|
82
|
+
return float64x2_type
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
complex32 = Complex(np.float16)
|
|
86
|
+
complex64 = Complex(np.float32)
|
|
87
|
+
complex128 = Complex(np.float64)
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
class Vector:
|
|
91
|
+
"""
|
|
92
|
+
Vector type that can be used to represent vector values both
|
|
93
|
+
on host and device side. Host side representation uses numpy structured
|
|
94
|
+
dtypes to represent the vector components, while device side representation
|
|
95
|
+
uses custom numba types. This difference is necessary because device
|
|
96
|
+
functions expect alignment of the vector types to be the same as the size of
|
|
97
|
+
the vector, which is not the case for numpy structured dtypes.
|
|
98
|
+
:py:const:`np_float16x2` and :py:const:`np_float16x4` are the host side
|
|
99
|
+
dtypes and :py:const:`float16x2_type` and :py:const:`float16x4_type` are the
|
|
100
|
+
device side types.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def __init__(self, real_dtype, size):
|
|
104
|
+
if size not in (2, 4):
|
|
105
|
+
raise ValueError(f"Unsupported vector size {size}, only 2 and 4 are supported")
|
|
106
|
+
if real_dtype != np.float16:
|
|
107
|
+
raise ValueError(f"Unsupported vector real dtype {real_dtype}, only float16 is supported")
|
|
108
|
+
self._real_dtype = real_dtype
|
|
109
|
+
self._size = size
|
|
110
|
+
|
|
111
|
+
@property
|
|
112
|
+
def real_dtype(self):
|
|
113
|
+
return self._real_dtype
|
|
114
|
+
|
|
115
|
+
@property
|
|
116
|
+
def size(self):
|
|
117
|
+
return self._size
|
|
118
|
+
|
|
119
|
+
@property
|
|
120
|
+
def dtype(self):
|
|
121
|
+
warnings.warn(_alignment_warning_msg, UserWarning, stacklevel=2)
|
|
122
|
+
if self._size == 2:
|
|
123
|
+
return np_float16x2
|
|
124
|
+
assert self._size == 4
|
|
125
|
+
return np_float16x4
|
|
126
|
+
|
|
127
|
+
@property
|
|
128
|
+
def _numba_type(self):
|
|
129
|
+
from .vector_types_numba import float16x2_type, float16x4_type
|
|
130
|
+
|
|
131
|
+
if self._size == 2:
|
|
132
|
+
return float16x2_type
|
|
133
|
+
assert self._size == 4
|
|
134
|
+
return float16x4_type
|
|
135
|
+
|
|
136
|
+
|
|
137
|
+
half2 = Vector(np.float16, 2)
|
|
138
|
+
half4 = Vector(np.float16, 4)
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
# Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"float16x2",
|
|
7
|
+
"float16x4",
|
|
8
|
+
"float32x2",
|
|
9
|
+
"float32x4",
|
|
10
|
+
"float64x2",
|
|
11
|
+
"float64x4",
|
|
12
|
+
"uint32x4",
|
|
13
|
+
"float16x2_type",
|
|
14
|
+
"float16x4_type",
|
|
15
|
+
"float32x2_type",
|
|
16
|
+
"float32x4_type",
|
|
17
|
+
"float64x2_type",
|
|
18
|
+
"float64x4_type",
|
|
19
|
+
"uint32x4_type",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
import typing
|
|
23
|
+
|
|
24
|
+
from llvmlite import ir
|
|
25
|
+
from numba.core import types
|
|
26
|
+
from numba.core.typeconv import Conversion
|
|
27
|
+
from numba.core.typing.templates import AttributeTemplate
|
|
28
|
+
from numba.cuda.cudadecl import registry as cuda_registry
|
|
29
|
+
from numba.cuda.cudaimpl import lower_attr as cuda_lower_attr
|
|
30
|
+
from numba.cuda.extending import as_numba_type, lower_builtin, lower_cast, overload, register_model, type_callable
|
|
31
|
+
from numba.extending import models
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def make_vector(
|
|
35
|
+
scalar_type: types.Number,
|
|
36
|
+
llvm_scalar_type,
|
|
37
|
+
vector_length: typing.Literal[2, 4],
|
|
38
|
+
):
|
|
39
|
+
assert vector_length == 2 or vector_length == 4
|
|
40
|
+
assert isinstance(scalar_type, (types.Float, types.Integer))
|
|
41
|
+
vector_name = f"{scalar_type.name}x{vector_length}"
|
|
42
|
+
vector_bitwidth = vector_length * scalar_type.bitwidth
|
|
43
|
+
if isinstance(scalar_type, types.Float):
|
|
44
|
+
scalar_class = types.Float
|
|
45
|
+
else:
|
|
46
|
+
scalar_class = types.Integer
|
|
47
|
+
|
|
48
|
+
# User visible types and functions
|
|
49
|
+
class vector:
|
|
50
|
+
def __init__(self, *args):
|
|
51
|
+
pass
|
|
52
|
+
|
|
53
|
+
def create(input):
|
|
54
|
+
pass
|
|
55
|
+
|
|
56
|
+
# FE types
|
|
57
|
+
class vectorType(types.Number):
|
|
58
|
+
def __init__(self):
|
|
59
|
+
super().__init__(name=f"vector({scalar_type.name}x{vector_length})")
|
|
60
|
+
self.dtype = scalar_type
|
|
61
|
+
self.count = vector_length
|
|
62
|
+
self.bitwidth = vector_bitwidth
|
|
63
|
+
self.make = vector
|
|
64
|
+
|
|
65
|
+
def can_convert_from(self, typingctx, other):
|
|
66
|
+
if scalar_class is types.Float and vector_length == 2 and isinstance(other, types.Complex):
|
|
67
|
+
if other.bitwidth == vector_bitwidth:
|
|
68
|
+
return Conversion.exact
|
|
69
|
+
elif other.bitwidth < vector_bitwidth:
|
|
70
|
+
return Conversion.safe
|
|
71
|
+
elif other.bitwidth > vector_bitwidth:
|
|
72
|
+
return Conversion.unsafe
|
|
73
|
+
|
|
74
|
+
def can_convert_to(self, typingctx, other):
|
|
75
|
+
if scalar_class is types.Float and vector_length == 2 and isinstance(other, types.Complex):
|
|
76
|
+
if other.bitwidth == vector_bitwidth:
|
|
77
|
+
return Conversion.exact
|
|
78
|
+
elif other.bitwidth < vector_bitwidth:
|
|
79
|
+
return Conversion.unsafe
|
|
80
|
+
elif other.bitwidth > vector_bitwidth:
|
|
81
|
+
return Conversion.safe
|
|
82
|
+
|
|
83
|
+
def __name__(self):
|
|
84
|
+
return f"vector({scalar_type.name}x{vector_length})"
|
|
85
|
+
|
|
86
|
+
vector_type = vectorType()
|
|
87
|
+
|
|
88
|
+
types.number_domain = types.number_domain | frozenset([vector_type])
|
|
89
|
+
|
|
90
|
+
# Type inference (Python --> FE type)
|
|
91
|
+
as_numba_type.register(vector, vector_type)
|
|
92
|
+
|
|
93
|
+
# How to build a vectorType from individual elements
|
|
94
|
+
@type_callable(vector)
|
|
95
|
+
def type(context):
|
|
96
|
+
def typer(x, y=None, z=None, w=None):
|
|
97
|
+
if (
|
|
98
|
+
(
|
|
99
|
+
scalar_class is types.Float
|
|
100
|
+
and vector_length == 2
|
|
101
|
+
and isinstance(x, types.Complex)
|
|
102
|
+
and y is None
|
|
103
|
+
and z is None
|
|
104
|
+
and w is None
|
|
105
|
+
)
|
|
106
|
+
or (vector_length == 2 and all(isinstance(v, scalar_class) for v in [x, y]) and z is None and w is None)
|
|
107
|
+
or (vector_length == 4 and all(isinstance(v, scalar_class) for v in [x, y, z, w]))
|
|
108
|
+
):
|
|
109
|
+
return vector_type
|
|
110
|
+
|
|
111
|
+
return typer
|
|
112
|
+
|
|
113
|
+
# How to represent vectorType in Memory?
|
|
114
|
+
@register_model(vectorType)
|
|
115
|
+
class vectorTypeModel(models.PrimitiveModel):
|
|
116
|
+
def __init__(self, dmm, fe_type):
|
|
117
|
+
be_type = ir.IntType(vector_bitwidth)
|
|
118
|
+
super().__init__(dmm, fe_type, be_type)
|
|
119
|
+
|
|
120
|
+
# How to build a vectorType from individual float values?
|
|
121
|
+
@lower_builtin(vector, *(vector_length * [scalar_class])) # FIXME
|
|
122
|
+
def vector_ctor(context, builder, sig, args):
|
|
123
|
+
typ = sig.return_type
|
|
124
|
+
args = list(args)
|
|
125
|
+
assert len(args) == vector_length
|
|
126
|
+
for i in range(vector_length):
|
|
127
|
+
args[i] = context.cast(builder, args[i], sig.args[i], scalar_type)
|
|
128
|
+
|
|
129
|
+
vt = ir.VectorType(llvm_scalar_type, vector_length)
|
|
130
|
+
val = ir.Constant(vt, ir.Undefined)
|
|
131
|
+
|
|
132
|
+
for i in range(vector_length):
|
|
133
|
+
val = builder.insert_element(val, args[i], context.get_constant(types.int32, i))
|
|
134
|
+
|
|
135
|
+
return builder.bitcast(val, context.get_value_type(typ))
|
|
136
|
+
|
|
137
|
+
# How to access .x, .y, .z, .w
|
|
138
|
+
@cuda_registry.register_attr
|
|
139
|
+
class complex_attrs(AttributeTemplate):
|
|
140
|
+
key = vectorType
|
|
141
|
+
|
|
142
|
+
def resolve_x(self, mod):
|
|
143
|
+
return scalar_type
|
|
144
|
+
|
|
145
|
+
def resolve_y(self, mod):
|
|
146
|
+
return scalar_type
|
|
147
|
+
|
|
148
|
+
def resolve_z(self, mod):
|
|
149
|
+
return scalar_type
|
|
150
|
+
|
|
151
|
+
def resolve_w(self, mod):
|
|
152
|
+
return scalar_type
|
|
153
|
+
|
|
154
|
+
def complex_get(context, builder, typ, val, i):
|
|
155
|
+
vt = ir.VectorType(llvm_scalar_type, vector_length)
|
|
156
|
+
vec = builder.bitcast(val, vt)
|
|
157
|
+
index = context.get_constant(types.int32, i)
|
|
158
|
+
return builder.extract_element(vec, index)
|
|
159
|
+
|
|
160
|
+
# This cannot be done with a loop
|
|
161
|
+
@cuda_lower_attr(vectorType, "x")
|
|
162
|
+
def complex_get_x(context, builder, typ, val):
|
|
163
|
+
return complex_get(context, builder, typ, val, 0)
|
|
164
|
+
|
|
165
|
+
@cuda_lower_attr(vectorType, "y")
|
|
166
|
+
def complex_get_y(context, builder, typ, val):
|
|
167
|
+
return complex_get(context, builder, typ, val, 1)
|
|
168
|
+
|
|
169
|
+
if vector_length == 4:
|
|
170
|
+
|
|
171
|
+
@cuda_lower_attr(vectorType, "z")
|
|
172
|
+
def complex_get_z(context, builder, typ, val):
|
|
173
|
+
return complex_get(context, builder, typ, val, 2)
|
|
174
|
+
|
|
175
|
+
@cuda_lower_attr(vectorType, "w")
|
|
176
|
+
def complex_get_w(context, builder, typ, val):
|
|
177
|
+
return complex_get(context, builder, typ, val, 3)
|
|
178
|
+
|
|
179
|
+
# Conversions from types.Complex
|
|
180
|
+
if scalar_class is types.Float and vector_length == 2:
|
|
181
|
+
|
|
182
|
+
@overload(create)
|
|
183
|
+
def make_complex(cplx):
|
|
184
|
+
if isinstance(cplx, types.Complex):
|
|
185
|
+
|
|
186
|
+
def impl(cplx):
|
|
187
|
+
return vector(cplx.real, cplx.imag)
|
|
188
|
+
|
|
189
|
+
return impl
|
|
190
|
+
|
|
191
|
+
@lower_cast(types.Complex, vector_type)
|
|
192
|
+
def np_complex_to_complex(context, builder, fromty, toty, val):
|
|
193
|
+
src = context.make_complex(builder, fromty, value=val)
|
|
194
|
+
src_float_ty = fromty.underlying_float
|
|
195
|
+
ctor_sig = toty(src_float_ty, src_float_ty)
|
|
196
|
+
ctor_args = (src.real, src.imag)
|
|
197
|
+
return vector_ctor(context, builder, ctor_sig, ctor_args)
|
|
198
|
+
|
|
199
|
+
@lower_cast(vector_type, types.Complex)
|
|
200
|
+
def complex_to_np_complex(context, builder, fromty, toty, val):
|
|
201
|
+
dst_float_ty = toty.underlying_float
|
|
202
|
+
x = complex_get(context, builder, fromty, val, 0)
|
|
203
|
+
y = complex_get(context, builder, fromty, val, 1)
|
|
204
|
+
x = context.cast(builder, x, scalar_type, dst_float_ty)
|
|
205
|
+
y = context.cast(builder, y, scalar_type, dst_float_ty)
|
|
206
|
+
vt = context.get_value_type(toty)
|
|
207
|
+
val = ir.Constant(vt, ir.Undefined)
|
|
208
|
+
val = builder.insert_value(val, x, [0])
|
|
209
|
+
val = builder.insert_value(val, y, [1])
|
|
210
|
+
return val
|
|
211
|
+
|
|
212
|
+
vector.__name__ = vector_name
|
|
213
|
+
attrs = "``x`` and ``y``" if vector_length == 2 else "``x``, ``y``, ``z``, and ``w``"
|
|
214
|
+
vector.__doc__ = f"""
|
|
215
|
+
{vector_name}({"x, y" if vector_length == 2 else "x, y, z, w"})
|
|
216
|
+
|
|
217
|
+
A Numba-compliant vector object for {scalar_type.name} with vector length {vector_length}.
|
|
218
|
+
Elements can be accessed with the {attrs} attributes.
|
|
219
|
+
"""
|
|
220
|
+
return vector, vector_type
|
|
221
|
+
|
|
222
|
+
|
|
223
|
+
class VectorTypeClass(types.NumberClass):
|
|
224
|
+
"""
|
|
225
|
+
NumberClass for the vector FE types.
|
|
226
|
+
Reuses NumberClass for the DTypeSpec behavior but routes cls(x, y, ...)
|
|
227
|
+
to the vector constructor instead of NumberClass's single-value scalar cast.
|
|
228
|
+
"""
|
|
229
|
+
|
|
230
|
+
def get_impl_key(self, sig):
|
|
231
|
+
return self.instance_type.make
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
register_model(VectorTypeClass)(models.OpaqueModel)
|
|
235
|
+
|
|
236
|
+
|
|
237
|
+
@cuda_registry.register_attr
|
|
238
|
+
class _VectorTypeClassCallAttribute(AttributeTemplate):
|
|
239
|
+
"""
|
|
240
|
+
Overrides NumberClass's scalar-cast __call__.
|
|
241
|
+
Resolves cls(x, y, ...) to the vector constructor,
|
|
242
|
+
so calls build a vector instead of attempting a single-value cast.
|
|
243
|
+
"""
|
|
244
|
+
|
|
245
|
+
key = VectorTypeClass
|
|
246
|
+
|
|
247
|
+
def resolve___call__(self, classty):
|
|
248
|
+
return self.context.resolve_value_type(classty.instance_type.make)
|
|
249
|
+
|
|
250
|
+
|
|
251
|
+
# IMPORTANT: take care to update symbol list in docs/sphinx/device-apis/utils.rst
|
|
252
|
+
# and docstring processing in docs/sphinx/conf.py if names are changed or types are added
|
|
253
|
+
float16x2, float16x2_type = make_vector(types.float16, ir.IntType(16), 2)
|
|
254
|
+
float16x4, float16x4_type = make_vector(types.float16, ir.IntType(16), 4)
|
|
255
|
+
float32x2, float32x2_type = make_vector(types.float32, ir.FloatType(), 2)
|
|
256
|
+
float32x4, float32x4_type = make_vector(types.float32, ir.FloatType(), 4)
|
|
257
|
+
float64x2, float64x2_type = make_vector(types.float64, ir.DoubleType(), 2)
|
|
258
|
+
float64x4, float64x4_type = make_vector(types.float64, ir.DoubleType(), 4)
|
|
259
|
+
uint32x4, uint32x4_type = make_vector(types.uint32, ir.IntType(32), 4)
|
|
@@ -0,0 +1,200 @@
|
|
|
1
|
+
# Copyright (c) 2025-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
|
|
9
|
+
if sys.platform != "linux":
|
|
10
|
+
raise ImportError("nvmath.distributed is only supported on Linux.")
|
|
11
|
+
|
|
12
|
+
import atexit
|
|
13
|
+
from collections.abc import Sequence
|
|
14
|
+
from dataclasses import dataclass
|
|
15
|
+
from threading import Lock
|
|
16
|
+
from typing import TYPE_CHECKING, Any, Literal
|
|
17
|
+
|
|
18
|
+
import numpy as np
|
|
19
|
+
from cuda.core import Device
|
|
20
|
+
|
|
21
|
+
if TYPE_CHECKING:
|
|
22
|
+
import mpi4py
|
|
23
|
+
|
|
24
|
+
from nvmath.internal.utils import device_ctx
|
|
25
|
+
|
|
26
|
+
from . import distribution, fft, linalg
|
|
27
|
+
from ._internal import nvshmem
|
|
28
|
+
from ._utils import allocate_symmetric_memory, free_symmetric_memory
|
|
29
|
+
from .process_group import MPIProcessGroup, ProcessGroup, TorchProcessGroup
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"initialize",
|
|
33
|
+
"finalize",
|
|
34
|
+
"get_context",
|
|
35
|
+
"ProcessGroup",
|
|
36
|
+
"MPIProcessGroup",
|
|
37
|
+
"TorchProcessGroup",
|
|
38
|
+
"allocate_symmetric_memory",
|
|
39
|
+
"free_symmetric_memory",
|
|
40
|
+
"distribution",
|
|
41
|
+
"fft",
|
|
42
|
+
"linalg",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
_initialize_mutex = Lock()
|
|
46
|
+
_atexit_registered = False
|
|
47
|
+
_ctx = None
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(frozen=True)
|
|
51
|
+
class DistributedContext:
|
|
52
|
+
"""
|
|
53
|
+
Context of initialized ``nvmath.distributed`` runtime.
|
|
54
|
+
|
|
55
|
+
Attributes:
|
|
56
|
+
device_id: CUDA device ID associated with the distributed runtime
|
|
57
|
+
on this process.
|
|
58
|
+
|
|
59
|
+
process_group: ``nvmath.distributed`` participating processes.
|
|
60
|
+
|
|
61
|
+
nvshmem_available: True if NVSHMEM backend was selected at initialization.
|
|
62
|
+
|
|
63
|
+
nccl_comm: nccl4py communicator if NCCL backend was selected at
|
|
64
|
+
initialization, None otherwise.
|
|
65
|
+
"""
|
|
66
|
+
|
|
67
|
+
device_id: int
|
|
68
|
+
process_group: ProcessGroup
|
|
69
|
+
nvshmem_available: bool
|
|
70
|
+
nccl_comm: Any | None
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def initialize(
|
|
74
|
+
device_id: int,
|
|
75
|
+
process_group: ProcessGroup | mpi4py.MPI.Comm,
|
|
76
|
+
backends: Sequence[Literal["nvshmem", "nccl"]],
|
|
77
|
+
) -> None:
|
|
78
|
+
"""Initialize ``nvmath.distributed`` runtime. This is required before any distributed
|
|
79
|
+
operations can be performed. **Note that this is a collective operation and must be
|
|
80
|
+
called by all processes.**
|
|
81
|
+
|
|
82
|
+
If the runtime is already initialized this function will raise an error. If you need
|
|
83
|
+
to reinitialize the runtime (for example with different backends) you have to finalize
|
|
84
|
+
it first.
|
|
85
|
+
|
|
86
|
+
Note: NCCL doesn't allow assigning more than one process to the same GPU.
|
|
87
|
+
|
|
88
|
+
Args:
|
|
89
|
+
device_id: CUDA device ID to associate with the ``nvmath.distributed`` runtime
|
|
90
|
+
on this process.
|
|
91
|
+
|
|
92
|
+
process_group: ProcessGroup (or mpi4py communicator) specifying the participating
|
|
93
|
+
processes. This is used for setup and not for communication during compute.
|
|
94
|
+
|
|
95
|
+
backends: Communication backends to use in distributed computations. Valid values
|
|
96
|
+
are "nvshmem" and "nccl". Note that specific libraries (cuFFTMp, cuBLASMp, ...)
|
|
97
|
+
have specific required backends.
|
|
98
|
+
"""
|
|
99
|
+
if not isinstance(device_id, int):
|
|
100
|
+
raise TypeError(
|
|
101
|
+
"The device ID used to initialize the nvmath.distributed module "
|
|
102
|
+
f"must be an integer. The provided device ID is {device_id}."
|
|
103
|
+
)
|
|
104
|
+
|
|
105
|
+
valid_backends = ("nvshmem", "nccl")
|
|
106
|
+
for backend in backends:
|
|
107
|
+
if backend not in valid_backends:
|
|
108
|
+
raise ValueError(f"backend must be one of {valid_backends}, got {backend}")
|
|
109
|
+
|
|
110
|
+
if len(backends) == 0:
|
|
111
|
+
raise ValueError(f"Need to specify at least one of {valid_backends} communication backends")
|
|
112
|
+
|
|
113
|
+
if process_group is None:
|
|
114
|
+
raise ValueError("process_group cannot be None")
|
|
115
|
+
|
|
116
|
+
with _initialize_mutex:
|
|
117
|
+
global _atexit_registered, _ctx
|
|
118
|
+
|
|
119
|
+
if _ctx is not None:
|
|
120
|
+
raise RuntimeError("nvmath.distributed has already been initialized")
|
|
121
|
+
|
|
122
|
+
if not _atexit_registered:
|
|
123
|
+
atexit.register(finalize)
|
|
124
|
+
_atexit_registered = True
|
|
125
|
+
|
|
126
|
+
if not isinstance(process_group, ProcessGroup):
|
|
127
|
+
valid_process_group = False
|
|
128
|
+
if "mpi4py" in sys.modules:
|
|
129
|
+
import mpi4py.MPI
|
|
130
|
+
|
|
131
|
+
if isinstance(process_group, mpi4py.MPI.Comm):
|
|
132
|
+
process_group = MPIProcessGroup(process_group)
|
|
133
|
+
valid_process_group = True
|
|
134
|
+
if not valid_process_group:
|
|
135
|
+
raise TypeError(
|
|
136
|
+
f"Unrecognized process group type ({process_group}). "
|
|
137
|
+
"Need nvmath.distributed.ProcessGroup or mpi4py communicator."
|
|
138
|
+
)
|
|
139
|
+
|
|
140
|
+
assert isinstance(process_group, ProcessGroup) # for type-checker
|
|
141
|
+
rank = process_group.rank
|
|
142
|
+
nranks = process_group.nranks
|
|
143
|
+
|
|
144
|
+
# Set the device for NVSHMEM and NCCL initialization, but also need to make sure
|
|
145
|
+
# that a CUDA context has been created. We can't rely on `device_ctx` to do it
|
|
146
|
+
# since it's not guaranteed to make a runtime API call.
|
|
147
|
+
old_device = Device()
|
|
148
|
+
device = Device(device_id)
|
|
149
|
+
device.set_current()
|
|
150
|
+
|
|
151
|
+
try:
|
|
152
|
+
nvshmem_available = False
|
|
153
|
+
if "nvshmem" in backends:
|
|
154
|
+
nvshmem.initialize(device_id, process_group)
|
|
155
|
+
nvshmem_available = True
|
|
156
|
+
|
|
157
|
+
nccl_comm = None
|
|
158
|
+
if "nccl" in backends:
|
|
159
|
+
# Don't want to import this at the global scope yet because
|
|
160
|
+
# `import nccl.core` imports cupy and torch (with nccl4py 0.1.1)
|
|
161
|
+
import nccl.core as nccl # type: ignore
|
|
162
|
+
|
|
163
|
+
# Create NCCL communicator.
|
|
164
|
+
unique_id = nccl.get_unique_id()
|
|
165
|
+
process_group.broadcast_buffer(unique_id.as_ndarray.view(np.int8), root=0)
|
|
166
|
+
nccl_comm = nccl.Communicator.init(nranks=nranks, rank=rank, unique_id=unique_id)
|
|
167
|
+
|
|
168
|
+
_ctx = DistributedContext(
|
|
169
|
+
device_id=device_id, process_group=process_group, nvshmem_available=nvshmem_available, nccl_comm=nccl_comm
|
|
170
|
+
)
|
|
171
|
+
finally:
|
|
172
|
+
old_device.set_current()
|
|
173
|
+
|
|
174
|
+
|
|
175
|
+
def finalize() -> None:
|
|
176
|
+
"""Finalize ``nvmath.distributed`` runtime (this is called automatically at exit
|
|
177
|
+
if the runtime is initialized). **Note that this is a collective operation and
|
|
178
|
+
must be called by all processes.**"""
|
|
179
|
+
global _ctx
|
|
180
|
+
with _initialize_mutex:
|
|
181
|
+
if _ctx is None:
|
|
182
|
+
return
|
|
183
|
+
|
|
184
|
+
linalg.advanced.matmulmod._grid_cache.clear()
|
|
185
|
+
linalg.generic._caching.clear_handles()
|
|
186
|
+
|
|
187
|
+
if _ctx.nccl_comm is not None:
|
|
188
|
+
with device_ctx(_ctx.device_id):
|
|
189
|
+
_ctx.nccl_comm.finalize()
|
|
190
|
+
_ctx.nccl_comm.destroy()
|
|
191
|
+
|
|
192
|
+
if _ctx.nvshmem_available:
|
|
193
|
+
nvshmem.finalize(_ctx.device_id)
|
|
194
|
+
|
|
195
|
+
_ctx = None
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
def get_context() -> DistributedContext | None:
|
|
199
|
+
"""Return the distributed runtime's context or None if not initialized."""
|
|
200
|
+
return _ctx
|
|
File without changes
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
# Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
from __future__ import annotations
|
|
6
|
+
|
|
7
|
+
__all__ = [
|
|
8
|
+
"nccl_empty_dlpack",
|
|
9
|
+
"NcclNDBufferAllocator",
|
|
10
|
+
]
|
|
11
|
+
|
|
12
|
+
import logging
|
|
13
|
+
from typing import TYPE_CHECKING
|
|
14
|
+
|
|
15
|
+
from .symmetric_memory import calculate_symmetric_size
|
|
16
|
+
|
|
17
|
+
if TYPE_CHECKING:
|
|
18
|
+
from cuda.core import Buffer, Stream
|
|
19
|
+
|
|
20
|
+
from nvmath.distributed import DistributedContext
|
|
21
|
+
from nvmath.distributed.process_group import ProcessGroup
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def nccl_empty_dlpack(
|
|
25
|
+
size: int,
|
|
26
|
+
device_id: int,
|
|
27
|
+
process_group: ProcessGroup,
|
|
28
|
+
make_symmetric: bool = False,
|
|
29
|
+
skip_symmetric_check: bool = False,
|
|
30
|
+
logger: logging.Logger | None = None,
|
|
31
|
+
) -> Buffer:
|
|
32
|
+
"""
|
|
33
|
+
Allocates a buffer with nccl4py. This function can be used to prepare a buffer
|
|
34
|
+
for symmetric memory registration, but **doesn't register the buffer as symmetric
|
|
35
|
+
memory** (because buffer registrations in NCCL are not global but on a
|
|
36
|
+
per-communicator basis, so for registration the buffer typically has to be passed
|
|
37
|
+
to the library that owns the communicator).
|
|
38
|
+
|
|
39
|
+
This is a collective call when ``make_symmetric=True`` or
|
|
40
|
+
``skip_symmetric_check=False``.
|
|
41
|
+
|
|
42
|
+
Args:
|
|
43
|
+
size: Number of bytes to allocate.
|
|
44
|
+
|
|
45
|
+
device_id: Device on which to allocate.
|
|
46
|
+
|
|
47
|
+
process_group: nvmath.distributed process group.
|
|
48
|
+
|
|
49
|
+
make_symmetric: If the buffer is going to be registered with NCCL symmetric memory,
|
|
50
|
+
use ``make_symmetric=True`` to allocate the same size on every process (padding
|
|
51
|
+
the buffer to the maximum size given by all processes).
|
|
52
|
+
|
|
53
|
+
skip_symmetric_check: True if buffer is not needed for NCCL symmetric memory or
|
|
54
|
+
already known to be of the same size on every process.
|
|
55
|
+
"""
|
|
56
|
+
|
|
57
|
+
logger = logger if logger is not None else logging.getLogger()
|
|
58
|
+
|
|
59
|
+
size = calculate_symmetric_size(size, process_group, make_symmetric, skip_symmetric_check, logger)
|
|
60
|
+
|
|
61
|
+
import nccl.core as nccl # type: ignore
|
|
62
|
+
|
|
63
|
+
mem_buffer = nccl.buffer.mem_alloc(size, device_id)
|
|
64
|
+
return mem_buffer
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
class NcclNDBufferAllocator:
|
|
68
|
+
__slots__ = ("ctx", "make_symmetric", "skip_symmetric_check")
|
|
69
|
+
|
|
70
|
+
def __init__(self, device_id: int, ctx: DistributedContext, make_symmetric: bool, skip_symmetric_check: bool):
|
|
71
|
+
assert ctx.device_id == device_id, (
|
|
72
|
+
"Internal error: attempting to allocate symmetric memory on a device not used by NCCL on this process"
|
|
73
|
+
)
|
|
74
|
+
self.ctx = ctx
|
|
75
|
+
self.make_symmetric = make_symmetric
|
|
76
|
+
self.skip_symmetric_check = skip_symmetric_check
|
|
77
|
+
|
|
78
|
+
def allocate(self, size: int, stream: Stream, logger: logging.Logger | None = None) -> Buffer:
|
|
79
|
+
return nccl_empty_dlpack(
|
|
80
|
+
size,
|
|
81
|
+
self.ctx.device_id,
|
|
82
|
+
self.ctx.process_group,
|
|
83
|
+
make_symmetric=self.make_symmetric,
|
|
84
|
+
skip_symmetric_check=self.skip_symmetric_check,
|
|
85
|
+
logger=logger,
|
|
86
|
+
)
|