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).

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

Worked example: Contractions

Given: a = (1, 2, 3), b = (4, 5, 6)

Find: aᵢbᵢ and εᵢⱼₖaⱼbₖ.

Answer: 32 and (−3, 6, −3)

Worked example: Kronecker substitution

Given: δᵢⱼδⱼₖ and δᵢⱼAᵢⱼ

Find: Simplify.

Answer: δᵢₖ and trace(A)

Worked example: Rotating a 2-D stress

Given: σₓ = 80, σᵧ = 20, τ = 40 MPa; Axes rotated θ = 30°

Find: σ′ₓ and τ′ₓᵧ.

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.0

einsum takes the index expression literally: repeated letters are summed. The trace does not change under rotation — it is an invariant.

Common mistakes

Applications

Key takeaways

Going further