4. Qiskit转译接口初探

在上一节中我们已经知道,Qiskit 中编写的量子电路是“逻辑电路”,它描述的是量子比特之间的抽象关系,并不关心这些比特在真实硬件上究竟位于哪个物理位置、支持哪些门、连接拓扑如何。而 transpile() 函数正是连接“逻辑电路”与“物理后端”的桥梁。本节我们围绕一次完整的基础转译,把输入、关键参数、转译过程与结果观察串起来。

一、转译到底在做什么

可以把转译理解为一次“编译”:输入是一段与硬件无关的 QuantumCircuit,输出是一段能够直接交给指定后端执行的电路。中间至少发生三类变换:

  • 布局映射(Layout):把逻辑比特分配到后端的物理比特上,并记录映射关系;
  • 路由(Routing):当双比特门作用在两个没有直接连线的物理比特上时,插入 SWAP 门把量子态搬运到相邻比特;
  • 门分解与优化(Translation & Optimization):把电路中的门转换成后端原生门集合(basis gates),并在指定优化级别下做化简。

因此转译后的电路往往比原电路“更长”,多出来的部分主要来自 SWAP 和门分解,这是正常现象,而不是错误。

二、准备一个可观察的示例电路

为了让结构变化足够明显,我们构造一个包含 3 个量子比特、带有一个跨比特 CNOT 和若干单比特门的电路。示例参数均为教学假设,实际运行结果会随后端与 Qiskit 版本不同而变化。

from qiskit import QuantumCircuit, transpile
from qiskit_ibm_runtime import QiskitRuntimeService

qc = QuantumCircuit(3, 3)
qc.h(0)
qc.cx(0, 2)      # 逻辑上 0 与 2 相互作用
qc.ry(0.4, 1)
qc.cx(1, 2)
qc.measure([0, 1, 2], [0, 1, 2])

print("原始电路深度:", qc.depth())
print("原始门数量:", qc.count_ops())

这里 cx(0, 2) 是关键:在逻辑层面 0 和 2 可以直接作用,但在真实芯片上它们未必相邻,这正是路由要解决的问题。

三、指定后端与优化级别

transpile() 的核心参数是 backend 与 optimization_level。前者决定目标硬件约束(耦合图、basis gates),后者取 0~3,数值越高优化越激进,编译耗时也越长。

service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)

tqc = transpile(
    qc,
    backend=backend,
    optimization_level=1,
    seed_transpiler=42,   # 固定随机种子,便于复现
)

print("转译后深度:", tqc.depth())
print("转译后门数量:", tqc.count_ops())
print("使用的物理比特:", tqc.layout.final_index_layout())

几个值得注意的点:seed_transpiler 让布局与路由的随机选择可复现;tqc.layout 记录了逻辑比特到物理比特的映射,是判断“哪个逻辑比特跑到了哪”的依据;count_ops() 则直接反映门集合是否已被替换为后端原生门。

四、转译前后的结构对比

下图把一次典型转译的关键变化集中呈现:左侧是逻辑电路,右侧是转译结果,中间标出布局、路由与门分解三条作用路径,底部给出可直接对照的观察指标。

逻辑电路(转译前) 3 比特,与硬件无关 q0 q1 q2 H Ry 深度较小,但含非原生门与远距离 CNOT transpile() 作用 布局 Layout 逻辑比特 → 物理比特 路由 Routing 插入 SWAP 打通耦合 分解与优化 转 basis gates,级别 0~3 受 backend 与 optimization_level 约束 物理电路(转译后) 已映射到物理比特,含 SWAP P0 P1 P2 √X Rz × × SWAP 深度增大,门集合为后端原生门 转译前后应重点观察的指标 depth() 电路深度 通常变大 count_ops() 门统计 门集合变为原生门 layout 映射 逻辑比特去向 SWAP 数量 路由代价

五、如何判读转译结果

拿到 tqc 后,建议按以下顺序检查,而不是只看深度一个数字:

  1. 门集合是否合规:tqc.count_ops() 中出现的门应全部属于 backend.operation_names,若仍出现 cx 之外的抽象门,说明分解未完成或后端声明有误。
  2. 布局是否合理:通过 tqc.layout.final_index_layout() 查看逻辑比特落点,若关键交互比特被分到相距很远的物理比特,SWAP 数量会明显上升。
  3. 深度与 SWAP 的代价:深度增加主要来自 SWAP 与单比特门分解。若 SWAP 数量异常多,可尝试提高 optimization_level 或更换耦合更友好的后端。
  4. 测量映射:转译后测量仍作用在正确的物理比特上,最终结果按 layout 反解回逻辑比特,这一步在后续结果处理中必须保持一致。
优化级别 0 基本只做“能跑”的转换,几乎不化简;级别 1 是默认值,做基础化简;级别 2、3 会尝试更激进的合并与重布线,编译时间更长,但在深电路上往往能显著降低深度。初学阶段建议先用级别 1 建立直觉,再对比级别 3 的差异。

六、一次可复现的对比实验

把同一电路在级别 0 与级别 3 下各转译一次,记录指标,就能直观看到优化级别的影响。下表为教学示例的典型量级,实际数值以后端与版本为准:

指标原始电路级别 0级别 3
电路深度约 5明显增大相对级别 0 更小
CNOT/SWAP 类双比特门2 个 CNOT含额外 SWAP 分解SWAP 可能被合并减少
门集合H、CX、Ry后端原生门后端原生门
编译耗时—最短最长
不要用“深度越小越好”作为唯一标准。优化级别提高可能改变布局,导致测量映射与预期不同;在需要严格复现实验时,务必固定 seed_transpiler 并保存 layout 信息。

完成本节后,你应当能够独立完成一次“构造逻辑电路 → 选择后端 → 指定优化级别 → 调用 transpile() → 对比深度、门统计与布局”的完整流程,并解释转译后电路为何变长、哪些变化来自路由、哪些来自门分解。这正是后续把电路真正提交到硬件或模拟器执行的基础。