Skip to content

Conversation

@marcelwa
Copy link
Contributor

@marcelwa marcelwa commented Dec 3, 2025

Description

This PR aims to introduce session authentication via QDMI to FoMaC. I didn't see a clear path to implement this without any API changes.

In essence, in C++ land, FoMaC has been renamed to Session (as agreed upon by @burgholzer) and is no longer a singleton; it must be instantiated to handle sessions. The implementation sets session parameters during construction. The Python bindings directly follow the new C++ semantics. Furthermore, the extensions have been propagated to backend.py and provider.py for Qiskit integration.

On the driver side, Driver::addDynamicDeviceLibrary has been extended with a qdmi::DeviceSessionConfig parameter to set QDMI_DEVICE_SESSION_PARAMETERs upon loading a library. This function has also been exposed to Python through mqt.core.qdmi.fomac, which is an inconsistency, but the Driver interface is currently not available in the bindings. We could clean this up later.

Known limitations:

All this will be addressed in future PRs.

Fixes #1350
Fixes #1332

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.

@marcelwa marcelwa added this to the QDMI Support milestone Dec 3, 2025
@marcelwa marcelwa self-assigned this Dec 3, 2025
@marcelwa marcelwa added enhancement New feature or request c++ Anything related to C++ code python Anything related to Python code QDMI Anything related to QDMI labels Dec 3, 2025
@codecov
Copy link

codecov bot commented Dec 3, 2025

Codecov Report

❌ Patch coverage is 90.61914% with 50 lines in your changes missing coverage. Please review.

Files with missing lines Patch % Lines
include/mqt-core/qdmi/Common.hpp 87.6% 25 Missing ⚠️
src/fomac/FoMaC.cpp 91.5% 14 Missing ⚠️
src/qdmi/Driver.cpp 82.1% 5 Missing ⚠️
include/mqt-core/fomac/FoMaC.hpp 91.1% 4 Missing ⚠️
src/qdmi/Common.cpp 90.4% 2 Missing ⚠️

📢 Thoughts on this report? Let us know!

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces QDMI authentication support to FoMaC by transforming it from a singleton pattern to an instantiable session-based architecture. The implementation enables per-session authentication while maintaining backward compatibility through module-level convenience functions.

Key Changes:

  • FoMaC is now instantiable with lazy initialization and session parameter management
  • Added SessionParameter enum with authentication options (TOKEN, AUTHFILE, AUTHURL, USERNAME, PASSWORD, PROJECTID, and 5 custom parameters)
  • Implemented move semantics to support session transfer while preventing copying
  • Python bindings expose FoMaC as Session class with backward-compatible devices() function

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
include/mqt-core/fomac/FoMaC.hpp Adds SessionParameter enum, removes singleton pattern, adds session management members (mutex, initialized flag, pendingParameters map) and move semantics
src/fomac/FoMaC.cpp Implements deferred initialization via ensureInitialized(), session parameter validation, move operations, and module-level convenience functions
src/na/fomac/Device.cpp Updates to use namespace-level getDevices() function instead of static class method
test/fomac/test_fomac.cpp Adds comprehensive authentication tests including parameter validation, URL/file validation, and initialization state tests
bindings/fomac/fomac.cpp Exposes SessionParameter enum and FoMaC class as Python Session with set_session_parameter and get_devices methods
python/mqt/core/fomac.pyi Adds type stubs for Session class, SessionParameter enum, and module-level session functions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 3, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features

    • Added Session-based API with configurable authentication parameters (token, auth file/URL, username, password, project ID, custom fields)
    • Added dynamic device library loading support (non-Windows platforms)
  • Refactoring

    • Refactored device access from global function to instance method via Session
    • Improved singleton lifecycle management for internal device implementations
    • Added shared utilities for QDMI parameter handling and error reporting
  • Documentation

    • Updated QDMI backend documentation with authentication examples and usage patterns

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

Walkthrough

Replaces the FoMaC singleton with an instantiable fomac::Session (SessionConfig with auth fields), migrates bindings/tests/docs to Session APIs, adds qdmi::DeviceSessionConfig and dynamic device library loading (non-Windows), and removes explicit singleton lifecycle across QDMI device implementations.

Changes

Cohort / File(s) Summary
Core API — Session
include/mqt-core/fomac/FoMaC.hpp, src/fomac/FoMaC.cpp
Rename FoMaC → Session, add SessionConfig (token, auth_file, auth_url, username, password, project_id, custom1..5), make getDevices() an instance method, add constructor/destructor, move semantics, and parameter validation.
Bindings — Python (FoMaC)
bindings/fomac/fomac.cpp, bindings/na/fomac/fomac.cpp
Swap pybind11 bindings from FoMaC types to Session types (Session, Session::Device/Job/Site/Operation), add Session constructor and get_devices(), remove top-level devices() binding, add non-Windows add_dynamic_device_library(...).
NA wrappers / Device types
include/mqt-core/na/fomac/Device.hpp, src/na/fomac/Device.cpp
Update NA wrappers to use fomac::Session::Device, adjust constructors, factory methods, and all signatures/types to Session variants.
QDMI driver & dynamic loading
include/mqt-core/qdmi/Driver.hpp, src/qdmi/Driver.cpp, src/qdmi/CMakeLists.txt
Add qdmi::DeviceSessionConfig (optional auth fields), extend QDMI_Device_impl_d ctor and Driver::addDynamicDeviceLibrary to accept config and bind session parameters into device initialization; adjust CMake targets and linkage.
QDMI common utilities
include/mqt-core/qdmi/Common.hpp, src/qdmi/Common.cpp
Add qdmi helpers: enum toString overloads, qdmi::throwIfError(...), unreachable() and validation macros used across QDMI layers.
Singleton lifecycle removal — QDMI devices
include/mqt-core/qdmi/*/Device.hpp, src/qdmi/dd/Device.cpp, src/qdmi/sc/Device.cpp, src/qdmi/na/Device.cpp
Remove atomic instance / initialize()/finalize() APIs; replace with function-local static singletons in get(), centralize argument validation (IS_INVALID_ARGUMENT), make finalize no-op where applicable.
NA directory reorg & includes
src/na/CMakeLists.txt, src/qdmi/na/*, tests test/na/*
Move NA device headers/impl from na/deviceqdmi/na, update include paths and CMake references.
Python stubs & plugins
python/mqt/core/fomac.pyi, python/mqt/core/plugins/qiskit/provider.py
Add Session stub with auth kwargs and get_devices(), remove top-level devices(), expose add_dynamic_device_library on non-Windows, update QDMIProvider to accept auth/session kwargs and construct a Session.
Tests — C++ & Python
test/fomac/*, test/na/*, test/qdmi/*, test/python/**
Update tests to use Session types and session.get_devices(), add Session auth tests, update monkeypatches/mocks to patch Session.get_devices, add DeviceSessionConfig-driven driver tests.
Docs & Changelog
docs/qdmi/driver.md, docs/qdmi/qdmi_backend.md, CHANGELOG.md
Update docs to use Session and session.get_devices(), document authentication and dynamic-loading usage; update changelog entries.

Sequence Diagram(s)

sequenceDiagram
    participant Client as Caller (Python / C++)
    participant Session as fomac::Session
    participant Driver as qdmi::Driver
    participant DynLib as DynamicDeviceLibrary
    participant Device as QDMI Device Impl

    Client->>Session: Session(SessionConfig{token, auth..., custom...})
    Note right of Session: validate params, initialize session state
    Client->>Driver: addDynamicDeviceLibrary(libPath, prefix, DeviceSessionConfig)
    Driver->>DynLib: load library (dlopen / platform loader)
    Driver->>Device: construct QDMI_Device_impl_d(lib, DeviceSessionConfig)
    Device->>Device: setParameter(baseUrl/token/authFile/...) (skip unsupported)
    Device-->>Driver: register devices in driver
    Client->>Session: session.get_devices()
    Session->>Driver: query loaded devices
    Driver-->>Session: return vector<Device>
    Session-->>Client: deliver devices list
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~60 minutes

  • Areas needing focused review:
    • Session constructor: parameter validation, filesystem/authUrl checks, error paths and move semantics.
    • Driver::addDynamicDeviceLibrary and QDMI_Device_impl_d: correct binding of DeviceSessionConfig, handling of unsupported params (skip vs throw).
    • qdmi::throwIfError and toString coverage / exception mapping.
    • All pybind11 bindings for remaining FoMaC references and correct non-Windows guard for dynamic loader binding.
    • Singleton → function-local static conversions for thread-safety and lifecycle (DD/SC/NA implementations).

Possibly related PRs

Suggested labels

refactor

Suggested reviewers

  • ystade
  • burgholzer

Poem

🐰 I hopped from singleton hill to a Session so neat,

Tokens and files tucked snug in a neat little seat.
Libraries load when the meadow's not Windows, hooray —
Devices now answer when called by session today.
A rabbit's small cheer for cleaner code on its feet.

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 31.73% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main change: introducing QDMI authentication via FoMaC. It is clear, concise, and directly reflects the primary objective of the PR.
Description check ✅ Passed The PR description comprehensively covers the changes, including the FoMaC-to-Session rename, removal of singleton pattern, authentication implementation, and driver extensions. It documents known limitations and references fixed issues (#1350, #1332). Most checklist items are completed.
Linked Issues check ✅ Passed The PR successfully addresses both linked issues: #1350 by adding authentication parameters to Session/backends/providers, and #1332 by reorganizing QDMI devices into a unified directory structure (qdmi/na). Session instantiation with auth parameters satisfies the authentication requirements.
Out of Scope Changes check ✅ Passed All changes align with the stated objectives. The FoMaC-to-Session refactor, authentication parameter addition, directory reorganization, singleton removal, and related test/documentation updates are all necessary for the PR's goals. No extraneous modifications detected.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch qdmi-auth

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between a3023d5 and 931e0e4.

📒 Files selected for processing (5)
  • CHANGELOG.md (4 hunks)
  • include/mqt-core/qdmi/Common.hpp (1 hunks)
  • src/fomac/FoMaC.cpp (13 hunks)
  • src/qdmi/CMakeLists.txt (2 hunks)
  • test/fomac/test_fomac.cpp (8 hunks)
🧰 Additional context used
🧠 Learnings (13)
📓 Common learnings
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: src/qdmi/sc/Device.cpp:97-102
Timestamp: 2025-12-07T09:10:31.820Z
Learning: In the munich-quantum-toolkit/core repository, duplication of QDMI-related macros (such as IS_INVALID_ARGUMENT) across device implementations (e.g., in src/qdmi/sc/Device.cpp and src/qdmi/dd/Device.cpp) is acceptable as a temporary measure. The preferred long-term solution is to upstream these macros to the QDMI repository rather than creating local shared headers, so they can be reused across all dependent projects.
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.
📚 Learning: 2025-10-14T14:37:38.047Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/yaqs PR: 212
File: CHANGELOG.md:12-15
Timestamp: 2025-10-14T14:37:38.047Z
Learning: In the munich-quantum-toolkit/yaqs project, changelog entries follow the template: "- $TITLE ([#$NUMBER]($URL)) ([**AUTHOR**](https://github.com/$AUTHOR))". Issue references should not be included in changelog entries; the PR number is sufficient for traceability.

Applied to files:

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

Applied to files:

  • CHANGELOG.md
  • test/fomac/test_fomac.cpp
📚 Learning: 2025-11-04T14:28:32.371Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.

Applied to files:

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

Applied to files:

  • CHANGELOG.md
  • src/qdmi/CMakeLists.txt
  • test/fomac/test_fomac.cpp
  • include/mqt-core/qdmi/Common.hpp
  • src/fomac/FoMaC.cpp
📚 Learning: 2025-12-01T11:00:40.342Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 1
File: CHANGELOG.md:18-18
Timestamp: 2025-12-01T11:00:40.342Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, the CHANGELOG.md intentionally references the parent MQT Core repository's release notes (https://github.com/munich-quantum-toolkit/core/releases) because the plugin repository is based on work performed in the parent repository.

Applied to files:

  • CHANGELOG.md
📚 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:

  • src/qdmi/CMakeLists.txt
  • test/fomac/test_fomac.cpp
📚 Learning: 2025-11-05T07:42:45.507Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.

Applied to files:

  • test/fomac/test_fomac.cpp
  • src/fomac/FoMaC.cpp
📚 Learning: 2025-10-17T11:09:50.147Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_site_handling.py:162-173
Timestamp: 2025-10-17T11:09:50.147Z
Learning: For QDMI tests that reference a specific named device via fixture (e.g., "MQT NA Default QDMI Device"), it is acceptable to hard-code device-specific values such as zone counts and indices, as these tests are intentionally coupled to that device's structure and serve as structural invariant checks.

Applied to files:

  • test/fomac/test_fomac.cpp
📚 Learning: 2025-11-04T14:26:25.420Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/conftest.py:11-19
Timestamp: 2025-11-04T14:26:25.420Z
Learning: In the munich-quantum-toolkit/core repository, Qiskit is always available as a dependency during testing, so import guards for qiskit-dependent imports in test files (e.g., test/python/qdmi/qiskit/*.py) are not necessary.

Applied to files:

  • test/fomac/test_fomac.cpp
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.

Applied to files:

  • test/fomac/test_fomac.cpp
📚 Learning: 2025-11-04T19:50:14.489Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/helpers/test_utils.cpp:103-118
Timestamp: 2025-11-04T19:50:14.489Z
Learning: In the munich-quantum-toolkit/core QDMI device tests, when QDMI APIs return a string result size `ks` that includes a null terminator, the pattern `std::string keys(ks - 1, '\0'); ... device_job_get_results(..., ks, keys.data(), nullptr)` is correct and safe. The string allocates `ks` bytes total (ks-1 content + implicit null), and the API writing `ks` bytes (including trailing null) fits exactly into this space without buffer overrun.

Applied to files:

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

Applied to files:

  • src/fomac/FoMaC.cpp
🧬 Code graph analysis (2)
include/mqt-core/qdmi/Common.hpp (3)
src/qdmi/sc/Device.cpp (5)
  • ADD_SINGLE_VALUE_PROPERTY (91-92)
  • ADD_SINGLE_VALUE_PROPERTY (94-95)
  • ADD_SINGLE_VALUE_PROPERTY (257-258)
  • ADD_SINGLE_VALUE_PROPERTY (366-367)
  • ADD_SINGLE_VALUE_PROPERTY (368-369)
src/qdmi/dd/Device.cpp (9)
  • ADD_SINGLE_VALUE_PROPERTY (196-197)
  • ADD_SINGLE_VALUE_PROPERTY (199-200)
  • ADD_SINGLE_VALUE_PROPERTY (208-209)
  • ADD_SINGLE_VALUE_PROPERTY (212-213)
  • ADD_SINGLE_VALUE_PROPERTY (304-305)
  • ADD_SINGLE_VALUE_PROPERTY (330-331)
  • ADD_SINGLE_VALUE_PROPERTY (333-334)
  • ADD_SINGLE_VALUE_PROPERTY (335-336)
  • ADD_SINGLE_VALUE_PROPERTY (400-401)
src/qdmi/na/Device.cpp (2)
  • ADD_SINGLE_VALUE_PROPERTY (88-89)
  • ADD_SINGLE_VALUE_PROPERTY (91-92)
src/fomac/FoMaC.cpp (3)
src/qdmi/Driver.cpp (8)
  • queryProperty (297-301)
  • queryProperty (297-298)
  • QDMI_session_alloc (414-416)
  • QDMI_session_alloc (414-414)
  • QDMI_session_free (425-427)
  • QDMI_session_free (425-425)
  • QDMI_session_init (418-423)
  • QDMI_session_init (418-418)
include/mqt-core/qdmi/Common.hpp (16)
  • throwIfError (150-150)
  • unreachable (28-28)
  • toString (109-139)
  • toString (109-109)
  • toString (153-181)
  • toString (153-153)
  • toString (184-202)
  • toString (184-184)
  • toString (206-235)
  • toString (206-207)
  • toString (238-280)
  • toString (238-238)
  • toString (283-321)
  • toString (283-283)
  • toString (324-372)
  • toString (324-324)
src/qdmi/Common.cpp (2)
  • throwIfError (25-55)
  • throwIfError (25-25)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
🔇 Additional comments (27)
include/mqt-core/qdmi/Common.hpp (6)

1-35: LGTM! Well-structured utility header.

The unreachable() helper is correctly implemented with compiler-specific intrinsics for GCC/Clang and MSVC. The header documentation clearly notes the intent to upstream this to QDMI core.


36-106: Macros are well-guarded and functional.

The macros are appropriately wrapped with NOLINTBEGIN/END for bugprone-macro-parentheses. Based on learnings, the duplication of these macros across device implementations is acceptable as a temporary measure until they are upstreamed to the QDMI repository.

Note: The STRNCPY macro requires manual null-termination on non-Windows platforms, which is correctly handled at the usage site (line 74).


141-150: Good separation of declaration and definition.

The throwIfError declaration with comprehensive documentation is clean. The implementation in src/qdmi/Common.cpp correctly maps error codes to appropriate C++ exception types.


152-235: Session and Device Session parameter toString overloads look complete.

All standard and custom parameter values are handled, including the *_MAX sentinel values.


237-372: Property toString overloads provide comprehensive coverage.

The toString overloads for QDMI_Site_Property, QDMI_Operation_Property, and QDMI_Device_Property handle all documented values including custom properties.


108-139: No action required—all QDMI_STATUS enum values are covered.

The switch statement handles all 13 status codes defined in QDMI v1.2.0: QDMI_SUCCESS, QDMI_WARN_GENERAL, and all 11 QDMI_ERROR_* variants. The unreachable() fallback is appropriate because it signals that the match is exhaustive for the current QDMI version.

src/qdmi/CMakeLists.txt (3)

9-30: LGTM! Clean common library setup.

The qdmi-common target is properly guarded with NOT TARGET, includes the correct sources and headers, and links to the required dependencies. The comment on line 12 correctly says "Add common library" (addressed from previous review).


52-57: Driver dependencies updated correctly.

The driver now correctly links to MQT::CoreQDMICommon publicly, making the common utilities available to consumers.


59-62: Target list propagation is correct.

Using list(APPEND ...) followed by set(... PARENT_SCOPE) ensures the updated target list is visible to the parent CMakeLists.txt.

src/fomac/FoMaC.cpp (8)

33-74: Site query methods correctly migrated to Session namespace.

All site property getters are properly namespaced under Session::Device::Site and use the existing queryProperty template pattern.


75-174: Operation query methods look correct.

The operation methods including getSites(), getSitePairs(), and various property getters are properly implemented with appropriate null checks and optional handling.


175-275: Device property methods correctly implemented.

All device property getters follow the established pattern and correctly handle optional properties.


277-305: Job submission uses centralized error handling.

The submitJob method correctly uses qdmi::throwIfError for all QDMI calls, maintaining consistent error handling throughout.


307-327: Job check/wait/cancel methods are correct.

The wait() method properly handles the QDMI_ERROR_TIMEOUT case by returning false instead of throwing.


605-697: Session constructor implements robust exception safety.

The constructor correctly:

  1. Allocates session and validates immediately
  2. Uses a cleanup lambda to ensure QDMI_session_free is called on any exception
  3. Validates authFile existence before use
  4. Validates authUrl format with a comprehensive regex
  5. Handles QDMI_ERROR_NOTSUPPORTED gracefully for optional parameters

The URL regex (lines 670-672) now correctly handles localhost, IPv4, and IPv6 addresses (addressed from previous reviews).


699-718: Move semantics correctly implemented.

The move constructor and move assignment operator properly transfer ownership and null out the source. Thread-safety was intentionally deferred per reviewer discussion.


720-730: LGTM! getDevices properly queries session property.

The method correctly queries QDMI_SESSION_PROPERTY_DEVICES and transforms the raw QDMI_Device handles into Device wrapper objects.

CHANGELOG.md (2)

14-14: Changelog entries are well-formatted and comprehensive.

The entries correctly follow the established format with PR references and author links. The changes are appropriately categorized under Added, Changed, and Fixed sections.

Also applies to: 25-29, 42-42


283-283: PR link reference added correctly.

The [#1355] link definition is properly placed in alphabetical order within the PR links section.

test/fomac/test_fomac.cpp (8)

31-68: Test fixtures correctly migrated to Session-based API.

All test fixtures properly use Session::Device, Session::Job, and related types. The getDDSimulatorDevice() helper correctly creates a Session instance to retrieve devices.


105-229: toString and throwIfError tests provide comprehensive coverage.

The tests verify all QDMI status codes, properties, and error handling behavior. Using qdmi::toString and qdmi::throwIfError correctly tests the new common utilities.


745-758: Session parameter toString tests are complete.

All QDMI_SESSION_PARAMETER_* values are tested including MAX and custom parameters (addressed from past review).


777-837: URL validation tests provide excellent coverage.

The tests cover:

  • Valid HTTPS/HTTP URLs
  • Localhost with and without port/path
  • IPv4 and IPv6 addresses
  • Invalid cases (not a URL, unsupported protocol, missing protocol, empty string)

This addresses past review feedback about edge cases.


839-863: Auth file tests correctly validate file existence.

The test uses a thread-based unique filename and properly cleans up the temporary file. The collision risk with parallel test execution was accepted per reviewer discussion.


898-958: Custom parameter tests appropriately handle backend variations.

The tests now use GTEST_SKIP() when custom parameters are unsupported, and verify getDevices() succeeds when Session construction succeeds. This addresses the previous review feedback about tests that always passed.


988-994: Helper function correctly placed in anonymous namespace.

The getDevices() helper is now in an anonymous namespace for internal linkage, and the redundant inline specifier was removed (both addressed from past reviews).


996-1036: Test instantiation correctly uses Session types.

The parameterized tests use testing::ValuesIn(getDevices()) and Session::Device for parameter info, properly supporting the new Session-based API.


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.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Dec 3, 2025

Note

Other AI code review bot(s) detected

CodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review.

📝 Walkthrough

Summary by CodeRabbit

Release Notes

  • New Features
    • Added session parameter management for authentication configuration (tokens, authentication files/URLs, credentials, and custom parameters).
    • Introduced multi-session support with isolated session instances.
    • Added validation for authentication parameters to ensure correctness before initialization.

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

Walkthrough

The PR introduces session parameter management to FoMaC by refactoring it from a singleton pattern to an instance-based architecture with lazy initialization. A new SessionParameter enum and Session class enable per-instance authentication configuration. Namespace-level helper functions provide backward-compatible access to a default session. Python bindings and comprehensive tests are added.

Changes

Cohort / File(s) Summary
Session Parameter Support
include/mqt-core/fomac/FoMaC.hpp, src/fomac/FoMaC.cpp
Added SessionParameter enum with TOKEN, AUTHFILE, AUTHURL, USERNAME, PASSWORD, PROJECTID, and CUSTOM1–CUSTOM5 values. Implemented setSessionParameter() method with parameter validation (AUTHFILE existence, AUTHURL format).
FoMaC Singleton → Instance Refactor
include/mqt-core/fomac/FoMaC.hpp, src/fomac/FoMaC.cpp
Refactored FoMaC from singleton to instance-based class. Added mutable mutex, pending parameters map, and ensureInitialized() for lazy initialization. Enabled move semantics (move constructor/assignment) and safeguarded destructor against null sessions. Changed getDevices() from static to non-static member.
Namespace-Level Convenience API
src/fomac/FoMaC.cpp
Added namespace-scope getDevices() and setSessionParameter() functions that delegate to a default static FoMaC instance for backward compatibility.
Python Bindings & Stubs
bindings/fomac/fomac.cpp, python/mqt/core/fomac.pyi
Exposed SessionParameter enum and Session class to Python, including instance methods set_session_parameter() and get_devices(), module-level set_session_parameter() function, and updated __all__ exports.
Namespace Qualifier Updates
src/na/fomac/Device.cpp, test/fomac/test_fomac.cpp
Updated calls from FoMaC::getDevices() to fomac::getDevices() to use the new namespace-level function.
Authentication Test Suite
test/fomac/test_fomac.cpp
Added comprehensive AuthenticationTest suite validating session parameters, URL/file format checking, parameter acceptance across all SessionParameter variants, and post-initialization write protection.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

  • Areas requiring focus:
    • Parameter validation logic in ensureInitialized() (file format checks, regex validation)
    • Move semantics implementation correctness and state transfer
    • Lazy initialization sequencing and thread-safety (mutex usage)
    • Python binding layer integration with C++ exceptions
    • Test coverage completeness for parameter validation edge cases

Possibly related PRs

  • #1299 — Updates FoMaC device access patterns to use namespace-level fomac::getDevices alongside this PR's refactor
  • #1236 — Related NA FoMaC device code and binding changes that coordinate with this PR's namespace-level function updates

Suggested labels

refactor

Suggested reviewers

  • burgholzer
  • ystade

Poem

🐰 A session hops in, parameters tucked tight,
Lazy and clever, it waits for the right sight,
With auth in its pocket and validation so keen,
The FoMaC now instances where singletons had been!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning, 1 inconclusive)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.19% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
Linked Issues check ❓ Inconclusive The PR addresses #1350's core requirement (authentication via QDMI), but the implementation is incomplete; critical remaining work (Qiskit Backend support, Python tests, documentation) is acknowledged but explicitly deferred pending feedback. Complete Qiskit Backend support and tests to fully satisfy #1350 requirements, or update issue scope if this PR is intended as a preliminary foundational change.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title 'QDMI authentication via FoMaC' clearly and concisely summarizes the main change, which is introducing authentication functionality to the FoMaC component via QDMI.
Description check ✅ Passed The description provides comprehensive context including problem statement, implementation approach, API changes, design decisions, and explicitly notes feedback is being sought before completing remaining work.
Out of Scope Changes check ✅ Passed All changes are directly related to introducing session authentication via QDMI (SessionParameter enum, Session class, setSessionParameter methods, validation logic, and refactored FoMaC initialization).
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch qdmi-auth

📜 Recent review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e30566c and f1d4360.

📒 Files selected for processing (6)
  • bindings/fomac/fomac.cpp (2 hunks)
  • include/mqt-core/fomac/FoMaC.hpp (4 hunks)
  • python/mqt/core/fomac.pyi (3 hunks)
  • src/fomac/FoMaC.cpp (6 hunks)
  • src/na/fomac/Device.cpp (1 hunks)
  • test/fomac/test_fomac.cpp (5 hunks)
🧰 Additional context used
🧠 Learnings (4)
📓 Common learnings
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.
📚 Learning: 2025-11-05T07:42:45.507Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.

Applied to files:

  • python/mqt/core/fomac.pyi
  • include/mqt-core/fomac/FoMaC.hpp
  • test/fomac/test_fomac.cpp
  • src/fomac/FoMaC.cpp
📚 Learning: 2025-11-04T14:28:32.371Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.

Applied to files:

  • test/fomac/test_fomac.cpp
  • src/fomac/FoMaC.cpp
📚 Learning: 2025-10-17T11:09:50.147Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_site_handling.py:162-173
Timestamp: 2025-10-17T11:09:50.147Z
Learning: For QDMI tests that reference a specific named device via fixture (e.g., "MQT NA Default QDMI Device"), it is acceptable to hard-code device-specific values such as zone counts and indices, as these tests are intentionally coupled to that device's structure and serve as structural invariant checks.

Applied to files:

  • test/fomac/test_fomac.cpp
🧬 Code graph analysis (5)
src/na/fomac/Device.cpp (3)
include/mqt-core/na/fomac/Device.hpp (1)
  • d (124-124)
src/fomac/FoMaC.cpp (4)
  • getDevices (888-899)
  • getDevices (888-888)
  • getDevices (909-911)
  • getDevices (909-909)
include/mqt-core/fomac/FoMaC.hpp (1)
  • getDevices (783-783)
python/mqt/core/fomac.pyi (2)
include/mqt-core/fomac/FoMaC.hpp (2)
  • param (769-770)
  • Device (646-646)
python/mqt/core/na/fomac.pyi (2)
  • Device (17-114)
  • devices (116-117)
bindings/fomac/fomac.cpp (2)
src/fomac/FoMaC.cpp (8)
  • setSessionParameter (862-886)
  • setSessionParameter (862-863)
  • setSessionParameter (913-916)
  • setSessionParameter (913-914)
  • getDevices (888-899)
  • getDevices (888-888)
  • getDevices (909-911)
  • getDevices (909-909)
include/mqt-core/fomac/FoMaC.hpp (2)
  • setSessionParameter (793-794)
  • getDevices (783-783)
include/mqt-core/fomac/FoMaC.hpp (1)
src/fomac/FoMaC.cpp (4)
  • noreturn (39-45)
  • FoMaC (811-814)
  • FoMaC (816-820)
  • FoMaC (822-827)
test/fomac/test_fomac.cpp (2)
src/fomac/FoMaC.cpp (14)
  • getDevices (888-899)
  • getDevices (888-888)
  • getDevices (909-911)
  • getDevices (909-909)
  • toString (48-78)
  • toString (48-48)
  • toString (80-106)
  • toString (80-80)
  • toString (108-145)
  • toString (108-108)
  • toString (146-179)
  • toString (146-146)
  • toString (180-223)
  • toString (180-180)
include/mqt-core/fomac/FoMaC.hpp (7)
  • getDevices (783-783)
  • toString (146-146)
  • toString (149-149)
  • toString (152-152)
  • toString (155-155)
  • toString (158-163)
  • toString (158-158)
🪛 GitHub Check: 🇨‌ Lint / 🚨 Lint
src/fomac/FoMaC.cpp

[warning] 864-864: src/fomac/FoMaC.cpp:864:9 [clang-diagnostic-ctad-maybe-unsupported]
'std::scoped_lock' may not intend to support class template argument deduction


[warning] 844-844: src/fomac/FoMaC.cpp:844:3 [modernize-use-scoped-lock]
use 'std::scoped_lock' instead of 'std::lock_guard'


[warning] 844-844: src/fomac/FoMaC.cpp:844:3 [misc-const-correctness]
variable 'lock' of type 'std::lock_guardstd::mutex' can be declared 'const'

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
🔇 Additional comments (17)
src/na/fomac/Device.cpp (1)

578-586: LGTM!

The update to use the namespace-scoped fomac::getDevices() correctly aligns with the new API design where device retrieval is exposed through a convenience function operating on the default session.

bindings/fomac/fomac.cpp (2)

29-44: LGTM!

The SessionParameter enum is correctly exposed to Python with all values matching the C++ counterpart. The docstring clearly describes its purpose.


203-213: LGTM!

The FoMaC class is appropriately exposed as Session in Python, providing an intent-revealing name. The bindings correctly expose the constructor, set_session_parameter, get_devices, and the module-level convenience functions for backward compatibility.

test/fomac/test_fomac.cpp (5)

57-64: LGTM!

The device retrieval in getDDSimulatorDevice() correctly uses the new namespace-scoped fomac::getDevices() function.


601-613: LGTM!

The SessionParameterToString test comprehensively verifies all SessionParameter enum values have correct string representations.


615-636: LGTM!

URL validation tests properly cover both valid URLs (http/https with various formats) and invalid URLs (non-URLs, unsupported schemes, missing protocol).


704-712: LGTM!

This test correctly verifies that session parameters cannot be set after initialization (when getDevices() is called), aligning with the QDMI API behavior documented in the retrieved learnings.


714-754: LGTM!

The test suite instantiation correctly uses the namespace-scoped fomac::getDevices() for all three parameterized test suites.

python/mqt/core/fomac.pyi (4)

12-20: LGTM!

The __all__ list correctly exports the new Session, SessionParameter, and set_session_parameter symbols alongside existing exports.


40-67: LGTM!

The SessionParameter enum is well-documented with clear docstrings for each parameter. The class-level docstring correctly notes that parameters must be set before the first call to devices().


69-105: LGTM!

The Session class stub is comprehensive with:

  • Clear class-level documentation about isolated sessions
  • Well-documented set_session_parameter method including error conditions and usage example
  • Properly typed get_devices return value

278-313: LGTM!

The module-level set_session_parameter function and updated devices() docstrings provide clear documentation, including:

  • Error conditions (RuntimeError cases)
  • Usage examples for both module-level and Session-based approaches
  • Note about using Session class for isolated sessions
include/mqt-core/fomac/FoMaC.hpp (3)

165-197: LGTM!

The SessionParameter enum is well-designed:

  • Correctly maps to underlying QDMI_SESSION_PARAMETER_* values
  • Comprehensive documentation for each parameter
  • Covers all authentication and custom parameter needs

710-717: LGTM!

The private members for session management are appropriate:

  • mutable std::mutex mutex_ for thread-safe initialization
  • bool initialized_ to track session state
  • std::unordered_map<SessionParameter, std::string> pendingParameters_ for deferred parameter application
  • ensureInitialized() for lazy initialization pattern

756-773: LGTM!

The setSessionParameter method declaration has comprehensive documentation covering:

  • Pre-initialization requirement
  • Validation behavior for AUTHFILE and AUTHURL
  • Exception conditions
  • Reference to underlying QDMI function
src/fomac/FoMaC.cpp (2)

80-106: Stringification and error mapping look consistent

The new toString(SessionParameter) and the updated toString overloads for QDMI properties plus throwError(int, const std::string&) form a coherent, centralized mapping between QDMI status/parameters and human-readable messages; behavior is clear and consistent with the existing pattern of using unreachable() for exhaustiveness. I don't see functional issues here.

Also applies to: 146-247


889-916: Default-session helpers and getDevices() initialization hook look good

Calling ensureInitialized() at the start of FoMaC::getDevices() and routing the module-level getDevices()/setSessionParameter() through a lazily-constructed getDefaultSession() gives a clear, backward-compatible story around a “default session” while still enabling explicit session control. This aligns well with the PR’s goal of minimizing breaking changes while adding authentication support.

Tip

✨ Issue Enrichment is now available for GitHub issues!

CodeRabbit can now help you manage issues more effectively:

  • Duplicate Detection — Identify similar or duplicate issues
  • Related Issues & PRs — Find relevant issues and PR's from your repository
  • Suggested Assignees — Find the best person to work on the issue
  • Implementation Planning — Generate detailed coding plans for engineers and agents
Disable automatic issue enrichment

To disable automatic issue enrichment, add the following to your .coderabbit.yaml:

issue_enrichment:
  auto_enrich:
    enabled: false

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
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 is a very quick review with the goal to dump some thoughts on the general direction here.
I'll try to summarize them again here:

  • Breaking changes are entirely fine here
  • I would call the entity Session also in C++. Makes the intent clearer
  • I would use keyword arguments for the constructor of the session (all optional; all defaulted to std::nullopt). That might safe exposing the actual enum.
  • We can drop the default/global get_devices in my opinion. In a not too distant future, we may need to provide credentials for some of the devices that we include.

I think that's it from the very direct feedback.
There is one aspect, that we won't be solving as part of this PR.
And that is Munich-Quantum-Software-Stack/QDMI#161
Right now, only one set of authentication credentials can be set.
However, each device might potentially require different authentication criteria. The current way we generally implement drivers and, potentially, the way the client interface is structured don't allow us to set individual parameters per device.
This is something to think about in the not-too-distant future, but not as part of this PR.

Copy link
Collaborator

@ystade ystade left a comment

Choose a reason for hiding this comment

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

Hi @marcelwa , this is a thoughtful and forward-looking refactoring of the FoMaC. To summarize my comments:

  • I am totally fine with dropping the name FoMaC and call it Session (in a qdmi namespace)
  • I would prefer to align the session handling closer to the original QDMI workflow, i.e., having individual memeber functions to set the indidivual parameters and then calling a function initialize. Only afterwards, one can for example retrieve the list of devices.

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

Caution

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

⚠️ Outside diff range comments (3)
test/fomac/test_fomac.cpp (1)

14-27: Remove unused includes.

Static analysis indicates that <filesystem>, <fstream>, and <thread> headers are not directly used in this file. These were likely added for authentication tests that were subsequently removed (as noted in the comment on lines 618-621).

 #include <cstdio>
-#include <filesystem>
-#include <fstream>
 #include <gmock/gmock-matchers.h>
 #include <gtest/gtest.h>
 #include <new>
 #include <numbers>
 #include <qdmi/client.h>
 #include <ranges>
 #include <stdexcept>
 #include <string>
-#include <thread>
 #include <tuple>
 #include <vector>
bindings/fomac/fomac.cpp (1)

13-21: Add missing <optional> include.

Static analysis indicates that std::optional is used (line 34) but the header is not directly included.

 #include <pybind11/cast.h>
 #include <pybind11/complex.h> // NOLINT(misc-include-cleaner)
 #include <pybind11/native_enum.h>
 #include <pybind11/operators.h>
 #include <pybind11/pybind11.h>
 #include <pybind11/stl.h> // NOLINT(misc-include-cleaner)
 #include <qdmi/client.h>
+#include <optional>
 #include <string>
 #include <vector>
src/fomac/FoMaC.cpp (1)

17-28: Remove unused <mutex> include.

Static analysis indicates this header is not used directly in this file.

 #include <filesystem>
 #include <iterator>
 #include <map>
-#include <mutex>
 #include <optional>
 #include <qdmi/client.h>
 #include <regex>
♻️ Duplicate comments (2)
src/fomac/FoMaC.cpp (2)

812-884: Check QDMI_session_alloc return value.

The constructor doesn't verify whether QDMI_session_alloc succeeded. If allocation fails, session_ could be in an invalid state, leading to undefined behavior in subsequent QDMI_session_set_parameter or QDMI_session_init calls.

 Session::Session(const SessionConfig& config) {
-  QDMI_session_alloc(&session_);
+  throwIfError(QDMI_session_alloc(&session_), "Allocating QDMI session");
 
   // Set and validate parameters from config

839-845: URL validation regex is permissive.

The regex allows malformed URLs like http:// (missing domain) or http://.com. Consider a stricter pattern that validates basic URL structure.

A more robust pattern would ensure at least one valid domain character after the scheme:

     // Validate URL format
     static const std::regex URL_PATTERN(
-        R"(^https?://[a-zA-Z0-9\-._~:/?#\[\]@!$&'()*+,;=%]+$)");
+        R"(^https?://[a-zA-Z0-9][a-zA-Z0-9\-._~:/?#\[\]@!$&'()*+,;=%]*$)");
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e30566c and 6a4dc71.

📒 Files selected for processing (14)
  • bindings/fomac/fomac.cpp (3 hunks)
  • bindings/na/fomac/fomac.cpp (2 hunks)
  • include/mqt-core/fomac/FoMaC.hpp (5 hunks)
  • include/mqt-core/na/fomac/Device.hpp (4 hunks)
  • python/mqt/core/fomac.pyi (2 hunks)
  • python/mqt/core/plugins/qiskit/provider.py (1 hunks)
  • src/fomac/FoMaC.cpp (21 hunks)
  • src/na/fomac/Device.cpp (10 hunks)
  • test/fomac/test_fomac.cpp (7 hunks)
  • test/na/fomac/test_fomac.cpp (2 hunks)
  • test/python/fomac/test_fomac.py (4 hunks)
  • test/python/plugins/qiskit/conftest.py (1 hunks)
  • test/python/plugins/qiskit/test_backend.py (2 hunks)
  • test/python/plugins/qiskit/test_provider.py (1 hunks)
🧰 Additional context used
🧠 Learnings (7)
📓 Common learnings
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.
📚 Learning: 2025-11-04T14:28:32.371Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.

Applied to files:

  • test/python/plugins/qiskit/conftest.py
  • python/mqt/core/plugins/qiskit/provider.py
  • test/python/plugins/qiskit/test_backend.py
  • test/na/fomac/test_fomac.cpp
  • bindings/na/fomac/fomac.cpp
  • test/fomac/test_fomac.cpp
  • test/python/fomac/test_fomac.py
  • include/mqt-core/na/fomac/Device.hpp
  • src/na/fomac/Device.cpp
📚 Learning: 2025-11-05T07:42:45.507Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.

Applied to files:

  • python/mqt/core/plugins/qiskit/provider.py
  • test/python/plugins/qiskit/test_backend.py
  • test/na/fomac/test_fomac.cpp
  • bindings/na/fomac/fomac.cpp
  • test/fomac/test_fomac.cpp
  • include/mqt-core/fomac/FoMaC.hpp
  • include/mqt-core/na/fomac/Device.hpp
  • src/na/fomac/Device.cpp
  • src/fomac/FoMaC.cpp
📚 Learning: 2025-10-17T11:09:50.147Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_site_handling.py:162-173
Timestamp: 2025-10-17T11:09:50.147Z
Learning: For QDMI tests that reference a specific named device via fixture (e.g., "MQT NA Default QDMI Device"), it is acceptable to hard-code device-specific values such as zone counts and indices, as these tests are intentionally coupled to that device's structure and serve as structural invariant checks.

Applied to files:

  • test/na/fomac/test_fomac.cpp
  • test/fomac/test_fomac.cpp
📚 Learning: 2025-11-26T10:29:36.435Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/fomac/test_fomac.py:54-66
Timestamp: 2025-11-26T10:29:36.435Z
Learning: In test/python/fomac/test_fomac.py, the `ddsim_device` fixture returns `Device | None` even though it only returns a `Device` or calls `pytest.skip()`. The `| None` annotation is required for the type checker to pass, as type checkers may not recognize `pytest.skip()` as a function that never returns.

Applied to files:

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

Applied to files:

  • include/mqt-core/fomac/FoMaC.hpp
📚 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:

  • src/na/fomac/Device.cpp
🧬 Code graph analysis (9)
python/mqt/core/plugins/qiskit/provider.py (2)
include/mqt-core/fomac/FoMaC.hpp (4)
  • Session (725-725)
  • Session (730-730)
  • Session (733-733)
  • Session (737-737)
python/mqt/core/fomac.pyi (2)
  • Session (37-95)
  • get_devices (90-95)
python/mqt/core/fomac.pyi (2)
include/mqt-core/fomac/FoMaC.hpp (5)
  • Session (725-725)
  • Session (730-730)
  • Session (733-733)
  • Session (737-737)
  • Device (636-636)
src/fomac/FoMaC.cpp (3)
  • Session (812-884)
  • Session (886-890)
  • Session (892-894)
test/python/plugins/qiskit/test_backend.py (2)
test/python/plugins/qiskit/test_provider.py (1)
  • mock_get_devices (83-84)
python/mqt/core/fomac.pyi (1)
  • Session (37-95)
test/na/fomac/test_fomac.cpp (3)
src/fomac/FoMaC.cpp (2)
  • getDevices (907-917)
  • getDevices (907-907)
src/na/fomac/Device.cpp (2)
  • getDevices (579-588)
  • getDevices (579-579)
test/fomac/test_fomac.cpp (2)
  • getDevices (624-627)
  • getDevices (624-624)
test/python/plugins/qiskit/test_provider.py (3)
test/python/plugins/qiskit/test_backend.py (2)
  • mock_get_devices (269-270)
  • mock_get_devices (301-302)
src/fomac/FoMaC.cpp (3)
  • Session (812-884)
  • Session (886-890)
  • Session (892-894)
python/mqt/core/fomac.pyi (1)
  • Session (37-95)
test/python/fomac/test_fomac.py (1)
python/mqt/core/fomac.pyi (3)
  • Device (153-266)
  • Session (37-95)
  • get_devices (90-95)
include/mqt-core/fomac/FoMaC.hpp (2)
src/fomac/FoMaC.cpp (3)
  • Session (812-884)
  • Session (886-890)
  • Session (892-894)
include/mqt-core/na/fomac/Device.hpp (2)
  • device (123-124)
  • Session (164-164)
bindings/fomac/fomac.cpp (3)
include/mqt-core/na/device/Device.hpp (2)
  • site (177-179)
  • operation (185-189)
src/fomac/FoMaC.cpp (36)
  • getDevices (907-917)
  • getDevices (907-907)
  • getId (542-552)
  • getId (542-542)
  • getProgramFormat (554-560)
  • getProgramFormat (554-554)
  • getProgram (562-573)
  • getProgram (562-562)
  • getNumShots (575-581)
  • getNumShots (575-575)
  • getName (257-259)
  • getName (257-257)
  • getName (290-295)
  • getName (290-292)
  • getName (390-392)
  • getName (390-390)
  • isZone (269-272)
  • isZone (269-269)
  • getSites (343-357)
  • getSites (343-344)
  • getSites (405-415)
  • getSites (405-405)
  • getVersion (393-395)
  • getVersion (393-393)
  • getStatus (396-398)
  • getStatus (396-396)
  • getLibraryVersion (399-401)
  • getLibraryVersion (399-399)
  • getRegularSites (416-422)
  • getRegularSites (416-416)
  • getZones (423-430)
  • getZones (423-423)
  • getOperations (431-442)
  • getOperations (431-431)
  • getLengthUnit (465-468)
  • getLengthUnit (465-465)
include/mqt-core/na/fomac/Device.hpp (1)
  • device (123-124)
src/fomac/FoMaC.cpp (2)
include/mqt-core/na/device/Device.hpp (7)
  • param (151-152)
  • param (219-220)
  • prop (118-119)
  • prop (170-171)
  • prop (226-227)
  • prop (306-307)
  • result (257-258)
include/mqt-core/fomac/FoMaC.hpp (10)
  • queryProperty (354-354)
  • queryProperty (438-441)
  • queryProperty (584-584)
  • queryProperty (704-705)
  • throwIfError (172-182)
  • throwIfError (172-172)
  • Session (725-725)
  • Session (730-730)
  • Session (733-733)
  • Session (737-737)
🪛 GitHub Check: 🇨‌ Lint / 🚨 Lint
test/fomac/test_fomac.cpp

[warning] 17-17: test/fomac/test_fomac.cpp:17:1 [misc-include-cleaner]
included header fstream is not used directly


[warning] 16-16: test/fomac/test_fomac.cpp:16:1 [misc-include-cleaner]
included header filesystem is not used directly


[warning] 26-26: test/fomac/test_fomac.cpp:26:1 [misc-include-cleaner]
included header thread is not used directly


[warning] 624-624: test/fomac/test_fomac.cpp:624:13 [misc-use-internal-linkage]
function 'getDevices' can be made static or moved into an anonymous namespace to enforce internal linkage

bindings/fomac/fomac.cpp

[warning] 34-34: bindings/fomac/fomac.cpp:34:25 [misc-include-cleaner]
no header providing "std::optional" is directly included

src/fomac/FoMaC.cpp

[warning] 20-20: src/fomac/FoMaC.cpp:20:1 [misc-include-cleaner]
included header mutex is not used directly


[warning] 81-81: src/fomac/FoMaC.cpp:81:11 [clang-diagnostic-switch]
enumeration value 'QDMI_SESSION_PARAMETER_MAX' not handled in switch

⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
🔇 Additional comments (34)
include/mqt-core/fomac/FoMaC.hpp (2)

184-203: LGTM!

The SessionConfig struct provides a clean, flexible API for authentication parameters. Using std::optional<std::string> for all fields allows users to specify only the parameters they need. This approach aligns with the consensus from past review discussions regarding the builder pattern for C++ and kwargs translation to Python.


719-741: LGTM!

The Session class API is well-designed:

  • Explicit constructor with default config enables both simple usage (Session()) and authenticated sessions (Session(config))
  • Copy semantics correctly deleted to prevent double-free of QDMI session handle
  • Move semantics enabled for safe ownership transfer
  • Instance-based getDevices() aligns with the session-per-instance model

The documentation accurately reflects that the session is initialized in the constructor.

test/na/fomac/test_fomac.cpp (2)

61-61: Consistent with line 45.

Same pattern as the first test - uses Session::getDevices() which should resolve to na::Session::getDevices().


45-45: The Session::getDevices() call is correct.

In this namespace (na), Session refers to na::Session, which is defined in include/mqt-core/na/fomac/Device.hpp (line 26) and inherits from fomac::Session. The na::Session::getDevices() method is indeed static (line 167), returning std::vector<Device>. The call Session::getDevices().front() correctly invokes this static method and retrieves the first device, which is properly assigned to nlohmann::json.

test/python/plugins/qiskit/conftest.py (1)

447-455: LGTM!

The fixture correctly migrates to the session-based API. Creating a Session instance and calling get_devices() aligns with the new design where sessions manage device discovery. The session object going out of scope after returning devices is appropriate since Device objects are independent of the session lifecycle for query operations.

test/python/plugins/qiskit/test_provider.py (1)

79-90: LGTM!

The monkeypatch correctly targets fomac.Session.get_devices instead of a module-level function. The mock function's _self parameter properly accepts the instance argument that will be passed when the method is called, though it's unused since we just want to return an empty list. This pattern is consistent with similar mocks in test_backend.py.

test/python/plugins/qiskit/test_backend.py (2)

268-272: LGTM! Correct approach for patching the Session instance method.

The mock function signature with _self: object parameter correctly matches the bound method pattern for Session.get_devices(). This is consistent with the pattern used in test_provider.py.


300-304: Consistent monkeypatching pattern applied.

The second occurrence follows the same correct pattern for mocking Session.get_devices. Both test functions are now properly aligned with the new Session-based API.

python/mqt/core/fomac.pyi (2)

12-17: Exports updated correctly for the new API.

The __all__ list properly exports Device, Job, ProgramFormat, and Session. This provides a clean public API surface where users create a Session to access devices.


37-95: Well-documented Session class with comprehensive authentication options.

The Session class API is cleanly designed with:

  • Keyword-only parameters preventing positional argument errors
  • Appropriate RuntimeError documentation for validation failures
  • Helpful usage examples in the docstring

The implementation aligns with the C++ SessionConfig structure from src/fomac/FoMaC.cpp which validates auth_file existence and auth_url format.

test/python/fomac/test_fomac.py (2)

20-27: Helper function correctly implements the Session-based device retrieval.

The _get_devices() function properly creates a Session instance and returns devices. Note that this function is called during pytest collection (via params=_get_devices()), creating a new Session for each parametrized fixture. This is functionally correct given the test structure.


30-30: Fixture parameterization correctly migrated to Session-based API.

All four fixtures (device, device_and_site, device_and_operation, and ddsim_device) now use _get_devices() consistently. Based on learnings, at least one FoMaC device is always available during testing, so this pattern will work reliably.

Also applies to: 40-40, 52-52, 71-71

include/mqt-core/na/fomac/Device.hpp (3)

23-26: Class refactoring from FoMaC to Session is correctly implemented.

The class inheritance chain is properly updated to na::Session : public fomac::Session, maintaining the established pattern.


34-34: Device type references consistently updated throughout.

All references to fomac::FoMaC::Device have been correctly updated to fomac::Session::Device, including:

  • The inheritance declaration
  • Constructor parameter
  • Factory method parameter and documentation

Also applies to: 82-88, 122-123


164-168: The static getDevices() method is correct by design.

The na::Session class intentionally deletes its default constructor and provides a static getDevices() method that internally creates a fomac::Session instance. This design is appropriate for neutral-atom device discovery, which does not require users to manage session instances directly. The method queries QDMI_SESSION_PROPERTY_DEVICES through an internal session, providing a convenient facade for device enumeration without requiring explicit session management.

bindings/na/fomac/fomac.cpp (3)

44-45: Device binding correctly updated to Session-based types.

The PyBind11 class binding properly declares na::Session::Device with fomac::Session::Device as its base class, enabling proper inheritance in Python.


94-103: Property and method bindings consistently use Session::Device.

The traps, t1, t2 properties and __repr__ method are all correctly updated to operate on na::Session::Device / fomac::Session::Device types.


107-109: NA module exposes Session-based device access.

The devices function and try_create_from_device static method are correctly bound to the new na::Session namespace. Note that this NA-specific devices() function differs from the base fomac module's Session.get_devices() instance method - this static function is appropriate for the neutral-atom extension that filters/wraps generic devices.

test/fomac/test_fomac.cpp (3)

31-68: LGTM!

The migration of test fixtures from FoMaC::Device, FoMaC::Device::Site, FoMaC::Device::Operation, and FoMaC::Job to their Session:: equivalents is consistent and correctly updates all member types and method return types.


604-616: LGTM!

The new AuthenticationTest comprehensively covers string representations for all QDMI_SESSION_PARAMETER_* constants, which aligns with the new toString overload added in src/fomac/FoMaC.cpp.


629-669: LGTM!

The parameterized test instantiations correctly use the updated Session::Device type for TestParamInfo, maintaining consistency with the refactored API.

src/na/fomac/Device.cpp (5)

41-46: LGTM!

Documentation and signature correctly updated to reference Session::Device::Site types.


113-172: LGTM!

The device initialization methods (initNameFromDevice, initMinAtomDistanceFromDevice, initQubitsNumFromDevice, initLengthUnitFromDevice, initDurationUnitFromDevice, initDecoherenceTimesFromDevice) are correctly updated to the Session::Device namespace with no logic changes.


173-296: LGTM!

The initTrapsfromDevice method correctly migrates to Session::Device namespace. The lattice reconstruction logic remains intact.


297-576: LGTM!

The initOperationsFromDevice method correctly updates all fomac::Session::Device::Operation and fomac::Session::Device::Site type references in loops and lambdas. The shuttling operation parsing and zoned/non-zoned operation handling logic is preserved.


577-588: LGTM!

The constructor correctly delegates to the base fomac::Session::Device, and Session::getDevices() properly creates a local fomac::Session to retrieve and filter devices using Device::tryCreateFromDevice.

bindings/fomac/fomac.cpp (3)

29-55: LGTM!

The Session binding correctly exposes a constructor with optional authentication parameters (token, auth_file, auth_url, username, password, project_id) that map to SessionConfig fields. The use of defaultConfig for default argument values ensures backward compatibility for users who don't need authentication.


57-77: LGTM!

The Job bindings are correctly migrated to Session::Job with all methods and properties properly exposed.


111-214: LGTM!

The Device, Site, and Operation bindings are comprehensively updated to use Session::Device, Session::Device::Site, and Session::Device::Operation types respectively. All properties and methods are properly exposed with consistent naming conventions.

src/fomac/FoMaC.cpp (5)

248-490: LGTM!

All Site, Operation, and Device accessor methods are correctly migrated to the Session::Device namespace. The implementation logic remains unchanged, only the class qualification has been updated.


492-518: LGTM!

The submitJob method correctly creates a Job with RAII semantics and properly sets all job parameters before submission.


520-810: LGTM!

All Job accessor methods (check, wait, cancel, getId, getProgramFormat, getProgram, getNumShots, getShots, getCounts, getDenseStateVector, getDenseProbabilities, getSparseStateVector, getSparseProbabilities) are correctly migrated to Session::Job namespace with robust error handling and size validation.


886-905: LGTM!

The destructor correctly handles null session pointers, and the move constructor/assignment operator properly transfer ownership by nullifying the source. Since there's no mutex member in the visible Session class definition, the current implementation is appropriate.


907-917: LGTM!

The getDevices method correctly queries the session property and transforms QDMI devices into Session::Device wrappers using the Token pattern for controlled construction.

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.

Alright, I looked a little bit more deeply into this the last couple of hours and pushed a bunch of commits that resulted from that investigation.
I tried to keep them as self-contained as possible and added explicit changelog items wherever I saw fit.
I am happy with this for now and hope CI will be happy as well.
Tagging this for auto-merge 🚀

@burgholzer burgholzer added the minor Minor version update label Dec 7, 2025
@burgholzer burgholzer enabled auto-merge (squash) December 7, 2025 02:35
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 80e88dc and 086f7b2.

📒 Files selected for processing (3)
  • src/qdmi/dd/Device.cpp (10 hunks)
  • src/qdmi/na/Device.cpp (10 hunks)
  • src/qdmi/sc/Device.cpp (9 hunks)
🧰 Additional context used
🧠 Learnings (6)
📓 Common learnings
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.
📚 Learning: 2025-11-05T07:42:45.507Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.

Applied to files:

  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • src/qdmi/dd/Device.cpp
📚 Learning: 2025-10-17T11:09:50.147Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_site_handling.py:162-173
Timestamp: 2025-10-17T11:09:50.147Z
Learning: For QDMI tests that reference a specific named device via fixture (e.g., "MQT NA Default QDMI Device"), it is acceptable to hard-code device-specific values such as zone counts and indices, as these tests are intentionally coupled to that device's structure and serve as structural invariant checks.

Applied to files:

  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • src/qdmi/dd/Device.cpp
📚 Learning: 2025-11-04T19:50:14.489Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/helpers/test_utils.cpp:103-118
Timestamp: 2025-11-04T19:50:14.489Z
Learning: In the munich-quantum-toolkit/core QDMI device tests, when QDMI APIs return a string result size `ks` that includes a null terminator, the pattern `std::string keys(ks - 1, '\0'); ... device_job_get_results(..., ks, keys.data(), nullptr)` is correct and safe. The string allocates `ks` bytes total (ks-1 content + implicit null), and the API writing `ks` bytes (including trailing null) fits exactly into this space without buffer overrun.

Applied to files:

  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
📚 Learning: 2025-11-04T14:28:32.371Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.

Applied to files:

  • src/qdmi/na/Device.cpp
  • src/qdmi/dd/Device.cpp
📚 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:

  • src/qdmi/na/Device.cpp
🧬 Code graph analysis (2)
src/qdmi/sc/Device.cpp (2)
src/qdmi/dd/Device.cpp (4)
  • finalize (239-249)
  • finalize (239-239)
  • get (251-256)
  • get (251-251)
src/qdmi/na/Device.cpp (4)
  • finalize (134-144)
  • finalize (134-134)
  • get (146-151)
  • get (146-146)
src/qdmi/dd/Device.cpp (2)
include/mqt-core/qdmi/dd/Device.hpp (7)
  • prop (120-121)
  • prop (186-187)
  • prop (297-298)
  • Device (64-64)
  • Device (73-73)
  • Device (76-76)
  • Device (80-80)
src/qdmi/na/Device.cpp (7)
  • Device (107-118)
  • initialize (120-132)
  • initialize (120-120)
  • finalize (134-144)
  • finalize (134-134)
  • get (146-151)
  • get (146-146)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
🔇 Additional comments (12)
src/qdmi/dd/Device.cpp (3)

209-214: IS_INVALID_ARGUMENT macro correctly centralizes validation logic.

The macro properly rejects property values >= prefix##_MAX while allowing CUSTOM1 through CUSTOM5 extension points. This addresses the previous review feedback about repetitive validation and aligns with the pattern used in the NA device implementation.


218-256: Reference-counted singleton lifecycle is correctly implemented.

The implementation matches the pattern in src/qdmi/na/Device.cpp and properly handles concurrent initialization with atomic CAS. The finalize() condition prev > 1 correctly ensures the instance is only deleted when the last reference is released.


323-326: LGTM!

Using a dedicated rngMutex_ for the RNG state is appropriate - it separates concerns from sessionsMutex_ which guards the sessions map, avoiding unnecessary contention.

src/qdmi/sc/Device.cpp (7)

105-151: Reference-counted singleton lifecycle is consistent with other device implementations.

The initialize(), finalize(), and get() implementations match the pattern in src/qdmi/dd/Device.cpp and src/qdmi/na/Device.cpp, ensuring uniform behavior across all QDMI device types.


170-175: Validation logic properly extended to support custom properties.

The IS_INVALID_ARGUMENT macro correctly rejects properties >= QDMI_DEVICE_PROPERTY_MAX while explicitly allowing CUSTOM1 through CUSTOM5, enabling device-specific property extensions.


221-232: LGTM!

Session parameter validation correctly uses the macro and maintains proper state checking (must be in ALLOCATED state to set parameters, per the learning about QDMI session parameter timing).


288-308: Job parameter and property validation is consistent.

Both setParameter and queryProperty methods use the same validation pattern with their respective property type prefixes.


329-339: LGTM!

The getResults validation properly uses QDMI_JOB_RESULT prefix for the macro.


345-355: Site property validation is correct.

The validation pattern is consistent with other property query methods.


408-418: Operation property validation is correct.

The validation includes the expected checks for sites, params, value, and the property enum validity.

src/qdmi/na/Device.cpp (2)

171-176: LGTM: Consistent validation across property/parameter queries.

The refactor to use the IS_INVALID_ARGUMENT macro centralizes validation logic across all query and parameter-setting methods. The consistent pattern of checking both (value != nullptr && size == 0) and IS_INVALID_ARGUMENT(...) improves maintainability and reduces code duplication.

Also applies to: 231-237, 299-307, 309-318, 340-349, 382-388, 547-559


96-100: Verify that CUSTOM symbol values respect the IS_INVALID_ARGUMENT logic across all prefixes.

The IS_INVALID_ARGUMENT macro assumes all CUSTOM1CUSTOM5 values are >= PREFIX_MAX for each of the seven prefixes it validates. If any CUSTOM value falls below its prefix's _MAX, valid custom properties would be incorrectly rejected.

Since the enum definitions are externally generated from the qdmi::qdmi package and not present in this repository, the assumptions cannot be verified locally. Confirm that the external QDMI enum definitions follow this constraint, or add a static assertion or compile-time check to enforce it.

@burgholzer burgholzer disabled auto-merge December 7, 2025 07:46
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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 086f7b2 and 7af7e00.

📒 Files selected for processing (7)
  • .clang-tidy (1 hunks)
  • include/mqt-core/qdmi/dd/Device.hpp (4 hunks)
  • include/mqt-core/qdmi/na/Device.hpp (3 hunks)
  • include/mqt-core/qdmi/sc/Device.hpp (3 hunks)
  • src/qdmi/dd/Device.cpp (10 hunks)
  • src/qdmi/na/Device.cpp (10 hunks)
  • src/qdmi/sc/Device.cpp (10 hunks)
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: src/qdmi/sc/Device.cpp:97-102
Timestamp: 2025-12-07T09:10:31.820Z
Learning: In the munich-quantum-toolkit/core repository, duplication of QDMI-related macros (such as IS_INVALID_ARGUMENT) across device implementations (e.g., in src/qdmi/sc/Device.cpp and src/qdmi/dd/Device.cpp) is acceptable as a temporary measure. The preferred long-term solution is to upstream these macros to the QDMI repository rather than creating local shared headers, so they can be reused across all dependent projects.
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.
📚 Learning: 2025-12-07T09:10:31.820Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: src/qdmi/sc/Device.cpp:97-102
Timestamp: 2025-12-07T09:10:31.820Z
Learning: In the munich-quantum-toolkit/core repository, duplication of QDMI-related macros (such as IS_INVALID_ARGUMENT) across device implementations (e.g., in src/qdmi/sc/Device.cpp and src/qdmi/dd/Device.cpp) is acceptable as a temporary measure. The preferred long-term solution is to upstream these macros to the QDMI repository rather than creating local shared headers, so they can be reused across all dependent projects.

Applied to files:

  • src/qdmi/sc/Device.cpp
  • include/mqt-core/qdmi/dd/Device.hpp
  • src/qdmi/na/Device.cpp
  • include/mqt-core/qdmi/sc/Device.hpp
  • include/mqt-core/qdmi/na/Device.hpp
  • src/qdmi/dd/Device.cpp
📚 Learning: 2025-11-05T07:42:45.507Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.

Applied to files:

  • src/qdmi/sc/Device.cpp
  • include/mqt-core/qdmi/dd/Device.hpp
  • src/qdmi/na/Device.cpp
  • include/mqt-core/qdmi/na/Device.hpp
  • src/qdmi/dd/Device.cpp
📚 Learning: 2025-10-17T11:09:50.147Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_site_handling.py:162-173
Timestamp: 2025-10-17T11:09:50.147Z
Learning: For QDMI tests that reference a specific named device via fixture (e.g., "MQT NA Default QDMI Device"), it is acceptable to hard-code device-specific values such as zone counts and indices, as these tests are intentionally coupled to that device's structure and serve as structural invariant checks.

Applied to files:

  • src/qdmi/sc/Device.cpp
  • include/mqt-core/qdmi/dd/Device.hpp
  • src/qdmi/na/Device.cpp
  • include/mqt-core/qdmi/sc/Device.hpp
  • include/mqt-core/qdmi/na/Device.hpp
  • src/qdmi/dd/Device.cpp
📚 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:

  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
📚 Learning: 2025-11-04T14:28:32.371Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.

Applied to files:

  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • include/mqt-core/qdmi/sc/Device.hpp
  • include/mqt-core/qdmi/na/Device.hpp
📚 Learning: 2025-11-04T19:50:14.489Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/helpers/test_utils.cpp:103-118
Timestamp: 2025-11-04T19:50:14.489Z
Learning: In the munich-quantum-toolkit/core QDMI device tests, when QDMI APIs return a string result size `ks` that includes a null terminator, the pattern `std::string keys(ks - 1, '\0'); ... device_job_get_results(..., ks, keys.data(), nullptr)` is correct and safe. The string allocates `ks` bytes total (ks-1 content + implicit null), and the API writing `ks` bytes (including trailing null) fits exactly into this space without buffer overrun.

Applied to files:

  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • include/mqt-core/qdmi/sc/Device.hpp
  • include/mqt-core/qdmi/na/Device.hpp
📚 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:

  • include/mqt-core/qdmi/na/Device.hpp
🧬 Code graph analysis (2)
src/qdmi/sc/Device.cpp (1)
src/qdmi/na/Device.cpp (4)
  • finalize (125-136)
  • finalize (125-125)
  • get (137-142)
  • get (137-137)
src/qdmi/dd/Device.cpp (4)
include/mqt-core/qdmi/dd/Device.hpp (7)
  • prop (123-124)
  • prop (189-190)
  • prop (300-301)
  • Device (64-64)
  • Device (76-76)
  • Device (79-79)
  • Device (83-83)
include/mqt-core/qdmi/sc/Device.hpp (8)
  • prop (111-112)
  • prop (163-164)
  • prop (224-225)
  • prop (283-284)
  • Device (52-52)
  • Device (64-64)
  • Device (67-67)
  • Device (71-71)
include/mqt-core/qdmi/Driver.hpp (3)
  • prop (243-244)
  • prop (306-307)
  • prop (386-387)
src/qdmi/na/Device.cpp (5)
  • Device (104-115)
  • initialize (116-124)
  • initialize (116-116)
  • finalize (125-136)
  • finalize (125-125)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
  • GitHub Check: 🐍 Test (ubuntu-24.04-arm) / 🐍 ubuntu-24.04-arm
🔇 Additional comments (11)
include/mqt-core/qdmi/dd/Device.hpp (2)

67-72: Reference-counted singleton with intentionally leaked mutex looks correct.

The pattern using inline static members with a dynamically allocated mutex that's intentionally never deleted is a well-known approach to avoid the static deinitialization order fiasco. The NOLINT annotation properly documents this design decision.


87-101: Documentation clearly describes the initialize/finalize contract.

The updated documentation properly explains the reference-counted lifecycle and the requirement for paired initialize()/finalize() calls.

include/mqt-core/qdmi/sc/Device.hpp (1)

55-60: Consistent singleton pattern with dd Device implementation.

The reference-counted singleton with the intentionally leaked mutex follows the same pattern as the dd Device, ensuring consistency across QDMI device implementations.

src/qdmi/na/Device.cpp (2)

96-101: IS_INVALID_ARGUMENT macro correctly validates property/parameter bounds.

The macro properly checks that values are within valid ranges, accounting for the _MAX sentinel and _CUSTOM1 through _CUSTOM5 extension points. Based on learnings, the duplication across device implementations is acceptable as a temporary measure until these macros are upstreamed to the QDMI repository.


116-142: Mutex-guarded singleton lifecycle is correctly implemented.

The initialize() and finalize() methods properly use std::scoped_lock to guard access to refCount_ and instance_. The comment explaining why the mutex is intentionally leaked to avoid static deinitialization issues is helpful.

include/mqt-core/qdmi/na/Device.hpp (1)

73-78: Consistent singleton pattern across all QDMI device implementations.

The na Device follows the same reference-counted singleton pattern with an intentionally leaked mutex as the dd and sc devices, ensuring consistency across the codebase.

src/qdmi/dd/Device.cpp (4)

209-213: IS_INVALID_ARGUMENT macro correctly centralizes argument validation.

The macro properly validates that enum values are within the valid range, checking against _MAX and allowing _CUSTOM1 through _CUSTOM5 extension points. This centralized approach reduces code duplication and ensures consistent validation across all query and set parameter paths.


220-246: Mutex-guarded singleton lifecycle implementation is correct.

The implementation properly guards both initialize() and finalize() with std::scoped_lock, ensuring thread-safe reference counting. The get() method correctly asserts that instance_ is initialized, which aligns with the documented usage contract requiring initialize() to be called before get().


312-314: RNG access correctly guarded by dedicated mutex.

The generateUniqueID() method now uses rngMutex_ instead of a more general mutex, which is appropriate for protecting the RNG state. This aligns with the header changes where rngMutex_ is declared separately from sessionsMutex_.


446-455: Program format validation is thorough.

The code correctly validates the program format using IS_INVALID_ARGUMENT first, then checks for specifically supported formats (QASM2 and QASM3). This two-step validation ensures both that the input is a valid enum value and that it's a format this device actually supports.

src/qdmi/sc/Device.cpp (1)

120-146: Reference-counted singleton pattern is consistent with na/Device.cpp.

The mutex-guarded initialization/finalization with reference counting and the unlocked get() accessor match the established pattern in na/Device.cpp exactly. The intentional mutex leak to avoid static deinitialization order issues is correctly documented.

The design assumes callers maintain proper reference counting discipline: initialize() must precede get(), and get() must not be called after the final finalize(). The assert provides debug-time verification of this contract.

Signed-off-by: burgholzer <[email protected]>
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7af7e00 and 090ee48.

📒 Files selected for processing (6)
  • include/mqt-core/qdmi/dd/Device.hpp (2 hunks)
  • include/mqt-core/qdmi/na/Device.hpp (1 hunks)
  • include/mqt-core/qdmi/sc/Device.hpp (1 hunks)
  • src/qdmi/dd/Device.cpp (11 hunks)
  • src/qdmi/na/Device.cpp (11 hunks)
  • src/qdmi/sc/Device.cpp (10 hunks)
🧰 Additional context used
🧠 Learnings (8)
📓 Common learnings
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: src/qdmi/sc/Device.cpp:97-102
Timestamp: 2025-12-07T09:10:31.820Z
Learning: In the munich-quantum-toolkit/core repository, duplication of QDMI-related macros (such as IS_INVALID_ARGUMENT) across device implementations (e.g., in src/qdmi/sc/Device.cpp and src/qdmi/dd/Device.cpp) is acceptable as a temporary measure. The preferred long-term solution is to upstream these macros to the QDMI repository rather than creating local shared headers, so they can be reused across all dependent projects.
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.
📚 Learning: 2025-12-07T09:10:31.820Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: src/qdmi/sc/Device.cpp:97-102
Timestamp: 2025-12-07T09:10:31.820Z
Learning: In the munich-quantum-toolkit/core repository, duplication of QDMI-related macros (such as IS_INVALID_ARGUMENT) across device implementations (e.g., in src/qdmi/sc/Device.cpp and src/qdmi/dd/Device.cpp) is acceptable as a temporary measure. The preferred long-term solution is to upstream these macros to the QDMI repository rather than creating local shared headers, so they can be reused across all dependent projects.

Applied to files:

  • include/mqt-core/qdmi/na/Device.hpp
  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • src/qdmi/dd/Device.cpp
  • include/mqt-core/qdmi/dd/Device.hpp
  • include/mqt-core/qdmi/sc/Device.hpp
📚 Learning: 2025-10-17T11:09:50.147Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_site_handling.py:162-173
Timestamp: 2025-10-17T11:09:50.147Z
Learning: For QDMI tests that reference a specific named device via fixture (e.g., "MQT NA Default QDMI Device"), it is acceptable to hard-code device-specific values such as zone counts and indices, as these tests are intentionally coupled to that device's structure and serve as structural invariant checks.

Applied to files:

  • include/mqt-core/qdmi/na/Device.hpp
  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • src/qdmi/dd/Device.cpp
  • include/mqt-core/qdmi/dd/Device.hpp
  • include/mqt-core/qdmi/sc/Device.hpp
📚 Learning: 2025-11-05T07:42:45.507Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.

Applied to files:

  • include/mqt-core/qdmi/na/Device.hpp
  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • src/qdmi/dd/Device.cpp
  • include/mqt-core/qdmi/dd/Device.hpp
📚 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:

  • include/mqt-core/qdmi/na/Device.hpp
📚 Learning: 2025-11-04T14:28:32.371Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.

Applied to files:

  • include/mqt-core/qdmi/na/Device.hpp
  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • include/mqt-core/qdmi/sc/Device.hpp
📚 Learning: 2025-11-04T19:50:14.489Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/helpers/test_utils.cpp:103-118
Timestamp: 2025-11-04T19:50:14.489Z
Learning: In the munich-quantum-toolkit/core QDMI device tests, when QDMI APIs return a string result size `ks` that includes a null terminator, the pattern `std::string keys(ks - 1, '\0'); ... device_job_get_results(..., ks, keys.data(), nullptr)` is correct and safe. The string allocates `ks` bytes total (ks-1 content + implicit null), and the API writing `ks` bytes (including trailing null) fits exactly into this space without buffer overrun.

Applied to files:

  • include/mqt-core/qdmi/na/Device.hpp
  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • include/mqt-core/qdmi/sc/Device.hpp
📚 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:

  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
🧬 Code graph analysis (2)
src/qdmi/sc/Device.cpp (3)
include/mqt-core/qdmi/sc/Device.hpp (4)
  • prop (84-85)
  • prop (136-137)
  • prop (197-198)
  • prop (256-257)
src/qdmi/na/Device.cpp (2)
  • get (115-121)
  • get (115-115)
src/qdmi/dd/Device.cpp (2)
  • get (220-226)
  • get (220-220)
src/qdmi/na/Device.cpp (4)
include/mqt-core/qdmi/na/Device.hpp (4)
  • prop (102-103)
  • prop (154-155)
  • prop (210-211)
  • prop (290-291)
include/mqt-core/qdmi/Driver.hpp (1)
  • instance (424-427)
src/qdmi/sc/Device.cpp (2)
  • get (119-125)
  • get (119-119)
src/qdmi/dd/Device.cpp (2)
  • get (220-226)
  • get (220-220)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
🔇 Additional comments (8)
include/mqt-core/qdmi/dd/Device.hpp (1)

49-49: Mutex member initialization change is behavior-preserving.

Dropping the {} brace-init on sessionsMutex_, rngMutex_, and jobsMutex_ keeps the same default construction semantics for std::mutex; no functional impact and consistent with usage in the .cpp.

Also applies to: 54-54, 130-130

src/qdmi/sc/Device.cpp (2)

96-100: Centralized argument validation via IS_INVALID_ARGUMENT looks correct.

The new IS_INVALID_ARGUMENT macro plus the (value != nullptr && size == 0) guards give consistent validation across device/session/job/site/operation properties and cleanly admit the CUSTOM1..CUSTOM5 ranges while rejecting out-of-range enums. This matches the patterns used in the other QDMI devices and improves robustness without changing supported behavior.

Also applies to: 146-147, 198-201, 267-269, 277-280, 308-311, 322-325, 387-392


119-125: Leaked function-local singleton and no-op finalize avoid lifecycle races.

Switching Device::get() to a leaked function-local static pointer and making MQT_SC_QDMI_device_finalize() a no-op removes the previous explicit destruction path and any related use-after-free risk. MQT_SC_QDMI_device_initialize() now simply ensures the singleton exists, which is consistent with the NA and DDSIM devices and acceptable for a process-lifetime backend.

Also applies to: 475-482

src/qdmi/na/Device.cpp (2)

51-56: String/list helper macros and IS_INVALID_ARGUMENT usage are consistent and safe.

ADD_STRING_PROPERTY’s size check plus the adjusted STRNCPY keep string writes bounded and explicitly null-terminated, and ADD_LIST_PROPERTY works correctly for both vectors and the std::span-based flattening. The new IS_INVALID_ARGUMENT checks (together with (value != nullptr && size == 0)) provide uniform, stricter validation across device/session/job/site/operation enums while still allowing CUSTOM1..CUSTOM5 to flow through as “potentially supported” properties.

Also applies to: 58-75, 96-99, 140-145, 200-206, 269-273, 281-285, 313-316, 351-357, 521-528


115-120: NA device singleton and C API lifecycle now match the other backends.

Device::get() now uses a leaked function-local static pointer, and the NA QDMI device_initialize/device_finalize C functions simply ensure initialization and then no-op on teardown. This removes manual destruction from the hot path, avoids lifetime races, and keeps behavior in line with the SC and DDSIM devices.

Also applies to: 642-649

src/qdmi/dd/Device.cpp (3)

210-213: Unified argument validation via IS_INVALID_ARGUMENT is correct and improves robustness.

Using IS_INVALID_ARGUMENT together with the (value|data != nullptr && size == 0) checks gives consistent, stricter input validation for device properties, session parameters, site/operation properties, job parameters, and job results. The macro’s whitelist for CUSTOM1..CUSTOM5 keeps those legal while rejecting out-of-range enums, and unsupported-but-valid enums now sensibly fall through to QDMI_ERROR_NOTSUPPORTED.

Also applies to: 248-252, 320-325, 367-368, 387-388, 417-420, 457-458, 723-726


292-295: RNG access is now properly synchronized.

Wrapping dis_(rng_) in a std::scoped_lock on rngMutex_ eliminates the data race on std::mt19937_64 when IDs are generated concurrently, without impacting observable behavior.


221-225: DDSIM device singleton and C API lifecycle refactor look sound.

Device::get() now uses a leaked function-local static pointer, and MQT_DDSIM_QDMI_device_initialize() simply forces construction while *_device_finalize() is a no-op. This avoids any use-after-free hazards around late jobs referencing the device while still keeping the lifetime bounded to the process, and it aligns the DDSIM backend with the NA/SC implementations.

Also applies to: 762-769

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 4

Caution

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

⚠️ Outside diff range comments (1)
src/qdmi/na/Device.cpp (1)

54-71: Add mutex protection for session management to prevent data races.

The sessions_ map in sessionAlloc and sessionFree is unprotected while the equivalent dd::Device implementation uses sessionsMutex_ to guard these operations. Since both are singleton instances exposed via C API and subject to concurrent access, the lack of synchronization in the NA implementation creates a potential data race. Add a mutable std::mutex sessionsMutex_ member to the Device class and protect the map operations with std::scoped_lock as done in dd::Device.

♻️ Duplicate comments (1)
include/mqt-core/fomac/FoMaC.hpp (1)

23-23: Unused <mutex> include still present.

A past review flagged that <mutex> is not used in this file or its implementation. This include should be removed to reduce compilation overhead.

-#include <mutex>
📜 Review details

Configuration used: CodeRabbit UI

Review profile: ASSERTIVE

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 090ee48 and a3023d5.

📒 Files selected for processing (19)
  • CHANGELOG.md (4 hunks)
  • include/mqt-core/fomac/FoMaC.hpp (13 hunks)
  • include/mqt-core/qdmi/Common.hpp (1 hunks)
  • include/mqt-core/qdmi/na/Device.hpp (0 hunks)
  • include/mqt-core/qdmi/sc/Device.hpp (0 hunks)
  • src/fomac/CMakeLists.txt (1 hunks)
  • src/fomac/FoMaC.cpp (13 hunks)
  • src/na/fomac/Device.cpp (17 hunks)
  • src/qdmi/CMakeLists.txt (2 hunks)
  • src/qdmi/Common.cpp (1 hunks)
  • src/qdmi/Driver.cpp (3 hunks)
  • src/qdmi/dd/CMakeLists.txt (1 hunks)
  • src/qdmi/dd/Device.cpp (12 hunks)
  • src/qdmi/na/CMakeLists.txt (4 hunks)
  • src/qdmi/na/Device.cpp (10 hunks)
  • src/qdmi/sc/CMakeLists.txt (2 hunks)
  • src/qdmi/sc/Device.cpp (10 hunks)
  • test/fomac/test_fomac.cpp (8 hunks)
  • test/qdmi/dd/results_sampling_test.cpp (1 hunks)
💤 Files with no reviewable changes (2)
  • include/mqt-core/qdmi/sc/Device.hpp
  • include/mqt-core/qdmi/na/Device.hpp
🧰 Additional context used
🧠 Learnings (19)
📓 Common learnings
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1355
File: src/qdmi/sc/Device.cpp:97-102
Timestamp: 2025-12-07T09:10:31.820Z
Learning: In the munich-quantum-toolkit/core repository, duplication of QDMI-related macros (such as IS_INVALID_ARGUMENT) across device implementations (e.g., in src/qdmi/sc/Device.cpp and src/qdmi/dd/Device.cpp) is acceptable as a temporary measure. The preferred long-term solution is to upstream these macros to the QDMI repository rather than creating local shared headers, so they can be reused across all dependent projects.
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.
📚 Learning: 2025-10-14T14:37:38.047Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/yaqs PR: 212
File: CHANGELOG.md:12-15
Timestamp: 2025-10-14T14:37:38.047Z
Learning: In the munich-quantum-toolkit/yaqs project, changelog entries follow the template: "- $TITLE ([#$NUMBER]($URL)) ([**AUTHOR**](https://github.com/$AUTHOR))". Issue references should not be included in changelog entries; the PR number is sufficient for traceability.

Applied to files:

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

Applied to files:

  • CHANGELOG.md
  • src/qdmi/dd/CMakeLists.txt
  • test/fomac/test_fomac.cpp
📚 Learning: 2025-11-04T14:28:32.371Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_backend.py:0-0
Timestamp: 2025-11-04T14:28:32.371Z
Learning: In the munich-quantum-toolkit/core repository, at least one FoMaC device is always available during testing, so skip logic for missing devices in QDMI Qiskit backend tests is not necessary.

Applied to files:

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

Applied to files:

  • CHANGELOG.md
  • src/qdmi/na/CMakeLists.txt
  • include/mqt-core/qdmi/Common.hpp
  • src/qdmi/dd/CMakeLists.txt
  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • src/qdmi/Driver.cpp
  • src/qdmi/CMakeLists.txt
  • src/qdmi/sc/CMakeLists.txt
  • src/qdmi/dd/Device.cpp
  • src/qdmi/Common.cpp
  • include/mqt-core/fomac/FoMaC.hpp
  • test/fomac/test_fomac.cpp
  • src/na/fomac/Device.cpp
  • src/fomac/FoMaC.cpp
📚 Learning: 2025-12-01T11:00:40.342Z
Learnt from: flowerthrower
Repo: munich-quantum-toolkit/core-plugins-catalyst PR: 1
File: CHANGELOG.md:18-18
Timestamp: 2025-12-01T11:00:40.342Z
Learning: In the munich-quantum-toolkit/core-plugins-catalyst repository, the CHANGELOG.md intentionally references the parent MQT Core repository's release notes (https://github.com/munich-quantum-toolkit/core/releases) because the plugin repository is based on work performed in the parent repository.

Applied to files:

  • CHANGELOG.md
📚 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:

  • CHANGELOG.md
  • src/fomac/CMakeLists.txt
  • src/qdmi/na/CMakeLists.txt
  • include/mqt-core/qdmi/Common.hpp
  • src/qdmi/dd/CMakeLists.txt
  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • src/qdmi/Driver.cpp
  • src/qdmi/CMakeLists.txt
  • src/qdmi/sc/CMakeLists.txt
  • src/qdmi/dd/Device.cpp
  • src/qdmi/Common.cpp
  • test/fomac/test_fomac.cpp
  • src/na/fomac/Device.cpp
📚 Learning: 2025-11-04T19:50:14.489Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/helpers/test_utils.cpp:103-118
Timestamp: 2025-11-04T19:50:14.489Z
Learning: In the munich-quantum-toolkit/core QDMI device tests, when QDMI APIs return a string result size `ks` that includes a null terminator, the pattern `std::string keys(ks - 1, '\0'); ... device_job_get_results(..., ks, keys.data(), nullptr)` is correct and safe. The string allocates `ks` bytes total (ks-1 content + implicit null), and the API writing `ks` bytes (including trailing null) fits exactly into this space without buffer overrun.

Applied to files:

  • test/qdmi/dd/results_sampling_test.cpp
  • include/mqt-core/qdmi/Common.hpp
  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • src/qdmi/Driver.cpp
  • src/fomac/FoMaC.cpp
📚 Learning: 2025-10-17T11:09:50.147Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/test_qdmi_qiskit_site_handling.py:162-173
Timestamp: 2025-10-17T11:09:50.147Z
Learning: For QDMI tests that reference a specific named device via fixture (e.g., "MQT NA Default QDMI Device"), it is acceptable to hard-code device-specific values such as zone counts and indices, as these tests are intentionally coupled to that device's structure and serve as structural invariant checks.

Applied to files:

  • src/qdmi/na/CMakeLists.txt
  • src/qdmi/sc/Device.cpp
  • src/qdmi/na/Device.cpp
  • src/qdmi/Driver.cpp
  • src/qdmi/dd/Device.cpp
  • test/fomac/test_fomac.cpp
📚 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:

  • src/qdmi/na/CMakeLists.txt
📚 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:

  • src/qdmi/na/CMakeLists.txt
  • src/qdmi/sc/CMakeLists.txt
📚 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:

  • src/qdmi/na/CMakeLists.txt
  • src/qdmi/sc/CMakeLists.txt
📚 Learning: 2025-11-05T07:42:45.507Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1287
File: test/qdmi/dd/error_handling_test.cpp:118-194
Timestamp: 2025-11-05T07:42:45.507Z
Learning: In the munich-quantum-toolkit/core QDMI device API, session parameters can only be set before calling `device_session_init()`. Once a session is initialized, any attempt to set a parameter returns `QDMI_ERROR_BADSTATE`. Since `SessionGuard` (in test/qdmi/dd/helpers/test_utils.hpp) automatically initializes the session in its constructor, tests that need to verify session parameter setting behavior before initialization must allocate a separate uninitialized session rather than reusing the `SessionGuard`'s session.

Applied to files:

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

Applied to files:

  • src/qdmi/dd/Device.cpp
📚 Learning: 2025-11-04T14:26:25.420Z
Learnt from: marcelwa
Repo: munich-quantum-toolkit/core PR: 1243
File: test/python/qdmi/qiskit/conftest.py:11-19
Timestamp: 2025-11-04T14:26:25.420Z
Learning: In the munich-quantum-toolkit/core repository, Qiskit is always available as a dependency during testing, so import guards for qiskit-dependent imports in test files (e.g., test/python/qdmi/qiskit/*.py) are not necessary.

Applied to files:

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

Applied to files:

  • include/mqt-core/fomac/FoMaC.hpp
📚 Learning: 2025-11-24T10:19:41.147Z
Learnt from: burgholzer
Repo: munich-quantum-toolkit/core PR: 1326
File: python/mqt/core/__init__.py:22-22
Timestamp: 2025-11-24T10:19:41.147Z
Learning: In the munich-quantum-toolkit/core repository, Ruff is configured with 'ALL' rules enabled by default, and only specific rules are selectively disabled. When reviewing changes that enable previously-disabled rules (like PLC0415), noqa directives for those rules become necessary and should be retained.

Applied to files:

  • test/fomac/test_fomac.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:

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

Applied to files:

  • src/fomac/FoMaC.cpp
🧬 Code graph analysis (9)
include/mqt-core/qdmi/Common.hpp (2)
src/qdmi/dd/Device.cpp (9)
  • ADD_SINGLE_VALUE_PROPERTY (196-197)
  • ADD_SINGLE_VALUE_PROPERTY (199-200)
  • ADD_SINGLE_VALUE_PROPERTY (208-209)
  • ADD_SINGLE_VALUE_PROPERTY (212-213)
  • ADD_SINGLE_VALUE_PROPERTY (304-305)
  • ADD_SINGLE_VALUE_PROPERTY (330-331)
  • ADD_SINGLE_VALUE_PROPERTY (333-334)
  • ADD_SINGLE_VALUE_PROPERTY (335-336)
  • ADD_SINGLE_VALUE_PROPERTY (400-401)
src/qdmi/na/Device.cpp (7)
  • ADD_SINGLE_VALUE_PROPERTY (88-89)
  • ADD_SINGLE_VALUE_PROPERTY (91-92)
  • ADD_SINGLE_VALUE_PROPERTY (99-100)
  • ADD_SINGLE_VALUE_PROPERTY (103-105)
  • ADD_SINGLE_VALUE_PROPERTY (106-107)
  • ADD_SINGLE_VALUE_PROPERTY (290-291)
  • ADD_SINGLE_VALUE_PROPERTY (292-293)
src/qdmi/sc/Device.cpp (3)
include/mqt-core/qdmi/Driver.hpp (1)
  • instance (424-427)
src/qdmi/dd/Device.cpp (2)
  • get (152-158)
  • get (152-152)
src/qdmi/na/Device.cpp (2)
  • get (47-53)
  • get (47-47)
src/qdmi/na/Device.cpp (2)
src/qdmi/dd/Device.cpp (2)
  • get (152-158)
  • get (152-152)
src/qdmi/sc/Device.cpp (2)
  • get (50-56)
  • get (50-50)
src/qdmi/Driver.cpp (2)
include/mqt-core/qdmi/Driver.hpp (4)
  • param (299-300)
  • param (379-380)
  • status (325-325)
  • libName (445-447)
include/mqt-core/qdmi/Common.hpp (14)
  • toString (106-136)
  • toString (106-106)
  • toString (150-178)
  • toString (150-150)
  • toString (181-199)
  • toString (181-181)
  • toString (203-232)
  • toString (203-204)
  • toString (235-277)
  • toString (235-235)
  • toString (280-318)
  • toString (280-280)
  • toString (321-369)
  • toString (321-321)
src/qdmi/dd/Device.cpp (3)
include/mqt-core/qdmi/Driver.hpp (1)
  • instance (424-427)
src/qdmi/na/Device.cpp (2)
  • get (47-53)
  • get (47-47)
src/qdmi/sc/Device.cpp (2)
  • get (50-56)
  • get (50-50)
src/qdmi/Common.cpp (1)
include/mqt-core/qdmi/Common.hpp (14)
  • toString (106-136)
  • toString (106-106)
  • toString (150-178)
  • toString (150-150)
  • toString (181-199)
  • toString (181-181)
  • toString (203-232)
  • toString (203-204)
  • toString (235-277)
  • toString (235-235)
  • toString (280-318)
  • toString (280-280)
  • toString (321-369)
  • toString (321-321)
include/mqt-core/fomac/FoMaC.hpp (1)
include/mqt-core/qdmi/Common.hpp (15)
  • toString (106-136)
  • toString (106-106)
  • toString (150-178)
  • toString (150-150)
  • toString (181-199)
  • toString (181-181)
  • toString (203-232)
  • toString (203-204)
  • toString (235-277)
  • toString (235-235)
  • toString (280-318)
  • toString (280-280)
  • toString (321-369)
  • toString (321-321)
  • throwIfError (147-147)
src/na/fomac/Device.cpp (1)
include/mqt-core/na/fomac/Device.hpp (3)
  • Device (88-88)
  • device (123-124)
  • d (125-125)
src/fomac/FoMaC.cpp (3)
src/qdmi/dd/Device.cpp (4)
  • queryProperty (180-222)
  • queryProperty (180-181)
  • queryProperty (384-403)
  • queryProperty (384-387)
include/mqt-core/qdmi/Common.hpp (15)
  • throwIfError (147-147)
  • toString (106-136)
  • toString (106-106)
  • toString (150-178)
  • toString (150-150)
  • toString (181-199)
  • toString (181-181)
  • toString (203-232)
  • toString (203-204)
  • toString (235-277)
  • toString (235-235)
  • toString (280-318)
  • toString (280-280)
  • toString (321-369)
  • toString (321-321)
src/qdmi/Common.cpp (2)
  • throwIfError (25-55)
  • throwIfError (25-25)
⏰ Context from checks skipped due to timeout of 900000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (4)
  • GitHub Check: 🐍 Test (macos-14) / 🐍 macos-14
  • GitHub Check: 🐍 Test (ubuntu-24.04) / 🐍 ubuntu-24.04
  • GitHub Check: 🐍 Test (macos-15-intel) / 🐍 macos-15-intel
  • GitHub Check: 🐍 Test (windows-2022) / 🐍 windows-2022
🔇 Additional comments (56)
test/qdmi/dd/results_sampling_test.cpp (1)

33-39: Good explicit typing to avoid unintended narrowing

Using size_t for sum aligns it with shots and typical histogram count types, avoiding potential narrowing compared to auto from 0U. Loop logic and assertion remain correct.

src/qdmi/na/CMakeLists.txt (3)

94-97: Path consistency between header location and directory creation is now correct.

The header output path (line 94) and directory creation (line 97) are now aligned at ${CMAKE_CURRENT_BINARY_DIR}/include/qdmi/na, resolving the previous inconsistency. The generated header will be written to a directory that actually exists.


27-27: Path updates to align with qdmi/na directory structure are consistent.

The header references have been updated consistently across all target definitions to use the new qdmi/na namespace structure:

  • Generator.hpp at line 27
  • Device.hpp at lines 128 and 178

These align with the directory creation change and the overall PR objective of unifying the directory structure for QDMI devices.

Also applies to: 128-128, 178-178


184-185: This dependency pattern is intentional and consistent across implementations.

The static device library and dynamic device library have different PRIVATE dependencies by design. The Generator library (line 33) and dynamic device library (lines 184–185) include MQT::ProjectOptions and MQT::ProjectWarnings for compiler configuration, while the static device library does not directly link them. This same pattern appears consistently in both src/qdmi/na/CMakeLists.txt and src/qdmi/sc/CMakeLists.txt, indicating this is intentional rather than an oversight. The separation reflects a deliberate choice to apply compiler options at the dynamic/generator level rather than the static level, which is a valid design.

src/qdmi/CMakeLists.txt (2)

28-30: LGTM!

The target collection pattern is correct: both the common and driver targets are appended locally, then propagated to the parent scope at the end. This ensures both targets are included in MQT_CORE_TARGETS.

Also applies to: 57-62


50-54: LGTM!

The driver's public linkage to MQT::CoreQDMICommon is appropriate, ensuring consumers have access to common QDMI utilities like throwIfError and toString overloads.

src/qdmi/sc/CMakeLists.txt (2)

131-135: LGTM!

The private linkage to MQT::CoreQDMICommon correctly provides access to shared QDMI utilities (like IS_INVALID_ARGUMENT macro and toString helpers) used in the SC device implementation.


180-186: LGTM!

The dynamic SC device library correctly adds MQT::CoreQDMICommon to its private dependencies while retaining MQT::ProjectOptions and MQT::ProjectWarnings for proper build settings in the shared library context.

src/qdmi/sc/Device.cpp (3)

50-56: LGTM!

The leaked static singleton pattern is consistent with dd/Device.cpp and na/Device.cpp implementations, correctly avoiding static deinitialization order issues. The NOLINT comment documents the intentional memory management choice.


77-79: Validation pattern is consistent across all query/set methods.

The IS_INVALID_ARGUMENT macro usage follows the established pattern. Based on learnings, duplication across device implementations is acceptable as a temporary measure pending upstream to the QDMI repository.


406-412: LGTM!

The initialization pattern correctly ensures the singleton is created via Device::get(), while device_finalize appropriately becomes a no-op for the leaked singleton approach. Using std::ignore to explicitly discard the return value is a clean way to suppress warnings.

src/fomac/CMakeLists.txt (1)

22-26: LGTM!

Adding MQT::CoreQDMICommon to the public linkage is appropriate since the Session API (formerly FoMaC) exposes types and utilities from the common library to consumers.

CHANGELOG.md (1)

14-14: LGTM!

The changelog entries accurately document the new authentication support, the FoMaC→Session refactor, NA device relocation, thread-safe reference counting, and the custom property handling fix. All entries follow the established format.

Also applies to: 26-29, 42-42

src/qdmi/dd/CMakeLists.txt (1)

40-44: LGTM!

The private linkage to MQT::CoreQDMICommon correctly provides the DD device access to shared QDMI utilities used in src/qdmi/dd/Device.cpp. This is consistent with the SC device's static target linkage pattern.

src/qdmi/Common.cpp (2)

25-55: LGTM!

The throwIfError function correctly maps QDMI status codes to appropriate C++ exceptions:

  • QDMI_SUCCESS → no action
  • QDMI_WARN_GENERAL → warning to stderr (non-throwing)
  • QDMI_ERROR_OUTOFMEMstd::bad_alloc (correctly constructed without message per standard)
  • QDMI_ERROR_OUTOFRANGE / QDMI_ERROR_INVALIDARGUMENT → domain-specific exceptions
  • Other errors → std::runtime_error

The nested switch structure cleanly separates non-error cases from error handling.


11-21: Well-documented with clear intent for future upstream.

The file documentation clearly notes this will be upstreamed to the QDMI core library. The includes are minimal and appropriate for the implementation.

src/qdmi/dd/Device.cpp (3)

152-158: LGTM! Singleton lifecycle change addresses previous race condition concerns.

The function-local static with intentional leak pattern correctly avoids static deinitialization order fiasco and eliminates the race condition identified in previous reviews. This is now consistent with src/qdmi/na/Device.cpp and src/qdmi/sc/Device.cpp.


694-700: LGTM! Simplified lifecycle management.

device_initialize now simply ensures the singleton exists via Device::get(), and device_finalize is correctly a no-op since the instance is intentionally leaked to avoid destruction order issues.


182-184: Consistent use of centralized validation macro.

The IS_INVALID_ARGUMENT macro provides unified validation across device, session, site, operation, job parameters, and results. Based on learnings, this duplication across device implementations is acceptable as a temporary measure until upstreamed to the QDMI repository.

src/qdmi/na/Device.cpp (2)

47-53: LGTM! Consistent singleton pattern.

The function-local static with intentional leak pattern matches the implementation in src/qdmi/dd/Device.cpp and src/qdmi/sc/Device.cpp, ensuring consistency across all QDMI device implementations.


574-580: LGTM! Consistent lifecycle pattern.

The device_initialize and device_finalize functions follow the same pattern as the DD device implementation.

src/qdmi/Driver.cpp (3)

162-185: LGTM! Robust parameter binding with proper error handling.

The setParameter lambda correctly:

  1. Skips if value is empty or function pointer is null
  2. Returns silently on QDMI_SUCCESS
  3. Logs and skips on QDMI_ERROR_NOTSUPPORTED (graceful degradation for devices without auth support)
  4. Frees the session and throws with detailed error message for actual failures

This addresses the previous review feedback about handling unsupported parameters gracefully.


187-197: LGTM! Complete parameter binding from config.

All authentication parameters from DeviceSessionConfig are bound: baseUrl, token, authFile, authUrl, username, password, and custom1custom5. This aligns with the PR objective of propagating session/authentication parameters to devices.


388-394: LGTM! Dynamic library loading with session configuration.

The addDynamicDeviceLibrary method now accepts and forwards DeviceSessionConfig to the device constructor, enabling authentication for dynamically loaded QDMI devices.

include/mqt-core/qdmi/Common.hpp (3)

28-34: LGTM! Well-implemented unreachable helper.

The unreachable() function correctly uses compiler-specific intrinsics (__builtin_unreachable() for GCC/Clang, __assume(false) for MSVC) and is properly marked [[noreturn]].


99-103: LGTM! Centralized validation macro.

The IS_INVALID_ARGUMENT macro correctly validates that a property/parameter value is within the valid range (less than *_MAX) while allowing CUSTOM1CUSTOM5 extension values. This centralizes validation logic previously duplicated across device implementations.


105-136: LGTM! Comprehensive status code stringification.

All QDMI_STATUS values are covered with human-readable strings, and the unreachable() fallback ensures compiler warnings for missing cases in future enum extensions.

include/mqt-core/fomac/FoMaC.hpp (3)

146-175: LGTM! Complete SessionConfig with custom parameters.

The SessionConfig struct includes all authentication parameters (token, authFile, authUrl, username, password, projectId) plus custom1custom5 as requested in previous review discussions. All fields are appropriately std::optional<std::string>.


326-358: LGTM! Improved error messages in queryProperty.

The queryProperty method now constructs descriptive error messages using qdmi::toString(prop) before calling qdmi::throwIfError, making debugging easier when property queries fail.


699-718: LGTM! Proper Session lifecycle management.

The Session class correctly:

  1. Takes SessionConfig as a constructor parameter for authentication
  2. Deletes copy constructor/assignment to prevent resource duplication
  3. Implements move semantics for safe ownership transfer
  4. Has a non-virtual destructor (addressing past review feedback)
test/fomac/test_fomac.cpp (11)

11-28: LGTM! Includes updated appropriately for Session-based API.

The new includes for <filesystem>, <fstream>, and <thread> support the authentication test cases that validate file existence and generate unique temp file names.


31-68: LGTM! Test fixtures correctly migrated to Session types.

The test classes (DeviceTest, SiteTest, OperationTest, DDSimulatorDeviceTest) are properly updated to use Session::Device, Session::Device::Site, and Session::Device::Operation types. The getDDSimulatorDevice() helper correctly instantiates a local Session to retrieve devices.


70-103: LGTM! Job-related test fixtures updated correctly.

JobTest and SimulatorJobTest now use Session::Job type, and the createTestJob() methods return the correct type.


105-229: LGTM! Status and property toString tests updated to use qdmi:: namespace.

The test cases correctly use the qdmi::toString and qdmi::throwIfError functions from the centralized qdmi/Common.hpp header.


745-758: LGTM! SessionParameterToString test provides good coverage.

The test covers all standard session parameters including QDMI_SESSION_PARAMETER_MAX and custom parameters, addressing earlier review feedback.


760-827: Good validation coverage for token and URL authentication parameters.

The tests cover valid and invalid URL formats including localhost variations, empty URLs, unsupported protocols, and missing protocols. The empty URL edge case (lines 823-827) was added per earlier review feedback.


829-853: Temporary file uniqueness relies on thread ID hash.

The temp file naming uses std::hash<std::thread::id> which provides reasonable uniqueness within a single process. Per earlier discussion with the maintainer, cross-process collision risk is accepted to avoid platform-specific code.


888-948: Custom parameters smoke tests improved with GTEST_SKIP().

The tests now use GTEST_SKIP() when custom parameters are unsupported, which is cleaner than the previous SUCCEED() in all branches. This addresses earlier review feedback about tests always passing regardless of outcome.


950-976: LGTM! Session instance tests verify basic functionality.

SessionGetDevicesReturnsList and SessionMultipleInstances tests verify that Session objects can be constructed and return devices, confirming the non-singleton behavior works correctly.


978-984: LGTM! Helper function correctly scoped in anonymous namespace.

The getDevices() helper is properly placed in an anonymous namespace without the redundant inline specifier, addressing earlier static analysis and review feedback.


986-1026: LGTM! Test suite instantiation updated for Session types.

The INSTANTIATE_TEST_SUITE_P macros correctly use getDevices() and Session::Device for parameter info types.

src/na/fomac/Device.cpp (6)

15-15: LGTM! Include path updated for reorganized directory structure.

The include path change from "na/device/Generator.hpp" to "qdmi/na/Generator.hpp" aligns with the directory structure unification mentioned in the PR objectives (#1332).


38-90: LGTM! Helper functions updated to accept Session types.

Both calculateExtentFromSites overloads correctly accept fomac::Session::Device::Site and pairs of sites. The logic remains unchanged.


113-172: LGTM! Device initialization methods migrated to Session::Device namespace.

The initNameFromDevice, initMinAtomDistanceFromDevice, initQubitsNumFromDevice, initLengthUnitFromDevice, initDurationUnitFromDevice, and initDecoherenceTimesFromDevice methods are correctly updated to the Session::Device namespace while preserving their implementation logic.


173-296: LGTM! initTrapsfromDevice migrated correctly.

The trap initialization logic is unchanged; only the namespace qualification is updated.


297-578: LGTM! initOperationsFromDevice correctly uses Session types.

The operation loop now uses fomac::Session::Device::Operation and the local variable is renamed from name to opName to avoid potential shadowing with the outer scope. The regex matching and shuttling unit logic remain intact.


579-590: LGTM! Device constructor and getDevices updated for Session-based API.

The Device constructor now accepts const fomac::Session::Device& and forwards to the base class. The getDevices() method creates a local fomac::Session instance, retrieves devices, and attempts to create NA Device objects from them. This correctly reflects the non-singleton Session design.

src/fomac/FoMaC.cpp (9)

13-30: LGTM! Includes updated for Session implementation.

New includes for <filesystem>, <regex>, and <spdlog/spdlog.h> support the authentication file validation, URL pattern matching, and logging respectively.


32-74: LGTM! Site property accessors migrated to Session::Device::Site namespace.

All site property getters are correctly updated to the Session::Device::Site namespace with unchanged implementation logic.


75-174: LGTM! Operation property accessors migrated to Session::Device::Operation namespace.

All operation property getters including getName, getQubitsNum, getDuration, getFidelity, getSites, getSitePairs, etc., are correctly updated.


175-275: LGTM! Device property accessors migrated to Session::Device namespace.

All device property getters are correctly updated with consistent use of the Session::Device namespace.


277-305: LGTM! Job submission uses centralized error handling.

The submitJob method correctly uses qdmi::throwIfError for consistent error handling across all QDMI operations.


307-603: LGTM! Job methods migrated with consistent error handling.

All Session::Job methods (check, wait, cancel, getId, getProgramFormat, getProgram, getNumShots, getShots, getCounts, getDenseStateVector, getDenseProbabilities, getSparseStateVector, getSparseProbabilities) correctly use qdmi::throwIfError and qdmi::unreachable().


605-675: Session constructor implements robust resource management and validation.

The implementation addresses previous review feedback:

  1. Resource leak prevention: The cleanup lambda ensures QDMI_session_free is called if any exception occurs after allocation.
  2. URL validation: The regex now accepts localhost URLs per maintainer request.
  3. File validation: authFile existence is checked before use.
  4. Optional parameter handling: The setParameter helper gracefully skips unsupported parameters with SPDLOG_INFO logging.

677-696: LGTM! Session lifecycle management is correct.

The destructor safely checks for null before freeing, and move semantics correctly transfer ownership while nullifying the source. Thread-safety concerns are deferred to a separate tracking issue per maintainer decision.


698-708: LGTM! getDevices returns wrapped Device objects.

The implementation correctly queries the session for devices and wraps them in Device objects using the Token{} pattern for access control.

@burgholzer burgholzer merged commit 67b7d6d into main Dec 7, 2025
34 checks passed
@burgholzer burgholzer deleted the qdmi-auth branch December 7, 2025 17:18
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 enhancement New feature or request minor Minor version update python Anything related to Python code QDMI Anything related to QDMI

Projects

None yet

Development

Successfully merging this pull request may close these issues.

✨ Authentication via QDMI-Qiskit Backend Unify the Directory Structure for QDMI Devices

4 participants