Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
Show all changes
78 commits
Select commit Hold shift + click to select a range
074c7ff
:sparkles: QDMI authentication via FoMaC
marcelwa Dec 3, 2025
f1d4360
:rotating_light: Fix `clang-tidy` warnings
marcelwa Dec 3, 2025
c184cc3
:rotating_light: Fix `clang-tidy` warnings
marcelwa Dec 4, 2025
9d17fe3
:recycle: Don't re-expose session parameter enum
marcelwa Dec 4, 2025
97a863c
:recycle: Rename `setSessionParameter` to `setParameter`
marcelwa Dec 4, 2025
5705a51
:fire: Remove module-level convenience functions for parameter settin…
marcelwa Dec 4, 2025
b2f8833
:label: Propagate changes to `.pyi`
marcelwa Dec 4, 2025
4369473
:recycle: Session authentication via constructor parameters
marcelwa Dec 4, 2025
84e6f35
:recycle: Rename the `FoMaC` class to `Session`
marcelwa Dec 4, 2025
6a4dc71
:recycle: Adjust Qiskit-QDMI Python interface to adhere to new API
marcelwa Dec 4, 2025
8380a22
:rotating_light: Fix `clang-tidy` warnings
marcelwa Dec 4, 2025
594b3b3
:white_check_mark: Add placeholder tests for authentication
marcelwa Dec 4, 2025
578b5f2
:bug: Guard session allocation
marcelwa Dec 4, 2025
b6ffb8a
:recycle: Improve URL validation regex
marcelwa Dec 4, 2025
1db21eb
:art: Address CodeRabbit's suggestions
marcelwa Dec 4, 2025
ed6d6b4
:memo: Update documentation to reflect code changes
marcelwa Dec 4, 2025
321ee04
:sparkles: Expose session authentication to QDMIProvider initializati…
marcelwa Dec 4, 2025
7f5cbe9
:bug: Fixed regex format
marcelwa Dec 4, 2025
36aaeae
:white_check_mark: Add authentication FoMaC C++ tests
marcelwa Dec 4, 2025
b4f495c
:rotating_light: Remove unused header
marcelwa Dec 4, 2025
25b13ab
:memo: Documentation on session authentication
marcelwa Dec 4, 2025
4dae4f1
:memo: CHANGELOG
marcelwa Dec 4, 2025
5655d59
:rotating_light: Fix `clang-tidy` warnings
marcelwa Dec 4, 2025
bed169b
:art: Light code cleanup
marcelwa Dec 4, 2025
9b76cd3
Merge branch 'main' into qdmi-auth
marcelwa Dec 4, 2025
3206c7d
:sparkles: Expose CUSTOM `QDMI_Program_Format` parameters
marcelwa Dec 5, 2025
77ed885
:art: Cleanup Session constructor
marcelwa Dec 5, 2025
5ffabe0
Merge branch 'main' into qdmi-auth
marcelwa Dec 5, 2025
fe6b067
:art: Cleanup Session constructor in bindings
marcelwa Dec 5, 2025
8f1b299
:rotating_light: Fix `clang-tidy` warnings
marcelwa Dec 5, 2025
7c8047a
:safety_vest: Ensure partially instantiated sessions get cleaned up p…
marcelwa Dec 5, 2025
5680e80
:art: Implement CodeRabbit's suggestions
marcelwa Dec 5, 2025
3d8d402
:recycle: Refactor URL validation to allow local URLs and IPs
marcelwa Dec 5, 2025
9843d31
:art: Refactor QDMI Provider args to kwargs
marcelwa Dec 5, 2025
f95d547
:recycle: Expose `QDMI_DEVICE_SESSION_PARAMETER`s via the `Driver`
marcelwa Dec 5, 2025
b0fed9a
:rotating_light: Address `clang-tidy` warnings
marcelwa Dec 6, 2025
69467b9
:recycle: Refactor `QDMI_Device_impl_d` and `Session` initialization …
marcelwa Dec 6, 2025
fbb7873
:white_check_mark: Stricter Session tests to properly test updated pa…
marcelwa Dec 6, 2025
74cfd1d
:white_check_mark: Test `Driver::addDynamicDeviceLibrary`
marcelwa Dec 6, 2025
7882499
:green_heart: Fix RTD build
marcelwa Dec 6, 2025
106452c
:art: Prefer SPDLOG macros over function calls
marcelwa Dec 6, 2025
d0d132c
:sparkles: Expose device library loading to Python
marcelwa Dec 6, 2025
2e10639
Merge branch 'main' into qdmi-auth
marcelwa Dec 6, 2025
f0b3a32
:art: Implement CodeRabbit's suggestions
marcelwa Dec 6, 2025
3fb8b1c
:rotating_light: Fix `clang-tidy` warnings
marcelwa Dec 6, 2025
ffb8ea5
Merge branch 'main' into qdmi-auth
marcelwa Dec 6, 2025
685c609
Merge branch 'main' into qdmi-auth
burgholzer Dec 6, 2025
5cd5f00
🩹 ensure session stays alive in provider
burgholzer Dec 6, 2025
0a4f68f
♻️ refactor provider initialization to use session kwargs
burgholzer Dec 6, 2025
8e7938c
🎨 remove redundant inline
burgholzer Dec 6, 2025
5ea53a6
✅♻️ refine smoke tests
burgholzer Dec 6, 2025
7b6fb8f
🐛 Fix custom QDMI property and parameter handling in SC and NA devices
burgholzer Dec 7, 2025
f8e719c
🐛 Lock the right mutex in the DD QDMI device
burgholzer Dec 7, 2025
6be3b68
🎨 remove redundant initialization
burgholzer Dec 7, 2025
5b7bd74
♻️ Enable thread-safe reference counting for QDMI devices singletons
burgholzer Dec 7, 2025
f9f0b6b
♻️ Allow repeated loading of QDMI device library with potentially dif…
burgholzer Dec 7, 2025
23b8846
🎨 slightly adjust session parameter setter
burgholzer Dec 7, 2025
951035b
🎨 reduce redundancy via new macro
burgholzer Dec 7, 2025
8e84370
♻️ slightly refactor reference counting
burgholzer Dec 7, 2025
d617428
🔥 remove openLibHandles
burgholzer Dec 7, 2025
d6a5467
✅ add one more test case
burgholzer Dec 7, 2025
e432e56
🚚 move `NA` QDMI device in its right place
burgholzer Dec 7, 2025
2e20fab
🩹 fix include dir
burgholzer Dec 7, 2025
80e88dc
📝 better singleton management docstrings
burgholzer Dec 7, 2025
086f7b2
🩹 fix invalid argument check
burgholzer Dec 7, 2025
0b74445
🚨 additional clang-tidy naming convention rule
burgholzer Dec 7, 2025
6d3dc52
♻️ rework thread-safe singleton logic
burgholzer Dec 7, 2025
7af7e00
Merge branch 'main' into qdmi-auth
burgholzer Dec 7, 2025
cb6f1c2
Revert "🚨 additional clang-tidy naming convention rule"
burgholzer Dec 7, 2025
676a8a1
♻️ simplify handling of static deinitialization order fiasco
burgholzer Dec 7, 2025
090ee48
🚨 clang-tidy
burgholzer Dec 7, 2025
724d2b8
🚨 fix compiler and linter warnings
burgholzer Dec 7, 2025
a3023d5
✨ add common definitions and utilities for QDMI
burgholzer Dec 7, 2025
59b6db4
:memo: Add missing PR reference
marcelwa Dec 7, 2025
1a49aea
:green_heart: Fix Windows tests by using `EXPECT_STREQ` instead of `E…
marcelwa Dec 7, 2025
df67ab4
:recycle: Refactor URL validation regex to allow IPv4 and IPv6 addresses
marcelwa Dec 7, 2025
2ea83b7
:pencil2: Fix typo
marcelwa Dec 7, 2025
931e0e4
:memo: Add comments to highlight the subtle difference between `strnc…
marcelwa Dec 7, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
29 changes: 28 additions & 1 deletion bindings/fomac/fomac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,23 @@ namespace py = pybind11;
using namespace py::literals;

PYBIND11_MODULE(MQT_CORE_MODULE_NAME, m, py::mod_gil_not_used()) {
// SessionParameter enum
py::native_enum<fomac::SessionParameter>(
m, "SessionParameter", "enum.Enum",
"Session parameters for authentication and configuration.")
.value("TOKEN", fomac::SessionParameter::TOKEN)
.value("AUTHFILE", fomac::SessionParameter::AUTHFILE)
.value("AUTHURL", fomac::SessionParameter::AUTHURL)
.value("USERNAME", fomac::SessionParameter::USERNAME)
.value("PASSWORD", fomac::SessionParameter::PASSWORD)
.value("PROJECTID", fomac::SessionParameter::PROJECTID)
.value("CUSTOM1", fomac::SessionParameter::CUSTOM1)
.value("CUSTOM2", fomac::SessionParameter::CUSTOM2)
.value("CUSTOM3", fomac::SessionParameter::CUSTOM3)
.value("CUSTOM4", fomac::SessionParameter::CUSTOM4)
.value("CUSTOM5", fomac::SessionParameter::CUSTOM5)
.finalize();

// Job class
auto job = py::class_<fomac::FoMaC::Job>(m, "Job");
job.def("check", &fomac::FoMaC::Job::check);
Expand Down Expand Up @@ -183,7 +200,17 @@ PYBIND11_MODULE(MQT_CORE_MODULE_NAME, m, py::mod_gil_not_used()) {
device.def(py::self == py::self); // NOLINT(misc-redundant-expression)
device.def(py::self != py::self); // NOLINT(misc-redundant-expression)

m.def("devices", &fomac::FoMaC::getDevices);
// FoMaC class (exposed as Session in Python)
auto fomac = py::class_<fomac::FoMaC>(m, "Session");
fomac.def(py::init<>());
fomac.def("set_session_parameter", &fomac::FoMaC::setSessionParameter,
"param"_a, "value"_a);
fomac.def("get_devices", &fomac::FoMaC::getDevices);

// Module-level convenience functions (use default session)
m.def("set_session_parameter", &fomac::setSessionParameter, "param"_a,
"value"_a);
m.def("devices", &fomac::getDevices);
}

} // namespace mqt
123 changes: 105 additions & 18 deletions include/mqt-core/fomac/FoMaC.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -18,11 +18,13 @@
#include <iostream>
#include <iterator>
#include <map>
#include <mutex>
#include <optional>
#include <qdmi/client.h>
#include <ranges>
#include <string>
#include <type_traits>
#include <unordered_map>
#include <utility>
#include <vector>

Expand Down Expand Up @@ -141,30 +143,64 @@ concept maybe_optional_value_or_string_or_vector =
value_or_string_or_vector<remove_optional_t<T>>;

/// @returns the string representation of the given QDMI_STATUS.
auto toString(QDMI_STATUS result) -> std::string;
auto toString(const QDMI_STATUS result) -> std::string;

/// @returns the string representation of the given QDMI_Site_Property.
auto toString(QDMI_Site_Property prop) -> std::string;
auto toString(const QDMI_Site_Property prop) -> std::string;

/// @returns the string representation of the given QDMI_Operation_Property.
auto toString(QDMI_Operation_Property prop) -> std::string;
auto toString(const QDMI_Operation_Property prop) -> std::string;

/// @returns the string representation of the given QDMI_Device_Property.
auto toString(QDMI_Device_Property prop) -> std::string;
auto toString(const QDMI_Device_Property prop) -> std::string;

/// @returns the string representation of the given QDMI_Session_Property.
constexpr auto toString(QDMI_Session_Property prop) -> std::string {
constexpr auto toString(const QDMI_Session_Property prop) -> std::string {
if (prop == QDMI_SESSION_PROPERTY_DEVICES) {
return "QDMI_SESSION_PROPERTY_DEVICES";
}
return "QDMI_SESSION_PROPERTY_UNKNOWN";
}

/**
* @brief Session parameters for authentication and configuration.
* @details These parameters must be set before the session is initialized
* (i.e., before the first call to getDevices()).
* @see QDMI_SESSION_PARAMETER_T
*/
enum class SessionParameter {
/// Authentication token
TOKEN = QDMI_SESSION_PARAMETER_TOKEN,
/// Path to authentication file
AUTHFILE = QDMI_SESSION_PARAMETER_AUTHFILE,
/// URL to authentication server
AUTHURL = QDMI_SESSION_PARAMETER_AUTHURL,
/// Username for authentication
USERNAME = QDMI_SESSION_PARAMETER_USERNAME,
/// Password for authentication
PASSWORD = QDMI_SESSION_PARAMETER_PASSWORD,
/// Project ID for session
PROJECTID = QDMI_SESSION_PARAMETER_PROJECTID,
/// Custom parameter 1 (driver-defined)
CUSTOM1 = QDMI_SESSION_PARAMETER_CUSTOM1,
/// Custom parameter 2 (driver-defined)
CUSTOM2 = QDMI_SESSION_PARAMETER_CUSTOM2,
/// Custom parameter 3 (driver-defined)
CUSTOM3 = QDMI_SESSION_PARAMETER_CUSTOM3,
/// Custom parameter 4 (driver-defined)
CUSTOM4 = QDMI_SESSION_PARAMETER_CUSTOM4,
/// Custom parameter 5 (driver-defined)
CUSTOM5 = QDMI_SESSION_PARAMETER_CUSTOM5
};

/// @returns the string representation of the given SessionParameter.
auto toString(const SessionParameter param) -> std::string;

/// Throws an exception corresponding to the given QDMI_STATUS code.
[[noreturn]] auto throwError(int result, const std::string& msg) -> void;
[[noreturn]] auto throwError(const int result, const std::string& msg) -> void;

/// Throws an exception if the result indicates an error.
inline auto throwIfError(int result, const std::string& msg) -> void {
inline auto throwIfError(const int result, const std::string& msg) -> void {
switch (result) {
case QDMI_SUCCESS:
break;
Expand All @@ -180,7 +216,6 @@ inline auto throwIfError(int result, const std::string& msg) -> void {
* @brief Class representing the FoMaC library.
* @details This class provides methods to query available devices and
* manage the QDMI session.
* @note This class is a singleton.
* @see QDMI_Session
*/
class FoMaC {
Expand Down Expand Up @@ -674,12 +709,13 @@ class FoMaC {

private:
QDMI_Session session_ = nullptr;
mutable std::mutex mutex_;
bool initialized_ = false;
std::unordered_map<SessionParameter, std::string> pendingParameters_;

/// @brief Ensures the session is initialized, applying pending parameters
auto ensureInitialized() -> void;

FoMaC();
static auto get() -> FoMaC& {
static FoMaC instance;
return instance;
}
template <size_constructible_contiguous_range T>
[[nodiscard]] auto queryProperty(const QDMI_Session_Property prop) const
-> T {
Expand All @@ -696,14 +732,65 @@ class FoMaC {
}

public:
/**
* @brief Constructs a new FoMaC session.
* @details Creates and allocates a new QDMI session. The session is not
* initialized until the first call to getDevices() or after setting
* authentication parameters.
*/
FoMaC();

/**
* @brief Destructor that releases the QDMI session.
*/
virtual ~FoMaC();
// Delete copy constructors and assignment operators to prevent copying the
// singleton instance.

// Delete copy constructors and assignment operators
FoMaC(const FoMaC&) = delete;
FoMaC& operator=(const FoMaC&) = delete;
FoMaC(FoMaC&&) = default;
FoMaC& operator=(FoMaC&&) = default;

// Allow move semantics
FoMaC(FoMaC&&) noexcept;
FoMaC& operator=(FoMaC&&) noexcept;

/**
* @brief Set a session parameter for authentication.
* @details This method must be called before the first call to getDevices().
* Once the session is initialized, parameters cannot be changed.
* For AUTHFILE parameter, the file path is validated for existence.
* For AUTHURL parameter, basic URL format validation is performed.
* @param param The parameter to set
* @param value The value to set
* @throws std::runtime_error if the session is already initialized
* @throws std::runtime_error if validation fails (file not found, invalid
* URL)
* @see QDMI_session_set_parameter
*/
auto setSessionParameter(const SessionParameter param,
const std::string& value) -> void;

/// @see QDMI_SESSION_PROPERTY_DEVICES
[[nodiscard]] static auto getDevices() -> std::vector<Device>;
[[nodiscard]] auto getDevices() -> std::vector<Device>;
};

/**
* @brief Get devices from a default FoMaC session.
* @details This is a convenience function that uses a static default FoMaC
* instance. For custom authentication or multiple sessions, create FoMaC
* instances directly.
* @return Vector of available devices
*/
[[nodiscard]] auto getDevices() -> std::vector<FoMaC::Device>;

/**
* @brief Set a session parameter on the default FoMaC session.
* @details This is a convenience function for the default session.
* For multiple sessions, create FoMaC instances directly.
* @param param The parameter to set
* @param value The value to set
* @see FoMaC::setSessionParameter
*/
auto setSessionParameter(SessionParameter param, const std::string& value)
-> void;

} // namespace fomac
106 changes: 105 additions & 1 deletion python/mqt/core/fomac.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,10 @@ __all__ = [
"Device",
"Job",
"ProgramFormat",
"Session",
"SessionParameter",
"devices",
"set_session_parameter",
]

class ProgramFormat(Enum):
Expand All @@ -34,6 +37,73 @@ class ProgramFormat(Enum):
CUSTOM4 = ...
CUSTOM5 = ...

class SessionParameter(Enum):
"""Session parameters for authentication and configuration.

These parameters must be set before the first call to devices().
"""

TOKEN = ...
"""Authentication token"""
AUTHFILE = ...
"""Path to authentication file"""
AUTHURL = ...
"""URL to authentication server"""
USERNAME = ...
"""Username for authentication"""
PASSWORD = ...
"""Password for authentication"""
PROJECTID = ...
"""Project ID for session"""
CUSTOM1 = ...
"""Custom parameter 1 (driver-defined)"""
CUSTOM2 = ...
"""Custom parameter 2 (driver-defined)"""
CUSTOM3 = ...
"""Custom parameter 3 (driver-defined)"""
CUSTOM4 = ...
"""Custom parameter 4 (driver-defined)"""
CUSTOM5 = ...
"""Custom parameter 5 (driver-defined)"""

class Session:
"""A FoMaC session for managing QDMI devices.

Allows creating isolated sessions with independent authentication settings.
"""

def __init__(self) -> None:
"""Create a new FoMaC session."""

def set_session_parameter(self, param: SessionParameter, value: str) -> None:
"""Set a session parameter for authentication.

This method must be called before the first call to get_devices().
Once the session is initialized, parameters cannot be changed.

Args:
param: The session parameter to set
value: The parameter value as a string

Raises:
RuntimeError: If session is already initialized
RuntimeError: If AUTHFILE does not exist
RuntimeError: If AUTHURL has invalid format

Example:
>>> from mqt.core.fomac import Session, SessionParameter
>>> session = Session()
>>> session.set_session_parameter(SessionParameter.TOKEN, "my_token")
>>> devices = session.get_devices()
"""

def get_devices(self) -> list[Device]:
"""Get available devices from this session.

Returns:
List of available devices
"""

class Job:
"""A job represents a submitted quantum program execution."""

Expand Down Expand Up @@ -205,5 +275,39 @@ class Device:
def __ne__(self, other: object) -> bool:
"""Checks if two devices are not equal."""

def set_session_parameter(param: SessionParameter, value: str) -> None:
"""Set a session parameter for authentication on the default session.

This function must be called before the first call to devices().
Once the session is initialized, parameters cannot be changed.

For custom sessions or multiple sessions, use the Session class directly.

Args:
param: The session parameter to set
value: The parameter value as a string

Raises:
RuntimeError: If session is already initialized
RuntimeError: If AUTHFILE does not exist
RuntimeError: If AUTHURL has invalid format

Example:
>>> import mqt.core.fomac as fomac
>>> fomac.set_session_parameter(fomac.SessionParameter.TOKEN, "my_token")
>>> devices = fomac.devices()

# Or use Session class for isolated sessions:
>>> session = fomac.Session()
>>> session.set_session_parameter(fomac.SessionParameter.TOKEN, "token")
>>> session_devices = session.get_devices()
"""

def devices() -> list[Device]:
"""Returns a list of available devices."""
"""Returns a list of available devices from the default session.

For custom sessions, create a Session instance and use its get_devices() method.

Returns:
List of available devices
"""
Loading
Loading