量子线路构建
使用 pyqpanda3 中的 QProg、QCircuit 和 << 运算符构建量子程序和可复用线路。
概述
pyqpanda3 提供两个主要的线路构建容器:
- QProg -- 顶层量子程序(quantum program)容器,持有门、线路、测量和控制流。它是你传递给模拟器或云后端执行的单元。
- QCircuit -- 可复用的、可组合的线路模块(linear module),封装一组量子门序列。可以将它想象为可嵌入任意多个程序中的命名子程序。
两个容器共享相同的追加机制:<<(左移)运算符。该运算符将门、线路甚至其他程序链接为单一的线性操作序列。
1. QProg -- 量子程序
QProg 是可执行量子程序的主容器。你创建门,将它们追加到程序中,然后在模拟器或云后端上执行该程序。
1.1 创建 QProg
from pyqpanda3 import core
# 空程序 -- 量子比特数从你添加的门中推断
prog = core.QProg()
# 预分配 3 个量子比特(和 0 个经典比特)的程序
prog = core.QProg(3)当你创建空的 QProg() 时,系统会根据你追加的门和测量自动跟踪你使用的量子比特。使用 QProg(n) 预分配会提前预留 n 个量子比特。
1.2 使用 << 追加操作
<< 运算符是构建程序的主要方式。它追加门、线路、测量甚至其他程序,并始终返回程序本身以支持链式调用:
from pyqpanda3 import core
# 创建程序并构建贝尔态
prog = core.QProg()
prog << core.H(0) # 量子比特 0 上的 Hadamard
prog << core.CNOT(0, 1) # CNOT: 控制=0, 目标=1
prog << core.measure([0, 1], [0, 1]) # 测量量子比特 [0,1] 存入经典比特 [0,1]
# 等效写法:在一个表达式中链式调用
prog2 = core.QProg()
prog2 << core.H(0) << core.CNOT(0, 1) << core.measure([0, 1], [0, 1])数据通过运算符链的流程如下图所示:
你也可以嵌入可复用线路或吸收其他程序:
# 嵌入 QCircuit
bell = core.QCircuit()
bell << core.H(0) << core.CNOT(0, 1)
prog = core.QProg()
prog << bell # 追加线路
prog << core.measure([0, 1], [0, 1])
# 吸收另一个 QProg
prep = core.QProg()
prep << core.H(0)
main_prog = core.QProg()
main_prog << prep # 吸收子程序
main_prog << core.CNOT(0, 1)1.3 QProg 方法
构建程序后,有多个方法可以检查和转换它。
qubits_num() 和 cbits_num()
返回程序使用的量子比特数和经典比特数:
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])
print(f"量子比特数: {prog.qubits_num()}") # 量子比特数: 2
print(f"经典比特数: {prog.cbits_num()}") # 经典比特数: 2depth()
返回线路深度(depth)——即顺序门层数。作用于不同量子比特的门可以并行执行,计为同一层。使用 only_q2=True 仅计算两量子比特门的深度:
prog = core.QProg()
prog << core.H(0) << core.H(1) # 深度 1:两个 H 作用于不同量子比特
prog << core.CNOT(0, 1) # 深度 2:CNOT 依赖两个量子比特
print(f"深度: {prog.depth()}") # 深度: 2
print(f"深度(仅两量子比特): {prog.depth(only_q2=True)}") # 深度: 1count_ops()
按类型统计门操作数量,返回字典(门名称 → 计数)。使用 only_q2=True 仅统计两量子比特门:
prog = core.QProg()
prog << core.H(0) << core.H(1) << core.CNOT(0, 1) << core.RX(0, 1.57)
print(f"所有门: {prog.count_ops()}") # 所有门: {'H': 2, 'CNOT': 1, 'RX': 1}
print(f"两量子比特门: {prog.count_ops(only_q2=True)}") # 两量子比特门: {'CNOT': 1}flatten()
递归展开所有嵌套线路和子程序为单一平面操作序列:
inner = core.QCircuit()
inner << core.H(0) << core.CNOT(0, 1)
prog = core.QProg()
prog << inner << core.H(2)
flat = prog.flatten() # 新的 QProg,所有嵌套已移除to_circuit()
将程序转换为 QCircuit 对象,用于需要线路格式的分析工具:
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
circuit = prog.to_circuit()
print(f"线路量子比特: {list(circuit.qubits())}")gate_operations() 和 operations()
获取程序中门和操作的详细信息:
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1) << core.measure([0, 1], [0, 1])
# 门级详情
for g in prog.gate_operations():
print(f"门: {g.name()}, 量子比特: {g.qubits()}")
# 所有操作(包括测量)
for op in prog.operations():
print(f"操作类型: {type(op).__name__}")get_measure_nodes()
从程序中提取测量节点,查看哪些量子比特被测量并存入哪些经典比特:
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1) << core.measure([0, 1], [0, 1])
for node in prog.get_measure_nodes():
print(f"测量节点: {node}")remap()
重映射程序中的量子比特索引。当在不同物理量子比特上运行相同逻辑线路时很有用:
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
remapped = prog.remap({0: 3, 1: 5}) # 量子比特 0 -> 3, 量子比特 1 -> 5
print(f"重映射后的量子比特数: {remapped.qubits_num()}")originir()
将程序序列化为 OriginIR 字符串格式——pyqpanda3 使用的原生中间表示(intermediate representation):
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
prog << core.measure([0, 1], [0, 1])
print(prog.originir())
# QINIT 2
# CREG 2
# H(q[0])
# CNOT(q[0], q[1])
# MEASURE(q[0], c[0])
# MEASURE(q[1], c[1])2. QCircuit -- 可复用线路模块
QCircuit 是用于量子门序列的可组合容器。与 QProg 不同,它不持有测量或经典控制流。它的主要目的是让你定义可复用的构建块,嵌入到多个程序中。
2.1 创建和构建 QCircuit
from pyqpanda3 import core
# 空线路
circ = core.QCircuit()
# 预分配 3 个量子比特的线路
circ = core.QCircuit(3)
# 使用 << 或 append() 构建
bell = core.QCircuit()
bell << core.H(0) << core.CNOT(0, 1)
# append() 是编程式构建的替代方法
circ2 = core.QCircuit()
circ2.append(core.H(0))
circ2.append(core.CNOT(0, 1))2.2 QCircuit 方法
size() 和 depth()
circ = core.QCircuit()
circ << core.H(0) << core.H(1) << core.CNOT(0, 1)
print(f"门数: {circ.size()}") # 门数: 3
print(f"深度: {circ.depth()}") # 深度: 2qubits()
返回线路使用的量子比特索引集合:
circ = core.QCircuit()
circ << core.H(0) << core.CNOT(0, 2)
print(f"使用的量子比特: {list(circ.qubits())}") # 使用的量子比特: [0, 2]count_ops() 和 num_2q_gate()
circ = core.QCircuit()
circ << core.H(0) << core.H(1) << core.CNOT(0, 1) << core.SWAP(1, 2)
print(f"所有门: {circ.count_ops()}") # 所有门: {'H': 2, 'CNOT': 1, 'SWAP': 1}
print(f"仅两量子比特: {circ.count_ops(only_q2=True)}") # 仅两量子比特: {'CNOT': 1, 'SWAP': 1}
print(f"num_2q_gate: {circ.num_2q_gate()}") # num_2q_gate: 2dagger()
返回线路的伴随(adjoint)(共轭转置)。这会反转门的顺序并将每个门替换为其伴随——对反计算(uncomputation)很有用:
circ = core.QCircuit()
circ << core.H(0) << core.S(1) << core.CNOT(0, 1)
inv_circ = circ.dagger()
# inv_circ = CNOT(0,1) -> Sdg(1) -> H(0)control()
返回线路的受控版本。每个门获得一个额外的控制量子比特:
circ = core.QCircuit()
circ << core.H(0) << core.CNOT(0, 1)
controlled = circ.control([2])
# 整个线路仅在量子比特 2 为 |1> 时执行expand()
递归展开嵌套线路为平面门序列:
inner = core.QCircuit()
inner << core.H(0) << core.CNOT(0, 1)
outer = core.QCircuit()
outer << inner << core.H(2)
expanded = outer.expand()matrix()
计算线路的酉矩阵表示(仅对小线路可行,因为维度以
circ = core.QCircuit()
circ << core.H(0) << core.CNOT(0, 1)
U = circ.matrix()
print(f"矩阵形状: {U.shape}") # 矩阵形状: (4, 4)对于贝尔态线路,酉矩阵为:
set_name() 和 originir()
circ = core.QCircuit(3)
circ << core.H(0) << core.CNOT(0, 1) << core.CNOT(1, 2)
circ.set_name("GHZ-3")
print(circ) # 文本可视化
print(circ.originir()) # H(q[0])\nCNOT(q[0],q[1])\nCNOT(q[1],q[2])2.3 复用线路
定义一次构建块,随处使用:
from pyqpanda3 import core
def rotation_block(qc: int, qt: int, angle: float) -> core.QCircuit:
block = core.QCircuit()
block << core.H(qt)
block << core.CU(qc, qt, 0, 0, angle, 0)
return block
# 使用可复用构建块构建 3 量子比特 QFT
qft = core.QCircuit(3)
qft << core.H(0)
qft << rotation_block(0, 1, 1.5708) # pi/2
qft << rotation_block(0, 2, 0.7854) # pi/4
qft << core.H(1)
qft << rotation_block(1, 2, 1.5708) # pi/2
qft << core.H(2)
qft << core.SWAP(0, 2)
# 在不同程序中使用相同的 QFT
prog1 = core.QProg()
prog1 << qft << core.measure([0, 1, 2], [0, 1, 2])
prog2 = core.QProg()
prog2 << core.X(0) << qft << core.measure([0, 1, 2], [0, 1, 2])3. << 运算符 -- 追加操作
<< 运算符是构建量子程序和线路的核心机制。它在 QProg 和 QCircuit 上都有定义,接受多种类型的右侧操作数。
3.1 可以追加的内容
| 右侧操作数 | 示例 | 描述 |
|---|---|---|
QGate | prog << core.H(0) | 追加单个量子门 |
QCircuit | prog << circuit | 追加线路中的所有门 |
QProg | prog << other_prog | 吸收另一个程序的操作 |
| 测量结果 | prog << core.measure([0], [0]) | 追加测量操作 |
3.2 追加门
from pyqpanda3 import core
prog = core.QProg()
prog << core.H(0)
prog << core.X(1) << core.Y(2) << core.Z(3)
prog << core.RX(0, 3.14159)
prog << core.U3(1, 0.5, 0.3, 0.1)3.3 追加线路和程序
# 追加线路
swap_circ = core.QCircuit()
swap_circ << core.CNOT(0, 1) << core.CNOT(1, 0) << core.CNOT(0, 1)
prog = core.QProg()
prog << core.H(0) << swap_circ
# 追加子程序
prep = core.QProg()
prep << core.H(0) << core.H(1) << core.H(2)
main = core.QProg()
main << prep << core.CNOT(0, 1)3.4 追加测量
core.measure() 函数创建将量子比特映射到经典比特的测量节点:
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)
# 测量量子比特 [0, 1] 存入经典比特 [0, 1]
prog << core.measure([0, 1], [0, 1])
# 测量单个量子比特
prog << core.measure(0, 0)3.5 运算符链流程
在内部,每次 << 调用的工作方式如下:
4. 门构造
pyqpanda3 提供 37+ 种量子门,组织为单量子比特、两量子比特和多量子比特类别。所有门都作为 core 模块中的工厂函数构造。
4.1 单量子比特门
无参数门
from pyqpanda3 import core
# 泡利门
core.X(0) # 泡利-X(比特翻转)
core.Y(0) # 泡利-Y
core.Z(0) # 泡利-Z(相位翻转)
# Clifford 门
core.H(0) # Hadamard
core.S(0) # S 门(Z 的平方根)
core.T(0) # T 门(S 的平方根)
# 恒等门和变体
core.I(0) # 恒等门
core.X1(0) # X1 门
core.Y1(0) # Y1 门
core.Z1(0) # Z1 门
# 特殊门
core.ECHO(0) # 回波门(带可选参数)
core.BARRIER([0]) # 屏障(阻止跨此点的优化)参数化门
import math
# 旋转门
core.RX(0, math.pi / 2) # 绕 X 轴旋转
core.RY(0, math.pi / 4) # 绕 Y 轴旋转
core.RZ(0, math.pi / 3) # 绕 Z 轴旋转
# 相位门
core.P(0, math.pi / 6) # 相位门(替代:RPhi)
# 通用酉门
core.U1(0, math.pi / 4) # 1 参数酉门
core.U2(0, 0, math.pi / 2) # 2 参数酉门
core.U3(0, 0.1, 0.2, 0.3) # 3 参数酉门
core.U4(0, 0.1, 0.2, 0.3, 0.4) # 4 参数酉门(最通用的单量子比特门)旋转门遵循标准定义:
4.2 两量子比特门
无参数门
core.CNOT(0, 1) # 受控-X: 控制=0, 目标=1
core.CZ(0, 1) # 受控-Z
core.SWAP(0, 1) # 交换量子比特 0 和 1
core.ISWAP(0, 1) # iSWAP 门
core.SQISWAP(0, 1) # iSWAP 的平方根
core.MS(0, 1) # Molmer-Sorensen(离子阱)参数化门
import math
core.CRX(0, 1, math.pi / 2) # 受控 RX
core.CRY(0, 1, math.pi / 3) # 受控 RY
core.CRZ(0, 1, math.pi / 4) # 受控 RZ
core.CP(0, 1, math.pi / 6) # 受控相位
core.CU(0, 1, 0.1, 0.2, 0.3, 0.4) # 4 参数受控酉门
core.RXX(0, 1, math.pi / 4) # XX 旋转
core.RYY(0, 1, math.pi / 4) # YY 旋转
core.RZZ(0, 1, math.pi / 4) # ZZ 旋转
core.RZX(0, 1, math.pi / 4) # ZX 旋转两量子比特旋转门:
4.3 多量子比特门
core.TOFFOLI(0, 1, 2) # CCX: 当控制 0, 1 都为 |1> 时翻转目标 24.4 特殊门
BARRIER 阻止编译器在屏障点之间合并或重排门:
prog = core.QProg()
prog << core.H(0) << core.H(1)
prog << core.BARRIER([0, 1]) # 不跨此线进行优化
prog << core.CNOT(0, 1)ECHO 是用于某些脉冲级校准的专用门。它接受可选参数:
echo_gate = core.ECHO(0)
echo_gate.set_parameters([1.5]) # 设置自定义参数Oracle 从真值表(truth table)或二值函数构造量子预言机(oracle):
oracle = core.Oracle(qubits, truth_table)create_gate() 从类型枚举和参数构造门:
gate = core.create_gate("H", [0], []) # 量子比特 0 上的 H 门
gate = core.create_gate("RX", [0], [1.57]) # 带角度的 RX 门4.5 门自省和变换
每个门对象都支持自省(introspection):
gate = core.RX(0, 1.57)
print(gate.name()) # "RX"
print(gate.gate_type()) # GateType.RX
print(gate.target_qubits()) # [0]
print(gate.control_qubits()) # []
print(gate.parameters()) # [1.57]
print(gate.qubits()) # [0]每个门也支持 dagger()、control() 和 matrix():
import numpy as np
# dagger() 返回伴随(逆)
gate = core.RX(0, 1.57)
inv = gate.dagger()
m1, m2 = gate.matrix(), inv.matrix()
print(np.allclose(np.dot(m1, m2), np.eye(2))) # True
# control() 返回受控版本
ch = core.H(0).control([2])
print(ch.qubits()) # [2, 0]
# matrix() 计算酉矩阵
print(f"H 矩阵:\n{core.H(0).matrix()}")
print(f"CNOT 矩阵形状: {core.CNOT(0, 1).matrix().shape}") # (4, 4)5. 基于 DAG 的线路分析
pyqpanda3 提供 DAGQCircuit 用于有向无环图(Directed Acyclic Graph)分析量子线路。这支持层分解(layer decomposition)、依赖分析和关键路径计算。
5.1 什么是 DAG?
DAG 表示线路中门的依赖结构。每个门是一个节点,边表示量子比特依赖:如果门 B 使用门 A 在同一量子比特上的输出,则从 A 到 B 有一条边。
5.2 构建 DAGQCircuit
from pyqpanda3 import core
circ = core.QCircuit()
circ << core.H(0) << core.H(1) << core.CNOT(0, 1) << core.X(2)
# 直接从 QCircuit
dag = core.DAGQCircuit()
dag.from_circuit(circ)
dag.build()
# 通过 to_circuit() 从 QProg
prog = core.QProg()
prog << circ
dag2 = core.DAGQCircuit()
c = prog.to_circuit()
if c is not None:
dag2.from_circuit(c)
dag2.build()5.3 检查 DAG
# 所有门
for gate in dag.gates():
print(f"{gate.name()} 作用于量子比特 {gate.qubits()}")
# 节点和边
print(f"节点: {list(dag.nodes())}")
print(f"边: {list(dag.edges())}")
# 特定节点索引处的门
for idx in dag.nodes():
gate = dag.get_gate(idx)
print(f"节点 {idx}: {gate.name()}{gate.qubits()}")5.4 层分析
dag = core.DAGQCircuit()
circ = core.QCircuit()
circ << core.H(0) << core.H(1) << core.H(2)
circ << core.CNOT(0, 1) << core.CNOT(1, 2)
dag.from_circuit(circ)
dag.build()
print(f"DAG 深度: {dag.get_depth()}") # 3
for i, layer in enumerate(dag.layers()):
names = [dag.get_gate(idx).name() for idx in layer]
print(f"层 {i}: {names}")
# 层 0: ['H', 'H', 'H']
# 层 1: ['CNOT']
# 层 2: ['CNOT']5.5 关键路径和两量子比特门分析
# 最长路径 = 关键路径
path = dag.longest_path()
for node in path:
gate = dag.get_gate(node)
print(f" {gate.name()} 作用于量子比特 {gate.qubits()}")
# 两量子比特门分析
two_q_gates = dag.two_qubit_gates()
for gate in two_q_gates:
if gate.is_controlled():
print(f" 受控门: 控制={gate.control_qubits()}, 目标={gate.target_qubits()}")
else:
print(f" 非受控门: 量子比特={gate.target_qubits()}")
two_q_nodes = list(dag.two_qubit_gate_nodes())5.6 实际示例:比较线路
from pyqpanda3 import core
# 使用 3 个 CNOT 的朴素 SWAP 与原生 SWAP 门
swap_naive = core.QCircuit()
swap_naive << core.CNOT(0, 1) << core.CNOT(1, 0) << core.CNOT(0, 1)
swap_native = core.QCircuit()
swap_native << core.SWAP(0, 1)
for name, circ in [("朴素 SWAP", swap_naive), ("原生 SWAP", swap_native)]:
dag = core.DAGQCircuit()
dag.from_circuit(circ)
dag.build()
print(f"--- {name} ---")
print(f" 门数: {len(dag.gates())}, 深度: {dag.get_depth()}, "
f"两量子比特门: {len(dag.two_qubit_gates())}")6. 完整示例
6.1 带完整分析的贝尔态
from pyqpanda3 import core
bell = core.QCircuit()
bell << core.H(0) << core.CNOT(0, 1)
prog = core.QProg()
prog << bell << core.measure([0, 1], [0, 1])
# 统计
print(f"量子比特: {prog.qubits_num()}, 深度: {prog.depth()}, "
f"门数: {prog.count_ops()}, 两量子比特门: {prog.count_ops(only_q2=True)}")
# 门数: {'H': 1, 'CNOT': 1}, 两量子比特门: {'CNOT': 1}
# DAG 分析
dag = core.DAGQCircuit()
dag.from_circuit(bell)
dag.build()
print(f"DAG 深度: {dag.get_depth()}")
for i, layer in enumerate(dag.layers()):
print(f" 层 {i}: {[dag.get_gate(idx).name() for idx in layer]}")
# 在模拟器上运行
machine = core.CPUQVM()
machine.run(prog, 1000)
print(f"结果: {machine.result().get_counts()}")
print(f"OriginIR:\n{prog.originir()}")6.2 反计算模式
from pyqpanda3 import core
compute = core.QCircuit()
compute << core.CNOT(0, 2) << core.CNOT(1, 2) # 异或到量子比特 2
use = core.QCircuit()
use << core.Z(2)
uncompute = compute.dagger()
prog = core.QProg()
prog << compute << use << uncompute
prog << core.measure([0, 1, 2], [0, 1, 2])
machine = core.CPUQVM()
machine.run(prog, 1000)
# 辅助量子比特 2 应该总是回到 |0>6.3 受控线路
from pyqpanda3 import core
sub = core.QCircuit()
sub << core.H(0) << core.RX(1, 0.5) << core.CNOT(0, 1)
controlled_sub = sub.control([3])
prog = core.QProg()
prog << core.H(3) << controlled_sub
prog << core.measure([0, 1, 3], [0, 1, 3])6.4 量子比特重映射
from pyqpanda3 import core
logical = core.QProg()
logical << core.H(0) << core.CNOT(0, 1) << core.measure([0, 1], [0, 1])
physical = logical.remap({0: 5, 1: 7}) # 映射到物理量子比特 5, 7
print(f"物理 OriginIR:\n{physical.originir()}")7. API 快速参考
QProg
| 方法 | 返回值 | 描述 |
|---|---|---|
core.QProg() / core.QProg(n) | QProg | 创建空或预分配程序 |
prog << x | QProg | 追加门、线路、程序或测量 |
prog.qubits_num() | int | 使用的量子比特数 |
prog.cbits_num() | int | 使用的经典比特数 |
prog.depth(only_q2) | int | 线路深度(可选仅计算两量子比特门) |
prog.count_ops(only_q2) | dict[str, int] | 按名称统计门计数(可选仅两量子比特) |
prog.flatten() | QProg | 展平所有嵌套子线路 |
prog.to_circuit() | QCircuit | 转换为 QCircuit |
prog.gate_operations() | list | 门操作列表 |
prog.operations() | list | 所有操作列表 |
prog.get_measure_nodes() | list | 提取测量节点 |
prog.remap(mapping) | QProg | 重映射量子比特索引 |
prog.originir() | str | 序列化为 OriginIR 字符串 |
QCircuit
| 方法 | 返回值 | 描述 |
|---|---|---|
core.QCircuit() / core.QCircuit(n) | QCircuit | 创建空或预分配线路 |
circ << x / circ.append(gate) | QCircuit | 追加门、线路或程序 |
circ.size() | int | 总门数 |
circ.depth() | int | 线路深度 |
circ.qubits() | set | 使用的量子比特索引集合 |
circ.count_ops(only_q2) | dict[str, int] | 按名称统计门计数(可选仅两量子比特) |
circ.num_2q_gate() | int | 两量子比特门数 |
circ.dagger() | QCircuit | 伴随(逆)线路 |
circ.control(qubits) | QCircuit | 线路的受控版本 |
circ.expand() | QCircuit | 展开嵌套子线路 |
circ.matrix() | ndarray | 酉矩阵表示 |
circ.set_name(name) | None | 设置人类可读名称 |
circ.originir() | str | 序列化为 OriginIR |
DAGQCircuit
| 方法 | 返回值 | 描述 |
|---|---|---|
dag.from_circuit(circ) | None | 从 QCircuit 加载门 |
dag.build() | None | 构建 DAG 边 |
dag.gates() | list | 所有门对象 |
dag.nodes() / dag.edges() | list | 节点索引 / 边对 |
dag.get_gate(idx) | QGate | 节点索引处的门 |
dag.layers() | iterator | 门层(并行组) |
dag.get_depth() | int | DAG 深度(层数) |
dag.longest_path() | list | 通过 DAG 的关键路径 |
dag.two_qubit_gates() | list | 所有两量子比特门对象 |
dag.two_qubit_gate_nodes() | list | 两量子比特门的节点索引 |
门工厂函数
| 类别 | 门 |
|---|---|
| 1Q 无参数 | H、X、Y、Z、S、T、I、X1、Y1、Z1、ECHO |
| 1Q 参数化 | RX、RY、RZ、P、RPhi、U1、U2、U3、U4 |
| 2Q 无参数 | CNOT、CZ、SWAP、ISWAP、SQISWAP、MS |
| 2Q 参数化 | CP、CR、CRX、CRY、CRZ、CU、RXX、RYY、RZZ、RZX |
| 多量子比特 | TOFFOLI |
| 特殊 | BARRIER、ECHO、Oracle、create_gate() |
总结
在本教程中你学到了:
QProg 是可执行量子程序容器。使用
<<追加门、线路、测量和其他程序。用depth()、count_ops()、qubits_num()等方法检查它。QCircuit 是可复用线路模块。它支持相同的
<<运算符以及额外方法如dagger()、control()、matrix()和num_2q_gate()。<<运算符 是通用追加机制。它返回容器本身,支持流式链式调用如prog << core.H(0) << core.CNOT(0, 1)。门构造 涵盖 37+ 种门,从单量子比特(
H、X、RX、U3)到两量子比特(CNOT、SWAP、CU)再到多量子比特(TOFFOLI),加上特殊门如BARRIER和Oracle。DAGQCircuit 支持高级线路分析:通过有向无环图表示进行层分解、关键路径查找、两量子比特门提取和深度计算。
系列中的下一个教程是模拟,你将学习如何在各种模拟器后端上执行这里构建的程序。
知识检查
测试你对 pyqpanda3 中线路构建的理解。
Q1: QProg 和 QCircuit 有什么区别?分别在什么场景使用?
A1: QProg 是顶层可执行量子程序容器——它可以持有门、线路、测量和控制流。QCircuit 是可复用线路模块,支持额外操作如 dagger()、control() 和 matrix()。使用 QProg 作为主程序包装器,使用 QCircuit 构建可反转、受控或组合的可复用子线路。
Q2: << 运算符返回什么?为什么它有用?
A2: << 运算符返回容器(QProg 或 QCircuit)本身,支持方法链式调用。这允许流式构建如 prog << H(0) << CNOT(0, 1) << measure([0, 1], [0, 1]),无需为每一步使用中间变量。
Q3: 如何创建线路的伴随(逆)?给出一个实际用例的例子。
A3: 在 QCircuit 上调用 .dagger():inverse_cir = cir.dagger()。这对于反计算(uncomputation)至关重要——在使用辅助量子比特(ancilla qubit)进行计算后,施加逆线路将它们恢复到 dagger()。
Q4: 解释 BARRIER、ECHO 和 IDLE 门之间的关系。为什么它们没有数学效果?
A4: 三者都是具有恒等酉矩阵(BARRIER 阻止优化期间跨它的门融合,ECHO 表示用于噪声抑制的自旋回波序列,IDLE 表示时间延迟。它们影响编译和调度,但不影响数学态演化。
Q5: 什么是 DAGQCircuit?与平面线路表示相比,它有什么优势?
A5: DAGQCircuit 将线路表示为有向无环图,其中门是节点,依赖(共享量子比特)是边。这支持高级分析:寻找关键路径(线路深度)、层分解用于并行门执行、计算两量子比特门、以及在转译期间进行拓扑感知路由。
Q6: 如何使用现有 pyqpanda3 原语构建受控 SWAP(Fredkin)门?
A6: Fredkin 门(受控 SWAP)可以分解为:CNOT(b, c) << TOFFOLI(a, c, b) << CNOT(b, c),其中 a 是控制,b、c 是 SWAP 目标。或者,使用 SWAP(1, 2).control(0) 创建 SWAP 线路的受控版本。
练习 1:可复用线路模块
创建一个参数化"量子加法器"线路,将编码在量子比特中的两个 2 位数相加。演示在程序中使用 dagger() 进行反计算。
解答:
from pyqpanda3.core import CPUQVM, QProg, QCircuit, CNOT, TOFFOLI, measure
def quantum_adder_2bit(a0, a1, b0, b1, carry):
"""构建 2 位行波进位加法器线路。
输入:a = (a1, a0), b = (b1, b0)
输出:a + b 存储在 b 量子比特中,进位在 carry 量子比特中
"""
cir = QCircuit()
# 第 0 位的半加器
cir << CNOT(a0, b0)
# 第 1 位的全加器(简化版)
cir << TOFFOLI(a0, b0, carry)
cir << CNOT(a0, b1)
cir << CNOT(b0, b1)
cir << TOFFOLI(a1, b1, carry)
return cir
qvm = CPUQVM()
# 测试:2 + 1 = 3 (a=10, b=01, 结果应为 b=11, carry=0)
prog = QProg()
from pyqpanda3.core import X
# 编码 a=2(二进制 10)
prog << X(1) # a1=1
# 编码 b=1(二进制 01)
prog << X(3) # b0=1
# 施加加法器
adder = quantum_adder_2bit(0, 1, 2, 3, 4)
prog << adder
prog << measure([2, 3, 4], [0, 1, 2])
qvm.run(prog, shots=100)
counts = qvm.result().get_counts()
print(f"2+1 结果: {counts}")
# 通过 dagger 反计算
prog2 = QProg()
prog2 << X(1) << X(3)
prog2 << adder
prog2 << adder.dagger() # 反计算
prog2 << measure([2, 3, 4], [0, 1, 2])
qvm.run(prog2, shots=100)
counts2 = qvm.result().get_counts()
print(f"反计算后: {counts2}")
# 应显示原始 b 值已恢复练习 2:DAGQCircuit 分析
构建一个 4 量子比特线路,将其转换为 DAGQCircuit,并分析其属性:深度、两量子比特门数和层结构。
解答:
from pyqpanda3.core import CPUQVM, QProg, QCircuit, DAGQCircuit
from pyqpanda3.core import H, X, CNOT, SWAP, CZ, TOFFOLI, measure
# 构建混合门类型的线路
cir = QCircuit()
cir << H(0) << H(1) << CNOT(0, 1) << CNOT(2, 3)
cir << SWAP(1, 2) << CZ(0, 3)
cir << TOFFOLI(0, 1, 2) << H(3)
dag = DAGQCircuit(circuit=cir)
# 分析属性
print("DAG 线路分析:")
print(f" 深度: {dag.get_depth()}")
print(f" 总门数: {dag.get_num_gates()}")
print(f" 两量子比特门: {len(dag.two_qubit_gates())}")
# 层分解
layers = dag.layers()
for i, layer in enumerate(layers):
gate_names = [str(dag.get_gate(idx)) for idx in layer]
print(f" 层 {i}: {gate_names}")