Limitation
Broadcasting defines register arguments as an
element-wise shorthand: every register operand must have the same length, and the gate is
applied once per index, pairing operands positionally. Single-qubit operands are repeated.
The spec's own example:
qubit[1] qr0;
qubit[3] qr1;
qubit[2] qr2;
qubit[3] qr3;
g4 qr0[0], qr1, qr2[0], qr3; // ok
is stated to be equivalent to
g4 qr0[0], qr1[0], qr2[0], qr3[0];
g4 qr0[0], qr1[1], qr2[0], qr3[1];
g4 qr0[0], qr1[2], qr2[0], qr3[2];
pyqasm instead flattens every operand into one list and slices it into consecutive chunks
of the gate's arity. For a two-register call this produces a different circuit with no
error raised, and for the mixed register/qubit form it raises a spurious validation error.
Example QASM failure
Wrong output, silently:
OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
qubit[4] r;
cx q, r;
>>> import pyqasm
>>> m = pyqasm.loads(qasm); m.unroll(); print(pyqasm.dumps(m))
OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
qubit[4] r;
cx q[0], q[1];
cx q[2], q[3];
cx r[0], r[1];
cx r[2], r[3];
Expected per spec:
cx q[0], r[0];
cx q[1], r[1];
cx q[2], r[2];
cx q[3], r[3];
Spurious rejection of the mixed form:
OPENQASM 3.0;
include "stdgates.inc";
qubit[4] q;
qubit[1] r;
cx q, r[0];
// ValidationError: Invalid number of qubits 5 for operation cx
OPENQASM 3.0;
gate g4 a, b, c, d { }
qubit[1] qr0; qubit[3] qr1; qubit[2] qr2; qubit[3] qr3;
g4 qr0[0], qr1, qr2[0], qr3;
// ValidationError: Qubit count mismatch for gate 'g4'. Expected 4 qubits, but got 8 instead.
Change Requested
Replace the flatten-and-chunk expansion with spec broadcasting:
- Resolve each operand to a list of qubits, keeping operand boundaries.
- Let
n be the length of the register operands. All register operands must share the same
n; otherwise raise a ValidationError naming the two mismatched operands.
- Emit
n gate applications; at iteration i take element i from each register operand
and repeat single-qubit operands unchanged.
- If there are no register operands, emit exactly one application.
Implementation Details
-
The defect is in Qasm3Visitor._unroll_multiple_target_qubits
(src/pyqasm/visitor.py:1039). It calls self._get_op_bits(operation, qubits=True), which
already discards operand boundaries, then chunks linearly:
for i in range(0, len(op_qubits), gate_qubit_count):
qubit_subsets.append(op_qubits[i : i + gate_qubit_count])
-
_get_op_bits needs a variant that returns list[list[...]] (one inner list per operand)
so the boundary information survives. _broadcast_gate_operation (visitor.py:1067)
consumes the result and needs no change.
-
This is a deliberate behaviour change, not a pure bug fix. The current docstring
documents flatten-and-chunk as intended: 'cx q[0], q[1], q[2], q[3]' →
[[q[0], q[1]], [q[2], q[3]]]. That calling form — four single-qubit operands for a
two-qubit gate — is not valid OpenQASM 3 broadcasting in the first place. Maintainers
should decide whether to keep it as a documented pyqasm extension or reject it; either way
the register case above must change.
-
The same expansion path serves the modifier route at visitor.py:1241 and visitor.py:1463,
so ctrl @ / inv @ broadcasts are affected identically and need coverage.
-
Tests: add tests/qasm3/test_gate.py cases for two-register cx, mixed register/qubit,
the spec g4 example, the size-mismatch error, and a broadcast under ctrl @.
Limitation
Broadcasting defines register arguments as an
element-wise shorthand: every register operand must have the same length, and the gate is
applied once per index, pairing operands positionally. Single-qubit operands are repeated.
The spec's own example:
is stated to be equivalent to
pyqasminstead flattens every operand into one list and slices it into consecutive chunksof the gate's arity. For a two-register call this produces a different circuit with no
error raised, and for the mixed register/qubit form it raises a spurious validation error.
Example QASM failure
Wrong output, silently:
Expected per spec:
Spurious rejection of the mixed form:
Change Requested
Replace the flatten-and-chunk expansion with spec broadcasting:
nbe the length of the register operands. All register operands must share the samen; otherwise raise aValidationErrornaming the two mismatched operands.ngate applications; at iterationitake elementifrom each register operandand repeat single-qubit operands unchanged.
Implementation Details
The defect is in
Qasm3Visitor._unroll_multiple_target_qubits(
src/pyqasm/visitor.py:1039). It callsself._get_op_bits(operation, qubits=True), whichalready discards operand boundaries, then chunks linearly:
_get_op_bitsneeds a variant that returnslist[list[...]](one inner list per operand)so the boundary information survives.
_broadcast_gate_operation(visitor.py:1067)consumes the result and needs no change.
This is a deliberate behaviour change, not a pure bug fix. The current docstring
documents flatten-and-chunk as intended:
'cx q[0], q[1], q[2], q[3]'→[[q[0], q[1]], [q[2], q[3]]]. That calling form — four single-qubit operands for atwo-qubit gate — is not valid OpenQASM 3 broadcasting in the first place. Maintainers
should decide whether to keep it as a documented pyqasm extension or reject it; either way
the register case above must change.
The same expansion path serves the modifier route at
visitor.py:1241andvisitor.py:1463,so
ctrl @/inv @broadcasts are affected identically and need coverage.Tests: add
tests/qasm3/test_gate.pycases for two-registercx, mixed register/qubit,the spec
g4example, the size-mismatch error, and a broadcast underctrl @.