4. 算符与态类型不匹配的运算错误
问题复现:Qobj 算符与错误类型的态对象相乘
在 QuTiP 中,Qobj 是统一的量子对象容器,但同一个 Qobj 类可以承载不同“类型”的对象:算符(operator)、右矢 ket、左矢 bra、超算符(super)等。类型信息并不只写在 type 属性里,更关键的是它由 dims 的结构决定——ket 的 dims 形如 [[2],[1]],bra 形如 [[1],[2]],算符形如 [[2],[2]]。当算符与一个“看起来像态、实际类型不对”的对象做乘法或期望值运算时,QuTiP 会抛出类型/维度相关的错误,而不是给出物理结果。
下面构造一个典型错误场景:一个 2 能级算符 sz,一个用 Qobj 直接包装的列向量(未声明为 ket),以及一个用 Qobj 包装的行向量(未声明为 bra)。
import numpy as np
import qutip as qt
# 正确的算符:dims = [[2],[2]]
sz = qt.sigmaz()
print("sz.type =", sz.type, "| sz.dims =", sz.dims)
# 错误做法 1:用裸 ndarray 列向量构造,dims 被推断为 [[2],[1]] 但 type 仍是 operator
psi_col = qt.Qobj(np.array([[1.0], [0.0]]))
print("psi_col.type =", psi_col.type, "| psi_col.dims =", psi_col.dims)
# 错误做法 2:用裸 ndarray 行向量构造
psi_row = qt.Qobj(np.array([[1.0, 0.0]]))
print("psi_row.type =", psi_row.type, "| psi_row.dims =", psi_row.dims)
# 触发错误:算符 * 非 ket 对象
try:
bad = sz * psi_col
print("sz * psi_col =", bad)
except Exception as e:
print("乘法错误:", type(e).__name__, "->", e)
# 触发错误:对非 ket 对象求期望值
try:
val = qt.expect(sz, psi_col)
print("expect =", val)
except Exception as e:
print("期望值错误:", type(e).__name__, "->", e)
运行后通常会在乘法处得到类似 TypeError: Incompatible quantum object dimensions 或 ValueError: incompatible dimensions 的提示;在 qt.expect 处则可能得到 TypeError: expected a ket or bra 一类信息。核心原因不是数值形状不对,而是 psi_col 的 type 被推断为 operator(因为 Qobj 默认按算符处理二维数组),而 sz * operator 在维度上虽然能乘,但 qt.expect 明确要求 ket/bra 类型。
定位方法:先看 type,再看 dims 的左右结构
排障时不要只盯着 shape。一个 (2,1) 的数组既可能是 ket,也可能被误建为 operator。可靠的检查顺序是:
- 打印
obj.type,确认是'ket'、'bra'、'oper'还是'super'。 - 打印
obj.dims,看左右两段结构:[[2],[1]]是 ket,[[1],[2]]是 bra,[[2],[2]]是算符。 - 若
type与dims的直觉不一致,说明对象是用底层数组直接构造的,需要显式转换。
例如对上面的 psi_col,psi_col.type 会显示 'oper',而 psi_col.dims 是 [[2],[1]]。这种“形状像 ket、类型是算符”的对象正是本案例错误的来源。
修复:转换为正确的 Qobj 形式
根据原始数据的来源,选择对应的转换方式,而不是继续用裸数组硬乘:
| 原始数据 | 推荐构造/转换 | 得到的 type 与 dims |
|---|---|---|
| 已知基矢索引 | qt.basis(2, 0) | ket,[[2],[1]] |
任意归一化列向量 v | qt.Qobj(v, dims=[[2],[1]]) | ket,[[2],[1]] |
已有 ket 对象 psi | psi.dag() | bra,[[1],[2]] |
密度矩阵 rho | qt.ket2dm(psi) 或 qt.Qobj(rho, dims=[[2],[2]]) | oper,[[2],[2]] |
| 误建的 operator 列向量 | qt.Qobj(psi_col.full(), dims=[[2],[1]]) | ket,[[2],[1]] |
关键点是显式传入 dims。QuTiP 会根据 dims 的左右结构推断类型:左段为 [2]、右段为 [1] 时识别为 ket;左段为 [1]、右段为 [2] 时识别为 bra。下面给出完整可运行的修复与验证代码:
import numpy as np
import qutip as qt
sz = qt.sigmaz()
# 修复 1:从列向量显式声明为 ket
v = np.array([[1.0], [0.0]])
psi = qt.Qobj(v, dims=[[2], [1]])
print("psi.type =", psi.type, "| psi.dims =", psi.dims) # ket, [[2],[1]]
# 修复 2:从行向量显式声明为 bra
bra = qt.Qobj(np.array([[1.0, 0.0]]), dims=[[1], [2]])
print("bra.type =", bra.type, "| bra.dims =", bra.dims) # bra, [[1],[2]]
# 验证乘法与期望值
out = sz * psi
print("sz * psi =", out)
print("expect(sz, psi) =", qt.expect(sz, psi)) # 1.0
print("bra * psi =", bra * psi) # 1.0
# 修复 3:把误建的 operator 列向量转回 ket
psi_col = qt.Qobj(np.array([[1.0], [0.0]]))
psi_fixed = qt.Qobj(psi_col.full(), dims=[[2], [1]])
print("psi_fixed.type =", psi_fixed.type) # ket
print("expect(sz, psi_fixed) =", qt.expect(sz, psi_fixed)) # 1.0
验证与常见陷阱
- 验证通过条件:
psi.type == 'ket'、bra.type == 'bra',且qt.expect(sz, psi)返回1.0(对|0⟩态),bra * psi返回1.0。 - 陷阱一:只改
shape不改dims。用reshape得到的仍是 operator,必须重新用Qobj(..., dims=...)包装。 - 陷阱二:对密度矩阵用
qt.expect(sz, rho)是合法的(QuTiP 支持对 oper 求期望值),但对误建的 operator 列向量不合法,因为它的dims右段是[1],不是方阵结构。 - 陷阱三:多比特系统中
dims要写成嵌套列表,如[[2,2],[1,1]]表示两比特 ket,不能只写[[4],[1]],否则后续张量运算会再次出现类型/维度问题。
type 后 dims,形状相同不代表类型相同;构造态对象时显式写 dims,比事后猜测更省时间。