Tous le code Python proposé est accessible dans un notebook Jupyter interactif.

Notations

On adopte les notations suivantes :

  • Tn++(R) désigne l’ensemble des matrices triangulaires supérieures de Mn(R) à coefficients diagonaux strictement positifs.
  • , désigne le produit scalaire usuel sur Mn,1(R) i.e. (X,Y)Mn,1(R)2, X,Y=XY.

Théorème

Soit AGLn(R). Alors il existe QOn(R) et RTn++(R) telles que A=QR.

Intérêt

Dès lors que l’on a une décomposition QR d’une matrice A, il devient trivial de résoudre un sytème AX=Y. En effet ce système équivaut à RX=Q1Y=QY. Le calcul de Q est immédiat et ce dernier système est triangulaire : il se résout donc très simplement.

Rappels sur le procédé d’orthonormalisation de Gram-Schmidt

Si (f1,,fn) est une base d’un espace euclidien E de dimension n, on peut la transformer en une base orthonormée (e1,,en) en appliquant le procédé suivant :

  • on norme le premier vecteur f1 en un vecteur e1 ;
  • une fois construits les vecteurs e1,,ek, on norme le projeté orthogonal de fk+1 sur vect(e1,,ek) pour obtenir le vecteur ek+1.

Du point de vue du calcul, cela revient à construire par récurrence les vecteurs f1,,fn via la relation de récurrence

k[[0,n1]],ek+1=e~k+1e~k+1 où e~k+1=fk+1j=1kfk+1,ejej

Remarque. On convient que la somme est nulle lorsque k=0.

En plus du fait que (e1,,en) est une base orthonormée de E, on vérifie aisément que

k[[1,n]],(vect(e1,,ek)=vect(f1,,fk))(ek,fk>0)

La décomposition QR via le procédé de Gram-Schmidt

Notons f1,,fn les colonnes de la matrice AGLn(R). Comme la matrice A est inversible, la famille (f1,,fn) est une base de Mn,1(R). On lui applique le procédé d’orthonormalisation de Gram-Schmidt pour obtenir une base orthonormée (pour le produit scalaire canonique) (e1,,en) de Mn,1(R).

Notons Q la matrice dont les colonnes sont e1,,en. Comme (e1,,en) est une base orthonormée, Q est une matrice orthogonale. En notant R la matrice de la base (f1,,fn) dans la base (e1,,en), on a alors bien A=QR.

De plus, on rappelle que

k[[1,n]],(vect(e1,,ek)=vect(f1,,fk))(ek,fk>0)

donc la matrice R est triangulaire supérieure et ses coefficients diagonaux sont strictement positifs. En effet, comme (e1,,en) est une base orthonormée, Ri,j=fj,ei pour tout (i,j)[[1,n]]2 et notamment Rk,k=fk,ek>0 pour tout k[[1,n]].

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, E désigne un espace euclidien de dimension n et B une base orthonormée de E.

Matrices de Householder

Soient n un vecteur non nul de E et s la réflexion par rapport à l’hyperplan vect(n). Le projeté orthogonal d’un vecteur xE sur vect(n) est n,x|n|2n. On en déduit que

s(x)=x2n,xn2n

Si on note N sa matrice de n dans la base B, alors la matrice de s dans la base B est donc

In2NNNN

Une telle matrice est appelée une matrice de Householder.

Réflexion échangeant deux vecteurs de même norme

Soient E un espace euclidien de dimension n ainsi que u et v deux vecteurs distincts et de même norme. Il existe alors une (unique) réflexion s échangeant u et v. Il s’agit de la réflexion par rapport à l’hyperplan vect(vu).

Notamment si S, U et V sont respectivement les matrices de s, u et v dans cette base B, alors

S=In2(UV)(UV)(UV)(UV)

Application à la décomposition QR

On procède à un raisonnement par récurrence. Il est clair que toute matrice de GL1(R) admet une décomposition QR. Supposons que ce soit vrai pour toute matrice de GLn1(R), où n est un entier supérieur ou égal à 2.

Soit AGLn(R). Notons U sa première colonne (nécessairement non nulle). Comme les vecteurs U et V=(|U|,0,,0) ont même norme, il existe une matrice de réflexion S telle que SU=V. La première colonne de SA est alors V. Notons A~ la matrice SA dont on a supprimé les premières lignes et premières colonnes. Autrement dit

SA=(U00A~)

Comme S et A sont inversibles, det(SA)=|U|det(A~)0 donc A~GLn1(R). Il existe donc une matrice Q~On1(R) et R~Tn1++(R) telle que A~=Q~R~. Puisque S=S1 (S est une matrice de réflexion), on a alors A=QR avec

Q=S(10000Q~)On(R)etR=(U00R~)Tn++(R)

On en déduit également un algorithme :


Entrée Une matrice AGLn(R)

  • Initialisation
    • RA
    • QIn
  • Pour k variant de 1 à n :
    • On extrait la première colonne UMnk+1,1(R) de la matrice extraite (Ri,j)ki,jn
    • Si U1|U|
      • V(|U|,0,,0).
      • SInk+12(UV)(UV)(UV)(UV)
      • Q1(Ik100S)
      • QQQ1
      • RQ1R

Renvoyer Q et R


On définit d’abord une fonction calculant la matrice de Householder associée à un vecteur U.

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 A.

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