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