Skip to content

Conversation

@denialhaag
Copy link
Member

Description

This PR fully replaces pybind11 with nanobind. This change will allow us to ship Stable ABI wheels, saving us PyPI space.

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.

@denialhaag denialhaag self-assigned this Jan 13, 2026
@denialhaag denialhaag added python Anything related to Python code c++ Anything related to C++ code packaging Anything related to Python packaging labels Jan 13, 2026
@denialhaag denialhaag added minor Changes leading to a minor version increase refactor Anything related to code refactoring labels Jan 13, 2026
@github-project-automation github-project-automation bot moved this to In Progress in MQT Compilation Jan 13, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 13, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Stable ABI wheel support added for Python 3.12+, improving cross‑Python binary compatibility.
  • Chores

    • Migrated Python bindings to a new backend and updated packaging/build configs to enable Stable ABI wheels.
    • Upgraded core dependency versions to 3.4.0.
  • Documentation

    • Updated changelog and upgrade guide with migration and CI/CD packaging notes.

✏️ Tip: You can customize this high-level summary in your review settings.

Walkthrough

Migrate Python bindings from pybind11 to nanobind across many modules, update CI/build (pyproject, CMake, GitHub Actions), remove pybind11_json, bump MQT Core to 3.4.0, and add Stable ABI / CPython 3.12 wheel packaging and changelog entries.

Changes

Cohort / File(s) Summary
CI & Packaging
\.github/workflows/ci.yml, pyproject.toml, .pre-commit-config.yaml
Replace pybind11nanobind in CI/build deps, add Stable ABI (cp312) wheel settings and cibuildwheel overrides, update auditwheel repair exclusions, and change pre-commit pattern PyBindNanobind.
Top-level CMake & deps
CMakeLists.txt, cmake/ExternalDependencies.cmake
Use ${SKBUILD_SABI_COMPONENT} in Python find_package, replace pybind11 integration with nanobind (nanobind --cmake_dir + find_package(nanobind CONFIG REQUIRED)), remove pybind11_json FetchContent, and bump MQT Core version/revision to 3.4.0.
Changelog & Docs
CHANGELOG.md, UPGRADING.md
Add migration notes and "Stable ABI wheels" section, restructure Unreleased/3.5.0 entries, add PR link, and adjust CI→CI/CD wording.
Binding helper invocations (CMake)
bindings/*/*/CMakeLists.txt (e.g., bindings/clifford_synthesis/CMakeLists.txt, bindings/hybrid_mapper/CMakeLists.txt, bindings/na/..., bindings/sc/CMakeLists.txt)
Replace add_mqt_python_binding(...) with add_mqt_python_binding_nanobind(...) and remove pybind11_json from LINK_LIBS across binding CMake files.
Clifford synthesis bindings
bindings/clifford_synthesis/clifford_synthesis.cpp
Migrate to NB_MODULE, convert enums/classes to nb::enum_ / nb::class_, switch constructors and property bindings (nb::init, def_rw/def_prop_ro), add JSON wrapper and import mqt.core.ir.
Hybrid mapper bindings
bindings/hybrid_mapper/hybrid_mapper.cpp
Full pybind11→nanobind migration: enums, classes (MapperParameters, MapperStats, NeutralAtomHybridArchitecture, HybridNAMapper, HybridSynthesisMapper), constructors, keep_alive and property bindings (def_rw/def_prop_ro).
NASP bindings
bindings/na/nasp/nasp.cpp
Migrate NASolver and nested Result bindings to nanobind (NB_MODULE, nb::class_), adjust defaults and JSON exposure using Python json.
Zoned NA bindings
bindings/na/zoned/zoned.cpp
Migrate to nanobind, change constructor pattern to __init__ lambdas with placement new(self), update stats/json to return Python objects via json.loads, and adjust exposed fields/params.
SC bindings
bindings/sc/sc.cpp
Extensive migration of enums/classes/constructors/overloads and JSON wrappers from pybind11 to nanobind (nb::enum_, nb::class_, def_rw/def_prop_rw, nb::overload_cast).

Sequence Diagram(s)

(omitted — changes are primarily API/binding and build migrations, not new multi-component runtime control flow)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

dependencies, enhancement

Suggested reviewers

  • burgholzer

Poem

🐰 From PyBind burrow I hopped to Nanobind's glade,
enums and classes stitched, a nimble binding braid.
Wheels for three-one-two now spin with steady cheer,
cmake carrots ready — bindings snug and clear. 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 50.00% 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 clearly and concisely summarizes the main change: replacing pybind11 with nanobind. It directly reflects the primary objective of the PR.
Description check ✅ Passed The PR description includes a brief explanation of the change and its benefit (Stable ABI wheels), but most checklist items are marked complete while one item about testing is unchecked with a strikethrough, suggesting tests were not added for this refactor.

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


📜 Recent review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2b09400 and 05c9825.

📒 Files selected for processing (1)
  • CHANGELOG.md
🧰 Additional context used
🧠 Learnings (3)
📓 Common learnings
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/dd/register_matrix_dds.cpp:64-109
Timestamp: 2025-12-15T01:54:22.129Z
Learning: In the munich-quantum-toolkit/core repository, after migrating to nanobind, docstrings for Python bindings are now added directly in the C++ binding code (using R"pb(...)pb" syntax) and stub files (.pyi) are auto-generated using the `bindings/generate-stubs.sh` script. This replaces the previous pybind11 approach where docstrings were manually maintained in stub files.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qcec PR: 817
File: pyproject.toml:81-82
Timestamp: 2026-01-09T17:58:10.350Z
Learning: In the Munich Quantum Toolkit projects using nanobind, setting `wheel.py-api = "cp312"` in `[tool.scikit-build]` enables Stable ABI wheels only for Python 3.12+ (where nanobind supports it), while automatically building regular non-ABI3 wheels for earlier Python versions (3.10, 3.11) and free-threading builds (3.14t). This allows a single configuration to appropriately handle both old and new Python versions without forcing incompatible ABI requirements.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: bindings/fomac/fomac.cpp:227-264
Timestamp: 2025-12-07T01:21:27.544Z
Learning: In the munich-quantum-toolkit/core repository, docstrings for Python bindings are added to the corresponding stub files (.pyi) rather than directly in the pybind11 C++ bindings code. This practice may change if the project adopts nanobind with automatic stub generation.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1246
File: bindings/CMakeLists.txt:0-0
Timestamp: 2025-10-10T08:09:54.528Z
Learning: In the Munich Quantum Toolkit (MQT) Core project, scikit-build-core is configured with `wheel.install-dir = "mqt/core"` in pyproject.toml, which automatically prefixes all CMake `DESTINATION` paths with `mqt/core/` during wheel installation. Therefore, CMake install destinations are relative to the `mqt/core` package namespace, not `site-packages`.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/fomac/fomac.cpp:111-116
Timestamp: 2025-12-19T00:05:54.428Z
Learning: In the munich-quantum-toolkit/core repository after migrating to nanobind, lifetime management differs from pybind11: `nb::keep_alive<nurse, patient>()` does not exist in nanobind. Instead, use `nb::rv_policy::reference_internal` when binding methods that return objects referencing internal state of the parent object (e.g., Session::getDevices returning Device objects that depend on the Session). This tells nanobind to keep the parent alive while children exist.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: python/mqt/core/ir/operations.pyi:9-16
Timestamp: 2025-12-15T01:59:17.023Z
Learning: In the munich-quantum-toolkit/core repository, stub files (.pyi) are auto-generated by nanobind's stubgen tool and should not be manually modified for style preferences, as changes would be overwritten during regeneration.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qmap PR: 862
File: pyproject.toml:65-66
Timestamp: 2025-12-13T20:08:45.549Z
Learning: In the qmap project (pyproject.toml), maintain broad compatibility with dependencies across supported Python versions. Avoid artificially raising minimum version requirements unless there's a specific need (e.g., to guarantee binary wheel availability for certain Python versions, or to access required features). The goal is to keep the software as broadly compatible as possible with the rest of the ecosystem.
📚 Learning: 2026-01-09T17:58:10.350Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qcec PR: 817
File: pyproject.toml:81-82
Timestamp: 2026-01-09T17:58:10.350Z
Learning: In the Munich Quantum Toolkit projects using nanobind, setting `wheel.py-api = "cp312"` in `[tool.scikit-build]` enables Stable ABI wheels only for Python 3.12+ (where nanobind supports it), while automatically building regular non-ABI3 wheels for earlier Python versions (3.10, 3.11) and free-threading builds (3.14t). This allows a single configuration to appropriately handle both old and new Python versions without forcing incompatible ABI requirements.

Applied to files:

  • CHANGELOG.md
📚 Learning: 2025-10-14T14:37:38.047Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/yaqs PR: 212
File: CHANGELOG.md:12-15
Timestamp: 2025-10-14T14:37:38.047Z
Learning: In the munich-quantum-toolkit/yaqs project, changelog entries follow the template: "- $TITLE ([#$NUMBER]($URL)) ([**AUTHOR**](https://github.com/$AUTHOR))". Issue references should not be included in changelog entries; the PR number is sufficient for traceability.

Applied to files:

  • CHANGELOG.md
🪛 LanguageTool
CHANGELOG.md

[typographical] ~19-~19: To join two clauses or introduce examples, consider using an em dash.
Context: ...([#911]) ([@denialhaag]) ## [3.5.0] - 2025-12-16 _If you are upgrading: pleas...

(DASH_RULE)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (13)
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🇨‌ Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Debug) / 🍎 macos-14 clang Debug
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Debug) / 🏁 windows-2022 msvc Debug
  • GitHub Check: 🇨‌ Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
  • GitHub Check: 🇨‌ Coverage / 📈 Coverage
🔇 Additional comments (1)
CHANGELOG.md (1)

14-18: LGTM! Changelog entries accurately document the migration.

The entries are well-formatted, correctly categorized under "Changed", and accurately reflect the migration from pybind11 to nanobind along with the introduction of Stable ABI wheels for Python 3.12+. The format follows the project's changelog conventions.


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

🤖 Fix all issues with AI agents
In @bindings/clifford_synthesis/clifford_synthesis.cpp:
- Around line 197-200: The call to synthesizer.def using
nb::init<qc::QuantumComputation&, bool>() is missing a comma between the
argument specifier "use_destabilizers"_a and the following docstring which
causes a compilation error; fix by inserting a comma after "use_destabilizers"_a
so the parameter list and the docstring are separate arguments in the
synthesizer.def invocation.

In @bindings/hybrid_mapper/hybrid_mapper.cpp:
- Around line 43-47: The enum registration for nb::enum_<na::InitialMapping> is
missing a comma between the enum name and its docstring, causing adjacent string
literal concatenation; update the call that starts
nb::enum_<na::InitialMapping>(m, "InitialCircuitMapping" "Initial mapping
between circuit qubits and hardware qubits.") to include the missing comma so
the enum name is "InitialCircuitMapping" and the docstring is passed separately
(i.e., separate the two string literals in the nb::enum_ call that uses m and
"InitialCircuitMapping").

In @bindings/sc/sc.cpp:
- Line 83: Update the module docstring set via m.doc() to replace the outdated
"pybind11" reference with "nanobind" (e.g., change the string in m.doc() to
mention nanobind and the MQT QMAP quantum circuit mapping tool) so the binding
description accurately reflects the current binding library.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f4bbd71 and e90a13e.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (17)
  • .github/workflows/ci.yml
  • .pre-commit-config.yaml
  • CHANGELOG.md
  • CMakeLists.txt
  • UPGRADING.md
  • bindings/clifford_synthesis/CMakeLists.txt
  • bindings/clifford_synthesis/clifford_synthesis.cpp
  • bindings/hybrid_mapper/CMakeLists.txt
  • bindings/hybrid_mapper/hybrid_mapper.cpp
  • bindings/na/nasp/CMakeLists.txt
  • bindings/na/nasp/nasp.cpp
  • bindings/na/zoned/CMakeLists.txt
  • bindings/na/zoned/zoned.cpp
  • bindings/sc/CMakeLists.txt
  • bindings/sc/sc.cpp
  • cmake/ExternalDependencies.cmake
  • pyproject.toml
🧰 Additional context used
🧠 Learnings (29)
📓 Common learnings
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/dd/register_matrix_dds.cpp:64-109
Timestamp: 2025-12-15T01:54:22.129Z
Learning: In the munich-quantum-toolkit/core repository, after migrating to nanobind, docstrings for Python bindings are now added directly in the C++ binding code (using R"pb(...)pb" syntax) and stub files (.pyi) are auto-generated using the `bindings/generate-stubs.sh` script. This replaces the previous pybind11 approach where docstrings were manually maintained in stub files.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qcec PR: 817
File: pyproject.toml:81-82
Timestamp: 2026-01-09T17:58:10.350Z
Learning: In the Munich Quantum Toolkit projects using nanobind, setting `wheel.py-api = "cp312"` in `[tool.scikit-build]` enables Stable ABI wheels only for Python 3.12+ (where nanobind supports it), while automatically building regular non-ABI3 wheels for earlier Python versions (3.10, 3.11) and free-threading builds (3.14t). This allows a single configuration to appropriately handle both old and new Python versions without forcing incompatible ABI requirements.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: bindings/fomac/fomac.cpp:227-264
Timestamp: 2025-12-07T01:21:27.544Z
Learning: In the munich-quantum-toolkit/core repository, docstrings for Python bindings are added to the corresponding stub files (.pyi) rather than directly in the pybind11 C++ bindings code. This practice may change if the project adopts nanobind with automatic stub generation.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/fomac/fomac.cpp:111-116
Timestamp: 2025-12-19T00:05:54.428Z
Learning: In the munich-quantum-toolkit/core repository after migrating to nanobind, lifetime management differs from pybind11: `nb::keep_alive<nurse, patient>()` does not exist in nanobind. Instead, use `nb::rv_policy::reference_internal` when binding methods that return objects referencing internal state of the parent object (e.g., Session::getDevices returning Device objects that depend on the Session). This tells nanobind to keep the parent alive while children exist.
📚 Learning: 2026-01-09T17:58:10.350Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qcec PR: 817
File: pyproject.toml:81-82
Timestamp: 2026-01-09T17:58:10.350Z
Learning: In the Munich Quantum Toolkit projects using nanobind, setting `wheel.py-api = "cp312"` in `[tool.scikit-build]` enables Stable ABI wheels only for Python 3.12+ (where nanobind supports it), while automatically building regular non-ABI3 wheels for earlier Python versions (3.10, 3.11) and free-threading builds (3.14t). This allows a single configuration to appropriately handle both old and new Python versions without forcing incompatible ABI requirements.

Applied to files:

  • UPGRADING.md
  • bindings/sc/CMakeLists.txt
  • .github/workflows/ci.yml
  • pyproject.toml
  • CHANGELOG.md
  • cmake/ExternalDependencies.cmake
📚 Learning: 2025-10-09T22:15:59.924Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1246
File: pyproject.toml:340-341
Timestamp: 2025-10-09T22:15:59.924Z
Learning: Qiskit publishes ABI3 wheels (e.g., cp39-abi3) that are forward-compatible with newer Python versions including Python 3.14, so no explicit Python 3.14 wheels are required for qiskit to work on Python 3.14.

Applied to files:

  • UPGRADING.md
  • pyproject.toml
📚 Learning: 2025-12-15T01:54:22.129Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/dd/register_matrix_dds.cpp:64-109
Timestamp: 2025-12-15T01:54:22.129Z
Learning: In the munich-quantum-toolkit/core repository, after migrating to nanobind, docstrings for Python bindings are now added directly in the C++ binding code (using R"pb(...)pb" syntax) and stub files (.pyi) are auto-generated using the `bindings/generate-stubs.sh` script. This replaces the previous pybind11 approach where docstrings were manually maintained in stub files.

Applied to files:

  • UPGRADING.md
  • bindings/sc/CMakeLists.txt
  • .github/workflows/ci.yml
  • bindings/na/nasp/CMakeLists.txt
  • bindings/hybrid_mapper/CMakeLists.txt
  • bindings/na/zoned/CMakeLists.txt
  • bindings/clifford_synthesis/CMakeLists.txt
  • pyproject.toml
  • cmake/ExternalDependencies.cmake
  • bindings/na/nasp/nasp.cpp
  • bindings/hybrid_mapper/hybrid_mapper.cpp
  • bindings/sc/sc.cpp
  • bindings/clifford_synthesis/clifford_synthesis.cpp
  • bindings/na/zoned/zoned.cpp
📚 Learning: 2025-10-11T19:39:32.050Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/debugger PR: 160
File: pyproject.toml:54-54
Timestamp: 2025-10-11T19:39:32.050Z
Learning: Qiskit packages use cp39-abi3 wheels (stable ABI) which are forward-compatible with Python 3.9+ including Python 3.14, even if the package classifiers don't explicitly list Python 3.14 support.

Applied to files:

  • UPGRADING.md
  • pyproject.toml
📚 Learning: 2025-12-21T22:35:08.572Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/fomac/fomac.cpp:348-364
Timestamp: 2025-12-21T22:35:08.572Z
Learning: In the munich-quantum-toolkit/core repository's nanobind bindings, use `.sig("...")` on parameter arguments that have vector or container defaults (e.g., `"sites"_a.sig("...") = std::vector<fomac::Session::Device::Site>{}`) to prevent exposing mutable defaults in the Python API, which would be flagged as a code smell by Python linters. This pattern is preferred over removing `.sig("...")` even though it shows `...` in the stub signature.

Applied to files:

  • bindings/sc/CMakeLists.txt
  • bindings/na/nasp/CMakeLists.txt
  • bindings/hybrid_mapper/CMakeLists.txt
  • bindings/na/zoned/CMakeLists.txt
  • bindings/clifford_synthesis/CMakeLists.txt
  • bindings/sc/sc.cpp
📚 Learning: 2025-12-07T01:21:27.544Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: bindings/fomac/fomac.cpp:227-264
Timestamp: 2025-12-07T01:21:27.544Z
Learning: In the munich-quantum-toolkit/core repository, docstrings for Python bindings are added to the corresponding stub files (.pyi) rather than directly in the pybind11 C++ bindings code. This practice may change if the project adopts nanobind with automatic stub generation.

Applied to files:

  • bindings/sc/CMakeLists.txt
  • bindings/na/nasp/CMakeLists.txt
  • bindings/hybrid_mapper/CMakeLists.txt
  • bindings/na/zoned/CMakeLists.txt
  • bindings/clifford_synthesis/CMakeLists.txt
  • pyproject.toml
  • bindings/na/nasp/nasp.cpp
  • bindings/hybrid_mapper/hybrid_mapper.cpp
  • bindings/sc/sc.cpp
  • bindings/clifford_synthesis/clifford_synthesis.cpp
📚 Learning: 2025-12-28T17:14:53.890Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1403
File: src/qdmi/na/CMakeLists.txt:31-38
Timestamp: 2025-12-28T17:14:53.890Z
Learning: In the munich-quantum-toolkit/core repository, the NA device generator target (mqt-core-qdmi-na-device-gen) is intentionally propagated to MQT_CORE_TARGETS via list(APPEND) because it's publicly linked to the NA device library (the NA device uses a public function from the generator). The SC device generator is not propagated because it has no such dependency with the SC device library.

Applied to files:

  • bindings/sc/CMakeLists.txt
  • bindings/na/nasp/CMakeLists.txt
  • bindings/hybrid_mapper/CMakeLists.txt
  • bindings/na/zoned/CMakeLists.txt
  • bindings/clifford_synthesis/CMakeLists.txt
  • cmake/ExternalDependencies.cmake
  • bindings/na/nasp/nasp.cpp
📚 Learning: 2025-12-22T01:25:21.609Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.

Applied to files:

  • bindings/sc/CMakeLists.txt
  • bindings/na/nasp/CMakeLists.txt
  • bindings/hybrid_mapper/CMakeLists.txt
  • bindings/na/zoned/CMakeLists.txt
  • bindings/clifford_synthesis/CMakeLists.txt
  • bindings/na/nasp/nasp.cpp
  • bindings/hybrid_mapper/hybrid_mapper.cpp
  • bindings/sc/sc.cpp
  • bindings/clifford_synthesis/clifford_synthesis.cpp
  • bindings/na/zoned/zoned.cpp
📚 Learning: 2025-12-19T00:05:54.428Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/fomac/fomac.cpp:111-116
Timestamp: 2025-12-19T00:05:54.428Z
Learning: In the munich-quantum-toolkit/core repository after migrating to nanobind, lifetime management differs from pybind11: `nb::keep_alive<nurse, patient>()` does not exist in nanobind. Instead, use `nb::rv_policy::reference_internal` when binding methods that return objects referencing internal state of the parent object (e.g., Session::getDevices returning Device objects that depend on the Session). This tells nanobind to keep the parent alive while children exist.

Applied to files:

  • bindings/sc/CMakeLists.txt
  • bindings/na/nasp/CMakeLists.txt
  • bindings/hybrid_mapper/CMakeLists.txt
  • bindings/na/zoned/CMakeLists.txt
  • bindings/na/nasp/nasp.cpp
  • bindings/hybrid_mapper/hybrid_mapper.cpp
  • bindings/sc/sc.cpp
  • bindings/clifford_synthesis/clifford_synthesis.cpp
  • bindings/na/zoned/zoned.cpp
📚 Learning: 2025-10-10T08:09:54.528Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1246
File: bindings/CMakeLists.txt:0-0
Timestamp: 2025-10-10T08:09:54.528Z
Learning: In the Munich Quantum Toolkit (MQT) Core project, scikit-build-core is configured with `wheel.install-dir = "mqt/core"` in pyproject.toml, which automatically prefixes all CMake `DESTINATION` paths with `mqt/core/` during wheel installation. Therefore, CMake install destinations are relative to the `mqt/core` package namespace, not `site-packages`.

Applied to files:

  • bindings/sc/CMakeLists.txt
  • .github/workflows/ci.yml
  • bindings/na/nasp/CMakeLists.txt
  • bindings/hybrid_mapper/CMakeLists.txt
  • bindings/na/zoned/CMakeLists.txt
  • bindings/clifford_synthesis/CMakeLists.txt
  • pyproject.toml
  • cmake/ExternalDependencies.cmake
📚 Learning: 2025-10-10T08:10:16.394Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1246
File: test/python/na/test_na_fomac.py:35-0
Timestamp: 2025-10-10T08:10:16.394Z
Learning: In the munich-quantum-toolkit/core repository, scikit-build-core is configured with `wheel.install-dir = "mqt/core"` in pyproject.toml, which means CMake `install()` commands with `DESTINATION <path>` install files relative to `mqt/core/` in the wheel, making them accessible via `files("mqt.core").joinpath("<path>")`.

Applied to files:

  • bindings/sc/CMakeLists.txt
  • bindings/hybrid_mapper/CMakeLists.txt
  • bindings/clifford_synthesis/CMakeLists.txt
  • pyproject.toml
  • cmake/ExternalDependencies.cmake
📚 Learning: 2025-12-15T01:59:17.023Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: python/mqt/core/ir/operations.pyi:9-16
Timestamp: 2025-12-15T01:59:17.023Z
Learning: In the munich-quantum-toolkit/core repository, stub files (.pyi) are auto-generated by nanobind's stubgen tool and should not be manually modified for style preferences, as changes would be overwritten during regeneration.

Applied to files:

  • bindings/sc/CMakeLists.txt
  • bindings/na/nasp/CMakeLists.txt
  • bindings/hybrid_mapper/CMakeLists.txt
  • bindings/clifford_synthesis/CMakeLists.txt
  • bindings/hybrid_mapper/hybrid_mapper.cpp
📚 Learning: 2026-01-04T23:54:33.540Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1356
File: src/qdmi/na/CMakeLists.txt:179-184
Timestamp: 2026-01-04T23:54:33.540Z
Learning: In the munich-quantum-toolkit/core repository, MSVC link-time code generation (LTCG) must be disabled for the NA device dynamic library target (mqt-core-qdmi-na-device-dyn) on Windows to avoid link errors and missing symbols. This issue affects both release-only builds and debug/release mixed builds, not just debug/release mixing scenarios.

Applied to files:

  • bindings/sc/CMakeLists.txt
  • bindings/na/nasp/CMakeLists.txt
  • bindings/na/zoned/CMakeLists.txt
📚 Learning: 2025-12-28T17:13:36.900Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1403
File: pyproject.toml:98-102
Timestamp: 2025-12-28T17:13:36.900Z
Learning: In the munich-quantum-toolkit/core project, scikit-build-core is intelligent enough to skip build targets listed in pyproject.toml that don't exist for a given platform, so platform-specific targets (like `-dyn` targets conditioned on `NOT WIN32`) can be unconditionally listed in `build.targets` without causing Windows build failures.

Applied to files:

  • bindings/sc/CMakeLists.txt
  • pyproject.toml
📚 Learning: 2025-12-13T20:08:45.549Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qmap PR: 862
File: pyproject.toml:65-66
Timestamp: 2025-12-13T20:08:45.549Z
Learning: In the qmap project (pyproject.toml), maintain broad compatibility with dependencies across supported Python versions. Avoid artificially raising minimum version requirements unless there's a specific need (e.g., to guarantee binary wheel availability for certain Python versions, or to access required features). The goal is to keep the software as broadly compatible as possible with the rest of the ecosystem.

Applied to files:

  • .github/workflows/ci.yml
📚 Learning: 2025-11-04T15:22:19.558Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/conftest.py:155-157
Timestamp: 2025-11-04T15:22:19.558Z
Learning: The munich-quantum-toolkit/core repository requires Python 3.10 or later, so Python 3.10+ features (such as `zip(..., strict=...)`, pattern matching, etc.) are acceptable and should not be flagged as compatibility issues.

Applied to files:

  • .github/workflows/ci.yml
  • pyproject.toml
📚 Learning: 2025-10-09T13:14:10.178Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:219-221
Timestamp: 2025-10-09T13:14:10.178Z
Learning: The MQT Core project (munich-quantum-toolkit/core repository) uses the C++20 standard, not C++17. C++20 features such as abbreviated function templates (e.g., `const auto&` parameters) are supported and valid in this codebase.

Applied to files:

  • .github/workflows/ci.yml
  • bindings/na/nasp/CMakeLists.txt
  • bindings/na/zoned/CMakeLists.txt
  • cmake/ExternalDependencies.cmake
  • bindings/na/nasp/nasp.cpp
📚 Learning: 2026-01-09T00:27:20.419Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 39
File: .pre-commit-config.yaml:135-135
Timestamp: 2026-01-09T00:27:20.419Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, `.pre-commit-config.yaml` intentionally uses `https://github.com/adhtruong/mirrors-typos` instead of the official `https://github.com/crate-ci/typos` repository to avoid pre-commit autoupdate tag confusion described in https://github.com/crate-ci/typos/issues/390.

Applied to files:

  • .pre-commit-config.yaml
📚 Learning: 2025-12-13T20:08:32.333Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qmap PR: 862
File: pyproject.toml:65-66
Timestamp: 2025-12-13T20:08:32.333Z
Learning: In pyproject.toml, maintain broad compatibility by avoiding unnecessary increases to the minimum Python version. Only raise minimum version when there is a concrete need (e.g., to guarantee binary wheel availability or access to required features). This helps keep the project compatible with the ecosystem and other dependencies across supported Python versions.

Applied to files:

  • pyproject.toml
📚 Learning: 2025-12-14T16:51:52.504Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 23
File: .readthedocs.yaml:13-18
Timestamp: 2025-12-14T16:51:52.504Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, LLVM and MLIR toolchains are required for the documentation build because `uv run` includes a full build of the package, which compiles C++/MLIR extensions using scikit-build-core.

Applied to files:

  • pyproject.toml
📚 Learning: 2025-10-14T14:37:38.047Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/yaqs PR: 212
File: CHANGELOG.md:12-15
Timestamp: 2025-10-14T14:37:38.047Z
Learning: In the munich-quantum-toolkit/yaqs project, changelog entries follow the template: "- $TITLE ([#$NUMBER]($URL)) ([**AUTHOR**](https://github.com/$AUTHOR))". Issue references should not be included in changelog entries; the PR number is sufficient for traceability.

Applied to files:

  • CHANGELOG.md
📚 Learning: 2025-12-05T17:45:37.602Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1360
File: .github/workflows/reusable-mlir-tests.yml:40-43
Timestamp: 2025-12-05T17:45:37.602Z
Learning: In the munich-quantum-toolkit/core repository, patch releases of LLVM dependencies don't require documentation updates, changelog entries, or additional tests beyond what's validated by passing CI checks.

Applied to files:

  • cmake/ExternalDependencies.cmake
📚 Learning: 2025-12-05T15:57:39.701Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 3
File: lib/Conversion/CatalystQuantumToMQTOpt/CMakeLists.txt:22-25
Timestamp: 2025-12-05T15:57:39.701Z
Learning: The munich-quantum-toolkit projects (core and core-plugins-catalyst) use `file(GLOB_RECURSE ...)` patterns in CMakeLists.txt files to collect header files, following an established convention in the parent repository for consistency across the codebase.

Applied to files:

  • cmake/ExternalDependencies.cmake
📚 Learning: 2025-11-03T23:09:26.881Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/CMakeLists.txt:9-21
Timestamp: 2025-11-03T23:09:26.881Z
Learning: The CMake functions `generate_device_defs_executable` and `generate_prefixed_qdmi_headers` used in QDMI device test CMakeLists.txt files are provided by the external QDMI library (fetched via FetchContent from https://github.com/Munich-Quantum-Software-Stack/qdmi.git), specifically in the cmake/PrefixHandling.cmake module of the QDMI repository.

Applied to files:

  • cmake/ExternalDependencies.cmake
📚 Learning: 2025-11-20T18:19:31.857Z
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: src/hybridmap/HybridNeutralAtomMapper.cpp:1679-1715
Timestamp: 2025-11-20T18:19:31.857Z
Learning: In src/hybridmap/HybridNeutralAtomMapper.cpp, the compareSwapAndBridge and compareShuttlingAndFlyingAncilla methods intentionally use division by zero to produce infinity when distance reductions are zero or negative. This infinity value marks that option as highly unfavorable in cost comparisons, which is the intended behavior rather than a bug.

Applied to files:

  • bindings/hybrid_mapper/hybrid_mapper.cpp
📚 Learning: 2025-11-27T08:52:06.428Z
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: src/hybridmap/HybridNeutralAtomMapper.cpp:1379-1383
Timestamp: 2025-11-27T08:52:06.428Z
Learning: In src/hybridmap/HybridNeutralAtomMapper.cpp, the getBestMovePos function throws std::runtime_error when no move position is found (finalBestPos.coords is empty). This behavior is acceptable for current use cases since tight architectures are not a concern. A future improvement would be to fall back to SWAP-based strategies instead of throwing.

Applied to files:

  • bindings/hybrid_mapper/hybrid_mapper.cpp
📚 Learning: 2025-11-05T09:23:46.540Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: src/qdmi/dd/Device.cpp:492-521
Timestamp: 2025-11-05T09:23:46.540Z
Learning: In the munich-quantum-toolkit/core repository, the `qasm3::Importer::imports()` function is backwards compatible with OpenQASM 2 programs. Therefore, it can be used to parse both QASM2 and QASM3 program formats without requiring separate importers for each version.

Applied to files:

  • bindings/clifford_synthesis/clifford_synthesis.cpp
📚 Learning: 2025-12-04T06:59:40.314Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1301
File: mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp:84-85
Timestamp: 2025-12-04T06:59:40.314Z
Learning: In the MQTOpt MLIR routing passes (NaiveRoutingPassSC, AStarRoutingPassSC), the input IR is guaranteed to contain only 1-qubit and 2-qubit gates. All 3+-qubit gates must be decomposed before routing; otherwise the input IR is invalid. This invariant allows skipTwoQubitBlock in LayeredUnit.cpp to safely assert wires.size() == 2.

Applied to files:

  • bindings/na/zoned/zoned.cpp
🔇 Additional comments (36)
.github/workflows/ci.yml (1)

158-158: LGTM!

The nanobind version (2.10.2) correctly matches the version specified in pyproject.toml build-system requirements, ensuring consistency between CI linting and the actual build.

UPGRADING.md (2)

7-17: LGTM!

The documentation clearly explains the Stable ABI wheel migration and accurately reflects the wheel matrix. Based on learnings, the cp312 Stable ABI configuration in nanobind correctly handles Python 3.10/3.11 with regular wheels while providing Stable ABI wheels for 3.12+.


41-41: LGTM!

The wording change from "CI" to "CI/CD" is appropriate as it reflects that both continuous integration and deployment pipelines benefit from reduced build times.

CHANGELOG.md (2)

186-186: PR link added correctly.

The PR link format follows the established convention in the changelog.


14-17: Verify the PR number references in the changelog entries.

The changelog references [#909] but I cannot locate this PR in the public repository to verify if it's the correct reference for the nanobind migration and Stable ABI wheel changes. Please confirm that #909 is the intended PR number and ensure the changelog accurately reflects the PR that introduced these changes.

.pre-commit-config.yaml (1)

137-137: LGTM!

The capitalization check correctly updated to catch improper capitalization of Nanobind (should be lowercase nanobind), reflecting the library naming convention after the migration.

pyproject.toml (5)

11-14: LGTM!

The build-system requires correctly updated to use nanobind>=2.10.2 and mqt.core~=3.4.0, maintaining version consistency with runtime dependencies.


86-87: LGTM!

The wheel.py-api = "cp312" configuration correctly enables Stable ABI wheels for Python 3.12+ while automatically building regular non-ABI3 wheels for earlier Python versions (3.10, 3.11) and free-threading builds (3.14t). Based on learnings from the Munich Quantum Toolkit projects, this is the expected configuration for nanobind-based Stable ABI support.


350-354: LGTM!

The auditwheel exclusions correctly updated to reference the new 3.4 version suffix for mqt-core shared libraries, aligning with the mqt.core~=3.4.0 dependency.


424-424: LGTM!

The test dependency mqt.qcec>=3.4.0 correctly updated to align with the mqt.core version bump.


370-373: The selector cp312-* correctly covers all Stable ABI wheels. When wheel.py-api = "cp312" is set, a single Stable ABI wheel tagged cp312-abi3 is produced and is forward-compatible with Python 3.12+; separate wheels tagged cp313-abi3 or cp314-abi3 are not created. The cp312-* selector matches this wheel appropriately.

Likely an incorrect or invalid review comment.

bindings/na/nasp/nasp.cpp (2)

19-31: LGTM: Nanobind migration setup is correct.

The namespace alias, module declaration, and mqt.core.ir import follow the established nanobind migration pattern used across the MQT ecosystem.


34-50: LGTM: Class bindings and JSON exposure are correctly migrated.

The NASolver and NASolver::Result bindings correctly use nanobind conventions:

  • nb::init<>() for constructors
  • nb::none() for optional parameter defaults
  • JSON conversion via Python's json.loads is the expected pattern when migrating from pybind11_json
bindings/na/zoned/zoned.cpp (3)

76-97: LGTM: Nanobind constructor binding with placement new and keep_alive.

The __init__ binding using placement new (new (self) ...) combined with nb::keep_alive<1, 2>() is the correct nanobind pattern for custom constructor logic where the compiler object needs to outlive the architecture reference. This properly mirrors the pybind11 keep_alive semantics for constructor bindings.


130-136: LGTM: JSON statistics exposure is correct.

The stats() method correctly imports Python's json module and uses json.loads to convert the dumped nlohmann::json to a Python object, which is the standard pattern after removing pybind11_json dependency.


145-182: LGTM: RoutingAwareCompiler binding follows the same correct pattern.

The constructor binding with placement new and nb::keep_alive<1, 2>() is consistent with the RoutingAgnosticCompiler binding.

bindings/hybrid_mapper/hybrid_mapper.cpp (3)

49-88: LGTM: MapperParameters binding with def_rw is correct.

The def_rw bindings correctly expose read-write properties in nanobind, which is the equivalent of pybind11's def_readwrite.


103-135: LGTM: NeutralAtomArchitecture binding correctly uses def_rw and def_prop_ro.

The mix of def_rw for writable attributes and def_prop_ro for read-only getter-based properties follows nanobind conventions correctly.


153-164: LGTM: NeutralAtomMapper lifetime management is correct.

Using nb::keep_alive<1, 2>() and nb::keep_alive<1, 3>() ensures that both arch and params outlive the mapper instance, which is the correct approach for constructor arguments that are stored by reference.

bindings/sc/sc.cpp (3)

86-174: LGTM: Enum bindings correctly migrated to nanobind.

All enum bindings use nb::enum_<> with proper value definitions. The removal of export_values() is consistent with nanobind's default behavior of scoped enums.


324-435: LGTM: Architecture and Properties bindings correctly use nanobind patterns.

The def_prop_rw for getter/setter pairs and nb::overload_cast<> for overloaded methods are correctly applied.


218-223: JSON conversion pattern differs from other files.

This file uses json.dumps(config.json()) which is inconsistent with the pattern in other migrated files (e.g., nasp.cpp, zoned.cpp) that use json.loads(result.json().dump()).

If config.json() returns a nlohmann::json object, nanobind won't automatically convert it to a Python dict without pybind11_json, and dumps expects a Python object, not a C++ object.

bindings/na/zoned/CMakeLists.txt (1)

9-19: LGTM: CMake binding configuration correctly migrated.

The change from add_mqt_python_binding to add_mqt_python_binding_nanobind and removal of pybind11_json from linked libraries is consistent with the nanobind migration.

bindings/na/nasp/CMakeLists.txt (1)

9-19: LGTM: CMake binding configuration correctly migrated.

Consistent with the other binding modules, the helper function is updated to the nanobind variant and pybind11_json is removed.

CMakeLists.txt (1)

62-63: LGTM: Stable ABI component configuration is correct.

Using ${SKBUILD_SABI_COMPONENT} variable (set by scikit-build-core) allows conditional inclusion of Development.SABIModule only when building Stable ABI wheels. Based on learnings, this works with wheel.py-api = "cp312" in pyproject.toml to enable Stable ABI wheels for Python 3.12+ while building regular wheels for earlier versions.

bindings/clifford_synthesis/CMakeLists.txt (1)

9-19: LGTM!

The migration to add_mqt_python_binding_nanobind is correctly implemented. The removal of pybind11_json from LINK_LIBS is appropriate since nanobind uses a different JSON handling approach (as seen in the corresponding .cpp file using json.dumps via Python import).

bindings/sc/CMakeLists.txt (1)

9-22: LGTM!

The migration to nanobind is correctly applied. The Z3_FOUND compile definition is preserved, and the removal of pybind11_json from link libraries aligns with the nanobind JSON handling pattern.

bindings/hybrid_mapper/CMakeLists.txt (1)

9-19: LGTM!

The migration follows the consistent pattern across all binding modules—switching to add_mqt_python_binding_nanobind and removing pybind11_json dependency.

cmake/ExternalDependencies.cmake (2)

36-40: LGTM!

The nanobind detection pattern correctly mirrors the mqt-core detection approach: obtaining the CMake directory via the Python module and then using find_package. The nanobind_ROOT variable is implicitly used by CMake's find_package to locate the package configuration.


44-48: Verify MQT Core 3.4.0 availability.

The version bump to 3.4.0 is available with Git tag v3.4.0. Ensure the referenced commit 6bcc01e7d135058c6439c64fdd5f14b65ab88816 is incorporated into your dependency resolution.

Likely an incorrect or invalid review comment.

bindings/clifford_synthesis/clifford_synthesis.cpp (6)

20-32: LGTM!

The header migration from pybind11 to nanobind is correct. The nb namespace alias and use of nb::literals for argument syntax follow nanobind conventions. The explicit import of mqt.core.ir ensures the dependent types are registered before use.


35-49: LGTM!

The enum bindings are correctly migrated to nb::enum_. The docstrings are preserved inline, which aligns with the nanobind approach where docstrings are added directly in C++ code and stub files are auto-generated.


52-142: LGTM!

The SynthesisConfiguration bindings correctly use:

  • nb::init<>() for the constructor
  • def_rw for read-write member bindings (nanobind equivalent of pybind11's def_readwrite)
  • JSON exposure via Python's json.dumps module, which is the correct nanobind pattern for handling nlohmann::json

Based on learnings from the mqt-core migration.


145-170: LGTM!

The SynthesisResults bindings correctly use def_prop_ro (nanobind equivalent of pybind11's def_property_readonly) for read-only property bindings.


172-185: LGTM!

The Tableau class bindings correctly use nb::init<> constructors with the nanobind argument syntax.


209-213: LGTM!

The def_prop_ro bindings for results and result_circuit are correctly implemented. The lambda for result_circuit properly imports QASM using the qasm3::Importer.

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

🤖 Fix all issues with AI agents
In @bindings/clifford_synthesis/clifford_synthesis.cpp:
- Around line 210-214: The binding for the read-only "results" property exposes
an internal Results& via cs::CliffordSynthesizer::getResults but lacks a
lifetime policy; update the synthesizer.def_prop_ro("results",
&cs::CliffordSynthesizer::getResults, ...) call to include
nb::rv_policy::reference_internal so the Python Results object is tied to the
parent CliffordSynthesizer; leave the existing result_circuit lambda (which
calls self.getResults().getResultCircuit()) unchanged.

In @bindings/na/nasp/nasp.cpp:
- Around line 44-50: Add a nanobind docstring to the binding for
NAStatePreparationSolver.Result by supplying an R"pb(... )pb" docstring to the
nb::class_<na::NASolver::Result>(m, "NAStatePreparationSolver.Result") and/or to
the .def("json", ...) call so the generated stub includes the method/class
documentation; update the .def("json", [](const na::NASolver::Result& result) {
... }) invocation to pass the docstring as the final argument (using the
R"pb(...)pb" literal) describing what json() returns and its behavior.

In @bindings/sc/sc.cpp:
- Around line 428-433: The property bindings for Architecture.coupling_map and
Architecture.properties use def_prop_rw with getters that return references
(Architecture::getCouplingMap and Architecture::getProperties); update those
def_prop_rw calls to pass nb::rv_policy::reference_internal as the fourth
argument so nanobind keeps the parent Architecture alive while the returned
CouplingMap&/Properties& are used.
- Around line 216-221: The current binding for Configuration::json passes a
nlohmann::json object into Python's json.dumps (and the stub claims dict
return), which fails because there is no nanobind caster and dumps returns a
string; change the lambda in the .def("json", ...) for class Configuration to
call Python's json.loads on config.json().dump() (i.e., convert the C++
nlohmann::json to a string via dump() and then call
nb::module_::import_("json").attr("loads") on that string) so the binding
returns a proper Python dict rather than attempting to pass the C++ json object
directly.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e90a13e and 8b13596.

📒 Files selected for processing (5)
  • bindings/clifford_synthesis/clifford_synthesis.cpp
  • bindings/hybrid_mapper/hybrid_mapper.cpp
  • bindings/na/nasp/nasp.cpp
  • bindings/na/zoned/zoned.cpp
  • bindings/sc/sc.cpp
🧰 Additional context used
🧠 Learnings (14)
📓 Common learnings
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/dd/register_matrix_dds.cpp:64-109
Timestamp: 2025-12-15T01:54:22.129Z
Learning: In the munich-quantum-toolkit/core repository, after migrating to nanobind, docstrings for Python bindings are now added directly in the C++ binding code (using R"pb(...)pb" syntax) and stub files (.pyi) are auto-generated using the `bindings/generate-stubs.sh` script. This replaces the previous pybind11 approach where docstrings were manually maintained in stub files.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/qcec PR: 817
File: pyproject.toml:81-82
Timestamp: 2026-01-09T17:58:10.350Z
Learning: In the Munich Quantum Toolkit projects using nanobind, setting `wheel.py-api = "cp312"` in `[tool.scikit-build]` enables Stable ABI wheels only for Python 3.12+ (where nanobind supports it), while automatically building regular non-ABI3 wheels for earlier Python versions (3.10, 3.11) and free-threading builds (3.14t). This allows a single configuration to appropriately handle both old and new Python versions without forcing incompatible ABI requirements.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: bindings/fomac/fomac.cpp:227-264
Timestamp: 2025-12-07T01:21:27.544Z
Learning: In the munich-quantum-toolkit/core repository, docstrings for Python bindings are added to the corresponding stub files (.pyi) rather than directly in the pybind11 C++ bindings code. This practice may change if the project adopts nanobind with automatic stub generation.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/fomac/fomac.cpp:111-116
Timestamp: 2025-12-19T00:05:54.428Z
Learning: In the munich-quantum-toolkit/core repository after migrating to nanobind, lifetime management differs from pybind11: `nb::keep_alive<nurse, patient>()` does not exist in nanobind. Instead, use `nb::rv_policy::reference_internal` when binding methods that return objects referencing internal state of the parent object (e.g., Session::getDevices returning Device objects that depend on the Session). This tells nanobind to keep the parent alive while children exist.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: python/mqt/core/ir/operations.pyi:9-16
Timestamp: 2025-12-15T01:59:17.023Z
Learning: In the munich-quantum-toolkit/core repository, stub files (.pyi) are auto-generated by nanobind's stubgen tool and should not be manually modified for style preferences, as changes would be overwritten during regeneration.
📚 Learning: 2025-12-15T01:54:22.129Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/dd/register_matrix_dds.cpp:64-109
Timestamp: 2025-12-15T01:54:22.129Z
Learning: In the munich-quantum-toolkit/core repository, after migrating to nanobind, docstrings for Python bindings are now added directly in the C++ binding code (using R"pb(...)pb" syntax) and stub files (.pyi) are auto-generated using the `bindings/generate-stubs.sh` script. This replaces the previous pybind11 approach where docstrings were manually maintained in stub files.

Applied to files:

  • bindings/na/zoned/zoned.cpp
  • bindings/clifford_synthesis/clifford_synthesis.cpp
  • bindings/na/nasp/nasp.cpp
  • bindings/hybrid_mapper/hybrid_mapper.cpp
  • bindings/sc/sc.cpp
📚 Learning: 2025-12-19T00:05:54.428Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/fomac/fomac.cpp:111-116
Timestamp: 2025-12-19T00:05:54.428Z
Learning: In the munich-quantum-toolkit/core repository after migrating to nanobind, lifetime management differs from pybind11: `nb::keep_alive<nurse, patient>()` does not exist in nanobind. Instead, use `nb::rv_policy::reference_internal` when binding methods that return objects referencing internal state of the parent object (e.g., Session::getDevices returning Device objects that depend on the Session). This tells nanobind to keep the parent alive while children exist.

Applied to files:

  • bindings/na/zoned/zoned.cpp
  • bindings/na/nasp/nasp.cpp
  • bindings/hybrid_mapper/hybrid_mapper.cpp
  • bindings/sc/sc.cpp
📚 Learning: 2025-12-22T01:25:21.609Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.

Applied to files:

  • bindings/na/zoned/zoned.cpp
  • bindings/clifford_synthesis/clifford_synthesis.cpp
  • bindings/na/nasp/nasp.cpp
  • bindings/hybrid_mapper/hybrid_mapper.cpp
  • bindings/sc/sc.cpp
📚 Learning: 2025-12-04T06:59:40.314Z
Learnt from: MatthiasReumann
Repo: munich-quantum-toolkit/core PR: 1301
File: mlir/lib/Dialect/MQTOpt/Transforms/Transpilation/LayeredUnit.cpp:84-85
Timestamp: 2025-12-04T06:59:40.314Z
Learning: In the MQTOpt MLIR routing passes (NaiveRoutingPassSC, AStarRoutingPassSC), the input IR is guaranteed to contain only 1-qubit and 2-qubit gates. All 3+-qubit gates must be decomposed before routing; otherwise the input IR is invalid. This invariant allows skipTwoQubitBlock in LayeredUnit.cpp to safely assert wires.size() == 2.

Applied to files:

  • bindings/na/zoned/zoned.cpp
📚 Learning: 2025-12-07T01:21:27.544Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: bindings/fomac/fomac.cpp:227-264
Timestamp: 2025-12-07T01:21:27.544Z
Learning: In the munich-quantum-toolkit/core repository, docstrings for Python bindings are added to the corresponding stub files (.pyi) rather than directly in the pybind11 C++ bindings code. This practice may change if the project adopts nanobind with automatic stub generation.

Applied to files:

  • bindings/clifford_synthesis/clifford_synthesis.cpp
  • bindings/na/nasp/nasp.cpp
  • bindings/hybrid_mapper/hybrid_mapper.cpp
  • bindings/sc/sc.cpp
📚 Learning: 2025-11-05T09:23:46.540Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: src/qdmi/dd/Device.cpp:492-521
Timestamp: 2025-11-05T09:23:46.540Z
Learning: In the munich-quantum-toolkit/core repository, the `qasm3::Importer::imports()` function is backwards compatible with OpenQASM 2 programs. Therefore, it can be used to parse both QASM2 and QASM3 program formats without requiring separate importers for each version.

Applied to files:

  • bindings/clifford_synthesis/clifford_synthesis.cpp
📚 Learning: 2025-12-07T09:10:31.836Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: src/qdmi/sc/Device.cpp:97-102
Timestamp: 2025-12-07T09:10:31.836Z
Learning: In the munich-quantum-toolkit/core repository, duplication of QDMI-related macros (such as IS_INVALID_ARGUMENT) across device implementations (e.g., in src/qdmi/sc/Device.cpp and src/qdmi/dd/Device.cpp) is acceptable as a temporary measure. The preferred long-term solution is to upstream these macros to the QDMI repository rather than creating local shared headers, so they can be reused across all dependent projects.

Applied to files:

  • bindings/clifford_synthesis/clifford_synthesis.cpp
📚 Learning: 2025-12-21T22:35:08.572Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/fomac/fomac.cpp:348-364
Timestamp: 2025-12-21T22:35:08.572Z
Learning: In the munich-quantum-toolkit/core repository's nanobind bindings, use `.sig("...")` on parameter arguments that have vector or container defaults (e.g., `"sites"_a.sig("...") = std::vector<fomac::Session::Device::Site>{}`) to prevent exposing mutable defaults in the Python API, which would be flagged as a code smell by Python linters. This pattern is preferred over removing `.sig("...")` even though it shows `...` in the stub signature.

Applied to files:

  • bindings/na/nasp/nasp.cpp
  • bindings/sc/sc.cpp
📚 Learning: 2025-10-09T13:14:10.178Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:219-221
Timestamp: 2025-10-09T13:14:10.178Z
Learning: The MQT Core project (munich-quantum-toolkit/core repository) uses the C++20 standard, not C++17. C++20 features such as abbreviated function templates (e.g., `const auto&` parameters) are supported and valid in this codebase.

Applied to files:

  • bindings/na/nasp/nasp.cpp
📚 Learning: 2025-12-28T17:14:53.890Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1403
File: src/qdmi/na/CMakeLists.txt:31-38
Timestamp: 2025-12-28T17:14:53.890Z
Learning: In the munich-quantum-toolkit/core repository, the NA device generator target (mqt-core-qdmi-na-device-gen) is intentionally propagated to MQT_CORE_TARGETS via list(APPEND) because it's publicly linked to the NA device library (the NA device uses a public function from the generator). The SC device generator is not propagated because it has no such dependency with the SC device library.

Applied to files:

  • bindings/na/nasp/nasp.cpp
📚 Learning: 2025-12-15T01:59:17.023Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: python/mqt/core/ir/operations.pyi:9-16
Timestamp: 2025-12-15T01:59:17.023Z
Learning: In the munich-quantum-toolkit/core repository, stub files (.pyi) are auto-generated by nanobind's stubgen tool and should not be manually modified for style preferences, as changes would be overwritten during regeneration.

Applied to files:

  • bindings/hybrid_mapper/hybrid_mapper.cpp
  • bindings/sc/sc.cpp
📚 Learning: 2025-11-20T18:19:31.857Z
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: src/hybridmap/HybridNeutralAtomMapper.cpp:1679-1715
Timestamp: 2025-11-20T18:19:31.857Z
Learning: In src/hybridmap/HybridNeutralAtomMapper.cpp, the compareSwapAndBridge and compareShuttlingAndFlyingAncilla methods intentionally use division by zero to produce infinity when distance reductions are zero or negative. This infinity value marks that option as highly unfavorable in cost comparisons, which is the intended behavior rather than a bug.

Applied to files:

  • bindings/hybrid_mapper/hybrid_mapper.cpp
📚 Learning: 2025-11-27T08:52:06.428Z
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: src/hybridmap/HybridNeutralAtomMapper.cpp:1379-1383
Timestamp: 2025-11-27T08:52:06.428Z
Learning: In src/hybridmap/HybridNeutralAtomMapper.cpp, the getBestMovePos function throws std::runtime_error when no move position is found (finalBestPos.coords is empty). This behavior is acceptable for current use cases since tight architectures are not a concern. A future improvement would be to fall back to SWAP-based strategies instead of throwing.

Applied to files:

  • bindings/hybrid_mapper/hybrid_mapper.cpp
🧬 Code graph analysis (3)
bindings/na/zoned/zoned.cpp (4)
test/na/zoned/test_heuristic_placer.cpp (3)
  • architecture (224-224)
  • architecture (246-246)
  • architecture (268-268)
python/mqt/qmap/na/state_preparation.pyi (1)
  • json (82-87)
include/na/zoned/Architecture.hpp (2)
  • json (229-230)
  • json (237-238)
python/mqt/qmap/na/zoned.pyi (2)
  • stats (139-144)
  • stats (234-239)
bindings/na/nasp/nasp.cpp (1)
python/mqt/qmap/na/state_preparation.pyi (2)
  • solve (89-139)
  • json (82-87)
bindings/hybrid_mapper/hybrid_mapper.cpp (2)
src/hybridmap/NeutralAtomArchitecture.cpp (2)
  • loadJson (41-157)
  • loadJson (41-41)
python/mqt/qmap/hybrid_mapper.pyi (1)
  • MapperParameters (24-40)
🪛 Cppcheck (2.19.0)
bindings/na/zoned/zoned.cpp

[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 29-29: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 29-29: Include file

(missingIncludeSystem)


[information] 30-30: Include file

(missingIncludeSystem)


[information] 31-31: Include file

(missingIncludeSystem)


[information] 32-32: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingInclude)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 29-29: Include file

(missingIncludeSystem)


[information] 30-30: Include file

(missingIncludeSystem)


[information] 31-31: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 29-29: Include file

(missingIncludeSystem)


[style] 70-70: The function 'isStorage' is never used.

(unusedFunction)

bindings/clifford_synthesis/clifford_synthesis.cpp

[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[error] 176-176: Boolean value assigned to pointer.

(assignBoolToPointer)


[style] 76-76: The function 'requiresMultiGateEncoding' is never used.

(unusedFunction)


[style] 100-100: The function 'runBinarySearch' is never used.

(unusedFunction)


[style] 123-123: The function 'runLinearSearch' is never used.

(unusedFunction)


[style] 48-48: The function 'setSingleQubitGates' is never used.

(unusedFunction)


[style] 49-49: The function 'setTwoQubitGates' is never used.

(unusedFunction)


[style] 50-50: The function 'setDepth' is never used.

(unusedFunction)


[style] 51-51: The function 'setRuntime' is never used.

(unusedFunction)


[style] 52-52: The function 'setSolverResult' is never used.

(unusedFunction)


[style] 53-53: The function 'setSolverCalls' is never used.

(unusedFunction)


[style] 82-82: The function 'getTableau' is never used.

(unusedFunction)


[style] 96-96: The function 'containsX' is never used.

(unusedFunction)


[style] 99-99: The function 'containsY' is never used.

(unusedFunction)


[style] 102-102: The function 'containsZ' is never used.

(unusedFunction)


[style] 105-105: The function 'containsH' is never used.

(unusedFunction)


[style] 108-108: The function 'containsS' is never used.

(unusedFunction)


[style] 111-111: The function 'containsSdag' is never used.

(unusedFunction)


[style] 34-34: The function 'limitGateCount' is never used.

(unusedFunction)


[style] 66-66: The function 'collectSingleQubitGateTerms' is never used.

(unusedFunction)


[style] 80-80: The function 'collectTwoQubitGateTerms' is never used.

(unusedFunction)


[style] 72-72: The function 'getVariables' is never used.

(unusedFunction)


[style] 160-160: The function 'cTypeFromString' is never used.

(unusedFunction)


[style] 185-185: The function 'isArith' is never used.

(unusedFunction)


[style] 199-199: The function 'isNumber' is never used.

(unusedFunction)


[style] 210-210: The function 'isCommutative' is never used.

(unusedFunction)


[style] 39-39: The function 'getModel' is never used.

(unusedFunction)


[style] 135-135: The function 'getID' is never used.

(unusedFunction)


[style] 136-136: The function 'getNodes' is never used.

(unusedFunction)


[style] 137-137: The function 'getOpType' is never used.

(unusedFunction)


[style] 138-138: The function 'getCType' is never used.

(unusedFunction)


[style] 139-139: The function 'getName' is never used.

(unusedFunction)


[style] 140-140: The function 'getLogic' is never used.

(unusedFunction)


[style] 190-190: The function 'reset' is never used.

(unusedFunction)

bindings/na/nasp/nasp.cpp

[information] 19-19: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 19-19: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[error] 42-42: Boolean value assigned to pointer.

(assignBoolToPointer)


[error] 42-42: Boolean value assigned to pointer.

(assignBoolToPointer)

bindings/hybrid_mapper/hybrid_mapper.cpp

[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingInclude)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 29-29: Include file

(missingIncludeSystem)


[information] 30-30: Include file

(missingIncludeSystem)


[information] 31-31: Include file

(missingIncludeSystem)


[information] 32-32: Include file

(missingIncludeSystem)


[information] 33-33: Include file

(missingIncludeSystem)


[information] 34-34: Include file

(missingIncludeSystem)


[information] 35-35: Include file

(missingIncludeSystem)


[information] 36-36: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 29-29: Include file

(missingIncludeSystem)


[information] 30-30: Include file

(missingIncludeSystem)


[information] 31-31: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingInclude)


[information] 21-21: Include file

(missingInclude)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingInclude)


[information] 22-22: Include file

(missingInclude)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 29-29: Include file

(missingIncludeSystem)


[information] 30-30: Include file

(missingIncludeSystem)


[information] 31-31: Include file

(missingIncludeSystem)


[information] 32-32: Include file

(missingIncludeSystem)


[information] 33-33: Include file

(missingIncludeSystem)


[information] 34-34: Include file

(missingIncludeSystem)


[information] 35-35: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingInclude)


[information] 24-24: Include file

(missingInclude)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 29-29: Include file

(missingIncludeSystem)


[information] 30-30: Include file

(missingIncludeSystem)


[information] 31-31: Include file

(missingIncludeSystem)


[information] 20-20: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingIncludeSystem)


[information] 22-22: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 26-26: Include file

(missingIncludeSystem)


[information] 27-27: Include file

(missingIncludeSystem)


[performance] 105-105: Function 'getGates()' should return member 'gates' by const reference.

(returnByReference)


[performance] 112-112: Function 'getNewGates()' should return member 'newGates' by const reference.

(returnByReference)


[style] 113-113: The function 'setCircuitQubit' is never used.

(unusedFunction)


[style] 136-136: The function 'getHwQubits' is never used.

(unusedFunction)


[style] 105-105: The function 'getGates' is never used.

(unusedFunction)


[style] 112-112: The function 'getNewGates' is never used.

(unusedFunction)


[style] 71-71: The function 'toCsv' is never used.

(unusedFunction)


[performance] 155-155: Function 'getAnimationViz()' should return member 'animation' by const reference.

(returnByReference)


[style] 215-215: The function 'mapToCoordIdx' is never used.

(unusedFunction)

bindings/sc/sc.cpp

[information] 34-34: Include file

(missingIncludeSystem)


[information] 35-35: Include file

(missingIncludeSystem)


[information] 36-36: Include file

(missingIncludeSystem)


[information] 37-37: Include file

(missingIncludeSystem)


[information] 38-38: Include file

(missingIncludeSystem)


[information] 39-39: Include file

(missingIncludeSystem)


[information] 40-40: Include file

(missingIncludeSystem)


[information] 41-41: Include file

(missingIncludeSystem)


[information] 42-42: Include file

(missingIncludeSystem)


[performance] 112-112: Prefer prefix ++/-- operators for non-primitive types.

(postfixOperator)


[style] 124-124: The function 'twoQubitErrorRateAvailable' is never used.

(unusedFunction)


[style] 111-111: The function 'dataLoggingEnabled' is never used.

(unusedFunction)


[style] 115-115: The function 'setTimeout' is never used.

(unusedFunction)


[style] 116-116: The function 'swapLimitsEnabled' is never used.

(unusedFunction)


[style] 124-124: The function 'getTotalFixedCost' is never used.

(unusedFunction)


[style] 120-120: The function 'update' is never used.

(unusedFunction)


[style] 140-140: The function 'restart' is never used.

(unusedFunction)


[style] 245-245: The function 'isEdgeConnected' is never used.

(unusedFunction)


[style] 253-253: The function 'isEdgeBidirectional' is never used.

(unusedFunction)


[style] 259-259: The function 'getDistanceTable' is never used.

(unusedFunction)


[style] 275-275: The function 'isFidelityAvailable' is never used.

(unusedFunction)


[style] 277-277: The function 'getFidelityDistanceTables' is never used.

(unusedFunction)


[style] 323-323: The function 'getFidelityTable' is never used.

(unusedFunction)


[style] 330-330: The function 'getSingleQubitFidelities' is never used.

(unusedFunction)


[style] 314-314: The function 'printLayering' is never used.

(unusedFunction)


[style] 330-330: The function 'printLocations' is never used.

(unusedFunction)


[style] 228-228: The function 'getConsideredQubits' is never used.

(unusedFunction)


[style] 405-405: The function 'isCalibrationDataAvailable' is never used.

(unusedFunction)


[style] 427-427: The function 'distance' is never used.

(unusedFunction)


[style] 411-411: The function 'computeEffectiveBranchingRate' is never used.

(unusedFunction)

⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (16)
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Debug) / 🍎 macos-14 clang Debug
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🇨‌ Test 🍎 (macos-15-intel, clang, Release) / 🍎 macos-15-intel clang Release
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🇨‌ Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Release) / 🍎 macos-14 clang Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Debug) / 🏁 windows-2022 msvc Debug
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Release) / 🏁 windows-2022 msvc Release
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
  • GitHub Check: 🇨‌ Coverage / 📈 Coverage
🔇 Additional comments (33)
bindings/na/nasp/nasp.cpp (3)

19-24: LGTM!

The nanobind includes are correctly structured with appropriate STL type converters. The NOLINT(misc-include-cleaner) annotations are appropriate since these headers enable automatic type conversions even when not explicitly referenced in this translation unit.


27-31: LGTM!

The module registration follows the established nanobind migration pattern. The nb::module_::import_("mqt.core.ir") correctly ensures type dependencies (e.g., QuantumComputation) are available before registering bindings that use them.


34-42: LGTM!

The class binding is correctly migrated to nanobind syntax. The default argument specifications (nb::none() for optional parameters, boolean defaults) are correct.

Note: The Cppcheck "assignBoolToPointer" warning on line 42 is a false positive — the "arg"_a = false syntax is valid nanobind usage where _a returns an nb::arg object that accepts default values via operator=.

bindings/na/zoned/zoned.cpp (8)

21-32: LGTM!

The nanobind includes and namespace setup are correct. The NOLINT comments for misc-include-cleaner are appropriate since these STL headers are used implicitly for automatic type conversions.


34-36: LGTM!

The explicit nb::module_::import_("mqt.core.ir") is essential for nanobind's cross-module type recognition. This ensures that types like qc::QuantumComputation from the core module are properly resolved at runtime.


55-68: LGTM!

The enum bindings correctly use nanobind's nb::enum_. The absence of .export_values() is appropriate—nanobind does not export enum values to the enclosing scope by default, which aligns with Python's standard enum access pattern (e.g., PlacementMethod.astar).


77-98: LGTM!

The __init__ binding correctly uses nanobind's placement-new pattern. The nb::keep_alive<1, 2>() appropriately ensures the architecture remains alive while the compiler exists, which is necessary since the compiler holds a reference to the architecture. Based on learnings, this is the correct nanobind pattern for lifetime management.


131-137: Acceptable JSON conversion pattern.

The json.loads(stats.dump()) pattern correctly converts nlohmann::json to a Python dict without requiring pybind11_json. The per-call module import is a minor overhead but acceptable for a statistics retrieval method that's typically not called in tight loops.


146-183: LGTM!

The RoutingAwareCompiler binding follows the same correct patterns as RoutingAgnosticCompiler. The additional configuration parameters are properly exposed with appropriate defaults from defaultConfig, and the nb::keep_alive<1, 2>() ensures correct lifetime management.


233-239: LGTM!

The stats method implementation is consistent with RoutingAgnosticCompiler.


38-53: LGTM!

The Architecture class binding correctly uses nanobind's nb::class_ with appropriate method bindings for JSON file/string operations and NAMachine export functionality.

bindings/clifford_synthesis/clifford_synthesis.cpp (6)

20-29: LGTM!

The nanobind includes and namespace setup follow standard conventions. The NOLINT(misc-include-cleaner) comments are appropriate since these STL headers enable implicit type conversions that are used indirectly.


31-40: LGTM!

The NB_MODULE macro and enum binding are correctly structured. The import of mqt.core.ir ensures the dependency module is loaded before using its types.


42-50: LGTM!

The plog::Severity enum is correctly bound with descriptive docstrings for each verbosity level.


53-143: LGTM!

The SynthesisConfiguration class binding correctly uses def_rw for read-write properties and properly handles JSON serialization via Python's json.dumps(). Based on learnings, docstrings are correctly included directly in the C++ binding code as per the nanobind migration pattern.


146-171: LGTM!

The SynthesisResults class binding correctly uses def_prop_ro for read-only properties. The getter methods return primitive types and strings by value, so no special return value policies are needed.


173-186: LGTM!

The Tableau class binding correctly uses multiple nb::init overloads for different constructors. The static analysis warning about "Boolean value assigned to pointer" at line 176 is a false positive—"include_destabilizers"_a = false is standard nanobind syntax for specifying a default argument value.

bindings/hybrid_mapper/hybrid_mapper.cpp (8)

20-34: LGTM! Nanobind setup follows established patterns.

The include migration, namespace alias, and module declaration correctly follow the nanobind conventions. The explicit nb::module_::import_("mqt.core.ir") ensures dependent types are registered before use. Based on learnings from the munich-quantum-toolkit/core migration.


37-48: LGTM! Enum bindings correctly migrated.

The enum bindings properly use nb::enum_<> without .export_values() (not needed in nanobind) and include docstrings directly in the binding code per the project's nanobind migration pattern.


50-102: LGTM! Class bindings use correct nanobind syntax.

The def_rw usage correctly replaces pybind11's def_readwrite, and all fields include descriptive docstrings. The bindings match the expected Python interface from the stub file.


104-152: LGTM! Architecture bindings properly use read-only and read-write properties.

The use of def_prop_ro for computed/immutable properties (dimensions, configuration values) and def_rw for the mutable name field correctly reflects the intended access patterns.


215-226: LGTM! HybridSynthesisMapper bindings follow consistent patterns.

The constructor correctly uses const references and applies nb::keep_alive for lifetime safety. The default parameter value na::MapperParameters() is properly handled.


268-282: LGTM! Matrix conversion correctly bridges C++ and Python types.

The lambda converts the internal SymmetricMatrix type to a std::vector<std::vector<int>> which nanobind can automatically convert to a Python list of lists. This is the expected pattern for exposing matrix data to Python.


1-310: Overall: Clean migration from pybind11 to nanobind.

The binding code correctly applies nanobind idioms throughout:

  • NB_MODULE macro with proper NOLINT suppression
  • def_rw/def_prop_ro replacing pybind11's def_readwrite/def_property_readonly
  • nb::keep_alive<> for lifetime management of stored references
  • Explicit module import for dependent types
  • Docstrings embedded directly in C++ (per project conventions after nanobind migration)

The static analysis warnings are false positives—they reference missing system includes (expected in isolated analysis) and unused functions in unrelated source files.

Note: The PR description indicates the test coverage checklist item is incomplete. Consider adding tests to verify the binding behavior, especially for edge cases around lifetime management.


154-165: Fix binding signature to match C++ constructor: const na::MapperParameters& instead of na::MapperParameters&.

The C++ constructor takes const MapperParameters& p and copies the parameters into a member variable (not storing a reference). The binding declares na::MapperParameters& (non-const), which doesn't match the actual C++ API and incorrectly suggests the mapper keeps a reference to the caller's parameters.

Additionally, nb::keep_alive<1, 3>() is unnecessary—since parameters are copied, there's no lifetime dependency. Keep nb::keep_alive<1, 2>() for the architecture pointer, which is stored as-is.

⛔ Skipped due to learnings
Learnt from: denialhaag
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/fomac/fomac.cpp:111-116
Timestamp: 2025-12-19T00:05:54.428Z
Learning: In the munich-quantum-toolkit/core repository after migrating to nanobind, lifetime management differs from pybind11: `nb::keep_alive<nurse, patient>()` does not exist in nanobind. Instead, use `nb::rv_policy::reference_internal` when binding methods that return objects referencing internal state of the parent object (e.g., Session::getDevices returning Device objects that depend on the Session). This tells nanobind to keep the parent alive while children exist.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/fomac/fomac.cpp:348-364
Timestamp: 2025-12-21T22:35:08.572Z
Learning: In the munich-quantum-toolkit/core repository's nanobind bindings, use `.sig("...")` on parameter arguments that have vector or container defaults (e.g., `"sites"_a.sig("...") = std::vector<fomac::Session::Device::Site>{}`) to prevent exposing mutable defaults in the Python API, which would be flagged as a code smell by Python linters. This pattern is preferred over removing `.sig("...")` even though it shows `...` in the stub signature.
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: src/hybridmap/HybridNeutralAtomMapper.cpp:1379-1383
Timestamp: 2025-11-27T08:52:06.428Z
Learning: In src/hybridmap/HybridNeutralAtomMapper.cpp, the getBestMovePos function throws std::runtime_error when no move position is found (finalBestPos.coords is empty). This behavior is acceptable for current use cases since tight architectures are not a concern. A future improvement would be to fall back to SWAP-based strategies instead of throwing.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1383
File: bindings/ir/register_permutation.cpp:153-171
Timestamp: 2025-12-22T01:25:21.609Z
Learning: In the munich-quantum-toolkit/core repository, when using nanobind iterator factory functions like `make_key_iterator` and `make_iterator`, the unqualified form (without explicit `nb::` prefix) is preferred. The clang-tidy configuration suggests removal of explicit namespace qualification, relying on ADL (Argument-Dependent Lookup) to resolve these functions correctly.
Learnt from: lsschmid
Repo: munich-quantum-toolkit/qmap PR: 832
File: src/hybridmap/HybridNeutralAtomMapper.cpp:1679-1715
Timestamp: 2025-11-20T18:19:31.857Z
Learning: In src/hybridmap/HybridNeutralAtomMapper.cpp, the compareSwapAndBridge and compareShuttlingAndFlyingAncilla methods intentionally use division by zero to produce infinity when distance reductions are zero or negative. This infinity value marks that option as highly unfavorable in cost comparisons, which is the intended behavior rather than a bug.
bindings/sc/sc.cpp (8)

34-45: LGTM!

The nanobind includes and namespace setup follow the expected conventions. The NOLINT comments for STL type casters are appropriate since these headers enable automatic type conversion in bindings even when not explicitly referenced.


47-78: LGTM!

The helper function is pure C++ logic unaffected by the binding library migration. Exception handling and move semantics are correctly applied.


80-172: LGTM!

The module definition and enum bindings are correctly migrated to nanobind. The NB_MODULE macro, module import, and nb::enum_ usage all follow nanobind conventions properly.


224-245: LGTM!

The MappingResults class binding is correctly migrated using nb::class_, nb::init<>(), and def_rw. The JSON serialization follows the same pattern established for Configuration.


248-263: LGTM!

The CircuitInfo nested class binding is correctly migrated. Properties are properly exposed using def_rw.


266-283: LGTM!

The HeuristicBenchmarkInfo class binding is correctly migrated with proper use of nb::class_, nb::init<>(), and def_rw.


286-320: LGTM!

The LayerHeuristicBenchmarkInfo class binding is correctly migrated with all benchmark properties properly exposed.


450-452: LGTM!

The main mapping function binding is straightforward and correctly uses nanobind's argument annotation syntax.

@denialhaag denialhaag requested a review from burgholzer January 13, 2026 16:53
Copy link
Member

@burgholzer burgholzer left a comment

Choose a reason for hiding this comment

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

This looks great besides the one small oversight 😌

Co-authored-by: Lukas Burgholzer <[email protected]>
Signed-off-by: Daniel Haag <[email protected]>
@denialhaag denialhaag enabled auto-merge (squash) January 13, 2026 22:45
@denialhaag
Copy link
Member Author

This looks great besides the one small oversight 😌

Thank you for catching the incorrect PR number! 🙂

@denialhaag denialhaag merged commit f818d7d into main Jan 13, 2026
32 checks passed
@denialhaag denialhaag deleted the nanobind branch January 13, 2026 23:04
@github-project-automation github-project-automation bot moved this from In Progress to Done in MQT Compilation Jan 13, 2026
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 minor Changes leading to a minor version increase packaging Anything related to Python packaging python Anything related to Python code refactor Anything related to code refactoring

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants