Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
47 commits
Select commit Hold shift + click to select a range
6691c07
Add inverse operation to QC dialect
Ectras Nov 21, 2025
041aad1
Add inverse operation to QCO dialect
Ectras Nov 24, 2025
3c4f859
Fix CancelNestedInv pass
Ectras Dec 11, 2025
56fad64
Add qco.inv to qc.inv conversion
Ectras Dec 18, 2025
1b72be6
Add qc.inv to qco.inv conversion
Ectras Dec 18, 2025
3386355
Merge branch 'main' into ectras/inv_modifier
Ectras Jan 19, 2026
24f5c20
Update inv modifier to current code state
Ectras Jan 27, 2026
b4cbac9
Merge branch 'main' into ectras/inv_modifier
Ectras Jan 27, 2026
ed407d6
Add unitary matrix getter to inv modifier
Ectras Jan 27, 2026
3089586
Add passes for inverting library gates
Ectras Jan 27, 2026
6e53cac
Remove unused includes in InvOp
Ectras Jan 30, 2026
d276aa5
Fix typo in QCToQCO error message
Ectras Jan 30, 2026
1a533ad
Remove useless instruction in InvOp QCToQCO conversion
Ectras Jan 30, 2026
8be434d
Rename inControlOp to inNestedRegion for QCToQCO conversion
Ectras Jan 30, 2026
b2c8d80
Fix QCO::Inv construction
Ectras Jan 30, 2026
8c55029
Add InvOp unit tests
Ectras Jan 30, 2026
85841df
Simplify angle negation in InvOp
Ectras Jan 30, 2026
9a73661
Add canonicalization for inverse of most gates
Ectras Jan 30, 2026
81f8815
Fix typo in InvOp
Ectras Feb 4, 2026
6672af4
Add canonicalization for ordering ctrl and inv
Ectras Feb 4, 2026
055b0ca
Revert "Fix typo in QCToQCO error message"
Ectras Feb 6, 2026
7610d9c
Rename inCtrlOp to inNestedRegion
Ectras Feb 6, 2026
a79e43d
Allow nested inv modifier
Ectras Feb 6, 2026
9bf8ac2
Merge branch 'main' into ectras/inv_modifier
Ectras Feb 6, 2026
58797d7
Implement getInputQubits/getOutputQubits for inv modifier
Ectras Feb 6, 2026
7868cb7
Add test for nested inv ctrl
Ectras Feb 6, 2026
5dc73a9
Add test for inv self-adjoint gate
Ectras Feb 7, 2026
11eec45
Fix outdated inv documentation
Ectras Feb 9, 2026
0f80939
Merge branch 'main' into ectras/inv_modifier
burgholzer Feb 9, 2026
6ca4a86
🚨 mark LLVM includes as SYSTEM
burgholzer Feb 9, 2026
9059412
🎨 also mark the QCO modifiers with the required terminator
burgholzer Feb 9, 2026
13d4ea7
♻️ `getNumQubits` can simply pass through underlying method
burgholzer Feb 9, 2026
f75d031
🚨 silence const correctness warnings in MLIR code
burgholzer Feb 9, 2026
2fa15ac
♻️ the `inv` operation itself should not have controls
burgholzer Feb 9, 2026
7353acb
🩹 fix inverse of U and U2
burgholzer Feb 9, 2026
6ab391e
🎨 inline angle negation
burgholzer Feb 9, 2026
b3369f2
🩹 add CancelNestedInv pattern to QCO canonicalization patterns
burgholzer Feb 9, 2026
817ea35
🚚 refer to block arguments as `qubits` instead of `targets`
burgholzer Feb 9, 2026
b93e330
🩹 ensure block argument mapping for InvOp builder
burgholzer Feb 9, 2026
4a7b5c3
🚨 fix warning about changed visibility in conversion
burgholzer Feb 9, 2026
9f491d6
🎨 slightly tweak conversion code for InvOp
burgholzer Feb 9, 2026
7a450e7
🎨 slightly tweak the tests
burgholzer Feb 9, 2026
71fbec8
📝 add PR and Manuel to changelog
burgholzer Feb 9, 2026
b863ffe
👌 declare methods as static where possible
burgholzer Feb 9, 2026
001ffb6
🩹 fix nested inv QCO canonicalization
burgholzer Feb 9, 2026
c377175
✅ modify test to trigger QCO canonicalization
burgholzer Feb 9, 2026
5fd84c3
🚨 fix missing include
burgholzer Feb 9, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 5 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,9 @@ This project adheres to [Semantic Versioning], with the exception that minor rel

### Added

- ✨ Add initial infrastructure for new QC and QCO MLIR dialects ([#1264], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**])
- ✨ Add initial infrastructure for new QC and QCO MLIR dialects
([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475])
([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**])

### Changed

Expand Down Expand Up @@ -365,6 +367,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool
[#1355]: https://github.com/munich-quantum-toolkit/core/pull/1355
[#1338]: https://github.com/munich-quantum-toolkit/core/pull/1338
[#1336]: https://github.com/munich-quantum-toolkit/core/pull/1336
[#1330]: https://github.com/munich-quantum-toolkit/core/pull/1330
[#1328]: https://github.com/munich-quantum-toolkit/core/pull/1328
[#1327]: https://github.com/munich-quantum-toolkit/core/pull/1327
[#1310]: https://github.com/munich-quantum-toolkit/core/pull/1310
Expand Down Expand Up @@ -502,6 +505,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool
[**@lsschmid**]: https://github.com/lsschmid
[**@marcelwa**]: https://github.com/marcelwa
[**@lirem101**]: https://github.com/lirem101
[**@Ectras**]: https://github.com/Ectras

<!-- General links -->

Expand Down
3 changes: 1 addition & 2 deletions cmake/SetupMLIR.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,7 @@ set(LLVM_ENABLE_RTTI ON)
set(LLVM_ENABLE_EH ON)
include(HandleLLVMOptions)

include_directories(${LLVM_INCLUDE_DIRS})
include_directories(${MLIR_INCLUDE_DIRS})
include_directories(SYSTEM ${LLVM_INCLUDE_DIRS} ${MLIR_INCLUDE_DIRS})
include_directories(${MQT_MLIR_SOURCE_INCLUDE_DIR})
include_directories(${MQT_MLIR_BUILD_INCLUDE_DIR})
link_directories(${LLVM_BUILD_LIBRARY_DIR})
Expand Down
3 changes: 2 additions & 1 deletion mlir/.clang-tidy
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ Checks: |
llvm-prefer-static-over-anonymous-namespace,
-misc-use-anonymous-namespace,
llvm-twine-local,
-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access
-cppcoreguidelines-pro-bounds-avoid-unchecked-container-access,
-*-const-correctness,

CheckOptions:
- key: misc-include-cleaner.IgnoreHeaders
Expand Down
19 changes: 19 additions & 0 deletions mlir/include/mlir/Dialect/QC/Builder/QCProgramBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -844,6 +844,25 @@ class QCProgramBuilder final : public ImplicitLocOpBuilder {
QCProgramBuilder& ctrl(ValueRange controls,
const std::function<void()>& body);

/**
* @brief Apply an inverse (i.e., adjoint) operation.
*
* @param body Function that builds the body containing the operation to
* invert
* @return Reference to this builder for method chaining
*
* @par Example:
* ```c++
* builder.inv([&] { builder.s(q0); });
* ```
* ```mlir
* qc.inv {
* qc.s %q0 : !qc.qubit
* }
* ```
*/
QCProgramBuilder& inv(const std::function<void()>& body);

//===--------------------------------------------------------------------===//
// Deallocation
//===--------------------------------------------------------------------===//
Expand Down
44 changes: 44 additions & 0 deletions mlir/include/mlir/Dialect/QC/IR/QCOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -973,4 +973,48 @@ def CtrlOp : QCOp<"ctrl",
let hasVerifier = 1;
}

def InvOp : QCOp<"inv",
traits = [
UnitaryOpInterface,
SingleBlockImplicitTerminator<"::mlir::qc::YieldOp">,
RecursiveMemoryEffects
]> {
let summary = "Invert a unitary operation";
let description = [{
A modifier operation that inverts the unitary operation defined in its body
region.

Example:
```mlir
qc.inv {
qc.s %q0 : !qc.qubit
}
```
}];

let regions = (region SizedRegion<1>:$region);
let assemblyFormat = "$region attr-dict";

let extraClassDeclaration = [{
[[nodiscard]] UnitaryOpInterface getBodyUnitary();
size_t getNumQubits();
size_t getNumTargets();
static size_t getNumControls();
Value getQubit(size_t i);
Value getTarget(size_t i);
static Value getControl(size_t i);
size_t getNumParams();
Value getParameter(size_t i);
static StringRef getBaseSymbol() { return "inv"; }
}];

let builders = [
OpBuilder<(ins "UnitaryOpInterface":$bodyUnitary)>,
OpBuilder<(ins "const std::function<void()>&":$bodyBuilder)>
];

let hasCanonicalizer = 1;
let hasVerifier = 1;
}

#endif // QC_OPS
25 changes: 25 additions & 0 deletions mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -1005,6 +1005,31 @@ class QCOProgramBuilder final : public ImplicitLocOpBuilder {
ctrl(ValueRange controls, ValueRange targets,
llvm::function_ref<llvm::SmallVector<Value>(ValueRange)> body);

/**
* @brief Apply an inverse operation
*
* @param qubits Qubits involved in the operation
* @param body Function that builds the body containing the target operation
* @return Output qubits
*
* @par Example:
* ```c++
* qubits_out = builder.inv(q0_in,
* [&](ValueRange qubits) -> llvm::SmallVector<Value> {
* return {builder.s(qubits[0])};
* }
* );
* ```
* ```mlir
* %qubits_out = qco.inv (%q = %q0_in) {
* %q_res = qco.s %q : !qco.qubit -> !qco.qubit
* qco.yield %q_res
* } : {!qco.qubit} -> {!qco.qubit}
* ```
*/
ValueRange inv(ValueRange qubits,
llvm::function_ref<llvm::SmallVector<Value>(ValueRange)> body);
Comment thread
coderabbitai[bot] marked this conversation as resolved.

//===--------------------------------------------------------------------===//
// Deallocation
//===--------------------------------------------------------------------===//
Expand Down
69 changes: 68 additions & 1 deletion mlir/include/mlir/Dialect/QCO/IR/QCOOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1074,7 +1074,7 @@ def CtrlOp : QCOOp<"ctrl", traits =
AttrSizedResultSegments,
SameOperandsAndResultType,
SameOperandsAndResultShape,
SingleBlock,
SingleBlockImplicitTerminator<"::mlir::qco::YieldOp">,
RecursiveMemoryEffects
]> {
let summary = "Add control qubits to a unitary operation";
Expand Down Expand Up @@ -1142,4 +1142,71 @@ def CtrlOp : QCOOp<"ctrl", traits =
let hasVerifier = 1;
}

def InvOp : QCOOp<"inv", traits =
[
UnitaryOpInterface,
SameOperandsAndResultType,
SameOperandsAndResultShape,
SingleBlockImplicitTerminator<"::mlir::qco::YieldOp">,
RecursiveMemoryEffects
]> {
let summary = "Invert a unitary operation";
let description = [{
A modifier operation that inverts the unitary operation defined in its body
region. The operation takes a variadic number of qubits as inputs and
produces corresponding output qubits.

Example:
```mlir
%q_out = qco.inv (%q = %q_in) {
%q_1 = qco.s %q : !qco.qubit -> !qco.qubit
qco.yield %q_1
} : {!qco.qubit} -> {!qco.qubit}
```
}];
Comment thread
coderabbitai[bot] marked this conversation as resolved.

let arguments = (ins Arg<Variadic<QubitType>, "the qubits involved in the operation", [MemRead]>:$qubits_in);
let results = (outs Variadic<QubitType>:$qubits_out);
let regions = (region SizedRegion<1>:$region);
let assemblyFormat = [{
custom<TargetAliasing>($region, $qubits_in)
attr-dict `:`
`{` type($qubits_in) `}`
`->`
`{` type($qubits_out) `}`
}];

let extraClassDeclaration = [{
UnitaryOpInterface getBodyUnitary();
size_t getNumQubits();
size_t getNumTargets();
static size_t getNumControls();
Value getInputQubit(size_t i);
OperandRange getInputQubits();
Value getOutputQubit(size_t i);
ResultRange getOutputQubits();
Value getInputTarget(size_t i);
Value getOutputTarget(size_t i);
static Value getInputControl(size_t i);
static Value getOutputControl(size_t i);
Value getInputForOutput(Value output);
Value getOutputForInput(Value input);
size_t getNumParams();
Value getParameter(size_t i);
[[nodiscard]] static StringRef getBaseSymbol() { return "inv"; }
[[nodiscard]] std::optional<Eigen::MatrixXcd> getUnitaryMatrix();
}];

let builders = [
OpBuilder<(ins "ValueRange":$qubits), [{
build($_builder, $_state, qubits.getTypes(), qubits);
}]>,
OpBuilder<(ins "ValueRange":$qubits, "UnitaryOpInterface":$bodyUnitary)>,
OpBuilder<(ins "ValueRange":$qubits, "llvm::function_ref<llvm::SmallVector<Value>(ValueRange)>":$bodyBuilder)>
];

let hasCanonicalizer = 1;
let hasVerifier = 1;
}

Comment thread
coderabbitai[bot] marked this conversation as resolved.
#endif // QCOOPS
87 changes: 75 additions & 12 deletions mlir/lib/Conversion/QCOToQC/QCOToQC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -812,6 +812,68 @@ struct ConvertQCOCtrlOp final : OpConversionPattern<qco::CtrlOp> {
}
};

/**
* @brief Converts qco.inv to qc.inv
*
* @par Example:
* ```mlir
* %q0_out = qco.inv (%q0 = %q0_in) {
* %q0_res = qco.s %q0 : !qco.qubit -> !qco.qubit
* qco.yield %q0_res
* } : {!qco.qubit} -> {!qco.qubit}
* ```
* is converted to
* ```mlir
* qc.inv {
* qc.s %q0 : !qc.qubit
* }
* ```
*/
struct ConvertQCOInvOp final : OpConversionPattern<qco::InvOp> {
using OpConversionPattern::OpConversionPattern;

LogicalResult
matchAndRewrite(qco::InvOp op, OpAdaptor adaptor,
ConversionPatternRewriter& rewriter) const override {
// Create qc.inv operation
auto qcOp = qc::InvOp::create(rewriter, op.getLoc());

// Clone body region from QCO to QC
auto& dstRegion = qcOp.getRegion();
rewriter.cloneRegionBefore(op.getRegion(), dstRegion, dstRegion.end());

auto& entryBlock = dstRegion.front();
const auto numArgs = entryBlock.getNumArguments();
if (adaptor.getQubitsIn().size() != numArgs) {
return op.emitOpError() << "qco.inv: entry block args (" << numArgs
<< ") must match number of target operands ("
<< adaptor.getQubitsIn().size() << ")";
}

// Remove all block arguments in the cloned region
rewriter.modifyOpInPlace(qcOp, [&] {
// 1. Replace uses (Must be done BEFORE erasing)
// We iterate 0..N using indices since the block args are still stable
// here.
for (auto i = 0UL; i < numArgs; ++i) {
entryBlock.getArgument(i).replaceAllUsesWith(adaptor.getQubitsIn()[i]);
}

// 2. Erase all block arguments
// Now that they have no uses, we can safely wipe them.
// We use a bulk erase for efficiency (start index 0, count N).
if (numArgs > 0) {
entryBlock.eraseArguments(0, numArgs);
}
});

// Replace the output qubits with the same QC references
rewriter.replaceOp(op, adaptor.getOperands());

return success();
}
};

/**
* @brief Converts qco.yield to qc.yield
*
Expand Down Expand Up @@ -865,6 +927,7 @@ struct ConvertQCOYieldOp final : OpConversionPattern<qco::YieldOp> {
struct QCOToQC final : impl::QCOToQCBase<QCOToQC> {
using QCOToQCBase::QCOToQCBase;

protected:
void runOnOperation() override {
MLIRContext* context = &getContext();
auto* module = getOperation();
Expand All @@ -879,18 +942,18 @@ struct QCOToQC final : impl::QCOToQCBase<QCOToQC> {

// Register operation conversion patterns
// Note: No state tracking needed - OpAdaptors handle type conversion
patterns.add<ConvertQCOAllocOp, ConvertQCODeallocOp, ConvertQCOStaticOp,
ConvertQCOMeasureOp, ConvertQCOResetOp, ConvertQCOGPhaseOp,
ConvertQCOIdOp, ConvertQCOXOp, ConvertQCOYOp, ConvertQCOZOp,
ConvertQCOHOp, ConvertQCOSOp, ConvertQCOSdgOp, ConvertQCOTOp,
ConvertQCOTdgOp, ConvertQCOSXOp, ConvertQCOSXdgOp,
ConvertQCORXOp, ConvertQCORYOp, ConvertQCORZOp, ConvertQCOPOp,
ConvertQCOROp, ConvertQCOU2Op, ConvertQCOUOp, ConvertQCOSWAPOp,
ConvertQCOiSWAPOp, ConvertQCODCXOp, ConvertQCOECROp,
ConvertQCORXXOp, ConvertQCORYYOp, ConvertQCORZXOp,
ConvertQCORZZOp, ConvertQCOXXPlusYYOp, ConvertQCOXXMinusYYOp,
ConvertQCOBarrierOp, ConvertQCOCtrlOp, ConvertQCOYieldOp>(
typeConverter, context);
patterns
.add<ConvertQCOAllocOp, ConvertQCODeallocOp, ConvertQCOStaticOp,
ConvertQCOMeasureOp, ConvertQCOResetOp, ConvertQCOGPhaseOp,
ConvertQCOIdOp, ConvertQCOXOp, ConvertQCOYOp, ConvertQCOZOp,
ConvertQCOHOp, ConvertQCOSOp, ConvertQCOSdgOp, ConvertQCOTOp,
ConvertQCOTdgOp, ConvertQCOSXOp, ConvertQCOSXdgOp, ConvertQCORXOp,
ConvertQCORYOp, ConvertQCORZOp, ConvertQCOPOp, ConvertQCOROp,
ConvertQCOU2Op, ConvertQCOUOp, ConvertQCOSWAPOp, ConvertQCOiSWAPOp,
ConvertQCODCXOp, ConvertQCOECROp, ConvertQCORXXOp, ConvertQCORYYOp,
ConvertQCORZXOp, ConvertQCORZZOp, ConvertQCOXXPlusYYOp,
ConvertQCOXXMinusYYOp, ConvertQCOBarrierOp, ConvertQCOCtrlOp,
ConvertQCOInvOp, ConvertQCOYieldOp>(typeConverter, context);

// Conversion of qco types in func.func signatures
// Note: This currently has limitations with signature changes
Expand Down
Loading
Loading