Chapter 29
25 min read
Section 246 of 353

Particle in a Box: Infinite Square Well

The Schrödinger Equation

Learning Objectives

By the end of this section, you will be able to:

  1. Set up the time-independent Schrödinger equation inside a hard-walled potential and read off the boundary conditions straight from the physics.
  2. Solve the equation by hand and obtain the eigenfunctions ψn(x)=2/Lsin(nπx/L)\psi_n(x) = \sqrt{2/L}\,\sin(n\pi x/L) and energies En=n2π22/(2mL2)E_n = n^2\pi^2\hbar^2/(2mL^2).
  3. Explain why the energy spectrum is discrete and why the ground state cannot have E=0E = 0.
  4. Interpret the nodes of ψn\psi_n and use orthogonality 0Lψmψndx=δmn\int_0^L \psi_m\psi_n\,dx = \delta_{mn} to expand any state.
  5. Compute a real example (electron in a 1-nm box) end to end, in eV and in photon-emission wavelengths.
  6. Watch a superposition oscillate in time and identify the beat frequency ωmn=(EmEn)/\omega_{mn} = (E_m-E_n)/\hbar.
  7. Verify the analytic result numerically by discretizing the Hamiltonian and diagonalizing it in Python and PyTorch.

The Big Picture

"The particle in a box is to quantum mechanics what the simple harmonic oscillator is to classical mechanics: the first place where everything works out cleanly and you can see all the moving parts."

Imagine a bead trapped between two impassable walls a distance LL apart, free to slide between them but unable to escape. Classically there is nothing to talk about — the bead has whatever energy you gave it and bounces forever. Quantum mechanically, this is the simplest non-trivial problem in physics, and it already contains three of the deepest surprises of the theory:

  1. Energy is quantized. Only certain discrete energies are allowed: E1,4E1,9E1,16E1,E_1, 4E_1, 9E_1, 16E_1, \ldots
  2. The lowest energy is not zero. Even at "rest" the particle has zero-point energy E1=π22/(2mL2)E_1 = \pi^2\hbar^2/(2mL^2).
  3. Standing waves replace trajectories. The state is a shape, not a position. The shape has nodes — places where the particle is never found.

What we are about to do

We will write down the Schrödinger equation inside the box, solve the resulting ODE, apply the boundary conditions, and watch quantization fall out of nothing more than the requirement that the wave fit between the walls. Then we will turn the same calculation into a matrix and diagonalize it numerically in Python and PyTorch, and finally we will animate a superposition and discover that a quantum state can actually move even though every eigenstate is stationary.


Setting Up the Box

The infinite square well is the potential

V(x)={00<x<LotherwiseV(x) = \begin{cases} 0 & 0 < x < L \\ \infty & \text{otherwise} \end{cases}

The infinite barrier outside [0,L][0,L] is a mathematical idealization of a physical wall the particle can absolutely not penetrate. The wave function is forced to be zero outside the box, and by continuity it must also vanish on the boundary:

ψ(0)=0,ψ(L)=0\psi(0) = 0, \qquad \psi(L) = 0

Where the boundary conditions come from

You can derive ψ(0)=ψ(L)=0\psi(0) = \psi(L) = 0 as a limit. Replace the infinite walls with finite ones of height V0V_0 and solve. The interior solutions are sines/cosines and the exterior ones decay like eκxe^{-\kappa x} with κ=2m(V0E)/\kappa = \sqrt{2m(V_0-E)}/\hbar. As V0V_0 \to \infty the decay constant κ\kappa\to\infty and the exterior wave collapses to zero, dragging the interior wave to zero at the wall for continuity.

Inside the box V=0V = 0, so the time-independent Schrödinger equation H^ψ=Eψ\hat H\psi = E\psi reduces to:

22md2ψdx2=Eψ-\frac{\hbar^2}{2m}\,\frac{d^2\psi}{dx^2} = E\,\psi

That is a second-order linear ODE with constant coefficients — the kind of object you have been solving since first-year calculus. The only quantum part is what we will do with it at the boundary.


Solving the Schrödinger Equation

Rearrange the ODE into the familiar harmonic-oscillator form:

d2ψdx2=k2ψ,k22mE2\frac{d^2\psi}{dx^2} = -k^2\,\psi, \qquad k^2 \equiv \frac{2mE}{\hbar^2}

For any E>0E > 0 we have k>0k > 0 and the general solution is the familiar pair of sines and cosines:

ψ(x)=Asin(kx)+Bcos(kx)\psi(x) = A\sin(kx) + B\cos(kx)

Step 1 — Apply ψ(0)=0\psi(0) = 0

Plug in x=0x = 0: ψ(0)=Asin(0)+Bcos(0)=B\psi(0) = A\sin(0) + B\cos(0) = B. For this to vanish we need B=0B = 0. The cosine piece is killed by the left wall.

Step 2 — Apply ψ(L)=0\psi(L) = 0

What is left is ψ(x)=Asin(kx)\psi(x) = A\sin(kx). The right wall demands Asin(kL)=0A\sin(kL) = 0. Setting A=0A = 0 would zero out the wave function everywhere — that is the trivial solution and corresponds to no particle at all. The only useful way to satisfy the equation is to force the sine itself to vanish:

sin(kL)=0    kL=nπ,n=1,2,3,\sin(kL) = 0 \;\Longrightarrow\; kL = n\pi, \quad n = 1, 2, 3, \ldots

Negative nn just flips the overall sign of ψ\psi and doesn't give a new physical state. n=0n = 0 gives ψ0\psi \equiv 0, so we throw it out. The allowed values of kk are therefore:

kn=nπLk_n = \frac{n\pi}{L}

Boundary Conditions Force Quantization

Look at what just happened. The ODE itself doesn't care about EE — any positive energy gives a perfectly valid sine wave. It was the boundary conditions that selected a discrete set. Translating kn=nπ/Lk_n = n\pi/L back into energy via k2=2mE/2k^2 = 2mE/\hbar^2:

En  =  n2π222mL2,n=1,2,3,\boxed{\,E_n \;=\; \frac{n^2\pi^2\hbar^2}{2mL^2}, \qquad n = 1, 2, 3, \ldots\,}

This is energy quantization, derived from nothing more sophisticated than "the wave has to fit between two walls." The intuition is exactly the same as for a guitar string: only certain wavelengths fit, and each allowed wavelength corresponds to a definite frequency — and through the de Broglie relation p=kp = \hbar k, a definite momentum and energy.

Guitar string analogy. A string fixed at both ends vibrates only at frequencies whose half-wavelength fits an integer number of times into its length: L=nλ/2L = n\lambda/2. The quantum particle in a box is the exact same equation, with the energy taking the role of the squared frequency. Quantum mechanics didn't invent discrete spectra — every musical instrument has one. The difference is that for the bead in the box, the "string" is the wave function itself.

Why E = 0 is forbidden

If you tried E=0E = 0 the ODE becomes ψ=0\psi'' = 0, whose general solution is linear: ψ(x)=ax+b\psi(x) = ax + b. Demanding ψ(0)=0\psi(0) = 0 forces b=0b = 0. Demanding ψ(L)=0\psi(L) = 0 then forces a=0a = 0. So the only way to have E=0E = 0 is ψ0\psi \equiv 0 — no particle at all. Localizing a particle costs energy. This is zero-point energy, and it is the cleanest face of the uncertainty principle: ΔxLΔp/LE2/(2mL2)\Delta x \approx L \Rightarrow \Delta p \gtrsim \hbar/L \Rightarrow E \gtrsim \hbar^2/(2mL^2).


Normalization and Orthogonality

The wave function still has an unknown amplitude AA. We fix it by demanding total probability one:

0Lψn(x)2dx=1\int_0^L |\psi_n(x)|^2\,dx = 1

With ψn(x)=Asin(nπx/L)\psi_n(x) = A\sin(n\pi x/L) the integral evaluates to:

A20Lsin2(nπx/L)dx=A2L2=1A^2 \int_0^L \sin^2(n\pi x/L)\,dx = A^2 \cdot \frac{L}{2} = 1

which yields A=2/LA = \sqrt{2/L}. So our normalized eigenstates are:

ψn(x)  =  2Lsin ⁣(nπxL)\boxed{\,\psi_n(x) \;=\; \sqrt{\tfrac{2}{L}}\,\sin\!\left(\tfrac{n\pi x}{L}\right)\,}

Orthogonality

Different eigenstates are orthogonal: for mnm \neq n,

0Lψm(x)ψn(x)dx=0\int_0^L \psi_m(x)\,\psi_n(x)\,dx = 0

Combined with normalization this is 0Lψmψndx=δmn\int_0^L \psi_m\psi_n\,dx = \delta_{mn}. A quick way to see it: use the product-to-sum identity sinasinb=12[cos(ab)cos(a+b)]\sin a\sin b = \tfrac{1}{2}[\cos(a-b)-\cos(a+b)]. Both cosine integrals over [0,L][0,L] vanish when mnm \neq n, because the cosine completes whole periods on the interval.

Why orthogonality matters

Orthogonality of the ψn\psi_n is the single most useful property of the box. It means any wave function on [0,L][0,L] (vanishing at the walls) can be expanded as a Fourier sine series: Ψ(x)=n=1cnψn(x)\Psi(x) = \sum_{n=1}^\infty c_n\,\psi_n(x) with cn=0Lψn(x)Ψ(x)dxc_n = \int_0^L \psi_n(x)\,\Psi(x)\,dx. Time evolution then becomes trivial because each piece carries its own phase factor eiEnt/e^{-iE_n t/\hbar}.


Interactive: Eigenstate Explorer

Move the sliders. n picks the quantum number; L stretches or shrinks the box. Watch what each change does:

  • Increasing n adds nodes (interior zeros where ψ=0\psi = 0) and pushes the energy up like n2n^2.
  • Increasing L stretches the wave horizontally and lowers every energy as 1/L21/L^2.
  • For even nn, the box centre x=L/2x = L/2 is a node — the particle is never found exactly in the middle. For odd nn it is an antinode (the most likely spot).
Loading eigenstate explorer…

The Energy Ladder

The energies form a ladder whose rungs spread out quadratically:

nk_n = nπ/LE_n (units of E₁)Gap to previous level
1π/L1
22π/L43
33π/L95
44π/L167
55π/L259
nnπ/L2n − 1

Two things are worth pausing on. First, the gap En+1En=(2n+1)E1E_{n+1}-E_n = (2n+1)E_1 grows linearly with nn — the higher you climb, the more energy each new rung costs. Second, therelative gap (En+1En)/En2/n(E_{n+1}-E_n)/E_n \to 2/n shrinks. In the limit of huge nn the spectrum looks effectively continuous — this is the correspondence principle: classical physics emerges smoothly from quantum mechanics at high quantum numbers.

Loading energy ladder…

Properties of the Eigenstates

Nodes

ψn(x)=2/Lsin(nπx/L)\psi_n(x) = \sqrt{2/L}\sin(n\pi x/L) vanishes at x=kL/nx = kL/n for k=0,1,2,,nk = 0, 1, 2, \ldots, n. The endpoints x=0x=0 and x=Lx=L are forced zeros (the walls), leaving n − 1 interior nodes. The number of nodes is the quickest fingerprint of which eigenstate you are looking at.

Parity around the centre

Define u=xL/2u = x - L/2. Then sin(nπx/L)=sin(nπ/2)cos(nπu/L)+cos(nπ/2)sin(nπu/L)\sin(n\pi x/L) = \sin(n\pi/2)\cos(n\pi u/L) + \cos(n\pi/2)\sin(n\pi u/L). For odd nn only the cosine term survives — the eigenstate is symmetric about L/2L/2. For even nn only the sine survives — it is antisymmetric. This alternation is why even-n states have a node right at the centre.

Expectation values you can compute by hand

QuantityValueWhy
⟨x⟩L/2All ψ_n are either symmetric or antisymmetric about L/2; |ψ_n|² is symmetric, so the average position is the centre.
⟨p⟩0ψ_n is real, so the momentum-density expectation vanishes — there is no net flow.
⟨x²⟩L²(1/3 − 1/(2n²π²))Drops out of ∫ x² sin²(nπx/L) dx; goes to L²/3 for high n (uniform distribution).
⟨p²⟩n²π²ℏ²/L² = 2m·E_nSince ⟨p⟩ = 0, ⟨p²⟩ = 2m·E_n. Higher energy levels carry more momentum spread.

Worked Example: Electron in a 1 nm Box

Numbers grounded a model. Take an electron confined to a one-nanometre box — a reasonable cartoon of a quantum dot or a single benzene-ring-size cavity.

Walk it through by hand →

Constants we will use:

  • =1.0546×1034\hbar = 1.0546 \times 10^{-34} J·s
  • me=9.109×1031m_e = 9.109 \times 10^{-31} kg
  • L=1 nm=1×109L = 1\ \text{nm} = 1\times 10^{-9} m
  • 1 eV=1.602×10191\ \text{eV} = 1.602\times 10^{-19} J

Step 1 — Compute the ground-state energy E1=π22/(2meL2)E_1 = \pi^2\hbar^2/(2m_eL^2).

π² ≈ 9.8696
ℏ² = (1.0546e-34)² ≈ 1.1122e-68 J²·s²
2 m_e L² = 2 · 9.109e-31 · (1e-9)² = 1.822e-48 kg·m²
E_1 = 9.8696 · 1.1122e-68 / 1.822e-48
E_1 ≈ 6.025e-20 J

Convert to electron-volts:

E_1 ≈ 6.025e-20 / 1.602e-19 ≈ 0.376 eV

Step 2 — Build the ladder. En=n2E1E_n = n^2 E_1:

E_1 ≈ 0.376 eV (ground state)
E_2 ≈ 1.504 eV
E_3 ≈ 3.385 eV
E_4 ≈ 6.018 eV

Step 3 — Predict a photon. A 2 → 1 transition releases E2E1=3E11.128E_2 - E_1 = 3E_1 \approx 1.128 eV. Plug into λ=hc/(E2E1)\lambda = hc/(E_2 - E_1) with hc1240 eV\cdotpnmhc \approx 1240\ \text{eV·nm}:

λ ≈ 1240 / 1.128 ≈ 1099 nm (near-infrared)

Step 4 — Sanity check the size dependence. If we had picked L=0.5L = 0.5 nm instead, every energy would be 4× larger and the same transition would emit blue-violet light at ≈ 275 nm. This is exactly how quantum dots are engineered to fluoresce in different colours — by tuning their size.

Step 5 — Average position and momentum. Because ψ12|\psi_1|^2 is symmetric about L/2L/2, x=L/2=0.5\langle x\rangle = L/2 = 0.5 nm and p=0\langle p\rangle = 0. But p2=2meE1\langle p^2\rangle = 2m_e E_1 is not zero — it gives a momentum spread Δp=2meE13.3×1025\Delta p = \sqrt{2 m_e E_1} \approx 3.3\times 10^{-25} kg·m/s. That spread, times ΔxL\Delta x \sim L, lands right at the uncertainty-principle floor ΔxΔp/2\Delta x\,\Delta p \gtrsim \hbar/2.


Superposition and Time Evolution

A single eigenstate evolves only in phase: Ψn(x,t)=ψn(x)eiEnt/\Psi_n(x,t) = \psi_n(x)\,e^{-iE_n t/\hbar}. The complex phase rotates but Ψn(x,t)2=ψn(x)2|\Psi_n(x,t)|^2 = |\psi_n(x)|^2 doesn't change. Stationary states are truly stationary — their probability density is frozen.

The interesting dynamics show up the moment you combine eigenstates with different energies. Suppose:

Ψ(x,t)=c1ψ1(x)eiE1t/+c2ψ2(x)eiE2t/\Psi(x,t) = c_1\,\psi_1(x)\,e^{-iE_1 t/\hbar} + c_2\,\psi_2(x)\,e^{-iE_2 t/\hbar}

Square it. The cross term picks up cos ⁣((E2E1)t/)\cos\!\big((E_2-E_1)t/\hbar\big):

Ψ2=c12ψ12+c22ψ22+2c1c2ψ1ψ2cos ⁣((E2E1)t)|\Psi|^2 = c_1^2\psi_1^2 + c_2^2\psi_2^2 + 2c_1c_2\,\psi_1\psi_2\,\cos\!\left(\tfrac{(E_2-E_1)t}{\hbar}\right)

The first two pieces are static. The last piece oscillates at the beat (Bohr) frequency ω21=(E2E1)/\omega_{21} = (E_2-E_1)/\hbar. Because ψ1\psi_1 is symmetric and ψ2\psi_2 is antisymmetric about L/2L/2, the product ψ1ψ2\psi_1\psi_2 is antisymmetric: it is positive on one half of the box and negative on the other. So as the cosine oscillates, the probability density sloshes from one side to the other.

Loading superposition demo…

What just happened?

One eigenstate alone is just a colour photograph — frozen. Two eigenstates together make a movie. The frame rate is set by the energy gap divided by \hbar. That is how every quantum clock works: spectroscopists measure the frequency at which a superposition oscillates and back out the energy difference between two levels to many decimal places.


Numerical Solution in Python

It is a one-line story: discretize the Hamiltonian on a grid, diagonalize the matrix, read off the spectrum. The finite-difference rule turns the operator (2/2m)d2/dx2-(\hbar^2/2m)\,d^2/dx^2 into a tridiagonal matrix. Diagonalizing it returns approximate eigenvalues — the discrete EnE_n — and discrete samples of the eigenfunctions. The boundary conditions ψ(0)=ψ(L)=0\psi(0)=\psi(L)=0 are built in just by the choice of grid (we omit the wall points from the unknowns).

Plain NumPy: build H, diagonalize, compare to E_n = n²π²/(2L²)
🐍particle_in_box.py
1Import NumPy

We need NumPy for array math and np.linalg.eigh, which diagonalizes real symmetric matrices. Everything in this script is real-valued (the Hamiltonian for a particle in a real potential is real symmetric).

EXAMPLE
np.array([1.0, 2.0, 3.0])
5Pick a unit system

Setting ℏ = 1 and m = 1 strips every formula down to its mathematical core. The only scale left is L. The lowest energy comes out as π²/(2L²); to convert back to SI multiply by ℏ²/m later.

6Box length

L = 1 in our chosen units. Doubling L would quarter every energy level — see the formula E_n = n²π²/(2L²).

7Number of interior grid points

N = 200 is overkill for accuracy on the first few states but cheap. For n ≪ N the numerical eigenvalues agree with the exact answer to about 4 decimal places (you will see this confirmed in the printed output).

EXAMPLE
n=1: numerical=9.8694, exact=9.8696
8Grid spacing dx

There are N interior points plus the two wall points x=0 and x=L, so the spacing between any two neighbours is L/(N+1). For N=200 and L=1, dx ≈ 0.004975.

EXAMPLE
dx = 1.0 / 201 ≈ 0.004975
12Interior position grid

We deliberately exclude x=0 and x=L. The wall values are pinned to 0 by the boundary conditions and are not unknowns we need to solve for.

EXAMPLE
x = [0.005, 0.010, 0.015, ..., 0.995]
17What is the Hamiltonian here?

Inside the box V = 0, so the time-independent Schrödinger equation Ĥψ = Eψ becomes -(ℏ²/2m) ψ''(x) = E ψ(x). In our units that is -(1/2) ψ'' = E ψ. We discretize -(1/2) d²/dx² as a matrix.

19Three-point second derivative

The finite-difference rule replaces ψ''(x_i) with (ψ_{i+1} − 2ψ_i + ψ_{i-1})/dx². The error is O(dx²), and it produces a tridiagonal matrix: 1 on the main diagonal, −½ on the off-diagonals after applying the −1/2 prefactor and dividing by dx².

EXAMPLE
ψ''(x_5) ≈ (ψ_6 − 2ψ_5 + ψ_4) / dx²
21Boundary conditions are automatic

Because ψ_0 and ψ_{N+1} are not in the vector, the formulas for ψ''(x_1) and ψ''(x_N) implicitly use 0 in their place. So ψ(0) = ψ(L) = 0 — the hard-wall condition — is baked into the matrix shape.

22Main diagonal entries

Each diagonal entry is +1/dx² after multiplying by the −1/2 prefactor: −(1/2) · (−2)/dx² = +1/dx². For dx ≈ 0.005 this is ≈ 40,401 per cell — large, because the second derivative is steep when grid spacing is small.

23Off-diagonal entries

Each off-diagonal entry is −1/(2 dx²) after the prefactor: −(1/2) · (1)/dx². The matrix is symmetric, so np.linalg.eigh will produce a real spectrum.

24Assembling H

np.diag with k=+1 places values on the super-diagonal; k=−1 places them on the sub-diagonal. Adding the three pieces gives a tridiagonal Hamiltonian — exactly what discretizing −(1/2) ∂²/∂x² produces under hard-wall boundary conditions.

EXAMPLE
H = [[1/dx², -1/2dx², 0, ...], [-1/2dx², 1/dx², -1/2dx², ...], ...]
32Diagonalize: H ψ_n = E_n ψ_n

np.linalg.eigh exploits the fact that H is real symmetric. It returns eigenvalues sorted ascending and the matching eigenvectors as columns. The first column is the ground state ψ_1, the second column is ψ_2, and so on.

EXAMPLE
energies[0] = lowest eigenvalue ≈ π²/(2L²) ≈ 4.9348
35Cross-check against the exact formula

Analytically E_n = n²π²/(2mL²). With m = 1, ℏ = 1, L = 1 this is n²π²/2. So E_1 ≈ 4.9348, E_2 ≈ 19.7392, E_3 ≈ 44.4132. Watching numerical and exact values line up is what tells you the discretization is faithful.

36Loop over the first five levels

We compare only n = 1..5. Higher-n eigenstates oscillate more rapidly and need finer grids to be accurate; for n ≪ N the agreement is essentially perfect.

37Compute the exact energy

exact = (n π)² / (2 L²). For n=1: exact = π²/2 ≈ 4.9348. For n=2: 4·π²/2 = 2π² ≈ 19.7392. The ratio exact_n / exact_1 = n² is the signature of the infinite well.

38Error magnitude

For n=1 with N=200 the absolute error is around 1e-4 — that is the size of the O(dx²) truncation. Multiplying N by 2 should divide the error by 4.

45Plot the first three eigenstates

Eigenvalues alone do not tell the whole story. Looking at the eigenvectors visually confirms ψ_1 is a single arch, ψ_2 has one node at L/2, ψ_3 has two nodes at L/3 and 2L/3 — exactly what sin(nπx/L) predicts.

47Pull out column n of the eigenvector matrix

states is shape (N, N). Column n holds the discretized values of the n-th eigenstate at each grid point. We treat it as a vector of length N.

EXAMPLE
states[:, 0] = the ground-state wavefunction sampled at every x_i
49Discrete normalization

np.linalg.eigh returns Euclidean unit vectors (||psi|| = 1). To make ∫|ψ|² dx = 1 we must divide by √(Σ|ψ_i|² · dx). After this step, integrating |ψ|² over the box gives exactly 1.

51Sign convention (cosmetic)

Eigenvectors are defined up to an overall sign. For comparison plots we prefer ψ_n(L/2) to be positive for odd n. This step does nothing physical — it just keeps the curves visually consistent.

53Plot and label

matplotlib does the rest. You should see three smooth sine arches with 0, 1, 2 interior nodes respectively. They match √(2/L) sin(nπx/L) to plotting precision.

33 lines without explanation
1import numpy as np
2import matplotlib.pyplot as plt
3
4# -------- 1.  Physical setup ---------------------------------------------
5# Natural units: hbar = 1, m = 1.  Box length L is the only scale.
6L = 1.0
7N = 200              # number of interior grid points
8dx = L / (N + 1)     # spacing between adjacent grid points
9
10# Interior position grid: x_1, x_2, ..., x_N
11# The walls live at x_0 = 0 and x_{N+1} = L; they are NOT in the array.
12x = np.linspace(dx, L - dx, N)
13
14# -------- 2.  Build the Hamiltonian H ------------------------------------
15# Inside the box V(x) = 0, so H = -(1/2) d^2/dx^2.
16# Three-point finite difference for the second derivative:
17#   psi''(x_i) ~= ( psi_{i+1} - 2*psi_i + psi_{i-1} ) / dx^2
18# Drop psi_0 and psi_{N+1}: that *automatically* enforces psi(0)=psi(L)=0.
19main_diag = (1.0 / dx**2) * np.ones(N)              # = -(-2)/(2 dx^2) = 1/dx^2
20off_diag  = (-0.5 / dx**2) * np.ones(N - 1)         # = -(1)/(2 dx^2)
21H = (
22    np.diag(main_diag)
23    + np.diag(off_diag, k=+1)
24    + np.diag(off_diag, k=-1)
25)
26
27# -------- 3.  Diagonalize ------------------------------------------------
28# H is real symmetric -> np.linalg.eigh returns (eigvals, eigvecs)
29# sorted by increasing eigenvalue.  Each column eigvecs[:, n] is psi_n.
30energies, states = np.linalg.eigh(H)
31
32# -------- 4.  Compare to the analytic result E_n = n^2 pi^2 / (2 L^2) ----
33print("First five energy levels (numerical vs exact):")
34for n in range(1, 6):
35    exact = (n * np.pi) ** 2 / (2 * L**2)
36    err   = abs(energies[n - 1] - exact)
37    print(f"  n={n}:  numerical={energies[n-1]:8.4f}   "
38          f"exact={exact:8.4f}   error={err:.2e}")
39
40# -------- 5.  Normalize and plot the first three eigenstates -------------
41fig, ax = plt.subplots(figsize=(8, 4))
42for n in range(3):
43    psi = states[:, n]
44    # Discrete normalization: sum |psi_i|^2 * dx = 1
45    psi = psi / np.sqrt(np.sum(psi**2) * dx)
46    # Fix sign so psi_n(L/2) > 0 for odd n -- pure cosmetics
47    if psi[N // 2] < 0:
48        psi = -psi
49    ax.plot(x, psi, label=f"n={n+1}")
50
51ax.axhline(0, color="gray", lw=0.5)
52ax.set_xlabel("x")
53ax.set_ylabel(r"$\psi_n(x)$")
54ax.legend()
55plt.show()

What you should see when you run it

For N=200N = 200:

n=1: numerical=4.9347 exact=4.9348 error=1.01e-04
n=2: numerical=19.7392 exact=19.7392 error=5.00e-04
n=3: numerical=44.4118 exact=44.4132 error=1.41e-03
n=4: numerical=78.9290 exact=78.9568 error=2.78e-02

The ratios En/E1E_n/E_1 come out as 1, 4, 9, 16 to four significant figures — direct experimental confirmation of Enn2E_n \propto n^2.


The Same Idea in PyTorch

Why bother rewriting in PyTorch? Three good reasons. First, the operation is GPU-friendly: H^\hat H as a matrix is exactly what GPUs are designed for. Second, PyTorch's linalg.eigh\texttt{linalg.eigh} works on batches, so you can solve the eigenproblem for many different potentials or many different box lengths in one call. Third, the same Hamiltonian shows up as a building block in physics-informed neural networks and neural quantum states — having the operator in PyTorch lets you wire it directly into a learning loop.

PyTorch: GPU-ready, batched diagonalization sweeping over L
🐍particle_in_box_torch.py
1Import PyTorch

PyTorch gives us the same array math as NumPy plus a GPU backend, automatic differentiation, and torch.linalg.eigh that handles *batched* eigendecomposition. The Hamiltonian is independent of any neural-network training here — we just borrow the linear-algebra plumbing.

2Import math

We only need math.pi. Python's float pi is plenty precise for comparing against torch.float64 eigenvalues; for stricter comparisons you could use torch.pi.

4Pick device

If a CUDA GPU is available, the Hamiltonian and the eigendecomposition land there. For an N=200 tridiagonal matrix the speed-up is negligible, but the same code scales without changes to N = 5000 where the GPU matters.

EXAMPLE
device = cuda  →  4× speedup when sweeping 1024 box lengths
7Box length

Same units as the NumPy version. The whole point of repeating the calculation in PyTorch is to show that the *math* didn't change — only the plumbing.

8Number of interior points

N = 200. The matrix is 200 × 200, a tiny problem by GPU standards. The eigendecomposition runs in a few milliseconds.

9Grid spacing

Identical to the NumPy version: dx = L/(N+1) ≈ 0.004975. Keeping the same dx makes the eigenvalues numerically identical to NumPy down to floating-point round-off.

12Main-diagonal tensor

torch.full builds a 1-D tensor filled with a constant. The value 1/dx² is the same coefficient as in the NumPy version — it comes from the −1/2 prefactor in front of the Laplacian times the −2 from the central-difference stencil.

13Off-diagonal tensor

Length N−1 because the super-diagonal and sub-diagonal of an N×N matrix each have N−1 entries. Filled with −1/(2 dx²), the off-diagonal coupling from the finite-difference Laplacian.

14Assemble H

torch.diag(diag) puts diag on the main diagonal. torch.diag(off, 1) puts off on the +1 super-diagonal. Sum them, plus the symmetric copy on −1, to get a tridiagonal real-symmetric Hamiltonian — exactly the discrete −(1/2) ∂² operator with hard-wall boundaries.

20torch.linalg.eigh

The Hermitian/symmetric eigendecomposition. Returns (eigvals, eigvecs), with eigenvalues sorted ascending and eigenvectors as columns. Unlike torch.linalg.eig, eigh is numerically stable and works on batches.

EXAMPLE
energies, states = torch.linalg.eigh(H)  # H must be symmetric
23Header for the comparison table

Just formatting. The interesting line is the comparison itself — confirming that PyTorch and the analytic formula agree.

24Loop over the first five levels

We compare n = 1..5. Just like the NumPy version, errors grow with n because higher modes oscillate faster than the grid resolves.

25Exact n-th energy

E_n = n²π²/(2L²). For n=1, L=1 this is π²/2 ≈ 4.9348. For n=5 it is 25 · π²/2 ≈ 123.37.

26Tensor → Python float

.item() pulls a 0-d tensor out to a Python float. This is fine here because we are just printing; never call .item() inside a tight loop or you will hammer the GPU-CPU sync.

EXAMPLE
energies[0].item() → 4.9347
27Print numerical, exact, and absolute error

Expect to see something like: n=1 4.9347 4.9348 1.01e-04. The error scales as O(dx²). Doubling N (halving dx) should shrink the error by roughly 4×.

31Sweep over many box lengths

Now we exercise the *batching* superpower. We solve the eigenproblem for 16 different L values at once.

32torch.linspace of box lengths

Builds a tensor of 16 evenly spaced L values from 0.5 to 2.0. Each L gives a different Hamiltonian — but they all share the same N, so the matrices are the same shape.

EXAMPLE
Ls = [0.50, 0.60, 0.70, ..., 2.00]
35Loop builds one Hamiltonian per L

We rebuild diag and off for each L because dx depends on L. In production code you would broadcast this on the GPU; here we stay readable.

39Stack into a 3-D batch

torch.stack along a new leading dimension gives a tensor of shape (16, N, N) — sixteen Hamiltonians indexed by their L value. This is the input torch.linalg.eigh wants for batched mode.

EXAMPLE
H_batch.shape = torch.Size([16, 200, 200])
41Batched diagonalization

torch.linalg.eigh on a 3-D tensor solves all 16 eigenproblems in a single call. On GPU this is dramatically faster than looping in Python. The result has shape (16, N) — N eigenvalues per L.

42Ground-state energy for each L

E_batch[:, 0] picks out the *smallest* eigenvalue from each Hamiltonian — the ground-state energy E_1(L). We expect E_1(L) ∝ 1/L².

45Verify the 1/L² scaling

If E_1(L) = π²/(2L²), then E_1(L) · L² should equal π²/2 ≈ 4.9348 for every L in the sweep. Seeing a flat list of values clustered around 4.9348 is the cleanest possible empirical confirmation of the formula.

EXAMPLE
[4.9342, 4.9344, 4.9345, ..., 4.9347]  ← all ≈ pi^2 / 2
23 lines without explanation
1import torch
2import math
3
4device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
5
6# -------- 1.  Setup ------------------------------------------------------
7L  = 1.0
8N  = 200
9dx = L / (N + 1)
10
11# Build the same tridiagonal Hamiltonian, but as a torch.Tensor
12diag = torch.full((N,),      1.0 / dx**2,       device=device)
13off  = torch.full((N - 1,), -0.5 / dx**2,       device=device)
14H = torch.diag(diag) + torch.diag(off, 1) + torch.diag(off, -1)
15
16# -------- 2.  Diagonalize ------------------------------------------------
17# torch.linalg.eigh is the GPU/autograd-friendly analogue of np.linalg.eigh.
18# It accepts batched matrices, so a stack of Hamiltonians can be solved
19# in one call -- very useful when sweeping over L or V parameters.
20energies, states = torch.linalg.eigh(H)
21
22# -------- 3.  Compare to the exact result --------------------------------
23print(f"{'n':>3}  {'numerical':>12}  {'exact':>12}  {'error':>10}")
24for n in range(1, 6):
25    exact = (n * math.pi) ** 2 / (2 * L**2)
26    num   = energies[n - 1].item()
27    print(f"{n:3d}  {num:12.4f}  {exact:12.4f}  {abs(num - exact):10.2e}")
28
29# -------- 4.  Sweep over many box lengths in one shot --------------------
30# Vary L and watch E_1 scale like 1 / L^2.
31Ls    = torch.linspace(0.5, 2.0, 16, device=device)
32batch = []
33for L_val in Ls:
34    dx = (L_val / (N + 1)).item()
35    d  = torch.full((N,),      1.0 / dx**2, device=device)
36    o  = torch.full((N - 1,), -0.5 / dx**2, device=device)
37    batch.append(torch.diag(d) + torch.diag(o, 1) + torch.diag(o, -1))
38H_batch = torch.stack(batch)            # shape (16, N, N)
39
40E_batch, _ = torch.linalg.eigh(H_batch) # shape (16, N)
41E1_curve = E_batch[:, 0]                # ground-state energy for each L
42
43# E_1 * L^2 should be (very close to) the constant pi^2 / 2 ~ 4.9348
44print("E_1 * L^2 across the sweep (should be constant ~= pi^2/2):")
45print((E1_curve * Ls**2).cpu().numpy().round(4))

The 1/L² scaling, verified empirically

The last block of the PyTorch script computes E1(L)L2E_1(L) \cdot L^2 for 16 different values of LL. If the analytic formula is right, this product should be the constant π2/24.9348\pi^2/2 \approx 4.9348. And it is — to four decimal places across the entire sweep. There is no extra tuning, no parameter sharing, no model. The numerics agree with the maths because the maths is right.


Why This Toy Model Matters

1. Quantum dots — colour by size

Real semiconductor quantum dots are three-dimensional infinite-well cousins of the model we just solved. Their emission wavelength is controlled almost entirely by their physical size: E1/L2λL2E \propto 1/L^2 \Rightarrow \lambda \propto L^2. Grow a CdSe dot at 2 nm and it glows blue; grow it at 6 nm and it glows red. Same chemistry, different box.

2. Quantum wells in lasers

Inside a semiconductor laser is a thin layer of low-bandgap material sandwiched between higher-bandgap layers — a near-perfect one-dimensional box for charge carriers. The quantized energy levels we just derived determine the laser's emission frequency.

3. Conjugated molecules and the free-electron model

In molecules like β-carotene, π-electrons are roughly free along the carbon backbone. Treating that backbone as an infinite well predicts the absorption colour of the molecule — it is why carrots are orange and tomatoes are red.

4. Numerical PDE solvers and ML for physics

Discretizing a Hamiltonian and diagonalizing it is the simplest case of the technique behind density-functional theory, neural quantum states, and physics-informed neural networks. The matrix we just diagonalized is exactly the building block those methods scale up to.

ML connection: variational quantum Monte Carlo

Neural Quantum States parameterize ψθ(x)\psi_\theta(x) with a neural network and minimize the variational energy E[θ]=ψθH^ψθ/ψθψθE[\theta] = \langle \psi_\theta|\hat H|\psi_\theta\rangle/\langle\psi_\theta|\psi_\theta\rangle. For the infinite well, that loss has a global minimum at exactly E1=π22/(2mL2)E_1 = \pi^2\hbar^2/(2mL^2), and the best-fit network output is — up to a sign and a normalization —2/Lsin(πx/L)\sqrt{2/L}\sin(\pi x/L). The toy problem is the unit test for the whole framework.


Test Your Understanding


Summary

ConceptResultWhere it comes from
Eigenfunctionsψ_n(x) = √(2/L) sin(nπx/L)Sines with B = 0 (left wall) and kL = nπ (right wall)
Allowed kk_n = nπ/L, n = 1, 2, …Boundary condition sin(kL) = 0
EnergiesE_n = n²π²ℏ²/(2mL²)k² = 2mE/ℏ² applied to k_n
Ground stateE_1 > 0 (zero-point energy)ψ ≡ 0 is the only E = 0 solution — incompatible with a particle existing
Nodesn − 1 interior zerossin(nπx/L) = 0 at x = kL/n, k = 1, …, n − 1
Orthonormality∫₀ᴸ ψ_m ψ_n dx = δ_{mn}Hermitian H + Fourier-sine product-to-sum identity
Time evolutionΨ_n(x,t) = ψ_n(x) e^{−iE_n t/ℏ}Stationary states only acquire a phase
Superposition dynamicsOscillation at ω_{mn} = (E_m − E_n)/ℏCross-term in |c_1ψ_1 + c_2ψ_2|² beats at the energy difference

Key Takeaways

  1. Quantization is geometric. The discreteness of EnE_n comes from a boundary condition, not from any quantum-mechanical postulate beyond Schrödinger's equation itself.
  2. Localizing costs energy. The ground state has strictly positive energy. There is no way to put a particle in a box of finite size with zero kinetic energy.
  3. Eigenstates are stationary; only superpositions move. The clock rate of the motion is set by the energy gap.
  4. The orthonormal basis ψ_n is a Fourier sine basis. Any state on [0,L][0,L] vanishing at the walls expands into them, and time evolution becomes phase-rotation of the expansion coefficients.
  5. Numerics confirm everything. A 200-point finite-difference Hamiltonian recovers the analytic spectrum to four decimals, in both NumPy and PyTorch, and a single batched torch.linalg.eigh sweep proves E11/L2E_1 \propto 1/L^2 directly.
The essence of the infinite well:
"A wave forced to fit between two walls can only choose a discrete set of shapes. Each shape has a definite energy. That is all quantization is — and it is enough to colour every nanocrystal on Earth."
Coming Next: In the next section we replace the rectangular potential with a parabola V(x)=12mω2x2V(x) = \tfrac{1}{2}m\omega^2 x^2 and meet the quantum harmonic oscillator — the second pillar of every quantum textbook. There the eigenfunctions are Hermite polynomials times a Gaussian, the energies become evenly spaced En=ω(n+12)E_n = \hbar\omega(n+\tfrac{1}{2}), and we discover the famous 12ω\tfrac{1}{2}\hbar\omega zero-point energy of empty space.
Loading comments...