Tensorial.jl icon indicating copy to clipboard operation
Tensorial.jl copied to clipboard

Statically sized tensors and related operations for Julia

Tensorial

Statically sized tensors and related operations for Julia

Stable CI codecov

Tensorial provides useful tensor operations (e.g., contraction; tensor product, ; inv; etc.) written in the Julia programming language. The library supports arbitrary size of non-symmetric and symmetric tensors, where symmetries should be specified to avoid wasteful duplicate computations. The way to give a size of the tensor is similar to StaticArrays.jl, and symmetries of tensors can be specified by using @Symmetry. For example, symmetric fourth-order tensor (symmetrizing tensor) is represented in this library as Tensor{Tuple{@Symmetry{3,3}, @Symmetry{3,3}}}. Einstein summation macro and automatic differentiation functions are also provided.

Speed

a = rand(Vec{3})                         # vector of length 3
A = rand(SecondOrderTensor{3})           # 3x3 second order tensor
S = rand(SymmetricSecondOrderTensor{3})  # 3x3 symmetric second order tensor
B = rand(Tensor{Tuple{3,3,3}})           # 3x3x3 third order tensor
AA = rand(FourthOrderTensor{3})          # 3x3x3x3 fourth order tensor
SS = rand(SymmetricFourthOrderTensor{3}) # 3x3x3x3 symmetric fourth order tensor (symmetrizing tensor)

See here for above aliases.

Operation Tensor Array speed-up
Single contraction
a ⋅ a 1.537 ns 16.943 ns ×11.0
A ⋅ a 1.538 ns 80.647 ns ×52.4
S ⋅ a 1.545 ns 79.630 ns ×51.5
Double contraction
A ⊡ A 2.730 ns 17.909 ns ×6.6
S ⊡ S 1.704 ns 19.099 ns ×11.2
B ⊡ A 4.886 ns 183.789 ns ×37.6
AA ⊡ A 7.035 ns 193.607 ns ×27.5
SS ⊡ S 3.589 ns 192.727 ns ×53.7
Tensor product
a ⊗ a 2.035 ns 53.872 ns ×26.5
Cross product
a × a 2.035 ns 53.872 ns ×26.5
Determinant
det(A) 1.541 ns 223.537 ns ×145.1
det(S) 1.542 ns 227.196 ns ×147.3
Inverse
inv(A) 5.432 ns 544.122 ns ×100.2
inv(S) 3.872 ns 552.627 ns ×142.7
inv(AA) 854.691 ns 1.727 μs ×2.0
inv(SS) 310.218 ns 1.728 μs ×5.6

The benchmarks are generated by runbenchmarks.jl on the following system:

julia> versioninfo()
Julia Version 1.6.3
Commit ae8452a9e0 (2021-09-23 17:34 UTC)
Platform Info:
  OS: macOS (x86_64-apple-darwin19.5.0)
  CPU: Intel(R) Core(TM) i7-7567U CPU @ 3.50GHz
  WORD_SIZE: 64
  LIBM: libopenlibm
  LLVM: libLLVM-11.0.1 (ORCJIT, skylake)

Installation

pkg> add Tensorial

Cheat Sheet

# tensor aliases
rand(Vec{3})                        # vector
rand(Mat{2,3})                      # matrix
rand(SecondOrderTensor{3})          # 3x3 second-order tensor (this is the same as the Mat{3,3})
rand(SymmetricSecondOrderTensor{3}) # 3x3 symmetric second-order tensor (3x3 symmetric matrix)
rand(FourthOrderTensor{3})          # 3x3x3x3 fourth-order tensor
rand(SymmetricFourthOrderTensor{3}) # 3x3x3x3 symmetric fourth-order tensor

# identity tensors
one(SecondOrderTensor{3,3})        # second-order identity tensor
one(SymmetricSecondOrderTensor{3}) # symmetric second-order identity tensor
one(FourthOrderTensor{3})          # fourth-order identity tensor
one(SymmetricFourthOrderTensor{3}) # symmetric fourth-order identity tensor (symmetrizing tensor)

# zero tensors
zero(Mat{2,3}) == zeros(2,3)
zero(SymmetricSecondOrderTensor{3}) == zeros(3,3)

# random tensors
rand(Mat{2,3})
randn(Mat{2,3})

# macros (same interface as StaticArrays.jl)
@Vec [1,2,3]
@Vec rand(4)
@Mat [1 2
      3 4]
@Mat rand(4,4)
@Tensor rand(2,2,2)

# statically sized getindex by `@Tensor`
x = @Mat [1 2
          3 4
          5 6]
@Tensor(x[2:3, :])   === @Mat [3 4
                               5 6]
@Tensor(x[[1,3], :]) === @Mat [1 2
                               5 6]

# contraction and tensor product
x = rand(Mat{2,2})
y = rand(SymmetricSecondOrderTensor{2})
x ⊗ y isa Tensor{Tuple{2,2,@Symmetry{2,2}}} # tensor product
x ⋅ y isa Tensor{Tuple{2,2}}                # single contraction (x_ij * y_jk)
x ⊡ y isa Real                              # double contraction (x_ij * y_ij)

# det/inv for 2nd-order tensor
A = rand(SecondOrderTensor{3})          # equal to one(Tensor{Tuple{3,3}})
S = rand(SymmetricSecondOrderTensor{3}) # equal to one(Tensor{Tuple{@Symmetry{3,3}}})
det(A); det(S)
inv(A) ⋅ A ≈ one(A)
inv(S) ⋅ S ≈ one(S)

# inv for 4th-order tensor
AA = rand(FourthOrderTensor{3})          # equal to one(Tensor{Tuple{3,3,3,3}})
SS = rand(SymmetricFourthOrderTensor{3}) # equal to one(Tensor{Tuple{@Symmetry{3,3}, @Symmetry{3,3}}})
inv(AA) ⊡ AA ≈ one(AA)
inv(SS) ⊡ SS ≈ one(SS)

# Einstein summation convention
A = rand(Mat{3,3})
B = rand(Mat{3,3})
(@einsum (i,j) -> A[i,k] * B[k,j]) == A ⋅ B
(@einsum A[i,j] * B[i,j]) == A ⊡ B

# Automatic differentiation
gradient(tr, rand(Mat{3,3})) == one(Mat{3,3}) # Tensor -> Real
gradient(identity, rand(SymmetricSecondOrderTensor{3})) == one(SymmetricFourthOrderTensor{3}) # Tensor -> Tensor

Other tensor packages

Inspiration