Use of `np.einsum()`
np.einsum() is a powerful function in NumPy that performs Einstein summation, which allows for flexible manipulation of multi-dimensional arrays (tensors) using summation notation. It can handle various operations like matrix multiplication, element-wise operations, and tensor contractions in a very efficient way.
Syntax:
np.einsum(subscripts, *operands, **kwargs)
-
subscripts: A string representing the Einstein summation convention. -
*operands: The arrays (tensors) on which the operation is performed. -
**kwargs: Optional arguments likeoptimize, which can be used to improve performance.
Einstein Summation Convention
The Einstein summation convention is a notational shorthand where repeated indices are implicitly summed over. For example:
-
ij,jk->ikdenotes a matrix multiplication between two 2D arrays. -
ii->isums the diagonal elements of a matrix.
Common Examples
-
Matrix Multiplication (
np.dotornp.matmul)import numpy as np A = np.array([[1, 2], [3, 4]]) B = np.array([[5, 6], [7, 8]]) # Matrix multiplication result = np.einsum('ij,jk->ik', A, B) print(result)Explanation:
-
ij: Refers to the indices of the matrixA. -
jk: Refers to the indices of the matrixB. -
ik: The result is the matrix multiplication ofAandB.
-
-
Sum over an axis (similar to
np.sum)input_array = np.array([[1, 2], [3, 4]]) # Sum over all elements (similar to np.sum) total_sum = np.einsum('ij->', input_array) print(total_sum)Explanation:
-
ij->: This notation sums over all indices of the matrixinput_array, returning the total sum.
-
-
Trace of a Matrix (sum of diagonal elements)
input_array = np.array([[1, 2], [3, 4]]) # Trace (sum of diagonal elements) trace = np.einsum('ii->', D) print(trace)Explanation:
-
ii->: This notation picks the diagonal elements of the matrixinput_arrayand sums them.
-
-
Element-wise multiplication
first_array = np.array([[1, 2], [3, 4]]) second_array = np.array([[5, 6], [7, 8]]) # Element-wise multiplication element_wise = np.einsum('ij,ij->ij', first_array, second_array) print(element_wise)Explanation:
-
ij,ij->ij: The indicesijare the same for both arraysfirst_arrayandsecond_array, resulting in element-wise multiplication.
-
- Dot product of vectors (
np.dot)x = np.array([1, 2, 3]) y = np.array([4, 5, 6]) # Dot product dot_product = np.einsum('i,i->', x, y) print(dot_product) -
Tensor contraction (generalized summation over axes)
first_array = np.random.rand(3, 3, 3) second_array = np.random.rand(3, 3) # Contract tensor G and matrix H tensor_contraction = np.einsum('ijk,jl->ikl', first_array, second_array) print(tensor_contraction.shape)Explanation:
-
ijk,jl->ikl: Summation is performed over the common axisj, resulting in a contraction of the tensor.Advantages of
np.einsum
-
- Flexibility: You can perform many types of operations in a single function call.
- Efficiency: It can be faster than separate functions like
np.dot,np.sum, etc., especially when you have complex operations to perform.