From 00ad822d53712971c499ec26b85c79b0724a6b56 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Mon, 4 May 2026 09:13:00 +0200 Subject: [PATCH 01/24] Add SWAPAbsorption Pass --- .../mlir/Dialect/QCO/Transforms/Passes.td | 6 + .../Optimizations/SwapAbsorption.cpp | 124 ++++++++++++++++++ .../Transforms/Optimizations/CMakeLists.txt | 2 +- .../Optimizations/test_swapabsorption.cpp | 84 ++++++++++++ 4 files changed, 215 insertions(+), 1 deletion(-) create mode 100644 mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp create mode 100644 mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index edca59797e..9109be2f5c 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -101,4 +101,10 @@ def MappingPass : Pass<"place-and-route", "mlir::ModuleOp"> { "The number of inserted SWAPs">]; } +def SwapAbsorptionPass : Pass<"absorb-swaps", "mlir::ModuleOp"> { + let dependentDialects = ["mlir::qco::QCODialect"]; + let summary = ""; + let options = []; +} + #endif // MLIR_DIALECT_QCO_TRANSFORMS_PASSES_TD diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp new file mode 100644 index 0000000000..f89ed1617b --- /dev/null +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -0,0 +1,124 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" +#include "mlir/Dialect/QCO/Utils/Drivers.h" +#include "mlir/Dialect/QCO/Utils/WireIterator.h" +#include "mlir/Dialect/QTensor/IR/QTensorOps.h" + +#include +#include +#include +#include +#include +#include +#include +#include + +namespace mlir::qco { +#define GEN_PASS_DEF_SWAPABSORPTIONPASS +#include "mlir/Dialect/QCO/Transforms/Passes.h.inc" + +namespace { +struct SwapAbsorption : impl::SwapAbsorptionPassBase { +public: + using SwapAbsorptionPassBase::SwapAbsorptionPassBase; + + void runOnOperation() override { + ModuleOp anchor = getOperation(); + IRRewriter rewriter(&getContext()); + insertStatics(anchor, rewriter); + + for (auto func : anchor.getOps()) { + SmallVector wires; + for (auto op : func.getOps()) { + wires.emplace_back(op.getQubit()); + } + + SmallVector readyToAbsorb; + readyToAbsorb.reserve((wires.size() + 1) / 2); + + std::ignore = walkProgramGraph( + wires, [&](const ReadyRange& ready, ReleasedOps& released) { + for (const auto& [op, indices] : ready) { + if (isa(op)) { + readyToAbsorb.emplace_back(op); + } + released.emplace_back(op); + } + return WalkResult::interrupt(); + }); + + for (auto swapOp : readyToAbsorb) { + auto in0 = swapOp.getQubit0In(); + auto in1 = swapOp.getQubit1In(); + + auto out0 = swapOp.getQubit0Out(); + auto out1 = swapOp.getQubit1Out(); + + // TODO: What if single qubit gates are in front of the SWAP input? + // Tipp: Use the WireIterator. + StaticOp op0 = cast(in0.getDefiningOp()); + StaticOp op1 = cast(in1.getDefiningOp()); + + rewriter.replaceAllUsesWith(out0, op1.getQubit()); + rewriter.replaceAllUsesWith(out1, op0.getQubit()); + rewriter.eraseOp(swapOp); + } + } + } + +private: + static void insertStatics(ModuleOp anchor, IRRewriter& rewriter) { + for (auto func : anchor.getOps()) { + SmallVector worklist; + for (Operation& op : func.getOps()) { + worklist.emplace_back(&op); + } + + std::size_t n = llvm::range_size(func.getOps()) - 1; + for (Operation* op : llvm::reverse(worklist)) { + rewriter.setInsertionPoint(op); + + if (auto tensorDealloc = dyn_cast(op)) { + rewriter.eraseOp(tensorDealloc); + continue; + } + + if (auto tensorInsert = dyn_cast(op)) { + auto q = tensorInsert.getScalar(); + rewriter.create(rewriter.getUnknownLoc(), q); + rewriter.eraseOp(tensorInsert); + continue; + } + + if (auto tensorExtract = dyn_cast(op)) { + auto q = tensorExtract.getResult(); + auto staticOp = + rewriter.create(rewriter.getUnknownLoc(), n); + rewriter.replaceAllUsesWith(q, staticOp.getQubit()); + rewriter.eraseOp(tensorExtract); + n--; + continue; + } + + if (auto tensorAlloc = dyn_cast(op)) { + rewriter.eraseOp(tensorAlloc); + continue; + } + } + } + } +}; +} // namespace +} // namespace mlir::qco \ No newline at end of file diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt index 73606c2efb..7a19471e2d 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt @@ -7,7 +7,7 @@ # Licensed under the MIT License set(target_name mqt-core-mlir-unittest-optimizations) -add_executable(${target_name} test_qco_merge_single_qubit_rotation.cpp) +add_executable(${target_name} test_swapabsorption.cpp test_qco_merge_single_qubit_rotation.cpp) target_link_libraries( ${target_name} diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp new file mode 100644 index 0000000000..331363f756 --- /dev/null +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2023 - 2026 Chair for Design Automation, TUM + * Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH + * All rights reserved. + * + * SPDX-License-Identifier: MIT + * + * Licensed under the MIT License + */ + +#include "mlir/Dialect/QC/IR/QCDialect.h" +#include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" +#include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/Transforms/Passes.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using namespace mlir; +using namespace mlir::qco; + +namespace { + +class SwapAbsorbPassTest : public testing::Test { + +protected: + void SetUp() override { + // Register all necessary dialects + DialectRegistry registry; + registry.insert(); + context = std::make_unique(); + context->appendDialectRegistry(registry); + context->loadAllAvailableDialects(); + } + + static void applySwapAbsorb(OwningOpRef& moduleOp) { + PassManager pm(moduleOp->getContext()); + pm.addPass(qco::createSwapAbsorptionPass()); + auto res = pm.run(*moduleOp); + + ASSERT_TRUE(succeeded(res)); + } + + std::unique_ptr context; +}; +}; // namespace + +TEST_F(SwapAbsorbPassTest, PassDoesNotChangeSwaplessProgram) { + + qco::QCOProgramBuilder builder(context.get()); + builder.initialize(); + + const auto q00 = builder.allocQubit(); + const auto q10 = builder.allocQubit(); + const auto q20 = builder.allocQubit(); + + const auto q01 = builder.h(q00); + const auto [q02, q11] = builder.cx(q01, q10); + + builder.sink(q02); + builder.sink(q11); + + auto moduleThroughPass = builder.finalize(); + auto originalModule = moduleThroughPass->clone(); + + applySwapAbsorb(moduleThroughPass); + ASSERT_TRUE(mlir::OperationEquivalence::isEquivalentTo( + moduleThroughPass.get(), originalModule, + mlir::OperationEquivalence::Flags::IgnoreLocations)); +} \ No newline at end of file From 77250d1445847f25423390fa60a3f08bdc91ffe1 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 07:15:07 +0000 Subject: [PATCH 02/24] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp | 2 +- .../Dialect/QCO/Transforms/Optimizations/CMakeLists.txt | 2 +- .../QCO/Transforms/Optimizations/test_swapabsorption.cpp | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp index f89ed1617b..a7864ed7ca 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -121,4 +121,4 @@ struct SwapAbsorption : impl::SwapAbsorptionPassBase { } }; } // namespace -} // namespace mlir::qco \ No newline at end of file +} // namespace mlir::qco diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt index 7a19471e2d..efdc904899 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt @@ -7,7 +7,7 @@ # Licensed under the MIT License set(target_name mqt-core-mlir-unittest-optimizations) -add_executable(${target_name} test_swapabsorption.cpp test_qco_merge_single_qubit_rotation.cpp) +add_executable(${target_name} test_qco_merge_single_qubit_rotation.cpp test_swapabsorption.cpp) target_link_libraries( ${target_name} diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp index 331363f756..d52c2cc17f 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp @@ -81,4 +81,4 @@ TEST_F(SwapAbsorbPassTest, PassDoesNotChangeSwaplessProgram) { ASSERT_TRUE(mlir::OperationEquivalence::isEquivalentTo( moduleThroughPass.get(), originalModule, mlir::OperationEquivalence::Flags::IgnoreLocations)); -} \ No newline at end of file +} From 515e6bfa2d6163ebf4d18798a5f10d604ab939d2 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Mon, 4 May 2026 09:32:30 +0200 Subject: [PATCH 03/24] Fix linting --- .../QCO/Transforms/Optimizations/SwapAbsorption.cpp | 8 ++++---- .../QCO/Transforms/Optimizations/test_swapabsorption.cpp | 6 +----- 2 files changed, 5 insertions(+), 9 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp index f89ed1617b..4cfd052d9c 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -8,8 +8,6 @@ * Licensed under the MIT License */ -#include "mlir/Dialect/QCO/IR/QCODialect.h" -#include "mlir/Dialect/QCO/IR/QCOInterfaces.h" #include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Dialect/QCO/Utils/Drivers.h" @@ -17,7 +15,6 @@ #include "mlir/Dialect/QTensor/IR/QTensorOps.h" #include -#include #include #include #include @@ -25,6 +22,9 @@ #include #include +#include +#include + namespace mlir::qco { #define GEN_PASS_DEF_SWAPABSORPTIONPASS #include "mlir/Dialect/QCO/Transforms/Passes.h.inc" @@ -33,7 +33,7 @@ namespace { struct SwapAbsorption : impl::SwapAbsorptionPassBase { public: using SwapAbsorptionPassBase::SwapAbsorptionPassBase; - +protected: void runOnOperation() override { ModuleOp anchor = getOperation(); IRRewriter rewriter(&getContext()); diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp index 331363f756..0fdc921b58 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp @@ -8,24 +8,21 @@ * Licensed under the MIT License */ -#include "mlir/Dialect/QC/IR/QCDialect.h" #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" #include -#include -#include #include #include #include #include #include +#include #include #include #include #include -#include #include #include @@ -66,7 +63,6 @@ TEST_F(SwapAbsorbPassTest, PassDoesNotChangeSwaplessProgram) { const auto q00 = builder.allocQubit(); const auto q10 = builder.allocQubit(); - const auto q20 = builder.allocQubit(); const auto q01 = builder.h(q00); const auto [q02, q11] = builder.cx(q01, q10); From b0474ff3e284575959d3fdcd31c1d130976498c0 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 4 May 2026 07:33:05 +0000 Subject: [PATCH 04/24] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp index 394d8328b3..ce02a636ab 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -22,8 +22,8 @@ #include #include -#include #include +#include namespace mlir::qco { #define GEN_PASS_DEF_SWAPABSORPTIONPASS @@ -33,6 +33,7 @@ namespace { struct SwapAbsorption : impl::SwapAbsorptionPassBase { public: using SwapAbsorptionPassBase::SwapAbsorptionPassBase; + protected: void runOnOperation() override { ModuleOp anchor = getOperation(); From 10302c52bd6b0d66c8b3e533e5e4aacb3c369736 Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Mon, 4 May 2026 21:55:14 +0200 Subject: [PATCH 05/24] remove SwapAbsorbtion::insertStatics() --- .../Optimizations/SwapAbsorption.cpp | 43 ------------------- 1 file changed, 43 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp index ce02a636ab..c98b7428df 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -38,7 +38,6 @@ struct SwapAbsorption : impl::SwapAbsorptionPassBase { void runOnOperation() override { ModuleOp anchor = getOperation(); IRRewriter rewriter(&getContext()); - insertStatics(anchor, rewriter); for (auto func : anchor.getOps()) { SmallVector wires; @@ -78,48 +77,6 @@ struct SwapAbsorption : impl::SwapAbsorptionPassBase { } } } - -private: - static void insertStatics(ModuleOp anchor, IRRewriter& rewriter) { - for (auto func : anchor.getOps()) { - SmallVector worklist; - for (Operation& op : func.getOps()) { - worklist.emplace_back(&op); - } - - std::size_t n = llvm::range_size(func.getOps()) - 1; - for (Operation* op : llvm::reverse(worklist)) { - rewriter.setInsertionPoint(op); - - if (auto tensorDealloc = dyn_cast(op)) { - rewriter.eraseOp(tensorDealloc); - continue; - } - - if (auto tensorInsert = dyn_cast(op)) { - auto q = tensorInsert.getScalar(); - rewriter.create(rewriter.getUnknownLoc(), q); - rewriter.eraseOp(tensorInsert); - continue; - } - - if (auto tensorExtract = dyn_cast(op)) { - auto q = tensorExtract.getResult(); - auto staticOp = - rewriter.create(rewriter.getUnknownLoc(), n); - rewriter.replaceAllUsesWith(q, staticOp.getQubit()); - rewriter.eraseOp(tensorExtract); - n--; - continue; - } - - if (auto tensorAlloc = dyn_cast(op)) { - rewriter.eraseOp(tensorAlloc); - continue; - } - } - } - } }; } // namespace } // namespace mlir::qco From da710c700d9e6f90f8b667c116dbc15dd48c9b70 Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Mon, 4 May 2026 21:57:00 +0200 Subject: [PATCH 06/24] change unittests to use static qubits --- .../QCO/Transforms/Optimizations/test_swapabsorption.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp index b2f529a1a8..880810439a 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp @@ -61,9 +61,9 @@ TEST_F(SwapAbsorbPassTest, PassDoesNotChangeSwaplessProgram) { qco::QCOProgramBuilder builder(context.get()); builder.initialize(); - const auto q00 = builder.allocQubit(); - const auto q10 = builder.allocQubit(); - + const auto q00 = builder.staticQubit(0); + const auto q10 = builder.staticQubit(1); + const auto q01 = builder.h(q00); const auto [q02, q11] = builder.cx(q01, q10); From d793c7392105c6132a0677e034749ff2f87d0b29 Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Tue, 5 May 2026 21:59:46 +0200 Subject: [PATCH 07/24] SwapAbsorbtion: PassReordersTwoQubitCircuitWithLeadingSwap --- .../Optimizations/test_swapabsorption.cpp | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp index 880810439a..70dee8cafb 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp @@ -10,6 +10,7 @@ #include "mlir/Dialect/QCO/Builder/QCOProgramBuilder.h" #include "mlir/Dialect/QCO/IR/QCODialect.h" +#include "mlir/Dialect/QCO/IR/QCOOps.h" #include "mlir/Dialect/QCO/Transforms/Passes.h" #include @@ -78,3 +79,26 @@ TEST_F(SwapAbsorbPassTest, PassDoesNotChangeSwaplessProgram) { moduleThroughPass.get(), originalModule, mlir::OperationEquivalence::Flags::IgnoreLocations)); } + +TEST_F(SwapAbsorbPassTest, PassReordersTwoQubitCircuitWithLeadingSwap) { + + qco::QCOProgramBuilder builder(context.get()); + builder.initialize(); + + const auto q00 = builder.staticQubit(0); + const auto q10 = builder.staticQubit(1); + + const auto [q01, q11] = builder.swap(q00, q10); + + const auto q02 = builder.id(q01); + const auto q12 = builder.id(q11); + + builder.sink(q02); + builder.sink(q12); + + auto moduleThroughPass = builder.finalize(); + applySwapAbsorb(moduleThroughPass); + + ASSERT_EQ(q10, ((IdOp)q02.getDefiningOp()).getInputQubit(0)); + ASSERT_EQ(q00, ((IdOp)q12.getDefiningOp()).getInputQubit(0)); +} From de8941126256305aa3cfa02f84eb572f6606d797 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 6 May 2026 05:40:14 +0000 Subject: [PATCH 08/24] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../QCO/Transforms/Optimizations/test_swapabsorption.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp index 70dee8cafb..5b0554846d 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp @@ -64,7 +64,7 @@ TEST_F(SwapAbsorbPassTest, PassDoesNotChangeSwaplessProgram) { const auto q00 = builder.staticQubit(0); const auto q10 = builder.staticQubit(1); - + const auto q01 = builder.h(q00); const auto [q02, q11] = builder.cx(q01, q10); @@ -87,7 +87,7 @@ TEST_F(SwapAbsorbPassTest, PassReordersTwoQubitCircuitWithLeadingSwap) { const auto q00 = builder.staticQubit(0); const auto q10 = builder.staticQubit(1); - + const auto [q01, q11] = builder.swap(q00, q10); const auto q02 = builder.id(q01); From 3dbe1a321ef82a51af492a986b174eadc08396ee Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Wed, 6 May 2026 22:21:10 +0200 Subject: [PATCH 09/24] swap-absorb: PassAbsorbsTwoIndependentSwaps --- .../Optimizations/test_swapabsorption.cpp | 32 +++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp index 5b0554846d..64ebace4e3 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp @@ -102,3 +102,35 @@ TEST_F(SwapAbsorbPassTest, PassReordersTwoQubitCircuitWithLeadingSwap) { ASSERT_EQ(q10, ((IdOp)q02.getDefiningOp()).getInputQubit(0)); ASSERT_EQ(q00, ((IdOp)q12.getDefiningOp()).getInputQubit(0)); } + +TEST_F(SwapAbsorbPassTest, PassAbsorbsTwoIndependentSwaps) { + + qco::QCOProgramBuilder builder(context.get()); + builder.initialize(); + + const auto q00 = builder.staticQubit(0); + const auto q10 = builder.staticQubit(1); + const auto q20 = builder.staticQubit(2); + const auto q30 = builder.staticQubit(3); + + const auto [q01, q11] = builder.swap(q00, q10); + const auto [q21, q31] = builder.swap(q20, q30); + + const auto q02 = builder.id(q01); + const auto q12 = builder.id(q11); + const auto q22 = builder.id(q21); + const auto q32 = builder.id(q31); + + builder.sink(q02); + builder.sink(q12); + builder.sink(q22); + builder.sink(q32); + + auto moduleThroughPass = builder.finalize(); + applySwapAbsorb(moduleThroughPass); + + ASSERT_EQ(q10, ((IdOp)q02.getDefiningOp()).getInputQubit(0)); + ASSERT_EQ(q00, ((IdOp)q12.getDefiningOp()).getInputQubit(0)); + ASSERT_EQ(q30, ((IdOp)q22.getDefiningOp()).getInputQubit(0)); + ASSERT_EQ(q20, ((IdOp)q32.getDefiningOp()).getInputQubit(0)); +} \ No newline at end of file From 3b2af5de4f89eeb7245d6b03004a9b2b95703138 Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Wed, 27 May 2026 17:45:53 +0200 Subject: [PATCH 10/24] remove unused includes --- .../Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp index c98b7428df..4a0ac4f57d 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -12,7 +12,6 @@ #include "mlir/Dialect/QCO/Transforms/Passes.h" #include "mlir/Dialect/QCO/Utils/Drivers.h" #include "mlir/Dialect/QCO/Utils/WireIterator.h" -#include "mlir/Dialect/QTensor/IR/QTensorOps.h" #include #include @@ -22,9 +21,6 @@ #include #include -#include -#include - namespace mlir::qco { #define GEN_PASS_DEF_SWAPABSORPTIONPASS #include "mlir/Dialect/QCO/Transforms/Passes.h.inc" From 6312c37d1f0417d1d8a27b6823a1eba032366b93 Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Wed, 27 May 2026 17:48:50 +0200 Subject: [PATCH 11/24] PassAbsorbsSwapWithLeadingSingleQubitGates --- .../Optimizations/SwapAbsorption.cpp | 9 ++----- .../Optimizations/test_swapabsorption.cpp | 26 +++++++++++++++++++ 2 files changed, 28 insertions(+), 7 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp index 4a0ac4f57d..4551662636 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -62,13 +62,8 @@ struct SwapAbsorption : impl::SwapAbsorptionPassBase { auto out0 = swapOp.getQubit0Out(); auto out1 = swapOp.getQubit1Out(); - // TODO: What if single qubit gates are in front of the SWAP input? - // Tipp: Use the WireIterator. - StaticOp op0 = cast(in0.getDefiningOp()); - StaticOp op1 = cast(in1.getDefiningOp()); - - rewriter.replaceAllUsesWith(out0, op1.getQubit()); - rewriter.replaceAllUsesWith(out1, op0.getQubit()); + rewriter.replaceAllUsesWith(out0, in1); + rewriter.replaceAllUsesWith(out1, in0); rewriter.eraseOp(swapOp); } } diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp index 64ebace4e3..557f640a1e 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp @@ -133,4 +133,30 @@ TEST_F(SwapAbsorbPassTest, PassAbsorbsTwoIndependentSwaps) { ASSERT_EQ(q00, ((IdOp)q12.getDefiningOp()).getInputQubit(0)); ASSERT_EQ(q30, ((IdOp)q22.getDefiningOp()).getInputQubit(0)); ASSERT_EQ(q20, ((IdOp)q32.getDefiningOp()).getInputQubit(0)); +} + +TEST_F(SwapAbsorbPassTest, PassAbsorbsSwapWithLeadingSingleQubitGates) { + + qco::QCOProgramBuilder builder(context.get()); + builder.initialize(); + + const auto q00 = builder.staticQubit(0); + const auto q10 = builder.staticQubit(1); + + const auto q01 = builder.id(q00); + const auto q11 = builder.id(q10); + + const auto [q02, q12] = builder.swap(q01, q11); + + const auto q03 = builder.id(q02); + const auto q13 = builder.id(q12); + + builder.sink(q03); + builder.sink(q13); + + auto moduleThroughPass = builder.finalize(); + applySwapAbsorb(moduleThroughPass); + + ASSERT_EQ(q11, ((IdOp)q03.getDefiningOp()).getInputQubit(0)); + ASSERT_EQ(q01, ((IdOp)q13.getDefiningOp()).getInputQubit(0)); } \ No newline at end of file From 1a884d8485e0ab0d370105f25f39835ab0cef5ea Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 28 May 2026 05:58:14 +0000 Subject: [PATCH 12/24] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../Dialect/QCO/Transforms/Optimizations/CMakeLists.txt | 7 +++---- .../QCO/Transforms/Optimizations/test_swapabsorption.cpp | 2 +- 2 files changed, 4 insertions(+), 5 deletions(-) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt index 229d57495c..47a33d0b01 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/CMakeLists.txt @@ -7,10 +7,9 @@ # Licensed under the MIT License set(target_name mqt-core-mlir-unittest-optimizations) -add_executable(${target_name} test_qco_hadamard_lifting.cpp - test_qco_merge_single_qubit_rotation.cpp - test_quantum_loop_unroll.cpp - test_swapabsorption.cpp) +add_executable( + ${target_name} test_qco_hadamard_lifting.cpp test_qco_merge_single_qubit_rotation.cpp + test_quantum_loop_unroll.cpp test_swapabsorption.cpp) target_link_libraries( ${target_name} diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp index 557f640a1e..bfe4c1f5ec 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp @@ -159,4 +159,4 @@ TEST_F(SwapAbsorbPassTest, PassAbsorbsSwapWithLeadingSingleQubitGates) { ASSERT_EQ(q11, ((IdOp)q03.getDefiningOp()).getInputQubit(0)); ASSERT_EQ(q01, ((IdOp)q13.getDefiningOp()).getInputQubit(0)); -} \ No newline at end of file +} From c4bb64fb8e72cb9d86cdc0c8ea3e7b9b4863d30a Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Thu, 28 May 2026 21:41:07 +0200 Subject: [PATCH 13/24] PassAbsorbsTwoDependentSwaps --- .../Optimizations/SwapAbsorption.cpp | 54 +++++++++++-------- .../Optimizations/test_swapabsorption.cpp | 28 ++++++++++ 2 files changed, 61 insertions(+), 21 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp index 4551662636..29a7c37646 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -13,11 +13,9 @@ #include "mlir/Dialect/QCO/Utils/Drivers.h" #include "mlir/Dialect/QCO/Utils/WireIterator.h" -#include #include #include #include -#include #include #include @@ -43,30 +41,44 @@ struct SwapAbsorption : impl::SwapAbsorptionPassBase { SmallVector readyToAbsorb; readyToAbsorb.reserve((wires.size() + 1) / 2); + findSwapsReadyForAbsorption(wires, readyToAbsorb); - std::ignore = walkProgramGraph( - wires, [&](const ReadyRange& ready, ReleasedOps& released) { - for (const auto& [op, indices] : ready) { - if (isa(op)) { - readyToAbsorb.emplace_back(op); - } - released.emplace_back(op); + do { + for (auto swapOp : readyToAbsorb) { + absorbSingleSwap(swapOp, rewriter); + } + readyToAbsorb.clear(); + findSwapsReadyForAbsorption(wires, readyToAbsorb); + } + while(!readyToAbsorb.empty()); + } + } + +private: + void findSwapsReadyForAbsorption(SmallVector wires, SmallVector &readyToAbsorb) + { + std::ignore = walkProgramGraph( + wires, [&](const ReadyRange& ready, ReleasedOps& released) { + for (const auto& [op, indices] : ready) { + if (isa(op)) { + readyToAbsorb.emplace_back(op); } - return WalkResult::interrupt(); - }); + released.emplace_back(op); + } + return WalkResult::interrupt(); + }); + } - for (auto swapOp : readyToAbsorb) { - auto in0 = swapOp.getQubit0In(); - auto in1 = swapOp.getQubit1In(); + void absorbSingleSwap(SWAPOp swapOp, IRRewriter &rewriter) { + auto in0 = swapOp.getQubit0In(); + auto in1 = swapOp.getQubit1In(); - auto out0 = swapOp.getQubit0Out(); - auto out1 = swapOp.getQubit1Out(); + auto out0 = swapOp.getQubit0Out(); + auto out1 = swapOp.getQubit1Out(); - rewriter.replaceAllUsesWith(out0, in1); - rewriter.replaceAllUsesWith(out1, in0); - rewriter.eraseOp(swapOp); - } - } + rewriter.replaceAllUsesWith(out0, in1); + rewriter.replaceAllUsesWith(out1, in0); + rewriter.eraseOp(swapOp); } }; } // namespace diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp index bfe4c1f5ec..fc81ffe748 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp @@ -160,3 +160,31 @@ TEST_F(SwapAbsorbPassTest, PassAbsorbsSwapWithLeadingSingleQubitGates) { ASSERT_EQ(q11, ((IdOp)q03.getDefiningOp()).getInputQubit(0)); ASSERT_EQ(q01, ((IdOp)q13.getDefiningOp()).getInputQubit(0)); } + +TEST_F(SwapAbsorbPassTest, PassAbsorbsTwoDependentSwaps) { + + qco::QCOProgramBuilder builder(context.get()); + builder.initialize(); + + const auto q00 = builder.staticQubit(0); + const auto q10 = builder.staticQubit(1); + const auto q20 = builder.staticQubit(2); + + const auto [q01, q11] = builder.swap(q00, q10); + const auto [q12, q21] = builder.swap(q11, q20); + + const auto q02 = builder.id(q01); + const auto q13 = builder.id(q12); + const auto q22 = builder.id(q21); + + builder.sink(q02); + builder.sink(q13); + builder.sink(q22); + + auto moduleThroughPass = builder.finalize(); + applySwapAbsorb(moduleThroughPass); + + ASSERT_EQ(q20, ((IdOp)q13.getDefiningOp()).getInputQubit(0)); + ASSERT_EQ(q00, ((IdOp)q22.getDefiningOp()).getInputQubit(0)); + ASSERT_EQ(q10, ((IdOp)q02.getDefiningOp()).getInputQubit(0)); +} From a7793613287bda29d1842325d8cf9556f5e91353 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Thu, 28 May 2026 19:41:42 +0000 Subject: [PATCH 14/24] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../QCO/Transforms/Optimizations/SwapAbsorption.cpp | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp index 29a7c37646..f9bb8d583d 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -49,14 +49,13 @@ struct SwapAbsorption : impl::SwapAbsorptionPassBase { } readyToAbsorb.clear(); findSwapsReadyForAbsorption(wires, readyToAbsorb); - } - while(!readyToAbsorb.empty()); + } while (!readyToAbsorb.empty()); } } private: - void findSwapsReadyForAbsorption(SmallVector wires, SmallVector &readyToAbsorb) - { + void findSwapsReadyForAbsorption(SmallVector wires, + SmallVector& readyToAbsorb) { std::ignore = walkProgramGraph( wires, [&](const ReadyRange& ready, ReleasedOps& released) { for (const auto& [op, indices] : ready) { @@ -69,7 +68,7 @@ struct SwapAbsorption : impl::SwapAbsorptionPassBase { }); } - void absorbSingleSwap(SWAPOp swapOp, IRRewriter &rewriter) { + void absorbSingleSwap(SWAPOp swapOp, IRRewriter& rewriter) { auto in0 = swapOp.getQubit0In(); auto in1 = swapOp.getQubit1In(); From 3bf15be56daa3d4f91eaddce969f619aa84a88b1 Mon Sep 17 00:00:00 2001 From: Johannes Moosburger Date: Fri, 29 May 2026 14:39:40 +0200 Subject: [PATCH 15/24] fix some lint warnings --- .../Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp index 29a7c37646..07a31bc1d4 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -19,6 +19,8 @@ #include #include +#include + namespace mlir::qco { #define GEN_PASS_DEF_SWAPABSORPTIONPASS #include "mlir/Dialect/QCO/Transforms/Passes.h.inc" @@ -55,7 +57,7 @@ struct SwapAbsorption : impl::SwapAbsorptionPassBase { } private: - void findSwapsReadyForAbsorption(SmallVector wires, SmallVector &readyToAbsorb) + static void findSwapsReadyForAbsorption(SmallVector wires, SmallVector &readyToAbsorb) { std::ignore = walkProgramGraph( wires, [&](const ReadyRange& ready, ReleasedOps& released) { @@ -69,7 +71,7 @@ struct SwapAbsorption : impl::SwapAbsorptionPassBase { }); } - void absorbSingleSwap(SWAPOp swapOp, IRRewriter &rewriter) { + static void absorbSingleSwap(SWAPOp swapOp, IRRewriter &rewriter) { auto in0 = swapOp.getQubit0In(); auto in1 = swapOp.getQubit1In(); From 03917b6f0ffdd578bd1275fbc6048903ed4e83a2 Mon Sep 17 00:00:00 2001 From: Daniel Haag <121057143+denialhaag@users.noreply.github.com> Date: Fri, 29 May 2026 15:23:37 +0200 Subject: [PATCH 16/24] Fix linter error --- .../Optimizations/SwapAbsorption.cpp | 20 +++++++++---------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp index 83ea451460..5bc2155727 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -36,27 +36,25 @@ struct SwapAbsorption : impl::SwapAbsorptionPassBase { IRRewriter rewriter(&getContext()); for (auto func : anchor.getOps()) { - SmallVector wires; - for (auto op : func.getOps()) { - wires.emplace_back(op.getQubit()); - } - SmallVector readyToAbsorb; - readyToAbsorb.reserve((wires.size() + 1) / 2); - findSwapsReadyForAbsorption(wires, readyToAbsorb); - do { - for (auto swapOp : readyToAbsorb) { - absorbSingleSwap(swapOp, rewriter); + SmallVector wires; + for (auto op : func.getOps()) { + wires.emplace_back(op.getQubit()); } + readyToAbsorb.clear(); findSwapsReadyForAbsorption(wires, readyToAbsorb); + + for (auto swapOp : readyToAbsorb) { + absorbSingleSwap(swapOp, rewriter); + } } while (!readyToAbsorb.empty()); } } private: - static void findSwapsReadyForAbsorption(SmallVector wires, + static void findSwapsReadyForAbsorption(MutableArrayRef wires, SmallVector& readyToAbsorb) { std::ignore = walkProgramGraph( wires, [&](const ReadyRange& ready, ReleasedOps& released) { From 8948b78a08dcd255cfdda82362a4acbfc89a51ce Mon Sep 17 00:00:00 2001 From: Johannes Moosburger <96540096+jmoosburger@users.noreply.github.com> Date: Mon, 1 Jun 2026 15:55:40 +0200 Subject: [PATCH 17/24] add pass summary and description Signed-off-by: Johannes Moosburger <96540096+jmoosburger@users.noreply.github.com> --- mlir/include/mlir/Dialect/QCO/Transforms/Passes.td | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index 335e22ecfb..52ab24ac92 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -176,8 +176,19 @@ def HadamardLifting : Pass<"hadamard-lifting", "mlir::ModuleOp"> { def SwapAbsorptionPass : Pass<"absorb-swaps", "mlir::ModuleOp"> { let dependentDialects = ["mlir::qco::QCODialect"]; - let summary = ""; + let summary = "Eliminate SWAP operations by rewiring circuit"; let options = []; + let description = [{ + This pass removes SWAP operations by absorbing their effect into the surrounding circuit. + + For a SWAP operation exchanging qubits q0 and q1, the pass replaces all uses of the first output wire + with the second input wire and vice versa. As a result, the logical permutation introduced by the SWAP is + propagated through the IR and the SWAP operation itself can be removed. + + The pass repeatedly identifies SWAP operations that are ready to be processed and absorbs them one by one. + Each absorption may expose additional SWAP operations that can subsequently be eliminated. + This process continues until no further SWAP operations can be absorbed. + }]; } #endif // MLIR_DIALECT_QCO_TRANSFORMS_PASSES_TD From 982efb7e95f8975d78bde07af06b552bfc442510 Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Mon, 1 Jun 2026 13:57:25 +0000 Subject: [PATCH 18/24] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Dialect/QCO/Transforms/Passes.td | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index 52ab24ac92..689e36af1f 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -181,12 +181,12 @@ def SwapAbsorptionPass : Pass<"absorb-swaps", "mlir::ModuleOp"> { let description = [{ This pass removes SWAP operations by absorbing their effect into the surrounding circuit. - For a SWAP operation exchanging qubits q0 and q1, the pass replaces all uses of the first output wire - with the second input wire and vice versa. As a result, the logical permutation introduced by the SWAP is + For a SWAP operation exchanging qubits q0 and q1, the pass replaces all uses of the first output wire + with the second input wire and vice versa. As a result, the logical permutation introduced by the SWAP is propagated through the IR and the SWAP operation itself can be removed. The pass repeatedly identifies SWAP operations that are ready to be processed and absorbs them one by one. - Each absorption may expose additional SWAP operations that can subsequently be eliminated. + Each absorption may expose additional SWAP operations that can subsequently be eliminated. This process continues until no further SWAP operations can be absorbed. }]; } From 343387dd46a543a9bc93d14534d2dc6156da3844 Mon Sep 17 00:00:00 2001 From: Johannes Moosburger <96540096+jmoosburger@users.noreply.github.com> Date: Tue, 2 Jun 2026 11:13:38 +0200 Subject: [PATCH 19/24] remove empty pass options Co-authored-by: matthias Signed-off-by: Johannes Moosburger <96540096+jmoosburger@users.noreply.github.com> --- mlir/include/mlir/Dialect/QCO/Transforms/Passes.td | 1 - 1 file changed, 1 deletion(-) diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index 689e36af1f..6ffb4707e6 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -177,7 +177,6 @@ def HadamardLifting : Pass<"hadamard-lifting", "mlir::ModuleOp"> { def SwapAbsorptionPass : Pass<"absorb-swaps", "mlir::ModuleOp"> { let dependentDialects = ["mlir::qco::QCODialect"]; let summary = "Eliminate SWAP operations by rewiring circuit"; - let options = []; let description = [{ This pass removes SWAP operations by absorbing their effect into the surrounding circuit. From a108a9b7c87ec5a38cce575ef6051caad39ad2eb Mon Sep 17 00:00:00 2001 From: Johannes Moosburger <96540096+jmoosburger@users.noreply.github.com> Date: Tue, 2 Jun 2026 11:15:20 +0200 Subject: [PATCH 20/24] remove unnecessary comment Co-authored-by: matthias Signed-off-by: Johannes Moosburger <96540096+jmoosburger@users.noreply.github.com> --- .../Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp index fc81ffe748..60c317bb72 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp @@ -37,7 +37,6 @@ class SwapAbsorbPassTest : public testing::Test { protected: void SetUp() override { - // Register all necessary dialects DialectRegistry registry; registry.insert(); context = std::make_unique(); From 5b9c7f75d2a2d190cc251d41470e4ac59c096be7 Mon Sep 17 00:00:00 2001 From: Johannes Moosburger <96540096+jmoosburger@users.noreply.github.com> Date: Tue, 2 Jun 2026 11:20:32 +0200 Subject: [PATCH 21/24] enhance swap absobrtion according review Signed-off-by: Johannes Moosburger <96540096+jmoosburger@users.noreply.github.com> --- .../Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp index 5bc2155727..4d6376b25d 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -37,11 +37,15 @@ struct SwapAbsorption : impl::SwapAbsorptionPassBase { for (auto func : anchor.getOps()) { SmallVector readyToAbsorb; + SmallVector wires; do { - SmallVector wires; + wires.clear(); for (auto op : func.getOps()) { wires.emplace_back(op.getQubit()); } + if (wires.empty()) { + return; + } readyToAbsorb.clear(); findSwapsReadyForAbsorption(wires, readyToAbsorb); From 4d762f536720384f6026757e73db4dfddd4e35e4 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 3 Jun 2026 08:42:11 +0200 Subject: [PATCH 22/24] Update CHANGELOG.md [skip ci] --- CHANGELOG.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index dad0d3abf3..2b55203b2d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel ### Added +- ✨ Add a `absorb-swaps` pass for absorbing initial SWAPs ([#1750]) ([**@jmoosburger**], [**@MatthiasReumann**]) - 🚸 Add [CMake presets] to provide a standardized and reproducible way to configure builds ([#1660]) ([**@denialhaag**]) - ✨ Add a `quantum-loop-unroll` pass for unrolling for-loop operations containing quantum operations ([#1718]) ([**@MatthiasReumann**]) - ✨ Add a `hadamard-lifting` pass for lifting Hadamard gates above Pauli gates ([#1605]) ([**@lirem101**], [**@burgholzer**]) @@ -402,6 +403,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool +[#1750]: https://github.com/munich-quantum-toolkit/core/pull/1750 [#1749]: https://github.com/munich-quantum-toolkit/core/pull/1749 [#1748]: https://github.com/munich-quantum-toolkit/core/pull/1748 [#1737]: https://github.com/munich-quantum-toolkit/core/pull/1737 @@ -649,6 +651,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool [**@simon1hofmann**]: https://github.com/simon1hofmann [**@keefehuang**]: https://github.com/keefehuang [**@J4MMlE**]: https://github.com/J4MMlE +[**@jmoosburger**]: https://github.com/jmoosburger From 806a4a9b5e4ef353bf5ce9061e3641d3c578f6f4 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 3 Jun 2026 08:56:36 +0200 Subject: [PATCH 23/24] Update pass tablegen --- .../mlir/Dialect/QCO/Transforms/Passes.td | 19 ++++++++----------- .../Optimizations/SwapAbsorption.cpp | 6 +++--- .../Optimizations/test_swapabsorption.cpp | 2 +- 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index 6ffb4707e6..c81909e4be 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -174,19 +174,16 @@ def HadamardLifting : Pass<"hadamard-lifting", "mlir::ModuleOp"> { }]; } -def SwapAbsorptionPass : Pass<"absorb-swaps", "mlir::ModuleOp"> { +def SwapAbsorption : Pass<"absorb-swaps", "mlir::ModuleOp"> { let dependentDialects = ["mlir::qco::QCODialect"]; - let summary = "Eliminate SWAP operations by rewiring circuit"; + let summary = "This pass absorbs SWAP operations into the initial program-to-hardware mapping."; let description = [{ - This pass removes SWAP operations by absorbing their effect into the surrounding circuit. - - For a SWAP operation exchanging qubits q0 and q1, the pass replaces all uses of the first output wire - with the second input wire and vice versa. As a result, the logical permutation introduced by the SWAP is - propagated through the IR and the SWAP operation itself can be removed. - - The pass repeatedly identifies SWAP operations that are ready to be processed and absorbs them one by one. - Each absorption may expose additional SWAP operations that can subsequently be eliminated. - This process continues until no further SWAP operations can be absorbed. + For a SWAP operation exchanging static qubits q0 and q1, the pass replaces the use of the + first (second) input qubit with the second (first) output qubit of the SWAP and subsequently + removes the operation. As a result, the initial program-to-hardware mapping is changed. + This process is repeated until no more SWAP operations can be absorbed. + + The pass assumes that the quantum program is already mapped to static qubits. }]; } diff --git a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp index 4d6376b25d..ccce35a807 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Optimizations/SwapAbsorption.cpp @@ -22,13 +22,13 @@ #include namespace mlir::qco { -#define GEN_PASS_DEF_SWAPABSORPTIONPASS +#define GEN_PASS_DEF_SWAPABSORPTION #include "mlir/Dialect/QCO/Transforms/Passes.h.inc" namespace { -struct SwapAbsorption : impl::SwapAbsorptionPassBase { +struct SwapAbsorption : impl::SwapAbsorptionBase { public: - using SwapAbsorptionPassBase::SwapAbsorptionPassBase; + using SwapAbsorptionBase::SwapAbsorptionBase; protected: void runOnOperation() override { diff --git a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp index 60c317bb72..eba0ec646d 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Optimizations/test_swapabsorption.cpp @@ -46,7 +46,7 @@ class SwapAbsorbPassTest : public testing::Test { static void applySwapAbsorb(OwningOpRef& moduleOp) { PassManager pm(moduleOp->getContext()); - pm.addPass(qco::createSwapAbsorptionPass()); + pm.addPass(qco::createSwapAbsorption()); auto res = pm.run(*moduleOp); ASSERT_TRUE(succeeded(res)); From 4ef76205a534197930a10b1094aa693dcb7db6ae Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 3 Jun 2026 06:57:29 +0000 Subject: [PATCH 24/24] =?UTF-8?q?=F0=9F=8E=A8=20pre-commit=20fixes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- mlir/include/mlir/Dialect/QCO/Transforms/Passes.td | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td index c81909e4be..1799b5aa18 100644 --- a/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td +++ b/mlir/include/mlir/Dialect/QCO/Transforms/Passes.td @@ -176,14 +176,15 @@ def HadamardLifting : Pass<"hadamard-lifting", "mlir::ModuleOp"> { def SwapAbsorption : Pass<"absorb-swaps", "mlir::ModuleOp"> { let dependentDialects = ["mlir::qco::QCODialect"]; - let summary = "This pass absorbs SWAP operations into the initial program-to-hardware mapping."; + let summary = "This pass absorbs SWAP operations into the initial " + "program-to-hardware mapping."; let description = [{ - For a SWAP operation exchanging static qubits q0 and q1, the pass replaces the use of the - first (second) input qubit with the second (first) output qubit of the SWAP and subsequently - removes the operation. As a result, the initial program-to-hardware mapping is changed. + For a SWAP operation exchanging static qubits q0 and q1, the pass replaces the use of the + first (second) input qubit with the second (first) output qubit of the SWAP and subsequently + removes the operation. As a result, the initial program-to-hardware mapping is changed. This process is repeated until no more SWAP operations can be absorbed. - - The pass assumes that the quantum program is already mapped to static qubits. + + The pass assumes that the quantum program is already mapped to static qubits. }]; }