4. 单量子比特状态的布洛赫球可视化
布洛赫球是单量子比特纯态最直观的几何表示。一个任意单量子比特纯态可以写成
|ψ⟩ = cos(θ/2)|0⟩ + eiφsin(θ/2)|1⟩
其中 θ 称为极角,取值范围 [0, π];φ 称为方位角,取值范围 [0, 2π)。态矢量在球面上的位置由 (θ, φ) 唯一确定,球心到球面的半径归一化为 1。需要特别强调:θ 是态矢量与 +z 轴(即 |0⟩ 极点)之间的夹角,而振幅中出现的却是 θ/2,这个"半角"关系是布洛赫球最容易出错的地方——θ = π 对应 |1⟩,此时 cos(π/2) = 0,与公式一致。
本节的任务是:给定一组态矢量参数,用 Python 把它映射到布洛赫球上,并在图上标注极角与方位角。我们采用 Qiskit 的 plot_bloch_vector 与 Matplotlib 的 mplot3d 两条路线,前者快速出图,后者便于自定义角度标注。
从态矢量到布洛赫坐标
布洛赫矢量 r = (x, y, z) 与 (θ, φ) 的换算关系为:
x = sinθ·cosφ,y = sinθ·sinφ,z = cosθ
也可以直接从密度矩阵求:r = (⟨σx⟩, ⟨σy⟩, ⟨σz⟩)。对纯态 |ψ⟩ = (α, β)T,有 x = 2Re(α*β),y = 2Im(α*β),z = |α|² − |β|²。两条路径结果一致,前者适合已知角度参数的场景,后者适合从任意复数振幅出发的场景。
下面给出一个可直接运行的示例。我们选取三个典型态:|0⟩(θ=0)、|+⟩(θ=π/2, φ=0)以及一个一般态(θ=2π/3, φ=π/4)。
import numpy as np
import matplotlib.pyplot as plt
from qiskit.visualization import plot_bloch_vector
def angles_to_bloch(theta, phi):
x = np.sin(theta) * np.cos(phi)
y = np.sin(theta) * np.sin(phi)
z = np.cos(theta)
return [x, y, z]
states = {
"|0>": (0.0, 0.0),
"|+>": (np.pi/2, 0.0),
"general": (2*np.pi/3, np.pi/4),
}
for name, (theta, phi) in states.items():
vec = angles_to_bloch(theta, phi)
print(f"{name:8s} theta={theta:.3f} phi={phi:.3f} -> r=({vec[0]:+.3f}, {vec[1]:+.3f}, {vec[2]:+.3f})")
按上述参数,示例输出应为:|0⟩ 对应 r=(0, 0, 1);|+⟩ 对应 r=(1, 0, 0);一般态对应 r≈(0.354, 0.354, −0.5)。这些数值可作为校验基准——若你的程序输出与此不符,多半是 θ/2 与 θ 混用,或 φ 的象限处理有误。
绘制球面并标注角度
Qiskit 的 plot_bloch_vector 接受笛卡尔坐标,一行即可出图:
plot_bloch_vector(angles_to_bloch(2*np.pi/3, np.pi/4),
title="一般态 (theta=2pi/3, phi=pi/4)")
但它默认不绘制 θ、φ 的弧线标注。若教学或报告中需要明确标出两个角,建议用 Matplotlib 手工搭建:先画单位球面的经纬线,再画态矢量箭头,最后用 plot 绘制两段圆弧并加 text 注释。
fig = plt.figure(figsize=(6, 6))
ax = fig.add_subplot(111, projection="3d")
# 单位球面网格
u = np.linspace(0, 2*np.pi, 60)
v = np.linspace(0, np.pi, 30)
xs = np.outer(np.cos(u), np.sin(v))
ys = np.outer(np.sin(u), np.sin(v))
zs = np.outer(np.ones_like(u), np.cos(v))
ax.plot_surface(xs, ys, zs, color="#cfe8ff", alpha=0.25, linewidth=0)
theta, phi = 2*np.pi/3, np.pi/4
x, y, z = angles_to_bloch(theta, phi)
# 态矢量
ax.quiver(0, 0, 0, x, y, z, color="#e8590c", arrow_length_ratio=0.12, linewidth=2.5)
# 极角弧:从 +z 轴转到态矢量方向(在 phi 所在子午面内)
t = np.linspace(0, theta, 40)
ax.plot(np.sin(t)*np.cos(phi), np.sin(t)*np.sin(phi), np.cos(t), color="#1c7ed6", lw=1.8)
ax.text(0.15*np.sin(theta/2)*np.cos(phi), 0.15*np.sin(theta/2)*np.sin(phi),
0.55, r"$\theta$", color="#1c7ed6", fontsize=14)
# 方位角弧:在 xy 平面内从 +x 轴转到 phi
p = np.linspace(0, phi, 40)
ax.plot(0.45*np.cos(p), 0.45*np.sin(p), np.zeros_like(p), color="#2f9e44", lw=1.8)
ax.text(0.5*np.cos(phi/2), 0.5*np.sin(phi/2), 0.05, r"$\phi$", color="#2f9e44", fontsize=14)
ax.set_xlabel("x"); ax.set_ylabel("y"); ax.set_zlabel("z")
ax.set_box_aspect([1, 1, 1])
plt.show()
绘制时有三处细节值得注意。第一,极角弧必须画在 φ 确定的子午面内,即弧上每一点的方位角都等于 φ,否则弧线会"飘"出态矢量所在的平面,视觉上误导读者。第二,方位角弧画在 xy 平面内、半径取 0.45 左右即可,半径太大会与球面赤道线重叠。第三,set_box_aspect([1,1,1]) 不可省略,否则 Matplotlib 会把三个轴拉伸成不同比例,球看起来像椭球,角度关系失真。
结果判读与常见错误
出图之后,应按以下顺序判读。首先看矢量长度:纯态必须落在球面上,|r| = 1;若箭头明显短于半径,说明输入的不是纯态,或归一化被破坏。其次看极点:θ = 0 时箭头应竖直指向 +z,θ = π 时指向 −z,这两个位置 φ 无定义,图上不应出现方位角弧。再次看象限:φ = π/4 时 x、y 均为正,箭头应落在第一卦限;若你的图落在第四卦限,通常是 φ 的符号或 y 轴方向定义反了。
实践中最高频的错误有三类。其一是把 θ 直接代入振幅公式,写成 cosθ|0⟩ + sinθ|1⟩,导致 θ = π/2 时得到 (0.707, 0.707) 而非 (1, 0),布洛赫矢量长度变成 0.707,箭头缩进球内。其二是方位角弧画在错误的平面内,前文已述。其三是用 plot_bloch_vector 时传入了球坐标 (r, θ, φ) 而非笛卡尔坐标,该函数默认 coords_type="cartesian",若要用球坐标需显式指定 coords_type="spherical",且其球坐标约定为 (r, θ, φ),θ 同样是相对 +z 轴的极角。
angles_to_bloch 与绘图封装成函数,循环调用 plot_bloch_vector 并排展示。对比 |0⟩、|+⟩、|+i⟩(θ=π/2, φ=π/2)三个态时,箭头分别指向 +z、+x、+y,能直观建立"六个本征态对应六个坐标轴方向"的印象。最后补充一个实用校验:对任意 (θ, φ),用密度矩阵公式重算一遍 r,与角度公式的结果比对,两者应在浮点误差内一致。这一步能同时验证半角关系与相位符号,是提交可视化结果前值得保留的自检环节。