Repository navigation
Conversation
…ting a gate cx a, b over two registers unrolled to cx a[0], a[1]; cx b[0], b[1] because every operand was flattened into one list and chunked by the gate's arity. When a call has one operand per gate qubit, register operands now pair by index and single qubits repeat, as the spec defines. Slices, aliases, subroutine register arguments and ctrl @ take the same path. Calls with fewer operands than gate qubits, such as cx q2, keep filling operands from the register as before. Closes #384
Argus reviewAuto-review is off for this repo. Tick the box below to run a review on this PR. Running Argus review... Estimated cost
Tip: you can also comment |
|
Important Review skippedAuto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the ⚙️ Run configuration
You can disable this status message by setting the Use the checkbox below for a quick retry:
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Codecov Report❌ Patch coverage is
📢 Thoughts on this report? Let us know! |
This comment has been minimized.
This comment has been minimized.
There was a problem hiding this comment.
Argus · 9/10 — Empty broadcast operands raise IndexError
Check 1 warning before you merge. Argus reviewed 3 files.
- 🟡 Warning · Empty broadcast operands raise IndexError ·
src/pyqasm/visitor.py:L1654
What this PR does · 9 author checks
- Fix gate broadcasting so register operands pair element by element and single-qubit operands repeat, rather than silently unrolling to a different circuit.
- Excludes: Rejecting the existing register-fills-several-operands extension or the four-single-qubit cx calling form.
- Excludes: Changing existing behavior for opaque gates or non-constant modifier arguments.
Author's checks
- _visit_generic_gate_operation broadcasts first through _broadcast_operands, resolving operands separately and preserving operand boundaries.
- Inside subroutines, operand resolution preserves formal names so existing argument mapping still applies.
- Calls with one operand per gate qubit, including controls, pair register operands by index and repeat single-qubit operands.
- Registers of different lengths raise ValidationError; for cx with lengths 2 and 3, report Cannot broadcast 'cx' over registers of different lengths [2, 3].
- Each broadcast application uses the normal path, retaining support for modifiers, custom gates, external gates and subroutines.
- Calls with fewer operands than gate qubits and the cx q[0], q[1], q[2], q[3] form retain existing behavior.
- Opaque gates and non-constant modifier arguments retain existing behavior.
- tests/qasm3/test_broadcast.py covers registers, slices, aliases, subroutine arguments, swap, ctrl @, mixed operands, g4 and length mismatch.
- The spec's mixed-operand g4 example emits three applications, pairing qr1 and qr3 by index while repeating qr0[0] and qr2[0].
usage: 282.3k tokens · 17 stages
Contract: production/full · checked: bug_hunter, security, architecture, regression, single-pass review, ponytail-review · review took 2m44s
| Stage | Model | Tokens | Cost |
|---|---|---|---|
| Intent | openai/gpt-6.1-sol |
6.5k | $0.0135 |
| Triage | openai/gpt-6.1-sol |
6.4k | $0.0070 |
| Lead agent | openai/gpt-6.1-sol |
1.3k | $0.0067 |
| Review specialists (10) | openai/gpt-6.1-sol |
237.3k | $0.5146 |
| Review | openai/gpt-6.1-sol |
14.2k | $0.0295 |
| Acceptance | openai/gpt-6.1-sol |
984 | $0.0026 |
| Simulation | openai/gpt-6.1-sol |
12.6k | $0.0363 |
| Scoring | openai/gpt-6.1-sol |
3.0k | $0.0038 |
Dashboard → · React 👎 on an inline comment to dismiss it · Reply to an inline comment or use @argus-eye help
🧠 Learned: 1 pattern · 1 PR summary · 1 finding memory · 1 file memory · …
Cross-Repo PR Coverage
No conflict found with qBraid/qBraid#1468.
Updated at 10:26 UTC on 2026-10-24
| span=operation.span, | ||
| ) | ||
| size = lengths.pop() | ||
| return [[bits[i] if len(bits) > 1 else bits[0] for bits in resolved] for i in range(size)] |
There was a problem hiding this comment.
🟡 Warning · Bug: Empty broadcast operands raise IndexError
Invalid empty operands bypass QASM error handling and crash unroll with an uncaught IndexError.
Same issue at src/pyqasm/visitor.py:L1643.
| return [[bits[i] if len(bits) > 1 else bits[0] for bits in resolved] for i in range(size)] | |
| if any(not bits for bits in resolved): | |
| raise_qasm3_error( | |
| f"Cannot broadcast '{operation.name.name}' over an empty operand", | |
| error_node=operation, | |
| span=operation.span, | |
| ) | |
| return [[bits[i] if len(bits) > 1 else bits[0] for bits in resolved] for i in range(size)] |
Argus did not compile or run this fix · React 👎 to dismiss
Closes #384
Problem
unroll()flattened every operand of a gate call into one list and chunked it by the gate's arity. With register operands this silently produced a different circuit:cx a, b;(twoqubit[2])cx a[0], a[1]; cx b[0], b[1];cx a[0], b[0]; cx a[1], b[1];cx q[0:1], q[2:3];cx q[0], q[1]; cx q[2], q[3];cx q[0], q[2]; cx q[1], q[3];let a = q[0:1]; let b = q[2:3]; cx a, b;def f(qubit[2] a, qubit[2] b) { cx a, b; }f(r, s);cx r[0], r[1]; cx s[0], s[1];cx r[0], s[0]; cx r[1], s[1];ctrl @ x a, b;cx a[0], a[1]; cx a[0], b[0]; cx a[0], b[1];cx a[0], b[0]; cx a[1], b[1];cx q, r[0];(qubit[3] q)ValidationErrorcx q[0], r[0]; cx q[1], r[0]; cx q[2], r[0];g4 qr0[0], qr1, qr2[0], qr3;ValidationErrorg4calls, one per index ofqr1/qr3Change
_visit_generic_gate_operationnow broadcasts first, in a new_broadcast_operands:ValidationError: Cannot broadcast 'cx' over registers of different lengths [2, 3].cx q2;,inv @ ccx q[0], q2;,ctrl (2) @ x a, b[0];, which existing tests rely on), thecx q[0], q[1], q[2], q[3]form, opaque gates, and non-constant modifier arguments. The issue left it to the maintainers whether to keep that register-fills-several-operands extension, so this PR leaves it alone.Testing
tests/qasm3/test_broadcast.py: two registers, slices, aliases, subroutine register arguments,swap,ctrl @, the mixed register/qubit form, the spec'sg4example, and the length mismatch. 9 of its 10 tests fail onmain.black,isort,pylint(10.00),headerspass.mypyreports only thetyper/richimport errors that come from the tox environment lacking the CLI extra.negctrl @, a parametrised custom two-qubit gate on registers, andcxkept withunroll(external_gates=["cx"])all pair by index.Found while testing qBraid/qBraid#1468, whose
qasm3 -> qasm2conversion currently detects register broadcasts and falls back to another path to avoid this bug.