Skip to content

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

Draft
denialhaag wants to merge 3 commits into
mainfrom
unroll-modifiers
Draft

✨ Add an unroll-modifiers pass for multi-operation modifiers#2015
denialhaag wants to merge 3 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 have 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

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: 7dbb33a4-adbf-4cef-a18c-83efe30fb342

📥 Commits

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

📒 Files selected for processing (2)
  • mlir/include/mlir/Dialect/Utils/Transforms/Passes.td
  • mlir/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp

📝 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 and improved handling of classical operations, target ordering, and qubit mappings.
    • Added validation for controlled operations containing multiple unitary operations.
  • Tests

    • Added comprehensive 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 dialect, conversion, round-trip, and fixture tests.

Changes

Modifier unrolling and conversion support

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

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

Sequence Diagram(s)

sequenceDiagram
  participant ModuleOp
  participant UnrollModifiers
  participant ConversionPipeline
  ModuleOp->>UnrollModifiers: Process nested ctrl and inv modifiers
  UnrollModifiers->>ModuleOp: Clone remapped unitary modifiers
  ModuleOp->>ConversionPipeline: Convert unrolled operations
Loading

Possibly related issues

Possibly related PRs

Suggested labels: c++

Suggested reviewers: burgholzer

Poem

A rabbit unrolls each gate in flight,
Ctrl hops left and inv turns right.
QIR paths follow tracks made bright,
Tests measure qubits through the night.

o.o< 🐇

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 47.83% 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
Title check ✅ Passed The title clearly and concisely identifies the new unroll-modifiers pass for multi-operation modifiers.
Description check ✅ Passed The description includes motivation, issue reference, implementation scope, testing, checklist status, and AI disclosure.
Linked Issues check ✅ Passed The changes implement the requested ctrl, inv, and pow handling, conversion integration, and boolean inCtrlOp state from #1758.
Out of Scope Changes check ✅ Passed The code, pipeline integration, state simplification, tests, and build updates are directly related to the linked issue.
✨ 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 on lines +251 to +256
for (auto* modifier : modifiers) {
llvm::TypeSwitch<Operation*>(modifier)
.Case<qc::CtrlOp, qc::InvOp, qco::CtrlOp, qco::InvOp>([&](auto op) {
static_cast<void>(unrollModifier(op, rewriter));
});
}

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.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Report skipped modifiers.

The result of unrollModifier is discarded. A modifier that cannot be unrolled stays in the IR without any signal. The failure then appears much later as a generic legalization error in the QC-to-QIR or QCO-to-jeff conversion. Emit a remark or a LLVM_DEBUG message with the reason so that the skip is traceable.

🤖 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/lib/Dialect/Utils/Transforms/UnrollModifiers.cpp` around lines 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.

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.

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