本文链接:个人站 | 简书 | CSDN
版权声明:除特别声明外,本博客文章均采用 BY-NC-SA 许可协议。转载请注明出处。
前几天整理电脑的时候发现了本科上量子力学讨论班时做的一个 Slide,觉得挺有意思的。花了点时间整理成这篇博客。
一维谐振子
一个质量为 的粒子,在一维势场 中运动。其哈密顿算符为
其中 为位置算符, 为动量算符。我们需要求解该体系的定态 Schrödinger 方程:
一维谐振子是除了氢原子之外,为数不多的可以解析求解的体系。那么我们为什么要费劲求它的数值解呢?正因为绝大多数的量子体系都无法解析求解,数值方法才显得尤为重要,吧啦吧啦吧啦~~(其实是这样的,当时那个讨论班每个人要做两次 Pre。第一次我讲了一下量子谐振子和经典谐振子的对比,可能实在是太水了,教授都不知道该问什么,就随口问了一下有没有谁会用数值方法求解谐振子,结果并没有人会。在准备第二次 Pre 的时候,班上的大神们已经开始讲二次量子化了。姿势水平不够高的我决定走差异化路线,于是就一脚踩进了数值计算的大坑里。)
有限差分法
回忆一下泰勒公式
令 ,有
两式相加,可得
将 在 区间离散化为
其中 ,则 Schrödinger 方程差分化为
在这里,我们假设 或 时 。这对能量较低的态是成立的。
将差分方程写成矩阵的形式为
这样一来,问题就转化为求差分矩阵的特征值和特征向量。
QR 算法
QR 算法是一种常见的特征值算法。它利用了矩阵的 QR 分解,即将矩阵 分解为一个正交矩阵 和一个上三角矩阵 的乘积:
为什么可以这么分解呢?我们回忆一下 Gram-Schmidt 正交化,将矩阵 的列向量看作一组基,则可以通过一系列初等列变换获得一组标准正交基。反过来看,对这组标准正交基所组成的矩阵 作初等列变换也可以得到矩阵 。我们知道,对矩阵作初等列变换相当于右乘初等矩阵,且由于 Gram-Schmidt 正交化不涉及列交换,这里用到的初等矩阵均为上三角矩阵。因此,矩阵 可以由正交矩阵 右乘一个上三角矩阵 得到。在实际应用中,除了 Gram-Schmidt 正交化,还可以用 Householder 变换、Givens 旋转 等方法实现 QR 分解。
那么如何利用 QR 分解求解矩阵的特征值呢?
记 ,对 ,
即在每一步,对 进行 QR 分解,再由分解后得到对 和 计算 ,如此迭代。
注意到 是正交矩阵,有 ,故
也就是说, 相似于 。根据递推关系, 全都是相似的,这意味着所有的 都有相同的特征值。在一定条件下, 会收敛为一个三角矩阵,特征值为其主对角元。
特别地,如果 是一个实对称正定矩阵(我们所要求解的差分矩阵刚好是这种情况), 将会收敛为一个对角矩阵 ,且 依次递减。(先立个 flag,等我看懂了这个结论的证明,就再补一篇博客。)考虑到
当 收敛时, 的列向量即为属于相应特征值的特征向量。
综上,对于实对称正定矩阵 ,我们令 ,对 ,
当 收敛时,就同时求得 的特征值和特征向量。
Code
当年的我是一个彻头彻尾的门外汉,连调包都不会,QR 分解是用 C++ 手写的。然而那份如此有纪念意义的代码竟然被我弄丢了,真是痛惜不已。
如今的我已经误打误撞地成为了一名算法调包工程师,知道可以直接调用 numpy.linalg.qr
作 QR 分解,可是却再也……再也想不出如何在这里编一段煽情的文字。
少废话,先看东西。
首先是 QR 算法:
import numpy as np
def qr_eig(A, iters=200, tol=1e-6):
"""
使用 QR 算法求解实对称矩阵的特征值和特征向量
Parameters
----------
A : Array-like
二维数组,表示待求解的实对称矩阵
iters : int
最大迭代次数
tol : float
提前退出循环的判断标准
Returns
-------
(numpy.ndarray, numpy.ndarray)
一维数组表示的特征值,二维数组表示的特征向量
"""
A = np.asarray(A)
S = np.eye(A.shape[0])
for _ in range(iters):
Q, R = np.linalg.qr(A)
newA = np.dot(R, Q)
newS = np.dot(S, Q)
if np.abs(max(np.diag(newA)) - max(np.diag(A))) < tol:
break
A = newA
S = newS
return np.diag(A), S
接下来就可以求解一维谐振子了。为了方便起见,我们令 。
# 取 [-5, 5] 的区间,离散化为 N 个点
N = 101
r = 5
dx = 2 * r / (N - 1)
x = np.linspace(-r, r, N, endpoint=True)
# 初始化差分矩阵
A = np.diag(0.5*x**2 + 1/dx**2)
for i in range(N -1 ):
A[i][i+1] = -0.5/dx**2
A[i+1][i] = -0.5/dx**2
# 求解差分矩阵的特征值和特征向量
Lambda, S = qr_eig(A)
# 结果展示
# 打印最低的 5 个能级
print(Lambda[-1:-8:-1])
# 画出能量最低的三个态的波函数
plt.plot(x, S[:,-1], label=f'n=0, E={Lambda[-1]:.4f}')
plt.plot(x, S[:,-2], label=f'n=1, E={Lambda[-2]:.4f}')
plt.plot(x, S[:,-3], label=f'n=2, E={Lambda[-3]:.4f}')
plt.xlabel('x')
plt.ylabel('psi(x)')
plt.legend()
>>> [0.4996873 1.49843574 2.49593067 3.49217016 4.48715598]
numerical.png
与解析解的对比
一维谐振子的本征能量为:
对应的本征态为:
其中
为 Hermite 多项式。前三个 Hermite 多项式为:
我们同样令 ,则
画出来看看
八九不离十吧。低能级的误差主要来自截断误差和舍入误差。此外,高能级需要有更大的 来保证 或 时 的假设,因此能级越高,误差越大。
网友评论