Skip to content

♻️ Auto-generate stub files#916

Merged
denialhaag merged 15 commits intomainfrom
stub-files
Jan 25, 2026
Merged

♻️ Auto-generate stub files#916
denialhaag merged 15 commits intomainfrom
stub-files

Conversation

@denialhaag
Copy link
Member

@denialhaag denialhaag commented Jan 23, 2026

Description

After we have switched from pybind11 to nanobind in #911, we can now auto-generate the stub files. This PR defines a corresponding nox session and copies over all existing docstrings to the bindings code.

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 23, 2026
@denialhaag denialhaag added python Anything related to Python code c++ Anything related to C++ code labels Jan 23, 2026
@denialhaag denialhaag added the refactor Anything related to code refactoring label Jan 23, 2026
@github-project-automation github-project-automation bot moved this to In Progress in MQT Compilation Jan 23, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 23, 2026

📝 Walkthrough

Summary by CodeRabbit

  • New Features

    • Automated type-stub generation workflow and new Python binding modules exposing richer functionality.
  • Improvements

    • Expanded public APIs with many enums, classes, properties and stronger type hints.
    • Renamed/normalized public symbols and parameters (e.g., compile → compile_, map → map_, include_WCNF → include_wcnf, input → input_).
  • Documentation

    • Extensive docstring additions, updated upgrade notes and changelog entries.
  • Tests

    • Test suite updated to use renamed public APIs.
  • CI / Chores

    • CI enhanced to check generated stubs and linting.

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

Walkthrough

Large migration from pybind11 to nanobind: adds nanobind modules and registration functions, expands and tightens many Python .pyi stubs, renames several public Python symbols, updates build/CMake/packaging, and introduces stub generation and CI checks for stubs.

Changes

Cohort / File(s) Summary
CI & Tooling
\ .github/workflows/ci.yml`, `noxfile.py`, `pyproject.toml`, `.license-tools-config.json`, `CHANGELOG.md``
Enable stub checking in CI, add a nox stubs session (duplicated), update ruff per-file ignores, add qmap_patterns.txt to license excludes, adjust build targets, and update changelog.
NA module & build
\bindings/na/CMakeLists.txt`, `bindings/na/na.cpp``
Add globbed NA source collection and new nanobind Python binding target; introduce NB_MODULE entry that registers state_preparation and zoned submodules.
NA — State Preparation bindings & stubs
\bindings/na/register_state_preparation.cpp`, `bindings/na/nasp.cpp`, `python/mqt/qmap/na/state_preparation.pyi`, `python/mqt/qmap/na/init.pyi``
Add NAStatePreparationSolver nanobind class with 10-arg ctor, solve returning Result, Result.json(), generate_code, get_ops_for_solver; update pyi to Sequence-typed ops, new Result type, and re-export submodules.
NA — Zoned bindings & stubs
\bindings/na/register_zoned.cpp`, `python/mqt/qmap/na/zoned.pyi``
Split registration into registerZoned, add docstrings, change some classmethod→staticmethod, use enum.Enum, tighten type hints to mqt.core.ir.QuantumComputation, and adjust defaults/signatures.
SC bindings & stubs, tests
\bindings/sc/sc.cpp`, `python/mqt/qmap/sc.pyi`, `test/python/sc/*``
Cast json() returns to typed dicts, rename include_WCNFinclude_wcnf, rename MappingResults.inputinput_, rename mapmap_, and update expanded SC .pyi surface plus tests.
Clifford synthesis bindings & stubs
\bindings/clifford_synthesis/clifford_synthesis.cpp`, `python/mqt/qmap/clifford_synthesis.pyi``
Change json() binding to return typed dict via cast, adjust docstrings; .pyi expanded with enums (TargetMetric, Verbosity) and richer configuration/results/tableau/synthesizer APIs.
Hybrid mapper stubs
\python/mqt/qmap/hybrid_mapper.pyi``
Large expansion: enums, MapperParameters/MapperStats, NeutralAtomHybridArchitecture, HybridNAMapper, HybridSynthesisMapper with typed methods/properties.
Binding registration split
\bindings/na/register_zoned.cpp`, `bindings/na/register_state_preparation.cpp``
Move registration into distinct functions (registerZoned, registerStatePreparation) and add Python-side JSON→dict conversions with docstrings.
Project packaging, docs & renamings
\bindings/qmap_patterns.txt`, `UPGRADING.md`, `docs/mapping.md`, `python/mqt/qmap/plugins/qiskit/sc/`, `test/python/sc/``
Add empty qmap_patterns.txt, document renamings (compile→compile_, map→map_, include_WCNF→include_wcnf, MappingResults.input→input_), update docs, module exports, and tests to new names/kwargs.

Sequence Diagram(s)

(Skipped — changes are broad binding/stub/build migrations without a single new multi-component sequential flow warranting a diagram.)

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

Possibly related PRs

Suggested labels

documentation

Suggested reviewers

  • burgholzer

Poem

"🐰 I hopped through bindings, soft and light,

Swapped pybind for nanobind overnight;
Stubs now bloom, enums gleam anew,
Docstrings twinkle — hop, review! ✨"

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 42.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 summarizes the main change: auto-generating stub files after the nanobind migration, which is the core objective of this PR.
Description check ✅ Passed The description clearly explains the motivation (nanobind migration enables stub generation), what was done (nox session + docstring updates), includes changelog entries, and comprehensively covers checked items.

✏️ 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
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: 11

Caution

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

⚠️ Outside diff range comments (3)
python/mqt/qmap/na/zoned.pyi (1)

129-143: Minor docstring style inconsistency between compiler classes.

The RoutingAgnosticCompiler methods use sentence case for argument descriptions (e.g., "The quantum circuit") while RoutingAwareCompiler uses the same style but with different capitalization patterns in the base docstrings. Since these stubs are auto-generated from C++ binding docstrings, this is a cosmetic issue that would need to be addressed in the source C++ bindings if consistency is desired.

Based on learnings, stub files are auto-generated by nanobind's stubgen tool.

python/mqt/qmap/na/state_preparation.pyi (1)

151-171: Missing Returns: section in generate_code docstring.

The generate_code function docstring documents Args and Raises but is missing a Returns: section to describe the return value (str). Since stubs are auto-generated, this should be fixed in the C++ binding docstring in bindings/na/nasp/nasp.cpp.

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

173-193: Update docstrings to reflect actual exception behavior or register custom exception translator.

The docstrings document that ValueError is raised for invalid JSON, but nlohmann::json::parse() throws nlohmann::json::parse_error, which derives from nlohmann::json::exception—not a standard C++ exception. By default, nanobind translates unregistered exceptions to RuntimeError, not ValueError. Users expecting ValueError will not catch the actual exception.

Fix by either:

  1. Updating the docstrings to document RuntimeError instead of ValueError, or
  2. Registering a custom nanobind exception translator to convert nlohmann::json::parse_error to ValueError

This issue affects:

  • Architecture::from_json_file (line 53)
  • Architecture::from_json_string (line 66)
  • RoutingAgnosticCompiler::from_json_string (line 192–193)
  • RoutingAwareCompiler::from_json_string (line 344–345)
🤖 Fix all issues with AI agents
In `@bindings/na/nasp/nasp.cpp`:
- Around line 119-129: The docstring for the .def("json", ...) wrapper around
na::NASolver::Result::json is inaccurate: the implementation uses Python's
json.loads and returns a JSON-compatible Python object (dict/list), not a JSON
string. Update the docstring for the .def("json", ...) method to state it
returns a JSON-compatible Python object (e.g., "Returns the result as a
JSON-compatible Python object") and adjust the Returns: line accordingly so it
matches the actual behavior of the lambda that calls
json.loads(result.json().dump()).
- Around line 141-154: Update the generate_code docstring to include a Returns:
section describing the return value; specifically, in the docstring for function
generate_code (the block that currently lists Args including qc, result,
min_atom_dist, no_interaction_radius, zone_dist) add a Returns: entry stating
that the function returns a str containing the generated code for the given
circuit (or similar concise description), and optionally note the format or
content of the string if needed.

In `@bindings/na/zoned/zoned.cpp`:
- Around line 201-208: Docstring typo: change "compilations result" to
"compilation result" inside the pybind raw string literals used for the binding
that takes "qc"_a (the R"pb(... .naviz format.)pb" docstring) and the second
identical raw-string docstring later (the other R"pb(... )pb" around line 360)
so both occurrences read "The compilation result as a string in the .naviz
format." Ensure you update both raw string literals associated with the binding
that uses "qc"_a.
- Around line 159-170: Update the docstring for the routing-agnostic compiler
binding to clarify accepted log-level formats: state that spdlog accepts both
long names ("debug", "info", "warning", "error", "critical") and short
single-letter forms ("D", "I", "W", "E", "C") and note the default is "I"
(info); apply the same clarification to the RoutingAwareCompiler binding
docstring. Reference the binding functions/docstrings for
create_routing_agnostic_compiler (the routing-agnostic compiler docstring shown)
and RoutingAwareCompiler to locate and edit the text accordingly.

In `@bindings/sc/sc.cpp`:
- Line 228: The binding change renamed the Python attribute to "input_" causing
a breaking API change; restore backward compatibility by exposing the original
name as an alias on the MappingResults pybind class (in addition to the new
"input_") so existing code can still access MappingResults.input; update the
binding where def_rw("input_", &MappingResults::input) is defined to also define
"input" (or a def_property/deprecated shim that delegates to
MappingResults::input and emits a deprecation warning) so both names map to the
same C++ member.

In `@python/mqt/qmap/clifford_synthesis.pyi`:
- Around line 209-211: Fix the docstring typo in the overloaded constructor for
__init__ (the signature overload in clifford_synthesis.pyi taking stabilizers:
str, destabilizers: str) by updating the C++ binding source so the docstring
reads "Stabilizers and Destabilizers" (i.e., insert the missing space between
"Stabilizers" and "and") so the generated Python .pyi shows the corrected text
for that constructor overload.
- Around line 192-196: Update the docstrings for the methods sat() and unsat()
in clifford_synthesis.pyi to use Python boolean literal casing (True/False)
instead of lowercase "true"/"false": locate the def sat(self) -> bool: and def
unsat(self) -> bool: stubs and change their docstrings to say "Returns `True` if
the synthesis was successful." and "Returns `False` if the synthesis was
unsuccessful." respectively.
- Around line 69-74: The docstring for the linear_search property in the Python
stub contains a typo ("liner" → "linear"); update the source of truth in the C++
binding generation so the generated docstring for the linear_search property
(getter/setter) reads "Use linear search instead of binary search scheme for
finding the optimum. Defaults to `false`." and regenerate the stub so both the
`@property` def linear_search and its setter carry the corrected text.

In `@python/mqt/qmap/na/state_preparation.pyi`:
- Around line 69-78: The stub for Result.json has an incorrect, overly specific
return annotation (dict[str, Any])—it should be a generic object to match the
C++ binding (nb::object returned from json.loads); regenerate the
nanobind-generated stubs (run the project's stub generation script) or update
the stub so Result.json() on the NA state preparation stub uses return type
object to match the other state_preparation stub and the C++ binding (refer to
Result.json and the C++ nasp binding that returns nb::object).

In `@python/mqt/qmap/state_preparation.pyi`:
- Around line 124-144: The generate_code function's docstring lacks a Returns
section describing its str return value; update the doc comments for
generate_code (the binding that populates python/mqt/qmap/state_preparation.pyi)
to include a "Returns:" entry stating it returns the generated code as a str
(and any format/encoding expectations), and mirror that change in the
corresponding C++ binding/source doc comment so the pyi is regenerated with the
new Returns description.
- Around line 32-65: The stub's docstring for the neutral-atom state preparation
solver (the docstring that documents parameters max_x, max_y, max_c, max_r,
max_h_offset, max_v_offset, max_h_dist, max_v_dist, min_entangling_y,
max_entangling_y) has inconsistent indentation between the main body (8 spaces)
and the Note/Args/Raises sections (4 spaces); fix the C++ binding source raw
string in the nasp binding (the raw docstring in bindings/na/nasp/nasp.cpp) so
the literal uses consistent indentation (prefer 4-space indentation for all
sections) or apply a dedent/format step when emitting the Python docstring,
ensuring the updated docstring regenerates the .pyi with uniform indentation.

@codecov
Copy link

codecov bot commented Jan 23, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

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

🤖 Fix all issues with AI agents
In `@bindings/na/state_preparation.cpp`:
- Around line 161-170: The lowercase transformation result opTypeLowerStr is
computed but never used; update the lambda in get_ops_for_solver to pass the
lowered string to qc::opTypeFromString (i.e., call
qc::opTypeFromString(opTypeLowerStr)) so case-insensitive matching works, or
alternatively remove the transformation if you intend to keep case-sensitive
behavior; ensure NASolver::getOpsForSolver still receives the correct op type
derived from the lowered string.

In `@bindings/na/zoned.cpp`:
- Around line 369-372: There is an extraneous semicolon immediately after the
return statement inside the lambda that returns nb::cast<nb::typed<nb::dict,
nb::str, nb::float_>>(dict); remove the trailing semicolon so the lambda ends
cleanly (i.e., delete the standalone ";" following the return); verify the
lambda compiles and retains the nb::cast usage as-is.

In `@python/mqt/qmap/clifford_synthesis.pyi`:
- Around line 63-140: Update the Python-facing docstrings for the listed
properties to use Python boolean casing "False" instead of "false"; specifically
edit the docstrings for properties use_maxsat, linear_search,
dump_intermediate_results, minimize_gates_after_depth_optimization,
try_higher_gate_limit_for_two_qubit_gate_optimization,
minimize_gates_after_two_qubit_gate_optimization, and heuristic so their
"Defaults to `false`" becomes "Defaults to `False`". Make this change in the C++
binding source that generates these stubs (the code that produces the docstrings
for the properties named above) so the generated .pyi reflects the Python
boolean convention and matches use_symmetry_breaking's "True".
- Around line 239-240: Add a missing docstring for the result_circuit property
on the Cliffordsynthesis binding: update the property definition that exposes
result_circuit (the getter for result_circuit) to include a descriptive
docstring such as "Returns the synthesized circuit as a QuantumComputation
object." so the Python stub and runtime binding both have consistent
documentation for the result_circuit property.
- Around line 63-68: Fix the typo in the docstring for the use_maxsat property:
change "really on the binary search scheme" to "rely on the binary search
scheme". Update the documentation string in the C++ nanobind binding that
defines the use_maxsat property/getter/setter (the binding for use_maxsat in the
clifford_synthesis binding source) so the generated stub and pyi will include
the corrected phrase; do not edit the autogenerated .pyi directly.
♻️ Duplicate comments (1)
bindings/sc/sc.cpp (1)

229-229: Breaking API change already flagged.

The rename from input to input_ was flagged in a previous review. This is a breaking change that will require users to update their code from MappingResults.input to MappingResults.input_.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

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

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

216-218: Add docstring for result_circuit property.

The result_circuit property lacks a docstring, unlike other properties in this class. This will result in the auto-generated stub missing documentation for this property.

🔧 Proposed fix
  synthesizer.def_prop_ro("result_circuit", [](cs::CliffordSynthesizer& self) {
    return qasm3::Importer::imports(self.getResults().getResultCircuit());
- });
+ },
+ "Returns the synthesized circuit as a QuantumComputation object.");
🤖 Fix all issues with AI agents
In `@python/mqt/qmap/clifford_synthesis.pyi`:
- Around line 213-240: The stub lacks a docstring for the result_circuit
property because the auto-generated pyi comes from the C++ binding; update the
binding in bindings/clifford_synthesis/clifford_synthesis.cpp by supplying a
docstring for the result_circuit exposure on the pybind11 class for
CliffordSynthesizer (the def_property_readonly / def_property that exposes
result_circuit) so the generated Python stub includes the same descriptive
docstring pattern used for synthesize and results; ensure the string describes
that result_circuit returns the synthesized mqt.core.ir.QuantumComputation
result.
♻️ Duplicate comments (1)
bindings/na/zoned.cpp (1)

159-170: Consider clarifying log level short forms in the docstring.

The docstring mentions "debug", "info", "warning", "error", "critical" as possible values, but the default is "I" (the spdlog short form for "info"). While spdlog::level::from_str() accepts both forms, users may be confused by the mismatch.

🔧 Proposed fix (applies to both compilers)
-    log_level: The log level for the compiler, possible values are "debug", "info", "warning", "error", "critical"
+    log_level: The log level for the compiler, possible values are "debug"/"D", "info"/"I", "warning"/"W", "error"/"E", "critical"/"C"

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@bindings/na/register_zoned.cpp`:
- Around line 210-218: The current "stats" binding casts the returned Python
dict to nb::typed<nb::dict, nb::str, nb::float_>, which forces all values to
float and can lose ints/bools/nested structures; change the cast to use a
generic container (nb::any or nb::object) so types are preserved: in the lambda
passed to routingAgnosticCompiler.def("stats", ...) replace nb::typed<nb::dict,
nb::str, nb::float_> with nb::object (or nb::any) and do the same for the
analogous binding around lines 363-371, keeping use of self.getStatistics() and
json.loads(stats.dump()) but returning the generic nb type instead of float-only
typed dict.

@denialhaag denialhaag added the minor Changes leading to a minor version increase label Jan 24, 2026
@denialhaag denialhaag requested a review from burgholzer January 24, 2026 14:37
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.

Nice. This looks great to me! I'll leave it to you whether you actually want to perform the breaking renaming changes.

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

Caution

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

⚠️ Outside diff range comments (2)
python/mqt/qmap/plugins/qiskit/sc/compile.py (1)

44-69: Make include_wcnf and following parameters keyword-only to avoid FBT warnings and prevent accidental positional misuse in the long function signature.

Add * separator before include_wcnf (line 63). All existing call sites across the codebase and documentation examples already use keyword arguments for trailing parameters, so this change is safe and has no breaking impact.

♻️ Proposed change
 def compile_(
     circ: CircuitInputType,
     arch: str | Arch | Architecture | BackendV2 | None,
@@
     commander_grouping: CommanderGrouping = CommanderGrouping.fixed3,
     swap_reduction: SwapReduction = SwapReduction.coupling_limit,
     swap_limit: int = 0,
+    *,
     include_wcnf: bool = False,
     use_subsets: bool = True,
     subgraph: set[int] | None = None,
docs/mapping.md (1)

152-152: Update the function reference for consistency.

The note references `compile` method but the function has been renamed to compile_. This should be updated to maintain consistency with the API change.

Proposed fix
-Such an optimization pass is conducted by default in the `compile` method after the circuit has been mapped.
+Such an optimization pass is conducted by default in the `compile_` method after the circuit has been mapped.
♻️ Duplicate comments (1)
bindings/sc/sc.cpp (1)

226-231: Keep a backward-compatible input alias for MappingResults.

Renaming to input_ alone breaks existing code; retain an alias (optionally deprecated).

🔧 Suggested compatibility shim
   nb::class_<MappingResults>(m, "MappingResults",
                              "Class representing the results of a mapping.")
       .def(nb::init<>())
       .def_rw("input_", &MappingResults::input)
+      .def_rw("input", &MappingResults::input) // TODO: deprecate later
       .def_rw("output", &MappingResults::output)

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

Caution

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

⚠️ Outside diff range comments (1)
docs/mapping.md (1)

174-174: Fix phrasing: “orders of magnitude.”

Use the standard phrase “orders of magnitude” (singular “magnitude”).

@denialhaag denialhaag enabled auto-merge (squash) January 25, 2026 10:31
@denialhaag denialhaag merged commit e76b22d into main Jan 25, 2026
32 checks passed
@denialhaag denialhaag deleted the stub-files branch January 25, 2026 10:38
@github-project-automation github-project-automation bot moved this from In Progress to Done in MQT Compilation Jan 25, 2026
@denialhaag denialhaag removed the c++ Anything related to C++ code label Jan 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

minor Changes leading to a minor version increase 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.

2 participants