Pauli’s exclusion principle and spin matrices#

In 1925 Pauli explained the shell structure of atoms with two rules: an electron needs a fourth quantum number with just two values, and no two electrons may share all four. Uhlenbeck and Goudsmit identified the fourth number as spin, and in 1927 Pauli wrote spin as a two-component wavefunction acted on by the matrices

\[\begin{split}\sigma_x = \begin{pmatrix}0&1\\1&0\end{pmatrix},\quad \sigma_y = \begin{pmatrix}0&-i\\i&0\end{pmatrix},\quad \sigma_z = \begin{pmatrix}1&0\\0&-1\end{pmatrix}.\end{split}\]

This example checks the Pauli-matrix algebra with spin_operator(), shows the spinor’s sign change under a full turn, builds the antisymmetric two-electron wavefunction that enforces exclusion, and fills atomic shells to recover the noble-gas numbers.

import matplotlib.pyplot as plt
import numpy as np
from scipy.linalg import expm

from physicskit.quantum.core.operators import commutator, spin_operator

The Pauli algebra#

\(\sigma_i\sigma_j = \delta_{ij}I + i\epsilon_{ijk}\sigma_k\): each matrix squares to the identity, they anticommute, and their commutators close into the angular-momentum algebra \([S_x,S_y]=i\hbar S_z\).

sig = {a: 2 * spin_operator(a, s=0.5) for a in "xyz"}
I2 = np.eye(2)
eps = {("x", "y"): "z", ("y", "z"): "x", ("z", "x"): "y"}
ok = all(np.allclose(sig[a] @ sig[a], I2) for a in "xyz")
for (a, b), c in eps.items():
    ok &= np.allclose(sig[a] @ sig[b], 1j * sig[c]) and np.allclose(sig[b] @ sig[a], -1j * sig[c])
print(f"sigma_i sigma_j = delta_ij + i eps_ijk sigma_k for all pairs: {ok}")
print(f"[S_x, S_y] = i S_z: {np.allclose(commutator(sig['x'] / 2, sig['y'] / 2), 1j * sig['z'] / 2)}")
print(f"eigenvalues of every sigma: {np.linalg.eigvalsh(sig['x'])}  (two states only)")
sigma_i sigma_j = delta_ij + i eps_ijk sigma_k for all pairs: True
[S_x, S_y] = i S_z: True
eigenvalues of every sigma: [-1.  1.]  (two states only)

A full turn flips the sign#

Rotating a spin-1/2 state by \(\theta\) about z applies \(e^{-i\theta\sigma_z/2}\). At \(\theta=2\pi\) the state comes back as \(-\psi\); only a \(4\pi\) turn is the identity. No classical vector behaves this way.

thetas = np.linspace(0, 4 * np.pi, 200)
overlap = np.array([np.trace(expm(-1j * t * sig["z"] / 2)).real / 2 for t in thetas])
print(f"U(2 pi) = {np.round(expm(-1j * 2 * np.pi * sig['z'] / 2).real, 6).tolist()}")
U(2 pi) = [[-1.0, 0.0], [0.0, -1.0]]

Exclusion from antisymmetry#

Two electrons in a box, in orbitals \(\phi_a\) and \(\phi_b\). The total wavefunction must change sign when the electrons are swapped. In the spin triplet the spatial part is the Slater determinant \(\phi_a(x_1)\phi_b(x_2)-\phi_b(x_1)\phi_a(x_2)\): it vanishes whenever \(x_1 = x_2\), and entirely if \(a = b\). That is the exclusion principle.

x = np.linspace(0, 1, 200)
phi = lambda k: np.sqrt(2) * np.sin(k * np.pi * x)  # noqa: E731
X1, X2 = np.meshgrid(x, x, indexing="ij")
a, b = 1, 2
triplet = (np.outer(phi(a), phi(b)) - np.outer(phi(b), phi(a))) / np.sqrt(2)
singlet = (np.outer(phi(a), phi(b)) + np.outer(phi(b), phi(a))) / np.sqrt(2)
same = np.outer(phi(a), phi(a)) - np.outer(phi(a), phi(a))
print(f"\nSlater determinant with both electrons in orbital {a}: max |psi| = {np.abs(same).max():.1e}")
print(f"triplet |psi|^2 on the diagonal x1 = x2: max {np.max(np.diag(triplet) ** 2):.1e}")
Slater determinant with both electrons in orbital 1: max |psi| = 0.0e+00
triplet |psi|^2 on the diagonal x1 = x2: max 0.0e+00

Filling the shells#

Each hydrogen-like orbital \((n,l,m_l)\) holds two electrons, one per spin state, so subshell \(l\) holds \(2(2l+1)\). Filling in the Madelung order (increasing \(n+l\), then \(n\)) and stopping at each completed p subshell gives the noble gases.

subshells = sorted(((n, l) for n in range(1, 8) for l in range(n)), key=lambda nl: (nl[0] + nl[1], nl[0]))
Z, noble = 0, []
for n, l in subshells:
    Z += 2 * (2 * l + 1)
    if l == 1 or (n, l) == (1, 0):
        noble.append(Z)
print(f"\nclosed shells (noble gases) at Z = {noble[:6]}  (He, Ne, Ar, Kr, Xe, Rn)")

fig, (ax1, ax2, ax3) = plt.subplots(1, 3, figsize=(14, 4))
ax1.plot(thetas / np.pi, overlap, color="steelblue")
ax1.axhline(0, color="0.7", lw=0.8)
for t in (2, 4):
    ax1.axvline(t, color="0.5", ls=":")
ax1.set_xlabel(r"rotation angle $\theta/\pi$")
ax1.set_ylabel(r"$\langle\psi|U(\theta)|\psi\rangle$ (averaged)")
ax1.set_title(r"Spinors: $-1$ after $2\pi$, $+1$ after $4\pi$")
for ax, psi, title in [(ax2, singlet, "spin singlet (symmetric space)"), (ax3, triplet, "spin triplet (antisymmetric space)")]:
    ax.pcolormesh(X1, X2, psi**2, cmap="magma", shading="auto")
    ax.plot([0, 1], [0, 1], "w--", lw=0.8)
    ax.set_xlabel("$x_1$")
    ax.set_ylabel("$x_2$")
    ax.set_aspect("equal")
    ax.set_title(title + r": $|\psi(x_1,x_2)|^2$", fontsize=9)
fig.tight_layout()

plt.show()
Spinors: $-1$ after $2\pi$, $+1$ after $4\pi$, spin singlet (symmetric space): $|\psi(x_1,x_2)|^2$, spin triplet (antisymmetric space): $|\psi(x_1,x_2)|^2$
closed shells (noble gases) at Z = [2, 10, 18, 36, 54, 86]  (He, Ne, Ar, Kr, Xe, Rn)

Check#

The Pauli algebra, the spinor sign flip under a 2 pi rotation, the vanishing of an antisymmetric state with both electrons in one orbital, and the noble-gas closed shells from the Madelung (n + l) filling order.

assert ok
assert np.allclose(expm(-1j * 2 * np.pi * sig["z"] / 2), -I2) and np.allclose(expm(-1j * 4 * np.pi * sig["z"] / 2), I2)
assert np.abs(same).max() == 0 and np.max(np.abs(np.diag(triplet))) < 1e-12
assert noble[:6] == [2, 10, 18, 36, 54, 86]

Total running time of the script: (0 minutes 0.110 seconds)

Gallery generated by Sphinx-Gallery