量子线路编译
学习如何使用 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. 门保真度和相干性。 不同的量子比特和门类型有不同的错误率。优秀的编译器会将逻辑量子比特放置在保真度最高的物理量子比特上,并最小化双量子比特门的总数。
如果没有编译,为理想化量子计算机编写的线路无法在真实硬件上运行。编译模块通过将您的抽象线路转换为满足硬件约束的线路(同时保持其逻辑行为)来弥合这一差距。
编译流程
pyqpanda3 中的编译由多个阶段组成,每个阶段解决一个或多个硬件约束:
门分解将不在目标硬件原生门集中的门进行重写。例如,一个 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]]签名:
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]]方形(网格)拓扑
方形拓扑将量子比特排列为二维网格。每个量子比特与其水平和垂直方向的邻居相连:
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]]方形拓扑模拟了许多真实的超导芯片,其中量子比特在平面衬底上制造,具有最近邻耦合。
全连接(全对全)拓扑
全连接拓扑将每个量子比特与所有其他量子比特相连:
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]]在全连接的情况下,路由不需要 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
# 构建需要路由的线路
prog = core.QProg()
prog << core.H(0)
prog << core.CNOT(0, 3) # 在 4 量子比特链中,量子比特 0 和 3 不相邻
prog << core.measure([0, 3], [0, 3])
# 将芯片拓扑定义为边列表
chip_edges = [[0, 1], [1, 2], [2, 3]]
# 编译:通过线性链路由并分解为基本门
transpiler = Transpiler()
transpiled = transpiler.transpile(
prog,
chip_edges, # 拓扑边列表
{}, # 初始映射(空 = 自动)
2 # 优化级别
)
print(transpiled)在此示例中,原始线路施加 CNOT(0, 3),但量子比特 0 和 3 在线性链中相隔三跳。SABRE 算法会自动寻找最优的初始映射和路由方案。例如,它可能将逻辑量子比特 0 映射到物理量子比特 3,从而无需插入 SWAP 门即可直接执行 CNOT。最终的编译结果取决于 SABRE 算法的启发式决策。
签名:
transpiler.transpile(
prog, # QProg:要编译的线路
chip_topology_edges, # list of [int, int]:物理连接
init_mapping, # dict:初始量子比特映射(空字典表示自动)
optimization_level, # int:0、1 或 2
basic_gates=None # list of str:目标门集(可选)
) -> 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()
通过云模块面向真实硬件时,您可以传递后端对象而非边列表。编译器自动从后端提取拓扑和原生门集:
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler
from pyqpanda3.qcloud import QCloudService
# 认证并获取后端
service = QCloudService("your_api_key")
backend = service.backend("origin_wukong")
# 构建线路
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1) << core.SWAP(0, 2)
prog << core.measure([0, 1, 2], [0, 1, 2])
# 使用后端的拓扑和门集进行编译
chip_backend = backend.chip_backend()
transpiler = Transpiler()
transpiled = transpiler.transpile(
prog,
chip_backend, # ChipBackend 对象(提取拓扑和门集)
{}, # 初始映射
2 # 优化级别
)通过 backend.chip_backend() 获取 ChipBackend 对象后,编译器内部从后端提取拓扑边列表和原生门集。这比手动提取和传递更加方便,也更不容易出错。
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 参数允许您指定哪个逻辑量子比特应从哪个物理量子比特开始。这是一个将逻辑量子比特索引映射到物理量子比特索引的字典:
# 将逻辑量子比特 0 映射到物理量子比特 2
# 将逻辑量子比特 1 映射到物理量子比特 0
# 让编译器决定其余部分
init_mapping = {0: 2, 1: 0}
transpiled = transpiler.transpile(prog, chip_edges, init_mapping, 2)当您传递空字典 {} 时,编译器使用 SABRE 算法自动查找初始映射。在大多数情况下,自动映射已经足够,您无需手动提供。
您可能需要在以下情况下指定自定义映射:
- 您知道硬件上某些量子比特的保真度更高,希望将重要的逻辑量子比特放置在那里。
- 您要运行多个线路并希望它们使用相同的映射以便比较结果。
2.6 指定基本门集
basic_gates 参数限制输出线路仅使用指定门集中的门。不在该门集中的任何门都将被分解:
# 以 IBM 风格的门集为目标
transpiled = transpiler.transpile(
prog, chip_edges, {}, 2,
basic_gates=["U3", "CNOT"]
)
# 以更简单的门集为目标(适用于分析)
transpiled = transpiler.transpile(
prog, chip_edges, {}, 2,
basic_gates=["H", "S", "T", "CNOT"]
)常见门集:
| 门集 | 门 | 硬件 |
|---|---|---|
| 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 为什么要分解门?
门分解有多个目的:
硬件兼容性: 真实的量子处理器实现一组固定的物理操作。该门集之外的任何门都必须表示为支持门的序列。
线路分析: 分解为标准门集使资源计数更容易(例如,实际需要多少双量子比特门)。
门数量优化: 像层次门
TOFFOLI这样的高级门在分解时需要 6 个 CNOT 门。了解这一点有助于估算线路成本。验证: 将酉矩阵分解为门,然后检查生成线路的矩阵是否与原始矩阵匹配,可以验证正确性。
分解规则遵循量子线路合成中的著名恒等式。例如:
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
# 构建包含非原生门的线路
prog = core.QProg()
prog << core.H(0)
prog << core.SWAP(0, 1) # SWAP 不在大多数原生门集中
prog << core.TOFFOLI(0, 1, 2) # TOFFOLI 总是会被分解
prog << core.measure([0, 1, 2], [0, 1, 2])
# 分解为 H + CNOT + 旋转门
decomposed = decompose(prog, ["H", "CNOT", "RZ", "X1"])
print(f"原始门数量: {prog.count_ops()}")
print(f"分解后门数量: {decomposed.count_ops()}")签名:
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
# 构建线路模块
circuit = core.QCircuit()
circuit << core.SWAP(0, 1)
circuit << core.CZ(1, 2)
# 分解为 CNOT + 单量子比特门
decomposed_circuit = decompose(circuit, ["H", "CNOT", "RZ", "X1"])
print(f"原始深度: {circuit.depth()}")
print(f"分解后深度: {decomposed_circuit.depth()}")签名:
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
# 定义一个 2x2 酉矩阵(单量子比特门)
# 这是一个相位门:diag(1, exp(i*pi/4))
mat = np.array([
[1, 0],
[0, np.exp(1j * np.pi / 4)]
], dtype=complex)
# 分解为作用于量子比特 0 的线路
circuit = decompose(mat, [0])
print(f"线路深度: {circuit.depth()}")
print(f"线路大小: {circuit.size()}")签名:
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
# 4x4 酉矩阵(双量子比特门)
# 创建受控相位门矩阵
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)
# 分解为作用于量子比特 0 和 1 的线路
circuit = decompose(cp_mat, [0, 1])
print(f"双量子比特线路深度: {circuit.depth()}")
print(f"双量子比特门数量: {circuit.count_ops(only_q2=True)}")验证:
您可以验证分解后的线路是否正确实现了原始矩阵:
import numpy as np
from pyqpanda3 import core
from pyqpanda3.transpilation import decompose
# 原始矩阵
mat = np.array([
[1, 0],
[0, np.exp(1j * np.pi / 4)]
], dtype=complex)
# 分解
circuit = decompose(mat, [0])
# 计算线路的酉矩阵
circuit_matrix = circuit.matrix()
# 检查是否匹配
print("矩阵匹配:", np.allclose(mat, circuit_matrix))
# 矩阵匹配: 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 完整编译工作流
以下是将线路从抽象表示编译为硬件就绪形式的完整工作流:
完整示例:
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler, generate_topology, decompose
# 步骤 1:构建抽象线路
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("=== 原始线路 ===")
print(f"深度: {prog.depth(only_q2=True)}")
print(f"门数: {prog.count_ops()}")
print(f"双量子比特门数: {prog.count_ops(only_q2=True)}")
# 步骤 2:定义芯片拓扑(4 量子比特线性链)
chip_edges = generate_topology(4, "linear")
print(f"\n芯片拓扑: {chip_edges}")
# 步骤 3:以优化级别 2 编译
transpiler = Transpiler()
transpiled = transpiler.transpile(
prog,
chip_edges,
{}, # 自动量子比特映射
2, # 积极优化
basic_gates=["H", "RZ", "X1", "CNOT"]
)
print("\n=== 编译后线路 ===")
print(f"深度: {transpiled.depth(only_q2=True)}")
print(f"门数: {transpiled.count_ops()}")
print(f"双量子比特门数: {transpiled.count_ops(only_q2=True)}")
# 步骤 4:在模拟器上验证正确性
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=== 验证 ===")
print(f"原始概率: {orig_probs}")
print(f"编译后概率: {transpiled_probs}")4.2 编译前后对比
编译后,务必在模拟器上对比原始线路和编译后的线路。测量概率应在统计噪声范围内匹配。第 4.1 节中展示的工作流演示了此模式:在 CPUQVM 上运行两个线路并比较 get_prob_dict() 输出。对于 10,000 次采样,每个比特串的概率应在约 0.05 的容差范围内一致。
4.3 面向真实硬件的编译
使用云模块面向真实量子处理器时,编译工作流与后端信息集成:
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler
from pyqpanda3.qcloud import QCloudService, QCloudOptions
# 连接到云端
service = QCloudService("your_api_key")
backend = service.backend("origin_wukong")
# 获取芯片配置
chip_info = backend.chip_info()
basic_gates = chip_info.get_basic_gates() # 例如 ["U3", "CZ"]
chip_backend = chip_info.get_chip_backend()
# 构建线路
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1) << core.CNOT(1, 2)
prog << core.SWAP(0, 2)
prog << core.measure([0, 1, 2], [0, 1, 2])
# 为目标硬件编译
transpiler = Transpiler()
transpiled = transpiler.transpile(prog, chip_backend, {}, 2)
# 提交到硬件
options = QCloudOptions()
options.set_amend(False) # 已编译
instruction_json = transpiled.to_instruction(chip_backend)
job = backend.run_instruction(instruction_json, 3000, options)本地编译(而非依赖服务器端编译)的关键优势在于对优化级别、初始映射和基本门集的精细控制。
5. 实际示例
5.1 编译 GHZ 线路
GHZ(Greenberger-Horne-Zeilinger)态是一种多量子比特最大纠缠态。由于物理量子比特的连接性受限,在真实硬件上制备 GHZ 态需要编译器通过芯片拓扑路由 CNOT 门。
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler, generate_topology
# 构建 4 量子比特 GHZ 态线路
# 逻辑线路: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 态线路 ===")
print(f"逻辑深度: {prog.depth(only_q2=True)}")
print(f"逻辑门数: {prog.count_ops()}")
# 为线性链拓扑编译
# 量子比特 0 必须与量子比特 1、2、3 交互
# 在线性链中:0-1-2-3
# CNOT(0,2) 和 CNOT(0,3) 需要 SWAP 路由
chip_edges = generate_topology(4, "linear")
transpiler = Transpiler()
transpiled = transpiler.transpile(prog, chip_edges, {}, 2)
print("\n=== 编译后 GHZ 线路 ===")
print(f"物理深度: {transpiled.depth(only_q2=True)}")
print(f"物理门数: {transpiled.count_ops()}")
print(f"物理双量子比特门数: {transpiled.count_ops(only_q2=True)}")
# 验证
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"\n原始 GHZ: {orig}")
print(f"编译后 GHZ: {trans}")在线性链上,编译器必须通过量子比特 1 和 2 路由 CNOT(0,3) 交互。这会引入额外的 SWAP 门,增加线路深度。在全连接拓扑上,不需要路由:
# 全连接不需要路由
full_topo = generate_topology(4, "all-to-all")
transpiled_full = transpiler.transpile(prog, full_topo, {}, 2)
print(f"\n全连接深度: {transpiled_full.depth(only_q2=True)}")5.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"编译时间: {elapsed:.2f} ms")
print(f"编译前深度: {prog.depth(only_q2=True)}, 编译后深度: {transpiled.depth(only_q2=True)}")5.3 分解酉矩阵
将酉矩阵分解为线路,然后验证生成的线路是否与原始矩阵匹配:
import numpy as np
from pyqpanda3.transpilation import decompose
# 自定义 U3 矩阵
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("正确:", np.allclose(u3_mat, circuit.matrix()))5.4 自定义拓扑和门集
以下示例展示如何将自定义 T 形拓扑与特定门集结合使用:
from pyqpanda3 import core
from pyqpanda3.transpilation import Transpiler, decompose
# T 形拓扑: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) # 需要路由:0->2->1
prog << core.CNOT(3, 4) # 需要路由: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"原始: {prog.count_ops()} 个门")
print(f"编译后: {transpiled.count_ops()} 个门, {transpiled.count_ops(only_q2=True)} 个双量子比特门")
# 与仅分解(无路由)对比
decomposed_only = decompose(prog, ["H", "RZ", "X1", "CNOT"])
print(f"仅分解: {decomposed_only.count_ops()} 个门")6. 性能考虑
编译时间取决于线路大小、拓扑稀疏性和优化级别。对于高达 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 文档