Solving Quantum Harmonic Oscillator by Time-Dependent Variational Principle
This article discusses using the Time-Dependent Variational Principle (TDVP) to solve the quantum harmonic oscillator. It provides insights into TDVP's application in quantum chemistry, based on a lab seminar homework problem.
Loading content…
Preface
Note: This article presents a solution to a problem assigned as homework in our lab's seminar, with the permission of the seminar organizer.
When solving for electronic states in quantum chemistry calculations, variational principles such as:
Machlachlan's Variational Principle
Dirac Frenkel Variational Principle
Time-Dependent Variational Principle (TDVP)
are often employed. This article provides a concrete example of calculations using TDVP.
Schrodinger Equation
The time-dependent Schrödinger equation we will solve is:
Here, the Hamiltonian is defined as:
We express the wave function using time-dependent parameters and as follows:
This Hamiltonian represents a parabolic potential centered at , and classically, the solution would be a simple harmonic oscillator.
The initial conditions are:
TDVP
In TDVP, we calculate matrix A and C from the partial derivatives of the wave function with respect to its parameters. By utilizing the following relationship, we update the parameters:
where R and I represent the real and imaginary parts of the elements, respectively.
A and C are defined as:
Since we have two parameters in this case, the explicit form is:
Calculation
Calculating the A and C matrices, we obtain:
Therefore, we have the following system of differential equations:
Solving these equations with the initial conditions given in , we obtain the solution for the wave function parameters:
Substituting these into , we get the wave function:
Verification
Let's verify that the solution obtained using TDVP, , satisfies the original Schrödinger equation except for a global phase.
We add a global phase factor to the wave function:
Substituting this into both sides of ,
and comparing the coefficients, we obtain the following conditions:
Since they holds for any , we have the following equations:
By substituting the solutions for and from , we can see that conditions and are naturally satisfied. leads to the following differential equation for the global phase :
Integrating this equation with the initial condition , we obtain the global phase:
Therefore, excluding the global phase factor, the solution satisfies the Schrödinger equation .
Visualization
The time evolution of the wave function was calculated with the initial conditions and , and the real and imaginary parts are visualized in the following animation.
The square of the probability amplitude is plotted in the following animation:
As can be seen, the probability wave oscillates around , resembling the behavior of a classical harmonic oscillator.
Appendix
The visualization was generated using the following program.
Note: The plots show the analytical solution, not the result by TDVP's time evolution.
# animation of real and imaginary partsimport numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.collections import LineCollection
x0 =0p0 =2xs = np.linspace(-5,5,100)defwf(x, t): theta_1 = x0 * np.cos(t)+ p0 * np.sin(t) theta_2 =-x0 * np.sin(t)+ p0 * np.cos(t) gamma =((x0**2+p0**2-1)*t-x0*p0*np.sin(2*t))/2 ret = np.pi**(-1/4)* np.exp(-1/2*(x - theta_1)**2+1j*theta_2*x+1j*gamma)return ret
fig, ax = plt.subplots()ax.set_xlim(-5,5)ax.set_ylim(-1,1)ln_r,= ax.plot([],[],'r-', label='real')ln_i,= ax.plot([],[],'b-', label='imag')definit(): ln_r.set_data([],[]) ln_i.set_data([],[])return ln_r, ln_i
defupdate(frame): data =[wf(x, frame)for x in xs] vals_r=[d.real for d in data] vals_i=[d.imag for d in data] ln_r.set_data(xs, vals_r) ln_i.set_data(xs, vals_i)return ln_r, ln_i
ani = FuncAnimation( fig, update, frames=np.linspace(0, np.pi*2,100, endpoint=False), init_func=init, blit=True, repeat=True)ax.legend()ani.save("wavefunc.gif", writer="pillow", fps=20)
# animation of probabilityimport numpy as np
import matplotlib.pyplot as plt
from matplotlib.animation import FuncAnimation
from matplotlib.collections import LineCollection
x0 =0p0 =2xs = np.linspace(-5,5,100)defwf(x, t): theta_1 = x0 * np.cos(t)+ p0 * np.sin(t) theta_2 =-x0 * np.sin(t)+ p0 * np.cos(t) gamma =((x0**2+p0**2-1)*t-x0*p0*np.sin(2*t))/2 ret = np.pi**(-1/4)* np.exp(-1/2*(x - theta_1)**2+1j*theta_2*x+1j*gamma)return ret
fig, ax = plt.subplots()ax.set_xlim(-5,5)ax.set_ylim(0,1)ln,= ax.plot([],[],'r-')lc = LineCollection([], cmap='twilight', linewidth=2)ax.add_collection(lc)norm = plt.Normalize(0,2* np.pi)sm = plt.cm.ScalarMappable(cmap='twilight', norm=norm)sm.set_array([])fig.colorbar(sm, ax=ax, orientation='vertical', label="Phase (rad)")definit(): ln.set_data([],[]) lc.set_segments([])return ln, lc
defupdate(frame): data =[wf(x, frame)for x in xs] amps =[np.abs(d)for d in data] phases =[np.angle(d)for d in data] ln.set_data(xs, amps) points = np.array([xs, amps]).T.reshape(-1,1,2) segments = np.concatenate([points[:-1], points[1:]], axis=1) lc.set_segments(segments) lc.set_array(phases)return ln, lc
ani = FuncAnimation( fig, update, frames=np.linspace(0, np.pi*2,100, endpoint=False), init_func=init, blit=True, repeat=True)ani.save("prob.gif", writer="pillow", fps=20)