Skip to content

OriginIR Syntax ​

OriginIR is the native intermediate representation (IR) of QPanda quantum programs — the text format used within the pyqpanda3 ecosystem for serializing, transporting, and debugging quantum programs. This chapter details the OriginIR grammar piece by piece: overall program structure, QINIT/CREG declarations, quantum gates of every arity, angle expressions, the DAGGER/CONTROL modifiers, QIF/QWHILE classical control flow, MEASURE/RESET operations, custom gates, and a common-error reference.

Converting OriginIR text to QProg (and back) is a "how to" concern — see IR conversion and the convert_originir_string_to_qprog API reference. This chapter only answers "what does the grammar look like".


1. Grammar Overview ​

Authoritative Grammar Definition ​

The authoritative grammar of OriginIR is defined by its ANTLR lexer/parser files (.g4):

  • originirLexer.g4 (138 lines): lexical rules — keywords, gate-name tokens, numbers and identifiers.
  • originirParser.g4 (330 lines): syntax rules — program structure and the productions of each statement.

The translator shipped inside pyqpanda3 0.4.1 (the intermediate_compiler module) implements the classic dialect: it agrees with the .g4 on QINIT/CREG, gate declarations, and MEASURE/RESET, but keeps the traditional spelling for the version header, expressions, control flow, and custom gates (every example below was verified by running convert_originir_string_to_qprog). The differences between the two dialects are itemized in section 9.

Program Structure ​

Per the program rule of the .g4, an OriginIR program is:

text
program: version? statement* EOF

that is, "an optional version header followed by any number of statements". Each statement is one of the kinds listed by the statement rule:

Statement kindGrammar ruleExample
Declaration (qubit / classical register)declaration → qinit_declaration / cinit_declarationQINIT 2, CREG 4
Quantum gate declarationquantum_gate_declarationH q[0], RX q[0],(PI/2)
Measurementmeasure_statementMEASURE q[0],c[0]
Resetreset_statementRESET q[1]
Classical assignmentassign_statementc[0]=c[0]+1 (see sections 5 and 9)
Barrierbarrier_statementBARRIER q[0],q[1]
File includeincludestatementinclude "other.originir" (see section 9)
Block statementblockstatementDAGGER/CONTROL/QIF/QWHILE/QGATE

The smallest complete program (the common skeleton of every example in this chapter):

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

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

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output (prog.originir() is a round-trip: QProg converted back to OriginIR text):

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

Every example in this chapter follows this pattern: OriginIR text → parsed into a QProg by convert_originir_string_to_qprog → print the round-trip text to verify. Note that angle parameters are evaluated into fixed-point decimals in the round-trip output (section 5), and CREG is normalized to the number of classical bits actually used by MEASURE (minimum 1 — this section prints CREG 1 with no measurement).


2. Header, Version Declaration, Comments ​

Version Header ​

The program rule of the .g4 allows an optional version header:

text
version: ORIGINIR VersionSpecifier SEMICOLON

e.g. ORIGINIR 1.0;. However, the pyqpanda3 0.4.1 translator has not implemented version-header parsing; the observed error is (verbatim runtime message):

text
ORIGINIR 1.0;
QINIT 2
H q[0]

Output:

text
Error:
	<Expected> OriginIR Statement
	<Problem> The string is invalid originir string.It's Identifier (ORIGINIR)
	<Result> Exit.

So in 0.4.1, do not write a version header — start directly with QINIT.

Comments ​

The lexer supports both C++ comment styles — // line comments and /* */ block comments (LineComment/BlockComment rules); both are skipped and may appear anywhere:

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 2
// line comment
/* block
   comment */
H q[0]  // trailing comment
CNOT q[0],q[1]"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

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

Statement Separation ​

The 0.4.1 dialect separates statements with line breaks (NEWLINE tokens are skipped; blank lines are allowed) and does not accept semicolons. The .g4 requires a semicolon after every statement, but 0.4.1 rejects a semicolon outright:

text
QINIT 2
H q[0];

Output:

text
Error:
	<Expected> OriginIR Statement
	<Problem> The string is invalid originir string.It's ;
	<Result> Exit.

Keyword Case Sensitivity ​

Keywords are strictly case-sensitive (the Identifier rule is [a-zA-Z_][a-zA-Z_0-9]*, and keywords such as QINIT are uppercase tokens). qinit is not recognized:

text
qinit 2
H q[0]

Output:

text
Error:
	<Expected> OriginIR Statement
	<Problem> The string is invalid originir string.It's Identifier (qinit)
	<Result> Exit.

3. QINIT / CREG Declarations ​

QINIT allocates qubits and CREG allocates classical registers; both are "keyword + space + count":

text
qinit_declaration : QINIT_KEY Integer_Literal ;
cinit_declaration : CREG_KEY Integer_Literal ;

The count must be a non-negative integer (Integer_Literal: [1-9][0-9]* | '0', i.e. no leading zeros). In 0.4.1 both must come before use:

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 4
CREG 4
H q[0]
H q[1]
H q[2]
H q[3]"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 4
CREG 1
H q[0]
H q[1]
H q[2]
H q[3]

Single Qubits and Whole Qubit Arrays ​

  • A single qubit is referenced as q[i] (index: LBRACK Integer_Literal RBRACK; the index must be an integer constant — see section 9 for q[c[0]]).
  • Some single-qubit gates and MEASURE also accept a bare q, meaning the whole qubit array (expanded to per-qubit operations):
python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 3
H q"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output (H q expands to one H per qubit):

text
QINIT 3
CREG 1
H q[0]
H q[1]
H q[2]

MEASURE q,c requires the qubit count to equal the register count (see section 7).

Common Errors: Count Mismatches ​

Mismatched qubit/register counts in MEASURE q,c:

text
QINIT 2
CREG 3
MEASURE q,c

Output:

text
Error: For "MEASURE q c", qbits' total num should be same as cbits'. Here, they are different.

Measuring without a declared CREG:

text
QINIT 1
MEASURE q[0],c[0]

Output:

text
add_measure_literal too little cbits is allocated

4. Single-, Double-, and Triple-Qubit Gates ​

quantum_gate_declaration classifies gates into 10 categories by "number of qubits × number of parameters". The table lists the gate tokens of the lexer and their category (the .g4 rules single_gate_without_parameter_type etc.); tokens listed in the official tutorial whose behavior in the .g4 differs from pyqpanda3 0.4.1 (CP/RZX/RXX/RYY/RZZ/ECHO/ISWAPTHETA/NOT) are covered in section 9:

CategoryGrammar ruleGates
Single-qubit, no parametersingle_gate_without_parameter_typeH T S X Y Z X1 Y1 Z1 I
Single-qubit, one parametersingle_gate_with_one_parameter_typeRX RY RZ U1 ECHO
Single-qubit, two parameterssingle_gate_with_two_parameter_typeU2 RPhi
Single-qubit, three parameterssingle_gate_with_three_parameter_typeU3
Single-qubit, four parameterssingle_gate_with_four_parameter_typeU4
Double-qubit, no parameterdouble_gate_without_parameter_typeCNOT CZ ISWAP SQISWAP SWAP
Double-qubit, one parameterdouble_gate_with_one_parameter_typeISWAPTHETA CR (0.4.1 also accepts CP RZX RXX RYY RZZ)
Double-qubit, four parametersdouble_gate_with_four_parameter_typeCU
Triple-qubit, no parametertriple_gate_without_parameter_typeTOFFOLI

General formats:

  • Parameterless gates: gate q[i] (single-qubit), gate q[i],q[j] (double-qubit), or gate q[i],q[j],q[k] (triple-qubit);
  • Parameterized gates: gate q[i],(p1,p2,...) — parameters in parentheses, comma-separated (see section 5 for the parameter syntax).

Single-Qubit Gates Without Parameters ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 3
H q[0]
X q[1]
T q[2]
I q[0]"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 3
CREG 1
H q[0]
X q[1]
T q[2]
I q[0]

Single-Qubit Gates With One Parameter ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 3
RX q[0],(PI/2)
RY q[1],(0.3)
RZ q[2],(-0.5)
U1 q[0],(PI)"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output (note the angles are evaluated):

text
QINIT 3
CREG 1
RX q[0],(1.57079633)
RY q[1],(0.30000000)
RZ q[2],(-0.50000000)
U1 q[0],(3.14159265)

Single-Qubit Gates With Two Parameters (U2 / RPhi) ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 2
U2 q[0],(PI/2,-PI/3)
RPhi q[1],(0.5,1.5)"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 2
CREG 1
U2 q[0],(1.57079633,-1.04719755)
RPhi q[1],(0.50000000,1.50000000)

Single-Qubit Gate With Three Parameters (U3) ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 1
U3 q[0],(PI/2,PI/3,PI/4)"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 1
CREG 1
U3 q[0],(1.57079633,1.04719755,0.78539816)

Single-Qubit Gate With Four Parameters (U4) ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 1
U4 q[0],(0.1,0.2,0.3,0.4)"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 1
CREG 1
U4 q[0],(0.10000000,0.20000000,0.30000000,0.40000000)

Double-Qubit Gates Without Parameters ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 4
CNOT q[0],q[1]
CZ q[1],q[2]
ISWAP q[2],q[3]
SQISWAP q[0],q[3]
SWAP q[1],q[2]"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 4
CREG 1
CNOT q[0],q[1]
CZ q[1],q[2]
ISWAP q[2],q[3]
SQISWAP q[0],q[3]
SWAP q[1],q[2]

Double-Qubit Gates With One Parameter ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 2
CR q[0],q[1],(PI/4)
RZX q[0],q[1],(0.5)
RXX q[0],q[1],(0.5)
RYY q[0],q[1],(0.5)
RZZ q[0],q[1],(0.5)
CP q[0],q[1],(0.5)"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output (CP is re-serialized as CR in the round trip; see section 9):

text
QINIT 2
CREG 1
CR q[0],q[1],(0.78539816)
RZX q[0],q[1],(0.50000000)
RXX q[0],q[1],(0.50000000)
RYY q[0],q[1],(0.50000000)
RZZ q[0],q[1],(0.50000000)
CR q[0],q[1],(0.50000000)

Double-Qubit Gate With Four Parameters (CU) ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 2
CU q[0],q[1],(0.1,0.2,0.3,0.4)"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 2
CREG 1
CU q[0],q[1],(0.10000000,0.20000000,0.30000000,0.40000000)

Triple-Qubit Gate (TOFFOLI) ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 3
TOFFOLI q[0],q[1],q[2]"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output (TOFFOLI is decomposed into a controlled-X block in the round trip; see CONTROL in section 6):

text
QINIT 3
CREG 1
CONTROL q[0],q[1]
X q[2]
ENDCONTROL

ECHO (One-Parameter Gate) ​

single_gate_with_one_parameter_type in the .g4 includes ECHO, and 0.4.1 supports it too:

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 1
ECHO q[0],(0.5)"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 1
CREG 1
ECHO q[0],(0.50000000)

5. Expressions and Constants ​

Constants ​

The constant rule admits only three forms:

text
constant : Integer_Literal | Double_Literal | PI ;

i.e. integers (no leading zeros), floats (e.g. 0.5, -3.141593), and PI.

Angle-Parameter Expressions ​

Every parameterized gate takes its parameters in parentheses: gate q[i],(expr1,expr2,...). 0.4.1 evaluates angle expressions at parse time, supporting + - * /, parentheses, and unary minus; results are written back into the round-trip output as fixed-point decimals (8 digits by default):

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 3
RX q[0],(PI/2)
RX q[1],(1+2*3)
RX q[2],(-PI/2)
RY q[0],(2*PI/3)"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output (1+2*3 evaluates to 7, 2*PI/3 to 2.09439510):

text
QINIT 3
CREG 1
RX q[0],(1.57079633)
RX q[1],(7.00000000)
RX q[2],(-1.57079633)
RY q[0],(2.09439510)

Formally, the constant_expression rule of the .g4 only allows "constant op constant" (a single operation: constant_expression: constant | constant arithmetic_op constant | arithmetic_op constant); in practice the 0.4.1 evaluator is more permissive and accepts arbitrary-length arithmetic chains (such as the 1+2*3 and 2*PI/3 above).

Identifiers may only appear as custom-gate formal parameters (section 8); an undefined identifier at the top level (e.g. RX q[0],(theta)) fails at evaluation time.

Classical Expressions and Assignment: Not Supported in 0.4.1 ​

The .g4 defines a full classical-expression chain and assignment statements (expression → logical_or_expression → … → primary_expression, plus assign_statement: c_KEY_declaration ASSIGN logical_or_expression), usable in QIF/QWHILE conditions and assignments such as c[0]=c[0]+1. 0.4.1 does not implement classical expressions (the official tutorial also marks them as "not supported for now"); the observed errors are:

text
QINIT 1
CREG 2
c[0]=c[0]+1

Output (the parse error is printed to stderr; the raised exception is Unknown exception):

text
Error at line 3, column 6: Expected expression, got c.
text
Unknown exception
text
QINIT 5
CREG 5
QWHILE c[0]<5
H q[c[0]]
QENDWHILE

Output:

text
Error:
	<Expected> OriginIR Statement
	<Problem> The string is invalid originir string.It's <
	<Result> Exit.

In 0.4.1 the QIF/QWHILE condition can only be a "list of measured classical bits" — see section 7.


6. The DAGGER / CONTROL Modifiers ​

DAGGER applies the transposed conjugate (inverse) to a group of gates; CONTROL adds control qubits to a group. The current .g4 form is the block syntax:

text
dagger_statement : DAGGER_KEY LBRACE statement* RBRACE ;
control_statement : CONTROL_KEY controlbit_list LBRACE statement* RBRACE ;

0.4.1 accepts both the traditional form (DAGGER … ENDDAGGER, CONTROL ctrlbit_list … ENDCONTROL) and the block form above (DAGGER { ... }, CONTROL q[i] { ... }).

Traditional Form DAGGER / ENDDAGGER ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 2
DAGGER
H q[0]
CNOT q[0],q[1]
ENDDAGGER"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output (0.4.1 emits one DAGGER block per gate in the round trip):

text
QINIT 2
CREG 1
DAGGER
CNOT q[0],q[1]
ENDDAGGER
DAGGER
H q[0]
ENDDAGGER

Block Form DAGGER { ... } ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 2
DAGGER {
H q[0]
CNOT q[0],q[1]
}"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 2
CREG 1
DAGGER
CNOT q[0],q[1]
ENDDAGGER
DAGGER
H q[0]
ENDDAGGER

Traditional Form CONTROL / ENDCONTROL ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 3
CONTROL q[2]
H q[0]
CNOT q[0],q[1]
ENDCONTROL"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 3
CREG 1
CONTROL q[2]
H q[0]
ENDCONTROL
CONTROL q[2]
CNOT q[0],q[1]
ENDCONTROL

Multiple Control Qubits ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 3
CONTROL q[1],q[2]
H q[0]
ENDCONTROL"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 3
CREG 1
CONTROL q[1],q[2]
H q[0]
ENDCONTROL

Never mix the block form with the traditional terminator

In pyqpanda3 0.4.1, DAGGER { ... } followed by ENDDAGGER (or CONTROL { ... } followed by ENDCONTROL) makes the parser loop forever (unbounded hang). Use either DAGGER { ... } or DAGGER ... ENDDAGGER — never both.


7. QIF / QWHILE Classical Control Flow and MEASURE / RESET ​

The current .g4 syntax writes conditions as parenthesized expressions plus blocks:

text
qif_statement : QIF_KEY LPAREN expression RPAREN LBRACE statement* RBRACE qelse_statement_fragment? ;
qwhile_statement : QWHILE_KEY LPAREN expression RPAREN LBRACE statement* RBRACE ;

0.4.1 implements the traditional form: QIF c[i],c[j] ... QENDIF and QWHILE c[i] ... QENDWHILE, with the condition being a comma-separated list of classical bits (no expressions — see sections 5 and 9).

QIF / QELSE / QENDIF ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 2
CREG 2
X q[0]
MEASURE q[0],c[0]
QIF c[0]
Y q[1]
QELSE
Z q[1]
QENDIF
MEASURE q[1],c[1]"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 2
CREG 2
X q[0]
MEASURE q[0],c[0]
QIF c[0]
Y q[1]
QELSE
Z q[1]
QENDIF
MEASURE q[1],c[1]

Multi-Branch Conditions: QELSEIF ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 4
CREG 4
X q[0]
QIF c[0]
Y q[1]
QELSEIF c[1]
Z q[2]
QELSE
I q[3]
QENDIF"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 4
CREG 1
X q[0]
QIF c[0]
Y q[1]
QELSEIF c[1]
Z q[2]
QELSE
I q[3]
QENDIF

Multiple Condition Bits: QIF c[0],c[1] ​

The condition may list several classical bits (the official tutorial's spelling). Per the official tutorial's model, the branch is taken when all of them are set:

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 3
CREG 3
QIF c[0],c[1]
X q[2]
QENDIF"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 3
CREG 1
QIF c[0],c[1]
X q[2]
QENDIF

QWHILE / QENDWHILE ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 2
CREG 2
X q[1]
QWHILE c[1]
X q[0]
X q[1]
MEASURE q[1],c[1]
QENDWHILE
MEASURE q[0],c[0]"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 2
CREG 2
X q[1]
QWHILE c[1]
X q[0]
X q[1]
MEASURE q[1],c[1]
QENDWHILE
MEASURE q[0],c[0]

The condition bit of QWHILE must already be set by a MEASURE before the loop starts, and the body must contain a measurement that flips it, or the loop cannot terminate (the official tutorial's spelling first does X q[1], then MEASURE q[1],c[1] inside the body).

0.4.1 Execution-Semantics Note ​

In pyqpanda3 0.4.1 (CPUQVM), QIF/QWHILE parsed via convert_originir_string_to_qprog do not fire their branches when executed — parsing and round-tripping work, but the branch body is not executed. The example below sets q[0] and measures it (c[0]=1), then applies X q[0] again under QIF c[0], and measures once more:

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

src = """QINIT 1
CREG 1
X q[0]
MEASURE q[0],c[0]
QIF c[0]
X q[0]
QENDIF
MEASURE q[0],c[0]"""

machine = CPUQVM()
machine.run(prog=convert_originir_string_to_qprog(src), shots=16)
print(machine.result().get_counts())

Output (if the branch fired, the second measurement would read '0'; it reads '1', so the branch did not execute):

text
{'1': 16}

An equivalent program built with the core API (the qif structure) executes the branch normally and measures {'0': 16}. To execute dynamic circuits in 0.4.1, use the core API's qif/qelse/qwhile (see the dynamic circuits tutorial). In get_counts() the bit string maps left to right to c[n-1] … c[0]; verified on 0.4.1, this holds both for programs parsed by OriginIR and for programs built with the core API's measure/qif (e.g. X q[1] followed by measure([0,1],[0,1]) returns '10').

The MEASURE Operation ​

MEASURE measures a qubit and writes the result into a classical register (the measure_statement rule: MEASURE_KEY q_KEY_declaration COMMA c_KEY_declaration):

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 1
CREG 1
H q[0]
MEASURE q[0],c[0]"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 1
CREG 1
H q[0]
MEASURE q[0],c[0]

When the qubit count equals the register count, a bare q,c measures everything at once (MEASURE_KEY Q_KEY COMMA C_KEY, expanded to per-qubit measurements; see the section-3 example); the error when the counts differ is in section 3.

The RESET Operation (Parsed, But Not Convertible in 0.4.1) ​

The .g4 has the reset_statement rule (RESET_KEY q_KEY_declaration | RESET_KEY Q_KEY), which 0.4.1 can parse but cannot convert to a QProg yet; it prints a warning (verbatim, including the source's Warrnig spelling) and fails:

text
QINIT 2
RESET q[1]

Output:

text
Compiler | BuildQProg.h | QProgNodeCollector | add_reset_literal | Warrnig: The Reset operation will be ok in future.Now, it doesn't work.Exit.
text
Caught an unknown exception!

8. Custom Gate Declarations and Scope ​

The current .g4 form is QGATE name(formal_params) { body } (the gate_func_statement rule); 0.4.1 implements the traditional form:

text
QGATE name qubit_formal_params,(angle_formal_params)
body (calls to built-in gates or previously defined custom gates)
ENDQGATE

Body statements are likewise separated by line breaks, without semicolons. A custom gate is invoked like a built-in: name q[0],q[1] or name q[0],(angle_arguments); arguments are evaluated at the call site and substituted into the body.

A Basic Custom Gate ​

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 2
QGATE new_H a,b
Y a
Z b
ENDQGATE
new_H q[0],q[1]"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output (custom-gate calls are inlined in the round trip):

text
QINIT 2
CREG 1
Y q[0]
Z q[1]

A Custom Gate With Angle Parameters ​

Angle formals are written in parentheses; body angle expressions may reference them (e.g. PI/2+b), and the actual arguments are substituted and evaluated at the call site:

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 2
QGATE new_RX a,(b)
RX a,(PI/2+b)
X a
ENDQGATE
new_RX q[1],(PI/4)"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output (PI/2+b = PI/2 + PI/4 = 2.35619449 after substitution):

text
QINIT 2
CREG 1
RX q[1],(2.35619449)
X q[1]

Nesting Custom-Gate Calls ​

A body may call custom gates defined earlier:

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 2
QGATE inner a
H a
ENDQGATE
QGATE outer a,b
inner a
X b
ENDQGATE
outer q[0],q[1]"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 2
CREG 1
H q[0]
X q[1]

Invoking a Custom Gate With a Bare q ​

When the number of qubit formals equals the total qubit count, the call site may use a bare q:

python
from pyqpanda3.intermediate_compiler import convert_originir_string_to_qprog

src = """QINIT 2
QGATE new_2 a,b
H a
X b
ENDQGATE
new_2 q"""

prog = convert_originir_string_to_qprog(src)
print(prog.originir())

Output:

text
QINIT 2
CREG 1
H q[0]
X q[1]

Argument-Count Mismatch ​

Calling with a different number of qubit arguments than formals fails:

text
QINIT 2
QGATE new_H a
H a
ENDQGATE
new_H q[0],q[1]

Output:

text
IRQProgNodeCollectorParser | parse_custom_gate_call_statement | Qubit parameter count of new_H (i.e. Custom gate) mismatch.

Scope and Naming Constraints ​

  • Formal parameter names are only valid inside the body (angle formals may appear in body angle expressions); statements outside the body cannot reference body formals.
  • Formal parameter names must not collide with keywords (per the comment in the .g4: the parameter names of custom gates must not conflict with the related keywords).
  • When converting custom gates, 0.4.1 prints debug information to stderr (Custom Gate: name, Qbit Params: ..., Gate Operations: ...); it does not affect the conversion result.

9. Common Errors (Lexical and Syntactic) ​

This section cross-references the ".g4 authoritative grammar" against "pyqpanda3 0.4.1 observed behavior". The exceptions raised by convert_originir_string_to_qprog on invalid text are mostly runtime messages (the generic Unknown exception is 0.4.1's uniform translation of several parse failures; more specific parse errors are printed to stderr). All references use the .g4 lexer/parser files introduced in section 1 as the authoritative baseline.

#Construct.g4 (authoritative)pyqpanda3 0.4.1 (observed)
1ORIGINIR 1.0; version headerprogram: version? statement* supports itError It's Identifier (ORIGINIR) — omit the version header
2include "file"includestatement, supportedError It's EOF (file includes not implemented in 0.4.1)
3Trailing semicolon ;every statement requires SEMICOLONError It's ; — statements are separated by line breaks
4QIF (expr) { ... }current qif_statement formstderr: Expected 'c', got '(', exception Unknown exception
5QWHILE (expr) { ... }current qwhile_statement formexception Unknown exception
6QGATE name(params) { ... }current gate_func_statement formstderr: Expected 'Identifier', got '(', exception Unknown exception
7Classical assignment c[0]=c[0]+1assign_statement, supportedexception Unknown exception (classical expressions unsupported)
8Comparison conditions QWHILE c[0]<5 / QIF c[0]==1expression supports comparisonsError It's < / It's == — the condition can only be a c[i] list
9Index expression q[c[0]]index: LBRACK Integer_Literal RBRACK, integer constants onlystderr: Expected expression, got c, exception Unknown exception
10NOT q[0]the lexer has a NOT_GATE token, but no _type rule references itError It's NOT
11ISWAPTHETA q[0],q[1],(0.5)double_gate_with_one_parameter_type includes ISWAPTHETAParses, but conversion fails: ISWAPTHETA is not supported by QGate. exit.
12RESET q[1]reset_statement, supportedParses, but conversion warns The Reset operation will be ok in future.Now, it doesn't work.Exit. then fails
13CP / RZX / RXX / RYY / RZZthe lexer only registers CP_GATE (likewise unreferenced by any _type rule); no tokens for RZX etc.All parse and execute in 0.4.1; CP is re-serialized as CR in the round trip
14ECHO q[0],(0.5)single_gate_with_one_parameter_type includes ECHOSupported
15Traditional terminators QENDIF/QENDWHILE/ENDQGATE/ENDDAGGER/ENDCONTROLthe lexer has the tokens, but the parser rules do not reference them (qif_statement only has ELSE)Part of the 0.4.1 traditional dialect, fully supported
16DAGGER { ... } followed by ENDDAGGER (mixed forms)—Parser hangs indefinitely (observed infinite loop) — never mix
17Executing QIF c[0] branches on CPUQVM—Branches parsed from OriginIR do not fire (observed); the equivalent program built with the core qif API works

Typical error texts (verbatim):

text
Error:
	<Expected> OriginIR Statement
	<Problem> The string is invalid originir string.It's Identifier (ORIGINIR)
	<Result> Exit.
text
Error:
	<Expected> OriginIR Statement
	<Problem> The string is invalid originir string.It's ;
	<Result> Exit.
text
Error at line 3, column 5: Expected 'c', got '('.
Expected(int) '132', got(int) '58'.
text
Error at line 2, column 10: Expected 'Identifier', got '('.
Expected(int) '113', got(int) '58'.
text
Error at line 3, column 5: Expected expression, got c.
text
current identifier:theta
Error: <Pos>:Statement.hpp >IdentifierExpression > getFP
text
BuildQProg | QProgNodeCollector | add_qgate |ISWAPTHETA is not supported by QGate. exit.
text
Compiler | BuildQProg.h | QProgNodeCollector | add_reset_literal | Warrnig: The Reset operation will be ok in future.Now, it doesn't work.Exit.
text
IRQProgNodeCollectorParser | parse_custom_gate_call_statement | Qubit parameter count of new_H (i.e. Custom gate) mismatch.

Released under the MIT License.