Description
rzz and rxx decompose to a body containing a QuantumPhase node, which Qasm2Module emits as gphase(...). OpenQASM 2 has no global-phase syntax at all, so the unroller turns a legal qelib1 gate into output no QASM 2 parser accepts.
Reproduction
from pyqasm import dumps, loads
src = """OPENQASM 2.0;
include "qelib1.inc";
qreg q[2];
creg m[1];
measure q[0] -> m[0];
if(m==1) rzz(0.3) q[0],q[1];
"""
m = loads(src)
m.unroll()
print(dumps(m))
if (m[0] == true) {
gphase(-0.15) q[0], q[1];
cx q[0], q[1];
...
}
The same applies outside a conditional — rzz(0.3) q[0],q[1]; at top level unrolls to a gphase statement.
QuantumPhase is also absent from Qasm2Module._whitelist_statements, so a second filtering pass over an already-unrolled AST rejects it by AST class name. That is reachable today: remove_idle_qubits() and reverse_qubit_order() both reassign _statements = _unrolled_ast.statements, and unroll() has no re-entry guard, so reverse_qubit_order() followed by remove_idle_qubits() raises where it passes on main.
Sweeping ~45 qelib1/pyqasm gate shapes: every gate but rzz/rxx produces only QuantumGate nodes.
Suggested fix
Either the unroller should not emit gphase for a Qasm2Module (global phase is unobservable, so dropping it is semantically safe for a QASM 2 target), or _qasm_ast_to_str should fold/drop the node at serialization. Whichever is chosen, add QuantumPhase handling to _whitelist_statements so the re-filtering path stops depending on it being absent.
#339 adds an interim QuantumPhase-specific diagnostic in _filter_branch_body so the error names global phase and points at rzz/rxx, rather than reporting an AST class for a program the user wrote as rzz. That is a message fix only — the underlying gap is this issue.
Found in review of #339 (M1).
Description
rzzandrxxdecompose to a body containing aQuantumPhasenode, whichQasm2Moduleemits asgphase(...). OpenQASM 2 has no global-phase syntax at all, so the unroller turns a legal qelib1 gate into output no QASM 2 parser accepts.Reproduction
The same applies outside a conditional —
rzz(0.3) q[0],q[1];at top level unrolls to agphasestatement.QuantumPhaseis also absent fromQasm2Module._whitelist_statements, so a second filtering pass over an already-unrolled AST rejects it by AST class name. That is reachable today:remove_idle_qubits()andreverse_qubit_order()both reassign_statements = _unrolled_ast.statements, andunroll()has no re-entry guard, soreverse_qubit_order()followed byremove_idle_qubits()raises where it passes onmain.Sweeping ~45 qelib1/pyqasm gate shapes: every gate but
rzz/rxxproduces onlyQuantumGatenodes.Suggested fix
Either the unroller should not emit
gphasefor aQasm2Module(global phase is unobservable, so dropping it is semantically safe for a QASM 2 target), or_qasm_ast_to_strshould fold/drop the node at serialization. Whichever is chosen, addQuantumPhasehandling to_whitelist_statementsso the re-filtering path stops depending on it being absent.#339 adds an interim
QuantumPhase-specific diagnostic in_filter_branch_bodyso the error names global phase and points atrzz/rxx, rather than reporting an AST class for a program the user wrote asrzz. That is a message fix only — the underlying gap is this issue.Found in review of #339 (M1).