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,2708 @@
|
|
|
1
|
+
# Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
__all__ = [
|
|
6
|
+
"Solver",
|
|
7
|
+
"CholeskySolver",
|
|
8
|
+
"LUSolver",
|
|
9
|
+
"LUPivotSolver",
|
|
10
|
+
"TriangularSolver",
|
|
11
|
+
"QRFactorize",
|
|
12
|
+
"LQFactorize",
|
|
13
|
+
"QRMultiply",
|
|
14
|
+
"LQMultiply",
|
|
15
|
+
"LeastSquaresSolver",
|
|
16
|
+
"compile_solver_execute",
|
|
17
|
+
]
|
|
18
|
+
|
|
19
|
+
from collections.abc import Sequence
|
|
20
|
+
from functools import cached_property
|
|
21
|
+
from typing import Any, Literal
|
|
22
|
+
|
|
23
|
+
import numpy as np
|
|
24
|
+
|
|
25
|
+
from nvmath._utils import get_nvrtc_version
|
|
26
|
+
from nvmath.bindings import mathdx
|
|
27
|
+
from nvmath.internal.utils import docstring_decorator
|
|
28
|
+
|
|
29
|
+
from .common import SHARED_DEVICE_DOCSTRINGS, check_code_type, parse_code_type, parse_sm
|
|
30
|
+
from .common_backend import get_isa_version, get_lto
|
|
31
|
+
from .common_cuda import (
|
|
32
|
+
Code,
|
|
33
|
+
ComputeCapability,
|
|
34
|
+
)
|
|
35
|
+
from .cusolverdx_backend import (
|
|
36
|
+
_ENABLE_CUSOLVERDX_0_3,
|
|
37
|
+
ALLOWED_ARRANGEMENT,
|
|
38
|
+
ALLOWED_CUSOLVERDX_FUNCTIONS,
|
|
39
|
+
ALLOWED_DATA_TYPE,
|
|
40
|
+
ALLOWED_DIAG,
|
|
41
|
+
ALLOWED_EXECUTION,
|
|
42
|
+
ALLOWED_FILL_MODE,
|
|
43
|
+
ALLOWED_REAL_NP_TYPES,
|
|
44
|
+
ALLOWED_SIDE,
|
|
45
|
+
ALLOWED_TRANSPOSE_MODE,
|
|
46
|
+
CUSOLVERDX_0_3_ALLOWED_FUNCTIONS,
|
|
47
|
+
DIAG_SUPPORTED_FUNCTIONS,
|
|
48
|
+
FILL_MODE_SUPPORTED_FUNCTIONS,
|
|
49
|
+
JOB_SUPPORT_MAP,
|
|
50
|
+
JOB_SUPPORTED_FUNCTIONS,
|
|
51
|
+
SIDE_SUPPORTED_FUNCTIONS,
|
|
52
|
+
generate_code,
|
|
53
|
+
generate_SOLVER,
|
|
54
|
+
get_int_traits,
|
|
55
|
+
get_str_trait,
|
|
56
|
+
validate,
|
|
57
|
+
)
|
|
58
|
+
from .types import Complex, complex64, complex128
|
|
59
|
+
|
|
60
|
+
# ==========================
|
|
61
|
+
# docs
|
|
62
|
+
# ==========================
|
|
63
|
+
|
|
64
|
+
SOLVER_DOCSTRING_SIZE_BASE = """Problem size specified as a sequence of 1 to 3 elements:
|
|
65
|
+
``(M,)`` (treated as ``(M, M, 1)``), ``(M, N)`` (treated as ``(M, N, 1)``), or ``(M, N, K)``.""".replace("\n", " ")
|
|
66
|
+
|
|
67
|
+
SOLVER_DOCSTRING = {
|
|
68
|
+
"function": f"""Solver function to be executed by the execute() method. List of available options:
|
|
69
|
+
{", ".join(f"``'{k}'`` ({v})" for k, v in ALLOWED_CUSOLVERDX_FUNCTIONS.items())}.
|
|
70
|
+
Functions {", ".join(f"``'{k}'``" for k in CUSOLVERDX_0_3_ALLOWED_FUNCTIONS)} require libmathdx 0.3.2 or later.""".replace(
|
|
71
|
+
"\n", " "
|
|
72
|
+
),
|
|
73
|
+
#
|
|
74
|
+
"size": f"""{SOLVER_DOCSTRING_SIZE_BASE} Please refer to cuSOLVERDx functionalities for detailed meaning.
|
|
75
|
+
""".replace("\n", " "),
|
|
76
|
+
#
|
|
77
|
+
"arrangement": f"""Storage layout for matrices A and B, specified as a sequence of 1 or 2 elements ``(arr_A, arr_B)``.
|
|
78
|
+
Each element can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_ARRANGEMENT)}.
|
|
79
|
+
Defaults to ``("col_major", "col_major")``.""".replace("\n", " "),
|
|
80
|
+
#
|
|
81
|
+
"transpose_mode": f"""Transpose mode of matrix A. Refer to cuSOLVERDx documentation,
|
|
82
|
+
as some functions do not support this option.
|
|
83
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_TRANSPOSE_MODE)}.
|
|
84
|
+
Defaults to ``'non_transposed'``.""".replace("\n", " "),
|
|
85
|
+
#
|
|
86
|
+
"side": f"""Side of matrix A in a multiplication operation.
|
|
87
|
+
Required and supported only by functions: {", ".join(f"``'{v}'``" for v in SIDE_SUPPORTED_FUNCTIONS)}.
|
|
88
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_SIDE)}.""".replace("\n", " "),
|
|
89
|
+
#
|
|
90
|
+
"diag": f"""Indicates whether the diagonal elements of matrix A are ones or not.
|
|
91
|
+
Required and supported only by {", ".join(f"``'{v}'``" for v in DIAG_SUPPORTED_FUNCTIONS)} functions.
|
|
92
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_DIAG)}.""".replace("\n", " "),
|
|
93
|
+
#
|
|
94
|
+
"fill_mode": f"""Indicates which part of matrix A is filled and should be used by function.
|
|
95
|
+
Required and supported only by functions: {", ".join(f"``'{v}'``" for v in FILL_MODE_SUPPORTED_FUNCTIONS)}.
|
|
96
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_FILL_MODE)}.""".replace("\n", " "),
|
|
97
|
+
#
|
|
98
|
+
"job": f"""Job type for eigenvalue computation.
|
|
99
|
+
Required and supported only by functions: {", ".join(f"``'{v}'``" for v in JOB_SUPPORTED_FUNCTIONS)}.
|
|
100
|
+
For ``'htev'``: {", ".join(f"``'{v}'``" for v in JOB_SUPPORT_MAP["htev"])}.
|
|
101
|
+
For ``'heev'``: {", ".join(f"``'{v}'``" for v in JOB_SUPPORT_MAP["heev"])}.
|
|
102
|
+
Requires libmathdx 0.3.2 or later.""".replace("\n", " "),
|
|
103
|
+
#
|
|
104
|
+
"batches_per_block": """Number of batches to compute in parallel in a single CUDA block.
|
|
105
|
+
Can be a positive integer or the string ``'suggested'`` for automatic selection of an optimal value.
|
|
106
|
+
We recommend using 1 for matrix A size larger than or equal to 16 x 16,
|
|
107
|
+
and using ``'suggested'`` for smaller sizes to achieve optimal performance.
|
|
108
|
+
Defaults to 1.""".replace("\n", " "),
|
|
109
|
+
#
|
|
110
|
+
"data_type": f"""The data type of the input matrices,
|
|
111
|
+
can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_DATA_TYPE)}.
|
|
112
|
+
Defaults to ``'real'``.""".replace("\n", " "),
|
|
113
|
+
#
|
|
114
|
+
"leading_dimensions": """The leading dimensions for input matrices A and B,
|
|
115
|
+
specified as a sequence of 1 or 2 elements (``lda``, ``ldb``) or ``None``.
|
|
116
|
+
If not provided, it will be automatically deduced from ``size`` and ``arrangement``.
|
|
117
|
+
""".replace("\n", " "),
|
|
118
|
+
#
|
|
119
|
+
"block_dim": """The block dimension for launching the CUDA kernel,
|
|
120
|
+
specified as a 1 to 3 integer sequence (x, y, z) where missing dimensions are assumed to be 1.
|
|
121
|
+
Can be a sequence of 1 to 3 positive integers, the string ``'suggested'``
|
|
122
|
+
for optimal value selection, or ``None`` for the default value.""".replace("\n", " "),
|
|
123
|
+
#
|
|
124
|
+
"execution": f"""A string specifying the execution method.
|
|
125
|
+
Supported values: {", ".join(f"``'{v}'``" for v in ALLOWED_EXECUTION)}.""".replace("\n", " "),
|
|
126
|
+
#
|
|
127
|
+
"precision": f"""The computation precision specified as a numpy float dtype.
|
|
128
|
+
Currently supports: {", ".join(f"``numpy.{v.__name__}``" for v in ALLOWED_REAL_NP_TYPES)}.""".replace("\n", " "),
|
|
129
|
+
#
|
|
130
|
+
"sm": SHARED_DEVICE_DOCSTRINGS["sm"],
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
# ==========================
|
|
134
|
+
# Solver class
|
|
135
|
+
# ==========================
|
|
136
|
+
|
|
137
|
+
|
|
138
|
+
@docstring_decorator(SOLVER_DOCSTRING, skip_missing=False)
|
|
139
|
+
class Solver:
|
|
140
|
+
"""
|
|
141
|
+
A class that encapsulates a partial dense matrix factorization and solve
|
|
142
|
+
device function.
|
|
143
|
+
|
|
144
|
+
**Memory Layout Requirements:**
|
|
145
|
+
|
|
146
|
+
Matrices must be stored in shared memory according
|
|
147
|
+
to their arrangement and leading dimension (ld):
|
|
148
|
+
|
|
149
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, rows, cols)``
|
|
150
|
+
with strides ``(ld * cols, 1, ld)``
|
|
151
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, rows, cols)``
|
|
152
|
+
with strides ``(ld * rows, ld, 1)``
|
|
153
|
+
|
|
154
|
+
Args:
|
|
155
|
+
function (str): {function}
|
|
156
|
+
|
|
157
|
+
size (Sequence[int]): {size}
|
|
158
|
+
|
|
159
|
+
precision (type[np.floating]): {precision}
|
|
160
|
+
|
|
161
|
+
execution (str): {execution}
|
|
162
|
+
|
|
163
|
+
sm (ComputeCapability): {sm}
|
|
164
|
+
|
|
165
|
+
arrangement (Sequence[str], optional): {arrangement}
|
|
166
|
+
|
|
167
|
+
transpose_mode (str, optional): {transpose_mode}
|
|
168
|
+
|
|
169
|
+
side (str, optional): {side}
|
|
170
|
+
|
|
171
|
+
diag (str, optional): {diag}
|
|
172
|
+
|
|
173
|
+
fill_mode (str, optional): {fill_mode}
|
|
174
|
+
|
|
175
|
+
batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
|
|
176
|
+
|
|
177
|
+
data_type (str, optional): {data_type}
|
|
178
|
+
|
|
179
|
+
leading_dimensions (Sequence[int], optional): {leading_dimensions}
|
|
180
|
+
|
|
181
|
+
block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
|
|
182
|
+
|
|
183
|
+
job (str, optional): {job}
|
|
184
|
+
|
|
185
|
+
See Also:
|
|
186
|
+
The attributes of this class provide a 1:1
|
|
187
|
+
mapping with the CUDA C++ cuSOLVERDx APIs.
|
|
188
|
+
For further details, please refer to :cusolverdx_doc:`cuSOLVERDx documentation
|
|
189
|
+
<index.html>`.
|
|
190
|
+
"""
|
|
191
|
+
|
|
192
|
+
def __init__(
|
|
193
|
+
self,
|
|
194
|
+
function: str,
|
|
195
|
+
size: Sequence[int],
|
|
196
|
+
precision: type[np.floating],
|
|
197
|
+
execution: str,
|
|
198
|
+
*,
|
|
199
|
+
sm=None,
|
|
200
|
+
arrangement: Sequence[str] | None = None,
|
|
201
|
+
transpose_mode: str | None = None,
|
|
202
|
+
side: str | None = None,
|
|
203
|
+
diag: str | None = None,
|
|
204
|
+
fill_mode: str | None = None,
|
|
205
|
+
batches_per_block: int | Literal["suggested"] | None = None,
|
|
206
|
+
data_type: str | None = None,
|
|
207
|
+
leading_dimensions: Sequence[int] | None = None,
|
|
208
|
+
block_dim: Sequence[int] | Literal["suggested"] | None = None,
|
|
209
|
+
job: str | None = None,
|
|
210
|
+
):
|
|
211
|
+
if get_nvrtc_version() < (12, 6, 85):
|
|
212
|
+
raise RuntimeError("cuSOLVERDx requires CUDA Toolkit 12.6 Update 3 or later.")
|
|
213
|
+
|
|
214
|
+
sm = parse_sm(sm)
|
|
215
|
+
|
|
216
|
+
validate(
|
|
217
|
+
function=function,
|
|
218
|
+
size=size,
|
|
219
|
+
precision=precision,
|
|
220
|
+
execution=execution,
|
|
221
|
+
sm=sm,
|
|
222
|
+
arrangement=arrangement,
|
|
223
|
+
transpose_mode=transpose_mode,
|
|
224
|
+
side=side,
|
|
225
|
+
diag=diag,
|
|
226
|
+
fill_mode=fill_mode,
|
|
227
|
+
batches_per_block=batches_per_block,
|
|
228
|
+
data_type=data_type,
|
|
229
|
+
leading_dimensions=leading_dimensions,
|
|
230
|
+
block_dim=block_dim,
|
|
231
|
+
job=job,
|
|
232
|
+
)
|
|
233
|
+
|
|
234
|
+
if len(size) == 1:
|
|
235
|
+
size = (size[0], size[0], 1)
|
|
236
|
+
elif len(size) == 2:
|
|
237
|
+
size = (size[0], size[1], 1)
|
|
238
|
+
else:
|
|
239
|
+
size = (size[0], size[1], size[2])
|
|
240
|
+
|
|
241
|
+
if block_dim is not None and block_dim != "suggested":
|
|
242
|
+
if len(block_dim) == 1:
|
|
243
|
+
block_dim = (block_dim[0], 1, 1)
|
|
244
|
+
elif len(block_dim) == 2:
|
|
245
|
+
block_dim = (block_dim[0], block_dim[1], 1)
|
|
246
|
+
else:
|
|
247
|
+
block_dim = (block_dim[0], block_dim[1], block_dim[2])
|
|
248
|
+
|
|
249
|
+
self._function = function
|
|
250
|
+
self._size = size
|
|
251
|
+
self._precision = precision
|
|
252
|
+
self._execution = execution
|
|
253
|
+
self._sm = sm
|
|
254
|
+
self._arrangement = tuple(arrangement) if arrangement is not None else None
|
|
255
|
+
self._transpose_mode = transpose_mode
|
|
256
|
+
self._side = side
|
|
257
|
+
self._diag = diag
|
|
258
|
+
self._fill_mode = fill_mode
|
|
259
|
+
self._data_type = data_type
|
|
260
|
+
self._job = job
|
|
261
|
+
|
|
262
|
+
self._batches_per_block = batches_per_block if batches_per_block != "suggested" else None
|
|
263
|
+
self._leading_dimensions = tuple(leading_dimensions) if leading_dimensions is not None else None
|
|
264
|
+
self._block_dim = block_dim if block_dim != "suggested" and block_dim is not None else None
|
|
265
|
+
|
|
266
|
+
if batches_per_block == "suggested" or block_dim == "suggested":
|
|
267
|
+
# Update suggested fields
|
|
268
|
+
traits = _SolverTraits(self)
|
|
269
|
+
|
|
270
|
+
if batches_per_block == "suggested":
|
|
271
|
+
self._batches_per_block = traits.suggested_batches_per_block
|
|
272
|
+
|
|
273
|
+
if block_dim == "suggested":
|
|
274
|
+
self._block_dim = traits.suggested_block_dim
|
|
275
|
+
|
|
276
|
+
# ==========================
|
|
277
|
+
# Operator properties
|
|
278
|
+
# ==========================
|
|
279
|
+
|
|
280
|
+
@property
|
|
281
|
+
def function(self) -> str:
|
|
282
|
+
return self._function
|
|
283
|
+
|
|
284
|
+
@property
|
|
285
|
+
def size(self) -> tuple[int, int, int]:
|
|
286
|
+
return self._size
|
|
287
|
+
|
|
288
|
+
@property
|
|
289
|
+
def m(self) -> int:
|
|
290
|
+
m, _, _ = self.size
|
|
291
|
+
return m
|
|
292
|
+
|
|
293
|
+
@property
|
|
294
|
+
def n(self) -> int:
|
|
295
|
+
_, n, _ = self.size
|
|
296
|
+
return n
|
|
297
|
+
|
|
298
|
+
@property
|
|
299
|
+
def k(self) -> int:
|
|
300
|
+
_, _, k = self.size
|
|
301
|
+
return k
|
|
302
|
+
|
|
303
|
+
@property
|
|
304
|
+
def precision(self) -> type[np.floating]:
|
|
305
|
+
return self._precision
|
|
306
|
+
|
|
307
|
+
@property
|
|
308
|
+
def execution(self) -> str:
|
|
309
|
+
return self._execution
|
|
310
|
+
|
|
311
|
+
@property
|
|
312
|
+
def sm(self) -> ComputeCapability:
|
|
313
|
+
return self._sm
|
|
314
|
+
|
|
315
|
+
@property
|
|
316
|
+
def arrangement(self) -> Sequence[str]:
|
|
317
|
+
if self._arrangement is None:
|
|
318
|
+
# TODO: replace with libmathdx trait when supported
|
|
319
|
+
return ("col_major", "col_major")
|
|
320
|
+
if len(self._arrangement) == 1:
|
|
321
|
+
return (self._arrangement[0], "col_major")
|
|
322
|
+
return self._arrangement
|
|
323
|
+
|
|
324
|
+
@property
|
|
325
|
+
def a_arrangement(self) -> str:
|
|
326
|
+
arr_a, _ = self.arrangement
|
|
327
|
+
return arr_a
|
|
328
|
+
|
|
329
|
+
@property
|
|
330
|
+
def b_arrangement(self) -> str:
|
|
331
|
+
_, arr_b = self.arrangement
|
|
332
|
+
return arr_b
|
|
333
|
+
|
|
334
|
+
@property
|
|
335
|
+
def transpose_mode(self) -> str | None:
|
|
336
|
+
return self._transpose_mode
|
|
337
|
+
|
|
338
|
+
@property
|
|
339
|
+
def side(self) -> str | None:
|
|
340
|
+
return self._side
|
|
341
|
+
|
|
342
|
+
@property
|
|
343
|
+
def diag(self) -> str | None:
|
|
344
|
+
return self._diag
|
|
345
|
+
|
|
346
|
+
@property
|
|
347
|
+
def fill_mode(self) -> str | None:
|
|
348
|
+
return self._fill_mode
|
|
349
|
+
|
|
350
|
+
@property
|
|
351
|
+
def batches_per_block(self) -> int:
|
|
352
|
+
if self._batches_per_block is None:
|
|
353
|
+
# TODO: replace with libmathdx trait when supported
|
|
354
|
+
return 1
|
|
355
|
+
return self._batches_per_block
|
|
356
|
+
|
|
357
|
+
@property
|
|
358
|
+
def data_type(self) -> str:
|
|
359
|
+
if self._data_type is None:
|
|
360
|
+
# TODO: replace with libmathdx trait when supported
|
|
361
|
+
return "real"
|
|
362
|
+
return self._data_type
|
|
363
|
+
|
|
364
|
+
@property
|
|
365
|
+
def leading_dimensions(self) -> tuple:
|
|
366
|
+
if self._leading_dimensions is None:
|
|
367
|
+
# TODO: replace with libmathdx trait when supported
|
|
368
|
+
return self._calculate_default_leading_dimensions()
|
|
369
|
+
|
|
370
|
+
if len(self._leading_dimensions) == 1:
|
|
371
|
+
return (self._leading_dimensions[0], self._calculate_default_leading_dimensions()[1])
|
|
372
|
+
return self._leading_dimensions
|
|
373
|
+
|
|
374
|
+
@property
|
|
375
|
+
def lda(self) -> int:
|
|
376
|
+
lda, _ = self.leading_dimensions
|
|
377
|
+
return lda
|
|
378
|
+
|
|
379
|
+
@property
|
|
380
|
+
def ldb(self) -> int:
|
|
381
|
+
_, ldb = self.leading_dimensions
|
|
382
|
+
return ldb
|
|
383
|
+
|
|
384
|
+
@property
|
|
385
|
+
def block_dim(self) -> tuple:
|
|
386
|
+
if self._block_dim is None:
|
|
387
|
+
return self._traits.block_dim
|
|
388
|
+
return self._block_dim
|
|
389
|
+
|
|
390
|
+
@property
|
|
391
|
+
def block_size(self) -> int:
|
|
392
|
+
return self.block_dim[0] * self.block_dim[1] * self.block_dim[2]
|
|
393
|
+
|
|
394
|
+
@property
|
|
395
|
+
def job(self) -> str | None:
|
|
396
|
+
return self._job
|
|
397
|
+
|
|
398
|
+
# ==========================
|
|
399
|
+
# Trait properties
|
|
400
|
+
# ==========================
|
|
401
|
+
|
|
402
|
+
@cached_property
|
|
403
|
+
def _traits(self):
|
|
404
|
+
return _SolverTraits(self)
|
|
405
|
+
|
|
406
|
+
@property
|
|
407
|
+
def workspace_size(self) -> int:
|
|
408
|
+
"""Workspace size in elements of :attr:`value_type`, not in bytes."""
|
|
409
|
+
if not _ENABLE_CUSOLVERDX_0_3:
|
|
410
|
+
raise RuntimeError("workspace size requires libmathdx 0.3.2 or later")
|
|
411
|
+
|
|
412
|
+
return self._traits.workspace_size
|
|
413
|
+
|
|
414
|
+
@property
|
|
415
|
+
def value_type(self) -> type[np.floating] | Complex:
|
|
416
|
+
if self.data_type == "complex":
|
|
417
|
+
return complex64 if self.precision == np.float32 else complex128
|
|
418
|
+
return self.precision
|
|
419
|
+
|
|
420
|
+
@property
|
|
421
|
+
def info_type(self) -> type[np.signedinteger]:
|
|
422
|
+
return np.int32
|
|
423
|
+
|
|
424
|
+
@property
|
|
425
|
+
def ipiv_type(self) -> type[np.signedinteger]:
|
|
426
|
+
return np.int32
|
|
427
|
+
|
|
428
|
+
@property
|
|
429
|
+
def tau_type(self) -> type[np.floating] | Complex:
|
|
430
|
+
return self.value_type
|
|
431
|
+
|
|
432
|
+
# ==========================
|
|
433
|
+
# execute()
|
|
434
|
+
# ==========================
|
|
435
|
+
|
|
436
|
+
def execute(*args):
|
|
437
|
+
raise RuntimeError("execute should not be called directly outside of a jitted kernel.")
|
|
438
|
+
|
|
439
|
+
# ==========================
|
|
440
|
+
# Private methods
|
|
441
|
+
# ==========================
|
|
442
|
+
|
|
443
|
+
def _generate_SOLVER(self, execution_api):
|
|
444
|
+
return generate_SOLVER(
|
|
445
|
+
function=self._function,
|
|
446
|
+
size=self._size,
|
|
447
|
+
precision=self._precision,
|
|
448
|
+
execution=self._execution,
|
|
449
|
+
sm=self._sm,
|
|
450
|
+
arrangement=self.arrangement, # TODO: remove property when libmathdx supports traits
|
|
451
|
+
transpose_mode=self._transpose_mode,
|
|
452
|
+
side=self._side,
|
|
453
|
+
diag=self._diag,
|
|
454
|
+
fill_mode=self._fill_mode,
|
|
455
|
+
batches_per_block=self.batches_per_block, # TODO: remove property when libmathdx supports traits
|
|
456
|
+
data_type=self.data_type, # TODO: remove property when libmathdx supports traits
|
|
457
|
+
leading_dimensions=self.leading_dimensions, # TODO: remove property when libmathdx supports traits
|
|
458
|
+
block_dim=self._block_dim,
|
|
459
|
+
execution_api=execution_api,
|
|
460
|
+
job=self._job,
|
|
461
|
+
)
|
|
462
|
+
|
|
463
|
+
def _calculate_default_leading_dimensions(self) -> tuple:
|
|
464
|
+
"""
|
|
465
|
+
Calculates default leading dimensions based on problem
|
|
466
|
+
size (m, n, k) for different solver functions.
|
|
467
|
+
"""
|
|
468
|
+
|
|
469
|
+
def calculate_ld(rows, cols, arr):
|
|
470
|
+
return rows if arr == "col_major" else cols
|
|
471
|
+
|
|
472
|
+
arr_a, arr_b = self.arrangement
|
|
473
|
+
m, n, k = self.size
|
|
474
|
+
|
|
475
|
+
match self.function:
|
|
476
|
+
case (
|
|
477
|
+
"potrf"
|
|
478
|
+
| "potrs"
|
|
479
|
+
| "posv"
|
|
480
|
+
| "getrf_no_pivot"
|
|
481
|
+
| "getrs_no_pivot"
|
|
482
|
+
| "gesv_no_pivot"
|
|
483
|
+
| "getrf_partial_pivot"
|
|
484
|
+
| "getrs_partial_pivot"
|
|
485
|
+
| "gesv_partial_pivot"
|
|
486
|
+
):
|
|
487
|
+
return (calculate_ld(m, n, arr_a), calculate_ld(n, k, arr_b))
|
|
488
|
+
case "trsm":
|
|
489
|
+
assert self.side is not None
|
|
490
|
+
return (m if self.side == "left" else n, calculate_ld(m, n, arr_b))
|
|
491
|
+
case "geqrf" | "gelqf" | "htev" | "unglq" | "ungqr" | "heev":
|
|
492
|
+
return (calculate_ld(m, n, arr_a), 0)
|
|
493
|
+
case "unmqr":
|
|
494
|
+
return (calculate_ld(m if self.side == "left" else n, k, arr_a), calculate_ld(m, n, arr_b))
|
|
495
|
+
case "unmlq":
|
|
496
|
+
return (calculate_ld(k, m if self.side == "left" else n, arr_a), calculate_ld(m, n, arr_b))
|
|
497
|
+
case "gels":
|
|
498
|
+
return (calculate_ld(m, n, arr_a), calculate_ld(max(m, n), k, arr_b)) # mirrors cusolverdx behaviour
|
|
499
|
+
case "gtsv_no_pivot":
|
|
500
|
+
return (0, calculate_ld(n, k, arr_b))
|
|
501
|
+
case _:
|
|
502
|
+
raise NotImplementedError(f"Function {self.function} is not supported for leading dimension calculation")
|
|
503
|
+
|
|
504
|
+
|
|
505
|
+
# ==========================
|
|
506
|
+
# Internal traits class
|
|
507
|
+
# ==========================
|
|
508
|
+
|
|
509
|
+
|
|
510
|
+
class _SolverTraits:
|
|
511
|
+
def __init__(self, solver: Solver):
|
|
512
|
+
h = solver._generate_SOLVER("compiled_leading_dim")
|
|
513
|
+
|
|
514
|
+
try:
|
|
515
|
+
self.suggested_batches_per_block = int(
|
|
516
|
+
mathdx.cusolverdx_get_trait_int64(h.descriptor, mathdx.CusolverdxTraitType.SUGGESTED_BATCHES_PER_BLOCK)
|
|
517
|
+
)
|
|
518
|
+
|
|
519
|
+
self.suggested_block_dim = tuple(get_int_traits(h.descriptor, mathdx.CusolverdxTraitType.SUGGESTED_BLOCK_DIM, 3))
|
|
520
|
+
self.block_dim = tuple(get_int_traits(h.descriptor, mathdx.CusolverdxTraitType.BLOCK_DIM, 3))
|
|
521
|
+
|
|
522
|
+
if _ENABLE_CUSOLVERDX_0_3:
|
|
523
|
+
self.workspace_size = int(
|
|
524
|
+
mathdx.cusolverdx_get_trait_int64(h.descriptor, mathdx.CusolverdxTraitType.WORKSPACE_SIZE)
|
|
525
|
+
)
|
|
526
|
+
|
|
527
|
+
except mathdx.LibMathDxError as e:
|
|
528
|
+
raise RuntimeError(
|
|
529
|
+
"Failed to compile the solver. This may indicate incompatible "
|
|
530
|
+
f"parameter combination. Please refer to the cuSOLVERDx documentation. Details: {e}"
|
|
531
|
+
) from e
|
|
532
|
+
|
|
533
|
+
|
|
534
|
+
# ==========================
|
|
535
|
+
# Compile function
|
|
536
|
+
# ==========================
|
|
537
|
+
|
|
538
|
+
|
|
539
|
+
def compile_solver_execute(
|
|
540
|
+
solver: Solver,
|
|
541
|
+
code_type: Any,
|
|
542
|
+
execution_api: str,
|
|
543
|
+
) -> tuple[Code, str]:
|
|
544
|
+
"""
|
|
545
|
+
Compiles the solver device function to LTO IR for the given code type.
|
|
546
|
+
|
|
547
|
+
Args:
|
|
548
|
+
solver (Solver): The solver device function to compile.
|
|
549
|
+
|
|
550
|
+
code_type (CodeType): The target code type, an instance of
|
|
551
|
+
:py:class:`nvmath.device.CodeType` or a compatible 2-tuple.
|
|
552
|
+
|
|
553
|
+
execution_api (str): The execution API, ``'compiled_leading_dim'`` or
|
|
554
|
+
``'runtime_leading_dim'``.
|
|
555
|
+
|
|
556
|
+
Returns:
|
|
557
|
+
A tuple of the compiled :py:class:`nvmath.device.Code` and the mangled symbol name
|
|
558
|
+
of the device function.
|
|
559
|
+
"""
|
|
560
|
+
code_type = parse_code_type(code_type)
|
|
561
|
+
check_code_type(code_type, "cuSOLVERDx")
|
|
562
|
+
|
|
563
|
+
h = solver._generate_SOLVER(execution_api).descriptor
|
|
564
|
+
|
|
565
|
+
try:
|
|
566
|
+
code = generate_code(h, code_type.cc)
|
|
567
|
+
except mathdx.LibMathDxError as e:
|
|
568
|
+
raise RuntimeError(
|
|
569
|
+
"Failed to compile the solver. This may indicate incompatible "
|
|
570
|
+
f"parameter combination. Please refer to the cuSOLVERDx documentation. Details: {e}"
|
|
571
|
+
) from e
|
|
572
|
+
|
|
573
|
+
lto = get_lto(code.descriptor)
|
|
574
|
+
isa_version = get_isa_version(code.descriptor)
|
|
575
|
+
symbol = get_str_trait(h, mathdx.CusolverdxTraitType.SYMBOL_NAME)
|
|
576
|
+
|
|
577
|
+
return Code(code_type, isa_version, lto), symbol
|
|
578
|
+
|
|
579
|
+
|
|
580
|
+
# ==========================
|
|
581
|
+
# Pythonic Adapters
|
|
582
|
+
# ==========================
|
|
583
|
+
|
|
584
|
+
|
|
585
|
+
def _calculate_strides(shape: tuple[int, int], ld: int, arrangement: str) -> tuple[int, int, int]:
|
|
586
|
+
return (ld * shape[1], 1, ld) if arrangement == "col_major" else (ld * shape[0], ld, 1)
|
|
587
|
+
|
|
588
|
+
|
|
589
|
+
class _SolverProperties:
|
|
590
|
+
def __init__(self, source: Solver):
|
|
591
|
+
self._properties_source = source
|
|
592
|
+
|
|
593
|
+
@property
|
|
594
|
+
def size(self) -> tuple[int, int, int]:
|
|
595
|
+
return self._properties_source.size
|
|
596
|
+
|
|
597
|
+
@property
|
|
598
|
+
def m(self) -> int:
|
|
599
|
+
return self._properties_source.m
|
|
600
|
+
|
|
601
|
+
@property
|
|
602
|
+
def n(self) -> int:
|
|
603
|
+
return self._properties_source.n
|
|
604
|
+
|
|
605
|
+
@property
|
|
606
|
+
def k(self) -> int:
|
|
607
|
+
return self._properties_source.k
|
|
608
|
+
|
|
609
|
+
@property
|
|
610
|
+
def precision(self) -> type[np.floating]:
|
|
611
|
+
return self._properties_source.precision
|
|
612
|
+
|
|
613
|
+
@property
|
|
614
|
+
def execution(self) -> str:
|
|
615
|
+
return self._properties_source.execution
|
|
616
|
+
|
|
617
|
+
@property
|
|
618
|
+
def sm(self) -> ComputeCapability:
|
|
619
|
+
return self._properties_source.sm
|
|
620
|
+
|
|
621
|
+
@property
|
|
622
|
+
def arrangement(self) -> Sequence[str]:
|
|
623
|
+
return self._properties_source.arrangement
|
|
624
|
+
|
|
625
|
+
@property
|
|
626
|
+
def a_arrangement(self) -> str:
|
|
627
|
+
return self._properties_source.a_arrangement
|
|
628
|
+
|
|
629
|
+
@property
|
|
630
|
+
def b_arrangement(self) -> str:
|
|
631
|
+
return self._properties_source.b_arrangement
|
|
632
|
+
|
|
633
|
+
@property
|
|
634
|
+
def batches_per_block(self) -> int:
|
|
635
|
+
return self._properties_source.batches_per_block
|
|
636
|
+
|
|
637
|
+
@property
|
|
638
|
+
def data_type(self) -> str:
|
|
639
|
+
return self._properties_source.data_type
|
|
640
|
+
|
|
641
|
+
@property
|
|
642
|
+
def leading_dimensions(self) -> tuple:
|
|
643
|
+
return self._properties_source.leading_dimensions
|
|
644
|
+
|
|
645
|
+
@property
|
|
646
|
+
def lda(self) -> int:
|
|
647
|
+
return self._properties_source.lda
|
|
648
|
+
|
|
649
|
+
@property
|
|
650
|
+
def ldb(self) -> int:
|
|
651
|
+
return self._properties_source.ldb
|
|
652
|
+
|
|
653
|
+
@property
|
|
654
|
+
def block_dim(self) -> tuple:
|
|
655
|
+
return self._properties_source.block_dim
|
|
656
|
+
|
|
657
|
+
@property
|
|
658
|
+
def block_size(self) -> int:
|
|
659
|
+
return self._properties_source.block_size
|
|
660
|
+
|
|
661
|
+
@property
|
|
662
|
+
def value_type(self) -> type[np.floating] | Complex:
|
|
663
|
+
return self._properties_source.value_type
|
|
664
|
+
|
|
665
|
+
|
|
666
|
+
class _LinearSolverProperties(_SolverProperties):
|
|
667
|
+
def __init__(self, source: Solver):
|
|
668
|
+
super().__init__(source)
|
|
669
|
+
|
|
670
|
+
@property
|
|
671
|
+
def info_type(self) -> type[np.signedinteger]:
|
|
672
|
+
return self._properties_source.info_type
|
|
673
|
+
|
|
674
|
+
@property
|
|
675
|
+
def info_shape(self) -> tuple[int]:
|
|
676
|
+
return (self.batches_per_block,)
|
|
677
|
+
|
|
678
|
+
@property
|
|
679
|
+
def info_strides(self) -> tuple[int]:
|
|
680
|
+
return (1,)
|
|
681
|
+
|
|
682
|
+
@property
|
|
683
|
+
def a_shape(self) -> tuple[int, int, int]:
|
|
684
|
+
return (self.batches_per_block, self.m, self.n)
|
|
685
|
+
|
|
686
|
+
@property
|
|
687
|
+
def b_shape(self) -> tuple[int, int, int]:
|
|
688
|
+
return (self.batches_per_block, self.n, self.k)
|
|
689
|
+
|
|
690
|
+
def a_strides(self, *, lda: int | None = None) -> tuple[int, int, int]:
|
|
691
|
+
lda = self.lda if lda is None else lda
|
|
692
|
+
return _calculate_strides(self.a_shape[1:], lda, self.a_arrangement)
|
|
693
|
+
|
|
694
|
+
def b_strides(self, *, ldb: int | None = None) -> tuple[int, int, int]:
|
|
695
|
+
ldb = self.ldb if ldb is None else ldb
|
|
696
|
+
return _calculate_strides(self.b_shape[1:], ldb, self.b_arrangement)
|
|
697
|
+
|
|
698
|
+
def a_size(self, *, lda: int | None = None) -> int:
|
|
699
|
+
return self.a_strides(lda=lda)[0] * self.a_shape[0]
|
|
700
|
+
|
|
701
|
+
def b_size(self, *, ldb: int | None = None) -> int:
|
|
702
|
+
return self.b_strides(ldb=ldb)[0] * self.b_shape[0]
|
|
703
|
+
|
|
704
|
+
|
|
705
|
+
# ==========================
|
|
706
|
+
# Cholesky Solver
|
|
707
|
+
# ==========================
|
|
708
|
+
|
|
709
|
+
ADAPTERS_API_LD_DOCSTRING = """
|
|
710
|
+
Note: When provided in the constructor, leading dimensions are set at compile-time.
|
|
711
|
+
To use runtime leading dimensions (avoiding recompilation for different leading dimensions),
|
|
712
|
+
provide the leading dimension parameters directly to the device methods instead.
|
|
713
|
+
""".replace("\n", " ")
|
|
714
|
+
|
|
715
|
+
CHOLESKY_SOLVER_DOCSTRING = SOLVER_DOCSTRING.copy()
|
|
716
|
+
del CHOLESKY_SOLVER_DOCSTRING["function"]
|
|
717
|
+
del CHOLESKY_SOLVER_DOCSTRING["transpose_mode"]
|
|
718
|
+
del CHOLESKY_SOLVER_DOCSTRING["side"]
|
|
719
|
+
del CHOLESKY_SOLVER_DOCSTRING["diag"]
|
|
720
|
+
del CHOLESKY_SOLVER_DOCSTRING["job"]
|
|
721
|
+
|
|
722
|
+
CHOLESKY_SOLVER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
|
|
723
|
+
``M`` represents the dimension of the square matrix A (``M`` x ``M``) used in factorization, ``N`` must be equal to ``M``.
|
|
724
|
+
``K`` represents the number of columns in the right-hand side matrix B
|
|
725
|
+
(dimensions ``M`` x ``K``) for the solve operation.""".replace("\n", " ")
|
|
726
|
+
CHOLESKY_SOLVER_DOCSTRING["leading_dimensions"] = SOLVER_DOCSTRING["leading_dimensions"] + ADAPTERS_API_LD_DOCSTRING
|
|
727
|
+
CHOLESKY_SOLVER_DOCSTRING["fill_mode"] = f"""Indicates which part of matrix A is filled and should be used by function.
|
|
728
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_FILL_MODE)}.""".replace("\n", " ")
|
|
729
|
+
|
|
730
|
+
|
|
731
|
+
@docstring_decorator(CHOLESKY_SOLVER_DOCSTRING, skip_missing=False)
|
|
732
|
+
class CholeskySolver(_LinearSolverProperties):
|
|
733
|
+
"""
|
|
734
|
+
A class that encapsulates Cholesky factorization and solve device functions
|
|
735
|
+
for Hermitian positive definite matrices.
|
|
736
|
+
|
|
737
|
+
**Available operations:**
|
|
738
|
+
|
|
739
|
+
* factorize: Computes the Cholesky factorization
|
|
740
|
+
A = L @ L^H (lower) or A = U^H @ U (upper),
|
|
741
|
+
where L is a lower triangular matrix and U is an upper triangular matrix.
|
|
742
|
+
The choice depends on the fill_mode parameter.
|
|
743
|
+
* solve: Solves the system Ax = B using a previously computed Cholesky factorization
|
|
744
|
+
|
|
745
|
+
**Memory Layout Requirements:**
|
|
746
|
+
|
|
747
|
+
Matrices must be stored in shared memory according
|
|
748
|
+
to their arrangement and leading dimension (ld):
|
|
749
|
+
|
|
750
|
+
**For matrix A (M x N):**
|
|
751
|
+
|
|
752
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
753
|
+
with strides ``(lda * N, 1, lda)``
|
|
754
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
755
|
+
with strides ``(lda * M, lda, 1)``
|
|
756
|
+
|
|
757
|
+
**For matrix B (N x K):**
|
|
758
|
+
|
|
759
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, N, K)``
|
|
760
|
+
with strides ``(ldb * K, 1, ldb)``
|
|
761
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, N, K)``
|
|
762
|
+
with strides ``(ldb * N, ldb, 1)``
|
|
763
|
+
|
|
764
|
+
Args:
|
|
765
|
+
size (Sequence[int]): {size}
|
|
766
|
+
|
|
767
|
+
precision (type[np.floating]): {precision}
|
|
768
|
+
|
|
769
|
+
execution (str): {execution}
|
|
770
|
+
|
|
771
|
+
fill_mode (str): {fill_mode}
|
|
772
|
+
|
|
773
|
+
sm (ComputeCapability): {sm}
|
|
774
|
+
|
|
775
|
+
arrangement (Sequence[str], optional): {arrangement}
|
|
776
|
+
|
|
777
|
+
batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
|
|
778
|
+
|
|
779
|
+
data_type (str, optional): {data_type}
|
|
780
|
+
|
|
781
|
+
leading_dimensions (Sequence[int], optional): {leading_dimensions}
|
|
782
|
+
|
|
783
|
+
block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
|
|
784
|
+
|
|
785
|
+
See Also:
|
|
786
|
+
For further details, please refer to the cuSOLVERDx documentation:
|
|
787
|
+
|
|
788
|
+
* :cusolverdx_doc:`factorize <get_started/functions/potrf.html>`
|
|
789
|
+
* :cusolverdx_doc:`solve <get_started/functions/potrs.html>`
|
|
790
|
+
"""
|
|
791
|
+
|
|
792
|
+
# ==========================
|
|
793
|
+
# Constructor
|
|
794
|
+
# ==========================
|
|
795
|
+
|
|
796
|
+
def __init__(
|
|
797
|
+
self,
|
|
798
|
+
size: Sequence[int],
|
|
799
|
+
precision: type[np.floating],
|
|
800
|
+
execution: str,
|
|
801
|
+
fill_mode: str,
|
|
802
|
+
*,
|
|
803
|
+
sm=None,
|
|
804
|
+
arrangement: Sequence[str] | None = None,
|
|
805
|
+
batches_per_block: int | Literal["suggested"] | None = None,
|
|
806
|
+
data_type: str | None = None,
|
|
807
|
+
leading_dimensions: Sequence[int] | None = None,
|
|
808
|
+
block_dim: Sequence[int] | Literal["suggested"] | None = None,
|
|
809
|
+
):
|
|
810
|
+
if fill_mode is None:
|
|
811
|
+
raise ValueError("fill_mode must be provided for CholeskySolver")
|
|
812
|
+
|
|
813
|
+
def construct(function):
|
|
814
|
+
return Solver(
|
|
815
|
+
function=function,
|
|
816
|
+
size=size,
|
|
817
|
+
precision=precision,
|
|
818
|
+
execution=execution,
|
|
819
|
+
sm=sm,
|
|
820
|
+
arrangement=arrangement,
|
|
821
|
+
fill_mode=fill_mode,
|
|
822
|
+
batches_per_block=batches_per_block,
|
|
823
|
+
data_type=data_type,
|
|
824
|
+
leading_dimensions=leading_dimensions,
|
|
825
|
+
block_dim=block_dim,
|
|
826
|
+
)
|
|
827
|
+
|
|
828
|
+
self._factorize = construct("potrf")
|
|
829
|
+
self._solve = construct("potrs")
|
|
830
|
+
|
|
831
|
+
super().__init__(self._solve)
|
|
832
|
+
|
|
833
|
+
if self.m != self.n:
|
|
834
|
+
raise ValueError("A must be a square matrix for CholeskySolver.")
|
|
835
|
+
|
|
836
|
+
# ==========================
|
|
837
|
+
# Property methods
|
|
838
|
+
# ==========================
|
|
839
|
+
|
|
840
|
+
@property
|
|
841
|
+
def fill_mode(self) -> str:
|
|
842
|
+
return self._solve.fill_mode
|
|
843
|
+
|
|
844
|
+
# ==========================
|
|
845
|
+
# Device function methods
|
|
846
|
+
# ==========================
|
|
847
|
+
|
|
848
|
+
def factorize(self, a, info, lda=None) -> None:
|
|
849
|
+
"""
|
|
850
|
+
Computes the Cholesky factorization of a Hermitian positive definite matrix A.
|
|
851
|
+
|
|
852
|
+
This device function computes A = L @ L^H (if fill_mode = ``'lower'``)
|
|
853
|
+
or A = U^H @ U (if fill_mode = ``'upper'``). Uses cuSOLVERDx ``'potrf'``.
|
|
854
|
+
|
|
855
|
+
If ``lda`` is provided, uses runtime version with the specified
|
|
856
|
+
leading dimension. If ``lda`` is not provided (``None``),
|
|
857
|
+
uses compile-time version with
|
|
858
|
+
default or constructor-provided leading dimensions.
|
|
859
|
+
|
|
860
|
+
For more details, see: :cusolverdx_doc:`get_started/functions/potrf.html`
|
|
861
|
+
|
|
862
|
+
Args:
|
|
863
|
+
a: Pointer to an array in shared memory, storing
|
|
864
|
+
the matrix according to the specified
|
|
865
|
+
arrangement and leading dimension (see :class:`CholeskySolver`).
|
|
866
|
+
On entry, contains the Hermitian positive definite matrix.
|
|
867
|
+
On exit, contains the triangular factor L (lower) or U (upper).
|
|
868
|
+
info: Pointer to a 1D array of ``int32``. On exit, ``info[batch_id] = 0``
|
|
869
|
+
indicates success for that batch, ``info[batch_id] != 0``
|
|
870
|
+
indicates the matrix is not positive definite.
|
|
871
|
+
lda: Optional runtime leading dimension of matrix A.
|
|
872
|
+
If not specified, the compile-time ``lda`` is used.
|
|
873
|
+
"""
|
|
874
|
+
raise RuntimeError("factorize should not be called directly outside of a jitted kernel.")
|
|
875
|
+
|
|
876
|
+
def solve(self, a, b, lda=None, ldb=None) -> None:
|
|
877
|
+
"""
|
|
878
|
+
Solves a system of linear equations Ax = B using the Cholesky factorization.
|
|
879
|
+
|
|
880
|
+
This device function uses the previously computed
|
|
881
|
+
factorization A = L @ L^H (lower) or A = U^H @ U (upper)
|
|
882
|
+
to solve the system. Uses cuSOLVERDx ``'potrs'``.
|
|
883
|
+
|
|
884
|
+
If ``lda`` and ``ldb`` are provided, uses
|
|
885
|
+
runtime version with the specified leading dimensions.
|
|
886
|
+
If not provided (``None``), uses
|
|
887
|
+
compile-time version with default or constructor-provided leading dimensions.
|
|
888
|
+
|
|
889
|
+
For more details, see: :cusolverdx_doc:`get_started/functions/potrs.html`
|
|
890
|
+
|
|
891
|
+
Args:
|
|
892
|
+
a: Pointer to an array in shared memory, storing
|
|
893
|
+
the triangular factor L (lower) or U (upper)
|
|
894
|
+
from the Cholesky factorization, according
|
|
895
|
+
to the specified arrangement
|
|
896
|
+
and leading dimension (see :class:`CholeskySolver`).
|
|
897
|
+
b: Pointer to an array in shared memory, storing the
|
|
898
|
+
matrix according to the specified
|
|
899
|
+
arrangement and leading dimension (see :class:`CholeskySolver`).
|
|
900
|
+
The matrix is overwritten in place with the solution matrix x.
|
|
901
|
+
lda: Optional runtime leading dimension of matrix A.
|
|
902
|
+
The ``lda`` and ``ldb`` must be specified together.
|
|
903
|
+
If not specified, the compile-time ``lda`` is used.
|
|
904
|
+
ldb: Optional runtime leading dimension of matrix B.
|
|
905
|
+
The ``lda`` and ``ldb`` must be specified together.
|
|
906
|
+
If not specified, the compile-time ``ldb`` is used.
|
|
907
|
+
"""
|
|
908
|
+
raise RuntimeError("solve should not be called directly outside of a jitted kernel.")
|
|
909
|
+
|
|
910
|
+
|
|
911
|
+
# ==========================
|
|
912
|
+
# LU Solver
|
|
913
|
+
# ==========================
|
|
914
|
+
|
|
915
|
+
LU_SOLVER_DOCSTRING = SOLVER_DOCSTRING.copy()
|
|
916
|
+
del LU_SOLVER_DOCSTRING["function"]
|
|
917
|
+
del LU_SOLVER_DOCSTRING["fill_mode"]
|
|
918
|
+
del LU_SOLVER_DOCSTRING["side"]
|
|
919
|
+
del LU_SOLVER_DOCSTRING["diag"]
|
|
920
|
+
del LU_SOLVER_DOCSTRING["job"]
|
|
921
|
+
|
|
922
|
+
LU_SOLVER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
|
|
923
|
+
``M`` and ``N`` represent the dimensions of the matrix A used in factorization.
|
|
924
|
+
``K`` represents the number of columns in the right-hand side matrix B (dimensions ``N`` x ``K``) for the ``solve`` operation.
|
|
925
|
+
To use :meth:`solve`, ``N`` must be equal to ``M``,
|
|
926
|
+
otherwise an exception will be thrown when ``solver.solve()`` is used.""".replace("\n", " ")
|
|
927
|
+
LU_SOLVER_DOCSTRING["leading_dimensions"] = SOLVER_DOCSTRING["leading_dimensions"] + ADAPTERS_API_LD_DOCSTRING
|
|
928
|
+
LU_SOLVER_DOCSTRING["transpose_mode"] = f"""Transpose mode of matrix A for the solve operation.
|
|
929
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_TRANSPOSE_MODE)}.
|
|
930
|
+
Defaults to ``'non_transposed'``.""".replace("\n", " ")
|
|
931
|
+
|
|
932
|
+
|
|
933
|
+
@docstring_decorator(LU_SOLVER_DOCSTRING, skip_missing=False)
|
|
934
|
+
class LUSolver(_LinearSolverProperties):
|
|
935
|
+
"""
|
|
936
|
+
A class that encapsulates cuSOLVERDx LU factorization without pivoting
|
|
937
|
+
and linear solver for general matrices.
|
|
938
|
+
|
|
939
|
+
**Available operations:**
|
|
940
|
+
|
|
941
|
+
* factorize: Computes the LU factorization A = L @ U,
|
|
942
|
+
where L is a unit lower triangular matrix and U is an upper triangular matrix.
|
|
943
|
+
* solve: Solves the system Ax = B using a previously computed LU factorization.
|
|
944
|
+
|
|
945
|
+
**Memory Layout Requirements:**
|
|
946
|
+
|
|
947
|
+
Matrices must be stored in shared memory according
|
|
948
|
+
to their arrangement and leading dimension (ld):
|
|
949
|
+
|
|
950
|
+
**For matrix A (M x N):**
|
|
951
|
+
|
|
952
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
953
|
+
with strides ``(lda * N, 1, lda)``
|
|
954
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
955
|
+
with strides ``(lda * M, lda, 1)``
|
|
956
|
+
|
|
957
|
+
**For matrix B (N x K):**
|
|
958
|
+
|
|
959
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, N, K)``
|
|
960
|
+
with strides ``(ldb * K, 1, ldb)``
|
|
961
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, N, K)``
|
|
962
|
+
with strides ``(ldb * N, ldb, 1)``
|
|
963
|
+
|
|
964
|
+
.. note::
|
|
965
|
+
If a nonsingular matrix A is diagonally dominant, then it is safe to factorize
|
|
966
|
+
without pivoting. If a matrix is not diagonally dominant, then pivoting is usually
|
|
967
|
+
required to ensure numerical stability (see :class:`LUPivotSolver`).
|
|
968
|
+
|
|
969
|
+
Args:
|
|
970
|
+
size (Sequence[int]): {size}
|
|
971
|
+
|
|
972
|
+
precision (type[np.floating]): {precision}
|
|
973
|
+
|
|
974
|
+
execution (str): {execution}
|
|
975
|
+
|
|
976
|
+
sm (ComputeCapability): {sm}
|
|
977
|
+
|
|
978
|
+
transpose_mode (str, optional): {transpose_mode}
|
|
979
|
+
|
|
980
|
+
arrangement (Sequence[str], optional): {arrangement}
|
|
981
|
+
|
|
982
|
+
batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
|
|
983
|
+
|
|
984
|
+
data_type (str, optional): {data_type}
|
|
985
|
+
|
|
986
|
+
leading_dimensions (Sequence[int], optional): {leading_dimensions}
|
|
987
|
+
|
|
988
|
+
block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
|
|
989
|
+
|
|
990
|
+
See Also:
|
|
991
|
+
For further details, please refer to the cuSOLVERDx documentation:
|
|
992
|
+
|
|
993
|
+
* :cusolverdx_doc:`factorize <get_started/functions/getrf.html>`
|
|
994
|
+
* :cusolverdx_doc:`solve <get_started/functions/getrs.html>`
|
|
995
|
+
|
|
996
|
+
"""
|
|
997
|
+
|
|
998
|
+
# ==========================
|
|
999
|
+
# Constructor
|
|
1000
|
+
# ==========================
|
|
1001
|
+
|
|
1002
|
+
def __init__(
|
|
1003
|
+
self,
|
|
1004
|
+
size: Sequence[int],
|
|
1005
|
+
precision: type[np.floating],
|
|
1006
|
+
execution: str,
|
|
1007
|
+
*,
|
|
1008
|
+
sm=None,
|
|
1009
|
+
transpose_mode: str = "non_transposed",
|
|
1010
|
+
arrangement: Sequence[str] | None = None,
|
|
1011
|
+
batches_per_block: int | Literal["suggested"] | None = None,
|
|
1012
|
+
data_type: str | None = None,
|
|
1013
|
+
leading_dimensions: Sequence[int] | None = None,
|
|
1014
|
+
block_dim: Sequence[int] | Literal["suggested"] | None = None,
|
|
1015
|
+
):
|
|
1016
|
+
def construct(function, transpose_mode):
|
|
1017
|
+
return Solver(
|
|
1018
|
+
function=function,
|
|
1019
|
+
size=size,
|
|
1020
|
+
precision=precision,
|
|
1021
|
+
execution=execution,
|
|
1022
|
+
sm=sm,
|
|
1023
|
+
arrangement=arrangement,
|
|
1024
|
+
batches_per_block=batches_per_block,
|
|
1025
|
+
data_type=data_type,
|
|
1026
|
+
leading_dimensions=leading_dimensions,
|
|
1027
|
+
block_dim=block_dim,
|
|
1028
|
+
transpose_mode=transpose_mode,
|
|
1029
|
+
)
|
|
1030
|
+
|
|
1031
|
+
if transpose_mode is None:
|
|
1032
|
+
raise ValueError("transpose_mode must be provided for LUSolver")
|
|
1033
|
+
|
|
1034
|
+
self._factorize = construct("getrf_no_pivot", "non_transposed")
|
|
1035
|
+
self._transpose_mode = transpose_mode
|
|
1036
|
+
self._solve = construct("getrs_no_pivot", transpose_mode) if self._factorize.m == self._factorize.n else None
|
|
1037
|
+
|
|
1038
|
+
super().__init__(self._factorize)
|
|
1039
|
+
|
|
1040
|
+
# ==========================
|
|
1041
|
+
# Property methods
|
|
1042
|
+
# ==========================
|
|
1043
|
+
|
|
1044
|
+
@property
|
|
1045
|
+
def transpose_mode(self) -> str:
|
|
1046
|
+
return self._transpose_mode
|
|
1047
|
+
|
|
1048
|
+
# ==========================
|
|
1049
|
+
# Device function methods
|
|
1050
|
+
# ==========================
|
|
1051
|
+
|
|
1052
|
+
def factorize(self, a, info, lda=None) -> None:
|
|
1053
|
+
"""
|
|
1054
|
+
Computes the LU factorization of a general matrix A without pivoting.
|
|
1055
|
+
|
|
1056
|
+
This device function computes A = L @ U, where L is a unit lower triangular matrix
|
|
1057
|
+
and U is an upper triangular matrix. This variant is suitable for diagonally
|
|
1058
|
+
dominant matrices or when pivoting is not required.
|
|
1059
|
+
Uses cuSOLVERDx ``'getrf_no_pivot'``.
|
|
1060
|
+
|
|
1061
|
+
If ``lda`` is provided, uses runtime version with the specified leading dimension.
|
|
1062
|
+
If ``lda`` is not provided (``None``), uses compile-time version with default
|
|
1063
|
+
or constructor-provided leading dimensions.
|
|
1064
|
+
|
|
1065
|
+
.. note::
|
|
1066
|
+
The ``transpose_mode`` parameter does not affect factorization.
|
|
1067
|
+
This operation always treats the input matrix as-is (non-transposed).
|
|
1068
|
+
|
|
1069
|
+
For more details, see: :cusolverdx_doc:`get_started/functions/getrf.html`
|
|
1070
|
+
|
|
1071
|
+
Args:
|
|
1072
|
+
a: Pointer to an array in shared memory, storing the batched matrix according
|
|
1073
|
+
to the specified arrangement and leading dimension (see :class:`LUSolver`).
|
|
1074
|
+
The matrix is overwritten in place.
|
|
1075
|
+
On exit, contains the factors L and U from the factorization A = L @ U.
|
|
1076
|
+
The unit diagonal elements of L are not stored.
|
|
1077
|
+
info: Pointer to a 1D array of ``int32``.
|
|
1078
|
+
On exit, ``info[batch_id] = 0`` indicates success for that batch,
|
|
1079
|
+
``info[batch_id] = i > 0`` indicates ``U(i,i)`` is exactly zero,
|
|
1080
|
+
meaning the factorization has been completed but the factor U
|
|
1081
|
+
is singular and division by zero will occur if it is used to solve
|
|
1082
|
+
a system of equations.
|
|
1083
|
+
lda: Optional runtime leading dimension of matrix A.
|
|
1084
|
+
If not specified, the compile-time ``lda`` is used.
|
|
1085
|
+
"""
|
|
1086
|
+
raise RuntimeError("factorize should not be called directly outside of a jitted kernel.")
|
|
1087
|
+
|
|
1088
|
+
def solve(self, a, b, lda=None, ldb=None) -> None:
|
|
1089
|
+
"""
|
|
1090
|
+
Solves a system of linear equations Ax = B
|
|
1091
|
+
using the LU factorization without pivoting.
|
|
1092
|
+
The ``a`` operand must be a square matrix (``M == N``),
|
|
1093
|
+
otherwise this function will throw an exception.
|
|
1094
|
+
|
|
1095
|
+
This device function uses the previously computed factorization A = L @ U
|
|
1096
|
+
to solve the system. Uses cuSOLVERDx ``'getrs_no_pivot'``.
|
|
1097
|
+
|
|
1098
|
+
If ``lda`` and ``ldb`` are provided, uses runtime version with
|
|
1099
|
+
the specified leading dimensions. If not provided (``None``),
|
|
1100
|
+
uses compile-time version with default or constructor-provided
|
|
1101
|
+
leading dimensions.
|
|
1102
|
+
|
|
1103
|
+
.. note::
|
|
1104
|
+
The ``transpose_mode`` parameter (set in constructor) determines which
|
|
1105
|
+
system is solved: A*x=B (``'non_transposed'``), A^T*x=B (``'transposed'``),
|
|
1106
|
+
or A^H*x=B (``'conj_transposed'`` for complex matrices).
|
|
1107
|
+
|
|
1108
|
+
For more details, see: :cusolverdx_doc:`get_started/functions/getrs.html`
|
|
1109
|
+
|
|
1110
|
+
Args:
|
|
1111
|
+
a: Pointer to an array in shared memory, storing the batched factors L and U
|
|
1112
|
+
from the LU factorization, according to the specified arrangement
|
|
1113
|
+
and leading dimension (see :class:`LUSolver`).
|
|
1114
|
+
The unit diagonal elements of L are not stored.
|
|
1115
|
+
See the :meth:`factorize` documentation for details.
|
|
1116
|
+
b: Pointer to an array in shared memory, storing the batched matrix according
|
|
1117
|
+
to the specified arrangement and leading dimension (see :class:`LUSolver`).
|
|
1118
|
+
The matrix is overwritten in place with the solution matrix x.
|
|
1119
|
+
lda: Optional runtime leading dimension of matrix A.
|
|
1120
|
+
The ``lda`` and ``ldb`` must be specified together.
|
|
1121
|
+
If not specified, the compile-time ``lda`` is used.
|
|
1122
|
+
ldb: Optional runtime leading dimension of matrix B.
|
|
1123
|
+
The ``lda`` and ``ldb`` must be specified together.
|
|
1124
|
+
If not specified, the compile-time ``ldb`` is used.
|
|
1125
|
+
"""
|
|
1126
|
+
raise RuntimeError("solve should not be called directly outside of a jitted kernel.")
|
|
1127
|
+
|
|
1128
|
+
|
|
1129
|
+
# ==========================
|
|
1130
|
+
# Triangular Solver
|
|
1131
|
+
# ==========================
|
|
1132
|
+
|
|
1133
|
+
TRIANGULAR_SOLVER_DOCSTRING = SOLVER_DOCSTRING.copy()
|
|
1134
|
+
del TRIANGULAR_SOLVER_DOCSTRING["function"]
|
|
1135
|
+
del TRIANGULAR_SOLVER_DOCSTRING["job"]
|
|
1136
|
+
|
|
1137
|
+
TRIANGULAR_SOLVER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
|
|
1138
|
+
``M`` and ``N`` represent the dimensions of matrices A and B.
|
|
1139
|
+
When ``side='left'``, A is ``M`` x ``M``, otherwise when ``side='right'``, A is ``N`` x ``N``.
|
|
1140
|
+
B is always ``M`` x ``N``.""".replace("\n", " ")
|
|
1141
|
+
|
|
1142
|
+
TRIANGULAR_SOLVER_DOCSTRING["side"] = f"""Side of matrix A in the triangular solve operation (required for TRSM).
|
|
1143
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_SIDE)}.
|
|
1144
|
+
If ``side='left'``, solves op(A) * X = B where A is ``M`` x ``M``.
|
|
1145
|
+
If ``side='right'``, solves X * op(A) = B where A is ``N`` x ``N``.""".replace("\n", " ")
|
|
1146
|
+
|
|
1147
|
+
TRIANGULAR_SOLVER_DOCSTRING["fill_mode"] = f"""Indicates which part of triangular matrix A is filled and should be used.
|
|
1148
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_FILL_MODE)}.
|
|
1149
|
+
For lower fill mode, only the diagonal and lower triangular part of A is processed, the upper part is untouched.
|
|
1150
|
+
For upper fill mode, only the diagonal and upper triangular part of A is processed, the lower part is untouched.""".replace(
|
|
1151
|
+
"\n", " "
|
|
1152
|
+
)
|
|
1153
|
+
|
|
1154
|
+
TRIANGULAR_SOLVER_DOCSTRING["diag"] = f"""Indicates whether the diagonal elements of matrix A are unity or not.
|
|
1155
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_DIAG)}.
|
|
1156
|
+
For unit diagonal mode, the diagonal elements of A are unity and are not accessed.
|
|
1157
|
+
For non-unit diagonal mode, the diagonal elements of A are used in the computation.""".replace("\n", " ")
|
|
1158
|
+
|
|
1159
|
+
TRIANGULAR_SOLVER_DOCSTRING["transpose_mode"] = f"""Transpose mode for operation op(A) applied to matrix A.
|
|
1160
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_TRANSPOSE_MODE)}.
|
|
1161
|
+
Defaults to ``'non_transposed'``.""".replace("\n", " ")
|
|
1162
|
+
|
|
1163
|
+
TRIANGULAR_SOLVER_DOCSTRING["leading_dimensions"] = SOLVER_DOCSTRING["leading_dimensions"] + ADAPTERS_API_LD_DOCSTRING
|
|
1164
|
+
|
|
1165
|
+
|
|
1166
|
+
@docstring_decorator(TRIANGULAR_SOLVER_DOCSTRING, skip_missing=False)
|
|
1167
|
+
class TriangularSolver(_SolverProperties):
|
|
1168
|
+
"""
|
|
1169
|
+
A class that encapsulates triangular matrix-matrix solve device function (``'trsm'``).
|
|
1170
|
+
|
|
1171
|
+
TRSM (TRiangular Solve for Matrix) solves a triangular linear system
|
|
1172
|
+
with multiple right-hand sides:
|
|
1173
|
+
|
|
1174
|
+
* op(A) * X = B (if ``side='left'``)
|
|
1175
|
+
* X * op(A) = B (if ``side='right'``)
|
|
1176
|
+
|
|
1177
|
+
where:
|
|
1178
|
+
|
|
1179
|
+
* A is the input batched triangular matrix stored in lower or upper mode
|
|
1180
|
+
* B is the batched right-hand side matrix, overwritten by the result X on exit
|
|
1181
|
+
* Operation op(A) indicates if matrix A is ``'non_transposed'``,
|
|
1182
|
+
``'transposed'`` (for real data type),
|
|
1183
|
+
or ``'conj_transposed'`` (for complex data type)
|
|
1184
|
+
|
|
1185
|
+
**Memory Layout Requirements:**
|
|
1186
|
+
|
|
1187
|
+
Matrices must be stored in shared memory according
|
|
1188
|
+
to their arrangement and leading dimension (ld):
|
|
1189
|
+
|
|
1190
|
+
**For matrix A (M x M) with ``side='left'``:**
|
|
1191
|
+
|
|
1192
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, M, M)``
|
|
1193
|
+
with strides ``(lda * M, 1, lda)``
|
|
1194
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, M, M)``
|
|
1195
|
+
with strides ``(lda * M, lda, 1)``
|
|
1196
|
+
|
|
1197
|
+
**For matrix A (N x N) with ``side='right'``:**
|
|
1198
|
+
|
|
1199
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, N, N)``
|
|
1200
|
+
with strides ``(lda * N, 1, lda)``
|
|
1201
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, N, N)``
|
|
1202
|
+
with strides ``(lda * N, lda, 1)``
|
|
1203
|
+
|
|
1204
|
+
**For matrix B (M x N):**
|
|
1205
|
+
|
|
1206
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
1207
|
+
with strides ``(ldb * N, 1, ldb)``
|
|
1208
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
1209
|
+
with strides ``(ldb * M, ldb, 1)``
|
|
1210
|
+
|
|
1211
|
+
.. note::
|
|
1212
|
+
The TRSM function is temporarily exposed in cuSOLVERDx library
|
|
1213
|
+
and will be moved to cuBLASDx library in a future release.
|
|
1214
|
+
|
|
1215
|
+
Args:
|
|
1216
|
+
size (Sequence[int]): {size}
|
|
1217
|
+
|
|
1218
|
+
precision (type[np.floating]): {precision}
|
|
1219
|
+
|
|
1220
|
+
execution (str): {execution}
|
|
1221
|
+
|
|
1222
|
+
side (str): {side}
|
|
1223
|
+
|
|
1224
|
+
fill_mode (str): {fill_mode}
|
|
1225
|
+
|
|
1226
|
+
diag (str): {diag}
|
|
1227
|
+
|
|
1228
|
+
transpose_mode (str, optional): {transpose_mode}
|
|
1229
|
+
|
|
1230
|
+
sm (ComputeCapability): {sm}
|
|
1231
|
+
|
|
1232
|
+
arrangement (Sequence[str], optional): {arrangement}
|
|
1233
|
+
|
|
1234
|
+
batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
|
|
1235
|
+
|
|
1236
|
+
data_type (str, optional): {data_type}
|
|
1237
|
+
|
|
1238
|
+
leading_dimensions (Sequence[int], optional): {leading_dimensions}
|
|
1239
|
+
|
|
1240
|
+
block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
|
|
1241
|
+
|
|
1242
|
+
See Also:
|
|
1243
|
+
For further details, please refer to the cuSOLVERDx documentation:
|
|
1244
|
+
|
|
1245
|
+
* :cusolverdx_doc:`trsm <get_started/functions/trsm.html>`
|
|
1246
|
+
"""
|
|
1247
|
+
|
|
1248
|
+
# ==========================
|
|
1249
|
+
# Constructor
|
|
1250
|
+
# ==========================
|
|
1251
|
+
|
|
1252
|
+
def __init__(
|
|
1253
|
+
self,
|
|
1254
|
+
size: Sequence[int],
|
|
1255
|
+
precision: type[np.floating],
|
|
1256
|
+
execution: str,
|
|
1257
|
+
side: str,
|
|
1258
|
+
fill_mode: str,
|
|
1259
|
+
diag: str,
|
|
1260
|
+
transpose_mode: str = "non_transposed",
|
|
1261
|
+
*,
|
|
1262
|
+
sm=None,
|
|
1263
|
+
arrangement: Sequence[str] | None = None,
|
|
1264
|
+
batches_per_block: int | Literal["suggested"] | None = None,
|
|
1265
|
+
data_type: str | None = None,
|
|
1266
|
+
leading_dimensions: Sequence[int] | None = None,
|
|
1267
|
+
block_dim: Sequence[int] | Literal["suggested"] | None = None,
|
|
1268
|
+
):
|
|
1269
|
+
self._solve: Solver = Solver(
|
|
1270
|
+
function="trsm",
|
|
1271
|
+
size=size,
|
|
1272
|
+
precision=precision,
|
|
1273
|
+
execution=execution,
|
|
1274
|
+
sm=sm,
|
|
1275
|
+
side=side,
|
|
1276
|
+
fill_mode=fill_mode,
|
|
1277
|
+
diag=diag,
|
|
1278
|
+
transpose_mode=transpose_mode,
|
|
1279
|
+
arrangement=arrangement,
|
|
1280
|
+
batches_per_block=batches_per_block,
|
|
1281
|
+
data_type=data_type,
|
|
1282
|
+
leading_dimensions=leading_dimensions,
|
|
1283
|
+
block_dim=block_dim,
|
|
1284
|
+
)
|
|
1285
|
+
|
|
1286
|
+
super().__init__(self._solve)
|
|
1287
|
+
|
|
1288
|
+
if not isinstance(self._solve.side, str):
|
|
1289
|
+
raise ValueError("side must be provided for TriangularSolver")
|
|
1290
|
+
|
|
1291
|
+
if not isinstance(self._solve.fill_mode, str):
|
|
1292
|
+
raise ValueError("fill_mode must be provided for TriangularSolver")
|
|
1293
|
+
|
|
1294
|
+
if not isinstance(self._solve.diag, str):
|
|
1295
|
+
raise ValueError("diag must be provided for TriangularSolver")
|
|
1296
|
+
|
|
1297
|
+
# ==========================
|
|
1298
|
+
# Property methods
|
|
1299
|
+
# ==========================
|
|
1300
|
+
|
|
1301
|
+
@property
|
|
1302
|
+
def side(self) -> str:
|
|
1303
|
+
assert self._solve.side is not None
|
|
1304
|
+
return self._solve.side
|
|
1305
|
+
|
|
1306
|
+
@property
|
|
1307
|
+
def fill_mode(self) -> str:
|
|
1308
|
+
assert self._solve.fill_mode is not None
|
|
1309
|
+
return self._solve.fill_mode
|
|
1310
|
+
|
|
1311
|
+
@property
|
|
1312
|
+
def diag(self) -> str:
|
|
1313
|
+
assert self._solve.diag is not None
|
|
1314
|
+
return self._solve.diag
|
|
1315
|
+
|
|
1316
|
+
@property
|
|
1317
|
+
def transpose_mode(self) -> str:
|
|
1318
|
+
assert self._solve.transpose_mode is not None
|
|
1319
|
+
return self._solve.transpose_mode
|
|
1320
|
+
|
|
1321
|
+
@property
|
|
1322
|
+
def a_shape(self) -> tuple[int, int, int]:
|
|
1323
|
+
dim = self.m if self.side == "left" else self.n
|
|
1324
|
+
return (self.batches_per_block, dim, dim)
|
|
1325
|
+
|
|
1326
|
+
@property
|
|
1327
|
+
def b_shape(self) -> tuple[int, int, int]:
|
|
1328
|
+
return (self.batches_per_block, self.m, self.n)
|
|
1329
|
+
|
|
1330
|
+
def a_strides(self, *, lda: int | None = None) -> tuple[int, int, int]:
|
|
1331
|
+
lda = self.lda if lda is None else lda
|
|
1332
|
+
return _calculate_strides(self.a_shape[1:], lda, self.a_arrangement)
|
|
1333
|
+
|
|
1334
|
+
def b_strides(self, *, ldb: int | None = None) -> tuple[int, int, int]:
|
|
1335
|
+
ldb = self.ldb if ldb is None else ldb
|
|
1336
|
+
return _calculate_strides(self.b_shape[1:], ldb, self.b_arrangement)
|
|
1337
|
+
|
|
1338
|
+
def a_size(self, *, lda: int | None = None) -> int:
|
|
1339
|
+
return self.a_strides(lda=lda)[0] * self.a_shape[0]
|
|
1340
|
+
|
|
1341
|
+
def b_size(self, *, ldb: int | None = None) -> int:
|
|
1342
|
+
return self.b_strides(ldb=ldb)[0] * self.b_shape[0]
|
|
1343
|
+
|
|
1344
|
+
# ==========================
|
|
1345
|
+
# Device function methods
|
|
1346
|
+
# ==========================
|
|
1347
|
+
|
|
1348
|
+
def solve(self, a, b, lda=None, ldb=None) -> None:
|
|
1349
|
+
"""
|
|
1350
|
+
Solves a triangular linear system with multiple right-hand sides:
|
|
1351
|
+
``op(A) * X = B`` (if ``side='left'``)
|
|
1352
|
+
``X * op(A) = B`` (if ``side='right'``)
|
|
1353
|
+
|
|
1354
|
+
This device function solves a triangular system where A is a triangular matrix.
|
|
1355
|
+
Uses cuSOLVERDx ``'trsm'``. The operation is in-place: result X overwrites B.
|
|
1356
|
+
|
|
1357
|
+
If ``lda`` and ``ldb`` are provided, uses runtime version with the
|
|
1358
|
+
specified leading dimensions. If not provided (``None``), uses compile-time
|
|
1359
|
+
version with default or constructor-provided leading dimensions.
|
|
1360
|
+
|
|
1361
|
+
For more details, see: :cusolverdx_doc:`get_started/functions/trsm.html`
|
|
1362
|
+
|
|
1363
|
+
Args:
|
|
1364
|
+
a: Pointer to an array in shared memory, storing the batched triangular matrix
|
|
1365
|
+
according to the specified arrangement
|
|
1366
|
+
and leading dimension (see :class:`TriangularSolver`).
|
|
1367
|
+
The ``fill_mode`` parameter denotes which
|
|
1368
|
+
part of the matrix is used (the other part is ignored).
|
|
1369
|
+
For unit diagonal mode (``diag='unit'``),
|
|
1370
|
+
diagonal elements are unity and not accessed.
|
|
1371
|
+
b: Pointer to an array in shared memory,
|
|
1372
|
+
storing the batched ``M`` x ``N`` right-hand side
|
|
1373
|
+
matrix according to the specified arrangement
|
|
1374
|
+
and leading dimension (see :class:`TriangularSolver`).
|
|
1375
|
+
The operation is in-place: result X overwrites B.
|
|
1376
|
+
lda: Optional runtime leading dimension for matrix A.
|
|
1377
|
+
The ``lda`` and ``ldb`` must be specified together.
|
|
1378
|
+
If not specified, the compile-time ``lda`` is used.
|
|
1379
|
+
ldb: Optional runtime leading dimension for matrix B.
|
|
1380
|
+
The ``lda`` and ``ldb`` must be specified together.
|
|
1381
|
+
If not specified, the compile-time ``ldb`` is used.
|
|
1382
|
+
"""
|
|
1383
|
+
raise RuntimeError("solve should not be called directly outside of a jitted kernel.")
|
|
1384
|
+
|
|
1385
|
+
|
|
1386
|
+
# ==========================
|
|
1387
|
+
# LU Pivot Solver
|
|
1388
|
+
# ==========================
|
|
1389
|
+
|
|
1390
|
+
|
|
1391
|
+
@docstring_decorator(LU_SOLVER_DOCSTRING, skip_missing=False)
|
|
1392
|
+
class LUPivotSolver(_LinearSolverProperties):
|
|
1393
|
+
"""
|
|
1394
|
+
A class that encapsulates cuSOLVERDx LU factorization with partial pivoting
|
|
1395
|
+
and linear solver for general matrices.
|
|
1396
|
+
|
|
1397
|
+
**Available operations:**
|
|
1398
|
+
|
|
1399
|
+
* factorize: Computes the LU factorization P @ A = L @ U with partial pivoting,
|
|
1400
|
+
where P is a permutation matrix, L is a
|
|
1401
|
+
unit lower triangular matrix and U is an upper triangular matrix.
|
|
1402
|
+
* solve: Solves the system Ax = B using a previously
|
|
1403
|
+
computed LU factorization with partial pivoting
|
|
1404
|
+
|
|
1405
|
+
**Memory Layout Requirements:**
|
|
1406
|
+
|
|
1407
|
+
Matrices must be stored in shared memory according
|
|
1408
|
+
to their arrangement and leading dimension (ld):
|
|
1409
|
+
|
|
1410
|
+
**For matrix A (M x N):**
|
|
1411
|
+
|
|
1412
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
1413
|
+
with strides ``(lda * N, 1, lda)``
|
|
1414
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
1415
|
+
with strides ``(lda * M, lda, 1)``
|
|
1416
|
+
|
|
1417
|
+
**For matrix B (N x K):**
|
|
1418
|
+
|
|
1419
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, N, K)``
|
|
1420
|
+
with strides ``(ldb * K, 1, ldb)``
|
|
1421
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, N, K)``
|
|
1422
|
+
with strides ``(ldb * N, ldb, 1)``
|
|
1423
|
+
|
|
1424
|
+
.. note::
|
|
1425
|
+
This solver uses partial pivoting for improved numerical stability and is
|
|
1426
|
+
suitable for general matrices. If your matrix is diagonally dominant,
|
|
1427
|
+
you may consider using :class:`LUSolver` which does not use pivoting
|
|
1428
|
+
and may be faster.
|
|
1429
|
+
|
|
1430
|
+
Args:
|
|
1431
|
+
size (Sequence[int]): {size}
|
|
1432
|
+
|
|
1433
|
+
precision (type[np.floating]): {precision}
|
|
1434
|
+
|
|
1435
|
+
execution (str): {execution}
|
|
1436
|
+
|
|
1437
|
+
sm (ComputeCapability): {sm}
|
|
1438
|
+
|
|
1439
|
+
transpose_mode (str, optional): {transpose_mode}
|
|
1440
|
+
|
|
1441
|
+
arrangement (Sequence[str], optional): {arrangement}
|
|
1442
|
+
|
|
1443
|
+
batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
|
|
1444
|
+
|
|
1445
|
+
data_type (str, optional): {data_type}
|
|
1446
|
+
|
|
1447
|
+
leading_dimensions (Sequence[int], optional): {leading_dimensions}
|
|
1448
|
+
|
|
1449
|
+
block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
|
|
1450
|
+
|
|
1451
|
+
See Also:
|
|
1452
|
+
For further details, please refer to the cuSOLVERDx documentation:
|
|
1453
|
+
|
|
1454
|
+
* :cusolverdx_doc:`factorize <get_started/functions/getrf.html>`
|
|
1455
|
+
* :cusolverdx_doc:`solve <get_started/functions/getrs.html>`
|
|
1456
|
+
|
|
1457
|
+
"""
|
|
1458
|
+
|
|
1459
|
+
# ==========================
|
|
1460
|
+
# Constructor
|
|
1461
|
+
# ==========================
|
|
1462
|
+
|
|
1463
|
+
def __init__(
|
|
1464
|
+
self,
|
|
1465
|
+
size: Sequence[int],
|
|
1466
|
+
precision: type[np.floating],
|
|
1467
|
+
execution: str,
|
|
1468
|
+
*,
|
|
1469
|
+
sm=None,
|
|
1470
|
+
transpose_mode: str = "non_transposed",
|
|
1471
|
+
arrangement: Sequence[str] | None = None,
|
|
1472
|
+
batches_per_block: int | Literal["suggested"] | None = None,
|
|
1473
|
+
data_type: str | None = None,
|
|
1474
|
+
leading_dimensions: Sequence[int] | None = None,
|
|
1475
|
+
block_dim: Sequence[int] | Literal["suggested"] | None = None,
|
|
1476
|
+
):
|
|
1477
|
+
def construct(function, transpose_mode):
|
|
1478
|
+
return Solver(
|
|
1479
|
+
function=function,
|
|
1480
|
+
size=size,
|
|
1481
|
+
precision=precision,
|
|
1482
|
+
execution=execution,
|
|
1483
|
+
sm=sm,
|
|
1484
|
+
arrangement=arrangement,
|
|
1485
|
+
batches_per_block=batches_per_block,
|
|
1486
|
+
data_type=data_type,
|
|
1487
|
+
leading_dimensions=leading_dimensions,
|
|
1488
|
+
block_dim=block_dim,
|
|
1489
|
+
transpose_mode=transpose_mode,
|
|
1490
|
+
)
|
|
1491
|
+
|
|
1492
|
+
if transpose_mode is None:
|
|
1493
|
+
raise ValueError("transpose_mode must be provided for LUPivotSolver")
|
|
1494
|
+
|
|
1495
|
+
self._factorize = construct("getrf_partial_pivot", "non_transposed")
|
|
1496
|
+
self._solve = construct("getrs_partial_pivot", transpose_mode) if self._factorize.m == self._factorize.n else None
|
|
1497
|
+
self._transpose_mode = transpose_mode
|
|
1498
|
+
|
|
1499
|
+
super().__init__(self._factorize)
|
|
1500
|
+
|
|
1501
|
+
# ==========================
|
|
1502
|
+
# Property methods
|
|
1503
|
+
# ==========================
|
|
1504
|
+
|
|
1505
|
+
@property
|
|
1506
|
+
def transpose_mode(self) -> str:
|
|
1507
|
+
return self._transpose_mode
|
|
1508
|
+
|
|
1509
|
+
@property
|
|
1510
|
+
def ipiv_type(self) -> type[np.signedinteger]:
|
|
1511
|
+
return self._factorize.ipiv_type
|
|
1512
|
+
|
|
1513
|
+
@property
|
|
1514
|
+
def ipiv_shape(self) -> tuple[int, int]:
|
|
1515
|
+
return (self.batches_per_block, min(self.m, self.n))
|
|
1516
|
+
|
|
1517
|
+
@property
|
|
1518
|
+
def ipiv_strides(self) -> tuple[int, int]:
|
|
1519
|
+
return (self.ipiv_shape[1], 1)
|
|
1520
|
+
|
|
1521
|
+
@property
|
|
1522
|
+
def ipiv_size(self) -> int:
|
|
1523
|
+
return self.ipiv_shape[0] * self.ipiv_shape[1]
|
|
1524
|
+
|
|
1525
|
+
# ==========================
|
|
1526
|
+
# Device function methods
|
|
1527
|
+
# ==========================
|
|
1528
|
+
|
|
1529
|
+
def factorize(self, a, ipiv, info, lda=None) -> None:
|
|
1530
|
+
"""
|
|
1531
|
+
Computes the LU factorization of a general matrix A with partial pivoting.
|
|
1532
|
+
|
|
1533
|
+
This device function computes P @ A = L @ U, where P is a permutation matrix,
|
|
1534
|
+
L is a unit lower triangular matrix and U is an upper triangular matrix.
|
|
1535
|
+
This variant uses partial pivoting for improved numerical stability
|
|
1536
|
+
and is suitable for general matrices. Uses cuSOLVERDx ``'getrf_partial_pivot'``.
|
|
1537
|
+
|
|
1538
|
+
If ``lda`` is provided, uses runtime version with the specified leading dimension.
|
|
1539
|
+
If ``lda`` is not provided (``None``), uses compile-time version with default
|
|
1540
|
+
or constructor-provided leading dimensions.
|
|
1541
|
+
|
|
1542
|
+
.. note::
|
|
1543
|
+
The ``transpose_mode`` parameter does not affect factorization.
|
|
1544
|
+
This operation always treats the input matrix as-is (non-transposed).
|
|
1545
|
+
|
|
1546
|
+
For more details, see: :cusolverdx_doc:`get_started/functions/getrf.html`
|
|
1547
|
+
|
|
1548
|
+
Args:
|
|
1549
|
+
a: Pointer to an array in shared memory, storing the batched matrix according
|
|
1550
|
+
to the specified arrangement
|
|
1551
|
+
and leading dimension (see :class:`LUPivotSolver`).
|
|
1552
|
+
The matrix is overwritten in place.
|
|
1553
|
+
On exit, contains the factors L and U from the factorization P @ A = L @ U.
|
|
1554
|
+
The unit diagonal elements of L are not stored.
|
|
1555
|
+
ipiv: Pointer to a 1D array of ``int32``, storing pivot indices.
|
|
1556
|
+
The array has size min(M, N) for each batch.
|
|
1557
|
+
On exit, ``ipiv[batch_id * min(M, N) + i]`` indicates that row i
|
|
1558
|
+
was interchanged with row ``ipiv[batch_id * min(M, N) + i] - 1``
|
|
1559
|
+
in the batch_id-th batch of A.
|
|
1560
|
+
info: Pointer to a 1D array of ``int32``. On exit, ``info[batch_id] = 0``
|
|
1561
|
+
indicates success for that batch, ``info[batch_id] = i > 0``
|
|
1562
|
+
indicates ``U(i,i)`` is exactly zero,
|
|
1563
|
+
meaning the factorization has been completed but the factor U
|
|
1564
|
+
is singular and division by zero will occur if it is used to solve
|
|
1565
|
+
a system of equations.
|
|
1566
|
+
lda: Optional runtime leading dimension of matrix A.
|
|
1567
|
+
If not specified, the compile-time ``lda`` is used.
|
|
1568
|
+
"""
|
|
1569
|
+
raise RuntimeError("factorize should not be called directly outside of a jitted kernel.")
|
|
1570
|
+
|
|
1571
|
+
def solve(self, a, ipiv, b, lda=None, ldb=None) -> None:
|
|
1572
|
+
"""
|
|
1573
|
+
Solves a system of linear equations Ax = B
|
|
1574
|
+
using the LU factorization with partial pivoting.
|
|
1575
|
+
The ``a`` operand must be a square matrix (``M == N``),
|
|
1576
|
+
otherwise this function will throw an exception.
|
|
1577
|
+
|
|
1578
|
+
This device function uses the previously computed factorization P @ A = L @ U
|
|
1579
|
+
to solve the system. Uses cuSOLVERDx ``'getrs_partial_pivot'``.
|
|
1580
|
+
|
|
1581
|
+
If ``lda`` and ``ldb`` are provided, uses runtime version with
|
|
1582
|
+
the specified leading dimensions. If not provided (``None``),
|
|
1583
|
+
uses compile-time version with default or constructor-provided
|
|
1584
|
+
leading dimensions.
|
|
1585
|
+
|
|
1586
|
+
.. note::
|
|
1587
|
+
The ``transpose_mode`` parameter (set in constructor) determines which
|
|
1588
|
+
system is solved: A*x=B (``'non_transposed'``), A^T*x=B (``'transposed'``),
|
|
1589
|
+
or A^H*x=B (``'conj_transposed'`` for complex matrices).
|
|
1590
|
+
|
|
1591
|
+
For more details, see: :cusolverdx_doc:`get_started/functions/getrs.html`
|
|
1592
|
+
|
|
1593
|
+
Args:
|
|
1594
|
+
a: Pointer to an array in shared memory, storing the batched factors L and U
|
|
1595
|
+
from the LU factorization with partial pivoting, according
|
|
1596
|
+
to the specified arrangement
|
|
1597
|
+
and leading dimension (see :class:`LUPivotSolver`).
|
|
1598
|
+
The unit diagonal elements of L are not stored.
|
|
1599
|
+
See the :meth:`factorize` documentation for details.
|
|
1600
|
+
ipiv: Pointer to a 1D array of ``int32`` in shared or global memory
|
|
1601
|
+
storing pivot indices.
|
|
1602
|
+
The array has size min(M, N) for each batch. The ipiv array should contain
|
|
1603
|
+
the pivot information from the :meth:`factorize` call.
|
|
1604
|
+
``ipiv[batch_id * min(M, N) + i]`` indicates that row i was interchanged
|
|
1605
|
+
with row ``ipiv[batch_id * min(M, N) + i] - 1``
|
|
1606
|
+
in the batch_id-th batch of A.
|
|
1607
|
+
b: Pointer to an array in shared memory, storing the batched matrix according
|
|
1608
|
+
to the specified arrangement
|
|
1609
|
+
and leading dimension (see :class:`LUPivotSolver`).
|
|
1610
|
+
The matrix is overwritten in place with the solution matrix x.
|
|
1611
|
+
lda: Optional runtime leading dimension of matrix A.
|
|
1612
|
+
The ``lda`` and ``ldb`` must be specified together.
|
|
1613
|
+
If not specified, the compile-time ``lda`` is used.
|
|
1614
|
+
ldb: Optional runtime leading dimension of matrix B.
|
|
1615
|
+
The ``lda`` and ``ldb`` must be specified together.
|
|
1616
|
+
If not specified, the compile-time ``ldb`` is used.
|
|
1617
|
+
"""
|
|
1618
|
+
raise RuntimeError("solve should not be called directly outside of a jitted kernel.")
|
|
1619
|
+
|
|
1620
|
+
|
|
1621
|
+
# ==========================
|
|
1622
|
+
# QR/LQ Factorizers Base
|
|
1623
|
+
# ==========================
|
|
1624
|
+
|
|
1625
|
+
ORTOGHONAL_FACTORIZER_DOCSTRING = SOLVER_DOCSTRING.copy()
|
|
1626
|
+
del ORTOGHONAL_FACTORIZER_DOCSTRING["function"]
|
|
1627
|
+
del ORTOGHONAL_FACTORIZER_DOCSTRING["fill_mode"]
|
|
1628
|
+
del ORTOGHONAL_FACTORIZER_DOCSTRING["side"]
|
|
1629
|
+
del ORTOGHONAL_FACTORIZER_DOCSTRING["diag"]
|
|
1630
|
+
del ORTOGHONAL_FACTORIZER_DOCSTRING["transpose_mode"]
|
|
1631
|
+
del ORTOGHONAL_FACTORIZER_DOCSTRING["job"]
|
|
1632
|
+
|
|
1633
|
+
ORTOGHONAL_FACTORIZER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
|
|
1634
|
+
``M`` and ``N`` represent the dimensions of the matrix A used in factorization.
|
|
1635
|
+
``K`` is ignored if specified.""".replace("\n", " ")
|
|
1636
|
+
|
|
1637
|
+
ORTOGHONAL_FACTORIZER_DOCSTRING["arrangement"] = """Storage layout for matrix A.
|
|
1638
|
+
Can be one of: ``'col_major'``, ``'row_major'``. Defaults to ``'col_major'``.""".replace("\n", " ")
|
|
1639
|
+
|
|
1640
|
+
ORTOGHONAL_FACTORIZER_DOCSTRING["leading_dimension"] = (
|
|
1641
|
+
"""The leading dimension for input matrix A, or ``None``.
|
|
1642
|
+
If not provided, it will be automatically deduced from ``size`` and ``arrangement``.""".replace("\n", " ")
|
|
1643
|
+
+ " "
|
|
1644
|
+
+ ADAPTERS_API_LD_DOCSTRING
|
|
1645
|
+
)
|
|
1646
|
+
|
|
1647
|
+
|
|
1648
|
+
class _OrthogonalFactorizerProperties:
|
|
1649
|
+
def __init__(self, source: Solver):
|
|
1650
|
+
self._properties_source = source
|
|
1651
|
+
|
|
1652
|
+
@property
|
|
1653
|
+
def size(self) -> tuple[int, int, int]:
|
|
1654
|
+
return self._properties_source.size
|
|
1655
|
+
|
|
1656
|
+
@property
|
|
1657
|
+
def m(self) -> int:
|
|
1658
|
+
return self._properties_source.m
|
|
1659
|
+
|
|
1660
|
+
@property
|
|
1661
|
+
def n(self) -> int:
|
|
1662
|
+
return self._properties_source.n
|
|
1663
|
+
|
|
1664
|
+
@property
|
|
1665
|
+
def precision(self) -> type[np.floating]:
|
|
1666
|
+
return self._properties_source.precision
|
|
1667
|
+
|
|
1668
|
+
@property
|
|
1669
|
+
def execution(self) -> str:
|
|
1670
|
+
return self._properties_source.execution
|
|
1671
|
+
|
|
1672
|
+
@property
|
|
1673
|
+
def sm(self) -> ComputeCapability:
|
|
1674
|
+
return self._properties_source.sm
|
|
1675
|
+
|
|
1676
|
+
@property
|
|
1677
|
+
def a_arrangement(self) -> str:
|
|
1678
|
+
return self._properties_source.a_arrangement
|
|
1679
|
+
|
|
1680
|
+
@property
|
|
1681
|
+
def batches_per_block(self) -> int:
|
|
1682
|
+
return self._properties_source.batches_per_block
|
|
1683
|
+
|
|
1684
|
+
@property
|
|
1685
|
+
def data_type(self) -> str:
|
|
1686
|
+
return self._properties_source.data_type
|
|
1687
|
+
|
|
1688
|
+
@property
|
|
1689
|
+
def lda(self) -> int:
|
|
1690
|
+
return self._properties_source.lda
|
|
1691
|
+
|
|
1692
|
+
@property
|
|
1693
|
+
def block_dim(self) -> tuple:
|
|
1694
|
+
return self._properties_source.block_dim
|
|
1695
|
+
|
|
1696
|
+
@property
|
|
1697
|
+
def block_size(self) -> int:
|
|
1698
|
+
return self._properties_source.block_size
|
|
1699
|
+
|
|
1700
|
+
@property
|
|
1701
|
+
def value_type(self) -> type[np.floating] | Complex:
|
|
1702
|
+
return self._properties_source.value_type
|
|
1703
|
+
|
|
1704
|
+
@property
|
|
1705
|
+
def tau_type(self) -> type[np.floating] | Complex:
|
|
1706
|
+
return self._properties_source.tau_type
|
|
1707
|
+
|
|
1708
|
+
@property
|
|
1709
|
+
def tau_shape(self) -> tuple[int, int]:
|
|
1710
|
+
return (self.batches_per_block, min(self.m, self.n))
|
|
1711
|
+
|
|
1712
|
+
@property
|
|
1713
|
+
def tau_strides(self) -> tuple[int, int]:
|
|
1714
|
+
return (self.tau_shape[1], 1)
|
|
1715
|
+
|
|
1716
|
+
@property
|
|
1717
|
+
def a_shape(self) -> tuple[int, int, int]:
|
|
1718
|
+
return (self.batches_per_block, self.m, self.n)
|
|
1719
|
+
|
|
1720
|
+
def a_strides(self, *, lda: int | None = None) -> tuple[int, int, int]:
|
|
1721
|
+
lda = self.lda if lda is None else lda
|
|
1722
|
+
return _calculate_strides(self.a_shape[1:], lda, self.a_arrangement)
|
|
1723
|
+
|
|
1724
|
+
def a_size(self, *, lda: int | None = None) -> int:
|
|
1725
|
+
return self.a_strides(lda=lda)[0] * self.a_shape[0]
|
|
1726
|
+
|
|
1727
|
+
@property
|
|
1728
|
+
def tau_size(self) -> int:
|
|
1729
|
+
return self.tau_shape[0] * self.tau_shape[1]
|
|
1730
|
+
|
|
1731
|
+
|
|
1732
|
+
# ==========================
|
|
1733
|
+
# QR Factorize
|
|
1734
|
+
# ==========================
|
|
1735
|
+
|
|
1736
|
+
|
|
1737
|
+
@docstring_decorator(ORTOGHONAL_FACTORIZER_DOCSTRING, skip_missing=False)
|
|
1738
|
+
class QRFactorize(_OrthogonalFactorizerProperties):
|
|
1739
|
+
"""
|
|
1740
|
+
A class that encapsulates QR orthogonal factorization device function
|
|
1741
|
+
for general matrices using Householder reflections.
|
|
1742
|
+
|
|
1743
|
+
**Available operation:**
|
|
1744
|
+
|
|
1745
|
+
* factorize: Computes the QR factorization A = Q @ R,
|
|
1746
|
+
where Q is a unitary M x M matrix
|
|
1747
|
+
and R is an upper triangular matrix (if M >= N)
|
|
1748
|
+
or upper trapezoidal matrix (if M < N).
|
|
1749
|
+
|
|
1750
|
+
The factorization uses Householder reflection transformations and does not explicitly
|
|
1751
|
+
form the unitary matrix Q. Instead, Q is represented as a product of Householder vectors
|
|
1752
|
+
stored in the input matrix A along with the tau array.
|
|
1753
|
+
|
|
1754
|
+
**Memory Layout Requirements:**
|
|
1755
|
+
|
|
1756
|
+
Matrices must be stored in shared memory according
|
|
1757
|
+
to their arrangement and leading dimension (ld):
|
|
1758
|
+
|
|
1759
|
+
**For matrix A (M x N):**
|
|
1760
|
+
|
|
1761
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
1762
|
+
with strides ``(lda * N, 1, lda)``
|
|
1763
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
1764
|
+
with strides ``(lda * M, lda, 1)``
|
|
1765
|
+
|
|
1766
|
+
Args:
|
|
1767
|
+
size (Sequence[int]): {size}
|
|
1768
|
+
|
|
1769
|
+
precision (type[np.floating]): {precision}
|
|
1770
|
+
|
|
1771
|
+
execution (str): {execution}
|
|
1772
|
+
|
|
1773
|
+
sm (ComputeCapability): {sm}
|
|
1774
|
+
|
|
1775
|
+
arrangement (str, optional): {arrangement}
|
|
1776
|
+
|
|
1777
|
+
batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
|
|
1778
|
+
|
|
1779
|
+
data_type (str, optional): {data_type}
|
|
1780
|
+
|
|
1781
|
+
leading_dimension (int, optional): {leading_dimension}
|
|
1782
|
+
|
|
1783
|
+
block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
|
|
1784
|
+
|
|
1785
|
+
See Also:
|
|
1786
|
+
For further details, please refer to the cuSOLVERDx documentation:
|
|
1787
|
+
|
|
1788
|
+
* :cusolverdx_doc:`factorize (geqrf) <get_started/functions/geqrf.html>`
|
|
1789
|
+
"""
|
|
1790
|
+
|
|
1791
|
+
# ==========================
|
|
1792
|
+
# Constructor
|
|
1793
|
+
# ==========================
|
|
1794
|
+
|
|
1795
|
+
def __init__(
|
|
1796
|
+
self,
|
|
1797
|
+
size: Sequence[int],
|
|
1798
|
+
precision: type[np.floating],
|
|
1799
|
+
execution: str,
|
|
1800
|
+
*,
|
|
1801
|
+
sm=None,
|
|
1802
|
+
arrangement: str | None = None,
|
|
1803
|
+
batches_per_block: int | Literal["suggested"] | None = None,
|
|
1804
|
+
data_type: str | None = None,
|
|
1805
|
+
leading_dimension: int | None = None,
|
|
1806
|
+
block_dim: Sequence[int] | Literal["suggested"] | None = None,
|
|
1807
|
+
):
|
|
1808
|
+
self._factorize = Solver(
|
|
1809
|
+
function="geqrf",
|
|
1810
|
+
size=size,
|
|
1811
|
+
precision=precision,
|
|
1812
|
+
execution=execution,
|
|
1813
|
+
sm=sm,
|
|
1814
|
+
arrangement=(arrangement,) if arrangement is not None else None,
|
|
1815
|
+
batches_per_block=batches_per_block,
|
|
1816
|
+
data_type=data_type,
|
|
1817
|
+
leading_dimensions=(leading_dimension,) if leading_dimension is not None else None,
|
|
1818
|
+
block_dim=block_dim,
|
|
1819
|
+
)
|
|
1820
|
+
super().__init__(self._factorize)
|
|
1821
|
+
|
|
1822
|
+
# ==========================
|
|
1823
|
+
# Device function methods
|
|
1824
|
+
# ==========================
|
|
1825
|
+
|
|
1826
|
+
def factorize(self, a, tau, lda=None) -> None:
|
|
1827
|
+
"""
|
|
1828
|
+
Computes the QR factorization of a general matrix A using Householder reflections.
|
|
1829
|
+
|
|
1830
|
+
This device function computes A = Q @ R, where Q is a unitary M x M matrix
|
|
1831
|
+
and R is an upper triangular matrix (if M >= N)
|
|
1832
|
+
or upper trapezoidal matrix (if M < N).
|
|
1833
|
+
Uses cuSOLVERDx ``'geqrf'``.
|
|
1834
|
+
|
|
1835
|
+
If ``lda`` is provided, uses runtime version with the
|
|
1836
|
+
specified leading dimension. If ``lda`` is not provided (``None``),
|
|
1837
|
+
uses compile-time version with
|
|
1838
|
+
default or constructor-provided leading dimensions.
|
|
1839
|
+
|
|
1840
|
+
Matrix Q is not explicitly formed. Instead, Q is represented as a product of
|
|
1841
|
+
min(M, N) Householder vectors: Q = H(0) * H(1) * ... * H(min(M, N) - 1).
|
|
1842
|
+
|
|
1843
|
+
Each Householder vector has the form H(i) = I - tau[i] * v * v^H, where:
|
|
1844
|
+
|
|
1845
|
+
* v is a vector of size M for each batch
|
|
1846
|
+
* v[0:i] = 0, v[i] = 1
|
|
1847
|
+
* v[i+1:M] is stored on exit in A[i+1:M, i]
|
|
1848
|
+
|
|
1849
|
+
For more details, see: :cusolverdx_doc:`get_started/functions/geqrf.html`
|
|
1850
|
+
|
|
1851
|
+
Args:
|
|
1852
|
+
a: Pointer to an array in shared memory, storing
|
|
1853
|
+
the batched matrix according
|
|
1854
|
+
to the specified arrangement
|
|
1855
|
+
and leading dimension (see :class:`QRFactorize`).
|
|
1856
|
+
The matrix is overwritten in place.
|
|
1857
|
+
On exit, the upper triangular or upper trapezoidal part (including diagonal)
|
|
1858
|
+
contains the matrix R. The elements below the diagonal, with the array tau,
|
|
1859
|
+
represent the unitary matrix Q as a product of Householder vectors.
|
|
1860
|
+
tau: Pointer to a 1D array of size min(M, N) for each batch.
|
|
1861
|
+
Contains the scalar factors of the Householder reflections.
|
|
1862
|
+
The tau array, together with the Householder vectors stored in A,
|
|
1863
|
+
defines the unitary matrix Q.
|
|
1864
|
+
lda: Optional runtime leading dimension of matrix A.
|
|
1865
|
+
If not specified, the compile-time ``lda`` is used.
|
|
1866
|
+
"""
|
|
1867
|
+
raise RuntimeError("factorize should not be called directly outside of a jitted kernel.")
|
|
1868
|
+
|
|
1869
|
+
|
|
1870
|
+
# ==========================
|
|
1871
|
+
# LQ Factorize
|
|
1872
|
+
# ==========================
|
|
1873
|
+
|
|
1874
|
+
|
|
1875
|
+
@docstring_decorator(ORTOGHONAL_FACTORIZER_DOCSTRING, skip_missing=False)
|
|
1876
|
+
class LQFactorize(_OrthogonalFactorizerProperties):
|
|
1877
|
+
"""
|
|
1878
|
+
A class that encapsulates LQ orthogonal factorization device function
|
|
1879
|
+
for general matrices using Householder reflections.
|
|
1880
|
+
|
|
1881
|
+
**Available operation:**
|
|
1882
|
+
|
|
1883
|
+
* factorize: Computes the LQ factorization A = L @ Q,
|
|
1884
|
+
where L is a lower triangular matrix (if M <= N)
|
|
1885
|
+
or lower trapezoidal matrix (if M > N)
|
|
1886
|
+
and Q is a unitary N x N matrix.
|
|
1887
|
+
|
|
1888
|
+
The factorization uses Householder reflection transformations and does not explicitly
|
|
1889
|
+
form the unitary matrix Q. Instead, Q is represented as a product of Householder vectors
|
|
1890
|
+
stored in the input matrix A along with the tau array.
|
|
1891
|
+
|
|
1892
|
+
**Memory Layout Requirements:**
|
|
1893
|
+
|
|
1894
|
+
Matrices must be stored in shared memory according
|
|
1895
|
+
to their arrangement and leading dimension (ld):
|
|
1896
|
+
|
|
1897
|
+
**For matrix A (M x N):**
|
|
1898
|
+
|
|
1899
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
1900
|
+
with strides ``(lda * N, 1, lda)``
|
|
1901
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
1902
|
+
with strides ``(lda * M, lda, 1)``
|
|
1903
|
+
|
|
1904
|
+
.. note::
|
|
1905
|
+
The LQ factorization is essentially equivalent to the
|
|
1906
|
+
QR factorization of A^T (or A^H for complex types).
|
|
1907
|
+
|
|
1908
|
+
Args:
|
|
1909
|
+
size (Sequence[int]): {size}
|
|
1910
|
+
|
|
1911
|
+
precision (type[np.floating]): {precision}
|
|
1912
|
+
|
|
1913
|
+
execution (str): {execution}
|
|
1914
|
+
|
|
1915
|
+
sm (ComputeCapability): {sm}
|
|
1916
|
+
|
|
1917
|
+
arrangement (str, optional): {arrangement}
|
|
1918
|
+
|
|
1919
|
+
batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
|
|
1920
|
+
|
|
1921
|
+
data_type (str, optional): {data_type}
|
|
1922
|
+
|
|
1923
|
+
leading_dimension (int, optional): {leading_dimension}
|
|
1924
|
+
|
|
1925
|
+
block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
|
|
1926
|
+
|
|
1927
|
+
See Also:
|
|
1928
|
+
For further details, please refer to the cuSOLVERDx documentation:
|
|
1929
|
+
|
|
1930
|
+
* :cusolverdx_doc:`factorize (gelqf) <get_started/functions/gelqf.html>`
|
|
1931
|
+
"""
|
|
1932
|
+
|
|
1933
|
+
# ==========================
|
|
1934
|
+
# Constructor
|
|
1935
|
+
# ==========================
|
|
1936
|
+
|
|
1937
|
+
def __init__(
|
|
1938
|
+
self,
|
|
1939
|
+
size: Sequence[int],
|
|
1940
|
+
precision: type[np.floating],
|
|
1941
|
+
execution: str,
|
|
1942
|
+
*,
|
|
1943
|
+
sm=None,
|
|
1944
|
+
arrangement: str | None = None,
|
|
1945
|
+
batches_per_block: int | Literal["suggested"] | None = None,
|
|
1946
|
+
data_type: str | None = None,
|
|
1947
|
+
leading_dimension: int | None = None,
|
|
1948
|
+
block_dim: Sequence[int] | Literal["suggested"] | None = None,
|
|
1949
|
+
):
|
|
1950
|
+
self._factorize = Solver(
|
|
1951
|
+
function="gelqf",
|
|
1952
|
+
size=size,
|
|
1953
|
+
precision=precision,
|
|
1954
|
+
execution=execution,
|
|
1955
|
+
sm=sm,
|
|
1956
|
+
arrangement=(arrangement,) if arrangement is not None else None,
|
|
1957
|
+
batches_per_block=batches_per_block,
|
|
1958
|
+
data_type=data_type,
|
|
1959
|
+
leading_dimensions=(leading_dimension,) if leading_dimension is not None else None,
|
|
1960
|
+
block_dim=block_dim,
|
|
1961
|
+
)
|
|
1962
|
+
super().__init__(self._factorize)
|
|
1963
|
+
|
|
1964
|
+
# ==========================
|
|
1965
|
+
# Device function methods
|
|
1966
|
+
# ==========================
|
|
1967
|
+
|
|
1968
|
+
def factorize(self, a, tau, lda=None) -> None:
|
|
1969
|
+
"""
|
|
1970
|
+
Computes the LQ factorization of a general matrix A using Householder reflections.
|
|
1971
|
+
|
|
1972
|
+
This device function computes A = L @ Q, where L is a lower triangular matrix
|
|
1973
|
+
(if M <= N) or lower trapezoidal matrix (if M > N),
|
|
1974
|
+
and Q is a unitary N x N matrix.
|
|
1975
|
+
Uses cuSOLVERDx ``'gelqf'``.
|
|
1976
|
+
|
|
1977
|
+
If ``lda`` is provided, uses runtime version with the
|
|
1978
|
+
specified leading dimension. If ``lda`` is not provided (``None``),
|
|
1979
|
+
uses compile-time version with
|
|
1980
|
+
default or constructor-provided leading dimensions.
|
|
1981
|
+
|
|
1982
|
+
The LQ factorization is essentially the same as the QR factorization of A^T
|
|
1983
|
+
(or A^H for complex data types).
|
|
1984
|
+
|
|
1985
|
+
Matrix Q is not explicitly formed. Instead, Q is represented as a product of
|
|
1986
|
+
min(M, N) Householder vectors: Q = H(min(M, N) - 1)^H * ... * H(1)^H * H(0)^H.
|
|
1987
|
+
|
|
1988
|
+
Each Householder vector has the form H(i) = I - tau[i] * v * v^H, where:
|
|
1989
|
+
|
|
1990
|
+
* v is a vector of size N for each batch
|
|
1991
|
+
* v[0:i] = 0, v[i] = 1
|
|
1992
|
+
* conjugate(v[i+1:N]) is stored on exit in A[i, i+1:N]
|
|
1993
|
+
|
|
1994
|
+
For more details, see: :cusolverdx_doc:`get_started/functions/gelqf.html`
|
|
1995
|
+
|
|
1996
|
+
Args:
|
|
1997
|
+
a: Pointer to an array in shared memory, storing
|
|
1998
|
+
the batched M x N matrix according
|
|
1999
|
+
to the specified arrangement
|
|
2000
|
+
and leading dimension (see :class:`LQFactorize`).
|
|
2001
|
+
The matrix is overwritten in place.
|
|
2002
|
+
On exit, the lower triangular or lower trapezoidal part (including diagonal)
|
|
2003
|
+
contains the matrix L. The elements above the diagonal, with the array tau,
|
|
2004
|
+
represent the unitary matrix Q as a product of Householder vectors.
|
|
2005
|
+
tau: Pointer to a 1D array of size min(M, N) for each batch.
|
|
2006
|
+
Contains the scalar factors of the Householder reflections.
|
|
2007
|
+
The tau array, together with the Householder vectors stored in A,
|
|
2008
|
+
defines the unitary matrix Q.
|
|
2009
|
+
lda: Optional runtime leading dimension of matrix A.
|
|
2010
|
+
If not specified, the compile-time ``lda`` is used.
|
|
2011
|
+
"""
|
|
2012
|
+
raise RuntimeError("factorize should not be called directly outside of a jitted kernel.")
|
|
2013
|
+
|
|
2014
|
+
|
|
2015
|
+
# ==========================
|
|
2016
|
+
# QR Multiplier (UNMQR)
|
|
2017
|
+
# ==========================
|
|
2018
|
+
|
|
2019
|
+
QR_MULTIPLIER_DOCSTRING = SOLVER_DOCSTRING.copy()
|
|
2020
|
+
del QR_MULTIPLIER_DOCSTRING["function"]
|
|
2021
|
+
del QR_MULTIPLIER_DOCSTRING["fill_mode"]
|
|
2022
|
+
del QR_MULTIPLIER_DOCSTRING["diag"]
|
|
2023
|
+
del QR_MULTIPLIER_DOCSTRING["job"]
|
|
2024
|
+
|
|
2025
|
+
QR_MULTIPLIER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
|
|
2026
|
+
``M`` and ``N`` represent the dimensions of matrix C.
|
|
2027
|
+
``K`` represents the number of Householder reflections from the QR factorization.
|
|
2028
|
+
If ``side='left'``, then ``K <= M`` and A is ``M`` x ``K``.
|
|
2029
|
+
If ``side='right'``, then ``K <= N`` and A is ``N`` x ``K``.""".replace("\n", " ")
|
|
2030
|
+
|
|
2031
|
+
QR_MULTIPLIER_DOCSTRING["side"] = f"""Side of matrix Q in the multiplication operation.
|
|
2032
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_SIDE)}.
|
|
2033
|
+
If ``side='left'``, computes op(Q) * C where Q is ``M`` x ``M``.
|
|
2034
|
+
If ``side='right'``, computes C * op(Q) where Q is ``N`` x ``N``.""".replace("\n", " ")
|
|
2035
|
+
|
|
2036
|
+
QR_MULTIPLIER_DOCSTRING["transpose_mode"] = f"""Transpose mode for operation op(Q) applied to matrix Q.
|
|
2037
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_TRANSPOSE_MODE)}.
|
|
2038
|
+
Defaults to ``'non_transposed'``.""".replace("\n", " ")
|
|
2039
|
+
|
|
2040
|
+
QR_MULTIPLIER_DOCSTRING["leading_dimensions"] = SOLVER_DOCSTRING["leading_dimensions"] + ADAPTERS_API_LD_DOCSTRING
|
|
2041
|
+
|
|
2042
|
+
|
|
2043
|
+
@docstring_decorator(QR_MULTIPLIER_DOCSTRING, skip_missing=False)
|
|
2044
|
+
class QRMultiply(_SolverProperties):
|
|
2045
|
+
"""
|
|
2046
|
+
A class that encapsulates the multiplication of a matrix C by the unitary matrix Q
|
|
2047
|
+
from a QR factorization (UNMQR operation).
|
|
2048
|
+
|
|
2049
|
+
**Memory Layout Requirements:**
|
|
2050
|
+
|
|
2051
|
+
Matrices must be stored in shared memory according
|
|
2052
|
+
to their arrangement and leading dimension (ld):
|
|
2053
|
+
|
|
2054
|
+
**For matrix A (containing Householder vectors):**
|
|
2055
|
+
|
|
2056
|
+
* If ``side='left'``: A is ``M`` x ``K``
|
|
2057
|
+
* If ``side='right'``: A is ``N`` x ``K``
|
|
2058
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, rows, K)``
|
|
2059
|
+
with strides ``(lda * K, 1, lda)``
|
|
2060
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, rows, K)``
|
|
2061
|
+
with strides ``(lda * rows, lda, 1)``
|
|
2062
|
+
|
|
2063
|
+
**For matrix C (M x N):**
|
|
2064
|
+
|
|
2065
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
2066
|
+
with strides ``(ldc * N, 1, ldc)``
|
|
2067
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
2068
|
+
with strides ``(ldc * M, ldc, 1)``
|
|
2069
|
+
|
|
2070
|
+
Args:
|
|
2071
|
+
size (Sequence[int]): {size}
|
|
2072
|
+
|
|
2073
|
+
precision (type[np.floating]): {precision}
|
|
2074
|
+
|
|
2075
|
+
execution (str): {execution}
|
|
2076
|
+
|
|
2077
|
+
side (str): {side}
|
|
2078
|
+
|
|
2079
|
+
sm (ComputeCapability): {sm}
|
|
2080
|
+
|
|
2081
|
+
transpose_mode (str, optional): {transpose_mode}
|
|
2082
|
+
|
|
2083
|
+
arrangement (Sequence[str], optional): {arrangement}
|
|
2084
|
+
|
|
2085
|
+
batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
|
|
2086
|
+
|
|
2087
|
+
data_type (str, optional): {data_type}
|
|
2088
|
+
|
|
2089
|
+
leading_dimensions (Sequence[int], optional): {leading_dimensions}
|
|
2090
|
+
|
|
2091
|
+
block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
|
|
2092
|
+
|
|
2093
|
+
See Also:
|
|
2094
|
+
For further details, please refer to the cuSOLVERDx documentation:
|
|
2095
|
+
|
|
2096
|
+
* :cusolverdx_doc:`unmqr <get_started/functions/unmqr.html>`
|
|
2097
|
+
"""
|
|
2098
|
+
|
|
2099
|
+
# ==========================
|
|
2100
|
+
# Constructor
|
|
2101
|
+
# ==========================
|
|
2102
|
+
|
|
2103
|
+
def __init__(
|
|
2104
|
+
self,
|
|
2105
|
+
size: Sequence[int],
|
|
2106
|
+
precision: type[np.floating],
|
|
2107
|
+
execution: str,
|
|
2108
|
+
side: str,
|
|
2109
|
+
*,
|
|
2110
|
+
sm=None,
|
|
2111
|
+
transpose_mode: str = "non_transposed",
|
|
2112
|
+
arrangement: Sequence[str] | None = None,
|
|
2113
|
+
batches_per_block: int | Literal["suggested"] | None = None,
|
|
2114
|
+
data_type: str | None = None,
|
|
2115
|
+
leading_dimensions: Sequence[int] | None = None,
|
|
2116
|
+
block_dim: Sequence[int] | Literal["suggested"] | None = None,
|
|
2117
|
+
):
|
|
2118
|
+
self._multiply: Solver = Solver(
|
|
2119
|
+
function="unmqr",
|
|
2120
|
+
size=size,
|
|
2121
|
+
precision=precision,
|
|
2122
|
+
execution=execution,
|
|
2123
|
+
sm=sm,
|
|
2124
|
+
side=side,
|
|
2125
|
+
transpose_mode=transpose_mode,
|
|
2126
|
+
arrangement=arrangement,
|
|
2127
|
+
batches_per_block=batches_per_block,
|
|
2128
|
+
data_type=data_type,
|
|
2129
|
+
leading_dimensions=leading_dimensions,
|
|
2130
|
+
block_dim=block_dim,
|
|
2131
|
+
)
|
|
2132
|
+
|
|
2133
|
+
super().__init__(self._multiply)
|
|
2134
|
+
|
|
2135
|
+
if not isinstance(self._multiply.transpose_mode, str):
|
|
2136
|
+
raise ValueError("transpose_mode must be provided for QRMultiply")
|
|
2137
|
+
|
|
2138
|
+
if not isinstance(self._multiply.side, str):
|
|
2139
|
+
raise ValueError("side must be provided for QRMultiply")
|
|
2140
|
+
|
|
2141
|
+
# ==========================
|
|
2142
|
+
# Property methods
|
|
2143
|
+
# ==========================
|
|
2144
|
+
|
|
2145
|
+
@property
|
|
2146
|
+
def side(self) -> str:
|
|
2147
|
+
assert self._multiply.side is not None
|
|
2148
|
+
return self._multiply.side
|
|
2149
|
+
|
|
2150
|
+
@property
|
|
2151
|
+
def transpose_mode(self) -> str:
|
|
2152
|
+
assert self._multiply.transpose_mode is not None
|
|
2153
|
+
return self._multiply.transpose_mode
|
|
2154
|
+
|
|
2155
|
+
@property
|
|
2156
|
+
def tau_type(self) -> type[np.floating] | Complex:
|
|
2157
|
+
return self.value_type
|
|
2158
|
+
|
|
2159
|
+
@property
|
|
2160
|
+
def tau_shape(self) -> tuple[int, int]:
|
|
2161
|
+
return (self.batches_per_block, self.k)
|
|
2162
|
+
|
|
2163
|
+
@property
|
|
2164
|
+
def tau_strides(self) -> tuple[int, int]:
|
|
2165
|
+
return (self.tau_shape[1], 1)
|
|
2166
|
+
|
|
2167
|
+
@property
|
|
2168
|
+
def a_shape(self) -> tuple[int, int, int]:
|
|
2169
|
+
rows = self.m if self.side == "left" else self.n
|
|
2170
|
+
return (self.batches_per_block, rows, self.k)
|
|
2171
|
+
|
|
2172
|
+
@property
|
|
2173
|
+
def c_shape(self) -> tuple[int, int, int]:
|
|
2174
|
+
return (self.batches_per_block, self.m, self.n)
|
|
2175
|
+
|
|
2176
|
+
def a_strides(self, *, lda: int | None = None) -> tuple[int, int, int]:
|
|
2177
|
+
lda = self.lda if lda is None else lda
|
|
2178
|
+
return _calculate_strides(self.a_shape[1:], lda, self.a_arrangement)
|
|
2179
|
+
|
|
2180
|
+
def c_strides(self, *, ldc: int | None = None) -> tuple[int, int, int]:
|
|
2181
|
+
ldc = self.ldb if ldc is None else ldc
|
|
2182
|
+
return _calculate_strides(self.c_shape[1:], ldc, self.b_arrangement)
|
|
2183
|
+
|
|
2184
|
+
def a_size(self, *, lda: int | None = None) -> int:
|
|
2185
|
+
return self.a_strides(lda=lda)[0] * self.a_shape[0]
|
|
2186
|
+
|
|
2187
|
+
def c_size(self, *, ldc: int | None = None) -> int:
|
|
2188
|
+
return self.c_strides(ldc=ldc)[0] * self.c_shape[0]
|
|
2189
|
+
|
|
2190
|
+
@property
|
|
2191
|
+
def tau_size(self) -> int:
|
|
2192
|
+
return self.tau_shape[0] * self.tau_shape[1]
|
|
2193
|
+
|
|
2194
|
+
# ==========================
|
|
2195
|
+
# Device function methods
|
|
2196
|
+
# ==========================
|
|
2197
|
+
|
|
2198
|
+
def multiply(self, a, tau, c, lda=None, ldc=None) -> None:
|
|
2199
|
+
"""
|
|
2200
|
+
Multiplies matrix C by the unitary matrix Q from a QR factorization.
|
|
2201
|
+
|
|
2202
|
+
This device function computes:
|
|
2203
|
+
``op(Q) * C`` (if ``side='left'``)
|
|
2204
|
+
``C * op(Q)`` (if ``side='right'``)
|
|
2205
|
+
|
|
2206
|
+
where Q is the unitary matrix from the QR factorization, represented
|
|
2207
|
+
by Householder vectors stored in A and the tau array.
|
|
2208
|
+
Uses cuSOLVERDx ``'unmqr'``. The result overwrites matrix C.
|
|
2209
|
+
|
|
2210
|
+
If ``lda`` and ``ldc`` are provided, uses runtime version with the
|
|
2211
|
+
specified leading dimensions. If not provided (``None``), uses compile-time
|
|
2212
|
+
version with default or constructor-provided leading dimensions.
|
|
2213
|
+
|
|
2214
|
+
For more details, see: :cusolverdx_doc:`get_started/functions/unmqr.html`
|
|
2215
|
+
|
|
2216
|
+
Args:
|
|
2217
|
+
a: Pointer to an array in shared memory, storing the batched matrix containing
|
|
2218
|
+
Householder vectors from the QR factorization, according to the specified
|
|
2219
|
+
arrangement and leading dimension (see :class:`QRMultiply`).
|
|
2220
|
+
The elements below the diagonal of A, with the array tau, represent the
|
|
2221
|
+
unitary matrix Q as a product of Householder reflections.
|
|
2222
|
+
If ``side='left'``, A is ``M`` x ``K``.
|
|
2223
|
+
If ``side='right'``, A is ``N`` x ``K``.
|
|
2224
|
+
tau: Pointer to a 1D array of size K for each batch, containing the scalar
|
|
2225
|
+
factors of the Householder reflections from the QR factorization.
|
|
2226
|
+
The tau array, together with the Householder vectors in A, defines Q.
|
|
2227
|
+
c: Pointer to an array in shared memory,
|
|
2228
|
+
storing the batched ``M`` x ``N`` matrix
|
|
2229
|
+
according to the specified arrangement
|
|
2230
|
+
and leading dimension (see :class:`QRMultiply`).
|
|
2231
|
+
The operation is in-place: result overwrites C.
|
|
2232
|
+
lda: Optional runtime leading dimension for matrix A.
|
|
2233
|
+
The ``lda`` and ``ldc`` must be specified together.
|
|
2234
|
+
If not specified, the compile-time ``lda`` is used.
|
|
2235
|
+
ldc: Optional runtime leading dimension for matrix C.
|
|
2236
|
+
The ``lda`` and ``ldc`` must be specified together.
|
|
2237
|
+
If not specified, the compile-time ``ldc`` is used.
|
|
2238
|
+
"""
|
|
2239
|
+
raise RuntimeError("multiply should not be called directly outside of a jitted kernel.")
|
|
2240
|
+
|
|
2241
|
+
|
|
2242
|
+
# ==========================
|
|
2243
|
+
# LQ Multiplier (UNMLQ)
|
|
2244
|
+
# ==========================
|
|
2245
|
+
|
|
2246
|
+
LQ_MULTIPLIER_DOCSTRING = SOLVER_DOCSTRING.copy()
|
|
2247
|
+
del LQ_MULTIPLIER_DOCSTRING["function"]
|
|
2248
|
+
del LQ_MULTIPLIER_DOCSTRING["fill_mode"]
|
|
2249
|
+
del LQ_MULTIPLIER_DOCSTRING["diag"]
|
|
2250
|
+
del LQ_MULTIPLIER_DOCSTRING["job"]
|
|
2251
|
+
|
|
2252
|
+
LQ_MULTIPLIER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
|
|
2253
|
+
``M`` and ``N`` represent the dimensions of matrix C.
|
|
2254
|
+
``K`` represents the number of Householder reflections from the LQ factorization.
|
|
2255
|
+
If ``side='left'``, then ``K <= M`` and A is ``K`` x ``M``.
|
|
2256
|
+
If ``side='right'``, then ``K <= N`` and A is ``K`` x ``N``.""".replace("\n", " ")
|
|
2257
|
+
|
|
2258
|
+
LQ_MULTIPLIER_DOCSTRING["side"] = f"""Side of matrix Q in the multiplication operation.
|
|
2259
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_SIDE)}.
|
|
2260
|
+
If ``side='left'``, computes op(Q) * C where Q is ``M`` x ``M``.
|
|
2261
|
+
If ``side='right'``, computes C * op(Q) where Q is ``N`` x ``N``.""".replace("\n", " ")
|
|
2262
|
+
|
|
2263
|
+
LQ_MULTIPLIER_DOCSTRING["transpose_mode"] = f"""Transpose mode for operation op(Q) applied to matrix Q.
|
|
2264
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_TRANSPOSE_MODE)}.
|
|
2265
|
+
Defaults to ``'non_transposed'``.""".replace("\n", " ")
|
|
2266
|
+
|
|
2267
|
+
LQ_MULTIPLIER_DOCSTRING["leading_dimensions"] = SOLVER_DOCSTRING["leading_dimensions"] + ADAPTERS_API_LD_DOCSTRING
|
|
2268
|
+
|
|
2269
|
+
|
|
2270
|
+
@docstring_decorator(LQ_MULTIPLIER_DOCSTRING, skip_missing=False)
|
|
2271
|
+
class LQMultiply(_SolverProperties):
|
|
2272
|
+
"""
|
|
2273
|
+
A class that encapsulates the multiplication of a matrix C by the unitary matrix Q
|
|
2274
|
+
from an LQ factorization (UNMLQ operation).
|
|
2275
|
+
|
|
2276
|
+
**Memory Layout Requirements:**
|
|
2277
|
+
|
|
2278
|
+
Matrices must be stored in shared memory according
|
|
2279
|
+
to their arrangement and leading dimension (ld):
|
|
2280
|
+
|
|
2281
|
+
**For matrix A (containing Householder vectors):**
|
|
2282
|
+
|
|
2283
|
+
* If ``side='left'``: A is ``K`` x ``M``
|
|
2284
|
+
* If ``side='right'``: A is ``K`` x ``N``
|
|
2285
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, K, cols)``
|
|
2286
|
+
with strides ``(lda * cols, 1, lda)``
|
|
2287
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, K, cols)``
|
|
2288
|
+
with strides ``(lda * K, lda, 1)``
|
|
2289
|
+
|
|
2290
|
+
**For matrix C (M x N):**
|
|
2291
|
+
|
|
2292
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
2293
|
+
with strides ``(ldc * N, 1, ldc)``
|
|
2294
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
2295
|
+
with strides ``(ldc * M, ldc, 1)``
|
|
2296
|
+
|
|
2297
|
+
Args:
|
|
2298
|
+
size (Sequence[int]): {size}
|
|
2299
|
+
|
|
2300
|
+
precision (type[np.floating]): {precision}
|
|
2301
|
+
|
|
2302
|
+
execution (str): {execution}
|
|
2303
|
+
|
|
2304
|
+
side (str): {side}
|
|
2305
|
+
|
|
2306
|
+
sm (ComputeCapability): {sm}
|
|
2307
|
+
|
|
2308
|
+
transpose_mode (str, optional): {transpose_mode}
|
|
2309
|
+
|
|
2310
|
+
arrangement (Sequence[str], optional): {arrangement}
|
|
2311
|
+
|
|
2312
|
+
batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
|
|
2313
|
+
|
|
2314
|
+
data_type (str, optional): {data_type}
|
|
2315
|
+
|
|
2316
|
+
leading_dimensions (Sequence[int], optional): {leading_dimensions}
|
|
2317
|
+
|
|
2318
|
+
block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
|
|
2319
|
+
|
|
2320
|
+
See Also:
|
|
2321
|
+
For further details, please refer to the cuSOLVERDx documentation:
|
|
2322
|
+
|
|
2323
|
+
* :cusolverdx_doc:`unmlq <get_started/functions/unmlq.html>`
|
|
2324
|
+
"""
|
|
2325
|
+
|
|
2326
|
+
# ==========================
|
|
2327
|
+
# Constructor
|
|
2328
|
+
# ==========================
|
|
2329
|
+
|
|
2330
|
+
def __init__(
|
|
2331
|
+
self,
|
|
2332
|
+
size: Sequence[int],
|
|
2333
|
+
precision: type[np.floating],
|
|
2334
|
+
execution: str,
|
|
2335
|
+
side: str,
|
|
2336
|
+
*,
|
|
2337
|
+
sm=None,
|
|
2338
|
+
transpose_mode: str = "non_transposed",
|
|
2339
|
+
arrangement: Sequence[str] | None = None,
|
|
2340
|
+
batches_per_block: int | Literal["suggested"] | None = None,
|
|
2341
|
+
data_type: str | None = None,
|
|
2342
|
+
leading_dimensions: Sequence[int] | None = None,
|
|
2343
|
+
block_dim: Sequence[int] | Literal["suggested"] | None = None,
|
|
2344
|
+
):
|
|
2345
|
+
self._multiply: Solver = Solver(
|
|
2346
|
+
function="unmlq",
|
|
2347
|
+
size=size,
|
|
2348
|
+
precision=precision,
|
|
2349
|
+
execution=execution,
|
|
2350
|
+
sm=sm,
|
|
2351
|
+
side=side,
|
|
2352
|
+
transpose_mode=transpose_mode,
|
|
2353
|
+
arrangement=arrangement,
|
|
2354
|
+
batches_per_block=batches_per_block,
|
|
2355
|
+
data_type=data_type,
|
|
2356
|
+
leading_dimensions=leading_dimensions,
|
|
2357
|
+
block_dim=block_dim,
|
|
2358
|
+
)
|
|
2359
|
+
|
|
2360
|
+
super().__init__(self._multiply)
|
|
2361
|
+
|
|
2362
|
+
if not isinstance(self._multiply.transpose_mode, str):
|
|
2363
|
+
raise ValueError("transpose_mode must be provided for LQMultiply")
|
|
2364
|
+
|
|
2365
|
+
if not isinstance(self._multiply.side, str):
|
|
2366
|
+
raise ValueError("side must be provided for LQMultiply")
|
|
2367
|
+
|
|
2368
|
+
# ==========================
|
|
2369
|
+
# Property methods
|
|
2370
|
+
# ==========================
|
|
2371
|
+
|
|
2372
|
+
@property
|
|
2373
|
+
def side(self) -> str:
|
|
2374
|
+
assert self._multiply.side is not None
|
|
2375
|
+
return self._multiply.side
|
|
2376
|
+
|
|
2377
|
+
@property
|
|
2378
|
+
def transpose_mode(self) -> str:
|
|
2379
|
+
assert self._multiply.transpose_mode is not None
|
|
2380
|
+
return self._multiply.transpose_mode
|
|
2381
|
+
|
|
2382
|
+
@property
|
|
2383
|
+
def tau_type(self) -> type[np.floating] | Complex:
|
|
2384
|
+
return self.value_type
|
|
2385
|
+
|
|
2386
|
+
@property
|
|
2387
|
+
def tau_shape(self) -> tuple[int, int]:
|
|
2388
|
+
return (self.batches_per_block, self.k)
|
|
2389
|
+
|
|
2390
|
+
@property
|
|
2391
|
+
def tau_strides(self) -> tuple[int, int]:
|
|
2392
|
+
return (self.tau_shape[1], 1)
|
|
2393
|
+
|
|
2394
|
+
@property
|
|
2395
|
+
def a_shape(self) -> tuple[int, int, int]:
|
|
2396
|
+
cols = self.m if self.side == "left" else self.n
|
|
2397
|
+
return (self.batches_per_block, self.k, cols)
|
|
2398
|
+
|
|
2399
|
+
@property
|
|
2400
|
+
def c_shape(self) -> tuple[int, int, int]:
|
|
2401
|
+
return (self.batches_per_block, self.m, self.n)
|
|
2402
|
+
|
|
2403
|
+
def a_strides(self, *, lda: int | None = None) -> tuple[int, int, int]:
|
|
2404
|
+
lda = self.lda if lda is None else lda
|
|
2405
|
+
return _calculate_strides(self.a_shape[1:], lda, self.a_arrangement)
|
|
2406
|
+
|
|
2407
|
+
def c_strides(self, *, ldc: int | None = None) -> tuple[int, int, int]:
|
|
2408
|
+
ldc = self.ldb if ldc is None else ldc
|
|
2409
|
+
return _calculate_strides(self.c_shape[1:], ldc, self.b_arrangement)
|
|
2410
|
+
|
|
2411
|
+
def a_size(self, *, lda: int | None = None) -> int:
|
|
2412
|
+
return self.a_strides(lda=lda)[0] * self.a_shape[0]
|
|
2413
|
+
|
|
2414
|
+
def c_size(self, *, ldc: int | None = None) -> int:
|
|
2415
|
+
return self.c_strides(ldc=ldc)[0] * self.c_shape[0]
|
|
2416
|
+
|
|
2417
|
+
@property
|
|
2418
|
+
def tau_size(self) -> int:
|
|
2419
|
+
return self.tau_shape[0] * self.tau_shape[1]
|
|
2420
|
+
|
|
2421
|
+
# ==========================
|
|
2422
|
+
# Device function methods
|
|
2423
|
+
# ==========================
|
|
2424
|
+
|
|
2425
|
+
def multiply(self, a, tau, c, lda=None, ldc=None) -> None:
|
|
2426
|
+
"""
|
|
2427
|
+
Multiplies matrix C by the unitary matrix Q from an LQ factorization.
|
|
2428
|
+
|
|
2429
|
+
This device function computes:
|
|
2430
|
+
``op(Q) * C`` (if ``side='left'``)
|
|
2431
|
+
``C * op(Q)`` (if ``side='right'``)
|
|
2432
|
+
|
|
2433
|
+
where Q is the unitary matrix from the LQ factorization, represented
|
|
2434
|
+
by Householder vectors stored in A and the tau array.
|
|
2435
|
+
Uses cuSOLVERDx ``'unmlq'``. The result overwrites matrix C.
|
|
2436
|
+
|
|
2437
|
+
If ``lda`` and ``ldc`` are provided, uses runtime version with the
|
|
2438
|
+
specified leading dimensions. If not provided (``None``), uses compile-time
|
|
2439
|
+
version with default or constructor-provided leading dimensions.
|
|
2440
|
+
|
|
2441
|
+
For more details, see: :cusolverdx_doc:`get_started/functions/unmlq.html`
|
|
2442
|
+
|
|
2443
|
+
Args:
|
|
2444
|
+
a: Pointer to an array in shared memory, storing the batched matrix containing
|
|
2445
|
+
Householder vectors from the LQ factorization, according to the specified
|
|
2446
|
+
arrangement and leading dimension (see :class:`LQMultiply`).
|
|
2447
|
+
The elements above the diagonal of A, with the array tau, represent the
|
|
2448
|
+
unitary matrix Q as a product of Householder reflections.
|
|
2449
|
+
If ``side='left'``, A is ``K`` x ``M``.
|
|
2450
|
+
If ``side='right'``, A is ``K`` x ``N``.
|
|
2451
|
+
tau: Pointer to a 1D array of size K for each batch, containing the scalar
|
|
2452
|
+
factors of the Householder reflections from the LQ factorization.
|
|
2453
|
+
The tau array, together with the Householder vectors in A, defines Q.
|
|
2454
|
+
c: Pointer to an array in shared memory,
|
|
2455
|
+
storing the batched ``M`` x ``N`` matrix
|
|
2456
|
+
according to the specified arrangement
|
|
2457
|
+
and leading dimension (see :class:`LQMultiply`).
|
|
2458
|
+
The operation is in-place: result overwrites C.
|
|
2459
|
+
lda: Optional runtime leading dimension for matrix A.
|
|
2460
|
+
The ``lda`` and ``ldc`` must be specified together.
|
|
2461
|
+
If not specified, the compile-time ``lda`` is used.
|
|
2462
|
+
ldc: Optional runtime leading dimension for matrix C.
|
|
2463
|
+
The ``lda`` and ``ldc`` must be specified together.
|
|
2464
|
+
If not specified, the compile-time ``ldc`` is used.
|
|
2465
|
+
"""
|
|
2466
|
+
raise RuntimeError("multiply should not be called directly outside of a jitted kernel.")
|
|
2467
|
+
|
|
2468
|
+
|
|
2469
|
+
# ==========================
|
|
2470
|
+
# Least Squares Solver
|
|
2471
|
+
# ==========================
|
|
2472
|
+
|
|
2473
|
+
LEAST_SQUARES_SOLVER_DOCSTRING = SOLVER_DOCSTRING.copy()
|
|
2474
|
+
del LEAST_SQUARES_SOLVER_DOCSTRING["function"]
|
|
2475
|
+
del LEAST_SQUARES_SOLVER_DOCSTRING["fill_mode"]
|
|
2476
|
+
del LEAST_SQUARES_SOLVER_DOCSTRING["side"]
|
|
2477
|
+
del LEAST_SQUARES_SOLVER_DOCSTRING["diag"]
|
|
2478
|
+
|
|
2479
|
+
LEAST_SQUARES_SOLVER_DOCSTRING["size"] = f"""{SOLVER_DOCSTRING_SIZE_BASE}
|
|
2480
|
+
``M`` and ``N`` represent the dimensions of matrix A (``M`` x ``N``).
|
|
2481
|
+
``K`` represents the number of columns in the right-hand side matrix B.""".replace("\n", " ")
|
|
2482
|
+
|
|
2483
|
+
LEAST_SQUARES_SOLVER_DOCSTRING["transpose_mode"] = f"""Transpose mode for operation op(A) applied to matrix A.
|
|
2484
|
+
Can be one of: {", ".join(f"``'{v}'``" for v in ALLOWED_TRANSPOSE_MODE)}.
|
|
2485
|
+
Defaults to ``'non_transposed'``.""".replace("\n", " ")
|
|
2486
|
+
|
|
2487
|
+
LEAST_SQUARES_SOLVER_DOCSTRING["leading_dimensions"] = SOLVER_DOCSTRING["leading_dimensions"] + ADAPTERS_API_LD_DOCSTRING
|
|
2488
|
+
|
|
2489
|
+
|
|
2490
|
+
@docstring_decorator(LEAST_SQUARES_SOLVER_DOCSTRING, skip_missing=False)
|
|
2491
|
+
class LeastSquaresSolver(_SolverProperties):
|
|
2492
|
+
"""
|
|
2493
|
+
A class that encapsulates least squares solver device function (``'gels'``).
|
|
2494
|
+
GELS (GEneral Least Squares) solves overdetermined
|
|
2495
|
+
or underdetermined least squares problems:
|
|
2496
|
+
|
|
2497
|
+
.. math::
|
|
2498
|
+
\\min \\| op(A) * X - B \\|_2
|
|
2499
|
+
|
|
2500
|
+
using the QR or LQ factorization of A, and overwriting B with the solution X.
|
|
2501
|
+
|
|
2502
|
+
**Memory layout requirements:**
|
|
2503
|
+
|
|
2504
|
+
Matrices must be stored in shared memory according
|
|
2505
|
+
to their arrangement and leading dimension (ld):
|
|
2506
|
+
|
|
2507
|
+
**For matrix A (M x N):**
|
|
2508
|
+
|
|
2509
|
+
* **Column-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
2510
|
+
with strides ``(lda * N, 1, lda)``
|
|
2511
|
+
* **Row-major arrangement**: Matrix shape ``(batches_per_block, M, N)``
|
|
2512
|
+
with strides ``(lda * M, lda, 1)``
|
|
2513
|
+
|
|
2514
|
+
**Matrix B and X** are stored in the same buffer (B is overwritten by X in place).
|
|
2515
|
+
The buffer has shape ``(batches_per_block, max(M, N), K)``. The second leading
|
|
2516
|
+
dimension ``ldb`` refers to this shared B/X buffer
|
|
2517
|
+
and must satisfy ``ldb >= max(M, N)``.
|
|
2518
|
+
Logical shapes differ by :attr:`transpose_mode`.
|
|
2519
|
+
Use :attr:`b_shape` and :attr:`x_shape` for B and X.
|
|
2520
|
+
|
|
2521
|
+
**Matrix B** (right-hand side):
|
|
2522
|
+
|
|
2523
|
+
* **Logical shape**: :attr:`b_shape` - ``(batches_per_block, M, K)`` if non-transposed,
|
|
2524
|
+
``(batches_per_block, N, K)`` if transposed.
|
|
2525
|
+
* **Column-major arrangement**: strides ``(ldb * K, 1, ldb)``.
|
|
2526
|
+
* **Row-major arrangement**: strides ``(ldb * max(M, N), ldb, 1)``.
|
|
2527
|
+
|
|
2528
|
+
**Matrix X** (solution):
|
|
2529
|
+
|
|
2530
|
+
* **Logical shape**: :attr:`x_shape` - ``(batches_per_block, N, K)`` if non-transposed,
|
|
2531
|
+
``(batches_per_block, M, K)`` if transposed.
|
|
2532
|
+
* **Column-major arrangement**: strides ``(ldb * K, 1, ldb)``.
|
|
2533
|
+
* **Row-major arrangement**: strides ``(ldb * max(M, N), ldb, 1)``.
|
|
2534
|
+
|
|
2535
|
+
.. note::
|
|
2536
|
+
GELS is an in-place function. Matrix A is overwritten by the QR or LQ factorization,
|
|
2537
|
+
and matrix B is overwritten by the solution X. Both B and X use the single buffer
|
|
2538
|
+
of shape ``(max(M, N), K)`` per batch.
|
|
2539
|
+
|
|
2540
|
+
Args:
|
|
2541
|
+
size (Sequence[int]): {size}
|
|
2542
|
+
|
|
2543
|
+
precision (type[np.floating]): {precision}
|
|
2544
|
+
|
|
2545
|
+
execution (str): {execution}
|
|
2546
|
+
|
|
2547
|
+
sm (ComputeCapability): {sm}
|
|
2548
|
+
|
|
2549
|
+
transpose_mode (str, optional): {transpose_mode}
|
|
2550
|
+
|
|
2551
|
+
arrangement (Sequence[str], optional): {arrangement}
|
|
2552
|
+
|
|
2553
|
+
batches_per_block (int | Literal["suggested"], optional): {batches_per_block}
|
|
2554
|
+
|
|
2555
|
+
data_type (str, optional): {data_type}
|
|
2556
|
+
|
|
2557
|
+
leading_dimensions (Sequence[int], optional): {leading_dimensions}
|
|
2558
|
+
|
|
2559
|
+
block_dim (Sequence[int] | Literal["suggested"], optional): {block_dim}
|
|
2560
|
+
|
|
2561
|
+
See Also:
|
|
2562
|
+
For further details, please refer to the cuSOLVERDx documentation:
|
|
2563
|
+
|
|
2564
|
+
* :cusolverdx_doc:`gels <get_started/functions/gels.html>`
|
|
2565
|
+
"""
|
|
2566
|
+
|
|
2567
|
+
# ==========================
|
|
2568
|
+
# Constructor
|
|
2569
|
+
# ==========================
|
|
2570
|
+
|
|
2571
|
+
def __init__(
|
|
2572
|
+
self,
|
|
2573
|
+
size: Sequence[int],
|
|
2574
|
+
precision: type[np.floating],
|
|
2575
|
+
execution: str,
|
|
2576
|
+
*,
|
|
2577
|
+
sm=None,
|
|
2578
|
+
transpose_mode: str = "non_transposed",
|
|
2579
|
+
arrangement: Sequence[str] | None = None,
|
|
2580
|
+
batches_per_block: int | Literal["suggested"] | None = None,
|
|
2581
|
+
data_type: str | None = None,
|
|
2582
|
+
leading_dimensions: Sequence[int] | None = None,
|
|
2583
|
+
block_dim: Sequence[int] | Literal["suggested"] | None = None,
|
|
2584
|
+
):
|
|
2585
|
+
self._solve: Solver = Solver(
|
|
2586
|
+
function="gels",
|
|
2587
|
+
size=size,
|
|
2588
|
+
precision=precision,
|
|
2589
|
+
execution=execution,
|
|
2590
|
+
sm=sm,
|
|
2591
|
+
transpose_mode=transpose_mode,
|
|
2592
|
+
arrangement=arrangement,
|
|
2593
|
+
batches_per_block=batches_per_block,
|
|
2594
|
+
data_type=data_type,
|
|
2595
|
+
leading_dimensions=leading_dimensions,
|
|
2596
|
+
block_dim=block_dim,
|
|
2597
|
+
)
|
|
2598
|
+
|
|
2599
|
+
super().__init__(self._solve)
|
|
2600
|
+
|
|
2601
|
+
# ==========================
|
|
2602
|
+
# Property methods
|
|
2603
|
+
# ==========================
|
|
2604
|
+
|
|
2605
|
+
@property
|
|
2606
|
+
def transpose_mode(self) -> str:
|
|
2607
|
+
assert self._solve.transpose_mode is not None
|
|
2608
|
+
return self._solve.transpose_mode
|
|
2609
|
+
|
|
2610
|
+
@property
|
|
2611
|
+
def tau_type(self) -> type[np.floating] | Complex:
|
|
2612
|
+
return self._solve.tau_type
|
|
2613
|
+
|
|
2614
|
+
@property
|
|
2615
|
+
def tau_shape(self) -> tuple[int, int]:
|
|
2616
|
+
return (self.batches_per_block, min(self.m, self.n))
|
|
2617
|
+
|
|
2618
|
+
@property
|
|
2619
|
+
def tau_strides(self) -> tuple[int, int]:
|
|
2620
|
+
return (self.tau_shape[1], 1)
|
|
2621
|
+
|
|
2622
|
+
@property
|
|
2623
|
+
def a_shape(self) -> tuple[int, int, int]:
|
|
2624
|
+
return (self.batches_per_block, self.m, self.n)
|
|
2625
|
+
|
|
2626
|
+
@property
|
|
2627
|
+
def b_shape(self) -> tuple[int, int, int]:
|
|
2628
|
+
return (self.batches_per_block, self.m if self.transpose_mode == "non_transposed" else self.n, self.k)
|
|
2629
|
+
|
|
2630
|
+
@property
|
|
2631
|
+
def x_shape(self) -> tuple[int, int, int]:
|
|
2632
|
+
return (self.batches_per_block, self.n if self.transpose_mode == "non_transposed" else self.m, self.k)
|
|
2633
|
+
|
|
2634
|
+
def a_strides(self, *, lda: int | None = None) -> tuple[int, int, int]:
|
|
2635
|
+
lda = self.lda if lda is None else lda
|
|
2636
|
+
return _calculate_strides(self.a_shape[1:], lda, self.a_arrangement)
|
|
2637
|
+
|
|
2638
|
+
def bx_strides(self, *, ldb: int | None = None) -> tuple[int, int, int]:
|
|
2639
|
+
ldb = self.ldb if ldb is None else ldb
|
|
2640
|
+
return _calculate_strides((max(self.m, self.n), self.k), ldb, self.b_arrangement)
|
|
2641
|
+
|
|
2642
|
+
def a_size(self, *, lda: int | None = None) -> int:
|
|
2643
|
+
return self.a_strides(lda=lda)[0] * self.a_shape[0]
|
|
2644
|
+
|
|
2645
|
+
def bx_size(self, *, ldb: int | None = None) -> int:
|
|
2646
|
+
return self.bx_strides(ldb=ldb)[0] * self.batches_per_block
|
|
2647
|
+
|
|
2648
|
+
@property
|
|
2649
|
+
def tau_size(self) -> int:
|
|
2650
|
+
return self.tau_shape[0] * self.tau_shape[1]
|
|
2651
|
+
|
|
2652
|
+
# ==========================
|
|
2653
|
+
# Device function methods
|
|
2654
|
+
# ==========================
|
|
2655
|
+
|
|
2656
|
+
def solve(self, a, tau, b, lda=None, ldb=None) -> None:
|
|
2657
|
+
"""
|
|
2658
|
+
Solves a least squares problem using QR or LQ factorization.
|
|
2659
|
+
|
|
2660
|
+
This device function solves:
|
|
2661
|
+
|
|
2662
|
+
.. math::
|
|
2663
|
+
\\min \\| op(A) * X - B \\|_2
|
|
2664
|
+
|
|
2665
|
+
using the QR or LQ factorization of A, and overwrites B with the solution X.
|
|
2666
|
+
Uses cuSOLVERDx ``'gels'``. The operation is in-place: matrix A is overwritten
|
|
2667
|
+
by the factorization, and matrix B is overwritten by the solution X.
|
|
2668
|
+
|
|
2669
|
+
If ``lda`` and ``ldb`` are provided, uses runtime version with the
|
|
2670
|
+
specified leading dimensions. If not provided (``None``), uses compile-time
|
|
2671
|
+
version with default or constructor-provided leading dimensions.
|
|
2672
|
+
|
|
2673
|
+
.. note::
|
|
2674
|
+
The choice between QR and LQ factorization depends on the problem dimensions
|
|
2675
|
+
and transpose mode:
|
|
2676
|
+
|
|
2677
|
+
* If ``op(A)`` is ``'non_transposed'`` and ``M >= N``: uses QR factorization
|
|
2678
|
+
* If ``op(A)`` is ``'non_transposed'`` and ``M < N``: uses LQ factorization
|
|
2679
|
+
* If ``op(A)`` is ``'transposed'`` or ``'conj_transposed'``
|
|
2680
|
+
and ``M >= N``: uses LQ factorization
|
|
2681
|
+
* If ``op(A)`` is ``'transposed'`` or ``'conj_transposed'``
|
|
2682
|
+
and ``M < N``: uses QR factorization
|
|
2683
|
+
|
|
2684
|
+
For more details, see: :cusolverdx_doc:`get_started/functions/gels.html`
|
|
2685
|
+
|
|
2686
|
+
Args:
|
|
2687
|
+
a: Pointer to an array in shared memory, storing the batched matrix
|
|
2688
|
+
according to the specified arrangement
|
|
2689
|
+
and leading dimension (see :class:`LeastSquaresSolver`).
|
|
2690
|
+
The matrix is overwritten in place by the QR or LQ factorization.
|
|
2691
|
+
tau: Pointer to a 1D array of size min(M, N) for each batch.
|
|
2692
|
+
Contains the scalar factors of the Householder reflections.
|
|
2693
|
+
The tau array, together with the Householder vectors stored in A,
|
|
2694
|
+
defines the unitary matrix Q.
|
|
2695
|
+
b: Pointer to an array in shared memory,
|
|
2696
|
+
storing the batched right-hand side matrix
|
|
2697
|
+
according to the specified arrangement
|
|
2698
|
+
and leading dimension (see :class:`LeastSquaresSolver`).
|
|
2699
|
+
The storage size is ``max(M, N) x K`` per batch.
|
|
2700
|
+
The operation is in-place: result X overwrites B.
|
|
2701
|
+
lda: Optional runtime leading dimension for matrix A.
|
|
2702
|
+
The ``lda`` and ``ldb`` must be specified together.
|
|
2703
|
+
If not specified, the compile-time ``lda`` is used.
|
|
2704
|
+
ldb: Optional runtime leading dimension for matrix B.
|
|
2705
|
+
The ``lda`` and ``ldb`` must be specified together.
|
|
2706
|
+
If not specified, the compile-time ``ldb`` is used.
|
|
2707
|
+
"""
|
|
2708
|
+
raise RuntimeError("solve should not be called directly outside of a jitted kernel.")
|