Skip to content

IR 转换 -- OriginIR 与 QASM(IR Conversion) ​

在 QProg、OriginIR 和 QASM 中间表示之间进行转换,实现电路互操作、序列化和调试。

OriginIR 的语法细节(程序结构、门声明、角度表达式、DAGGER/CONTROL、QIF/QWHILE、自定义门与常见错误对照)见 OriginIR 语法 章节。


问题 ​

使用 pyqpanda3 构建的量子电路以内存中的 QProg 对象形式存在。在许多情况下,你需要将电路表示为纯文本字符串,以便存储、传输或与其他框架共享。支持两种基于文本的中间表示(Intermediate Representation, IR):

OriginIR 是 QPanda 的原生 IR 格式。它为每个 pyqpanda3 功能提供完整保真度,包括动态电路(Dynamic Circuits, QIF/QELSE/QWHILE)、自定义门定义(QGATE)、受控和求逆操作以及所有原生门类型。当需要在 pyqpanda3 生态系统内序列化和反序列化电路时使用 OriginIR。

OpenQASM 2.0 是 Qiskit、Cirq 及许多其他框架支持的行业标准量子汇编语言。当需要与非 QPanda 工具交换电路、从开源社区导入电路或导出电路用于发表时使用 QASM。

你需要在以下场景进行 IR 转换:

  • 互操作性(Interoperability)——导入在 Qiskit 等框架中编写的 QASM 电路,或将 pyqpanda3 电路导出为 QASM 给其他框架使用。
  • 持久化(Persistence)——将电路作为 OriginIR 或 QASM 文件保存到磁盘,之后无需重新运行构建代码即可加载。
  • 验证(Verification)——将电路导出为文本,目视检查门序列,或通过比较字符串表示来比较两个电路。
  • 调试(Debugging)——打印复杂 QProg 的 IR 以了解其门结构、控制流和测量操作。
  • 往返测试(Round-Trip Testing)——构建电路、导出为 IR、再导入回来,验证酉矩阵是否匹配以确认转换管道的正确性。

下图展示了 intermediate_compiler 模块支持的转换路径:

mermaid
flowchart LR
    QProg["QProg<br/>(in-memory)"]

    subgraph Export
        E1["convert_qprog_to_originir()"]
        E2["convert_qprog_to_qasm()"]
    end

    subgraph Import
        I1["convert_originir_string_to_qprog()"]
        I2["convert_originir_file_to_qprog()"]
        I3["convert_qasm_string_to_qprog()"]
        I4["convert_qasm_file_to_qprog()"]
    end

    OIR["OriginIR<br/>string / file"]
    QASM["QASM 2.0<br/>string / file"]

    QProg --> E1 --> OIR
    QProg --> E2 --> QASM
    OIR --> I1 --> QProg
    OIR --> I2 --> QProg
    QASM --> I3 --> QProg
    QASM --> I4 --> QProg

    style QProg fill:#4a90d9,color:#fff,stroke:#2c5f9e
    style OIR fill:#50c878,color:#fff,stroke:#2d8a4e
    style QASM fill:#f4a460,color:#fff,stroke:#b87333

方案 ​

intermediate_compiler 模块提供六个函数,覆盖所有转换方向:

函数方向输入输出
convert_qprog_to_originir(prog, precision)QProg -> OriginIRQProg,可选 intstr
convert_qprog_to_qasm(prog, precision)QProg -> QASMQProg,可选 intstr
convert_originir_string_to_qprog(ir_string)OriginIR 字符串 -> QProgstrQProg
convert_originir_file_to_qprog(file_path)OriginIR 文件 -> QProgstr(路径)QProg
convert_qasm_string_to_qprog(qasm_string)QASM 字符串 -> QProgstrQProg
convert_qasm_file_to_qprog(file_path)QASM 文件 -> QProgstr(路径)QProg

precision 参数(默认值:8)控制导出字符串中浮点角度值使用的小数位数。更高的精度保留更多准确性,这对往返保真度很重要。

导出方向存在一处边界。0.5.0 引入的原生 RESET 门无法用 convert_qprog_to_qasm() 所输出的 QASM 2.0 子集表示,因此导出含 RESET 的程序会抛出 RuntimeError:

python
from pyqpanda3.core import QProg, RESET, X
from pyqpanda3.intermediate_compiler import convert_qprog_to_qasm, convert_qprog_to_originir

prog = QProg() << X(0) << RESET(0)
print("OriginIR:", convert_qprog_to_originir(prog).strip().splitlines()[-1])

try:
    convert_qprog_to_qasm(prog)
except RuntimeError as e:
    print("QASM:", type(e).__name__, "-", str(e).splitlines()[0])

运行结果:

text
OriginIR: RESET q[0]
QASM: RuntimeError - QGate::matrix() error gate identifier.

当程序中含有 RESET 时,请改经 OriginIR 序列化。导入方向不受影响:convert_qasm_string_to_qprog() 会把 OpenQASM 的 reset q[i]; 语句解析为原生门,因此使用了 reset 的 QASM 程序可以导入后再导出为 OriginIR。

步骤 1:将电路导出为 OriginIR 或 QASM ​

使用标准 pyqpanda3 API 构建 QProg,然后调用相应的转换函数:

python
from pyqpanda3.core import QProg, H, CNOT, RX, measure
from pyqpanda3.intermediate_compiler import (
    convert_qprog_to_originir,
    convert_qprog_to_qasm,
)

prog = QProg()
prog << H(0)
prog << CNOT(0, 1)
prog << RX(1, 3.14159)
prog << measure([0, 1], [0, 1])

originir_str = convert_qprog_to_originir(prog)
qasm_str = convert_qprog_to_qasm(prog)

print("=== OriginIR ===")
print(originir_str)
print("=== QASM ===")
print(qasm_str)

运行结果:

=== OriginIR ===
QINIT 2
CREG 2
H q[0]
CNOT q[0],q[1]
RX q[1],(3.14159000)
MEASURE q[0],c[0]
MEASURE q[1],c[1]

=== QASM ===
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0],q[1];
rx(3.14159000) q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];

步骤 2:将 OriginIR 或 QASM 字符串导入为 QProg ​

将文本表示解析回可执行的 QProg:

python
from pyqpanda3.intermediate_compiler import (
    convert_originir_string_to_qprog,
    convert_qasm_string_to_qprog,
)

originir_str = """
    QINIT 2
    CREG 2
    H q[0]
    CNOT q[0],q[1]
    MEASURE q[0],c[0]
    MEASURE q[1],c[1]
"""

prog_from_originir = convert_originir_string_to_qprog(originir_str)

步骤 3:从文件加载电路 ​

对于存储在磁盘上的电路,使用基于文件的导入函数:

python
from pyqpanda3.intermediate_compiler import (
    convert_originir_file_to_qprog,
    convert_qasm_file_to_qprog,
)

import tempfile
import os

# Create a temporary OriginIR file
with tempfile.NamedTemporaryFile(mode='w', suffix='.originir', delete=False) as f:
    f.write('QINIT 2\nCREG 2\nH q[0]\nCNOT q[0],q[1]\nMEASURE q[0],c[0]\nMEASURE q[1],c[1]')
    tmp_path = f.name

# Load from OriginIR file
prog = convert_originir_file_to_qprog(tmp_path)

# 同样可以加载 QASM 文件(请替换为实际文件路径)
with open(tmp_path + '.qasm', 'w') as fq:
    fq.write('OPENQASM 2.0;\ninclude "qelib1.inc";\nqreg q[2];\ncreg c[2];\nh q[0];\ncx q[0],q[1];\n')
prog = convert_qasm_file_to_qprog(tmp_path + '.qasm')
print("Circuits loaded successfully")

步骤 4:验证往返保真度 ​

在往返转换(先导出再导入)后,比较酉矩阵以确认没有信息丢失:

python
from pyqpanda3.quantum_info import Unitary

prog = QProg()
prog << H(0) << CNOT(0, 1)

qasm_str = convert_qprog_to_qasm(prog, precision=15)
prog2 = convert_qasm_string_to_qprog(qasm_str)

mat1 = Unitary(prog.to_circuit())
mat2 = Unitary(prog2.to_circuit())

# Compute squared distance between the two unitary matrices
diff = abs(mat1.ndarray() - mat2.ndarray()) ** 2
print(f"Round-trip error: {diff.max():.2e}")

运行结果:

Round-trip error: 0.00e+00

代码 ​

示例 1:构建 Bell 态并导出为两种格式 ​

本示例创建 Bell 态电路并将其导出为 OriginIR 和 QASM:

python
from pyqpanda3.core import QProg, H, CNOT, measure
from pyqpanda3.intermediate_compiler import (
    convert_qprog_to_originir,
    convert_qprog_to_qasm,
)

# Build a Bell state circuit
prog = QProg()
prog << H(0)
prog << CNOT(0, 1)
prog << measure([0, 1], [0, 1])

# Export to OriginIR (QPanda's native format)
originir_str = convert_qprog_to_originir(prog)
print("OriginIR representation:")
print(originir_str)
print()

# Export to QASM 2.0 (industry standard)
qasm_str = convert_qprog_to_qasm(prog)
print("QASM representation:")
print(qasm_str)

运行结果:

OriginIR representation:
QINIT 2
CREG 2
H q[0]
CNOT q[0],q[1]
MEASURE q[0],c[0]
MEASURE q[1],c[1]


QASM representation:
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0],q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];

预期输出(同时打印 OriginIR 和 QASM 两种表示):

OriginIR representation:
QINIT 2
CREG 2
H q[0]
CNOT q[0],q[1]
MEASURE q[0],c[0]
MEASURE q[1],c[1]


QASM representation:
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0],q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];

示例 2:导入 OriginIR 字符串并运行 ​

本示例解析 OriginIR 字符串,在模拟器上运行结果电路,并打印测量计数:

python
from pyqpanda3.core import CPUQVM
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

originir_str = """
    QINIT 3
    CREG 2
    H q[2]
    H q[0]
    H q[1]
    CONTROL q[1]
    RX q[2],(-3.141593)
    ENDCONTROL
    CONTROL q[0]
    RX q[2],(-3.141593)
    RX q[2],(-3.141593)
    ENDCONTROL
    DAGGER
    H q[1]
    H q[0]
    ENDDAGGER
    MEASURE q[0],c[0]
    MEASURE q[1],c[1]
"""

prog = convert_originir_string_to_qprog(originir_str)

machine = CPUQVM()
machine.run(prog=prog, shots=1000)

counts = machine.result().get_counts()
print("Measurement counts:", counts)

运行结果:

Measurement counts: {'11': 475, '01': 525}

示例 3:导入 QASM 字符串并运行 ​

本示例导入用 QASM 编写的量子隐形传态电路:

python
from pyqpanda3.core import CPUQVM
from pyqpanda3.intermediate_compiler import convert_qasm_string_to_qprog

qasm_str = """
    // quantum teleportation example
    OPENQASM 2.0;
    include "qelib1.inc";
    qreg q[3];
    creg c[3];
    gate post q {
        h q;
    }
    u3(0.3,0.2,0.1) q[0];
    h q[1];
    cx q[1],q[2];
    barrier q;
    cx q[0],q[1];
    h q[0];
    measure q[0] -> c[0];
    measure q[1] -> c[1];
    if(c==1) z q[2];
    if(c==2) x q[2];
    if(c==3) y q[2];
    post q[2];
    measure q[2] -> c[2];
"""

prog = convert_qasm_string_to_qprog(qasm_str)

machine = CPUQVM()
machine.run(prog=prog, shots=1000)

counts = machine.result().get_counts()
print("Teleportation counts:", counts)

运行结果:

Teleportation counts: {'101': 76, '100': 102, '110': 97, '111': 165, '010': 161, '000': 164, '011': 85, '001': 150}

示例 4:带验证的往返转换 ​

本示例演示完整的往返流程:构建电路、导出为 QASM、导入回来,并验证酉矩阵是否匹配。这是验证转换管道保持电路语义的常见模式。

python
from pyqpanda3.core import QProg, H, CNOT, RX, RY, RZ, CZ
from pyqpanda3.intermediate_compiler import (
    convert_qprog_to_qasm,
    convert_qasm_string_to_qprog,
    convert_qprog_to_originir,
)
from pyqpanda3.quantum_info import Unitary
import numpy as np

# Build a non-trivial circuit
prog = QProg()
prog << H(0)
prog << H(1)
prog << CNOT(0, 1)
prog << RZ(0, 1.2345)
prog << RZ(1, 2.3456)
prog << CNOT(0, 1)
prog << RX(0, 0.5678)
prog << RY(1, 0.9012)
prog << CZ(0, 1)

# Use high precision for the round-trip to avoid floating-point loss
qasm_str = convert_qprog_to_qasm(prog, precision=15)
print("Exported QASM:")
print(qasm_str)

# Import the QASM string back into a QProg
prog2 = convert_qasm_string_to_qprog(qasm_str)

# Also verify the OriginIR path
originir_str = convert_qprog_to_originir(prog, precision=15)
print("\nExported OriginIR:")
print(originir_str)

# Compare unitary matrices
mat1 = Unitary(prog.to_circuit())
mat2 = Unitary(prog2.to_circuit())

diff = np.abs(mat1.ndarray() - mat2.ndarray()) ** 2
max_error = diff.max()
print(f"\nRound-trip max squared error: {max_error:.2e}")
assert max_error < 1e-10, "Round-trip fidelity check failed!"
print("Round-trip verified successfully.")

运行结果:

Exported QASM:
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[0];
h q[0];
h q[1];
cx q[0],q[1];
rz(1.234500000000000) q[0];
rz(2.345600000000000) q[1];
cx q[0],q[1];
rx(0.567800000000000) q[0];
ry(0.901200000000000) q[1];
cz q[0],q[1];

Exported OriginIR:
QINIT 2
CREG 1
H q[0]
H q[1]
CNOT q[0],q[1]
RZ q[0],(1.234500000000000)
RZ q[1],(2.345600000000000)
CNOT q[0],q[1]
RX q[0],(0.567800000000000)
RY q[1],(0.901200000000000)
CZ q[0],q[1]


Round-trip max squared error: 0.00e+00
Round-trip verified successfully.

示例 5:将电路保存到文件并加载 ​

本示例展示如何将 OriginIR 或 QASM 字符串写入文件并读回:

python
import os
import tempfile
from pyqpanda3.core import QProg, H, CNOT, measure
from pyqpanda3.intermediate_compiler import (
    convert_qprog_to_originir,
    convert_qprog_to_qasm,
    convert_originir_file_to_qprog,
    convert_qasm_file_to_qprog,
)

# Build a circuit
prog = QProg()
prog << H(0)
prog << CNOT(0, 1)
prog << measure([0, 1], [0, 1])

tmp_dir = tempfile.gettempdir()

# --- Save as OriginIR ---
originir_str = convert_qprog_to_originir(prog)
originir_path = os.path.join(tmp_dir, "bell_circuit.txt")
with open(originir_path, "w") as f:
    f.write(originir_str)
print(f"Saved OriginIR to {originir_path}")

# --- Save as QASM ---
qasm_str = convert_qprog_to_qasm(prog)
qasm_path = os.path.join(tmp_dir, "bell_circuit.qasm")
with open(qasm_path, "w") as f:
    f.write(qasm_str)
print(f"Saved QASM to {qasm_path}")

# --- Load back from files ---
prog_from_oir = convert_originir_file_to_qprog(originir_path)
prog_from_qasm = convert_qasm_file_to_qprog(qasm_path)

# Verify both loaded circuits produce the same OriginIR
print("\nOriginIR from file:")
print(convert_qprog_to_originir(prog_from_oir))
print("\nQASM from file:")
print(convert_qprog_to_qasm(prog_from_qasm))

# Clean up
os.remove(originir_path)
os.remove(qasm_path)

运行结果:

Saved OriginIR to <tempdir>/bell_circuit.txt
Saved QASM to <tempdir>/bell_circuit.qasm

OriginIR from file:
QINIT 2
CREG 2
H q[0]
CNOT q[0],q[1]
MEASURE q[0],c[0]
MEASURE q[1],c[1]


QASM from file:
OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg c[2];
h q[0];
cx q[0],q[1];
measure q[0] -> c[0];
measure q[1] -> c[1];

示例 6:处理受控和求逆操作 ​

OriginIR 原生支持 CONTROL/ENDCONTROL 块和 DAGGER/ENDDAGGER 块。本示例演示这些在 QProg API 和 IR 之间的映射关系:

python
from pyqpanda3.core import QProg, H, RX, X
from pyqpanda3.intermediate_compiler import convert_qprog_to_originir

prog = QProg()
prog << H(0)
prog << H(1)
prog << H(2)

# Apply RX on qubit 2, controlled by qubit 1
prog << RX(2, -3.141593).control([1])

# Apply RX on qubit 2, controlled by qubit 0 (twice, effectively 2x the angle)
prog << RX(2, -3.141593).control([0])
prog << RX(2, -3.141593).control([0])

# Apply the dagger (inverse) of H(1) followed by H(0)
prog << H(1).dagger()
prog << H(0).dagger()

originir_str = convert_qprog_to_originir(prog)
print(originir_str)

运行结果:

QINIT 3
CREG 1
H q[0]
H q[1]
H q[2]
CONTROL q[1]
RX q[2],(-3.14159300)
ENDCONTROL
CONTROL q[0]
RX q[2],(-3.14159300)
ENDCONTROL
CONTROL q[0]
RX q[2],(-3.14159300)
ENDCONTROL
DAGGER
H q[1]
ENDDAGGER
DAGGER
H q[0]
ENDDAGGER

输出中包含 CONTROL/ENDCONTROL 和 DAGGER/ENDDAGGER 块。每次 .control() 调用创建独立的 CONTROL 块,每次 .dagger() 调用创建独立的 DAGGER 块。

示例 7:OriginIR 中的自定义门定义 ​

OriginIR 通过 QGATE/ENDQGATE 语法支持用户定义的门。你可以定义带参数的可复用门块:

python
from pyqpanda3.core import CPUQVM
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

originir_str = """
    QINIT 2
    CREG 2

    H q
    X q[0]
    Y q[1]

    QGATE new_H a,b
    Y a
    Z b
    ENDQGATE

    QGATE new_RX a,b,(d,e)
    H a
    RX b,(PI/2+d+e)
    new_H a,b
    ENDQGATE

    new_RX q[1],q[0],(PI/2,3)
    new_RX q,(1,2)

    MEASURE q[0],c[0]
    MEASURE q[1],c[1]
"""

prog = convert_originir_string_to_qprog(originir_str)

machine = CPUQVM()
machine.run(prog=prog, shots=1000)
counts = machine.result().get_counts()
print("Counts:", counts)

运行结果:

Custom Gate: new_H
Qbit Params: a=1
Qbit Params: b=0

Gate Operations: 
	Y 1,
Gate Operations: 
	Z 0,
Custom Gate: new_H
Qbit Params: a=0
Qbit Params: b=1

Gate Operations: 
	Y 0,
Gate Operations: 
	Z 1,
Counts: {'10': 589, '00': 411}

上面的 QGATE 块将 new_H 定义为对两个量子比特施加 Y 和 Z 的复合门,new_RX 定义为使用 PI 作为内置常量并引用参数 d 和 e 的参数化门。

示例 8:OriginIR 中的动态电路(QIF / QWHILE) ​

OriginIR 支持动态电路构造——条件执行(QIF/QELSE/QELSEIF/QENDIF)和电路中途循环(QWHILE/QENDWHILE)。这些对于后续操作依赖于先前测量结果的电路至关重要:

python
from pyqpanda3.core import QProg, CPUQVM, X, Y, Z, H, measure, qif, qwhile
from pyqpanda3.intermediate_compiler import (
    convert_qprog_to_originir,
    convert_originir_string_to_qprog,
)

# Build a dynamic circuit using the pyqpanda3 API
prog = QProg()
inner_loop = QProg()
inner_loop << Y(2) << Y(1) << measure(2, 2)

if_body = QProg()
if_body << Y(0) << H(1)
if_body << qwhile([2]).loop(inner_loop)

prog << X(1)
prog << qif([1]).then(if_body).qendif()
prog << measure(0, 0)
prog << measure(1, 1)

# Export to OriginIR to see the dynamic circuit representation
originir_str = convert_qprog_to_originir(prog)
print("Dynamic circuit as OriginIR:")
print(originir_str)

# Parse it back and verify the round-trip
prog2 = convert_originir_string_to_qprog(originir_str)
originir_str2 = convert_qprog_to_originir(prog2)
print("\nRound-trip OriginIR:")
print(originir_str2)

# Run on a simulator
machine = CPUQVM()
machine.run(prog=prog, shots=100)
state = machine.result().get_state_vector()
print("\nState vector (first 4 amplitudes):")
for i, v in enumerate(state[:4]):
    print(f"  |{i:02b}>: ({v.real:.6f}, {v.imag:.6f})")

运行结果:

Dynamic circuit as OriginIR:
QINIT 3
CREG 3
X q[1]
QIF c[1]
Y q[0]
H q[1]
QWHILE c[2]
Y q[2]
Y q[1]
MEASURE q[2],c[2]
QENDWHILE
QENDIF
MEASURE q[0],c[0]
MEASURE q[1],c[1]


Round-trip OriginIR:
QINIT 3
CREG 3
X q[1]
QIF c[1]
Y q[0]
H q[1]
QWHILE c[2]
Y q[2]
Y q[1]
MEASURE q[2],c[2]
QENDWHILE
QENDIF
MEASURE q[0],c[0]
MEASURE q[1],c[1]


State vector (first 4 amplitudes):
  |00>: (0.000000, 0.000000)
  |01>: (-0.000000, 1.000000)
  |10>: (0.000000, 0.000000)
  |11>: (0.000000, 0.000000)

注意: 态矢量输出是非确定性的,因为动态电路的执行路径取决于电路中途的测量结果(QIF 条件依赖 c[1],QWHILE 条件依赖 c[2])。实际输出每次运行都会有所不同。


解析 ​

OriginIR 语法概述 ​

OriginIR 是面向行的文本 IR。每行包含一条指令或一个块分隔符。一般结构为:

QINIT <n>           -- Declare n qubits
CREG <m>            -- Declare m classical bits

<gate> q[<idx>]     -- Single-qubit gate
<gate> q[<i>],q[<j>]  -- Two-qubit gate
<gate> q[<i>],(<angle>)  -- Parameterized gate with angle in radians

头部行(Header Lines)(QINIT 和 CREG)是必需的。它们声明量子比特数和经典比特数。量子比特索引范围为 [0,n−1],其中 n 为 QINIT 的值。

门指令(Gate Instructions)遵循模式 GATE_NAME target_qubit(s),(parameter(s))。例如:

OriginIR 行含义
H q[0]对量子比特 0 施加 Hadamard
X q[1]对量子比特 1 施加 Pauli-X
RX q[0],(1.570796)绕 X 轴旋转 π/2 弧度
CNOT q[0],q[1]CNOT,控制=0,目标=1
MEASURE q[0],c[0]测量量子比特 0 存入经典比特 0

控制块(Control Blocks)使用 CONTROL/ENDCONTROL:

CONTROL q[0],q[1]
X q[2]
Z q[3]
ENDCONTROL

仅当量子比特 0 和 1 都处于 |1⟩ 态时才执行包含的门。

求逆块(Dagger Blocks)使用 DAGGER/ENDDAGGER 来施加包含操作的逆操作:

DAGGER
H q[0]
RX q[1],(1.570796)
ENDDAGGER

自定义门(Custom Gates)使用 QGATE/ENDQGATE 配合可选参数:

QGATE my_gate a,b,(theta,phi)
  RX a,(theta)
  RY b,(phi)
  CNOT a,b
ENDQGATE

这里 a 和 b 是形式量子比特参数,theta 和 phi 是形式参数名。内置常量 PI 表示 π≈3.14159265358979。

条件执行(Conditional Execution)使用 QIF/QELSE/QELSEIF/QENDIF:

QIF c[0],c[1]
  X q[2]
QELSEIF c[2]
  Y q[2]
QELSE
  Z q[2]
QENDIF

条件检查指定的经典比特被解释为小端整数时是否等于 1。可以列出多个经典比特。

循环构造(Loop Constructs)使用 QWHILE/QENDWHILE:

QWHILE c[0]
  X q[1]
  MEASURE q[1],c[0]
QENDWHILE

只要条件经典比特评估为 1,循环体就重复执行。

注释(Comments)是以 // 开头的单行注释:

H q[0]         // Apply Hadamard
// This is a full-line comment

QASM 2.0 语法概述 ​

QASM 2.0 是 IBM 定义的标准量子汇编格式。QASM 2.0 程序的结构如下:

text
OPENQASM 2.0;
include "qelib1.inc";
qreg q[<n>];
creg c[<m>];
<gate> q[<i>];
<gate> q[<i>],q[<j>];
measure q[<i>] -> c[<j>];

与 OriginIR 的主要区别:

  • 头部为 OPENQASM 2.0; 后跟 include "qelib1.inc";。
  • 量子比特寄存器使用 qreg q[n]; 而非 QINIT n。
  • 经典寄存器使用 creg c[m]; 而非 CREG m。
  • 测量语法使用 -> 而非逗号:measure q[0] -> c[0]。
  • 门名称为小写:h、cx、cz、rx、ry、rz 等。
  • QASM 通过 gate 关键字支持自定义门定义。
  • QASM 通过 if(c==<val>) 语法支持条件执行。

表示之间的门映射 ​

下表展示常见门在 pyqpanda3、OriginIR 和 QASM 2.0 之间的映射:

pyqpanda3 APIOriginIRQASM 2.0
H(q)H q[i]h q[i]
X(q)X q[i]x q[i]
Y(q)Y q[i]y q[i]
Z(q)Z q[i]z q[i]
S(q)S q[i]s q[i]
T(q)T q[i]t q[i]
RX(q, angle)RX q[i],(angle)rx(angle) q[i]
RY(q, angle)RY q[i],(angle)ry(angle) q[i]
RZ(q, angle)RZ q[i],(angle)rz(angle) q[i]
P(q, angle)P q[i],(angle)p(angle) q[i]
CNOT(c, t)CNOT q[c],q[t]cx q[c],q[t]
CZ(c, t)CZ q[c],q[t]cz q[c],q[t]
SWAP(a, b)SWAP q[a],q[b]swap q[a],q[b]
TOFFOLI(a, b, t)TOFFOLI q[a],q[b],q[t]ccx q[a],q[b],q[t]
U1(q, lam)U1 q[i],(lam)u1(lam) q[i]
U2(q, phi, lam)U2 q[i],(phi,lam)u2(phi,lam) q[i]
U3(q, theta, phi, lam)U3 q[i],(theta,phi,lam)u3(theta,phi,lam) q[i]
CU(c, t, theta, phi, lam, gamma)CU q[c],q[t],(theta,phi,lam,gamma)cu(theta,phi,lam,gamma) q[c],q[t]
measure(q, c)MEASURE q[i],c[j]measure q[i] -> c[j]
BARRIER([qubits])BARRIER q[i],q[j],...barrier q[i],q[j],...

限制与格式选择 ​

每种格式都有特定的优势和限制:

mermaid
flowchart TD
    subgraph "OriginIR"
        O1["Full gate set"]
        O2["Dynamic circuits QIF/QWHILE"]
        O3["Custom gate QGATE"]
        O4["CONTROL / DAGGER blocks"]
        O5["QProg constructor"]
    end

    subgraph QASM20["QASM 2.0"]
        Q1["Standard gate set"]
        Q2["gate keyword definitions"]
        Q3["Conditional if(c==val)"]
        Q4["Barrier support"]
        Q5["Industry standard"]
    end

    subgraph QASMLim["QASM Limitations"]
        QL1["No QWHILE loops"]
        QL2["No RPHI gate"]
        QL3["No ISWAP gate"]
        QL4["Limited dynamic circuits"]
    end

    subgraph OIRLim["OriginIR Limitations"]
        OL1["QPanda-specific"]
        OL2["Limited external support"]
    end

    style OriginIR fill:#50c878,color:#fff,stroke:#2d8a4e
    style QASM20 fill:#f4a460,color:#fff,stroke:#b87333
    style QASMLim fill:#e74c3c,color:#fff,stroke:#c0392b
    style OIRLim fill:#e74c3c,color:#fff,stroke:#c0392b

何时使用 OriginIR:

  • 序列化 pyqpanda3 电路用于存储和后续检索。
  • 包含动态控制流(QIF、QWHILE)的电路。
  • 使用 RPHI、ISWAP、SQISWAP 或其他 QPanda 原生门的电路。
  • 在 pyqpanda3 生态系统内进行完整保真度的往返转换。
  • 使用 QProg(ir_string) 快速构建电路。

何时使用 QASM 2.0:

  • 与 Qiskit、Cirq 或其他框架交换电路。
  • 从公共仓库或教科书导入电路。
  • 以广泛理解的格式发布电路。
  • 提交到接受 QASM 的量子云服务。

需要注意的主要限制:

  • QASM 2.0 不支持 QWHILE 循环。如果你将包含 qwhile 构造的电路导出为 QASM,循环结构将不会被保留。
  • 某些 QPanda 原生门(如 ISWAP、RPHI)没有直接的 QASM 2.0 等价物。将这些门导出为 QASM 可能会产生错误或分解为受支持的门。
  • precision 参数影响浮点保真度。对于包含许多旋转门的电路,使用 precision=15 或更高值以确保往返准确性。
  • OriginIR 和 QASM 对 measure 语法使用不同的约定:OriginIR 使用 MEASURE q[i],c[j],而 QASM 使用 measure q[i] -> c[j]。

便捷功能:QProg OriginIR 构造函数 ​

除了 convert_originir_string_to_qprog 函数外,你还可以使用 QProg 构造函数直接从 OriginIR 字符串构造 QProg:

python
from pyqpanda3.core import QProg, CPUQVM

# Direct construction from OriginIR string
prog = QProg("""
    QINIT 2
    CREG 2
    H q[0]
    CNOT q[0],q[1]
    MEASURE q[0],c[0]
    MEASURE q[1],c[1]
""")

# The program is ready to run
machine = CPUQVM()
machine.run(prog=prog, shots=1000)
counts = machine.result().get_counts()
print(counts)

运行结果:

{'11': 509, '00': 491}

类似地,QProg 实例上的 .originir() 方法无需使用模块级函数即可返回 OriginIR 字符串:

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

# These two calls are equivalent:
ir1 = prog.originir()
# ir1 = convert_qprog_to_originir(prog)

print(ir1)

运行结果:

QINIT 2
CREG 1
H q[0]
CNOT q[0],q[1]

精度与往返保真度 ​

默认精度 8 位小数对大多数实际电路足够。然而,在执行往返转换(先导出再导入)时,对于包含许多参数化门的电路,浮点舍入误差可能累积。原始电路和往返电路酉矩阵之间的平方矩阵距离应满足:

maxi,j|Uijoriginal−Uijround-trip|2<ϵ

其中 ϵ 取决于精度和电路深度。对于包含 10+ 个旋转门的电路,使用 precision=15 以保持 ϵ<10−10。

实用的验证模式:

python
from pyqpanda3.quantum_info import Unitary
import numpy as np

def verify_round_trip(prog, precision=15, tolerance=1e-6):
    """Verify that export-import round-trip preserves circuit unitary."""
    qasm_str = convert_qprog_to_qasm(prog, precision=precision)
    prog2 = convert_qasm_string_to_qprog(qasm_str)

    mat1 = Unitary(prog.to_circuit()).ndarray()
    mat2 = Unitary(prog2.to_circuit()).ndarray()

    # Account for global phase by comparing squared distances
    diff = np.abs(mat1 - mat2) ** 2
    max_error = diff.max()

    if max_error > tolerance:
        raise AssertionError(
            f"Round-trip error {max_error:.2e} exceeds tolerance {tolerance:.2e}"
        )
    return True

电路序列化模式 ​

常见的工作流是以编程方式构建电路、将其序列化以进行存储或传输,之后反序列化:

mermaid
flowchart TD
    A["Build QProg"] --> B["Export to IR"]
    B --> C["Store (file/DB)"]
    C --> D["Load IR"]
    D --> E["Import to QProg"]
    E --> F["Execute on simulator"]

    style A fill:#4a90d9,color:#fff,stroke:#2c5f9e
    style B fill:#50c878,color:#fff,stroke:#2d8a4e
    style C fill:#7b68ee,color:#fff,stroke:#4a3db5
    style D fill:#7b68ee,color:#fff,stroke:#4a3db5
    style E fill:#50c878,color:#fff,stroke:#2d8a4e
    style F fill:#f4a460,color:#fff,stroke:#b87333

模式 1:OriginIR 用于内部存储(完整保真度)

python
# Serialize
originir_str = convert_qprog_to_originir(prog, precision=15)
with open("circuit_cache.txt", "w") as f:
    f.write(originir_str)

# Deserialize
prog = convert_originir_file_to_qprog("circuit_cache.txt")
# Clean up the temporary file when done:
# import os; os.remove("circuit_cache.txt")

模式 2:QASM 用于框架互操作性

python
# Serialize for Qiskit consumption
qasm_str = convert_qprog_to_qasm(prog, precision=15)
with open("circuit_for_qiskit.qasm", "w") as f:
    f.write(qasm_str)

# In Qiskit:
# from qiskit import QuantumCircuit
# qc = QuantumCircuit.from_qasm_file("circuit_for_qiskit.qasm")

模式 3:批量转换

当你需要在格式之间转换许多电路时,使用带错误处理的循环:

python
from pyqpanda3.intermediate_compiler import (
    convert_qasm_string_to_qprog,
    convert_qprog_to_originir,
)

qasm_circuits = ["<QASM 1>", "<QASM 2>", "<QASM 3>"]  # your QASM strings

for i, qasm_str in enumerate(qasm_circuits):
    try:
        prog = convert_qasm_string_to_qprog(qasm_str)
        originir_str = convert_qprog_to_originir(prog)
        with open(f"circuit_{i:04d}.txt", "w") as f:
            f.write(originir_str)
    except Exception as e:
        print(f"Failed to convert circuit {i}: {e}")

运行结果:

Failed to convert circuit 0: Unknown exception
Failed to convert circuit 1: Unknown exception
Failed to convert circuit 2: Unknown exception

常见陷阱 ​

  1. OriginIR 缺少头部行:每个 OriginIR 字符串必须以 QINIT 和 CREG 声明开头。省略它们会导致解析错误。

  2. 往返转换使用错误精度:默认精度 8 位小数可能在包含许多旋转门的电路中丢失有效数字。进行往返转换时始终使用 precision=15。

  3. QASM 中不支持的门:ISWAP、SQISWAP 和 RPHI 等门没有 QASM 2.0 等价物。将包含这些门的电路导出为 QASM 会失败或产生不正确结果。改用 OriginIR。

  4. 测量语法混淆:记住 OriginIR 使用 MEASURE q[i],c[j](逗号),而 QASM 使用 measure q[i] -> c[j](箭头)。

  5. QASM 中的动态电路:QASM 2.0 的 if(c==val) 语法仅支持有限形式的条件执行。包含 QWHILE 循环的复杂动态电路无法导出为 QASM。完整动态电路支持请使用 OriginIR。

  6. 门名称大小写敏感:OriginIR 门名称为大写(H、CNOT、RX)。QASM 门名称为小写(h、cx、rx)。混合使用会导致解析错误。

API 参考摘要 ​

所有函数从 pyqpanda3.intermediate_compiler 导入:

python
from pyqpanda3.intermediate_compiler import (
    convert_originir_string_to_qprog,   # str -> QProg
    convert_originir_file_to_qprog,     # filepath -> QProg
    convert_qasm_string_to_qprog,       # str -> QProg
    convert_qasm_file_to_qprog,         # filepath -> QProg
    convert_qprog_to_originir,          # QProg -> str
    convert_qprog_to_qasm,              # QProg -> str
)

函数签名:

text
convert_qprog_to_originir(prog: QProg, precision: int = 8) -> str
convert_qprog_to_qasm(prog: QProg, precision: int = 8) -> str
convert_originir_string_to_qprog(ir_string: str) -> QProg
convert_originir_file_to_qprog(file_path: str) -> QProg
convert_qasm_string_to_qprog(qasm_string: str) -> QProg
convert_qasm_file_to_qprog(file_path: str) -> QProg

precision 参数控制导出 IR 字符串中浮点值的小数位数。它必须是非负整数。更高的值保留更多准确性,但会产生更长的字符串输出。

另见:二进制通信协议编码 ​

上面这些都是文本形式的 IR 转换。如果你需要把 OriginIR 直接编码成量子云使用的二进制通信协议字节流(例如批量提交),使用 pyqpanda3.utils 中的函数,无需先构造 QProg:

python
from pyqpanda3.utils import CommProtocolConfig, comm_protocol_encode_originir_string

config = CommProtocolConfig()
config.shots = 2048
payload = comm_protocol_encode_originir_string("QINIT 1\nCREG 1\nH q[0]\nMEASURE q[0],c[0]\n", config)
print(type(payload).__name__, len(payload) > 0)   # bytes True

comm_protocol_encode_originir_file(file_path, config) 从文件读取 OriginIR;comm_protocol_encode([prog], config) 从 QProg 编码(与 OriginIR 直编码等价)。逆向把指令 JSON 还原成程序用 pyqpanda3.core.instruction_to_qprog(单流返回 QProg,多流返回 list[QProg])。完整说明见 通信协议 API 参考 与 云计算教程 §10.6。

Released under the MIT License.