Skip to content

✨ Add inv Modifier for MLIR Redesign#1330

Merged
burgholzer merged 47 commits intomainfrom
ectras/inv_modifier
Feb 9, 2026
Merged

✨ Add inv Modifier for MLIR Redesign#1330
burgholzer merged 47 commits intomainfrom
ectras/inv_modifier

Conversation

@Ectras
Copy link
Collaborator

@Ectras Ectras commented Nov 24, 2025

Description

Adds a qc.inv and qco.inv Op, similar to the existing CtrlOp.

Example:
QC:

qc.inv {
  qc.s %q0 : !qc.qubit
}

QCO:

%targets_out = qco.inv (%arg0 = %targets_in) {
    %targets_res = qco.s %arg0 : !qco.qubit -> !qco.qubit
    qco.yield %targets_res : !qco.qubit
} : {!qco.qubit} -> {!qco.qubit}

Things to note:

  • The canonicalizations are currently in QCO (such as replacing known gates with their inverse). Could happen already in QC or in both.
  • QuantumComputing (which eagerly replaces gates by their inverse) is not touched by this PR. Afaik, the QC and QCO dialects are not directly used by QuantumComputing yet.
  • Missing: Handling of invs of unknown gates, for example iSWAP (the only standard gate for which there's no "trivial" inverse). inv { iSWAP } will currently error on the translation to QIR. A reasonable tactic could be to get the inverse unitary matrix and decompose it into native gates. I think this is outside the scope of this PR, though.

Fixes #1130

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.

@Ectras Ectras changed the base branch from main to dialect-implementation November 24, 2025 12:19
@codecov
Copy link

codecov bot commented Nov 24, 2025

@Ectras Ectras self-assigned this Nov 24, 2025
@munich-quantum-toolkit munich-quantum-toolkit deleted a comment from coderabbitai bot Nov 24, 2025
@Ectras
Copy link
Collaborator Author

Ectras commented Nov 24, 2025

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 24, 2025

✅ Actions performed

Full review triggered.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Nov 24, 2025

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review
📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Added inverse (adjoint) modifier to QC/QCO dialects with builder APIs and unitary accessors.
  • Bug Fixes / Improvements
    • Added canonicalization and verification for inverse modifier; improved conversion coverage between dialects to handle inverses.
  • Tests
    • New end-to-end compiler pipeline tests for inverse operations and nested/control scenarios.
  • Chores
    • Updated build include handling, lint config, and refreshed changelog/contributor entries.

Walkthrough

Adds an inv (inverse/adjoint) modifier to QC and QCO MLIR dialects: op definitions, builders, verifiers, canonicalizations, conversions (QC↔QCO), builder runtime wiring, and end-to-end compiler pipeline tests.

Changes

Cohort / File(s) Summary
QC — op & builder
mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h, mlir/include/mlir/Dialect/QC/IR/QCOps.td, mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp
Add qc.inv op definition, public accessors/builders, verifier and canonicalization patterns (MoveCtrlOutside, CancelNestedInv), and QCProgramBuilder::inv(...) API.
QCO — op, builder & runtime
mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h, mlir/include/mlir/Dialect/QCO/IR/QCOps.td, mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp, mlir/lib/Dialect/QCO/Builder/QCOProgramBuilder.cpp
Add qco.inv op definition, accessors/builders, verifier and canonicalizations (InlineSelfAdjoint, ReplaceWithKnownGates, CancelNestedInv), getUnitaryMatrix() support, and QCOProgramBuilder::inv(...) runtime implementation.
Conversions / Passes
mlir/lib/Conversion/QCToQCO/QCToQCO.cpp, mlir/lib/Conversion/QCOToQC/QCOToQC.cpp
Add conversion patterns for qc.invqco.inv; migrate nested-region tracking (inCtrlOpinNestedRegion); register Inv conversion patterns and adjust pass visibility.
Tests
mlir/unittests/Compiler/test_compiler_pipeline.cpp
Add end-to-end pipeline tests: InvX, InvRx, NestedInvs, NestedInvCtrlS validating inv across compiler stages.
Tooling / config / docs
CHANGELOG.md, cmake/SetupMLIR.cmake, mlir/.clang-tidy
Changelog formatting/author updates; consolidate include_directories to SYSTEM; update clang-tidy disabled checks.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  participant Builder as Builder (QC/QCO)
  participant InvOp as InvOp (qc/qco.inv)
  participant Canon as Canonicalizer
  participant Conv as Converter (QC↔QCO)
  participant Pipeline as Compiler Pipeline

  Builder->>InvOp: create region body (block of unitary ops)
  InvOp->>InvOp: verify region (unitary-only, yields, no duplicates)
  InvOp->>Canon: register canonicalization patterns
  Pipeline->>Canon: run canonicalization
  alt canonicalizable
    Canon->>InvOp: apply per-op adjoints / cancel nested invs / move ctrl
  else preserved
    Canon-->>Pipeline: keep inv for later lowering
  end
  Pipeline->>Conv: lower/convert inv between QC and QCO
  Conv->>InvOp: clone/translate region, wire block args/targets
  Conv-->>Pipeline: lowered IR for downstream stages
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related issues

Possibly related PRs

Suggested labels

c++

Suggested reviewers

  • denialhaag

Poem

🐰 I hopped through blocks and flipped each spin,
I wrapped the unitary and turned it thin,
Builders stitched the body, passes took the floor,
Inv danced with Canon, conversions opened the door,
Carrot-coded tests clap when CI runs galore!

🚥 Pre-merge checks | ✅ 4 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 8.47% 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 describes the main change: adding an inv modifier to the MLIR redesign, which is the primary purpose of this PR.
Description check ✅ Passed The description provides code examples for both QC and QCO forms, notes important considerations (canonicalization location, unknown gates), and references the fixed issue (#1130). However, two checklist items are unchecked: changelog entries and migration instructions.
Linked Issues check ✅ Passed The PR implements all core objectives from issue #1130: qc.inv and qco.inv operations with lazy semantics, UnitaryOpInterface support, canonicalizations for standard gates (self-adjoint, S/T inverses, rotation negation), region-level inversion with operation order reversal, tests for single-op and block inversion, and documentation updates.
Out of Scope Changes check ✅ Passed Most changes are directly aligned with #1130 objectives (inv operation implementation, builders, canonicalizations, conversions, tests). Minor out-of-scope changes include: cmake/SetupMLIR.cmake (system includes modification), mlir/.clang-tidy (disabled checks), and CHANGELOG.md/contributor updates, which are peripheral to the core inv functionality.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch ectras/inv_modifier

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 and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7eb161a and b5b7535.

📒 Files selected for processing (10)
  • mlir/include/mlir/Dialect/Flux/Builder/FluxProgramBuilder.h (1 hunks)
  • mlir/include/mlir/Dialect/Flux/IR/FluxOps.td (1 hunks)
  • mlir/include/mlir/Dialect/Quartz/Builder/QuartzProgramBuilder.h (1 hunks)
  • mlir/include/mlir/Dialect/Quartz/IR/QuartzOps.td (1 hunks)
  • mlir/include/mlir/Dialect/Utils/MatrixUtils.h (1 hunks)
  • mlir/lib/Conversion/FluxToQuartz/FluxToQuartz.cpp (2 hunks)
  • mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp (1 hunks)
  • mlir/lib/Dialect/Flux/IR/FluxOps.cpp (4 hunks)
  • mlir/lib/Dialect/Quartz/Builder/QuartzProgramBuilder.cpp (1 hunks)
  • mlir/lib/Dialect/Quartz/IR/QuartzOps.cpp (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-09T13:13:51.224Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:171-180
Timestamp: 2025-10-09T13:13:51.224Z
Learning: In MQT Core MLIR, UnitaryInterface operations guarantee 1-1 correspondence between input and output qubits in the same order. When cloning or modifying unitary operations (e.g., removing controls), this correspondence is maintained by construction, so yielding getAllInQubits() in else-branches matches the result types from the operation's outputs.

Applied to files:

  • mlir/include/mlir/Dialect/Quartz/IR/QuartzOps.td
  • mlir/include/mlir/Dialect/Flux/IR/FluxOps.td
  • mlir/lib/Dialect/Flux/IR/FluxOps.cpp
  • mlir/lib/Dialect/Quartz/IR/QuartzOps.cpp
🧬 Code graph analysis (6)
mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp (2)
mlir/lib/Dialect/Quartz/Builder/QuartzProgramBuilder.cpp (2)
  • inv (406-410)
  • inv (407-407)
mlir/lib/Dialect/Quartz/IR/QuartzOps.cpp (2)
  • invOp (379-392)
  • invOp (379-380)
mlir/include/mlir/Dialect/Flux/Builder/FluxProgramBuilder.h (2)
mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp (3)
  • inv (513-525)
  • inv (513-515)
  • ValueRange (479-489)
mlir/lib/Dialect/Quartz/Builder/QuartzProgramBuilder.cpp (2)
  • inv (406-410)
  • inv (407-407)
mlir/lib/Dialect/Quartz/Builder/QuartzProgramBuilder.cpp (1)
mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp (2)
  • inv (513-525)
  • inv (513-515)
mlir/include/mlir/Dialect/Quartz/Builder/QuartzProgramBuilder.h (2)
mlir/lib/Dialect/Quartz/Builder/QuartzProgramBuilder.cpp (3)
  • QuartzProgramBuilder (32-34)
  • inv (406-410)
  • inv (407-407)
mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp (2)
  • inv (513-525)
  • inv (513-515)
mlir/lib/Conversion/FluxToQuartz/FluxToQuartz.cpp (1)
mlir/lib/Conversion/QuartzToFlux/QuartzToFlux.cpp (17)
  • op (203-221)
  • op (204-205)
  • op (243-259)
  • op (244-245)
  • op (281-300)
  • op (282-283)
  • op (330-354)
  • op (331-332)
  • op (381-400)
  • op (382-383)
  • op (418-422)
  • op (419-420)
  • op (440-444)
  • op (441-442)
  • op (462-466)
  • op (463-464)
  • context (929-981)
mlir/lib/Dialect/Quartz/IR/QuartzOps.cpp (2)
mlir/include/mlir/Dialect/Quartz/IR/QuartzDialect.h (4)
  • getNumTargets (71-71)
  • getNumControls (72-72)
  • getNumPosControls (73-73)
  • getNumNegControls (74-74)
mlir/include/mlir/Dialect/Utils/MatrixUtils.h (1)
  • getMatrixAdj (181-211)
🪛 ast-grep (0.40.0)
mlir/lib/Dialect/Flux/IR/FluxOps.cpp

[warning] 356-356: The getTargetsIn function returns NULL on error and this line dereferences the return value without checking for NULL.
Context: getTargetsIn()[i]
Note: [CWE-476] NULL Pointer Dereference. [REFERENCES]
- https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers

(null-library-function-cpp)


[warning] 358-358: The getTargetsOut function returns NULL on error and this line dereferences the return value without checking for NULL.
Context: getTargetsOut()[i]
Note: [CWE-476] NULL Pointer Dereference. [REFERENCES]
- https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers

(null-library-function-cpp)


[warning] 378-378: The getTargetsOut function returns NULL on error and this line dereferences the return value without checking for NULL.
Context: getTargetsOut()[i]
Note: [CWE-476] NULL Pointer Dereference. [REFERENCES]
- https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers

(null-library-function-cpp)


[warning] 379-379: The getTargetsIn function returns NULL on error and this line dereferences the return value without checking for NULL.
Context: getTargetsIn()[i]
Note: [CWE-476] NULL Pointer Dereference. [REFERENCES]
- https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers

(null-library-function-cpp)


[warning] 387-387: The getTargetsIn function returns NULL on error and this line dereferences the return value without checking for NULL.
Context: getTargetsIn()[i]
Note: [CWE-476] NULL Pointer Dereference. [REFERENCES]
- https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers

(null-library-function-cpp)


[warning] 388-388: The getTargetsOut function returns NULL on error and this line dereferences the return value without checking for NULL.
Context: getTargetsOut()[i]
Note: [CWE-476] NULL Pointer Dereference. [REFERENCES]
- https://wiki.sei.cmu.edu/confluence/display/c/EXP34-C.+Do+not+dereference+null+pointers

(null-library-function-cpp)

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
🔇 Additional comments (4)
mlir/include/mlir/Dialect/Utils/MatrixUtils.h (1)

181-211: Implementation verified as correct and actively used; acknowledge known test gaps in PR objectives.

The conjugate transpose implementation is mathematically correct and follows established patterns in the codebase. Verification confirms getMatrixAdj is actively used by InvOp::tryGetStaticMatrix() in both mlir/lib/Dialect/Quartz/IR/QuartzOps.cpp:347 and mlir/lib/Dialect/Flux/IR/FluxOps.cpp:404.

No existing tests were found for this function; however, this aligns with the PR objectives which explicitly acknowledge missing tests as a known limitation. Ensure test coverage is added as part of the PR's test implementation phase.

mlir/lib/Conversion/FluxToQuartz/FluxToQuartz.cpp (1)

650-686: ConvertFluxInvOp semantics look consistent with existing Ctrl conversion

The new ConvertFluxInvOp correctly mirrors the CtrlOp pattern: it creates a quartz::InvOp, clones the Flux body region into the Quartz op, and rewires the Flux inv results to the already-converted operands to model in-place semantics on Quartz. Registration in the pattern set is also consistent with the surrounding conversions. No changes needed.

Also applies to: 761-762

mlir/lib/Dialect/Quartz/IR/QuartzOps.cpp (1)

282-348: Quartz InvOp implementation aligns with modifier semantics

The added InvOp builders, accessors, and verifier cleanly mirror the Ctrl-style modifier pattern: they encapsulate a single unitary op plus YieldOp, delegate all qubit/control/parameter queries to the body unitary, and derive the static matrix via getMatrixAdj of the body unitary’s static matrix. This is consistent with the UnitaryOpInterface contract and Quartz’s in-place semantics.

mlir/lib/Dialect/Flux/IR/FluxOps.cpp (1)

304-405: Flux InvOp implementation matches Ctrl-style modifier patterns

The Flux InvOp builders, accessors, and verifier are structured analogously to CtrlOp: they construct a single-unitary body plus flux.yield, delegate all qubit/control/parameter queries to the inner unitary, and derive adjoint static matrices via getMatrixAdj on the body’s static matrix. The verifier’s checks on body shape, yield arity, and uniqueness of input/output qubits align with the UnitaryOpInterface invariants. This looks solid.

Also applies to: 407-442

@Ectras Ectras marked this pull request as draft November 24, 2025 14:22
@Ectras Ectras force-pushed the ectras/inv_modifier branch from b5b7535 to cbef1a9 Compare November 28, 2025 16:46
@Ectras Ectras force-pushed the ectras/inv_modifier branch 2 times, most recently from 1da9a01 to 99fade4 Compare December 11, 2025 15:49
@Ectras Ectras force-pushed the ectras/inv_modifier branch 2 times, most recently from 8a8c79e to abb9346 Compare December 15, 2025 13:40
Base automatically changed from dialect-implementation to main December 18, 2025 00:37
Before, the passes tried to delete the innerInvOp but this failed as
they still used the innerInnerUnitary. Now, the innerInnerUnitary is
cloned and the operation can be deleted (happens within the
replaceOp(...)).
@Ectras Ectras force-pushed the ectras/inv_modifier branch from abb9346 to 1b72be6 Compare December 18, 2025 12:00
@burgholzer burgholzer added this to the MLIR Support milestone Dec 19, 2025
@burgholzer burgholzer added feature New feature or request MLIR Anything related to MLIR labels Dec 19, 2025
@burgholzer
Copy link
Member

Hey @Ectras 👋🏼
Thanks for the work on this 🙌🏼 now that we finally merged the initial Dialect Redesign, this should hopefully be unblocked and we can push this forward more actively.

Canonicalizations would be great because I agree with your assessment that we can't really handle the inverse modifier in the QIR conversion because QIR has no such concept. Thus, these need to be resolved before that.

This is something that I already observed for the CtrlOp modifier, but there will probably be quite a bit of duplication because the canonicalizations probably make sense in both dialects; modifiers only ever contain a single operation so it is equally easy to apply the canoncicalization in the QC dialect as in the QCO dialect.
Maybe it makes sense to only implement them in the QC dialect for now, since none of the currently available passes will actually produce an inverse modifier. (The same thing would actually hold for the nested control canonicalisation as well)

Just some initial thoughts though. The more we can unify, the better. The power modifier will introduce some similar code as well supposedly (with similar consequences).

@Ectras
Copy link
Collaborator Author

Ectras commented Dec 19, 2025

Thanks for the input @burgholzer!

I think it makes sense to start simple and iterate as needed; e.g., first just do the canonicalization based on gate names (this is how the inverse operation is currently resolved in QuantumComputing) and later maybe introduce Hermitian or Rotation traits to allow for easier extensibility.

Same for code duplication: I agree that there will be quite some duplication a) between the passes on the two dialects and b) between the different modifiers. I'd first get things to work and then refine where sensible. Implementing canonicalizations mainly in the QC dialect for now sounds reasonable.

@burgholzer
Copy link
Member

Thanks for the input @burgholzer!

I think it makes sense to start simple and iterate as needed; e.g., first just do the canonicalization based on gate names (this is how the inverse operation is currently resolved in QuantumComputing) and later maybe introduce Hermitian or Rotation traits to allow for easier extensibility.

Same for code duplication: I agree that there will be quite some duplication a) between the passes on the two dialects and b) between the different modifiers. I'd first get things to work and then refine where sensible. Implementing canonicalizations mainly in the QC dialect for now sounds reasonable.

Fully agree. Just one correction: the canonicalizations can actually be done on the individual operation level not just with gate names. Each gate has its own class now and we do define some canonicalizations already for most of them. It should be pretty simple to adapt the convenience functions that @denialhaag already created for inverse cancellation and rotation merging.

@burgholzer
Copy link
Member

@Ectras Happy new year! 🎉
May I ask for an update here? Is there anything that is currently blocking you?
In case you have not seen it: #1436 slightly changed how we implement the Ctrl modifier and I believe similar changes also apply to the inverse modifier here.

@Ectras
Copy link
Collaborator Author

Ectras commented Jan 13, 2026

@Ectras Happy new year! 🎉 May I ask for an update here? Is there anything that is currently blocking you? In case you have not seen it: #1436 slightly changed how we implement the Ctrl modifier and I believe similar changes also apply to the inverse modifier here.

@burgholzer Thanks, and a happy new year to you, too 🎆
Some deadlines are keeping me busy these days, but I certainly have this PR on my radar. I need to check what the current state of the ctrl modifier is and update this PR (thanks for the heads up about #1436). I don't think there are any major roadblocks :)

@Ectras
Copy link
Collaborator Author

Ectras commented Feb 9, 2026

@burgholzer I think the PR is ready for review now

@burgholzer
Copy link
Member

Great to see this being ready for review 🎉

It's on my pile of PRs to go through. More likely than not, I am going to push some changes directly to the branch as I work through it. I'll try to get to the PR asap.

Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Copy link
Member

@burgholzer burgholzer left a comment

Choose a reason for hiding this comment

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

@Ectras Thanks for working on this.
This was already looking really good before the commits I just pushed.
I fixed up a couple of things here and there (hope the individual commits are clear enough).
Let's see what the rabbit has to say, but otherwise this should be good to go for.
Testing itself is a little bit on the low end, but I wanted to expand the test suite a little bit anyway. So I will just include some more tests in a follow-up PR.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@mlir/include/mlir/Dialect/QCO/IR/QCOOps.td`:
- Around line 1145-1211: The InvOp accessor methods for controls
(getNumControls, getInputControl, getOutputControl) should be declared static to
match BarrierOp and the zero-control pattern: update the extraClassDeclaration
in the InvOp TD to make getNumControls(), getInputControl(size_t), and
getOutputControl(size_t) static (keeping their return types and parameter
lists), and ensure any callers/use-sites expect static access (or adjust call
sites accordingly); this keeps the API consistent with BarrierOp while retaining
the same behavior if these methods simply return 0/assert.

In `@mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp`:
- Around line 223-238: The CancelNestedInv::matchAndRewrite currently clones the
innerInnerUnitary but leaves its block arguments bound to the inner InvOp's
block args (out of scope); before calling rewriter.clone, create a
BlockAndValueMapping that maps innerInnerUnitary's block arguments to the outer
op's input targets (use op.getTargets() or the outerUnitary's operands/arguments
as the source values), apply that mapping when cloning (e.g.,
rewriter.clone(*innerInnerUnitary.getOperation(), mapping)), and then call
rewriter.replaceOp(op, clonedOp->getResults()); this ensures the cloned
operation's block args are remapped to the outer InvOp's inputs.

Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Signed-off-by: burgholzer <burgholzer@me.com>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@mlir/lib/Dialect/QC/IR/Modifiers/InvOp.cpp`:
- Around line 126-139: The verifier InvOp::verify currently enforces exactly two
operations in the body (one unitary + yield) which blocks multi-op region
inversion; change the check to allow one-or-more unitary ops followed by a
single YieldOp: validate that the body has at least 2 operations, that the last
operation is a YieldOp, and that every operation before the last implements
UnitaryOpInterface (use block.getOperations(), block.back(),
block.front()/iterators to inspect). Update the error messages accordingly, and
then implement/update the lowering/canonicalization logic that handles
region-level inversion to reverse the order of these multiple unitary ops and
replace each with its adjoint (reverse+adjoint lowering/canonicalization for the
Inv region) so multi-op blocks are correctly inverted.

In `@mlir/lib/Dialect/QCO/IR/Modifiers/InvOp.cpp`:
- Around line 75-216: The matchAndRewrite in InvOp's TypeSwitch is missing cases
for controlled-rotation ops (e.g., CRXOp/MCRXOp, CRYOp/MCRYOp, CRZOp/MCRZOp,
CRXXOp/MCRXXOp, etc.), so add .Case handlers for each controlled rotation that
create a negated angle via arith::NegFOp::create(rewriter, op.getLoc(),
<op>.getTheta()) and call rewriter.replaceOpWithNewOp<ThatControlledOp>(op,
/*preserve control operands and target operands in the same order*/
controlOperands..., targetOperands..., negTheta) (for multi-controlled variants
preserve the list of controls/targets similarly); update patterns for any ops
exposing getTheta()/getPhi()/getLambda() to negate the correct parameter(s) and
return success(). Ensure you reference matchAndRewrite, InvOp, TypeSwitch, and
use rewriter.replaceOpWithNewOp and arith::NegFOp to implement each case.

@burgholzer burgholzer merged commit 8d38d03 into main Feb 9, 2026
33 of 34 checks passed
@burgholzer burgholzer deleted the ectras/inv_modifier branch February 9, 2026 15:32
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.

🔁 MLIR - inv (adjoint) modifier (op- and region-level)

2 participants