Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel

### Added

- ✨ Return device handle from `add_dynamic_device_library` for direct backend creation ([#1381]) ([**@marcelwa**])
- ✨ Add IQM JSON support for job submission in Qiskit-QDMI Backend ([#1375]) ([**@marcelwa**])
- ✨ Add authentication support for QDMI sessions with token, username/password, auth file, auth URL, and project ID parameters ([#1355]) ([**@marcelwa**])
- ✨ Add a new QDMI device that represents a superconducting architecture featuring a coupling map ([#1328]) ([**@ystade**])
Expand Down Expand Up @@ -279,6 +280,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool

<!-- PR links -->

[#1381]: https://github.com/munich-quantum-toolkit/core/pull/1381
[#1375]: https://github.com/munich-quantum-toolkit/core/pull/1375
[#1371]: https://github.com/munich-quantum-toolkit/core/pull/1371
[#1359]: https://github.com/munich-quantum-toolkit/core/pull/1359
Expand Down
8 changes: 5 additions & 3 deletions bindings/fomac/fomac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,8 @@ PYBIND11_MODULE(MQT_CORE_MODULE_NAME, m, py::mod_gil_not_used()) {
const std::optional<std::string>& custom2 = std::nullopt,
const std::optional<std::string>& custom3 = std::nullopt,
const std::optional<std::string>& custom4 = std::nullopt,
const std::optional<std::string>& custom5 = std::nullopt) -> void {
const std::optional<std::string>& custom5 =
std::nullopt) -> fomac::Session::Device {
const qdmi::DeviceSessionConfig config{.baseUrl = baseUrl,
.token = token,
.authFile = authFile,
Expand All @@ -252,8 +253,9 @@ PYBIND11_MODULE(MQT_CORE_MODULE_NAME, m, py::mod_gil_not_used()) {
.custom3 = custom3,
.custom4 = custom4,
.custom5 = custom5};
qdmi::Driver::get().addDynamicDeviceLibrary(libraryPath, prefix,
config);
auto* const qdmiDevice = qdmi::Driver::get().addDynamicDeviceLibrary(
libraryPath, prefix, config);
return fomac::Session::Device::fromQDMIDevice(qdmiDevice);
},
"library_path"_a, "prefix"_a, "base_url"_a = std::nullopt,
"token"_a = std::nullopt, "auth_file"_a = std::nullopt,
Expand Down
10 changes: 10 additions & 0 deletions include/mqt-core/fomac/FoMaC.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -612,6 +612,16 @@ class Session {
* @param device The QDMI_Device handle to wrap.
*/
Device(Session::Token /* unused */, QDMI_Device device) : device_(device) {}
/**
* @brief Creates a Device object from a QDMI_Device handle.
* @param device The QDMI_Device handle to wrap.
* @return A Device object wrapping the given handle.
* @note This is a factory method for use in bindings where Token
* construction is not accessible.
*/
[[nodiscard]] static auto fromQDMIDevice(QDMI_Device device) -> Device {
return Device(Session::Token{}, device);
}
/// @returns the underlying QDMI_Device object.
[[nodiscard]] auto getQDMIDevice() const -> QDMI_Device { return device_; }
// NOLINTNEXTLINE(google-explicit-constructor, *-explicit-conversions)
Expand Down
5 changes: 4 additions & 1 deletion include/mqt-core/qdmi/Driver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -437,14 +437,17 @@ class Driver final {
* library.
* @param config Configuration for device session parameters.
*
* @return A pointer to the newly created device.
*
* @note This function is only available on non-Windows platforms.
*
* @throws std::runtime_error If the device cannot be initialized.
* @throws std::bad_alloc If memory allocation fails during the process.
*/
auto addDynamicDeviceLibrary(const std::string& libName,
const std::string& prefix,
const DeviceSessionConfig& config = {}) -> void;
const DeviceSessionConfig& config = {})
-> QDMI_Device;
#endif // _WIN32
/**
* @brief Allocates a new session.
Expand Down
13 changes: 8 additions & 5 deletions python/mqt/core/fomac.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -297,7 +297,7 @@ if sys.platform != "win32":
custom3: str | None = None,
custom4: str | None = None,
custom5: str | None = None,
) -> None:
) -> Device:
"""Load a dynamic device library into the QDMI driver.

This function loads a shared library (.so, .dll, or .dylib) that implements
Expand All @@ -320,19 +320,22 @@ if sys.platform != "win32":
custom4: Optional custom configuration parameter 4.
custom5: Optional custom configuration parameter 5.

Returns:
Device: The newly loaded device that can be used to create backends.

Raises:
RuntimeError: If library loading fails or configuration is invalid.

Examples:
Load a device library with configuration:

>>> import mqt.core.fomac as fomac
>>> fomac.add_dynamic_device_library(
>>> device = fomac.add_dynamic_device_library(
... "/path/to/libmy_device.so", "MY_DEVICE", base_url="http://localhost:8080", custom1="API_V2"
... )

Now the device is available in sessions:
Now the device can be used directly:

>>> session = fomac.Session()
>>> devices = session.get_devices()
>>> from mqt.core.plugins.qiskit import QDMIBackend
>>> backend = QDMIBackend(device=device)
"""
3 changes: 2 additions & 1 deletion src/qdmi/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -388,9 +388,10 @@ Driver::~Driver() {
auto Driver::addDynamicDeviceLibrary(const std::string& libName,
const std::string& prefix,
const DeviceSessionConfig& config)
-> void {
-> QDMI_Device {
devices_.emplace_back(std::make_unique<QDMI_Device_impl_d>(
std::make_unique<DynamicDeviceLibrary>(libName, prefix), config));
return devices_.back().get();
}
#endif

Expand Down
23 changes: 23 additions & 0 deletions test/qdmi/test_driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -705,6 +705,29 @@ TEST(DeviceSessionConfigTest, IdempotentLoadingWithDifferentConfigs) {
}
}
}

TEST(DynamicDeviceLibraryTest, addDynamicDeviceLibraryReturnsDevice) {
// Test that addDynamicDeviceLibrary returns a valid device pointer
if constexpr (DYN_DEV_LIBS.empty()) {
GTEST_SKIP() << "No dynamic device libraries configured for testing.";
}
auto& driver = qdmi::Driver::get();
for (const auto& [lib, prefix] : DYN_DEV_LIBS) {
const qdmi::DeviceSessionConfig config;
QDMI_Device device = nullptr;
ASSERT_NO_THROW(
{ device = driver.addDynamicDeviceLibrary(lib, prefix, config); });
ASSERT_NE(device, nullptr)
<< "addDynamicDeviceLibrary should return a non-null device pointer";

// Verify the device is valid by querying its name
size_t size = 0;
EXPECT_EQ(QDMI_device_query_device_property(
device, QDMI_DEVICE_PROPERTY_NAME, 0, nullptr, &size),
QDMI_SUCCESS);
EXPECT_GT(size, 0) << "Device should have a non-empty name";
}
}
#endif // _WIN32

INSTANTIATE_TEST_SUITE_P(
Expand Down
Loading