PSPOSVX(l)
π PSPOSVX π
okay so this is a LAPACK routine, and the version we're looking at is version 1.5. The manual name is PSPOSVX(l) β that's the one, that's our girl. She solves linear systems by Cholesky and then tells you how much to trust the answer. Main character energy, fr fr. π
πΈ NAME πΈ
PSPOSVX uses the Cholesky factorization A = U**T*U or A = L*L**T to compute the solution to a real system of linear equations
A(IA:IA+N-1,JA:JA+N-1) * X = B(IB:IB+N-1,JB:JB+NRHS-1),
like, that's the whole job bestie. π
π SYNOPSIS π
SUBROUTINE PSPOSVX( FACT, UPLO, N, NRHS, A, IA, JA, DESCA, AF, IAF, JAF,
DESCAF, EQUED, SR, SC, B, IB, JB, DESCB, X, IX, JX,
DESCX, RCOND, FERR, BERR, WORK, LWORK, IWORK,
LIWORK, INFO )
CHARACTER EQUED, FACT, UPLO
INTEGER IA, IAF, IB, INFO, IX, JA, JAF, JB, JX, LIWORK,
LWORK, N, NRHS
REAL RCOND
INTEGER DESCA( * ), DESCAF( * ), DESCB( * ), DESCX( * ),
IWORK( * )
REAL A( * ), AF( * ), B( * ), BERR( * ), FERR( * ), SC( *
), SR( * ), WORK( * ), X( * )
look at all those arguments β she's high maintenance but she's worth it, iykyk. β¨
π¦ PURPOSE π¦
So PSPOSVX takes the Cholesky factorization A = U**T*U or A = L*L**T and uses it to compute the solution to a real system of linear equations
A(IA:IA+N-1,JA:JA+N-1) * X = B(IB:IB+N-1,JB:JB+NRHS-1),
where A(IA:IA+N-1,JA:JA+N-1) is an N-by-N matrix, and X and B(IB:IB+N-1,JB:JB+NRHS-1) are N-by-NRHS matrices. π«Ά
She also hands you error bounds on the solution and a condition estimate. Not just solving it, she's grading it. Iconic.
Also, in all the comments that follow, Y denotes Y(IY:IY+M-1,JY:JY+K-1), a M-by-K matrix, where Y can be A, AF, B or X. π
π Notes π
Every global data object has an associated description vector bestie. That vector stores all the info you need to map an element of the object to its process and its memory location. It's giving β¨directoryβ¨.
Let A be a generic term for any 2D block cyclicly distributed array. That global array has an associated description vector DESCA. In the comments below, read the character _ as "of the global array". π
DTYPE_A(global), stored inDESCA( DTYPE_ )β the descriptor type. In this case,DTYPE_A = 1.CTXT_A(global), stored inDESCA( CTXT_ )β the BLACS context handle, indicating the BLACS process gridAis distributed over. The context itself is global, but the handle (the integer value) may vary.M_A(global), stored inDESCA( M_ )β the number of rows in the global arrayA.N_A(global), stored inDESCA( N_ )β the number of columns in the global arrayA.MB_A(global), stored inDESCA( MB_ )β the blocking factor used to distribute the rows of the array.NB_A(global), stored inDESCA( NB_ )β the blocking factor used to distribute the columns of the array.RSRC_A(global), stored inDESCA( RSRC_ )β the process row over which the first row of the arrayAis distributed.CSRC_A(global), stored inDESCA( CSRC_ )β the process column over which the first column of the arrayAis distributed.LLD_A(local), stored inDESCA( LLD_ )β the leading dimension of the local array.LLD_A >= MAX(1,LOCr(M_A)).
Now, let K be the number of rows or columns of a distributed matrix, and assume its process grid has dimension p x q.
LOCr( K ) is the number of elements of K that a process would receive if K were distributed over the p processes of its process column.
Similarly, LOCc( K ) is the number of elements of K that a process would receive if K were distributed over the q processes of its process row. π¦
You can get LOCr() and LOCc() by calling the ScaLAPACK tool function NUMROC:
LOCr( M ) = NUMROC( M, MB_A, MYROW, RSRC_A, NPROW ),
LOCc( N ) = NUMROC( N, NB_A, MYCOL, CSRC_A, NPCOL ).
And if you want an upper bound for these quantities, that's:
LOCr( M ) <= ceil( ceil(M/MB_A)/NPROW )*MB_A
LOCc( N ) <= ceil( ceil(N/NB_A)/NPCOL )*NB_A
math but make it cute πβ¨
π DESCRIPTION π
Here's the whole routine, step by step, no notes skipped, bestie:
- If
FACT = 'E', real scaling factors are computed to equilibrate the system:
diag(SR) * A * diag(SC) * inv(diag(SC)) * X = diag(SR) * B
Whether or not the system will be equilibrated depends on the scaling of the matrix A, but if equilibration is used, A is overwritten by diag(SR)*A*diag(SC) and B by diag(SR)*B. π³ (yes she overwrites your stuff, that's the deal)
- If
FACT = 'N'or'E', the Cholesky decomposition is used to factor the matrixA(after equilibration ifFACT = 'E') as
A = U**T* U, if UPLO = 'U', or
A = L * L**T, if UPLO = 'L',
where U is an upper triangular matrix and L is a lower triangular matrix.
- The factored form of
Ais used to estimate the condition number of the matrixA. If the reciprocal of the condition number is less than machine precision, steps 4-6 are skipped. π«’ - The system of equations is solved for
Xusing the factored form ofA. - Iterative refinement is applied to improve the computed solution matrix and calculate error bounds and backward error estimates for it. β¨ glow up β¨
- If equilibration was used, the matrix
Xis premultiplied bydiag(SR)so that it solves the original system before equilibration. π
more of this page is coming bestie, we're not done yet π
π ARGUMENTS π
okay bestie, deep breath, this is the long part but it's literally just the cast list. every single one of these is a character and they all matter, no skipping π«Ά
FACT(global input)CHARACTERβ tells us whether the factored form of matrix A is already being handed to us on entry, and if it's not, whether we should equilibrate A before factoring it. the options are:'F': on entry,AFholds the factored form of A. ifEQUED = 'Y', then A has been equilibrated with the scaling factors given byS. A and AF will not be modified. untouched, periodt.'N': A gets copied toAFand then factored. we're doing the work ourselves.'E': A gets equilibrated if it needs to be, then copied toAFand factored.
UPLO(global input)CHARACTERβ which triangle of A are we even looking at:'U': upper triangle of A is stored.'L': lower triangle of A is stored.
N(global input)INTEGERβ the number of rows and columns we're operating on, i.e. the order of the distributed submatrixA(IA:IA+N-1,JA:JA+N-1). constraint:N >= 0.NRHS(global input)INTEGERβ the number of right hand sides, i.e. the number of columns of the distributed submatricesBandX. constraint:NRHS >= 0.A(local input/local output)REALpointer into the local memory to an array of local dimension( LLD_A, LOCc(JA+N-1) ). on entry, this is the symmetric matrix A β except ifFACT = 'F'andEQUED = 'Y', in which case A must contain the equilibrated matrixdiag(SR)*A*diag(SC). ifUPLO = 'U', the leading N-by-N upper triangular part of A holds the upper triangular part of the matrix A, and the strictly lower triangular part of A is not referenced. ifUPLO = 'L', the leading N-by-N lower triangular part of A holds the lower triangular part of the matrix A, and the strictly upper triangular part of A is not referenced. A is not modified ifFACT = 'F'or'N', or ifFACT = 'E'andEQUED = 'N'on exit.
on exit, if FACT = 'E' and EQUED = 'Y', A gets overwritten by diag(SR)*A*diag(SC). glowed up and scaled π
IA(global input)INTEGERβ the row index in the global array A marking the first row ofsub( A ).JA(global input)INTEGERβ the column index in the global array A marking the first column ofsub( A ).DESCA(global and local input)INTEGERarray of dimensionDLEN_β the array descriptor for the distributed matrix A.AF(local input or local output)REALpointer into the local memory to an array of local dimension( LLD_AF, LOCc(JA+N-1)). here's the tea depending onFACT:- if
FACT = 'F', thenAFis an input argument, and on entry it contains the triangular factorUorLfrom the Cholesky factorizationA = U**T*UorA = L*L**T, in the same storage format as A. ifEQUED .ne. 'N', thenAFis the factored form of the equilibrated matrixdiag(SR)*A*diag(SC). - if
FACT = 'N', thenAFis an output argument, and on exit it returns the triangular factorUorLfrom the Cholesky factorizationA = U**T*UorA = L*L**Tof the original matrix A. - if
FACT = 'E', thenAFis an output argument, and on exit it returns the triangular factorUorLfrom the Cholesky factorizationA = U**T*UorA = L*L**Tof the equilibrated matrix A (peep the description of A for what that equilibrated matrix looks like).
- if
IAF(global input)INTEGERβ the row index in the global arrayAFmarking the first row ofsub( AF ).JAF(global input)INTEGERβ the column index in the global arrayAFmarking the first column ofsub( AF ).DESCAF(global and local input)INTEGERarray of dimensionDLEN_β the array descriptor for the distributed matrixAF.EQUED(global input/global output)CHARACTERβ says what form of equilibration went down:'N': no equilibration (always true ifFACT = 'N').'Y': equilibration was done, i.e. A has been replaced bydiag(SR) * A * diag(SC).
EQUED is an input variable if FACT = 'F'; otherwise it's an output variable. she switches roles, iconic.
SR(local input/local output)REALarray, dimension(LLD_A). the scale factors for A distributed across process rows; not accessed ifEQUED = 'N'.SRis an input variable ifFACT = 'F'; otherwiseSRis an output variable. ifFACT = 'F'andEQUED = 'Y', every element ofSRmust be positive.SC(local input/local output)REALarray, dimension(LOC(N_A)). the scale factors for A distributed across process columns; not accessed ifEQUED = 'N'.SCis an input variable ifFACT = 'F'; otherwiseSCis an output variable. ifFACT = 'F'andEQUED = 'Y', every element ofSCmust be positive.B(local input/local output)REALpointer into the local memory to an array of local dimension( LLD_B, LOCc(JB+NRHS-1) ). on entry, the N-by-NRHS right-hand side matrix B. on exit: ifEQUED = 'N', B is not modified; ifTRANS = 'N'andEQUED = 'R'or'B', B gets overwritten bydiag(R)*B; ifTRANS = 'T'or'C'andEQUED = 'C'or'B', B gets overwritten bydiag(C)*B.IB(global input)INTEGERβ the row index in the global array B marking the first row ofsub( B ).JB(global input)INTEGERβ the column index in the global array B marking the first column ofsub( B ).DESCB(global and local input)INTEGERarray of dimensionDLEN_β the array descriptor for the distributed matrix B.X(local input/local output)REALpointer into the local memory to an array of local dimension( LLD_X, LOCc(JX+NRHS-1) ). ifINFO = 0, this is the N-by-NRHS solution matrix X to the original system of equations. heads up: A and B are modified on exit ifEQUED .ne. 'N', and the solution to the equilibrated system isinv(diag(SC))*XifTRANS = 'N'andEQUED = 'C'or or'B'. (yes, that "or or" is in there, i'm just the messenger π« )IX(global input)INTEGERβ the row index in the global array X marking the first row ofsub( X ).JX(global input)INTEGERβ the column index in the global array X marking the first column ofsub( X ).DESCX(global and local input)INTEGERarray of dimensionDLEN_β the array descriptor for the distributed matrix X.RCOND(global output)REALβ the estimate of the reciprocal condition number of the matrix A after equilibration (if that happened). ifRCONDis less than the machine precision (especially ifRCOND = 0), the matrix is singular to working precision. that situation gets flagged by a return code ofINFO > 0, and the solution and error bounds are not computed. so like. nothing gets computed. it's giving "we tried" πFERR(local output)REALarray, dimension(LOC(N_B))β the estimated forward error bounds for each solution vectorX(j)(the j-th column of the solution matrix X). ifXTRUEis the true solution,FERR(j)bounds the magnitude of the largest entry in(X(j) - XTRUE)divided by the magnitude of the largest entry inX(j). the quality of that error bound depends on the quality of the estimate ofnorm(inv(A))computed in the code; if the estimate ofnorm(inv(A))is accurate, the error bound is guaranteed. accuracy is load-bearing, fr fr.BERR(local output)REALarray, dimension(LOC(N_B))β the componentwise relative backward error of each solution vectorX(j)(i.e., the smallest relative change in any entry of A or B that makesX(j)an exact solution).WORK(local workspace/local output)REALarray, dimension(LWORK). on exit,WORK(1)returns the minimal and optimalLWORK.LWORK(local or global input)INTEGERβ the dimension of the arrayWORK.LWORKis local input and must be at least
LWORK = MAX( PSPOCON( LWORK ), PSPORFS( LWORK ) ) + LOCr( N_A ).
LWORK = 3*DESCA( LLD_ )
if LWORK = -1, then LWORK is global input and a workspace query is assumed; the routine only calculates the minimum and optimal size for all work arrays. each of those values comes back in the first entry of the corresponding work array, and no error message is issued by PXERBLA. sneaky little query mode, we love her π§
IWORK(local workspace/local output)INTEGERarray, dimension(LIWORK). on exit,IWORK(1)returns the minimal and optimalLIWORK.LIWORK(local or global input)INTEGERβ the dimension of the arrayIWORK.LIWORKis local input and must be at least
LIWORK = DESCA( LLD_ )
LIWORK = LOCr(N_A).
if LIWORK = -1, then LIWORK is global input and a workspace query is assumed; the routine only calculates the minimum and optimal size for all work arrays. each of those values comes back in the first entry of the corresponding work array, and no error message is issued by PXERBLA.
INFO(global output)INTEGERβ the vibe report:= 0: successful exit. slay. β¨< 0: ifINFO = -i, the i-th argument had an illegal value.> 0: ifINFO = i, and i is<= N: ifINFO = i, the leading minor of order i of A is not positive definite, so the factorization could not be completed, and the solution and error bounds could not be computed.= N+1:RCONDis less than machine precision. the factorization has been completed, but the matrix is singular to working precision, and the solution and error bounds have not been computed.
okay that was a LOT of arguments but you ate that, bestie. every single one of them matters, no cap π
this whole page came from LAPACK version 1.5, dated 12 May 1997 β yes she's an icon from the 90s and she's still running the math world, we stan a vintage queen ππΈ
π¦ where this page comes from
- ubuntu jammy: scalapack-doc 1.5-11
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms