Skip to content

✨ Heuristic Mapping Pass#1537

Open
MatthiasReumann wants to merge 32 commits intomainfrom
feat/heuristic-mapping-pass
Open

✨ Heuristic Mapping Pass#1537
MatthiasReumann wants to merge 32 commits intomainfrom
feat/heuristic-mapping-pass

Conversation

@MatthiasReumann
Copy link
Collaborator

@MatthiasReumann MatthiasReumann commented Mar 3, 2026

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 WireIterators 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:

  • The pull request only contains commits that are focused and relevant to this change.
  • I have added appropriate tests that cover the new/changed functionality.
  • I have updated the documentation to reflect these changes.
  • I have added entries to the changelog for any noteworthy additions, changes, fixes, or removals.
  • I have added migration instructions to the upgrade guide (if needed).
  • The changes follow the project's style guidelines and introduce no new warnings.
  • The changes are fully tested and pass the CI checks.
  • I have reviewed my own code changes.

@MatthiasReumann MatthiasReumann added c++ Anything related to C++ code MLIR Anything related to MLIR labels Mar 3, 2026
@MatthiasReumann MatthiasReumann changed the title ✨Heuristic Mapping Pass 🚧 Heuristic Mapping Pass Mar 3, 2026
@codecov
Copy link

codecov bot commented Mar 3, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@MatthiasReumann
Copy link
Collaborator Author

@coderabbitai full review

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 4, 2026

✅ Actions performed

Full review triggered.

@MatthiasReumann MatthiasReumann marked this pull request as ready for review March 4, 2026 13:53
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Mar 4, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added HeuristicMappingPass for quantum circuit mapping with configurable options: nlookahead, alpha, lambda, repeats.
    • Added Architecture model to represent hardware topology, adjacency, distances, and neighbor queries.
  • Tests

    • Added unit tests validating the heuristic mapping pass on representative architectures and circuits.
  • Chores

    • Integrated a new Passes module into the build and test configurations; added pass registration and documentation generation.

Walkthrough

Adds a new MLIR Passes module (QcoPasses) including a HeuristicMappingPass, Architecture model (shortest-paths & neighbours), TableGen wiring and CMake integration, implementation sources, and unit tests validating mapping on a RigettiNovera topology.

Changes

Cohort / File(s) Summary
Top-level CMake changes
mlir/include/mlir/CMakeLists.txt, mlir/lib/CMakeLists.txt, mlir/unittests/CMakeLists.txt
Reorders module inclusion and adds the Passes subdirectory to include, lib, and unittests build lists.
Passes TableGen & includes
mlir/include/mlir/Passes/CMakeLists.txt, mlir/include/mlir/Passes/Passes.td, mlir/include/mlir/Passes/Passes.h
Adds TableGen target for Passes.td, generates pass declarations (name QCO), exposes QcoPassesIncGen, enables pass doc gen, and defines HeuristicMappingPass with CLI options.
Passes library CMake
mlir/lib/Passes/CMakeLists.txt
Adds QcoPasses MLIR library target, collects sources/headers, links dialect libs and TableGen dependency, and exposes public headers via FILE_SET HEADERS.
Architecture model
mlir/include/mlir/Passes/Mapping/Architecture.h, mlir/lib/Passes/Mapping/Architecture.cpp
Adds Architecture class: coupling set, neighbour vectors, Floyd–Warshall path reconstruction (prev_/dist_), adjacency/distance APIs, and neighbour collection.
Heuristic mapping pass implementation
mlir/lib/Passes/Mapping/HeuristicMapping.cpp
Implements mlir::qco::HeuristicMappingPass: Layout model, Parameters, A*-style Node/search, cold/hot routing that emits SWAPs, placement logic, and MLIR pass integration.
Unit tests & test CMake
mlir/unittests/Passes/*, mlir/unittests/Passes/Mapping/CMakeLists.txt, mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp
Adds unit-test target and parameterized tests (GHZ, Sabre) for RigettiNovera; links QcoPasses and verifies mapped output is executable on the architecture.

Sequence Diagram(s)

sequenceDiagram
    participant MLIR as MLIR Pass Framework
    participant HMP as HeuristicMappingPass
    participant Arch as Architecture
    participant Planner as A* Planner
    participant IR as MLIR IR

    MLIR->>HMP: runOnOperation()
    HMP->>Arch: construct/load architecture
    Arch->>Arch: floydWarshallWithPathReconstruction()
    HMP->>Planner: initialize root Node with Layout
    loop A* search
        Planner->>Arch: query distance / adjacency
        Planner->>Planner: expand successors (simulate swaps on Layout)
        Planner->>HMP: select candidate (routeHot)
        HMP->>IR: emit SWAP ops and update mapping
    end
    HMP->>IR: finalize transformed module
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

feature

Suggested reviewers

  • burgholzer
  • ystade

Poem

🐇 I hopped through CMake, .td, and code so bright,

I traced each coupling and found the shortest flight,
Floyd paved the paths and A* chose the swaps,
SWAPs hopped in place until all gates could hop,
Hooray — mapped qubits march to hardware tonight!

🚥 Pre-merge checks | ✅ 2 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.22% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The title '✨ Heuristic Mapping Pass' clearly and concisely summarizes the main change: introducing a new heuristic mapping pass.
Description check ✅ Passed The description covers the main change, motivation (reimplementation of old mqtopt dialect), key implementation details (lightweight Layout, WireIterator, LightSABRE-like traversal), and includes most checklist items. However, changelog entries are explicitly marked as not completed.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/heuristic-mapping-pass

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 9

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mlir/lib/Compiler/CMakeLists.txt`:
- Line 23: The target currently links QcoPasses as a PUBLIC dependency, exposing
it transitively; change the linkage to PRIVATE so QcoPasses is not exported in
the public interface. Locate the target_link_libraries line that references
QcoPasses in the mlir/lib/Compiler/CMakeLists.txt and replace PUBLIC with
PRIVATE (keeping the same target name and ordering), since public headers under
mlir/include/mlir/Compiler/* do not expose QcoPasses symbols and only
implementation uses passes like QCToQCO, QCOToQC, and QCToQIR.

In `@mlir/lib/Passes/Mapping/Architecture.cpp`:
- Around line 44-47: The loop in Architecture.cpp uses u and v from couplingSet_
as direct indices into dist_, prev_ and neighbours_ without bounds checks;
validate that u and v are within the valid ranges before indexing (e.g., ensure
u < dist_.size() and v < dist_[u].size() and v < prev_[u].size() and u/v <
neighbours_.size() as appropriate), and if an index is out-of-range either skip
the pair (continue) or emit a clear error/diagnostic; apply the same validation
to the other occurrences where couplingSet_ pairs are indexed (the references
around the lines corresponding to the second occurrence at 70-71) and use the
unique symbols couplingSet_, dist_, prev_, neighbours_ to locate and fix all
spots.

In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp`:
- Line 331: Remove the unconditional call to layout.dump() from the normal pass
path and only print when debugging is enabled: locate the call to layout.dump()
in HeuristicMapping (the pass implementation in HeuristicMapping.cpp) and wrap
it with the pass/module debug check (e.g., a conditional using LLVM_DEBUG,
DEBUG_TYPE, or a isDebugEnabled-style flag used in this codebase) so that
layout.dump() executes only under debug logging; ensure you reference the same
debug macro or helper used elsewhere in this file to keep behavior consistent.
- Around line 313-321: The code assumes the number of dynamic qubits (dyn from
collectDynamicQubits) fits the hardware; if dyn.size() > arch.nqubits()
subsequent Layout lookups (Layout::identity, etc.) will index out of range; add
a guard after computing dyn (and before Layout::identity and the SABRE loop)
that checks if dyn.size() > arch.nqubits() and handle it deterministically
(e.g., return a failed LogicalResult/Optional, emit an error/message via the
existing logging/diagnostic mechanism, or early-clamp/resize mappings) so later
uses of layout and any indexing into arch.nqubits()-sized structures are safe;
reference symbols: collectDynamicQubits, dyn, arch.nqubits(), Layout::identity,
and the repeats loop.
- Around line 303-309: The code currently hardcodes the architecture when
constructing Architecture arch(...) which ignores the user-provided -arch
option; update the constructor to use the pass option (e.g. this->archName)
instead of the literal "RigettiNovera" and avoid hardcoding the qubit
count/edges: replace the call Architecture arch("RigettiNovera", 9, {...}) with
a construction that uses the selected architecture name and its associated
topology (for example Architecture arch(this->archName, /*numQubits*/
this->nqubits or query a helper that returns the node count and edge list for
archName) or call a factory like Architecture::fromName(this->archName)),
ensuring the code references the pass field (this->archName) so the -arch option
is honored; keep Parameters weights(this->alpha, this->lambda, this->nlookahead)
as-is.
- Around line 609-620: The SWAPOp is being created with a stale insertion point
because rewriter.create<SWAPOp>(unknown, ...) is called without first moving the
insertion point; before calling rewriter.create in the loop that iterates over
swaps (the block using wires, swaps, in0, in1 and then calling
rewriter.replaceAllUsesExcept), set the rewriter insertion point to the current
wire/front location (e.g., use the appropriate rewriter.setInsertionPoint or
setInsertionPointAfter API on the operation representing the wire front) so the
SWAPOp is inserted at the correct place relative to in0/in1, then create the
SWAPOp and perform replaceAllUsesExcept as shown.
- Line 478: The variable "step" is declared with the unsigned type std::size_t
but needs to hold negative values for Direction::Backward; change its type to a
signed pointer-difference type (e.g., std::ptrdiff_t) so it matches
WireIterator::difference_type and avoids underflow when set to -1; update the
declaration near the constexpr std::size_t step = d == Direction::Forward ? 1 :
-1; (used with std::ranges::advance and wire iteration) to use std::ptrdiff_t
(or WireIterator::difference_type) instead.

In `@mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp`:
- Line 135: Remove the unconditional IR dumps from the test by deleting or
gating the module->dump() calls so they do not print on every successful run;
locate the invocations of module->dump() in test_heuristic_mapping.cpp (the
calls at and near the two occurrences noted) and either remove them or wrap them
behind a debug/verbose guard (e.g., LLVM_DEBUG/if (verbose)) so CI output is not
noisy while preserving the ability to enable dumps for debugging.
- Around line 66-73: The current code uses mappings[op.getQubit(...)] which can
insert default entries and only checks op.getNumQubits() > 1; change this to
defensively lookup qubit indices (e.g., use mappings.find or contains) for
op.getQubit(0) and op.getQubit(1), verify both exist before using them, and
explicitly require op.getNumQubits() == 2 (reject ops with 0, 1, or >2 qubits) —
if a lookup fails or the arity is not 2, set executable = false and return
WalkResult::interrupt() (retain checks using arch.areAdjacent(i0, i1) only after
successful lookups).

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: dccba142-3321-49bd-bcfa-8639de6bb40c

📥 Commits

Reviewing files that changed from the base of the PR and between cb7c22c and 4586450.

📒 Files selected for processing (14)
  • mlir/include/mlir/CMakeLists.txt
  • mlir/include/mlir/Passes/CMakeLists.txt
  • mlir/include/mlir/Passes/Mapping/Architecture.h
  • mlir/include/mlir/Passes/Passes.h
  • mlir/include/mlir/Passes/Passes.td
  • mlir/lib/CMakeLists.txt
  • mlir/lib/Compiler/CMakeLists.txt
  • mlir/lib/Passes/CMakeLists.txt
  • mlir/lib/Passes/Mapping/Architecture.cpp
  • mlir/lib/Passes/Mapping/HeuristicMapping.cpp
  • mlir/unittests/CMakeLists.txt
  • mlir/unittests/Passes/CMakeLists.txt
  • mlir/unittests/Passes/Mapping/CMakeLists.txt
  • mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp`:
- Around line 315-319: The error message built in HeuristicMapping.cpp when
dyn.size() > arch.nqubits() concatenates "supports" and the qubit count without
a space; update the emission in the if block (the func.emitError() call) to
include a space after "supports" (or otherwise format the message with
separators) so it prints "the targeted architecture supports 9 qubits, got X";
keep the same use of arch.nqubits(), dyn.size(), and signalPassFailure()
unchanged.

In `@mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp`:
- Around line 80-87: Duplicate RigettiNovera architecture is defined in
getRigettiNovera() in the test and again in HeuristicMapping.cpp; extract the
shared coupling map into a single source of truth and use it from both places.
Replace the local static COUPLING in getRigettiNovera() by referencing the
shared symbol (or a factory function) you create (e.g., RigettiNoveraCoupling or
makeRigettiNoveraArchitecture) so both the test and HeuristicMapping.cpp use the
same unique identifier; ensure the new symbol returns the same Architecture
(name, qubit count, coupling) and update callers to use that symbol instead of
duplicating the literal list.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 60e67c87-341c-4173-a238-b33bcd270229

📥 Commits

Reviewing files that changed from the base of the PR and between 4586450 and 068f881.

📒 Files selected for processing (2)
  • mlir/lib/Passes/Mapping/HeuristicMapping.cpp
  • mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (5)
mlir/lib/Passes/Mapping/Architecture.cpp (1)

44-47: ⚠️ Potential issue | 🔴 Critical

Validate coupling indices before matrix/vector indexing.

Line 46/Line 47 and Line 72 assume every (u, v) in couplingSet_ is < nqubits_; invalid input causes out-of-bounds access.

🛡️ Proposed fix
 void Architecture::floydWarshallWithPathReconstruction() {
   for (const auto& [u, v] : couplingSet_) {
+    if (u >= nqubits_ || v >= nqubits_) {
+      report_fatal_error("coupling edge out of range: (" + Twine(u) + ", " +
+                         Twine(v) + ") for architecture size " +
+                         Twine(nqubits_));
+    }
     dist_[u][v] = 1;
     prev_[u][v] = u;
   }
@@
 void Architecture::collectNeighbours() {
   for (const auto& [u, v] : couplingSet_) {
+    if (u >= nqubits_ || v >= nqubits_) {
+      report_fatal_error("coupling edge out of range: (" + Twine(u) + ", " +
+                         Twine(v) + ") for architecture size " +
+                         Twine(nqubits_));
+    }
     neighbours_[u].push_back(v);
   }
 }

Also applies to: 70-72

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mlir/lib/Passes/Mapping/Architecture.cpp` around lines 44 - 47, In
Architecture::floydWarshallWithPathReconstruction ensure you validate each
coupling index before using it: for every (u, v) from couplingSet_ check that 0
<= u < nqubits_ and 0 <= v < nqubits_ (or the equivalent unsigned bounds) before
assigning into dist_[u][v] and prev_[u][v]; if an index is out of range, skip
that pair (or handle/report it) to prevent out-of-bounds access — apply the same
validation wherever couplingSet_ elements are used later in the function (e.g.,
the loop around lines 70–72).
mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp (2)

67-70: ⚠️ Potential issue | 🟠 Major

Avoid DenseMap::operator[] in executability validation.

Line 68 and Line 69 can silently insert missing qubit mappings and mask invalid test state. Please assert invariants and use contains()/lookup().

#!/bin/bash
rg -n -C3 'op.getNumQubits\(\) > 1|mappings\[op.getQubit' mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp
🔧 Proposed fix
+#include <cassert>
@@
-      if (op.getNumQubits() > 1) {
-        const auto i0 = mappings[op.getQubit(0)];
-        const auto i1 = mappings[op.getQubit(1)];
+      if (op.getNumQubits() > 1) {
+        assert(op.getNumQubits() == 2 &&
+               "Expected only 2-qubit gates after decomposition");
+        assert(mappings.contains(op.getQubit(0)) &&
+               "Missing mapping for first qubit");
+        assert(mappings.contains(op.getQubit(1)) &&
+               "Missing mapping for second qubit");
+        const auto i0 = mappings.lookup(op.getQubit(0));
+        const auto i1 = mappings.lookup(op.getQubit(1));
         if (!arch.areAdjacent(i0, i1)) {
           executable = false;
           return WalkResult::interrupt();

Based on learnings: In the MQTOpt MLIR routing passes, input IR is guaranteed to contain only 1-qubit and 2-qubit gates; 3+-qubit gates are invalid before routing.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp` around lines 67 -
70, The test uses DenseMap::operator[] on mappings when checking two-qubit op
executability (in the block guarded by op.getNumQubits() > 1), which can
silently insert missing keys; instead assert the invariant that mappings
contains both keys (use mappings.contains(op.getQubit(0)) and
mappings.contains(op.getQubit(1))) and fetch values with lookup()/find()/at()
(e.g., use mappings.lookup(op.getQubit(0)) and mappings.lookup(op.getQubit(1))).
Update the code around op.getNumQubits(), mappings, op.getQubit and
arch.areAdjacent to explicitly assert the mapping presence (or fail the test)
before calling arch.areAdjacent(i0, i1).

81-87: 🧹 Nitpick | 🔵 Trivial

Deduplicate the RigettiNovera coupling map definition.

The topology here duplicates the literal in mlir/lib/Passes/Mapping/HeuristicMapping.cpp (Line 307-Line 311). Extract a shared factory/source-of-truth to prevent drift.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp` around lines 81 -
87, The RigettiNovera coupling set in getRigettiNovera() duplicates the literal
defined in HeuristicMapping.cpp; refactor by extracting a single shared factory
or accessor (e.g., Architecture::rigettiNovera() or a free function
createRigettiNoveraArchitecture()) that returns the Architecture with the
COUPLING set, replace the local COUPLING and getRigettiNovera() to call that
shared factory, and update the occurrence in
mlir/lib/Passes/Mapping/HeuristicMapping.cpp to use the same factory so the
coupling map is maintained in one place.
mlir/lib/Passes/Mapping/HeuristicMapping.cpp (2)

306-311: ⚠️ Potential issue | 🟠 Major

Honor the -arch option instead of hardcoding RigettiNovera.

Line 307 ignores user-selected architecture and always routes with RigettiNovera.

🔧 Proposed fix
-    // TODO: Hardcoded architecture.
+    if (!archName.empty() && archName != "RigettiNovera") {
+      getOperation()->emitError()
+          << "unsupported architecture '" << archName
+          << "' (currently only 'RigettiNovera' is implemented)";
+      signalPassFailure();
+      return;
+    }
     Architecture arch("RigettiNovera", 9,
                       {{0, 3}, {3, 0}, {0, 1}, {1, 0}, {1, 4}, {4, 1},
                        {1, 2}, {2, 1}, {2, 5}, {5, 2}, {3, 6}, {6, 3},
                        {3, 4}, {4, 3}, {4, 7}, {7, 4}, {4, 5}, {5, 4},
                        {5, 8}, {8, 5}, {6, 7}, {7, 6}, {7, 8}, {8, 7}});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp` around lines 306 - 311, The
code hardcodes an Architecture instance via Architecture arch("RigettiNovera",
...) which ignores the user-provided -arch option; replace this hardcoded
construction with building the Architecture from the parsed CLI option (use the
variable or accessor that holds the -arch value, e.g.,
optArch/archName/archOption used elsewhere in the pass) and/or use the existing
architecture factory/lookup routine to create the topology from that option,
ensuring the rest of the code references that Architecture instance
(Architecture arch) instead of the literal "RigettiNovera".

617-627: ⚠️ Potential issue | 🔴 Critical

Set insertion point before creating SWAPOp.

Line 621 emits SWAPs at whatever insertion point was last set, which can misorder operations and violate dominance after replaceAllUsesExcept.

#!/bin/bash
rg -n -C8 'for \(const auto \[hw0, hw1\] : \*swaps\)|create<SWAPOp>|setInsertionPoint' mlir/lib/Passes/Mapping/HeuristicMapping.cpp
🔧 Proposed fix
       const auto unknown = rewriter.getUnknownLoc();
       for (const auto [hw0, hw1] : *swaps) {
         const auto in0 = wires[hw0].qubit();
         const auto in1 = wires[hw1].qubit();
+        assert(in0.getNumUses() == 1 && in1.getNumUses() == 1 &&
+               "expected linear qubit wires");
+        auto* use0 = *in0.getUsers().begin();
+        auto* use1 = *in1.getUsers().begin();
+        auto* insertBefore = use0->isBeforeInBlock(use1) ? use0 : use1;
+        rewriter.setInsertionPoint(insertBefore);

         auto op = rewriter.create<SWAPOp>(unknown, in0, in1);
         const auto out0 = op.getQubit0Out();
         const auto out1 = op.getQubit1Out();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp` around lines 617 - 627, The
SWAPOp is created at whatever insertion point was last used, which can misorder
ops and break dominance after calling rewriter.replaceAllUsesExcept; before
calling rewriter.create<SWAPOp>(...) inside the loop over swaps, explicitly set
the rewriter insertion point to a deterministic location that dominates the uses
(e.g. the block/position where the swap should be placed) so the created SWAPOp
is emitted in the correct place; update the loop around wires, swaps, SWAPOp,
and replaceAllUsesExcept to call rewriter.setInsertionPoint... (to a dominating
op/block for in0/in1) immediately before rewriter.create and then proceed with
rewriter.replaceAllUsesExcept.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp`:
- Around line 316-320: The check that compares dyn.size() to arch.nqubits()
emits an error and calls signalPassFailure() but then continues execution,
leading to invalid state and potential out-of-bounds layout access; immediately
return from the enclosing pass handler after calling signalPassFailure() (i.e.,
add a return following signalPassFailure() in the function that contains this
block so no further code runs when func.emitError()/signalPassFailure() is
invoked), referencing the existing dyn, arch, func.emitError(), and
signalPassFailure() symbols to locate the spot to change.

---

Duplicate comments:
In `@mlir/lib/Passes/Mapping/Architecture.cpp`:
- Around line 44-47: In Architecture::floydWarshallWithPathReconstruction ensure
you validate each coupling index before using it: for every (u, v) from
couplingSet_ check that 0 <= u < nqubits_ and 0 <= v < nqubits_ (or the
equivalent unsigned bounds) before assigning into dist_[u][v] and prev_[u][v];
if an index is out of range, skip that pair (or handle/report it) to prevent
out-of-bounds access — apply the same validation wherever couplingSet_ elements
are used later in the function (e.g., the loop around lines 70–72).

In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp`:
- Around line 306-311: The code hardcodes an Architecture instance via
Architecture arch("RigettiNovera", ...) which ignores the user-provided -arch
option; replace this hardcoded construction with building the Architecture from
the parsed CLI option (use the variable or accessor that holds the -arch value,
e.g., optArch/archName/archOption used elsewhere in the pass) and/or use the
existing architecture factory/lookup routine to create the topology from that
option, ensuring the rest of the code references that Architecture instance
(Architecture arch) instead of the literal "RigettiNovera".
- Around line 617-627: The SWAPOp is created at whatever insertion point was
last used, which can misorder ops and break dominance after calling
rewriter.replaceAllUsesExcept; before calling rewriter.create<SWAPOp>(...)
inside the loop over swaps, explicitly set the rewriter insertion point to a
deterministic location that dominates the uses (e.g. the block/position where
the swap should be placed) so the created SWAPOp is emitted in the correct
place; update the loop around wires, swaps, SWAPOp, and replaceAllUsesExcept to
call rewriter.setInsertionPoint... (to a dominating op/block for in0/in1)
immediately before rewriter.create and then proceed with
rewriter.replaceAllUsesExcept.

In `@mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp`:
- Around line 67-70: The test uses DenseMap::operator[] on mappings when
checking two-qubit op executability (in the block guarded by op.getNumQubits() >
1), which can silently insert missing keys; instead assert the invariant that
mappings contains both keys (use mappings.contains(op.getQubit(0)) and
mappings.contains(op.getQubit(1))) and fetch values with lookup()/find()/at()
(e.g., use mappings.lookup(op.getQubit(0)) and mappings.lookup(op.getQubit(1))).
Update the code around op.getNumQubits(), mappings, op.getQubit and
arch.areAdjacent to explicitly assert the mapping presence (or fail the test)
before calling arch.areAdjacent(i0, i1).
- Around line 81-87: The RigettiNovera coupling set in getRigettiNovera()
duplicates the literal defined in HeuristicMapping.cpp; refactor by extracting a
single shared factory or accessor (e.g., Architecture::rigettiNovera() or a free
function createRigettiNoveraArchitecture()) that returns the Architecture with
the COUPLING set, replace the local COUPLING and getRigettiNovera() to call that
shared factory, and update the occurrence in
mlir/lib/Passes/Mapping/HeuristicMapping.cpp to use the same factory so the
coupling map is maintained in one place.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 013352b5-7595-4d79-bd38-0cb9b8690413

📥 Commits

Reviewing files that changed from the base of the PR and between 068f881 and ed65865.

📒 Files selected for processing (4)
  • mlir/include/mlir/Passes/Mapping/Architecture.h
  • mlir/lib/Passes/Mapping/Architecture.cpp
  • mlir/lib/Passes/Mapping/HeuristicMapping.cpp
  • mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp (2)

85-92: 🧹 Nitpick | 🔵 Trivial

Extract RigettiNovera coupling data to a shared source of truth.

This architecture fixture still duplicates production coupling information, which can drift over time and invalidate the test’s meaning.

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp` around lines 85 -
92, The test helper getRigettiNovera() is duplicating production coupling data;
extract the coupling set into a single shared source of truth and consume that
from the test. Replace the inline const static Architecture::CouplingSet
COUPLING in getRigettiNovera() with a reference to the canonical definition
(e.g., an exported constant or accessor such as
Architecture::RigettiNoveraCoupling() or a shared constant
RigettiNoveraCoupling) and construct the Architecture("RigettiNovera", 9, ...)
using that shared symbol so the test no longer drifts from production data.

67-74: ⚠️ Potential issue | 🟠 Major

Avoid mutating mappings during validation by replacing operator[] with find/lookup.

At Line 72 and Line 73, DenseMap::operator[] can insert default entries when assertions are disabled, which can silently mask missing mappings in this test helper.

Does llvm::DenseMap::operator[] insert a default value when the key is missing, and should lookup/find be preferred for read-only checks?
Proposed fix
-        assert(mappings.contains(op.getQubit(0)) && "Qubit 0 not in mapping");
-        assert(mappings.contains(op.getQubit(1)) && "Qubit 1 not in mapping");
-        const auto i0 = mappings[op.getQubit(0)];
-        const auto i1 = mappings[op.getQubit(1)];
+        const auto it0 = mappings.find(op.getQubit(0));
+        const auto it1 = mappings.find(op.getQubit(1));
+        assert(it0 != mappings.end() && "Qubit 0 not in mapping");
+        assert(it1 != mappings.end() && "Qubit 1 not in mapping");
+        if (it0 == mappings.end() || it1 == mappings.end()) {
+          executable = false;
+          return WalkResult::interrupt();
+        }
+        const auto i0 = it0->second;
+        const auto i1 = it1->second;
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp` around lines 67 -
74, The test uses mappings[op.getQubit(0)] and mappings[op.getQubit(1)] which
can insert default entries when operator[] is used; change these reads to
non-mutating lookups (e.g., use mappings.find or mappings.lookup) to avoid
modifying mappings during validation: first use mappings.find(op.getQubit(0))
and ensure the iterator is valid (or use lookup and check result) before
dereferencing to get i0, do the same for op.getQubit(1) to produce i1, and then
call arch.areAdjacent(i0, i1); update the assert messages to reference the
lookup result so missing mappings fail the test without mutating the DenseMap.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp`:
- Around line 105-109: The tests call runHeuristicMapping() but the
HeuristicMappingPass is hardcoded to RigettiNovera; add a pass option for the
target architecture in the pass definition (Passes.td) and update the
HeuristicMappingPass implementation (HeuristicMapping.cpp) to read that option
instead of using the hardcoded RigettiNovera, then change runHeuristicMapping()
and all its call sites (where createHeuristicMappingPass() is invoked) to pass
the parameterized architecture value (from GetParam().factory() or the arch
argument) into createHeuristicMappingPass() so each test instantiates the pass
with the correct architecture.

---

Duplicate comments:
In `@mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp`:
- Around line 85-92: The test helper getRigettiNovera() is duplicating
production coupling data; extract the coupling set into a single shared source
of truth and consume that from the test. Replace the inline const static
Architecture::CouplingSet COUPLING in getRigettiNovera() with a reference to the
canonical definition (e.g., an exported constant or accessor such as
Architecture::RigettiNoveraCoupling() or a shared constant
RigettiNoveraCoupling) and construct the Architecture("RigettiNovera", 9, ...)
using that shared symbol so the test no longer drifts from production data.
- Around line 67-74: The test uses mappings[op.getQubit(0)] and
mappings[op.getQubit(1)] which can insert default entries when operator[] is
used; change these reads to non-mutating lookups (e.g., use mappings.find or
mappings.lookup) to avoid modifying mappings during validation: first use
mappings.find(op.getQubit(0)) and ensure the iterator is valid (or use lookup
and check result) before dereferencing to get i0, do the same for op.getQubit(1)
to produce i1, and then call arch.areAdjacent(i0, i1); update the assert
messages to reference the lookup result so missing mappings fail the test
without mutating the DenseMap.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 3e958d42-45a0-4252-ab89-cf8bcd7d48d5

📥 Commits

Reviewing files that changed from the base of the PR and between ed65865 and dce2425.

📒 Files selected for processing (1)
  • mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (1)
mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp (1)

106-110: ⚠️ Potential issue | 🟠 Major

Parameterized architecture is not wired into pass execution.

Line 106-Line 110 instantiate createHeuristicMappingPass without any architecture input, while Line 139 and Line 187 call runHeuristicMapping(module) without passing arch. Given mlir/lib/Passes/Mapping/HeuristicMapping.cpp currently hardcodes RigettiNovera, all parameterized cases effectively test the same target architecture.

Also applies to: 139-140, 187-188

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp` around lines 106 -
110, The runHeuristicMapping helper currently constructs the pass without
passing any architecture, so all tests use the hardcoded default; modify
runHeuristicMapping to accept an architecture parameter (e.g., Arch arch) and
forward it into qco::createHeuristicMappingPass by setting the architecture
field on qco::HeuristicMappingPassOptions (or using the overload that accepts
arch), then update all call sites (the calls to runHeuristicMapping at the test
lines indicated) to pass the appropriate arch value so the pass executes against
the intended parameterized architecture rather than the hardcoded RigettiNovera.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp`:
- Around line 44-47: The test param struct ArchitectureParam currently uses
std::function<Architecture()> for its factory member which is heavier and
non-idiomatic for MLIR tests; change the type of ArchitectureParam::factory to
llvm::function_ref<Architecture()> (or a plain function pointer Architecture
(*)() if you prefer simpler semantics), update the struct definition
(ArchitectureParam) and any places that construct or store factories to use the
new type, and add the required include (llvm/ADT/FunctionRef.h) when using
llvm::function_ref so static factory functions continue to bind correctly.

---

Duplicate comments:
In `@mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp`:
- Around line 106-110: The runHeuristicMapping helper currently constructs the
pass without passing any architecture, so all tests use the hardcoded default;
modify runHeuristicMapping to accept an architecture parameter (e.g., Arch arch)
and forward it into qco::createHeuristicMappingPass by setting the architecture
field on qco::HeuristicMappingPassOptions (or using the overload that accepts
arch), then update all call sites (the calls to runHeuristicMapping at the test
lines indicated) to pass the appropriate arch value so the pass executes against
the intended parameterized architecture rather than the hardcoded RigettiNovera.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 64c50248-6961-40ce-a71c-bfe73de4b520

📥 Commits

Reviewing files that changed from the base of the PR and between b87beb0 and 84286a4.

📒 Files selected for processing (1)
  • mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

♻️ Duplicate comments (2)
mlir/lib/Passes/Mapping/HeuristicMapping.cpp (2)

623-629: ⚠️ Potential issue | 🔴 Critical

SWAPOp is created with a stale insertion point.

rewriter.create<SWAPOp> at Line 628 is not preceded by a local setInsertionPoint(...). This can insert swaps away from the current wire front and break routing correctness.

🛠️ Proposed fix
       const auto unknown = rewriter.getUnknownLoc();
       for (const auto [hw0, hw1] : *swaps) {
         const auto in0 = wires[hw0].qubit();
         const auto in1 = wires[hw1].qubit();
+        assert(in0.hasOneUse() && in1.hasOneUse() &&
+               "expected linear qubit wires");
+        Operation* use0 = (*in0.use_begin()).getOwner();
+        Operation* use1 = (*in1.use_begin()).getOwner();
+        assert(use0->getBlock() == use1->getBlock() &&
+               "expected both wire-front uses in same block");
+        Operation* insertBefore =
+            use0->isBeforeInBlock(use1) ? use0 : use1;
+        rewriter.setInsertionPoint(insertBefore);

         auto op = rewriter.create<SWAPOp>(unknown, in0, in1);
         const auto out0 = op.getQubit0Out();
         const auto out1 = op.getQubit1Out();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp` around lines 623 - 629, The
SWAPOp is being created with a stale insertion point: before calling
rewriter.create<SWAPOp>(unknown, in0, in1) ensure the rewriter is positioned at
the current wire front so swaps are inserted at the correct place. Locate the
loop iterating over *swaps that uses wires[hw0].qubit()/wires[hw1].qubit() and
before creating SWAPOp call rewriter.setInsertionPoint(...) using the current
front operation (e.g., the wire front/op or an available Operation* from
wires[hw0]/wires[hw1]) so the newly created SWAPOp and subsequent uses
(op.getQubit0Out(), op.getQubit1Out()) are placed correctly relative to the wire
front.

305-311: ⚠️ Potential issue | 🟠 Major

Hardcoded architecture ignores pass configurability.

Line 307 always constructs Architecture("RigettiNovera", ...), so architecture selection cannot vary per invocation.

🔧 Proposed fix
-    // TODO: Hardcoded architecture.
-    Architecture arch("RigettiNovera", 9,
-                      {{0, 3}, {3, 0}, {0, 1}, {1, 0}, {1, 4}, {4, 1},
-                       {1, 2}, {2, 1}, {2, 5}, {5, 2}, {3, 6}, {6, 3},
-                       {3, 4}, {4, 3}, {4, 7}, {7, 4}, {4, 5}, {5, 4},
-                       {5, 8}, {8, 5}, {6, 7}, {7, 6}, {7, 8}, {8, 7}});
+    const auto selectedArch =
+        this->archName.empty() ? std::string("RigettiNovera") : this->archName;
+    if (selectedArch != "RigettiNovera") {
+      getOperation()->emitError()
+          << "unsupported architecture '" << selectedArch
+          << "' (currently only 'RigettiNovera' is implemented)";
+      signalPassFailure();
+      return;
+    }
+    Architecture arch(selectedArch, 9,
+                      {{0, 3}, {3, 0}, {0, 1}, {1, 0}, {1, 4}, {4, 1},
+                       {1, 2}, {2, 1}, {2, 5}, {5, 2}, {3, 6}, {6, 3},
+                       {3, 4}, {4, 3}, {4, 7}, {7, 4}, {4, 5}, {5, 4},
+                       {5, 8}, {8, 5}, {6, 7}, {7, 6}, {7, 8}, {8, 7}});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp` around lines 305 - 311, The
code currently constructs a hardcoded Architecture via Architecture
arch("RigettiNovera", 9, ...) inside HeuristicMapping, which prevents
configuring the target architecture per pass; replace this hardcoded
construction with a configurable source (e.g., use an existing pass option or a
member like this->architectureName/this->architectureSpec or a function such as
getArchitectureFromOptions()) and build Architecture from that input instead of
the literal "RigettiNovera" and its edges; update uses of Parameters
weights(this->alpha, this->lambda, this->nlookahead) and the location where
Architecture arch is constructed so the pass reads the architecture
name/size/edges from the pass configuration (or a provided config object) and
falls back to a sensible default only if no config is supplied.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp`:
- Around line 623-629: The SWAPOp is being created with a stale insertion point:
before calling rewriter.create<SWAPOp>(unknown, in0, in1) ensure the rewriter is
positioned at the current wire front so swaps are inserted at the correct place.
Locate the loop iterating over *swaps that uses
wires[hw0].qubit()/wires[hw1].qubit() and before creating SWAPOp call
rewriter.setInsertionPoint(...) using the current front operation (e.g., the
wire front/op or an available Operation* from wires[hw0]/wires[hw1]) so the
newly created SWAPOp and subsequent uses (op.getQubit0Out(), op.getQubit1Out())
are placed correctly relative to the wire front.
- Around line 305-311: The code currently constructs a hardcoded Architecture
via Architecture arch("RigettiNovera", 9, ...) inside HeuristicMapping, which
prevents configuring the target architecture per pass; replace this hardcoded
construction with a configurable source (e.g., use an existing pass option or a
member like this->architectureName/this->architectureSpec or a function such as
getArchitectureFromOptions()) and build Architecture from that input instead of
the literal "RigettiNovera" and its edges; update uses of Parameters
weights(this->alpha, this->lambda, this->nlookahead) and the location where
Architecture arch is constructed so the pass reads the architecture
name/size/edges from the pass configuration (or a provided config object) and
falls back to a sensible default only if no config is supplied.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 7a6b466e-a3d9-4c92-b5ff-eee8d594fa57

📥 Commits

Reviewing files that changed from the base of the PR and between 84286a4 and 23864ef.

📒 Files selected for processing (2)
  • mlir/lib/Passes/Mapping/HeuristicMapping.cpp
  • mlir/unittests/Passes/Mapping/test_heuristic_mapping.cpp

@MatthiasReumann MatthiasReumann changed the title 🚧 Heuristic Mapping Pass ✨ Heuristic Mapping Pass Mar 5, 2026
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 3

♻️ Duplicate comments (1)
mlir/lib/Passes/Mapping/HeuristicMapping.cpp (1)

306-311: ⚠️ Potential issue | 🟠 Major

Honor the -arch option instead of hardcoding RigettiNovera.

Line 307 ignores user configuration and makes pass behavior non-configurable.

🔧 Proposed fix
-    // TODO: Hardcoded architecture.
-    Architecture arch("RigettiNovera", 9,
+    if (!archName.empty() && archName != "RigettiNovera") {
+      getOperation()->emitError()
+          << "unsupported architecture '" << archName
+          << "' (currently only 'RigettiNovera' is implemented)";
+      signalPassFailure();
+      return;
+    }
+    Architecture arch(archName.empty() ? "RigettiNovera" : archName, 9,
                       {{0, 3}, {3, 0}, {0, 1}, {1, 0}, {1, 4}, {4, 1},
                        {1, 2}, {2, 1}, {2, 5}, {5, 2}, {3, 6}, {6, 3},
                        {3, 4}, {4, 3}, {4, 7}, {7, 4}, {4, 5}, {5, 4},
                        {5, 8}, {8, 5}, {6, 7}, {7, 6}, {7, 8}, {8, 7}});
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp` around lines 306 - 311, Replace
the hardcoded Architecture("RigettiNovera", ...) in HeuristicMapping.cpp with
code that reads and uses the -arch option value (or the pass's configured
architecture parameter) to construct the Architecture instance; locate the
symbol Architecture arch(...) and change it to create the architecture from the
pass option (e.g., query the existing -arch CLI/pass option or injected config,
map that name to the topology data, and then construct Architecture using that
name and its connectivity) so the pass honors user configuration instead of
always using "RigettiNovera".
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp`:
- Line 23: Remove the unused include header by deleting the line that includes
TopologicalSortUtils.h in HeuristicMapping.cpp; locate the include directive
"#include <mlir/Analysis/TopologicalSortUtils.h>" near the top of the file (in
the compilation unit that defines the HeuristicMapping pass) and remove it so
only required headers remain, then rebuild to confirm no missing references in
functions/classes within that file.
- Around line 314-345: Before attempting placement/routing, skip functions that
the mapper cannot handle: detect and continue past declarations or bodies with
unsupported shapes by checking
func.isDeclaration()/func.getFunctionBody().empty() (or that the body does not
consist of exactly one block) and validate the wire shape returned by
collectDynamicQubits is compatible (e.g., non-empty and represents a
linear/contiguous set). If a function is unsupported emit a non-fatal diagnostic
on the FuncOp and continue the loop instead of calling place,
computeBidirectionalLayers, routeCold, or routeHot; keep existing error paths
for true mapping failures but avoid dereferencing front()/back() on
empty/structured bodies.
- Line 299: The alias MinQueue currently uses std::vector as the underlying
container which is inconsistent with project conventions; change the backing
container to llvm::SmallVector for the alias named MinQueue (i.e. replace
std::vector<Node> with SmallVector<Node, <appropriate inline capacity>>),
keeping the comparator std::greater<> and the Node element type unchanged so the
new typedef reads the same except for the container type; pick a sensible inline
capacity (e.g., 8 or matching nearby usages) and ensure SmallVector is in scope
where MinQueue is declared.

---

Duplicate comments:
In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp`:
- Around line 306-311: Replace the hardcoded Architecture("RigettiNovera", ...)
in HeuristicMapping.cpp with code that reads and uses the -arch option value (or
the pass's configured architecture parameter) to construct the Architecture
instance; locate the symbol Architecture arch(...) and change it to create the
architecture from the pass option (e.g., query the existing -arch CLI/pass
option or injected config, map that name to the topology data, and then
construct Architecture using that name and its connectivity) so the pass honors
user configuration instead of always using "RigettiNovera".

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 429a91b3-4fcd-4a09-9397-fb74f4e44bbb

📥 Commits

Reviewing files that changed from the base of the PR and between 23864ef and 5808d74.

📒 Files selected for processing (1)
  • mlir/lib/Passes/Mapping/HeuristicMapping.cpp

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@mlir/lib/Passes/Mapping/HeuristicMapping.cpp`:
- Around line 284-295: The cost function h computes nswaps as
arch.distanceBetween(hw0, hw1) - 1 but assumes hw0 != hw1; add a defensive check
before using distanceBetween: after calling layout.getHardwareIndices(prog0,
prog1) in h, assert hw0 != hw1 (or if assertions are disabled,
early-continue/skip this pair or handle as zero-cost) to avoid underflow when
prog0==prog1 or both map to the same hardware qubit; reference symbols: function
h, layout.getHardwareIndices, arch.distanceBetween, params.decay, and the loop
over layers.
- Around line 394-402: The loop variable p is misleading because it iterates
synthetic hardware positions, not program qubit indices; rename p to something
clearer (e.g., hwPos or hwIdx) and add a brief comment above the loop explaining
that the range [dynQubits.size(), layout.nqubits()) represents synthetic
hardware slots to be filled with StaticOp instances. Update uses of p in the
loop (the call to layout.getHardwareIndex(p), creation of StaticOp/DeallocOp,
and assignment to statics[hw]) to the new name and keep funcBody, StaticOp,
DeallocOp, dynQubits and statics references intact.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 95719ba4-6bb4-4b67-b8ff-70533c8d9a46

📥 Commits

Reviewing files that changed from the base of the PR and between 5808d74 and 5bceb0b.

📒 Files selected for processing (1)
  • mlir/lib/Passes/Mapping/HeuristicMapping.cpp

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

c++ Anything related to C++ code MLIR Anything related to MLIR

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant