Annexe A — Factorisation de Cholesky

Introduction

La factorisation de Cholesky est une variante de la factorisation LU, spécialement conçue pour les matrices symétriques définies positives. Elle exploite la structure particulière de ces matrices pour offrir une méthode plus efficace et numériquement stable.

💡

André-Louis Cholesky (1875-1918)

Ingénieur géodésien français, Cholesky a développé cette méthode pour résoudre des systèmes d'équations normales en géodésie. Sa méthode a été publiée posthumément par un collègue après sa mort au combat en 1918.


Matrices symétriques définies positives

Définition

Une matrice AA est symétrique définie positive (SDP) si :

  1. AA est symétrique : A=ATA = A^T
  2. Pour tout vecteur non nul xx : xTAx>0x^T A x > 0

Propriétés importantes

Les matrices SDP possèdent plusieurs propriétés remarquables :

PropriétéConséquence
Valeurs propres strictement positivesLa matrice est toujours inversible
Éléments diagonaux positifsaii>0a_{ii} > 0 pour tout ii
Mineurs principaux positifsFactorisation LU existe toujours
SymétriePermet de réduire le stockage et les calculs

Exemples de matrices SDP

Les matrices SDP apparaissent naturellement dans de nombreux contextes :

  • Matrices de covariance en statistiques
  • Matrices de rigidité en mécanique des structures
  • Matrices ATAA^T A dans les moindres carrés (si AA est de rang colonne plein)
  • Matrices laplaciennes en analyse numérique

Principe de la factorisation

L'idée fondamentale

Pour une matrice SDP AA, on cherche une matrice triangulaire inférieure LL telle que :

A=LLTA = L \cdot L^T

Contrairement à la factorisation LU classique où A=LUA = L \cdot U avec LUTL \neq U^T, ici on exploite la symétrie : la matrice triangulaire supérieure est simplement la transposée de LL.

🚨

Théorème de Cholesky

Toute matrice symétrique définie positive admet une unique factorisation A=LLTA = L \cdot L^TLL est triangulaire inférieure avec des éléments diagonaux strictement positifs.

Structure de la décomposition

Pour une matrice 3×3 :

(a11a12a13a12a22a23a13a23a33)=(l1100l21l220l31l32l33)(l11l21l310l22l3200l33)\begin{pmatrix} a_{11} & a_{12} & a_{13} \\ a_{12} & a_{22} & a_{23} \\ a_{13} & a_{23} & a_{33} \end{pmatrix} = \begin{pmatrix} l_{11} & 0 & 0 \\ l_{21} & l_{22} & 0 \\ l_{31} & l_{32} & l_{33} \end{pmatrix} \cdot \begin{pmatrix} l_{11} & l_{21} & l_{31} \\ 0 & l_{22} & l_{32} \\ 0 & 0 & l_{33} \end{pmatrix}

Formules de calcul

Dérivation des formules

En développant le produit LLT=AL \cdot L^T = A, on obtient :

aij=k=1min(i,j)likljka_{ij} = \sum_{k=1}^{\min(i,j)} l_{ik} \cdot l_{jk}

Pour les éléments diagonaux (i=ji = j) :

aii=k=1ilik2=li12+li22++lii2a_{ii} = \sum_{k=1}^{i} l_{ik}^2 = l_{i1}^2 + l_{i2}^2 + \cdots + l_{ii}^2

D'où :

lii=aiik=1i1lik2l_{ii} = \sqrt{a_{ii} - \sum_{k=1}^{i-1} l_{ik}^2}

Pour les éléments hors diagonale (i>ji > j) :

aij=k=1jlikljk=li1lj1++lijljja_{ij} = \sum_{k=1}^{j} l_{ik} \cdot l_{jk} = l_{i1} l_{j1} + \cdots + l_{ij} l_{jj}

D'où :

lij=1ljj(aijk=1j1likljk)l_{ij} = \frac{1}{l_{jj}} \left( a_{ij} - \sum_{k=1}^{j-1} l_{ik} \cdot l_{jk} \right)

Algorithme

cholesky.pypython
import numpy as np

def cholesky(A):
  """
  Factorisation de Cholesky : A = L·L^T

  Paramètres:
      A : matrice symétrique définie positive (n x n)

  Retourne:
      L : matrice triangulaire inférieure
  """
  n = len(A)
  L = np.zeros((n, n))

  for i in range(n):
      # Élément diagonal
      somme = sum(L[i, k]**2 for k in range(i))
      val = A[i, i] - somme

      if val <= 0:
          raise ValueError(f"Matrice non définie positive (étape {i})")

      L[i, i] = np.sqrt(val)

      # Éléments sous la diagonale
      for j in range(i + 1, n):
          somme = sum(L[i, k] * L[j, k] for k in range(i))
          L[j, i] = (A[j, i] - somme) / L[i, i]

  return L

# Exemple
A = np.array([[4, 2, 2],
            [2, 5, 1],
            [2, 1, 6]], dtype=float)

L = cholesky(A)
print("L =")
print(L)
print("\nVérification L·L^T =")
print(L @ L.T)

Exemple détaillé

Factorisons la matrice :

A=(422251216)A = \begin{pmatrix} 4 & 2 & 2 \\ 2 & 5 & 1 \\ 2 & 1 & 6 \end{pmatrix}

Étape 1 : Première colonne

l11=a11=4=2l_{11} = \sqrt{a_{11}} = \sqrt{4} = 2 l21=a21/l11=2/2=1l_{21} = a_{21} / l_{11} = 2/2 = 1 l31=a31/l11=2/2=1l_{31} = a_{31} / l_{11} = 2/2 = 1

Étape 2 : Deuxième colonne

l22=a22l212=51=2l_{22} = \sqrt{a_{22} - l_{21}^2} = \sqrt{5 - 1} = 2 l32=(a32l31l21)/l22=(111)/2=0l_{32} = (a_{32} - l_{31} \cdot l_{21}) / l_{22} = (1 - 1 \cdot 1) / 2 = 0

Étape 3 : Troisième colonne

l33=a33l312l322=610=5l_{33} = \sqrt{a_{33} - l_{31}^2 - l_{32}^2} = \sqrt{6 - 1 - 0} = \sqrt{5}

Résultat

L=(200120105)L = \begin{pmatrix} 2 & 0 & 0 \\ 1 & 2 & 0 \\ 1 & 0 & \sqrt{5} \end{pmatrix}

Vérification

LLT=(200120105)(211020005)=(422251216)=AL \cdot L^T = \begin{pmatrix} 2 & 0 & 0 \\ 1 & 2 & 0 \\ 1 & 0 & \sqrt{5} \end{pmatrix} \cdot \begin{pmatrix} 2 & 1 & 1 \\ 0 & 2 & 0 \\ 0 & 0 & \sqrt{5} \end{pmatrix} = \begin{pmatrix} 4 & 2 & 2 \\ 2 & 5 & 1 \\ 2 & 1 & 6 \end{pmatrix} = A \quad \checkmark

Résolution de systèmes

Pour résoudre Ax=bA \cdot x = b avec A=LLTA = L \cdot L^T :

Étape 1 : Résoudre Ly=bL \cdot y = b (substitution avant)

Étape 2 : Résoudre LTx=yL^T \cdot x = y (substitution arrière)

resolution_cholesky.pypython
def resoudre_cholesky(A, b):
  """
  Résout A·x = b où A est symétrique définie positive.
  """
  L = cholesky(A)
  n = len(b)

  # Substitution avant : L·y = b
  y = np.zeros(n)
  for i in range(n):
      y[i] = (b[i] - sum(L[i, k] * y[k] for k in range(i))) / L[i, i]

  # Substitution arrière : L^T·x = y
  x = np.zeros(n)
  for i in range(n - 1, -1, -1):
      x[i] = (y[i] - sum(L[j, i] * x[j] for j in range(i + 1, n))) / L[i, i]

  return x

Avantages et inconvénients

Avantages

AvantageExplication
Efficacité~2× moins d'opérations que LU (n3/6n^3/6 vs n3/3n^3/3)
Stockage réduitUne seule matrice triangulaire à stocker (vs L et U)
Stabilité numériquePas besoin de pivotage, toujours stable
Détection automatiqueSi une racine carrée échoue → matrice non SDP

Inconvénients

InconvénientExplication
Applicabilité limitéeUniquement pour matrices symétriques définies positives
Racines carréesOpération plus coûteuse que multiplication/division

Comparaison avec LU

AspectLUCholesky
ApplicabilitéToute matrice (avec pivotage)Matrices SDP uniquement
Complexitén33\frac{n^3}{3} flopsn36\frac{n^3}{6} flops
StockageL et U (ou compact)L seulement
PivotageSouvent nécessaireJamais nécessaire
StabilitéDépend du pivotageToujours stable

Variante : Cholesky LDLT

Pour éviter les racines carrées, on peut utiliser la factorisation :

A=LDLTA = L \cdot D \cdot L^T

LL est triangulaire inférieure avec des 1 sur la diagonale, et DD est diagonale.

Cette variante est parfois préférée car elle évite les nn racines carrées, au prix d'une matrice supplémentaire.


Applications pratiques

En NumPy/SciPy

cholesky_numpy.pypython
import numpy as np
from scipy.linalg import cholesky, cho_solve, cho_factor

# Matrice symétrique définie positive
A = np.array([[4, 2, 2],
            [2, 5, 1],
            [2, 1, 6]], dtype=float)
b = np.array([1, 2, 3], dtype=float)

# Factorisation
L = cholesky(A, lower=True)
print("L =\n", L)

# Résolution efficace
c, low = cho_factor(A)
x = cho_solve((c, low), b)
print("Solution x =", x)

Cas d'usage typiques

  1. Moindres carrés : ATAA^T A est SDP dès que AA est de rang colonne plein
  2. Krigeage/Interpolation : Matrices de covariance
  3. Optimisation : Matrices hessiennes (si définies positives)
  4. Éléments finis : Matrices de rigidité

Résumé

La factorisation de Cholesky est la méthode de choix pour les matrices symétriques définies positives :

  • Forme : A=LLTA = L \cdot L^T
  • Condition : AA symétrique et définie positive
  • Complexité : n36\frac{n^3}{6} (2× plus rapide que LU)
  • Stabilité : Toujours stable, pas de pivotage nécessaire
  • Détection : Échec de la racine carrée → matrice non SDP
💡

Recommandation pratique

Quand vous savez que votre matrice est symétrique définie positive (covariance, rigidité, ATAA^T A, etc.), utilisez toujours Cholesky plutôt que LU — c'est 2× plus rapide et plus stable.