From 200763193613282151c193caf469ceebcd064c78 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 11 Aug 2026 15:39:22 +0200 Subject: [PATCH 1/7] Implement drive-by --- .../QCO/Transforms/Mapping/Mapping.cpp | 28 +++++++++---------- 1 file changed, 14 insertions(+), 14 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp index b258c5b1ec..138c571bb9 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -982,25 +983,23 @@ struct MappingPass : impl::MappingPassBase { /// Return the "average" layout by computing the borda count, where each /// layout is a voter and each hardware index counts as a candidate. One vote /// is the order (the permutation) of program-to-hardware indices. - template static Layout vote(Range layouts) { + template Layout vote(Range layouts) { assert(!layouts.empty() && "expected at least one layout"); - const auto ncandidates = (*layouts.begin()).nqubits(); - SmallVector scores(ncandidates, 0); - for (const Layout& layout : layouts) { - for (const auto [rank, hw] : enumerate(layout.getProgramToHardware())) { - scores[hw] += ncandidates - rank - 1; + FGraph f(*target); + Layout curr(*(layouts.begin())); + + for (const auto& layout : llvm::drop_begin(layouts)) { + f.reset(); + f.construct(curr, layout); + if (const auto happy = f.findHappySWAPChain()) { + for (const auto& swap : *happy) { + curr.swap(swap.first, swap.second); + } } } - auto mapping = llvm::to_vector(llvm::seq(ncandidates)); - llvm::sort(mapping, [&](const size_t lhs, const size_t rhs) { - return scores[lhs] != scores[rhs] - ? scores[lhs] > scores[rhs] - : lhs < rhs; // Ensure order on borda equality. - }); - - return Layout::fromMapping(mapping); + return curr; } /// Skip to the end of the two-qubit block for both wire iterators, where @@ -1472,6 +1471,7 @@ struct MappingPass : impl::MappingPassBase { })); for (RoutingBundle& child : children) { const auto swaps = restore(child.layout, winner); + llvm::dbgs() << "swaps: " << swaps.size() << '\n'; insertSWAPs(swaps, child, totalStats, rewriter); } return winner; From 78a10e3b1578561d2743c2bff83d221c8b5935c3 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 11 Aug 2026 15:52:08 +0200 Subject: [PATCH 2/7] Implement back and forth traversal --- .../QCO/Transforms/Mapping/Mapping.cpp | 25 +++++++++++++------ 1 file changed, 18 insertions(+), 7 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp index 138c571bb9..81da2c0a6a 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp @@ -989,12 +989,24 @@ struct MappingPass : impl::MappingPassBase { FGraph f(*target); Layout curr(*(layouts.begin())); - for (const auto& layout : llvm::drop_begin(layouts)) { - f.reset(); - f.construct(curr, layout); - if (const auto happy = f.findHappySWAPChain()) { - for (const auto& swap : *happy) { - curr.swap(swap.first, swap.second); + for (size_t i = 0; i < 2; ++i) { + for (const auto& layout : llvm::drop_begin(layouts)) { + f.reset(); + f.construct(curr, layout); + if (const auto happy = f.findHappySWAPChain()) { + for (const auto& swap : *happy) { + curr.swap(swap.first, swap.second); + } + } + } + + for (const auto& layout : llvm::drop_begin(llvm::reverse(layouts))) { + f.reset(); + f.construct(curr, layout); + if (const auto happy = f.findHappySWAPChain()) { + for (const auto& swap : *happy) { + curr.swap(swap.first, swap.second); + } } } } @@ -1471,7 +1483,6 @@ struct MappingPass : impl::MappingPassBase { })); for (RoutingBundle& child : children) { const auto swaps = restore(child.layout, winner); - llvm::dbgs() << "swaps: " << swaps.size() << '\n'; insertSWAPs(swaps, child, totalStats, rewriter); } return winner; From fac4daa4ffd35ea25e9e04bb2d3caccca7ce8a60 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Tue, 11 Aug 2026 15:56:40 +0200 Subject: [PATCH 3/7] Add niterations parameter --- mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp index 81da2c0a6a..ab5c8e5048 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp @@ -983,13 +983,14 @@ struct MappingPass : impl::MappingPassBase { /// Return the "average" layout by computing the borda count, where each /// layout is a voter and each hardware index counts as a candidate. One vote /// is the order (the permutation) of program-to-hardware indices. - template Layout vote(Range layouts) { + template + Layout vote(Range layouts, const size_t niterations = 1) { assert(!layouts.empty() && "expected at least one layout"); FGraph f(*target); Layout curr(*(layouts.begin())); - for (size_t i = 0; i < 2; ++i) { + for (size_t i = 0; i < niterations; ++i) { for (const auto& layout : llvm::drop_begin(layouts)) { f.reset(); f.construct(curr, layout); From d6f1c0183b903c0367984aad09648b9e1c227a0c Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 12 Aug 2026 07:48:26 +0200 Subject: [PATCH 4/7] Clean up --- .../QCO/Transforms/Mapping/Mapping.cpp | 46 +++++++++---------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp index ab5c8e5048..bf3a8c10e7 100644 --- a/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp +++ b/mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp @@ -27,7 +27,6 @@ #include #include #include -#include #include #include #include @@ -980,36 +979,33 @@ struct MappingPass : impl::MappingPassBase { return {layouts[0], std::move(swaps[0]), std::move(swaps[1])}; } - /// Return the "average" layout by computing the borda count, where each - /// layout is a voter and each hardware index counts as a candidate. One vote - /// is the order (the permutation) of program-to-hardware indices. + /// Compute a routing-friendly layout compromise between a range of layouts. + /// Using the first layout of the range as an anchor, the function repeatedly + /// nudges the current layout towards the next one using happy SWAP chains. + /// Inspired by SABRE and to reduce ordering bias, the function performs an + /// additional backward pass. template - Layout vote(Range layouts, const size_t niterations = 1) { + Layout driveby(Range layouts, const size_t niterations = 1) { assert(!layouts.empty() && "expected at least one layout"); FGraph f(*target); Layout curr(*(layouts.begin())); - for (size_t i = 0; i < niterations; ++i) { - for (const auto& layout : llvm::drop_begin(layouts)) { - f.reset(); - f.construct(curr, layout); - if (const auto happy = f.findHappySWAPChain()) { - for (const auto& swap : *happy) { - curr.swap(swap.first, swap.second); - } + // Nudge curr towards target by applying a happy SWAP chain. + const auto merge = [&](const Layout& target) { + f.reset(); + f.construct(curr, target); + if (const auto happy = f.findHappySWAPChain()) { + for (const auto& swap : *happy) { + curr.swap(swap.first, swap.second); } } + }; - for (const auto& layout : llvm::drop_begin(llvm::reverse(layouts))) { - f.reset(); - f.construct(curr, layout); - if (const auto happy = f.findHappySWAPChain()) { - for (const auto& swap : *happy) { - curr.swap(swap.first, swap.second); - } - } - } + // Perform multiple rounds of forward and backward drive-by's. + for (size_t i = 0; i < niterations; ++i) { + for_each(drop_begin(layouts), merge); + for_each(drop_begin(reverse(layouts)), merge); } return curr; @@ -1478,15 +1474,15 @@ struct MappingPass : impl::MappingPassBase { return convergedLayout; }) .template Case([&](IndexSwitchOp) { - auto winner = vote(map_range( + auto compromise = driveby(map_range( children, [](const RoutingBundle& b) -> const Layout& { return b.layout; })); for (RoutingBundle& child : children) { - const auto swaps = restore(child.layout, winner); + const auto swaps = restore(child.layout, compromise); insertSWAPs(swaps, child, totalStats, rewriter); } - return winner; + return compromise; }); if constexpr (Mode == RoutingMode::Hot) { From 646771d5a34797baca112f070b363c802c41fe52 Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 12 Aug 2026 07:59:28 +0200 Subject: [PATCH 5/7] Update unit test --- mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp b/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp index 76a797190c..ffce533474 100644 --- a/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp +++ b/mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp @@ -1663,7 +1663,7 @@ TEST_P(MappingPassTest, MapIndexSwitchUsesVotedLayout) { m->walk([&](SWAPOp) { ++numSwaps; }); // The three routed cases agree on the voted exit layout; only the default // case must be restored to it. Restoring every case to the parent needs 12. - EXPECT_EQ(numSwaps, 4UL); + EXPECT_EQ(numSwaps, 6UL); } TEST_P(MappingPassTest, MapPaddedCXCZGrid) { From 588961f0b85c620a0e8a59692ac2fefea1f48b7e Mon Sep 17 00:00:00 2001 From: Matthias Reumann Date: Wed, 12 Aug 2026 08:11:09 +0200 Subject: [PATCH 6/7] Update CHANGELOG.md [no ci] --- CHANGELOG.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index a3ea580f7c..42f63a40ba 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -86,7 +86,7 @@ releases may include breaking changes. circuits to compiler-target topologies while preserving target site IDs and materializing routing workspace on demand ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588], [#1600], [#1664], [#1709], [#1716], [#1748], [#1805], - [#1870], [#1904], [#1911], [#1951], [#1997], [#2016]) ([**@MatthiasReumann**], + [#1870], [#1904], [#1911], [#1951], [#1997], [#2016], [#2060]) ([**@MatthiasReumann**], [**@burgholzer**]) - ✨ Add a pass for qubit reuse in quantum programs, as well as related auxiliary passes and patterns ([#1705], [#1755], [#1756], [#1923], [#1924]) @@ -725,6 +725,7 @@ for previous changelogs._ +[#2060]: https://github.com/munich-quantum-toolkit/core/pull/2060 [#2030]: https://github.com/munich-quantum-toolkit/core/pull/2030 [#2028]: https://github.com/munich-quantum-toolkit/core/pull/2028 [#2026]: https://github.com/munich-quantum-toolkit/core/pull/2026 From 8e7b9817689f15bf04fab90aaffbcdd156d99dab Mon Sep 17 00:00:00 2001 From: "pre-commit-ci[bot]" <66853113+pre-commit-ci[bot]@users.noreply.github.com> Date: Wed, 12 Aug 2026 06:39:59 +0000 Subject: [PATCH 7/7] =?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 --- CHANGELOG.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d3f0aa8cce..954cf12300 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -91,8 +91,8 @@ releases may include breaking changes. circuits to compiler-target topologies while preserving target site IDs and materializing routing workspace on demand ([#1537], [#1547], [#1568], [#1581], [#1583], [#1588], [#1600], [#1664], [#1709], [#1716], [#1748], [#1805], - [#1870], [#1904], [#1911], [#1951], [#1997], [#2016], [#2060]) ([**@MatthiasReumann**], - [**@burgholzer**]) + [#1870], [#1904], [#1911], [#1951], [#1997], [#2016], [#2060]) + ([**@MatthiasReumann**], [**@burgholzer**]) - ✨ Add a pass for qubit reuse in quantum programs, as well as related auxiliary passes and patterns ([#1705], [#1755], [#1756], [#1923], [#1924]) ([**@DRovara**], [**@burgholzer**])