Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
60 commits
Select commit Hold shift + click to select a range
5a23021
Add RoutingDriver struct
MatthiasReumann Nov 3, 2025
eb4e104
Remove worklist from route function
MatthiasReumann Nov 5, 2025
5750df5
Add working global scheduler and routing process
MatthiasReumann Nov 5, 2025
50a66bc
Add mapper base and naive mapper files
MatthiasReumann Nov 6, 2025
d6c702f
Implement driver approach
MatthiasReumann Nov 8, 2025
260036c
Fix scf funcs for AStarDriver
MatthiasReumann Nov 9, 2025
d59bfc1
Add statistics struct
MatthiasReumann Nov 9, 2025
7f9a8ca
Improve a-star scheduler
MatthiasReumann Nov 11, 2025
f9106f6
Implement fix-and-repair approach
MatthiasReumann Nov 12, 2025
a4f43fe
Split naive and astar router into separate passes
MatthiasReumann Nov 12, 2025
8531ed7
Add astar functionality to the respective pass
MatthiasReumann Nov 12, 2025
dc908e1
Update schedule function to handle scf.for and scf.if as well
MatthiasReumann Nov 13, 2025
24aba99
Compute anchor when adding operations to layer
MatthiasReumann Nov 13, 2025
fdbbbb4
Implement branching logic
MatthiasReumann Nov 14, 2025
27f23b1
Improve documentation of WireIterator
MatthiasReumann Nov 14, 2025
40b005e
Fix linting
MatthiasReumann Nov 14, 2025
8772335
Add missing header
MatthiasReumann Nov 14, 2025
993163a
Improve documentation
MatthiasReumann Nov 14, 2025
78d4d67
Remove closed map in A* for now
MatthiasReumann Nov 14, 2025
7dcccac
Add skip-two-qubit-block function
MatthiasReumann Nov 15, 2025
beb0b81
Add A* fallback mechanism.
MatthiasReumann Nov 16, 2025
ab8b7a7
Remove layout from scheduling
MatthiasReumann Nov 16, 2025
11bf8d7
Use TypeSwitch
MatthiasReumann Nov 16, 2025
882909c
Clean up pull-request
MatthiasReumann Nov 18, 2025
c201372
Add Unit abstraction
MatthiasReumann Nov 19, 2025
69a4734
Implement LayeredUnit
MatthiasReumann Nov 21, 2025
befa5f7
Implement SequentialUnit
MatthiasReumann Nov 21, 2025
79b76f8
Fix linting
MatthiasReumann Nov 21, 2025
346fe58
Add LLVM_DEBUG
MatthiasReumann Nov 21, 2025
a23d79c
Fix more linting issues
MatthiasReumann Nov 21, 2025
10412da
Remove grid layouts
MatthiasReumann Nov 21, 2025
6438f07
Fix heuristic bug
MatthiasReumann Nov 27, 2025
9d1df84
Merge branch 'main' into enh/routing-driver
MatthiasReumann Dec 1, 2025
1365ad1
Merge branch 'main' into enh/routing-driver
MatthiasReumann Dec 1, 2025
48ff8f4
🎨 pre-commit fixes
pre-commit-ci[bot] Dec 1, 2025
33448a1
Reduce code duplication
MatthiasReumann Dec 1, 2025
4ade668
Merge branch 'enh/routing-driver' of https://github.com/munich-quantu…
MatthiasReumann Dec 1, 2025
3404e7a
Remove stack from verification pass
MatthiasReumann Dec 1, 2025
c317604
Add std::queue header
MatthiasReumann Dec 1, 2025
fcfbff6
Remove header
MatthiasReumann Dec 1, 2025
dcc49fb
Add bunny suggestions
MatthiasReumann Dec 1, 2025
6aa5927
Add QubitIndex.h
MatthiasReumann Dec 1, 2025
8993868
Fix linting
MatthiasReumann Dec 1, 2025
8d3419e
Remove SlidingWindow Iterator
MatthiasReumann Dec 2, 2025
13f2900
Fix linting
MatthiasReumann Dec 2, 2025
fabda23
Apply bunny suggestions
MatthiasReumann Dec 2, 2025
0746263
Apply bunny suggestions
MatthiasReumann Dec 3, 2025
5ca6ec0
Remove history
MatthiasReumann Dec 3, 2025
cb4baa0
Merge branch 'main' into enh/routing-driver
MatthiasReumann Dec 4, 2025
f60eecb
Update CHANGELOG.md
MatthiasReumann Dec 4, 2025
0fcf836
Fix "A Unit"
MatthiasReumann Dec 4, 2025
06e1d7e
Make remap functions class methods
MatthiasReumann Dec 4, 2025
e4676c1
Use templated range for insertSWAPs function
MatthiasReumann Dec 4, 2025
2fb08dd
Add swap check to unitary remap method
MatthiasReumann Dec 4, 2025
b4e658d
Remove casting header
MatthiasReumann Dec 4, 2025
2122f50
Apply suggestions from code review
MatthiasReumann Dec 4, 2025
dca2e0a
🎨 pre-commit fixes
pre-commit-ci[bot] Dec 4, 2025
1264ab3
Add 'shortestSWAPsBetween' to architecture class
MatthiasReumann Dec 4, 2025
869fee6
Remove QubitIndex and add isExecutable to Architecture
MatthiasReumann Dec 4, 2025
b050bf5
Fix linting
MatthiasReumann Dec 4, 2025
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel

### Changed

- ♻️ Group circuit operations into scheduling units for MLIR routing ([#1301]) ([**@MatthiasReumann**])
- 👷 Use `munich-quantum-software/setup-mlir` to set up MLIR ([#1294]) ([**@denialhaag**])
- ♻️ Preserve tuple structure and improve site type clarity of the MQT NA Default QDMI Device ([#1299]) ([**@marcelwa**])
- ♻️ Move DD package evaluation module to standalone script ([#1327]) ([**@burgholzer**])
Expand Down Expand Up @@ -266,6 +267,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool
[#1336]: https://github.com/munich-quantum-toolkit/core/pull/1336
[#1327]: https://github.com/munich-quantum-toolkit/core/pull/1327
[#1310]: https://github.com/munich-quantum-toolkit/core/pull/1310
[#1301]: https://github.com/munich-quantum-toolkit/core/pull/1301
[#1300]: https://github.com/munich-quantum-toolkit/core/pull/1300
[#1299]: https://github.com/munich-quantum-toolkit/core/pull/1299
[#1294]: https://github.com/munich-quantum-toolkit/core/pull/1294
Expand Down
1 change: 0 additions & 1 deletion mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.h
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,6 @@ class RewritePatternSet;
namespace mqt::ir::opt {

enum class PlacementStrategy : std::uint8_t { Random, Identity };
enum class RoutingMethod : std::uint8_t { Naive, AStar };

#define GEN_PASS_DECL
#include "mlir/Dialect/MQTOpt/Transforms/Passes.h.inc" // IWYU pragma: export
Expand Down
29 changes: 20 additions & 9 deletions mlir/include/mlir/Dialect/MQTOpt/Transforms/Passes.td
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ def ReuseQubitsPass : Pass<"reuse-qubits", "mlir::ModuleOp"> {
//===----------------------------------------------------------------------===//

def PlacementPassSC : Pass<"placement-sc", "mlir::ModuleOp"> {
let summary = "This pass maps dynamic qubits to static qubits on superconducting quantum devices using initial placement strategies.";
let summary = "This pass maps program qubits to hardware qubits on superconducting quantum devices using initial placement strategies.";
let options = [
Option<"strategy", "strategy", "PlacementStrategy", "PlacementStrategy::Random",
"The initial placement strategy to use.", [{llvm::cl::values(
Expand All @@ -124,16 +124,27 @@ def PlacementPassSC : Pass<"placement-sc", "mlir::ModuleOp"> {
];
}

def RoutingPassSC : Pass<"route-sc", "mlir::ModuleOp"> {
let summary = "This pass ensures that a program meets the connectivity constraints of a given architecture.";
def NaiveRoutingPassSC : Pass<"route-naive-sc", "mlir::ModuleOp"> {
let summary = "This pass ensures that all two-qubit gates are executable on the target architecture.";
let description = [{
This pass inserts SWAP operations to ensure two-qubit gates are executable on a given target architecture.
Simple pre-order traversal of the IR that routes any non-executable gates by inserting SWAPs along the shortest path.
}];
let options = [
Option<"archName", "arch", "std::string", "",
"The name of the targeted architecture.">,
];
let statistics = [
Statistic<"numSwaps", "num-additional-swaps", "The number of additional SWAPs">
];
}

def AStarRoutingPassSC : Pass<"route-astar-sc", "mlir::ModuleOp"> {
let summary = "This pass ensures that all two-qubit gates are executable on the target architecture.";
let description = [{
Routes the program by dividing the circuit into layers of parallel two-qubit gates and iteratively searches and
inserts SWAPs for each layer using A*-search.
}];
let options = [
Option<"method", "method", "RoutingMethod", "RoutingMethod::AStar",
"The routing method to use.", [{llvm::cl::values(
clEnumValN(RoutingMethod::Naive, "naive", "Swap along shortest paths"),
clEnumValN(RoutingMethod::AStar, "astar", "A*-search-based routing algorithm"))}]>,
Option<"archName", "arch", "std::string", "",
"The name of the targeted architecture.">,
Option<"nlookahead", "nlookahead", "std::size_t", "1",
Expand All @@ -149,7 +160,7 @@ def RoutingPassSC : Pass<"route-sc", "mlir::ModuleOp"> {
}

def RoutingVerificationSCPass : Pass<"verify-routing-sc", "mlir::ModuleOp"> {
let summary = "This pass verifies that a program meets the connectivity constraints of a given architecture.";
let summary = "This pass verifies that all two-qubit gates are executable on the target architecture.";
let description = [{
This pass ensures that all two-qubit gates are executable on the target's architecture.
}];
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,16 +10,20 @@

#pragma once

#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h"
#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h"
#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h"

#include <cstddef>
#include <cstdint>
#include <llvm/ADT/DenseMapInfo.h>
#include <llvm/ADT/DenseSet.h>
#include <llvm/ADT/SmallVector.h>
#include <llvm/ADT/StringRef.h>
#include <memory>
#include <mlir/Support/LLVM.h>
#include <string>
#include <string_view>
#include <utility>

namespace mqt::ir::opt {

Expand All @@ -29,8 +33,8 @@ namespace mqt::ir::opt {
*/
class Architecture {
public:
using CouplingSet = mlir::DenseSet<std::pair<QubitIndex, QubitIndex>>;
using NeighbourVector = mlir::SmallVector<mlir::SmallVector<QubitIndex, 4>>;
using CouplingSet = mlir::DenseSet<std::pair<uint32_t, uint32_t>>;
using NeighbourVector = mlir::SmallVector<mlir::SmallVector<uint32_t, 4>>;

explicit Architecture(std::string name, std::size_t nqubits,
CouplingSet couplingSet)
Expand All @@ -55,27 +59,33 @@ class Architecture {
/**
* @brief Return true if @p u and @p v are adjacent.
*/
[[nodiscard]] bool areAdjacent(QubitIndex u, QubitIndex v) const {
[[nodiscard]] bool areAdjacent(uint32_t u, uint32_t v) const {
return couplingSet_.contains({u, v});
}

/**
* @brief Collect the shortest path between @p u and @p v.
* @returns The path from the destination (v) to source (u) qubit.
* @brief Collect the shortest SWAP sequence to make @p u and @p v adjacent.
* @returns The SWAP sequence from the destination (v) to source (u) qubit.
*/
[[nodiscard]] llvm::SmallVector<std::size_t>
shortestPathBetween(QubitIndex u, QubitIndex v) const;
[[nodiscard]] llvm::SmallVector<std::pair<uint32_t, uint32_t>>
shortestSWAPsBetween(uint32_t u, uint32_t v) const;

/**
* @brief Return the length of the shortest path between @p u and @p v.
*/
[[nodiscard]] std::size_t distanceBetween(QubitIndex u, QubitIndex v) const;
[[nodiscard]] std::size_t distanceBetween(uint32_t u, uint32_t v) const;

/**
* @brief Collect all neighbours of @p u.
*/
[[nodiscard]] llvm::SmallVector<QubitIndex, 4>
neighboursOf(QubitIndex u) const;
[[nodiscard]] llvm::SmallVector<uint32_t, 4> neighboursOf(uint32_t u) const;

/**
* @brief Validate if a two-qubit op is executable on the architecture for a
* given layout.
*/
[[nodiscard]] bool isExecutable(UnitaryInterface op,
const Layout& layout) const;

private:
using Matrix = llvm::SmallVector<llvm::SmallVector<std::size_t>>;
Expand Down
83 changes: 68 additions & 15 deletions mlir/include/mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h
Original file line number Diff line number Diff line change
Expand Up @@ -11,29 +11,20 @@
#pragma once

#include "mlir/Dialect/MQTOpt/IR/MQTOptDialect.h"
#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h"

#include <concepts>
#include <cstddef>
#include <cstdint>
#include <llvm/ADT/StringRef.h>
#include <mlir/Dialect/Func/IR/FuncOps.h>
#include <mlir/IR/BuiltinAttributes.h>
#include <mlir/IR/PatternMatch.h>
#include <mlir/IR/Value.h>
#include <ranges>
#include <utility>

namespace mqt::ir::opt {
/**
* @brief 'For' pushes once onto the stack, hence the parent is at depth one.
*/
constexpr std::size_t FOR_PARENT_DEPTH = 1UL;

/**
* @brief 'If' pushes twice onto the stack, hence the parent is at depth two.
*/
constexpr std::size_t IF_PARENT_DEPTH = 2UL;

/**
* @brief Type alias for qubit indices.
*/
using QubitIndex = uint32_t;

/**
* @brief A pair of SSA Values.
Expand All @@ -43,7 +34,7 @@ using ValuePair = std::pair<mlir::Value, mlir::Value>;
/**
* @brief Represents a pair of qubit indices.
*/
using QubitIndexPair = std::pair<QubitIndex, QubitIndex>;
using QubitIndexPair = std::pair<uint32_t, uint32_t>;

/**
* @brief Return true if the function contains "entry_point" in the passthrough
Expand Down Expand Up @@ -80,4 +71,66 @@ using QubitIndexPair = std::pair<QubitIndex, QubitIndex>;
*/
[[nodiscard]] mlir::Operation* getUserInRegion(mlir::Value v,
mlir::Region* region);

/**
* @brief Create and return SWAPOp for two qubits.
*
* Expects the rewriter to be set to the correct position.
*
* @param location The Location to attach to the created op.
* @param in0 First input qubit SSA value.
* @param in1 Second input qubit SSA value.
* @param rewriter A PatternRewriter.
* @return The created SWAPOp.
*/
[[nodiscard]] SWAPOp createSwap(mlir::Location location, mlir::Value in0,
mlir::Value in1,
mlir::PatternRewriter& rewriter);

/**
* @brief Replace all uses of a value within a region and its nested regions,
* except for a specific operation.
*
* @param oldValue The value to replace.
* @param newValue The new value to use.
* @param region The region in which to perform replacements.
* @param exceptOp Operation to exclude from replacements.
* @param rewriter The pattern rewriter.
*/
void replaceAllUsesInRegionAndChildrenExcept(mlir::Value oldValue,
mlir::Value newValue,
mlir::Region* region,
mlir::Operation* exceptOp,
mlir::PatternRewriter& rewriter);

/**
* @brief Insert SWAP ops at the rewriter's insertion point.
*
* @param loc The location of the inserted SWAP ops.
* @param swaps A range of hardware indices for the SWAPs.
* @param layout The current layout.
* @param rewriter The pattern rewriter.
*/
template <typename Range>
requires std::same_as<std::ranges::range_value_t<Range>, QubitIndexPair>
void insertSWAPs(mlir::Location loc, Range&& swaps, Layout& layout,
mlir::PatternRewriter& rewriter) {
for (const auto [hw0, hw1] : std::forward<Range>(swaps)) {
const mlir::Value in0 = layout.lookupHardwareValue(hw0);
const mlir::Value in1 = layout.lookupHardwareValue(hw1);

auto swap = createSwap(loc, in0, in1, rewriter);

rewriter.setInsertionPointAfter(swap);

mlir::Region* region = swap->getParentRegion();
mlir::Value out0 = swap.getOutQubits()[0];
mlir::Value out1 = swap.getOutQubits()[1];

replaceAllUsesInRegionAndChildrenExcept(in0, out1, region, swap, rewriter);
replaceAllUsesInRegionAndChildrenExcept(in1, out0, region, swap, rewriter);

layout.remap(swap);
}
}
} // namespace mqt::ir::opt
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
/*
* Copyright (c) 2023 - 2025 Chair for Design Automation, TUM
* Copyright (c) 2025 Munich Quantum Software Company GmbH
* All rights reserved.
*
* SPDX-License-Identifier: MIT
*
* Licensed under the MIT License
*/

#pragma once

#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Common.h"
#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Layout.h"
#include "mlir/Dialect/MQTOpt/Transforms/Transpilation/Unit.h"

#include <cstddef>
#include <llvm/ADT/STLExtras.h>
#include <llvm/Support/Debug.h>
#include <mlir/Support/LLVM.h>

namespace mqt::ir::opt {

struct Layer {
/// @brief All (zero, one, two-qubit) ops contained inside this layer.
mlir::SmallVector<mlir::Operation*, 64> ops;
/// @brief The program index pairs of all two-qubit ops.
mlir::SmallVector<QubitIndexPair, 16> twoQubitProgs;
/// @brief The first op in ops in textual IR order.
mlir::Operation* anchor{};

/// @brief Add op to ops and reset anchor if necessary.
void addOp(mlir::Operation* op) {
ops.emplace_back(op);
if (anchor == nullptr || op->isBeforeInBlock(anchor)) {
anchor = op;
}
}
/// @returns true iff. there are no ops in this layer.
[[nodiscard]] bool hasZeroOps() const { return ops.empty(); }
/// @returns true iff. there are no two-qubit ops in this layer.
[[nodiscard]] bool hasZero2QOps() const { return twoQubitProgs.empty(); }
};

/// @brief A LayeredUnit traverses a program layer-by-layer.
class LayeredUnit : public Unit {
public:
using Layers = mlir::SmallVector<Layer, 0>;

[[nodiscard]] static LayeredUnit
fromEntryPointFunction(mlir::func::FuncOp func, std::size_t nqubits);

LayeredUnit(Layout layout, mlir::Region* region, bool restore = false);

[[nodiscard]] mlir::SmallVector<LayeredUnit, 3> next();
[[nodiscard]] Layers::const_iterator begin() const { return layers_.begin(); }
[[nodiscard]] Layers::const_iterator end() const { return layers_.end(); }
[[nodiscard]] const Layer& operator[](std::size_t i) const {
return layers_[i];
}
[[nodiscard]] std::size_t size() const { return layers_.size(); }

#ifndef NDEBUG
LLVM_DUMP_METHOD void dump(llvm::raw_ostream& os = llvm::dbgs()) const;
#endif

private:
Layers layers_;
};
} // namespace mqt::ir::opt
Loading
Loading