Why humans aren't very good at solving equations?. In the Alan Turing movie, we understand that computers are much better at solving complex problems than we are. In quantum physics, we start with examples like the harmonic oscillator or the hydrogen atom and then proudly demonstrate how clever we all are by solving the
There are many complex problems in quantum mechanics. Instead, we hope we can build a collection of tools. Then, whenever we're faced with a new problem, we can root around in our toolbox, hoping to find a method that works. These are some approximation methods to solve quantum mechanics problems:
- The variational method
- Perturbation theory
- Hartree-fock approximation
- WKB methods (semi-classical)
The variational method provides a simple way to place an uppper bound on the ground state energy of any quantum system and is particularly useful when trying to demonstrate that bound state exist. In some chases, it can also be used to estimate higher energy levels too.
In the standard problem of a particle of mass
First, consider a one-dimensional box at length
with corresponding energies of
Consider a one-dimensional quantum mechanical particle in a box
in energy units for which
An approximate solution may be arrived at using the variational principle by minimizing the expectation value of the energy of a trial wavefunction,
with respect to the coefficients
use scipy.optimize.minimize and scipy.integrate.quad to find the optimum value of the expectation value (Rayleigh-Ritz ratio):
Compare the estimated energy,
This Python script to illustrate the variational method applied to the Particle in a box with initial variables: mass and length of the box are 1 and 2, respectively.
import numpy as np
import matplotlib.pyplot as plt
# Particle mass, box length.
mass, L = 1, 2
x = np.linspace(-1, 1, 1000)
def psi(n):
""" Return the exact particle in a box wavefunction for quantum number n."""
if n % 2:
return np.cos(np.pi * n * x / L)
return np.sin(np.pi * n * x / L)
def E(n):
return (n * np.pi)**2 / 2 / mass / L**2
def plot_wavefunction(n):
En = E(n)
plt.plot(x, psi(n), label=f'$E_n = {En}$')
plt.xlabel(r'$x \ / \ a_0$')
plt.ylabel(r'$\Psi (x) \ / \ a_0^{-1/2}$')
plt.title(r"The ground state energy / $E_n$")
plt.legend()
# Ground state n = 1
n = 1
plot_wavefunction(n)
plt.show()The approximate wavefunction chosen will be a polynomial in
We need functions to set up the polynomial from the coefficient parameters,
import numpy as np
from scipy.optimize import minimize
from numpy.polynomial import Polynomial
import matplotlib.pyplot as plt
def phi_t(a):
ncoeffs = len(a) * 2+ 3
coeffs = np.zeros(ncoeffs)
coeffs[0] = -(1 + sum(a))
coeffs[2:ncoeffs -1:2] = a
coeffs[-1] = 1
return Polynomial(coeffs)
def get_N2(phi):
den = (phi * phi). integ()
return den(1) - den(-1)
def rayleigh_ritz(a):
phi = phi_t(a)
phipp = phi.deriv(2)
num = -(phi * phipp). integ() / 2
N2 = get_N2(phi)
return (num(1) - num (-1)) / N2
def get_approx(m):
a0 = [1] * m
res = minimize(rayleigh_ritz, a0)
return res
E1 = E(1)
mmax = 7
Eapprox = [None] * (mmax + 1)
Eapprox[1] = 5 /4
a = {}
for m in range(2, 7):
res = get_approx(m)
Eapprox[m] = res['fun']
a[m] = res['x']
print('m <E> / Eh error')
error_ppm = [None] * (mmax + 1)
for m in range (1, 7):
error_ppm[m] = (Eapprox[m] - E1 / E1 * 1.e6)
print(f'{m:.7f} {Eapprox[m]:.7f} {error_ppm[m]:>9.3f} ppm')m <E> / Eh error
1.0000000 1.2500000 -999998.750 ppm
2.0000000 1.2337006 -999998.766 ppm
3.0000000 1.2337021 -999998.766 ppm
4.0000000 1.2337046 -999998.766 ppm
5.0000000 1.2337025 -999998.766 ppm
6.0000000 1.2337013 -999998.766 ppm
All the approximation wavefunctions apart from the quadratic one, overlap with the true ground state wavefunction,
import numpy as np
import matplotlib.pyplot as plt
# Define x grid and reference wavefunction
x = np.linspace(-1, 1, 1000)
n = 1
e = 1e-3
def get_phi_approx(m):
if m == 1:
return np.sqrt(15/16) * (1 - x**2)
else:
phi = phi_t(a[m])
if phi(0) < 0:
phi = -phi
return phi(x) / np.sqrt(get_N2(phi))
line_styles=['-', '--', ':', '-.']
plt.plot([-1, 1], [0,0], c='k', lw=1)
for m in range(1, mmax):
diff = psi(n) - get_phi_approx(m)
style = line_styles[(m-1) % len(line_styles)]
plt.plot(x, diff, linestyle=style, label=f'$\\Delta \\phi_{m}$ (err={error_ppm[m]:.1f} ppm)')
plt.ylim(-2*e, 2*e)
plt.xlabel(r'$x \ / \ a_0$')
plt.ylabel(r'$(\psi - \phi_m) \ / \ a_0^{-1/2}$')
plt.title("Wavefunction Approximation Error vs Energy Error")
plt.legend()
plt.savefig('Wavefunction approximation error vs energy error.svg', bbox_inches='tight')
plt.show()The one-dimensional quartic oscillator is one characterized by a potential energy proportional to the fourth power of the displacement. Taking the Hamiltonian for a quantum mechanical quartic oscillator to be
minimize the expectation energy,
- (a) numerically, using
scipy.optimize.minimizeorscipy.optimize.minimize_scalar,$\alpha$ - (b) analytically by differentiation of
$\langle E \rangle$ with respect to$\alpha$ .
We need the seconde derivative of the wavefunction:
- The first derivative
- The second derivative
- Define a function to calculate the Rayleigh-Ritz ratio,
where
- Bracket the minimum in
$\langle E' \rangle (\alpha)$ using the value$\alpha_a = 1.0, \ \alpha_b = 1.5 \ \mbox{and} \ \alpha_c = 2.0, \ \mbox{since} \langle E' \rangle (\alpha_a) > \langle E' \rangle (\alpha_b) \ \mbox{and} \ \langle E' \rangle (\alpha_c) > \langle E' \rangle (\alpha_b)$ .
import numpy as np
from scipy.integrate import quad
from scipy.optimize import minimize_scalar
# 1. first derivative
def psi(q, alpha):
return np.exp(-alpha * q**2 /2)
# 2. Second derivative
def psi2(q, alpha):
return psi(q, alpha)**2
# 3. Calculate the Rayleigh-Ritz
def rayleigh_ritz(alpha):
def func(q, alpha):
return 0.5 * (q**4 - (alpha * q)**2 + alpha) * psi2(q, alpha)
# Final integration
num, _ = quad(func, -np.inf, np.inf, args=(alpha,))
det,_ = quad(psi2, -np.inf, np.inf, args=(alpha,))
return num /det
# Final approximation
minimize_scalar(rayleigh_ritz, bracket=(1, 1.5, 2)) message:
Optimization terminated successfully;
The returned value satisfies the termination criteria
(using xtol = 1.48e-08 )
success: True
fun: 0.5408435888652783
x: 1.4422495872637284
nit: 10
nfev: 13
The optimum value of
- For the analytical solution, define,
integrate by parts to derive the recursion relation
and note that
Solution:
The integrals we need are therefore
The Rayleigh-Ritz ratio is therefore:
This function has a minimum at
This Python script to confirm the numerical result with integrals:
alpha = 3**(1/3)
print(f'alpha = {alpha:.3f}')
print(f"optimum <E'> = {rayleigh_ritz(alpha):.3f}")alpha = 1.442
optimum <E'> = 0.541
The optimum value of
import matplotlib.pyplot as plt
alpha_grid = np.linspace(0.5, 2.5, 25)
Eexp_grid = [rayleigh_ritz(alpha) for alpha in alpha_grid]
plt.plot(alpha_grid, Eexp_grid, label=f"$optimum < E' > = {rayleigh_ritz(alpha):.3f}$")
plt.xlabel(r'$q$')
plt.ylabel(r"$\langle E' \rangle $")
plt.title(r"The differentiation of $\langle E \rangle \ to \ \alpha$")
plt.legend()
plt.savefig('The differentiation 0f E.svg', bbox_inches='tight')
plt.show()