|
| 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 |
0 commit comments