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.
  • 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 14, 2026
@denialhaag denialhaag added c++ Anything related to C++ code python Anything related to python code minor Minor version update labels Jan 14, 2026
@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Added Python type stubs for improved IDE support and type checking.
    • Added Python 3.12 support with ABI3 wheel validation.
  • Chores

    • Modernized Python bindings infrastructure.
    • Updated core dependencies to latest stable versions.

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

Walkthrough

Migrate Python bindings and build tooling from pybind11 to nanobind: update CMake and dependency discovery, convert binding sources and module init to nanobind, rename some exposed attributes (e.g., typetype_), add stub generation and linting, and update packaging/CI to use nanobind.

Changes

Cohort / File(s) Summary
CI workflows
.github/workflows/ci.yml
Replace pybind11 with nanobind in cpp-linter install-pkgs; add check-stubs: true to python-linter usage.
Top-level CMake
CMakeLists.txt
Compact Python find_package call to include SKBUILD_SABI_COMPONENT in COMPONENTS list.
External deps & package discovery
cmake/ExternalDependencies.cmake
Remove pybind11 discovery/blocks, add nanobind discovery (execute_process + find_package(nanobind CONFIG REQUIRED)), bump MQT core version vars, and simplify FetchContent wiring (mqt-core, Eigen3, googletest).
Bindings CMake & link changes
bindings/CMakeLists.txt
Rename binding helper to add_mqt_python_binding_nanobind and remove pybind11_json from LINK_LIBS.
Binding entry & module init
bindings/bindings.cpp, include/python/* (deleted)
Replace PYBIND11_MODULENB_MODULE, add nb = nanobind alias and nb::literals, declare bind functions with nb::module_&; remove old pybind11 header prototypes.
Binding implementations
bindings/InterfaceBindings.cpp, bindings/dd/DDSimDebugBindings.cpp, other binding sources
Convert pybind11 → nanobind: headers, namespace, module param types, nb::enum_/nb::class_, nb::init/def_rw/named _a args, return policies → nb::rv_policy, and rename exposed members (e.g., typetype_).
Stub generation & typing
noxfile.py, bindings/debugger_patterns.txt, python/mqt/debugger/pydebugger.pyi
Add nox stubs session using nanobind.stubgen, add debugger_patterns.txt, and expand .pyi with new/renamed classes, factory functions, properties, and updated method signatures.
Packaging & tooling
pyproject.toml
Replace pybind11nanobind in build deps, add wheel.py-api = "cp312", add cibuildwheel CP312 overrides, and adjust Ruff per-file ignores for *.pyi.
Runtime & tests (attribute renames)
python/mqt/debugger/dap/dap_server.py, python/mqt/debugger/dap/messages/change_bit_dap_message.py, test/python/test_diagnosis.py, test/python/test_python_bindings.py
Update runtime code and tests to use renamed binding attributes (e.g., ErrorCause.typeErrorCause.type_, Variable.typeVariable.type_).

Sequence Diagram(s)

(Skipped.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

dependencies, documentation

Suggested reviewers

  • burgholzer
  • DRovara

"I hopped through headers, CMake, and CI,
Swapped py for nb with a twitch of my ear;
Stubs now dance while bindings rearrange,
Names shifted gently — nothing strange;
A rabbit's nibble, tidy and clear. 🥕🐇"

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 79.78% 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 throughout the codebase.
Description check ✅ Passed The description explains the motivation (Stable ABI wheels and PyPI space savings), includes appropriate checklist completion, and aligns with the template structure.

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

✨ Finishing touches
  • 📝 Generate docstrings

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

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
bindings/InterfaceBindings.cpp (1)

841-878: Pre-existing memory management pattern - consider RAII wrapper.

The manual malloc/free pattern works but could leak if an exception occurs before the cleanup loop. This is pre-existing code, but for future improvement consider using std::unique_ptr with a custom deleter or std::vector<std::vector<char>> for exception safety.

🤖 Fix all issues with AI agents
In `@bindings/InterfaceBindings.cpp`:
- Around line 647-649: The docstring assigned to SimulationState in
InterfaceBindings.cpp contains an extra leading double-quote at the start of the
second line; edit the .doc() string for SimulationState to remove that stray `"`
so the multi-line C++ raw string becomes: a single coherent paragraph without
the errant quote, ensuring the opening/closing quotes and parentheses for R"(
... )" remain balanced and the text reads: Represenets the state... and then the
following sentence without the leading `"`.

In `@noxfile.py`:
- Around line 242-254: The code assumes pyi_files contains items; if
nanobind.stubgen produced none you will call session.run with no files which is
confusing—add an explicit check on pyi_files before running prek: if pyi_files
is empty, log a clear message and abort or skip the task (e.g., use
session.error(...) or session.skip(...)) and return early, otherwise proceed to
call session.run("prek", ...) for license-tools, ruff-check, ruff-format and the
final ruff-check; reference the pyi_files variable and the session.run("prek",
...) invocations when adding the guard.

In `@python/mqt/debugger/pydebugger.pyi`:
- Around line 302-306: The SimulationState class docstring in the generated stub
contains an extra leading quote; fix the source C++ binding where the
SimulationState docstring is set (the py::class_<SimulationState> / doc string
assignment in the binding that generates stubs) by removing the stray leading
double-quote so the string begins correctly, then regenerate the nanobind stubs
so the corrected docstring appears in python/mqt/debugger/pydebugger.pyi.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f88c15b and 6f6c02b.

⛔ Files ignored due to path filters (1)
  • uv.lock is excluded by !**/*.lock
📒 Files selected for processing (17)
  • .github/workflows/ci.yml
  • CMakeLists.txt
  • bindings/CMakeLists.txt
  • bindings/InterfaceBindings.cpp
  • bindings/bindings.cpp
  • bindings/dd/DDSimDebugBindings.cpp
  • bindings/debugger_patterns.txt
  • cmake/ExternalDependencies.cmake
  • include/python/InterfaceBindings.hpp
  • include/python/dd/DDSimDebugBindings.hpp
  • noxfile.py
  • pyproject.toml
  • python/mqt/debugger/dap/dap_server.py
  • python/mqt/debugger/dap/messages/change_bit_dap_message.py
  • python/mqt/debugger/pydebugger.pyi
  • test/python/test_diagnosis.py
  • test/python/test_python_bindings.py
💤 Files with no reviewable changes (2)
  • include/python/InterfaceBindings.hpp
  • include/python/dd/DDSimDebugBindings.hpp
🧰 Additional context used
🧠 Learnings (22)
📓 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: 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: 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: 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: 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: 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:

  • cmake/ExternalDependencies.cmake
  • bindings/CMakeLists.txt
  • test/python/test_python_bindings.py
  • CMakeLists.txt
  • noxfile.py
  • .github/workflows/ci.yml
  • bindings/dd/DDSimDebugBindings.cpp
  • bindings/bindings.cpp
  • pyproject.toml
  • bindings/InterfaceBindings.cpp
📚 Learning: 2026-01-14T14:38:00.745Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/syrec PR: 514
File: cmake/ExternalDependencies.cmake:28-32
Timestamp: 2026-01-14T14:38:00.745Z
Learning: In Munich Quantum Toolkit projects, the standard pattern for Python module CMake discovery (e.g., nanobind, pybind11) uses execute_process with Python -m <module> --cmake_dir without ERROR_QUIET or explicit nanobind_ROOT validation before find_package, as the error messages from find_package failures are considered sufficiently clear.

Applied to files:

  • cmake/ExternalDependencies.cmake
  • CMakeLists.txt
  • .github/workflows/ci.yml
📚 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:

  • cmake/ExternalDependencies.cmake
  • bindings/CMakeLists.txt
  • CMakeLists.txt
  • .github/workflows/ci.yml
  • pyproject.toml
📚 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-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:

  • cmake/ExternalDependencies.cmake
  • bindings/CMakeLists.txt
  • noxfile.py
  • .github/workflows/ci.yml
  • bindings/dd/DDSimDebugBindings.cpp
  • bindings/bindings.cpp
  • bindings/InterfaceBindings.cpp
📚 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:

  • cmake/ExternalDependencies.cmake
  • CMakeLists.txt
  • .github/workflows/ci.yml
  • pyproject.toml
📚 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:

  • cmake/ExternalDependencies.cmake
  • bindings/CMakeLists.txt
  • pyproject.toml
📚 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:

  • cmake/ExternalDependencies.cmake
  • bindings/CMakeLists.txt
📚 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-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/CMakeLists.txt
  • bindings/dd/DDSimDebugBindings.cpp
  • bindings/bindings.cpp
  • bindings/InterfaceBindings.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/CMakeLists.txt
  • bindings/dd/DDSimDebugBindings.cpp
  • bindings/bindings.cpp
  • bindings/InterfaceBindings.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/CMakeLists.txt
  • bindings/dd/DDSimDebugBindings.cpp
  • bindings/bindings.cpp
  • bindings/InterfaceBindings.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:

  • noxfile.py
  • .github/workflows/ci.yml
  • bindings/InterfaceBindings.cpp
📚 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:

  • .github/workflows/ci.yml
📚 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
📚 Learning: 2025-10-11T13:21:15.212Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/ddsim PR: 674
File: .github/workflows/cd.yml:56-56
Timestamp: 2025-10-11T13:21:15.212Z
Learning: In the ddsim repository, modifying .github/workflows/cd.yml intentionally triggers CD workflow testing in CI through a change-detection mechanism in ci.yml. The change-detection job outputs a run-cd flag that controls whether build-sdist and build-wheel jobs execute in CI, allowing testing of the CD workflow (including wheel building and testing) before actual releases.

Applied to files:

  • .github/workflows/ci.yml
📚 Learning: 2025-12-02T10:08:36.022Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 1
File: .github/workflows/cd.yml:40-48
Timestamp: 2025-12-02T10:08:36.022Z
Learning: In munich-quantum-toolkit workflows, the reusable-python-packaging-sdist.yml workflow uploads the sdist artifact with the name `cibw-sdist` (or `dev-cibw-sdist` for pull requests), which means it is covered by the `cibw-*` glob pattern when downloading artifacts for deployment.

Applied to files:

  • .github/workflows/ci.yml
  • 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:

  • pyproject.toml
📚 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:

  • pyproject.toml
📚 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:

  • pyproject.toml
📚 Learning: 2025-10-09T13:13:51.224Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:171-180
Timestamp: 2025-10-09T13:13:51.224Z
Learning: In MQT Core MLIR, UnitaryInterface operations guarantee 1-1 correspondence between input and output qubits in the same order. When cloning or modifying unitary operations (e.g., removing controls), this correspondence is maintained by construction, so yielding getAllInQubits() in else-branches matches the result types from the operation's outputs.

Applied to files:

  • bindings/InterfaceBindings.cpp
🧬 Code graph analysis (7)
python/mqt/debugger/dap/messages/change_bit_dap_message.py (1)
python/mqt/debugger/pydebugger.pyi (1)
  • VariableType (157-167)
test/python/test_python_bindings.py (1)
python/mqt/debugger/pydebugger.pyi (6)
  • get_classical_variable (502-513)
  • type_ (40-41)
  • type_ (44-44)
  • type_ (208-209)
  • type_ (212-212)
  • VariableType (157-167)
test/python/test_diagnosis.py (1)
python/mqt/debugger/pydebugger.pyi (7)
  • type_ (40-41)
  • type_ (44-44)
  • type_ (208-209)
  • type_ (212-212)
  • ErrorCauseType (13-27)
  • get_diagnostics (617-622)
  • potential_error_causes (127-135)
python/mqt/debugger/pydebugger.pyi (2)
test/utils/common_fixtures.hpp (4)
  • instruction (138-145)
  • instruction (138-138)
  • instruction (192-198)
  • instruction (192-192)
src/backend/dd/DDSimDebug.cpp (1)
  • amplitudes (685-685)
bindings/dd/DDSimDebugBindings.cpp (2)
include/backend/dd/DDSimDebug.hpp (1)
  • destroyDDSimulationState (666-666)
src/backend/dd/DDSimDebug.cpp (2)
  • destroyDDSimulationState (1463-1467)
  • destroyDDSimulationState (1463-1463)
python/mqt/debugger/dap/dap_server.py (1)
python/mqt/debugger/pydebugger.pyi (5)
  • type_ (40-41)
  • type_ (44-44)
  • type_ (208-209)
  • type_ (212-212)
  • ErrorCauseType (13-27)
bindings/InterfaceBindings.cpp (2)
python/mqt/debugger/pydebugger.pyi (6)
  • real (235-236)
  • real (239-239)
  • imaginary (241-242)
  • imaginary (245-245)
  • amplitudes (269-273)
  • amplitudes (276-276)
src/backend/dd/DDSimDebug.cpp (1)
  • amplitudes (685-685)
🪛 Cppcheck (2.19.0)
bindings/dd/DDSimDebugBindings.cpp

[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 24-24: 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] 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] 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] 24-24: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingInclude)


[information] 24-24: Include file

(missingInclude)


[information] 25-25: Include file

(missingInclude)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingInclude)


[style] 27-27: The function 'bindBackend' is never used.

(unusedFunction)

bindings/bindings.cpp

[information] 18-18: Include file

(missingIncludeSystem)

bindings/InterfaceBindings.cpp

[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: 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)


[style] 63-63: The function 'bindFramework' is never used.

(unusedFunction)


[style] 652-652: The function 'bindDiagnostics' 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). (15)
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Debug) / 🏁 windows-2022 msvc Debug
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Release) / 🏁 windows-2022 msvc Release
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🇨‌ Test 🍎 (macos-14, clang, Release) / 🍎 macos-14 clang Release
  • GitHub Check: 🇨‌ Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • 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 🍎 (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: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
  • GitHub Check: 🐍 Lint / 🚨 Lint
🔇 Additional comments (27)
python/mqt/debugger/dap/messages/change_bit_dap_message.py (1)

136-136: LGTM!

The attribute rename from .type to .type_ correctly reflects the nanobind migration. This is a common pattern in nanobind bindings where type (a Python built-in) is suffixed with an underscore to avoid naming conflicts. The comparison logic against VariableType.VarBool remains correct.

bindings/debugger_patterns.txt (1)

1-3: LGTM - Valid nanobind stubgen pattern file.

This pattern file correctly defines the _hashable_values_ and _unhashable_values_map_ directives used by nanobind's stubgen tool. The empty definitions are appropriate for the default stub generation behavior.

python/mqt/debugger/dap/dap_server.py (1)

386-391: LGTM - Correct adaptation to nanobind naming convention.

The attribute rename from type to type_ aligns with nanobind's convention of appending an underscore to avoid shadowing Python builtins. Both branches are consistently updated.

noxfile.py (1)

212-240: LGTM - Well-structured stub generation session.

The session correctly follows the MQT pattern for nanobind stub generation: build the project, run nanobind.stubgen with appropriate flags, then apply license and formatting toolchains. Based on learnings, this aligns with the established approach in the munich-quantum-toolkit repositories.

.github/workflows/ci.yml (2)

128-129: LGTM - Enables stub verification in CI.

Adding check-stubs: true ensures that auto-generated stub files are verified during linting, catching any drift between the C++ bindings and their Python type stubs.


91-91: LGTM — Correct migration to nanobind in CI.

The package installation correctly switches from pybind11==3.0.1 to nanobind==2.10.2 for the C++ linter build. nanobind 2.10.2 is the latest stable version and aligns with the project's pyproject.toml requirement.

cmake/ExternalDependencies.cmake (2)

12-18: LGTM - Standard nanobind discovery pattern.

The nanobind CMake discovery correctly follows the established MQT pattern: use execute_process with Python -m nanobind --cmake_dir to locate nanobind's CMake configuration, then call find_package. Based on learnings, the absence of ERROR_QUIET is intentional since find_package errors are considered sufficiently clear.


22-27: MQT Core 3.4.0 version confirmed. The version is released and the commit hash 6bcc01e7d135058c6439c64fdd5f14b65ab88816 correctly corresponds to the v3.4.0 tag.

bindings/CMakeLists.txt (1)

8-19: LGTM!

The migration from add_mqt_python_binding to add_mqt_python_binding_nanobind is correct for the nanobind transition. Removing pybind11_json from LINK_LIBS is appropriate since nanobind has different JSON handling mechanisms.

CMakeLists.txt (1)

45-46: LGTM!

The integration of ${SKBUILD_SABI_COMPONENT} into the find_package call correctly enables Stable ABI wheel building. Based on learnings from other MQT projects, this pattern with wheel.py-api = "cp312" in pyproject.toml will build ABI3 wheels for Python 3.12+ while automatically falling back to regular wheels for earlier Python versions.

bindings/bindings.cpp (1)

18-32: LGTM!

The nanobind migration is correctly implemented:

  • NB_MODULE macro properly replaces PYBIND11_MODULE
  • Forward declarations correctly use nb::module_& type
  • The using namespace nb::literals enables named argument syntax ("arg"_a)

The static analysis hint about missing include is a false positive—nanobind headers are resolved via CMake's nanobind integration.

bindings/dd/DDSimDebugBindings.cpp (1)

27-52: LGTM!

The nanobind migration is correctly implemented:

  • Function signature properly uses nb::module_&
  • The "state"_a named argument improves the Python API
  • Docstrings use the correct raw string literal format for nanobind's auto-generated stubs

The static analysis hint about bindBackend being unused is a false positive—it's called from bindings.cpp via forward declaration.

pyproject.toml (5)

9-14: LGTM!

Build dependencies correctly updated from pybind11 to nanobind with appropriate minimum version constraint (>=2.10.2).


82-86: LGTM!

The Stable ABI configuration is correct. Based on learnings from MQT projects, wheel.py-api = "cp312" enables ABI3 wheels for Python 3.12+ (where nanobind supports Stable ABI) while automatically building regular wheels for earlier Python versions (3.10, 3.11) and free-threading builds.


240-240: LGTM!

Adding E501 (line length) to the .pyi file ignores is appropriate since stub files are auto-generated by nanobind's stubgen tool and may contain long type signatures that shouldn't be manually reformatted. Based on learnings, these stubs should not be manually modified.


308-311: LGTM!

The cibuildwheel override correctly integrates abi3audit to verify Stable ABI compliance:

  • select = "cp312-*" targets the wheels where ABI3 is enabled
  • inherit.repair-wheel-command = "append" ensures abi3audit runs after platform-specific repairs (e.g., delvewheel on Windows)
  • --strict --report flags will fail the build if ABI3 violations are detected

This is a good practice to catch any accidental use of unstable ABI features.


314-319: LGTM!

The dependency-groups build section correctly mirrors the build-system requirements with nanobind>=2.10.2.

python/mqt/debugger/pydebugger.pyi (1)

223-233: LGTM - Complex overloads are correctly structured.

The two __init__ overloads correctly represent the nanobind binding: a default constructor and one with optional real/imaginary parameters. The docstring appearing on the no-arg overload is a known stubgen artifact.

bindings/InterfaceBindings.cpp (6)

63-69: LGTM - VariableType enum binding correctly migrated.

The enum binding properly uses nb::enum_<> without export_values() (which is the nanobind convention), and includes appropriate docstrings.


84-90: LGTM - Variable binding with type_ rename.

The typetype_ rename correctly avoids shadowing Python's built-in type function, and the pattern is consistently applied across both Variable and ErrorCause classes.


93-129: LGTM - Complex class binding is well-structured.

The dual constructor pattern with default values and the __str__/__repr__ methods are correctly implemented for nanobind.


282-306: LGTM - Type checking order is correct.

The check order (nb::bool_nb::int_nb::float_) is correct. Since Python's bool is a subclass of int, checking bool first ensures boolean values are handled correctly.


619-626: LGTM - Correct lifetime management with rv_policy::reference_internal.

Using reference_internal ensures the SimulationState parent stays alive while the returned Diagnostics object exists. This is the correct nanobind pattern for objects referencing internal state. Based on learnings about nanobind lifetime management.


652-672: LGTM - ErrorCauseType and ErrorCause bindings.

The enum and class bindings follow the established nanobind patterns, with consistent type_ naming to avoid Python keyword conflict.

test/python/test_python_bindings.py (1)

325-327: LGTM - Test updated to use renamed type_ attribute.

The test correctly accesses Variable.type_ instead of Variable.type, consistent with the binding layer changes that rename the attribute to avoid shadowing Python's built-in type.

test/python/test_diagnosis.py (2)

70-74: LGTM - Test assertions updated to use type_ attribute.

The test correctly accesses ErrorCause.type_ for assertions, consistent with the binding layer changes.


81-84: LGTM - List comprehension filters updated to use type_ attribute.

The filter expressions correctly use x.type_ to filter ErrorCause objects by type, matching the updated binding API.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link

@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

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
python/mqt/debugger/pydebugger.pyi (1)

552-576: Misleading docstrings for state vector getter methods.

The docstrings for get_state_vector_full (lines 555-556) and get_state_vector_sub (lines 565-566) state: "The state vector is expected to be initialized with the correct number of qubits and allocated space for the amplitudes before calling this method."

This language describes a pre-condition for an input parameter, but these methods return a Statevector—they don't take one as input. This appears to be documentation carried over from a C-style API where the caller passes in a pre-allocated buffer.

Since stub files are auto-generated, update the docstrings in the C++ bindings to reflect that these methods return a fully-constructed Statevector.

🤖 Fix all issues with AI agents
In `@bindings/InterfaceBindings.cpp`:
- Around line 93-104: The default constructor declaration nb::init<>() for class
Complex currently includes a docstring that describes parameters that don't
exist; remove the docstring from the nb::init<>() call and ensure the long
parameter docstring remains only on the parameterized constructor
nb::init<double, double>() so that only the constructor that accepts "real" and
"imaginary" exposes those argument docs.
- Line 711: Replace the inconsistent nb::arg("instruction"),
nb::arg("include_callers") usage with the literal-argument syntax used
elsewhere: use "instruction"_a, "include_callers"_a = false so the binding
arguments match the rest of the file; ensure the pybind11 literals are available
(e.g., pybind11::literals namespace or equivalent using directive) where
InterfaceBindings.cpp defines the binding using these parameter names.

In `@python/mqt/debugger/pydebugger.pyi`:
- Around line 223-233: The docstring describing parameters is attached to the
parameterless __init__ overload instead of the (real: float, imaginary: float)
overload; update the C++ nanobind binding so the constructor docstring is bound
to the overload that takes real and imaginary (the __init__ overload with
signature matching real: float = 0.0, imaginary: float = 0.0) — e.g., move or
reassign the documentation call (nb::doc / set_documentation) from the
parameterless ctor binding to the ctor overload with parameters so stubgen emits
the docstring on the correct __init__ overload.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6f6c02b and 783b5d7.

📒 Files selected for processing (3)
  • bindings/InterfaceBindings.cpp
  • bindings/dd/DDSimDebugBindings.cpp
  • python/mqt/debugger/pydebugger.pyi
🧰 Additional context used
🧠 Learnings (8)
📓 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.
Learnt from: denialhaag
Repo: munich-quantum-toolkit/syrec PR: 514
File: cmake/ExternalDependencies.cmake:28-32
Timestamp: 2026-01-14T14:38:00.745Z
Learning: In Munich Quantum Toolkit projects, the standard pattern for Python module CMake discovery (e.g., nanobind, pybind11) uses execute_process with Python -m <module> --cmake_dir without ERROR_QUIET or explicit nanobind_ROOT validation before find_package, as the error messages from find_package failures are considered sufficiently clear.
📚 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:

  • python/mqt/debugger/pydebugger.pyi
  • bindings/InterfaceBindings.cpp
📚 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:

  • python/mqt/debugger/pydebugger.pyi
  • bindings/dd/DDSimDebugBindings.cpp
  • bindings/InterfaceBindings.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:

  • python/mqt/debugger/pydebugger.pyi
  • bindings/dd/DDSimDebugBindings.cpp
  • bindings/InterfaceBindings.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:

  • python/mqt/debugger/pydebugger.pyi
  • bindings/dd/DDSimDebugBindings.cpp
  • bindings/InterfaceBindings.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/dd/DDSimDebugBindings.cpp
  • bindings/InterfaceBindings.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/InterfaceBindings.cpp
📚 Learning: 2025-10-09T13:13:51.224Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:171-180
Timestamp: 2025-10-09T13:13:51.224Z
Learning: In MQT Core MLIR, UnitaryInterface operations guarantee 1-1 correspondence between input and output qubits in the same order. When cloning or modifying unitary operations (e.g., removing controls), this correspondence is maintained by construction, so yielding getAllInQubits() in else-branches matches the result types from the operation's outputs.

Applied to files:

  • bindings/InterfaceBindings.cpp
🧬 Code graph analysis (2)
bindings/dd/DDSimDebugBindings.cpp (2)
include/backend/dd/DDSimDebug.hpp (1)
  • destroyDDSimulationState (666-666)
src/backend/dd/DDSimDebug.cpp (2)
  • destroyDDSimulationState (1463-1467)
  • destroyDDSimulationState (1463-1463)
bindings/InterfaceBindings.cpp (2)
python/mqt/debugger/pydebugger.pyi (12)
  • init (52-53)
  • init (311-312)
  • name (202-203)
  • name (206-206)
  • value (214-215)
  • value (218-218)
  • amplitudes (269-273)
  • amplitudes (276-276)
  • opt (290-291)
  • opt (294-294)
  • instruction (34-35)
  • instruction (38-38)
src/backend/dd/DDSimDebug.cpp (1)
  • amplitudes (685-685)
🪛 Cppcheck (2.19.0)
bindings/dd/DDSimDebugBindings.cpp

[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 24-24: 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] 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] 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] 24-24: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingInclude)


[information] 24-24: Include file

(missingInclude)


[information] 25-25: Include file

(missingInclude)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingInclude)


[style] 28-28: The function 'bindBackend' is never used.

(unusedFunction)

bindings/InterfaceBindings.cpp

[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: 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)


[style] 63-63: The function 'bindFramework' is never used.

(unusedFunction)


[style] 653-653: The function 'bindDiagnostics' 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). (15)
  • GitHub Check: 🇨‌ Test 🏁 (windows-2022, msvc, Release) / 🏁 windows-2022 msvc 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 🍎 (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 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
  • GitHub Check: 🐍 Lint / 🚨 Lint
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
🔇 Additional comments (18)
python/mqt/debugger/pydebugger.pyi (9)

9-11: LGTM!

Imports are appropriate for the type annotations used throughout the stub file.


13-28: LGTM!

The enum is well-documented with clear descriptions for each error cause type.


29-155: LGTM!

The ErrorCause and Diagnostics classes are well-structured with comprehensive docstrings. The type_ naming convention appropriately avoids collision with Python's built-in type.


157-218: LGTM!

The variable representation classes are well-designed. The VariableValue docstring clearly explains that only one field is valid at a time based on the VariableType.


259-267: Verify intent: num_states setter may conflict with documented invariant.

The docstring states that num_states is "always equal to 2^num_qubits", implying a derived property. However, exposing a setter allows users to set an inconsistent value that violates this invariant.

If num_states is truly always derived from num_qubits, consider making it read-only in the C++ bindings. If the setter is intentional for low-level control, the docstring should clarify this flexibility.


278-300: LGTM!

The CompilationSettings class is well-documented with clear parameter descriptions.


302-306: LGTM!

The SimulationState docstring is clean. The previously flagged errant quote issue appears to have been resolved.


578-632: LGTM!

The breakpoint management, stack trace, diagnostics, and compilation methods are well-documented with clear signatures.


634-646: LGTM!

Factory functions are well-documented. The explicit destroy_ddsim_simulation_state function is appropriate for deterministic resource cleanup of the DD backend.

bindings/dd/DDSimDebugBindings.cpp (2)

42-52: LGTM!

The destroy_ddsim_simulation_state binding is correctly migrated to nanobind with proper named argument usage and appropriate lint suppression for the necessary reinterpret_cast.


29-40: Memory management pattern is intentional—no issue here.

The create_ddsim_simulation_state / destroy_ddsim_simulation_state pairing is an explicit manual memory management API, not a hidden leak risk. All existing usage (tests, DAP handlers, utilities) properly calls destroy() after use, and this contract is documented in the .pyi stub. This pattern is carried over from pybind11 by design and poses no concern for the nanobind migration.

bindings/InterfaceBindings.cpp (7)

24-34: LGTM!

The nanobind includes and namespace setup follow the standard pattern. The NOLINT(misc-include-cleaner) annotations are appropriate for the STL type caster headers which are used implicitly.


65-69: LGTM!

The VariableType enum binding correctly uses nanobind's nb::enum_ without export_values(), which is the preferred pattern for scoped enum access.


85-90: LGTM!

The Variable binding correctly uses type_ to avoid Python's type builtin, and def_rw is the proper nanobind equivalent of pybind11's def_readwrite.


132-160: LGTM!

The StatevectorCPP and CompilationSettings bindings are correctly migrated with proper def_rw usage and named constructor arguments.


619-649: LGTM!

The get_diagnostics method correctly uses nb::rv_policy::reference_internal for lifetime management, ensuring the SimulationState stays alive while Diagnostics objects exist. This aligns with the nanobind migration pattern from the learnings. The docstring issue noted in previous reviews appears to have been resolved.


665-673: LGTM!

The ErrorCause binding correctly uses type_ to avoid Python's type builtin, maintaining consistency with the Variable class binding pattern.


675-890: LGTM!

The Diagnostics class binding is correctly migrated. The reinterpret_cast for bool* conversion is necessary for the C interface and properly suppressed with lint comments. The memory management in suggest_new_assertions correctly pairs malloc with free.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@denialhaag denialhaag requested a review from burgholzer January 14, 2026 16:54
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 clean and once CI is green, can go in I suppose.

Copy link

@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: 0

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
bindings/InterfaceBindings.cpp (1)

833-870: Consider RAII for buffer management.

The suggest_new_assertions binding uses manual malloc/free for buffer allocation. While functional, using std::vector<char> or std::unique_ptr<char[]> would provide automatic cleanup and eliminate the need for NOLINT suppressions.

This is not a nanobind migration issue but an optional improvement opportunity.

♻️ Suggested refactor using std::vector
           [](Diagnostics* self) {
             const size_t stringSize = 2 << 17;
             const size_t count =
                 self->suggestNewAssertions(self, nullptr, nullptr, 0);
             std::vector<size_t> positions(count);
-            std::vector<char*> buffers(count);
-            for (auto& b : buffers) {
-              // NOLINTBEGIN(cppcoreguidelines-owning-memory,
-              // cppcoreguidelines-pro-type-reinterpret-cast,
-              // cppcoreguidelines-pro-bounds-pointer-arithmetic)
-              char* buffer = reinterpret_cast<char*>(
-                  malloc(sizeof(char) * stringSize)); // NOLINT
-              for (size_t i = 0; i < stringSize; i++) {
-                buffer[i] = '\0';
-              }
-              // NOLINTEND(cppcoreguidelines-owning-memory,
-              // cppcoreguidelines-pro-type-reinterpret-cast,
-              // cppcoreguidelines-pro-bounds-pointer-arithmetic)
-              b = buffer;
-            }
+            std::vector<std::vector<char>> bufferStorage(count, std::vector<char>(stringSize, '\0'));
+            std::vector<char*> buffers(count);
+            for (size_t i = 0; i < count; ++i) {
+              buffers[i] = bufferStorage[i].data();
+            }

             self->suggestNewAssertions(self, positions.data(), buffers.data(),
                                        count);
             std::vector<std::string> assertions;
-            for (auto* b : buffers) {
-              assertions.emplace_back(b);
-              free(b); // NOLINT
+            for (const auto& b : bufferStorage) {
+              assertions.emplace_back(b.data());
             }
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0e1228d and 427ad10.

📒 Files selected for processing (3)
  • bindings/InterfaceBindings.cpp
  • bindings/dd/DDSimDebugBindings.cpp
  • python/mqt/debugger/pydebugger.pyi
🧰 Additional context used
🧠 Learnings (8)
📓 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/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/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: 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.
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: denialhaag
Repo: munich-quantum-toolkit/syrec PR: 514
File: cmake/ExternalDependencies.cmake:28-32
Timestamp: 2026-01-14T14:38:00.745Z
Learning: In Munich Quantum Toolkit projects, the standard pattern for Python module CMake discovery (e.g., nanobind, pybind11) uses execute_process with Python -m <module> --cmake_dir without ERROR_QUIET or explicit nanobind_ROOT validation before find_package, as the error messages from find_package failures are considered sufficiently clear.
📚 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/dd/DDSimDebugBindings.cpp
  • python/mqt/debugger/pydebugger.pyi
  • bindings/InterfaceBindings.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/dd/DDSimDebugBindings.cpp
  • bindings/InterfaceBindings.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/dd/DDSimDebugBindings.cpp
  • python/mqt/debugger/pydebugger.pyi
  • bindings/InterfaceBindings.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/dd/DDSimDebugBindings.cpp
  • python/mqt/debugger/pydebugger.pyi
  • bindings/InterfaceBindings.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:

  • python/mqt/debugger/pydebugger.pyi
  • bindings/InterfaceBindings.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/InterfaceBindings.cpp
📚 Learning: 2025-10-09T13:13:51.224Z
Learnt from: DRovara
Repo: munich-quantum-toolkit/core PR: 1108
File: mlir/lib/Dialect/MQTOpt/Transforms/ReplaceBasisStateControlsWithIfPattern.cpp:171-180
Timestamp: 2025-10-09T13:13:51.224Z
Learning: In MQT Core MLIR, UnitaryInterface operations guarantee 1-1 correspondence between input and output qubits in the same order. When cloning or modifying unitary operations (e.g., removing controls), this correspondence is maintained by construction, so yielding getAllInQubits() in else-branches matches the result types from the operation's outputs.

Applied to files:

  • bindings/InterfaceBindings.cpp
🧬 Code graph analysis (2)
bindings/dd/DDSimDebugBindings.cpp (1)
src/backend/dd/DDSimDebug.cpp (2)
  • destroyDDSimulationState (1463-1467)
  • destroyDDSimulationState (1463-1463)
bindings/InterfaceBindings.cpp (2)
python/mqt/debugger/pydebugger.pyi (14)
  • init (52-53)
  • init (313-314)
  • name (202-203)
  • name (206-206)
  • value (214-215)
  • value (218-218)
  • real (237-238)
  • real (241-241)
  • amplitudes (271-275)
  • amplitudes (278-278)
  • opt (292-293)
  • opt (296-296)
  • instruction (34-35)
  • instruction (38-38)
src/backend/dd/DDSimDebug.cpp (1)
  • amplitudes (685-685)
🪛 Cppcheck (2.19.0)
bindings/dd/DDSimDebugBindings.cpp

[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 24-24: 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] 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] 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] 24-24: Include file

(missingIncludeSystem)


[information] 23-23: Include file

(missingInclude)


[information] 24-24: Include file

(missingInclude)


[information] 25-25: Include file

(missingInclude)


[information] 27-27: Include file

(missingIncludeSystem)


[information] 28-28: Include file

(missingIncludeSystem)


[information] 21-21: Include file

(missingInclude)


[style] 28-28: The function 'bindBackend' is never used.

(unusedFunction)

bindings/InterfaceBindings.cpp

[information] 24-24: Include file

(missingIncludeSystem)


[information] 25-25: Include file

(missingIncludeSystem)


[information] 24-24: Include file

(missingIncludeSystem)


[information] 24-24: 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)


[style] 64-64: The function 'bindFramework' is never used.

(unusedFunction)


[style] 644-644: The function 'bindDiagnostics' is never used.

(unusedFunction)


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

(assignBoolToPointer)

⏰ 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). (8)
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Debug) / 🐧 ubuntu-24.04 gcc Debug
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04-arm, gcc, Release) / 🐧 ubuntu-24.04-arm gcc Release
  • GitHub Check: 🇨‌ Test 🐧 (ubuntu-24.04, gcc, Release) / 🐧 ubuntu-24.04 gcc Release
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🇨‌ Lint / 🚨 Lint
  • GitHub Check: 🐍 Lint / 🚨 Lint
🔇 Additional comments (15)
bindings/dd/DDSimDebugBindings.cpp (3)

19-28: LGTM! Clean migration to nanobind.

The namespace alias, literals usage, and function signature update follow the standard nanobind migration pattern used in other MQT projects.


29-40: LGTM!

The simulation state creation binding is correctly migrated. The manual memory management pattern with explicit create/destroy functions is appropriate for this C-style API.


42-52: LGTM!

The addition of "state"_a named argument improves the Python API. The reinterpret_cast is correctly suppressed with NOLINTNEXTLINE given the C-style interface design.

python/mqt/debugger/pydebugger.pyi (5)

1-12: LGTM!

The stub file header and imports are correctly structured for the auto-generated nanobind stubs.


13-45: LGTM!

The ErrorCauseType enum and ErrorCause class are correctly generated with proper docstrings and the type_ naming convention to avoid Python keyword conflict.


220-247: LGTM!

The Complex class overloaded constructors are correctly documented after the previous fix. The parameterless constructor has a simple docstring, while the parameterized one includes the Args documentation.


304-308: LGTM!

The SimulationState class docstring is now clean after the previous fix. The class exposes a comprehensive debugging API with proper type annotations.


630-641: LGTM!

The factory functions are correctly typed and documented, matching the bindings in DDSimDebugBindings.cpp.

bindings/InterfaceBindings.cpp (7)

21-34: LGTM!

The nanobind includes and namespace setup follow the standard pattern. The NOLINT comments on STL includes are appropriate as these are required for type conversions but may trigger include-cleaner warnings.


85-91: LGTM!

The Variable binding correctly maps type_ to &Variable::type, following the convention to avoid Python keyword conflicts.


93-126: LGTM!

The Complex class binding is correctly structured with two constructor overloads and proper docstrings after the previous fix.


128-141: LGTM!

The StatevectorCPP wrapper is correctly bound as Statevector in Python, making the state vector accessible with proper properties.


610-617: LGTM!

The get_diagnostics binding correctly uses nb::rv_policy::reference_internal to ensure the SimulationState stays alive while the returned Diagnostics object exists. Based on learnings, this is the proper nanobind pattern for lifetime management.


700-702: LGTM - static analysis false positive.

The Cppcheck warning "Boolean value assigned to pointer" at line 702 is a false positive. The code "include_callers"_a = false is valid nanobind syntax for setting a default parameter value, not a pointer assignment.


656-664: LGTM!

The ErrorCause binding correctly uses type_ to map to &ErrorCause::type, consistent with the Variable binding pattern.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@denialhaag denialhaag enabled auto-merge (squash) January 14, 2026 23:29
@denialhaag denialhaag disabled auto-merge January 14, 2026 23:43
@denialhaag
Copy link
Member Author

@coderabbitai review

@coderabbitai
Copy link

coderabbitai bot commented Jan 14, 2026

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@noxfile.py`:
- Around line 212-240: The stubs session calls nanobind.stubgen which imports
mqt.debugger.pydebugger but never builds the extension module first; add a build
invocation before the stubgen run (e.g., call session.run("uv", "build",
"--no-build-isolation-package", "mqt-debugger", env=env) or equivalent) so the
compiled extension exists, then proceed with the existing session.run of "python
-m nanobind.stubgen" in the stubs() function.
📜 Review details

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 427ad10 and e63f7b8.

📒 Files selected for processing (2)
  • cmake/ExternalDependencies.cmake
  • noxfile.py
🧰 Additional context used
🧠 Learnings (13)
📓 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/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/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: 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/syrec PR: 514
File: cmake/ExternalDependencies.cmake:28-32
Timestamp: 2026-01-14T14:38:00.745Z
Learning: In Munich Quantum Toolkit projects, the standard pattern for Python module CMake discovery (e.g., nanobind, pybind11) uses execute_process with Python -m <module> --cmake_dir without ERROR_QUIET or explicit nanobind_ROOT validation before find_package, as the error messages from find_package failures are considered sufficiently clear.
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: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:

  • noxfile.py
📚 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:

  • noxfile.py
  • cmake/ExternalDependencies.cmake
📚 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:

  • noxfile.py
📚 Learning: 2026-01-14T14:38:00.745Z
Learnt from: denialhaag
Repo: munich-quantum-toolkit/syrec PR: 514
File: cmake/ExternalDependencies.cmake:28-32
Timestamp: 2026-01-14T14:38:00.745Z
Learning: In Munich Quantum Toolkit projects, the standard pattern for Python module CMake discovery (e.g., nanobind, pybind11) uses execute_process with Python -m <module> --cmake_dir without ERROR_QUIET or explicit nanobind_ROOT validation before find_package, as the error messages from find_package failures are considered sufficiently clear.

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

  • cmake/ExternalDependencies.cmake
📚 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: 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:

  • cmake/ExternalDependencies.cmake
📚 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:

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

  • cmake/ExternalDependencies.cmake
📚 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:

  • cmake/ExternalDependencies.cmake
⏰ 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). (1)
  • GitHub Check: 🇨‌ Test 🏁 (windows-11-arm, msvc, Release) / 🏁 windows-11-arm msvc Release
🔇 Additional comments (6)
cmake/ExternalDependencies.cmake (4)

12-18: LGTM!

The nanobind discovery pattern correctly follows the Munich Quantum Toolkit standard: execute_process with Python -m nanobind --cmake_dir without explicit validation, relying on find_package error messages for diagnostics. Based on learnings, this is the established pattern.


42-56: LGTM!

The Eigen3 fetch configuration is well-structured:

  • GIT_SHALLOW TRUE for efficient cloning.
  • Build flags correctly disable unnecessary Eigen tests and documentation.
  • BUILD_TESTING=OFF appropriately prevents fetched dependencies from building their own tests, while the project's tests are controlled separately via BUILD_MQT_DEBUGGER_TESTS.

58-68: LGTM!

The Google Test fetch is well-configured:

  • FIND_PACKAGE_ARGS enables using system-installed GTest when available.
  • gtest_force_shared_crt=ON ensures proper Windows CRT linkage.
  • Clean URL-based fetch pattern.

22-35: LGTM!

The version bump to 3.4.0 and simplified unconditional FetchContent_Declare are clean. The removal of FIND_PACKAGE_ARGS ensures consistent builds by always fetching from source. The commit hash is correctly tagged to v3.4.0.

noxfile.py (2)

244-246: LGTM!

Good handling of the edge case where no stub files are generated.


248-258: LGTM!

The workflow is well-structured: allowing exit code 1 during fix-up passes (license-tools, ruff-check, ruff-format) to accommodate file modifications, followed by a final strict ruff-check to verify everything is clean.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@denialhaag denialhaag enabled auto-merge (squash) January 14, 2026 23:50
@denialhaag denialhaag merged commit a462ea5 into main Jan 15, 2026
44 of 47 checks passed
@denialhaag denialhaag deleted the nanobind branch January 15, 2026 06:06
svenjeschmitt-ops added a commit to svenjeschmitt-ops/adjustments_for_vsc that referenced this pull request Jan 15, 2026
svenjeschmitt-ops added a commit to svenjeschmitt-ops/adjustments_for_vsc that referenced this pull request Jan 15, 2026
svenjeschmitt-ops added a commit to svenjeschmitt-ops/adjustments_for_vsc that referenced this pull request Jan 15, 2026
svenjeschmitt-ops pushed a commit to svenjeschmitt-ops/adjustments_for_vsc that referenced this pull request Jan 15, 2026
* Update mqt-core

* Replace pybind11 with nanobind

* Enable Stable ABI wheels

* Auto-generate stub file

* Ignore linter errors

* Clean up docstrings

* Simplify ExternalDependencies.cmake

* Improve stubs session
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 Minor version update python Anything related to python code

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants