Skip to content

✨ Add an unroll-modifiers pass for unrolling multi-operation modifiers - #2015

Open
denialhaag wants to merge 9 commits into
mainfrom
unroll-modifiers
Open

✨ Add an unroll-modifiers pass for unrolling multi-operation modifiers#2015
denialhaag wants to merge 9 commits into
mainfrom
unroll-modifiers

Conversation

@denialhaag

@denialhaag denialhaag commented Aug 6, 2026

Copy link
Copy Markdown
Member

Description

🤖 AI text below 🤖

Since #1751, modifier bodies may contain more than one unitary operation. This adds an unroll-modifiers pass that splits such ctrl and inv modifiers into a sequence of modifiers holding a single operation each. The targets of the new modifiers are narrowed to the qubits of the respective operation (and rewired through the SSA chain in QCO), and the order of the operations is reversed for inv, since $(ab)^{-1} = b^{-1} a^{-1}$. Classical operations are moved in front of the modifier; a modifier is left untouched when one of them is impure or depends on the body's qubits. pow modifiers are left untouched as well, because $(ab)^r$ is generally not equal to $a^r b^r$ — modifiers nested inside a pow body are still unrolled. Following #1986, the pass is shared between QC and QCO and lives in mlir::mqt. It now runs before the QCO-to-jeff and QC-to-QIR conversions, which cannot fully handle multi-operation modifiers.

Fixes #1758

AI Notice

The first version of the changes was assisted by Opus 5 via Claude Code. I subsequently iterated on the changes.

Checklist

  • The pull request only contains commits that are focused and relevant to this change.
  • I have added appropriate tests that cover the new/changed functionality.
  • I have updated the documentation to reflect these changes.
  • I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.
  • I have added migration instructions to the upgrade guide (if needed).
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • I have reviewed my own code changes.

If PR contains AI-assisted content:

  • Any agent that created, edited, or submitted GitHub content was explicitly authorized for that scope, as required by our AI Usage Guidelines.
  • Every agent-authored or agent-edited public text body begins with the visible disclosure 🤖 *AI text below* 🤖 (titles are exempt).
  • I have disclosed AI assistance in the PR description.
  • I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it.

Assisted-by: Claude Opus 5 via Claude Code
@denialhaag denialhaag self-assigned this Aug 6, 2026
@denialhaag denialhaag added feature New feature or request MLIR Anything related to MLIR labels Aug 6, 2026
@denialhaag denialhaag added this to the MLIR Support milestone Aug 6, 2026
@denialhaag

Copy link
Copy Markdown
Member Author

@coderabbitai full review

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added automatic unrolling for multi-operation controlled and inverse modifiers, including nested modifiers.
    • Integrated modifier unrolling into supported compilation workflows.
    • Preserved power modifiers while improving classical-operation handling, target ordering, and qubit mapping.
    • Added validation for controlled operations containing multiple unitary operations.
  • Bug Fixes

    • Prevented unsupported static and other non-unitary operations within modifiers.
  • Tests

    • Added coverage for modifier splitting, inversion, nesting, target ordering, power modifiers, and conversion round trips.

Walkthrough

Added the public UnrollModifiers pass for multi-operation ctrl and inv modifiers in QC and QCO. Integrated it before Jeff and QIR conversions. Simplified QIR control tracking and added verification, conversion, round-trip, and fixture tests.

Changes

Modifier unrolling and conversion support

Layer / File(s) Summary
Control-state contract and modifier verification
mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h, mlir/lib/Conversion/QCToQIR/QIRCommon/QIRCommon.cpp, mlir/lib/Dialect/QC/IR/Modifiers/ModifierUtils.cpp, mlir/lib/Dialect/QCO/IR/Modifiers/*
LoweringState::inCtrlOp now uses a boolean. Control validation and cleanup use the updated state model. Modifier verification rejects StaticOp and unsupported control bodies.
Modifier unrolling implementation
mlir/include/mlir/Dialect/Utils/Transforms/Passes.td, mlir/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp
The pass splits multi-operation ctrl and inv bodies, remaps targets, reverses inverse operations, hoists eligible classical operations, handles nested modifiers, and preserves pow.
Pass registration and compiler pipelines
mlir/lib/Dialect/Utils/Transforms/CMakeLists.txt, mlir/lib/Support/Passes.cpp, mlir/lib/Compiler/Programs.cpp, mlir/tools/mqt-cc/mqt-cc.cpp, mlir/unittests/Conversion/*/CMakeLists.txt
The pass is compiled, registered, and runs before Jeff, QIR Base, and QIR Adaptive conversions.
Modifier and conversion validation
mlir/unittests/Dialect/*, mlir/unittests/Conversion/*, mlir/unittests/programs/*
Tests cover control splitting, target reordering, inverse reversal, nesting, trailing operations, preserved powers, conversion round trips, and reference programs.

Estimated code review effort: 4 (Complex) | ~45 minutes

Possibly related issues

Possibly related PRs

Suggested labels: refactor, c++

Suggested reviewers: burgholzer

Poem

A rabbit splits modifiers with care,
Ctrl hops forward, inv turns the air.
QIR follows each measured trail,
While tests check every qubit detail.

o.o< 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.65% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The implementation satisfies #1758 by adding the pass, handling ctrl, inv, and pow cases, integrating required pipelines, and simplifying inCtrlOp.
Out of Scope Changes check ✅ Passed The changes support the linked issue through implementation, integration, validation updates, and focused tests without unrelated functionality.
Title check ✅ Passed The title clearly and concisely describes the added pass for unrolling multi-operation modifiers.
Description check ✅ Passed The description provides the change summary, motivation, issue reference, testing details, checklist, and AI disclosure; dependencies are not listed explicitly.
✨ Finishing Touches
✨ Simplify code
  • Create PR with simplified code
  • Commit simplified code in branch unroll-modifiers

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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 4

🤖 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 `@mlir/include/mlir/Dialect/Utils/Transforms/Passes.td`:
- Around line 40-51: Update the Passes.td description for the relevant pass to
document all implemented limitations: classical operations remain inside the
modifier when they are impure or use values defined in the body, ctrl/inv
modifiers remain unchanged when body unitaries do not thread every distinct
qubit operand to a result, and unsupported modifiers are skipped without
diagnostics. Retain the existing pow behavior and nested-modifier scope.

In `@mlir/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp`:
- Around line 35-41: Add static linkage to the free functions getBodyUnitaries,
getQubitOperands, hoistClassicalOps, cloneIntoBody, hasThreadedBodyUnitaries,
and all four unrollModifier overloads. Remove these functions from the anonymous
namespace, while retaining only the UnrollModifiers struct there.
- Around line 251-256: Update the modifier loop’s `unrollModifier` handling to
inspect its result instead of discarding it, and emit a remark or `LLVM_DEBUG`
diagnostic when unrolling is skipped, including the returned reason. Keep
successful unrolling behavior unchanged and ensure the diagnostic identifies the
affected modifier.
- Around line 193-197: In the result-building logic around the qubits mapping
and qco::CtrlOp::create, replace unchecked qubits.lookup calls with checked
lookups that assert each yielded value has a non-null mapped Value before
appending or using it. Fail fast on missing mappings, including the lookup used
for replacement operands before rewriter.replaceOp.
🪄 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: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 42503a67-d662-4e1e-bff8-bebe17f8b3c5

📥 Commits

Reviewing files that changed from the base of the PR and between 52cf1e7 and eee1742.

📒 Files selected for processing (22)
  • mlir/include/mlir/Conversion/QCToQIR/QIRCommon/QIRCommon.h
  • mlir/include/mlir/Dialect/Utils/Transforms/Passes.td
  • mlir/lib/Compiler/Programs.cpp
  • mlir/lib/Conversion/QCToQIR/QIRCommon/QIRCommon.cpp
  • mlir/lib/Dialect/Utils/Transforms/CMakeLists.txt
  • mlir/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp
  • mlir/lib/Support/Passes.cpp
  • mlir/tools/mqt-cc/mqt-cc.cpp
  • mlir/unittests/Conversion/JeffRoundTrip/CMakeLists.txt
  • mlir/unittests/Conversion/JeffRoundTrip/test_jeff_round_trip.cpp
  • mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/CMakeLists.txt
  • mlir/unittests/Conversion/QCToQIR/QCToQIRAdaptive/test_qc_to_qir_adaptive.cpp
  • mlir/unittests/Conversion/QCToQIR/QCToQIRBase/CMakeLists.txt
  • mlir/unittests/Conversion/QCToQIR/QCToQIRBase/test_qc_to_qir_base.cpp
  • mlir/unittests/Dialect/QC/IR/CMakeLists.txt
  • mlir/unittests/Dialect/QC/IR/test_qc_ir.cpp
  • mlir/unittests/Dialect/QCO/IR/CMakeLists.txt
  • mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp
  • mlir/unittests/programs/qc_programs.cpp
  • mlir/unittests/programs/qc_programs.h
  • mlir/unittests/programs/qco_programs.cpp
  • mlir/unittests/programs/qco_programs.h

Comment thread mlir/include/mlir/Dialect/Utils/Transforms/Passes.td
Comment thread mlir/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp Outdated
Comment thread mlir/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp
Comment thread mlir/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp
Assisted-by: Claude Opus 5 via Claude Code
@denialhaag

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 6, 2026

Copy link
Copy Markdown
Contributor
✅ 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.

@codecov

codecov Bot commented Aug 7, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 91.30435% with 12 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...r/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp 91.4% 10 Missing ⚠️
...lir/lib/Conversion/QCToQIR/QIRCommon/QIRCommon.cpp 81.8% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

@denialhaag denialhaag changed the title ✨ Add an unroll-modifiers pass for multi-operation modifiers ✨ Add an unroll-modifiers pass for unrolling multi-operation modifiers Aug 7, 2026
Assisted-by: Claude Opus 5 via Claude Code
@denialhaag
denialhaag marked this pull request as ready for review August 7, 2026 13:14
@denialhaag

Copy link
Copy Markdown
Member Author

@coderabbitai review

@coderabbitai

coderabbitai Bot commented Aug 7, 2026

Copy link
Copy Markdown
Contributor
✅ 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
Contributor

Choose a reason for hiding this comment

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

♻️ Duplicate comments (1)
mlir/include/mlir/Dialect/Utils/Transforms/Passes.td (1)

45-48: 📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

🤖 AI text below 🤖

Correct the pass limitation and typo.

hoistClassicalOps leaves the modifier unchanged if a classical operation is impure or depends on a value defined in the body. State this condition. Replace intouched with untouched.

As per coding guidelines, MLIR pass descriptions must align with implemented limitations.

Proposed documentation update
-    Classical operations of the body are moved in front of the new modifiers.
+    Pure classical operations that do not depend on values defined in the body
+    are moved in front of the new modifiers. Otherwise, the modifier is left
+    untouched.
 
-    If a modifier cannot be unrolled, the modifier is left intouched. Such
+    If a modifier cannot be unrolled, the modifier is left untouched. Such
🤖 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 `@mlir/include/mlir/Dialect/Utils/Transforms/Passes.td` around lines 45 - 48,
Update the pass description for hoistClassicalOps to state that modifiers remain
unchanged when classical operations are impure or depend on values defined in
the body, and replace “intouched” with “untouched.”

Source: Coding guidelines

🤖 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.

Duplicate comments:
In `@mlir/include/mlir/Dialect/Utils/Transforms/Passes.td`:
- Around line 45-48: Update the pass description for hoistClassicalOps to state
that modifiers remain unchanged when classical operations are impure or depend
on values defined in the body, and replace “intouched” with “untouched.”

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: a2d21c2f-9695-4a07-a806-ddf468cba053

📥 Commits

Reviewing files that changed from the base of the PR and between 0cf9047 and 491e802.

📒 Files selected for processing (11)
  • mlir/include/mlir/Dialect/Utils/Transforms/Passes.td
  • mlir/lib/Dialect/QC/IR/Modifiers/ModifierUtils.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/CtrlOp.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp
  • mlir/lib/Dialect/QCO/IR/Modifiers/PowOp.cpp
  • mlir/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp
  • mlir/unittests/Conversion/JeffRoundTrip/test_jeff_round_trip.cpp
  • mlir/unittests/Dialect/QC/IR/test_qc_ir.cpp
  • mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp
  • mlir/unittests/programs/qco_programs.cpp
  • mlir/unittests/programs/qco_programs.h
💤 Files with no reviewable changes (1)
  • mlir/unittests/Dialect/QCO/IR/test_qco_ir.cpp

@denialhaag
denialhaag requested a review from burgholzer August 7, 2026 13:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature New feature or request MLIR Anything related to MLIR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Add pass for unrolling multi-operation modifiers

1 participant