前述のブログに示した自由波束の時間発展の様子をシミュレーションして見た.Bernd Thaller : 「Visual Quantum Mechanics」と「Advanced Visual Quantum Mechanics」を参照して, その python プログラムを書く手順とその結果の動画を示しておく.
まずはシミュレーションプログラムを書くための予備知識を確認しておく.
Schrödinger 理論による時間発展
自由粒子のシュレディンガー方程式は,
この平面波解は,
ただし,
ただし
一般解
従って
- 自由粒子の一般解を求める手順は次となる:
- 初期関数
のフーリエ変換 を求める. の逆フーリエ変換を求める.
具体例として初期関数として, 任意定数
(第1ステップ) : この関数のフーリエ変換は次で与えられる:
このとき, 関数
(第2ステップ) : 初期値問題の時間
最後の積分中の指数関数は 次に書くことが出来る:
従って
このとき,
よって
このとき, この指数関数の実部は 次のように書くことが出来る:
従って, 時間
ガウス関数
Dirac 理論による自由粒子波束の時間発展
時間依存する自由粒子の Dirac 方程式は, 前述の Schrödinger 理論と全く同じ仕方で解くことが出来る.まず, 二乗可積の初期スピノルとして 例えば,
のフーリエ変換
次に, この列ベクトルを基礎スピノル
ここで展開係数
運動量空間に於ける自由粒子の Dirac 演算子
で与えられ, その固有値は次である:
すると
従って, 正の固有値
次のステップは, 式 (18) の
最後に, この
ただし, 最後の段階の
この式 (27) によって, 初期状態がガウス波束
複素数値関数の可視化
ここでは複素数を可視化するのに「位相」をカラーコードで表す方法を用いる.それは, 位置
シミュレーションプログラムとその結果
最初に Schrödinger 理論による時間発展のシミュレーションプログラムとその結果を示す.以下の python プログラムは全て, 今流行の生成 AI に書いてもらった.
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from matplotlib import cm
# Define the function Psi
def Psi(x, t, a, p):
return (1 / np.sqrt(1 + 1j * a * t)) * np.exp(-(a * x**2 - 2 * 1j * x * p + 1j * p**2 * t) / (2 * (1 + 1j * a * t)))
# Define the ColorFunction
def color_function(x, t, a, p):
return cm.hsv(np.mod(np.angle(Psi(x, t, a, p)), 2 * np.pi) / (2 * np.pi))
# Set up the figure and axis
fig, ax = plt.subplots(figsize=(8, 6))
x = np.linspace(-10, 30, 400)
a = 1/16
p = 3/4
t_values = np.linspace(0, 50, 300) # Time values for animation
# Initialize plot elements
line, = ax.plot(x, np.abs(Psi(x, 0, a, p)), lw=2) # Initialize with t=0 data
ax.set_xlim([-10, 30])
ax.set_ylim([0, 1.05])
ax.set_xlabel(r'$x$')
ax.set_ylabel(r'$|\psi(x)|$')
plt.title("Time Evolution of " r'$|\psi(x, t)|$')
# Initial fill with colors
fill_collections = []
for i in range(len(x) - 1):
x_fill = [x[i], x[i+1]]
y_fill = [np.abs(Psi(x[i], 0, a, p)), np.abs(Psi(x[i+1], 0, a, p))]
fill = ax.fill_between(x_fill, y_fill, 0, color=color_function(x[i], 0, a, p), alpha=0.7)
fill_collections.append(fill)
def update(frame):
t = t_values[frame]
y = np.abs(Psi(x, t, a, p))
line.set_data(x, y)
# Remove previous fills
for fill in fill_collections:
fill.remove()
fill_collections.clear()
# Update fill colors
for i in range(len(x) - 1):
x_fill = [x[i], x[i+1]]
y_fill = [y[i], y[i+1]]
fill = ax.fill_between(x_fill, y_fill, 0, color=color_function(x[i], t, a, p), alpha=0.7)
fill_collections.append(fill)
return line,
# Create animation
ani = animation.FuncAnimation(fig, update, frames=len(t_values), blit=False, interval=50)
# Save animation as GIF
ani.save("Psi_wave3.gif", writer="pillow", fps=20)
plt.show()
出力されたシミュレーション動画は次である:
このグラフの色と偏角との関係は, 次のカラーマップによって配色されている:
この図もやはり生成AIの助けを借りて書いたものである.上記本のカラーページに載っていた図を生成AIに与え,「これと同じグラフを書いて」とお願いするだけであった.
次は同様なガウス波束を, 今度は Dirac 理論によってシミュレーションしたプログラムを示す:
import matplotlib
matplotlib.use('TkAgg')
import numpy as np
import scipy.integrate as integrate
import matplotlib.pyplot as plt
import matplotlib.animation as animation
def La(k):
return np.sqrt(k**2 + 1)
def dp(k):
return np.sqrt(1 + 1/La(k)) / np.sqrt(2)
def dm(k):
return np.sign(k) * np.sqrt(1 - 1/La(k)) / np.sqrt(2)
def b(k):
return (2/np.pi)**(1/4) * np.exp(-4 * (k-3/4)**2) * (dp(k) + dm(k))
def d(k):
return (2/np.pi)**(1/4) * np.exp(-4 * (k-3/4)**2) * (dp(k) - dm(k))
def up1(k, x, t):
return dp(k) / np.sqrt(2) * np.exp(1j * k * x - 1j * La(k) * t)
def up2(k, x, t):
return dm(k) / np.sqrt(2) * np.exp(1j * k * x - 1j * La(k) * t)
def um1(k, x, t):
return -dm(k) / np.sqrt(2) * np.exp(1j * k * x + 1j * La(k) * t)
def um2(k, x, t):
return dp(k) / np.sqrt(2) * np.exp(1j * k * x + 1j * La(k) * t)
def psi1(x, t):
def integrand_real(k):
return np.real(b(k) * up1(k, x, t) + d(k) * um1(k, x, t))
def integrand_imag(k):
return np.imag(b(k) * up1(k, x, t) + d(k) * um1(k, x, t))
result_real, _ = integrate.quad(integrand_real, -np.inf, np.inf, epsabs=1e-3, epsrel=1e-3)
result_imag, _ = integrate.quad(integrand_imag, -np.inf, np.inf, epsabs=1e-3, epsrel=1e-3)
return result_real + 1j * result_imag
def psi2(x, t):
def integrand_real(k):
return np.real(b(k) * up2(k, x, t) + d(k) * um2(k, x, t))
def integrand_imag(k):
return np.imag(b(k) * up2(k, x, t) + d(k) * um2(k, x, t))
result_real, _ = integrate.quad(integrand_real, -np.inf, np.inf, epsabs=1e-3, epsrel=1e-3)
result_imag, _ = integrate.quad(integrand_imag, -np.inf, np.inf, epsabs=1e-3, epsrel=1e-3)
return result_real + 1j * result_imag
def rho(x, t):
return np.abs(psi1(x, t))**2 + np.abs(psi2(x, t))**2
x_values = np.linspace(-20, 20, 100)
times = np.linspace(0, 30, 300)
fig, ax = plt.subplots()
ax.set_xlim(-20, 20)
ax.set_ylim(-0.5, 1)
ax.grid()
ax.set_xlabel(r'$x$')
ax.set_ylabel(r'$\rho(x)$')
ax.set_title("Time evolution of a Gaussian wave packet with positive momentum")
fill = ax.fill_between(x_values, np.zeros_like(x_values), np.zeros_like(x_values), color='blue', alpha=0.5)
line1, = ax.plot([], [], color='red', lw=2) # Line for y_psi1
line2, = ax.plot([], [], color='green', lw=2) # Line for y_psi2
def animate(t):
y_rho = [rho(x, t) for x in x_values]
y_psi1 = [np.abs(psi1(x, t))**2 for x in x_values]
y_psi2 = [-1*np.abs(psi2(x, t))**2 for x in x_values]
global fill
fill.remove()
fill = ax.fill_between(x_values, 0, y_rho, color='blue', alpha=0.5)
line1.set_data(x_values, y_psi1)
line2.set_data(x_values, y_psi2)
return fill, line1, line2
ani = animation.FuncAnimation(fig, animate, frames=times, interval=20, blit=False, repeat=False)
ani.save("wave_packet_momentum.gif", writer="pillow") # gif動画として保存する.
#plt.show() # 表示もさせたいならコメントアウトのマークを外す.
print("Program is finished!")
この場合, 数値積分の計算などを何回も行うので動画作成に時間がだいぶ掛かってしまう(非力のMac miniでは30分前後掛かった).
そのため cython を導入して高速化してみた.以下は生成AIの指示である:
【準備】
(1) まず Cython用の .pyx ファイル: wave_packet.pyx (Cythonコード)を作成します:
#
# wave_packet.pyx
#
# distutils: language = c
# cython: boundscheck=False, wraparound=False, nonecheck=False, cdivision=True
import numpy as np
cimport numpy as cnp
from scipy.integrate import quad
cimport cython
from libc.math cimport sqrt, exp, sin, cos
cdef double La(double k):
return sqrt(k * k + 1)
cdef double dp(double k):
return sqrt(1 + 1/La(k)) / sqrt(2)
cdef double dm(double k):
return (1 if k >= 0 else -1) * sqrt(1 - 1/La(k)) / sqrt(2)
cdef double complex up1(double k, double x, double t):
cdef double complex phase = cos(k*x - La(k)*t) + 1j*sin(k*x - La(k)*t)
return dp(k) / sqrt(2) * phase
cdef double complex up2(double k, double x, double t):
cdef double complex phase = cos(k*x - La(k)*t) + 1j*sin(k*x - La(k)*t)
return dm(k) / sqrt(2) * phase
cdef double complex um1(double k, double x, double t):
cdef double complex phase = cos(k*x + La(k)*t) + 1j*sin(k*x + La(k)*t)
return -dm(k) / sqrt(2) * phase
cdef double complex um2(double k, double x, double t):
cdef double complex phase = cos(k*x + La(k)*t) + 1j*sin(k*x + La(k)*t)
return dp(k) / sqrt(2) * phase
cdef double complex b(double k):
""" Computes the function b(k) """
cdef double norm = (2/3.141592653589793)**0.25
return norm * exp(-4 * (k - 0.75)**2) * (dp(k) + dm(k))
cdef double complex d(double k):
""" Computes the function d(k) """
cdef double norm = (2/3.141592653589793)**0.25
return norm * exp(-4 * (k - 0.75)**2) * (dp(k) - dm(k))
@cython.boundscheck(False)
@cython.wraparound(False)
def psi1(double x, double t):
""" Compute psi1 using numerical integration """
cdef double result_real, result_imag
result_real, _ = quad(lambda k: (b(k) * up1(k, x, t) + d(k) * um1(k, x, t)).real, -10, 10, epsabs=1e-3, epsrel=1e-3)
result_imag, _ = quad(lambda k: (b(k) * up1(k, x, t) + d(k) * um1(k, x, t)).imag, -10, 10, epsabs=1e-3, epsrel=1e-3)
return result_real + 1j * result_imag
@cython.boundscheck(False)
@cython.wraparound(False)
def psi2(double x, double t):
""" Compute psi2 using numerical integration """
cdef double result_real, result_imag
result_real, _ = quad(lambda k: (b(k) * up2(k, x, t) + d(k) * um2(k, x, t)).real, -10, 10, epsabs=1e-3, epsrel=1e-3)
result_imag, _ = quad(lambda k: (b(k) * up2(k, x, t) + d(k) * um2(k, x, t)).imag, -10, 10, epsabs=1e-3, epsrel=1e-3)
return result_real + 1j * result_imag
@cython.boundscheck(False)
@cython.wraparound(False)
def rho(double x, double t):
""" Compute probability density """
return abs(psi1(x, t))**2 + abs(psi2(x, t))**2
(2) Cythonコンパイル用 setup.py を作成します:
#
# setup.py
#
from setuptools import setup, Extension
from Cython.Build import cythonize
import numpy
ext_modules = [
Extension(
"wave_packet",
["wave_packet.pyx"],
include_dirs=[numpy.get_include()],
define_macros=[("NPY_NO_DEPRECATED_API", "NPY_1_7_API_VERSION")],
)
]
setup(
ext_modules=cythonize(ext_modules, language_level=3, annotate=True),
)
(3) Pythonスクリプト main.py を作成します:
#
# main.py
#
import numpy as np
import matplotlib.pyplot as plt
import matplotlib.animation as animation
from wave_packet import psi1, psi2, rho
x_values = np.linspace(-20, 20, 100)
times = np.linspace(0, 30, 300)
fig, ax = plt.subplots()
ax.set_xlim(-20, 20)
ax.set_ylim(-0.5, 1)
ax.grid()
ax.set_xlabel(r'$x$')
ax.set_ylabel(r'$\rho(x)$')
ax.set_title("Time evolution of a Gaussian wave packet with positive momentum")
fill = ax.fill_between(x_values, np.zeros_like(x_values), np.zeros_like(x_values), color='blue', alpha=0.5)
line1, = ax.plot([], [], color='red', lw=2)
line2, = ax.plot([], [], color='green', lw=2)
def animate(t):
y_rho = np.array([rho(x, t) for x in x_values])
y_psi1 = np.array([abs(psi1(x, t))**2 for x in x_values])
y_psi2 = -1 * np.array([abs(psi2(x, t))**2 for x in x_values])
global fill
fill.remove()
fill = ax.fill_between(x_values, 0, y_rho, color='blue', alpha=0.5)
line1.set_data(x_values, y_psi1)
line2.set_data(x_values, y_psi2)
return fill, line1, line2
ani = animation.FuncAnimation(fig, animate, frames=times, interval=20, blit=False, repeat=False)
ani.save("wave_packet_momentum.gif", writer="pillow")
print("Program is finished!")
【使い方】
1. Cythonコードをコンパイルします(ターミナルで):
python setup.py build_ext --inplace
2. Pythonスクリプト (main.py) を実行します(ターミナルで):
python main.py
この結果, 動画作成に掛かった時間は約30秒となった!.
シミュレーション結果は次である:
今度はディラックスピノルの絶対値
参考のために「初期状態で静止しているガウス波束の時間発展」の様子をシミュレーションした動画も示しておこう.左右に細かく揺れていて「Zitterbewegung」現象の存在がよく分かると思う: