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,511 @@
|
|
|
1
|
+
# Copyright (c) 2026, NVIDIA CORPORATION & AFFILIATES. ALL RIGHTS RESERVED.
|
|
2
|
+
#
|
|
3
|
+
# SPDX-License-Identifier: Apache-2.0
|
|
4
|
+
|
|
5
|
+
"""Per-rank problem spec and cross-rank reducer for ``DirectSolver``."""
|
|
6
|
+
|
|
7
|
+
from __future__ import annotations
|
|
8
|
+
|
|
9
|
+
from dataclasses import dataclass, field
|
|
10
|
+
from typing import Any, Literal
|
|
11
|
+
|
|
12
|
+
from nvmath.distributed.distribution import BlockCyclic, ProcessGrid
|
|
13
|
+
|
|
14
|
+
from ._configuration import DirectSolverOptions
|
|
15
|
+
|
|
16
|
+
_SUPPORTED_DTYPES = ("float32", "float64", "complex64", "complex128")
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
# Operand-index → display label, used in error messages emitted by the
|
|
20
|
+
# leaf checks and the reducer.
|
|
21
|
+
_OPERAND_NAMES = ("A", "b")
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def _grid_coords(grid: ProcessGrid, rank: int) -> tuple[int, int]:
|
|
25
|
+
"""Return ``(myprow, mypcol)`` of ``rank`` inside a 2-D ``ProcessGrid``.
|
|
26
|
+
|
|
27
|
+
Mirrors :meth:`BlockCyclic._calc_local_shape`:
|
|
28
|
+
- COL_MAJOR: ``(r % nprow, r // nprow)``
|
|
29
|
+
- ROW_MAJOR: ``(r // npcol, r % npcol)``
|
|
30
|
+
|
|
31
|
+
Custom ``process_array`` grids are not handled.
|
|
32
|
+
"""
|
|
33
|
+
nprow, npcol = grid.shape
|
|
34
|
+
if grid.layout == ProcessGrid.Layout.COL_MAJOR:
|
|
35
|
+
return rank % nprow, rank // nprow
|
|
36
|
+
return rank // npcol, rank % npcol
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
@dataclass(slots=True)
|
|
40
|
+
class _DirectSolverProblemSpec:
|
|
41
|
+
@dataclass(slots=True)
|
|
42
|
+
class Options:
|
|
43
|
+
"""Serialisable subset of :class:`DirectSolverOptions` for the spec
|
|
44
|
+
reduction. ``logger`` is intentionally excluded.
|
|
45
|
+
The custom ``__init__`` overrides dataclass-generated init so
|
|
46
|
+
we can construct from a :class:`DirectSolverOptions`; ``__eq__`` /
|
|
47
|
+
``__repr__`` are still auto-generated from the field annotations.
|
|
48
|
+
"""
|
|
49
|
+
|
|
50
|
+
def __init__(self, options: DirectSolverOptions):
|
|
51
|
+
self.inplace_a = options.inplace_a
|
|
52
|
+
self.inplace_b = options.inplace_b
|
|
53
|
+
self.blocking = options.blocking
|
|
54
|
+
# Every rank supplies its own handle or none do,
|
|
55
|
+
# so the handle-creation path is taken uniformly.
|
|
56
|
+
self.handle_is_none = options.handle is None
|
|
57
|
+
|
|
58
|
+
inplace_a: bool = True
|
|
59
|
+
inplace_b: bool = True
|
|
60
|
+
blocking: Literal[True, "auto"] = "auto"
|
|
61
|
+
handle_is_none: bool = True
|
|
62
|
+
|
|
63
|
+
# ---- cross-rank-meaningful field ----
|
|
64
|
+
|
|
65
|
+
# local shapes of [A, b] as supplied by the caller
|
|
66
|
+
shapes: list[list[int]]
|
|
67
|
+
|
|
68
|
+
# dtype name of [A, b] as supplied by the caller
|
|
69
|
+
operands_dtypes: list[str]
|
|
70
|
+
|
|
71
|
+
packages: list[str]
|
|
72
|
+
memory_spaces: list[Literal["cuda", "cpu"]]
|
|
73
|
+
|
|
74
|
+
# We type this as ``list[Any]`` because the element type changes mid-life
|
|
75
|
+
# and neither end of the range works as a single static annotation.
|
|
76
|
+
# At construction each element is an arbitrary ``Distribution``; the
|
|
77
|
+
# converter then reassigns the list in place to ``list[BlockCyclic]``
|
|
78
|
+
# (and the finalizer subsequently does ``distributions[1] = promoted``
|
|
79
|
+
# when 1-D b is promoted to 2-D), after which downstream code reads
|
|
80
|
+
# ``BlockCyclic``-only attributes off the elements.
|
|
81
|
+
# ``Distribution`` is too loose for those read sites,
|
|
82
|
+
# ``BlockCyclic`` is too tight for the construction site, and mypy
|
|
83
|
+
# can't track the in-place narrowing, so we use ``Any`` to avoid
|
|
84
|
+
# scattering ``cast(BlockCyclic, ...)`` at every post-converter read.
|
|
85
|
+
# ``list`` (rather than ``Sequence``) so indexed-assignment paths in
|
|
86
|
+
# the finalizer typecheck. The runtime invariant is enforced by an
|
|
87
|
+
# ``isinstance(d, BlockCyclic)`` assert in the converter.
|
|
88
|
+
distributions: list[Any]
|
|
89
|
+
|
|
90
|
+
options: Options
|
|
91
|
+
|
|
92
|
+
nranks: int
|
|
93
|
+
|
|
94
|
+
# is_leaf=True means this _ProblemSpec hasn't yet been merged with another.
|
|
95
|
+
is_leaf: bool = True
|
|
96
|
+
|
|
97
|
+
# ---- leaf-only fields only meaningful while is_leaf=True; the
|
|
98
|
+
# reducer does not preserve any cross-rank meaning for them ----
|
|
99
|
+
|
|
100
|
+
# column-major flags of [A, b]
|
|
101
|
+
is_F: list[bool] = field(default_factory=list)
|
|
102
|
+
|
|
103
|
+
# int device id per operand (or "cpu")
|
|
104
|
+
device_ids: list[int | str] = field(default_factory=list)
|
|
105
|
+
|
|
106
|
+
# distributed runtime's device on this rank
|
|
107
|
+
runtime_device_id: int = -1
|
|
108
|
+
|
|
109
|
+
rank: int = -1
|
|
110
|
+
|
|
111
|
+
|
|
112
|
+
def _leaf_check_operands_ndim(p: _DirectSolverProblemSpec) -> None:
|
|
113
|
+
"""
|
|
114
|
+
Validate operand dimensionality: A is 2-D; b is 1-D (single RHS) or
|
|
115
|
+
2-D (one or more RHS). Per-axis extents are not checked here -- concrete
|
|
116
|
+
extent constraints (e.g. ``A.cols == b.rows``) need the post-allreduce
|
|
117
|
+
global shape and are validated in
|
|
118
|
+
:meth:`_ResolvedProblemSpec.from_merged_spec`.
|
|
119
|
+
"""
|
|
120
|
+
assert p.is_leaf, "p.is_leaf must be True"
|
|
121
|
+
|
|
122
|
+
A_shape, b_shape = p.shapes
|
|
123
|
+
if len(A_shape) != 2:
|
|
124
|
+
raise ValueError(f"Operand A on rank {p.rank} must be 2-D (got {len(A_shape)}-D).")
|
|
125
|
+
if len(b_shape) not in (1, 2):
|
|
126
|
+
raise ValueError(f"Operand b on rank {p.rank} must be 1-D or 2-D (got {len(b_shape)}-D).")
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
def _leaf_check_operands_dtypes_and_layout(p: _DirectSolverProblemSpec) -> None:
|
|
130
|
+
"""
|
|
131
|
+
Validate per-operand dtype is supported and the storage is column-major
|
|
132
|
+
(optionally padded along the leading dimension).
|
|
133
|
+
"""
|
|
134
|
+
assert p.is_leaf, "p.is_leaf must be True"
|
|
135
|
+
|
|
136
|
+
for i, dtype in enumerate(p.operands_dtypes):
|
|
137
|
+
if dtype not in _SUPPORTED_DTYPES:
|
|
138
|
+
raise ValueError(
|
|
139
|
+
f"Unsupported dtype {dtype} for operand {_OPERAND_NAMES[i]} on rank {p.rank}; "
|
|
140
|
+
f"supported dtypes are {_SUPPORTED_DTYPES}."
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
for i, is_F in enumerate(p.is_F):
|
|
144
|
+
if not is_F:
|
|
145
|
+
raise ValueError(
|
|
146
|
+
f"Operand {_OPERAND_NAMES[i]} on rank {p.rank} must be column-major "
|
|
147
|
+
f"(unit row stride; leading dimension >= local rows)."
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
def _leaf_check_operands_placement(p: _DirectSolverProblemSpec) -> None:
|
|
152
|
+
"""
|
|
153
|
+
Validate package/memory/device consistency and that the
|
|
154
|
+
operands sit on the same device as the distributed runtime on this rank.
|
|
155
|
+
"""
|
|
156
|
+
assert p.is_leaf, "p.is_leaf must be True"
|
|
157
|
+
|
|
158
|
+
if len(set(p.packages)) != 1:
|
|
159
|
+
raise ValueError(
|
|
160
|
+
f"The package for {_OPERAND_NAMES[0]} ({p.packages[0]}) and {_OPERAND_NAMES[1]} "
|
|
161
|
+
f"({p.packages[1]}) on process {p.rank} must be the same."
|
|
162
|
+
)
|
|
163
|
+
if len(set(p.memory_spaces)) != 1:
|
|
164
|
+
raise ValueError(
|
|
165
|
+
f"The operands on process {p.rank} are not in the same memory space: got operand memory spaces {p.memory_spaces}"
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
if len(set(p.device_ids)) != 1:
|
|
169
|
+
raise ValueError(
|
|
170
|
+
f"The device id for {_OPERAND_NAMES[0]} ({p.device_ids[0]}) and {_OPERAND_NAMES[1]} "
|
|
171
|
+
f"({p.device_ids[1]}) on process {p.rank} must be the same."
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
# When operands are on GPU, they must be on the same device as the
|
|
175
|
+
# runtime. All operands share one device id (checked just above), so
|
|
176
|
+
# one comparison covers both.
|
|
177
|
+
if p.memory_spaces[0] != "cpu" and p.device_ids[0] != p.runtime_device_id:
|
|
178
|
+
raise RuntimeError(
|
|
179
|
+
f"The operands are not on the same device as the one assigned to the "
|
|
180
|
+
f"distributed runtime on process {p.rank}: operand device ID is "
|
|
181
|
+
f"{p.device_ids[0]} and the runtime device ID is {p.runtime_device_id}"
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
|
|
185
|
+
def _check_block_cyclic_requirements(label: str, dist: BlockCyclic, rank: int) -> None:
|
|
186
|
+
# cusolverMpCreateMatrixDesc itself requires the descriptor's
|
|
187
|
+
# first-process pair (RSRC_A / CSRC_A) to be (0, 0) -- the docs
|
|
188
|
+
# state "Only the value of 0 is currently supported" for both:
|
|
189
|
+
# https://docs.nvidia.com/cuda/cusolvermp/usage/functions.html#cusolvermpcreatematrixdesc
|
|
190
|
+
if dist.first_process != (0, 0):
|
|
191
|
+
raise ValueError(
|
|
192
|
+
f"first_process for {label} on rank {rank} must be (0, 0); got {dist.first_process}. "
|
|
193
|
+
f"This most likely means you set a non-origin first_process when building the "
|
|
194
|
+
f"distribution (the first_process= argument of BlockCyclic / BlockNonCyclic). "
|
|
195
|
+
f"Note a 1-D distribution is promoted to 2-D, so a 1-D first_process like (1,) "
|
|
196
|
+
f"shows up here as (1, 0). Check the first_process you passed."
|
|
197
|
+
)
|
|
198
|
+
|
|
199
|
+
assert dist.ndim == 2, f"distribution for {label} on rank {rank} should be 2-D; got ndim={dist.ndim}"
|
|
200
|
+
|
|
201
|
+
# cusolverMpCreateDeviceGrid only accepts a COL_MAJOR or ROW_MAJOR
|
|
202
|
+
# mapping enum,
|
|
203
|
+
# see https://docs.nvidia.com/cuda/cusolvermp/usage/types.html#cusolvermpgridmapping-t
|
|
204
|
+
if dist.process_grid.layout not in (ProcessGrid.Layout.COL_MAJOR, ProcessGrid.Layout.ROW_MAJOR):
|
|
205
|
+
raise ValueError(
|
|
206
|
+
f"DirectSolver requires a COL_MAJOR or ROW_MAJOR process grid for {label} "
|
|
207
|
+
f"on rank {rank} (got layout {dist.process_grid.layout})."
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
# ----------------------------------------------------------------------
|
|
212
|
+
# Distribution contract
|
|
213
|
+
# ----------------------------------------------------------------------
|
|
214
|
+
#
|
|
215
|
+
# DirectSolver accepts ``(distribution_for_A, distribution_for_b)``.
|
|
216
|
+
# cuSOLVERMp wants 2-D block-cyclic layout
|
|
217
|
+
# (https://docs.nvidia.com/cuda/cusolvermp/getting_started/index.html#introduction),
|
|
218
|
+
# so whichever user-facing type the caller picked, ``Slab``,
|
|
219
|
+
# ``BlockNonCyclic``, or ``BlockCyclic``, we convert it to ``BlockCyclic``
|
|
220
|
+
# and used the converted distribution objects for cuSOLVERMp.
|
|
221
|
+
# "Convert" here transforms the distribution *object's type*.
|
|
222
|
+
#
|
|
223
|
+
# Once a distribution is 2-D, `_check_block_cyclic_requirements` runs to enforce
|
|
224
|
+
# the cuSOLVERMp-specific requirements: ``first_process == (0, 0)``
|
|
225
|
+
# (RSRC_A / CSRC_A must be 0, see
|
|
226
|
+
# https://docs.nvidia.com/cuda/cusolvermp/usage/functions.html#cusolvermpcreatematrixdesc)
|
|
227
|
+
# and a ``COL_MAJOR`` / ``ROW_MAJOR`` process-grid layout.
|
|
228
|
+
#
|
|
229
|
+
# Legend:
|
|
230
|
+
# shape ndim -- ndim of the local operand array the caller passed.
|
|
231
|
+
# dist ndim -- ndim declared by the distribution (``None`` for ``Slab``,
|
|
232
|
+
# which then adopts the operand's ndim).
|
|
233
|
+
# convert -- re-express the distribution object as a 2-D ``BlockCyclic``
|
|
234
|
+
# (no operand reshape).
|
|
235
|
+
#
|
|
236
|
+
# Operand shape ndim dist ndim distribution handling
|
|
237
|
+
# ------ ---------- --------- -----------------------------------------
|
|
238
|
+
# A 2 2 convert to BlockCyclic
|
|
239
|
+
# A 2 1 convert to BlockCyclic (1-D dist promoted
|
|
240
|
+
# to 2-D; no operand reshape)
|
|
241
|
+
# b 2 2 convert to BlockCyclic
|
|
242
|
+
# b 1 1 convert to BlockCyclic, then promote to 2-D
|
|
243
|
+
# b 1 2 reject in :func:`_leaf_convert_distributions`
|
|
244
|
+
# b 2 1 convert to BlockCyclic (1-D dist promoted
|
|
245
|
+
# to 2-D ``(nprow, 1)``; no operand reshape)
|
|
246
|
+
#
|
|
247
|
+
# The table only lists explicitly-dimensioned distributions. A ``Slab`` has
|
|
248
|
+
# ``dist ndim = None``: it adopts the operand's shape ndim, so it always behaves
|
|
249
|
+
# like the row whose ``dist ndim`` equals ``shape ndim``.
|
|
250
|
+
#
|
|
251
|
+
# A distribution with ``ndim`` outside the supported set ``{1, 2}`` (for both
|
|
252
|
+
# A and b) -- e.g. a ``BlockCyclic`` over a 3-D ``ProcessGrid`` -- is also
|
|
253
|
+
# rejected by the supported-ndim guard in :func:`_leaf_convert_distributions`.
|
|
254
|
+
#
|
|
255
|
+
# The pipeline is split across the leaf/cross-rank stages because the 1-D b
|
|
256
|
+
# promotion can only run *after* :func:`_cross_check_operands_ndim` confirms
|
|
257
|
+
# that every rank agrees b is 1-D (otherwise a 1-D b on one rank and a 2-D b
|
|
258
|
+
# on another would be silently equalized to the same 2-D shape):
|
|
259
|
+
#
|
|
260
|
+
# 1. :func:`_leaf_convert_distributions`
|
|
261
|
+
# 2. :func:`_leaf_check_a_distribution_requirements`
|
|
262
|
+
# 3. cross-rank checks
|
|
263
|
+
# 4. :func:`_leaf_finalize_b_distribution`
|
|
264
|
+
|
|
265
|
+
|
|
266
|
+
def _leaf_convert_distributions(p: _DirectSolverProblemSpec) -> None:
|
|
267
|
+
"""Convert each distribution to ``BlockCyclic`` with ndim matching the
|
|
268
|
+
operand's shape.
|
|
269
|
+
|
|
270
|
+
Pure mutation: no BlockCyclic-requirements check. Two pre-checks run
|
|
271
|
+
ahead of ``.to()`` so the user sees ``DirectSolver``-aware messages
|
|
272
|
+
instead of the generic ``"ndim argument (X) doesn't match..."`` from
|
|
273
|
+
:meth:`Distribution._to_checks`:
|
|
274
|
+
|
|
275
|
+
1. **Supported-ndim guard** (per operand): both A and b accept a 1-D or
|
|
276
|
+
2-D distribution. ``ndim is None`` (e.g. ``Slab``) is always allowed --
|
|
277
|
+
``.to()`` picks up our ``ndim=`` argument.
|
|
278
|
+
2. **1-D b + 2-D dist guard**: a 1-D b paired with a 2-D distribution is
|
|
279
|
+
rejected here (``npcol > 1`` would split the single RHS column across
|
|
280
|
+
process-columns) with a remediation-tailored message. The reverse, a
|
|
281
|
+
2-D b with a 1-D dist, is allowed: ``.to()`` promotes the 1-D dist to
|
|
282
|
+
its 2-D ``(nprow, 1)`` analog with no operand reshape.
|
|
283
|
+
"""
|
|
284
|
+
assert p.is_leaf, "p.is_leaf must be True"
|
|
285
|
+
|
|
286
|
+
# Cardinality check has to happen before any unpacking below so a user
|
|
287
|
+
# who passes a single distribution gets the friendly message instead of
|
|
288
|
+
# "not enough values to unpack".
|
|
289
|
+
if len(p.distributions) != 2:
|
|
290
|
+
msg = f"DirectSolver expects 2 distributions (for A, b) on rank {p.rank}; got {len(p.distributions)}."
|
|
291
|
+
raise ValueError(msg)
|
|
292
|
+
|
|
293
|
+
# Both A and b accept a 1-D or 2-D distribution: a 1-D distribution is a
|
|
294
|
+
# row-/column-only partition that is promoted to its 2-D analog. (b may
|
|
295
|
+
# itself be 1-D, the single-RHS shorthand; A is always 2-D.)
|
|
296
|
+
supported_dist_ndims = (1, 2)
|
|
297
|
+
|
|
298
|
+
new_dists: list[Any] = []
|
|
299
|
+
for i, d in enumerate(p.distributions):
|
|
300
|
+
shape_ndim = len(p.shapes[i])
|
|
301
|
+
if d.ndim is not None:
|
|
302
|
+
if d.ndim not in supported_dist_ndims:
|
|
303
|
+
raise ValueError(
|
|
304
|
+
f"Distribution for {_OPERAND_NAMES[i]} on rank {p.rank} is {d.ndim}-D. "
|
|
305
|
+
f"DirectSolver requires a 1-D or 2-D distribution for {_OPERAND_NAMES[i]}."
|
|
306
|
+
)
|
|
307
|
+
|
|
308
|
+
# The following ``if`` rejects 1-D b with a 2-D distribution.
|
|
309
|
+
# Only b can reach it: :func:`_leaf_check_operands_ndim` runs earlier
|
|
310
|
+
# in the pipeline and rejects any A that isn't 2-D, so A's
|
|
311
|
+
# ``shape_ndim`` is always 2 and never satisfies ``shape_ndim == 1``.
|
|
312
|
+
# But, a 2-D b with a 1-D dist is intentionally allowed:
|
|
313
|
+
# a 1-D dist describes a row-only partition of a (possibly multi-RHS) b,
|
|
314
|
+
# and ``.to(BlockCyclic, ndim=2)`` below promotes it to the equivalent
|
|
315
|
+
# ``(nprow, 1)`` 2-D dist with no operand reshape.
|
|
316
|
+
if shape_ndim == 1 and d.ndim == 2:
|
|
317
|
+
raise ValueError(
|
|
318
|
+
f"Distribution for b on rank {p.rank} is 2-D, but b is 1-D. Either pass b "
|
|
319
|
+
f"as a 2-D (rows, 1) array (and keep the 2-D distribution), or use a 1-D "
|
|
320
|
+
f"distribution (the 1-D-b shorthand for single-RHS solves)."
|
|
321
|
+
)
|
|
322
|
+
new_dists.append(d.to(BlockCyclic, ndim=shape_ndim, copy=True))
|
|
323
|
+
p.distributions = new_dists
|
|
324
|
+
for d in p.distributions:
|
|
325
|
+
assert isinstance(d, BlockCyclic) # only for type checker
|
|
326
|
+
|
|
327
|
+
|
|
328
|
+
def _leaf_check_a_distribution_requirements(p: _DirectSolverProblemSpec) -> None:
|
|
329
|
+
"""A's 2-D ``BlockCyclic`` requirements. Runs pre-cross-rank because A is
|
|
330
|
+
always 2-D after :func:`_leaf_convert_distributions` -- there's nothing
|
|
331
|
+
for it to wait on. b's requirements are checked post-cross-rank in
|
|
332
|
+
:func:`_leaf_finalize_b_distribution` because they may need the 1-D -> 2-D
|
|
333
|
+
promotion to have run first.
|
|
334
|
+
"""
|
|
335
|
+
assert p.is_leaf, "p.is_leaf must be True"
|
|
336
|
+
_check_block_cyclic_requirements("A", p.distributions[0], p.rank)
|
|
337
|
+
|
|
338
|
+
|
|
339
|
+
def _cross_check_operands_ndim(p1: _DirectSolverProblemSpec, p2: _DirectSolverProblemSpec) -> None:
|
|
340
|
+
"""
|
|
341
|
+
Raise if any operand's number of dimensions disagrees across ranks.
|
|
342
|
+
Must run before :func:`_leaf_finalize_b_distribution` so a 1-D b
|
|
343
|
+
on one rank and a 2-D b on another surfaces as an explicit ndim
|
|
344
|
+
mismatch instead of being silently normalized to the same 2-D shape.
|
|
345
|
+
"""
|
|
346
|
+
for i in range(2):
|
|
347
|
+
if len(p1.shapes[i]) != len(p2.shapes[i]):
|
|
348
|
+
raise ValueError(
|
|
349
|
+
f"The number of dimensions of the operand {_OPERAND_NAMES[i]} is inconsistent "
|
|
350
|
+
f"across processes: {len(p1.shapes[i])} != {len(p2.shapes[i])}"
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def _cross_check_operands_dtypes(p1: _DirectSolverProblemSpec, p2: _DirectSolverProblemSpec) -> None:
|
|
355
|
+
for i in range(2):
|
|
356
|
+
if p1.operands_dtypes[i] != p2.operands_dtypes[i]:
|
|
357
|
+
raise ValueError(
|
|
358
|
+
f"Operand {_OPERAND_NAMES[i]} dtype does not match across processes: "
|
|
359
|
+
f"{p1.operands_dtypes[i]} != {p2.operands_dtypes[i]}"
|
|
360
|
+
)
|
|
361
|
+
|
|
362
|
+
|
|
363
|
+
def _cross_check_operands_placement(p1: _DirectSolverProblemSpec, p2: _DirectSolverProblemSpec) -> None:
|
|
364
|
+
if p1.packages[0] != p2.packages[0]:
|
|
365
|
+
raise ValueError(f"Operands do not belong to the same package on all processes: {p1.packages[0]} != {p2.packages[0]}")
|
|
366
|
+
if p1.memory_spaces[0] != p2.memory_spaces[0]:
|
|
367
|
+
raise ValueError(
|
|
368
|
+
f'Operands are not in the same memory space ("cpu", "cuda") on all processes: '
|
|
369
|
+
f"{p1.memory_spaces[0]} != {p2.memory_spaces[0]}"
|
|
370
|
+
)
|
|
371
|
+
|
|
372
|
+
|
|
373
|
+
def _cross_check_options(p1: _DirectSolverProblemSpec, p2: _DirectSolverProblemSpec) -> None:
|
|
374
|
+
for attr in ("inplace_a", "inplace_b", "blocking"):
|
|
375
|
+
v1 = getattr(p1.options, attr)
|
|
376
|
+
v2 = getattr(p2.options, attr)
|
|
377
|
+
if v1 != v2:
|
|
378
|
+
raise ValueError(f"DirectSolverOptions.{attr} is inconsistent across processes: {v1!r} != {v2!r}")
|
|
379
|
+
|
|
380
|
+
# A handle's pointer value is per-rank, so only its presence is compared:
|
|
381
|
+
# either every rank supplies its own handle or none do.
|
|
382
|
+
if p1.options.handle_is_none != p2.options.handle_is_none:
|
|
383
|
+
raise ValueError(
|
|
384
|
+
"DirectSolverOptions.handle presence is inconsistent across processes: "
|
|
385
|
+
"some ranks supplied a handle while others did not; either pass a handle "
|
|
386
|
+
"on every rank or on none."
|
|
387
|
+
)
|
|
388
|
+
|
|
389
|
+
|
|
390
|
+
def _cross_check_distributions(p1: _DirectSolverProblemSpec, p2: _DirectSolverProblemSpec) -> None:
|
|
391
|
+
for i, (d1, d2) in enumerate(zip(p1.distributions, p2.distributions, strict=False)):
|
|
392
|
+
if d1 != d2:
|
|
393
|
+
raise ValueError(f"Distribution for {_OPERAND_NAMES[i]} doesn't match across processes: {d1} != {d2}")
|
|
394
|
+
|
|
395
|
+
|
|
396
|
+
def _leaf_finalize_b_distribution(p: _DirectSolverProblemSpec) -> None:
|
|
397
|
+
"""Post-cross-rank b-distribution handling.
|
|
398
|
+
|
|
399
|
+
Two responsibilities:
|
|
400
|
+
|
|
401
|
+
1. **Promote 1-D b to 2-D if needed.** Users may pass b as a 1-D array of
|
|
402
|
+
length ``rows`` (paired with a 1-D distribution) as shorthand for a
|
|
403
|
+
single-RHS solve (NRHS=1). We convert the 1-D distribution to
|
|
404
|
+
the 2-D analog of the distribution.
|
|
405
|
+
|
|
406
|
+
2. **Check b's 2-D ``BlockCyclic`` requirements.** Runs unconditionally on
|
|
407
|
+
the (now always 2-D) ``p.distributions[1]``.
|
|
408
|
+
|
|
409
|
+
Why post-cross-rank: step 1 must wait for :func:`_cross_check_operands_ndim`
|
|
410
|
+
to confirm every rank agrees b is 1-D, otherwise a 1-D b on one rank and
|
|
411
|
+
a 2-D b on another would be silently normalized to the same 2-D shape.
|
|
412
|
+
|
|
413
|
+
Precondition: :func:`_leaf_convert_distributions` has run, so
|
|
414
|
+
``p.distributions[1]`` is a ``BlockCyclic`` whose ndim matches
|
|
415
|
+
``len(p.shapes[1])``.
|
|
416
|
+
"""
|
|
417
|
+
assert p.is_leaf, "p.is_leaf must be True"
|
|
418
|
+
|
|
419
|
+
b_shape = p.shapes[1]
|
|
420
|
+
if len(b_shape) == 1:
|
|
421
|
+
dist_b = p.distributions[1]
|
|
422
|
+
assert dist_b.ndim == 1, "1-D b shape paired with non-1-D dist should have been rejected by the converter"
|
|
423
|
+
assert not dist_b._bound, "user-supplied distributions should be not bound"
|
|
424
|
+
# Promote the 1-D (nprow,) distribution to its 2-D (nprow, 1) analog.
|
|
425
|
+
# The promoted grid's layout is irrelevant since one axis is size 1.
|
|
426
|
+
p.distributions[1] = dist_b.to(BlockCyclic, ndim=2)
|
|
427
|
+
# The promoted dist has process grid (nprow, 1), so every rank owns
|
|
428
|
+
# the single global RHS column → local b is (rows, 1).
|
|
429
|
+
p.shapes[1] = [b_shape[0], 1]
|
|
430
|
+
|
|
431
|
+
_check_block_cyclic_requirements("b", p.distributions[1], p.rank)
|
|
432
|
+
|
|
433
|
+
|
|
434
|
+
def _leaf_mask_local_shapes(p: _DirectSolverProblemSpec) -> None:
|
|
435
|
+
"""
|
|
436
|
+
Mirrors ``matmulmod._problem_spec_reducer``.
|
|
437
|
+
"""
|
|
438
|
+
assert p.is_leaf, "p.is_leaf must be True"
|
|
439
|
+
|
|
440
|
+
for i in range(2):
|
|
441
|
+
d = p.distributions[i]
|
|
442
|
+
if d._is_1d_distribution():
|
|
443
|
+
continue
|
|
444
|
+
myprow, mypcol = _grid_coords(d.process_grid, p.rank)
|
|
445
|
+
if myprow != 0:
|
|
446
|
+
p.shapes[i][1] = 0
|
|
447
|
+
if mypcol != 0:
|
|
448
|
+
p.shapes[i][0] = 0
|
|
449
|
+
|
|
450
|
+
|
|
451
|
+
def _accumulate_partitioned_dims(p1: _DirectSolverProblemSpec, p2: _DirectSolverProblemSpec) -> None:
|
|
452
|
+
"""
|
|
453
|
+
Mirrors ``matmulmod._problem_spec_reducer``.
|
|
454
|
+
"""
|
|
455
|
+
for i in range(2):
|
|
456
|
+
d = p1.distributions[i]
|
|
457
|
+
partitioned_dims = (0,) if d._is_row_wise() else (1,) if d._is_col_wise() else (0, 1)
|
|
458
|
+
if len(partitioned_dims) == 1 and any(p1.shapes[i][j] != p2.shapes[i][j] for j in (0, 1) if j != partitioned_dims[0]):
|
|
459
|
+
raise ValueError(
|
|
460
|
+
f"The {_OPERAND_NAMES[i]} non-partitioned shape is inconsistent across "
|
|
461
|
+
f"processes: {p1.shapes[i]} != {p2.shapes[i]}"
|
|
462
|
+
)
|
|
463
|
+
if p1 is not p2: # nranks==1 fast path passes p1 is p2
|
|
464
|
+
for dim in partitioned_dims:
|
|
465
|
+
p1.shapes[i][dim] += p2.shapes[i][dim]
|
|
466
|
+
|
|
467
|
+
|
|
468
|
+
def _direct_solver_problem_spec_reducer(p1, p2) -> _DirectSolverProblemSpec | Exception:
|
|
469
|
+
"""
|
|
470
|
+
Pairwise reduction op for ``allreduce_object`` over the problem spec.
|
|
471
|
+
|
|
472
|
+
Running inside this reducer means every check above happens during the
|
|
473
|
+
``allreduce_object`` collective: a failure on any rank becomes an
|
|
474
|
+
``Exception``, so every rank raises the same error.
|
|
475
|
+
"""
|
|
476
|
+
# IMPORTANT: the order below matters since some checks depend on others
|
|
477
|
+
# do not change it before understanding the dependencies.
|
|
478
|
+
|
|
479
|
+
try:
|
|
480
|
+
# Early exit on an ``Exception`` from an earlier merge or local build.
|
|
481
|
+
if isinstance(p1, Exception):
|
|
482
|
+
return p1
|
|
483
|
+
if isinstance(p2, Exception):
|
|
484
|
+
return p2
|
|
485
|
+
|
|
486
|
+
for p in (p1, p2):
|
|
487
|
+
if p.is_leaf:
|
|
488
|
+
_leaf_check_operands_ndim(p)
|
|
489
|
+
_leaf_check_operands_dtypes_and_layout(p)
|
|
490
|
+
_leaf_check_operands_placement(p)
|
|
491
|
+
_leaf_convert_distributions(p)
|
|
492
|
+
_leaf_check_a_distribution_requirements(p)
|
|
493
|
+
|
|
494
|
+
_cross_check_operands_ndim(p1, p2)
|
|
495
|
+
_cross_check_operands_dtypes(p1, p2)
|
|
496
|
+
_cross_check_operands_placement(p1, p2)
|
|
497
|
+
_cross_check_options(p1, p2)
|
|
498
|
+
_cross_check_distributions(p1, p2)
|
|
499
|
+
|
|
500
|
+
for p in (p1, p2):
|
|
501
|
+
if p.is_leaf:
|
|
502
|
+
_leaf_finalize_b_distribution(p)
|
|
503
|
+
_leaf_mask_local_shapes(p)
|
|
504
|
+
|
|
505
|
+
_accumulate_partitioned_dims(p1, p2)
|
|
506
|
+
|
|
507
|
+
except Exception as e:
|
|
508
|
+
return e
|
|
509
|
+
|
|
510
|
+
p1.is_leaf = False
|
|
511
|
+
return p1
|