# MQT Core’s QDMI Driver Implementation

## Objective

A QDMI Driver manages the communication between QDMI devices, such as
[MQT Core’s SC QDMI Device](sc_device.html.md) or
[MQT Core’s DDSIM QDMI Device](ddsim_device.html.md), and QDMI clients, see the
[QDMI specification](https://munich-quantum-software-stack.github.io/QDMI/).
It is responsible for loading the device, forwarding requests from the client to
the device, and sending back the results. MQT Core’s QDMI Driver,
[`qdmi::Driver`](cpp/classqdmi_1_1Driver.html), comes with several preloaded devices when the
bundled devices are enabled. Other devices can be loaded dynamically at runtime
via [`qdmi::Driver::registerDevice`](cpp/classqdmi_1_1Driver.html#a5a64e968298301a89404c66150f743d6) and
[`qdmi::Driver::open`](cpp/classqdmi_1_1Driver.html#aebcbeac2d27d74e573eab0aca4358d3d). Built-in and external devices can also be
registered through
[versioned QDMI device configuration](configuration.html.md).

The driver shares a loaded provider across path aliases with the same symbol
prefix and retains it for the process lifetime. Closing a device session frees
that session without finalizing the provider while another session may use it.
Initialization is serialized within each loaded module. A slow provider
initializer does not hold the driver cache lock while other modules are opened.

## Building the Bundled Devices

Standalone MQT Core builds include the DDSIM and superconducting QDMI device
libraries by default. When MQT Core is embedded in another CMake project using
`FetchContent` or `add_subdirectory`, these device libraries are
disabled by default so the consumer does not build implementations it may not
use. They can be selected independently before making MQT Core available:

- `BUILD_MQT_CORE_QDMI_DDSIM_DEVICE`
- `BUILD_MQT_CORE_QDMI_SC_DEVICE`

The DDSIM device uses the MLIR compiler infrastructure for both OpenQASM and QIR
programs. Its target is skipped when `BUILD_MQT_CORE_MLIR` is `OFF`,
while the QDMI driver and superconducting device remain available.

For example, an embedded simulator consumer can enable only the DDSIM device,
while CUDA-Q can enable the DDSIM and superconducting devices used by its
integration tests.

The QDMI driver and QDMI libraries are available independently. Device-free
builds can register external device libraries through
[QDMI device configuration](configuration.html.md). C++ test builds require every
bundled device available in the selected build configuration.

## Python Bindings

The QDMI interface is the low-level contract implemented by a QDMI device. The
MQT Core QDMI driver loads device libraries and implements the QDMI client
interface. The C++ QDMI library adds owning wrappers for QDMI devices, sites,
operations, and jobs. The Python module exposes these QDMI entities through
[`mqt.core.qdmi`](../api/mqt/core/qdmi/index.html.md#module-mqt.core.qdmi). Its [`mqt.core.qdmi.driver`](../api/mqt/core/qdmi/driver/index.html.md#module-mqt.core.qdmi.driver) submodule provides
device discovery, registration, and opening.

Native device opening, property queries, job calls, and compiler-target
snapshots release Python’s GIL. Other Python threads can run while a provider
waits for a remote response. Python argument and result conversion still holds
the GIL. Concurrent calls into a shared device or job must satisfy the
provider’s thread safety contract; releasing the GIL does not serialize provider
access.

### Custom job parameter types

The `custom1` through `custom5` arguments of
[`mqt.core.qdmi.Device.submit_job()`](../api/mqt/core/qdmi/index.html.md#mqt.core.qdmi.Device.submit_job) and
[`mqt.core.mlir.submit_program()`](../api/mqt/core/mlir/index.html.md#mqt.core.mlir.submit_program) use the device’s documented types.
Strings include a null terminator; `bool`, `int`, and `float` use C++ `bool`,
`int`, and `double`. For a device-defined binary payload, pass nonempty `bytes`:

```python
job = device.submit_job(program, program_format, custom1=b"\x01\x00\xff")
```

QDMI copies raw bytes without a terminator; empty payloads raise `ValueError`.
The device defines their meaning and size. In C++, pass a
`std::span<const std::byte>` whose buffer stays valid until submission returns.
The bytes describe a local QDMI ABI value, not a network encoding.

## Usage

The following example opens each registered device by its stable ID.

```ipython3
from mqt.core.qdmi.driver import open_device, registered_device_ids

for device_id in registered_device_ids():
    device = open_device(device_id)
    print(device.name())
```

```myst-ansi
MQT Core DDSIM QDMI Device
```
