Skip to content

fix: drop unroll-emitted global phase for QASM 2 targets - #358

Open
ryanhill1 wants to merge 3 commits into
mainfrom
fix-qasm2-gphase
Open

fix: drop unroll-emitted global phase for QASM 2 targets#358
ryanhill1 wants to merge 3 commits into
mainfrom
fix-qasm2-gphase

Conversation

@ryanhill1

@ryanhill1 ryanhill1 commented Aug 7, 2026

Copy link
Copy Markdown
Member

Fixes #351

rzz/rxx decompose to a body containing a QuantumPhase node, which Qasm2Module emitted as gphase(...) — syntax OpenQASM 2 does not have, so unrolling a legal qelib1 gate produced output no QASM 2 parser accepts. The same node also broke re-filtering after reverse_qubit_order()/remove_idle_qubits().

Global phase is unobservable, so Qasm2Module.accept() now strips QuantumPhase statements (descending into conditional bodies) from the unrolled AST. A user-written gphase in QASM 2 source is still rejected, and the #339 interim diagnostic is retained for that case with its message trimmed.

Summary by CodeRabbit

  • Bug Fixes

    • Fixed QASM 2 transformations involving conditional rzz and rxx operations.
    • Generated global-phase operations are now removed during unrolling, while user-authored gphase statements remain unsupported and are rejected.
    • Improved compatibility with idle-qubit removal and repeated reload/unroll workflows.
  • Tests

    • Added regression coverage for operation decomposition, conditional transformations, and round-trip processing.

rzz/rxx decompose to a body containing a QuantumPhase node, which
Qasm2Module emitted as gphase(...) — syntax OpenQASM 2 does not have,
so the unrolled output was not a loadable QASM 2 program. Global phase
is unobservable, so Qasm2Module.accept() now strips QuantumPhase
statements (descending into conditional bodies) from the unrolled AST.
User-written gphase in QASM 2 source is still rejected.

Fixes #351
@ryanhill1
ryanhill1 requested a review from TheGupta2012 as a code owner August 7, 2026 13:08
@argus-eye

argus-eye Bot commented Aug 7, 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 (±): 142
  • Historical avg: ~318.9k tokens · ~$1.35 · across last 6 review(s)

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

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown

Review Change Stack

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: 951fafdd-0bbb-4d32-9654-e59c42083ca0

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
📝 Walkthrough

Walkthrough

QASM 2 unrolling now removes generated global phases from top-level and conditional statements. User-written gphase statements remain invalid. Tests cover decompositions, conditional handling, refiltering, round trips, and validation.

Changes

QASM 2 global-phase handling

Layer / File(s) Summary
Filter generated global phases
src/pyqasm/modules/qasm2.py
Conditional validation distinguishes user-written phases. Recursive filtering removes QuantumPhase statements before the AST is stored.
Validate QASM 2 unrolling
tests/qasm2/test_operations.py, tests/qasm2/test_conditional_body.py, CHANGELOG.md
Tests cover rzz and rxx decompositions, conditional unrolling, refiltering, round-trip unrolling, and continued rejection of authored gphase statements. The changelog records the fix.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: thegupta2012

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: removing unroll-emitted global phases for QASM 2 targets.
Linked Issues check ✅ Passed The changes address issue #351 by removing emitted QuantumPhase nodes, preserving user-written gphase rejection, and adding rzz/rxx regression tests.
Out of Scope Changes check ✅ Passed The changelog, implementation, and tests are directly related to preventing invalid gphase output in QASM 2.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch fix-qasm2-gphase

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

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@ryanhill1

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ryanhill1

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
⚠️ Action not completed

Review rate limited.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@ryanhill1

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/pyqasm/modules/qasm2.py (1)

151-176: 📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add required type annotations and complete docstrings.

_drop_global_phase has no parameter or return annotation. Its docstring has no Args or Returns section. accept also has an untyped visitor parameter and no documented return value. Add compatible annotations and document the None return values.

As per coding guidelines, **/*.py requires type annotations for all functions and methods, and docstrings that explain purpose, parameters, and return values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/pyqasm/modules/qasm2.py` around lines 151 - 176, Add compatible type
annotations to _drop_global_phase, including its statements parameter and list
return type, and document the parameter and returned filtered statements in its
docstring. Annotate accept’s visitor parameter with the appropriate visitor type
and its return type as None, then add a Returns section documenting that it
returns None.

Source: Coding guidelines

🧹 Nitpick comments (1)
tests/qasm2/test_conditional_body.py (1)

67-73: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Keep coverage for authored conditional gphase.

This test replaces the conditional global-phase rejection test. test_user_written_gphase_rejected covers only a top-level statement. Add a companion test that verifies if(m==1) gphase(0.3); raises ValidationError, because Qasm2Module._filter_branch_body() owns this separate validation path.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/qasm2/test_conditional_body.py` around lines 67 - 73, Add a companion
test near test_conditional_rzz_survives_refiltering that loads an authored
conditional gphase statement, such as if(m==1) gphase(0.3);, and asserts
module.unroll() raises ValidationError. Keep the existing refiltering test
unchanged and target the conditional validation path implemented by
Qasm2Module._filter_branch_body().
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@CHANGELOG.md`:
- Line 29: Update the changelog entry describing the `rzz`/`rxx` fix to
explicitly state that OpenQASM 2 unrolling emitted invalid `gphase(...)` output,
while preserving the existing details about dropping unroll-emitted phases and
rejecting user-written `gphase`.

In `@tests/qasm2/test_conditional_body.py`:
- Around line 67-69: Add the return type annotation -> None to
test_conditional_rzz_survives_refiltering, and extend its docstring with a
Returns section documenting that the test returns None.

In `@tests/qasm2/test_operations.py`:
- Around line 70-72: Update all affected test functions in this file, including
test_rzz_unrolls_without_gphase and the additional referenced tests, to declare
-> None and extend each docstring with a Returns section documenting that the
function returns None.

---

Outside diff comments:
In `@src/pyqasm/modules/qasm2.py`:
- Around line 151-176: Add compatible type annotations to _drop_global_phase,
including its statements parameter and list return type, and document the
parameter and returned filtered statements in its docstring. Annotate accept’s
visitor parameter with the appropriate visitor type and its return type as None,
then add a Returns section documenting that it returns None.

---

Nitpick comments:
In `@tests/qasm2/test_conditional_body.py`:
- Around line 67-73: Add a companion test near
test_conditional_rzz_survives_refiltering that loads an authored conditional
gphase statement, such as if(m==1) gphase(0.3);, and asserts module.unroll()
raises ValidationError. Keep the existing refiltering test unchanged and target
the conditional validation path implemented by
Qasm2Module._filter_branch_body().
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 9b6c4e00-49f0-49df-ad43-a03e36765e9d

📥 Commits

Reviewing files that changed from the base of the PR and between 7e05f4c and 34693e1.

📒 Files selected for processing (4)
  • CHANGELOG.md
  • src/pyqasm/modules/qasm2.py
  • tests/qasm2/test_conditional_body.py
  • tests/qasm2/test_operations.py

Comment thread CHANGELOG.md Outdated
### Removed

### Fixed
- Fixed `rzz`/`rxx` in an OpenQASM 2 program unrolling to a `gphase(...)` statement, which QASM 2 has no syntax for, so the output was not a loadable QASM 2 program. Global phase is unobservable, so unroll-emitted phases are now dropped for a QASM 2 target; a user-written `gphase` is still rejected. ([#351](https://github.com/qBraid/pyqasm/issues/351))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Clarify the failed unrolling behavior.

The phrase “rzz/rxx in an OpenQASM 2 program unrolling to” is unclear. State that OpenQASM 2 unrolling of rzz and rxx emitted invalid gphase(...) output.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@CHANGELOG.md` at line 29, Update the changelog entry describing the
`rzz`/`rxx` fix to explicitly state that OpenQASM 2 unrolling emitted invalid
`gphase(...)` output, while preserving the existing details about dropping
unroll-emitted phases and rejecting user-written `gphase`.

Comment on lines +67 to +69
def test_conditional_rzz_survives_refiltering():
"""Test that transformations which re-filter an already unrolled body no longer
trip over the rzz global phase: it is dropped for a QASM 2 target (issue #351)"""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add the test annotation and return documentation.

Add -> None. Add a Returns section that documents the None return value.

As per coding guidelines, **/*.py requires type annotations for all functions and docstrings that explain return values.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/qasm2/test_conditional_body.py` around lines 67 - 69, Add the return
type annotation -> None to test_conditional_rzz_survives_refiltering, and extend
its docstring with a Returns section documenting that the test returns None.

Source: Coding guidelines

Comment on lines +70 to +72
def test_rzz_unrolls_without_gphase():
"""Test that the global phase from the rzz decomposition is dropped for a QASM 2
target, which has no global-phase syntax (issue #351)"""

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Add test function annotations and return documentation.

Add -> None to each test function. Add a Returns section that documents the None return value.

As per coding guidelines, **/*.py requires type annotations for all functions and docstrings that explain return values.

Also applies to: 98-100, 126-127, 146-148, 163-165

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/qasm2/test_operations.py` around lines 70 - 72, Update all affected
test functions in this file, including test_rzz_unrolls_without_gphase and the
additional referenced tests, to declare -> None and extend each docstring with a
Returns section documenting that the function returns None.

Source: Coding guidelines

@ryanhill1

Copy link
Copy Markdown
Member Author

Applied the changelog wording fix. Skipping the -> None / Returns annotations on test functions: no test in this repo's suite annotates -> None or documents a Returns section (0 occurrences across tests/), so adding it to only these six would be inconsistent with the established convention.

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.

gphase emitted for QASM 2 modules: rzz/rxx unroll to a global phase QASM 2 cannot express

2 participants