Repository navigation
Fix bit order of unrolled classical register conditions - #444
Conversation
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 |
|
Navigate logical layers of code changes, visualize relationships, and explore their blast radius. 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:
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Repository: qBraid/pyqasm/.coderabbit.yaml Review profile: CHILL Plan: Advanced Run ID: 📒 Files selected for processing (5)
Included review availability: This review used your included allowance. Your plan provides up to 1 included review per hour; 0 remain after this review. 📝 WalkthroughWalkthroughClassical-register condition unrolling now tests bits from most significant to least significant. Single-bit comparisons and negation receive explicit handling. Tests cover equality, relational comparisons, out-of-range values, and rebasing output. ChangesClassical Register Conditions
Priority: ➖ Normal Estimated code review effort: 3 (Moderate) | ~20 minutes Change: Bug fix · Severity of issue fixed: Low Sequence Diagram(s)sequenceDiagram
participant QasmVisitor
participant Qasm3Transformer
participant BranchingStatement
QasmVisitor->>Qasm3Transformer: Pass register, operator, value, and branch blocks
Qasm3Transformer->>BranchingStatement: Build nested MSB-first bit branches
Qasm3Transformer-->>QasmVisitor: Return generated branch statements
Merge Risk: ⚪ Minimal · up to This change corrects the bit order used when 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
Full details: Docstring CoverageExplanation Docstring coverage is 75.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 12 functions across 4 files. (1 skipped: 1 unsupported.) ✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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✅ All modified and coverable lines are covered by tests. 📢 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 — Bit inequality raises KeyError
Check 1 warning before you merge. Argus also found 1 suggestion in 5 files.
- 🟡 Warning · Bit inequality raises KeyError ·
src/pyqasm/visitor.py:L97 - 💡 Suggestion · Constant comparisons lack else-branch coverage ·
tests/qasm3/test_if.py:L300
What this PR does · 5 author checks
- Fix unrolled classical-register conditions so comparisons use the correct bit order and select the correct branch.
Author's checks
- Treat c[0] as the least significant bit and test register bits from most significant to least significant.
- Make equality and relational comparisons choose the same branch as integer comparison, including when the comparison value is outside the register's range.
- Fold comparisons that always hold to the plain body and comparisons that never hold to the else branch.
- Route single-bit conditions through the shared comparison logic so operators and values select the correct branches.
- Pass the exhaustive checks across operators, register widths 1–3, comparison values from −1 through 2ʷ, and every register value.
⚠️ Argus could not finish 5 file checks, so this review is partial. More findings can exist.
4 earlier findings may still apply to files this PR changes
src/pyqasm/visitor.py· Branch-condition normalization is not written back into the AST · from qBraid/pyqasm#404src/pyqasm/visitor.py· Whole-register assignment discards the bit register's width metadata · from qBraid/pyqasm#404src/pyqasm/visitor.py· Mixed-program detection checks total declared capacity instead of declaration presence · from qBraid/pyqasm#362src/pyqasm/visitor.py· Custom gates double-count branch operations after expansion · from qBraid/pyqasm#345
usage: 744.1k tokens · 16 stages
Contract: production/full · checked: bug_hunter, security, architecture, regression · review took 2m30s
| Stage | Model | Tokens | Cost |
|---|---|---|---|
| Intent | openai/gpt-5.6-terra |
4.7k | $0.0056 |
| Triage | openai/gpt-6-luna |
9.4k | $0.0007 |
| Lead agent | openai/gpt-5.6-terra |
1.8k | $0.0105 |
| Review specialists (11) | openai/gpt-6-sol |
404.6k | $0.9225 |
| Simulation | openai/gpt-5.6-terra |
20.7k | $0.0813 |
| Scoring | openai/gpt-5.6-terra |
3.0k | $0.0079 |
Dashboard → · React 👎 on an inline comment to dismiss it · Reply to an inline comment or use @argus-eye help
🧠 Learned: 2 patterns · 2 finding memories · 2 file memories · 1 PR summary · …
Argus flagged that `!=` would raise KeyError in the single-bit path. It could not: get_branch_params rejected `!=` first. Support it end to end instead, as `==` with the branches swapped. The QASM 2 printer from #338 assumed the old MSB-first chain, so it printed `if (m == 2)` for `if (m == 1)`. Read c[0] as the LSB there too. The exhaustive comparison test now has an else branch, so it also checks which branch runs when a comparison folds. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
TheGupta2012
left a comment
There was a problem hiding this comment.
@ryanhill1 I have merged #337 and updated the tests as well, lgtm overall
Summary of changes
When
unroll()expands a whole-register condition into per-bit branches, it uses the wrong bit order. It readsc[0]as the most significant bit, but in OpenQASMc[0]is the least significant. Soif (c == 2)runs its body whenc == 1, and nothing reports an error.mainif (c[0] == true) { if (c[1] == false) { ... } }(this tests forc == 1)if (c[1] == true) { if (c[0] == false) { ... } }I simulated both versions with Qiskit Aer, preparing
c = 2. The original program flips the flag qubit, butmain's unrolled output does not.Same bug elsewhere in the conversion
>=,<=,>,<skipped bits that looked irrelevant, which was only correct for some values. For example,c >= 1reduced to a check on one bit.c == 4onbit[2]raisedValidationError) or nonsense likebin(-1). Now a comparison that always holds unrolls to the plain body, and one that never holds unrolls to theelsebranch.c[0] < 1becamec[0] == true, andc[0] == 2becamec[0] == true.Fix
The bits-to-condition logic now lives in one helper,
Qasm3Transformer.unroll_register_comparison. It treatsc[0]as the LSB and tests bits from MSB to LSB. At the first bit that differs from the value, it jumps toif_blockorelse_block. Comparisons that are constant over the register's range are folded away. Single-bit conditions go through the same helper after they are reduced to the bit values that satisfy them.Tests
c == Nat widths 1, 2, 3 and 5, plus the minimalc == 2case as text.c[1] < 1,c[1] == 2,c[1] >= 0,!c[1].test_multi_bit_ifandtest_rebase_if, whose expected output encoded the bug.Interaction with #338
#338 rebuilds QASM 2
if(c==N)from the per-bit chain and works around this bug by reversing the bit order. Once this PR lands, that reversal produces wrong values. Whichever of the two PRs merges second must include this one-line change insrc/pyqasm/modules/qasm2.py,_flatten_branch:I checked this by applying #338 on top of this branch. Without the flip, 3 of #338's own tests fail (
test_multibit_branch_survives_unrolling[1|2]andtest_branch_on_register_level_measurement_round_trips). With the flip, the full suite passes, andif(c==N)round-trips for N = 0–3.Summary by CodeRabbit
Also in this PR:
Auto-enriched by Argus