Skip to content

Conversation

@Ectras
Copy link
Collaborator

@Ectras Ectras commented Nov 24, 2025

Description

Adds a quartz.inv and flux.inv Op, similar to the existing CtrlOp.

Example:
Quartz:

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

Flux:

%targets_out = flux.inv %targets_in {
    %targets_res = flux.s %targets_in : !flux.qubit -> !flux.qubit
    flux.yield %targets_res : !flux.qubit
} : {!flux.qubit} -> {!flux.qubit}

Missing / Unclear things:

  • The added code has quite a bit of copy-paste from the CtrlOp. Factoring out to reduce code duplication (in particular as we add more modifiers) might be a good idea.
  • Tests are missing.
  • Most canonicalizations, in particular replacing with known gates, e.g. inv(s) -> sdg
  • Handling of Quartz -> QIR lowering for quartz.inv. I think we assume that the canonicalizations already ran and the only invs left are those for which we don't know the inverse. In that case, we can only error.

This PR depends on #1264.

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

📝 Walkthrough

Walkthrough

Introduces a new InvOp (inverse/adjoint operation) across Flux and Quartz dialects with corresponding builders, verifiers, and canonicalization support. Adds a matrix adjoint utility function and implements Flux-to-Quartz dialect conversion for the new operation.

Changes

Cohort / File(s) Summary
Flux dialect declarations
mlir/include/mlir/Dialect/Flux/Builder/FluxProgramBuilder.h, mlir/include/mlir/Dialect/Flux/IR/FluxOps.td
Declares inv() builder method and defines InvOp with variadic target operands/results, body region, UnitaryOpInterface trait, and accessor methods for qubit/control counts.
Quartz dialect declarations
mlir/include/mlir/Dialect/Quartz/Builder/QuartzProgramBuilder.h, mlir/include/mlir/Dialect/Quartz/IR/QuartzOps.td
Declares inv() builder method and defines InvOp with single-block body region, UnitaryOpInterface trait, and public accessor surface mirroring Flux variant.
Utility function
mlir/include/mlir/Dialect/Utils/MatrixUtils.h
Adds getMatrixAdj() function to compute conjugate-transposed (adjoint) matrices from DenseElementsAttr.
Dialect conversion
mlir/lib/Conversion/FluxToQuartz/FluxToQuartz.cpp
Introduces ConvertFluxInvOp pattern to lower Flux InvOp to Quartz InvOp by cloning the body region.
Flux dialect implementations
mlir/lib/Dialect/Flux/Builder/FluxProgramBuilder.cpp, mlir/lib/Dialect/Flux/IR/FluxOps.cpp
Implements inv() builder method and full InvOp functionality: multiple build overloads, body/qubit accessors, input/output mapping, verification, and CancelNestedInv canonicalization pattern (inv(inv(x)) → x).
Quartz dialect implementations
mlir/lib/Dialect/Quartz/Builder/QuartzProgramBuilder.cpp, mlir/lib/Dialect/Quartz/IR/QuartzOps.cpp
Implements inv() builder method and InvOp with similar structure to Flux: build overloads, delegated accessors, verification, and CancelNestedInv canonicalization; also extends CtrlOp with additional accessor methods.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant Builder as Flux/Quartz<br/>Builder
    participant InvOp
    participant Canonicalizer
    
    User->>Builder: inv(targets, body_fn)
    Builder->>InvOp: create with targets &<br/>body region
    InvOp->>InvOp: verify body structure<br/>& qubit uniqueness
    
    rect rgb(240, 248, 255)
    Note over Canonicalizer: Canonicalization phase
    Canonicalizer->>InvOp: detect inv(inv(x))?
    alt Nested inverse found
        Canonicalizer->>InvOp: extract inner body
        Canonicalizer->>InvOp: replace outer inv<br/>with inner unitary
        Canonicalizer->>InvOp: erase inner inv
        Canonicalizer-->>User: simplified to x
    else Single or non-nested
        Canonicalizer-->>User: unchanged
    end
    end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

  • Flux and Quartz InvOp implementations (FluxOps.cpp, QuartzOps.cpp): Dense logic with multiple build overloads, accessor delegation patterns, verification logic, and canonicalization rewrite patterns. Each file requires understanding of body region handling and unitary interface delegation.
  • Dialect conversion logic (FluxToQuartz.cpp): New ConvertFluxInvOp pattern requires understanding region cloning semantics and adaptor-based operand handling.
  • Builder implementations (FluxProgramBuilder.cpp, QuartzProgramBuilder.cpp): Qubit tracking and region/block setup patterns; especially in FluxProgramBuilder.cpp, the extension to CtrlOp updating targetsOut tracking should be verified for correctness.
  • Cross-dialect consistency: Verify that Flux and Quartz InvOp semantics and canonicalization patterns align.

Possibly related issues

  • The changes directly implement the proposed inv modifier/wrapper operations with region-level support, builders, verifiers, and canonicalization framework across both Flux and Quartz dialects.
  • Both Flux and Quartz InvOp implementations include CancelNestedInv canonicalization patterns to optimize nested inverse operations, addressing adjoint simplification requirements.

Suggested labels

MLIR, c++, feature

Suggested reviewers

  • burgholzer

Poem

🐰✨ A rabbit hops through quantum gates,
Where inv now inverts their fates,
Nested inverses fade away,
Canonicals save the day—
Two dialects, one elegant way! 🎭

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 12.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Description check ❓ Inconclusive The description provides examples and context but author explicitly acknowledges that tests are missing, canonicalizations are incomplete, and several requirements remain unclear or unimplemented. Address the missing/unclear items before merging: add tests, implement key canonicalizations (e.g., inv(s)->sdg), clarify and handle Quartz->QIR lowering, and complete the checklist items marked incomplete.
✅ Passed checks (1 passed)
Check name Status Explanation
Title check ✅ Passed The title 'Add inv Modifier for MLIR Redesign' clearly describes the main change: introducing an inverse modifier operation for MLIR, mirroring the CtrlOp pattern across Flux and Quartz dialects.
✨ 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

Tip

📝 Customizable high-level summaries are now available in beta!

You can now customize how CodeRabbit generates the high-level summary in your pull requests — including its content, structure, tone, and formatting.

  • Provide your own instructions using the high_level_summary_instructions setting.
  • Format the summary however you like (bullet lists, tables, multi-section layouts, contributor stats, etc.).
  • Use high_level_summary_in_walkthrough to move the summary from the description to the walkthrough section.

Example instruction:

"Divide the high-level summary into five sections:

  1. 📝 Description — Summarize the main change in 50–60 words, explaining what was done.
  2. 📓 References — List relevant issues, discussions, documentation, or related PRs.
  3. 📦 Dependencies & Requirements — Mention any new/updated dependencies, environment variable changes, or configuration updates.
  4. 📊 Contributor Summary — Include a Markdown table showing contributions:
    | Contributor | Lines Added | Lines Removed | Files Changed |
  5. ✔️ Additional Notes — Add any extra reviewer context.
    Keep each section concise (under 200 words) and use bullet or numbered lists for clarity."

Note: This feature is currently in beta for Pro-tier users, and pricing will be announced later.


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
@github-actions
Copy link
Contributor

github-actions bot commented Nov 28, 2025

Cpp-Linter Report ⚠️

Some files did not pass the configured checks!

clang-tidy (v21.1.8) reports: 3 concern(s)
  • mlir/lib/Dialect/Flux/IR/Operations/StandardGates/XXPlusYYOp.cpp:60:48: warning: [misc-include-cleaner]

    no header providing "mlir::arith::AddFOp" is directly included

       11 |     auto newParameter = rewriter.create<arith::AddFOp>(
          |                                                ^
  • mlir/lib/Dialect/Quartz/IR/Operations/StandardGates/U2Op.cpp:14:1: warning: [misc-include-cleaner]

    included header Arith.h is not used directly

       14 | #include <mlir/Dialect/Arith/IR/Arith.h>
          | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       15 | #include <mlir/IR/Builders.h>
  • mlir/lib/Dialect/Quartz/IR/Operations/StandardGates/U2Op.cpp:16:1: warning: [misc-include-cleaner]

    included header BuiltinAttributes.h is not used directly

       16 | #include <mlir/IR/BuiltinAttributes.h>
          | ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
       17 | #include <mlir/IR/OperationSupport.h>

Have any feedback or feature suggestions? Share it here.

@Ectras Ectras force-pushed the ectras/inv_modifier branch from cbef1a9 to 1da9a01 Compare December 8, 2025 15:51
@Ectras Ectras force-pushed the ectras/inv_modifier branch from 1da9a01 to 99fade4 Compare December 11, 2025 15:49
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 99fade4 to 8a8c79e Compare December 11, 2025 16:17
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.

2 participants