Skip to content

IR Conversion -- OriginIR and QASM

Convert between QProg, OriginIR, and QASM intermediate representations for circuit interoperability, serialization, and debugging.


Problem

Quantum circuits built with pyqpanda3 exist as in-memory QProg objects. In many situations you need to represent a circuit as a plain-text string so that it can be stored, transmitted, or shared with other frameworks. Two text-based intermediate representations (IRs) are supported:

OriginIR is QPanda's native IR format. It provides full fidelity for every pyqpanda3 feature, including dynamic circuits (QIF/QELSE/QWHILE), custom gate definitions (QGATE), controlled and daggered operations, and all native gate types. Use OriginIR when you need to serialize and deserialize circuits within the pyqpanda3 ecosystem.

OpenQASM 2.0 is the industry-standard quantum assembly language supported by Qiskit, Cirq, and many other frameworks. Use QASM when you need to exchange circuits with non-QPanda tools, import circuits from the open-source community, or export circuits for publication.

You need IR conversion when:

  • Interoperability -- Import a circuit authored in Qiskit via QASM, or export a pyqpanda3 circuit for use in another framework.
  • Persistence -- Save a circuit to disk as an OriginIR or QASM file, then load it later without re-running the construction code.
  • Verification -- Export a circuit to text, visually inspect the gate sequence, or compare two circuits by comparing their string representations.
  • Debugging -- Print the IR of a complex QProg to understand its gate structure, control flow, and measurement operations.
  • Round-trip testing -- Build a circuit, export it to IR, import it back, and verify the unitary matrices match to confirm correctness of the conversion pipeline.

The following diagram shows the conversion paths supported by the intermediate_compiler module:


Solution

The intermediate_compiler module provides six functions that cover all conversion directions:

FunctionDirectionInputOutput
convert_qprog_to_originir(prog, precision)QProg -> OriginIRQProg, optional intstr
convert_qprog_to_qasm(prog, precision)QProg -> QASMQProg, optional intstr
convert_originir_string_to_qprog(ir_string)OriginIR str -> QProgstrQProg
convert_originir_file_to_qprog(file_path)OriginIR file -> QProgstr (path)QProg
convert_qasm_string_to_qprog(qasm_string)QASM str -> QProgstrQProg
convert_qasm_file_to_qprog(file_path)QASM file -> QProgstr (path)QProg

The precision parameter (default: 8) controls the number of decimal places used for floating-point angle values in the exported string. A higher precision preserves more accuracy, which is important for round-trip fidelity.

Step 1: Export a circuit to OriginIR or QASM

Build a QProg using the standard pyqpanda3 API, then call the appropriate conversion function:

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)

Step 2: Import an OriginIR or QASM string into a QProg

Parse a text representation back into an executable 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)

Step 3: Load a circuit from a file

For circuits stored on disk, use the file-based import functions:

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

prog = convert_originir_file_to_qprog("/path/to/circuit.txt")
prog = convert_qasm_file_to_qprog("/path/to/circuit.qasm")

Step 4: Verify round-trip fidelity

After a round-trip conversion (export then import), compare the unitary matrices to confirm no information was lost:

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

Code

Example 1: Build a Bell State and Export to Both Formats

This example creates a Bell state circuit and exports it to both OriginIR and 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)

Expected OriginIR output:

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

Expected QASM output:

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];

Example 2: Import an OriginIR String and Run It

This example parses an OriginIR string, runs the resulting circuit on a simulator, and prints the measurement counts:

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)

Example 3: Import a QASM String and Run It

This example imports a quantum teleportation circuit written in 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)

Example 4: Round-Trip Conversion with Verification

This example demonstrates a full round-trip: build a circuit, export to QASM, import back, and verify that the unitary matrices match. This is a common pattern for validating that the conversion pipeline preserves circuit semantics.

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.")

Example 5: Saving Circuits to Files and Loading Them

This example shows how to write an OriginIR or QASM string to a file and read it back:

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

# --- Save as OriginIR ---
originir_str = convert_qprog_to_originir(prog)
originir_path = "/tmp/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 = "/tmp/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)

Example 6: Working with Controlled and Daggered Operations

OriginIR natively supports CONTROL/ENDCONTROL blocks and DAGGER/ENDDAGGER blocks. This example demonstrates how these map between the QProg API and the 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)

The output will contain CONTROL/ENDCONTROL and DAGGER/ENDDAGGER blocks:

QINIT 3
CREG 0
H q[0]
H q[1]
H q[2]
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

Example 7: Custom Gate Definitions in OriginIR

OriginIR supports user-defined gates via the QGATE/ENDQGATE syntax. You can define reusable gate blocks with parameters:

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)

The QGATE block above defines new_H as a composite of Y and Z on two qubits, and new_RX as a parameterized gate that uses PI as a built-in constant and references parameters d and e.

Example 8: Dynamic Circuits (QIF / QWHILE) in OriginIR

OriginIR supports dynamic circuit constructs -- conditional execution (QIF/QELSE/QELSEIF/QENDIF) and mid-circuit loops (QWHILE/QENDWHILE). These are essential for circuits where subsequent operations depend on earlier measurement results:

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

Explanation

OriginIR Syntax Overview

OriginIR is a line-oriented, text-based IR. Each line contains a single instruction or a block delimiter. The general structure is:

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 and CREG) are required. They declare the number of qubits and classical bits. The qubit index range is [0,n1] where n is the QINIT value.

Gate instructions follow the pattern GATE_NAME target_qubit(s),(parameter(s)). For example:

OriginIR LineMeaning
H q[0]Hadamard on qubit 0
X q[1]Pauli-X on qubit 1
RX q[0],(1.570796)Rotation around X axis by π/2 radians
CNOT q[0],q[1]CNOT with control=0, target=1
MEASURE q[0],c[0]Measure qubit 0 into classical bit 0

Control blocks use CONTROL/ENDCONTROL:

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

This applies the enclosed gates only when both qubit 0 and qubit 1 are in the |1 state.

Dagger blocks use DAGGER/ENDDAGGER to apply the inverse of the enclosed operations:

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

Custom gates use QGATE/ENDQGATE with optional parameters:

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

Here a and b are formal qubit arguments, and theta and phi are formal parameter names. The built-in constant PI represents π3.14159265358979.

Conditional execution uses QIF/QELSE/QELSEIF/QENDIF:

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

The condition checks whether the specified classical bits, when interpreted as a little-endian integer, equal 1. Multiple classical bits can be listed.

Loop constructs use QWHILE/QENDWHILE:

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

The loop body executes repeatedly as long as the condition classical bit evaluates to 1.

Comments are single-line comments beginning with //:

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

QASM 2.0 Syntax Overview

QASM 2.0 is the standard quantum assembly format defined by IBM. A QASM 2.0 program has this structure:

qasm
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>];

Key differences from OriginIR:

  • The header is OPENQASM 2.0; followed by include "qelib1.inc";.
  • Qubit registers use qreg q[n]; instead of QINIT n.
  • Classical registers use creg c[m]; instead of CREG m.
  • The measurement syntax uses -> instead of a comma: measure q[0] -> c[0].
  • Gate names are lowercase: h, cx, cz, rx, ry, rz, etc.
  • QASM supports custom gate definitions via the gate keyword.
  • QASM supports conditional execution via if(c==<val>) syntax.

Gate Mapping Between Representations

The following table shows how common gates map between pyqpanda3, OriginIR, and 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],...

Limitations and Format Selection

Each format has specific strengths and limitations:

When to use OriginIR:

  • Serializing pyqpanda3 circuits for storage and later retrieval.
  • Circuits with dynamic control flow (QIF, QWHILE).
  • Circuits using RPHI, ISWAP, SQISWAP, or other QPanda-native gates.
  • Full-fidelity round-trips within the pyqpanda3 ecosystem.
  • Quick circuit construction using QProg(ir_string).

When to use QASM 2.0:

  • Exchanging circuits with Qiskit, Cirq, or other frameworks.
  • Importing circuits from public repositories or textbooks.
  • Publishing circuits in a widely understood format.
  • Submitting to quantum cloud services that accept QASM.

Key limitations to be aware of:

  • QASM 2.0 does not support QWHILE loops. If you export a circuit containing a qwhile construct to QASM, the loop structure will not be preserved.
  • Some QPanda-native gates (e.g., ISWAP, RPHI) do not have direct QASM 2.0 equivalents. Exporting these gates to QASM may produce an error or decompose them into supported gates.
  • The precision parameter affects floating-point fidelity. For circuits with many rotation gates, use precision=15 or higher to ensure round-trip accuracy.
  • OriginIR and QASM use different conventions for the measure syntax: OriginIR uses MEASURE q[i],c[j] while QASM uses measure q[i] -> c[j].

Convenience: QProg OriginIR Constructor

In addition to the convert_originir_string_to_qprog function, you can also construct a QProg directly from an OriginIR string using the QProg constructor:

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)

Similarly, the .originir() method on a QProg instance returns the OriginIR string without needing the module-level function:

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)

Precision and Round-Trip Fidelity

The default precision of 8 decimal places is sufficient for most practical circuits. However, when performing round-trip conversion (export then import), floating-point rounding can accumulate errors for circuits with many parameterized gates. The squared matrix distance between the original and round-tripped circuit unitaries should satisfy:

maxi,j|UijoriginalUijround-trip|2<ϵ

where ϵ depends on the precision and circuit depth. For circuits with 10+ rotation gates, use precision=15 to keep ϵ<1010.

A practical verification pattern:

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

Circuit Serialization Patterns

A common workflow is to build circuits programmatically, serialize them for storage or transfer, and deserialize them later:

Pattern 1: OriginIR for internal storage (full fidelity)

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

Pattern 2: QASM for framework interoperability

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

Pattern 3: Batch conversion

When you have many circuits to convert between formats, use a loop with error handling:

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

Common Pitfalls

  1. Missing header lines in OriginIR: Every OriginIR string must begin with QINIT and CREG declarations. Omitting them causes a parse error.

  2. Wrong precision for round-trips: The default precision of 8 decimal places may lose significant digits in circuits with many rotation gates. Always use precision=15 for round-trip conversions.

  3. Unsupported gates in QASM: Gates like ISWAP, SQISWAP, and RPHI have no QASM 2.0 equivalent. Exporting circuits containing these gates to QASM will fail or produce incorrect results. Use OriginIR instead.

  4. Measurement syntax confusion: Remember that OriginIR uses MEASURE q[i],c[j] (comma), while QASM uses measure q[i] -> c[j] (arrow).

  5. Dynamic circuits in QASM: The QASM 2.0 if(c==val) syntax supports only a limited form of conditional execution. Complex dynamic circuits with QWHILE loops cannot be exported to QASM. Use OriginIR for full dynamic circuit support.

  6. Gate name case sensitivity: OriginIR gate names are uppercase (H, CNOT, RX). QASM gate names are lowercase (h, cx, rx). Mixing the two causes parse errors.

API Reference Summary

All functions are imported from 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
)

Function signatures:

python
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

The precision parameter controls the number of decimal places for floating-point values in the exported IR string. It must be a non-negative integer. Higher values preserve more accuracy at the cost of longer string output.

Released under the MIT License.