Tous le code Python proposé est accessible dans un notebook Jupyter interactif.
Notations
On adopte les notations suivantes :
désigne l’ensemble des matrices triangulaires supérieures de à coefficients diagonaux strictement positifs. désigne le produit scalaire usuel sur i.e. , .
Théorème
Soit
Intérêt
Dès lors que l’on a une décomposition QR d’une matrice
Rappels sur le procédé d’orthonormalisation de Gram-Schmidt
Si
- on norme le premier vecteur
en un vecteur ; - une fois construits les vecteurs
, on norme le projeté orthogonal de sur pour obtenir le vecteur .
Du point de vue du calcul, cela revient à construire par récurrence les vecteurs
Remarque. On convient que la somme est nulle lorsque
En plus du fait que
La décomposition QR via le procédé de Gram-Schmidt
Notons
Notons
De plus, on rappelle que
donc la matrice
Algorithme en Python
import numpy as np
def QR_gram_schmidt(A):
Q = np.zeros_like(A)
R = np.zeros_like(A)
for i in range(A.shape[1]):
dp = Q[:, :i].T@A[:, i]
R[:i, i] = dp
Q[:, i] = A[:, i]-Q[:, :i]@dp
Q[:, i] = Q[:, i]/np.linalg.norm(Q[:, i])
R[i, i] = Q[:, i].T@A[:, i]
return Q, R
Méthode de Householder
Dans ce qui suit,
Matrices de Householder
Soient
Si on note
Une telle matrice est appelée une matrice de Householder.
Réflexion échangeant deux vecteurs de même norme
Soient
Notamment si
Application à la décomposition QR
On procède à un raisonnement par récurrence. Il est clair que toute matrice de
Soit
Comme
On en déduit également un algorithme :
Entrée Une matrice
- Initialisation
- Pour
variant de à :- On extrait la première colonne
de la matrice extraite - Si
.
- On extrait la première colonne
Renvoyer
On définit d’abord une fonction calculant la matrice de Householder associée à un vecteur
import numpy as np
def matrice_householder(U):
return np.eye(U.shape[0]) - 2 * (U@U.T) / (U.T@U)
On implémente alors un algorithme itératif calculant la décomposition QR d’une matrice inversible
def QR_householder(A):
n = A.shape[0]
R = A.copy()
Q = np.eye(n)
for k in range(n):
U = R[k:, k].copy()
norme_U = np.linalg.norm(U)
if U[0] != norme_U:
U[0] -= norme_U
S = matrice_householder(U)
Q[:, k:] = Q[:, k:]@S
R[k:, :] = S@R[k:, :]
return Q, R
On peut également proposer une fonction récursive.
def QR_householder_recu(A):
if A.shape[0] == 1:
return (np.matrix([[1]]), A) if A[0, 0] > 0 else (np.matrix([[-1]]), -A)
U = A[:, 0].copy()
norme_U = np.linalg.norm(U)
if U[0] != norme_U:
U[0] -= norme_U
Q = matrice_householder(U)
R = Q@A
else:
Q = np.eye(A.shape[0])
R = A
Q1, R1 = QR_householder_recu(R[1:, 1:])
Q[:, 1:] = Q[:, 1:]@Q1
R[1:, 1:] = R1
return Q, R