This post is a comprehensive complement of the Quantum Physics paperMatrix Numerov method for solving Schrödinger’s equation, which present both an intellectual interest and an important historical root of underneath OpenML
PrerequisitesGeneral mastery of Fundamental Calculus and Differential Equations are assumed. We recommend the following texts:
- Calculus With Analytic Geometry, George Simmons
- Differential Equations and Boundary Value Problems, Edwards, Penney, & Calvis
The Numerov method is a specialized integration formula for numerically integrating differential equations of the form
It’s an ordinary differential equations of second order in which the first-order term does not appear
The Numerov method was developed by the Russian astronomer and geophysicist Boris Vasilievich Numerov (1891–1941) who originally developed the method in the 1920s to solve problems in celestial mechanics, specifically for calculating the orbits of celestial bodies (integration of perturbations). He published the method in paper “A method of extrapolation of perturbations”, in which Numerov originally applied the algorithm to celestial mechanics for calculating the orbits of astronomical bodies, which involves solving second-order differential equations similar in form to the Schrödinger equation (where the first derivative term is absent).

Boris Numerov was a prominent scientist who served as the director of the Central Astronomical Observatory of Geophysics. Tragically, he was arrested in 1936 during the Stalinist purges and executed in 1941 🕯️. The method remains his most enduring legacy in computational physics 🌺.
TIPWhile the paper applies the method to the Schrödinger equation (a problem in quantum mechanics), Numerov designed it to solve the same type of differential equation found in astronomy: second-order equations where the first-derivative term is missing ()
In his 1924 original paper, B. V. Numerov assumes the reader is already familiar with the standard equations of celestial mechanics (which look like ) and refers to it in the very first sentence: “numerical integration of the equations of perturbed motion”.
The “equations of perturbed motion” that Numerov refers to in the 1924 paper are mathematically identical in structure to the Schrödinger equation. This shared structure is exactly why the Numerov method works for both astronomy and quantum mechanics.
Both problems involve a second-order linear differential equation where the first-derivative term is missing.
| Component | Celestial Mechanics (Numerov, 1924) | Quantum Mechanics (Schrödinger) |
|---|---|---|
| Independent Variable | Time () | Position () |
| Dependent Variable | Planet Coordinate () | Wavefunction () |
| Function | Gravity Factor | Scaled Potential |
| Equation |
In the table above, is the radius vector which changes with time, making the gravity factor a function of , just as the potential is a function of .
WARNINGFor the context of Schrödinger’s equation only, the Matrix Numerov method works only for time-independent 1D Schrödinger’s equation of the form
The standard result known as the Numerov’s method (or Numerov scheme) is exactly what we see presented as equation (2) from the paper:

It is a fourth-order linear multistep method used to solve second-order differential equations of the form where the first-derivative term is absent. The derivation relies on the Taylor expansion of the function and the specific differential equation given in equation
Deriving Equation (2)
The paper proceeds with the result of the equation (2) as shown below:

We patch the missing derivation of this equation starting with Taylor Expansion
Step 1: Taylor Expansion
Taylor ExpansionLet be a function that is infinitely differentiable at a real number . The Taylor series of centered at is the power series:
where:
- denotes the factorial of .
- denotes the -th derivative of evaluated at the point .
- The zeroth derivative is defined as the function itself, and .
In the context of numerical analysis, we often use Taylor’s Theorem, which states that for a function with derivatives on an interval containing and , the function can be approximated by a polynomial of degree (the -th order Taylor polynomial) plus a remainder term :
The remainder term, often expressed in Peano form as where , represents the error of the approximation and is commonly denoted using Big O notation as .
To derive the equation used in the Numerov method paper, we apply the Taylor series definition to the wave function . Let
CAUTIONWe assume is smooth and differentiable.
Then we expand around the current grid point, so let . We want to find the value at the next grid point, so let . The difference term , therefore, becomes:
Substituting these values into the formal definition up to the 4th order () gives us:
Now we expand around a point to find the values at the neighboring points and .
When we sum these two expansions, the odd-power terms (first and third derivatives) cancel out:
Step 2: Substitution of the Differential Equation
We use the differential equation provided in the paper, . This allows us to replace the second derivative with :
We still have the fourth derivative to deal with. We can find an expression for it by differentiating the original equation twice:
Step 3: Discretizing the Fourth Derivative
Instead of calculating analytically (which would be complex), we approximate it using the same central difference formula we essentially derived in Step 1. The second derivative of any function can be approximated as with an error of .
Applying this to :
Substituting this approximation back into our sum equation:
Simplifying the term:
Step 4: Rearrangement to Equation (2)
Now we group the terms by their indices (, , ).
Group terms:
Group terms:
Group terms:
The equation becomes:
Multiplying the entire equation by 12 to remove denominators:
Solving for :
Finally, divide by . To exactly match the paper’s notation in equation (2), we multiply the numerator and denominator of the fraction by (flipping the signs):
This matches equation (2) in the paper.
Matrix Numerov Simulator
Here is a Python implementation of the Matrix Numerov method to solve the dimensionless Schrödinger equation for the Simple Harmonic Oscillator (SHO) with the potential of (Harmonic Oscillator in natural units ). The expected energy (i.e., 0.5, 1.5, 2.5, …)
Explanation of the Implementation:
-
Matrix Construction:
- Matrix A represents the second derivative operator . In the finite difference method, this leads to the familiar pattern on the diagonals.
- Matrix B is the unique feature of the Numerov method. It modifies the kinetic energy term to account for fourth-order error terms ( locally), making it significantly more accurate than standard finite difference methods which are usually only second-order accurate.
- Matrix V is simply the potential energy evaluated at each grid point on the diagonal.
-
The Hamiltonian: The code solves equation (5) from the paper: .
- We inverted explicitly using
np.linalg.inv. For 1D problems where is small (hundreds or thousands), this is computationally cheap and easy to read.
- We inverted explicitly using
-
Boundary Conditions: By using a fixed grid size from to and not connecting the ends, we implicitly enforce “hard wall” boundary conditions ( outside the grid). This is why choosing a sufficiently large (like for low energy states) is important—the wavefunction must naturally decay to zero before hitting the edge.
import numpy as npimport matplotlib.pyplot as plt
def solve_sho_matrix_numerov(N=200, L=6.0): """ Solves the 1D Simple Harmonic Oscillator using the Matrix Numerov method.
Args: N (int): Number of grid points. L (float): Spatial boundary +/- L.
Returns: x (ndarray): The spatial grid. eigenvalues (ndarray): Calculated energy levels. eigenvectors (ndarray): Normalized wave functions (columns). """ # 1. Define the Grid # We use a linspace from -L to L x = np.linspace(-L, L, N) d = x[1] - x[0] # Grid spacing
# 2. Construct the Matrix A (Second Derivative Approximation) # A = (I_{i-1} - 2I_i + I_{i+1}) / d^2 # Diagonals: 1, -2, 1 main_diag_A = -2.0 * np.ones(N) off_diag_A = np.ones(N - 1) A = (np.diag(main_diag_A) + np.diag(off_diag_A, k=1) + np.diag(off_diag_A, k=-1)) / d**2
# 3. Construct the Matrix B (Numerov Consistency Matrix) # B = (I_{i-1} + 10I_i + I_{i+1}) / 12 # Diagonals: 1, 10, 1 main_diag_B = 10.0 * np.ones(N) off_diag_B = np.ones(N - 1) B = (np.diag(main_diag_B) + np.diag(off_diag_B, k=1) + np.diag(off_diag_B, k=-1)) / 12.0
# 4. Construct the Potential Matrix V # V is diagonal with V_ii = 0.5 * x_i^2 V_diagonal = 0.5 * x**2 V = np.diag(V_diagonal)
# 5. Construct the Hamiltonian Matrix # Form: H = - (1/2) * B^(-1) * A + V # Note: We assume h_bar = 1, mass = 1. # We use np.linalg.inv for B. Since B is strictly diagonally dominant, it is well-conditioned. B_inv = np.linalg.inv(B) Kinetic = -0.5 * np.dot(B_inv, A) H = Kinetic + V
# 6. Solve the Eigenvalue Problem # Since H is not strictly symmetric in this representation (B^-1 * A is not necessarily symmetric), # we use the general eigensolver `eig` rather than `eigh`. # However, physically, the eigenvalues must be real. eigenvalues, eigenvectors = np.linalg.eig(H)
# 7. Sort and Clean Results # Sort by real part of eigenvalues idx = eigenvalues.argsort() eigenvalues = eigenvalues[idx] eigenvectors = eigenvectors[:, idx]
# Normalize wavefunctions (Simple sum of squares approx) # integral |psi|^2 dx = 1 => sum |psi|^2 * d = 1 for i in range(N): norm = np.sqrt(np.sum(eigenvectors[:, i]**2) * d) eigenvectors[:, i] /= norm
# Enforce consistent phase (make max amplitude positive for plotting consistency) for i in range(N): if np.max(eigenvectors[:, i]) < abs(np.min(eigenvectors[:, i])): eigenvectors[:, i] *= -1
return x, eigenvalues.real, eigenvectors.real
def plot_results(x, eigenvalues, eigenvectors, num_states=4): """Plots the potential and the first few wave functions offset by their energies.""" plt.figure(figsize=(10, 7))
# Plot Potential V(x) plt.plot(x, 0.5 * x**2, 'k--', linewidth=1.5, label=r'$V(x) = \frac{1}{2}x^2$')
# Plot Wavefunctions # We scale the amplitude for visibility: Energy + scale * psi scale_factor = 0.8 colors = ['#d62728', '#1f77b4', '#ff7f0e', '#2ca02c'] # Red, Blue, Orange, Green
for n in range(num_states): E = eigenvalues[n] psi = eigenvectors[:, n]
# Determine label with calculated energy label = f'$n={n}, E_{{calc}}={E:.4f}$'
# Plot the wavefunction baseline at E plt.axhline(E, color='gray', linestyle=':', alpha=0.5) plt.plot(x, E + scale_factor * psi, color=colors[n % len(colors)], label=label, linewidth=2)
plt.title('Schrödinger Solutions: Harmonic Oscillator via Matrix Numerov') plt.xlabel('Position $x$') plt.ylabel('Energy (and $\psi(x)$ amplitude)') plt.ylim(0, eigenvalues[num_states-1] + 1.5) plt.legend(loc='lower right') plt.grid(True, alpha=0.3) plt.tight_layout() plt.show()
if __name__ == "__main__": # Parameters # N=300 provides high accuracy for the first few states x_grid, energies, wavefuncs = solve_sho_matrix_numerov(N=300, L=6.0)
# Print accuracy check print(f"{'State':<10} | {'Calc. Energy':<15} | {'Exact Energy':<15} | {'Error':<15}") print("-" * 65) for n in range(5): exact = n + 0.5 error = abs(energies[n] - exact) print(f"n={n:<8} | {energies[n]:<15.6f} | {exact:<15.6f} | {error:.2e}")
plot_results(x_grid, energies, wavefuncs)Simply running the code above with python3 solver.py would give us the following eigenstates printed in terminal:
tate | Calc. Energy | Exact Energy | Error-----------------------------------------------------------------n=0 | 0.500000 | 0.500000 | 1.01e-08n=1 | 1.500000 | 1.500000 | 7.10e-08n=2 | 2.500000 | 2.500000 | 2.53e-07n=3 | 3.499999 | 3.500000 | 6.39e-07n=4 | 4.499999 | 4.500000 | 1.31e-06and the following wave functions:

One can visit matrix-numerov.openml.io for an interactive version:
(To be continued…)

