QR Factorization#
GEQRF (GEneral QR Factorization) function computes batched QR factorization of a general matrix:
using Householder reflection transformations, where:
\(A\) is a batched \(M \times N\) matrix, with leading dimension \(\mathrm{lda} \geq M\) if matrix
Ais in column-major layout, or \(\mathrm{lda} \geq N\) if matrixAis row-major,\(Q\) is a unitary \(M \times M\) matrix, and
\(R\) is an upper triangular matrix if \(M \geq N\), or upper trapezoidal matrix if \(M < N\).
cuSolverDx geqrf device functions are (see Execution Methods):
__device__ void execute(data_type* A, data_type* tau);
// with the runtime lda
__device__ void execute(data_type* A, const unsigned int lda, data_type* tau);
After the function, the upper triangular or upper trapezoidal part of input A, including diagonal elements, is replaced by the matrix R.
Matrix \(Q\) is not explicitly formed. The elements below the diagonal of A, with the array tau, represent the matrix \(Q\) as a product of \(\min(M, N)\) Householder vectors:
Each Householder vector has the form \(H(i) = I - \mathrm{tau}[i] v v^H\), where:
tauis an array of size \(\min(M, N)\) for each batch, andvis a vector of sizeMfor each batch, withv[0 : i-1] = 0,v[i] = 1, andv[i+1 : M]is stored on exit inA[i+1 : M, i].
The functions support A being either column- or row-major memory layout, see Arrangement operator.
Warning
Unlike LAPACK’s geqrf, cuSolverDx geqrf functions do not guarantee the diagonal of R is always real valued for complex inputs and applies reflectors in some places they could be skipped.
Cluster execution (experimental support starting from version 0.6.0)#
Starting from cuSolverDx 0.6.0, Cluster operator provides an experimental GEQRF path for matrices that do not fit in a single thread block’s shared memory. A thread block cluster stores a tile of A in distributed shared memory (DSMEM) and runs a binary-tree reduction across BlocksPerCluster (BPC) cooperating CTAs, then reconstructs compact Householder vectors matching the standard GEQRF output layout (Algorithm 9 of Ballard et al. [1]).
Row tiling the global matrix
The tall M x N matrix is partitioned into BPC tiles. Each CTA owns one tile of size TileSize x N, where:
To be able to use the cluster execution:
the matrix size must satisfy
M >= N * BPC;BlocksPerClustermust be a power of two; andrequired shared memory size must not exceed the total shared memory size available on the selected GPU.
When BPC = 1, cluster GEQRF falls back to the ordinary block GEQRF path.
Each CTA allocates Solver::shared_memory_size bytes for its local TileSize x N tile, the length-N tau buffer, and the internal workspace (Solver::workspace_size elements). Remote tiles are accessed through DSMEM via cluster.map_shared_rank.
Example ( M = 1023, N = 32, BPC = 4 ):
NB = ceil(1023 / 4) = 256
block 0: rows [0, 256)
block 1: rows [256, 512)
block 2: rows [512, 768)
block 3: rows [768, 1023)
Load/store helpers
Before calling the cluster execution device function, load each CTA’s row tile from global memory into DSMEM, then store the factored tiles back afterward. Use the description member functions:
Solver::load_global_to_dsmem(A, lda, a_local)— each cluster block loads itsTileSize x Nrow tile from global memory into local shared memorySolver::store_dsmem_to_global(A, lda, a_local)— the reverse store afterexecute
Note
Cluster execute expects the tile workspace in shared memory to use exactly the same distribution and layout as produced by load_global_to_dsmem (and read back by store_dsmem_to_global): same TileSize = ceil(M / BPC) row ownership and arrangement. If you load or arrange tiles differently, execute will not factor the matrix correctly.
Important
Output tau array of the entire matrix is written by block 0 into its local shared memory tau buffer during the execute function. Only copy the local tau shared memory buffer in the first thread block to global memory after the cluster execution function returns.
Device function API
Cluster execute function signature is:
__device__ void execute(data_type* A, data_type* tau, data_type* workspace);
Amust point to the localTileSize x Ntile in shared memory (not the full global matrix). There is no runtimeldaargument.tauis a length-Nbuffer in shared memory.workspaceis a temporary buffer ofSolver::workspace_sizeelements in shared memory (included inSolver::shared_memory_size).
TileSize is fixed for GEQRF cluster: it must equal TileSize = ceil(M / BPC) (also exposed as Solver::suggested_tile_size). See geqrf_batched_cluster for a complete kernel.