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 模块支持的转换路径:
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 -> OriginIR | QProg,可选 int | str |
convert_qprog_to_qasm(prog, precision) | QProg -> QASM | QProg,可选 int | str |
convert_originir_string_to_qprog(ir_string) | OriginIR 字符串 -> QProg | str | QProg |
convert_originir_file_to_qprog(file_path) | OriginIR 文件 -> QProg | str(路径) | QProg |
convert_qasm_string_to_qprog(qasm_string) | QASM 字符串 -> QProg | str | QProg |
convert_qasm_file_to_qprog(file_path) | QASM 文件 -> QProg | str(路径) | QProg |
precision 参数(默认值:8)控制导出字符串中浮点角度值使用的小数位数。更高的精度保留更多准确性,这对往返保真度很重要。
导出方向存在一处边界。0.5.0 引入的原生 RESET 门无法用 convert_qprog_to_qasm() 所输出的 QASM 2.0 子集表示,因此导出含 RESET 的程序会抛出 RuntimeError:
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])运行结果:
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,然后调用相应的转换函数:
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:
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:从文件加载电路
对于存储在磁盘上的电路,使用基于文件的导入函数:
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:验证往返保真度
在往返转换(先导出再导入)后,比较酉矩阵以确认没有信息丢失:
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:
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 字符串,在模拟器上运行结果电路,并打印测量计数:
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 编写的量子隐形传态电路:
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、导入回来,并验证酉矩阵是否匹配。这是验证转换管道保持电路语义的常见模式。
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 字符串写入文件并读回:
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 之间的映射关系:
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 语法支持用户定义的门。你可以定义带参数的可复用门块:
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)。这些对于后续操作依赖于先前测量结果的电路至关重要:
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)是必需的。它们声明量子比特数和经典比特数。量子比特索引范围为 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 轴旋转 |
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 都处于
求逆块(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 表示
条件执行(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 commentQASM 2.0 语法概述
QASM 2.0 是 IBM 定义的标准量子汇编格式。QASM 2.0 程序的结构如下:
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 API | OriginIR | QASM 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],... |
限制与格式选择
每种格式都有特定的优势和限制:
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:
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 字符串:
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 位小数对大多数实际电路足够。然而,在执行往返转换(先导出再导入)时,对于包含许多参数化门的电路,浮点舍入误差可能累积。原始电路和往返电路酉矩阵之间的平方矩阵距离应满足:
其中 precision=15 以保持
实用的验证模式:
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电路序列化模式
常见的工作流是以编程方式构建电路、将其序列化以进行存储或传输,之后反序列化:
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 用于内部存储(完整保真度)
# 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 用于框架互操作性
# 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:批量转换
当你需要在格式之间转换许多电路时,使用带错误处理的循环:
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常见陷阱
OriginIR 缺少头部行:每个 OriginIR 字符串必须以
QINIT和CREG声明开头。省略它们会导致解析错误。往返转换使用错误精度:默认精度 8 位小数可能在包含许多旋转门的电路中丢失有效数字。进行往返转换时始终使用
precision=15。QASM 中不支持的门:
ISWAP、SQISWAP和RPHI等门没有 QASM 2.0 等价物。将包含这些门的电路导出为 QASM 会失败或产生不正确结果。改用 OriginIR。测量语法混淆:记住 OriginIR 使用
MEASURE q[i],c[j](逗号),而 QASM 使用measure q[i] -> c[j](箭头)。QASM 中的动态电路:QASM 2.0 的
if(c==val)语法仅支持有限形式的条件执行。包含QWHILE循环的复杂动态电路无法导出为 QASM。完整动态电路支持请使用 OriginIR。门名称大小写敏感:OriginIR 门名称为大写(
H、CNOT、RX)。QASM 门名称为小写(h、cx、rx)。混合使用会导致解析错误。
API 参考摘要
所有函数从 pyqpanda3.intermediate_compiler 导入:
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
)函数签名:
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) -> QProgprecision 参数控制导出 IR 字符串中浮点值的小数位数。它必须是非负整数。更高的值保留更多准确性,但会产生更长的字符串输出。
另见:二进制通信协议编码
上面这些都是文本形式的 IR 转换。如果你需要把 OriginIR 直接编码成量子云使用的二进制通信协议字节流(例如批量提交),使用 pyqpanda3.utils 中的函数,无需先构造 QProg:
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 Truecomm_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。