NumPy Einsum para la Computación Científica

NumPyNumPyIntermediate
Practicar Ahora

💡 Este tutorial está traducido por IA desde la versión en inglés. Para ver la versión original, puedes hacer clic aquí

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.


Skills Graph

%%%%{init: {'theme':'neutral'}}%%%% flowchart RL numpy(("NumPy")) -.-> numpy/ArrayManipulationGroup(["Array Manipulation"]) numpy(("NumPy")) -.-> numpy/MathandStatisticsGroup(["Math and Statistics"]) numpy(("NumPy")) -.-> numpy/AdvancedFeaturesGroup(["Advanced Features"]) numpy(("NumPy")) -.-> numpy/ArrayBasicsGroup(["Array Basics"]) numpy/ArrayBasicsGroup -.-> numpy/multi_array("Multi-dimensional Array Creation") numpy/ArrayManipulationGroup -.-> numpy/transpose("Transpose and Axis Swap") numpy/MathandStatisticsGroup -.-> numpy/math_ops("Math Operations") numpy/MathandStatisticsGroup -.-> numpy/lin_alg("Linear Algebra") numpy/AdvancedFeaturesGroup -.-> numpy/ufuncs("Universal Functions") subgraph Lab Skills numpy/multi_array -.-> lab-4991{{"NumPy Einsum para la Computación Científica"}} numpy/transpose -.-> lab-4991{{"NumPy Einsum para la Computación Científica"}} numpy/math_ops -.-> lab-4991{{"NumPy Einsum para la Computación Científica"}} numpy/lin_alg -.-> lab-4991{{"NumPy Einsum para la Computación Científica"}} numpy/ufuncs -.-> lab-4991{{"NumPy Einsum para la Computación Científica"}} end

Comprensión de 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
Open Python Shell

¿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)
Dot Product

La notación 'i,i->' significa:

  • i representa el índice de la primera matriz (u)
  • El segundo i representa 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 entrada
  • output_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, j son las dimensiones de la primera matriz
  • j, k son las dimensiones de la segunda matriz
  • j aparece en ambas matrices de entrada, por lo que sumamos sobre esta dimensión
  • i, k aparecen 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:

  • ij representa los índices de la matriz de entrada (i para filas, j para columnas)
  • ji representa 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:

  • ij representa los índices de la matriz A (i para filas, j para columnas)
  • jk representa los índices de la matriz B (j para filas, k para columnas)
  • ik representa los índices de la matriz de salida C (i para filas, k para columnas)
  • El índice repetido j se 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:

  • ij representa los índices de la matriz A
  • ij representa los índices de la matriz B
  • ij representa 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:

  • 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:

  1. Interfaz unificada: einsum proporciona una única función para muchas operaciones de arreglos
  2. Flexibilidad: Puede expresar operaciones que de otro modo requerirían múltiples pasos
  3. Legibilidad: Una vez que entiendes la notación, el código se vuelve más conciso
  4. 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.

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:

  1. Conceptos básicos de Einsum: Aprendiste cómo usar la notación einsum para expresar operaciones de arreglos, con índices que representan las dimensiones del arreglo y índices repetidos que indican sumación.

  2. 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
  3. 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.