Index Notation & Tensors
Tensors generalise scalars and vectors to quantities that need several directions to describe — stress, strain, inertia, conductivity. Learn index (Einstein) notation, the Kronecker delta and Levi-Civita symbol, tensor order, transformation rules under rotation, contraction, symmetric/antisymmetric parts, and how to translate between index form and matrix form.
A scalar (temperature) needs one number; a vector (force) needs three and a direction. Stress needs nine numbers because it relates two directions: the orientation of a surface and the direction of the force on it. Such objects are second-order tensors.
Index notation lets you write long vector and matrix identities in one line and manipulate them like algebra. It is the language of continuum mechanics, electromagnetics, relativity and modern ML (where 'tensor' just means multi-dimensional array).
An analogy
A tensor is like a machine: a second-order tensor takes in a direction (the surface normal) and outputs a vector (the traction). The numbers inside depend on your coordinate system, but what the machine does is physical and fixed.
Free and dummy indices
An index appearing once in a term is free — the equation holds for each value 1, 2, 3. An index appearing twice is a dummy — it is summed and can be renamed. An index may never appear three times in one term.
Example: yᵢ = Aᵢⱼxⱼ is three equations (i free) each with a three-term sum (j dummy).
Tensor order
The number of free indices is the order (rank).
- Order 0: scalar — temperature, energy
- Order 1: vector — velocity, force, heat flux
- Order 2: stress, strain, inertia, thermal conductivity in anisotropic materials
- Order 4: elasticity tensor Cᵢⱼₖₗ relating stress to strain (81 components, 21 independent in the most general case, 2 for isotropic)
What makes it a tensor
Not every array is a tensor: its components must transform by the rule T′ = QTQᵀ when axes rotate. This guarantees physical statements (like σ·n = t) are true in every coordinate system. Quantities built only from contractions — trace, determinant, eigenvalues — are invariant.
Proving identities with ε–δ
a × (b × c) = b(a·c) − c(a·b) takes one line: [a × (b × c)]ᵢ = εᵢⱼₖaⱼεₖₗₘbₗcₘ = (δᵢₗδⱼₘ − δᵢₘδⱼₗ)aⱼbₗcₘ = bᵢ(aⱼcⱼ) − cᵢ(aⱼbⱼ).
In curvilinear coordinates and relativity, upper (contravariant) and lower (covariant) indices are distinguished, and the metric tensor gᵢⱼ raises and lowers them.
Formulas
- Summation convention:
aᵢbᵢ ≡ Σᵢ aᵢbᵢ = a₁b₁ + a₂b₂ + a₃b₃ - Dot and cross products:
a·b = aᵢbᵢ; (a × b)ᵢ = εᵢⱼₖ aⱼ bₖ - Matrix product:
(AB)ᵢⱼ = Aᵢₖ Bₖⱼ; (Ax)ᵢ = Aᵢⱼ xⱼ - Kronecker delta:
δᵢᵢ = 3; δᵢⱼ aⱼ = aᵢ - ε–δ identity:
εᵢⱼₖ εᵢₘₙ = δⱼₘδₖₙ − δⱼₙδₖₘ - Vector transformation:
a′ᵢ = Qᵢⱼ aⱼ - Tensor transformation:
T′ᵢⱼ = Qᵢₖ Qⱼₗ Tₖₗ (T′ = Q T Qᵀ) - Symmetric + antisymmetric:
Tᵢⱼ = ½(Tᵢⱼ + Tⱼᵢ) + ½(Tᵢⱼ − Tⱼᵢ)
Worked example: Contractions
Given: a = (1, 2, 3), b = (4, 5, 6)
Find: aᵢbᵢ and εᵢⱼₖaⱼbₖ.
- aᵢbᵢ = 4 + 10 + 18
- (a×b)₁ = 2·6 − 3·5 = −3; (a×b)₂ = 3·4 − 1·6 = 6; (a×b)₃ = 1·5 − 2·4 = −3
Answer: 32 and (−3, 6, −3)
Worked example: Kronecker substitution
Given: δᵢⱼδⱼₖ and δᵢⱼAᵢⱼ
Find: Simplify.
- δ replaces j by i: δᵢⱼδⱼₖ = δᵢₖ
- δᵢⱼAᵢⱼ = Aᵢᵢ
Answer: δᵢₖ and trace(A)
Worked example: Rotating a 2-D stress
Given: σₓ = 80, σᵧ = 20, τ = 40 MPa; Axes rotated θ = 30°
Find: σ′ₓ and τ′ₓᵧ.
- σ′ₓ = 50 + 30cos60° + 40sin60°
- τ′ = −30sin60° + 40cos60°
Answer: σ′ₓ = 99.64 MPa, τ′ = −5.98 MPa
Index notation with numpy.einsum
import numpy as np
a, b = np.array([1., 2, 3]), np.array([4., 5, 6])
eps = np.zeros((3, 3, 3)) # Levi-Civita symbol
eps[0,1,2] = eps[1,2,0] = eps[2,0,1] = 1
eps[0,2,1] = eps[2,1,0] = eps[1,0,2] = -1
print("a_i b_i =", np.einsum("i,i->", a, b))
print("eps_ijk a_j b_k =", np.einsum("ijk,j,k->i", eps, a, b))
print("delta_ii =", np.einsum("ii->", np.eye(3)))
# Rotate a tensor 30 degrees about z: T' = Q T Q^T
th = np.radians(30)
Q = np.array([[np.cos(th), np.sin(th), 0], [-np.sin(th), np.cos(th), 0], [0, 0, 1]])
T = np.array([[80., 40, 0], [40, 20, 0], [0, 0, 0]])
Tp = np.einsum("ik,jl,kl->ij", Q, Q, T)
print("T'_11, T'_12 =", np.round(Tp[0, 0], 2), np.round(Tp[0, 1], 2))
print("trace invariant =", np.trace(T), np.round(np.trace(Tp), 10))
# a_i b_i = 32.0
# eps_ijk a_j b_k = [-3. 6. -3.]
# delta_ii = 3.0
# T'_11, T'_12 = 99.64 -5.98
# trace invariant = 100.0 100.0einsum takes the index expression literally: repeated letters are summed. The trace does not change under rotation — it is an invariant.
Common mistakes
- Wrong: Using an index three times: aᵢbᵢcᵢ. — Right: Not valid; rename or use explicit sums.
- Wrong: Transforming a tensor like a vector (T′ = QT). — Right: Second order needs Q on both sides: QTQᵀ.
- Wrong: Confusing engineering shear strain γ with tensor ε₁₂. — Right: ε₁₂ = γ₁₂/2.
Applications
- Solid mechanics: Stress, strain and elasticity tensors.
- Dynamics: Inertia tensor Iᵢⱼ for rotating bodies and spacecraft.
- Anisotropic materials: Conductivity and permeability tensors in composites and rocks.
- Machine learning: einsum/tensor contractions in neural-network libraries.
Key takeaways
- Repeated index = sum; free index = equation per value.
- Order = number of free indices.
- Tensors transform with one Q per index.
- Invariants are coordinate-free physics.
Going further
- Covariant/contravariant: Upper/lower indices and the metric tensor.
- Tensor calculus: Christoffel symbols and covariant derivatives in curved coordinates.
- Voigt notation: Storing symmetric tensors as 6-vectors in FEM codes.