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/__init__.pxd
ADDED
|
File without changes
|
nvmath/__init__.py
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
import importlib.metadata
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
# Attempt to preload libraries. Fail silently if preload fails.
|
|
9
|
+
def _force_lib_load():
|
|
10
|
+
from nvmath._utils import module_init_force_cupy_lib_load
|
|
11
|
+
|
|
12
|
+
module_init_force_cupy_lib_load()
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
_force_lib_load()
|
|
16
|
+
|
|
17
|
+
from nvmath import ( # noqa: E402
|
|
18
|
+
bindings, # noqa: E402
|
|
19
|
+
fft,
|
|
20
|
+
linalg,
|
|
21
|
+
sparse,
|
|
22
|
+
tensor,
|
|
23
|
+
)
|
|
24
|
+
from nvmath._utils import ( # noqa: E402
|
|
25
|
+
ComputeType,
|
|
26
|
+
CudaDataType,
|
|
27
|
+
LibraryPropertyType,
|
|
28
|
+
)
|
|
29
|
+
from nvmath.memory import BaseCUDAMemoryManager, BaseCUDAMemoryManagerAsync, MemoryPointer # noqa: E402
|
|
30
|
+
|
|
31
|
+
__all__ = [
|
|
32
|
+
"BaseCUDAMemoryManager",
|
|
33
|
+
"BaseCUDAMemoryManagerAsync",
|
|
34
|
+
"bindings",
|
|
35
|
+
"ComputeType",
|
|
36
|
+
"CudaDataType",
|
|
37
|
+
"fft",
|
|
38
|
+
"LibraryPropertyType",
|
|
39
|
+
"linalg",
|
|
40
|
+
"MemoryPointer",
|
|
41
|
+
"sparse",
|
|
42
|
+
"tensor",
|
|
43
|
+
]
|
|
44
|
+
|
|
45
|
+
__version__ = importlib.metadata.version("nvmath-python")
|
|
File without changes
|
|
@@ -0,0 +1,330 @@
|
|
|
1
|
+
# Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Generic factory that builds a Python class with one ``property`` per
|
|
7
|
+
enum member, plus optional ``set_<attr>_unchecked`` methods, from
|
|
8
|
+
caller-supplied closures.
|
|
9
|
+
|
|
10
|
+
The low-level :func:`_make_attribute_interface_impl` is generic:
|
|
11
|
+
it receives *make_getter* / *make_setter* / *make_setter_unchecked*
|
|
12
|
+
callables that produce the per-attribute closures, then attaches them
|
|
13
|
+
as properties.
|
|
14
|
+
|
|
15
|
+
Library-specific wrappers (e.g. :func:`make_cublas_attribute_interface`)
|
|
16
|
+
supply the closure factories that encode a particular C API convention
|
|
17
|
+
(argument order, ``size_written`` type, logging, etc.).
|
|
18
|
+
"""
|
|
19
|
+
|
|
20
|
+
__all__ = ["make_cublas_attribute_interface"]
|
|
21
|
+
|
|
22
|
+
import ctypes
|
|
23
|
+
import enum
|
|
24
|
+
import logging
|
|
25
|
+
import sys
|
|
26
|
+
from collections.abc import Callable
|
|
27
|
+
from typing import TYPE_CHECKING, Any
|
|
28
|
+
|
|
29
|
+
import numpy as np
|
|
30
|
+
|
|
31
|
+
logger = logging.getLogger()
|
|
32
|
+
|
|
33
|
+
# A closure factory accepted by _impl. Called once per attribute name;
|
|
34
|
+
# returns the bound method to attach as a getter, setter, etc.
|
|
35
|
+
_ClosureFactory = Callable[[str], Callable]
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def _make_attribute_interface_impl(
|
|
39
|
+
*,
|
|
40
|
+
class_module: str,
|
|
41
|
+
class_name: str,
|
|
42
|
+
attribute_names: list[str],
|
|
43
|
+
make_getter: _ClosureFactory,
|
|
44
|
+
make_setter: _ClosureFactory | None = None,
|
|
45
|
+
make_setter_unchecked: _ClosureFactory | None = None,
|
|
46
|
+
) -> type:
|
|
47
|
+
"""Build and return an attribute interface class.
|
|
48
|
+
|
|
49
|
+
This is the low-level implementation behind library-specific wrappers
|
|
50
|
+
such as :func:`make_cublas_attribute_interface`. It performs no
|
|
51
|
+
caller-site validation and is completely agnostic to the underlying
|
|
52
|
+
C API convention -- all library-specific logic lives in the closure
|
|
53
|
+
factories passed by the caller.
|
|
54
|
+
|
|
55
|
+
Args:
|
|
56
|
+
class_module: ``__module__`` for the generated class.
|
|
57
|
+
class_name: ``__name__`` and ``__qualname__`` for the generated
|
|
58
|
+
class.
|
|
59
|
+
attribute_names: Uppercase attribute names (e.g.
|
|
60
|
+
``["POINTER_MODE", "EPILOGUE"]``).
|
|
61
|
+
make_getter: ``(name) -> getter`` factory that returns a
|
|
62
|
+
property getter closure.
|
|
63
|
+
make_setter: ``(name) -> setter`` factory that returns a
|
|
64
|
+
property setter closure, or ``None`` for read-only
|
|
65
|
+
interfaces.
|
|
66
|
+
make_setter_unchecked: ``(name) -> setter`` factory for
|
|
67
|
+
hot-path setters without logging, or ``None`` to skip.
|
|
68
|
+
"""
|
|
69
|
+
|
|
70
|
+
class AttributeInterface: # noqa: D101
|
|
71
|
+
if TYPE_CHECKING:
|
|
72
|
+
|
|
73
|
+
def __getattr__(self, name: str) -> Any: ...
|
|
74
|
+
def __setattr__(self, name: str, value: Any) -> None: ...
|
|
75
|
+
|
|
76
|
+
def __init__(self, obj: Any) -> None:
|
|
77
|
+
self._handle = obj
|
|
78
|
+
|
|
79
|
+
AttributeInterface.__name__ = class_name
|
|
80
|
+
AttributeInterface.__qualname__ = class_name
|
|
81
|
+
AttributeInterface.__module__ = class_module
|
|
82
|
+
|
|
83
|
+
for _name in attribute_names:
|
|
84
|
+
_lower = _name.lower()
|
|
85
|
+
|
|
86
|
+
getter = make_getter(_name)
|
|
87
|
+
|
|
88
|
+
if make_setter is not None:
|
|
89
|
+
setter = make_setter(_name)
|
|
90
|
+
if make_setter_unchecked is not None:
|
|
91
|
+
setattr(
|
|
92
|
+
AttributeInterface,
|
|
93
|
+
f"set_{_lower}_unchecked",
|
|
94
|
+
make_setter_unchecked(_name),
|
|
95
|
+
)
|
|
96
|
+
else:
|
|
97
|
+
setter = None
|
|
98
|
+
setattr(AttributeInterface, _lower, property(getter, setter))
|
|
99
|
+
|
|
100
|
+
return AttributeInterface
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
# ---------------------------------------------------------------------------
|
|
104
|
+
# cuBLAS-specific wrapper
|
|
105
|
+
# ---------------------------------------------------------------------------
|
|
106
|
+
|
|
107
|
+
|
|
108
|
+
def _build_enum_to_ctype_map(
|
|
109
|
+
attribute_enum: type[enum.IntEnum],
|
|
110
|
+
get_attribute_dtype_fn: Callable[[enum.IntEnum], np.dtype],
|
|
111
|
+
exclude: set[str] | None = None,
|
|
112
|
+
) -> dict[str, tuple[int, type, int]]:
|
|
113
|
+
"""
|
|
114
|
+
Return ``{NAME: (enum_int_value, ctypes_type, sizeof)}`` for every
|
|
115
|
+
non-deprecated enum member whose name is not in *exclude*.
|
|
116
|
+
"""
|
|
117
|
+
result: dict[str, tuple[int, type, int]] = {}
|
|
118
|
+
for member in attribute_enum:
|
|
119
|
+
name: str = member.name
|
|
120
|
+
|
|
121
|
+
if exclude and name in exclude:
|
|
122
|
+
continue
|
|
123
|
+
|
|
124
|
+
try:
|
|
125
|
+
dtype: np.dtype = get_attribute_dtype_fn(member)
|
|
126
|
+
except ValueError:
|
|
127
|
+
continue
|
|
128
|
+
|
|
129
|
+
try:
|
|
130
|
+
ctype = np.ctypeslib.as_ctypes_type(dtype)
|
|
131
|
+
except NotImplementedError as exc:
|
|
132
|
+
raise TypeError(
|
|
133
|
+
f"get_attribute_dtype_fn returned a dtype for {name!r} that cannot be converted to a ctypes type"
|
|
134
|
+
) from exc
|
|
135
|
+
|
|
136
|
+
result[name] = (member.value, ctype, ctypes.sizeof(ctype))
|
|
137
|
+
|
|
138
|
+
return result
|
|
139
|
+
|
|
140
|
+
|
|
141
|
+
def _cublas_interface_class_doc(
|
|
142
|
+
*,
|
|
143
|
+
attribute_enum: type[enum.IntEnum],
|
|
144
|
+
get_attribute_fn: Callable[..., None],
|
|
145
|
+
set_attribute_fn: Callable[..., None] | None,
|
|
146
|
+
with_unchecked: bool,
|
|
147
|
+
attribute_names: list[str],
|
|
148
|
+
) -> str:
|
|
149
|
+
enum_qualname = f"{attribute_enum.__module__}.{attribute_enum.__qualname__}"
|
|
150
|
+
getter_qualname = f"{get_attribute_fn.__module__}.{get_attribute_fn.__qualname__}"
|
|
151
|
+
|
|
152
|
+
names_sorted = sorted(attribute_names)
|
|
153
|
+
if names_sorted:
|
|
154
|
+
example_upper = names_sorted[0]
|
|
155
|
+
example_lower = example_upper.lower()
|
|
156
|
+
else:
|
|
157
|
+
example_upper = "UPPER_CASE"
|
|
158
|
+
example_lower = "lower_case"
|
|
159
|
+
|
|
160
|
+
properties_extra = (
|
|
161
|
+
" (and their ``set_<attr>_unchecked`` variants)" if set_attribute_fn is not None and with_unchecked else ""
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
lines = [
|
|
165
|
+
f"Property-based access to attributes enumerated in\n``{enum_qualname}``.",
|
|
166
|
+
"",
|
|
167
|
+
f"Properties{properties_extra} are generated \ndynamically at import time by ``make_cublas_attribute_interface``.",
|
|
168
|
+
"",
|
|
169
|
+
"This avoids hand-writing a property for every entry in "
|
|
170
|
+
f"``{enum_qualname}``, \nkeeps the interface in sync automatically when new\n"
|
|
171
|
+
"attributes are added to the binding, and normalises the UPPER_CASE enum\n"
|
|
172
|
+
"names to lowercase property names "
|
|
173
|
+
f"(e.g. ``{example_upper}`` becomes ``{example_lower}``).",
|
|
174
|
+
"",
|
|
175
|
+
]
|
|
176
|
+
|
|
177
|
+
if set_attribute_fn is not None:
|
|
178
|
+
set_qualname = f"{set_attribute_fn.__module__}.{set_attribute_fn.__qualname__}"
|
|
179
|
+
delegates = f"``{getter_qualname}`` / ``{set_qualname}``"
|
|
180
|
+
else:
|
|
181
|
+
delegates = f"``{getter_qualname}``"
|
|
182
|
+
lines.append(
|
|
183
|
+
"Each generated property delegates to the corresponding\n"
|
|
184
|
+
f"{delegates} \n"
|
|
185
|
+
"call with the correct enum value and ctypes type\n"
|
|
186
|
+
"already baked in via closure."
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
if set_attribute_fn is not None and with_unchecked:
|
|
190
|
+
lines.extend(
|
|
191
|
+
[
|
|
192
|
+
"",
|
|
193
|
+
"The ``set_<attr>_unchecked`` methods are identical to the property\n"
|
|
194
|
+
"setters but skip debug logging. They are useful in low-overhead code paths \n"
|
|
195
|
+
"where the attribute type has already been validated during initialization.",
|
|
196
|
+
]
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
return "\n".join(lines)
|
|
200
|
+
|
|
201
|
+
|
|
202
|
+
def make_cublas_attribute_interface(
|
|
203
|
+
*,
|
|
204
|
+
class_module: str,
|
|
205
|
+
class_name: str,
|
|
206
|
+
attribute_enum: type[enum.IntEnum],
|
|
207
|
+
get_attribute_dtype_fn: Callable[[enum.IntEnum], np.dtype],
|
|
208
|
+
get_attribute_fn: Callable[..., None],
|
|
209
|
+
set_attribute_fn: Callable[..., None] | None = None,
|
|
210
|
+
with_unchecked: bool = False,
|
|
211
|
+
exclude: set[str] | None = None,
|
|
212
|
+
) -> type:
|
|
213
|
+
"""
|
|
214
|
+
Build and return an attribute interface class for a cuBLAS-style
|
|
215
|
+
descriptor whose attributes are accessed through a uniform
|
|
216
|
+
``*_get_attribute`` / ``*_set_attribute`` C API pattern.
|
|
217
|
+
|
|
218
|
+
The returned class's docstring is generated automatically from the
|
|
219
|
+
enum and the getter/setter callables (see *Returns*).
|
|
220
|
+
|
|
221
|
+
This is the public entry point for all cublasLt and cublasMp
|
|
222
|
+
descriptor / algorithm interface modules.
|
|
223
|
+
For example, the entire ``MatmulDescInterface`` for
|
|
224
|
+
``nvmath.linalg`` is reduced to::
|
|
225
|
+
|
|
226
|
+
from nvmath.bindings import cublasLt as cublaslt
|
|
227
|
+
|
|
228
|
+
MatmulDescInterface = make_cublas_attribute_interface(
|
|
229
|
+
class_module=__name__,
|
|
230
|
+
class_name="MatmulDescInterface",
|
|
231
|
+
attribute_enum=cublaslt.MatmulDescAttribute,
|
|
232
|
+
get_attribute_dtype_fn=cublaslt.get_matmul_desc_attribute_dtype,
|
|
233
|
+
get_attribute_fn=cublaslt.matmul_desc_get_attribute,
|
|
234
|
+
set_attribute_fn=cublaslt.matmul_desc_set_attribute,
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
The opaque library handle passed to ``__init__`` is stored as
|
|
238
|
+
``self._handle``. Subclasses that need direct access to it (e.g.
|
|
239
|
+
for variable-length attribute queries) should use ``self._handle``.
|
|
240
|
+
|
|
241
|
+
Args:
|
|
242
|
+
class_module: The ``__module__`` to assign to the generated
|
|
243
|
+
class. Callers should pass ``__name__``.
|
|
244
|
+
class_name: The ``__name__`` and ``__qualname__`` to assign to
|
|
245
|
+
the generated class. Must match the module-level variable
|
|
246
|
+
name the caller assigns the result to.
|
|
247
|
+
attribute_enum: The binding's enum type whose members enumerate
|
|
248
|
+
all gettable/settable attributes.
|
|
249
|
+
get_attribute_dtype_fn: Callable that maps an enum member to its
|
|
250
|
+
numpy dtype.
|
|
251
|
+
get_attribute_fn: Callable invoked as
|
|
252
|
+
``(handle, enum_value, buf_addr, buf_size, size_written_addr)``
|
|
253
|
+
that must store the attribute's value into *buf_addr*.
|
|
254
|
+
set_attribute_fn: Callable invoked as
|
|
255
|
+
``(handle, enum_value, buf_addr, buf_size)`` that must apply
|
|
256
|
+
the new value from *buf_addr*. ``None`` for read-only
|
|
257
|
+
interfaces.
|
|
258
|
+
with_unchecked: If ``True``, generate ``set_<attr>_unchecked``
|
|
259
|
+
methods (no debug logging). Requires *set_attribute_fn*.
|
|
260
|
+
exclude: Enum member names to skip.
|
|
261
|
+
|
|
262
|
+
Returns:
|
|
263
|
+
A class with one *lowercase* property per enum member.
|
|
264
|
+
Its ``__doc__`` is built automatically from the enum
|
|
265
|
+
and the getter/setter callables.
|
|
266
|
+
|
|
267
|
+
.. note::
|
|
268
|
+
This function **must** be called at module scope (i.e. as a
|
|
269
|
+
top-level statement in the module body). A ``RuntimeError`` is
|
|
270
|
+
raised otherwise. This constraint ensures that the generated
|
|
271
|
+
class receives a correct ``__qualname__``.
|
|
272
|
+
"""
|
|
273
|
+
|
|
274
|
+
if with_unchecked and set_attribute_fn is None:
|
|
275
|
+
raise ValueError("with_unchecked=True requires set_attribute_fn")
|
|
276
|
+
|
|
277
|
+
caller_frame = sys._getframe(1)
|
|
278
|
+
if caller_frame.f_code.co_name != "<module>":
|
|
279
|
+
raise RuntimeError("make_cublas_attribute_interface must be called at module level")
|
|
280
|
+
|
|
281
|
+
attr_info = _build_enum_to_ctype_map(attribute_enum, get_attribute_dtype_fn, exclude)
|
|
282
|
+
|
|
283
|
+
def _cublas_make_getter(name):
|
|
284
|
+
enum_value, ctype, sizeof = attr_info[name]
|
|
285
|
+
|
|
286
|
+
def getter(self):
|
|
287
|
+
logger.debug("Getting attribute %s.", name)
|
|
288
|
+
buf = ctype()
|
|
289
|
+
sw = ctypes.c_uint64()
|
|
290
|
+
get_attribute_fn(self._handle, enum_value, ctypes.addressof(buf), sizeof, ctypes.addressof(sw))
|
|
291
|
+
return buf.value
|
|
292
|
+
|
|
293
|
+
return getter
|
|
294
|
+
|
|
295
|
+
def _cublas_make_setter(name):
|
|
296
|
+
enum_value, ctype, sizeof = attr_info[name]
|
|
297
|
+
|
|
298
|
+
def setter(self, value):
|
|
299
|
+
logger.debug("Setting attribute %s to %s.", name, value)
|
|
300
|
+
v = ctype(value)
|
|
301
|
+
set_attribute_fn(self._handle, enum_value, ctypes.addressof(v), sizeof)
|
|
302
|
+
|
|
303
|
+
return setter
|
|
304
|
+
|
|
305
|
+
def _cublas_make_setter_unchecked(name):
|
|
306
|
+
enum_value, ctype, sizeof = attr_info[name]
|
|
307
|
+
|
|
308
|
+
def setter_unc(self, value):
|
|
309
|
+
v = ctype(value)
|
|
310
|
+
set_attribute_fn(self._handle, enum_value, ctypes.addressof(v), sizeof)
|
|
311
|
+
|
|
312
|
+
return setter_unc
|
|
313
|
+
|
|
314
|
+
attr_names = list(attr_info)
|
|
315
|
+
iface_cls = _make_attribute_interface_impl(
|
|
316
|
+
class_module=class_module,
|
|
317
|
+
class_name=class_name,
|
|
318
|
+
attribute_names=attr_names,
|
|
319
|
+
make_getter=_cublas_make_getter,
|
|
320
|
+
make_setter=_cublas_make_setter if set_attribute_fn is not None else None,
|
|
321
|
+
make_setter_unchecked=(_cublas_make_setter_unchecked if (set_attribute_fn is not None and with_unchecked) else None),
|
|
322
|
+
)
|
|
323
|
+
iface_cls.__doc__ = _cublas_interface_class_doc(
|
|
324
|
+
attribute_enum=attribute_enum,
|
|
325
|
+
get_attribute_fn=get_attribute_fn,
|
|
326
|
+
set_attribute_fn=set_attribute_fn,
|
|
327
|
+
with_unchecked=with_unchecked,
|
|
328
|
+
attribute_names=attr_names,
|
|
329
|
+
)
|
|
330
|
+
return iface_cls
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
# Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
A collection of (internal use) helper functions for shape/stride
|
|
7
|
+
validation and manipulation.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
from collections.abc import Sequence
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
def is_contiguous_layout(sorted_shape: Sequence[int], sorted_strides: Sequence[int]) -> bool:
|
|
14
|
+
return all(sorted_shape[s - 1] * sorted_strides[s - 1] == sorted_strides[s] for s in range(1, len(sorted_strides)))
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
def is_contiguous_in_memory(shape: Sequence[int], strides: Sequence[int]) -> bool:
|
|
18
|
+
"""
|
|
19
|
+
Check if the provided (shape, strides) result in a contiguous memory layout.
|
|
20
|
+
"""
|
|
21
|
+
sorted_strides, sorted_shape = zip(*sorted(zip(strides, shape, strict=True)), strict=True)
|
|
22
|
+
return is_contiguous_layout(sorted_shape, sorted_strides)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
def is_contiguous_and_dense(shape: Sequence[int], strides: Sequence[int]) -> bool:
|
|
26
|
+
"""
|
|
27
|
+
Check if the provided (shape, strides) result in a contiguous memory layout
|
|
28
|
+
with no extra stride in least strided dimension.
|
|
29
|
+
"""
|
|
30
|
+
if not shape and not strides:
|
|
31
|
+
return True
|
|
32
|
+
sorted_strides, sorted_shape = zip(*sorted(zip(strides, shape, strict=True)), strict=True)
|
|
33
|
+
if len(sorted_strides) > 0 and sorted_strides[0] != 1:
|
|
34
|
+
return False
|
|
35
|
+
return is_contiguous_layout(sorted_shape, sorted_strides)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def is_overlapping_layout(shape: Sequence[int], strides: Sequence[int]) -> bool:
|
|
39
|
+
"""
|
|
40
|
+
For a tensor `t`, if `not is_overlapping_layout(t.shape, t.strides)`,
|
|
41
|
+
there are no two different valid nd-indices `idxs` such that
|
|
42
|
+
`t[idxs_0]` and `t[idxs_1]` map to the same offset in the memory.
|
|
43
|
+
Checks that the strides:
|
|
44
|
+
1. are positive
|
|
45
|
+
2. any n - 1 extents maximal offset is smaller than the stride
|
|
46
|
+
of the n-th extent.
|
|
47
|
+
The check should return False for contiguous
|
|
48
|
+
or contiguous and sliced tensors.
|
|
49
|
+
"""
|
|
50
|
+
if not shape and not strides:
|
|
51
|
+
return False
|
|
52
|
+
sorted_strides, sorted_shape = zip(*sorted(zip(strides, shape, strict=True)), strict=True)
|
|
53
|
+
cur_max_offset = 0
|
|
54
|
+
for s in range(1, len(sorted_strides)):
|
|
55
|
+
stride = sorted_strides[s - 1]
|
|
56
|
+
extent = sorted_shape[s - 1]
|
|
57
|
+
if stride <= 0:
|
|
58
|
+
return True
|
|
59
|
+
cur_max_offset += stride * (extent - 1)
|
|
60
|
+
if cur_max_offset >= sorted_strides[s]:
|
|
61
|
+
return True
|
|
62
|
+
return False
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
def check_monotonic_strides(strides, reverse: bool):
|
|
66
|
+
"""
|
|
67
|
+
Check if the strides are monotonically increasing (reverse=False) or
|
|
68
|
+
decreasing (reverse=True).
|
|
69
|
+
"""
|
|
70
|
+
return sorted(strides, reverse=reverse) == list(strides)
|
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
# Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
import dataclasses
|
|
6
|
+
import logging
|
|
7
|
+
from logging import Logger
|
|
8
|
+
from typing import ClassVar, Literal
|
|
9
|
+
|
|
10
|
+
from nvmath import memory
|
|
11
|
+
from nvmath.internal import utils
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
@dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
|
|
15
|
+
class ExecutionCUDA:
|
|
16
|
+
"""
|
|
17
|
+
A data class for providing GPU execution options.
|
|
18
|
+
|
|
19
|
+
Attributes:
|
|
20
|
+
device_id: CUDA device ordinal (only used if the operand resides on the CPU). The
|
|
21
|
+
default value is 0.
|
|
22
|
+
|
|
23
|
+
.. seealso::
|
|
24
|
+
:class:`ExecutionCPU`
|
|
25
|
+
"""
|
|
26
|
+
|
|
27
|
+
name: ClassVar[Literal["cuda"]] = "cuda"
|
|
28
|
+
device_id: int = 0
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
@dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
|
|
32
|
+
class ExecutionCPU:
|
|
33
|
+
"""
|
|
34
|
+
A data class for providing CPU execution options.
|
|
35
|
+
|
|
36
|
+
Attributes:
|
|
37
|
+
num_threads: The number of CPU threads used to execute the operation.
|
|
38
|
+
If not specified, defaults to the number of CPU cores available to the
|
|
39
|
+
process.
|
|
40
|
+
|
|
41
|
+
.. seealso::
|
|
42
|
+
:class:`ExecutionCUDA`
|
|
43
|
+
"""
|
|
44
|
+
|
|
45
|
+
name: ClassVar[Literal["cpu"]] = "cpu"
|
|
46
|
+
num_threads: int | None = None
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def copy_operand_perhaps(
|
|
50
|
+
internal_operand: utils.TensorHolder | None,
|
|
51
|
+
operand: utils.TensorHolder,
|
|
52
|
+
stream_holder: utils.StreamHolder | None,
|
|
53
|
+
execution_device_id: int | Literal["cpu"],
|
|
54
|
+
operands_device_id: int | Literal["cpu"],
|
|
55
|
+
) -> tuple[utils.TensorHolder, utils.TensorHolder | None]:
|
|
56
|
+
"""Private implementation of memory space management for tensor operands.
|
|
57
|
+
|
|
58
|
+
The `copy_operand_perhaps` function facilitates transitions of tensor operands between
|
|
59
|
+
different memory spaces, ensuring compatibility with execution requirements. Its role is
|
|
60
|
+
to determine whether a tensor operand needs to be copied to accommodate differing
|
|
61
|
+
execution and operand memory spaces, while preserving the original operand for cases
|
|
62
|
+
requiring in-place operations.
|
|
63
|
+
|
|
64
|
+
Args:
|
|
65
|
+
internal_operand: Represents an internal tensor for in-place
|
|
66
|
+
memory operations, or `None` if not applicable.
|
|
67
|
+
|
|
68
|
+
operand: Tensor to possibly copied to the execution memory space.
|
|
69
|
+
|
|
70
|
+
stream_holder: Manages the CUDA stream for device operations.
|
|
71
|
+
|
|
72
|
+
execution_device_id: Specifies the target execution space.
|
|
73
|
+
|
|
74
|
+
operands_device_id: Specifies the current operand memory space.
|
|
75
|
+
|
|
76
|
+
Returns:
|
|
77
|
+
A tuple containing:
|
|
78
|
+
- The operand copied to the execution space, or the original operand if
|
|
79
|
+
no copy is necessary.
|
|
80
|
+
- The original operand, or `None` if no copy occurred.
|
|
81
|
+
|
|
82
|
+
"""
|
|
83
|
+
if execution_device_id == operands_device_id:
|
|
84
|
+
return operand, None
|
|
85
|
+
else:
|
|
86
|
+
# Copy the `operand` to memory that matches the exec space
|
|
87
|
+
# and keep the original `operand` to handle `options.inplace=True`
|
|
88
|
+
if internal_operand is None:
|
|
89
|
+
exec_space_copy = operand.to(execution_device_id, stream_holder)
|
|
90
|
+
return exec_space_copy, operand
|
|
91
|
+
else:
|
|
92
|
+
# In-place copy to existing pointer
|
|
93
|
+
internal_operand.copy_(src=operand, stream_holder=stream_holder)
|
|
94
|
+
return internal_operand, operand
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
@dataclasses.dataclass(frozen=True, slots=True, kw_only=True)
|
|
98
|
+
class StatefulAPIOptions:
|
|
99
|
+
"""A dataclass for providing options to a stateful API object.
|
|
100
|
+
|
|
101
|
+
Attributes:
|
|
102
|
+
allocator: An object that supports the :class:`BaseCUDAMemoryManager` protocol, used
|
|
103
|
+
to draw device memory. If an allocator is not provided, a memory allocator from
|
|
104
|
+
the library package will be used (:func:`torch.cuda.caching_allocator_alloc` for
|
|
105
|
+
PyTorch operands, :func:`cupy.cuda.alloc` otherwise).
|
|
106
|
+
|
|
107
|
+
blocking: A flag specifying the behavior of the stream-ordered functions and
|
|
108
|
+
methods. When ``blocking`` is `True`, the stream-ordered methods do not return
|
|
109
|
+
until the operation is complete. When ``blocking`` is ``"auto"``, the methods
|
|
110
|
+
return immediately when the inputs are on the GPU. The stream-ordered methods
|
|
111
|
+
always block when the operands are on the CPU to ensure that the user doesn't
|
|
112
|
+
inadvertently use the result before it becomes available. The default is
|
|
113
|
+
``"auto"``.
|
|
114
|
+
|
|
115
|
+
logger: Python Logger object. The root logger will be used if a
|
|
116
|
+
logger object is not provided.
|
|
117
|
+
"""
|
|
118
|
+
|
|
119
|
+
allocator: memory.BaseCUDAMemoryManager | memory.BaseCUDAMemoryManagerAsync | None = None
|
|
120
|
+
blocking: Literal[True, "auto"] = "auto"
|
|
121
|
+
logger: Logger = dataclasses.field(default_factory=logging.getLogger)
|
|
122
|
+
|
|
123
|
+
def __post_init__(self):
|
|
124
|
+
if self.blocking not in (True, "auto"):
|
|
125
|
+
raise ValueError("The value specified for blocking must be either True or 'auto'.")
|
|
126
|
+
|
|
127
|
+
if self.allocator is not None and not isinstance(
|
|
128
|
+
self.allocator, memory.BaseCUDAMemoryManager | memory.BaseCUDAMemoryManagerAsync
|
|
129
|
+
):
|
|
130
|
+
raise TypeError("The allocator must be an object of type that fulfills the BaseCUDAMemoryManager protocol.")
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
# Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
"""Reusable per-thread data structures."""
|
|
6
|
+
|
|
7
|
+
import sys
|
|
8
|
+
import threading
|
|
9
|
+
import typing
|
|
10
|
+
import weakref
|
|
11
|
+
|
|
12
|
+
__all__ = ["HandleCache"]
|
|
13
|
+
|
|
14
|
+
Key = typing.TypeVar("Key")
|
|
15
|
+
|
|
16
|
+
|
|
17
|
+
class _CachedHandle(typing.Generic[Key]):
|
|
18
|
+
# A raw integer handle cannot be the referent of `weakref.finalize`, so we wrap it in
|
|
19
|
+
# this object and attach the finalizer to the wrapper. The finalizer fires when the
|
|
20
|
+
# wrapper is garbage-collected — i.e. when the owning thread's `threading.local` storage
|
|
21
|
+
# is dropped, or at interpreter shutdown.
|
|
22
|
+
|
|
23
|
+
__slots__ = ("handle", "__weakref__")
|
|
24
|
+
|
|
25
|
+
handle: typing.Final[int]
|
|
26
|
+
|
|
27
|
+
def __init__(self, key: Key, create: typing.Callable[[Key], int], destroy: typing.Callable[[int], None]):
|
|
28
|
+
self.handle = create(key)
|
|
29
|
+
|
|
30
|
+
def finalize(handle: int) -> None:
|
|
31
|
+
# `destroy` is the binding-agnostic callable injected by the consumer. It is
|
|
32
|
+
# expected to be safe to run on any thread regardless of the current device:
|
|
33
|
+
# e.g. `cublasDestroy` / `cublasLtDestroy` each internally call
|
|
34
|
+
# `cudaDeviceSynchronize()` on the handle's own device. That matters because the
|
|
35
|
+
# finalizer may run on whichever thread garbage-collects the cached handle, not
|
|
36
|
+
# necessarily the one that created it.
|
|
37
|
+
try:
|
|
38
|
+
destroy(handle)
|
|
39
|
+
except Exception:
|
|
40
|
+
# Tolerate destroy failures only at interpreter shutdown, where the bindings
|
|
41
|
+
# module may already be partially torn down. Outside shutdown a destroy
|
|
42
|
+
# failure is a real bug; re-raising surfaces it as the standard "Exception
|
|
43
|
+
# ignored in:" message from the finalizer rather than silently leaking the
|
|
44
|
+
# handle.
|
|
45
|
+
if not sys.is_finalizing():
|
|
46
|
+
raise
|
|
47
|
+
|
|
48
|
+
weakref.finalize(self, finalize, self.handle)
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
class HandleCache(typing.Generic[Key]):
|
|
52
|
+
"""Per-thread cache of integer library handles, keyed by an opaque key.
|
|
53
|
+
|
|
54
|
+
The key is any hashable the caller chooses: a bare ``device_id``, or a tuple such as
|
|
55
|
+
``(device_id, ...)`` for libraries that need to distinguish handles by more than device.
|
|
56
|
+
The cache never interprets the key — only the injected ``create`` callable does. Because
|
|
57
|
+
the caching behavior is per-thread, keys only have to be unique per-thread.
|
|
58
|
+
|
|
59
|
+
Args:
|
|
60
|
+
|
|
61
|
+
create: ``create(key) -> int`` makes a new handle for ``key``. The
|
|
62
|
+
callable must perform any device-context handling it needs.
|
|
63
|
+
|
|
64
|
+
destroy: ``destroy(handle) -> None`` destroys a handle. It is invoked
|
|
65
|
+
from ``weakref.finalize`` when the owning thread or the interpreter exits, so
|
|
66
|
+
must be able to run on a different thread than the one that created the handle.
|
|
67
|
+
|
|
68
|
+
Examples:
|
|
69
|
+
|
|
70
|
+
.. code-block:: python
|
|
71
|
+
|
|
72
|
+
from nvmath._internal import threadsafe
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
def create_handle(device_id: int) -> int:
|
|
76
|
+
with utils.device_ctx(device_id):
|
|
77
|
+
return cublas.create()
|
|
78
|
+
|
|
79
|
+
|
|
80
|
+
threadsafe.HandleCache[int](
|
|
81
|
+
create=create_handle,
|
|
82
|
+
destroy=cublas.destroy,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
"""
|
|
86
|
+
|
|
87
|
+
__slots__ = ("_create", "_destroy", "_tls")
|
|
88
|
+
|
|
89
|
+
def __init__(self, create: typing.Callable[[Key], int], destroy: typing.Callable[[int], None]):
|
|
90
|
+
self._create = create
|
|
91
|
+
self._destroy = destroy
|
|
92
|
+
# Per-thread storage; each thread mutates only its own `cache` dict, so
|
|
93
|
+
# no lock is required.
|
|
94
|
+
self._tls = threading.local()
|
|
95
|
+
|
|
96
|
+
def get(self, key: Key) -> int:
|
|
97
|
+
"""Return the cached handle for ``key`` on the current thread."""
|
|
98
|
+
try:
|
|
99
|
+
cache = self._tls.cache
|
|
100
|
+
except AttributeError:
|
|
101
|
+
cache = self._tls.cache = {}
|
|
102
|
+
item = cache.get(key)
|
|
103
|
+
if item is None:
|
|
104
|
+
item = _CachedHandle(key, self._create, self._destroy)
|
|
105
|
+
cache[key] = item
|
|
106
|
+
return item.handle
|