Skip to content

Commit ab78766

Browse files
MatthiasReumannpre-commit-ci[bot]burgholzer
authored
✨ Heuristic Mapping Pass (#1537)
## Description This pull request reimplements the heuristic mapping pass of the old mqtopt dialect. Similarly to #1510 the mapping pass ignores structured control flow (for now). I've copied the CMake setup of #1206. A key difference between the implementations is that here I avoid building the "fat" Layout object (which keeps track of the SSA values) and use the `WireIterator`s and the "lightweight" Layout instead. Moreover, just because I wanted to highlight how cool the `WireIterator` is, this pull request adds LightSABRE-like back and forth traversal to find a better initial layout. Lastly, I think it should be possible to use the ideas implemented here for a unitized IR also. ## Checklist: <!--- This checklist serves as a reminder of a couple of things that ensure your pull request will be merged swiftly. --> - [x] The pull request only contains commits that are focused and relevant to this change. - [x] I have added appropriate tests that cover the new/changed functionality. - [x] I have updated the documentation to reflect these changes. - [x] I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals. - [x] I have added migration instructions to the upgrade guide (if needed). - [x] The changes follow the project's style guidelines and introduce no new warnings. - [x] The changes are fully tested and pass the CI checks. - [x] I have reviewed my own code changes. --------- Signed-off-by: burgholzer <burgholzer@me.com> Co-authored-by: pre-commit-ci[bot] <66853113+pre-commit-ci[bot]@users.noreply.github.com> Co-authored-by: burgholzer <burgholzer@me.com>
1 parent 917fcdb commit ab78766

14 files changed

Lines changed: 1202 additions & 2 deletions

File tree

CHANGELOG.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel
1111

1212
### Added
1313

14+
- ✨ Add a `place-and-route` pass for mapping circuits to architectures with restricted topologies ([#1537]) ([**@MatthiasReumann**])
1415
- ✨ Add initial infrastructure for new QC and QCO MLIR dialects
1516
([#1264], [#1330], [#1402], [#1428], [#1430], [#1436], [#1443], [#1446], [#1464], [#1465], [#1470], [#1471], [#1472], [#1474], [#1475], [#1506], [#1510], [#1513], [#1521])
1617
([**@burgholzer**], [**@denialhaag**], [**@taminob**], [**@DRovara**], [**@li-mingbao**], [**@Ectras**], [**@MatthiasReumann**])
@@ -327,6 +328,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool
327328

328329
<!-- PR links -->
329330

331+
[#1537]: https://github.com/munich-quantum-toolkit/core/pull/1537
330332
[#1521]: https://github.com/munich-quantum-toolkit/core/pull/1521
331333
[#1513]: https://github.com/munich-quantum-toolkit/core/pull/1513
332334
[#1510]: https://github.com/munich-quantum-toolkit/core/pull/1510

mlir/include/mlir/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,5 +6,6 @@
66
#
77
# Licensed under the MIT License
88

9-
add_subdirectory(Dialect)
109
add_subdirectory(Conversion)
10+
add_subdirectory(Dialect)
11+
add_subdirectory(Passes)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
2+
# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
3+
# All rights reserved.
4+
#
5+
# SPDX-License-Identifier: MIT
6+
#
7+
# Licensed under the MIT License
8+
9+
set(LLVM_TARGET_DEFINITIONS Passes.td)
10+
mlir_tablegen(Passes.h.inc -gen-pass-decls -name QCO)
11+
add_public_tablegen_target(QcoPassesIncGen)
12+
add_mlir_doc(Passes QcoPasses Passes/ -gen-pass-doc)
Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
/*
2+
* Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
3+
* Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
4+
* All rights reserved.
5+
*
6+
* SPDX-License-Identifier: MIT
7+
*
8+
* Licensed under the MIT License
9+
*/
10+
11+
#pragma once
12+
13+
#include <llvm/ADT/DenseSet.h>
14+
#include <llvm/ADT/SmallVector.h>
15+
#include <llvm/ADT/Twine.h>
16+
#include <llvm/Support/ErrorHandling.h>
17+
#include <mlir/Support/LLVM.h>
18+
19+
#include <cstddef>
20+
#include <string>
21+
#include <utility>
22+
23+
/**
24+
* @brief A quantum accelerator's architecture.
25+
*/
26+
class [[nodiscard]] Architecture {
27+
public:
28+
using CouplingSet = mlir::DenseSet<std::pair<std::size_t, std::size_t>>;
29+
using NeighbourVector = mlir::SmallVector<mlir::SmallVector<std::size_t, 4>>;
30+
31+
explicit Architecture(std::string name, std::size_t nqubits,
32+
CouplingSet couplingSet)
33+
: name_(std::move(name)), nqubits_(nqubits),
34+
couplingSet_(std::move(couplingSet)), neighbours_(nqubits),
35+
dist_(nqubits, mlir::SmallVector<std::size_t>(nqubits, UINT64_MAX)),
36+
prev_(nqubits, mlir::SmallVector<std::size_t>(nqubits, UINT64_MAX)) {
37+
floydWarshallWithPathReconstruction();
38+
collectNeighbours();
39+
}
40+
41+
/**
42+
* @brief Return the architecture's name.
43+
*/
44+
[[nodiscard]] std::string_view name() const;
45+
46+
/**
47+
* @brief Return the architecture's number of qubits.
48+
*/
49+
[[nodiscard]] std::size_t nqubits() const;
50+
51+
/**
52+
* @brief Return true if @p u and @p v are adjacent.
53+
*/
54+
[[nodiscard]] bool areAdjacent(std::size_t u, std::size_t v) const;
55+
56+
/**
57+
* @brief Return the length of the shortest path between @p u and @p v.
58+
*/
59+
[[nodiscard]] std::size_t distanceBetween(std::size_t u, std::size_t v) const;
60+
61+
/**
62+
* @brief Collect all neighbours of @p u.
63+
*/
64+
[[nodiscard]] mlir::SmallVector<std::size_t, 4>
65+
neighboursOf(std::size_t u) const;
66+
67+
private:
68+
using Matrix = mlir::SmallVector<mlir::SmallVector<std::size_t, 0>, 0>;
69+
70+
/**
71+
* @brief Find all shortest paths in the coupling map between two qubits.
72+
* @details Vertices are the qubits. Edges connected two qubits. Has a time
73+
* and memory complexity of O(nqubits^3) and O(nqubits^2), respectively.
74+
* @link Adapted from https://en.wikipedia.org/wiki/Floyd–Warshall_algorithm
75+
*/
76+
void floydWarshallWithPathReconstruction();
77+
78+
/**
79+
* @brief Collect the neighbours of all qubits.
80+
* @details Has a time complexity of O(nqubits)
81+
*/
82+
void collectNeighbours();
83+
84+
std::string name_;
85+
std::size_t nqubits_;
86+
CouplingSet couplingSet_;
87+
NeighbourVector neighbours_;
88+
89+
Matrix dist_;
90+
Matrix prev_;
91+
};

mlir/include/mlir/Passes/Passes.h

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
/*
2+
* Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
3+
* Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
4+
* All rights reserved.
5+
*
6+
* SPDX-License-Identifier: MIT
7+
*
8+
* Licensed under the MIT License
9+
*/
10+
11+
#pragma once
12+
13+
#include "mlir/Dialect/QCO/IR/QCODialect.h"
14+
15+
#include <mlir/Dialect/Arith/IR/Arith.h>
16+
#include <mlir/Pass/Pass.h>
17+
#include <mlir/Pass/PassRegistry.h>
18+
19+
namespace mlir::qco {
20+
21+
#define GEN_PASS_DECL
22+
#include "mlir/Passes/Passes.h.inc" // IWYU pragma: export
23+
24+
//===----------------------------------------------------------------------===//
25+
// Registration
26+
//===----------------------------------------------------------------------===//
27+
28+
/// Generate the code for registering passes.
29+
#define GEN_PASS_REGISTRATION
30+
#include "mlir/Passes/Passes.h.inc" // IWYU pragma: export
31+
32+
} // namespace mlir::qco

mlir/include/mlir/Passes/Passes.td

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
// Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
2+
// Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
3+
// All rights reserved.
4+
//
5+
// SPDX-License-Identifier: MIT
6+
//
7+
// Licensed under the MIT License
8+
9+
#ifndef QCO_PASSES
10+
#define QCO_PASSES
11+
12+
include "mlir/Pass/PassBase.td"
13+
14+
//===----------------------------------------------------------------------===//
15+
// Transpilation Passes
16+
//===----------------------------------------------------------------------===//
17+
18+
def MappingPass : Pass<"place-and-route", "mlir::ModuleOp"> {
19+
let dependentDialects = ["mlir::qco::QCODialect"];
20+
let summary = "This pass ensures that a program meets the connectivity constraints of a target architecture.";
21+
let description = [{
22+
This pass maps the dynamically allocated qubits in a quantum program to the static qubits of a target architecture.
23+
The pass performs both placement and routing of the qubits to ensure that all two-qubit operations in the program
24+
can be executed on the target architecture.
25+
26+
First, the pass assigns static qubits to the dynamically allocated ones by creating an initial dynamic-to-static
27+
mapping, which is referred to as the initial layout. Then, it traverses the circuit and inserts `SWAP` operations to
28+
ensure that all two-qubit operations are executable on the target architecture, a process known as routing.
29+
30+
For routing, the pass first divides the circuit into layers. A layer is a set of independently executable (sequences
31+
or blocks of) two-qubit operations. Subsequently, the pass performs an A* search for each layer to find and insert a
32+
sequence of `SWAP` operations that makes these two-qubit operations executable. The A* search also considers
33+
subsequent layers, which are determined by the `nlookahead` parameter.
34+
35+
The cost function of the A* search is defined as follows:
36+
37+
* `f(n) = g(n) + h(n)`
38+
* `g(n) = alpha * depth(n)`
39+
* `h(n) = sum(pow(lambda, i) * h(L, p) for [i, L] in enumerate(layers))`
40+
41+
Where:
42+
* `p` is the dynamic-to-static mapping associated with search node `n`.
43+
* `layers` is an array of layers with size `1 + nlookahead`.
44+
* `depth(n)` returns the distance from the node `n` to the root node.
45+
* `dist(i, j)` returns the distance between the qubits `i` and `j` on the target's coupling graph.
46+
* `h(L, p) := sum(dist(p[gate.first], p[gate.second]) for gate in L)`
47+
48+
To iteratively refine the mapping, the pass performs multiple forward and backward traversals of the circuit. In
49+
each traversal, the pass routes the circuit and updates the dynamic-to-static mapping based on the routing decisions
50+
made during that traversal. By performing multiple traversals, the pass can iteratively refine the mapping and
51+
potentially find a more optimal solution. This is behavior is controlled by the `niterations` parameter.
52+
}];
53+
let options = [
54+
Option<"nlookahead", "nlookahead", "std::size_t", "1",
55+
"The number of lookahead steps.">,
56+
Option<"alpha", "alpha", "float", "1.0F",
57+
"The alpha factor in the cost function. Must be > 0.">,
58+
Option<"lambda", "lambda", "float", "0.5F",
59+
"The lambda factor in the cost function.">,
60+
Option<"niterations", "niterations", "std::size_t", "2",
61+
"The number of forwards and backwards traversal to improve the initial layout.">
62+
];
63+
}
64+
65+
#endif // QCO_PASSES

mlir/lib/CMakeLists.txt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
#
77
# Licensed under the MIT License
88

9-
add_subdirectory(Dialect)
109
add_subdirectory(Conversion)
1110
add_subdirectory(Compiler)
11+
add_subdirectory(Dialect)
12+
add_subdirectory(Passes)
1213
add_subdirectory(Support)

mlir/lib/Passes/CMakeLists.txt

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
2+
# Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
3+
# All rights reserved.
4+
#
5+
# SPDX-License-Identifier: MIT
6+
#
7+
# Licensed under the MIT License
8+
9+
file(GLOB_RECURSE PASSES_SOURCES *.cpp)
10+
11+
get_property(dialect_libs GLOBAL PROPERTY MLIR_DIALECT_LIBS)
12+
13+
add_mlir_library(
14+
QcoPasses
15+
${PASSES_SOURCES}
16+
LINK_LIBS
17+
PUBLIC
18+
PRIVATE
19+
${dialect_libs}
20+
DEPENDS
21+
QcoPassesIncGen)
22+
23+
# collect header files
24+
file(GLOB_RECURSE PASSES_HEADERS_SOURCE ${MQT_MLIR_SOURCE_INCLUDE_DIR}/mlir/Passes/*.h)
25+
file(GLOB_RECURSE PASSES_HEADERS_BUILD ${MQT_MLIR_BUILD_INCLUDE_DIR}/mlir/Passes/*.inc)
26+
27+
# add public headers using file sets
28+
target_sources(
29+
QcoPasses
30+
PUBLIC FILE_SET
31+
HEADERS
32+
BASE_DIRS
33+
${MQT_MLIR_SOURCE_INCLUDE_DIR}
34+
FILES
35+
${PASSES_HEADERS_SOURCE}
36+
FILE_SET
37+
HEADERS
38+
BASE_DIRS
39+
${MQT_MLIR_BUILD_INCLUDE_DIR}
40+
FILES
41+
${PASSES_HEADERS_BUILD})
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
/*
2+
* Copyright (c) 2023 - 2026 Chair for Design Automation, TUM
3+
* Copyright (c) 2025 - 2026 Munich Quantum Software Company GmbH
4+
* All rights reserved.
5+
*
6+
* SPDX-License-Identifier: MIT
7+
*
8+
* Licensed under the MIT License
9+
*/
10+
11+
#include "mlir/Passes/Mapping/Architecture.h"
12+
13+
#include <llvm/Support/ErrorHandling.h>
14+
#include <mlir/Support/LLVM.h>
15+
16+
#include <cstddef>
17+
#include <cstdint>
18+
#include <string_view>
19+
#include <utility>
20+
21+
using namespace mlir;
22+
23+
std::string_view Architecture::name() const { return name_; }
24+
25+
std::size_t Architecture::nqubits() const { return nqubits_; }
26+
27+
bool Architecture::areAdjacent(std::size_t u, std::size_t v) const {
28+
return couplingSet_.contains(std::make_pair(u, v));
29+
}
30+
31+
std::size_t Architecture::distanceBetween(std::size_t u, std::size_t v) const {
32+
if (dist_[u][v] == UINT64_MAX) {
33+
report_fatal_error("Floyd-warshall failed to compute the distance "
34+
"between qubits " +
35+
Twine(u) + " and " + Twine(v));
36+
}
37+
return dist_[u][v];
38+
}
39+
40+
SmallVector<std::size_t, 4> Architecture::neighboursOf(std::size_t u) const {
41+
return neighbours_[u];
42+
}
43+
44+
void Architecture::floydWarshallWithPathReconstruction() {
45+
for (const auto& [u, v] : couplingSet_) {
46+
dist_[u][v] = 1;
47+
prev_[u][v] = u;
48+
}
49+
for (std::size_t v = 0; v < nqubits(); ++v) {
50+
dist_[v][v] = 0;
51+
prev_[v][v] = v;
52+
}
53+
54+
for (std::size_t k = 0; k < nqubits(); ++k) {
55+
for (std::size_t i = 0; i < nqubits(); ++i) {
56+
for (std::size_t j = 0; j < nqubits(); ++j) {
57+
if (dist_[i][k] == UINT64_MAX || dist_[k][j] == UINT64_MAX) {
58+
continue; // Avoid overflow with "infinite" distances.
59+
}
60+
const std::size_t sum = dist_[i][k] + dist_[k][j];
61+
if (dist_[i][j] > sum) {
62+
dist_[i][j] = sum;
63+
prev_[i][j] = prev_[k][j];
64+
}
65+
}
66+
}
67+
}
68+
}
69+
70+
void Architecture::collectNeighbours() {
71+
for (const auto& [u, v] : couplingSet_) {
72+
neighbours_[u].push_back(v);
73+
}
74+
}

0 commit comments

Comments
 (0)