Un vecteur de \mathbb{R}^n peut être vu comme un tableau de dimension n : \boldsymbol{a}=\begin{bmatrix}a_1\\a_2\\a_3\end{bmatrix} est un vecteur de dimension 3, contenant 3 composantes notées a_i.
La matrice \boldsymbol{A} = \begin{bmatrix}A_{11} & A_{12} \\ A_{21} & A_{22} \\ A_{31} & A_{32}\end{bmatrix} est de dimension 3\times 2 et contient des composantes A_{ij}, où i est l’indice de ligne et j l’indice de colonne.
Par défaut, tous les vecteurs peuvent être considérés comme des matrices colonnes (n lignes et une seule colonne).
Vecteurs et matrices en python avec NumPy
En python, nous utilisons la bibliothèque numpy pour gérer les vecteurs et matrices.
import numpy as np a = np.array([ 1, 2, 3])A = np.array([[ 1, 2, 3], [4, 5, 6]])print("Vecteur a de dimension "+str(len(a)) +" :\n", a)print("Matrice A de taille "+str(A.shape) +" :\n", A)print("a_1 =", a[0])print("A_23 =", A[1, 2])
Vecteur a de dimension 3 :
[1 2 3]
Matrice A de taille (2, 3) :
[[1 2 3]
[4 5 6]]
a_1 = 1
A_23 = 6
Remarque : numpy affiche toujours les vecteurs en ligne, mais nous pouvons considérer qu’ils sont en colonne dans nos opérations (numpy se charge de le transposer dans le bon sens).
C.1.1 Transposée
La transposée \boldsymbol{A}^T = \begin{bmatrix}A_{11} & A_{21} & A_{31} \\ A_{12} & A_{22} & A_{32} \end{bmatrix} de la matrice \boldsymbol{A} s’obtient en inversant les lignes et les colonnes.
La transposée \boldsymbol{a}^T=\begin{bmatrix}a_1 & a_2 & a_3\end{bmatrix} du vecteur \boldsymbol{a} est donc une matrice ligne.
Si \boldsymbol{A}^T = \boldsymbol{A}, alors \boldsymbol{A} est dite symétrique.
En python
En python/numpy, la transposée s’écrit « .T » :
print("Matrice A:\n", A)print("Transposée de A :\n", A.T)
Matrice A:
[[1 2 3]
[4 5 6]]
Transposée de A :
[[1 4]
[2 5]
[3 6]]
Remarque : A.T est une simple « vue » sur la matrice, ce qui signifie qu’elle ne nécessite pas de calculs pour l’obtenir, mais aussi que modifier A.T modifie aussi A.
C.1.2 Addition
Les additions de vecteurs ou de matrices de mêmes dimensions se font composante à composante (case par case).
En python
En python/numpy, les opérateurs d’addition/soustraction sont étendus aux vecteurs et matrices :
Le produit scalaire entre deux vecteurs \boldsymbol{a} et \boldsymbol{b} de même dimension n est donné par la somme des produits des composantes :
\left\langle \boldsymbol{a}, \boldsymbol{b} \right\rangle = \sum_{i=1}^n a_i b_i
En notation matricielle, le produit scalaire correspond à la multiplication matricielle de la transposée de \boldsymbol{a} avec \boldsymbol{b} :
\left\langle \boldsymbol{a}, \boldsymbol{b} \right\rangle = \boldsymbol{a}^T \boldsymbol{b}
A noter : le produit scalaire est symétrique, \boldsymbol{a}^T \boldsymbol{b} = \boldsymbol{b}^T \boldsymbol{a}.
C.1.4 Multiplication matricielle
Soit \boldsymbol{A} de dimension m\times n et \boldsymbol{B} de dimension n\times p. Le produit \boldsymbol{C} = \boldsymbol{A} \boldsymbol{B} a m lignes et p colonnes, et chaque composante de \boldsymbol{C} correspond au produit scalaire entre une ligne de la matrice \boldsymbol{A} et une colonne de la matrice \boldsymbol{B} :
C_{ij} = \sum_{k=1}^n A_{ik} B_{kj} = \boldsymbol{A}_{i,:} \boldsymbol{B}_{:,j}
où \boldsymbol{A}_{i,:} est la ième ligne de \boldsymbol{A} et \boldsymbol{B}_{:,j} est la jème colonne de \boldsymbol{B}.
Par exemple, avec
\boldsymbol{A} = \begin{bmatrix}A_{11} & A_{12} \\ A_{21} & A_{22} \\ A_{31} & A_{32}\end{bmatrix},\qquad \boldsymbol{B} = \begin{bmatrix}B_{11}& B_{12} \\ B_{21} & B_{22}\end{bmatrix}
on obtient \boldsymbol{C} de dimension 3\times 2 avec
\boldsymbol{C} = \boldsymbol{A} \boldsymbol{B} = \begin{bmatrix}A_{11} & A_{12} \\ A_{21} & A_{22} \\ A_{31} & A_{32}\end{bmatrix} \begin{bmatrix}B_{11}& B_{12} \\ B_{21} & B_{22}\end{bmatrix} = \begin{bmatrix}A_{11}B_{11} + A_{12}B_{21} & A_{11}B_{12} + A_{12}B_{22} \\ A_{21}B_{11} + A_{22}B_{21} & A_{21}B_{12} + A_{22}B_{22} \\ A_{31}B_{11} + A_{32}B_{21} & A_{31}B_{12} + A_{32}B_{22} \end{bmatrix}
À noter : la multiplication matricielle n’est pas symétrique, en général \boldsymbol{A}\boldsymbol{B} \neq \boldsymbol{B} \boldsymbol{A}.
La transposée d’un produit matriciel inverse l’ordre du produit :
(\boldsymbol{A} \boldsymbol{B})^T = \boldsymbol{B}^T \boldsymbol{A}^T
En python
En python/numpy, la multiplication mattricielle s’écrit « @ » :
print("Produit scalaire a^T a :\n", a.T @ a)print("Produit matriciel A^T A :\n", A.T @ A)
Produit scalaire a^T a :
14
Produit matriciel A^T A :
[[17 22 27]
[22 29 36]
[27 36 45]]
Remarque : on trouve la notation A.dot(B) ou np.dot(A, B) qui sont équivalents à A @ B.
C.1.5 Normes
La norme euclidienne d’un vecteur est
\|\boldsymbol{a}\| = \sqrt{\sum_{i=1}^n a_i^2} = \sqrt{\boldsymbol{a}^T \boldsymbol{a}}
La norme \ell_1 d’un vecteur est
\|\boldsymbol{a}\|_1 = \sum_{i=1}^n |a_i|
Il existe aussi beaucoup de normes matricielles différentes.
En python
En python/numpy, les normes sont implémentée par np.linalg.norm :
print("Norme euclidienne de a =", np.linalg.norm(a), " ou", np.sqrt( a.T@a ) )print("Norme l1 de a =", np.linalg.norm(a, ord=1) , " ou", np.sum( np.abs(a) ) )
Norme euclidienne de a = 3.7416573867739413 ou 3.7416573867739413
Norme l1 de a = 6.0 ou 6
C.1.6 Vecteurs et matrices particuliers
La notation \boldsymbol{0} est utilisée à la fois pour le vecteur et les matrices dont toutes les composantes sont nulles. De même, on note parfois \boldsymbol{1} le vecteur dont toutes les composantes valent 1.
La matrice identité est la matrice carrée notée \boldsymbol{I} neutre pour le produit matriciel :
\boldsymbol{I} = \begin{bmatrix} 1 & & 0\\ & \ddots & \\ 0 & & 1\end{bmatrix} = diag(\boldsymbol{1})
et, pour toute matrice \boldsymbol{A} carrée et vecteur \boldsymbol{a},
\boldsymbol{A} \boldsymbol{I} = \boldsymbol{I} \boldsymbol{A} = \boldsymbol{A}\qquad et\qquad \boldsymbol{I} \boldsymbol{a} = \boldsymbol{a}
L’inégalité de Cauchy-Schwarz permet de borner l’amplitude d’un produit scalaire en fonction des normes des vecteurs : pour tout vecteurs \boldsymbol{u} et \boldsymbol{v},
\left| \boldsymbol{u}^T \boldsymbol{v} \right| \leq \|\boldsymbol{u}\| \|\boldsymbol{v}\|
et la borne est atteinte, |\boldsymbol{u}^T \boldsymbol{v} | = \|\boldsymbol{u}\| \|\boldsymbol{v}\|, si et seulement si les vecteurs sont colinéaires, c’est-à-dire si \boldsymbol{u} = \lambda \boldsymbol{v}.
C.3 Matrice inverse
Une matrice carrée \boldsymbol{A}\in\mathbb{R}^{n\times n} est dite inversible si sa matrice inverse \boldsymbol{A}^{-1} existe telle que
\boldsymbol{A} \boldsymbol{A}^{-1} = \boldsymbol{A}^{-1}\boldsymbol{A} = \boldsymbol{I}
C.4 Rang d’une matrice
Le rang d’une matrice est le nombre de lignes ou colonnes linéairement indépendantes. De manière générale, pour une matrice à m lignes et n colonnes :
\text{rang}(\boldsymbol{A}) \leq \min\{ m, n\}
et
\text{rang}(\boldsymbol{A}\boldsymbol{B}) \leq min\{ \text{rang}(\boldsymbol{A}), \text{rang}(\boldsymbol{B})\}
(car la multiplication par \boldsymbol{B} revient à créer des combinaisons linéaires des colonnes de \boldsymbol{A}, et donc ne peut pas rendre indépendantes des colonnes qui ne l’étaient pas).
Le rang d’une matrice carrée correspond au nombre de valeurs propres non nulles. Pour une matrice rectangulaire, le rang correspond au nombre de valeurs singulières non nulles.
\boldsymbol{\Lambda} est une matrice diagonale contenant les nvaleurs propres\lambda_i,
\boldsymbol{U}\in\mathbb{R}^{n\times n} est une matrice carrée contenant les nvecteurs propres\boldsymbol{u}_i associés tels que
\boldsymbol{A} \boldsymbol{u}_i = \lambda_i \boldsymbol{u}_i
avec \boldsymbol{u}_i \neq \boldsymbol{0}.
Pour une matrice \boldsymbol{A}\in\mathbb{R}^{n\times n}symétrique (\boldsymbol{A}^T = \boldsymbol{A}) : \boldsymbol{U} est orthogonale (\boldsymbol{U}^{-1}=\boldsymbol{U}^T) et les \lambda_i sont réelles.
Exemple en python
M = A.T @ Aprint("Matrice M :\n", M)# eigh calcule les vecteurs propres d'une matrice symétriqueLambda, U = np.linalg.eigh(M)print("U :\n", U, "\nValeurs propres (par ordre croissant) :\n", Lambda)print("ULU^T :\n", U @ np.diag(Lambda) @ U.T )print("M u = lambda * u ? Vérification pour u3 :\n")print(M @ U[:,2])print(Lambda[2] * U[:,2])
Toute matrice \boldsymbol{X}\in\mathbb{R}^{n\times d} possède une décomposition en valeurs singulières : \boldsymbol{X} = \boldsymbol{U}\boldsymbol{S} \boldsymbol{V}^T \qquad ou\ \underbrace{\begin{bmatrix} \text{---}\ \boldsymbol{x}_1^\top \text{---} \\\vdots\\ \text{---}\ \boldsymbol{x}_n^\top \text{---} \end{bmatrix}}_{n\times d} = \underbrace{\begin{bmatrix} | & & |\\ \boldsymbol{u}_1 & \cdots & \boldsymbol{u}_n \\ | & & |\end{bmatrix}}_{n\times n} \underbrace{\begin{bmatrix}\sigma_1& & 0 & 0 \\ & \ddots & & \vdots\\ 0 & & \sigma_n & 0\end{bmatrix}}_{n\times d} \underbrace{\begin{bmatrix} \text{---}\ \boldsymbol{v}_1^\top \text{---} \\\vdots\\ \text{---}\ \boldsymbol{v}_d^\top \text{---} \end{bmatrix}}_{d\times d}
avec les matrices
\boldsymbol{U}^T \boldsymbol{U} =\boldsymbol{U} \boldsymbol{U}^T= \boldsymbol{I},\quad et\quad \boldsymbol{V}^T \boldsymbol{V} =\boldsymbol{V} \boldsymbol{V}^T= \boldsymbol{I}
qui contiennent les vecteurs singuliers à gauche et à droite et \boldsymbol{S} qui contient les valeurs singluières.
Exemple en python
M = A.T @ Aprint("Matrice M :\n", M)# eigh calcule les vecteurs propres d'une matrice symétriqueLambda, U = np.linalg.eigh(M)print("U :\n", U, "\nValeurs propres (par ordre croissant) :\n", Lambda)print("ULU^T :\n", U @ np.diag(Lambda) @ U.T )print("M u = lambda * u ? Vérification pour u3 :\n")print(M @ U[:,2])print(Lambda[2] * U[:,2])
Dans la décomposition en valeurs singulières, les vecteurs \boldsymbol{v}_k et les valeurs singulières \sigma_k sont liés aux vecteurs/valeurs propres de \boldsymbol{X}^T \boldsymbol{X} : \boldsymbol{X}^T \boldsymbol{X} = \boldsymbol{V} \boldsymbol{S}^T \boldsymbol{U}^T \boldsymbol{U} \boldsymbol{S} \boldsymbol{V}^T = \boldsymbol{V} \boldsymbol{S}^T\boldsymbol{S} \boldsymbol{V}^T
= \boldsymbol{V} \boldsymbol{S}^2 \boldsymbol{V}^T
où \boldsymbol{S}^2= diag(\sigma_1^2,\dots, \sigma_n^2,0,\dots). Donc \boldsymbol{X}^T \boldsymbol{X} \boldsymbol{v}_k = \sigma_k^2 \boldsymbol{v}_k, car, par exemple pour k=1 :
\boldsymbol{X}^T \boldsymbol{X} \boldsymbol{v}_1 = \boldsymbol{V} \boldsymbol{S}^2 \boldsymbol{V}^T \boldsymbol{v}_1 = \boldsymbol{V} \boldsymbol{S}^2 \begin{bmatrix}1\\0\\ | \end{bmatrix} = \boldsymbol{V} \begin{bmatrix}\sigma_1^2\\0\\ | \end{bmatrix} = \sigma_1^2 \boldsymbol{v}_1
Exemple en python
M = A.T @ A# eigh calcule les vecteurs propres d'une matrice symétriqueLambda, U = np.linalg.eigh(M)print("U :\n", U[:,::-1], "\nValeurs propres (par ordre décroissant) :\n", Lambda[::-1] )# svdU, s, Vt = np.linalg.svd(A)print("Vecteurs propres par SVD :\n", Vt.T, "\nValeurs propres par SVD (par ordre décroissant en ignorant les valeurs nulles) :\n", s**2)
U :
[[-0.42866713 -0.80596391 0.40824829]
[-0.56630692 -0.11238241 -0.81649658]
[-0.7039467 0.58119908 0.40824829]]
Valeurs propres (par ordre décroissant) :
[9.04026725e+01 5.97327474e-01 2.91325866e-15]
Vecteurs propres par SVD :
[[-0.42866713 0.80596391 0.40824829]
[-0.56630692 0.11238241 -0.81649658]
[-0.7039467 -0.58119908 0.40824829]]
Valeurs propres par SVD (par ordre décroissant en ignorant les valeurs nulles) :
[90.40267253 0.59732747]
A noter : les vecteurs propres ne sont retrouvés qu’au signe près, car si \boldsymbol{M}\boldsymbol{u} = \lambda\boldsymbol{u} alors \boldsymbol{M}(-\boldsymbol{u}) = \lambda (-\boldsymbol{u}) aussi.
C.6.2 SVD fine ou tronquée
Si d>n, alors la SVD fine construit une matrice \boldsymbol{S}_n ne contenant que les n premières colonnes de \boldsymbol{S} et une matrice \boldsymbol{V}_n^T ne contenant que les n premières lignes de \boldsymbol{V}^T, sans modifier le résultat :
\boldsymbol{X} = \boldsymbol{U}\boldsymbol{S}_n \boldsymbol{V}_n^T
La SVD tronquée reprend ce principe, mais limite les calculs à p < n composantes. En revanche, dans ce cas, le résultat peut être modifié et \boldsymbol{X} \neq \hat{\boldsymbol{X}}= \boldsymbol{U} \boldsymbol{S}_p \boldsymbol{V}_p^T si p < rang(\boldsymbol{X}). Cependant, \hat{\boldsymbol{X}} constitue la meilleure approximation de rang p de \boldsymbol{X}.