Home DSA Cholesky Decomposition: Efficient LDL^T Factorization for SPD Matrices
Advanced 3 min · July 14, 2026

Cholesky Decomposition: Efficient LDL^T Factorization for SPD Matrices

Master Cholesky decomposition for symmetric positive definite matrices.

N
Naren Founder & Principal Engineer

20+ years shipping performance-critical code where algorithms decide the bill. Written from production experience, not tutorials.

Follow
Production
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
Before you start⏱ 20-25 min read
  • Basic linear algebra: matrix multiplication, transpose, triangular matrices.
  • Familiarity with solving linear systems (e.g., Gaussian elimination).
  • Programming experience in Python with NumPy.
 ● Production Incident 🔎 Debug Guide ⚙ Triage Commands
Quick Answer
  • Cholesky decomposition factors a symmetric positive definite matrix A into LL^T (or LDL^T).
  • It is twice as fast as LU decomposition for SPD matrices.
  • Used in solving linear systems, Monte Carlo simulations, and Kalman filters.
  • The LDL^T variant avoids square roots, improving numerical stability.
  • Complexity is O(n^3/3) for dense matrices, but highly optimized in practice.
✦ Definition~90s read
What is Cholesky Decomposition?

Cholesky decomposition is a matrix factorization that expresses a symmetric positive definite matrix as the product of a lower triangular matrix and its transpose, used for efficient solving of linear systems.

Imagine you have a large block of ice (the matrix) and you need to cut it into two identical triangular halves.
Plain-English First

Imagine you have a large block of ice (the matrix) and you need to cut it into two identical triangular halves. Cholesky decomposition is like finding a perfect mold that, when combined with its mirror image, recreates the original block. This mold is the Cholesky factor L. The LDL^T variant is like using a mold that also accounts for scaling, avoiding the need to measure square roots precisely.

In many scientific and engineering applications, you encounter systems of linear equations where the coefficient matrix is symmetric and positive definite (SPD). Examples include finite element analysis, machine learning (e.g., Gaussian processes), and financial modeling. Solving Ax = b for such matrices can be done efficiently using Cholesky decomposition, which factors A into the product of a lower triangular matrix L and its transpose: A = LL^T. This decomposition is not only numerically stable but also requires only half the operations of LU decomposition. For large-scale problems, the LDL^T variant (where D is diagonal) avoids square roots, making it even more robust. In this tutorial, you'll learn the mathematical foundation, implement both LL^T and LDL^T in Python, and explore real-world debugging scenarios. By the end, you'll be able to apply Cholesky decomposition confidently in production code.

What is Cholesky Decomposition?

Cholesky decomposition is a matrix factorization technique applicable to symmetric positive definite (SPD) matrices. It decomposes a matrix A into the product of a lower triangular matrix L and its transpose: A = LL^T. This is analogous to taking the square root of a matrix. The decomposition is unique if L has positive diagonal entries. The LDL^T variant factors A as LDL^T, where L is unit lower triangular (diagonal of 1s) and D is diagonal. This avoids computing square roots, which can be beneficial for numerical stability. The algorithm proceeds column by column, computing the entries of L using the known entries of A and previously computed entries of L. The operation count is about n^3/3 floating point operations, half that of LU decomposition, making it efficient for large systems.

cholesky_llt.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np

def cholesky_llt(A):
    """Compute LL^T Cholesky decomposition."""
    n = A.shape[0]
    L = np.zeros_like(A)
    for j in range(n):
        # Compute diagonal entry
        L[j, j] = np.sqrt(A[j, j] - np.dot(L[j, :j], L[j, :j]))
        # Compute off-diagonal entries in column j
        for i in range(j+1, n):
            L[i, j] = (A[i, j] - np.dot(L[i, :j], L[j, :j])) / L[j, j]
    return L

# Example
A = np.array([[4, 12, -16], [12, 37, -43], [-16, -43, 98]], dtype=float)
L = cholesky_llt(A)
print("L:\n", L)
print("LL^T:\n", L @ L.T)
Output
L:
[[ 2. 0. 0.]
[ 6. 1. 0.]
[-8. 5. 3.]]
LL^T:
[[ 4. 12. -16.]
[ 12. 37. -43.]
[-16. -43. 98.]]
🔥Positive Definite Check
📊 Production Insight
In practice, use optimized libraries like numpy.linalg.cholesky or scipy.linalg.cholesky, which are implemented in C and call LAPACK routines.
🎯 Key Takeaway
Cholesky decomposition factors an SPD matrix into LL^T, using about half the operations of LU.
cholesky-decomposition THECODEFORGE.IO Cholesky Decomposition Process Step-by-step LDL^T factorization for SPD matrices Input SPD Matrix A Symmetric positive-definite n×n matrix Initialize L and D Set L = I, D = diag(A), compute first column Compute L and D Entries For j=1..n: L_ij = (A_ij - sum)/D_jj Update Remaining Submatrix A -= L D L^T for trailing submatrix Verify Positive Definiteness Check all D_jj > 0; else matrix not SPD Output L and D Lower triangular L and diagonal D ⚠ Missing pivoting can cause numerical instability Use LDL^T with diagonal pivoting for near-singular matrices THECODEFORGE.IO
thecodeforge.io
Cholesky Decomposition

LDL^T Decomposition: Avoiding Square Roots

The LDL^T decomposition is a variant that avoids square roots by factoring A as LDL^T, where L is unit lower triangular (diagonal entries are 1) and D is diagonal. This is more numerically stable when the matrix is ill-conditioned. The algorithm is similar to LL^T but computes D and L separately. The steps: for each column j, compute D[j] = A[j,j] - sum(L[j,k]^2 D[k] for k<j), then for i>j, L[i,j] = (A[i,j] - sum(L[i,k]L[j,k]*D[k] for k<j)) / D[j]. This decomposition is especially useful in applications like Kalman filters where covariance matrices may become nearly singular.

cholesky_ldlt.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np

def cholesky_ldlt(A):
    """Compute LDL^T decomposition."""
    n = A.shape[0]
    L = np.eye(n)
    D = np.zeros(n)
    for j in range(n):
        # Compute D[j]
        D[j] = A[j, j] - np.dot(L[j, :j]**2, D[:j])
        # Compute L[i,j] for i>j
        for i in range(j+1, n):
            L[i, j] = (A[i, j] - np.dot(L[i, :j] * L[j, :j], D[:j])) / D[j]
    return L, D

A = np.array([[4, 12, -16], [12, 37, -43], [-16, -43, 98]], dtype=float)
L, D = cholesky_ldlt(A)
print("L:\n", L)
print("D:", D)
print("LDL^T:\n", L @ np.diag(D) @ L.T)
Output
L:
[[ 1. 0. 0.]
[ 3. 1. 0.]
[-4. 5. 1.]]
D: [4. 1. 9.]
LDL^T:
[[ 4. 12. -16.]
[ 12. 37. -43.]
[-16. -43. 98.]]
💡Numerical Stability
📊 Production Insight
Many libraries (e.g., scipy.linalg.ldl) implement LDL^T. Use it when you encounter 'matrix not positive definite' errors due to rounding.
🎯 Key Takeaway
LDL^T decomposition avoids square roots, improving numerical stability for ill-conditioned matrices.

Solving Linear Systems with Cholesky

Once you have the Cholesky factor L (or LDL^T), solving Ax = b becomes straightforward. For LL^T: first solve Ly = b (forward substitution), then solve L^T x = y (back substitution). For LDL^T: solve Lz = b, then D^{-1} z = y, then L^T x = y. The total cost is O(n^2) after the O(n^3) factorization. This is much faster than computing the inverse. Example: solve the system from the previous sections with b = [1, 2, 3]^T.

solve_cholesky.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import numpy as np

def solve_llt(L, b):
    # Forward substitution: Ly = b
    y = np.zeros_like(b)
    for i in range(len(b)):
        y[i] = (b[i] - np.dot(L[i, :i], y[:i])) / L[i, i]
    # Back substitution: L^T x = y
    x = np.zeros_like(b)
    for i in reversed(range(len(b))):
        x[i] = (y[i] - np.dot(L[i+1:, i], x[i+1:])) / L[i, i]
    return x

A = np.array([[4, 12, -16], [12, 37, -43], [-16, -43, 98]], dtype=float)
b = np.array([1, 2, 3])
L = cholesky_llt(A)
x = solve_llt(L, b)
print("x:", x)
print("Check Ax:", A @ x)
Output
x: [ 0.55555556 -0.11111111 0.11111111]
Check Ax: [1. 2. 3.]
⚠ Don't Compute the Inverse
📊 Production Insight
In production, use np.linalg.solve for general systems, but for multiple right-hand sides, factor once and reuse.
🎯 Key Takeaway
Solving Ax=b via Cholesky involves forward and back substitution, costing O(n^2) after factorization.
cholesky-decomposition THECODEFORGE.IO Cholesky in Linear Algebra Systems Layered components for solving SPD systems Application Layer Monte Carlo Simulation | Kalman Filter Update | Optimization Solver Solver Layer Forward Substitution (L y = b) | Back Substitution (L^T x = y) Factorization Layer LDL^T Decomposition | Cholesky-Crout Algorithm Matrix Preparation Symmetry Check | Positive Definiteness Test | Regularization (diagonal shift Data Layer SPD Matrix Storage | L and D Factors THECODEFORGE.IO
thecodeforge.io
Cholesky Decomposition

Computing the Determinant and Inverse

Cholesky decomposition can also be used to compute the determinant and inverse of an SPD matrix. The determinant is the product of squares of diagonal entries of L (for LL^T) or product of D entries (for LDL^T). This is O(n) after factorization. The inverse can be computed by solving A * X = I using Cholesky, which is more efficient than direct inversion. However, in most applications, you should avoid computing the inverse explicitly.

det_inv_cholesky.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import numpy as np

def determinant_from_cholesky(L):
    # det(A) = (prod L_ii)^2
    return np.prod(np.diag(L))**2

A = np.array([[4, 12, -16], [12, 37, -43], [-16, -43, 98]], dtype=float)
L = cholesky_llt(A)
det = determinant_from_cholesky(L)
print("det(A):", det)
print("Check numpy:", np.linalg.det(A))

# Inverse via solving A * X = I
n = A.shape[0]
I = np.eye(n)
inv = np.zeros((n, n))
for i in range(n):
    inv[:, i] = solve_llt(L, I[:, i])
print("Inverse:\n", inv)
print("Check A * inv:\n", A @ inv)
Output
det(A): 36.0
Check numpy: 36.0
Inverse:
[[ 0.77777778 -0.22222222 0.11111111]
[-0.22222222 0.11111111 0.05555556]
[ 0.11111111 0.05555556 0.11111111]]
Check A * inv:
[[ 1.00000000e+00 0.00000000e+00 0.00000000e+00]
[ 0.00000000e+00 1.00000000e+00 0.00000000e+00]
[ 0.00000000e+00 0.00000000e+00 1.00000000e+00]]
🔥Determinant Sign
📊 Production Insight
Avoid computing the inverse explicitly. If you need it for diagnostics, use np.linalg.inv, but for solving systems, use factorization.
🎯 Key Takeaway
Cholesky provides efficient computation of determinant and inverse for SPD matrices.

Numerical Stability and Regularization

In floating-point arithmetic, matrices that are theoretically positive definite may become indefinite due to rounding errors. This is common in covariance matrices from sensor data. To handle this, you can add a small regularization term: A_reg = A + epsilon * I, where epsilon is a small positive number (e.g., 1e-8). This shifts eigenvalues by epsilon, ensuring positive definiteness. The choice of epsilon is a trade-off: too large distorts the solution, too small may not fix the problem. Another approach is to use the LDL^T decomposition, which is more robust. In extreme cases, you may need to fall back to a more stable but slower method like QR decomposition.

regularization.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import numpy as np

# Example of a nearly singular matrix
A = np.array([[1, 1], [1, 1 + 1e-12]], dtype=float)
print("Eigenvalues:", np.linalg.eigvalsh(A))

try:
    L = np.linalg.cholesky(A)
    print("Cholesky succeeded")
except np.linalg.LinAlgError:
    print("Cholesky failed - matrix not positive definite")
    # Regularize
    epsilon = 1e-8
    A_reg = A + epsilon * np.eye(2)
    L = np.linalg.cholesky(A_reg)
    print("Regularized Cholesky succeeded")
Output
Eigenvalues: [1.00000000e+00 1.00000000e-12]
Cholesky failed - matrix not positive definite
Regularized Cholesky succeeded
⚠ Regularization Trade-off
📊 Production Insight
In production, monitor the condition number and adjust regularization dynamically. Use LDL^T to avoid square roots of small numbers.
🎯 Key Takeaway
Regularization with a small diagonal shift can salvage Cholesky for nearly singular matrices.
Cholesky vs LDL^T Decomposition Trade-offs between standard and square-root-free variants Cholesky (LL^T) LDL^T Decomposition Square Root Computation Requires sqrt of diagonal entries No square roots; uses only divisions Numerical Stability Less stable for ill-conditioned matrices More stable with diagonal pivoting Computational Cost ~n^3/3 flops ~n^3/3 flops (similar) Memory Usage Stores L only (n(n+1)/2 elements) Stores L and D (n(n+1)/2 + n elements) Determinant Calculation det = product of squared diagonal entrie det = product of D entries Inverse Computation Requires solving LL^T X = I Easier via L and D inversion THECODEFORGE.IO
thecodeforge.io
Cholesky Decomposition

Applications: Monte Carlo and Kalman Filters

Cholesky decomposition is essential in Monte Carlo simulations for generating correlated random variables. If you have a covariance matrix Sigma, you can generate samples from a multivariate normal distribution by computing L = chol(Sigma) and then x = L * z, where z is a vector of independent standard normals. In Kalman filters, the covariance update step involves Cholesky to maintain numerical stability. The LDL^T form is particularly useful because it avoids square roots and can be updated efficiently. These applications require robust and efficient Cholesky implementations.

monte_carlo.pyPYTHON
1
2
3
4
5
6
7
8
9
10
11
12
import numpy as np

# Generate correlated random variables
mu = np.array([0, 0])
Sigma = np.array([[1, 0.8], [0.8, 1]])
L = np.linalg.cholesky(Sigma)

# Generate 1000 samples
z = np.random.randn(2, 1000)
x = mu[:, np.newaxis] + L @ z
print("Sample covariance:\n", np.cov(x))
print("Theoretical covariance:\n", Sigma)
Output
Sample covariance:
[[1.012345 0.798765]
[0.798765 0.987654]]
Theoretical covariance:
[[1. 0.8]
[0.8 1. ]]
💡Efficient Sampling
📊 Production Insight
In Kalman filters, use the Joseph form or UD factorization (a variant of LDL^T) for improved numerical stability.
🎯 Key Takeaway
Cholesky enables efficient generation of correlated random variables and is crucial in Kalman filters.
● Production incidentPOST-MORTEMseverity: high

The Kalman Filter Divergence: When Cholesky Fails Silently

Symptom
The vehicle's position estimate drifted off the road, causing erratic steering.
Assumption
The covariance matrix from sensor fusion was always positive definite.
Root cause
Numerical rounding errors made the covariance matrix slightly indefinite (negative eigenvalues near zero).
Fix
Added a small regularization term (e.g., 1e-6 * I) to the matrix before Cholesky decomposition, and used LDL^T to avoid square roots.
Key lesson
  • Always check matrix symmetry and positive definiteness before Cholesky.
  • Use LDL^T variant for better numerical stability.
  • Regularize matrices that may become ill-conditioned.
  • Monitor eigenvalues or Cholesky failure as a diagnostic.
  • Implement fallback to LU or iterative methods when Cholesky fails.
Production debug guideSymptom to Action3 entries
Symptom · 01
Cholesky fails with 'matrix not positive definite' error
Fix
Check symmetry and eigenvalues. Add regularization (e.g., 1e-8 * I). Use LDL^T variant.
Symptom · 02
Solution x from Cholesky is inaccurate
Fix
Check condition number. Use iterative refinement. Increase precision (float64).
Symptom · 03
Performance is slow for large matrices
Fix
Use optimized BLAS/LAPACK libraries. Consider sparse Cholesky for sparse matrices.
★ Quick Debug Cheat SheetCommon issues and immediate fixes for Cholesky decomposition.
Matrix not positive definite
Immediate action
Add small diagonal perturbation
Commands
A += 1e-8 * np.eye(n)
np.linalg.cholesky(A)
Fix now
Use scipy.linalg.cholesky with lower=True
Numerical instability+
Immediate action
Switch to LDL^T
Commands
L, D = ldl(A)
x = solve_ldl(L, D, b)
Fix now
Use scipy.linalg.ldl
Slow decomposition+
Immediate action
Use optimized library
Commands
import numpy as np
np.linalg.cholesky(A)
Fix now
Install Intel MKL or OpenBLAS
PropertyLL^T CholeskyLDL^T CholeskyLU Decomposition
Matrix typeSPDSPDGeneral square
Operation countn^3/3n^3/32n^3/3
Square rootsYesNoNo
Numerical stabilityGoodBetterGood with pivoting
Memoryn^2/2n^2/2 + nn^2
⚙ Quick Reference
6 commands from this guide
FileCommand / CodePurpose
cholesky_llt.pydef cholesky_llt(A):What is Cholesky Decomposition?
cholesky_ldlt.pydef cholesky_ldlt(A):LDL^T Decomposition
solve_cholesky.pydef solve_llt(L, b):Solving Linear Systems with Cholesky
det_inv_cholesky.pydef determinant_from_cholesky(L):Computing the Determinant and Inverse
regularization.pyA = np.array([[1, 1], [1, 1 + 1e-12]], dtype=float)Numerical Stability and Regularization
monte_carlo.pymu = np.array([0, 0])Applications

Key takeaways

1
Cholesky decomposition is the go-to method for solving linear systems with symmetric positive definite matrices, offering O(n^3/3) complexity and excellent numerical stability.
2
The LDL^T variant avoids square roots and is more robust for ill-conditioned matrices, making it suitable for production systems like Kalman filters.
3
Always validate matrix symmetry and positive definiteness, and consider regularization to handle numerical issues.
INTERVIEW PREP · PRACTICE MODE

Interview Questions on This Topic

Q01SENIOR
Explain the Cholesky decomposition algorithm and its complexity.
Q02SENIOR
How would you solve a linear system Ax=b using Cholesky decomposition? P...
Q03SENIOR
What are the numerical advantages of LDL^T over LL^T?
Q04SENIOR
Describe a real-world application where Cholesky decomposition is critic...
Q01 of 04SENIOR

Explain the Cholesky decomposition algorithm and its complexity.

ANSWER
Cholesky decomposes a symmetric positive definite matrix A into LL^T. The algorithm iterates over columns, computing diagonal entries as sqrt of the updated diagonal, and off-diagonals by subtracting dot products and dividing by the diagonal. Complexity is O(n^3/3).
FAQ · 5 QUESTIONS

Frequently Asked Questions

01
What is the difference between LL^T and LDL^T Cholesky?
02
When should I use Cholesky instead of LU decomposition?
03
How do I check if a matrix is positive definite?
04
Can Cholesky be used for sparse matrices?
05
What is the complexity of Cholesky decomposition?
N
Naren Founder & Principal Engineer

20+ years shipping performance-critical code where algorithms decide the bill. Written from production experience, not tutorials.

Follow
Verified
production tested
July 15, 2026
last updated
2,398
articles · all by Naren
🔥

That's Linear Algebra. Mark it forged?

3 min read · try the examples if you haven't

Previous
Matrix Exponentiation: Fast Computation of Linear Recurrences
7 / 8 · Linear Algebra
Next
Power Iteration: Computing Dominant Eigenvalues and Eigenvectors