Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
59f87ae
fix(mlir): :bug: fix one-time-use of variables for the `ctrl` modifier
DRovara Jan 9, 2026
a50d3af
Merge branch 'main' into mlir/fix/ctrl-one-use
DRovara Jan 9, 2026
fd061c7
fix(mlir): :bug: fix error introduced by merge
DRovara Jan 9, 2026
7179197
docs(mlir): :memo: update examples in QCOOps.td
DRovara Jan 9, 2026
ce1ae87
refactor(mlir): :rotating_light: fix some linter issues
DRovara Jan 9, 2026
b88acbd
refactor(mlir): :rotating_light: further linter issue fixes
DRovara Jan 9, 2026
e6a09e1
refactor(mlir): :rotating_light: fix more linter issues
DRovara Jan 9, 2026
becf786
refactor(mlir): :recycle: reuse numTargets in conversion
DRovara Jan 9, 2026
a92d2c4
refactor(mlir): :recycle: implement coderabbit suggestions
DRovara Jan 9, 2026
0059475
feat(mlir): :sparkles: implement lambda-based builder for QCO CtrlOps
DRovara Jan 9, 2026
6c190b5
fix(mlir): :bug: make `qco.ctrl` builder that take a `UnitaryOpInterf…
DRovara Jan 9, 2026
87dd322
chore(mlir): :recycle: add verification for the block argument type i…
DRovara Jan 9, 2026
dedbd64
test(mlir): :white_check_mark: add test for UnitaryOpInterface builde…
DRovara Jan 9, 2026
38a4046
test(mlir): :white_check_mark: add tests for CtrlOp verifier
DRovara Jan 9, 2026
c214261
refactor(mlir): :recycle: apply coderabbit recommendations
DRovara Jan 9, 2026
411b1d5
fix(mlir): :bug: fix datatypes of valueranges in test
DRovara Jan 9, 2026
d055ff1
refactor(mlir): :rotating_light: fix linter issues
DRovara Jan 9, 2026
98ddd5c
test(mlir): :white_check_mark: also test custom parsing
DRovara Jan 9, 2026
3c94d7b
refactor(mlir): :recycle: replace some `EXPECT` with `ASSERT` for cle…
DRovara Jan 9, 2026
2bbb63b
fix linter issues
DRovara Jan 10, 2026
d6c53a5
fix linter issues
DRovara Jan 10, 2026
eeb8b9a
🐛 body lambda function must not return ValueRange
burgholzer Jan 10, 2026
5f330fa
🎨 use `<OP>::create` over `builder.create` for better autocompletion …
burgholzer Jan 10, 2026
012e55f
🎨 micro-optimizations
burgholzer Jan 10, 2026
baff792
♻️ rework tests to rely more on the QCOProgramBuilder for more concis…
burgholzer Jan 10, 2026
d553b39
🐛 fix the QCOProgramBuilder convenience methods
burgholzer Jan 10, 2026
91474b1
🚨👌 Address clang-tidy warnings and CodeRabbit feedback
burgholzer Jan 10, 2026
d513e6b
🚨 fix missing header
burgholzer Jan 10, 2026
4a60744
👌 Address CodeRabbit feedback
burgholzer Jan 10, 2026
f1659bd
🎨 even more idiomatic MLIR code
burgholzer Jan 11, 2026
015e0b5
🚨 clang-tidy warnings
burgholzer Jan 11, 2026
3c2fae2
📝 add to changelog
burgholzer Jan 11, 2026
4fe36f2
👌 more CodeRabbit feedback
burgholzer Jan 11, 2026
20b3a7e
Merge branch 'main' into mlir/fix/ctrl-one-use
burgholzer Jan 11, 2026
631f3b3
👌 more CodeRabbit feedback
burgholzer Jan 11, 2026
a4311b3
👌 more CodeRabbit feedback
burgholzer Jan 11, 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
3 changes: 2 additions & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ 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]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**])
- ✨ Add initial infrastructure for new QC and QCO MLIR dialects ([#1264], [#1402], [#1428], [#1430], [#1436]) ([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**])

### Changed

Expand Down Expand Up @@ -308,6 +308,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool
<!-- PR links -->

[#1437]: https://github.com/munich-quantum-toolkit/core/pull/1437
[#1436]: https://github.com/munich-quantum-toolkit/core/pull/1436
[#1430]: https://github.com/munich-quantum-toolkit/core/pull/1430
[#1428]: https://github.com/munich-quantum-toolkit/core/pull/1428
[#1415]: https://github.com/munich-quantum-toolkit/core/pull/1415
Expand Down
13 changes: 7 additions & 6 deletions mlir/include/mlir/Dialect/QCO/Builder/QCOProgramBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
#include <cstdint>
#include <functional>
#include <llvm/ADT/DenseSet.h>
#include <llvm/ADT/STLFunctionalExtras.h>
#include <llvm/ADT/SmallVector.h>
#include <llvm/Support/ErrorHandling.h>
#include <mlir/IR/Builders.h>
Expand Down Expand Up @@ -988,21 +989,21 @@ class QCOProgramBuilder final : public OpBuilder {
* @par Example:
* ```c++
* {controls_out, targets_out} =
* builder.ctrl(q0_in, q1_in, [&](ValueRange targets) {
* auto q1_res = builder.x(targets[0]);
* return {q1_res};
* builder.ctrl(q0_in, q1_in,
* [&](ValueRange targets) -> llvm::SmallVector<Value> {
* return {builder.x(targets[0])};
* });
* ```
* ```mlir
* %controls_out, %targets_out = qco.ctrl(%q0_in) %q1_in {
* %q1_res = qco.x %q1_in : !qco.qubit -> !qco.qubit
* %controls_out, %targets_out = qco.ctrl(%q0_in) targets(%t = %q1_in) {
* %q1_res = qco.x %t : !qco.qubit -> !qco.qubit
* qco.yield %q1_res
* } : ({!qco.qubit}, {!qco.qubit}) -> ({!qco.qubit}, {!qco.qubit})
* ```
*/
std::pair<ValueRange, ValueRange>
ctrl(ValueRange controls, ValueRange targets,
const std::function<ValueRange(ValueRange)>& body);
llvm::function_ref<llvm::SmallVector<Value>(ValueRange)> body);

//===--------------------------------------------------------------------===//
// Deallocation
Expand Down
24 changes: 13 additions & 11 deletions mlir/include/mlir/Dialect/QCO/IR/QCOOps.td
Original file line number Diff line number Diff line change
Expand Up @@ -1025,10 +1025,10 @@ def YieldOp : QCOOp<"yield", traits = [Terminator]> {

Example:
```mlir
%ctrl_q_out, %tgt_q_out = qco.ctrl(%ctrl_q_in) %tgt_q_in {
%tgt_q_res = qco.h %tgt_q_in : !qco.qubit -> !qco.qubit
qco.yield %tgt_q_res : !qco.qubit
} : ({!qco.qubit}, {!qco.qubit}) -> ({!qco.qubit}, {!qco.qubit})
%res_ctrl, %res_tgt:2 = qco.ctrl(%ctrl) targets(%a0 = %q0, %a1 = %q1) {
%a0_1, %a1_1 = qco.swap %a0, %a1 : !qco.qubit, !qco.qubit -> !qco.qubit, !qco.qubit
qco.yield %a0_1, %a1_1
} : ({!qco.qubit}, {!qco.qubit, !qco.qubit}) -> ({!qco.qubit}, {!qco.qubit, !qco.qubit})
```
}];

Expand Down Expand Up @@ -1057,10 +1057,10 @@ def CtrlOp : QCOOp<"ctrl", traits =

Example:
```mlir
%ctrl_q_out, %tgt_q_out = qco.ctrl(%ctrl_q_in) %tgt_q_in {
%tgt_q_res = qco.h %tgt_q_in : !qco.qubit -> !qco.qubit
qco.yield %tgt_q_res : !qco.qubit
} : ({!qco.qubit}, {!qco.qubit}) -> ({!qco.qubit}, {!qco.qubit})
%res_ctrl, %res_tgt:2 = qco.ctrl(%ctrl) targets(%a0 = %q0, %a1 = %q1) {
%a0_1, %a1_1 = qco.swap %a0, %a1 : !qco.qubit, !qco.qubit -> !qco.qubit, !qco.qubit
qco.yield %a0_1, %a1_1
} : ({!qco.qubit}, {!qco.qubit, !qco.qubit}) -> ({!qco.qubit}, {!qco.qubit, !qco.qubit})
```
}];

Expand All @@ -1069,8 +1069,10 @@ def CtrlOp : QCOOp<"ctrl", traits =
let results = (outs Variadic<QubitType>:$controls_out, Variadic<QubitType>:$targets_out);
let regions = (region SizedRegion<1>:$region);
let assemblyFormat = [{
`(` $controls_in `)` $targets_in
$region attr-dict `:`
`(` $controls_in `)`
`targets`
custom<TargetAliasing>($region, $targets_in)
attr-dict `:`
`(` `{` type($controls_in) `}` ( `,` `{` type($targets_in)^ `}` )? `)`
`->`
`(` `{` type($controls_out) `}` ( `,` `{` type($targets_out)^ `}` )? `)`
Expand Down Expand Up @@ -1099,7 +1101,7 @@ def CtrlOp : QCOOp<"ctrl", traits =
build($_builder, $_state, controls.getTypes(), targets.getTypes(), controls, targets);
}]>,
OpBuilder<(ins "ValueRange":$controls, "ValueRange":$targets, "UnitaryOpInterface":$bodyUnitary)>,
OpBuilder<(ins "ValueRange":$controls, "ValueRange":$targets, "const std::function<ValueRange(ValueRange)>&":$bodyBuilder)>
OpBuilder<(ins "ValueRange":$controls, "ValueRange":$targets, "llvm::function_ref<llvm::SmallVector<Value>(ValueRange)>":$bodyBuilder)>
];

let hasCanonicalizer = 1;
Expand Down
29 changes: 27 additions & 2 deletions mlir/lib/Conversion/QCOToQC/QCOToQC.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -774,12 +774,37 @@ struct ConvertQCOCtrlOp final : OpConversionPattern<qco::CtrlOp> {
const auto& qcControls = adaptor.getControlsIn();

// Create qc.ctrl operation
auto qcoOp = rewriter.create<qc::CtrlOp>(op.getLoc(), qcControls);
auto qcOp = qc::CtrlOp::create(rewriter, op.getLoc(), qcControls);

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

auto& entryBlock = dstRegion.front();
const auto numArgs = entryBlock.getNumArguments();
if (adaptor.getTargetsIn().size() != numArgs) {
return op.emitOpError() << "qco.ctrl: entry block args (" << numArgs
<< ") must match number of target operands ("
<< adaptor.getTargetsIn().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.getTargetsIn()[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());

Expand Down
19 changes: 17 additions & 2 deletions mlir/lib/Conversion/QCToQCO/QCToQCO.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1106,7 +1106,7 @@ struct ConvertQCCtrlOp final : StatefulOpConversionPattern<qc::CtrlOp> {

// Create qco.ctrl
auto qcoOp =
rewriter.create<qco::CtrlOp>(op.getLoc(), qcoControls, qcoTargets);
qco::CtrlOp::create(rewriter, op.getLoc(), qcoControls, qcoTargets);

// Update the state map if this is a top-level CtrlOp
// Nested CtrlOps are managed via the targetsIn and targetsOut maps
Expand All @@ -1124,12 +1124,27 @@ struct ConvertQCCtrlOp final : StatefulOpConversionPattern<qc::CtrlOp> {

// Update modifier information
state.inCtrlOp++;
state.targetsIn.try_emplace(state.inCtrlOp, qcoTargets);

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

// Create block arguments for target qubits and store them in
// `state.targetsIn`.
auto& entryBlock = dstRegion.front();
assert(entryBlock.getNumArguments() == 0 &&
"QC ctrl region unexpectedly has entry block arguments");
SmallVector<Value> qcoTargetAliases;
qcoTargetAliases.reserve(numTargets);
const auto qubitType = qco::QubitType::get(qcoOp.getContext());
const auto opLoc = op.getLoc();
rewriter.modifyOpInPlace(qcoOp, [&] {
for (auto i = 0UL; i < numTargets; i++) {
qcoTargetAliases.emplace_back(entryBlock.addArgument(qubitType, opLoc));
}
});
state.targetsIn[state.inCtrlOp] = std::move(qcoTargetAliases);

rewriter.eraseOp(op);
return success();
}
Expand Down
Loading
Loading