Nov 21, 2024

Wiki

Python

Aide

edit SideBar

Search

Elements Propres Des Matrices


On découvre ici différentes techniques de calcul des éléments propres d'une matrice donnée, ainsi que des applications associées. On suppose que le lecteur est familier avec les notions de diagonalisation.

Ce TP s'inspire du livre Maths et Maple de Jean-Michel Ferrard : il condense le chapitre 6, le transpose à python, et propose des travaux pratiques en plus.

Éléments propres en python

Elements propres avec numpy

Valeurs propres, vecteurs propres

Pour obtenir les valeurs propres, importez le paquetage linalg, et utilisez la fonction eigvals :

  >>> from numpy import *
  >>> A = matrix((1,2,3,4,5,6,7,8,1)).reshape((3,3))
  >>> eigvals(A)
  array([ 12.45416474,  -0.37976219,  -5.07440255])

On peut obtenir les vecteurs propres ainsi :

  >>> eig(A)
  (array([ 12.45416474,  -0.37976219,  -5.07440255]),
  matrix([ [-0.29373774,   -0.73967882, -0.29720654],
           [-0.69005397,  0.66500848, -0.39870229],
           [-0.66147083, -0.10314536,  0.86758559] ]))

Le retour est constitué du tableau des valeurs propres, suivi de la matrice des vecteurs propres.

Diagonalisation

Donnons un premier exemple de diagonalisation matricielle, dans le cas d'une matrice diagonalisable. On commence par importer ce qu'il faut, et définir la matrice à diagonaliser...

  >>> from numpy import *
  >>> from numpy.linalg import *
  >>> A = matrix((1,2,3,4,5,6,7,8,9)).reshape((3,3))
  >>> set_printoptions(suppress = True)

On rappelle que la fonction eig renvoie le tableau des valeurs propres, et la matrice des vecteurs propres.

  >>> eig(A)
  (array([ 16.11684397,  -1.11684397,  -0.        ]), 
  matrix([[-0.23197069,   -0.78583024,  0.40824829],
          [-0.52532209, -0.08675134, -0.81649658],
          [-0.8186735 ,  0.61232756,  0.40824829]]))

$A$ possède 3 valeurs propres distinctes, et est de taille $3 \times 3$, donc est diagonalisable.

L'écriture diagonale de $A$ correspond à l'insertion des valeurs propres dans la diagonale d'une matrice nulle. Cela se fait grâce à la fonction diag:

  >>> D=diag(eig(A)[0])
  >>> D
  array([[ 16.11684397,   0.        ,   0.        ],
         [  0.        ,  -1.11684397,   0.        ],
         [  0.        ,   0.        ,  -0.        ]])

Quant à la matrice de passage P de la base canonique vers la base où $A$ est diagonale, elle correspond à la matrice des vecteurs propres :

  >>> P=eig(A)[1]

On vérifie que l'on a bien $A = P D P^{-1}$ :

  >>> P*D*inv(P)
  matrix([[ 1.,  2.,  3.],
          [ 4.,  5.,  6.],
          [ 7.,  8.,  9.]])
  >>> A
  matrix([[1, 2, 3],
          [4, 5, 6],
          [7, 8, 9]])
  >>> D
  array([[ 16.11684397,   0.        ,   0.        ],
         [  0.        ,  -1.11684397,   0.        ],
         [  0.        ,   0.        ,  -0.        ]])

La diagonalisation est réalisée !

Polynôme caractéristique

Reste à obtenir le polynôme caractéristique :

  >>> from numpy import *
  >>> M=matrix((1,2,3,4)).reshape((2,2))
  >>> poly(M)
  array([ 1., -5., -2.])

C'est-à-dire que le polynôme caractéristique est $\chi_M(\lambda) = \lambda^2-5\lambda-2$.

Elements propres avec sympy

Valeurs propres, vecteurs propres

Cas des valeurs propres

Pour obtenir les valeurs propres, on utilise la méthode eigenvals :

  >>> from sympy import *
  >>> A = Matrix((-5,2,4,-4,
10,-3,-6,8,
11,-4,-6,8,
22,-8,-14,17)).reshape(4,4) >>> A [-5, 2, 4, -4] [10, -3, -6, 8] [11, -4, -6, 8] [22, -8, -14, 17] >>> vp=A.eigenvals() >>> vp {1: 2, 2: 1, -1: 1}

Une fois encore, le calcul est ici exact. Les valeurs derrière les : correspondent aux multiplicités des valeurs propres.

Le retour de la fonction eigenvals est un dictionnaire. Pour récupérer ses clés, qui correspondent aux valeurs propres à proprement parlé :

  >>> vp.keys()
  [1, 2, -1]

Cas des vecteurs propres

Pour les vecteurs propres associés, la méthode est eigenvects :

  >>> A.eigenvects()

Cependant, dans la version de sympy que je possède, cette méthode a un problème. Je vous propose un petit correctif :

  >>> def eigenvects2(self, **flags):
  ...     if flags.has_key('multiple'):
  ...           del flags['multiple']
  ...       out, vlist = [], self.eigenvals(**flags)
  ...       for r, k in vlist.iteritems():
  ...           tmp = self - eye(self.lines)*r
  ...           basis = tmp.nullspace()
  ...           if len(basis) != k:
  ...               basis = tmp.nullspace(simplified=True)
  ...           out.append((r, k, basis))
  ...       return out
  ...

Pour l'utiliser :

  >>> VP = eigenvects2(A)
  >>> len(VP) 
  3

Il y a donc trois valeurs propres différentes...

  >>> VP[0]
  (1, 2, [[1]
  [1]
  [1]
  [0], [0]
  [2]
  [0]
  [1]])

Donc 1 est de multiplicité 2, et possède pour vecteurs propres linéairement indépendants $(1,1,1,0)^T$ et $(0,2,0,1)^T$. De même,

  >>> VP[1]
  (2, 1, [[  0]
  [  1]
  [1/2]
  [  1]])

Donc 2 est de multiplicité 1, et possède pour vecteur propre $\left(0,1,\frac{1}{2},1\right)^T$. etc.

Polynôme caractéristique

Reste à montrer comment obtenir le polynôme caractéristique :

  >>> P = A.charpoly('x')
  >>> P
  Poly(x**3 - 15*x**2 - 18*x, x)

Diagonalisation

Le principe

Cas de diagonalisabilité

On reprend l'exemple précédent de diagonalisation, en l'enrichissant : on va tester si la matrice considérée est diagonalisable, avant de réaliser sa diagonalisation proprement dite.

On rappelle quelles sont les conditions pour qu'une matrice $A$ puisse s'écrire sous la forme $P D P^{-1}$$D$ est diagonale, c'est-à-dire pour qu'il existe une base dans laquelle $A$ a une forme diagonale.

Pour cela, il faut que :

  • la somme des multiplicités des valeurs propres soit égale à $n$,
  • la somme des dimensions des sous-espaces propres est égale à $n$.

On rappelle que les sous-espaces propres sont les sous-espaces vectoriels engendrés par les vecteurs propres associés à chacune des valeurs propres (il y a un sous-espace propre pour chacune des valeurs propres distinctes).

Travaux pratiques (sympy)

  • Faire une fonction qui teste si une matrice donnée est diagonalisable.
Puisque la méthode eigenvects2 renvoie la multiplicité des valeurs propres, et les bases de vecteurs propres, il suffit de vérifier si, pour chaque valeur propre, sa multiplicité est égale au nombre de vecteurs propres associés.
  • Faire une fonction qui renvoie le couple $(D,P)$, après avoir testé si la matrice passée en argument est effectivement diagonalisable.

Applications

On montre, dans ce qui suit, deux applications classiques de la diagonalisation : le calcul de la puissance nième d'une matrice, et la résolution d'un système différentiel...

Calcul de la puissance d'une matrice

Technique

Pour calculer $M^n$, on évite de faire $M \times M \times ... \times M$, $n$ fois, vu le coût d'un calcul matriciel.

On préfère la technique suivante, quand elle est possible : on diagonalise $M$, on élève la réduite diagonale $D$ à la puissance $n$, puis on utilise la relation

$M^n = P D^n P^{-1}$

$P$ est la matrice de passage de la diagonalisation.

Exemple

Voici un exemple : considérons la matrice $M$ suivante, dont on souhaite obtenir la puissance $M^{7}$...

$\left(\begin{array}{ccc}1 & 4 & 3 \\ 1 & -2 & 1 \\ 0 & 1 & 3 \end{array}\right)$

On calcule sa matrice diagonale, et sa matrice de passage :

  >>> from numpy import *
  >>> from numpy.linalg import *
  >>> M = matrix((1,4,3,1,-2,1,0,1,3)).reshape((3,3))
  >>> M
  matrix([[ 1,  4,  3],
          [ 1, -2,  1],
          [ 0,  1,  3]])
  >>> D,P=diag(eig(M)[0]),eig(M)[1]
  >>> D
  array([[-3.03404184,  0.        ,  0.        ],
         [ 0.        ,  1.48653522,  0.        ],
         [ 0.        ,  0.        ,  3.54750661]])
  >>> P
  matrix([[ 0.6505674 ,  0.96068514,  0.87265468],
        [-0.74922933,  0.23164273,  0.23451873],
        [ 0.12416708, -0.15305459,  0.42833955]])

Alors la matrice $M^{7}$ sera égale à $P D^{7} P^{-1}$, où $D^{7}$ s'obtient en élevant chaque nombre sur la diagonale à la puissance 7 :

  >>> D7 = zeros((3,3))
  >>> D7[0,0]=D[0,0]**7
  >>> D7[1,1]=D[1,1]**7
  >>> D7[2,2]=D[2,2]**7
  >>> D7
  array([[-2366.74219863,     0.        ,     0.        ],
       [    0.        ,    16.04081493,     0.        ],
       [    0.        ,     0.        ,  7070.7013837 ]])

D'où la puissance septième de $M$ :

  >>> P*D7*inv(P)
  matrix([[   656.,   4392.,  10664.],
          [   768.,  -1208.,   2968.],
          [   440.,   1648.,   5272.]])

Travaux pratiques

  1. Calculez, de manière approchée, la puissance huitième de

$\left(\begin{array}{ccc} 0 & 1 & 1 \\-3 & 4 & 3 \\ -1 & 1 & 2 \end{array}\right)$

  1. Calculez, de manière formelle, la puissance nième de la matrice

$\left(\begin{array}{cc} 1 & 4 \\-1 & -2 \end{array}\right)$

On utilisera, pour diagonaliser les matrices concernées, la fonction définie dans la section précédente.

Résolution d'un système différentiel

Rappel du théorème

On peut utiliser la diagonalisation de $M$ pour résoudre le système différentiel $X' = MX$.

En effet, si $M$ est diagonalisable, alors $ X' = MX$ est équivalent à $X' = P D P^{-1} X$. Donc $P^{-1} X' = D P^{-1} X$, et le système est alors équivalent à

$Y' = D Y$.

avec $Y = P^{-1} X$. Or, le nouveau système est d'une forme bien plus sympathique :

$\frac{d}{dt} \left( \begin{array}{c} y_1 \\ y_2 \\ \vdots \\ y_n \end{array} \right) = \left( \begin{array}{cccc} d_1 & 0 & ... & 0 \\ 0 & d_2 & \ddots & \vdots \\ \vdots & \ddots & \ddots & \vdots \\ 0 & ... & 0 & d_n \end{array} \right) \times \left( \begin{array}{c} y_1 \\ y_2 \\ \vdots \\ y_n \end{array} \right)$

C'est-à-dire qu'il correspond au système

$\left\{ \begin{array}{ccc}y_1^' & = & d_1 y_1 \\ y_2^' & = & d_2 y_2 \\ \vdots & &\vdots \\ y_n^' & = & d_n y_n \end{array}\right.$

Ce système admet pour solution

$\left\{ \begin{array}{ccc}y_1 & = & e^{d_1t} u_1 \\ y_2 & = & e^{d_2t} u_2 \\ \vdots & &\vdots \\ y_n & = & e^{d_nt} u_n \end{array}\right.$

$U = (u_1, ..., u_n)^T$ est le vecteur des constantes d'intégration, déterminables à l'aide des conditions initiales. Cette solution donne, sous forme matricielle :

$\left( \begin{array}{c} y_1 \\ y_2 \\ \vdots \\ y_n \end{array} \right) = \left( \begin{array}{cccc} e^{d_1t} & 0 & ... & 0 \\ 0 & e^{d_2t} & \ddots & \vdots \\ \vdots & \ddots & \ddots & \vdots \\ 0 & ... & 0 & e^{d_nt }\end{array} \right) \times \left( \begin{array}{c} u_1 \\ u_2 \\ \vdots \\ u_n \end{array} \right) = \left( \begin{array}{cccc} \sum_{k=0}^{+\infty} \frac{d_1^kt}{k!} & 0 & ... & 0 \\ 0 & \sum_{k=0}^{+\infty} \frac{d_2^kt}{k!} & \ddots & \vdots \\ \vdots & \ddots & \ddots & \vdots \\ 0 & ... & 0 & \sum_{k=0}^{+\infty} \frac{d_n^kt}{k!} \end{array} \right) \times \left( \begin{array}{c} u_1 \\ u_2 \\ \vdots \\ u_n \end{array} \right)$

On reconnaît alors une exponentielle de matrice...

$Y = \sum_{k=0}^{+\infty} \frac{1}{k!} \left( \begin{array}{cccc} d_1t & 0 & ... & 0 \\ 0 & d_2t & \ddots & \vdots \\ \vdots & \ddots & \ddots & \vdots \\ 0 & ... & 0 & d_nt \end{array} \right)^k \times \left( \begin{array}{c} u_1 \\ u_2 \\ \vdots \\ u_n \end{array} \right) = e^{Dt} U$

Finalement, en se rappelant que $Y = P^{-1} X$...

  Théorème : 
  La solution du système différentiel $X' = MX$, où la matrice $M$ est 
  diagonalisable ($M = PDP^{-1}$), est $X = P e^{Dt} U$, où $U = P^{-1}X(0)$.

Exemple de résolution avec sympy

On souhaite résoudre le système différentiel :

$\left\{ \begin{array}{lll} x_1^' & = & x_1 + 4 x_2 \\ x_2^' & = & x_1 - 2 x_2 \end{array} \right.$

avec la condition initiale $x_1(0) = 1, x_2(0) = 1$.

Définissons ce système en python :

  >>> from sympy import *
  >>> M = Matrix((1,4,1,-2)).reshape(2,2)
  >>> X0 = Matrix((1,1)).reshape(2,1)
  >>> X0
  [1]
  [1]

Puis, recherchons les éléments propres de $M$

  >>> M.eigenvects()
  [(2, 1, [[4]
  [1]]), (-3, 1, [[-1]
  [ 1]])]
  >>> D = Matrix((2,0,0,-3)).reshape(2,2)
  >>> D
  [2,  0]
  [0, -3]
  >>> P = Matrix((4,-1,1,1)).reshape(2,2)
  >>> P
  [4, -1]
  [1,  1]

On peut maintenant calculer $e^{Dt}$ :

  >>> t = Symbol('t')
  >>> d = Matrix((exp(2*t),0,0,exp(-3*t))).reshape(2,2)
  >>> d
  [exp(2*t),         0]
  [       0, exp(-3*t)]

Puis $U$ :

  >>> U = P.inv()*X0
  >>> U
  [2/5]
  [3/5]

On peut donc obtenir la solution $X(t)$ :

  >>> X = P*d*U
  >>> X
  [-3*exp(-3*t)/5 + 8*exp(2*t)/5]
  [ 3*exp(-3*t)/5 + 2*exp(2*t)/5]

On peut alors, par exemple, regarder quelle sera la solution du système aux temps $t=1$ et $t=7,5$ :

  >>> X.subs(t,1)
  [-3*exp(-3)/5 + 8*exp(2)/5]
  [ 3*exp(-3)/5 + 2*exp(2)/5]
  >>> X.subs(t,1).evalf()
  [11.7926175172683]
  [2.98549468059298]
  >>> X.subs(t,7.5)
  [8*exp(15.0000000000000)/5 - 3*exp(-22.5000000000000)/5]
  [2*exp(15.0000000000000)/5 + 3*exp(-22.5000000000000)/5]
  >>> X.subs(t,7.5).evalf()
  [5230427.79595538]
  [1307606.94898884]

Travaux pratiques

  • Résoudre le système différentiel :

$\left\{ \begin{array}{lll} x_1^' & = & x_2 + x_3 \\ x_2^' & = & -3 x_1 + 4 x_2 + 3 x_3 \\ x_3^' & = & -x_1+x_2+2 x_3\end{array} \right.$

avec la condition initiale de votre choix.
  • Faire une méthode qui reçoit une matrice $M$ et une condition initiale, et qui renvoie la solution du système $X' = MX$ satisfaisant à cette condition initiale.

Méthodes QR, LU et Choleski

Les décompositions LU, QR et de Choleski peuvent servir à approcher les valeurs propres, voire les vecteurs propres...

Utilisation de la décomposition QR

La méthode

Soit $A_1$ une matrice inversible. Alors on sait qu'il existe un couple $(Q_1,R_1)$ de matrices telles que $A_1 = Q_1 \times R_1$, où :

  • $Q_1$ est orthogonale,
  • $R_1$ est triangulaire supérieure à diagonale strictement positive.

Soit alors $A_2 = R_1 \times Q_1$. On peut montrer que cette matrice est semblable à $A_1$ : elles ont les mêmes valeurs propres. On applique alors à $A_2$ une décomposition QR : $A_2 = Q_2 \times R_2$.

Soit alors $A_3 = R_2 \times Q_2$, etc.

Imaginons continuer ainsi le procédé. Alors on peut aboutir, sous certaines hypothèses, à trouver les valeurs propres de $A$.

Plus précisément, on peut montrer que :

  Théorème : 
  Si :
   * les valeurs propres de A sont de module deux à deux distincts,
   * une matrice de passage diagonalisant A possède une décomposition
     LU,
  Alors la suite de matrices :
   * $A_0 = A$,
   * $A_{i+1} = R_i \times Q_i$, où $(Q_i,R_i)$ est le couple de la 
     décomposition QR de $A_i$
  converge vers une matrice triangulaire, dont la diagonale est 
  constituée des valeurs propres de A.

Bien évidemment, les hypothèses du théorème précédent sont impossibles à vérifier, puisqu'on cherche précisément quelles sont les valeurs propres de $A$. On se contente donc d'agir de façon empirique.

Exemple de réalisation

Cette méthode peut facilement se mettre en œuvre, par exemple pour trouver les valeurs propres de la matrice

$\left( \begin{array}{ccc} -4 & 4 & -2 \\ 1 & -4 & 5 \\ -4 & 8 & 2 \end{array}\right)$

  >>> from sympy import *
  >>> M = Matrix((-4,4,-2,1,-4,5,-4,8,2)).reshape(3,3)
  >>> for k in range(20):
  ...     (Q,R) = M.QRdecomposition()
  ...     M = R.evalf()*Q.evalf()
  ... 
  >>> M
  [    -10.0000974898348,    2.13808816953320, 4.01150932531371]
  [ -5.21174585283222e-5,   -1.99998604282076, 1.15476205049695]
  [-0.000361064881559241, 9.66940332900396e-5, 6.00008353265553]

On tend donc à se rapprocher d'une matrice triangulaire, dont la diagonale est approximativement égale à -10, -2, 6. Vérifions que l'on a bien cela pour valeurs propres...

  >>> from numpy import *
  >>> from numpy.linalg import *
  >>> M = matrix((-4,4,-2,1,-4,5,-4,8,2)).reshape((3,3))
  >>> eigvals(M)
  array([-10.,  -2.,   6.])

C'est effectivement le cas.

Discussion

On constate les points suivants :

  • La vitesse de convergence est d'autant plus rapide que les modules des différentes valeurs propres sont bien séparés les uns des autres.
  • Si deux valeurs propres sont de même module, le procédé risque de ne pas converger.

Travaux pratiques

  • Programmez une fonction de détermination de valeurs propres par la méthode QR, en utilisant votre propre fonction de décomposition.
  • Vérifiez le premier point de la discussion sur la matrice

$\left(\begin{array}{cccc} 337 & 124 & -101 & -11 \\ 528 & 453 & 2 & -178 \\ 1227 & 718 & -199 & -209 \\ 2037 & 1258 & -299 & -279 \end{array}\right)$

  • Vérifiez le second point de la discussion sur la matrice

$\left(\begin{array}{ccc} 1 & 2 & 3 \\ 3 & 2 & 1 \\ 2 & 3 & 1 \end{array}\right)$

Vers une utilisation efficace

Dans le cas général,

  • on n'est pas certain de la convergence de la suite de matrices,
  • le procédé est très coûteux en temps de calcul,

on devra donc trouver mieux.

Cependant, cette méthode s'applique bien si la matrice est de Hessenberg (i.e. presque triangulaire supérieure, sauf les coefficients immédiatement sous la diagonale), ou tridiagonale symétrique.

Cela peut sembler franchement restrictif. Cependant, on peut montrer que toute matrice réelle (respectivement symétrique réelle) peut être transformée en matrice de Hessenberg (resp. tridiagonale symétrique), par des méthodes de type Householder, ou Givens.

Dans le même esprit, comme la rapidité de la convergence est fonction de la bonne séparation des modules des valeurs propres, on peut améliorer la méthode QR en effectuant un décalage de la matrice. On ne s'intéresse alors plus aux $A_i$, mais aux $A_i - \beta I_n$.

Nous ne détaillerons pas ici ces points. Voir le livre de Ferrard pour plus d'informations.

Utilisation de la décomposition LU

Présentation

On peut, comme ci-dessus, utiliser la décomposition LU pour déterminer les valeurs propres d'une matrice $A$.

On rappelle que si, pour tout entier k compris entre 1 et la taille de $A$, la sous-matrice formée des $k$ premières lignes et des $k$ premières colonnes de $A$ est inversible, alors $A$ peut s'écrire sous la forme d'un produit matriciel $L \times U$, où

  • $L$ est triangulaire inférieure à diagonale unité,
  • $U$ est triangulaire supérieure.

La méthode

La méthode de déterminisation des valeurs propres est alors la suivante :

  1. On décompose $A = L \times U$.
  2. On pose $A_1 = U \times L$.
  3. On décompose $A_1 = L_1 \times U_1$.
  4. On pose $A_2 = U_1 \times L_1$.
  5. On décompose $A_2 = L_2 \times U_2$.
  6. etc.

Sous certaines conditions non triviales, la suite des matrices $L_k$ tend vers la matrice identité, quand la suite $U_k$ tend vers une matrice triangulaire supérieure limite, dont les coefficients diagonaux sont les valeurs propres de $A$.

Prolongements

Cette technique peut s'adapter au cas où $A$ n'est pas LU-décomposable, en utilisant une certaine matrice de permutation.

Travaux pratiques

  1. Faire une fonction qui retourne les valeurs propres de $A$ en utilisant la décomposition LU.
  2. L'appliquer à la matrice

$\left(\begin{array}{ccc} 4 & 4 & -2 \\ 1 & -4 & 5 \\ -4 & 8 & 2 \end{array}\right)$

Utilisation de la décomposition de Choleski

On peut enfin utiliser la décomposition de Choleski, dernière décomposition matricielle classique, pour déterminer les valeurs propres d'une matrice.

Nous n'entrerons pas plus dans les détails.

Méthode de Jacobi

Rappels

Dans le TP sur les décompositions matricielles, on a vu une méthode de Givens permettant de décomposer une matrice carrée réelle inversible $M$ sous la forme $QR$.

Chacune de ces matrices de rotation est responsable de l'annulation d'un coefficient sous-diagonal de $M$, la progression s'effectuant :

  • de la gauche vers la droite,
  • de haut en bas, au sein d'une même colonne.

On arrive donc à une écriture $GM = R$, où $G$ est le produit des matrices de rotation, et $R$ triangulaire supérieure à diagoale positive. Puis, à $M = QR$, où $Q = G^T = G^{-1}$.

Principe de Jacobi

On pourrait penser adapter cette méthode au calcul des valeurs propres et vecteurs propres, en espérant que :

  • ces éléments propres sont plus faciles à calculer pour $Q$ et $R$,
  • $M$ a les mêmes éléments propres que $Q$ et/ou $R$.

Hélas, le deuxième point n'est pas vérifié, et la méthode ci-dessus ne convient pas.

Cependant, on peut quand même utiliser les rotations de Givens dans la recherche des valeurs propres de $M$, si $M$ est symétrique, ce qui sera supposé dans toute cette section.

Jacobi repose sur le fait que si $G$ est une matrice de rotation et $M$ symétrique, alors $M_1 = GMG^T$ est une matrice semblable à $M$ : elles ont les mêmes valeurs propres. Comme $M_1$ reste symétrique, on peut continuer le procédé avec $M_1$.

Le but est alors d'utiliser des matrices de rotation $G_k$, pour que la matrice $M_p$ issue du produit $G_p \times G_{p-1} \times ... \times G_1 \times M \times G_1^T \times ... \times G_{p-1}^T \times G_p^T$ est telle que ses valeurs propres soient bien plus faciles à déterminer en pratique que celles de $M$ (et, on le sait, $M$ et $M_p$ ont les mêmes valeurs propres).

On prendra pour $G_k$ des matrices de Givens bien choisies, de sorte que les 0 introduits conduisent à un produit $G_p \times G_{p-1} \times ... \times G_1 \times M \times G_1^T \times ... \times G_{p-1}^T \times G_p^T$ égal à une matrice diagonale : les valeurs propres de $M$ seront les coefficients de cette diagonale !

Dans le détail, à chaque nouvelle multiplication par une matrice de Givens,

  • on annule un nouveau coefficient,
  • mais il se peut qu'un autre coefficient, précédemment annulé, redevienne non nul.

Cela n'est pas grave : la méthode de Jacobi est ainsi faite que la somme des carrés des coefficients non diagonaux décroît strictement à chaque itération : on se rapproche donc, à chaque fois, un peu plus d'une matrice diagonale...

Réalisation pratique

Rappels sur les matrices de Givens

On rappelle la forme générale des matrices de rotation de Givens...

  >>> from sympy import *
  >>> def G(n,i,j,x):
  ...     M = eye(n)
  ...     M[i,i] = M[j,j] = cos(x)
  ...     M[i,j] = -sin(x)
  ...     M[j,i] = sin(x)
  ...     return M
  ... 
  >>> x=Symbol('x')
  >>> G(6,2,4,x)
  [1, 0,      0, 0,       0, 0]
  [0, 1,      0, 0,       0, 0]
  [0, 0, cos(x), 0, -sin(x), 0]
  [0, 0,      0, 1,       0, 0]
  [0, 0, sin(x), 0,  cos(x), 0]
  [0, 0,      0, 0,       0, 1]

La méthode de Jacobi

A chaque étape, on attaque le coefficient non diagonal $m_0$ de plus grande valeur absolue dans $M$, qui est en position $(i_0,j_0)$, en multipliant par une matrice de Givens $G$ choisie de manière à annuler $m_0$.

Cette matrice de Givens bien choisie $G$ est $G(n,i_0,j_0,x)$, où

  • $n$ est la taille de $M$
  • $(i_0,j_0)$ sont les indices du coefficient non diagonal de plus grande valeur absolue de $M$
  • $x$ est égal à $arctan(c_0)$, où :
    • $c_0 = -c + \sqrt{c^2+1}$ si $c \geqslant 0$,
    • $c_0 = -c - \sqrt{c^2+1}$ sinon,
    • $c$ étant égal à $\frac{M_{j_0,j_0} - M_{i_0,i_0}}{2 m_0}$.

On remplace alors la matrice $M$ par $G M G^T$, et on recommence.

Le procédé est reproduit :

  • jusqu'à atteindre le nombre d'itérations spécifié par l'utilisateur,
  • ou jusqu'à ce que la somme des coefficients des carrés des éléments non diagonaux passe en-dessous d'une certaine limite, fixée par l'utilisateur.

Les valeurs propres (approchées) seront les éléments diagonaux de la dernière matrice obtenue.

Chaque coefficient annulé est en général désannulé par les étapes suivantes, mais l'important est qu'à chaque étape la matrice $M_{k+1}$ est plus proche d'une matrice diagonale que ne l'était $M_k$ : la méthode de Jacobi converge.

Travaux pratiques

Réalisez la méthode de Jacobi.

Méthodes de puissances itérées

Présentation

Pour trouver les valeurs propres d'une matrice carrée de manière approchée, il existe donc plusieurs méthodes, plus ou moins efficaces, plus ou moins faciles à utiliser.

Certaines ne s'appliquent qu'à des matrices d'un type un peu particulier (symétriques réelles, etc.) Les méthodes de puissances itérées, couramment employées, ont l'avantage de s'appliquer à des matrices quelconques. Elles permettent de trouver à la fois des valeurs propres et des vecteurs propres.

Leur principal défaut est de supposer que les valeurs propres sont dans une configuration favorable, ce que l'on ne peut savoir à l'avance.

Deux méthodes de puissances itérées

Puissances itérées directes

La méthode des puissances itérées directes permet d'approcher la valeur propre de plus grand module (encore faut-il qu'il y en ait une).

On peut alors envisager une méthode de déflation, pour trouver toutes les valeurs propres (encore faut-il que leurs modules soient deux à deux différents).

Notons, cependant, que cette méthode est source d'erreurs d'arrondis.

Puissances itérées inverses

La méthode des puissances itérées inverses permet de trouver la valeur propre de plus petit module (sauf, éventuellement, 0).

Une variante de cette méthode permet alors d'approcher une valeur propre quelconque, à condition d'en connaître une estimation initiale.

La méthode

Principe

Considérons une matrice diagonalisable, dont les valeurs propres ont des modules distincts deux à deux.

On part d'un vecteur quelconque, que l'on multiplie un certain nombre de fois par la matrice $M$ dont on cherche les éléments propres.

Alors, au bout d'un certain nombre d'itérations, le vecteur résultant tend vers un vecteur propre associé à la plus grande valeur propre (en module)...

Exemple

Illustrons cette méthode sur la matrice

$M = \left(\begin{array}{ccc} -7 & -3 & 0 \\ 54 & 8 & -36 \\ 3 & -3 & -10 \end{array} \right)$

  >>> from numpy import *
  >>> M = matrix((-7,-3,0,54,8,-36, 3, -3,-10)).reshape((3,3))
  >>> from numpy.linalg import *
  >>> eigvals(M)
  array([ 2., -1., -10.])

Les valeurs propres à trouver sont donc 2, -1 et -10. Tirons un vecteur aléatoirement

  >>> from random import randint
  >>> U = matrix((0,0,0)).reshape((3,1))
  >>> U
  matrix([[0],
          [0],
          [0]])
  >>> for k in range(3):
  ...     U[k,0] = randint(-1000,1000)
  ... 
  >>> U
  matrix([[ 430],
          [ -84],
          [-316]])

Multiplions 10 fois ce dernier vecteur par $M$

  >>> for k in range(10):
  ...    U = M*U
  ... 
  >>> U
  matrix([[355111006],
          [367443780],
          [713304158]])

Voici donc une approximation d'un vecteur propre associé à la plus grande valeur propre. Trouvons un plus beau vecteur propre...

  >>> U/float(U[0,0])
  matrix([[ 1.        ],
          [ 1.03472935],
          [ 2.00867939]])

Donc le vecteur $(1,1,2)^T$ semble bien être un vecteur propre. Vérifions-le, et profitons-en pour trouver ladite plus grande valeur propre...

  >>> V = matrix((1,1,2)).reshape((3,1))
  >>> M*V
  matrix([[-10],
          [-10],
          [-20]])
  >>> M*V==-10*V
  matrix([[ True],
          [ True],
          [ True]], dtype=bool)

On trouve bien -10 pour la plus grande valeur propre (en valeur absolue).

Discussion

  • Si on tire aléatoirement un mauvais vecteur, c'est-à-dire dont la composante suivant le vecteur propre recherché est nulle, alors la convergence s'effectue vers le vecteur propre associé à la deuxième plus grande valeur propre (en terme de module).
    • Le risque est très faible, cependant.
    • De plus, des erreurs d'arrondis, pour une fois les bienvenues, font que à terme on devrait quitter ce mauvais sous-espace, pour tendre à nouveau vers la plus grande des valeurs propres.
    • Dans le pire des cas, on peut toujours recommencer avec un autre vecteur aléatoire.
  • Le calcul des $U_k$ risque de conduire rapidement à un débordement de capacité. Pour éviter cela, on peut normer le vecteur à chaque étape (et commencer avec un vecteur de norme 1).
  • On peut alors se poser la question du choix de la norme vectorielle utilisée :
    • somme des valeurs absolue des composantes du vecteur (aka norme 1),
    • racine carrée de la somme des carrés (aka norme 2, ou euclidienne, ou de Frobenius)
    • racine $n$ième de la somme des composantes à la puissance $n$ (norme $n$)
    • maximum des valeurs absolues des composantes (norme infinie).

Travaux pratiques

  • Faire une fonction qui retourne les valeurs propres et vecteurs propres d'une matrice donnée, par la méthode ci-dessus.
Pour obtenir facilement la valeur propre, dans votre fonction, on pourra utiliser le fait que
  • comme le vecteur limite $V$ est unitaire, alors $||V|| = 1$,
  • $V$ est un vecteur propre : $MV = \lambda V$,
  • Donc le produit scalaire entre $V$ et $MV$ est égal à la valeur propre : $<MV,V> = \lambda$.
  • La fonction devrait retourner le couple $(V, V.dot(M*V))$
  • L'adapter pour intégrer la normalisation du vecteur propre à chaque étape.
  • Passer en option le choix de la norme à utiliser.
  • Comparer les résultats correspondant aux différentes normes possibles.
  • Divers tests d'arrêt sont possibles :
    • Un nombre d'itérations fixées par l'utilisateur.
    • La différence $U_{k+1} - \frac{\lambda}{||\lambda||} U_k$ inférieure à une valeur donnée.
    • etc.
  • L'appliquer aux matrices

$\left( \begin{array}{ccc} -4 & 4 & -2 \\ 1 & -4 & 5 \\ -4 & 8 & 2 \end{array}\right)$

et

$\left( \begin{array}{ccc} 15+6i & -16+7i & 5+10i \\ -8+16i & -5-19i & -12+4i \\ 11+23i & -27-11i & -6+18i \end{array}\right)$

Généralisation

Cas des valeurs propres de multiplicité $m>1$

On a montré comment obtenir un vecteur propre associé à la valeur propre dominante.

Si la multiplicité de cette valeur propre est plus grande que 1, c'est-à-dire :

  • le sous-espace propre associé n'est pas une droite, mais un sous-espace vectoriel de plus grande dimension,
  • il existe plusieurs vecteurs propres linéairement indépendants,

alors on peut reproduire l'algorithme plusieurs fois, en partant de vecteurs aléatoires différents : une nouvelle itération a de bonnes chances de conduire à un vecteur propre linéairement indépendant de ceux déjà obtenus.

Pour savoir si les vecteurs obtenus sont bien indépendants, on peut procéder à une élimination de Gauss : si l'on obtient un 0 sur la diagonale du système triangulaire obtenu par la méthode de Gauss, alors la famille n'est pas libre.

Vitesse de convergence

On peut montrer que la vitesse de convergence vers la valeur propre dominante $\lambda_1$ est de type exponentiel, avec une raison en général égale à $\frac{\lambda_2}{\lambda_1}$, où $\lambda_2$ est la deuxième plus grande valeur propre (pour le module).

En d'autres termes, le nombre de calculs élémentaires, donc le temps, nécessaire pour obtenir $n$ itérations, est de l'ordre de $\left(\frac{\lambda_2}{\lambda_1}\right)^n$.

Si la matrice n'est pas diagonale, la convergence persiste, mais est bien plus lente (de l'ordre de $\frac{1}{n}$).

Travaux pratiques

  • Adaptez votre fonction pour obtenir non pas un, mais une famille de vecteurs propres linéairement indépendants, associés à la plus grande valeur propre.
  • Vérifiez l'affirmation concernant la vitesse de convergence.
    • On comparera le cas diagonalisable au cas général.
    • On pourra utiliser le module time, ou timeit,
    • On tracera l'évolution du temps de calcul en fonction des itérations, à l'aide du module PyX.
    • On pourra effectuer les mesures sur un grand nombre de vecteurs initiaux différents.

Méthode de déflation

Il est possible de trouver toutes les valeurs propres d'une matrice $M$ donnée (toujours telle que ses valeurs propres soient, en module, deux à deux distinctes), en itérant la méthode ci-dessus.

En effet, si :

  • les valeurs propres de $M$ sont $(\lambda_1, ..., \lambda_n)$, rangées par module (strictement) décroissant,
  • $U_1$ est un vecteur propre associé à $\lambda_1$,

alors $N = M-\lambda_1 U_1 U_1^T$ possède pour valeurs propres, rangées par module (strictement) décroissant : $(\lambda_2, ..., \lambda_n, 0)$.

On peut donc réappliquer la méthode des puissances itérées à $N$, pour trouver $\lambda_2$, et poursuivre le procédé jusqu'à trouver toutes les valeurs propres.

Notons, cependant, que cette méthode de déflation ne permet pas de trouver la valeur propre nulle.

Puissances itérées inverses

La méthode précédente permet d'être adaptée au calcul de la plus petite valeur propre $M$ : il suffit de l'appliquer à $M^{-1}$ (ce qui suppose que 0 ne soit pas valeur propre de $M$).

La croissance est rapide, de type exponentielle (de rapport $\frac{|\lambda_1|}{|\lambda_2|}$, où $\lambda_1$ est ici la plus petite valeur propre de $M$, en module, et $\lambda_2$ la deuxième plus petite).

Travaux pratiques

  1. Mettre en oeuvre la méthode de déflation.
  2. Faire de même pour la méthode des puissances itérées inverses.

Pour aller plus loin

Améliorer la précision sur une valeur propre

Supposons que l'on connaisse une approximation $\lambda'$ d'une valeur propre $\lambda$ de $M$, et que l'on souhaite améliorer cette connaissance.

On peut montrer que $N=M-\lambda'I_n$ possède une valeur propre égale à $\lambda-\lambda'$, qui est sûrement la plus petite valeur propre de $N$, si $\lambda'$ est une approximation de $\lambda$.

La méthode des puissances itérées inverses permettra donc de trouver $\lambda-\lambda'$, donc $\lambda$ puisque l'on connaît $\lambda'$. De plus, on récupère le vecteur propre $V$, associé à la valeur propre $\lambda-\lambda'$ pour $N$, donc à $\lambda$ pour $M$.

L'intérêt de cette technique est double :

  • trouver un vecteur propre associé,
  • améliorer, rapidement, la connaissance de $\lambda$.
En effet, on sait qu'alors la convergence est de type exponentiel, avec une raison égale à $\frac{|\lambda'-\lambda|}{|\mu-\lambda|}$, où $\mu$ représente la seconde valeur propre la plus proche de la valeur $\lambda'$). Cette raison est très inférieure à 1, d'où la convergence rapide.

La méthode des puissances itérées inverses est donc, en général, utilisée après une autre méthode donnant seulement une valeur approchée d'une valeur propre : recherche approchée des zéros du polynôme caractéristique, localisation par dichotomie, ou une méthode du texte.

Remarquons, pour finir cette section, que la méthode des puissances itérées inverses appliquée à $N$ suppose d'inverser $N$. Dans la pratique, on évite de le faire :

  • Faire $U_{k+1} = N^{-1} U_k$, avec $N = M - \lambda I_n$ revient à résoudre le système $(M-\lambda I_n) U_{k+1} = U_k$.
  • Ces résolutions répétées de sytèmes linéaires, dont seul le second membre change, sont facilitées si l'on décompose une fois pour toute la matrice $M-\lambda I_n$ sous forme LU : on aura donc à résoudre, à chaque itération, deux systèmes triangulaires ($L V_k = U_k$ et $U_{k+1} = V_k$).

Méthode du quotient de Rayleigh

Dans l'accélération de convergence ci-dessus, on résolvait, à chaque itération, le système $(M-\lambda' I_n) U_{k} = U_{k-1}$, en ayant fait une fois pour toutes la décomposition LU. Dans ce qui précède, $\lambda'$ est l'approximation initiale de la valeur propre recherchée.

La méthode du quotient de Rayleigh consiste à remarquer que $\lambda_k = <M U_k, U_k>$ converge vers $\lambda$ ($<,>$ représente le produit scalaire). On remplace donc la suite de système linéaires $(M-\lambda' I_n) U_{k} = U_{k-1}$ en $(M-\lambda_k I_n) U_{k} = U_{k-1}$

  • L'inconvénient est qu'il faut faire une décomposition LU de $M-\lambda_k$ à chaque étape, vue que cette matrice change.
  • L'avantage est que la convergence est plus rapide.

Plus $\lambda_k$ se rapproche de $\lambda$, plus $M-\lambda_k I_n$ risque de ne plus être inversible ($\lambda$ est valeur propre de $M$ si et seulement si $M-\lambda I_n$ n'est pas inversible). La résolution du système $(M-\lambda_k I_n) U_{k} = U_{k-1}$ peut ne plus être possible au-delà d'une certaine itération : il faudra alors stopper le programme.

Méthode des réflexions de Householder

Pour être exhaustif, finissons ce tour d'horizon des méthodes de détermination des valeurs propres par l'évocation de la méthode dite des réflexions de Householder.

Le principe est ici d'utiliser les matrices de Householder pour ramener la recherche des valeurs propres d'une matrice de taille $n$ à celle d'une matrice de taille $n-1$. Nous n'en dirons pas plus.

Travaux pratiques

  1. Mettre en place la technique d'amélioration de la précision d'une valeur propre, en utilisant votre programme de décomposition LU.
  2. Programmez la méthode du quotient de Rayleigh.
  3. Renseignez-vous sur la méthode des réflexions de Householder, et mettez-la en oeuvre, si le coeur vous en dit.

Page Actions

Recent Changes

Group & Page

Back Links