Basic Usage
In the following example, we demonstrate the einsum notation for basic tensor operations.
Einsum notation
To specify the operation, the user can either use the @ein_str
-string literal or the EinCode
object. For example, both the following code snippets define the matrix multiplication operation:
julia> using OMEinsum
julia> code1 = ein"ij,jk -> ik" # the string literal
ij, jk -> ik
julia> ixs = [[1, 2], [2, 3]] # the input indices
2-element Vector{Vector{Int64}}: [1, 2] [2, 3]
julia> iy = [1, 3] # the output indices
2-element Vector{Int64}: 1 3
julia> code2 = EinCode(ixs, iy) # the EinCode object (equivalent to the string literal)
1∘2, 2∘3 -> 1∘3
The @ein_str
macro can be used to define the einsum notation directly in the function call.
julia> A, B = randn(2, 3), randn(3, 4);
julia> code1(A, B) # matrix multiplication
2×4 Matrix{Float64}: 0.815756 1.9698 0.906113 -1.03114 1.17761 -0.339905 0.316626 0.084276
julia> size_dict = OMEinsum.get_size_dict(getixsv(code1), (A, B)) # get the size of the labels
Dict{Char, Int64} with 3 entries: 'j' => 3 'i' => 2 'k' => 4
julia> einsum(code1, (A, B), size_dict) # lower-level function
2×4 Matrix{Float64}: 0.815756 1.9698 0.906113 -1.03114 1.17761 -0.339905 0.316626 0.084276
julia> einsum!(code1, (A, B), zeros(2, 4), true, false, size_dict) # the in-place operation
2×4 Matrix{Float64}: 0.815756 1.9698 0.906113 -1.03114 1.17761 -0.339905 0.316626 0.084276
julia> @ein C[i,k] := A[i,j] * B[j,k] # all-in-one macro
2×4 Matrix{Float64}: 0.815756 1.9698 0.906113 -1.03114 1.17761 -0.339905 0.316626 0.084276
Here, we show that the @ein
macro combines the einsum notation defintion and the operation in a single line, which is more convenient for simple operations. Separating the einsum notation and the operation (the first approach) can be useful for reusing the einsum notation for multiple input tensors. Lower level functions, einsum
and einsum!
, can be used for more control over the operation.
For more than two input tensors, the @ein_str
macro does not optimize the contraction order. In such cases, the user can use the @optein_str
string literal to optimize the contraction order or specify the contraction order manually.
julia> tensors = [randn(100, 100) for _ in 1:4];
julia> optein"ij,jk,kl,lm->im"(tensors...) # optimized contraction (without knowing the size)
100×100 Matrix{Float64}: 772.037 242.855 -961.968 … 987.992 431.657 105.605 -1362.34 1418.97 668.521 178.241 813.71 -426.068 140.406 -297.093 -689.838 228.569 -817.923 689.101 229.008 -710.368 -1336.9 511.542 348.596 -275.557 -923.88 -470.409 -727.274 -891.78 360.767 -1296.85 -297.03 13.8973 -149.46 … -1050.8 48.4599 -813.76 -467.228 108.213 -1118.7 680.517 -87.086 -841.93 -301.979 444.783 -1516.13 492.875 708.983 344.039 -216.073 -729.436 -354.813 532.492 -243.59 -761.036 942.681 -752.188 219.081 1291.61 1590.7 377.303 ⋮ ⋱ 109.494 -265.966 -411.084 127.081 1529.92 -687.84 -75.8842 1511.48 521.704 348.976 -1250.93 1204.27 -740.917 792.635 1437.87 861.705 2375.74 -614.117 -1763.4 256.833 1391.75 1713.88 1764.3 1602.2 -1781.88 -945.265 414.702 … 771.2 2240.79 610.02 1856.13 -1414.74 941.018 -169.387 -1164.72 -671.997 -569.241 443.017 1076.9 -1308.76 597.594 -492.722 99.6067 -703.261 1125.68 1443.36 445.565 177.958 49.7803 -2063.14 62.1675 -673.704 -525.343 -1090.68
julia> ein"(ij,jk),(kl,lm)->im"(tensors...) # manually specified contraction
100×100 Matrix{Float64}: 772.037 242.855 -961.968 … 987.992 431.657 105.605 -1362.34 1418.97 668.521 178.241 813.71 -426.068 140.406 -297.093 -689.838 228.569 -817.923 689.101 229.008 -710.368 -1336.9 511.542 348.596 -275.557 -923.88 -470.409 -727.274 -891.78 360.767 -1296.85 -297.03 13.8973 -149.46 … -1050.8 48.4599 -813.76 -467.228 108.213 -1118.7 680.517 -87.086 -841.93 -301.979 444.783 -1516.13 492.875 708.983 344.039 -216.073 -729.436 -354.813 532.492 -243.59 -761.036 942.681 -752.188 219.081 1291.61 1590.7 377.303 ⋮ ⋱ 109.494 -265.966 -411.084 127.081 1529.92 -687.84 -75.8842 1511.48 521.704 348.976 -1250.93 1204.27 -740.917 792.635 1437.87 861.705 2375.74 -614.117 -1763.4 256.833 1391.75 1713.88 1764.3 1602.2 -1781.88 -945.265 414.702 … 771.2 2240.79 610.02 1856.13 -1414.74 941.018 -169.387 -1164.72 -671.997 -569.241 443.017 1076.9 -1308.76 597.594 -492.722 99.6067 -703.261 1125.68 1443.36 445.565 177.958 49.7803 -2063.14 62.1675 -673.704 -525.343 -1090.68
Sometimes, manually optimizing the contraction order can be beneficial. Please check Contraction order optimization for more details.
Einsum examples
We first define the tensors and then demonstrate the einsum notation for various tensor operations.
julia> using OMEinsum
julia> s = fill(1) # scalar
0-dimensional Array{Int64, 0}: 1
julia> w, v = [1, 2], [4, 5]; # vectors
julia> A, B = [1 2; 3 4], [5 6; 7 8]; # matrices
julia> T1, T2 = reshape(1:8, 2, 2, 2), reshape(9:16, 2, 2, 2); # 3D tensor
Unary examples
julia> ein"i->"(w) # sum of the elements of a vector.
0-dimensional Array{Int64, 0}: 3
julia> ein"ij->i"(A) # sum of the rows of a matrix.
2-element Vector{Int64}: 3 7
julia> ein"ii->"(A) # sum of the diagonal elements of a matrix, i.e., the trace.
0-dimensional Array{Int64, 0}: 5
julia> ein"ij->"(A) # sum of the elements of a matrix.
0-dimensional Array{Int64, 0}: 10
julia> ein"i->ii"(w) # create a diagonal matrix.
2×2 Matrix{Int64}: 1 0 0 2
julia> ein"i->ij"(w; size_info=Dict('j'=>2)) # repeat a vector to form a matrix.
2×2 Matrix{Int64}: 1 1 2 2
julia> ein"ijk->ikj"(T1) # permute the dimensions of a tensor.
2×2×2 Array{Int64, 3}: [:, :, 1] = 1 5 2 6 [:, :, 2] = 3 7 4 8
Binary examples
julia> ein"ij, jk -> ik"(A, B) # matrix multiplication.
2×2 Matrix{Int64}: 19 22 43 50
julia> ein"ijb,jkb->ikb"(T1, T2) # batch matrix multiplication.
2×2×2 Array{Int64, 3}: [:, :, 1] = 39 47 58 70 [:, :, 2] = 163 187 190 218
julia> ein"ij,ij->ij"(A, B) # element-wise multiplication.
2×2 Matrix{Int64}: 5 12 21 32
julia> ein"ij,ij->"(A, B) # sum of the element-wise multiplication.
0-dimensional Array{Int64, 0}: 70
julia> ein"ij,->ij"(A, s) # element-wise multiplication by a scalar.
2×2 Matrix{Int64}: 1 2 3 4
Nary examples
julia> optein"ai,aj,ak->ijk"(A, A, B) # star contraction.
2×2×2 Array{Int64, 3}: [:, :, 1] = 68 94 94 132 [:, :, 2] = 78 108 108 152
julia> optein"ia,ajb,bkc,cld,dm->ijklm"(A, T1, T2, T1, A) # tensor train contraction.
2×2×2×2×2 Array{Int64, 5}: [:, :, 1, 1, 1] = 9500 14564 21604 33420 [:, :, 2, 1, 1] = 11084 17012 25204 39036 [:, :, 1, 2, 1] = 13644 20916 31028 47996 [:, :, 2, 2, 1] = 15932 24452 36228 56108 [:, :, 1, 1, 2] = 13214 20258 30050 46486 [:, :, 2, 1, 2] = 15414 23658 35050 54286 [:, :, 1, 2, 2] = 19430 29786 44186 68350 [:, :, 2, 2, 2] = 22686 34818 51586 79894