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 literalij, jk -> ik
julia> ixs = [[1, 2], [2, 3]] # the input indices2-element Vector{Vector{Int64}}: [1, 2] [2, 3]
julia> iy = [1, 3] # the output indices2-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 multiplication2×4 Matrix{Float64}: -0.530387 0.103982 0.394786 -2.50975 0.186964 0.224952 0.76923 -1.7149
julia> size_dict = OMEinsum.get_size_dict(getixsv(code1), (A, B)) # get the size of the labelsDict{Char, Int64} with 3 entries: 'j' => 3 'i' => 2 'k' => 4
julia> einsum(code1, (A, B), size_dict) # lower-level function2×4 Matrix{Float64}: -0.530387 0.103982 0.394786 -2.50975 0.186964 0.224952 0.76923 -1.7149
julia> einsum!(code1, (A, B), zeros(2, 4), true, false, size_dict) # the in-place operation2×4 Matrix{Float64}: -0.530387 0.103982 0.394786 -2.50975 0.186964 0.224952 0.76923 -1.7149
julia> @ein C[i,k] := A[i,j] * B[j,k] # all-in-one macro2×4 Matrix{Float64}: -0.530387 0.103982 0.394786 -2.50975 0.186964 0.224952 0.76923 -1.7149

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}: 119.217 -633.886 -93.1506 … -1174.24 473.039 401.976 -529.751 813.88 209.427 444.211 302.827 -663.561 -2440.07 287.288 -796.652 737.614 148.987 -851.17 240.583 -1046.61 92.9316 233.581 1028.5 123.254 -745.222 1507.03 2255.73 1567.24 3184.23 611.674 -1109.25 -472.273 -1909.46 … -461.676 512.094 -1293.7 -642.444 444.672 781.153 -885.646 2972.66 -606.307 2239.83 529.03 1004.49 -1808.32 -954.176 1551.17 386.864 -743.682 386.233 -349.538 1556.52 1068.03 532.785 -772.055 437.851 102.547 1356.19 -963.399 ⋮ ⋱ 595.644 100.263 1013.93 145.415 -1021.53 -767.206 2944.25 -993.13 258.112 -70.1739 346.863 47.5432 361.025 62.4367 935.364 -758.311 929.607 -838.204 -664.852 -1288.62 -964.951 -876.197 -590.486 3009.44 401.874 524.027 1402.12 … -165.079 566.41 -20.8126 1583.17 -472.669 400.909 -1022.04 257.126 2066.93 -700.477 -328.547 571.745 678.831 -205.258 262.322 -2.32978 -385.381 366.777 -513.497 708.122 -234.999 63.4085 11.4416 829.721 545.025 -789.453 -182.639
julia> ein"(ij,jk),(kl,lm)->im"(tensors...) # manually specified contraction100×100 Matrix{Float64}: 119.217 -633.886 -93.1506 … -1174.24 473.039 401.976 -529.751 813.88 209.427 444.211 302.827 -663.561 -2440.07 287.288 -796.652 737.614 148.987 -851.17 240.583 -1046.61 92.9316 233.581 1028.5 123.254 -745.222 1507.03 2255.73 1567.24 3184.23 611.674 -1109.25 -472.273 -1909.46 … -461.676 512.094 -1293.7 -642.444 444.672 781.153 -885.646 2972.66 -606.307 2239.83 529.03 1004.49 -1808.32 -954.176 1551.17 386.864 -743.682 386.233 -349.538 1556.52 1068.03 532.785 -772.055 437.851 102.547 1356.19 -963.399 ⋮ ⋱ 595.644 100.263 1013.93 145.415 -1021.53 -767.206 2944.25 -993.13 258.112 -70.1739 346.863 47.5432 361.025 62.4367 935.364 -758.311 929.607 -838.204 -664.852 -1288.62 -964.951 -876.197 -590.486 3009.44 401.874 524.027 1402.12 … -165.079 566.41 -20.8126 1583.17 -472.669 400.909 -1022.04 257.126 2066.93 -700.477 -328.547 571.745 678.831 -205.258 262.322 -2.32978 -385.381 366.777 -513.497 708.122 -234.999 63.4085 11.4416 829.721 545.025 -789.453 -182.639

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) # scalar0-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