Learning Objectives
By the end of this section, you will be able to:
- Explain why the vast majority of Schrödinger problems must be solved numerically, and why "exact" hydrogen is the rare exception.
- Discretise the kinetic-energy operator by a 3-point finite-difference stencil and write the Hamiltonian as a tridiagonal matrix.
- Convert a continuous eigenvalue problem into a matrix eigenproblem you can solve with one library call.
- Apply the shooting method and Numerov's higher-order integrator to find bound states one at a time.
- Evolve a wave packet in time using the split-operator (FFT) method, and use it to watch tunnelling happen.
- Estimate the convergence rate of each method and pick the right tool for a given physics problem.
- Implement a finite-difference solver from scratch in NumPy and a batched GPU version in PyTorch.
Why We Need Numerical Methods
"The exactly solvable problems are a measure-zero subset of all the problems you actually want to solve." — every computational physicist, ever
The Schrödinger equation looks simple. It is a single linear PDE: . Yet of all the atoms, molecules, semiconductors, and quantum dots in the universe, only a handful can be solved with pencil and paper. The list is essentially: free particle, infinite square well, finite square well (transcendentally), the harmonic oscillator, the hydrogen atom, the delta-function potential, and a few exotic toys (Pöschl–Teller, Morse, ...). Everything else — including the helium atom with its measly two electrons — needs a computer.
The intuition in one sentence
This is more than a workaround. The numerical viewpoint is more general than the analytical one: you can change the potential to anything you want, change the boundary conditions, add time dependence, couple multiple particles — the code structure stays nearly identical. Once you understand the recipe, the variety of physics you can simulate explodes.
The three problems we will solve
| Problem | What we want | Best tool |
|---|---|---|
| Bound states of a 1D potential | Energies E_n and stationary states ψ_n(x) | Finite-difference + dense eigensolve |
| A single bound state at a known approximate energy | Refine E to high precision; localised eigenfunction | Shooting method (Numerov) |
| How a wave packet evolves in time | ψ(x, t) for t > 0 from a given ψ(x, 0) | Split-operator method (FFT) |
The Recipe: Turn an Operator Into a Matrix
Take a moment to look at the time-independent Schrödinger equation in one dimension:
For the rest of this section we use the standard atomic units , which absorbs all the physical constants into the choice of length and energy scales. The equation simplifies to:
Think of the left-hand side as an operator acting on the function ψ:
Question: if ψ is a function on a continuous line, how do we make a computer understand it?
Answer: pretend you only care about ψ at a finite list of points . The wavefunction is now a column vector of N numbers, and becomes an matrix . The eigenvalue problem becomes:
That is just a matrix eigenvalue problem. We already have one-line library calls for it: numpy.linalg.eigh, torch.linalg.eigh, scipy.sparse.linalg.eigsh. The only question left is: what does the matrix look like?
Finite Differences for the Second Derivative
The Schrödinger equation has a single hard piece: the second derivative . Everything else (V, E, ψ itself) is pointwise. So if we can write on a grid, we are done.
Take three neighbouring points separated by spacing . Taylor-expand ψ at the two outer points around :
Add them. The odd-order terms cancel:
Solve for :
This is the famous 3-point central difference stencil. It says: to find the curvature at point i, compare the average of your neighbours to your own value, and divide by . The error is — second-order accurate. Cut in half, and the error drops by a factor of four.
Now plug this into the kinetic-energy operator :
Read off the matrix entries: on row i, the diagonal entry is , the two off-diagonal entries are , and the rest of the row is zero.
The Finite-Difference Eigenproblem
Add the potential and the entire Hamiltonian appears in front of us:
This is a real, symmetric, tridiagonal matrix. Its eigenvalues are the approximate bound-state energies; its eigenvectors are the approximate bound-state wavefunctions sampled at the grid points.
Let's look at three rows of for , , and a harmonic potential on , so . Then and :
| i | x_i | V(x_i) | H_{i,i-1} | H_{i,i} | H_{i,i+1} |
|---|---|---|---|---|---|
| 1 | −0.4 | 0.080 | — | 25.080 | −12.5 |
| 2 | −0.2 | 0.020 | −12.5 | 25.020 | −12.5 |
| 3 | 0.0 | 0.000 | −12.5 | 25.000 | −12.5 |
| 4 | 0.2 | 0.020 | −12.5 | 25.020 | −12.5 |
| 5 | 0.4 | 0.080 | −12.5 | 25.080 | — |
This 5×5 matrix is enough that you could diagonalise it by hand with characteristic polynomials. With a computer we push N up to a few hundred or a few thousand and the answers converge to machine precision.
Interactive: Build the Hamiltonian Yourself
Pick a potential. Move the slider for the shape parameter. Drag N to change the grid resolution. The viz builds , diagonalises it in your browser, and shows the lowest six eigenfunctions stacked on their energy levels — exactly the figure you would get from the Python solver below. The right-hand table compares the numerical energies to the analytic ones (when available) so you can see the convergence as you slide N up.
Things to try
- On the harmonic potential, start with N = 20 and watch the error in E₀ shrink as you move toward N = 200. Doubling N really does cut the error by ~4×.
- Switch to double well and decrease the parameter a. The two lowest eigenvalues collapse toward each other — you are seeing the tunnel-doublet form in real time.
- On the finite well, count the bound states (energies below zero). Make the well shallower (smaller V₀) and bound states disappear from the top of the well one by one — exactly the transcendental-equation story from §29.4.
The Shooting Method
The finite-difference matrix approach finds all bound states at once. That is wasteful if you only care about one — say, the ground state to ten digits of accuracy. The shooting method is the surgical alternative.
The idea is delightfully physical. Treat the time-independent Schrödinger equation as an initial-value problem in x:
Choose a trial energy E. Start at the left wall with and (any small number — the equation is linear, so the overall scale is irrelevant). Integrate ψ rightward step by step using your favourite ODE method. Look at the value at the right wall.
- If , E is an eigenvalue: you found a bound state.
- If blows up positive, E is too low.
- If blows up negative, E is too high.
The sign change of as you scan E across a true eigenvalue is exactly what makes this method work. Wrap a one-dimensional root finder (bisection, Brent) around it and you get bound-state energies to arbitrary precision.
Numerov: A Smarter Integrator
For an equation of the form there is a beautiful 4th-order method that costs the same as plain finite differences: Numerov's method. Define . The recurrence is
with . The local truncation error is ; the global error is — two orders better than the simple 3-point stencil, for the same number of arithmetic operations per step.
Where does the magic come from? Numerov absorbs the leading term in the Taylor expansion by cleverly distributing the function values over three points. Read this as: the second-derivative formula has a built-in correction that exploits the particular structure — it would not work for an arbitrary right-hand side, but our Schrödinger ODE is precisely of this form.
Interactive: Shoot the Harmonic Oscillator
Set the trial energy with the slider. The orange curve is the result of integrating ψ from the left wall using Numerov. Whenever E is far from an eigenvalue, the wavefunction explodes near the right wall. Walk E slowly toward 0.5, 1.5, 2.5, 3.5, … and watch the curve collapse back to zero — those are the bound states .
Why is the slider so sensitive?
Time Evolution: The Split-Operator Method
The time-dependent Schrödinger equation
has the formal solution . The matrix exponential of an N×N matrix is expensive — but the kinetic and potential parts of have a beautiful property: each is diagonal in its natural basis.
- The potential operator is diagonal in position space.
- The kinetic operator is diagonal in momentum space, with eigenvalue .
And the Fast Fourier Transform shuffles between the two bases in time. That suggests:
This is the Strang splitting form of the split-operator method. The error per step is ; the global error is . The scheme is unitary by construction (every multiplied factor is a complex phase of magnitude 1), so it conserves the total probability to floating-point precision indefinitely. That is enormous — naive Euler schemes on the Schrödinger equation are catastrophically wrong because they slowly leak probability and eventually make ψ explode.
The intuition: each step is three rotations
Interactive: Watch a Wave Packet Hit a Barrier
A Gaussian wave packet is launched from the left with mean momentum . The energy is approximately . Move the barrier height above E and watch most of the packet bounce back — but a thin transmitted tail leaks through the wall. That tail is quantum tunnelling, visible because we did the time evolution properly.
Things to try
- Free potential: a Gaussian packet just translates and slowly spreads — the dispersion of a quantum free particle.
- Barrier with : classical particles reflect with probability 1, but the green |ψ|² shows a small transmitted bump on the far side.
- Harmonic: an off-centre Gaussian oscillates back and forth in the trap forever — that is a coherent state, the classical-like motion you met in §29.5.
Convergence, Error, and Choosing a Method
Every numerical method has a budget: dx (or dt), N, polynomial order, boundary placement. The art is matching the method to the physics.
| Method | Accuracy | Cost | Best for |
|---|---|---|---|
| 3-pt finite difference + dense eigh | O(dx²) | O(N³) to diagonalise | Bound states of arbitrary V(x), small N (≤ 2000) |
| 5-pt or 7-pt FD + sparse eigsh | O(dx⁴) or O(dx⁶) | O(k · N) per Lanczos step | Large N, only a few eigenstates needed |
| Numerov + shooting + Brent root-find | O(dx⁴) per integration | ~ N · (# bisections) per eigenvalue | High-precision single eigenvalue |
| Split-operator (Strang) + FFT | O(dt²) | O(N log N) per step | Time evolution; preserves unitarity |
| Spectral (Hermite / Chebyshev) | Exponential in N | O(N²) per eigensolve | Smooth potentials, very high accuracy |
For most non-pathological 1D bound-state problems, the recipe is: start with finite differences. Once it works, refine N until the answer stops moving. If you need higher accuracy or larger N, upgrade to a higher-order stencil or a spectral basis. If you need time evolution, reach for split-operator.
Worked Example: Particle in a Box by Hand
Let's walk through one full finite-difference calculation by hand, using the simplest non-trivial potential: the infinite square well of width , with V = 0 inside and ψ = 0 at both walls. Analytic energies: for , i.e. 0.5, 2.0, 4.5, 8.0, ….
📐 Click to expand — hand calculation with N = 4
Use four interior grid points on . The spacing is , so and .
The Hamiltonian (V = 0 inside) is the 4×4 matrix:
H ≈ [ 2.533 -1.267 0 0 ]
[ -1.267 2.533 -1.267 0 ]
[ 0 -1.267 2.533 -1.267 ]
[ 0 0 -1.267 2.533 ]For a tridiagonal Toeplitz matrix with diagonal and off-diagonal there is a closed-form spectrum:
With , , :
| k | cos(kπ/5) | λ_k = 2.533 + 2(−1.267)·cos | analytic E = k²/2 | error |
|---|---|---|---|---|
| 1 | 0.8090 | 2.533 − 2.050 = 0.483 | 0.500 | 0.017 |
| 2 | 0.3090 | 2.533 − 0.783 = 1.750 | 2.000 | 0.250 |
| 3 | −0.3090 | 2.533 + 0.783 = 3.316 | 4.500 | 1.184 |
| 4 | −0.8090 | 2.533 + 2.050 = 4.583 | 8.000 | 3.417 |
With only 4 grid points the lowest eigenvalue is already within 3% of the truth (0.483 vs 0.500). The higher modes are awful because their wavelength is comparable to dx — a wave with three or four nodes simply cannot live faithfully on a 4-point grid. This is aliasing, exactly the same phenomenon as in digital signal processing.
Repeat with N = 40: dx = π/41 ≈ 0.0766, 1/dx² ≈ 170.5, −1/(2 dx²) ≈ −85.2. Plug into the same closed-form spectrum with k = 1, 2, 3, 4 and you get:
| k | λ_k (N = 40) | analytic | error |
|---|---|---|---|
| 1 | 0.49975 | 0.50000 | 2.5 × 10⁻⁴ |
| 2 | 1.99602 | 2.00000 | 4.0 × 10⁻³ |
| 3 | 4.47980 | 4.50000 | 2.0 × 10⁻² |
| 4 | 7.93722 | 8.00000 | 6.3 × 10⁻² |
From N = 4 to N = 40 the error on dropped from 1.7 × 10⁻² to 2.5 × 10⁻⁴ — a factor of ≈ 68, very close to the (N₂/N₁)² = 100 you would predict from the error scaling (the discrepancy is because dx scales as 1/(N+1), not 1/N). The convergence is real, predictable, and exactly what the interactive viz above lets you watch live.
Python: Tiny Finite-Difference Solver
Here is a clean, complete NumPy implementation of the recipe. It is about 30 lines of code and reproduces every plot in §§29.4–29.5 to four-digit precision. Read it once, then read the per-line annotations for every subtlety.
PyTorch: Batched Eigensolves on the GPU
Real physics problems — varying a parameter, sweeping a barrier height, training a neural surrogate — often need hundreds of Schrödinger solves. NumPy does them one at a time; PyTorch does them all in parallel on a GPU. The code structure is essentially the same. What changes is the leading batch dimension and the device.
From physics to ML
Where This Is Used
| Field | What is solved numerically | Why exact solutions fail |
|---|---|---|
| Quantum chemistry | Electronic structure of molecules (Hartree–Fock, DFT, post-HF) | Many-electron Coulomb correlation has no closed form |
| Solid-state physics | Band structure, defect states, quantum dots | Periodic potentials are smooth but not analytically tractable |
| Cold-atom experiments | Bose-Einstein condensates in shaped optical traps | Trap potentials are designed in software, not given by formulas |
| Nuclear physics | Bound nucleon orbitals in nuclei | Nuclear potential is empirical; no analytic eigenfunctions |
| Photonics & quantum optics | Modes of curved waveguides and microresonators | Helmholtz / paraxial equations are Schrödinger-like with arbitrary V |
| Machine learning | Generating training data for neural force fields and surrogates | Need thousands of solves at different geometries — only numerics scale |
Every commercial quantum-chemistry package — Gaussian, ORCA, NWChem, Psi4, VASP — at its core does some flavour of the recipe in this section: discretise the Schrödinger operator, build a (huge, sparse, often non-Hermitian) matrix, and diagonalise. The methods get fancier (Gaussian basis sets, plane waves, multi-reference perturbation theory), but the spirit is the same: turn an operator into a matrix and let linear algebra do the rest.
Summary
- The Schrödinger equation is solvable in closed form only in a handful of exceptional cases. Every interesting real-world problem requires numerics.
- The universal recipe is: sample ψ on a grid → approximate the second derivative by finite differences → build a sparse symmetric matrix H → diagonalise. Eigenvalues are energies, eigenvectors are wavefunctions.
- The 3-point central stencil is accurate. Doubling N divides the error by ≈ 4.
- The shooting method turns the eigenproblem into a 1D root-finding problem on the boundary residue . Pair it with Numerov for accuracy at the same cost as the basic FD method.
- For time evolution, use the split-operator method: alternate phase rotations in position and momentum space, glued together by FFTs. The scheme is unitary and conserves probability exactly.
- PyTorch's batched eigensolver lets you do hundreds of Schrödinger problems in parallel on a GPU — the bridge from computational physics to quantum machine learning.
The deepest lesson of this section is not any specific algorithm. It is the realisation that continuous operators are matrices in disguise. Once you internalise that, you stop being afraid of differential equations: every PDE in physics, from Maxwell's to Navier–Stokes, surrenders to the same recipe.