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,624 @@
|
|
|
1
|
+
# Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Defines dataclasses for tracking matrix layout and traits specifically for wrangling inputs
|
|
7
|
+
into a form accepted by matrix multiplication operations in BLAS APIs.
|
|
8
|
+
|
|
9
|
+
The module defines three classes: BLASMatrixTraits which represents a single matrix,
|
|
10
|
+
InputMMTraits which represents a collection of BLASMatrixTraits to be used together in a
|
|
11
|
+
matrix multiplication, and BLASMMTraitsView which represents a view of the InputMMTraits
|
|
12
|
+
that is compatible with the BLAS API.
|
|
13
|
+
"""
|
|
14
|
+
|
|
15
|
+
from __future__ import annotations
|
|
16
|
+
|
|
17
|
+
import itertools
|
|
18
|
+
import typing
|
|
19
|
+
from collections.abc import Sequence
|
|
20
|
+
from dataclasses import dataclass
|
|
21
|
+
|
|
22
|
+
import nvmath.bindings.cublas as cublas
|
|
23
|
+
import nvmath.bindings.cublasLt as cublaslt
|
|
24
|
+
from nvmath._internal.utils import LoggerLike
|
|
25
|
+
from nvmath.internal import typemaps
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def check_extents(shape: Sequence[int], name: str):
|
|
29
|
+
"""Raises an error if any element in shape is non-positive.
|
|
30
|
+
|
|
31
|
+
0D tensors are expected to have shape () and stride ().
|
|
32
|
+
"""
|
|
33
|
+
if any(e <= 0 for e in shape):
|
|
34
|
+
message = f"The specified extents {shape} for operand {name} are not valid. The extents must be strictly positive."
|
|
35
|
+
raise ValueError(message)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
def check_strides(strides: Sequence[int], name: str):
|
|
39
|
+
"""Raises an error if any element in strides is negative.
|
|
40
|
+
|
|
41
|
+
0D tensors are expected to have shape () and stride().
|
|
42
|
+
Strides may be 0 or positive.
|
|
43
|
+
BLAS disallows negative strides, but the cuBLAS dgmm extension allows negative strides.
|
|
44
|
+
"""
|
|
45
|
+
if any(s < 0 for s in strides):
|
|
46
|
+
message = f"The specified strides {strides} for operand {name} are not valid. The strides must be non-negative."
|
|
47
|
+
raise ValueError(message)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass(slots=True)
|
|
51
|
+
class BLASMatrixTraits:
|
|
52
|
+
"""Manages a tensor's layout and its compatibility with BLAS API matmuls.
|
|
53
|
+
|
|
54
|
+
:class:`BLASMatrixTraits` encapsulates attributes and methods to handle single matrix
|
|
55
|
+
layouts, including memory order, operations (e.g., transpose, conjugation), and
|
|
56
|
+
dimensions. It provides functionality to transform matrices into compatible formats for
|
|
57
|
+
BLAS matrix multiplication.
|
|
58
|
+
|
|
59
|
+
Diagonal matrices should be described as the equivalent square matrix, with equal
|
|
60
|
+
strides in both dimensions.
|
|
61
|
+
|
|
62
|
+
Attributes:
|
|
63
|
+
dtype : The CUDA data type representing the matrix element.
|
|
64
|
+
|
|
65
|
+
shape: The number of elements along each dimension.
|
|
66
|
+
|
|
67
|
+
strides: The number of elements in memory to move between elements along the
|
|
68
|
+
corresponding dimension. We expect a stride of 0 for singleton dimensions.
|
|
69
|
+
|
|
70
|
+
is_conjugate : A flag indicating if the matrix elements should be conjugated.
|
|
71
|
+
|
|
72
|
+
is_transpose : A flag indicating if the matrix should be transposed.
|
|
73
|
+
|
|
74
|
+
is_lower : A flag indicating if the matrix is a lower (true) or upper (false)
|
|
75
|
+
triangular matrix. We track this by default even if the matrix is full because
|
|
76
|
+
we can always ignore this parameter.
|
|
77
|
+
"""
|
|
78
|
+
|
|
79
|
+
dtype: typemaps.cudaDataType
|
|
80
|
+
shape: Sequence[int]
|
|
81
|
+
strides: Sequence[int]
|
|
82
|
+
is_conjugate: bool
|
|
83
|
+
is_transpose: bool
|
|
84
|
+
is_lower: bool
|
|
85
|
+
|
|
86
|
+
def __post_init__(self):
|
|
87
|
+
assert len(self.shape) < 3, "Internal Error: BLASMatrixTraits supports only 0..2D matrices."
|
|
88
|
+
assert len(self.strides) == len(self.shape), "Internal Error: BLASMatrixTraits strides and shape must have same length."
|
|
89
|
+
assert all(extent > 0 for extent in self.shape), "Internal Error: BLASMatrixTraits supports only positive shapes."
|
|
90
|
+
assert all(stride >= 0 for stride in self.strides), (
|
|
91
|
+
"Internal Error: BLASMatrixTraits supports only non-negative strides."
|
|
92
|
+
)
|
|
93
|
+
|
|
94
|
+
@property
|
|
95
|
+
def order(self) -> cublaslt.Order:
|
|
96
|
+
"""The indexing order of the matrix in memory."""
|
|
97
|
+
if len(self.shape) < 2:
|
|
98
|
+
# For vectors and scalars we return COL-order because COL is the default.
|
|
99
|
+
return cublaslt.Order.COL
|
|
100
|
+
msg = f"Unsupported layout for shape: {self.shape} strides: {self.strides}. At least one dimension must be contiguous."
|
|
101
|
+
if (strides_contiguous_perhaps := self.strides_contiguous_perhaps) is None:
|
|
102
|
+
raise ValueError(msg)
|
|
103
|
+
if self.shape[0] * strides_contiguous_perhaps[0] <= strides_contiguous_perhaps[1]:
|
|
104
|
+
return cublaslt.Order.COL
|
|
105
|
+
if self.shape[1] * strides_contiguous_perhaps[1] <= strides_contiguous_perhaps[0]:
|
|
106
|
+
return cublaslt.Order.ROW
|
|
107
|
+
raise ValueError(msg)
|
|
108
|
+
|
|
109
|
+
@property
|
|
110
|
+
def ld(self) -> int:
|
|
111
|
+
"""The leading dimension of the matrix without operations applied."""
|
|
112
|
+
assert self.order == cublaslt.Order.COL, (
|
|
113
|
+
f"Internal Error: {self.__class__.__name__}.ld should only be accessed if the matrix is COL-order."
|
|
114
|
+
)
|
|
115
|
+
assert len(self.shape) == 2, (
|
|
116
|
+
f"Internal Error: {self.__class__.__name__}.ld should only be accessed if the matrix is 2D."
|
|
117
|
+
)
|
|
118
|
+
if self.shape == (1, 1):
|
|
119
|
+
return 1 # A special case where the strides don't matter
|
|
120
|
+
if self.strides[0] == 0 and self.shape[0] != 1 or self.strides[1] == 0 and self.shape[1] != 1:
|
|
121
|
+
raise ValueError("The leading dimension cannot be 0 for a non-singleton dimension.")
|
|
122
|
+
if (strides_contiguous_perhaps := self.strides_contiguous_perhaps) is None:
|
|
123
|
+
msg = (
|
|
124
|
+
f"Unsupported layout for shape: {self.shape} strides: {self.strides}. "
|
|
125
|
+
"At least one dimension must be contiguous."
|
|
126
|
+
)
|
|
127
|
+
raise ValueError(msg)
|
|
128
|
+
return strides_contiguous_perhaps[1]
|
|
129
|
+
|
|
130
|
+
@property
|
|
131
|
+
def operation(self) -> cublas.Operation:
|
|
132
|
+
"""The operation to be applied to the matrix before multiplication."""
|
|
133
|
+
match (self.is_conjugate, self.is_transpose):
|
|
134
|
+
case (False, False):
|
|
135
|
+
return cublas.Operation.N
|
|
136
|
+
case (False, True):
|
|
137
|
+
return cublas.Operation.T
|
|
138
|
+
case (True, True):
|
|
139
|
+
return cublas.Operation.C
|
|
140
|
+
case (True, False):
|
|
141
|
+
raise NotImplementedError("Conjugate non-transpose operation is not supported.")
|
|
142
|
+
case _:
|
|
143
|
+
raise NotImplementedError("Conjugate and transpose flags must be python booleans.")
|
|
144
|
+
|
|
145
|
+
@property
|
|
146
|
+
def is_contiguous(self) -> bool:
|
|
147
|
+
"""Whether the matrix is contiguous in memory."""
|
|
148
|
+
return 1 in self.strides or 0 in self.strides or len(self.shape) == 0
|
|
149
|
+
|
|
150
|
+
@property
|
|
151
|
+
def has_contiguous_view(self) -> bool:
|
|
152
|
+
"""Whether the matrix can be viewed as contiguous in memory.
|
|
153
|
+
|
|
154
|
+
A view/slice of a non-contiguous matrix can be contiguous if the matrix is a vector
|
|
155
|
+
or a scalar.
|
|
156
|
+
"""
|
|
157
|
+
return self.is_contiguous or 1 in self.shape or 0 in self.shape
|
|
158
|
+
|
|
159
|
+
@property
|
|
160
|
+
def strides_contiguous_perhaps(self) -> Sequence[int] | None:
|
|
161
|
+
"""A contiguous view of the matrix's strides if the matrix can be viewed as
|
|
162
|
+
contiguous in memory, otherwise None.
|
|
163
|
+
"""
|
|
164
|
+
if self.is_contiguous:
|
|
165
|
+
return self.strides
|
|
166
|
+
if self.has_contiguous_view:
|
|
167
|
+
new_strides = tuple(
|
|
168
|
+
0 if shape in (0, 1) else stride for shape, stride in zip(self.shape, self.strides, strict=True)
|
|
169
|
+
)
|
|
170
|
+
return new_strides
|
|
171
|
+
return None
|
|
172
|
+
|
|
173
|
+
@property
|
|
174
|
+
def has_a_contiguous_dim(self) -> bool:
|
|
175
|
+
"""Whether the matrix has at least one contiguous dimension in memory."""
|
|
176
|
+
return 1 in self.strides or 0 in self.strides or len(self.shape) == 0
|
|
177
|
+
|
|
178
|
+
@property
|
|
179
|
+
def has_singleton_dim(self) -> bool:
|
|
180
|
+
"""Whether the matrix has a dimension that can be viewed as contiguous in memory.
|
|
181
|
+
|
|
182
|
+
A view/slice of a non-contiguous matrix can be contiguous if the matrix is a vector
|
|
183
|
+
or a scalar.
|
|
184
|
+
"""
|
|
185
|
+
return 1 in self.shape or 0 in self.shape
|
|
186
|
+
|
|
187
|
+
def transpose_and_reorder(self, logger: LoggerLike) -> BLASMatrixTraits:
|
|
188
|
+
"""Return a new :class:`BLASMatrixTraits` that has been transposed, reordered,
|
|
189
|
+
and the transpose flag flipped.
|
|
190
|
+
|
|
191
|
+
Simultaneous transpose and reorder is a useful operation because the data does not
|
|
192
|
+
need to move, but we can change the layout order of the matrix. Flipping the
|
|
193
|
+
transpose flag makes the matrix the same shape as the original.
|
|
194
|
+
"""
|
|
195
|
+
assert self.has_contiguous_view, "Internal Error: transpose_and_reorder only supports for contiguous matrices."
|
|
196
|
+
new = BLASMatrixTraits(
|
|
197
|
+
dtype=self.dtype,
|
|
198
|
+
shape=self.shape[::-1],
|
|
199
|
+
strides=self.strides[::-1],
|
|
200
|
+
is_conjugate=self.is_conjugate,
|
|
201
|
+
is_transpose=not self.is_transpose,
|
|
202
|
+
is_lower=not self.is_lower,
|
|
203
|
+
)
|
|
204
|
+
logger.debug("The matrix was transposed and reordered from %s to %s.", self.order.name, new.order.name)
|
|
205
|
+
return new
|
|
206
|
+
|
|
207
|
+
def flip_transpose_flag(self, logger: LoggerLike) -> BLASMatrixTraits:
|
|
208
|
+
"""Return a new :class:`BLASMatrixTraits` with the transpose flag flipped."""
|
|
209
|
+
new = BLASMatrixTraits(
|
|
210
|
+
dtype=self.dtype,
|
|
211
|
+
shape=self.shape,
|
|
212
|
+
strides=self.strides,
|
|
213
|
+
is_conjugate=self.is_conjugate,
|
|
214
|
+
is_transpose=not self.is_transpose,
|
|
215
|
+
is_lower=self.is_lower,
|
|
216
|
+
)
|
|
217
|
+
logger.debug("The matrix was transposed.")
|
|
218
|
+
return new
|
|
219
|
+
|
|
220
|
+
def promote_left(self, logger: LoggerLike, ndim: int = 2) -> BLASMatrixTraits:
|
|
221
|
+
"""Return a new :class:`BLASMatrixTraits` with new singleton dimensions added to
|
|
222
|
+
the left side of `shape` until the matrix has at least `ndim` dimensions."""
|
|
223
|
+
add = max(ndim - len(self.shape), 0)
|
|
224
|
+
promoted = BLASMatrixTraits(
|
|
225
|
+
dtype=self.dtype,
|
|
226
|
+
shape=(*[1] * add, *self.shape),
|
|
227
|
+
strides=(*[0] * add, *self.strides),
|
|
228
|
+
is_conjugate=self.is_conjugate,
|
|
229
|
+
is_transpose=self.is_transpose,
|
|
230
|
+
is_lower=self.is_lower,
|
|
231
|
+
)
|
|
232
|
+
if add > 0:
|
|
233
|
+
logger.debug("The matrix was promoted from shape %s to shape %s", self.shape, promoted.shape)
|
|
234
|
+
return promoted
|
|
235
|
+
|
|
236
|
+
def promote_right(self, logger: LoggerLike, ndim: int = 2) -> BLASMatrixTraits:
|
|
237
|
+
"""Return a new :class:`BLASMatrixTraits` with new singleton dimensions added to
|
|
238
|
+
the right side of `shape` until the matrix has at least `ndim` dimensions."""
|
|
239
|
+
add = max(ndim - len(self.shape), 0)
|
|
240
|
+
promoted = BLASMatrixTraits(
|
|
241
|
+
dtype=self.dtype,
|
|
242
|
+
shape=(*self.shape, *[1] * add),
|
|
243
|
+
strides=(*self.strides, *[0] * add),
|
|
244
|
+
is_conjugate=self.is_conjugate,
|
|
245
|
+
is_transpose=self.is_transpose,
|
|
246
|
+
is_lower=self.is_lower,
|
|
247
|
+
)
|
|
248
|
+
if add > 0:
|
|
249
|
+
logger.debug("The matrix was promoted from shape %s to shape %s", self.shape, promoted.shape)
|
|
250
|
+
return promoted
|
|
251
|
+
|
|
252
|
+
def __str__(self):
|
|
253
|
+
match self.is_conjugate, self.is_transpose:
|
|
254
|
+
case (True, True):
|
|
255
|
+
operation = "CONJ-TRAN"
|
|
256
|
+
case (True, False):
|
|
257
|
+
operation = "CONJ"
|
|
258
|
+
case (False, True):
|
|
259
|
+
operation = "TRAN"
|
|
260
|
+
case (False, False):
|
|
261
|
+
operation = "NONE"
|
|
262
|
+
return f"shape {self.shape} strides {self.strides} order {self.order.name} and operation {operation}"
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
@dataclass(slots=True, frozen=True)
|
|
266
|
+
class InputMMTraits:
|
|
267
|
+
"""
|
|
268
|
+
InputMMTraits represents the traits of the input and output matrices for BLAS-compatible
|
|
269
|
+
matrix multiplications, including operand A, operand B, an optional operand C, and the
|
|
270
|
+
result matrix, D. It ensures that the operands are compatible with each other.
|
|
271
|
+
|
|
272
|
+
Attributes:
|
|
273
|
+
|
|
274
|
+
M : The number of rows in the resulting matrix multiplication.
|
|
275
|
+
|
|
276
|
+
N : The number of columns in the resulting matrix multiplication.
|
|
277
|
+
|
|
278
|
+
K : The shared dimension between operands A and B for matrix multiplication.
|
|
279
|
+
|
|
280
|
+
a_layout_traits : Layout traits of operand A.
|
|
281
|
+
|
|
282
|
+
b_layout_traits : Layout traits of operand B.
|
|
283
|
+
|
|
284
|
+
c_layout_traits : Layout traits of operand C.
|
|
285
|
+
|
|
286
|
+
d_layout_traits : Layout traits of the result tensor or operand C if inplace.
|
|
287
|
+
|
|
288
|
+
inplace: whether the result will overwrite operand C or the result will be copied
|
|
289
|
+
into a new memory-compact array, D, the same shape as C, but potentially
|
|
290
|
+
different strides.
|
|
291
|
+
"""
|
|
292
|
+
|
|
293
|
+
M: int | None
|
|
294
|
+
N: int | None
|
|
295
|
+
K: int | None
|
|
296
|
+
a_layout_traits: BLASMatrixTraits
|
|
297
|
+
b_layout_traits: BLASMatrixTraits
|
|
298
|
+
c_layout_traits: BLASMatrixTraits
|
|
299
|
+
d_layout_traits: BLASMatrixTraits
|
|
300
|
+
inplace: bool
|
|
301
|
+
|
|
302
|
+
@staticmethod
|
|
303
|
+
def from_layouts(
|
|
304
|
+
a_layout: BLASMatrixTraits,
|
|
305
|
+
b_layout: BLASMatrixTraits,
|
|
306
|
+
c_layout: BLASMatrixTraits | None,
|
|
307
|
+
inplace: bool,
|
|
308
|
+
logger: LoggerLike,
|
|
309
|
+
):
|
|
310
|
+
"""Create a `InputMMTraits` from 2 or 3 `BLASMatrixTraits`.
|
|
311
|
+
|
|
312
|
+
See nvmath.linalg.advanced.matmulmod semantics docstring for matrix promotion and
|
|
313
|
+
broadcasting rules.
|
|
314
|
+
"""
|
|
315
|
+
logger.debug("Checking Matmul operands for layout compatibility.")
|
|
316
|
+
match len(a_layout.shape):
|
|
317
|
+
case 0:
|
|
318
|
+
M0, K0 = None, None
|
|
319
|
+
case 1:
|
|
320
|
+
M0, K0 = None, a_layout.shape[0]
|
|
321
|
+
case _:
|
|
322
|
+
M0, K0 = a_layout.shape[::-1] if a_layout.is_transpose else a_layout.shape
|
|
323
|
+
match len(b_layout.shape):
|
|
324
|
+
case 0:
|
|
325
|
+
K1, N0 = None, None
|
|
326
|
+
case 1:
|
|
327
|
+
K1, N0 = b_layout.shape[0], None
|
|
328
|
+
case _:
|
|
329
|
+
K1, N0 = b_layout.shape[::-1] if b_layout.is_transpose else b_layout.shape
|
|
330
|
+
if inplace:
|
|
331
|
+
assert c_layout is not None, "Internal Error: Cannot have inplace operation without C."
|
|
332
|
+
d_layout = c_layout
|
|
333
|
+
else:
|
|
334
|
+
shape: Sequence[int]
|
|
335
|
+
strides: Sequence[int]
|
|
336
|
+
if M0 is None and N0 is not None:
|
|
337
|
+
shape = (N0,)
|
|
338
|
+
strides = (1,)
|
|
339
|
+
elif M0 is not None and N0 is None:
|
|
340
|
+
shape = (M0,)
|
|
341
|
+
strides = (1,)
|
|
342
|
+
elif M0 is not None and N0 is not None:
|
|
343
|
+
shape = (M0, N0)
|
|
344
|
+
# Match order of input C by default.
|
|
345
|
+
# If input C is None, ROW if all ROW else COL
|
|
346
|
+
order = (
|
|
347
|
+
(a_layout.order == cublaslt.Order.ROW and b_layout.order == cublaslt.Order.ROW)
|
|
348
|
+
if c_layout is None
|
|
349
|
+
else c_layout.order
|
|
350
|
+
)
|
|
351
|
+
strides = (N0, 1) if order == cublaslt.Order.ROW else (1, M0)
|
|
352
|
+
else: # both are None
|
|
353
|
+
shape = ()
|
|
354
|
+
strides = ()
|
|
355
|
+
d_layout = BLASMatrixTraits(
|
|
356
|
+
dtype=a_layout.dtype,
|
|
357
|
+
shape=shape,
|
|
358
|
+
strides=strides,
|
|
359
|
+
is_transpose=False,
|
|
360
|
+
is_conjugate=False,
|
|
361
|
+
is_lower=True,
|
|
362
|
+
)
|
|
363
|
+
logger.debug("Out-of-place result has traits of %s", d_layout)
|
|
364
|
+
if c_layout is None:
|
|
365
|
+
c_layout = d_layout
|
|
366
|
+
match len(c_layout.shape):
|
|
367
|
+
case 0:
|
|
368
|
+
M1, N1 = None, None
|
|
369
|
+
case 1:
|
|
370
|
+
if len(a_layout.shape) <= 1:
|
|
371
|
+
M1, N1 = c_layout.shape[0], None
|
|
372
|
+
else:
|
|
373
|
+
M1, N1 = None, c_layout.shape[0]
|
|
374
|
+
case _:
|
|
375
|
+
M1, N1 = c_layout.shape[::-1] if c_layout.is_transpose else c_layout.shape
|
|
376
|
+
if K0 != K1:
|
|
377
|
+
raise ValueError(
|
|
378
|
+
f"The 'K' extent must match for the operands: K={K0} in operand A is not equal to K={K1} in operand B."
|
|
379
|
+
)
|
|
380
|
+
if M0 is None and N0 is None:
|
|
381
|
+
# Both a,b are vectors; c must be a scalar
|
|
382
|
+
# NOTE: Since BLAS does not support broadcasting c, the shape of c should match
|
|
383
|
+
# a@b exactly not shape (1, 1) or (1,)
|
|
384
|
+
if (M1, N1) != (None, None):
|
|
385
|
+
raise ValueError(f"When both operands A,B are vectors, operand C must be scalar-like, not shape {(M1, N1)}.")
|
|
386
|
+
elif (M0 is not None and N0 is None) or (M0 is None and N0 is not None):
|
|
387
|
+
# One of a,b is a vector; c must be a vector.
|
|
388
|
+
if (M1 or N1) not in [M0, N0]:
|
|
389
|
+
raise ValueError(
|
|
390
|
+
f"When one of operands A,B is a vector, operand C must be a vector with shape ({M0 or N0},), "
|
|
391
|
+
f"not shape ({M1}, {N1})."
|
|
392
|
+
)
|
|
393
|
+
else:
|
|
394
|
+
# Both a,b are matrices; c must have shape (M0, N0)
|
|
395
|
+
if M0 != M1:
|
|
396
|
+
raise ValueError(
|
|
397
|
+
f"The 'M' extent must match for the operands: M={M0} in operand A is not equal to M={M1} in operand C."
|
|
398
|
+
)
|
|
399
|
+
if N0 != N1:
|
|
400
|
+
raise ValueError(
|
|
401
|
+
f"The 'N' extent must match for the operands: N={N0} in operand B is not equal to N={N1} in operand C."
|
|
402
|
+
)
|
|
403
|
+
logger.debug("Matmul operands are layout compatible.")
|
|
404
|
+
|
|
405
|
+
if not (a_layout.has_singleton_dim or a_layout.has_a_contiguous_dim):
|
|
406
|
+
raise ValueError("Unsupported layout: Operand A is not contiguous.")
|
|
407
|
+
if not (b_layout.has_singleton_dim or b_layout.has_a_contiguous_dim):
|
|
408
|
+
raise ValueError("Unsupported layout: Operand B is not contiguous.")
|
|
409
|
+
if not (c_layout.has_singleton_dim or c_layout.has_a_contiguous_dim):
|
|
410
|
+
raise ValueError("Unsupported layout: Operand C is not contiguous.")
|
|
411
|
+
|
|
412
|
+
return InputMMTraits(
|
|
413
|
+
M=M0,
|
|
414
|
+
N=N0,
|
|
415
|
+
K=K0,
|
|
416
|
+
a_layout_traits=a_layout,
|
|
417
|
+
b_layout_traits=b_layout,
|
|
418
|
+
c_layout_traits=c_layout,
|
|
419
|
+
d_layout_traits=d_layout,
|
|
420
|
+
inplace=inplace,
|
|
421
|
+
)
|
|
422
|
+
|
|
423
|
+
|
|
424
|
+
@dataclass(slots=True, frozen=True)
|
|
425
|
+
class BLASMMTraitsView:
|
|
426
|
+
"""
|
|
427
|
+
BLASMMTraitsView represents a view of the InputMMTraits that is compatible with the BLAS
|
|
428
|
+
API. i.e. one where all operands are COL order.
|
|
429
|
+
|
|
430
|
+
A series of no-op (no data movement required) transformations are applied to the
|
|
431
|
+
InputMMTraits to create a BLASMMTraitsView.
|
|
432
|
+
|
|
433
|
+
Attributes:
|
|
434
|
+
|
|
435
|
+
M : The number of rows in the resulting matrix multiplication.
|
|
436
|
+
|
|
437
|
+
N : The number of columns in the resulting matrix multiplication.
|
|
438
|
+
|
|
439
|
+
K : The shared dimension between operands A and B for matrix multiplication.
|
|
440
|
+
|
|
441
|
+
a_layout_traits : A BLAS compatible view of operand A.
|
|
442
|
+
|
|
443
|
+
b_layout_traits : A BLAS compatible view of operand B.
|
|
444
|
+
|
|
445
|
+
c_layout_traits : A BLAS compatible view of operand C.
|
|
446
|
+
|
|
447
|
+
is_swapped_AB : Indicates if operands A and B were swapped in order to ensure
|
|
448
|
+
compatibility.
|
|
449
|
+
"""
|
|
450
|
+
|
|
451
|
+
M: int
|
|
452
|
+
N: int
|
|
453
|
+
K: int
|
|
454
|
+
a_layout_traits: BLASMatrixTraits
|
|
455
|
+
b_layout_traits: BLASMatrixTraits
|
|
456
|
+
c_layout_traits: BLASMatrixTraits
|
|
457
|
+
is_swapped_AB: bool
|
|
458
|
+
|
|
459
|
+
@staticmethod
|
|
460
|
+
def from_input_traits(
|
|
461
|
+
input_traits: InputMMTraits,
|
|
462
|
+
layout_checker: MMLayoutChecker,
|
|
463
|
+
logger: LoggerLike,
|
|
464
|
+
lookup_table_table: dict[MMLayoutChecker, MMLayoutCheckerLookupTable] | None = None,
|
|
465
|
+
) -> BLASMMTraitsView:
|
|
466
|
+
logger.debug("Creating a BLAS compatible view of Matmul operands.")
|
|
467
|
+
|
|
468
|
+
a_layout_traits = input_traits.a_layout_traits.promote_left(logger)
|
|
469
|
+
b_layout_traits = input_traits.b_layout_traits.promote_right(logger)
|
|
470
|
+
if input_traits.M is None:
|
|
471
|
+
c_layout_traits = input_traits.d_layout_traits.promote_left(logger)
|
|
472
|
+
else:
|
|
473
|
+
c_layout_traits = input_traits.d_layout_traits.promote_right(logger)
|
|
474
|
+
M0, K0 = a_layout_traits.shape[::-1] if a_layout_traits.is_transpose else a_layout_traits.shape
|
|
475
|
+
K1, N0 = b_layout_traits.shape[::-1] if b_layout_traits.is_transpose else b_layout_traits.shape
|
|
476
|
+
M1, N1 = c_layout_traits.shape[::-1] if c_layout_traits.is_transpose else c_layout_traits.shape
|
|
477
|
+
assert M0 == M1, "Internal Error: M must match."
|
|
478
|
+
assert K0 == K1, "Internal Error: K must match."
|
|
479
|
+
assert N0 == N1, "Internal Error: N must match."
|
|
480
|
+
|
|
481
|
+
supported_layout = BLASMMTraitsView(
|
|
482
|
+
M=M0,
|
|
483
|
+
N=N0,
|
|
484
|
+
K=K0,
|
|
485
|
+
a_layout_traits=a_layout_traits,
|
|
486
|
+
b_layout_traits=b_layout_traits,
|
|
487
|
+
c_layout_traits=c_layout_traits,
|
|
488
|
+
is_swapped_AB=False,
|
|
489
|
+
).lookup_supported_layout(layout_checker, lookup_table_table, logger)
|
|
490
|
+
logger.debug("A BLAS compatible view of the operands was created.")
|
|
491
|
+
return supported_layout # type: ignore[return-value]
|
|
492
|
+
|
|
493
|
+
def swap_AB_and_transpose_ABC(self, logger: LoggerLike) -> BLASMMTraitsView:
|
|
494
|
+
"""Return a new :class:`BLASMMTraits` with operands A and B swapped."""
|
|
495
|
+
logger.debug("Operands A, B will be swapped and transposed in order to transpose C.")
|
|
496
|
+
return BLASMMTraitsView(
|
|
497
|
+
M=self.N,
|
|
498
|
+
N=self.M,
|
|
499
|
+
K=self.K,
|
|
500
|
+
a_layout_traits=self.b_layout_traits.flip_transpose_flag(logger),
|
|
501
|
+
b_layout_traits=self.a_layout_traits.flip_transpose_flag(logger),
|
|
502
|
+
c_layout_traits=self.c_layout_traits.flip_transpose_flag(logger),
|
|
503
|
+
is_swapped_AB=not self.is_swapped_AB,
|
|
504
|
+
)
|
|
505
|
+
|
|
506
|
+
def transpose_and_reorder_A(self, logger: LoggerLike) -> BLASMMTraitsView:
|
|
507
|
+
"""Return a new :class:`BLASMMTraits` with operand A transposed and reordered."""
|
|
508
|
+
logger.debug("Operand A was transposed and reordered.")
|
|
509
|
+
return BLASMMTraitsView(
|
|
510
|
+
M=self.M,
|
|
511
|
+
N=self.N,
|
|
512
|
+
K=self.K,
|
|
513
|
+
a_layout_traits=self.a_layout_traits.transpose_and_reorder(logger),
|
|
514
|
+
b_layout_traits=self.b_layout_traits,
|
|
515
|
+
c_layout_traits=self.c_layout_traits,
|
|
516
|
+
is_swapped_AB=self.is_swapped_AB,
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
def transpose_and_reorder_B(self, logger: LoggerLike) -> BLASMMTraitsView:
|
|
520
|
+
"""Return a new :class:`BLASMMTraits` with operand B transposed and reordered."""
|
|
521
|
+
logger.debug("Operand B was transposed and reordered.")
|
|
522
|
+
return BLASMMTraitsView(
|
|
523
|
+
M=self.M,
|
|
524
|
+
N=self.N,
|
|
525
|
+
K=self.K,
|
|
526
|
+
a_layout_traits=self.a_layout_traits,
|
|
527
|
+
b_layout_traits=self.b_layout_traits.transpose_and_reorder(logger),
|
|
528
|
+
c_layout_traits=self.c_layout_traits,
|
|
529
|
+
is_swapped_AB=self.is_swapped_AB,
|
|
530
|
+
)
|
|
531
|
+
|
|
532
|
+
def transpose_and_reorder_C(self, logger: LoggerLike) -> BLASMMTraitsView:
|
|
533
|
+
"""Return a new :class:`BLASMMTraits` with operand C transposed and reordered."""
|
|
534
|
+
logger.debug("Operand C was transposed and reordered.")
|
|
535
|
+
return BLASMMTraitsView(
|
|
536
|
+
M=self.M,
|
|
537
|
+
N=self.N,
|
|
538
|
+
K=self.K,
|
|
539
|
+
a_layout_traits=self.a_layout_traits,
|
|
540
|
+
b_layout_traits=self.b_layout_traits,
|
|
541
|
+
c_layout_traits=self.c_layout_traits.transpose_and_reorder(logger),
|
|
542
|
+
is_swapped_AB=self.is_swapped_AB,
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
def find_supported_layout(self, layout_checker: MMLayoutChecker, logger: LoggerLike) -> BLASMMTraitsView:
|
|
546
|
+
# If we assume that A, B, C are all non-transposed, but may be conjugate and may be
|
|
547
|
+
# either ROW or COL, then the input space for the matmul is 2 * 2 = 4 for EACH
|
|
548
|
+
# operand (two layouts and either conjugate or not). That makes the total input
|
|
549
|
+
# space 4 * 4 * 4 = 64 possible input combinations which is too large to enumerate.
|
|
550
|
+
#
|
|
551
|
+
# The operations we are allowed to apply to the operands are:
|
|
552
|
+
# - Transpose and reorder A
|
|
553
|
+
# - Transpose and reorder B
|
|
554
|
+
# - Transpose and reorder C
|
|
555
|
+
# - Swap A, B and transpose A, B, C
|
|
556
|
+
#
|
|
557
|
+
# The order in which the operations are applied does not matter, and applying each
|
|
558
|
+
# operation twice is a no-op. Therefore we only care about whether to apply each
|
|
559
|
+
# operation or not.
|
|
560
|
+
#
|
|
561
|
+
# That makes the possible search space of applying each of these operations 2
|
|
562
|
+
# * 2 * 2 * 2 = 16 which is much smaller than 64. We just need check whether the
|
|
563
|
+
# result of applying each of the 16 combinations results in a supported layout.
|
|
564
|
+
allowed_operations = [
|
|
565
|
+
"transpose_and_reorder_A",
|
|
566
|
+
"transpose_and_reorder_B",
|
|
567
|
+
"transpose_and_reorder_C",
|
|
568
|
+
"swap_AB_and_transpose_ABC",
|
|
569
|
+
]
|
|
570
|
+
# Prioritize the simplest cases: when operands are all COL (apply no operations) or
|
|
571
|
+
# all are ROW (apply all operations)
|
|
572
|
+
for search_depth in [0, 4, 1, 2, 3]:
|
|
573
|
+
for combination in itertools.combinations(allowed_operations, search_depth):
|
|
574
|
+
new_traits = self
|
|
575
|
+
logger.debug("Trying combination: %s", combination)
|
|
576
|
+
for operation in combination:
|
|
577
|
+
new_traits = getattr(new_traits, operation)(logger)
|
|
578
|
+
if layout_checker(new_traits):
|
|
579
|
+
return new_traits # type: ignore[return-value]
|
|
580
|
+
raise ValueError("No BLAS compatible view of the operands was found.")
|
|
581
|
+
|
|
582
|
+
def lookup_supported_layout(
|
|
583
|
+
self,
|
|
584
|
+
layout_checker: MMLayoutChecker,
|
|
585
|
+
lookup_table_table: dict[MMLayoutChecker, MMLayoutCheckerLookupTable] | None,
|
|
586
|
+
logger: LoggerLike,
|
|
587
|
+
) -> BLASMMTraitsView:
|
|
588
|
+
if lookup_table_table is None or layout_checker not in lookup_table_table:
|
|
589
|
+
logger.debug("No cached layout lookup table found for layout checker; performing exhaustive search.")
|
|
590
|
+
return self.find_supported_layout(layout_checker, logger)
|
|
591
|
+
lookup_table = lookup_table_table[layout_checker]
|
|
592
|
+
key = (
|
|
593
|
+
self.a_layout_traits.order,
|
|
594
|
+
self.b_layout_traits.order,
|
|
595
|
+
self.c_layout_traits.order,
|
|
596
|
+
self.a_layout_traits.is_conjugate,
|
|
597
|
+
self.b_layout_traits.is_conjugate,
|
|
598
|
+
self.c_layout_traits.is_conjugate,
|
|
599
|
+
)
|
|
600
|
+
assert not self.a_layout_traits.is_transpose, "Internal Error: A must be non-transpose."
|
|
601
|
+
assert not self.b_layout_traits.is_transpose, "Internal Error: B must be non-transpose."
|
|
602
|
+
assert not self.c_layout_traits.is_transpose, "Internal Error: C must be non-transpose."
|
|
603
|
+
try:
|
|
604
|
+
operations = lookup_table[key]
|
|
605
|
+
logger.debug("Layout lookup table contains a mapping.")
|
|
606
|
+
except KeyError:
|
|
607
|
+
# We have to fall back to the exhaustive search because ambigously ordered
|
|
608
|
+
# matrices (vectors/scalars) miss the cache
|
|
609
|
+
logger.debug("Layout lookup table does not contain a mapping; performing exhaustive search.")
|
|
610
|
+
return self.find_supported_layout(layout_checker, logger)
|
|
611
|
+
new_traits = self
|
|
612
|
+
for operation in operations:
|
|
613
|
+
new_traits = getattr(new_traits, operation)(logger)
|
|
614
|
+
assert layout_checker(new_traits), "Internal Error: Layout checker returned False for a supported layout."
|
|
615
|
+
return new_traits # type: ignore[return-value]
|
|
616
|
+
|
|
617
|
+
|
|
618
|
+
"""A function which checks if a layout is supported for a BLAS matrix multiplication."""
|
|
619
|
+
MMLayoutChecker: typing.TypeAlias = typing.Callable[[BLASMMTraitsView], bool]
|
|
620
|
+
|
|
621
|
+
"""Maps from input traits to the operations needed to get a supported layout."""
|
|
622
|
+
MMLayoutCheckerLookupTable: typing.TypeAlias = dict[
|
|
623
|
+
tuple[cublaslt.Order, cublaslt.Order, cublaslt.Order, bool, bool, bool], list[str]
|
|
624
|
+
]
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
# Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Interface class to encapsulate low-level calls to get and set matmul descriptor attributes.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
__all__ = ["MatmulDescInterface"]
|
|
10
|
+
|
|
11
|
+
from nvmath._internal.attribute_ifc_factory import make_cublas_attribute_interface
|
|
12
|
+
from nvmath.bindings import cublasLt as cublaslt
|
|
13
|
+
|
|
14
|
+
# Create a class, MatmulDescInterface, such that each enum member in
|
|
15
|
+
# MatmulDescAttribute is exposed as a lowercase property (getter + setter)
|
|
16
|
+
# and a ``set_<attr>_unchecked`` method (setter without debug logging).
|
|
17
|
+
# For example, if the class instance is stored as ``desc_ifc``,
|
|
18
|
+
# then the enum member ``POINTER_MODE`` becomes the property
|
|
19
|
+
# ``desc_ifc.pointer_mode``.
|
|
20
|
+
MatmulDescInterface = make_cublas_attribute_interface(
|
|
21
|
+
class_module=__name__,
|
|
22
|
+
class_name="MatmulDescInterface",
|
|
23
|
+
attribute_enum=cublaslt.MatmulDescAttribute,
|
|
24
|
+
get_attribute_dtype_fn=cublaslt.get_matmul_desc_attribute_dtype,
|
|
25
|
+
get_attribute_fn=cublaslt.matmul_desc_get_attribute,
|
|
26
|
+
set_attribute_fn=cublaslt.matmul_desc_set_attribute,
|
|
27
|
+
with_unchecked=True, # Used by Matmul.reset_operands_unchecked hot paths.
|
|
28
|
+
)
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Copyright (c) 2024-2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
"""
|
|
6
|
+
Interface class to encapsulate low-level calls to get and set matmul plan preference
|
|
7
|
+
attributes.
|
|
8
|
+
"""
|
|
9
|
+
|
|
10
|
+
__all__ = ["MatmulPreferenceInterface"]
|
|
11
|
+
|
|
12
|
+
from nvmath._internal.attribute_ifc_factory import make_cublas_attribute_interface
|
|
13
|
+
from nvmath.bindings import cublasLt as cublaslt
|
|
14
|
+
|
|
15
|
+
# Create a class, MatmulPreferenceInterface, such that each enum member in
|
|
16
|
+
# MatmulPreferenceAttribute is exposed as a lowercase property (getter + setter).
|
|
17
|
+
# For example, if the class instance is stored as ``pref_ifc``,
|
|
18
|
+
# then the enum member ``POINTER_MODE_MASK`` becomes the property
|
|
19
|
+
# ``pref_ifc.pointer_mode_mask``.
|
|
20
|
+
MatmulPreferenceInterface = make_cublas_attribute_interface(
|
|
21
|
+
class_module=__name__,
|
|
22
|
+
class_name="MatmulPreferenceInterface",
|
|
23
|
+
attribute_enum=cublaslt.MatmulPreferenceAttribute,
|
|
24
|
+
get_attribute_dtype_fn=cublaslt.get_matmul_preference_attribute_dtype,
|
|
25
|
+
get_attribute_fn=cublaslt.matmul_preference_get_attribute,
|
|
26
|
+
set_attribute_fn=cublaslt.matmul_preference_set_attribute,
|
|
27
|
+
)
|