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 @@ -25,6 +25,7 @@ This project adheres to [Semantic Versioning], with the exception that minor rel

### Changed

- ♻️🏁 Remove Windows-specific restrictions for dynamic QDMI device library handling ([#1406]) ([**@burgholzer**])
- ♻️ Migrate Python bindings from `pybind11` to `nanobind` ([#1383]) ([**@denialhaag**], [**@burgholzer**])
- 📦️ Provide Stable ABI wheels for Python 3.12+ ([#1383]) ([**@burgholzer**], [**@denialhaag**])
- 🚚 Create dedicated `mqt.core.na` submodule to closely follow the structure of other submodules ([#1383]) ([**@burgholzer**])
Expand Down Expand Up @@ -286,6 +287,7 @@ _📚 Refer to the [GitHub Release Notes](https://github.com/munich-quantum-tool

<!-- PR links -->

[#1406]: https://github.com/munich-quantum-toolkit/core/pull/1406
[#1402]: https://github.com/munich-quantum-toolkit/core/pull/1402
[#1385]: https://github.com/munich-quantum-toolkit/core/pull/1385
[#1384]: https://github.com/munich-quantum-toolkit/core/pull/1384
Expand Down
8 changes: 1 addition & 7 deletions bindings/fomac/fomac.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,9 +420,7 @@ All authentication parameters are optional and can be provided as keyword argume
operation.def(nb::self != nb::self,
nb::sig("def __ne__(self, arg: object, /) -> bool"));

#ifndef _WIN32
// Module-level function to add dynamic device libraries on non-Windows
// systems
// Module-level function to add dynamic device libraries
m.def(
"add_dynamic_device_library",
[](const std::string& libraryPath, const std::string& prefix,
Expand Down Expand Up @@ -463,9 +461,6 @@ All authentication parameters are optional and can be provided as keyword argume

This function loads a shared library (.so, .dll, or .dylib) that implements a QDMI device interface and makes it available for use in sessions.

Note:
This function is only available on non-Windows platforms.

Args:
library_path: Path to the shared library file to load.
prefix: Function prefix used by the library (e.g., "MY_DEVICE").
Expand Down Expand Up @@ -499,7 +494,6 @@ This function loads a shared library (.so, .dll, or .dylib) that implements a QD

>>> from mqt.core.plugins.qiskit import QDMIBackend
>>> backend = QDMIBackend(device=device))pb");
#endif // _WIN32
}

} // namespace mqt
10 changes: 2 additions & 8 deletions include/mqt-core/qdmi/Driver.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -116,16 +116,14 @@ struct DeviceLibrary {
virtual ~DeviceLibrary() = default;
};

#ifndef _WIN32
/**
* @brief Definition of the dynamic device library.
* @details This class is used to load the QDMI device interface functions
* from a dynamic library at runtime. It inherits from DeviceLibrary and
* overrides the constructor and destructor to open and close the library.
* @note This class is only available on non-Windows platforms.
*/
class DynamicDeviceLibrary final : public DeviceLibrary {
/// @brief Handle to the dynamic library returned by `dlopen`.
/// @brief Handle to the dynamic library
void* libHandle_;

public:
Expand All @@ -145,7 +143,6 @@ class DynamicDeviceLibrary final : public DeviceLibrary {
*/
~DynamicDeviceLibrary() override;
};
#endif // _WIN32

// Macro to define a static library class that inherits from DeviceLibrary.
// It binds all device library functions to the functions of the static library.
Expand Down Expand Up @@ -428,7 +425,6 @@ class Driver final {

/// @brief Destructor for the Driver class.
~Driver();
#ifndef _WIN32
/**
* @brief Loads a dynamic device library and adds it to the driver.
*
Expand All @@ -439,16 +435,14 @@ class Driver final {
*
* @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 = {})
-> QDMI_Device;
#endif // _WIN32

/**
* @brief Allocates a new session.
* @see QDMI_session_alloc
Expand Down
3 changes: 0 additions & 3 deletions python/mqt/core/fomac.pyi
Original file line number Diff line number Diff line change
Expand Up @@ -364,9 +364,6 @@ def add_dynamic_device_library(

This function loads a shared library (.so, .dll, or .dylib) that implements a QDMI device interface and makes it available for use in sessions.

Note:
This function is only available on non-Windows platforms.

Args:
library_path: Path to the shared library file to load.
prefix: Function prefix used by the library (e.g., "MY_DEVICE").
Expand Down
30 changes: 21 additions & 9 deletions src/qdmi/Driver.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,9 @@
#include <utility>
#include <vector>

#ifndef _WIN32
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#endif // _WIN32

Expand Down Expand Up @@ -83,10 +85,19 @@ DEFINE_STATIC_LIBRARY(MQT_NA)
DEFINE_STATIC_LIBRARY(MQT_DDSIM)
DEFINE_STATIC_LIBRARY(MQT_SC)

#ifndef _WIN32
#ifdef _WIN32
#define DL_OPEN(lib) LoadLibraryA((lib))
#define DL_SYM(lib, sym) GetProcAddress(static_cast<HMODULE>((lib)), (sym))
#define DL_CLOSE(lib) FreeLibrary(static_cast<HMODULE>((lib)))
#else
#define DL_OPEN(lib) dlopen((lib), RTLD_NOW | RTLD_LOCAL)
#define DL_SYM(lib, sym) dlsym((lib), (sym))
#define DL_CLOSE(lib) dlclose((lib))
#endif

DynamicDeviceLibrary::DynamicDeviceLibrary(const std::string& libName,
const std::string& prefix)
: libHandle_(dlopen(libName.c_str(), RTLD_NOW | RTLD_LOCAL)) {
: libHandle_(DL_OPEN(libName.c_str())) {
if (libHandle_ == nullptr) {
throw std::runtime_error("Couldn't open the device library: " + libName);
}
Expand All @@ -98,7 +109,7 @@ DynamicDeviceLibrary::DynamicDeviceLibrary(const std::string& libName,
{ \
const std::string symbolName = std::string(prefix) + "_QDMI_" + #symbol; \
(symbol) = reinterpret_cast<decltype(symbol)>( \
dlsym(libHandle_, symbolName.c_str())); \
DL_SYM(libHandle_, symbolName.c_str())); \
if ((symbol) == nullptr) { \
throw std::runtime_error("Failed to load symbol: " + symbolName); \
} \
Expand Down Expand Up @@ -131,7 +142,7 @@ DynamicDeviceLibrary::DynamicDeviceLibrary(const std::string& libName,
LOAD_DYNAMIC_SYMBOL(device_session_query_operation_property)
// NOLINTEND(cppcoreguidelines-pro-type-reinterpret-cast)
} catch (const std::exception&) {
dlclose(libHandle_);
DL_CLOSE(libHandle_);
throw;
}
// initialize the device
Expand All @@ -145,10 +156,13 @@ DynamicDeviceLibrary::~DynamicDeviceLibrary() {
}
// close the dynamic library
if (libHandle_ != nullptr) {
dlclose(libHandle_);
DL_CLOSE(libHandle_);
}
}
#endif // _WIN32

#undef DL_OPEN
#undef DL_SYM
#undef DL_CLOSE
} // namespace qdmi

QDMI_Device_impl_d::QDMI_Device_impl_d(
Expand Down Expand Up @@ -384,7 +398,6 @@ Driver::~Driver() {
devices_.clear();
}

#ifndef _WIN32
auto Driver::addDynamicDeviceLibrary(const std::string& libName,
const std::string& prefix,
const DeviceSessionConfig& config)
Expand All @@ -393,7 +406,6 @@ auto Driver::addDynamicDeviceLibrary(const std::string& libName,
std::make_unique<DynamicDeviceLibrary>(libName, prefix), config));
return devices_.back().get();
}
#endif

auto Driver::sessionAlloc(QDMI_Session* session) -> int {
if (session == nullptr) {
Expand Down
81 changes: 39 additions & 42 deletions src/qdmi/na/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -151,47 +151,44 @@ if(NOT TARGET ${TARGET_NAME})
# in the tests
add_library(qdmi::mqt_na_device ALIAS ${TARGET_NAME})

# Do not build dynamic NA device on Windows because it cannot be used anyways in the current setup
if(NOT WIN32)
set(DYN_TARGET_NAME ${MQT_CORE_TARGET_NAME}-qdmi-na-device-dyn)
if(NOT TARGET ${DYN_TARGET_NAME})
# Set prefix for QDMI
set(QDMI_PREFIX "MQT_NA_DYN")
# Generate prefixed QDMI headers
generate_prefixed_qdmi_headers(${QDMI_PREFIX})
file(GLOB_RECURSE QDMI_HDRS ${CMAKE_CURRENT_BINARY_DIR}/include/mqt_na_dyn_qdmi/**.hpp)
# Add dynamic library target
add_library(${DYN_TARGET_NAME} SHARED)
add_dependencies(${DYN_TARGET_NAME} ${TARGET_NAME})
# add sources to target
target_sources(${DYN_TARGET_NAME} PRIVATE DynDevice.cpp)
# add headers using file sets
target_sources(
${DYN_TARGET_NAME}
PUBLIC FILE_SET
HEADERS
BASE_DIRS
${MQT_CORE_INCLUDE_BUILD_DIR}
${CMAKE_CURRENT_BINARY_DIR}/include
FILES
${DEVICE_HDR}
${MQT_CORE_INCLUDE_BUILD_DIR}/qdmi/na/Device.hpp
${QDMI_HDRS})
# add link libraries
target_link_libraries(
${DYN_TARGET_NAME}
PUBLIC qdmi::qdmi
PRIVATE ${TARGET_NAME} MQT::CoreQDMICommon MQT::ProjectOptions MQT::ProjectWarnings
spdlog::spdlog)
# set c++ standard
target_compile_features(${DYN_TARGET_NAME} PRIVATE cxx_std_20)
# set versioning information
set_target_properties(
${DYN_TARGET_NAME}
PROPERTIES VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
EXPORT_NAME CoreQDMINaDeviceDyn)
add_library(MQT::CoreQDMINaDeviceDyn ALIAS ${DYN_TARGET_NAME})
endif()
set(DYN_TARGET_NAME ${MQT_CORE_TARGET_NAME}-qdmi-na-device-dyn)
if(NOT TARGET ${DYN_TARGET_NAME})
# Set prefix for QDMI
set(QDMI_PREFIX "MQT_NA_DYN")
# Generate prefixed QDMI headers
generate_prefixed_qdmi_headers(${QDMI_PREFIX})
file(GLOB_RECURSE QDMI_HDRS ${CMAKE_CURRENT_BINARY_DIR}/include/mqt_na_dyn_qdmi/**.hpp)
# Add dynamic library target
add_library(${DYN_TARGET_NAME} SHARED)
add_dependencies(${DYN_TARGET_NAME} ${TARGET_NAME})
# add sources to target
target_sources(${DYN_TARGET_NAME} PRIVATE DynDevice.cpp)
# add headers using file sets
target_sources(
${DYN_TARGET_NAME}
PUBLIC FILE_SET
HEADERS
BASE_DIRS
${MQT_CORE_INCLUDE_BUILD_DIR}
${CMAKE_CURRENT_BINARY_DIR}/include
FILES
${DEVICE_HDR}
${MQT_CORE_INCLUDE_BUILD_DIR}/qdmi/na/Device.hpp
${QDMI_HDRS})
# add link libraries
target_link_libraries(
${DYN_TARGET_NAME}
PUBLIC qdmi::qdmi
PRIVATE ${TARGET_NAME} MQT::CoreQDMICommon MQT::ProjectOptions MQT::ProjectWarnings
spdlog::spdlog)
# set c++ standard
target_compile_features(${DYN_TARGET_NAME} PRIVATE cxx_std_20)
# set versioning information
set_target_properties(
${DYN_TARGET_NAME}
PROPERTIES VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
EXPORT_NAME CoreQDMINaDeviceDyn)
add_library(MQT::CoreQDMINaDeviceDyn ALIAS ${DYN_TARGET_NAME})
endif()
endif()
81 changes: 39 additions & 42 deletions src/qdmi/sc/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -151,47 +151,44 @@ if(NOT TARGET ${TARGET_NAME})
# in the tests
add_library(qdmi::mqt_sc_device ALIAS ${TARGET_NAME})

# Do not build dynamic SC device on Windows because it cannot be used anyways in the current setup
if(NOT WIN32)
set(DYN_TARGET_NAME ${MQT_CORE_TARGET_NAME}-qdmi-sc-device-dyn)
if(NOT TARGET ${DYN_TARGET_NAME})
# Set prefix for QDMI
set(QDMI_PREFIX "MQT_SC_DYN")
# Generate prefixed QDMI headers
generate_prefixed_qdmi_headers(${QDMI_PREFIX})
file(GLOB_RECURSE QDMI_HDRS ${CMAKE_CURRENT_BINARY_DIR}/include/mqt_sc_dyn_qdmi/**.hpp)
# Add dynamic library target
add_library(${DYN_TARGET_NAME} SHARED)
add_dependencies(${DYN_TARGET_NAME} ${TARGET_NAME})
# add sources to target
target_sources(${DYN_TARGET_NAME} PRIVATE DynDevice.cpp)
# add headers using file sets
target_sources(
${DYN_TARGET_NAME}
PUBLIC FILE_SET
HEADERS
BASE_DIRS
${MQT_CORE_INCLUDE_BUILD_DIR}
${CMAKE_CURRENT_BINARY_DIR}/include
FILES
${DEVICE_HDR}
${MQT_CORE_INCLUDE_BUILD_DIR}/qdmi/sc/Device.hpp
${QDMI_HDRS})
# add link libraries
target_link_libraries(
${DYN_TARGET_NAME}
PUBLIC qdmi::qdmi
PRIVATE ${TARGET_NAME} MQT::CoreQDMICommon MQT::ProjectOptions MQT::ProjectWarnings
spdlog::spdlog)
# set c++ standard
target_compile_features(${DYN_TARGET_NAME} PRIVATE cxx_std_20)
# set versioning information
set_target_properties(
${DYN_TARGET_NAME}
PROPERTIES VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
EXPORT_NAME CoreQDMIScDeviceDyn)
add_library(MQT::CoreQDMIScDeviceDyn ALIAS ${DYN_TARGET_NAME})
endif()
set(DYN_TARGET_NAME ${MQT_CORE_TARGET_NAME}-qdmi-sc-device-dyn)
if(NOT TARGET ${DYN_TARGET_NAME})
# Set prefix for QDMI
set(QDMI_PREFIX "MQT_SC_DYN")
# Generate prefixed QDMI headers
generate_prefixed_qdmi_headers(${QDMI_PREFIX})
file(GLOB_RECURSE QDMI_HDRS ${CMAKE_CURRENT_BINARY_DIR}/include/mqt_sc_dyn_qdmi/**.hpp)
# Add dynamic library target
add_library(${DYN_TARGET_NAME} SHARED)
add_dependencies(${DYN_TARGET_NAME} ${TARGET_NAME})
# add sources to target
target_sources(${DYN_TARGET_NAME} PRIVATE DynDevice.cpp)
# add headers using file sets
target_sources(
${DYN_TARGET_NAME}
PUBLIC FILE_SET
HEADERS
BASE_DIRS
${MQT_CORE_INCLUDE_BUILD_DIR}
${CMAKE_CURRENT_BINARY_DIR}/include
FILES
${DEVICE_HDR}
${MQT_CORE_INCLUDE_BUILD_DIR}/qdmi/sc/Device.hpp
${QDMI_HDRS})
# add link libraries
target_link_libraries(
${DYN_TARGET_NAME}
PUBLIC qdmi::qdmi
PRIVATE ${TARGET_NAME} MQT::CoreQDMICommon MQT::ProjectOptions MQT::ProjectWarnings
spdlog::spdlog)
# set c++ standard
target_compile_features(${DYN_TARGET_NAME} PRIVATE cxx_std_20)
# set versioning information
set_target_properties(
${DYN_TARGET_NAME}
PROPERTIES VERSION ${PROJECT_VERSION}
SOVERSION ${PROJECT_VERSION_MAJOR}.${PROJECT_VERSION_MINOR}
EXPORT_NAME CoreQDMIScDeviceDyn)
add_library(MQT::CoreQDMIScDeviceDyn ALIAS ${DYN_TARGET_NAME})
endif()
endif()
19 changes: 5 additions & 14 deletions test/python/fomac/test_fomac.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,14 +10,13 @@

from __future__ import annotations

import sys
import tempfile
from pathlib import Path
from typing import cast

import pytest

from mqt.core.fomac import Device, Job, ProgramFormat, Session
from mqt.core.fomac import Device, Job, ProgramFormat, Session, add_dynamic_device_library


def _get_devices() -> list[Device]:
Expand Down Expand Up @@ -803,15 +802,7 @@ def test_session_multiple_instances() -> None:
assert len(devices1) == len(devices2)


if sys.platform != "win32":
from mqt.core import fomac

def test_add_dynamic_device_library_exists() -> None:
"""Test that add_dynamic_device_library function exists on non-Windows platforms."""
assert hasattr(fomac, "add_dynamic_device_library")
assert callable(fomac.add_dynamic_device_library)

def test_add_dynamic_device_library_nonexistent_library() -> None:
"""Test that loading a non-existent library raises an error."""
with pytest.raises(RuntimeError):
fomac.add_dynamic_device_library("/nonexistent/lib.so", "PREFIX")
def test_add_dynamic_device_library_nonexistent_library() -> None:
"""Test that loading a non-existent library raises an error."""
with pytest.raises(RuntimeError):
add_dynamic_device_library("/nonexistent/lib.so", "PREFIX")
Loading
Loading