第三章 QuTiP核心对象:Qobj的创建与属性

各位同学,今天我们来聊聊QuTiP里最核心的东西——Qobj。说白了,它就是量子对象的缩写,是QuTiP用来描述量子态、算符、密度矩阵的统一数据结构。我刚开始用QuTiP的时候,觉得这东西不就是个矩阵吗?后来踩了几个坑才发现,它远不止这么简单。

3.1 什么是Qobj?

Qobj本质上是一个封装好的矩阵,但它比普通矩阵多了很多量子力学的“灵魂”。它知道自己是作用在哪个希尔伯特空间上的,也知道自己是态矢量还是算符。嗯,这一点在实际仿真中特别重要。

我个人习惯把Qobj理解成一个“智能矩阵”。它不光存数据,还存了维度信息、类型信息。你想想看,在量子光学里,我们经常要处理多个模式、多个能级,如果光靠手动管理维度,那代码写起来得多痛苦?

核心要点:Qobj = 矩阵 + 维度信息 + 类型标签

3.2 创建Qobj的几种方式

创建Qobj其实很简单,最直接的方法就是传入一个NumPy数组或列表。我在项目中遇到过一个问题:刚开始总是忘记指定维度,结果算出来的东西完全不对。所以这里我建议大家养成好习惯,创建时就明确维度。

3.2.1 从列表或数组创建

import numpy as np
import qutip as qt

# 最简单的创建方式
psi = qt.Qobj([[1], [0]])  # 一个二能级系统的基态
print(psi)

# 指定维度
psi_dims = qt.Qobj([[1], [0]], dims=[[2], [1]])
print(psi_dims)

这里要注意,dims参数是个列表的列表。第一个列表表示“行”的维度,第二个列表表示“列”的维度。对于态矢量,列维度永远是1。

3.2.2 使用内置函数创建

QuTiP提供了很多便捷函数,省得我们手动敲矩阵。我建议你优先用这些函数,既快又不容易出错。

函数 功能 示例
qt.basis(N, i) 创建N维希尔伯特空间的第i个基矢 qt.basis(3, 1) 创建|1⟩态
qt.fock(N, n) 创建N维Fock空间的n光子态 qt.fock(5, 2) 创建|2⟩态
qt.coherent(N, alpha) 创建相干态 qt.coherent(10, 1.5)
qt.qeye(N) 创建N维单位算符 qt.qeye(3)
qt.destroy(N) 创建N维湮灭算符 qt.destroy(4)

小技巧:创建相干态时,N要选得足够大,一般取光子数期望值的3-5倍。我曾经取太小,结果相干态被截断得面目全非,仿真结果完全不能用。

3.3 Qobj的核心属性

创建好Qobj后,我们可以查看它的各种属性。这些属性就像是量子对象的“身份证”,告诉你它到底是什么。

psi = qt.basis(3, 1)  # 三能级系统的第一激发态

print("数据矩阵:\n", psi.data)
print("维度信息:", psi.dims)
print("形状:", psi.shape)
print("是否是厄米算符:", psi.isherm)
print("是否是单位算符:", psi.isunit)
print("类型:", psi.type)

运行结果会告诉你:dims是[[3], [1]],type是'ket'(态矢量)。如果是密度矩阵,type会显示为'oper'或'dm'。

这里有个坑我要提醒你:type属性是根据dims自动推断的,不是手动设置的。如果你创建了一个2×2的矩阵,但dims设置成[[2], [2]],那它会被当成算符而不是态矢量。

避坑指南:我曾经在写多模系统时,把两个模式的维度搞反了,结果type显示为'oper',但我明明想创建的是态矢量。检查了半天才发现是dims顺序错了。所以创建后第一件事就是打印dimstype确认一下。

3.4 态矢量与密度矩阵

在量子光学里,我们经常要在纯态和混合态之间切换。纯态用态矢量表示,混合态用密度矩阵表示。QuTiP让这种切换变得非常方便。

3.4.1 态矢量转密度矩阵

# 创建一个纯态
psi = (qt.basis(2, 0) + qt.basis(2, 1)).unit()  # 归一化的|+⟩态

# 转成密度矩阵
rho = qt.ket2dm(psi)
print("密度矩阵:\n", rho)
print("纯度:", (rho * rho).tr())  # 纯态纯度=1

这里unit()方法是对态矢量做归一化。我建议你养成习惯,创建态后先归一化,不然很多运算结果会莫名其妙。

3.4.2 密度矩阵的迹和纯度

密度矩阵的迹永远是1(对于合法量子态),纯度则告诉我们这个态有多“纯”。纯态的纯度是1,混合态小于1。

# 创建一个混合态
rho_mixed = 0.5 * qt.ket2dm(qt.basis(2, 0)) + 0.5 * qt.ket2dm(qt.basis(2, 1))
print("混合态纯度:", (rho_mixed * rho_mixed).tr())  # 应该是0.5

关键点:密度矩阵的迹为1是物理上可接受的必要条件。如果计算过程中迹偏离了1,说明数值误差累积了,需要重新归一化。

3.5 基本运算与操作

QuTiP的Qobj支持很多直观的运算,这让量子力学的计算变得像普通代数一样自然。

3.5.1 加减乘除与内积

psi0 = qt.basis(2, 0)
psi1 = qt.basis(2, 1)

# 叠加态
psi_super = (psi0 + psi1).unit()

# 内积(重叠积分)
overlap = psi0.dag() * psi1  # 结果是0,因为正交
print("⟨0|1⟩ =", overlap)

# 算符作用在态上
a = qt.destroy(5)
psi_fock = qt.fock(5, 3)
a_psi = a * psi_fock  # 湮灭算符作用,得到√3|2⟩
print("a|3⟩ =", a_psi)

这里dag()方法返回厄米共轭,也就是转置共轭。对于态矢量,psi.dag()就是左矢⟨ψ|。

3.5.2 张量积

多模系统是量子光学的家常便饭。张量积用&运算符,非常直观。

# 两个单模态的张量积
psi_mode1 = qt.basis(3, 1)  # 模式1的|1⟩态
psi_mode2 = qt.basis(3, 0)  # 模式2的|0⟩态
psi_two_modes = qt.tensor(psi_mode1, psi_mode2)  # 或者用 psi_mode1 & psi_mode2

print("双模态维度:", psi_two_modes.dims)  # [[3, 3], [1, 1]]

我个人习惯用qt.tensor()函数,因为可读性更好。但&运算符写起来更简洁,看个人喜好。

3.5.3 部分迹

当我们只关心多模系统中的某个模式时,就需要用到部分迹。这在研究纠缠和退相干时特别有用。

# 创建一个纠缠态
psi_entangled = (qt.basis(2, 0) & qt.basis(2, 0) + qt.basis(2, 1) & qt.basis(2, 1)).unit()
rho_entangled = qt.ket2dm(psi_entangled)

# 对模式2求部分迹,得到模式1的约化密度矩阵
rho_mode1 = qt.ptrace(rho_entangled, 0)  # 0表示保留模式1
print("模式1的约化密度矩阵:\n", rho_mode1)
print("纯度:", (rho_mode1 * rho_mode1).tr())  # 纠缠态下纯度小于1

经验之谈:部分迹是诊断纠缠的好工具。如果约化密度矩阵的纯度小于1,说明两个模式之间存在纠缠。我在做量子密钥分发仿真时,就靠这个来判断纠缠质量。

3.6 本章知识体系

下面这张图总结了Qobj的核心概念和操作流程,你可以把它当作一个快速参考。

Qobj核心知识体系 Qobj 量子对象 创建方式 从数组/列表创建 内置函数:basis, fock, coherent 算符:destroy, create, qeye 核心属性 dims:维度信息 type:ket/bra/oper/dm isherm, isunit:性质检查 基本运算 加减乘除、内积 张量积:tensor, & 部分迹:ptrace 应用:态演化、纠缠分析、退相干模拟

这张图把Qobj的创建、属性和运算串在了一起。你从创建开始,然后检查属性确认类型,最后进行各种运算。这个流程我每次写仿真代码都会走一遍,已经成了肌肉记忆。

3.7 小结

Qobj是QuTiP的基石,理解它就等于拿到了量子光学仿真的钥匙。记住三点:创建时指定维度、用内置函数减少错误、运算前检查类型。做到这三点,你的仿真代码至少能少一半的bug。

嗯,这一章的内容就到这里。代码不多,但都是实打实的干货。你可以在自己的环境里把示例跑一遍,感受一下Qobj的“智能”之处。


专注资料整理