1. 量子程序异常分类与诊断流程建立

为什么先分类,再排障

一段带测量和条件分支的量子程序,从源码到硬件执行要穿过四道关卡:解析器 → 编译器/转译器 → 模拟器 → 硬件后端。每一道关卡失败时抛出的异常形态完全不同,如果一上来就盯着报错文本猜原因,很容易把“语法写错”误判成“硬件噪声”。因此第一步不是修 bug,而是定位异常发生在哪一层,再按层归类根因。

下面这段示例程序(Qiskit 风格)故意混合了多种潜在问题,用它来演示分层诊断:

from qiskit import QuantumCircuit, transpile
from qiskit_aer import AerSimulator

qc = QuantumCircuit(2, 2)
qc.h(0)
qc.cx(0, 1)
qc.rz(0, 1)          # 参数个数错误:rz 需要 (theta, qubit)
qc.measure(0, 0)
qc.measure(1, 1)
with qc.if_test((qc.clbits[0], 1)):   # 条件分支:c0 == 1 时执行
    qc.x(1)

backend = AerSimulator()
tqc = transpile(qc, backend)
result = backend.run(tqc, shots=1024).result()
print(result.get_counts())

四类异常的判别特征

异常类别发生阶段典型报错形态判别要点
语法错误构造线路 / 解析TypeError、参数个数不符、门名不存在线路对象尚未生成,报错指向源码行
编译错误transpile / 映射TranspilerError、无法映射到后端耦合图线路本身合法,但目标后端不支持
模拟器运行错误backend.runQiskitError、条件分支不被支持、内存超限线路已编译,执行期才失败
硬件执行错误真实后端提交/回读作业排队失败、校准过期、结果全为噪声程序逻辑正确,结果不符合预期
量子程序源码(含测量+条件分支) ① 解析 / 构造 门名、参数个数、寄存器引用 ② 编译 / 转译 耦合图映射、门集转换 ③ 模拟器执行 条件分支支持、内存、shots ④ 硬件执行 排队、校准、噪声、回读 语法错误 TypeError / 参数个数不符 编译错误 TranspilerError / 无法映射 模拟器运行错误 QiskitError / 条件分支不支持 硬件执行错误 结果异常 / 校准过期 分层诊断决策路径 线路对象是否构造成功? transpile 是否通过? 模拟器 run 是否成功? 结果分布是否符合预期? 否 → 语法错误 否 → 编译错误 否 → 模拟器运行错误 否 → 硬件执行错误 是 程序 正常

可复用的排障检查清单

把上面的分层判断固化成清单,每次遇到异常按顺序勾选,避免跳层排查:

  • 第 1 关 · 构造:线路对象能否成功创建?报错是否指向源码行?门名拼写、参数个数、寄存器索引是否与 API 签名一致?
  • 第 2 关 · 编译:transpile 是否指定了目标后端?后端耦合图是否支持线路中的两比特门?是否需要 optimization_level 或 basis_gates 调整?
  • 第 3 关 · 模拟:模拟器是否支持条件分支(if_test)?shots 与内存是否合理?是否误用了只支持无噪声的模拟器?
  • 第 4 关 · 硬件:作业是否成功提交并回读?后端校准是否过期?结果分布是否与理论概率偏差过大(考虑噪声与读出误差)?
  • 记录:每关记录“通过/失败 + 报错原文 + 定位行号”,形成可追溯的排障日志。
诊断顺序应严格从第 1 关向第 4 关推进:低层错误会伪装成高层异常。例如参数个数写错时,某些框架会在 transpile 阶段才抛出难以理解的映射错误,若直接跳到编译层排查就会走弯路。
示例中的 qc.rz(0, 1) 是故意写错的参数形式,用于演示语法错误判别;实际修复应写成 qc.rz(theta, 1) 并给出合法角度值。条件分支在不同框架/版本中的支持程度不同,提交前需确认目标后端能力。