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
.
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:
ii
representa el índice repetido para los elementos diagonales de A
i
significa 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:
ii
representa 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:
nmp
representa los índices del lote A (n para el lote, m para las filas, p para las columnas)
npq
representa los índices del lote B (n para el lote, p para las filas, q para las columnas)
nmq
representa los índices del lote de salida C (n para el lote, m para las filas, q para las columnas)
- El índice repetido
p
se 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:
einsum
proporciona 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
einsum
están optimizadas y son eficientes
Para operaciones complejas de tensores, einsum
a menudo proporciona la implementación más clara y directa.