量子线路编译
学习如何使用 pyqpanda3 的编译模块将抽象量子线路转换为硬件可执行的形式。本教程涵盖芯片拓扑建模、线路路由、门分解以及完整的编译流程。
前置条件: 量子线路构建 —— 在学习本教程之前,您应该能够熟练构建 QProg 和 QCircuit 对象。
目录
为什么需要编译
当您在 pyqpanda3 中编写量子线路时,您使用抽象门集来表达计算——可以使用 core 模块提供的 37 种以上的门。例如,您可以使用 CNOT(0, 3) 在量子比特 0 和 3 之间施加受控非门,或使用 SWAP(1, 4) 交换量子比特 1 和 4 的状态。
然而,真实的量子硬件存在三个基本限制:
1. 原生门集。 量子处理器并不直接实现所有门。大多数超导芯片只支持一小组基本门,例如 {U3, CZ} 或 {RZ, X1, CNOT}。像 SWAP 或 TOFFOLI 这样的门必须在执行前分解为原生门的序列。
2. 量子比特连接性。 物理量子比特按特定拓扑排列。双量子比特门只能应用于物理上相邻的量子比特之间。如果您的线路包含 CNOT(0, 3),但硬件只连接量子比特 0-1、1-2 和 2-3,则编译器必须插入 SWAP 操作,将逻辑交互通过物理连接进行路由。
3. 门保真度和相干性。 不同的量子比特和门类型有不同的错误率。优秀的编译器会将逻辑量子比特放置在保真度最高的物理量子比特上,并最小化双量子比特门的总数。
如果没有编译,为理想化量子计算机编写的线路无法在真实硬件上运行。编译模块通过将您的抽象线路转换为满足硬件约束的线路(同时保持其逻辑行为)来弥合这一差距。
flowchart LR
subgraph "抽象线路"
A1["H(0)"]
A2["CNOT(0,3)"]
A3["T(1)"]
A4["SWAP(0,2)"]
end
subgraph "编译"
T["Transpiler"]
end
subgraph "硬件线路"
B1["RZ(0, pi/2)"]
B2["X1(0)"]
B3["CNOT(0,1)"]
B4["CNOT(1,2)"]
B5["CNOT(2,3)"]
B6["CNOT(1,2)"]
B7["CNOT(0,1)"]
B8["RZ(1, pi/4)"]
end
A1 --> T
A2 --> T
A3 --> T
A4 --> T
T --> B1
T --> B2
T --> B3
T --> B4
T --> B5
T --> B6
T --> B7
T --> B8
style T fill:#50c878,color:#fff,stroke:#2d8a4e编译流程
pyqpanda3 中的编译由多个阶段组成,每个阶段解决一个或多个硬件约束:
flowchart TD
INPUT["输入 QProg / QCircuit"]
subgraph "阶段 1:门分解"
D1["非原生门<br/>分解为<br/>基本门集"]
end
subgraph "阶段 2:拓扑路由"
D2["量子比特映射<br/>和 SWAP 插入<br/>使用 SABRE 算法"]
end
subgraph "阶段 3:优化"
D3["门消除<br/>和线路<br/>简化"]
end
OUTPUT["输出 QProg"]
INPUT --> D1
D1 --> D2
D2 --> D3
D3 --> OUTPUT
style INPUT fill:#4a90d9,color:#fff
style OUTPUT fill:#50b86c,color:#fff
style D1 fill:#f4a460,color:#fff
style D2 fill:#f4a460,color:#fff
style D3 fill:#f4a460,color:#fff门分解将不在目标硬件原生门集中的门进行重写。例如,一个 TOFFOLI 门可能会被分解为六个 CNOT 门加上若干单量子比特旋转门。
拓扑路由将逻辑量子比特映射到物理量子比特,并在需要时插入 SWAP 门。pyqpanda3 使用 SABRE(SWAP-based BidiREctional 启发式搜索)算法,该算法效率高且能产生高质量的映射。
优化应用局部重写规则来消除冗余门对(例如,连续两个 H 门等价于恒等操作)并合并相邻的单量子比特旋转门。
transpilation 模块导出的三个函数分别对应此流程的不同部分:
| 函数 | 用途 |
|---|---|
generate_topology(n, type) | 生成用于测试的合成芯片拓扑 |
Transpiler().transpile(...) | 完整流程:分解、路由和优化 |
decompose(...) | 独立的门分解(不进行路由) |
1. 芯片拓扑
1.1 什么是芯片拓扑?
芯片拓扑描述了哪些物理量子比特对可以直接执行双量子比特门。它用边列表表示:一组 [qubit_a, qubit_b] 对,表示量子比特 a 和 b 物理上相连。
例如,4 量子比特线性链的拓扑为:
[0, 1], [1, 2], [2, 3]直观表示为:
q0 --- q1 --- q2 --- q34 量子比特方形(网格)拓扑增加了对角连接:
q0 --- q1
| |
q2 --- q3拓扑决定了路由问题。在线性链中,非相邻量子比特之间的任何双量子比特门都需要 SWAP 门将逻辑状态移到一起。在全连接拓扑中,不需要路由,因为每个量子比特对都直接相连。
1.2 generate_topology()
pyqpanda3 提供 generate_topology() 来创建用于测试和实验的合成拓扑边列表。您无需手动构建常见拓扑的边列表。
from pyqpanda3.transpilation import generate_topology
# 生成 4 量子比特的线性链拓扑
topo = generate_topology(4, "linear")
print(topo)
# [[0, 1], [1, 2], [2, 3]]运行结果:
[[0, 1], [1, 2], [2, 3]]签名:
generate_topology(num_qubits: int, topology_type: str) -> list参数:
| 参数 | 类型 | 说明 |
|---|---|---|
num_qubits | int | 拓扑中物理量子比特的数量 |
topology_type | str | 拓扑布局类型(见下文) |
返回值: 表示物理连接的 [qubit_a, qubit_b] 对列表。
1.3 拓扑类型
支持以下拓扑类型:
| 类型 | 说明 | 适用场景 |
|---|---|---|
"linear" | 链式:q0-q1-q2-...-q(n-1) | 简单测试线路 |
"square" | 二维网格排列 | 基于网格的量子处理器 |
"all-to-all" | 每个量子比特与其他所有量子比特相连 | 理想化测试 |
线性拓扑
线性链将每个量子比特与其直接邻居相连:
from pyqpanda3.transpilation import generate_topology
topo = generate_topology(5, "linear")
print(topo)
# [[0, 1], [1, 2], [2, 3], [3, 4]]运行结果:
[[0, 1], [1, 2], [2, 3], [3, 4]]graph LR
Q0["Q0"] --- Q1["Q1"] --- Q2["Q2"] --- Q3["Q3"] --- Q4["Q4"]
style Q0 fill:#4a90d9,color:#fff
style Q1 fill:#4a90d9,color:#fff
style Q2 fill:#4a90d9,color:#fff
style Q3 fill:#4a90d9,color:#fff
style Q4 fill:#4a90d9,color:#fff方形(网格)拓扑
方形拓扑将量子比特排列为二维网格。每个量子比特与其水平和垂直方向的邻居相连:
from pyqpanda3.transpilation import generate_topology
topo = generate_topology(4, "square")
print(topo)
# [[0,1], [0,3], [1,2], [1,4], [2,5],
# [3,4], [3,6], [4,5], [4,7], [5,8],
# [6,7], [7,8]]运行结果:
[[0, 1], [0, 3], [1, 2], [1, 4], [2, 5], [3, 4], [3, 6], [4, 5], [4, 7], [5, 8], [6, 7], [7, 8]]方形拓扑模拟了许多真实的超导芯片,其中量子比特在平面衬底上制造,具有最近邻耦合。
全连接(全对全)拓扑
全连接拓扑将每个量子比特与所有其他量子比特相连:
from pyqpanda3.transpilation import generate_topology
topo = generate_topology(4, "all-to-all")
print(topo)
# [[0,1], [0,2], [0,3], [1,2], [1,3], [2,3]]运行结果:
[[0, 1], [0, 2], [0, 3], [1, 2], [1, 3], [2, 3]]在全连接的情况下,路由不需要 SWAP 门。这种拓扑适用于独立测试分解或模拟理想化硬件。真实的离子阱量子计算机通常具有全对全连接性。
1.4 自定义拓扑
您也可以直接传递自定义边列表。例如,以量子比特 0 为中心的星形图:
custom_topo = [[0, 1], [0, 2], [0, 3], [0, 4]]或者环形(环形)拓扑:
ring_topo = [[0, 1], [1, 2], [2, 3], [3, 4], [4, 0]]2. Transpiler 类
Transpiler 类是线路编译的主要入口点。它将门分解、量子比特路由和线路优化组合为单个操作。
2.1 创建 Transpiler
创建 Transpiler 实例无需参数:
from pyqpanda3.transpilation import Transpiler
transpiler = Transpiler()编译器是无状态的——它在调用之间不保存持久配置。所有参数在编译时提供。
2.2 使用边列表的 transpile()
使用编译器最常见的方式是提供描述芯片拓扑的边列表:
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler, generate_topology
# Build a circuit that requires routing
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 3) # qubits 0 and 3 are not adjacent in a 4-qubit chain
prog << core.measure([0, 3], [0, 3])
# Define the chip topology as an edge list
chip_edges = [[0, 1], [1, 2], [2, 3]]
# Transpile: route through the linear chain and decompose to basic gates
transpiler = Transpiler()
transpiled = transpiler.transpile(
prog,
chip_edges, # topology edge list
{}, # initial mapping (empty = automatic)
2 # optimization level
)
print(transpiled)运行结果:
┌─┐ ┌─┐
q_0: |0>─┤H├ ───*── ─┤M├────
└─┘ ┌──┴─┐ └╥┘ ┌─┐
q_1: |0>──── ┤CNOT├ ──╫──┤M├
└────┘ ║ └╥┘
c : / ══════════════╩═══╩═
0 3在此示例中,原始线路施加 CNOT(0, 3),但量子比特 0 和 3 在线性链中相隔三跳。SABRE 算法会自动寻找最优的初始映射和路由方案。例如,它可能将逻辑量子比特 0 映射到物理量子比特 3,从而无需插入 SWAP 门即可直接执行 CNOT。最终的编译结果取决于 SABRE 算法的启发式决策。
签名:
transpiler.transpile(
prog, # QProg: the circuit to transpile
chip_topology_edges, # list of [int, int]: physical connections
init_mapping, # dict: initial qubit mapping (empty dict for automatic)
optimization_level, # int: 0, 1, or 2
basic_gates=None # list of str: target gate set (optional)
) -> QProg参数:
| 参数 | 类型 | 说明 |
|---|---|---|
prog | QProg | 要编译的量子程序 |
chip_topology_edges | list[list[int]] | 芯片拓扑的边列表 |
init_mapping | dict | 初始逻辑到物理量子比特映射。传 {} 表示自动。 |
optimization_level | int | 优化级别:0(无)、1(基本)、2(积极) |
basic_gates | list[str] 或 None | 目标门集名称。如果为 None,使用默认门集。 |
2.3 使用后端对象的 transpile()
您可以使用 generate_topology() 创建用于开发和测试的芯片拓扑:
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler, generate_topology
# Generate a 4-qubit linear topology
chip_edges = generate_topology(4, "linear")
print(f"Chip topology: {chip_edges}")
# Build a circuit
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1) << core.SWAP(0, 2)
prog << core.measure([0, 1, 2], [0, 1, 2])
# Transpile using the edge list
transpiler = Transpiler()
transpiled = transpiler.transpile(
prog,
chip_edges,
{}, # initial mapping
2 # optimization level
)
print("Transpile succeeded")运行结果:
Chip topology: [[0, 1], [1, 2], [2, 3]]
Transpile succeeded面向真实量子硬件时,您需要通过云服务进行认证并从后端获取芯片拓扑。generate_topology() 适用于本地测试和模拟。
2.4 优化级别
optimization_level 参数控制编译器优化输出线路的程度。更高级别产生更短的线路,但编译时间更长。
| 级别 | 行为 | 适用场景 |
|---|---|---|
0 | 无优化。仅路由和分解。 | 调试;查看原始路由输出 |
1 | 基本优化。消除相邻的逆门并合并旋转门。 | 快速编译并清理 |
2 | 积极优化。所有重写规则,包括基于交换性的重排序。 | 面向硬件的生产线路 |
# 级别 0:原始输出,无简化
transpiled = transpiler.transpile(prog, chip_edges, {}, 0)
# 级别 1:基本门消除
transpiled = transpiler.transpile(prog, chip_edges, {}, 1)
# 级别 2:积极优化(推荐用于硬件执行)
transpiled = transpiler.transpile(prog, chip_edges, {}, 2)2.5 初始量子比特映射
init_mapping 参数允许您指定哪个逻辑量子比特应从哪个物理量子比特开始。这是一个将逻辑量子比特索引映射到物理量子比特索引的字典:
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler
# 构建 2 量子比特线路
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])
# 3 量子比特线性链:物理量子比特 0-1-2
chip_edges = [[0, 1], [1, 2]]
# 将逻辑量子比特 0 映射到物理量子比特 2
# 将逻辑量子比特 1 映射到物理量子比特 0
init_mapping = {0: 2, 1: 0}
transpiler = Transpiler()
transpiled = transpiler.transpile(prog, chip_edges, init_mapping, 2)
print("Transpile succeeded with custom mapping")运行结果:
Transpile succeeded with custom mapping当您传递空字典 {} 时,编译器使用 SABRE 算法自动查找初始映射。在大多数情况下,自动映射已经足够,您无需手动提供。
您可能需要在以下情况下指定自定义映射:
- 您知道硬件上某些量子比特的保真度更高,希望将重要的逻辑量子比特放置在那里。
- 您要运行多个线路并希望它们使用相同的映射以便比较结果。
2.6 指定基本门集
basic_gates 参数限制输出线路仅使用指定门集中的门。不在该门集中的任何门都将被分解:
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler
# 构建线路
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])
chip_edges = [[0, 1], [1, 2]]
transpiler = Transpiler()
# 以 IBM 风格的门集为目标:U3 + CNOT 是通用门集
transpiled = transpiler.transpile(
prog, chip_edges, {}, 2,
basic_gates=["U3", "CNOT"]
)
print("Transpiled with U3/CNOT gate set")运行结果:
Transpiled with U3/CNOT gate set常见门集:
| 门集 | 门 | 硬件 |
|---|---|---|
| IBM 风格 | ["U3", "CNOT"] 或 ["RZ", "X1", "CNOT"] | 超导 |
| OriginQ 风格 | ["U3", "CZ"] 或 ["RZ", "X1", "CZ"] | 超导 |
| Clifford+T | ["H", "S", "T", "CNOT"] | 理论分析 |
| 离子阱 | ["RXX", "RZ", "RX"] 或 ["MS", "RZ"] | 离子阱 |
如果未指定 basic_gates(或为 None),编译器使用默认门集。
3. 门分解
decompose 函数执行门分解而不进行路由。它接收线路(或矩阵)并将所有门重写为指定基本门集的形式。当您想单独查看门分解效果而不修改量子比特分配时,这非常有用。
3.1 为什么要分解门?
门分解有多个目的:
硬件兼容性: 真实的量子处理器实现一组固定的物理操作。该门集之外的任何门都必须表示为支持门的序列。
线路分析: 分解为标准门集使资源计数更容易(例如,实际需要多少双量子比特门)。
验证: 将酉矩阵分解为门,然后检查生成线路的矩阵是否与原始矩阵匹配,可以验证正确性。
分解规则遵循量子线路合成中的著名恒等式。例如:
SWAP(a, b)=CNOT(a,b)+CNOT(b,a)+CNOT(a,b)(3 个 CNOT 门)CZ(a, b)=H(b)+CNOT(a,b)+H(b)(1 个 CNOT + 2 个 H 门)TOFFOLI(a, b, c)= 6 个 CNOT + 若干单量子比特门
3.2 decompose(prog, basic_gates)
将 QProg 中的所有门分解为指定的基本门集:
from pyqpanda3 import core
from pyqpanda3.transpilation import decompose
# Build a circuit with non-native gates
prog = core.QProg()
prog << core.H(0)
prog << core.SWAP(0, 1) # SWAP is not in most native gate sets
prog << core.TOFFOLI(0, 1, 2) # TOFFOLI is always decomposed
prog << core.measure([0, 1, 2], [0, 1, 2])
# Decompose into H + CNOT + rotation gates
decomposed = decompose(prog, ["H", "CNOT", "RZ", "X1"])
print(f"Original gate count: {prog.count_ops()}")
print(f"Decomposed gate count: {decomposed.count_ops()}")运行结果:
Original gate count: {'H': 1, 'SWAP': 1, 'CCX': 1}
Decomposed gate count: {'CNOT': 9, 'H': 3, 'RZ': 7}签名:
decompose(prog: QProg, basic_gates: list) -> QProg参数:
| 参数 | 类型 | 说明 |
|---|---|---|
prog | QProg | 输入量子程序 |
basic_gates | list[str] | 目标门集名称 |
返回值: 一个新的 QProg,其中所有门都用 basic_gates 表示。
原始程序不会被修改。decompose 函数总是返回一个新的程序对象。
3.3 decompose(circuit, basic_gates)
分解 QCircuit 对象中的门:
from pyqpanda3 import core
from pyqpanda3.transpilation import decompose
# Build a circuit module
circuit = core.QCircuit()
circuit << core.SWAP(0, 1)
circuit << core.CZ(1, 2)
# Decompose into CNOT + single-qubit gates
decomposed_circuit = decompose(circuit, ["H", "CNOT", "RZ", "X1"])
print(f"Original depth: {circuit.depth()}")
print(f"Decomposed depth: {decomposed_circuit.depth()}")运行结果:
Original depth: 2
Decomposed depth: 7签名:
decompose(circuit: QCircuit, basic_gates: list) -> QCircuit参数:
| 参数 | 类型 | 说明 |
|---|---|---|
circuit | QCircuit | 输入量子线路 |
basic_gates | list[str] | 目标门集名称 |
返回值: 一个新的 QCircuit,其中所有门都用 basic_gates 表示。
此重载适用于处理可复用的线路模块而非完整程序时。分解后的线路可以嵌入到任何 QProg 中。
3.4 decompose(matrix, qubits)
将酉矩阵分解为量子线路。这是最强大的分解形式——它接收一个任意
import numpy as np
from pyqpanda3.transpilation import decompose
# Define a 2x2 unitary matrix (single-qubit gate)
# This is a phase gate: diag(1, exp(i*pi/4))
mat = np.array([
[1, 0],
[0, np.exp(1j * np.pi / 4)]
], dtype=complex)
# Decompose into a circuit acting on qubit 0
circuit = decompose(mat, [0])
print(f"Circuit depth: {circuit.depth()}")
print(f"Circuit size: {circuit.size()}")运行结果:
Circuit depth: 1
Circuit size: 1签名:
decompose(matrix: np.ndarray, qubits: list) -> QCircuit参数:
| 参数 | 类型 | 说明 |
|---|---|---|
matrix | np.ndarray | 要分解的酉矩阵。对于 n 个量子比特,必须是 |
qubits | list[int] | 线路作用的量子比特索引。长度必须为 n。 |
返回值: 一个 QCircuit,在指定量子比特上实现给定的酉操作。
双量子比特示例:
import numpy as np
from pyqpanda3.transpilation import decompose
# A 4x4 unitary (two-qubit gate)
# Create a controlled-phase gate matrix
cp_mat = np.array([
[1, 0, 0, 0],
[0, 1, 0, 0],
[0, 0, 1, 0],
[0, 0, 0, np.exp(1j * np.pi / 3)]
], dtype=complex)
# Decompose into a circuit on qubits 0 and 1
circuit = decompose(cp_mat, [0, 1])
print(f"2Q circuit depth: {circuit.depth()}")
print(f"2Q gate count: {circuit.count_ops(only_q2=True)}")运行结果:
2Q circuit depth: 5
2Q gate count: {'CZ': 2}验证:
您可以验证分解后的线路是否正确实现了原始矩阵:
import numpy as np
from pyqpanda3 import core
from pyqpanda3.transpilation import decompose
# Original matrix
mat = np.array([
[1, 0],
[0, np.exp(1j * np.pi / 4)]
], dtype=complex)
# Decompose
circuit = decompose(mat, [0])
# Compute the circuit's unitary
circuit_matrix = circuit.matrix()
# Check that they match
print("Matrices match:", np.allclose(mat, circuit_matrix))
# Matrices match: True运行结果:
Matrices match: True3.5 分解规则
分解引擎应用著名的量子线路恒等式。以下是最重要的几个:
SWAP 分解:
这需要 3 个 CNOT 门。
from pyqpanda3 import core
from pyqpanda3.transpilation import decompose
swap_prog = core.QProg()
swap_prog << core.SWAP(0, 1)
result = decompose(swap_prog, ["H", "CNOT", "RZ", "X1"])
# result 现在包含:CNOT(0,1), CNOT(1,0), CNOT(0,1)CZ 分解:
TOFFOLI 分解:
Toffoli(CCX)门分解为 6 个 CNOT 门和 7 个单量子比特门:
单量子比特门分解:
任何单量子比特酉操作
或分解为 U3 门:
4. 综合应用
4.1 完整编译工作流
以下是将线路从抽象表示编译为硬件就绪形式的完整工作流:
flowchart TD
A["1. 使用 core 门构建<br/>抽象线路"] --> B["2. 定义芯片拓扑<br/>或从后端获取"]
B --> C["3. 选择优化级别<br/>和基本门集"]
C --> D["4. 调用 transpiler.transpile()"]
D --> E["5. 验证:对比<br/>编译前后结果"]
E --> F{结果匹配?}
F -->|是| G["6. 在模拟器上运行<br/>或提交到硬件"]
F -->|否| H["调试映射<br/>或分解"]
H --> C
style A fill:#4a90d9,color:#fff
style G fill:#50b86c,color:#fff
style H fill:#c0392b,color:#fff完整示例:
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler, generate_topology, decompose
# Step 1: Build the abstract circuit
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 1)
prog << core.SWAP(0, 2)
prog << core.T(1)
prog << core.measure([0, 1, 2], [0, 1, 2])
print("=== Original Circuit ===")
print(f"Depth: {prog.depth(only_q2=True)}")
print(f"Gates: {prog.count_ops()}")
print(f"2Q Gates: {prog.count_ops(only_q2=True)}")
# Step 2: Define chip topology (4-qubit linear chain)
chip_edges = generate_topology(4, "linear")
print(f"\nChip topology: {chip_edges}")
# Step 3: Transpile with optimization level 2
transpiler = Transpiler()
transpiled = transpiler.transpile(
prog,
chip_edges,
{}, # automatic qubit mapping
2, # aggressive optimization
basic_gates=["H", "RZ", "X1", "CNOT"]
)
print("\n=== Transpiled Circuit ===")
print(f"Depth: {transpiled.depth(only_q2=True)}")
print(f"Gates: {transpiled.count_ops()}")
print(f"2Q Gates: {transpiled.count_ops(only_q2=True)}")
# Step 4: Verify correctness on a simulator
machine = core.CPUQVM()
machine.run(prog, 10000)
orig_probs = machine.result().get_prob_dict()
machine.run(transpiled, 10000)
transpiled_probs = machine.result().get_prob_dict()
print("\n=== Verification ===")
print(f"Original probabilities: {orig_probs}")
print(f"Transpiled probabilities: {transpiled_probs}")运行结果:
=== Original Circuit ===
Depth: 2
Gates: {'CNOT': 1, 'H': 1, 'SWAP': 1, 'T': 1}
2Q Gates: {'CNOT': 1, 'SWAP': 1}
Chip topology: [[0, 1], [1, 2], [2, 3]]
=== Transpiled Circuit ===
Depth: 4
Gates: {'RZ': 3, 'X1': 1, 'CNOT': 4}
2Q Gates: {'CNOT': 4}
=== Verification ===
原始概率: {'000': 0.4984, '110': 0.5016}
编译后概率: {'000': 0.5018, '110': 0.4982}4.2 编译前后对比
编译后,务必在模拟器上对比原始线路和编译后的线路。测量概率应在统计噪声范围内匹配。第 4.1 节中展示的工作流演示了此模式:在 CPUQVM 上运行两个线路并比较 get_prob_dict() 输出。对于 10,000 次采样,每个比特串的概率应在约 0.05 的容差范围内一致。
4.3 面向真实硬件的编译
编译器在硬件拓扑约束量子比特连接性时处理线路路由。以下示例使用 4 量子比特线性链,其中循环 CNOT 模式强制编译器插入 SWAP 门:
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler, generate_topology
# 4 量子比特线性链:0-1-2-3
chip_edges = generate_topology(4, "linear")
# 构建带有回路的线路,强制编译器进行路由
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 1)
prog << core.CNOT(1, 2)
prog << core.CNOT(2, 3)
prog << core.CNOT(3, 0)
prog << core.measure([0, 1, 2, 3], [0, 1, 2, 3])
print("=== Original Circuit ===")
print(f"Depth: {prog.depth(only_q2=True)}")
print(f"Gates: {prog.count_ops()}")
print(f"2Q Gates: {prog.count_ops(only_q2=True)}")
# 基本优化级别编译
transpiler = Transpiler()
transpiled = transpiler.transpile(prog, chip_edges, {}, 1)
print("\n=== Transpiled Circuit (level 1) ===")
print(f"Depth: {transpiled.depth(only_q2=True)}")
print(f"Gates: {transpiled.count_ops()}")
print(f"2Q Gates: {transpiled.count_ops(only_q2=True)}")
# 在模拟器上验证
machine = core.CPUQVM()
machine.run(prog, 10000)
orig_probs = machine.result().get_prob_dict()
machine.run(transpiled, 10000)
tran_probs = machine.result().get_prob_dict()
print(f"\n=== Verification ===")
print(f"Original probabilities: {orig_probs}")
print(f"Transpiled probabilities: {tran_probs}")运行结果:
=== Original Circuit ===
Depth: 4
Gates: {'CNOT': 4, 'H': 1}
2Q Gates: {'CNOT': 4}
=== Transpiled Circuit (level 1) ===
Depth: 7
Gates: {'U4': 1, 'CNOT': 4, 'SWAP': 3}
2Q Gates: {'CNOT': 4, 'SWAP': 3}
=== Verification ===
Original probabilities: {'0000': 0.5076, '1110': 0.4924}
Transpiled probabilities: {'0000': 0.5104, '1110': 0.4896}本地编译的关键优势在于对优化级别、初始映射和基本门集的精细控制。面向真实量子硬件时,您需要通过云服务进行认证以获取芯片拓扑和门集,然后应用相同的编译工作流。
4.4 保真度感知布局与指令导出
从 pyqpanda3 0.4.0 开始,ChipBackend 可以携带单比特、读出和双比特门的保真度映射。你既可以通过 layout_method="fidelity" 让后端重载自动计算布局,也可以先手动调用 fidelity_layout() 查看初始映射。
from pyqpanda3.core import QProg, H, CZ, measure
from pyqpanda3.transpilation import ChipBackend, Transpiler, fidelity_layout
prog = QProg()
prog << H(0) << CZ(0, 1) << measure(0, 0) << measure(1, 1)
backend = ChipBackend()
backend.chip_topology_edges = [[0, 1], [1, 2]]
backend.basic_gates = ["RPhi", "CZ"]
backend.single_gate_fidelity = {0: 0.99, 1: 0.99, 2: 0.99}
backend.readout_fidelity = {0: 0.90, 1: 0.90, 2: 0.99}
backend.double_gate_fidelity = {(0, 1): 0.90, (1, 2): 0.99}
mapping = fidelity_layout(
prog,
backend.chip_topology_edges,
backend.single_gate_fidelity,
backend.readout_fidelity,
backend.double_gate_fidelity,
)
transpiled = Transpiler().transpile(prog, backend, {}, 1, "fidelity")
print(mapping)
print(len(transpiled.operations()) > 0)如果后续要导出指令 JSON,请先确保程序处在 virtual_z() 支持的门族中。一个稳妥的做法是先分解到 ["X1", "RZ", "CZ"],再转换成虚拟 Z 形式,设置后端时序后导出带调度的 JSON:
import json
from pyqpanda3.core import QProg, H, CZ, measure
from pyqpanda3.transpilation import ChipBackend, decompose, virtual_z
prog = QProg()
prog << H(0) << CZ(0, 1) << measure(0, 0) << measure(1, 1)
backend = ChipBackend()
backend.chip_topology_edges = [[0, 1], [1, 2]]
backend.basic_gates = ["RPhi", "CZ"]
backend.single_gate_timing = 40
backend.double_gate_timing = 80
instruction_ready = virtual_z(decompose(prog, ["X1", "RZ", "CZ"]))
instruction_json = instruction_ready.to_instruction(
backend,
offset=1,
is_scheduling=True,
compensate_pulse=False,
)
instructions = json.loads(instruction_json)
print(type(instruction_json).__name__, len(instructions))
print(instructions[0])输出:
str 3
{'RPhi': [1, 270.0, 90.0, 0]}如果你需要更完整的云端工作流,可以继续阅读专门的 QCloud 保真度布局 教程。
4.5 高级选项:LightSABRE 路由与转译指标
当默认转译不足以满足需求时——比如耦合度受限、线路较深,或想显式控制布局/路由策略并回收指标——可以使用 0.4.1 引入的强类型入口 TranspilationOptions,配合 TranspilationResult 一次性拿到转译后的程序、编译耗时和指标。
from pyqpanda3.core import QProg, H, CZ, measure
from pyqpanda3.transpilation import (
ChipBackend,
LayoutMethod,
RoutingMethod,
Transpiler,
TranspilationOptions,
)
prog = QProg()
prog << H(0) << CZ(0, 1) << measure(0, 0) << measure(1, 1)
backend = ChipBackend()
backend.chip_topology_edges = [[0, 1], [1, 2], [1, 3]]
backend.basic_gates = ["RPhi", "CZ"]
backend.single_gate_fidelity = {0: 0.99, 1: 0.99, 2: 0.99, 3: 0.99}
backend.readout_fidelity = {0: 0.95, 1: 0.95, 2: 0.95, 3: 0.95}
backend.double_gate_fidelity = {(0, 1): 0.97, (1, 2): 0.96, (1, 3): 0.96}
options = TranspilationOptions()
options.optimization_level = 2
options.layout_method = LayoutMethod.FIDELITY
options.routing_method = RoutingMethod.LIGHT_SABRE
options.approximation_degree = 0.99
options.execution.routing_trials = 8
options.execution.seed = 42
result = Transpiler().transpile(prog, backend, options)
print(type(result).__name__)
print(result.compile_time_ns > 0)
print(sorted(result.metrics.keys()))
print(result.metrics["two_qubit_gate_count"])运行结果(指标数值随后端标定变化):
TranspilationResult
True
['depth', 'fidelity_exact', 'fidelity_fallback', 'fidelity_unknown', 'log_expected_fidelity', 'two_qubit_gate_count']
1几点说明:
RoutingMethod.LIGHT_SABRE(也可传字符串"light_sabre")启用分层前瞻路由器,按层扩展交互图,通常能减少双比特门插入与交换深度,适合耦合度受限或线路较深的后端。approximation_degree控制保真度驱动的双比特门近似合成强度:1.0表示精确,None使用目标双比特门错误率,[0, 1]内的值按比例缩放。result.metrics与独立函数transpilation_metrics(prog, backend)返回的字典结构一致,包含two_qubit_gate_count、depth、log_expected_fidelity等。详见 转译选项与指标。- 0.5.0:这些选项背后的布局与路由栈已围绕共享的 LightSABRE 引擎重建——带均衡布局-路由目标函数的双向 SABRE 布局搜索、面向可嵌入子图的 VF2 补全、环形邻域布局候选,以及针对病态实例的工作量熔断器。Python API 保持不变,但编译时间显著下降、输出质量可度量地改善(在与 Qiskit 2.5.2 的 1,044 例 benchpress 套件上,pyqpanda3 0.5.0 在 1,040 例上更快,几何平均时间比 0.233x,同时双量子比特门数保持 0.957x)。固定
execution.seed后同一程序的重复转译产生完全相同的输出;execution.max_threads与execution.compile_time_budget_ms分别约束线程池与墙钟预算。完整分析见基准测试页面。
5. 实际示例
5.1 编译 GHZ 线路
GHZ(Greenberger-Horne-Zeilinger)态是一种多量子比特最大纠缠态。由于物理量子比特的连接性受限,在真实硬件上制备 GHZ 态需要编译器通过芯片拓扑路由 CNOT 门。
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler, generate_topology
# Build a 4-qubit GHZ state circuit
# Logical circuit: H(0) -> CNOT(0,1) -> CNOT(0,2) -> CNOT(0,3)
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 1)
prog << core.CNOT(0, 2)
prog << core.CNOT(0, 3)
prog << core.measure([0, 1, 2, 3], [0, 1, 2, 3])
print("=== GHZ State Circuit ===")
print(f"Logical depth: {prog.depth(only_q2=True)}")
print(f"Logical gates: {prog.count_ops()}")
# Transpile for a linear chain topology
# Qubit 0 must interact with qubits 1, 2, 3
# In a linear chain: 0-1-2-3
# CNOT(0,2) and CNOT(0,3) require SWAP routing
chip_edges = generate_topology(4, "linear")
transpiler = Transpiler()
transpiled = transpiler.transpile(prog, chip_edges, {}, 2)
print("\n=== Transpiled GHZ Circuit ===")
print(f"Physical depth: {transpiled.depth(only_q2=True)}")
print(f"Physical gates: {transpiled.count_ops()}")
print(f"Physical 2Q gates: {transpiled.count_ops(only_q2=True)}")
# Verify
machine = core.CPUQVM()
machine.run(prog, 10000)
orig = machine.result().get_prob_dict()
machine.run(transpiled, 10000)
trans = machine.result().get_prob_dict()
print(f"\nOriginal GHZ: {orig}")
print(f"Transpiled GHZ: {trans}")运行结果:
=== GHZ 态线路 ===
逻辑深度: 3
逻辑门数: {'CNOT': 3, 'H': 1}
=== 编译后 GHZ 线路 ===
物理深度: 4
物理门数: {'CNOT': 3, 'H': 1, 'SWAP': 1}
物理双量子比特门数: {'CNOT': 3, 'SWAP': 1}
原始 GHZ: {'0000': 0.5017, '1111': 0.4983}
编译后 GHZ: {'0000': 0.5033, '1111': 0.4967}在线性链上,编译器必须通过量子比特 1 和 2 路由 CNOT(0,3) 交互。这会引入额外的 SWAP 门,增加线路深度。在全连接拓扑上,不需要路由:
# No routing needed with all-to-all connectivity
full_topo = generate_topology(4, "all-to-all")
transpiled_full = transpiler.transpile(prog, full_topo, {}, 2)
print(f"\nAll-to-all depth: {transpiled_full.depth(only_q2=True)}")运行结果:
All-to-all depth: 35.2 编译随机线路
随机线路适用于基准测试编译性能。此示例生成一个 3 量子比特随机线路并在 100 量子比特方形拓扑上编译:
import time
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler, generate_topology
qubits = list(range(3))
circuit = core.random_qcircuit(qubits, 20, ["RX", "RY", "RZ", "H", "CNOT", "SWAP"])
prog = core.QProg()
prog.append(circuit)
for q in qubits:
prog.append(core.measure(q, q))
topo = generate_topology(100, "square")
transpiler = Transpiler()
start = time.time()
transpiled = transpiler.transpile(prog, topo, {}, 2)
elapsed = (time.time() - start) * 1000
print(f"Transpilation time: {elapsed:.2f} ms")
print(f"Before depth: {prog.depth(only_q2=True)}, After depth: {transpiled.depth(only_q2=True)}")运行结果:
Transpilation time: 6.19 ms
Before depth: 15, After depth: 65.3 分解酉矩阵
将酉矩阵分解为线路,然后验证生成的线路是否与原始矩阵匹配:
import numpy as np
from pyqpanda3.transpilation import decompose
# Custom U3 matrix
theta, phi, lam = np.pi / 5, np.pi / 7, np.pi / 11
u3_mat = np.array([
[np.cos(theta / 2), -np.exp(1j * lam) * np.sin(theta / 2)],
[np.exp(1j * phi) * np.sin(theta / 2),
np.exp(1j * (phi + lam)) * np.cos(theta / 2)]
], dtype=complex)
circuit = decompose(u3_mat, [0])
print("Correct:", np.allclose(u3_mat, circuit.matrix()))运行结果:
Correct: True5.4 自定义拓扑和门集
以下示例展示如何将自定义 T 形拓扑与特定门集结合使用:
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler, decompose
# T-shaped topology: q0--q2--q3, q2--q1, q2--q4
t_topo = [[0, 2], [1, 2], [2, 3], [2, 4]]
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 1) # requires routing: 0->2->1
prog << core.CNOT(3, 4) # requires routing: 3->2->4
prog << core.measure([0, 1, 2, 3, 4], [0, 1, 2, 3, 4])
transpiler = Transpiler()
transpiled = transpiler.transpile(prog, t_topo, {}, 2, basic_gates=["H", "RZ", "X1", "CNOT"])
print(f"Original: {prog.count_ops()} gates")
print(f"Transpiled: {transpiled.count_ops()} gates, {transpiled.count_ops(only_q2=True)} 2Q")
# Compare with decomposition only (no routing)
decomposed_only = decompose(prog, ["H", "RZ", "X1", "CNOT"])
print(f"Decomposed only: {decomposed_only.count_ops()} gates")运行结果:
Original: {'CNOT': 2, 'H': 1} gates
Transpiled: {'RZ': 2, 'X1': 1, 'CNOT': 5} gates, {'CNOT': 5} 2Q
Decomposed only: {'CNOT': 2, 'H': 1} gates6. 性能考虑
编译时间取决于线路大小、拓扑稀疏性和优化级别。对于高达 100 个量子比特、深度 1000 的线路,编译通常在不到一秒内完成。
通过稀疏拓扑路由会增加线路深度。如果两个量子比特在拓扑图中的最短路径距离为 d(即需要经过 d 条边才能从一个量子比特到达另一个,例如相邻量子比特的距离为 1),则它们之间的 CNOT 需要插入 2(d-1) 个 SWAP 门,每个 SWAP 消耗 3 个 CNOT 门,因此双量子比特门数量增加 6(d-1)。
| 距离 | 增加的 SWAP | 双量子比特门增加量 |
|---|---|---|
| 1(相邻) | 0 | 0 |
| 2 | 2 | 12 |
| 3 | 4 | 24 |
d | 2(d-1) | 6(d-1) |
选择拓扑: 使用 "all-to-all" 进行分解测试,使用 "linear" 进行最坏情况路由测试,使用 "square" 模拟真实芯片,使用 backend.chip_info() 获取真实硬件信息。
选择优化级别: 调试使用 0,快速测试使用 1,生产使用 2。对于非常大的线路(>50 个量子比特),级别 1 可能比级别 2 更快。
7. API 快速参考
generate_topology
| 签名 | 返回值 | 说明 |
|---|---|---|
generate_topology(num_qubits, topology_type) | list[list[int]] | 生成芯片拓扑边列表 |
拓扑类型: "linear"、"square"、"all-to-all"
Transpiler
| 方法 | 返回值 | 说明 |
|---|---|---|
Transpiler() | Transpiler | 创建新的编译器实例 |
.transpile(prog, edges, init_map, opt_level) | QProg | 使用边列表拓扑编译 |
.transpile(prog, backend, init_map, opt_level) | QProg | 使用后端对象编译 |
decompose(3 个重载)
| 签名 | 返回值 | 说明 |
|---|---|---|
decompose(prog, basic_gates) | QProg | 分解 QProg 中的门 |
decompose(circuit, basic_gates) | QCircuit | 分解 QCircuit 中的门 |
decompose(matrix, qubits) | QCircuit | 从酉矩阵合成线路 |
总结
在本教程中,您学习了:
为什么需要编译 —— 真实的量子硬件具有有限的原生门集和受限的量子比特连接性。编译弥补了抽象线路与硬件约束之间的差距。
芯片拓扑 ——
generate_topology(n, type)创建用于测试的合成拓扑。提供三种类型:"linear"(链式)、"square"(网格)和"all-to-all"(全对全)。Transpiler 类 ——
Transpiler().transpile()执行完整的编译流程:门分解、通过 SABRE 算法进行量子比特路由以及线路优化。您可以传递边列表或云后端对象。优化级别 —— 级别 0 不做优化,级别 1 应用基本门消除,级别 2 应用积极优化规则。硬件执行推荐使用级别 2。
门分解 ——
decompose函数将门重写为指定基本集的形式。三个重载分别处理QProg、QCircuit和原始酉矩阵。验证 —— 在提交到真实硬件之前,务必在模拟器上对比原始线路和编译后线路以确认正确性。
编译模块是您的量子算法与真实量子处理器之间的桥梁。理解其功能有助于您编写既正确又在硬件上高效的线路。
下一步:
- 量子云计算 —— 将编译后的线路提交到真实量子处理器
- 可视化 —— 绘制和对比原始线路与编译后线路
- 噪声模拟 —— 在编译后的线路上模拟噪声
- API 参考:transpilation —— 完整 API 文档