Skip to content
Draft
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
503 changes: 503 additions & 0 deletions .agent/plans/qdmi-integration-redesign.md

Large diffs are not rendered by default.

3 changes: 3 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -165,6 +165,9 @@ jobs:
setup-python: true
install-pkgs: "nanobind==2.13.0"
cpp-linter-extra-args: "-std=c++20"
# Private headers have no standalone compile command; their including
# translation units are still checked by clang-tidy.
cpp-linter-ignore-extra: "vendor/**|src/qdmi/DeviceApi.hpp"
setup-mlir: true
llvm-version: 22.1.7

Expand Down
3 changes: 3 additions & 0 deletions .pre-commit-config.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -74,13 +74,15 @@ repos:
rev: v1.48.0
hooks:
- id: typos
exclude: ^vendor/
priority: 3

## Check license headers
- repo: https://github.com/emzeat/mz-lictools
rev: v2.9.0
hooks:
- id: license-tools
exclude: ^vendor/
priority: 4

## Format BibTeX files with bibtex-tidy
Expand Down Expand Up @@ -136,6 +138,7 @@ repos:
hooks:
- id: clang-format
types_or: [c++, c, cuda]
exclude: ^vendor/
priority: 5
- id: clang-format
name: clang-format (TableGen)
Expand Down
38 changes: 38 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,11 @@ releases may include breaking changes.

- ✨ Add Python bindings for the MQT Compiler Collection ([#1815])
([**@burgholzer**], [**@denialhaag**])
- ✨ Add versioned JSON/TOML QDMI configuration discovery, relocatable built-in
manifests, and lazy `qdmi::DeviceManager`/`mqt.core.qdmi` APIs with per-device
sessions and failure isolation ([#1901]) ([**@burgholzer**])
- ✨ Add ID-keyed `DeviceManager::openAll`/`DeviceManager.open_all` results for
independently opening every configured device ([#1901]) ([**@burgholzer**])
- ✨ Add support for QDMI child devices to the driver and FoMaC libraries
([#1897]) ([**@burgholzer**])
- ✨ Add typed custom property and result queries to the C++ and Python FoMaC
Expand Down Expand Up @@ -64,6 +69,27 @@ releases may include breaking changes.

### Changed

- ♻️ Replace the legacy device-management layering with the
`qdmi::DeviceRegistry` → `qdmi::DeviceManager` → `qdmi::Device` object model.
Device libraries now load lazily, sessions are configured per device, child
objects retain their required runtime state, and definitions can be inspected
without executing provider code ([#1901]) ([**@burgholzer**])
- ♻️ Load provider libraries through one private `DeviceApi` that owns the
library and stores the exact QDMI function pointer types. The public C++ API
uses QDMI's existing device-status, job-status, and program-format enums
directly instead of redefining them, while client handles remain private
([#1901]) ([**@burgholzer**])
- 📝 Add binding-local docstrings for the complete public Python QDMI API
([#1901]) ([**@burgholzer**])
- ♻️ Use `device_id` for the Python `DeviceDefinition` property and constructor
argument, avoiding collisions with Python's built-in `id` while retaining `id`
in configuration and C++ ([#1901]) ([**@burgholzer**])
- 📦 Vendor the current toml++ single-header distribution used to parse project
configuration, including its embedded upstream license and pinned provenance
([#1901]) ([**@burgholzer**])
- ♻️ Migrate the Qiskit provider and neutral-atom adapter to lazily opened
configured QDMI devices and the unified `mqt.core.qdmi` API ([#1901])
([**@burgholzer**])
- ⬆️ Raise the minimum supported QDMI version to 1.3.2 ([#1897])
([**@burgholzer**])
- ⬆️ Require LLVM 22.1 for C++ library builds ([#1549]) ([**@burgholzer**],
Expand All @@ -73,13 +99,24 @@ releases may include breaking changes.

### Removed

- 🔥 Remove the legacy device-management namespace, Python module, global
session API, source/include/test trees, and compatibility CMake targets
([#1901]) ([**@burgholzer**])
- 🔥 Remove the QDMI client-interface implementation, the `Driver` singleton,
and `addDynamicDeviceLibrary` ([#1901]) ([**@burgholzer**])
- 🔥 Remove central built-in device enumeration and compiled-in library names;
generated relocatable manifest fragments now register built-in devices
([#1901]) ([**@burgholzer**])
- 🔥 Remove the density matrix support from the MQT Core DD package ([#1466])
([**@burgholzer**])
- 🔥 Remove `datastructures` (`ds`) (sub)library from MQT Core ([#1458])
([**@burgholzer**])

### Fixed

- 🐛 Reuse live QDMI provider libraries across device managers, reject operation
sites from another device session, and remove definitions replaced by disabled
runtime registrations ([#1901]) ([**@burgholzer**])
- 🐛 Fix QIR function names for adjoint gates ([#1830]) ([**@denialhaag**])

## [3.7.0] - 2026-07-09
Expand Down Expand Up @@ -638,6 +675,7 @@ changelogs._
<!-- PR links -->

[#1904]: https://github.com/munich-quantum-toolkit/core/pull/1904
[#1901]: https://github.com/munich-quantum-toolkit/core/pull/1901
[#1897]: https://github.com/munich-quantum-toolkit/core/pull/1897
[#1895]: https://github.com/munich-quantum-toolkit/core/pull/1895
[#1887]: https://github.com/munich-quantum-toolkit/core/pull/1887
Expand Down
3 changes: 2 additions & 1 deletion CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ include(PreventInSourceBuilds)
include(PackageAddTest)
include(Cache)
include(AddMQTCoreLibrary)
include(AddMQTQDMIDevice)

option(BUILD_MQT_CORE_BINDINGS "Build the MQT Core Python bindings" OFF)
if(BUILD_MQT_CORE_BINDINGS)
Expand Down Expand Up @@ -163,7 +164,7 @@ if(BUILD_MQT_CORE_MLIR)

if(BUILD_MQT_CORE_DOCUMENTATION)
add_custom_target(mqt-core-docs DEPENDS mlir-doc)
foreach(binding ir dd fomac na)
foreach(binding ir dd qdmi na)
add_dependencies(${MQT_CORE_TARGET_NAME}-${binding}-bindings mqt-core-docs)
endforeach()
endif()
Expand Down
236 changes: 236 additions & 0 deletions UPGRADING.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,242 @@ of changes including minor and patch releases, please refer to the

## [Unreleased]

### QDMI device management has been redesigned

MQT Core v4 replaces the former driver, QDMI client-interface, and compatibility
layers with one object model:

```text
configuration -> DeviceRegistry -> DeviceManager -> Device
|-> Site / Operation
`-> Job / child Device
```

Discovery is side-effect free. A provider library is loaded and a session is
initialized only when its stable device ID is passed to `open`. The QDMI
implementation is held by one private library/symbol object; public MQT objects
do not expose client handles. The supported QDMI version is selected centrally
in `cmake/ExternalDependencies.cmake`; incomplete provider implementations fail
when the selected device is opened.

| Concern | C++ | Python |
| --- | --- | --- |
| Discover registrations | `qdmi::DeviceRegistry` | `qdmi.DeviceManager.definitions` |
| Open one device | `qdmi::DeviceManager::open` | `DeviceManager.open` |
| Open every definition | `qdmi::DeviceManager::openAll` | `DeviceManager.open_all` |
| Device capabilities | `qdmi::Device` | `qdmi.Device` |
| Sites, operations, and jobs | `qdmi::Site`, `Operation`, `Job` | `Device.Site`, `Device.Operation`, `Job` |
| Neutral-atom view | `na::qdmi::Device` | `mqt.core.na.qdmi.Device` |
| CMake target | `MQT::CoreQDMI` | not applicable |

There is intentionally no compatibility module in v4. The following names and
entry points have been removed:

- `mqt.core.fomac`, `fomac::*`, their compatibility CMake target, and the
neutral-atom wrappers;
- the `Driver` singleton and `addDynamicDeviceLibrary`;
- MQT Core's QDMI client C functions and public client handles;
- global authentication/session configuration.

Replace old includes with `qdmi/Device.hpp` or `qdmi/DeviceManager.hpp`, and
link `MQT::CoreQDMI`.

The C++ object model uses QDMI's own enum types rather than defining parallel
MQT enums. Pass and compare the QDMI constants directly:

```cpp
device.submitJob(program, QDMI_PROGRAM_FORMAT_QASM3, shots);
if (job.check() == QDMI_JOB_STATUS_DONE) {
// ...
}
```

`Device::getStatus()`, `Job::check()`, and the program-format methods therefore
return `QDMI_Device_Status`, `QDMI_Job_Status`, and `QDMI_Program_Format`
respectively. Python retains the convenient `Device.Status`, `Job.Status`, and
`ProgramFormat` names, but those bindings represent the QDMI enums themselves.

#### Before and after

Python code that used the removed compatibility module:

```python
# MQT Core 3.x
from mqt.core import fomac

session = fomac.Session()
device = session.get_devices()[0]
```

should select a configured stable ID:

```python
# MQT Core 4.x
from mqt.core import qdmi

manager = qdmi.DeviceManager()
device = manager.open("mqt.ddsim.default")
```

The equivalent C++ migration is:

```cpp
// MQT Core 3.x
#include "fomac/FoMaC.hpp"
fomac::Session session;
const auto device = session.getDevices().front();
```

```cpp
// MQT Core 4.x
#include "qdmi/DeviceManager.hpp"
qdmi::DeviceManager manager;
auto device = manager.open("mqt.ddsim.default");
```

#### Opening devices

Code that needs every configured device can use the bulk-open result. Each
definition is opened independently, and errors remain associated with stable
device IDs.

Python:

```python
from mqt.core import qdmi

result = qdmi.DeviceManager().open_all()
for device_id, device in result.devices.items():
print(device.name())
for device_id, error in result.errors.items():
print(f"{device_id} could not be opened: {error}")
```

C++:

```cpp
#include "qdmi/DeviceManager.hpp"

const auto result = qdmi::DeviceManager().openAll();
for (const auto& [id, device] : result.devices) {
// Use the opened device.
}
for (const auto& [id, error] : result.errors) {
// Report or otherwise handle this provider failure.
}
```

Use `definitions` and `open` instead when selection must happen before any
provider library is loaded. In Python the stable identifier property is named
`device_id`; configuration files and C++ continue to use `id`.

#### Per-device session parameters

Authentication and provider settings no longer belong to a process-wide session.
Put defaults on each device definition and override them for one `open` call:

```python
from mqt.core import qdmi

parameters = qdmi.SessionParameters()
parameters.token = obtain_token()
parameters.custom1 = "provider-specific-value"

device = qdmi.DeviceManager().open(
"vendor.qpu.production",
session_overrides=parameters,
)
```

```cpp
qdmi::SessionParameters parameters;
parameters.token = obtainToken();
parameters.custom1 = "provider-specific-value";

auto device = manager.open("vendor.qpu.production", parameters);
```

QDMI has no standard project-ID session parameter. Provider-specific project or
organization identifiers must use the custom slot documented by that provider.
MQT Core no longer accepts a `project_id` value that it cannot forward.

Multiple definitions may refer to the same shared library while using
independent session parameters. Open devices, child devices, sites, operations,
and jobs share the required internal state, so these objects remain valid after
the manager that opened them is destroyed. Unregistering a definition likewise
does not invalidate already opened devices.

#### Registering devices with configuration

Device registration is versioned and keyed by a stable, unique `id`:

```json
{
"schema-version": 1,
"qdmi": {
"devices": [
{
"id": "vendor.qpu.production",
"library": "./libvendor-qdmi-device.so",
"prefix": "VENDOR",
"enabled": true,
"session": {
"base-url": "https://qpu.example",
"auth-file": "./credentials.json"
}
}
]
}
}
```

Relative paths are resolved against the file containing them. Configuration
layers are merged field by field by `id`, and `enabled = false` masks a
lower-precedence definition. Duplicate IDs within one source, unknown keys,
invalid types, and incomplete enabled definitions are errors with source and
configuration-path diagnostics.

The precedence order, from lowest to highest, is:

1. packaged manifest fragments;
2. system `mqt-core.json`;
3. user or XDG `mqt-core.json`;
4. the nearest project `mqt-core.json` or `[tool.mqt-core.qdmi]` table in
`pyproject.toml`;
5. `MQT_CORE_QDMI_CONFIG_JSON`;
6. C++ or Python runtime overrides.

A dedicated `mqt-core.json` wins over `pyproject.toml` in the same directory.
`MQT_CORE_QDMI_CONFIG_FILE` or `ConfigOptions.explicitFile` replaces the system,
user, and project layers while retaining packaged devices. Set `isolated` to
exclude packaged manifests as well. See the
{doc}`configuration reference <qdmi/configuration>` for complete schemas and
administrator, project, environment, and runtime examples.

Static C++ executables must set `ConfigOptions.configRoot`: unlike a shared
library or Python extension, a fully static executable has no portable module
location from which built-in manifests can be discovered.

#### QDMI child devices

`qdmi::Device::getChildDevices()` and `Device.child_devices()` return direct
child devices as ordinary device objects. Each child owns the provider session
and library state required by its QDMI handle; retaining a child is therefore
safe even after discarding its parent or manager. Providers without child-device
support return an empty list.

#### Qiskit integration

`QDMIProvider` now uses `DeviceManager` internally and creates a backend for
every successfully opened, convertible device. Existing code that only creates
`QDMIProvider()` does not need to manage device objects itself. Authentication
keyword arguments are converted to per-open `SessionParameters`.

Code that directly constructs a `QDMIBackend` should pass a
`mqt.core.qdmi.Device` returned by `DeviceManager.open`. Tests and downstream
integrations that mocked global device enumeration should instead inject runtime
definitions or mock `DeviceManager.definitions` and `DeviceManager.open`.

### MLIR enabled by default for C++ and Python package builds

The MLIR-based functionality within MQT Core has long been experimental and
Expand Down
2 changes: 1 addition & 1 deletion bindings/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@

add_subdirectory(ir)
add_subdirectory(dd)
add_subdirectory(fomac)
add_subdirectory(qdmi)
add_subdirectory(na)

if(BUILD_MQT_CORE_MLIR)
Expand Down
Loading
Loading