In [1]:
import numpy as np
import numpy.linalg as lin

np.set_printoptions(precision=3, linewidth=150, suppress=True)

Conditionnement d'une matrice¶

Soit la matrice A suivante :

In [2]:
A = np.array([[10, 7, 8, 7], [7, 5, 6, 5], [8, 6, 10, 9], [7, 5, 9, 10]])
A
Out[2]:
array([[10,  7,  8,  7],
       [ 7,  5,  6,  5],
       [ 8,  6, 10,  9],
       [ 7,  5,  9, 10]])

Une matrice symétrique qui n'a rien de méchant a priori. Son déterminant est 1.

In [3]:
lin.det(A)
Out[3]:
0.9999999999999869

Construisons b de telle sorte que la solution du système matriciel A x = b soit [1,1,1,1] :

In [4]:
b = A.sum(axis=1)
print(b)
x = lin.solve(A, b)
x
[32 23 33 31]
Out[4]:
array([1., 1., 1., 1.])

Perturbons légèrement b. Dans le cas d'une expérience cela s'appelle une erreur de mesure. En informatique cela peut être le résultat d'erreurs d'arrondi.

In [5]:
bp = [32.1, 22.9, 33.1, 30.9]
eb = lin.norm(b - bp) / lin.norm(b) # une erreur se mesure par rapport à la valeur de la donnée
eb
Out[5]:
0.0033319453118976702

On a une erreur sur b de l'ordre de 0,3 %. On la note $ ||{\bf \delta b}|| \, / \,||{\bf b}||$.

On pourrait espérer une erreur sur le résultat du même ordre de grandeur. Regardons la solution x de notre système matriciel perturbé :

In [6]:
xp = lin.solve(A, bp)
xp
Out[6]:
array([  9.2, -12.6,   4.5,  -1.1])

Cette solution n'a rien à voir avec [1, 1, 1, 1].

In [7]:
ex = lin.norm(x - xp) / lin.norm(x)
ex
Out[7]:
8.19847546803699

L'erreur est de l'ordre de 8 pour 1.

L'erreur est 2460 fois plus grande que l'erreur sur b.

In [8]:
ex / eb
Out[8]:
2460.567236431514

Pourquoi ?¶

On a

$$ \begin{align} & A ({\bf x} + {\bf \delta x}) = {\bf b} + {\bf \delta b} \quad \textrm{et donc} \\ & A \, {\bf \delta x} = {\bf \delta b} \; \textrm{ puisque } A {\bf x} = {\bf b} \quad \textrm{et finalement}\\ & {\bf \delta x} = A^{-1} \, {\bf \delta b} \end{align} $$

Comme A et son inverse sont des applications linéaires on a

$$ \begin{align} & ||{\bf b}|| \le ||A|| \, ||{\bf x}|| \quad \textrm{et} \quad ||{\bf \delta x}|| \le ||A^{-1}|| \, ||{\bf \delta b}|| \end{align} $$

donc

$$ \begin{align} \frac{||{\bf \delta x}||}{||{\bf x}||} \le ||A^{-1}|| \, \frac{||{\bf \delta b}||}{||{\bf x}||} \le ||A^{-1}|| \, ||A|| \, \frac{||{\bf \delta b}||}{||{\bf b}||} \end{align} $$

In [9]:
lin.norm(lin.inv(A)) * lin.norm(A)
Out[9]:
3009.5787080586942

Le problème est là.

On appelle cela le conditionnement de A :

cond(A) = $||A^{-1}|| \, ||A||$

Une matrice mal conditionnée va générer des erreurs de calcul lors de la résolution du système matriciel.

In [10]:
np.linalg.cond(A) # scipy n'a pas le conditionnement mais numpy l'a. 
Out[10]:
2984.0927016757555

Bizarre cette différence avec le calul ci-dessus qui a donné 3009.

Perturbons la matrice¶

Que ce passe-t-il si on perturbe A et non b ?

In [11]:
np.random.seed(0)

dA = 2 * np.random.random(size = A.shape) - 1
display(dA)
ea = lin.norm(dA) / lin.norm(A)
print('Erreur relative sur A :', ea)
array([[ 0.098,  0.43 ,  0.206,  0.09 ],
       [-0.153,  0.292, -0.125,  0.784],
       [ 0.927, -0.233,  0.583,  0.058],
       [ 0.136,  0.851, -0.858, -0.826]])
Erreur relative sur A : 0.06868857112100454
In [12]:
Ap = A + dA
Ap
Out[12]:
array([[10.098,  7.43 ,  8.206,  7.09 ],
       [ 6.847,  5.292,  5.875,  5.784],
       [ 8.927,  5.767, 10.583,  9.058],
       [ 7.136,  5.851,  8.142,  9.174]])
In [13]:
xp = lin.solve(Ap, b)
xp
Out[13]:
array([-12.365,  15.574,  10.146,  -5.94 ])
In [14]:
ex = lin.norm(xp - x) / lin.norm(x)
ex
Out[14]:
11.432687335993894
In [15]:
ex / ea
Out[15]:
166.44235204505293

On note que l'erreur est nettement moins grande. La raison est qu'on n'a pas trouvé l'erreur sur A qui perturbera le plus possible le résultat. En fait ce n'est pas que le conditionnement de A qui compte, l'erreur est aussi importante. Deux erreurs de même norme pertuberont différemment le résultat.

Notons aussi que dans ce cas les maths sont un peu différente mais on retrouve le conditionnement de A :

$$ \begin{align} & (A + \Delta A) \, ({\bf x} + {\bf \delta x}) = {\bf b} \quad \textrm{et donc} \\ & A \, {\bf \delta x} + \Delta A \, ({\bf x} + {\bf \delta x}) = 0 \; \textrm{ puisque } A {\bf x} = {\bf b} \quad \textrm{et finalement}\\ & {\bf \delta x} = -A^{-1} \,\Delta A \, ({\bf x} + {\bf \delta x}) \quad \textrm{et} \\ & ||{\bf \delta x}|| \le ||A^{-1}|| \, ||\Delta A|| \, ||{\bf x} + {\bf \delta x}|| \end{align} $$

Ainsi

$$ \begin{align} \frac{||{\bf \delta x}||}{||{\bf x} + {\bf \delta x}||} \le ||A^{-1}|| \, ||\Delta A|| = ||A^{-1}|| \, ||A|| \, \frac{||\Delta A||}{||A||} \end{align} $$

ou

$$ \begin{align} \frac{||{\bf \delta x}||}{||{\bf x} + {\bf \delta x}||} \le cond(A) \, \frac{||\Delta A||}{||A||} \end{align} $$

L'erreur ne se mesure pas par rapport à x mais par rapport à ${\bf x} + {\bf \delta x}$.

Propriétés¶

  • $cond(A) \ge 1$ car $Id = A\, A^{-1}$ et donc $1 \le ||A||\, ||A^{-1}||$ (pour les normes subordonnées car $||Id||_F = \sqrt{n}$)
  • $cond(A) = cond(A^{-1})$ par définition du conditionnement
  • $\displaystyle cond_2(A) = \frac{\max_i |\lambda_i|}{\min_i |\lambda_i|}$ si la matrice est normale où le 2 indique qu'on utilise la norme 2 et $\lambda_i$ sont les valeurs propres de A
In [16]:
vp = lin.eigvals(A)
vp.max() / vp.min()  # et voici la différence avec le calcul ||A|| ||inv(A)|| ci-dessus
                     # ca sent l'erreur numérique
Out[16]:
(2984.092701676269+0j)
  • si A est unitaire ou orthogonale alors $cond_2(A) = 1$ (une rotation ou symétrie ne va pas agrandir l'erreur)
  • le conditionnement n'est pas modifié par transformation unitaire

Préconditionnement¶

Si le conditionnement n'est pas modifié par une transformation unitaire, il l'est par d'autres transformations. Ainsi

$$ \forall A, \exists B \; \textrm{appelée matrice de préconditionnement t.q.} \quad cond(B\, A) \le cond(A) $$

aussi on lieu de résoudre $A {\bf x} = {\bf b}$ on résoud $B\, A {\bf x} = B\, {\bf b}$

Toute la difficulté consiste à trouver une matrice de préconditionnement B qui soit simple à calculer.

Exercice 11.1¶

Quelle est la meilleure matrice de préconditionnement de A ? Pourquoi ?

In [ ]: