Metadata-Version: 2.4
Name: mqss-qdmi-mqv
Version: 0.1.0
Summary: Implementation of a MQV QDMI device
Author: Munich Quantum Valley
License-Expression: Apache-2.0 WITH LLVM-exception
License-File: LICENSE
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Information Technology
Classifier: Intended Audience :: Science/Research
Classifier: Natural Language :: English
Classifier: Operating System :: POSIX :: Linux
Classifier: Programming Language :: C++
Classifier: Programming Language :: Python
Classifier: Topic :: Scientific/Engineering :: Quantum Computing
Classifier: Typing :: Typed
Requires-Python: >=3.11
Description-Content-Type: text/markdown

# MQV QDMI Device

<!-- [DOXYGEN MAIN] -->

A C++20 library that implements the QDMI 1.4 device interface for an MQSS
**MQP-API**: it lists an MQP-API resource (qubits, native gates, coupling map,
status) as a QDMI device and runs QDMI jobs on it as MQP-API jobs, through
[MQSS-Client](https://github.com/Munich-Quantum-Software-Stack/MQSS-Client).

<!-- [DOXYGEN MAIN] -->

**Status: prototype** for the SC26 openQSE demo. Generated from the official
[QDMI](https://github.com/Munich-Quantum-Software-Stack/QDMI) v1.4.0 device
template; the MQP-API logic is ported from the LRZ network-device proof of
concept to QDMI 1.4. Tested against the bundled mock MQP-API and, read-only,
against an MQP-API test instance.

## How it works

| QDMI                                                                | MQP-API (via MQSS-Client, REST mode)                          |
| ------------------------------------------------------------------- | ------------------------------------------------------------- |
| Session parameter `BASEURL`                                         | MQP-API base URL, e.g. `https://host:4000/v1/`                |
| Session parameter `TOKEN`                                           | Sent as `Authorization: Bearer <token>`                       |
| Session parameter `CUSTOM1`                                         | MQP-API resource name, e.g. `MAQCS`                           |
| Session parameter `CUSTOM2`                                         | Job field `no_modify`, `true` (default) or `false`            |
| Device properties (qubits, sites, coupling map, operations, status) | `GET resources/<name>`, falling back to `GET resources`       |
| Job with one OpenQASM 2 or 3 program                                | `POST job`, then `GET job/<id>/status`, `GET job/<id>/result` |
| Results                                                             | Histogram (keys and values)                                   |

- Each of `BASEURL`, `TOKEN`, `CUSTOM1` and `CUSTOM2` falls back to an
  environment variable if not set: `MQV_QDMI_BASEURL`, `MQV_QDMI_TOKEN`,
  `MQV_QDMI_RESOURCE`, `MQV_QDMI_NO_MODIFY`.
- `no_modify` defaults to `true`: QDMI clients such as MQT Core's Qiskit backend
  already send programs in the device's native gates, and MQSS currently routes
  OpenQASM jobs only with `no_modify=true`.
- Results are read with the device's own `GET job/<id>/result`, which accepts
  histograms as `{"00": n}`, a list of those, or wrapped as `{"counts": {...}}`,
  as MQP-API workers return them; MQSS-Client's parser accepts only the first
  two.
- Session initialization does not contact the MQP-API; the resource is loaded on
  first use, and the status is probed live (cached for a few seconds).
- Coupling map: the resource's connectivity; if MQSS-Client can't parse it (it
  reads only `[a, b]` pairs, not `(a, b)` as e.g. MAQCS lists them), the pairs
  of the explicitly placed two-qubit gates; if there are none (connectivity
  `None`, e.g. AQT20), all-to-all. An instruction listed as `{None: None}`
  applies to any qubits (pairs).
- If `CURL_CA_BUNDLE` is unset, the device sets it to the system CA bundle,
  since MQSS-Client otherwise disables libcurl's default bundle for HTTPS.
- The default device ID is `mqv.default` (CMake cache variable
  `MQV_QDMI_DEVICE_ID`); a driver can expose the same library under other IDs,
  e.g. one per resource. The QDMI symbol prefix is `MQV`.

## Loading it with MQT Core's QDMI driver (e.g. openQSE/QFw)

For openQSE/QFw: QFw opens a device ID with the site's MQP-API base URL and a
token, and sends OpenQASM 3 with a declared register (`qubit[n] q;`) for MAQCS;
Qiskit's `qiskit.qasm3.dumps` writes it once the circuit's layout is removed.

The device ships a manifest, `mqv-qdmi-device.qdmi.json`, installed next to the
library, in the format MQT Core's built-in driver reads (as the IQM and Amazon
Braket QDMI devices do):

| Device ID                                                                                                | Session (fixed by the manifest)                                                  |
| -------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------- |
| `mqv.default`                                                                                            | none: the client sets `BASEURL`, `TOKEN` and `CUSTOM1` (resource)                |
| `mqv.aqt20`, `mqv.maqcs`, `mqv.muniqc-atoms20`, `mqv.q20`, `mqv.q5`, `mqv.qexa20`, `mqv.qlm`, `mqv.wmi3` | `CUSTOM1` (one ID per MQP-API resource): the client passes `BASEURL` and `TOKEN` |

The resource list is a CMake cache variable (`MQV_QDMI_MANIFEST_RESOURCES`). The
MQP-API endpoint is site-specific, so the manifest carries no base URL; a site
build can fix one with `MQV_QDMI_MANIFEST_BASEURL`.

- **Wheel:** `pip install mqss-qdmi-mqv`; the entry point
  `mqt.core.qdmi.manifests` makes MQT Core find the manifest, with no further
  configuration. The wheel bundles `librabbitmq`.
- **CMake install:** register the installed manifest, e.g.
  `MQT_CORE_QDMI_CONFIG_FILE=<prefix>/lib64/mqv-qdmi-device.qdmi.json`.

```python
from mqt.core.qdmi import builtin_driver

device = builtin_driver.open_device("mqv.maqcs", base_url="https://<mqp-api-host>/v1/", token="<MQP-API token>")
```

## Build and test

```console
cmake -S . -B build -DCMAKE_BUILD_TYPE=Release
cmake --build build
ctest --test-dir build -E MQVDeviceE2E      # unit and QDMI conformance tests
test/mock_mqp/run_e2e.sh build              # end-to-end tests against the mock MQP-API
```

The end-to-end script starts the mock MQP-API (`test/mock_mqp/mqp_mock.py`,
needs [uv](https://docs.astral.sh/uv/)) and runs the suite five times: a
standard resource, the same with `GET resources/<name>` denied, a resource
encoded with connectivity `None` and `{None: None}` instructions, a MAQCS-style
resource with `(a, b)` connectivity, and the same answering as MAQCS does today
(results as `[{"counts": ...}]`, jobs with `no_modify=false` cancelled). The
mock's token is a fixed test value, not a credential.

Runtime dependencies: libcurl, rabbitmq-c (`librabbitmq`, linked by
MQSS-Client). `-DMQV_QDMI_BUNDLE_RABBITMQ=ON` installs `librabbitmq.so.4` next
to the device library (RPATH `$ORIGIN`) for systems without rabbitmq-c.

A wheel is built with `uv build --wheel` (scikit-build-core; tag `py311`, also
for Python 3.12). Portable `manylinux` wheels, as published on PyPI, are built
with cibuildwheel (`.github/workflows/wheels.yml`).

## Tested environments

| Platform                                                    | What                                                                                                                                                        |
| ----------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
| macOS arm64                                                 | unit/conformance and end-to-end tests; wheel and CMake install loaded by MQT Core 4.1.1's built-in driver (entry-point discovery, Bell circuit on the mock) |
| Rocky Linux 10.1 x86_64 (base of the openQSE testbed image) | unit/conformance and end-to-end tests; deployed in an openQSE testbed, reading all resources of an MQP-API test instance                                    |
| Linux aarch64, manylinux wheels                             | not yet                                                                                                                                                     |

Pinned dependencies: QDMI v1.4.0 (`6db03d8`), MQSS-Client (`08cc79b`), both
fetched by CMake. A Linux wheel built directly on a distribution gets a plain
`linux_x86_64` tag, not a portable `manylinux` one.

## Documentation

The C++ API reference can be built with [Doxygen](https://doxygen.nl/):

```console
cmake -S . -B build -DBUILD_MQV_QDMI_DOCS=ON
cmake --build build --target mqv_qdmi_device_docs
```

The documentation is generated in `build/docs/`.

## License

Apache License 2.0 with LLVM Exceptions, as the QDMI template; the license text
is in the `LICENSE` file, also included in the wheel.
