3. 量子态归一化异常排查

问题复现:未归一化态上的期望值

QuTiP 的 Qobj 在创建 ket 时不会自动归一化,也不会在 expect() 时强制检查范数。因此一个范数为 2 的 ket 参与期望值计算时,QuTiP 会照常返回结果,但该结果在物理上无意义。先构造一个可复现的最小例子:

import qutip as qt
import numpy as np

# 故意构造未归一化 ket:|ψ> = 2|0> + 1|1>
psi_raw = qt.Qobj([[2.0], [1.0]], dims=[[2], [1]])
print("norm:", psi_raw.norm())        # 2.2360679...
print("isket:", psi_raw.isket)        # True

sz = qt.sigmaz()
print("expect(sz):", qt.expect(sz, psi_raw))   # -0.6,非物理
print("prob sum:", (psi_raw.dag() * psi_raw)[0, 0])  # 5.0,不是 1

关键观察:isket 为 True 只说明形状是列向量,不代表已归一化;expect() 内部按 ⟨ψ|A|ψ⟩ 直接计算,分母没有 ⟨ψ|ψ⟩,所以范数被平方后带进了结果。上例中 σz 的期望值 −0.6 来自 (4·1 + 1·(−1))/5 = 3/5,而正确归一化后应为 (4−1)/5 = 0.6。

原始 ket(未归一化) |ψ⟩ = 2|0⟩ + 1|1⟩ ‖ψ‖ = √5 ≈ 2.236 ⟨ψ|ψ⟩ = 5 ≠ 1 qt.expect(A, ψ) = ⟨ψ|A|ψ⟩ = 3(σz 情形) 分母缺失 ⟨ψ|ψ⟩ 返回的“期望值” ⟨σz⟩ = −0.6 超出 [−1, 1] 的物理范围 无报错,静默错误 修正:归一化 psi = psi_raw.unit() ‖ψ‖ = 1.000 ⟨ψ|ψ⟩ = 1 重新计算 qt.expect(σz, psi) = 0.6 落在 [−1, 1] 内 验证判据 abs(psi.norm()−1) < 1e−10 (psi.dag()*psi)[0,0] ≈ 1 通过 上排:未归一化态直接计算 → 静默错误;下排:unit() 归一化后重算 → 结果落入物理范围

识别 QuTiP 的归一化检查行为

QuTiP 对归一化的“检查”是分散的,需要分清哪些地方会报错、哪些地方不会:

  • Qobj.unit():显式归一化,返回新对象,不修改原对象;对零矢量会抛 ZeroDivisionError 或产生 NaN,需自行防护。
  • Qobj.norm():默认返回迹范数(对 ket 即 √⟨ψ|ψ⟩),不是 1 就说明未归一化。
  • qt.expect():不做归一化检查,直接算 ⟨ψ|A|ψ⟩,这是本例错误的根源。
  • qt.mesolve() / qt.sesolve():初始态若未归一化,演化结果会整体缩放,但不会报错。
  • Qobj.dag() 与 tensor():不检查范数,张量积后范数为各因子范数之积,容易累积偏差。

因此排查顺序应是:先看 norm() 是否接近 1,再看 dims 是否正确,最后才怀疑算符本身。

修正态矢量并验证

修正方式取决于态的来源。若态由手工构造或从外部数据读入,用 unit() 归一化;若态由算符作用产生(如 A * psi),作用后必须重新归一化;若态来自 tensor() 组合,应检查每个因子是否已归一化。

# 方式一:直接归一化
psi = psi_raw.unit()

# 方式二:算符作用后归一化(注意零结果需防护)
phi = (qt.sigmap() * psi_raw)
if phi.norm() > 1e-12:
    phi = phi.unit()
else:
    raise ValueError("算符作用后得到零矢量,检查算符与态是否匹配")

# 验证:范数与概率和
assert abs(psi.norm() - 1) < 1e-10
assert abs((psi.dag() * psi)[0, 0] - 1) < 1e-10

# 验证:期望值落入物理范围
val = qt.expect(qt.sigmaz(), psi)
assert -1 - 1e-10 <= val <= 1 + 1e-10
print("归一化后期望值:", val)   # 0.6
对多比特系统,tensor(a, b).norm() 等于 a.norm() * b.norm()。若两个因子各自偏差 1e−6,组合后偏差约 2e−6,仍可能通过 1e−10 的严格断言失败。建议在 tensor() 之前逐个归一化因子,而不是组合后统一归一化,这样能更早定位是哪个子系统出了问题。
不要用 psi / psi.norm() 替代 unit() 作为通用写法:当 psi 是 bra 时,norm() 的语义与 ket 一致但除法方向容易写错;unit() 对 ket 和 bra 都按自身范数缩放,行为更稳定。另外,unit() 返回新对象,原对象不变,若忘记接收返回值会误以为已修正。

排查清单

检查项命令通过条件不通过时的处理
范数psi.norm()|‖ψ‖−1| < 1e−10psi = psi.unit()
概率和(psi.dag()*psi)[0,0]≈ 1同上,并检查是否有 NaN
形状psi.shape(2,1) 等列向量检查是否误用 bra 或行向量
dimspsi.dims与算符 dims 一致修正 tensor 结构
期望值范围qt.expect(A, psi)落在 A 的谱范围内回到范数检查

按此顺序执行,绝大多数“期望值明显不对但没报错”的情形都能在第一步定位到未归一化的态矢量。