Introdução
Em computação científica, a realização de operações eficientes de álgebra linear é essencial. NumPy, uma biblioteca Python fundamental para computações numéricas, fornece inúmeras ferramentas para essas operações. Dentre essas ferramentas, einsum (Somatório de Einstein) se destaca como uma função particularmente poderosa para expressar operações complexas em arrays de forma concisa.
Este tutorial irá guiá-lo através da compreensão do que é einsum e como usá-lo efetivamente em seu código Python. Ao final deste laboratório, você será capaz de realizar várias operações em arrays usando a função einsum e entender suas vantagens sobre as funções NumPy tradicionais.
Entendendo os Fundamentos do NumPy Einsum
A Somatória de Einstein (einsum) é uma poderosa função NumPy que permite expressar muitas operações em arrays usando uma notação concisa. Ela segue a convenção de somatório de Einstein, comumente usada em física e matemática para simplificar equações complexas.
Abrindo o Shell Python
Vamos começar abrindo o shell Python. Abra um terminal na Área de Trabalho e digite:
python3
Você deve ver o prompt Python (>>>), indicando que agora está no shell interativo Python.
Importando NumPy
Primeiro, precisamos importar a biblioteca NumPy:
import numpy as np

O que é Einsum?
A função einsum em NumPy permite que você especifique operações em arrays usando uma notação de string que descreve quais índices (dimensões) dos arrays devem ser operados.
O formato básico de uma operação einsum é:
np.einsum('notação', array1, array2, ...)
Onde a string de notação descreve a operação a ser realizada.
Um Exemplo Simples: Produto Escalar de Vetores
Vamos começar com um exemplo simples: calcular o produto escalar de dois vetores. Em notação matemática, o produto escalar de dois vetores u e v é:
$$\sum_i u_i \times v_i$$
Aqui está como calculá-lo usando einsum:
## Crie dois vetores aleatórios
u = np.random.rand(5)
v = np.random.rand(5)
## Imprima os vetores para ver seus valores
print("Vector u:", u)
print("Vector v:", v)
## Calcule o produto escalar usando einsum
dot_product = np.einsum('i,i->', u, v)
print("Dot product using einsum:", dot_product)
## Verifique com a função dot do NumPy
numpy_dot = np.dot(u, v)
print("Dot product using np.dot:", numpy_dot)

A notação 'i,i->' significa:
irepresenta o índice do primeiro array (u)- O segundo
irepresenta o índice do segundo array (v) - A seta
->seguida por nada indica que queremos um resultado escalar (soma sobre todos os índices)
Compreendendo a Notação Einsum
A notação einsum segue este padrão geral:
'índice1,índice2,...->índices_saída'
índice1,índice2: Rótulos para as dimensões de cada array de entradaíndices_saída: Rótulos para as dimensões no array de saída- Índices repetidos em arrays de entrada são somados
- Índices que aparecem na saída são mantidos no resultado
Por exemplo, na notação 'ij,jk->ik':
i,jsão as dimensões do primeiro arrayj,ksão as dimensões do segundo arrayjaparece em ambos os arrays de entrada, então somamos sobre esta dimensãoi,kaparecem na saída, então essas dimensões são mantidas
Esta é exatamente a fórmula para multiplicação de matrizes!
Operações Comuns com Einsum
Agora que entendemos os fundamentos do einsum, vamos explorar algumas operações comuns que podemos realizar usando esta poderosa função.
Transposição de Matriz
Transpor uma matriz significa trocar suas linhas e colunas. Em notação matemática, se A é uma matriz, sua transposta A^T é definida como:
$$A^T_{ij} = A_{ji}$$
Vamos ver como realizar a transposição de matrizes usando einsum:
## Crie uma matriz aleatória
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)
A notação 'ij->ji' significa:
ijrepresenta os índices da matriz de entrada (i para linhas, j para colunas)jirepresenta os índices da matriz de saída (j para linhas, i para colunas)- Estamos essencialmente trocando as posições dos índices
Multiplicação de Matrizes
A multiplicação de matrizes é uma operação fundamental em álgebra linear. Para duas matrizes A e B, seu produto C é definido como:
$$C_{ik} = \sum_j A_{ij} \times B_{jk}$$
Aqui está como realizar a multiplicação de matrizes usando 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)
A notação 'ij,jk->ik' significa:
ijrepresenta os índices da matriz A (i para linhas, j para colunas)jkrepresenta os índices da matriz B (j para linhas, k para colunas)ikrepresenta os índices da matriz de saída C (i para linhas, k para colunas)- O índice repetido
jé somado (multiplicação de matrizes)
Multiplicação Elemento a Elemento
A multiplicação elemento a elemento envolve a multiplicação dos elementos correspondentes de dois arrays. Para duas matrizes A e B com a mesma forma, seu produto elemento a elemento C é:
$$C_{ij} = A_{ij} \times B_{ij}$$
Aqui está como realizar a multiplicação elemento a elemento usando 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)
A notação 'ij,ij->ij' significa:
ijrepresenta os índices da matriz Aijrepresenta os índices da matriz Bijrepresenta os índices da matriz de saída C- Nenhum índice é somado, o que significa que estamos apenas multiplicando os elementos correspondentes
Operações Avançadas com Einsum
Agora que estamos confortáveis com as operações básicas do einsum, vamos explorar algumas aplicações mais avançadas. Essas operações demonstram o verdadeiro poder e flexibilidade da função einsum.
Extração da Diagonal
Extrair os elementos da diagonal de uma matriz é uma operação comum em álgebra linear. Para uma matriz A, seus elementos diagonais formam um vetor d onde:
$$d_i = A_{ii}$$
Aqui está como extrair a diagonal usando einsum:
## Crie uma matriz quadrada aleatória
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)
A notação 'ii->i' significa:
iirepresenta o índice repetido para os elementos da diagonal de Aisignifica que extraímos esses elementos em um array 1D
Traço da Matriz
O traço de uma matriz é a soma de seus elementos da diagonal. Para uma matriz A, seu traço é:
$$\text{trace}(A) = \sum_i A_{ii}$$
Aqui está como calcular o traço usando 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)
A notação 'ii->' significa:
iirepresenta o índice repetido para os elementos da diagonal- O índice de saída vazio significa que somamos todos os elementos da diagonal para obter um escalar
Multiplicação de Matrizes em Lote (Batch)
einsum realmente se destaca ao realizar operações em arrays de dimensões superiores. Por exemplo, a multiplicação de matrizes em lote envolve a multiplicação de pares de matrizes de dois lotes.
Se tivermos um lote de matrizes A com forma (n, m, p) e um lote de matrizes B com forma (n, p, q), a multiplicação de matrizes em lote nos dá um resultado C com forma (n, m, q):
$$C_{ijk} = \sum_l A_{ijl} \times B_{ilk}$$
Aqui está como realizar a multiplicação de matrizes em lote usando 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])
A notação 'nmp,npq->nmq' significa:
nmprepresenta os índices do lote A (n para lote, m para linhas, p para colunas)npqrepresenta os índices do lote B (n para lote, p para linhas, q para colunas)nmqrepresenta os índices do lote de saída C (n para lote, m para linhas, q para colunas)- O índice repetido
pé somado (multiplicação de matrizes)
Por que Usar Einsum?
Você pode se perguntar por que devemos usar einsum quando o NumPy já fornece funções especializadas para essas operações. Aqui estão algumas vantagens:
- Interface Unificada:
einsumfornece uma única função para muitas operações em arrays - Flexibilidade: Pode expressar operações que, de outra forma, exigiriam várias etapas
- Legibilidade: Uma vez que você entende a notação, o código se torna mais conciso
- Desempenho: Em muitos casos, as operações
einsumsão otimizadas e eficientes
Para operações complexas com tensores, einsum geralmente fornece a implementação mais clara e direta.
Resumo
Neste laboratório, você explorou a poderosa função einsum no NumPy, que implementa a convenção de somação de Einstein para operações em arrays. Vamos recapitular o que você aprendeu:
Conceitos Básicos do Einsum: Você aprendeu como usar a notação
einsumpara expressar operações em arrays, com índices representando dimensões do array e índices repetidos indicando somação.Operações Comuns: Você implementou várias operações fundamentais usando
einsum:- Produto escalar (dot product) de vetores
- Transposição de matrizes
- Multiplicação de matrizes
- Multiplicação elemento a elemento
Aplicações Avançadas: Você explorou operações mais complexas:
- Extração da diagonal
- Traço da matriz
- Multiplicação de matrizes em lote (batch)
A função einsum fornece uma abordagem unificada e flexível para operações em arrays no NumPy. Embora funções especializadas como np.dot, np.matmul e np.transpose estejam disponíveis para operações específicas, einsum oferece uma interface consistente para uma ampla gama de operações, o que se torna especialmente valioso ao trabalhar com arrays de dimensões superiores.
À medida que você continua sua jornada em computação científica e ciência de dados, einsum será uma ferramenta poderosa em seu arsenal para realizar operações complexas em arrays com código conciso e legível.



