3. 仿真环境搭建与工具链选型
量子纠错循环仿真的第一步不是写纠错算法,而是把“谁来算、算什么、怎么算得快”这三件事定下来。量子纠错的核心对象是稳定子测量与错误传播,仿真框架需要同时处理两类任务:一是生成带噪声的测量结果(采样),二是根据测量结果推断最可能的错误(解码)。Stim、PyMatching、qiskit-qec 分别覆盖了这两类任务的不同环节,理解它们的分工比记住安装命令更重要。
三个框架的定位与分工
Stim 是一个专注于 Clifford 电路采样的高性能模拟器。它不直接做通用量子态演化,而是利用稳定子形式化,把“测量结果”以极快速度批量生成。对于重复码、表面码这类以稳定子测量为主的纠错电路,Stim 的采样速度通常比通用态矢量模拟器快几个数量级。PyMatching 则是一个最小权完美匹配(MWPM)解码器,输入是探测事件(detector events)和它们之间的图结构,输出是最可能的错误链。qiskit-qec 是 Qiskit 生态中的纠错模块,提供码的定义、综合、解码接口,适合与 Qiskit 的电路层和噪声模型配合使用,但在大规模采样上通常不如 Stim 直接。
一个实用的组合是:用 Stim 生成带噪声的探测事件,用 PyMatching 做解码,再用统计脚本计算逻辑错误率。qiskit-qec 更适合教学演示和与 Qiskit 后端联调。下面这张图展示了三者在一次重复码循环仿真中的信号链关系。
安装与最小配置
推荐使用 Python 3.10 或 3.11 的虚拟环境,避免与系统包冲突。三个框架的安装命令如下:
python -m venv qec-env
source qec-env/bin/activate # Windows 用 qec-env\Scripts\activate
pip install stim pymatching
pip install qiskit qiskit-qec # qiskit-qec 视版本可能需从源码或指定索引安装
安装完成后,用三行代码验证 Stim 是否可用,并确认 PyMatching 能正常导入:
import stim, pymatching
c = stim.Circuit.generated("repetition_code:memory", distance=3, rounds=3,
before_round_data_depolarization=0.05)
print(c.num_detectors, c.num_observables)
m = pymatching.Matching.from_detector_error_model(c.detector_error_model())
print(m.num_detectors)
这里 before_round_data_depolarization=0.05 表示每轮数据比特上以 5% 概率发生去极化错误,是教学示例参数,不是实测值。运行后应看到探测器数量和观测量数量,说明电路与解码图已正确构建。
完成第一个重复码循环仿真
下面用 Stim 采样、PyMatching 解码,完成一次完整的重复码记忆实验。重复码 d=3 使用 3 个数据比特和 2 个稳定子测量,逻辑观测量取 q0 的 Z 值。噪声模型采用每轮数据去极化错误,错误率 p 从 0.01 到 0.10 扫描。
import stim, pymatching, numpy as np
def run_rep_code(d=3, rounds=3, p=0.05, shots=20000):
circuit = stim.Circuit.generated(
"repetition_code:memory",
distance=d, rounds=rounds,
before_round_data_depolarization=p,
before_measure_flip_probability=p,
after_reset_flip_probability=p)
dem = circuit.detector_error_model(decompose_errors=True)
matching = pymatching.Matching.from_detector_error_model(dem)
sampler = circuit.compile_detector_sampler()
dets, obs = sampler.sample(shots, separate_observables=True)
pred = matching.decode_batch(dets)
logical_errors = np.sum(np.any(pred != obs, axis=1))
return logical_errors / shots
for p in [0.01, 0.03, 0.05, 0.08, 0.10]:
print(f"p={p:.2f} p_L={run_rep_code(p=p):.4f}")
这段代码的关键点有三处。第一,detector_error_model(decompose_errors=True) 把电路级噪声转换成解码器可用的图模型,分解错误是为了让 MWPM 能处理超边。第二,compile_detector_sampler() 返回的采样器直接输出探测事件矩阵,形状为 (shots, num_detectors),不需要手动从测量比特串做异或。第三,decode_batch 批量解码,返回预测的观测量翻转,与真实 obs 比较即可统计逻辑错误率。
一次典型运行会得到类似下面的输出(示例数据,非实测):
p=0.01 p_L=0.0021
p=0.03 p_L=0.0087
p=0.05 p_L=0.0196
p=0.08 p_L=0.0412
p=0.10 p_L=0.0635
判断仿真是否正确的依据是:随着物理错误率 p 增大,逻辑错误率 p_L 应单调上升;在 p 较小时,p_L 应显著低于 p,说明纠错在起作用。如果 p_L 与 p 接近甚至更高,通常说明解码图构建有误、噪声模型过强,或者观测量与解码输出没有对齐。
性能对比与选型建议
在相同 d=3、rounds=3、shots=20000 的条件下,Stim + PyMatching 的采样与解码通常在秒级完成。qiskit-qec 若走 Qiskit 的 Aer 模拟器,采样速度会明显慢于 Stim,因为后者不模拟完整量子态,只追踪稳定子。下表给出的是量级上的定性对比,不是精确基准。
| 框架 | 主要任务 | 采样速度 | 解码能力 | 适用场景 |
|---|---|---|---|---|
| Stim | Clifford 电路采样 | 极快 | 不内置解码 | 大规模重复码/表面码采样 |
| PyMatching | MWPM 解码 | — | 强 | 与 Stim 配合做解码 |
| qiskit-qec | 码定义与综合 | 中等 | 接口层 | 教学、与 Qiskit 后端联调 |
选型建议很直接:如果目标是快速扫描参数、评估逻辑错误率曲线,用 Stim + PyMatching;如果目标是演示码的结构、与 Qiskit 电路层交互,用 qiskit-qec;如果两者都需要,可以用 qiskit-qec 定义码,再导出为 Stim 电路做加速采样。不要一开始就追求最大距离或最复杂噪声模型,先用 d=3、rounds=3 跑通闭环,确认探测事件数量、解码输出维度和逻辑错误率趋势都符合预期,再逐步增加距离和轮数。