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,957 @@
|
|
|
1
|
+
# Copyright (c) 2025, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
The tensor format DSL of the Universal Sparse Tensor maps tensor dimensions
|
|
7
|
+
(logical tensor indices) to storage levels (physical memory indices) using
|
|
8
|
+
an invertible function that defines how each level should be stored.
|
|
9
|
+
"""
|
|
10
|
+
|
|
11
|
+
__all__ = [
|
|
12
|
+
"is_ordered",
|
|
13
|
+
"is_unique",
|
|
14
|
+
"Dimension",
|
|
15
|
+
"LevelFormat",
|
|
16
|
+
"LevelProperty",
|
|
17
|
+
"LevelExpr",
|
|
18
|
+
"TensorFormat",
|
|
19
|
+
"NamedFormats",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
from collections.abc import Sequence
|
|
23
|
+
from enum import IntEnum
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
def is_ordered(prop):
|
|
27
|
+
"""Determines if given property is ordered."""
|
|
28
|
+
if isinstance(prop, Sequence):
|
|
29
|
+
for p in prop:
|
|
30
|
+
if not is_ordered(p):
|
|
31
|
+
return False
|
|
32
|
+
elif isinstance(prop, LevelProperty) and prop == LevelProperty.UNORDERED:
|
|
33
|
+
return False
|
|
34
|
+
return True # ordered unless seen otherwise
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def is_unique(prop):
|
|
38
|
+
"""Determines if given property is unique."""
|
|
39
|
+
if isinstance(prop, Sequence):
|
|
40
|
+
for p in prop:
|
|
41
|
+
if not is_unique(p):
|
|
42
|
+
return False
|
|
43
|
+
elif isinstance(prop, LevelProperty) and prop == LevelProperty.NONUNIQUE:
|
|
44
|
+
return False
|
|
45
|
+
return True # unique unless seen otherwise
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class LevelFormat(IntEnum):
|
|
49
|
+
DENSE = 0
|
|
50
|
+
BATCH = 1
|
|
51
|
+
COMPRESSED = 2
|
|
52
|
+
SINGLETON = 3
|
|
53
|
+
RANGE = 4
|
|
54
|
+
DELTA = 5
|
|
55
|
+
STRUCTURED = 6
|
|
56
|
+
|
|
57
|
+
def __repr__(self):
|
|
58
|
+
"""Returns representation without index."""
|
|
59
|
+
return f"<{self.__class__.__name__}.{self.name}>"
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class LevelProperty(IntEnum):
|
|
63
|
+
UNIQUE = 0
|
|
64
|
+
NONUNIQUE = 1
|
|
65
|
+
ORDERED = 2
|
|
66
|
+
UNORDERED = 3
|
|
67
|
+
|
|
68
|
+
def __repr__(self):
|
|
69
|
+
"""Returns representation without index."""
|
|
70
|
+
return f"<{self.__class__.__name__}.{self.name}>"
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
# The inverse map is used to check for certain incompatible property sequences.
|
|
74
|
+
LevelPropertyInverse = {
|
|
75
|
+
LevelProperty.UNIQUE: LevelProperty.NONUNIQUE,
|
|
76
|
+
LevelProperty.NONUNIQUE: LevelProperty.UNIQUE,
|
|
77
|
+
LevelProperty.ORDERED: LevelProperty.UNORDERED,
|
|
78
|
+
LevelProperty.UNORDERED: LevelProperty.ORDERED,
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def _find_range(levels, expr1, expr2):
|
|
83
|
+
for rl, (k, v) in enumerate(levels.items()):
|
|
84
|
+
if v == LevelFormat.RANGE and k in (expr1, expr2):
|
|
85
|
+
return rl, k
|
|
86
|
+
raise ValueError(f"Cannot find {expr1} or {expr2} in levels")
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Operator:
|
|
90
|
+
"""
|
|
91
|
+
An abstract binary level expression operator type.
|
|
92
|
+
"""
|
|
93
|
+
|
|
94
|
+
# TODO: use protocol.
|
|
95
|
+
pass
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
class Add(Operator):
|
|
99
|
+
"""
|
|
100
|
+
Add two level expressions.
|
|
101
|
+
"""
|
|
102
|
+
|
|
103
|
+
def __call__(self, expr1, expr2, as_size):
|
|
104
|
+
if as_size:
|
|
105
|
+
return expr1 + expr2 - 1
|
|
106
|
+
return expr1 + expr2
|
|
107
|
+
|
|
108
|
+
def invert(self, expr1, expr2, dimensions, levels, dim_indices, lvl_indices, idx):
|
|
109
|
+
rl, re = _find_range(levels, expr1, expr2)
|
|
110
|
+
if re == expr2:
|
|
111
|
+
dim_indices[dimensions.index(expr1)] = lvl_indices[idx] - lvl_indices[rl]
|
|
112
|
+
else:
|
|
113
|
+
assert re == expr1
|
|
114
|
+
dim_indices[dimensions.index(expr2)] = lvl_indices[idx] - lvl_indices[rl]
|
|
115
|
+
|
|
116
|
+
def __repr__(self):
|
|
117
|
+
return "+"
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class Subtract(Operator):
|
|
121
|
+
"""
|
|
122
|
+
Subtract a level expression from another.
|
|
123
|
+
"""
|
|
124
|
+
|
|
125
|
+
def __call__(self, expr1, expr2, as_size):
|
|
126
|
+
if as_size:
|
|
127
|
+
return expr1 + expr2 - 1
|
|
128
|
+
return expr1 - expr2
|
|
129
|
+
|
|
130
|
+
def invert(self, expr1, expr2, dimensions, levels, dim_indices, lvl_indices, idx):
|
|
131
|
+
rl, re = _find_range(levels, expr1, expr2)
|
|
132
|
+
if re == expr2:
|
|
133
|
+
dim_indices[dimensions.index(expr1)] = lvl_indices[rl] + lvl_indices[idx]
|
|
134
|
+
else:
|
|
135
|
+
assert re == expr1
|
|
136
|
+
dim_indices[dimensions.index(expr2)] = lvl_indices[rl] - lvl_indices[idx]
|
|
137
|
+
|
|
138
|
+
def __repr__(self):
|
|
139
|
+
return "-"
|
|
140
|
+
|
|
141
|
+
|
|
142
|
+
class Divide(Operator):
|
|
143
|
+
"""
|
|
144
|
+
Divide a level expression by an integer.
|
|
145
|
+
"""
|
|
146
|
+
|
|
147
|
+
def __call__(self, expr1, expr2, as_size):
|
|
148
|
+
if as_size:
|
|
149
|
+
assert expr2 > 0, f"Internal error: {expr2}"
|
|
150
|
+
if expr1 % expr2 != 0:
|
|
151
|
+
raise NotImplementedError(f"block size {expr2} does not evenly divide size {expr1}")
|
|
152
|
+
return expr1 // expr2
|
|
153
|
+
|
|
154
|
+
def invert(self, expr1, expr2, dimensions, levels, dim_indices, lvl_indices, idx):
|
|
155
|
+
i = dimensions.index(expr1)
|
|
156
|
+
dim_indices[i] = lvl_indices[idx] * expr2
|
|
157
|
+
|
|
158
|
+
def __repr__(self):
|
|
159
|
+
return "//"
|
|
160
|
+
|
|
161
|
+
|
|
162
|
+
class Modulo(Operator):
|
|
163
|
+
"""
|
|
164
|
+
The integer modulo of a level expression.
|
|
165
|
+
"""
|
|
166
|
+
|
|
167
|
+
def __call__(self, expr1, expr2, as_size):
|
|
168
|
+
if as_size:
|
|
169
|
+
assert expr2 > 0, f"Internal error: {expr2}"
|
|
170
|
+
return expr2
|
|
171
|
+
return expr1 % expr2
|
|
172
|
+
|
|
173
|
+
def invert(self, expr1, expr2, dimensions, levels, dim_indices, lvl_indices, idx):
|
|
174
|
+
i = dimensions.index(expr1)
|
|
175
|
+
dim_indices[i] += lvl_indices[idx] # update (seen after div)
|
|
176
|
+
|
|
177
|
+
def __repr__(self):
|
|
178
|
+
return "%"
|
|
179
|
+
|
|
180
|
+
|
|
181
|
+
class LevelExpr:
|
|
182
|
+
"""
|
|
183
|
+
A (binary) level expression is a triple: dimension object or level expression,
|
|
184
|
+
operator, dimension or level expression or int.
|
|
185
|
+
|
|
186
|
+
Args:
|
|
187
|
+
expression1: a Dimension or LevelExpr object.
|
|
188
|
+
operator: operator derived from Operator (Add, Subtract, Divide, Modulo).
|
|
189
|
+
expression2: a Dimension or LevelExpr object, or an integer.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
__slots__ = ("expression1", "operator", "expression2")
|
|
193
|
+
|
|
194
|
+
def __init__(self, *, expression1, operator, expression2):
|
|
195
|
+
lhs_level_expr_types = Dimension, LevelExpr
|
|
196
|
+
rhs_level_expr_types = lhs_level_expr_types + (int,)
|
|
197
|
+
if not isinstance(expression1, lhs_level_expr_types):
|
|
198
|
+
raise TypeError(f"LHS expression {expression1} must have one of these types: {lhs_level_expr_types}.")
|
|
199
|
+
if not isinstance(operator, Operator):
|
|
200
|
+
raise TypeError(f"Operator {operator} must be derived from Operator.")
|
|
201
|
+
if not isinstance(expression2, rhs_level_expr_types):
|
|
202
|
+
raise TypeError(f"RHS expression {expression2} must have one of these types: {rhs_level_expr_types}.")
|
|
203
|
+
self.expression1 = expression1
|
|
204
|
+
self.operator = operator
|
|
205
|
+
self.expression2 = expression2
|
|
206
|
+
|
|
207
|
+
def __repr__(self):
|
|
208
|
+
return f"({self.expression1} {self.operator} {self.expression2})"
|
|
209
|
+
|
|
210
|
+
def __add__(self, expression):
|
|
211
|
+
return LevelExpr(expression1=self, operator=Add(), expression2=expression)
|
|
212
|
+
|
|
213
|
+
def __sub__(self, expression):
|
|
214
|
+
return LevelExpr(expression1=self, operator=Subtract(), expression2=expression)
|
|
215
|
+
|
|
216
|
+
def __floordiv__(self, divisor):
|
|
217
|
+
return LevelExpr(expression1=self, operator=Divide(), expression2=divisor)
|
|
218
|
+
|
|
219
|
+
def __mod__(self, modulus):
|
|
220
|
+
return LevelExpr(expression1=self, operator=Modulo(), expression2=modulus)
|
|
221
|
+
|
|
222
|
+
def evaluate(self, dimensions, dim_indices, as_size):
|
|
223
|
+
e1 = self.expression1.evaluate(dimensions, dim_indices, as_size)
|
|
224
|
+
e2 = (
|
|
225
|
+
self.expression2.evaluate(dimensions, dim_indices, as_size)
|
|
226
|
+
if not isinstance(self.expression2, int)
|
|
227
|
+
else self.expression2
|
|
228
|
+
)
|
|
229
|
+
return self.operator(e1, e2, as_size)
|
|
230
|
+
|
|
231
|
+
def invert(self, dimensions, levels, dim_indices, lvl_indices, idx):
|
|
232
|
+
self.operator.invert(
|
|
233
|
+
self.expression1,
|
|
234
|
+
self.expression2,
|
|
235
|
+
dimensions,
|
|
236
|
+
levels,
|
|
237
|
+
dim_indices,
|
|
238
|
+
lvl_indices,
|
|
239
|
+
idx,
|
|
240
|
+
)
|
|
241
|
+
|
|
242
|
+
|
|
243
|
+
class Dimension:
|
|
244
|
+
"""
|
|
245
|
+
A dimension object encapsulates a dimension name.
|
|
246
|
+
|
|
247
|
+
Args:
|
|
248
|
+
dimension_name: A name for the dimension.
|
|
249
|
+
"""
|
|
250
|
+
|
|
251
|
+
__slots__ = ("dimension_name",)
|
|
252
|
+
|
|
253
|
+
def __init__(self, *, dimension_name):
|
|
254
|
+
if not isinstance(dimension_name, str):
|
|
255
|
+
raise TypeError(f"The dimension name {dimension_name} must be a string.")
|
|
256
|
+
|
|
257
|
+
self.dimension_name = dimension_name
|
|
258
|
+
|
|
259
|
+
def __repr__(self):
|
|
260
|
+
return f"{self.dimension_name}"
|
|
261
|
+
|
|
262
|
+
def __add__(self, expression):
|
|
263
|
+
return LevelExpr(expression1=self, operator=Add(), expression2=expression)
|
|
264
|
+
|
|
265
|
+
def __sub__(self, expression):
|
|
266
|
+
return LevelExpr(expression1=self, operator=Subtract(), expression2=expression)
|
|
267
|
+
|
|
268
|
+
def __floordiv__(self, divisor):
|
|
269
|
+
return LevelExpr(expression1=self, operator=Divide(), expression2=divisor)
|
|
270
|
+
|
|
271
|
+
def __mod__(self, modulus):
|
|
272
|
+
return LevelExpr(expression1=self, operator=Modulo(), expression2=modulus)
|
|
273
|
+
|
|
274
|
+
def evaluate(self, dimensions, dim_indices, as_size):
|
|
275
|
+
return dim_indices[dimensions.index(self)]
|
|
276
|
+
|
|
277
|
+
def invert(self, dimensions, levels, dim_indices, lvl_indices, idx):
|
|
278
|
+
dim_indices[dimensions.index(self)] = lvl_indices[idx]
|
|
279
|
+
|
|
280
|
+
|
|
281
|
+
class TensorFormat:
|
|
282
|
+
"""
|
|
283
|
+
A universal sparse tensor format maps dimension specifications
|
|
284
|
+
(dimensions for short) to level specifications (levels for short).
|
|
285
|
+
|
|
286
|
+
Args:
|
|
287
|
+
dimensions: a sequence of Dimension objects.
|
|
288
|
+
levels: an ordered dictionary from a LevelExpr or Dimension object
|
|
289
|
+
to a LevelFormat, or a (LevelFormat, LevelProperty) pair.
|
|
290
|
+
name: a name for the format as a string. If the format corresponds to an
|
|
291
|
+
existing format like CSR, COO, etc. use the canonical name. A name
|
|
292
|
+
will be generated based on the level specification if none is provided.
|
|
293
|
+
"""
|
|
294
|
+
|
|
295
|
+
__slots__ = ("_dimensions", "_levels", "_name", "_is_identity", "_is_ordered", "_is_unique")
|
|
296
|
+
|
|
297
|
+
def __init__(self, dimensions, levels, *, name=None):
|
|
298
|
+
self._dimensions = dimensions
|
|
299
|
+
self._levels = levels
|
|
300
|
+
|
|
301
|
+
# TODO: create a better default name.
|
|
302
|
+
if name is None:
|
|
303
|
+
name = f"{levels}".replace("\n", " ")
|
|
304
|
+
if not isinstance(name, str):
|
|
305
|
+
raise TypeError(f"The name {name} must be a string.")
|
|
306
|
+
self._name = name
|
|
307
|
+
|
|
308
|
+
# Perform semantic checks on syntactic structure.
|
|
309
|
+
# Throws a TypeError on failure.
|
|
310
|
+
self._semantically_validate()
|
|
311
|
+
|
|
312
|
+
# Cached properties.
|
|
313
|
+
self._is_identity = (
|
|
314
|
+
all(isinstance(lvl, Dimension) and self.dimensions.index(lvl) == idx for idx, lvl in enumerate(levels))
|
|
315
|
+
if self.num_dimensions == self.num_levels
|
|
316
|
+
else False
|
|
317
|
+
)
|
|
318
|
+
self._is_ordered = all(is_ordered(lvl[1]) if isinstance(lvl, tuple) else True for lvl in levels.values())
|
|
319
|
+
self._is_unique = any(is_unique(lvl[1]) if isinstance(lvl, tuple) else True for lvl in levels.values())
|
|
320
|
+
|
|
321
|
+
@property
|
|
322
|
+
def name(self):
|
|
323
|
+
"""
|
|
324
|
+
Get the name of the sparse tensor format.
|
|
325
|
+
"""
|
|
326
|
+
return self._name
|
|
327
|
+
|
|
328
|
+
@property
|
|
329
|
+
def num_dimensions(self):
|
|
330
|
+
"""
|
|
331
|
+
Get the number of dimensions (rank of tensor).
|
|
332
|
+
"""
|
|
333
|
+
return len(self._dimensions)
|
|
334
|
+
|
|
335
|
+
@property
|
|
336
|
+
def num_levels(self):
|
|
337
|
+
"""
|
|
338
|
+
Get the number of levels (rank of storage).
|
|
339
|
+
"""
|
|
340
|
+
return len(self._levels)
|
|
341
|
+
|
|
342
|
+
@property
|
|
343
|
+
def dimensions(self):
|
|
344
|
+
"""
|
|
345
|
+
Get the dimension specifications for this format.
|
|
346
|
+
"""
|
|
347
|
+
return self._dimensions
|
|
348
|
+
|
|
349
|
+
@property
|
|
350
|
+
def levels(self):
|
|
351
|
+
"""
|
|
352
|
+
Get the level specifications for this format.
|
|
353
|
+
"""
|
|
354
|
+
return self._levels
|
|
355
|
+
|
|
356
|
+
@property
|
|
357
|
+
def is_identity(self):
|
|
358
|
+
"""
|
|
359
|
+
Determine whether the format uses identity mapping between dimensions and levels.
|
|
360
|
+
"""
|
|
361
|
+
return self._is_identity
|
|
362
|
+
|
|
363
|
+
@property
|
|
364
|
+
def is_ordered(self):
|
|
365
|
+
"""
|
|
366
|
+
Determine whether the format is ordered.
|
|
367
|
+
"""
|
|
368
|
+
return self._is_ordered
|
|
369
|
+
|
|
370
|
+
@property
|
|
371
|
+
def is_unique(self):
|
|
372
|
+
"""
|
|
373
|
+
Determine whether the format is unique.
|
|
374
|
+
"""
|
|
375
|
+
# TODO: imprecise, but works for expected cases like
|
|
376
|
+
# e.g. COO3 (compressed(!U), singleton(!U), singleton(U)) is still unique
|
|
377
|
+
# but COO3 (compressed(!U), singleton(!U), singleton(!U)) is nonunique
|
|
378
|
+
return self._is_unique
|
|
379
|
+
|
|
380
|
+
def _semantically_validate(self):
|
|
381
|
+
"""
|
|
382
|
+
Semantically validates the sparse tensor format. The grammar allows for specifying
|
|
383
|
+
a very wide range of sparse tensor formats. However, not every syntactically
|
|
384
|
+
valid input is also semanticaly valid. This method throws a TypeError for
|
|
385
|
+
tensor formats where the following constraints are not satisfied :
|
|
386
|
+
|
|
387
|
+
(1) the input syntax has an actual implementation
|
|
388
|
+
(2) the dim-to-level mapping is easily invertible
|
|
389
|
+
|
|
390
|
+
Note that currently the checks are **stricter** than really required.
|
|
391
|
+
However, developers that relax the checks below are also responsible for
|
|
392
|
+
ensuring that constraints (1) and (2) are still satisfied.
|
|
393
|
+
"""
|
|
394
|
+
# Reject incorrect dimension specifications types.
|
|
395
|
+
if not (isinstance(self.dimensions, Sequence) and all(isinstance(d, Dimension) for d in self.dimensions)):
|
|
396
|
+
raise TypeError(f"Dimension specifications {self.dimensions} must be a sequence of Dimension objects.")
|
|
397
|
+
|
|
398
|
+
# Reject dimension specifications that are not unique names (e.g. (i,i)).
|
|
399
|
+
all_dims = set(self.dimensions)
|
|
400
|
+
if len(all_dims) != len(self.dimensions):
|
|
401
|
+
raise TypeError(f"Dimension specifications {self.dimensions} has repeated identifiers.")
|
|
402
|
+
|
|
403
|
+
# Reject incorrect level specification types.
|
|
404
|
+
if not (isinstance(self.levels, dict)):
|
|
405
|
+
raise TypeError(f"Level specifications {self.levels} must be an ordered dictionary.")
|
|
406
|
+
|
|
407
|
+
# Reject incorrect level specification structure.
|
|
408
|
+
used_dims = set()
|
|
409
|
+
range_dims = {}
|
|
410
|
+
block_dims = {}
|
|
411
|
+
batch = 0
|
|
412
|
+
for idx, (k, v) in enumerate(self.levels.items()):
|
|
413
|
+
# Check the level expression.
|
|
414
|
+
if isinstance(k, Dimension):
|
|
415
|
+
if k not in self.dimensions:
|
|
416
|
+
raise TypeError(f"Dimension {k} does not appear in {self.dimensions}.")
|
|
417
|
+
used_dims.add(k)
|
|
418
|
+
elif isinstance(k, LevelExpr):
|
|
419
|
+
# Check for unnested level expressions and impose some constraints.
|
|
420
|
+
# We expect (Add, Subtract, Divide, Modulo) only.
|
|
421
|
+
if isinstance(k.operator, (Add, Subtract)):
|
|
422
|
+
# Only allow for direct expressions like i - j.
|
|
423
|
+
if k.expression1 not in self.dimensions:
|
|
424
|
+
raise TypeError(f"LHS {k.expression1} in {k} does not appear in {self.dimensions}.")
|
|
425
|
+
if k.expression2 not in self.dimensions:
|
|
426
|
+
raise TypeError(f"RHS {k.expression2} in {k} does not appear in {self.dimensions}.")
|
|
427
|
+
used_dims.add(k.expression1)
|
|
428
|
+
used_dims.add(k.expression2)
|
|
429
|
+
# Find matching range.
|
|
430
|
+
if k.expression1 in range_dims or k.expression2 in range_dims:
|
|
431
|
+
raise TypeError(f"Operation {k} reuses dimension of a prior range computation.")
|
|
432
|
+
range_dims[k.expression1] = k.expression2
|
|
433
|
+
range_dims[k.expression2] = k.expression1
|
|
434
|
+
elif isinstance(k.operator, (Divide, Modulo)):
|
|
435
|
+
# Only allow for direct expressions like i % 3.
|
|
436
|
+
if k.expression1 not in self.dimensions:
|
|
437
|
+
raise TypeError(f"LHS {k.expression1} in {k} does not appear in {self.dimensions}.")
|
|
438
|
+
if not isinstance(k.expression2, int):
|
|
439
|
+
raise TypeError(f"RHS {k.expression2} in {k} must be an integer.")
|
|
440
|
+
elif k.expression2 <= 0:
|
|
441
|
+
raise TypeError(f"RHS {k.expression2} in {k} must be strictly positive integer.")
|
|
442
|
+
used_dims.add(k.expression1)
|
|
443
|
+
# Find matching blocking.
|
|
444
|
+
if isinstance(k.operator, Divide):
|
|
445
|
+
if k.expression1 in block_dims:
|
|
446
|
+
raise TypeError(f"Division {k} reuses dimension of a prior division.")
|
|
447
|
+
block_dims[k.expression1] = k.expression2
|
|
448
|
+
elif isinstance(k.operator, Modulo):
|
|
449
|
+
if block_dims.get(k.expression1, 0) != k.expression2:
|
|
450
|
+
raise TypeError(f"Modulo {k} does not match any prior division.")
|
|
451
|
+
block_dims[k.expression1] = 0 # avoid further usage
|
|
452
|
+
else:
|
|
453
|
+
raise TypeError(f"Unexpected operator in level expression {k}.")
|
|
454
|
+
else:
|
|
455
|
+
raise TypeError(f"Level expression {k} must be a Dimension or LevelExpr object.")
|
|
456
|
+
|
|
457
|
+
# Inspect the level format for explicit or implicit properties.
|
|
458
|
+
if isinstance(v, tuple):
|
|
459
|
+
fmt, prop = v
|
|
460
|
+
else:
|
|
461
|
+
fmt, prop = v, LevelProperty.ORDERED
|
|
462
|
+
|
|
463
|
+
# Check level format.
|
|
464
|
+
if not isinstance(fmt, LevelFormat):
|
|
465
|
+
raise TypeError(f"Invalid level format {fmt}.")
|
|
466
|
+
elif fmt == LevelFormat.RANGE:
|
|
467
|
+
if not isinstance(k, Dimension):
|
|
468
|
+
raise TypeError(f"Range uses compound level expression {k}.")
|
|
469
|
+
other = range_dims.get(k, 0)
|
|
470
|
+
if other == 0:
|
|
471
|
+
raise TypeError(f"Range uses dimension {k} that is not uniquely defined.")
|
|
472
|
+
# Consume range tuple.
|
|
473
|
+
range_dims[k] = 0
|
|
474
|
+
range_dims[other] = 0
|
|
475
|
+
|
|
476
|
+
# Verify batching.
|
|
477
|
+
if fmt == LevelFormat.BATCH:
|
|
478
|
+
if k != self.dimensions[idx]:
|
|
479
|
+
raise TypeError(f"Batch uses non-identity {k} for {self.dimensions[idx]}.")
|
|
480
|
+
if batch == -1:
|
|
481
|
+
raise TypeError(f"Batch is used in inner level {idx}.")
|
|
482
|
+
batch += 1
|
|
483
|
+
elif batch > 0 and fmt != LevelFormat.DENSE:
|
|
484
|
+
raise TypeError("Batch levels must end in a dense level.")
|
|
485
|
+
else:
|
|
486
|
+
batch = -1
|
|
487
|
+
|
|
488
|
+
# Check level properties.
|
|
489
|
+
if isinstance(prop, Sequence):
|
|
490
|
+
invalid = []
|
|
491
|
+
for p_ in prop:
|
|
492
|
+
if not isinstance(p_, LevelProperty):
|
|
493
|
+
raise TypeError(f"Invalid level property {p_}.")
|
|
494
|
+
if LevelPropertyInverse[p_] in prop:
|
|
495
|
+
invalid += (p_,)
|
|
496
|
+
if invalid:
|
|
497
|
+
raise TypeError(f"Invalid level property combination: {invalid}.")
|
|
498
|
+
elif not isinstance(prop, LevelProperty) and not isinstance(prop, int):
|
|
499
|
+
raise TypeError(f"Invalid level property {prop}.")
|
|
500
|
+
|
|
501
|
+
# Check that every dimension is used eventually.
|
|
502
|
+
if len(all_dims) != len(used_dims):
|
|
503
|
+
raise TypeError(f"The following dimensions are not used: {all_dims - used_dims}.")
|
|
504
|
+
|
|
505
|
+
# Check that every div is matched by mod.
|
|
506
|
+
unmatched = [key for key, value in block_dims.items() if value != 0]
|
|
507
|
+
if len(unmatched) != 0:
|
|
508
|
+
raise TypeError(f"Some division dimensions are not matched by modulo: {unmatched}.")
|
|
509
|
+
|
|
510
|
+
# Check that every add/sub is matched by range.
|
|
511
|
+
unmatched = [key for key, value in range_dims.items() if value != 0]
|
|
512
|
+
if len(unmatched) != 0:
|
|
513
|
+
raise TypeError(f"Some add/sub dimensions are not matched by range: {unmatched}.")
|
|
514
|
+
|
|
515
|
+
# Check that batching ends in dense.
|
|
516
|
+
if batch > 0:
|
|
517
|
+
raise TypeError("Batch levels are not properly closed by a dense level.")
|
|
518
|
+
|
|
519
|
+
def dim2lvl(self, dim_indices, as_size=False):
|
|
520
|
+
"""
|
|
521
|
+
Maps the dimension index list to the level index list.
|
|
522
|
+
|
|
523
|
+
Examples:
|
|
524
|
+
CSR: [0,4] -> [0,4]
|
|
525
|
+
CSC: [0,4] -> [4,0]
|
|
526
|
+
BSRRight(2,2): [2,1] -> [1,0,0,1]
|
|
527
|
+
"""
|
|
528
|
+
return [ls.evaluate(self.dimensions, dim_indices, as_size) for ls in self.levels]
|
|
529
|
+
|
|
530
|
+
def lvl2dim(self, lvl_indices):
|
|
531
|
+
"""
|
|
532
|
+
Maps the level index list to the dimension index list (the inverse of dim2lvl).
|
|
533
|
+
We can safely assume that the mapping is invertible given that the tensor format
|
|
534
|
+
passed validation.
|
|
535
|
+
|
|
536
|
+
Examples:
|
|
537
|
+
CSR: [0,4] -> [0,4]
|
|
538
|
+
CSC: [4,0] -> [0,4]
|
|
539
|
+
BSRRight(2,2): [1,0,0,1] -> [2,1]
|
|
540
|
+
"""
|
|
541
|
+
dim_indices = [0] * self.num_dimensions # pre-populate
|
|
542
|
+
for idx, ls in enumerate(self.levels):
|
|
543
|
+
ls.invert(self.dimensions, self.levels, dim_indices, lvl_indices, idx)
|
|
544
|
+
return dim_indices
|
|
545
|
+
|
|
546
|
+
def __repr__(self):
|
|
547
|
+
return (
|
|
548
|
+
f"{self.dimensions} -> ("
|
|
549
|
+
+ ", ".join([f"{k}: {v if isinstance(v, Sequence) else repr(v)}" for k, v in self.levels.items()])
|
|
550
|
+
+ ")"
|
|
551
|
+
)
|
|
552
|
+
|
|
553
|
+
|
|
554
|
+
class NamedFormats:
|
|
555
|
+
"""
|
|
556
|
+
A number of pre-defined common tensor formats (direct or using builders).
|
|
557
|
+
Clients can always construct these or other tensor formats directly.
|
|
558
|
+
"""
|
|
559
|
+
|
|
560
|
+
i, j, k, b = (
|
|
561
|
+
Dimension(dimension_name="i"),
|
|
562
|
+
Dimension(dimension_name="j"),
|
|
563
|
+
Dimension(dimension_name="k"),
|
|
564
|
+
Dimension(dimension_name="batch"),
|
|
565
|
+
)
|
|
566
|
+
|
|
567
|
+
# Scalar format.
|
|
568
|
+
Scalar = TensorFormat([], {}, name="Scalar")
|
|
569
|
+
|
|
570
|
+
# Vector formats.
|
|
571
|
+
DenseVector = TensorFormat([i], {i: LevelFormat.DENSE}, name="DenseVector")
|
|
572
|
+
SparseVector = TensorFormat([i], {i: LevelFormat.COMPRESSED}, name="SparseVector")
|
|
573
|
+
|
|
574
|
+
# Dense matrix formats.
|
|
575
|
+
DenseMatrixRight = TensorFormat(
|
|
576
|
+
[i, j],
|
|
577
|
+
{i: LevelFormat.DENSE, j: LevelFormat.DENSE},
|
|
578
|
+
name="DenseMatrixRight",
|
|
579
|
+
)
|
|
580
|
+
DenseMatrixLeft = TensorFormat(
|
|
581
|
+
[i, j],
|
|
582
|
+
{j: LevelFormat.DENSE, i: LevelFormat.DENSE},
|
|
583
|
+
name="DenseMatrixLeft",
|
|
584
|
+
)
|
|
585
|
+
|
|
586
|
+
# Sparse matrix formats.
|
|
587
|
+
COO = TensorFormat(
|
|
588
|
+
[i, j],
|
|
589
|
+
{
|
|
590
|
+
i: (LevelFormat.COMPRESSED, LevelProperty.NONUNIQUE),
|
|
591
|
+
j: LevelFormat.SINGLETON,
|
|
592
|
+
},
|
|
593
|
+
name="COO",
|
|
594
|
+
)
|
|
595
|
+
CSR = TensorFormat([i, j], {i: LevelFormat.DENSE, j: LevelFormat.COMPRESSED}, name="CSR")
|
|
596
|
+
CSC = TensorFormat([i, j], {j: LevelFormat.DENSE, i: LevelFormat.COMPRESSED}, name="CSC")
|
|
597
|
+
DCSR = TensorFormat(
|
|
598
|
+
[i, j],
|
|
599
|
+
{i: LevelFormat.COMPRESSED, j: LevelFormat.COMPRESSED},
|
|
600
|
+
name="DCSR",
|
|
601
|
+
)
|
|
602
|
+
DCSC = TensorFormat(
|
|
603
|
+
[i, j],
|
|
604
|
+
{j: LevelFormat.COMPRESSED, i: LevelFormat.COMPRESSED},
|
|
605
|
+
name="DCSC",
|
|
606
|
+
)
|
|
607
|
+
CROW = TensorFormat([i, j], {i: LevelFormat.COMPRESSED, j: LevelFormat.DENSE}, name="CROW")
|
|
608
|
+
CCOL = TensorFormat([i, j], {j: LevelFormat.COMPRESSED, i: LevelFormat.DENSE}, name="CCOL")
|
|
609
|
+
|
|
610
|
+
# Diagonal matrix formats.
|
|
611
|
+
DIAI = TensorFormat(
|
|
612
|
+
[i, j],
|
|
613
|
+
{j - i: LevelFormat.COMPRESSED, i: LevelFormat.RANGE},
|
|
614
|
+
name="DIAI",
|
|
615
|
+
)
|
|
616
|
+
DIAJ = TensorFormat(
|
|
617
|
+
[i, j],
|
|
618
|
+
{j - i: LevelFormat.COMPRESSED, j: LevelFormat.RANGE},
|
|
619
|
+
name="DIAJ",
|
|
620
|
+
)
|
|
621
|
+
SkewDIAI = TensorFormat(
|
|
622
|
+
[i, j],
|
|
623
|
+
{i + j: LevelFormat.COMPRESSED, i: LevelFormat.RANGE},
|
|
624
|
+
name="SkewDIAI",
|
|
625
|
+
)
|
|
626
|
+
SkewDIAJ = TensorFormat(
|
|
627
|
+
[i, j],
|
|
628
|
+
{i + j: LevelFormat.COMPRESSED, j: LevelFormat.RANGE},
|
|
629
|
+
name="SkewDIAJ",
|
|
630
|
+
)
|
|
631
|
+
|
|
632
|
+
# Batched sparse matrices.
|
|
633
|
+
BatchedCSR = TensorFormat(
|
|
634
|
+
[b, i, j],
|
|
635
|
+
{b: LevelFormat.DENSE, i: LevelFormat.DENSE, j: LevelFormat.COMPRESSED},
|
|
636
|
+
name="BatchedCSR",
|
|
637
|
+
)
|
|
638
|
+
BatchedDIAINonUniform = TensorFormat(
|
|
639
|
+
[b, i, j],
|
|
640
|
+
{b: LevelFormat.DENSE, j - i: LevelFormat.COMPRESSED, i: LevelFormat.RANGE},
|
|
641
|
+
name="BatchedDIAINonUniform",
|
|
642
|
+
)
|
|
643
|
+
BatchedDIAIUniform = TensorFormat(
|
|
644
|
+
[b, i, j],
|
|
645
|
+
{j - i: LevelFormat.COMPRESSED, b: LevelFormat.DENSE, i: LevelFormat.RANGE},
|
|
646
|
+
name="BatchedDIAIUniform",
|
|
647
|
+
)
|
|
648
|
+
|
|
649
|
+
#
|
|
650
|
+
# Tensor format builders for vectors, matrices, and tensors.
|
|
651
|
+
#
|
|
652
|
+
|
|
653
|
+
@staticmethod
|
|
654
|
+
def BlockVector(blocksize):
|
|
655
|
+
"""
|
|
656
|
+
Block vector format.
|
|
657
|
+
|
|
658
|
+
Args:
|
|
659
|
+
blocksize: an integer specifying the blocksize in the i'th dimension.
|
|
660
|
+
"""
|
|
661
|
+
i = NamedFormats.i
|
|
662
|
+
name = f"BlockVector{blocksize}"
|
|
663
|
+
return TensorFormat(
|
|
664
|
+
[i],
|
|
665
|
+
{i // blocksize: LevelFormat.COMPRESSED, i % blocksize: LevelFormat.DENSE},
|
|
666
|
+
name=name,
|
|
667
|
+
)
|
|
668
|
+
|
|
669
|
+
@staticmethod
|
|
670
|
+
def BSRRight(blocksize):
|
|
671
|
+
"""
|
|
672
|
+
BSRRight matrix format.
|
|
673
|
+
|
|
674
|
+
Args:
|
|
675
|
+
blocksize: a pair specifying the blocksize in the i'th and j'th dimensions.
|
|
676
|
+
"""
|
|
677
|
+
i, j = NamedFormats.i, NamedFormats.j
|
|
678
|
+
m, n = blocksize
|
|
679
|
+
name = f"BSRRight{m}x{n}"
|
|
680
|
+
return TensorFormat(
|
|
681
|
+
[i, j],
|
|
682
|
+
{
|
|
683
|
+
i // m: LevelFormat.DENSE,
|
|
684
|
+
j // n: LevelFormat.COMPRESSED,
|
|
685
|
+
i % m: LevelFormat.DENSE,
|
|
686
|
+
j % n: LevelFormat.DENSE,
|
|
687
|
+
},
|
|
688
|
+
name=name,
|
|
689
|
+
)
|
|
690
|
+
|
|
691
|
+
@staticmethod
|
|
692
|
+
def BSRLeft(blocksize):
|
|
693
|
+
"""
|
|
694
|
+
BSRLeft matrix format.
|
|
695
|
+
|
|
696
|
+
Args:
|
|
697
|
+
blocksize: a pair specifying the blocksize in the i'th and j'th dimensions.
|
|
698
|
+
"""
|
|
699
|
+
i, j = NamedFormats.i, NamedFormats.j
|
|
700
|
+
m, n = blocksize
|
|
701
|
+
name = f"BSRLeft{m}x{n}"
|
|
702
|
+
return TensorFormat(
|
|
703
|
+
[i, j],
|
|
704
|
+
{
|
|
705
|
+
i // m: LevelFormat.DENSE,
|
|
706
|
+
j // n: LevelFormat.COMPRESSED,
|
|
707
|
+
j % n: LevelFormat.DENSE,
|
|
708
|
+
i % m: LevelFormat.DENSE,
|
|
709
|
+
},
|
|
710
|
+
name=name,
|
|
711
|
+
)
|
|
712
|
+
|
|
713
|
+
@staticmethod
|
|
714
|
+
def BSCRight(blocksize):
|
|
715
|
+
"""
|
|
716
|
+
BSCRight matrix format.
|
|
717
|
+
|
|
718
|
+
Args:
|
|
719
|
+
blocksize: a pair specifying the blocksize in the i'th and j'th dimensions.
|
|
720
|
+
"""
|
|
721
|
+
i, j = NamedFormats.i, NamedFormats.j
|
|
722
|
+
m, n = blocksize
|
|
723
|
+
name = f"BSCRow{m}x{n}"
|
|
724
|
+
return TensorFormat(
|
|
725
|
+
[i, j],
|
|
726
|
+
{
|
|
727
|
+
j // n: LevelFormat.DENSE,
|
|
728
|
+
i // m: LevelFormat.COMPRESSED,
|
|
729
|
+
i % m: LevelFormat.DENSE,
|
|
730
|
+
j % n: LevelFormat.DENSE,
|
|
731
|
+
},
|
|
732
|
+
name=name,
|
|
733
|
+
)
|
|
734
|
+
|
|
735
|
+
@staticmethod
|
|
736
|
+
def BSCLeft(blocksize):
|
|
737
|
+
"""
|
|
738
|
+
BSCLeft matrix format.
|
|
739
|
+
|
|
740
|
+
Args:
|
|
741
|
+
blocksize: a pair specifying the blocksize in the i'th and j'th dimensions.
|
|
742
|
+
"""
|
|
743
|
+
i, j = NamedFormats.i, NamedFormats.j
|
|
744
|
+
m, n = blocksize
|
|
745
|
+
name = f"BSCCol{m}x{n}"
|
|
746
|
+
return TensorFormat(
|
|
747
|
+
[i, j],
|
|
748
|
+
{
|
|
749
|
+
j // n: LevelFormat.DENSE,
|
|
750
|
+
i // m: LevelFormat.COMPRESSED,
|
|
751
|
+
j % n: LevelFormat.DENSE,
|
|
752
|
+
i % m: LevelFormat.DENSE,
|
|
753
|
+
},
|
|
754
|
+
name=name,
|
|
755
|
+
)
|
|
756
|
+
|
|
757
|
+
@staticmethod
|
|
758
|
+
def DELTA(delta):
|
|
759
|
+
"""
|
|
760
|
+
Delta compression matrix format.
|
|
761
|
+
|
|
762
|
+
Args:
|
|
763
|
+
delta: number of bits for delta
|
|
764
|
+
"""
|
|
765
|
+
if delta <= 0:
|
|
766
|
+
raise ValueError(f"Invalid DELTA parameter: delta={delta}. Require 0 < delta.")
|
|
767
|
+
i, j = NamedFormats.i, NamedFormats.j
|
|
768
|
+
name = f"Delta{delta}"
|
|
769
|
+
return TensorFormat(
|
|
770
|
+
[i, j],
|
|
771
|
+
{i: LevelFormat.DENSE, j: (LevelFormat.DELTA, delta)},
|
|
772
|
+
name=name,
|
|
773
|
+
)
|
|
774
|
+
|
|
775
|
+
@staticmethod
|
|
776
|
+
def Structured(N, M):
|
|
777
|
+
"""
|
|
778
|
+
N:M Matrices (structured sparsity), e.g. 2:4
|
|
779
|
+
|
|
780
|
+
Args:
|
|
781
|
+
N: number of nonzeros per strip
|
|
782
|
+
M: number of elements in a strip
|
|
783
|
+
"""
|
|
784
|
+
if not (0 < N <= M):
|
|
785
|
+
raise ValueError(f"Invalid N:M parameters: N={N}, M={M}. Require 0 < N <= M.")
|
|
786
|
+
i, j = NamedFormats.i, NamedFormats.j
|
|
787
|
+
name = f"Structured{N}:{M}"
|
|
788
|
+
return TensorFormat(
|
|
789
|
+
[i, j],
|
|
790
|
+
{i: LevelFormat.DENSE, j // M: LevelFormat.DENSE, j % M: (LevelFormat.STRUCTURED, N)},
|
|
791
|
+
name=name,
|
|
792
|
+
)
|
|
793
|
+
|
|
794
|
+
@staticmethod
|
|
795
|
+
def BSR3(blocksize):
|
|
796
|
+
"""
|
|
797
|
+
3D BSR format.
|
|
798
|
+
|
|
799
|
+
Args:
|
|
800
|
+
blocksize: a triple specifying the blocksize in the i'th, j'th, and k'th
|
|
801
|
+
dimensions.
|
|
802
|
+
"""
|
|
803
|
+
i, j, k = NamedFormats.i, NamedFormats.j, NamedFormats.k
|
|
804
|
+
m, n, o = blocksize
|
|
805
|
+
name = f"BSR3{m}x{n}x{o}"
|
|
806
|
+
return TensorFormat(
|
|
807
|
+
[i, j, k],
|
|
808
|
+
{
|
|
809
|
+
i // m: LevelFormat.DENSE,
|
|
810
|
+
j // n: LevelFormat.COMPRESSED,
|
|
811
|
+
k // o: LevelFormat.COMPRESSED,
|
|
812
|
+
i % m: LevelFormat.DENSE,
|
|
813
|
+
j % n: LevelFormat.DENSE,
|
|
814
|
+
k % o: LevelFormat.DENSE,
|
|
815
|
+
},
|
|
816
|
+
name=name,
|
|
817
|
+
)
|
|
818
|
+
|
|
819
|
+
@staticmethod
|
|
820
|
+
def DensedRight(dim):
|
|
821
|
+
dims = [Dimension(dimension_name=f"{chr(ord('i') + d)}") for d in range(dim)]
|
|
822
|
+
return TensorFormat(
|
|
823
|
+
dims,
|
|
824
|
+
{dims[d]: LevelFormat.DENSE for d in range(dim)},
|
|
825
|
+
name=f"Dense{dim}Right",
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
@staticmethod
|
|
829
|
+
def DensedLeft(dim):
|
|
830
|
+
dims = [Dimension(dimension_name=f"{chr(ord('i') + d)}") for d in range(dim)]
|
|
831
|
+
return TensorFormat(
|
|
832
|
+
dims,
|
|
833
|
+
{dims[dim - d - 1]: LevelFormat.DENSE for d in range(dim)},
|
|
834
|
+
name=f"Dense{dim}Left",
|
|
835
|
+
)
|
|
836
|
+
|
|
837
|
+
@staticmethod
|
|
838
|
+
def COOd(sparse_dim, dense_dim=0):
|
|
839
|
+
dim = sparse_dim + dense_dim
|
|
840
|
+
dims = [Dimension(dimension_name=f"{chr(ord('i') + d)}") for d in range(dim)]
|
|
841
|
+
return TensorFormat(
|
|
842
|
+
dims,
|
|
843
|
+
{
|
|
844
|
+
dims[d]: (
|
|
845
|
+
(LevelFormat.COMPRESSED if sparse_dim == 1 else (LevelFormat.COMPRESSED, LevelProperty.NONUNIQUE))
|
|
846
|
+
if d == 0
|
|
847
|
+
else LevelFormat.SINGLETON
|
|
848
|
+
if d < sparse_dim
|
|
849
|
+
else LevelFormat.DENSE
|
|
850
|
+
)
|
|
851
|
+
for d in range(dim)
|
|
852
|
+
},
|
|
853
|
+
name=("COO" if dim == 2 else f"COO{dim}") if dense_dim == 0 else None,
|
|
854
|
+
)
|
|
855
|
+
|
|
856
|
+
@staticmethod
|
|
857
|
+
def CSRd(batch_dim=0, dense_dim=0):
|
|
858
|
+
dim = batch_dim + 2 + dense_dim
|
|
859
|
+
dims = [Dimension(dimension_name=f"{chr(ord('i') + d)}") for d in range(dim)]
|
|
860
|
+
|
|
861
|
+
name = "CSR"
|
|
862
|
+
if batch_dim > 0:
|
|
863
|
+
name += f"-b{batch_dim}"
|
|
864
|
+
if dense_dim > 0:
|
|
865
|
+
name += f"-d{dense_dim}"
|
|
866
|
+
|
|
867
|
+
return TensorFormat(
|
|
868
|
+
dims,
|
|
869
|
+
{
|
|
870
|
+
dims[d]:
|
|
871
|
+
# Format
|
|
872
|
+
LevelFormat.BATCH if d < batch_dim else LevelFormat.COMPRESSED if d == batch_dim + 1 else LevelFormat.DENSE
|
|
873
|
+
for d in range(dim)
|
|
874
|
+
},
|
|
875
|
+
name=name,
|
|
876
|
+
)
|
|
877
|
+
|
|
878
|
+
@staticmethod
|
|
879
|
+
def CSCd(batch_dim=0, dense_dim=0):
|
|
880
|
+
dim = batch_dim + 2 + dense_dim
|
|
881
|
+
dims = [Dimension(dimension_name=f"{chr(ord('i') + d)}") for d in range(dim)]
|
|
882
|
+
|
|
883
|
+
name = "CSC"
|
|
884
|
+
if batch_dim > 0:
|
|
885
|
+
name += f"-b{batch_dim}"
|
|
886
|
+
if dense_dim > 0:
|
|
887
|
+
name += f"-d{dense_dim}"
|
|
888
|
+
|
|
889
|
+
return TensorFormat(
|
|
890
|
+
dims,
|
|
891
|
+
{
|
|
892
|
+
dims[d + 1 if d == batch_dim else d - 1 if d == batch_dim + 1 else d]:
|
|
893
|
+
# Format
|
|
894
|
+
LevelFormat.BATCH if d < batch_dim else LevelFormat.COMPRESSED if d == batch_dim + 1 else LevelFormat.DENSE
|
|
895
|
+
for d in range(dim)
|
|
896
|
+
},
|
|
897
|
+
name=name,
|
|
898
|
+
)
|
|
899
|
+
|
|
900
|
+
@staticmethod
|
|
901
|
+
def BSRRightd(blocksize, batch_dim=0, dense_dim=0):
|
|
902
|
+
m, n = blocksize
|
|
903
|
+
dim = batch_dim + 2 + dense_dim
|
|
904
|
+
dims = [Dimension(dimension_name=f"{chr(ord('i') + d)}") for d in range(dim)]
|
|
905
|
+
|
|
906
|
+
name = f"BSRRight{m}x{n}"
|
|
907
|
+
if batch_dim > 0:
|
|
908
|
+
name += f"-b{batch_dim}"
|
|
909
|
+
if dense_dim > 0:
|
|
910
|
+
name += f"-d{dense_dim}"
|
|
911
|
+
|
|
912
|
+
return TensorFormat(
|
|
913
|
+
dims,
|
|
914
|
+
{dims[d]: LevelFormat.BATCH for d in range(batch_dim)}
|
|
915
|
+
| {
|
|
916
|
+
dims[batch_dim] // m: LevelFormat.DENSE,
|
|
917
|
+
dims[batch_dim + 1] // n: LevelFormat.COMPRESSED,
|
|
918
|
+
dims[batch_dim] % m: LevelFormat.DENSE,
|
|
919
|
+
dims[batch_dim + 1] % n: LevelFormat.DENSE,
|
|
920
|
+
}
|
|
921
|
+
| {dims[d]: LevelFormat.DENSE for d in range(batch_dim + 2, dim)},
|
|
922
|
+
name=name,
|
|
923
|
+
)
|
|
924
|
+
|
|
925
|
+
@staticmethod
|
|
926
|
+
def BSCRightd(blocksize, batch_dim=0, dense_dim=0):
|
|
927
|
+
m, n = blocksize
|
|
928
|
+
dim = batch_dim + 2 + dense_dim
|
|
929
|
+
dims = [Dimension(dimension_name=f"{chr(ord('i') + d)}") for d in range(dim)]
|
|
930
|
+
|
|
931
|
+
name = f"BSCRight{m}x{n}"
|
|
932
|
+
if batch_dim > 0:
|
|
933
|
+
name += f"-b{batch_dim}"
|
|
934
|
+
if dense_dim > 0:
|
|
935
|
+
name += f"-d{dense_dim}"
|
|
936
|
+
|
|
937
|
+
return TensorFormat(
|
|
938
|
+
dims,
|
|
939
|
+
{dims[d]: LevelFormat.BATCH for d in range(batch_dim)}
|
|
940
|
+
| {
|
|
941
|
+
dims[batch_dim + 1] // n: LevelFormat.DENSE,
|
|
942
|
+
dims[batch_dim] // m: LevelFormat.COMPRESSED,
|
|
943
|
+
dims[batch_dim] % m: LevelFormat.DENSE,
|
|
944
|
+
dims[batch_dim + 1] % n: LevelFormat.DENSE,
|
|
945
|
+
}
|
|
946
|
+
| {dims[d]: LevelFormat.DENSE for d in range(batch_dim + 2, dim)},
|
|
947
|
+
name=name,
|
|
948
|
+
)
|
|
949
|
+
|
|
950
|
+
@staticmethod
|
|
951
|
+
def CSFd(dim):
|
|
952
|
+
dims = [Dimension(dimension_name=f"{chr(ord('i') + d)}") for d in range(dim)]
|
|
953
|
+
return TensorFormat(
|
|
954
|
+
dims,
|
|
955
|
+
{dims[d]: LevelFormat.COMPRESSED for d in range(dim)},
|
|
956
|
+
name=f"CSF{dim}",
|
|
957
|
+
)
|