Skip to content

量子线路构建 ​

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


概述 ​

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

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

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

mermaid
graph TD
    subgraph "Container Hierarchy"
        QProg["QProg<br/><i>executable program</i>"]
        QCircuit["QCircuit<br/><i>reusable module</i>"]
        QGate["QGate<br/><i>single gate</i>"]
        Measure["measure()<br/><i>measurement</i>"]
    end

    QProg -->|"<< gate"| QGate
    QProg -->|"<< circuit"| QCircuit
    QProg -->|"<< prog"| QProg
    QProg -->|"<< measure"| Measure
    QCircuit -->|"<< gate"| QGate
    QCircuit -->|"<< circuit"| QCircuit

    style QProg fill:#4a90d9,color:#fff
    style QCircuit fill:#50b86c,color:#fff
    style QGate fill:#e8913a,color:#fff
    style Measure fill:#c0392b,color:#fff

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)           # Hadamard on qubit 0
prog << core.CNOT(0, 1)     # CNOT: control=0, target=1
prog << core.measure([0, 1], [0, 1])  # Measure qubits [0,1] into cbits [0,1]

# 等效写法:在一个表达式中链式调用
prog2 = core.QProg()
prog2 << core.H(0) << core.CNOT(0, 1) << core.measure([0, 1], [0, 1])

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

mermaid
graph LR
    A["prog"] -->|"<<"| B["core.H(0)"]
    B -->|"returns prog"| C["prog"]
    C -->|"<<"| D["core.CNOT(0, 1)"]
    D -->|"returns prog"| E["prog"]
    E -->|"<<"| F["core.measure(...)"]
    F -->|"returns prog"| G["prog (complete)"]

    style A fill:#4a90d9,color:#fff
    style G fill:#4a90d9,color:#fff

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

python
# 嵌入 QCircuit
bell = core.QCircuit()
bell << core.H(0) << core.CNOT(0, 1)

prog = core.QProg()
prog << bell                    # append a circuit
prog << core.measure([0, 1], [0, 1])

# 吸收另一个 QProg
prep = core.QProg()
prep << core.H(0)

main_prog = core.QProg()
main_prog << prep               # absorb a sub-program
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"Qubits: {prog.qubits_num()}")  # Qubits: 2
print(f"Cbits:   {prog.cbits_num()}")   # Cbits:   2

运行结果:

Qubits: 2
Cbits:   2```


#### depth()

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

```python
prog = core.QProg()
prog << core.H(0) << core.H(1)  # depth 1: both H act on different qubits
prog << core.CNOT(0, 1)         # depth 2: CNOT depends on both qubits

print(f"Depth: {prog.depth()}")                    # Depth: 2
print(f"Depth (2Q only): {prog.depth(only_q2=True)}")  # Depth: 1

运行结果:

Depth: 2
Depth (2Q only): 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"All gates: {prog.count_ops()}")                # All gates: {'H': 2, 'CNOT': 1, 'RX': 1}
print(f"2-qubit only: {prog.count_ops(only_q2=True)}")  # 2-qubit only: {'CNOT': 1}

运行结果:

All gates: {'RX': 1, 'CNOT': 1, 'H': 2}
2-qubit only: {'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()  # new QProg with all nesting removed

to_circuit() ​

将程序转换为 QCircuit 对象,用于需要线路格式的分析工具:

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

circuit = prog.to_circuit()
print(f"Circuit qubits: {list(circuit.qubits())}")

运行结果:

Circuit qubits: [0, 1]```


#### 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"Gate: {g.name()}, qubits: {g.qubits()}")

# 所有操作(包括测量)
for op in prog.operations():
    print(f"Op type: {type(op).__name__}")

运行结果:

Gate: H, qubits: [0]
Gate: CNOT, qubits: [0, 1]
Op type: QGate
Op type: QGate
Op type: MeasureNode
Op type: MeasureNode```


#### 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"Measure node: {node}")

运行结果:

Measure node: <pyqpanda3.core.core.MeasureNode object at 0x00000272A9562A30>
Measure node: <pyqpanda3.core.core.MeasureNode object at 0x00000272A9562CB0>```


#### remap()

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

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

remapped = prog.remap({0: 3, 1: 5})  # qubit 0 -> 3, qubit 1 -> 5
print(f"Remapped qubits: {remapped.qubits_num()}")

运行结果:

Remapped qubits: 6```


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

运行结果:

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"Gate count: {circ.size()}")  # Gate count: 3
print(f"Depth:      {circ.depth()}")  # Depth: 2

运行结果:

Gate count: 3
Depth:      2```


#### qubits()

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

```python
circ = core.QCircuit()
circ << core.H(0) << core.CNOT(0, 2)
print(f"Qubits used: {list(circ.qubits())}")  # Qubits used: [0, 2]

运行结果:

Qubits used: [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"All gates: {circ.count_ops()}")               # All gates: {'H': 2, 'CNOT': 1, 'SWAP': 1}
print(f"2-qubit only: {circ.count_ops(only_q2=True)}")  # 2-qubit only: {'CNOT': 1, 'SWAP': 1}
print(f"num_2q_gate: {circ.num_2q_gate()}")           # num_2q_gate: 2

运行结果:

All gates: {'CNOT': 1, 'H': 2, 'SWAP': 1}
2-qubit only: {'CNOT': 1, 'SWAP': 1}
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"Matrix shape: {U.shape}")  # Matrix shape: (4, 4)

运行结果:

Matrix shape: (4, 4)```


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

$$
U_{\text{Bell}} = \text{CNOT} \cdot (H \otimes I) = \frac{1}{\sqrt{2}}\begin{pmatrix} 1 & 0 & 1 & 0 \\ 0 & 1 & 0 & 1 \\ 0 & 1 & 0 & -1 \\ 1 & 0 & -1 & 0 \end{pmatrix}
$$

#### set_name(), draw() 和 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)               # text visualization
print(circ.originir())    # H q[0]\nCNOT q[0],q[1]\nCNOT q[1],q[2]

运行结果:

          ┌─┐               
q_0:  |0>─┤H├ ───*── ────── 
          └─┘ ┌──┴─┐        
q_1:  |0>──── ┤CNOT├ ───*── 
              └────┘ ┌──┴─┐ 
q_2:  |0>──── ────── ┤CNOT├ 
                     └────┘ 
 c :   / ══════════════════
                           


QINIT 3
CREG 1
H q[0]
CNOT q[0],q[1]
CNOT 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. << 运算符 -- 追加操作 ​

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

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 运算符链流程 ​

在内部,每次 << 调用的工作方式如下:

mermaid
sequenceDiagram
    participant P as QProg
    participant G1 as H(0)
    participant G2 as CNOT(0,1)
    participant M as measure(...)

    P->>G1: prog << core.H(0)
    G1-->>P: 返回 prog(现在包含 H(0))
    P->>G2: prog << core.CNOT(0, 1)
    G2-->>P: 返回 prog(现在包含 H(0), CNOT)
    P->>M: prog << core.measure(...)
    M-->>P: 返回 prog(完整程序)

4. 门构造 ​

pyqpanda3 提供 37+ 种量子门,组织为单量子比特、两量子比特和多量子比特类别。所有门都作为 core 模块中的工厂函数构造。

4.1 单量子比特门 ​

无参数门 ​

python
from pyqpanda3 import core

# 泡利门
core.X(0)   # Pauli-X (bit flip)
core.Y(0)   # Pauli-Y
core.Z(0)   # Pauli-Z (phase flip)

# Clifford 门
core.H(0)   # Hadamard
core.S(0)   # S gate (sqrt of Z)
core.T(0)   # T gate (sqrt of S)

# 恒等门和变体
core.I(0)   # Identity
core.X1(0)  # X1 gate
core.Y1(0)  # Y1 gate
core.Z1(0)  # Z1 gate

# 特殊门
core.ECHO(0)    # Echo gate (with optional parameter)
core.BARRIER([0]) # Barrier (prevents optimization across this point)

参数化门 ​

python
from pyqpanda3 import core
import math

# 旋转门
core.RX(0, math.pi / 2)     # Rotation around X axis
core.RY(0, math.pi / 4)     # Rotation around Y axis
core.RZ(0, math.pi / 3)     # Rotation around Z axis

# 相位门
core.P(0, math.pi / 6)      # Phase gate (diag(1, e^{i\theta}))

# 通用酉门
core.U1(0, math.pi / 4)             # 1-parameter unitary
core.U2(0, 0, math.pi / 2)          # 2-parameter unitary
core.U3(0, 0.1, 0.2, 0.3)           # 3-parameter unitary
core.U4(0, 0.1, 0.2, 0.3, 0.4)     # 4-parameter unitary (most general 1Q gate)

旋转门遵循标准定义:

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

4.2 两量子比特门 ​

无参数门 ​

python
core.CNOT(0, 1)      # Controlled-X: control=0, target=1
core.CZ(0, 1)        # Controlled-Z
core.SWAP(0, 1)      # Swap qubits 0 and 1
core.ISWAP(0, 1)     # iSWAP gate
core.SQISWAP(0, 1)   # Square root of iSWAP
core.MS(0, 1)        # Molmer-Sorensen (ion trap)

参数化门 ​

python
from pyqpanda3 import core
import math

core.CRX(0, 1, math.pi / 2)   # Controlled-RX
core.CRY(0, 1, math.pi / 3)   # Controlled-RY
core.CRZ(0, 1, math.pi / 4)   # Controlled-RZ
core.CP(0, 1, math.pi / 6)    # Controlled phase
core.CU(0, 1, 0.1, 0.2, 0.3, 0.4)  # 4-parameter controlled unitary
core.RXX(0, 1, math.pi / 4)   # XX rotation
core.RYY(0, 1, math.pi / 4)   # YY rotation
core.RZZ(0, 1, math.pi / 4)   # ZZ rotation
core.RZX(0, 1, math.pi / 4)   # ZX rotation

两量子比特旋转门:RXX(θ)=e−iθ(X⊗X)/2,RYY(θ)=e−iθ(Y⊗Y)/2,RZZ(θ)=e−iθ(Z⊗Z)/2。

4.3 多量子比特门 ​

python
core.TOFFOLI(0, 1, 2)  # CCX: flips target=2 if controls 0,1 are both |1>

4.4 特殊门 ​

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

python
prog = core.QProg()
prog << core.H(0) << core.H(1)
prog << core.BARRIER([0, 1])  # no optimization across this line
prog << core.CNOT(0, 1)

RESET(0.5.0 新增)是原生非幺正重置信道:它将量子比特确定性地坍缩到 |0>,且不影响经典寄存器——不再需要 measure 加条件 X 的模拟写法。它在 CPU 态矢量与密度矩阵模拟器上受支持,并可经 OriginIR 双向往返:

python
from pyqpanda3.core import CPUQVM, QProg, RESET, X, measure

prog = QProg() << X(0) << RESET(0) << measure(0, 0)
qvm = CPUQVM()
qvm.run(prog, 100)
print(qvm.result().get_counts())  # {'0': 100}——X 门的效果被 RESET 撤销

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

python
echo_gate = core.ECHO(0)
echo_gate.set_parameters([1.5])  # set custom parameter

Oracle 从酉矩阵构造量子预言机(oracle)。矩阵维度必须为 2n×2n,其中 n 为量子比特数:

python
import numpy as np
from pyqpanda3 import core

# Oracle from a unitary matrix (e.g., marking |11>)
oracle_matrix = np.eye(4, dtype=complex)
oracle_matrix[3, 3] = -1  # flip phase for |11>
prog = core.QProg()
prog << core.H(0) << core.H(1)
prog << core.Oracle([0, 1], oracle_matrix)

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

python
gate = core.create_gate("H", [0], [])           # H gate on qubit 0
gate = core.create_gate("RX", [0], [1.57])      # RX gate with angle

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]

运行结果:

RX
GateType.RX
[0]
[]
[1.57]
[0]

每个门也支持 dagger()、control() 和 matrix():

python
from pyqpanda3 import core
import numpy as np

# dagger() 返回伴随(逆)
gate = core.H(0)           # H is self-adjoint: H.dagger() = H
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 matrix:\n{core.H(0).matrix()}")
print(f"CNOT matrix shape: {core.CNOT(0, 1).matrix().shape}")  # (4, 4)

运行结果:

True
[2, 0]
H matrix:
[[ 0.70710678+0.j  0.70710678+0.j]
 [ 0.70710678+0.j -0.70710678+0.j]]
CNOT matrix shape: (4, 4)```


---

## 5. 基于 DAG 的线路分析

pyqpanda3 提供 [`DAGQCircuit`](../api/core/dag.md) 用于有向无环图(Directed Acyclic Graph)分析量子线路。这支持层分解(layer decomposition)、依赖分析和关键路径计算。

### 5.1 什么是 DAG?

DAG 表示线路中门的依赖结构。每个门是一个节点,边表示量子比特依赖:如果门 B 使用门 A 在同一量子比特上的输出,则从 A 到 B 有一条边。

```mermaid
graph TD
    subgraph "Circuit: H(0), H(1), CNOT(0,1), X(2)"
        H0["H(0)"] --> CNOT["CNOT(0,1)"]
        H1["H(1)"] --> CNOT
        X2["X(2)"]
    end

    subgraph "DAG Layers"
        L1["Layer 0<br/>H(0), H(1), X(2)"]
        L2["Layer 1<br/>CNOT(0,1)"]
        L1 --> L2
    end

    style H0 fill:#e8913a,color:#fff
    style H1 fill:#e8913a,color:#fff
    style X2 fill:#e8913a,color:#fff
    style CNOT fill:#4a90d9,color:#fff

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()} on qubits {gate.qubits()}")

# 节点和边
print(f"Nodes: {list(dag.nodes())}")
print(f"Edges: {list(dag.edges())}")

# 特定节点索引处的门
for idx in dag.nodes():
    gate = dag.get_gate(idx)
    print(f"Node {idx}: {gate.name()}{gate.qubits()}")

运行结果:

H on qubits [0]
H on qubits [1]
CNOT on qubits [0, 1]
X on qubits [2]
Nodes: [0, 1, 2, 3]
Edges: [(0, 2), (1, 2)]
Node 0: H[0]
Node 1: H[1]
Node 2: CNOT[0, 1]
Node 3: X[2]```


### 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 depth: {dag.get_depth()}")  # 3

for i, layer in enumerate(dag.layers()):
    names = [dag.get_gate(idx).name() for idx in layer]
    print(f"Layer {i}: {names}")
# 层 0: ['H', 'H', 'H']
# 层 1: ['CNOT']
# 层 2: ['CNOT']

运行结果:

DAG depth: 3
Layer 0: ['H', 'H', 'H']
Layer 1: ['CNOT']
Layer 2: ['CNOT']```


### 5.5 关键路径和两量子比特门分析

```python
# 最长路径 = 关键路径
path = dag.longest_path()
for node in path:
    gate = dag.get_gate(node)
    print(f"  {gate.name()} on qubits {gate.qubits()}")

# 两量子比特门分析
two_q_gates = dag.two_qubit_gates()
for gate in two_q_gates:
    if gate.is_controlled():
        print(f"  Controlled: ctrl={gate.control_qubits()}, tgt={gate.target_qubits()}")
    else:
        print(f"  Non-controlled: qubits={gate.target_qubits()}")

two_q_nodes = list(dag.two_qubit_gate_nodes())

运行结果:

  H on qubits [0]
  CNOT on qubits [0, 1]
  CNOT on qubits [1, 2]
  Non-controlled: qubits=[0, 1]
  Non-controlled: qubits=[1, 2]```


### 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 [("Naive SWAP", swap_naive), ("Native SWAP", swap_native)]:
    dag = core.DAGQCircuit()
    dag.from_circuit(circ)
    dag.build()
    print(f"--- {name} ---")
    print(f"  Gates: {len(dag.gates())}, Depth: {dag.get_depth()}, "
          f"2Q gates: {len(dag.two_qubit_gates())}")

运行结果:

--- Naive SWAP ---
  Gates: 3, Depth: 3, 2Q gates: 3
--- Native SWAP ---
  Gates: 1, Depth: 1, 2Q gates: 1```


---

## 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"Qubits: {prog.qubits_num()}, Depth: {prog.depth()}, "
      f"Gates: {prog.count_ops()}, 2Q: {prog.count_ops(only_q2=True)}")
# 门数: {'CNOT': 1, 'H': 1}, 两量子比特门: {'CNOT': 1}

# DAG 分析
dag = core.DAGQCircuit()
dag.from_circuit(bell)
dag.build()
print(f"DAG depth: {dag.get_depth()}")
for i, layer in enumerate(dag.layers()):
    print(f"  Layer {i}: {[dag.get_gate(idx).name() for idx in layer]}")

# 在模拟器上运行
machine = core.CPUQVM()
machine.run(prog, 1000)
print(f"Results: {machine.result().get_counts()}")
print(f"OriginIR:\n{prog.originir()}")

运行结果:

Qubits: 2, Depth: 2, Gates: {'CNOT': 1, 'H': 1}, 2Q: {'CNOT': 1}
DAG depth: 2
  Layer 0: ['H']
  Layer 1: ['CNOT']
Results: {'00': 512, '11': 488}
OriginIR:
QINIT 2
CREG 2
H q[0]
CNOT q[0],q[1]
MEASURE q[0],c[0]
MEASURE q[1],c[1]

6.2 反计算模式 ​

python
from pyqpanda3 import core

compute = core.QCircuit()
compute << core.CNOT(0, 2) << core.CNOT(1, 2)  # XOR onto qubit 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})  # map to physical qubits 5, 7
print(f"Physical OriginIR:\n{physical.originir()}")

运行结果:

Physical OriginIR:
QINIT 8
CREG 2
H q[5]
CNOT q[5],q[7]
MEASURE q[5],c[0]
MEASURE q[7],c[1]```


---

## 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`](../api/core/circuit.md) | 节点索引处的门 |
| `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`](../api/core/gates.md)、[`X`](../api/core/gates.md)、[`Y`](../api/core/gates.md)、[`Z`](../api/core/gates.md)、[`S`](../api/core/gates.md)、[`T`](../api/core/gates.md)、[`I`](../api/core/gates.md)、[`X1`](../api/core/gates.md)、[`Y1`](../api/core/gates.md)、[`Z1`](../api/core/gates.md)、[`ECHO`](../api/core/gates.md) |
| 1Q 参数化 | [`RX`](../api/core/gates.md)、[`RY`](../api/core/gates.md)、[`RZ`](../api/core/gates.md)、[`P`](../api/core/gates.md)、`RPhi`、[`U1`](../api/core/gates.md)、[`U2`](../api/core/gates.md)、[`U3`](../api/core/gates.md)、[`U4`](../api/core/gates.md) |
| 2Q 无参数 | [`CNOT`](../api/core/gates.md)、[`CZ`](../api/core/gates.md)、[`SWAP`](../api/core/gates.md)、[`ISWAP`](../api/core/gates.md)、[`SQISWAP`](../api/core/gates.md)、[`MS`](../api/core/gates.md) |
| 2Q 参数化 | [`CP`](../api/core/gates.md)、[`CR`](../api/core/gates.md)、[`CRX`](../api/core/gates.md)、[`CRY`](../api/core/gates.md)、[`CRZ`](../api/core/gates.md)、[`CU`](../api/core/gates.md)、[`RXX`](../api/core/gates.md)、[`RYY`](../api/core/gates.md)、[`RZZ`](../api/core/gates.md)、[`RZX`](../api/core/gates.md) |
| 多量子比特 | [`TOFFOLI`](../api/core/gates.md) |
| 特殊 | [`BARRIER`](../api/core/gates.md)、[`ECHO`](../api/core/gates.md)、[`Oracle`](../api/core/gates.md)、`create_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+ 种门,从单量子比特([`H`](../api/core/gates.md)、[`X`](../api/core/gates.md)、[`RX`](../api/core/gates.md)、[`U3`](../api/core/gates.md))到两量子比特([`CNOT`](../api/core/gates.md)、[`SWAP`](../api/core/gates.md)、[`CU`](../api/core/gates.md))再到多量子比特([`TOFFOLI`](../api/core/gates.md)),加上特殊门如 [`BARRIER`](../api/core/gates.md) 和 [`Oracle`](../api/core/gates.md)。

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

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

---

## 知识检查

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

<strong>Q1: `QProg` 和 `QCircuit` 有什么区别?分别在什么场景使用?</strong>

A1: `QProg` 是顶层可执行量子程序容器——它可以持有门、线路、测量和控制流。`QCircuit` 是可复用线路模块,支持额外操作如 `dagger()`、`control()` 和 `matrix()`。使用 `QProg` 作为主程序包装器,使用 `QCircuit` 构建可反转、受控或组合的可复用子线路。

<strong>Q2: `<<` 运算符返回什么?为什么它有用?</strong>

A2: `<<` 运算符返回容器(QProg 或 QCircuit)本身,支持方法链式调用。这允许流式构建如 `prog << H(0) << CNOT(0, 1) << measure([0, 1], [0, 1])`,无需为每一步使用中间变量。

<strong>Q3: 如何创建线路的伴随(逆)?给出一个实际用例的例子。</strong>

A3: 在 `QCircuit` 上调用 `.dagger()`:`inverse_cir = cir.dagger()`。这对于反计算(uncomputation)至关重要——在使用辅助量子比特(ancilla qubit)进行计算后,施加逆线路将它们恢复到 $|0\rangle$ 以便重用。例如,在 Grover 算法中,预言机和扩散算子都使用了 `dagger()`。

<strong>Q4: 解释 `BARRIER`、`ECHO` 和 [`IDLE`](../api/core/gates.md) 门之间的关系。为什么它们没有数学效果?</strong>

A4: 三者都是具有恒等酉矩阵($I$)的元操作。`BARRIER` 阻止优化期间跨它的门融合,`ECHO` 表示用于噪声抑制的自旋回波序列,[`IDLE`](../api/core/gates.md) 表示时间延迟。它们影响编译和调度,但不影响数学态演化。

<strong>Q5: 什么是 [`DAGQCircuit`](../api/core/dag.md)?与平面线路表示相比,它有什么优势?</strong>

A5: `DAGQCircuit` 将线路表示为有向无环图,其中门是节点,依赖(共享量子比特)是边。这支持高级分析:寻找关键路径(线路深度)、层分解用于并行门执行、计算两量子比特门、以及在转译期间进行拓扑感知路由。

<strong>Q6: 如何使用现有 pyqpanda3 原语构建受控 SWAP(Fredkin)门?</strong>

A6: Fredkin 门(受控 SWAP)可以分解为:`CNOT(b, c) << TOFFOLI(a, c, b) << CNOT(b, c)`,其中 `a` 是控制,`b`、`c` 是 SWAP 目标。或者,使用 `SWAP(1, 2).control(0)` 创建 SWAP 线路的受控版本。

---

**练习 1:可复用线路模块**

创建一个参数化"量子半加器"线路,并在程序中演示使用 `dagger()` 进行反计算。

<strong>解答:</strong>

```python
from pyqpanda3.core import CPUQVM, QProg, QCircuit, CNOT, TOFFOLI, X, measure

def quantum_half_adder(a, b, carry):
    """1-bit quantum half-adder (TOFFOLI first to preserve original values).
    Input:  a, b (1-qubit inputs), carry (initialized to |0>)
    Output: b = a XOR b (sum), carry = a AND b
    """
    cir = QCircuit()
    cir << TOFFOLI(a, b, carry)  # carry = a AND b (original values)
    cir << CNOT(a, b)            # b = a XOR b (sum)
    return cir

qvm = CPUQVM()

# 测试所有四种输入组合
for a_val, b_val in [(0, 0), (0, 1), (1, 0), (1, 1)]:
    prog = QProg()
    if a_val:
        prog << X(0)  # Set a=1
    if b_val:
        prog << X(1)  # Set b=1

    adder = quantum_half_adder(0, 1, 2)
    prog << adder
    prog << measure([1, 2], [0, 1])

    qvm.run(prog, shots=100)
    counts = qvm.result().get_counts()
    # 结果位:(sum=b, carry=c) — 例如 "10" 表示 sum=1, carry=0
    print(f"{a_val}+{b_val}: {counts}")

# 演示反计算
prog2 = QProg()
prog2 << X(0) << X(1)          # a=1, b=1 → 1+1
adder = quantum_half_adder(0, 1, 2)
prog2 << adder
prog2 << adder.dagger()        # Uncompute
prog2 << measure([0, 1, 2], [0, 1, 2])
qvm.run(prog2, shots=100)
counts2 = qvm.result().get_counts()
print(f"After uncompute (1+1): {counts2}")

运行结果:

0+0: {'00': 100}
0+1: {'01': 100}
1+0: {'01': 100}
1+1: {'10': 100}
After uncompute (1+1): {'011': 100}

注意: 扩展到完整的 2 位行波进位加法器需要额外的门(MAJ/UMA 块)来正确传播中间进位。上述半加器演示了关键原则:在用 CNOT 修改输入值之前,先用 TOFFOLI 对原始输入值计算进位。

练习 2:DAGQCircuit 分析

构建一个 4 量子比特线路,将其转换为 DAGQCircuit,并分析其属性:深度、两量子比特门数和层结构。

解答:

python
from pyqpanda3.core import 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()
dag.from_circuit(cir)
dag.build()

# 分析属性
print("DAG Circuit Analysis:")
print(f"  Depth: {dag.get_depth()}")
print(f"  Total gates: {dag.get_num_gates()}")
print(f"  Two-qubit gates: {len(dag.two_qubit_gates())}")

# 层分解
layers = dag.layers()
for i, layer in enumerate(layers):
    gate_names = [dag.get_gate(idx).name() for idx in layer]
    print(f"  Layer {i}: {gate_names}")

运行结果:

DAG Circuit Analysis:
  Depth: 3
  Total gates: 8
  Two-qubit gates: 4
  Layer 0: ['H', 'H', 'CNOT', 'X', 'H']
  Layer 1: ['CNOT']
  Layer 2: ['SWAP', 'CZ']```

Released under the MIT License.