Matrix Factorizations
LAPACK++ provides comprehensive matrix factorization routines for decomposing matrices into products of simpler matrices with special structure.
LU Factorization
getrf - LU factorization with partial pivoting: \(A = PLU\)
getrf2 - Recursive LU with partial pivoting
gbtrf - LU factorization of band matrix
getri - Compute matrix inverse from LU factorization
getrs - Solve system using LU factorization
Cholesky Factorization
potrf - Cholesky factorization: \(A = LL^H\) or \(A = U^HU\)
potrf2 - Recursive Cholesky factorization
pbtrf - Cholesky factorization of band matrix
pftrf - Cholesky factorization in RFP format
potri - Compute inverse from Cholesky factorization
potrs - Solve system using Cholesky factorization
pstrf - Cholesky with pivoting for semidefinite matrices
QR Factorization
geqrf - QR factorization: \(A = QR\)
geqp3 - QR with column pivoting: \(AP = QR\)
geqrt - QR using recursive algorithm
orgqr - Generate orthogonal Q from geqrf
ormqr - Multiply by orthogonal Q from geqrf
tpqrt - QR of triangular-pentagonal matrix
LQ Factorization
gelqf - LQ factorization: \(A = LQ\)
gelqt - LQ using recursive algorithm
orglq - Generate orthogonal Q from gelqf
ormlq - Multiply by orthogonal Q from gelqf
QL Factorization
geqlf - QL factorization
orgql - Generate orthogonal Q from geqlf
ormql - Multiply by orthogonal Q from geqlf
RQ Factorization
gerqf - RQ factorization
orgrq - Generate orthogonal Q from gerqf
ormrq - Multiply by orthogonal Q from gerqf
Symmetric/Hermitian Factorizations
sytrf, hetrf - Bunch-Kaufman factorization: \(A = UDU^T\) or \(LDL^T\)
sytri, hetri - Compute inverse from Bunch-Kaufman
sytrs, hetrs - Solve using Bunch-Kaufman
sptrf, hptrf - Bunch-Kaufman for packed storage
Triangular Factorizations
trtri - Compute inverse of triangular matrix
trtrs - Solve triangular system
Bidiagonal Reduction
gebrd - Reduce general matrix to bidiagonal form
gbbrd - Reduce band matrix to bidiagonal
orgbr - Generate orthogonal matrices from gebrd
ormbr - Multiply by orthogonal matrices from gebrd
Tridiagonal Reduction
sytrd, hetrd - Reduce symmetric/Hermitian to tridiagonal
sbtrd, hbtrd - Reduce banded to tridiagonal
orgtr, ungtr - Generate orthogonal matrix from ∗ytrd
ormtr, unmtr - Multiply by orthogonal matrix from ∗ytrd
Hessenberg Reduction
gehrd - Reduce general matrix to upper Hessenberg form
orghr, unghr - Generate orthogonal matrix from gehrd
ormhr, unmhr - Multiply by orthogonal matrix from gehrd
Example Usage
LU factorization and solve:
int64_t n = 100, nrhs = 5;
std::vector<double> A(n * n);
std::vector<double> B(n * nrhs);
std::vector<int64_t> ipiv(n);
// Factor: A = P*L*U
int64_t info = lapack::getrf(n, n, A.data(), n, ipiv.data());
if (info == 0) {
// Solve A*X = B using factors
info = lapack::getrs(lapack::Op::NoTrans, n, nrhs,
A.data(), n, ipiv.data(),
B.data(), n);
// Solution in B
}
Cholesky factorization:
int64_t n = 100;
std::vector<double> A(n * n); // Positive definite
// Factor: A = L*L^T (lower triangle)
int64_t info = lapack::potrf(lapack::Uplo::Lower, n,
A.data(), n);
if (info == 0) {
// L stored in lower triangle of A
// Can now use potrs for solving, or potri for inverse
} else if (info > 0) {
// A(info,info) not positive definite
}
QR factorization:
int64_t m = 200, n = 100;
std::vector<double> A(m * n);
std::vector<double> tau(std::min(m, n));
// Factor: A = Q*R
int64_t info = lapack::geqrf(m, n, A.data(), m, tau.data());
if (info == 0) {
// R stored in upper triangle of A
// Elementary reflectors stored below diagonal
// tau contains scalar factors
// Generate explicit Q matrix
std::vector<double> Q = A; // Copy
lapack::orgqr(m, m, n, Q.data(), m, tau.data());
// Q now contains explicit orthogonal matrix
}
QR with column pivoting (rank-revealing):
int64_t m = 200, n = 100;
std::vector<double> A(m * n);
std::vector<double> tau(std::min(m, n));
std::vector<int64_t> jpvt(n, 0); // Initialize to 0
// Factor: A*P = Q*R with column pivoting
int64_t info = lapack::geqp3(m, n, A.data(), m,
jpvt.data(), tau.data());
if (info == 0) {
// jpvt[j] indicates A(:,jpvt[j]) is j-th column of Q*R
// Can estimate rank by examining diagonal of R
double tol = 1e-10;
int64_t rank = 0;
for (int64_t i = 0; i < std::min(m, n); ++i) {
if (std::abs(A[i + i*m]) > tol) {
rank++;
}
}
}
Symmetric indefinite factorization (Bunch-Kaufman):
int64_t n = 100;
std::vector<double> A(n * n); // Symmetric indefinite
std::vector<int64_t> ipiv(n);
// Factor: A = U*D*U^T or L*D*L^T
int64_t info = lapack::sytrf(lapack::Uplo::Lower, n,
A.data(), n, ipiv.data());
if (info == 0) {
// Factors stored in A and ipiv
// Can use sytrs to solve systems
}
Reduce to bidiagonal (for SVD):
int64_t m = 200, n = 100;
std::vector<double> A(m * n);
std::vector<double> d(std::min(m, n)); // Diagonal
std::vector<double> e(std::min(m, n) - 1); // Off-diagonal
std::vector<double> tauq(std::min(m, n));
std::vector<double> taup(std::min(m, n));
// Reduce A to bidiagonal form: A = U*B*V^T
int64_t info = lapack::gebrd(m, n, A.data(), m,
d.data(), e.data(),
tauq.data(), taup.data());
if (info == 0) {
// B has diagonal d and superdiagonal e
// Can generate U and V with orgbr/ormbr
}
Reduce symmetric to tridiagonal (for eigenvalues):
int64_t n = 100;
std::vector<double> A(n * n); // Symmetric
std::vector<double> d(n); // Diagonal
std::vector<double> e(n - 1); // Off-diagonal
std::vector<double> tau(n - 1);
// Reduce A to tridiagonal: A = Q*T*Q^T
int64_t info = lapack::sytrd(lapack::Uplo::Lower, n,
A.data(), n,
d.data(), e.data(), tau.data());
if (info == 0) {
// Tridiagonal T has diagonal d and off-diagonal e
// Generate orthogonal Q with orgtr if needed
}
Reduce to Hessenberg (for eigenvalues):
int64_t n = 100;
std::vector<double> A(n * n);
std::vector<double> tau(n - 1);
// Reduce A to Hessenberg: A = Q*H*Q^T
int64_t info = lapack::gehrd(n, 1, n, A.data(), n, tau.data());
if (info == 0) {
// Upper Hessenberg H stored in A
// Generate Q with orghr if needed
}
Factorization Algorithms
LU (getrf):
Uses partial (row) pivoting for stability
Complexity: O(n³)
Backward stable: \((L U - P A) / ||A|| = O(\epsilon)\)
Growth factor can be large but rarely occurs in practice
Cholesky (potrf):
No pivoting needed (matrix positive definite)
~2x faster than LU
Complexity: O(n³/3)
Backward stable for positive definite matrices
Fails if matrix not positive definite
QR (geqrf):
Uses Householder reflectors
Complexity: O(2mn² - 2n³/3) for m >= n
Excellent numerical stability
Minimum-norm solution for rank-deficient systems
QR with pivoting (geqp3):
Reveals numerical rank
Can handle rank-deficiency
Slightly slower than geqrf
Essential for rank-revealing decompositions
Bunch-Kaufman (sytrf/hetrf):
For symmetric/Hermitian indefinite matrices
Uses 1x1 and 2x2 pivots
More complex than Cholesky
Required when matrix not positive definite
Storage Efficiency
Packed storage: sptrf, hptrf, pptrf save ~50% memory for triangular/symmetric
RFP format: pftrf uses rectangular full packed format (cache-friendly)
Band storage: gbtrf, pbtrf exploit band structure
In-place: Most factorizations overwrite input matrix
Performance Characteristics
Relative costs for n = 1000:
Cholesky (potrf): 1.0x (fastest for SPD)
LU (getrf): 1.5x
QR (geqrf): 2.0x
Bunch-Kaufman (sytrf): 2.5x
QR with pivoting (geqp3): 3.0x
Blocking and Level-3 BLAS
All factorizations use blocked algorithms calling Level-3 BLAS for efficiency:
Panel factorization (Level-2)
Trailing matrix update (Level-3 gemm, syrk, etc.)
Block size typically 32-256 depending on architecture
Choosing a Factorization
For linear systems:
Positive definite → Cholesky (potrf)
General → LU (getrf)
Symmetric indefinite → Bunch-Kaufman (sytrf)
Overdetermined → QR (geqrf) or SVD
Underdetermined → LQ (gelqf) or SVD
For least squares:
Full rank → QR (geqrf + ormqr)
Rank-deficient → QR with pivoting (geqp3) or SVD
For eigenvalue problems:
Symmetric/Hermitian → Tridiagonal (sytrd) → syev
General → Hessenberg (gehrd) → geev
For SVD:
Bidiagonal (gebrd) → gesvd/gesdd
See Also
Linear Systems - Using factorizations to solve systems
Eigenvalue Problems - Using reductions for eigenproblems
Singular Value Decomposition (SVD) - Using bidiagonal reduction for SVD