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
|
@@ -0,0 +1,1016 @@
|
|
|
1
|
+
# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
This module defines the universal sparse tensor (UST).
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__all__ = ["Tensor"]
|
|
10
|
+
|
|
11
|
+
import contextlib
|
|
12
|
+
import re
|
|
13
|
+
from typing import Literal
|
|
14
|
+
|
|
15
|
+
import numpy as np
|
|
16
|
+
from cuda import pathfinder
|
|
17
|
+
|
|
18
|
+
from nvmath._internal.layout import is_contiguous_and_dense
|
|
19
|
+
from nvmath.internal import tensor_wrapper, utils
|
|
20
|
+
from nvmath.sparse._internal.common_utils import sparse_or_dense, wrap_sparse_operand
|
|
21
|
+
|
|
22
|
+
from ._converters import TensorConverter
|
|
23
|
+
from ._drawer import animate_tensor, draw_tensor, draw_tensor_raw, draw_tensor_storage
|
|
24
|
+
from ._emitter import emit_apply_kernel, populate_apply_parameters
|
|
25
|
+
from ._jit import compile_cpp_and_link, compile_python_function, launch_kernel
|
|
26
|
+
from ._utils import LevelMap, resolve_stream, stream_context
|
|
27
|
+
from .tensor_format import Dimension, LevelFormat, TensorFormat, is_unique
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def _tensor_to_list(wrapped_operand, truncate=32):
|
|
31
|
+
m = wrapped_operand.memory_buffer()
|
|
32
|
+
stream = None if m.device_id == "cpu" else utils.get_or_create_stream(m.device_id, None, m.name)
|
|
33
|
+
o = [str(v.item()) for v in m.__class__(m.tensor[:truncate]).to("cpu", stream).tensor]
|
|
34
|
+
if m.size > truncate:
|
|
35
|
+
o += ["..."]
|
|
36
|
+
if m.size > 2 * truncate:
|
|
37
|
+
o += [str(v.item()) for v in m.__class__(m.tensor[-truncate:]).to("cpu", stream).tensor]
|
|
38
|
+
|
|
39
|
+
return "[{}]".format(", ".join(o))
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def _top_array(value, *, dtype, device_id, dense_tensorholder_type, stream_holder):
|
|
43
|
+
a = utils.create_empty_tensor(dense_tensorholder_type, (2,), dtype, device_id, stream_holder, verify_strides=False)
|
|
44
|
+
a.tensor[0], a.tensor[1] = 0, value
|
|
45
|
+
return a
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
def _axis_order_in_memory(shape, strides):
|
|
49
|
+
"""
|
|
50
|
+
Compute the order in which the axes appear in memory.
|
|
51
|
+
"""
|
|
52
|
+
# Scalars.
|
|
53
|
+
if len(shape) == 0:
|
|
54
|
+
return []
|
|
55
|
+
|
|
56
|
+
# The shape is used to resolve cases like (1, 2, 1) : (2, 1, 1) in CuTe notation.
|
|
57
|
+
# Use numpy's lexsort for better performance (avoids creating intermediate tuples)
|
|
58
|
+
ndim = len(strides)
|
|
59
|
+
axis_order = np.lexsort((np.arange(ndim), shape, strides))
|
|
60
|
+
return tuple(axis_order)
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
def _calculate_strides(shape, axis_order):
|
|
64
|
+
"""
|
|
65
|
+
Calculate the strides for the provided shape and axis order.
|
|
66
|
+
"""
|
|
67
|
+
strides = [None] * len(shape)
|
|
68
|
+
|
|
69
|
+
stride = 1
|
|
70
|
+
for axis in axis_order:
|
|
71
|
+
strides[axis] = stride
|
|
72
|
+
stride *= shape[axis]
|
|
73
|
+
|
|
74
|
+
return strides
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def _dense_ust_axis_order(tensor_format, shape):
|
|
78
|
+
"""
|
|
79
|
+
Compute the strides for a dense contiguous N-D tensor represented as a UST.
|
|
80
|
+
"""
|
|
81
|
+
dimensions = tensor_format.dimensions
|
|
82
|
+
level_keys = list(tensor_format.levels.keys())
|
|
83
|
+
axis_order = [None] * len(dimensions)
|
|
84
|
+
for d, label in enumerate(dimensions):
|
|
85
|
+
index = level_keys.index(label)
|
|
86
|
+
axis_order[index] = d
|
|
87
|
+
|
|
88
|
+
return list(reversed(axis_order))
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
def _get_smallest_index_type(index):
|
|
92
|
+
dtypes = [np.int32, np.int64]
|
|
93
|
+
for dtype in dtypes:
|
|
94
|
+
try:
|
|
95
|
+
dtype(index)
|
|
96
|
+
break
|
|
97
|
+
except OverflowError:
|
|
98
|
+
pass
|
|
99
|
+
|
|
100
|
+
return np.dtype(dtype).name
|
|
101
|
+
|
|
102
|
+
|
|
103
|
+
SHARED_UST_DOCUMENTATION = utils.COMMON_SHARED_DOC_MAP.copy()
|
|
104
|
+
SHARED_UST_DOCUMENTATION.update(
|
|
105
|
+
{
|
|
106
|
+
#
|
|
107
|
+
"extents": "The extents (shape) of the sparse tensor as a Python sequence",
|
|
108
|
+
#
|
|
109
|
+
"tensor_format": "The sparse tensor format as a :class:`TensorFormat` object.",
|
|
110
|
+
#
|
|
111
|
+
"index_type": """\
|
|
112
|
+
The index type of the tensor as a NumPy-recognizable string or CudaDataType.""".replace("\n", " "),
|
|
113
|
+
#
|
|
114
|
+
"dtype": """\
|
|
115
|
+
The datatype of the tensor as a NumPy-recognizable string or CudaDataType.""".replace("\n", " "),
|
|
116
|
+
}
|
|
117
|
+
)
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
@utils.docstring_decorator(SHARED_UST_DOCUMENTATION, skip_missing=False)
|
|
121
|
+
class Tensor:
|
|
122
|
+
"""
|
|
123
|
+
The universal sparse tensor binds extents, data type, index type, etc with a universal
|
|
124
|
+
sparse tensor format object.
|
|
125
|
+
|
|
126
|
+
.. note:: The constructor is currently private and for internal use only. Users should
|
|
127
|
+
use any of the methods :meth:`Tensor.from_package`, :meth:`Tensor.from_file`, or
|
|
128
|
+
:meth:`Tensor.convert` to construct a UST.
|
|
129
|
+
|
|
130
|
+
|
|
131
|
+
Args:
|
|
132
|
+
extents: {extents}
|
|
133
|
+
tensor_format: {tensor_format}
|
|
134
|
+
index_type: {index_type}
|
|
135
|
+
dtype: {dtype}
|
|
136
|
+
package: The package using which the dense tensors underlying the sparse
|
|
137
|
+
representation will be allocated. It must be a Python module object.
|
|
138
|
+
"""
|
|
139
|
+
|
|
140
|
+
def __init__(
|
|
141
|
+
self, extents, *, tensor_format=None, index_type="int32", dtype="float32", package=None, options=None, stream=None
|
|
142
|
+
):
|
|
143
|
+
if tensor_format is None:
|
|
144
|
+
raise TypeError("The tensor format must be specified.")
|
|
145
|
+
if not isinstance(tensor_format, TensorFormat):
|
|
146
|
+
raise TypeError(f"The tensor format {tensor_format} must be a TensorFormat object.")
|
|
147
|
+
if len(extents) != tensor_format.num_dimensions:
|
|
148
|
+
raise TypeError(
|
|
149
|
+
f"The tensor rank {len(extents)} must equal the format dimensionality \
|
|
150
|
+
{tensor_format.num_dimensions}."
|
|
151
|
+
)
|
|
152
|
+
if np.prod(extents) <= 0:
|
|
153
|
+
raise TypeError(f"The tensor extents {extents} do not define any elements.")
|
|
154
|
+
|
|
155
|
+
assert options is None, "Internal error."
|
|
156
|
+
|
|
157
|
+
self._extents = list(extents)
|
|
158
|
+
self._levels = tensor_format.dim2lvl(extents, True)
|
|
159
|
+
self._tensor_format = tensor_format
|
|
160
|
+
self._index_type = index_type
|
|
161
|
+
self._dtype = dtype
|
|
162
|
+
self._package = package
|
|
163
|
+
|
|
164
|
+
# The UST uses a dictionary from "levels" to the package arrays for the positions
|
|
165
|
+
# and coordinates, and a direct array for values.
|
|
166
|
+
self._pos = LevelMap()
|
|
167
|
+
self._crd = LevelMap()
|
|
168
|
+
self._val = None
|
|
169
|
+
|
|
170
|
+
# Set wrapped operand.
|
|
171
|
+
self._wrapped_operand = None
|
|
172
|
+
|
|
173
|
+
# Kernel.
|
|
174
|
+
self._kernel = None
|
|
175
|
+
self._with_indices = False
|
|
176
|
+
|
|
177
|
+
@classmethod
|
|
178
|
+
def from_file(cls, filename, stream=None):
|
|
179
|
+
"""
|
|
180
|
+
A helper to create a universal sparse tensor (in COO representation) from a file.
|
|
181
|
+
The supported file formats are the Matrix Market format (.mtx) for matrices and
|
|
182
|
+
the FROSTT format (.tns) for tensors.
|
|
183
|
+
|
|
184
|
+
Args:
|
|
185
|
+
filename: The path to the source file (with a '.mtx' or '.tns' extension).
|
|
186
|
+
stream: {stream}
|
|
187
|
+
"""
|
|
188
|
+
message = f"The format of {filename} is not recognized. The supported extensions are '.mtx' and '.tns'"
|
|
189
|
+
m = re.match(r".*\.(\w+)", filename)
|
|
190
|
+
if m is None:
|
|
191
|
+
raise TypeError(message)
|
|
192
|
+
suffix = m.group(1)
|
|
193
|
+
|
|
194
|
+
if suffix == "mtx":
|
|
195
|
+
# As of version 1.12, scipy.io.mmread is based on fast_matrix_market. This
|
|
196
|
+
# provides the fastest way to read from the Matrix Market and simplifies
|
|
197
|
+
# getting the COO in canonical format.
|
|
198
|
+
|
|
199
|
+
try:
|
|
200
|
+
import scipy as sp
|
|
201
|
+
except ImportError:
|
|
202
|
+
raise ImportError("SciPy is required for this functionality.") from None
|
|
203
|
+
|
|
204
|
+
coo = sp.io.mmread(filename, spmatrix=True)
|
|
205
|
+
coo.sum_duplicates()
|
|
206
|
+
return cls.from_package(coo, stream=stream)
|
|
207
|
+
|
|
208
|
+
if suffix == "tns":
|
|
209
|
+
# Convenience Python utility to map file data to indices and values. Note
|
|
210
|
+
# that we could use this map to construct the UST directly as an unordered
|
|
211
|
+
# and nonunique COO format. However, we go through torch tensor to simplify
|
|
212
|
+
# getting the COO in canonical format.
|
|
213
|
+
try:
|
|
214
|
+
import torch
|
|
215
|
+
except ImportError:
|
|
216
|
+
raise ImportError("PyTorch is required for this functionality.") from None
|
|
217
|
+
|
|
218
|
+
data = np.genfromtxt(filename, comments="#", unpack=True, dtype=None)
|
|
219
|
+
num_dimensions = len(data) - 1
|
|
220
|
+
|
|
221
|
+
if num_dimensions < 1:
|
|
222
|
+
raise ValueError(f"cannot parse {filename} with FROSTT structure")
|
|
223
|
+
|
|
224
|
+
extents = tuple(max(data[d]) for d in range(num_dimensions))
|
|
225
|
+
indices = torch.stack([torch.as_tensor(data[d] - 1) for d in range(num_dimensions)], dim=0)
|
|
226
|
+
values = data[num_dimensions]
|
|
227
|
+
|
|
228
|
+
coo = torch.sparse_coo_tensor(indices, values, extents)
|
|
229
|
+
return cls.from_package(coo.coalesce(), stream=stream)
|
|
230
|
+
|
|
231
|
+
raise TypeError(message)
|
|
232
|
+
|
|
233
|
+
@classmethod
|
|
234
|
+
def from_package(cls, tensor, stream=None):
|
|
235
|
+
"""
|
|
236
|
+
Create an universal sparse tensor from a sparse package tensor. This is a
|
|
237
|
+
zero-copy operation where the UST shares a view of the sparse data with the
|
|
238
|
+
sparse tensor. A strided tensor (like :class:`numpy.ndarray` or
|
|
239
|
+
:class:`torch.Tensor`) can also be viewed as a UST, with the current limitation
|
|
240
|
+
that there are no "holes" (the strided tensor is dense, in other words).
|
|
241
|
+
|
|
242
|
+
The currently supported libraries are NumPy, SciPy, CuPy, and PyTorch and
|
|
243
|
+
both CPU and GPU (CUDA) memory spaces are supported.
|
|
244
|
+
|
|
245
|
+
Args:
|
|
246
|
+
tensor: The source sparse tensor from NumPy, SciPy, CuPy, or PyTorch packages.
|
|
247
|
+
stream: {stream}
|
|
248
|
+
|
|
249
|
+
Returns:
|
|
250
|
+
A UST view of the sparse or dense package tensor.
|
|
251
|
+
"""
|
|
252
|
+
# Note: since the API takes in a stream object, it's the user's responsibility
|
|
253
|
+
# to ensure ordering of operations wrt to the provided stream. If we decide to use
|
|
254
|
+
# a package stream for UST, then we will internally order it wrt the user stream.
|
|
255
|
+
# The `wrapped.operand.to_ust()` method does this if a stream pointer is provided.
|
|
256
|
+
|
|
257
|
+
tensor_type = sparse_or_dense(tensor)
|
|
258
|
+
if tensor_type == "sparse":
|
|
259
|
+
try:
|
|
260
|
+
wrapped_operand = wrap_sparse_operand(tensor)
|
|
261
|
+
# If we decide to use a package stream for UST, then we will internally
|
|
262
|
+
# order it wrt the user stream. The `wrapped.operand.to_ust()` method does
|
|
263
|
+
# this if a stream pointer is provided.
|
|
264
|
+
device_id = wrapped_operand.device_id
|
|
265
|
+
device_ctx = contextlib.nullcontext() if device_id == "cpu" else utils.device_ctx(device_id)
|
|
266
|
+
with device_ctx:
|
|
267
|
+
ust = wrapped_operand.to_ust(stream=stream)
|
|
268
|
+
ust._wrapped_operand = wrapped_operand
|
|
269
|
+
return ust
|
|
270
|
+
except Exception as e:
|
|
271
|
+
raise TypeError(f"The sparse operand type {type(tensor)} is unsupported or invalid.") from e
|
|
272
|
+
|
|
273
|
+
# If we decide to use a package stream for UST, then we will internally order
|
|
274
|
+
# it wrt the user stream. The `synchronize_dense_tensor` function does this
|
|
275
|
+
# for dense operands.
|
|
276
|
+
|
|
277
|
+
assert tensor_type == "dense", "Internal error."
|
|
278
|
+
|
|
279
|
+
return cls._from_dense(tensor)
|
|
280
|
+
|
|
281
|
+
def _is_dense_format(self):
|
|
282
|
+
"""
|
|
283
|
+
Check if the UST representation is a dense strided layout.
|
|
284
|
+
"""
|
|
285
|
+
# TODO: Use the level spec instead of inferring from the name.
|
|
286
|
+
return self.tensor_format.name.startswith("Scalar") or self.tensor_format.name.startswith("Dense")
|
|
287
|
+
|
|
288
|
+
def _dense_tensor_from_buffer(self):
|
|
289
|
+
"""
|
|
290
|
+
Create a view of the buffer as a dense tensor (only applicable to strided layout).
|
|
291
|
+
"""
|
|
292
|
+
shape = self.extents
|
|
293
|
+
axis_order = _dense_ust_axis_order(self.tensor_format, shape)
|
|
294
|
+
strides = _calculate_strides(shape, axis_order)
|
|
295
|
+
|
|
296
|
+
wrapped_dense = self._val.memory_buffer_to_tensor(shape, strides) # unflatten
|
|
297
|
+
|
|
298
|
+
return wrapped_dense
|
|
299
|
+
|
|
300
|
+
def to_package(self, options=None, stream=None):
|
|
301
|
+
"""
|
|
302
|
+
Create a sparse package tensor from the universal sparse tensor. This is a
|
|
303
|
+
zero-copy operation where the UST shares a view of the sparse data with the package
|
|
304
|
+
tensor.
|
|
305
|
+
|
|
306
|
+
Returns:
|
|
307
|
+
The sparse or dense package tensor view into this UST.
|
|
308
|
+
"""
|
|
309
|
+
device_id = self.device_id
|
|
310
|
+
device_ctx = contextlib.nullcontext() if device_id == "cpu" else utils.device_ctx(device_id)
|
|
311
|
+
with device_ctx:
|
|
312
|
+
if self._is_dense_format():
|
|
313
|
+
# Extract from the wrapped operand, if it exists.
|
|
314
|
+
if self._wrapped_operand is not None:
|
|
315
|
+
return self._wrapped_operand.tensor
|
|
316
|
+
|
|
317
|
+
# Otherwise, create a new dense tensor view.
|
|
318
|
+
return self._dense_tensor_from_buffer().tensor
|
|
319
|
+
|
|
320
|
+
return self._wrapped_operand.to_package()
|
|
321
|
+
|
|
322
|
+
def convert(self, *, tensor_format=None, index_type=None, dtype=None, copy=None, stream=None):
|
|
323
|
+
"""
|
|
324
|
+
Convert a UST into a new UST with the specified format, index type, and data
|
|
325
|
+
type. The default values of these for the target UST are taken from the
|
|
326
|
+
corresponding ones in the source UST.
|
|
327
|
+
|
|
328
|
+
Please note that this is a proof-of-concept implementation that supports
|
|
329
|
+
converting any UST format into any other UST format. Even though some
|
|
330
|
+
fast-path solutions are provided, the current general solution in
|
|
331
|
+
:class:`TensorConverter` has not been optimized for speed yet.
|
|
332
|
+
|
|
333
|
+
.. note:: The target shares a source kernel only for trivial conversions.
|
|
334
|
+
|
|
335
|
+
Args:
|
|
336
|
+
tensor_format: {tensor_format}
|
|
337
|
+
index_type: {index_type}
|
|
338
|
+
dtype: {dtype}
|
|
339
|
+
copy: for the case where the target tensor format, data and index
|
|
340
|
+
types match those of the source object, return the same object if
|
|
341
|
+
``copy=None`` or return a copy if ``copy=True``. In the complementary
|
|
342
|
+
case (where any of tensor format, data or index types don't match)
|
|
343
|
+
a copy is always made.
|
|
344
|
+
|
|
345
|
+
Returns:
|
|
346
|
+
A UST in the specified format, with the specified index and data types.
|
|
347
|
+
"""
|
|
348
|
+
if tensor_format is None:
|
|
349
|
+
tensor_format = self.tensor_format
|
|
350
|
+
if index_type is None:
|
|
351
|
+
index_type = self.index_type
|
|
352
|
+
if dtype is None:
|
|
353
|
+
dtype = self.dtype
|
|
354
|
+
|
|
355
|
+
# Fast path.
|
|
356
|
+
if self.tensor_format.name == tensor_format.name and self.index_type == index_type and self.dtype == dtype:
|
|
357
|
+
return self.clone(stream=stream) if copy else self
|
|
358
|
+
|
|
359
|
+
target = Tensor(
|
|
360
|
+
self.extents,
|
|
361
|
+
tensor_format=tensor_format,
|
|
362
|
+
index_type=index_type,
|
|
363
|
+
dtype=dtype,
|
|
364
|
+
stream=stream,
|
|
365
|
+
)
|
|
366
|
+
TensorConverter(self, target).run(stream=stream)
|
|
367
|
+
|
|
368
|
+
# Set wrapped operand.
|
|
369
|
+
if target._is_dense_format():
|
|
370
|
+
target._wrapped_operand = target._dense_tensor_from_buffer()
|
|
371
|
+
else:
|
|
372
|
+
target._wrapped_operand = wrap_sparse_operand(target)
|
|
373
|
+
|
|
374
|
+
return target
|
|
375
|
+
|
|
376
|
+
def to(self, device_id: int | Literal["cpu"], stream=None):
|
|
377
|
+
"""Copy the UST to a different device (contents only).
|
|
378
|
+
|
|
379
|
+
The sparse representation is not copied (it is a view) if the UST
|
|
380
|
+
is already on the requested device.
|
|
381
|
+
|
|
382
|
+
.. note:: Any kernel associated with the source is not copied over to target.
|
|
383
|
+
|
|
384
|
+
Args:
|
|
385
|
+
device_id: The CUDA device ordinal, or "cpu".
|
|
386
|
+
stream: {stream}
|
|
387
|
+
|
|
388
|
+
Returns:
|
|
389
|
+
A copy of the UST on the specified device.
|
|
390
|
+
"""
|
|
391
|
+
package = self._dense_tensorholder_type.name
|
|
392
|
+
|
|
393
|
+
if (device_id == "cpu") != (self.device_id == "cpu") and package != "torch":
|
|
394
|
+
raise NotImplementedError(
|
|
395
|
+
f"The to() operation from memory space {self.device_id} to memory space {device_id} \
|
|
396
|
+
currently requires the UST to be backed by PyTorch storage."
|
|
397
|
+
)
|
|
398
|
+
|
|
399
|
+
if (
|
|
400
|
+
isinstance(device_id, int)
|
|
401
|
+
and isinstance(self.device_id, int)
|
|
402
|
+
and device_id != self.device_id
|
|
403
|
+
and package != "torch"
|
|
404
|
+
):
|
|
405
|
+
raise ValueError(
|
|
406
|
+
"The to() operation currently supports copying across different devices only for \
|
|
407
|
+
a UST backed by PyTorch storage."
|
|
408
|
+
)
|
|
409
|
+
|
|
410
|
+
stream_holder = resolve_stream(stream, self, device_id)
|
|
411
|
+
return self._to(device_id, stream_holder)
|
|
412
|
+
|
|
413
|
+
def _to(self, device_id: int | Literal["cpu"], stream_holder):
|
|
414
|
+
target = Tensor(
|
|
415
|
+
self.extents,
|
|
416
|
+
tensor_format=self.tensor_format,
|
|
417
|
+
index_type=self.index_type,
|
|
418
|
+
dtype=self.dtype,
|
|
419
|
+
package=self._package,
|
|
420
|
+
options=None,
|
|
421
|
+
stream=stream_holder,
|
|
422
|
+
)
|
|
423
|
+
|
|
424
|
+
# Set the UST data.
|
|
425
|
+
target._pos = self._pos.to(device_id, stream_holder)
|
|
426
|
+
target._crd = self._crd.to(device_id, stream_holder)
|
|
427
|
+
assert self._val is not None, "Internal error: UST is only partially constructed."
|
|
428
|
+
target._val = self._val.to(device_id, stream_holder)
|
|
429
|
+
|
|
430
|
+
# Set wrapped operand.
|
|
431
|
+
if target._is_dense_format():
|
|
432
|
+
target._wrapped_operand = target._dense_tensor_from_buffer()
|
|
433
|
+
else:
|
|
434
|
+
target._wrapped_operand = wrap_sparse_operand(target)
|
|
435
|
+
|
|
436
|
+
return target
|
|
437
|
+
|
|
438
|
+
def copy_(self, src, stream=None):
|
|
439
|
+
"""
|
|
440
|
+
Copy a compatible UST into this one, where compatible means that the source
|
|
441
|
+
UST has the same shape, tensor format, non-zero structure, index type, and data
|
|
442
|
+
type as this one.
|
|
443
|
+
|
|
444
|
+
.. note:: Any kernel associated with the source is not copied over to target, and
|
|
445
|
+
any kernel that was associated with this one is reset to None.
|
|
446
|
+
|
|
447
|
+
Args:
|
|
448
|
+
src: The source UST (:class:`nvmath.sparse.ust.Tensor`) that will be
|
|
449
|
+
copied into this one.
|
|
450
|
+
stream: {stream}
|
|
451
|
+
"""
|
|
452
|
+
|
|
453
|
+
if self.tensor_format.name != src.tensor_format.name:
|
|
454
|
+
raise ValueError("The source and target formats are not compatible.")
|
|
455
|
+
|
|
456
|
+
if self.extents != src.extents:
|
|
457
|
+
raise ValueError(f"The source {src.extents} and target {self.extents} extents are not compatible.")
|
|
458
|
+
|
|
459
|
+
if self.index_type != src.index_type:
|
|
460
|
+
raise ValueError(f"The source {src.index_type} and target {self.index_type} index types are not compatible.")
|
|
461
|
+
|
|
462
|
+
if self.dtype != src.dtype:
|
|
463
|
+
raise ValueError(f"The source {src.dtype} and target {self.dtype} dtypes are not compatible.")
|
|
464
|
+
|
|
465
|
+
stream_holder = resolve_stream(stream, src, self.device_id)
|
|
466
|
+
|
|
467
|
+
# Copy the UST data.
|
|
468
|
+
self._pos.copy_(src._pos, stream_holder)
|
|
469
|
+
self._crd.copy_(src._crd, stream_holder)
|
|
470
|
+
assert self._val is not None, "Internal error: UST is only partially constructed."
|
|
471
|
+
self._val.copy_(src._val, stream_holder)
|
|
472
|
+
|
|
473
|
+
self._kernel = None
|
|
474
|
+
self._with_indices = False
|
|
475
|
+
|
|
476
|
+
def clone(self, stream=None):
|
|
477
|
+
"""
|
|
478
|
+
Clone the UST. The sparse representation of the UST is copied to the target, while
|
|
479
|
+
any associated kernel is shared between source and target.
|
|
480
|
+
|
|
481
|
+
.. note:: Any kernel associated with the source is shared with the target.
|
|
482
|
+
|
|
483
|
+
Args:
|
|
484
|
+
stream: {stream}
|
|
485
|
+
|
|
486
|
+
Returns:
|
|
487
|
+
A clone of this UST.
|
|
488
|
+
"""
|
|
489
|
+
stream_holder = resolve_stream(stream, self)
|
|
490
|
+
|
|
491
|
+
target = Tensor(
|
|
492
|
+
self.extents,
|
|
493
|
+
tensor_format=self.tensor_format,
|
|
494
|
+
index_type=self.index_type,
|
|
495
|
+
dtype=self.dtype,
|
|
496
|
+
package=self._package,
|
|
497
|
+
options=None,
|
|
498
|
+
stream=stream_holder,
|
|
499
|
+
)
|
|
500
|
+
|
|
501
|
+
# Empty UST data.
|
|
502
|
+
target._pos = self._pos.empty_like(stream_holder)
|
|
503
|
+
target._crd = self._crd.empty_like(stream_holder)
|
|
504
|
+
target._val = utils.create_empty_tensor(
|
|
505
|
+
self._dense_tensorholder_type, self.nse, self.dtype, self.device_id, stream_holder, verify_strides=False
|
|
506
|
+
)
|
|
507
|
+
|
|
508
|
+
# Copy the UST data.
|
|
509
|
+
target._pos.copy_(self._pos, stream_holder)
|
|
510
|
+
target._crd.copy_(self._crd, stream_holder)
|
|
511
|
+
target._val.copy_(self._val, stream_holder)
|
|
512
|
+
|
|
513
|
+
target._kernel = self._kernel # not deeply cloned
|
|
514
|
+
target._with_indices = self._with_indices
|
|
515
|
+
|
|
516
|
+
# Set wrapped operand.
|
|
517
|
+
if target._is_dense_format():
|
|
518
|
+
target._wrapped_operand = target._dense_tensor_from_buffer()
|
|
519
|
+
else:
|
|
520
|
+
target._wrapped_operand = wrap_sparse_operand(target)
|
|
521
|
+
|
|
522
|
+
return target
|
|
523
|
+
|
|
524
|
+
def set_kernel(self, user_code, *, with_indices=True, arch=None):
|
|
525
|
+
"""
|
|
526
|
+
Set the callback code (a unary Python function, possibly already compiled into
|
|
527
|
+
LTO-IR) and link this with the actual traversal kernel for the UST. This can be
|
|
528
|
+
used to apply an user-defined transformation to each element of the UST, where the
|
|
529
|
+
user-defined transformation can potentially depend on the coordinates (indices)
|
|
530
|
+
of the element.
|
|
531
|
+
|
|
532
|
+
Args:
|
|
533
|
+
user_code: The callback code as a unary Python function object or as a bytes
|
|
534
|
+
object with the LTO-IR code. The function must take a single argument
|
|
535
|
+
representing the original value if ``with_indices`` is ``False``, otherwise
|
|
536
|
+
it takes the original value followed by the coordinate for that value, as a
|
|
537
|
+
sequence. The original value must be representable in the data type of the
|
|
538
|
+
UST, while the indices must be integers.
|
|
539
|
+
|
|
540
|
+
with_indices: A flag to choose between the value-only or value-with-indices
|
|
541
|
+
signatures.
|
|
542
|
+
|
|
543
|
+
.. tip:: If the indices (coordinate) are not needed for the user-defined
|
|
544
|
+
transformation, it is much more efficient to set ``with_indices=False``.
|
|
545
|
+
"""
|
|
546
|
+
|
|
547
|
+
if self._val.device == "cpu":
|
|
548
|
+
raise ValueError("A CUDA kernel cannot be specified for the CPU memory space.")
|
|
549
|
+
|
|
550
|
+
if not isinstance(user_code, bytes):
|
|
551
|
+
signature = self.dtype, self.dtype # return type, value dtype
|
|
552
|
+
if with_indices:
|
|
553
|
+
signature += (self.index_type,) * self.num_dimensions
|
|
554
|
+
user_code = compile_python_function(user_code, "apply", signature)
|
|
555
|
+
|
|
556
|
+
driver_src, grid_iter = emit_apply_kernel(self, with_indices)
|
|
557
|
+
complex_path = pathfinder.find_nvidia_header_directory("cccl")
|
|
558
|
+
narrow_prec_path = pathfinder.find_nvidia_header_directory("cudart")
|
|
559
|
+
compiler_options = {
|
|
560
|
+
"std": "c++17",
|
|
561
|
+
"link_time_optimization": True,
|
|
562
|
+
"arch": arch,
|
|
563
|
+
"include_path": [complex_path, narrow_prec_path],
|
|
564
|
+
}
|
|
565
|
+
linker_options = {"link_time_optimization": True}
|
|
566
|
+
|
|
567
|
+
self._kernel = compile_cpp_and_link(
|
|
568
|
+
src_code=driver_src,
|
|
569
|
+
object_code=user_code,
|
|
570
|
+
function_name="apply_kernel",
|
|
571
|
+
compiler_options=compiler_options,
|
|
572
|
+
linker_options=linker_options,
|
|
573
|
+
)
|
|
574
|
+
self._grid_iter = grid_iter
|
|
575
|
+
self._with_indices = with_indices
|
|
576
|
+
|
|
577
|
+
def _check_valid_kernel(self, *args, **kwargs):
|
|
578
|
+
"""
|
|
579
|
+
Check if a kernel has been set.
|
|
580
|
+
"""
|
|
581
|
+
if not self._kernel:
|
|
582
|
+
raise ValueError("A kernel has not been set using `set_kernel`.")
|
|
583
|
+
|
|
584
|
+
@utils.precondition(_check_valid_kernel)
|
|
585
|
+
def run_kernel(self, stream=None):
|
|
586
|
+
"""
|
|
587
|
+
Run the kernel associated with this UST object.
|
|
588
|
+
"""
|
|
589
|
+
|
|
590
|
+
assert self._kernel is not None, "Internal error."
|
|
591
|
+
stream_holder = resolve_stream(stream, self)
|
|
592
|
+
|
|
593
|
+
(grid, block), params = populate_apply_parameters(
|
|
594
|
+
self, self._with_indices, self._grid_iter, stream_holder=stream_holder
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
device_id = self._val.device_id
|
|
598
|
+
launch_kernel(self._kernel, grid, block, params, device_id=device_id, stream_holder=stream_holder)
|
|
599
|
+
|
|
600
|
+
def get_value(self, indices, stream=None):
|
|
601
|
+
"""
|
|
602
|
+
Retrieve the value at specified indices (coordinate) or ``None`` if there is no
|
|
603
|
+
value at that coordinate. This is *not* random access, and searches compressed
|
|
604
|
+
levels.
|
|
605
|
+
|
|
606
|
+
Args:
|
|
607
|
+
indices: The logical dimension indices of the element to retrieve.
|
|
608
|
+
stream: {stream}
|
|
609
|
+
|
|
610
|
+
Returns:
|
|
611
|
+
``tensor.get_value[i, j, k, ...]` returns the value of the element appearing at
|
|
612
|
+
logical dimension indices ``i, j, k, ...` or ``None`` if there is no element at
|
|
613
|
+
that coordinate.
|
|
614
|
+
"""
|
|
615
|
+
if len(indices) != self.num_dimensions:
|
|
616
|
+
raise ValueError(
|
|
617
|
+
f"The number of indices specified {indices} doesn't match the number of \
|
|
618
|
+
tensor dimensions {self.num_dimensions}."
|
|
619
|
+
)
|
|
620
|
+
stream_holder = resolve_stream(stream, self)
|
|
621
|
+
with stream_context(stream_holder):
|
|
622
|
+
p = self._locate(list(self.tensor_format.levels.values()), self.tensor_format.dim2lvl(indices), 0, 0)
|
|
623
|
+
return p if p is None else self._val.tensor[p].item()
|
|
624
|
+
|
|
625
|
+
def draw(self, name=None, stream=None):
|
|
626
|
+
"""
|
|
627
|
+
Draws the tensor contents (for 1D, 2D, 3D). The method saves the constructed
|
|
628
|
+
:class:`PIL.Image` in a file named `name` when name is not None. It always
|
|
629
|
+
returns the :class:`PIL.Image` object directly as well, so that the caller
|
|
630
|
+
can directly manipulate the constructed image in some other manner.
|
|
631
|
+
|
|
632
|
+
This method is useful to illustrate tensor contents of smaller examples.
|
|
633
|
+
|
|
634
|
+
Args:
|
|
635
|
+
name: filename to save the image to, if not None
|
|
636
|
+
stream: {stream}
|
|
637
|
+
Returns:
|
|
638
|
+
PIL.Image, can be displayed with show()
|
|
639
|
+
|
|
640
|
+
Examples:
|
|
641
|
+
>>> import scipy.sparse as sp
|
|
642
|
+
>>> from nvmath.sparse.ust import Tensor
|
|
643
|
+
|
|
644
|
+
Create a sparse scipy matrix in CSR format.
|
|
645
|
+
|
|
646
|
+
>>> a = sp.random_array((8, 8), density=0.1, format="csr", dtype="float64")
|
|
647
|
+
|
|
648
|
+
Convert the scipy matrix to UST.
|
|
649
|
+
|
|
650
|
+
>>> u = Tensor.from_package(a)
|
|
651
|
+
|
|
652
|
+
Obtain the tensor contents as image (and e.g. display as ``img.show()``).
|
|
653
|
+
|
|
654
|
+
>>> img = u.draw() # doctest: +SKIP
|
|
655
|
+
"""
|
|
656
|
+
return draw_tensor(self, name, stream=stream)
|
|
657
|
+
|
|
658
|
+
def draw_storage(self, name=None, stream=None):
|
|
659
|
+
"""
|
|
660
|
+
Draws the tensor storage (for any UST). The method saves the constructed
|
|
661
|
+
:class:`PIL.Image` in a file named `name` when name is not None. It always
|
|
662
|
+
returns the :class:`PIL.Image` object directly as well, so that the caller
|
|
663
|
+
can directly manipulate the constructed image in some other manner.
|
|
664
|
+
|
|
665
|
+
This method is useful to illustrate the UST storage of smaller examples.
|
|
666
|
+
|
|
667
|
+
Args:
|
|
668
|
+
name: filename to save the image to, if not None
|
|
669
|
+
stream: {stream}
|
|
670
|
+
Returns:
|
|
671
|
+
PIL.Image, can be displayed with show()
|
|
672
|
+
|
|
673
|
+
Examples:
|
|
674
|
+
>>> import scipy.sparse as sp
|
|
675
|
+
>>> from nvmath.sparse.ust import Tensor
|
|
676
|
+
|
|
677
|
+
Create a sparse scipy matrix in CSR format.
|
|
678
|
+
|
|
679
|
+
>>> a = sp.random_array((8, 8), density=0.1, format="csr", dtype="float64")
|
|
680
|
+
|
|
681
|
+
Convert the scipy matrix to UST.
|
|
682
|
+
|
|
683
|
+
>>> u = Tensor.from_package(a)
|
|
684
|
+
|
|
685
|
+
Obtain the tensor storage as image (and e.g. display as ``img.show()``).
|
|
686
|
+
|
|
687
|
+
>>> img = u.draw_storage() # doctest: +SKIP
|
|
688
|
+
"""
|
|
689
|
+
return draw_tensor_storage(self, name, stream=stream)
|
|
690
|
+
|
|
691
|
+
def draw_raw(self, name=None, stream=None):
|
|
692
|
+
"""
|
|
693
|
+
Draws the tensor nonzero structure (for 2D, 3D). The method saves the constructed
|
|
694
|
+
:class:`PIL.Image` in a file named `name` when name is not None. It always
|
|
695
|
+
returns the :class:`PIL.Image` object directly as well, so that the caller
|
|
696
|
+
can directly manipulate the constructed image in some other manner.
|
|
697
|
+
|
|
698
|
+
This method scales to larger tensors.
|
|
699
|
+
|
|
700
|
+
Args:
|
|
701
|
+
name: filename to save the image to, if not None
|
|
702
|
+
stream: {stream}
|
|
703
|
+
Returns:
|
|
704
|
+
PIL.Image, can be displayed with show()
|
|
705
|
+
|
|
706
|
+
Examples:
|
|
707
|
+
>>> import scipy.sparse as sp
|
|
708
|
+
>>> from nvmath.sparse.ust import Tensor
|
|
709
|
+
|
|
710
|
+
Create a sparse scipy matrix in CSR format.
|
|
711
|
+
|
|
712
|
+
>>> a = sp.random_array((512, 512), density=0.01, format="csr", dtype="float64")
|
|
713
|
+
|
|
714
|
+
Convert the scipy matrix to UST.
|
|
715
|
+
|
|
716
|
+
>>> u = Tensor.from_package(a)
|
|
717
|
+
|
|
718
|
+
Obtain the tensor nonzero structure as image (e.g. display as ``img.show()``).
|
|
719
|
+
|
|
720
|
+
>>> img = u.draw_raw() # doctest: +SKIP
|
|
721
|
+
"""
|
|
722
|
+
return draw_tensor_raw(self, name, stream=stream)
|
|
723
|
+
|
|
724
|
+
def animate(self, name=None, stream=None):
|
|
725
|
+
"""
|
|
726
|
+
Animates the tensor nonzero structure (for 3D). The method either saves the
|
|
727
|
+
animated GIF in a file named `name` when name is not None, or otherwise
|
|
728
|
+
returns a :class:`IPython.display.HTML` that can be embedded by the caller.
|
|
729
|
+
|
|
730
|
+
This method scales to larger tensors.
|
|
731
|
+
|
|
732
|
+
Args:
|
|
733
|
+
name: filename to save the animated GIF to, if not None
|
|
734
|
+
stream: {stream}
|
|
735
|
+
Returns:
|
|
736
|
+
IPython.display.HTML, if name is None (to embed in other output)
|
|
737
|
+
|
|
738
|
+
Examples:
|
|
739
|
+
>>> import torch
|
|
740
|
+
>>> from nvmath.sparse.ust import Tensor
|
|
741
|
+
|
|
742
|
+
Create a 3-D tensor, as COO.
|
|
743
|
+
|
|
744
|
+
>>> a = torch.eye(32).repeat(32, 1, 2).to_sparse()
|
|
745
|
+
|
|
746
|
+
Convert the tensor to UST.
|
|
747
|
+
|
|
748
|
+
>>> u = Tensor.from_package(a)
|
|
749
|
+
|
|
750
|
+
Obtain the animation as HTML.
|
|
751
|
+
|
|
752
|
+
>>> html = u.animate() # doctest: +SKIP
|
|
753
|
+
"""
|
|
754
|
+
return animate_tensor(self, name, stream=stream)
|
|
755
|
+
|
|
756
|
+
@property
|
|
757
|
+
def extents(self):
|
|
758
|
+
"""
|
|
759
|
+
The extents (shape) of this UST object.
|
|
760
|
+
"""
|
|
761
|
+
return self._extents
|
|
762
|
+
|
|
763
|
+
shape = extents
|
|
764
|
+
|
|
765
|
+
@property
|
|
766
|
+
def levels(self):
|
|
767
|
+
"""
|
|
768
|
+
The extents corresponding to each level for this UST object.
|
|
769
|
+
"""
|
|
770
|
+
return self._levels
|
|
771
|
+
|
|
772
|
+
@property
|
|
773
|
+
def size(self):
|
|
774
|
+
"""
|
|
775
|
+
The envelope of this UST object (product of the extents).
|
|
776
|
+
"""
|
|
777
|
+
# TODO: rename this to envelope or bounding_volume?
|
|
778
|
+
return np.prod(self.extents)
|
|
779
|
+
|
|
780
|
+
@property
|
|
781
|
+
def num_dimensions(self):
|
|
782
|
+
"""
|
|
783
|
+
The number of dimensions of this UST object.
|
|
784
|
+
"""
|
|
785
|
+
return self.tensor_format.num_dimensions
|
|
786
|
+
|
|
787
|
+
@property
|
|
788
|
+
def num_levels(self):
|
|
789
|
+
"""
|
|
790
|
+
The number of levels of this UST object.
|
|
791
|
+
"""
|
|
792
|
+
return self.tensor_format.num_levels
|
|
793
|
+
|
|
794
|
+
@property
|
|
795
|
+
def tensor_format(self):
|
|
796
|
+
"""
|
|
797
|
+
The UST format as a :class:`nvmath.sparse.ust.TensorFormat` object.
|
|
798
|
+
"""
|
|
799
|
+
return self._tensor_format
|
|
800
|
+
|
|
801
|
+
@property
|
|
802
|
+
def index_type(self):
|
|
803
|
+
"""
|
|
804
|
+
The index type of this UST object as a string.
|
|
805
|
+
"""
|
|
806
|
+
return self._index_type
|
|
807
|
+
|
|
808
|
+
@property
|
|
809
|
+
def dtype(self):
|
|
810
|
+
"""
|
|
811
|
+
The data type of this UST object as a string.
|
|
812
|
+
"""
|
|
813
|
+
return self._dtype
|
|
814
|
+
|
|
815
|
+
def pos(self, level):
|
|
816
|
+
"""
|
|
817
|
+
The positions corresponding to the specified level.
|
|
818
|
+
|
|
819
|
+
Args:
|
|
820
|
+
level: An ordinal specifying the level.
|
|
821
|
+
"""
|
|
822
|
+
holder = self._pos.get(level, None)
|
|
823
|
+
return None if holder is None else holder.tensor
|
|
824
|
+
|
|
825
|
+
def crd(self, level):
|
|
826
|
+
"""
|
|
827
|
+
The coordinates corresponding to the specified level.
|
|
828
|
+
|
|
829
|
+
Args:
|
|
830
|
+
level: An ordinal specifying the level.
|
|
831
|
+
"""
|
|
832
|
+
holder = self._crd.get(level, None)
|
|
833
|
+
return None if holder is None else holder.tensor
|
|
834
|
+
|
|
835
|
+
@property
|
|
836
|
+
def val(self):
|
|
837
|
+
"""
|
|
838
|
+
The explicit values stored in this UST object.
|
|
839
|
+
"""
|
|
840
|
+
return [] if self._val is None else self._val.tensor
|
|
841
|
+
|
|
842
|
+
@property
|
|
843
|
+
def device(self):
|
|
844
|
+
"""
|
|
845
|
+
The memory space of this UST object ("cpu" or "cuda").
|
|
846
|
+
"""
|
|
847
|
+
return "cpu" if self._val is None else self._val.device
|
|
848
|
+
|
|
849
|
+
@property
|
|
850
|
+
def device_id(self):
|
|
851
|
+
"""
|
|
852
|
+
The device ID of this UST object (CUDA ordinal or "cpu").
|
|
853
|
+
"""
|
|
854
|
+
return "cpu" if self._val is None else self._val.device_id
|
|
855
|
+
|
|
856
|
+
@property
|
|
857
|
+
def nse(self):
|
|
858
|
+
"""
|
|
859
|
+
The number of stored elements in this UST object.
|
|
860
|
+
"""
|
|
861
|
+
return 0 if self._val is None else self._val.size
|
|
862
|
+
|
|
863
|
+
@property
|
|
864
|
+
def base(self):
|
|
865
|
+
"""
|
|
866
|
+
Get the "package" tensor from which the UST view was created, if available.
|
|
867
|
+
"""
|
|
868
|
+
if self._wrapped_operand is not None:
|
|
869
|
+
return self._wrapped_operand.tensor
|
|
870
|
+
|
|
871
|
+
@property
|
|
872
|
+
def _dense_tensorholder_type(self):
|
|
873
|
+
"""
|
|
874
|
+
(Internal use only) The tensor holder type for the wrapped dense tensors.
|
|
875
|
+
"""
|
|
876
|
+
return self._val.__class__
|
|
877
|
+
|
|
878
|
+
def __repr__(self):
|
|
879
|
+
s = (
|
|
880
|
+
f"---- Sparse Tensor<VAL={self.dtype},"
|
|
881
|
+
f"POS={self.index_type},CRD={self.index_type},"
|
|
882
|
+
f"DIM={self.num_dimensions},LVL={self.num_levels}>\n"
|
|
883
|
+
)
|
|
884
|
+
s += f"format : {self.tensor_format}\n"
|
|
885
|
+
s += f"device : {self.device}\n"
|
|
886
|
+
s += f"dim : {self.extents}\n"
|
|
887
|
+
s += f"lvl : {self.levels}\n"
|
|
888
|
+
s += f"nse : {self.nse}\n"
|
|
889
|
+
data = 0
|
|
890
|
+
for level in range(self.num_levels):
|
|
891
|
+
pos = self._pos.get(level)
|
|
892
|
+
if pos is None:
|
|
893
|
+
continue
|
|
894
|
+
s += f"pos[{level}] : {_tensor_to_list(pos)} #{pos.size}\n"
|
|
895
|
+
data += pos.size * pos.itemsize
|
|
896
|
+
for level in range(self.num_levels):
|
|
897
|
+
crd = self._crd.get(level)
|
|
898
|
+
if crd is None:
|
|
899
|
+
continue
|
|
900
|
+
s += f"crd[{level}] : {_tensor_to_list(crd)} #{crd.size}\n"
|
|
901
|
+
data += crd.size * crd.itemsize
|
|
902
|
+
if self.nse > 0:
|
|
903
|
+
val = self._val
|
|
904
|
+
s += f"values : {_tensor_to_list(val)} #{val.size}\n"
|
|
905
|
+
data += val.size * val.itemsize
|
|
906
|
+
s += f"data : {data} bytes\n"
|
|
907
|
+
s += f"sparsity : {(100.0 - (100.0 * self.nse) / self.size):.2f}%\n"
|
|
908
|
+
s += "----"
|
|
909
|
+
return s
|
|
910
|
+
|
|
911
|
+
def _locate(self, formats, lvls, level, p):
|
|
912
|
+
# Exhausted all levels.
|
|
913
|
+
if level == self.num_levels:
|
|
914
|
+
return p
|
|
915
|
+
# Obtain format and properties at current level.
|
|
916
|
+
v = formats[level]
|
|
917
|
+
if isinstance(v, tuple):
|
|
918
|
+
fmt, prop = v
|
|
919
|
+
else:
|
|
920
|
+
fmt, prop = v, None
|
|
921
|
+
# Handle level format.
|
|
922
|
+
if fmt == LevelFormat.DENSE or fmt == LevelFormat.BATCH or fmt == LevelFormat.RANGE:
|
|
923
|
+
return self._locate(formats, lvls, level + 1, p * self.levels[level] + lvls[level])
|
|
924
|
+
elif fmt == LevelFormat.COMPRESSED:
|
|
925
|
+
unique = is_unique(prop)
|
|
926
|
+
pos = self._pos[level].tensor
|
|
927
|
+
crd = self._crd[level].tensor
|
|
928
|
+
assert pos.ndim == crd.ndim
|
|
929
|
+
adjust = 0
|
|
930
|
+
if pos.ndim > 1:
|
|
931
|
+
# This only happens for prior BATCH. Correct the higher-dimensions in
|
|
932
|
+
# both pos and crd buffers and adjust position by #nnz per batch.
|
|
933
|
+
assert pos.ndim == level
|
|
934
|
+
for i in range(level - 1):
|
|
935
|
+
assert formats[i] == LevelFormat.BATCH
|
|
936
|
+
pos = pos[lvls[i]]
|
|
937
|
+
crd = crd[lvls[i]]
|
|
938
|
+
adjust = pos[-1].item() * (p - lvls[level - 1]) // self.levels[level - 1]
|
|
939
|
+
p = lvls[level - 1]
|
|
940
|
+
lo = pos[p].item()
|
|
941
|
+
hi = pos[p + 1].item()
|
|
942
|
+
for i in range(lo, hi):
|
|
943
|
+
if crd[i].item() == lvls[level]:
|
|
944
|
+
cpos = self._locate(formats, lvls, level + 1, i + adjust)
|
|
945
|
+
if unique:
|
|
946
|
+
return cpos # always end scan (unique)
|
|
947
|
+
elif cpos is not None:
|
|
948
|
+
return cpos # only end scan on success (non-unique)
|
|
949
|
+
elif fmt == LevelFormat.SINGLETON:
|
|
950
|
+
crd = self._crd[level].tensor
|
|
951
|
+
assert crd.ndim == 1
|
|
952
|
+
if crd[p].item() == lvls[level]:
|
|
953
|
+
return self._locate(formats, lvls, level + 1, p)
|
|
954
|
+
elif fmt == LevelFormat.DELTA:
|
|
955
|
+
assert is_unique(prop)
|
|
956
|
+
pos = self._pos[level].tensor
|
|
957
|
+
crd = self._crd[level].tensor
|
|
958
|
+
assert pos.ndim == 1 and crd.ndim == 1
|
|
959
|
+
corig = 0
|
|
960
|
+
lo = pos[p].item()
|
|
961
|
+
hi = pos[p + 1].item()
|
|
962
|
+
for i in range(lo, hi):
|
|
963
|
+
corig += crd[i].item()
|
|
964
|
+
if corig == lvls[level]:
|
|
965
|
+
return self._locate(formats, lvls, level + 1, i)
|
|
966
|
+
corig += 1
|
|
967
|
+
elif fmt == LevelFormat.STRUCTURED:
|
|
968
|
+
assert isinstance(prop, int) # n is the property
|
|
969
|
+
crd = self._crd[level].tensor
|
|
970
|
+
assert crd.ndim == 1
|
|
971
|
+
lo = p * prop
|
|
972
|
+
hi = lo + prop
|
|
973
|
+
for i in range(lo, hi):
|
|
974
|
+
if crd[i] == lvls[level]:
|
|
975
|
+
return self._locate(formats, lvls, level + 1, i)
|
|
976
|
+
else:
|
|
977
|
+
raise AssertionError(f"Unsupported: {fmt}")
|
|
978
|
+
return None
|
|
979
|
+
|
|
980
|
+
@classmethod
|
|
981
|
+
def _from_dense(cls, operand):
|
|
982
|
+
# Wrap operand to canonicalize attributes.
|
|
983
|
+
operand = tensor_wrapper.wrap_operand(operand)
|
|
984
|
+
|
|
985
|
+
if not is_contiguous_and_dense(operand.shape, operand.strides):
|
|
986
|
+
raise NotImplementedError("A UST cannot be currently created from strided tensors with holes (that is, not dense).")
|
|
987
|
+
|
|
988
|
+
# Axis order, to determine UST dense format.
|
|
989
|
+
axis_order = _axis_order_in_memory(operand.shape, operand.strides)
|
|
990
|
+
num_dimensions = len(axis_order)
|
|
991
|
+
dimensions = [Dimension(dimension_name=f"{chr(ord('i') + d)}") for d in range(num_dimensions)]
|
|
992
|
+
levels = {dimensions[d]: LevelFormat.DENSE for d in reversed(axis_order)}
|
|
993
|
+
|
|
994
|
+
# UST format.
|
|
995
|
+
if num_dimensions == 0:
|
|
996
|
+
name = "Scalar"
|
|
997
|
+
elif num_dimensions == 1:
|
|
998
|
+
name = "DenseVector"
|
|
999
|
+
elif num_dimensions == 2:
|
|
1000
|
+
name = "Densed" + ("Left" if axis_order == (0, 1) else "Right")
|
|
1001
|
+
else:
|
|
1002
|
+
name = f"Dense{num_dimensions}D-" + "-".join(str(d) for d in reversed(axis_order))
|
|
1003
|
+
tensor_format = TensorFormat(dimensions, levels, name=name)
|
|
1004
|
+
|
|
1005
|
+
# UST data.
|
|
1006
|
+
index_type = _get_smallest_index_type(max(operand.shape) if num_dimensions > 0 else 0)
|
|
1007
|
+
values = operand.memory_buffer()
|
|
1008
|
+
dtype = values.dtype
|
|
1009
|
+
|
|
1010
|
+
# Create UST.
|
|
1011
|
+
ust = cls(operand.shape, tensor_format=tensor_format, index_type=index_type, dtype=dtype)
|
|
1012
|
+
ust._val = values
|
|
1013
|
+
|
|
1014
|
+
ust._wrapped_operand = operand
|
|
1015
|
+
|
|
1016
|
+
return ust
|