larfb_gett(3)
๐ NAME ๐
larfb_gett โ okay so this is a <pink>step in ungtsqr_row</pink>, bestie. That's the whole name section. Short and iconic. Also hi, this is the larfb_gett(3) page, manual name folded in nice and tidy like we promised โจ
๐ธ SYNOPSIS ๐ธ
๐ Functions
subroutine clarfb_gett (ident, m, n, k, t, ldt, a, lda, b, ldb, work, ldwork)โCLARFB_GETTsubroutine dlarfb_gett (ident, m, n, k, t, ldt, a, lda, b, ldb, work, ldwork)โDLARFB_GETTsubroutine slarfb_gett (ident, m, n, k, t, ldt, a, lda, b, ldb, work, ldwork)โSLARFB_GETTsubroutine zlarfb_gett (ident, m, n, k, t, ldt, a, lda, b, ldb, work, ldwork)โZLARFB_GETT
Four flavors, same energy. Pick your precision, queen ๐
๐ฆ Detailed Description ๐ฆ
Lowkey this is where the actual lore lives โ keep scrolling for the Function Documentation and the details, because this page has SO much more coming (like, seven parts of more, we're just getting started ๐ซถ).
๐ Function Documentation ๐
โญ subroutine clarfb_gett (character ident, integer m, integer n, integer k, complex, dimension( ldt, * ) t, integer ldt, complex, dimension( lda, * ) a, integer lda, complex, dimension( ldb, * ) b, integer ldb, complex, dimension( ldwork, * ) work, integer ldwork)
CLARFB_GETT
๐ Purpose:
!>
!> CLARFB_GETT applies a complex Householder block reflector H from the
!> left to a complex (K+M)-by-N matrix
!> composed of two block matrices: an upper trapezoidal K-by-N matrix A
!> stored in the array A, and a rectangular M-by-(N-K) matrix B, stored
!> in the array B. The block reflector H is stored in a compact
!> WY-representation, where the elementary reflectors are in the
!> arrays A, B and T. See Further Details section.
!>
So like โ CLARFB_GETT takes this complex Householder block reflector H and applies it from the left to a complex (K+M)-by-N matrix, which is literally two block matrices glued together: an upper trapezoidal K-by-N matrix A (living in the array A) and a rectangular M-by-(N-K) matrix B (living in the array B). The reflector H is stored in a compact WY-representation, with the elementary reflectors chillin' in A, B and T. The Further Details section has the receipts โจ
๐ Parameters
IDENT
!> IDENT is CHARACTER*1
!> If IDENT = not 'I', or not 'i', then V1 is unit
!> lower-triangular and stored in the left K-by-K block of
!> the input matrix A,
!> If IDENT = 'I' or 'i', then V1 is an identity matrix and
!> not stored.
!> See Further Details section.
!>
IDENT is a CHARACTER*1. If it's not 'I' or 'i', then V1 is unit lower-triangular and it IS stored in the left K-by-K block of the input matrix A. If IDENT is 'I' or 'i', then V1 is an identity matrix and it's simply not stored at all. The Further Details section knows more ๐
M
!> M is INTEGER
!> The number of rows of the matrix B.
!> M >= 0.
!>
M is INTEGER. It's the number of rows of matrix B. And M >= 0, no exceptions bestie.
N
!> N is INTEGER
!> The number of columns of the matrices A and B.
!> N >= 0.
!>
N is INTEGER, the number of columns of the matrices A and B. Also N >= 0. Slay.
K
!> K is INTEGER
!> The number or rows of the matrix A.
!> K is also order of the matrix T, i.e. the number of
!> elementary reflectors whose product defines the block
!> reflector. 0 <= K <= N.
!>
K is INTEGER, the number of rows of matrix A. It's also the order of matrix T โ like, the number of elementary reflectors whose product defines the block reflector. Constraint: 0 <= K <= N. Periodt.
T
!> T is COMPLEX array, dimension (LDT,K)
!> The upper-triangular K-by-K matrix T in the representation
!> of the block reflector.
!>
T is a COMPLEX array, dimension (LDT,K) โ the upper-triangular K-by-K matrix T in the block reflector representation ๐
LDT
!> LDT is INTEGER
!> The leading dimension of the array T. LDT >= K.
!>
LDT is INTEGER, the leading dimension of array T. Respect the LDT >= K, fr fr.
A
!> A is COMPLEX array, dimension (LDA,N)
!>
!> On entry:
!> a) In the K-by-N upper-trapezoidal part A: input matrix A.
!> b) In the columns below the diagonal: columns of V1
!> (ones are not stored on the diagonal).
!>
!> On exit:
!> A is overwritten by rectangular K-by-N product H*A.
!>
!> See Further Details section.
!>
A is a COMPLEX array, dimension (LDA,N). On entry you got two things goin' on: a) in the K-by-N upper-trapezoidal part A there's your input matrix A, and b) in the columns below the diagonal, the columns of V1 โ and no, the ones are not stored on the diagonal, iykyk. On exit, A gets overwritten by the rectangular K-by-N product H*A. Check the Further Details section for the full tea ๐ธ
LDA
!> LDB is INTEGER
!> The leading dimension of the array A. LDA >= max(1,K).
!>
LDA is INTEGER (yes, the page says LDB right here โ we are faithfully reporting, not judging ๐). It's the leading dimension of array A, and LDA >= max(1,K).
B
!> B is COMPLEX array, dimension (LDB,N)
!>
!> On entry:
!> a) In the M-by-(N-K) right block: input matrix B.
!> b) In the M-by-N left block: columns of V2.
!>
!> On exit:
!> B is overwritten by rectangular M-by-N product H*B.
!>
!> See Further Details section.
!>
B is a COMPLEX array, dimension (LDB,N). On entry: a) in the M-by-(N-K) right block lives your input matrix B, and b) in the M-by-N left block, the columns of V2. On exit, B is overwritten by the rectangular M-by-N product H*B. Further Details section, as always ๐
LDB
!> LDB is INTEGER
!> The leading dimension of the array B. LDB >= max(1,M).
!>
LDB is INTEGER, the leading dimension of array B, with LDB >= max(1,M). โจ
WORK
!> WORK is COMPLEX array,
!> dimension (LDWORK,max(K,N-K))
!>
WORK is a COMPLEX array with dimension (LDWORK,max(K,N-K)). Do your thing, workspace ๐ซถ
LDWORK
!> LDWORK is INTEGER
!> The leading dimension of the array WORK. LDWORK>=max(1,K).
!>
!>
LDWORK is INTEGER, the leading dimension of array WORK, and LDWORK>=max(1,K). That's the vibe check, passed ๐ฅ
๐ Author
Univ. of Tennessee
Univ. of California Berkeley
Univ. of Colorado Denver
NAG Ltd.
๐ง Contributors:
!>
!> November 2020, Igor Kozachenko,
!> Computer Science Division,
!> University of California, Berkeley
!>
!>
November 2020, Igor Kozachenko, Computer Science Division, University of California, Berkeley โ shoutout, icon ๐
๐ Further Details:
hold that thought bestie, this is where part 1 hands off โ the details are literally right there in the next bit and we are NOT done yet ๐
okay so this is the part where it gets mathematical ๐ but don't panic โ it's literally just "here's what's in the boxes, here's what comes out the other side." i gotchu.
๐ (1) Description of the Algebraic Operation
so. the matrix A is a K-by-N matrix made of two column blocks: A1, which is K-by-K, and A2, which is K-by-(N-K). like this:
A = ( A1, A2 ).
and the matrix B is an M-by-N matrix, also two column blocks: B1, which is M-by-K, and B2, which is M-by-(N-K). like this:
B = ( B1, B2 ).
here's the operation we're performing, no cap:
( A_out ) := H * ( A_in ) = ( I - V * T * V**H ) * ( A_in ) =
( B_out ) ( B_in ) ( B_in )
= ( I - ( V1 ) * T * ( V1**H, V2**H ) ) * ( A_in )
( V2 ) ( B_in )
on input: โจ
a) ( A_in ) over ( B_in ) consists of two block columns:
( A_in ) = (( A1_in ) ( A2_in )) = (( A1_in ) ( A2_in ))
( B_in ) (( B1_in ) ( B2_in )) (( 0 ) ( B2_in )),
where the column blocks are:
( A1_in )is a K-by-K upper-triangular matrix stored in the upper triangular part of the arrayA(1:K,1:K).( B1_in )is an M-by-K rectangular ZERO matrix and not stored.( A2_in )is a K-by-(N-K) rectangular matrix stored in the arrayA(1:K,K+1:N).( B2_in )is an M-by-(N-K) rectangular matrix stored in the arrayB(1:M,K+1:N).
b) V, which is built like this:
b) V = ( V1 )
( V2 )
where:
- if
IDENT == 'I',V1is a K-by-K identity matrix, not stored; - if
IDENT != 'I',V1is a K-by-K unit lower-triangular matrix, stored in the lower-triangular part of the arrayA(1:K,1:K)(ones are not stored),
and V2 is an M-by-K rectangular stored the array B(1:M,1:K), (because on input B1_in is a rectangular zero matrix that is not stored and the space is used to store V2).
c) T is a K-by-K upper-triangular matrix stored in the array T(1:K,1:K).
on output: ๐ธ
a) ( A_out ) over ( B_out ) consists of two block columns:
( A_out ) = (( A1_out ) ( A2_out ))
( B_out ) (( B1_out ) ( B2_out )),
where the column blocks are:
( A1_out )is a K-by-K square matrix, or a K-by-K upper-triangular matrix, ifV1is an identity matrix. AiOut is stored in the arrayA(1:K,1:K).( B1_out )is an M-by-K rectangular matrix stored in the arrayB(1:M,K:N).( A2_out )is a K-by-(N-K) rectangular matrix stored in the arrayA(1:K,K+1:N).( B2_out )is an M-by-(N-K) rectangular matrix stored in the arrayB(1:M,K+1:N).
the operation above can be represented as the same operation on each block column, which is honestly so satisfying:
( A1_out ) := H * ( A1_in ) = ( I - V * T * V**H ) * ( A1_in )
( B1_out ) ( 0 ) ( 0 )
( A2_out ) := H * ( A2_in ) = ( I - V * T * V**H ) * ( A2_in )
( B2_out ) ( B2_in ) ( B2_in )
if IDENT != 'I':
the computation for column block 1:
A1_out: = A1_in - V1*T*(V1**H)*A1_in
B1_out: = - V2*T*(V1**H)*A1_in
the computation for column block 2, which exists if N > K:
A2_out: = A2_in - V1*T*( (V1**H)*A2_in + (V2**H)*B2_in )
B2_out: = B2_in - V2*T*( (V1**H)*A2_in + (V2**H)*B2_in )
if IDENT == 'I':
the operation for column block 1:
A1_out: = A1_in - V1*T*A1_in
B1_out: = - V2*T*A1_in
the computation for column block 2, which exists if N > K:
A2_out: = A2_in - T*( A2_in + (V2**H)*B2_in )
B2_out: = B2_in - V2*T*( A2_in + (V2**H)*B2_in )
๐งฎ (2) Description of the Algorithmic Computation
in the first step, we compute column block 2, i.e. A2 and B2. here, we need to use the K-by-(N-K) rectangular workspace matrix W2 that is of the same size as the matrix A2. W2 is stored in the array WORK(1:K,1:(N-K)).
in the second step, we compute column block 1, i.e. A1 and B1. here, we need to use the K-by-K square workspace matrix W1 that is of the same size as the as the matrix A1. W1 is stored in the array WORK(1:K,1:K).
NOTE: hence, in this routine, we need the workspace arrayWORKonly of sizeWORK(1:K,1:max(K,N-K))so it can hold bothW2from the first step andW1from the second step.
case (A), when V1 is unit lower-triangular, i.e. IDENT != 'I', more computations than in the Case (B). ๐ซถ
if( IDENT != 'I' ) then
if ( N > K ) then
(First Step - column block 2)
col2_(1) W2: = A2
col2_(2) W2: = (V1**H) * W2 = (unit_lower_tr_of_(A1)**H) * W2
col2_(3) W2: = W2 + (V2**H) * B2 = W2 + (B1**H) * B2
col2_(4) W2: = T * W2
col2_(5) B2: = B2 - V2 * W2 = B2 - B1 * W2
col2_(6) W2: = V1 * W2 = unit_lower_tr_of_(A1) * W2
col2_(7) A2: = A2 - W2
else
(Second Step - column block 1)
col1_(1) W1: = A1
col1_(2) W1: = (V1**H) * W1 = (unit_lower_tr_of_(A1)**H) * W1
col1_(3) W1: = T * W1
col1_(4) B1: = - V2 * W1 = - B1 * W1
col1_(5) square W1: = V1 * W1 = unit_lower_tr_of_(A1) * W1
col1_(6) square A1: = A1 - W1
end if
end if
case (B), when V1 is an identity matrix, i.e. IDENT == 'I', less computations than in the Case (A):
if( IDENT == 'I' ) then
if ( N > K ) then
(First Step - column block 2)
col2_(1) W2: = A2
col2_(3) W2: = W2 + (V2**H) * B2 = W2 + (B1**H) * B2
col2_(4) W2: = T * W2
col2_(5) B2: = B2 - V2 * W2 = B2 - B1 * W2
col2_(7) A2: = A2 - W2
else
(Second Step - column block 1)
col1_(1) W1: = A1
col1_(3) W1: = T * W1
col1_(4) B1: = - V2 * W1 = - B1 * W1
col1_(6) upper-triangular_of_(A1): = A1 - W1
end if
end if
combine these cases (A) and (B) together, this is the resulting algorithm (she's iconic, she contains multitudes):
if ( N > K ) then
(First Step - column block 2)
col2_(1) W2: = A2
if( IDENT != 'I' ) then
col2_(2) W2: = (V1**H) * W2
= (unit_lower_tr_of_(A1)**H) * W2
end if
col2_(3) W2: = W2 + (V2**H) * B2 = W2 + (B1**H) * B2]
col2_(4) W2: = T * W2
col2_(5) B2: = B2 - V2 * W2 = B2 - B1 * W2
if( IDENT != 'I' ) then
col2_(6) W2: = V1 * W2 = unit_lower_tr_of_(A1) * W2
end if
col2_(7) A2: = A2 - W2
else
(Second Step - column block 1)
col1_(1) W1: = A1
if( IDENT != 'I' ) then
col1_(2) W1: = (V1**H) * W1
= (unit_lower_tr_of_(A1)**H) * W1
end if
col1_(3) W1: = T * W1
col1_(4) B1: = - V2 * W1 = - B1 * W1
if( IDENT != 'I' ) then
col1_(5) square W1: = V1 * W1 = unit_lower_tr_of_(A1) * W1
col1_(6_a) below_diag_of_(A1): = - below_diag_of_(W1)
end if
col1_(6_b) up_tr_of_(A1): = up_tr_of_(A1) - up_tr_of_(W1)
end if
okay and that's the whole vibe of the first version. Definition at line 390 of file clarfb_gett.f.
๐ฆ subroutine dlarfb_gett
subroutine dlarfb_gett (character ident, integer m, integer n, integer k,
double precision, dimension( ldt, * ) t, integer ldt, double precision,
dimension( lda, * ) a, integer lda, double precision, dimension( ldb, *
) b, integer ldb, double precision, dimension( ldwork, * ) work, integer
ldwork)
DLARFB_GETT โ miss thing has arrived ๐
Purpose
okay so, what does she even do:
DLARFB_GETT applies a real Householder block reflector H from the left to a real (K+M)-by-N matrix composed of two block matrices: an upper trapezoidal K-by-N matrix A stored in the array A, and a rectangular M-by-(N-K) matrix B, stored in the array B. the block reflector H is stored in a compact WY-representation, where the elementary reflectors are in the arrays A, B and T. see the Further Details section โจ
Parameters
IDENTโ isCHARACTER*1- if
IDENT = not 'I', or not 'i', thenV1is unit lower-triangular and stored in the left K-by-K block of the input matrixA, - if
IDENT = 'I' or 'i', thenV1is an identity matrix and not stored. - see the Further Details section.
- if
Mโ isINTEGER. the number of rows of the matrixB.M >= 0.Nโ isINTEGER. the number of columns of the matricesAandB.N >= 0.Kโ isINTEGER. the number or rows of the matrixA.Kis also order of the matrixT, i.e. the number of elementary reflectors whose product defines the block reflector.0 <= K <= N.TTโ aDOUBLE PRECISIONarray, dimension(LDT,K). This is the upper-triangular K-by-K matrixTliving inside the representation of the block reflector. She's the triangle. She's iconic. ๐LDTโINTEGER. The leading dimension of the arrayT, and the vibe check requiresLDT >= K. no cap โจAโDOUBLE PRECISIONarray, dimension(LDA,N). Okay so this one has a whole before-and-after arc:
On entry, bestie:
- a) In the K-by-N upper-trapezoidal part
A: the input matrixA. Like, the actual data. - b) In the columns below the diagonal: the columns of
V1โ and heads up, the ones are not stored on the diagonal. they're implied. we stan an efficient queen ๐
On exit:
Ais overwritten by the rectangular K-by-N productH*A. rip originalA, you had a good run ๐ญ
See the Further Details section, iykyk.
LDAโ so the original page literally saysLDB is INTEGERright here, and honestly? we're not fixing it, we're just reporting it ๐ What it means is: this isINTEGER, the leading dimension of the arrayA, andLDA >= max(1,K). Periodt.BโDOUBLE PRECISIONarray, dimension(LDB,N). Same duality, different letter:
On entry:
- a) In the M-by-(N-K) right block: the input matrix
B. - b) In the M-by-N left block: the columns of
V2.
On exit:
Bis overwritten by the rectangular M-by-N productH*B. gone but not forgotten ๐ฆ
See Further Details section again โ yes, both of them point there.
WORKโDOUBLE PRECISIONarray, dimension(LDWORK,max(K,N-K)). It's the workspace. It's doing the behind-the-scenes labor and getting zero credit, main character energy tbh โญLDWORKโINTEGER, the leading dimension of the arrayWORK, and the requirement isLDWORK>=max(1,K). don't skip it ๐ฅ
๐ Author
Univ. of Tennessee
Univ. of California Berkeley
Univ. of Colorado Denver
NAG Ltd.
๐ Contributors
November 2020, Igor Kozachenko, Computer Science Division, University of California, Berkeley
๐ Further Details:
okay bestie the details go here โ next part, don't go anywhere ๐ซถ
(1) Description of the Algebraic Operation ๐
okay so bestie, picture the setup: A is a K-by-N matrix made of two column blocks โ A1 is K-by-K, and A2 is K-by-(N-K). So literally:
A = ( A1, A2 ).
And B is an M-by-N matrix made of two column blocks โ B1 is M-by-K, and B2 is M-by-(N-K):
B = ( B1, B2 ).
Then the operation being performed is:
( A_out ) := H * ( A_in ) = ( I - V * T * V**T ) * ( A_in ) =
( B_out ) ( B_in ) ( B_in )
= ( I - ( V1 ) * T * ( V1**T, V2**T ) ) * ( A_in )
( V2 ) ( B_in )
On input, ( A_in ) / ( B_in ) consists of two block columns:
( A_in ) = (( A1_in ) ( A2_in )) = (( A1_in ) ( A2_in ))
( B_in ) (( B1_in ) ( B2_in )) (( 0 ) ( B2_in )),
where the column blocks are:
A1_inis aK-by-Kupper-triangular matrix stored in the upper triangular part ofA(1:K,1:K).B1_inis anM-by-Krectangular ZERO matrix and not stored.A2_inis aK-by-(N-K)rectangular matrix stored inA(1:K,K+1:N).B2_inis anM-by-(N-K)rectangular matrix stored inB(1:M,K+1:N).
Then V is split like this:
V = ( V1 )
( V2 )
where:
- if
IDENT == 'I',V1is aK-by-Kidentity matrix, not stored; - if
IDENT != 'I',V1is aK-by-Kunit lower-triangular matrix, stored in the lower-triangular part ofA(1:K,1:K)(ones are not stored),
and V2 is an M-by-K rectangular matrix stored in B(1:M,1:K) โ because on input B1_in is a rectangular zero matrix that is not stored and the space is used to store V2.
Also, T is a K-by-K upper-triangular matrix stored in T(1:K,1:K).
On output, ( A_out ) / ( B_out ) consists of two block columns:
( A_out ) = (( A1_out ) ( A2_out ))
( B_out ) (( B1_out ) ( B2_out )),
where the column blocks are:
A1_outis aK-by-Ksquare matrix, or aK-by-Kupper-triangular matrix ifV1is an identity matrix.AiOutis stored inA(1:K,1:K).B1_outis anM-by-Krectangular matrix stored inB(1:M,K:N).A2_outis aK-by-(N-K)rectangular matrix stored inA(1:K,K+1:N).B2_outis anM-by-(N-K)rectangular matrix stored inB(1:M,K+1:N).
The operation above can be represented as the same operation on each block column:
( A1_out ) := H * ( A1_in ) = ( I - V * T * V**T ) * ( A1_in )
( B1_out ) ( 0 ) ( 0 )
( A2_out ) := H * ( A2_in ) = ( I - V * T * V**T ) * ( A2_in )
( B2_out ) ( B2_in ) ( B2_in )
If IDENT != 'I', the computation for column block 1 is:
A1_out: = A1_in - V1*T*(V1**T)*A1_in
B1_out: = - V2*T*(V1**T)*A1_in
The computation for column block 2, which exists if N > K, is:
A2_out: = A2_in - V1*T*( (V1**T)*A2_in + (V2**T)*B2_in )
B2_out: = B2_in - V2*T*( (V1**T)*A2_in + (V2**T)*B2_in )
If IDENT == 'I', the operation for column block 1 is:
A1_out: = A1_in - V1*T**A1_in
B1_out: = - V2*T**A1_in
The computation for column block 2, which exists if N > K, is:
A2_out: = A2_in - T*( A2_in + (V2**T)*B2_in )
B2_out: = B2_in - V2*T*( A2_in + (V2**T)*B2_in )
(2) Description of the Algorithmic Computation โจ
In the first step, we compute column block 2, i.e. A2 and B2. Here, we need to use the K-by-(N-K) rectangular workspace matrix W2 that is of the same size as the matrix A2. W2 is stored in the array WORK(1:K,1:(N-K)).
In the second step, we compute column block 1, i.e. A1 and B1. Here, we need to use the K-by-K square workspace matrix W1 that is of the same size as the matrix A1. W1 is stored in the array WORK(1:K,1:K).
NOTE: Hence, in this routine, we need the workspace arrayWORKonly of sizeWORK(1:K,1:max(K,N-K))so it can hold bothW2from the first step andW1from the second step.
Case (A), when V1 is unit lower-triangular, i.e. IDENT != 'I', has more computations than in Case (B):
if( IDENT != 'I' ) then
if ( N > K ) then
(First Step - column block 2)
col2_(1) W2: = A2
col2_(2) W2: = (V1**T) * W2 = (unit_lower_tr_of_(A1)**T) * W2
col2_(3) W2: = W2 + (V2**T) * B2 = W2 + (B1**T) * B2
col2_(4) W2: = T * W2
col2_(5) B2: = B2 - V2 * W2 = B2 - B1 * W2
col2_(6) W2: = V1 * W2 = unit_lower_tr_of_(A1) * W2
col2_(7) A2: = A2 - W2
else
(Second Step - column block 1)
col1_(1) W1: = A1
col1_(2) W1: = (V1**T) * W1 = (unit_lower_tr_of_(A1)**T) * W1
col1_(3) W1: = T * W1
col1_(4) B1: = - V2 * W1 = - B1 * W1
col1_(5) square W1: = V1 * W1 = unit_lower_tr_of_(A1) * W1
col1_(6) square A1: = A1 - W1
end if
end if
Case (B), when V1 is an identity matrix, i.e. IDENT == 'I', has less computations than in Case (A):
if( IDENT == 'I' ) then
if ( N > K ) then
(First Step - column block 2)
col2_(1) W2: = A2
col2_(3) W2: = W2 + (V2**T) * B2 = W2 + (B1**T) * B2
col2_(4) W2: = T * W2
col2_(5) B2: = B2 - V2 * W2 = B2 - B1 * W2
col2_(7) A2: = A2 - W2
else
(Second Step - column block 1)
col1_(1) W1: = A1
col1_(3) W1: = T * W1
col1_(4) B1: = - V2 * W1 = - B1 * W1
col1_(6) upper-triangular_of_(A1): = A1 - W1
end if
end if
Combine these cases (A) and (B) together, and this is the resulting algorithm:
if ( N > K ) then
(First Step - column block 2)
col2_(1) W2: = A2
if( IDENT != 'I' ) then
col2_(2) W2: = (V1**T) * W2
= (unit_lower_tr_of_(A1)**T) * W2
end if
col2_(3) W2: = W2 + (V2**T) * B2 = W2 + (B1**T) * B2]
col2_(4) W2: = T * W2
col2_(5) B2: = B2 - V2 * W2 = B2 - B1 * W2
if( IDENT != 'I' ) then
col2_(6) W2: = V1 * W2 = unit_lower_tr_of_(A1) * W2
end if
col2_(7) A2: = A2 - W2
else
(Second Step - column block 1)
col1_(1) W1: = A1
if( IDENT != 'I' ) then
col1_(2) W1: = (V1**T) * W1
= (unit_lower_tr_of_(A1)**T) * W1
end if
col1_(3) W1: = T * W1
col1_(4) B1: = - V2 * W1 = - B1 * W1
if( IDENT != 'I' ) then
col1_(5) square W1: = V1 * W1 = unit_lower_tr_of_(A1) * W1
col1_(6_a) below_diag_of_(A1): = - below_diag_of_(W1)
end if
col1_(6_b) up_tr_of_(A1): = up_tr_of_(A1) - up_tr_of_(W1)
end if
Definition at line 390 of file dlarfb_gett.f.
subroutine slarfb_gett ๐
subroutine slarfb_gett (character ident, integer m, integer n, integer k,
real, dimension( ldt, * ) t, integer ldt, real, dimension( lda, * ) a,
integer lda, real, dimension( ldb, * ) b, integer ldb, real, dimension(
ldwork, * ) work, integer ldwork)
SLARFB_GETT
Purpose ๐
SLARFB_GETT applies a real Householder block reflector H from the left to a real (K+M)-by-N matrix composed of two block matrices: an upper trapezoidal K-by-N matrix A stored in the array A, and a rectangular M-by-(N-K) matrix B, stored in the array B. The block reflector H is stored in a compact WY-representation, where the elementary reflectors are in the arrays A, B and T. See Further Details section.
Parameters ๐ธ
IDENTโIDENTisCHARACTER*1.- If
IDENT= not'I', or not'i', thenV1is unit lower-triangular and stored in the leftK-by-Kblock of the input matrixA. - If
IDENT='I'or'i', thenV1is an identity matrix and not stored. - See Further Details section.
- If
MโMisINTEGER. The number of rows of the matrixB.M >= 0.NโNisINTEGER. The number of columns of the matricesAandB.N >= 0.KโKisINTEGER. The number of rows of the matrixA.Kis also order of the matrixT, i.e. the number of elementary reflectors whose product defines the block reflector.0 <= K <= N.TโTisREAL array, dimension (LDT,K). The upper-triangularK-by-KmatrixTin the representation of the block reflector.LDTโLDTisINTEGERโ the leading dimension of the arrayT. rule of the land:LDT >= K. ๐Ais aREAL array, dimension (LDA,N)๐
On entry, bestie:
a) In the K-by-N upper-trapezoidal part A: the input matrix A. (yes, that's the whole vibe)
b) In the columns below the diagonal: the columns of V1 โ and heads up, the ones are NOT stored on the diagonal ๐ซถ
On exit: A gets overwritten by the rectangular K-by-N product H*A. ๐
See the Further Details section (it's coming, don't worry).
LDAโ okay so real talk, the source page literally saysLDB is INTEGERright here in this block, which is not a typo I made, that's just what's in the file ๐ญ๐ anyway: it'sINTEGER, it's the leading dimension of the arrayA, andLDA >= max(1,K).Bis aREAL array, dimension (LDB,N)๐ฆ
On entry:
a) In the M-by-(N-K) right block: the input matrix B. โญ
b) In the M-by-N left block: the columns of V2. ๐ธ
On exit: B gets overwritten by the rectangular M-by-N product H*B. ๐ฅ
See Further Details โ same deal as above.
LDBisINTEGERโ the leading dimension of the arrayB. the bar isLDB >= max(1,M). no notes. ๐WORKis aREAL array, with dimension(LDWORK,max(K,N-K))โจLDWORKisINTEGERโ the leading dimension of the arrayWORK, andLDWORK>=max(1,K). ๐
๐ Author
Univ. of Tennessee
Univ. of California Berkeley
Univ. of Colorado Denver
NAG Ltd.
Not a group project you can ghost, fr fr. ๐ซถ
๐ง Contributors
November 2020, Igor Kozachenko, Computer Science Division, University of California, Berkeley
Absolute icon behaviour, we stan. ๐
๐ Further Details
๐ (1) Description of the Algebraic Operation
Okay so picture this. The matrix A is a K-by-N matrix built out of two column block matrices: A1, which is K-by-K, and A2, which is K-by-(N-K):
A = ( A1, A2 ).
The matrix B is an M-by-N matrix, also built from two column block matrices: B1, which is M-by-K, and B2, which is M-by-(N-K):
B = ( B1, B2 ).
Then we perform the operation:
( A_out ) := H * ( A_in ) = ( I - V * T * V**T ) * ( A_in ) =
( B_out ) ( B_in ) ( B_in )
= ( I - ( V1 ) * T * ( V1**T, V2**T ) ) * ( A_in )
( V2 ) ( B_in )
On input:
a) The stacked ( A_in ) / ( B_in ) situation consists of two block columns โจ
( A_in ) = (( A1_in ) ( A2_in )) = (( A1_in ) ( A2_in ))
( B_in ) (( B1_in ) ( B2_in )) (( 0 ) ( B2_in )),
where the column blocks are:
( A1_in ) is a K-by-K upper-triangular matrix stored in the
upper triangular part of the array A(1:K,1:K).
( B1_in ) is an M-by-K rectangular ZERO matrix and not stored.
( A2_in ) is a K-by-(N-K) rectangular matrix stored
in the array A(1:K,K+1:N).
( B2_in ) is an M-by-(N-K) rectangular matrix stored
in the array B(1:M,K+1:N).
b) The V situation ๐ฆ
V = ( V1 )
( V2 )
where:
- if
IDENT == 'I',V1is a K-by-K identity matrix, and it is not stored; - if
IDENT != 'I',V1is a K-by-K unit lower-triangular matrix, stored in the lower-triangular part of the arrayA(1:K,1:K)(ones are not stored),
and V2 is an M-by-K rectangular matrix stored in the array B(1:M,1:K) โ <lavender>(because on input B1_in is a rectangular zero matrix that is not stored and the space is used to store V2)</lavender>. Reusing the space, eco-friendly, we love to see it. โป๏ธ๐
c) T is a K-by-K upper-triangular matrix stored in the array T(1:K,1:K). โญ
On output:
a) The output stack also has two block columns:
( A_out ) = (( A1_out ) ( A2_out ))
( B_out ) (( B1_out ) ( B2_out )),
where the column blocks are:
( A1_out ) is a K-by-K square matrix, or a K-by-K
upper-triangular matrix, if V1 is an
identity matrix. AiOut is stored in
the array A(1:K,1:K).
( B1_out ) is an M-by-K rectangular matrix stored
in the array B(1:M,K:N).
( A2_out ) is a K-by-(N-K) rectangular matrix stored
in the array A(1:K,K+1:N).
( B2_out ) is an M-by-(N-K) rectangular matrix stored
in the array B(1:M,K+1:N).
(yeah, AiOut and that B(1:M,K:N) are exactly what the page says โ I'm not touching them, that's above my pay grade ๐)
The operation above can be represented as the same operation on each block column:
( A1_out ) := H * ( A1_in ) = ( I - V * T * V**T ) * ( A1_in )
( B1_out ) ( 0 ) ( 0 )
( A2_out ) := H * ( A2_in ) = ( I - V * T * V**T ) * ( A2_in )
( B2_out ) ( B2_in ) ( B2_in )
If IDENT != 'I': ๐
The computation for column block 1:
A1_out: = A1_in - V1*T*(V1**T)*A1_in
B1_out: = - V2*T*(V1**T)*A1_in
The computation for column block 2, which exists if N > K:
A2_out: = A2_in - V1*T*( (V1**T)*A2_in + (V2**T)*B2_in )
B2_out: = B2_in - V2*T*( (V1**T)*A2_in + (V2**T)*B2_in )
If IDENT == 'I': ๐ฆ
The operation for column block 1:
A1_out: = A1_in - V1*T**A1_in
B1_out: = - V2*T**A1_in
The computation for column block 2, which exists if N > K:
A2_out: = A2_in - T*( A2_in + (V2**T)*B2_in )
B2_out: = B2_in - V2*T*( A2_in + (V2**T)*B2_in )
๐ (2) Description of the Algorithmic Computation
In the first step, we compute column block 2, i.e. A2 and B2. Here, we need to use the K-by-(N-K) rectangular workspace matrix W2 that is of the same size as the matrix A2. W2 is stored in the array WORK(1:K,1:(N-K)). ๐ฅ
In the second step, we compute column block 1, i.e. A1 and B1. Here, we need to use the K-by-K square workspace matrix W1 that is of the same size as the as the matrix A1. W1 is stored in the array WORK(1:K,1:K). (yes, "as the as the" โ the source said it first, I'm just the messenger ๐ญ)
NOTE: Hence, in this routine, we need the workspace arrayWORKonly of sizeWORK(1:K,1:max(K,N-K))so it can hold bothW2from the first step andW1from the second step. One array, two jobs, main character energy. ๐
Case (A), when V1 is unit lower-triangular, i.e. IDENT != 'I' โ more computations than in the Case (B). ๐
if( IDENT != 'I' ) then
if ( N > K ) then
(First Step - column block 2)
col2_(1) W2: = A2
col2_(2) W2: = (V1**T) * W2 = (unit_lower_tr_of_(A1)**T) * W2
col2_(3) W2: = W2 + (V2**T) * B2 = W2 + (B1**T) * B2
col2_(4) W2: = T * W2
col2_(5) B2: = B2 - V2 * W2 = B2 - B1 * W2
col2_(6) W2: = V1 * W2 = unit_lower_tr_of_(A1) * W2
col2_(7) A2: = A2 - W2
else
(Second Step - column block 1)
col1_(1) W1: = A1
col1_(2) W1: = (V1**T) * W1 = (unit_lower_tr_of_(A1)**T) * W1
col1_(3) W1: = T * W1
col1_(4) B1: = - V2 * W1 = - B1 * W1
col1_(5) square W1: = V1 * W1 = unit_lower_tr_of_(A1) * W1
col1_(6) square A1: = A1 - W1
end if
end if
Case (B), when V1 is an identity matrix, i.e. IDENT == 'I' โ less computations than in the Case (A). ๐ฆ
if( IDENT == 'I' ) then
if ( N > K ) then
(First Step - column block 2)
col2_(1) W2: = A2
col2_(3) W2: = W2 + (V2**T) * B2 = W2 + (B1**T) * B2
col2_(4) W2: = T * W2
col2_(5) B2: = B2 - V2 * W2 = B2 - B1 * W2
col2_(7) A2: = A2 - W2
else
(Second Step - column block 1)
col1_(1) W1: = A1
col1_(3) W1: = T * W1
col1_(4) B1: = - V2 * W1 = - B1 * W1
col1_(6) upper-triangular_of_(A1): = A1 - W1
end if
end if
Combine these cases (A) and (B) together, and this is the resulting algorithm โ the final boss form ๐๐ฅ:
if ( N > K ) then
(First Step - column block 2)
col2_(1) W2: = A2
if( IDENT != 'I' ) then
col2_(2) W2: = (V1**T) * W2
= (unit_lower_tr_of_(A1)**T) * W2
end if
col2_(3) W2: = W2 + (V2**T) * B2 = W2 + (B1**T) * B2]
col2_(4) W2: = T * W2
col2_(5) B2: = B2 - V2 * W2 = B2 - B1 * W2
if( IDENT != 'I' ) then
col2_(6) W2: = V1 * W2 = unit_lower_tr_of_(A1) * W2
end if
col2_(7) A2: = A2 - W2
else
(Second Step - column block 1)
col1_(1) W1: = A1
if( IDENT != 'I' ) then
col1_(2) W1: = (V1**T) * W1
= (unit_lower_tr_of_(A1)**T) * W1
end if
col1_(3) W1: = T * W1
col1_(4) B1: = - V2 * W1 = - B1 * W1
if( IDENT != 'I' ) then
col1_(5) square W1: = V1 * W1 = unit_lower_tr_of_(A1) * W1
col1_(6_a) below_diag_of_(A1): = - below_diag_of_(W1)
end if
col1_(6_b) up_tr_of_(A1): = up_tr_of_(A1) - up_tr_of_(W1)
end if
(and yeah, that stray ] on the col2_(3) line is coming straight from the source, we leave it be ๐ซถ)
Definition at line 390 of file slarfb_gett.f. ๐
๐ซถ zlarfb_gett โ the complex*16 twin, aka the double-complex one ๐
subroutine zlarfb_gett (character ident, integer m, integer n, integer k,
complex*16, dimension( ldt, * ) t, integer ldt, complex*16, dimension(
lda, * ) a, integer lda, complex*16, dimension( ldb, * ) b, integer ldb,
complex*16, dimension( ldwork, * ) work, integer ldwork)
ZLARFB_GETT โ that's the name, lowercase in the code, screaming in the docs, we love a versรกtil icon โจ
๐ Purpose
Okay so bestie, here's the vibe: ZLARFB_GETT takes a complex Householder block reflector โ call her H โ and slaps her on the left side of a complex (K+M)-by-N matrix. And that big matrix? She's actually two block matrices glued together, main character energy ๐:
- an upper trapezoidal
K-by-NmatrixA, chilling in the arrayA, and - a rectangular
M-by-(N-K)matrixB, chilling in the arrayB๐ฆ
The reflector H is stored in that compact WY-representation everyone keeps talking about, where the elementary reflectors live inside the arrays A, B and T. Not me being mysterious โ there's literally a Further Details section below, iykyk ๐
๐ธ Parameters
IDENTโIDENT is CHARACTER*1. Okay so this flag is doing a whole fork-in-the-road moment:- if
IDENT= not'I', or not'i', thenV1is unit lower-triangular and it's stored in the left K-by-K block of the input matrixA๐ซถ - if
IDENT='I'or'i', thenV1is an identity matrix and it is not stored at all, she's on vacation ๐ - peep the Further Details section for the full lore ๐
- if
MโM is INTEGER. The number of rows of the matrixB. Constraint:M >= 0. Yes bestie, non-negative, we're not doing negative rows today ๐NโN is INTEGER. The number of columns of the matricesAandB. Constraint:N >= 0. Same deal, zero is allowed, negative is not the vibe ๐ธKโK is INTEGER, and honestly she's the busiest one here:- the number of rows of the matrix
A(yes, the doc literally says "number or rows", we forgive her ๐ญ) - also the order of the matrix
T, meaning it's the number of elementary reflectors whose product defines the block reflector ๐ฅ - constraint:
0 <= K <= Nโ keep it in that window, no cap โญ
- the number of rows of the matrix
TโT is COMPLEX*16 array, dimension (LDT,K). This is the upper-triangular K-by-K matrixTin the representation of the block reflector. Queen of the WY-representation, literally ๐LDTโLDT is INTEGER. The leading dimension of the arrayT. Constraint:LDT >= K. Don't skimp on the leading dimension, it's giving main character ๐AโA is COMPLEX*16 array, dimension (LDA,N). Two whole personalities here:- On entry:
- a) in the
K-by-Nupper-trapezoidal partA: the input matrixA๐ฆ - b) in the columns below the diagonal: the columns of
V1โ and heads up, the ones are not stored on the diagonal, they're implied, we're keeping it subtle ๐
- a) in the
- On exit:
Ais overwritten by the rectangularK-by-NproductH*A๐ฅ
- see Further Details section, again, she keeps sending you there ๐
- On entry:
LDAโLDB is INTEGER. Yeah bestie, the original literally writesLDBin theLDAspot โ copy-paste moment, iconic, but it's talking aboutLDA, fr fr. The leading dimension of the arrayA. Constraint:LDA >= max(1,K)โจBโB is COMPLEX*16 array, dimension (LDB,N). Also dual-wielding:- On entry:
- a) in the
M-by-(N-K)right block: the input matrixB๐ธ - b) in the
M-by-Nleft block: the columns ofV2๐ซถ
- a) in the
- On exit:
Bis overwritten by the rectangularM-by-NproductH*B๐ฅ
- Further Details section. She said what she said ๐
- On entry:
LDBโLDB is INTEGER. The leading dimension of the arrayB. Constraint:LDB >= max(1,M)โญWORKโWORK is COMPLEX*16 array, dimension (LDWORK,max(K,N-K)). This is the workspace, the backstage area where all the actual work happens while the big names take the credit ๐LDWORKโLDWORK is INTEGER. The leading dimension of the arrayWORK. Constraint:LDWORK>=max(1,K). Give the workspace room to breathe, she deserves it ๐
๐ Author
- Univ. of Tennessee
- Univ. of California Berkeley
- Univ. of Colorado Denver
- NAG Ltd.
Four whole institutions, the academic avengers, we stan ๐ซถ
๐ง Contributors
- November 2020, Igor Kozachenko, Computer Science Division, University of California, Berkeley ๐
Shoutout Igor, dropping this in November 2020, a whole era ๐
๐ Further Details
Okay this is the part where she actually explains the WY-representation, so grab a snack ๐ โ
(1) Description of the Algebraic Operation ๐
Okay bestie, matrix tea time โโจ. The matrix A is a K-by-N matrix made of two column block matrices, A1 (that's K-by-K) and A2 (that's K-by-(N-K)):
A = ( A1, A2 ).
The matrix B is an M-by-N matrix made of two column block matrices, B1 (that's M-by-K) and B2 (that's M-by-(N-K)):
B = ( B1, B2 ).
Perform the operation:
( A_out ) := H * ( A_in ) = ( I - V * T * V**H ) * ( A_in ) =
( B_out ) ( B_in ) ( B_in )
= ( I - ( V1 ) * T * ( V1**H, V2**H ) ) * ( A_in )
( V2 ) ( B_in )
On input:
a) ( A_in ) over ( B_in ) consists of two block columns:
( A_in ) = (( A1_in ) ( A2_in )) = (( A1_in ) ( A2_in ))
( B_in ) (( B1_in ) ( B2_in )) (( 0 ) ( B2_in )),
where the column blocks are:
( A1_in )is a K-by-K upper-triangular matrix stored in the upper triangular part of the arrayA(1:K,1:K).( B1_in )is an M-by-K rectangular ZERO matrix and not stored.( A2_in )is a K-by-(N-K) rectangular matrix stored in the arrayA(1:K,K+1:N).( B2_in )is an M-by-(N-K) rectangular matrix stored in the arrayB(1:M,K+1:N).
b)
V = ( V1 )
( V2 )
where:
- if
IDENT == 'I',V1is a K-by-K identity matrix, not stored; - if
IDENT != 'I',V1is a K-by-K unit lower-triangular matrix, stored in the lower-triangular part of the arrayA(1:K,1:K)(ones are not stored),
and V2 is an M-by-K rectangular stored the array B(1:M,1:K), (because on input B1_in is a rectangular zero matrix that is not stored and the space is used to store V2).
c) T is a K-by-K upper-triangular matrix stored in the array T(1:K,1:K).
On output:
a) ( A_out ) over ( B_out ) consists of two block columns:
( A_out ) = (( A1_out ) ( A2_out ))
( B_out ) (( B1_out ) ( B2_out )),
where the column blocks are:
( A1_out )is a K-by-K square matrix, or a K-by-K upper-triangular matrix, ifV1is an identity matrix.AiOutis stored in the arrayA(1:K,1:K).( B1_out )is an M-by-K rectangular matrix stored in the arrayB(1:M,K:N).( A2_out )is a K-by-(N-K) rectangular matrix stored in the arrayA(1:K,K+1:N).( B2_out )is an M-by-(N-K) rectangular matrix stored in the arrayB(1:M,K+1:N).
The operation above can be represented as the same operation on each block column:
( A1_out ) := H * ( A1_in ) = ( I - V * T * V**H ) * ( A1_in )
( B1_out ) ( 0 ) ( 0 )
( A2_out ) := H * ( A2_in ) = ( I - V * T * V**H ) * ( A2_in )
( B2_out ) ( B2_in ) ( B2_in )
If IDENT != 'I':
The computation for column block 1:
A1_out: = A1_in - V1*T*(V1**H)*A1_in
B1_out: = - V2*T*(V1**H)*A1_in
The computation for column block 2, which exists if N > K:
A2_out: = A2_in - V1*T*( (V1**H)*A2_in + (V2**H)*B2_in )
B2_out: = B2_in - V2*T*( (V1**H)*A2_in + (V2**H)*B2_in )
If IDENT == 'I':
The operation for column block 1:
A1_out: = A1_in - V1*T*A1_in
B1_out: = - V2*T*A1_in
The computation for column block 2, which exists if N > K:
A2_out: = A2_in - T*( A2_in + (V2**H)*B2_in )
B2_out: = B2_in - V2*T*( A2_in + (V2**H)*B2_in )
(2) Description of the Algorithmic Computation โจ
In the first step, we compute column block 2, i.e. A2 and B2. Here, we need to use the K-by-(N-K) rectangular workspace matrix W2 that is of the same size as the matrix A2. W2 is stored in the array WORK(1:K,1:(N-K)).
In the second step, we compute column block 1, i.e. A1 and B1. Here, we need to use the K-by-K square workspace matrix W1 that is of the same size as the as the matrix A1. W1 is stored in the array WORK(1:K,1:K).
NOTE: Hence, in this routine, we need the workspace arrayWORKonly of sizeWORK(1:K,1:max(K,N-K))so it can hold bothW2from the first step andW1from the second step. ๐ซถ
Case (A), when V1 is unit lower-triangular, i.e. IDENT != 'I', more computations than in the Case (B).
if( IDENT != 'I' ) then
if ( N > K ) then
(First Step - column block 2)
col2_(1) W2: = A2
col2_(2) W2: = (V1**H) * W2 = (unit_lower_tr_of_(A1)**H) * W2
col2_(3) W2: = W2 + (V2**H) * B2 = W2 + (B1**H) * B2
col2_(4) W2: = T * W2
col2_(5) B2: = B2 - V2 * W2 = B2 - B1 * W2
col2_(6) W2: = V1 * W2 = unit_lower_tr_of_(A1) * W2
col2_(7) A2: = A2 - W2
else
(Second Step - column block 1)
col1_(1) W1: = A1
col1_(2) W1: = (V1**H) * W1 = (unit_lower_tr_of_(A1)**H) * W1
col1_(3) W1: = T * W1
col1_(4) B1: = - V2 * W1 = - B1 * W1
col1_(5) square W1: = V1 * W1 = unit_lower_tr_of_(A1) * W1
col1_(6) square A1: = A1 - W1
end if
end if
Case (B), when V1 is an identity matrix, i.e. IDENT == 'I', less computations than in the Case (A)
if( IDENT == 'I' ) then
if ( N > K ) then
(First Step - column block 2)
col2_(1) W2: = A2
col2_(3) W2: = W2 + (V2**H) * B2 = W2 + (B1**H) * B2
col2_(4) W2: = T * W2
col2_(5) B2: = B2 - V2 * W2 = B2 - B1 * W2
col2_(7) A2: = A2 - W2
else
(Second Step - column block 1)
col1_(1) W1: = A1
col1_(3) W1: = T * W1
col1_(4) B1: = - V2 * W1 = - B1 * W1
col1_(6) upper-triangular_of_(A1): = A1 - W1
end if
end if
Combine these cases (A) and (B) together, this is the resulting algorithm:
if ( N > K ) then
(First Step - column block 2)
col2_(1) W2: = A2
if( IDENT != 'I' ) then
col2_(2) W2: = (V1**H) * W2
= (unit_lower_tr_of_(A1)**H) * W2
end if
col2_(3) W2: = W2 + (V2**H) * B2 = W2 + (B1**H) * B2]
col2_(4) W2: = T * W2
col2_(5) B2: = B2 - V2 * W2 = B2 - B1 * W2
if( IDENT != 'I' ) then
col2_(6) W2: = V1 * W2 = unit_lower_tr_of_(A1) * W2
end if
col2_(7) A2: = A2 - W2
else
(Second Step - column block 1)
col1_(1) W1: = A1
if( IDENT != 'I' ) then
col1_(2) W1: = (V1**H) * W1
= (unit_lower_tr_of_(A1)**H) * W1
end if
col1_(3) W1: = T * W1
col1_(4) B1: = - V2 * W1 = - B1 * W1
if( IDENT != 'I' ) then
col1_(5) square W1: = V1 * W1 = unit_lower_tr_of_(A1) * W1
col1_(6_a) below_diag_of_(A1): = - below_diag_of_(W1)
end if
col1_(6_b) up_tr_of_(A1): = up_tr_of_(A1) - up_tr_of_(W1)
end if
Definition at line 390 of file zlarfb_gett.f.
Author ๐ซถ
Generated automatically by Doxygen for LAPACK from the source code. The robots handled this one, no cap. ๐ค๐
Footer check: LAPACK Version 3.12.0, page larfb_gett(3), and the date slot is whatever the official footer carries โ this excerpt didn't print one, so we're not inventing it. Periodt. ๐โจ
๐ฆ where this page comes from
- arch rolling: lapack-doc 3.12.1-2 ยท custom
- ๐ girlified by deepseek-ai/DeepSeek-V4.1-Flash-1017 on : a language model's rewrite of the original, under its terms