Skip to content

量子线路构建

使用 pyqpanda3 中的 QProgQCircuit<< 运算符构建量子程序和可复用线路。


概述

pyqpanda3 提供两个主要的线路构建容器:

  • QProg -- 顶层量子程序(quantum program)容器,持有门、线路、测量和控制流。它是你传递给模拟器或云后端执行的单元。
  • QCircuit -- 可复用的、可组合的线路模块(linear module),封装一组量子门序列。可以将它想象为可嵌入任意多个程序中的命名子程序。

两个容器共享相同的追加机制:<<(左移)运算符。该运算符将门、线路甚至其他程序链接为单一的线性操作序列。


1. QProg -- 量子程序

QProg 是可执行量子程序的主容器。你创建门,将它们追加到程序中,然后在模拟器或云后端上执行该程序。

1.1 创建 QProg

python
from pyqpanda3 import core

# 空程序 -- 量子比特数从你添加的门中推断
prog = core.QProg()

# 预分配 3 个量子比特(和 0 个经典比特)的程序
prog = core.QProg(3)

当你创建空的 QProg() 时,系统会根据你追加的门和测量自动跟踪你使用的量子比特。使用 QProg(n) 预分配会提前预留 n 个量子比特。

1.2 使用 << 追加操作

<< 运算符是构建程序的主要方式。它追加门、线路、测量甚至其他程序,并始终返回程序本身以支持链式调用:

python
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])

数据通过运算符链的流程如下图所示:

你也可以嵌入可复用线路或吸收其他程序:

python
# 嵌入 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()

返回程序使用的量子比特数和经典比特数:

python
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()}")   # 经典比特数: 2

depth()

返回线路深度(depth)——即顺序门层数。作用于不同量子比特的门可以并行执行,计为同一层。使用 only_q2=True 仅计算两量子比特门的深度:

python
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)}")  # 深度: 1

count_ops()

按类型统计门操作数量,返回字典(门名称 → 计数)。使用 only_q2=True 仅统计两量子比特门:

python
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()

递归展开所有嵌套线路和子程序为单一平面操作序列:

python
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 对象,用于需要线路格式的分析工具:

python
prog = core.QProg()
prog << core.H(0) << core.CNOT(0, 1)

circuit = prog.to_circuit()
print(f"线路量子比特: {list(circuit.qubits())}")

gate_operations() 和 operations()

获取程序中门和操作的详细信息:

python
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()

从程序中提取测量节点,查看哪些量子比特被测量并存入哪些经典比特:

python
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()

重映射程序中的量子比特索引。当在不同物理量子比特上运行相同逻辑线路时很有用:

python
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):

python
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

python
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()

python
circ = core.QCircuit()
circ << core.H(0) << core.H(1) << core.CNOT(0, 1)

print(f"门数: {circ.size()}")  # 门数: 3
print(f"深度: {circ.depth()}")  # 深度: 2

qubits()

返回线路使用的量子比特索引集合:

python
circ = core.QCircuit()
circ << core.H(0) << core.CNOT(0, 2)
print(f"使用的量子比特: {list(circ.qubits())}")  # 使用的量子比特: [0, 2]

count_ops() 和 num_2q_gate()

python
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: 2

dagger()

返回线路的伴随(adjoint)(共轭转置)。这会反转门的顺序并将每个门替换为其伴随——对反计算(uncomputation)很有用:

python
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()

返回线路的受控版本。每个门获得一个额外的控制量子比特:

python
circ = core.QCircuit()
circ << core.H(0) << core.CNOT(0, 1)

controlled = circ.control([2])
# 整个线路仅在量子比特 2 为 |1> 时执行

expand()

递归展开嵌套线路为平面门序列:

python
inner = core.QCircuit()
inner << core.H(0) << core.CNOT(0, 1)

outer = core.QCircuit()
outer << inner << core.H(2)

expanded = outer.expand()

matrix()

计算线路的酉矩阵表示(仅对小线路可行,因为维度以 2n 增长):

python
circ = core.QCircuit()
circ << core.H(0) << core.CNOT(0, 1)

U = circ.matrix()
print(f"矩阵形状: {U.shape}")  # 矩阵形状: (4, 4)

对于贝尔态线路,酉矩阵为:

UBell=CNOT(HI)=12(1001011001101001)

set_name() 和 originir()

python
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 复用线路

定义一次构建块,随处使用:

python
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. << 运算符 -- 追加操作

<< 运算符是构建量子程序和线路的核心机制。它在 QProgQCircuit 上都有定义,接受多种类型的右侧操作数。

3.1 可以追加的内容

右侧操作数示例描述
QGateprog << core.H(0)追加单个量子门
QCircuitprog << circuit追加线路中的所有门
QProgprog << other_prog吸收另一个程序的操作
测量结果prog << core.measure([0], [0])追加测量操作

3.2 追加门

python
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 追加线路和程序

python
# 追加线路
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() 函数创建将量子比特映射到经典比特的测量节点:

python
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 单量子比特门

无参数门

python
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]) # 屏障(阻止跨此点的优化)

参数化门

python
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 参数酉门(最通用的单量子比特门)

旋转门遵循标准定义:

RX(θ)=eiθX/2=(cos(θ/2)isin(θ/2)isin(θ/2)cos(θ/2))RY(θ)=eiθY/2=(cos(θ/2)sin(θ/2)sin(θ/2)cos(θ/2))RZ(θ)=eiθZ/2=(eiθ/200eiθ/2)

4.2 两量子比特门

无参数门

python
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(离子阱)

参数化门

python
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 旋转

两量子比特旋转门:RXX(θ)=eiθ(XX)/2RYY(θ)=eiθ(YY)/2RZZ(θ)=eiθ(ZZ)/2

4.3 多量子比特门

python
core.TOFFOLI(0, 1, 2)  # CCX: 当控制 0, 1 都为 |1> 时翻转目标 2

4.4 特殊门

BARRIER 阻止编译器在屏障点之间合并或重排门:

python
prog = core.QProg()
prog << core.H(0) << core.H(1)
prog << core.BARRIER([0, 1])  # 不跨此线进行优化
prog << core.CNOT(0, 1)

ECHO 是用于某些脉冲级校准的专用门。它接受可选参数:

python
echo_gate = core.ECHO(0)
echo_gate.set_parameters([1.5])  # 设置自定义参数

Oracle 从真值表(truth table)或二值函数构造量子预言机(oracle):

python
oracle = core.Oracle(qubits, truth_table)

create_gate() 从类型枚举和参数构造门:

python
gate = core.create_gate("H", [0], [])             # 量子比特 0 上的 H 门
gate = core.create_gate("RX", [0], [1.57])      # 带角度的 RX 门

4.5 门自省和变换

每个门对象都支持自省(introspection):

python
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()

python
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

python
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

python
# 所有门
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 层分析

python
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 关键路径和两量子比特门分析

python
# 最长路径 = 关键路径
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 实际示例:比较线路

python
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 带完整分析的贝尔态

python
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 反计算模式

python
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 受控线路

python
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 量子比特重映射

python
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 << xQProg追加门、线路、程序或测量
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()intDAG 深度(层数)
dag.longest_path()list通过 DAG 的关键路径
dag.two_qubit_gates()list所有两量子比特门对象
dag.two_qubit_gate_nodes()list两量子比特门的节点索引

门工厂函数

类别
1Q 无参数HXYZSTIX1Y1Z1ECHO
1Q 参数化RXRYRZPRPhiU1U2U3U4
2Q 无参数CNOTCZSWAPISWAPSQISWAPMS
2Q 参数化CPCRCRXCRYCRZCURXXRYYRZZRZX
多量子比特TOFFOLI
特殊BARRIERECHOOraclecreate_gate()

总结

在本教程中你学到了:

  1. QProg 是可执行量子程序容器。使用 << 追加门、线路、测量和其他程序。用 depth()count_ops()qubits_num() 等方法检查它。

  2. QCircuit 是可复用线路模块。它支持相同的 << 运算符以及额外方法如 dagger()control()matrix()num_2q_gate()

  3. << 运算符 是通用追加机制。它返回容器本身,支持流式链式调用如 prog << core.H(0) << core.CNOT(0, 1)

  4. 门构造 涵盖 37+ 种门,从单量子比特(HXRXU3)到两量子比特(CNOTSWAPCU)再到多量子比特(TOFFOLI),加上特殊门如 BARRIEROracle

  5. DAGQCircuit 支持高级线路分析:通过有向无环图表示进行层分解、关键路径查找、两量子比特门提取和深度计算。

系列中的下一个教程是模拟,你将学习如何在各种模拟器后端上执行这里构建的程序。


知识检查

测试你对 pyqpanda3 中线路构建的理解。

Q1: QProgQCircuit 有什么区别?分别在什么场景使用?

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)进行计算后,施加逆线路将它们恢复到 |0 以便重用。例如,在 Grover 算法中,预言机和扩散算子都使用了 dagger()

Q4: 解释 BARRIERECHOIDLE 门之间的关系。为什么它们没有数学效果?

A4: 三者都是具有恒等酉矩阵(I)的元操作。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 是控制,bc 是 SWAP 目标。或者,使用 SWAP(1, 2).control(0) 创建 SWAP 线路的受控版本。


练习 1:可复用线路模块

创建一个参数化"量子加法器"线路,将编码在量子比特中的两个 2 位数相加。演示在程序中使用 dagger() 进行反计算。

解答:

python
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,并分析其属性:深度、两量子比特门数和层结构。

解答:

python
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}")

Released under the MIT License.