引言
你写下的量子电路,几乎不可能直接在硬件上运行。原因很朴素:你用的是理想化的逻辑门(CNOT、Toffoli、任意单比特旋转),而硬件只提供少数几种物理门,且这些门只能在特定的量子比特对上执行。把「逻辑电路」翻译成「硬件能跑的电路」这件事,就是量子编译器与电路转译(transpilation)的工作。
这活儿的难度被严重低估。一个 100 量子比特、深度 1000 的电路,如果初始布局选得差,转译后可能需要插入上千个 SWAP 门,深度翻几倍,保真度直接崩掉。反过来,一个好的编译器能把同样的逻辑电路的两比特门数压掉 30%~50%。在 NISQ 时代,硬件噪声是主要瓶颈,编译质量几乎和硬件质量同等重要。
本文系统讲解量子编译:先讲编译流水线的四个阶段,再逐层拆解门分解、初始布局、路由与 SWAP 插入、调度,然后是脉冲级编译、优化 Pass 与代价模型、QIR 与 MLIR 中间表示,最后给出 Qiskit 与 tket 的实战代码。目标:理解「为什么转译会显著改变结果」,以及怎么读懂转译报告的每一个数字。
编译流水线的四个阶段
量子编译的结构和经典编译惊人地相似,只是多了「空间」这个维度——经典编译管的是时间(指令顺序),量子编译还要管位置(量子比特放哪)。
逻辑电路
│
├─ 1. 门分解 (decomposition / synthesis)
│ 任意单比特门 → 原生单比特门序列
│ 任意两比特门 → CNOT/CZ + 单比特门
│
├─ 2. 初始布局 (initial layout)
│ 逻辑量子比特 → 物理量子比特的映射
│
├─ 3. 路由 (routing / mapping)
│ 为不满足拓扑的 CNOT 插入 SWAP
│
├─ 4. 调度 (scheduling)
│ 排定门的执行时刻,处理资源冲突
│
▼
物理电路(原生门集 + 拓扑合法)
│
└─ 5. 脉冲级编译(可选)
原生门 → 带定时的脉冲序列
这四个阶段互相耦合:布局影响路由的 SWAP 数量,路由会改变门顺序从而影响后续优化。所以现代编译器把布局与路由做成联合优化(SABRE 就是典型),而不是串行两遍。
| 阶段 | 输入 | 输出 | 主要代价指标 |
|---|---|---|---|
| 门分解 | 任意门 | 原生门序列 | 门数、单比特门数 |
| 初始布局 | 逻辑电路 | 物理映射 | 后续 SWAP 数 |
| 路由 | 映射 + 拓扑 | 拓扑合法电路 | SWAP 数、深度 |
| 调度 | 合法电路 | 带时刻电路 | 总时长、并行度 |
| 脉冲级 | 原生门 | 脉冲序列 | 保真度、串扰 |
硬件原生门集与门分解
每个硬件平台都有自己的原生门集(native gate set)。你不能直接给硬件一个任意角度的 $R_y(\theta)$,除非它在原生门集里,或者能分解成原生门。
| 平台 | 原生单比特门 | 原生两比特门 | 备注 |
|---|---|---|---|
| IBM 超导 | RZ, SX, X | ECR / CX | RZ 是虚拟门,几乎零成本 |
| Rigetti | RX, RZ | CZ | — |
| IonQ | R, RZ | MS | MS 门任意角度可调 |
| Quantinuum | RZ, SX, X | ZZ / MS | — |
| 中性原子 | RZ, RX | CZ | 需重排 |
注意 RZ 是虚拟门(virtual Z):它通过改变后续脉冲的相位实现,不需要实际施加微波,因此门时间接近零、保真度接近 100%。这个事实有重要后果——编译器应该尽量把旋转往 RZ 上搬,因为 RZ 几乎免费。
门分解的经典结果:
- 任意单比特幺正 → 最多 3 个旋转(ZYZ 分解)+ 全局相位
- 任意两比特幺正 → 最多 3 个 CNOT + 单比特门
- CNOT → CZ + 两个 Hadamard(若硬件原生是 CZ)
- Toffoli → 6 个 CNOT + 单比特门(T 门计数版本需 7 个 T)
- 多控门 → 线性数量的 CNOT(辅助比特方案)
# Qiskit: 把任意门分解到指定基底
from qiskit import QuantumCircuit
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime.fake_provider import FakeSherbrooke
qc = QuantumCircuit(3)
qc.h(0)
qc.mcx([0, 1], 2) # 多控 X,硬件没有原生实现
qc.ry(0.37, 1)
qc.cx(1, 2)
backend = FakeSherbrooke()
pm = generate_preset_pass_manager(optimization_level=3, backend=backend)
isa_circuit = pm.run(qc)
print("原生门集:", backend.operation_names)
print("转译前门数:", qc.count_ops())
print("转译后门数:", isa_circuit.count_ops())
print("转译后深度:", isa_circuit.depth())
初始布局:把逻辑量子比特放对位置
布局(layout)决定哪个逻辑量子比特映射到哪个物理量子比特。这一步看似随意,实则决定路由代价的上限。
布局策略
| 策略 | 思路 | 适用 |
|---|---|---|
| TrivialLayout | 第 i 个逻辑比特 → 第 i 个物理比特 | 小电路、拓扑稠密 |
| DenseLayout | 选连通性最好的子图 | 中等电路 |
| NoiseAdaptiveLayout | 选噪声最低的比特 | 噪声差异大时 |
| SabreLayout | 用路由反馈迭代优化布局 | 大电路、稀疏拓扑 |
| 自定义 | 按算法结构手工指定 | 有先验知识时 |
一个经验事实:布局错了,后面怎么优化都补不回来。比如一个只需要在 (0,1)、(1,2)、(2,3) 之间通信的电路,如果被映射到一条 4 比特链上,SWAP 数是 0;如果被映射到一个星形拓扑的「叶子」上,可能要多出好几倍 SWAP。
用路由代价反推布局
SABRE 的核心思想很聪明:布局与路由不能分开做,因为「好布局」的定义依赖于路由结果。它的做法是交替迭代——先随机或贪心给一个布局,跑一遍 SABRE 路由统计 SWAP 数,根据路由过程中的「交换倾向」更新布局,再跑一遍,如此往复若干轮,取 SWAP 最少的那个。
# Qiskit: SABRE 布局 + 路由(optimization_level=3 时默认启用)
from qiskit.transpiler import CouplingMap
from qiskit.transpiler.passes import SabreLayout, SabreSwap
coupling = CouplingMap.from_heavy_hex(7) # 127 比特 heavy-hex 拓扑
sabre_layout = SabreLayout(coupling, seed=42)
sabre_swap = SabreSwap(coupling, heuristic="decay", seed=42)
路由:SWAP 插入与代价模型
路由(routing)是量子编译最核心也最贵的部分:硬件拓扑上,两个相邻量子比特才能做两比特门。如果你的 CNOT 要作用在两个不相邻的物理比特上,就必须通过一串 SWAP 把它们的态「搬」到一起。
SWAP 是什么代价
SWAP 本身不是原生门,要分解成 3 个 CNOT:
SWAP = CNOT(a,b) · CNOT(b,a) · CNOT(a,b) # 3 个 CX
= 在 CZ 基底上 = 3 个 CZ + 若干 H
所以插入 1 个 SWAP 的代价是 3 个两比特门。一个深度 500 的电路,如果路由插了 200 个 SWAP,就凭空多出 600 个两比特门——而两比特门通常是保真度最低的操作。这就是为什么路由算法值得单独研究。
主要路由算法
| 算法 | 思路 | 复杂度 | 特点 |
|---|---|---|---|
| 贪心 BFS | 每次找最短路径插入 SWAP | 高 | 简单但易陷入局部 |
| 基于交换的搜索 | 枚举可行 SWAP,评估代价 | 很高 | 质量好但慢 |
| SABRE | 启发式 + 前瞻代价函数 | 中 | 工业界主流 |
| tket 路由 | 结合架构感知与门合并 | 中 | 与优化 Pass 协同 |
| 随机化路由 | 多次随机跑取最优 | 中 | 用时间换质量 |
SABRE 的启发式代价函数同时考虑两个因素:距离(当前 SWAP 能否让待执行的门更近)和前瞻(未来若干层门的距离之和)。它维护一个「前导层(front layer)」集合,只对前导层里的门计算距离,避免扫描整个电路。
SABRE 单步逻辑:
1. 从电路前面取若干未满足的门,构成 front layer
2. 对每个候选 SWAP,计算代价:
cost = Σ 前导层门两端的距离
+ 0.5 * Σ 扩展层门两端的距离 (前瞻项)
3. 选代价最小的 SWAP 执行
4. 更新电路状态,重复直到所有门满足拓扑
减少 SWAP 的三种手段
- 优化布局:把通信密集的比特放在连通性好的子图里。
- 门顺序重排:交换互不依赖的门顺序,让路由有更多选择。这属于经典编译里的指令调度,和经典编译器里做依赖分析与指令重排的思路一致。
- 远程门分解:某些平台支持远程门(如离子阱全连接),或在拓扑上做特殊分解(如线性最近邻 LNN 的专用算法)。
调度与脉冲级编译
电路合法了不等于能跑。还需要调度(scheduling):决定每个门在什么时刻执行,处理资源冲突(同一个物理比特不能同时参与两个门)、满足约束(有些硬件要求门之间有最小间隔)。
调度约束
| 约束类型 | 说明 | 典型值 |
|---|---|---|
| 数据依赖 | 门必须等前驱完成 | 由电路决定 |
| 资源冲突 | 同一比特串行执行 | 硬约束 |
| 最小间隔 | 门之间需冷却/复位时间 | 超导 ~ns,离子阱 ~µs |
| 串扰规避 | 相邻比特不同时操作 | 取决于硬件 |
| 读出延迟 | 测量后需复位 | ~µs |
调度目标是压缩总时长(wall-clock),而不是门数。这对相干时间短的平台(超导 ~100 µs)至关重要——如果电路总时长超过相干时间,结果就废了。
脉冲级编译
再往下一层,原生门也要编译成脉冲。这是 脉冲级控制 的领域,编译器在这里要处理的是:
- 门的脉冲实现(DRAG 脉冲参数、MS 门的激光包络)
- 并行门的串扰分析(同时打的两个门会不会互相干扰)
- 虚拟 Z 的相位跟踪(RZ 通过相位偏移实现)
- 校准表绑定(每个门绑定到具体校准数据)
# Qiskit 里查看门到脉冲的映射(示意)
from qiskit import schedule # 旧 API;新版本用 Qiskit Dynamics
# 现代做法:用 backend.defaults() 查 gate 的 pulse 定义
# 或用 Qiskit Pulse 的 builder 构造自定义脉冲
脉冲级编译的难点是校准数据依赖:编译结果必须绑定到当前硬件校准。校准每天变,编译结果就得重新生成。这催生了「按需编译」的云服务模式——提交电路时才编译,保证用的是最新校准。
优化 Pass 与代价模型
转译不只是「翻译」,还包括大量优化。Qiskit 的 Transpiler 有 100 多个 Pass,tket 有自己的优化流水线。
常见优化 Pass
| Pass | 作用 | 收益 |
|---|---|---|
| 单比特门合并 | 相邻单比特门合成一个 | 减少门数 |
| 门抵消 | 相邻的自逆门对消(HH=I) | 减少门数 |
| 模板匹配 | 用预定义等价模板替换 | 减少 CX |
| 相位多项式化简 | 把 CX-RZ 网络转成相位多项式 | 大幅减少 CX |
| Clifford 化简 | 用稳定子形式简化 Clifford 段 | 大幅减少门 |
| ZX 化简 | 用 ZX 演算做图重写 | 减少 CX 与深度 |
| 恒等门消除 | 移除 0 角度旋转 | 减少门数 |
相位多项式化简(phase polynomial / Paulihedral) 是收益最大的技术之一。它把电路中的 CX 网络与 RZ 门提取成一个「相位多项式」——每对量子比特的奇偶性对应一个相位项。因为 RZ 门对易,可以把同类项合并,把原本需要 $O(n)$ 个 CX 的电路压到 $O(n/\log n)$。这是经典编译里「公共子表达式消除」在量子场景的对应物。
代价模型
编译器需要量化「哪个方案更好」。常用指标:
cost = w1 * two_qubit_gates # 两比特门数(保真度主导)
+ w2 * circuit_depth # 深度(相干时间主导)
+ w3 * single_qubit_gates # 单比特门数
+ w4 * total_duration # 总时长(含调度)
+ w5 * crosstalk_penalty # 串扰惩罚
权重不是常数。相干时间长的平台(离子阱)可以容忍更深但门数更少的电路;相干时间短的平台(超导)宁可多几个门也要压深度。所以**「同一个电路的最优编译」在不同平台上是不同的**,这就是为什么跨平台迁移电路需要重新编译。
工具链:Qiskit、tket、QIR 与 MLIR
| 工具 | 语言 | 特点 | 适用 |
|---|---|---|---|
| Qiskit Transpiler | Python | PassManager 可组合、生态最大 | IBM 及通用 |
| pytket (tket) | Python/C++ | 优化强、多后端、支持符号角 | 跨平台 |
| Cirq + OpenFermion | Python | Google 系、适合化学模拟 | 研究 |
| QIR | LLVM IR 扩展 | 硬件无关中间表示 | 跨工具链互操作 |
| MLIR 量子方言 | MLIR | 多层 IR、可扩展 | 下一代编译器 |
| BQSKit | Python | 数值合成强 | 门合成研究 |
QIR(Quantum Intermediate Representation) 是 LLVM IR 的量子扩展,由微软与 Quantinuum 等推动。它把量子操作表达为对 Qubit 类型的函数调用,同时支持经典控制流。好处是能复用 LLVM 的优化基础设施,并让不同前端(Q#、Qiskit、Cirq)编译到同一表示。
; QIR 片段(示意)
%q = call %Qubit* @__quantum__rt__qubit_allocate()
call void @__quantum__qis__h__body(%Qubit* %q)
call void @__quantum__qis__mz__body(%Qubit* %q, %Result* %r)
MLIR 的量子方言则把编译分成多层:高层表示算法结构,中层做门优化,底层绑定硬件。这种多层设计让优化可以在合适的抽象层级进行——在高层合并大块结构,在低层做脉冲调度。这与经典编译器的分层 IR 思路一脉相承,与 TVM/MLIR 这类 AI 编译器 里讨论的 pass 管理是同一套方法论。
# pytket: 编译到特定后端并查看优化效果
from pytket import Circuit
from pytket.backends.ibm import IBMQBackend
c = Circuit(3)
c.H(0).CX(0, 1).CX(1, 2).Rz(0.3, 2)
# 指定架构与原生门集
compiled = backend.get_compiled_circuit(c, optimisation_level=2)
print("CX 数:", compiled.n_gates_of_type(OpType.CX))
print("深度:", compiled.depth())
转译报告怎么读
云平台返回的转译结果里有一堆数字,读懂它们才能判断电路能不能跑。
| 指标 | 含义 | 关注点 |
|---|---|---|
| 逻辑比特数 | 电路声明的量子比特 | 与物理可用数对比 |
| 物理比特数 | 实际占用的物理比特 | 布局是否浪费 |
| 深度 | 关键路径上的门层数 | 是否超过相干时间/门数 |
| 两比特门数 | 主要误差来源 | 与逻辑门数对比看开销 |
| 单比特门数 | 次要误差来源 | 是否被 RZ 虚拟门吸收 |
| 总时长 | 含调度的 wall-clock | 与 T2 对比 |
| SWAP 数 | 路由开销 | 布局是否合理 |
一条实用的检查清单:
转译后自检:
[ ] 两比特门数 / 逻辑两比特门数 < 5? (否则布局或路由有问题)
[ ] 深度 × 单门时间 < T2 的 1/10?
[ ] 是否用了噪声感知布局(噪声差异大时)?
[ ] 是否有可手工优化的结构(如对称的 QFT)?
[ ] 换一个 seed / 优化等级,结果差多少?
最后一条很重要:转译是启发式的,换随机种子结果会变。如果你的电路对 SWAP 数量敏感,跑几次不同 seed 取最优,往往能白捡 10%~20% 的改进。
编译时间与规模挑战
编译本身也要花时间,而且这个时间随电路规模超线性增长。
| 电路规模 | 门数 | 典型编译时间 | 瓶颈 |
|---|---|---|---|
| 小 | < 100 | < 1 s | 几乎无感 |
| 中 | 100~10^4 | 数秒~分钟 | 路由搜索 |
| 大 | 10^4~10^6 | 分钟~小时 | 路由 + 优化 Pass |
| 超大 | > 10^6 | 小时~不可行 | 内存与搜索空间 |
三个实际痛点:
- 路由的搜索空间爆炸。SABRE 每步要评估几十个候选 SWAP,每个候选要算前导层距离,复杂度与门数、比特数都相关。10^6 门级电路的路由可能跑几小时。
- 优化 Pass 的组合爆炸。Pass 之间有顺序依赖,同一组 Pass 换个顺序结果不同。有些框架尝试用自动搜索(类似经典编译里的 autotuning)找最优 Pass 序列,但搜索本身也很贵。
- 校准数据绑定。脉冲级编译要绑定当前校准,而校准每天更新,所以大规模电路难以「编译一次、多次运行」。云平台的做法是每次提交都重新编译,用户感知到的延迟就包含编译时间。
应对手段:
- 分块编译:把大电路切成子电路,分别编译后拼接
- 增量编译:只重编译变化的子电路(类似增量构建)
- 缓存 Pass 结果:不变的前缀直接复用
- 降低优化等级:optimization_level=1 换取编译速度
- 并行路由:多个 seed 并行跑,取最优(用多核或分布式)
最后一点值得展开:因为转译是启发式的、可并行的,用算力换电路质量是完全合理的策略。跑 32 个不同 seed 的转译,取两比特门数最少的那个,代价只是几十秒编译时间,收益可能是 20% 的保真度提升。这在经典编译里少见(编译结果通常确定),但在量子编译里是常态。
小结
量子编译器的核心任务是回答三个问题:用什么门(分解)、放哪里(布局)、怎么连线(路由)。
- 编译流水线分门分解、初始布局、路由、调度四阶段,布局与路由必须联合优化。
- 原生门集决定分解目标;RZ 是虚拟门几乎免费,应尽量把旋转搬过去。
- SWAP 代价是 3 个两比特门,路由质量直接决定保真度。
- SABRE 通过启发式代价函数与前瞻,在工业界成为事实标准。
- 调度目标是压缩 wall-clock,脉冲级编译进一步绑定校准数据。
- 相位多项式、ZX 化简等 Pass 能大幅削减 CX 数,代价模型需按平台调权重。
- QIR 与 MLIR 提供硬件无关的中间表示,是跨工具链互操作的基础。
实践上,先写清楚逻辑电路,再用目标后端的 PassManager 转译,然后逐项对照转译报告,找出开销异常的部分。如果你的电路有结构性对称(QFT、量子傅里叶、组合优化中的对称约束),手工指定布局往往比自动布局更好。最后记住:转译不是一次性的,硬件校准变了、拓扑变了、优化等级变了,都要重新编译。
继续阅读
探索更多技术文章
浏览归档,发现更多关于系统设计、工具链和工程实践的内容。