Skip to content

✨ Map qco.if statements by "converge" strategy#1870

Open
MatthiasReumann wants to merge 23 commits into
mainfrom
enh/sc-map-scf-if-stmt
Open

✨ Map qco.if statements by "converge" strategy#1870
MatthiasReumann wants to merge 23 commits into
mainfrom
enh/sc-map-scf-if-stmt

Conversation

@MatthiasReumann

@MatthiasReumann MatthiasReumann commented Jul 8, 2026

Copy link
Copy Markdown
Collaborator

Description

  • Adds a dispatch method to handle the recursive route calling and insertion of epilogue SWAPs.
  • Adds FGraph datastructure to reduce code duplication

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.

If PR contains AI-assisted content:

  • I have disclosed the use of AI tools in the PR description as per our AI Usage Guidelines.
  • AI-assisted commits include an Assisted-by: [Model Name] via [Tool Name] footer.
  • I confirm that I have personally reviewed and understood all AI-generated content, and accept full responsibility for it.

Summary by CodeRabbit

  • New Features

    • Added support for routing and mapping programs with conditional branches, including nested branch handling alongside loops.
    • Expanded validation so mapped programs with conditionals are checked across both branches.
  • Bug Fixes

    • Improved correctness of qubit placement and swap insertion during recursive mapping.
    • Fixed changelog references and cleaned up an internal validation check for conditional values.
  • Tests

    • Added coverage for conditional routing with a GHZ-style circuit and randomized execution order.

@MatthiasReumann MatthiasReumann self-assigned this Jul 8, 2026
@MatthiasReumann MatthiasReumann added c++ Anything related to C++ code MLIR Anything related to MLIR labels Jul 8, 2026
@MatthiasReumann MatthiasReumann added this to the MLIR Support milestone Jul 8, 2026
Comment thread mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp Outdated
@codecov

codecov Bot commented Jul 8, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 96.81818% with 7 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
...lir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp 96.7% 7 Missing ⚠️

📢 Thoughts on this report? Let us know!

@mergify mergify Bot removed the conflict label Jul 9, 2026
@MatthiasReumann MatthiasReumann marked this pull request as ready for review July 9, 2026 10:56
@coderabbitai

coderabbitai Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

This PR extends the QCO mapping/routing pass to support qco::IfOp in addition to scf::ForOp. It corrects tied-yield ownership checks in IfOp.cpp, introduces an FGraph helper refactoring restore/converge, adds IfOp extension and recursive dispatch logic in Mapping.cpp, and adds corresponding unit tests, plus a changelog update.

Changes

IfOp Mapping/Routing Support

Layer / File(s) Summary
IfOp tied-yield validation
mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp
getTiedThenYieldedValue/getTiedElseYieldedValue now validate block-argument ownership via parent-op comparison, returning nullptr on mismatch.
FGraph and restore/converge refactor
mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp, mlir/lib/Dialect/QCO/Utils/Graph.cpp
New FGraph struct encapsulates happy/unhappy SWAP graph logic; restore and converge rebuilt to use FGraph instances.
IfOp region extension in place
mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp
extend() gains an IfOp overload; place() recursively extends then/else regions and pushes them onto the recursion stack.
Recursive dispatch for IfOp branches
mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp
New RecursiveRoutingStack types; advance() pushes IfOp items; new dispatch() routes both branches and converges layouts via converge; route() delegates to dispatch.
Executability checks and GHZ test
mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp
isExecutable adds an IfOp case checking branch layout consistency; new RandomOrderGHZ test exercises branching mapping.
Changelog update
CHANGELOG.md
Adds PR #1870 reference to place-and-route entry and its link definition.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

Suggested labels: enhancement, refactor

Suggested reviewers: burgholzer

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 51.85% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: mapping qco.if statements using a converge strategy.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch enh/sc-map-scf-if-stmt

Warning

Tools execution failed with the following error:

Failed to run tools: 13 INTERNAL: Received RST_STREAM with code 2 (Internal server error)


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.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick comments (2)
mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp (1)

691-691: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value

Remove commented-out debug call.

// entry->dumpPretty(); is a leftover debug artifact.

🧹 Suggested cleanup
-  // entry->dumpPretty();
-
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp` at line 691,
Remove the leftover debug artifact in the test around the entry handling code by
deleting the commented-out entry->dumpPretty() call. Keep the surrounding test
logic intact and make sure no other debug-only commented statements remain in
this area.
mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp (1)

1120-1133: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Inner loop variable i shadows the outer for (size_t i : indices).

The nested for (size_t i = 0; i < children.size(); ++i) in both the forward and backward branches shadows the outer range variable. It happens to be harmless because the outer loop is range-based, but it obscures which i is in scope (and will trip -Wshadow). Consider renaming the inner index.

♻️ Suggested rename
-            if constexpr (Direction == WireDirection::Forward) {
-              for (size_t i = 0; i < children.size(); ++i) {
-                children[i].wires.emplace_back(args[i]);
-                children[i].infos.map(index, prog);
-              }
-            } else {
+            if constexpr (Direction == WireDirection::Forward) {
+              for (size_t c = 0; c < children.size(); ++c) {
+                children[c].wires.emplace_back(args[c]);
+                children[c].infos.map(index, prog);
+              }
+            } else {
               const std::array yields{
                   ifOp.getTiedThenYieldedValue(args[0])->get(),
                   ifOp.getTiedElseYieldedValue(args[1])->get()};
-              for (size_t i = 0; i < children.size(); ++i) {
-                children[i].wires.emplace_back(yields[i]);
-                children[i].infos.map(index, prog);
-              }
+              for (size_t c = 0; c < children.size(); ++c) {
+                children[c].wires.emplace_back(yields[c]);
+                children[c].infos.map(index, prog);
+              }
             }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp` around lines 1120 -
1133, The nested index variable inside the `Direction` branches in `Mapping.cpp`
shadows the outer `for (size_t i : indices)` loop variable, which can trigger
shadowing warnings and makes the scope unclear. Rename the inner loop index in
both branches of the `if constexpr (Direction == WireDirection::Forward)` block
to a distinct name, and update the corresponding `children[...]` accesses
consistently. Keep the change localized to the loop that populates
`children[i].wires` and `children[i].infos.map(index, prog)` so the outer
`indices` loop remains unaffected.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Nitpick comments:
In `@mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp`:
- Around line 1120-1133: The nested index variable inside the `Direction`
branches in `Mapping.cpp` shadows the outer `for (size_t i : indices)` loop
variable, which can trigger shadowing warnings and makes the scope unclear.
Rename the inner loop index in both branches of the `if constexpr (Direction ==
WireDirection::Forward)` block to a distinct name, and update the corresponding
`children[...]` accesses consistently. Keep the change localized to the loop
that populates `children[i].wires` and `children[i].infos.map(index, prog)` so
the outer `indices` loop remains unaffected.

In `@mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp`:
- Line 691: Remove the leftover debug artifact in the test around the entry
handling code by deleting the commented-out entry->dumpPretty() call. Keep the
surrounding test logic intact and make sure no other debug-only commented
statements remain in this area.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: b4f6c932-6662-4390-94eb-dd2943906e53

📥 Commits

Reviewing files that changed from the base of the PR and between c92ea1e and 1906f13.

📒 Files selected for processing (5)
  • CHANGELOG.md
  • mlir/lib/Dialect/QCO/IR/SCF/IfOp.cpp
  • mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp
  • mlir/lib/Dialect/QCO/Utils/Graph.cpp
  • mlir/unittests/Dialect/QCO/Transforms/Mapping/test_mapping.cpp

…g pass

• Addons Construction: Refined the filtering logic in scf::ForOp and IfOp to use SmallPtrSet and llvm::make_filter_range. This achieves $O(N+M)$ complexity, efficiently filtering qubit inputs and eliminating expensive intermediate DenseSet creation and repeated is_contained checks.

• WireInfos Mapping: Replaced DenseMap<size_t, size_t> with std::vector<size_t> for wire-to-program index lookups. This provides $O(1)$ direct access and improves cache locality, which is critical for scalability in larger circuits.
• A Search Optimization: Replaced DenseSet<IndexPairType> with SmallVector<IndexPairType, 8> in the A search expansion set. This leverages the small, constant architecture connectivity to avoid unnecessary hashing and heap allocations in the hot search loop.
@github-actions

github-actions Bot commented Jul 9, 2026

Copy link
Copy Markdown
Contributor

Cpp-Linter Report ⚠️

Some files did not pass the configured checks!

clang-tidy (v22.1.8) reports: 5 concern(s)
  • mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp:272:5: warning: [modernize-use-nodiscard]

    function 'findHappySWAPChain' should be marked [[nodiscard]]

      272 |     std::optional<SmallVector<IndexPairType>> findHappySWAPChain() const {
          |     ^
          |     [[nodiscard]] 
  • mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp:289:5: warning: [modernize-use-nodiscard]

    function 'findUnhappySWAP' should be marked [[nodiscard]]

      289 |     std::optional<IndexPairType> findUnhappySWAP() const {
          |     ^
          |     [[nodiscard]] 
  • mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp:307:5: warning: [modernize-use-nodiscard]

    function 'shouldAddEdge' should be marked [[nodiscard]]

      307 |     bool shouldAddEdge(const size_t u, const size_t v, const Layout& from,
          |     ^
          |     [[nodiscard]] 
  • mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp:786:3: warning: [modernize-use-nodiscard]

    function 'restore' should be marked [[nodiscard]]

      786 |   SmallVector<IndexPairType> restore(const Layout& from,
          |   ^
          |   [[nodiscard]] 
  • mlir/lib/Dialect/QCO/Transforms/Mapping/Mapping.cpp:822:3: warning: [modernize-use-nodiscard]

    function 'converge' should be marked [[nodiscard]]

      822 |   std::pair<SmallVector<IndexPairType>, SmallVector<IndexPairType>>
          |   ^
          |   [[nodiscard]] 

Have any feedback or feature suggestions? Share it here.

@burgholzer burgholzer left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Spent way too much time with cc63e32 now probably 😅

Added an AI-summary to the commit message as well with some more details. Overall, this should improve the efficiency of quite a few of the code pieces in the mapping pass.
One of the main arguments was that DenseSet is pretty heavy when the number of elements is small and a simple linear search over a small vector is typically faster and more cache friendly.

Similarly, I think it is safe to assume that we can contiguously index the program and hardware qubits (even though the hardware identifiers may not be contiguous for some architectures). I haven't really come across relevant modern architectures that do not index their hardware qubits from 0...N-1. Hence, the program to hardware mapping can be realized with SmallVectors instead of DenseSets as well.

I also optimized some of the SCF tracking code to compute fewer temporaries.

Let me know how you feel about the changes. I am happy with this as it is now 🙌🏼

Edit: I'll leave adding the 5 [[nodiscard]s to you 😌

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.

2 participants