2. Qobj 维度不匹配错误的定位与修复

问题复现:两个“看起来能乘”的 Qobj

QuTiP 中 Qobj 的维度由 dims 属性描述,它是一个嵌套列表:[[行空间各子系统维数], [列空间各子系统维数]]。矩阵乘法要求左操作数的列空间与右操作数的行空间逐项一致,否则 QuTiP 会抛出 TypeError: incompatible dimensions 或 ValueError: shapes ... not aligned 类错误。下面构造一个典型场景:一个两比特算符与一个三能级算符相乘。

import qutip as qt
import numpy as np

# 两比特算符:dims = [[2,2],[2,2]],shape = (4,4)
A = qt.tensor(qt.sigmaz(), qt.sigmax())
# 三能级算符:dims = [[3],[3]],shape = (3,3)
B = qt.Qobj(np.diag([1, 2, 3]))

print("A.dims =", A.dims, " A.shape =", A.shape)
print("B.dims =", B.dims, " B.shape =", B.shape)

try:
    C = A * B
except Exception as e:
    print(type(e).__name__, ":", e)

预期输出(QuTiP 5.x 环境下):A.dims = [[2, 2], [2, 2]] A.shape = (4, 4),B.dims = [[3], [3]] B.shape = (3, 3),随后抛出 TypeError: incompatible dimensions。注意:即使 A.shape[1] == 4 与 B.shape[0] == 3 已经不等,QuTiP 仍会先检查 dims 的子系统结构,因此错误信息指向的是“维度不兼容”而非单纯的矩阵形状。

Qobj 维度不匹配的定位路径 A = tensor(σz, σx) dims = [[2,2],[2,2]] shape = (4, 4) 列空间:2 ⊗ 2 B = Qobj(diag[1,2,3]) dims = [[3],[3]] shape = (3, 3) 行空间:3 A * B 检查 dims 对齐 对齐条件 A.dims[1] == B.dims[0] ? [2,2] ≠ [3] → 抛出维度错误 TypeError: incompatible dimensions 修正后:统一为两比特空间 B2 = tensor(I₂, diag[1,2,3]) → dims [[2,3],[2,3]]

从错误信息反推 dims 结构

QuTiP 的报错通常只给出“incompatible dimensions”,不会直接告诉你哪一维对不上。定位的关键是把两个 Qobj 的 dims 并排打印,逐项比较左操作数的 dims[1](列空间)与右操作数的 dims[0](行空间)。上例中 A.dims[1] = [2, 2],B.dims[0] = [3],长度和数值都不一致,因此必然失败。若两者长度相同但某一项不同(例如 [2,3] 对 [2,2]),同样会报错,此时需要检查张量积中哪个子系统的维数写错了。

快速自检:print(A.dims[1], B.dims[0])。若两者不相等,问题一定出在 dims 结构而非数值内容。

修复:重建张量积结构

修复思路不是“强行 reshape”,而是让两个 Qobj 描述同一个复合希尔伯特空间。若希望 B 作用在两比特系统的第二个比特上(三能级嵌入两比特空间并不物理,这里仅作结构演示),应显式用 tensor 构造:

I2 = qt.qeye(2)
B2 = qt.tensor(I2, qt.Qobj(np.diag([1, 2, 3])))
print("B2.dims =", B2.dims, " B2.shape =", B2.shape)
# B2.dims = [[2, 3], [2, 3]]  B2.shape = (6, 6)

# 此时 A 也需扩展到同一空间
A2 = qt.tensor(qt.sigmaz(), qt.sigmax(), qt.qeye(3))
print("A2.dims =", A2.dims, " A2.shape =", A2.shape)
# A2.dims = [[2, 2, 3], [2, 2, 3]]  A2.shape = (12, 12)

# 若 A2 与 B2 仍不一致,继续对齐;正确做法是让两者 dims 完全一致
C = A2 * qt.tensor(qt.qeye(2), qt.qeye(2), qt.Qobj(np.diag([1,2,3])))
print("C.dims =", C.dims, " C.shape =", C.shape)

更常见的真实修复是:确认目标系统由哪些子系统组成,然后用同一组 tensor 因子分别构造算符和态,保证 dims 完全一致。例如两比特系统统一写成 tensor(op1, op2),不要一边用 tensor、另一边用裸矩阵。

验证修复是否成功

  • 打印 A.dims == B.dims,应为 True(对同空间算符)。
  • 执行乘法后检查结果 C.dims 与输入一致,C.shape 符合预期。
  • 对态矢量做 expect 时,算符与态的 dims 必须匹配,否则同样报维度错误。
不要用 Qobj(np.array(...)) 直接包裹已 reshape 的大矩阵来“绕过”错误——这样得到的 dims 是 [[N],[N]],会丢失子系统结构,后续 ptrace、tensor 等操作会再次失败。