Skip to content

Fix gate broadcasting over multiple registers (#384) - #403

Open
TheGupta2012 wants to merge 1 commit into
mainfrom
bugfix-384-gate-broadcasting
Open

Fix gate broadcasting over multiple registers (#384)#403
TheGupta2012 wants to merge 1 commit into
mainfrom
bugfix-384-gate-broadcasting

Conversation

@TheGupta2012

@TheGupta2012 TheGupta2012 commented Aug 24, 2026

Copy link
Copy Markdown
Member

Fixes #384

Problem

Broadcasting is element-wise: every register operand must have the same length n, the gate is applied once per index pairing operands positionally, and single-qubit operands are repeated.

pyqasm flattened every operand into one list and sliced it into consecutive chunks, so cx q, r on two qubit[4] registers emitted the wrong circuit with no error:

cx q[0], q[1];   cx q[2], q[3];   cx r[0], r[1];   cx r[2], r[3];   // was
cx q[0], r[0];   cx q[1], r[1];   cx q[2], r[2];   cx q[3], r[3];   // spec

cx q, r[0] and the spec's g4 qr0[0], qr1, qr2[0], qr3 were rejected outright.

The boundary was lost earlier than the issue suggested: _visit_generic_gate_operation overwrote operation.qubits with the flat list from _get_op_bits before _unroll_multiple_target_qubits ever ran.

Fix

New _get_op_bits_per_operand returns list[list[...]], one inner list per source-level operand; _get_op_bits becomes a wrapper over it. The grouped shape threads through the basic / external / custom gate visits as operand_groups. ctrl @ peeling keeps its flat-index semantics but splits the affected group, so a ctrl boundary landing mid-register still works.

Dispatch is ordered so one input matches at most one rule: exact-fit single application, then spec broadcast, then the legacy all-single-qubit chunking, else a ValidationError.

Behaviour change

Deliberate. cx q[0], q[1], q[2], q[3] (all single-qubit operands) still chunks as a pyqasm extension. Mismatched register lengths now raise naming both operands, and an ambiguous shape such as cx q, r, s (three registers, arity 2) is rejected rather than silently chunked.

Two rejection tests in tests/qasm3/resources/gates.py had their message regex updated — the same shapes are still rejected, with a clearer message. No test was removed.

Tests

New tests/qasm3/test_gate_broadcasting.py: two-register cx, mixed register/single-qubit, the spec g4 example, the size mismatch, ctrl @, and the ambiguous multi-register case.

808 passed, 3 skipped. pylint 10.00/10, black + isort clean.

🤖 Generated with Claude Code

@argus-eye

argus-eye Bot commented Aug 24, 2026

Copy link
Copy Markdown

Argus review

Auto-review is off for this repo. Tick the box below to run a review on this PR.

  • Trigger Argus review

Estimated cost

  • Files changed: 4
  • Diff lines (±): 472
  • Historical avg: ~243.6k tokens · ~$0.95 · across last 10 review(s)

Tip: you can also comment @argus-eye review at any time.

@coderabbitai

coderabbitai Bot commented Aug 24, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: ee46a582-dd7b-42d4-a257-3909639428d7

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@codecov-commenter

codecov-commenter commented Aug 24, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 94.25287% with 5 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
src/pyqasm/visitor.py 95.23% 3 Missing ⚠️
src/pyqasm/analyzer.py 77.77% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@TheGupta2012
TheGupta2012 force-pushed the bugfix-384-gate-broadcasting branch 3 times, most recently from db4a6a1 to 902ad15 Compare August 24, 2026 12:10
`cx q, r` with two same-size registers used to emit the linear chunk
`cx q[0],q[1]; cx q[2],q[3]; cx r[0],r[1]; cx r[2],r[3]` because
`_get_op_bits` flattened operand boundaries in `_visit_generic_gate_operation`
before `_unroll_multiple_target_qubits` sliced the resolved qubits by gate
arity. It now zips element-wise per the OpenQASM 3 spec, emitting
`cx q[i], r[i]`; single-qubit operands (e.g. `cx q, r[0]`) repeat unchanged.

Implementation:
- Add `_get_op_bits_per_operand` returning per-operand qubit groups;
  keep `_get_op_bits` as a flattening wrapper for existing callers.
- Preserve operand boundaries end-to-end from `_visit_generic_gate_operation`
  through the basic/external/custom gate visits via a new
  `operand_groups` parameter.
- Peel `ctrl @` qubits by flat index while splitting the affected group,
  so `ctrl @ x q2` (qubit[2]) still works and `ctrl @ cx cq, a, b` (a, b
  registers) broadcasts.
- Add `_broadcast_operand_groups` implementing spec element-wise
  broadcasting with a clear register-mismatch error naming both operands.
- Extend `_visit_custom_gate_operation` to iterate once per application.
- Dispatch rules in `_unroll_multiple_target_qubits`: flat_count == arity
  → one application; group_count == arity → spec broadcast; all-single
  operands with matching total → pyqasm legacy chunking; else raise.

Ambiguous shapes such as `cx q, r, s` (three registers, arity 2) now
raise instead of silently linear-chunking to the wrong circuit.

Two existing tests encoded the old "Invalid number of qubits" message
for shapes that now hit the richer new dispatch error; regexes updated.

Fixes #384

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
@TheGupta2012
TheGupta2012 force-pushed the bugfix-384-gate-broadcasting branch from 902ad15 to 033e173 Compare August 24, 2026 12:40
@TheGupta2012
TheGupta2012 requested a review from ryanhill1 August 24, 2026 12:44
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Gate broadcasting over multiple registers unrolls to the wrong circuit

2 participants