Introducción
En la computación científica, realizar operaciones de álgebra lineal de manera eficiente es esencial. NumPy, una biblioteca fundamental de Python para cálculos numéricos, proporciona numerosas herramientas para estas operaciones. Entre estas herramientas, einsum (Sumación de Einstein) se destaca como una función especialmente poderosa para expresar de manera concisa operaciones complejas de matrices.
Este tutorial lo guiará a través de la comprensión de qué es einsum y cómo usarlo de manera efectiva en su código de Python. Al final de este laboratorio (LabEx), podrá realizar diversas operaciones de matrices utilizando la función einsum y entender sus ventajas sobre las funciones tradicionales de NumPy.
Comprendiendo los conceptos básicos de NumPy Einsum
La Sumación de Einstein (einsum) es una poderosa función de NumPy que te permite expresar muchas operaciones de matrices utilizando una notación concisa. Sigue la convención de sumación de Einstein, que se utiliza comúnmente en física y matemáticas para simplificar ecuaciones complejas.
Abrir la consola de Python
Comencemos abriendo la consola de Python. Abre una terminal en el escritorio y escribe:
python3
Deberías ver el indicador de Python (>>>), lo que indica que ahora estás en la consola interactiva de Python.
Importar NumPy
Primero, necesitamos importar la biblioteca NumPy:
import numpy as np

¿Qué es Einsum?
La función einsum en NumPy te permite especificar operaciones de matrices utilizando una notación de cadena que describe qué índices (dimensiones) de las matrices deben ser operados.
El formato básico de una operación einsum es:
np.einsum('notation', array1, array2, ...)
Donde la cadena de notación describe la operación a realizar.
Un ejemplo sencillo: Producto escalar de vectores
Comencemos con un ejemplo sencillo: calcular el producto escalar de dos vectores. En notación matemática, el producto escalar de dos vectores u y v es:
$$\sum_i u_i \times v_i$$
Así es como se calcula utilizando 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 notación 'i,i->' significa:
irepresenta el índice de la primera matriz (u)- El segundo
irepresenta el índice de la segunda matriz (v) - La flecha
->seguida de nada indica que queremos un resultado escalar (suma sobre todos los índices)
Comprensión de la notación Einsum
La notación einsum sigue este patrón general:
'index1,index2,...->output_indices'
index1,index2: Etiquetas para las dimensiones de cada matriz de entradaoutput_indices: Etiquetas para las dimensiones en la matriz de salida- Los índices repetidos en las matrices de entrada se suman
- Los índices que aparecen en la salida se mantienen en el resultado
Por ejemplo, en la notación 'ij,jk->ik':
i,json las dimensiones de la primera matrizj,kson las dimensiones de la segunda matrizjaparece en ambas matrices de entrada, por lo que sumamos sobre esta dimensióni,kaparecen en la salida, por lo que se mantienen estas dimensiones
¡Esta es exactamente la fórmula para la multiplicación de matrices!
Operaciones comunes de Einsum
Ahora que comprendemos los conceptos básicos de einsum, exploremos algunas operaciones comunes que podemos realizar utilizando esta poderosa función.
Transposición de matrices
Transponer una matriz significa intercambiar sus filas y columnas. En notación matemática, si A es una matriz, su transpuesta A^T se define como:
$$A^T_{ij} = A_{ji}$$
Veamos cómo realizar la transposición de una matriz utilizando 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 notación 'ij->ji' significa:
ijrepresenta los índices de la matriz de entrada (i para filas, j para columnas)jirepresenta los índices de la matriz de salida (j para filas, i para columnas)- Esencialmente, estamos intercambiando las posiciones de los índices
Multiplicación de matrices
La multiplicación de matrices es una operación fundamental en álgebra lineal. Para dos matrices A y B, su producto C se define como:
$$C_{ik} = \sum_j A_{ij} \times B_{jk}$$
Así es como se realiza la multiplicación de matrices utilizando 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 notación 'ij,jk->ik' significa:
ijrepresenta los índices de la matriz A (i para filas, j para columnas)jkrepresenta los índices de la matriz B (j para filas, k para columnas)ikrepresenta los índices de la matriz de salida C (i para filas, k para columnas)- El índice repetido
jse suma (multiplicación de matrices)
Multiplicación elemento a elemento
La multiplicación elemento a elemento consiste en multiplicar los elementos correspondientes de dos matrices. Para dos matrices A y B con la misma forma, su producto elemento a elemento C es:
$$C_{ij} = A_{ij} \times B_{ij}$$
Así es como se realiza la multiplicación elemento a elemento utilizando 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 notación 'ij,ij->ij' significa:
ijrepresenta los índices de la matriz Aijrepresenta los índices de la matriz Bijrepresenta los índices de la matriz de salida C- No se suman índices, lo que significa que solo estamos multiplicando los elementos correspondientes
Operaciones avanzadas de Einsum
Ahora que estamos familiarizados con las operaciones básicas de einsum, exploremos algunas aplicaciones más avanzadas. Estas operaciones demuestran el verdadero poder y flexibilidad de la función einsum.
Extracción de la diagonal
Extraer los elementos de la diagonal de una matriz es una operación común en álgebra lineal. Para una matriz A, sus elementos diagonales forman un vector d donde:
$$d_i = A_{ii}$$
Así es como se extrae la diagonal utilizando 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 notación 'ii->i' significa:
iirepresenta el índice repetido para los elementos diagonales de Aisignifica que extraemos estos elementos en un arreglo unidimensional
Traza de una matriz
La traza de una matriz es la suma de sus elementos diagonales. Para una matriz A, su traza es:
$$\text{trace}(A) = \sum_i A_{ii}$$
Así es como se calcula la traza utilizando 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 notación 'ii->' significa:
iirepresenta el índice repetido para los elementos diagonales- El índice de salida vacío significa que sumamos todos los elementos diagonales para obtener un escalar
Multiplicación por lotes de matrices
einsum realmente se destaca cuando se realizan operaciones en arreglos de dimensiones superiores. Por ejemplo, la multiplicación por lotes de matrices implica multiplicar pares de matrices de dos lotes.
Si tenemos un lote de matrices A con forma (n, m, p) y un lote de matrices B con forma (n, p, q), la multiplicación por lotes de matrices nos da un resultado C con forma (n, m, q):
$$C_{ijk} = \sum_l A_{ijl} \times B_{ilk}$$
Así es como se realiza la multiplicación por lotes de matrices utilizando 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 notación 'nmp,npq->nmq' significa:
nmprepresenta los índices del lote A (n para el lote, m para las filas, p para las columnas)npqrepresenta los índices del lote B (n para el lote, p para las filas, q para las columnas)nmqrepresenta los índices del lote de salida C (n para el lote, m para las filas, q para las columnas)- El índice repetido
pse suma (multiplicación de matrices)
¿Por qué usar Einsum?
Puedes preguntarte por qué deberíamos usar einsum cuando NumPy ya proporciona funciones especializadas para estas operaciones. Aquí hay algunas ventajas:
- Interfaz unificada:
einsumproporciona una única función para muchas operaciones de arreglos - Flexibilidad: Puede expresar operaciones que de otro modo requerirían múltiples pasos
- Legibilidad: Una vez que entiendes la notación, el código se vuelve más conciso
- Rendimiento: En muchos casos, las operaciones de
einsumestán optimizadas y son eficientes
Para operaciones complejas de tensores, einsum a menudo proporciona la implementación más clara y directa.
Resumen
En este laboratorio, has explorado la poderosa función einsum en NumPy, que implementa la convención de sumación de Einstein para operaciones de arreglos. Repasemos lo que has aprendido:
Conceptos básicos de Einsum: Aprendiste cómo usar la notación
einsumpara expresar operaciones de arreglos, con índices que representan las dimensiones del arreglo y índices repetidos que indican sumación.Operaciones comunes: Implementaste varias operaciones fundamentales utilizando
einsum:- Producto escalar de vectores
- Transposición de matrices
- Multiplicación de matrices
- Multiplicación elemento a elemento
Aplicaciones avanzadas: Exploraste operaciones más complejas:
- Extracción de la diagonal
- Traza de una matriz
- Multiplicación por lotes de matrices
La función einsum proporciona un enfoque unificado y flexible para las operaciones de arreglos en NumPy. Si bien hay funciones especializadas como np.dot, np.matmul y np.transpose para operaciones específicas, einsum ofrece una interfaz consistente para una amplia gama de operaciones, lo que se vuelve especialmente valioso cuando se trabaja con arreglos de dimensiones superiores.
A medida que continúes tu camino en la computación científica y la ciencia de datos, einsum será una poderosa herramienta en tu arsenal para realizar operaciones complejas de arreglos con código conciso y legible.



