Introduction
En calcul scientifique, effectuer des opérations d'algèbre linéaire de manière efficace est essentiel. NumPy, une bibliothèque Python fondamentale pour les calculs numériques, offre de nombreux outils pour ces opérations. Parmi ces outils, einsum (Somme d'Einstein) se distingue comme une fonction particulièrement puissante pour exprimer de manière concise des opérations complexes sur les tableaux.
Ce tutoriel vous guidera dans la compréhension de ce que einsum est et de la manière de l'utiliser efficacement dans votre code Python. À la fin de ce labo (LabEx), vous serez en mesure d'effectuer diverses opérations sur les tableaux à l'aide de la fonction einsum et de comprendre ses avantages par rapport aux fonctions traditionnelles de NumPy.
Comprendre les bases de NumPy Einsum
La Somme d'Einstein (einsum) est une fonction puissante de NumPy qui vous permet d'exprimer de nombreuses opérations sur les tableaux à l'aide d'une notation concise. Elle suit la convention de sommation d'Einstein, qui est couramment utilisée en physique et en mathématiques pour simplifier les équations complexes.
Ouverture du shell Python
Commençons par ouvrir le shell Python. Ouvrez un terminal sur le Bureau et tapez :
python3
Vous devriez voir l'invite Python (>>>), ce qui indique que vous êtes maintenant dans le shell interactif Python.
Importation de NumPy
Tout d'abord, nous devons importer la bibliothèque NumPy :
import numpy as np

Qu'est-ce que Einsum ?
La fonction einsum de NumPy vous permet de spécifier des opérations sur les tableaux à l'aide d'une notation sous forme de chaîne de caractères qui décrit quels indices (dimensions) des tableaux doivent être utilisés dans l'opération.
Le format de base d'une opération einsum est le suivant :
np.einsum('notation', array1, array2, ...)
Où la chaîne de caractères de notation décrit l'opération à effectuer.
Un exemple simple : Produit scalaire de vecteurs
Commençons par un exemple simple : le calcul du produit scalaire de deux vecteurs. En notation mathématique, le produit scalaire de deux vecteurs u et v est :
$$\sum_i u_i \times v_i$$
Voici comment le calculer à l'aide de einsum :
## Create two random vectors
u = np.random.rand(5)
v = np.random.rand(5)
## Print the vectors to see their values
print("Vector u:", u)
print("Vector v:", v)
## Calculate dot product using einsum
dot_product = np.einsum('i,i->', u, v)
print("Dot product using einsum:", dot_product)
## Verify with NumPy's dot function
numpy_dot = np.dot(u, v)
print("Dot product using np.dot:", numpy_dot)

La notation 'i,i->' signifie :
ireprésente l'indice du premier tableau (u)- Le deuxième
ireprésente l'indice du deuxième tableau (v) - La flèche
->suivie de rien indique que nous voulons un résultat scalaire (somme sur tous les indices)
Comprendre la notation Einsum
La notation einsum suit ce schéma général :
'index1,index2,...->output_indices'
index1,index2: Étiquettes pour les dimensions de chaque tableau d'entréeoutput_indices: Étiquettes pour les dimensions du tableau de sortie- Les indices répétés dans les tableaux d'entrée sont sommés
- Les indices qui apparaissent dans la sortie sont conservés dans le résultat
Par exemple, dans la notation 'ij,jk->ik' :
i,jsont les dimensions du premier tableauj,ksont les dimensions du deuxième tableaujapparaît dans les deux tableaux d'entrée, donc nous effectuons une somme sur cette dimensioni,kapparaissent dans la sortie, donc ces dimensions sont conservées
C'est exactement la formule de la multiplication de matrices !
Opérations Einsum courantes
Maintenant que nous comprenons les bases de einsum, explorons quelques opérations courantes que nous pouvons effectuer à l'aide de cette fonction puissante.
Transposition de matrice
Transposer une matrice consiste à échanger ses lignes et ses colonnes. En notation mathématique, si A est une matrice, sa transposée A^T est définie comme suit :
$$A^T_{ij} = A_{ji}$$
Voyons comment effectuer la transposition d'une matrice à l'aide de einsum :
## Create a random matrix
A = np.random.rand(3, 4)
print("Original matrix A:")
print(A)
print("Shape of A:", A.shape) ## Should be (3, 4)
## Transpose using einsum
A_transpose = np.einsum('ij->ji', A)
print("\nTransposed matrix using einsum:")
print(A_transpose)
print("Shape of transposed A:", A_transpose.shape) ## Should be (4, 3)
## Verify with NumPy's transpose function
numpy_transpose = A.T
print("\nTransposed matrix using A.T:")
print(numpy_transpose)
La notation 'ij->ji' signifie :
ijreprésente les indices de la matrice d'entrée (i pour les lignes, j pour les colonnes)jireprésente les indices de la matrice de sortie (j pour les lignes, i pour les colonnes)- Nous échangeons essentiellement les positions des indices
Multiplication de matrices
La multiplication de matrices est une opération fondamentale en algèbre linéaire. Pour deux matrices A et B, leur produit C est défini comme suit :
$$C_{ik} = \sum_j A_{ij} \times B_{jk}$$
Voici comment effectuer la multiplication de matrices à l'aide de einsum :
## Create two random matrices
A = np.random.rand(3, 4) ## 3x4 matrix
B = np.random.rand(4, 2) ## 4x2 matrix
print("Matrix A shape:", A.shape)
print("Matrix B shape:", B.shape)
## Matrix multiplication using einsum
C = np.einsum('ij,jk->ik', A, B)
print("\nResult matrix C using einsum:")
print(C)
print("Shape of C:", C.shape) ## Should be (3, 2)
## Verify with NumPy's matmul function
numpy_matmul = np.matmul(A, B)
print("\nResult matrix using np.matmul:")
print(numpy_matmul)
La notation 'ij,jk->ik' signifie :
ijreprésente les indices de la matrice A (i pour les lignes, j pour les colonnes)jkreprésente les indices de la matrice B (j pour les lignes, k pour les colonnes)ikreprésente les indices de la matrice de sortie C (i pour les lignes, k pour les colonnes)- L'indice répété
jest sommés (multiplication de matrices)
Multiplication élément par élément
La multiplication élément par élément consiste à multiplier les éléments correspondants de deux tableaux. Pour deux matrices A et B de même forme, leur produit élément par élément C est :
$$C_{ij} = A_{ij} \times B_{ij}$$
Voici comment effectuer la multiplication élément par élément à l'aide de einsum :
## Create two random matrices of the same shape
A = np.random.rand(3, 3)
B = np.random.rand(3, 3)
print("Matrix A:")
print(A)
print("\nMatrix B:")
print(B)
## Element-wise multiplication using einsum
C = np.einsum('ij,ij->ij', A, B)
print("\nElement-wise product using einsum:")
print(C)
## Verify with NumPy's multiply function
numpy_multiply = A * B
print("\nElement-wise product using A * B:")
print(numpy_multiply)
La notation 'ij,ij->ij' signifie :
ijreprésente les indices de la matrice Aijreprésente les indices de la matrice Bijreprésente les indices de la matrice de sortie C- Aucun indice n'est sommés, ce qui signifie que nous multiplions simplement les éléments correspondants
Opérations Einsum avancées
Maintenant que nous sommes à l'aise avec les opérations de base de einsum, explorons quelques applications plus avancées. Ces opérations démontrent le véritable pouvoir et la flexibilité de la fonction einsum.
Extraction de la diagonale
Extraire les éléments diagonaux d'une matrice est une opération courante en algèbre linéaire. Pour une matrice A, ses éléments diagonaux forment un vecteur d tel que :
$$d_i = A_{ii}$$
Voici comment extraire la diagonale à l'aide de einsum :
## Create a random square matrix
A = np.random.rand(4, 4)
print("Matrix A:")
print(A)
## Extract diagonal using einsum
diagonal = np.einsum('ii->i', A)
print("\nDiagonal elements using einsum:")
print(diagonal)
## Verify with NumPy's diagonal function
numpy_diagonal = np.diagonal(A)
print("\nDiagonal elements using np.diagonal():")
print(numpy_diagonal)
La notation 'ii->i' signifie :
iireprésente l'indice répété pour les éléments diagonaux de Aisignifie que nous extrayons ces éléments dans un tableau 1D
Trace d'une matrice
La trace d'une matrice est la somme de ses éléments diagonaux. Pour une matrice A, sa trace est :
$$\text{trace}(A) = \sum_i A_{ii}$$
Voici comment calculer la trace à l'aide de einsum :
## Using the same matrix A from above
trace = np.einsum('ii->', A)
print("Trace of matrix A using einsum:", trace)
## Verify with NumPy's trace function
numpy_trace = np.trace(A)
print("Trace of matrix A using np.trace():", numpy_trace)
La notation 'ii->' signifie :
iireprésente l'indice répété pour les éléments diagonaux- L'indice de sortie vide signifie que nous sommons tous les éléments diagonaux pour obtenir un scalaire
Multiplication par lots de matrices
einsum s'avère particulièrement utile lorsqu'il s'agit d'effectuer des opérations sur des tableaux de dimensions supérieures. Par exemple, la multiplication par lots de matrices consiste à multiplier des paires de matrices de deux lots.
Si nous avons un lot de matrices A de forme (n, m, p) et un lot de matrices B de forme (n, p, q), la multiplication par lots de matrices nous donne un résultat C de forme (n, m, q) :
$$C_{ijk} = \sum_l A_{ijl} \times B_{ilk}$$
Voici comment effectuer la multiplication par lots de matrices à l'aide de einsum :
## Create batches of matrices
n, m, p, q = 5, 3, 4, 2 ## Batch size and matrix dimensions
A = np.random.rand(n, m, p) ## Batch of 5 matrices, each 3x4
B = np.random.rand(n, p, q) ## Batch of 5 matrices, each 4x2
print("Shape of batch A:", A.shape)
print("Shape of batch B:", B.shape)
## Batch matrix multiplication using einsum
C = np.einsum('nmp,npq->nmq', A, B)
print("\nShape of result batch C:", C.shape) ## Should be (5, 3, 2)
## Let's check the first matrix multiplication in the batch
print("\nFirst result matrix from batch using einsum:")
print(C[0])
## Verify with NumPy's matmul function
numpy_batch_matmul = np.matmul(A, B)
print("\nFirst result matrix from batch using np.matmul:")
print(numpy_batch_matmul[0])
La notation 'nmp,npq->nmq' signifie :
nmpreprésente les indices du lot A (n pour le lot, m pour les lignes, p pour les colonnes)npqreprésente les indices du lot B (n pour le lot, p pour les lignes, q pour les colonnes)nmqreprésente les indices du lot de sortie C (n pour le lot, m pour les lignes, q pour les colonnes)- L'indice répété
pest sommés (multiplication de matrices)
Pourquoi utiliser Einsum ?
Vous vous demandez peut-être pourquoi nous devrions utiliser einsum alors que NumPy propose déjà des fonctions spécialisées pour ces opérations. Voici quelques avantages :
- Interface unifiée :
einsumoffre une seule fonction pour de nombreuses opérations sur les tableaux - Flexibilité : Elle peut exprimer des opérations qui nécessiteraient autrement plusieurs étapes
- Lisibilité : Une fois que vous comprenez la notation, le code devient plus concis
- Performances : Dans de nombreux cas, les opérations
einsumsont optimisées et efficaces
Pour les opérations tensorielle complexes, einsum offre souvent la mise en œuvre la plus claire et la plus directe.
Résumé
Dans ce laboratoire, vous avez exploré la puissante fonction einsum de NumPy, qui met en œuvre la convention de sommation d'Einstein pour les opérations sur les tableaux. Récapitulons ce que vous avez appris :
Concepts de base d'Einsum : Vous avez appris à utiliser la notation
einsumpour exprimer les opérations sur les tableaux, les indices représentant les dimensions des tableaux et les indices répétés indiquant une sommation.Opérations courantes : Vous avez implémenté plusieurs opérations fondamentales à l'aide de
einsum:- Produit scalaire de vecteurs
- Transposition de matrice
- Multiplication de matrices
- Multiplication élément par élément
Applications avancées : Vous avez exploré des opérations plus complexes :
- Extraction de la diagonale
- Trace d'une matrice
- Multiplication par lots de matrices
La fonction einsum offre une approche unifiée et flexible pour les opérations sur les tableaux dans NumPy. Bien que des fonctions spécialisées telles que np.dot, np.matmul et np.transpose soient disponibles pour des opérations spécifiques, einsum propose une interface cohérente pour une large gamme d'opérations, ce qui devient particulièrement précieux lorsqu'on travaille avec des tableaux de dimensions supérieures.
Au fur et à mesure de votre progression dans le domaine du calcul scientifique et de la science des données, einsum sera un outil puissant à votre disposition pour effectuer des opérations complexes sur les tableaux avec un code concis et lisible.



