Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
e66e481
✨ Integrate MQT Core QDMI device configuration
burgholzer Jul 16, 2026
3f9f3be
♻️ Simplify MQT Core QDMI integration
burgholzer Jul 16, 2026
759c1b7
♻️ Finalize MQT Core QDMI integration
burgholzer Jul 16, 2026
769ff9a
📝 Correct QDMI manifest discovery guidance
burgholzer Jul 16, 2026
ab67ead
🐛 Preserve registered IQM endpoint defaults
burgholzer Jul 23, 2026
658b205
🐛 Treat empty IQM endpoints as unset
burgholzer Jul 24, 2026
5ef748f
⬆️ Pin the published MQT Core integration
burgholzer Jul 24, 2026
d728c43
♻️ Align IQM integration with QDMI device APIs
burgholzer Jul 24, 2026
e712504
⬆️ Pin the published MQT Core revision
burgholzer Jul 24, 2026
2221991
Merge origin/main into configurable QDMI integration
burgholzer Jul 25, 2026
0451f20
📝 Add changelog attribution guidance
burgholzer Jul 25, 2026
7f1d148
🔀 Merge origin/main into QDMI Core integration
burgholzer Jul 27, 2026
de8fce7
⬆️ Repin the published MQT Core revision
burgholzer Jul 27, 2026
926ebbf
♻️ Adopt neutral QDMI device metadata
burgholzer Jul 27, 2026
3efa09d
♻️ Use the QDMI device target helper
burgholzer Jul 27, 2026
35a28a3
🔧 Set up MLIR for MQT Core source builds
burgholzer Jul 27, 2026
3d71892
🐛 Scope documentation build settings to IQM QDMI
burgholzer Jul 29, 2026
ed36153
⬆️ Repin the rebased QDMI integration
burgholzer Jul 29, 2026
3ea088b
Merge origin/main into configurable QDMI integration
burgholzer Jul 29, 2026
51fae05
⬆️ Repin the merged-MLIR MQT Core revision
burgholzer Jul 29, 2026
4d2f27a
🐛 Isolate the documentation build directory
burgholzer Jul 29, 2026
aa4eadb
♻️ Export the stable IQM device metadata
burgholzer Jul 29, 2026
316a452
⬆️ Track the MQT Core 3.8 backport
burgholzer Jul 29, 2026
ca8cf25
⬆️ Use the released MQT Core 3.8.0 package
burgholzer Jul 30, 2026
980cc4a
♻️ Use released QDMI device metadata
burgholzer Jul 30, 2026
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
3 changes: 3 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,6 +93,9 @@ explicit intent, valid credentials, and an appropriate target.
- MUST follow existing patterns in neighboring source and test files.
- MUST document new user-facing behavior and update `CHANGELOG.md` for
noteworthy user-facing changes.
- MUST format changelog entries with the pull request reference and every
contributing author, for example `([#123]) ([**@username**])`, and define the
corresponding links at the bottom of `CHANGELOG.md`.
- MUST fold additions, enhancements, or bug fixes for features that are still in
`[Unreleased]` into the existing `[Unreleased]` `CHANGELOG.md` entry for that
feature (appending the PR number) rather than creating separate bullet points
Expand Down
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ releases may include breaking changes.

- ✨ Validate IQM backend and target-QC availability once per node for each
Slurm job step before launching tasks ([#136]) ([**@burgholzer**])
- ✨ Export the stable IQM device ID and symbol prefix on the installed CMake
target so MQT Core can synthesize relocatable manifests ([#140])
([**@burgholzer**])
- ✨ Add `iqm.qdmi.offloader` module exposing programmatic `sample` and
`estimate` functions (including `qc_id`/`qc_alias` SPANK device-selection
parameters) for Slurm job submissions ([#104], [#130], [#133])
Expand All @@ -33,11 +36,15 @@ releases may include breaking changes.
environment variables, bound each launch-time request to 30 seconds by
default, emit task diagnostics once per node and step, and require Slurm 20.02
or newer ([#136]) ([**@burgholzer**])
- ⬆️ Update `mqt-core` to version 3.8.0 and use its stable device registry with
per-backend device sessions ([#140]) ([**@burgholzer**])
- ♻️ Further align C++ HTTP and authentication handling with [cpr] abstractions
([#122]) ([**@burgholzer**])

### Fixed

- 🩹 Preserve a configured `iqm.default` endpoint unless `IQMBackend` receives
an explicit or environment-provided base URL ([#140]) ([**@burgholzer**])
- 🩹 Fix rate limit handling and retry logic for API requests ([#122])
([**@burgholzer**])

Expand Down Expand Up @@ -126,6 +133,7 @@ Compatible with QDMI `v1.3.0`.
<!-- PR links -->

[#147]: https://github.com/iqm-finland/QDMI-on-IQM/pull/147
[#140]: https://github.com/iqm-finland/QDMI-on-IQM/pull/140
[#136]: https://github.com/iqm-finland/QDMI-on-IQM/pull/136
[#134]: https://github.com/iqm-finland/QDMI-on-IQM/pull/134
[#133]: https://github.com/iqm-finland/QDMI-on-IQM/pull/133
Expand Down
11 changes: 11 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,17 @@ endif()

# Add the tests
if(BUILD_IQM_QDMI_TESTS)
get_target_property(IQM_QDMI_DEVICE_ID ${QDMI_TARGET_NAME} QDMI_DEVICE_ID)
get_target_property(IQM_QDMI_DEVICE_PREFIX ${QDMI_TARGET_NAME}
QDMI_DEVICE_PREFIX)
if(NOT IQM_QDMI_DEVICE_ID STREQUAL "iqm.default")
message(
FATAL_ERROR "The IQM QDMI target does not export its stable device ID")
endif()
if(NOT IQM_QDMI_DEVICE_PREFIX STREQUAL "${QDMI_PREFIX}")
message(FATAL_ERROR "The IQM QDMI target does not export its symbol prefix")
endif()

enable_testing()
include(GoogleTest)
add_subdirectory(test)
Expand Down
11 changes: 7 additions & 4 deletions docs/conf.py
Original file line number Diff line number Diff line change
Expand Up @@ -143,13 +143,16 @@
breathe_projects = {"QDMI on IQM": "_build/doxygen/xml"}
breathe_default_project = "QDMI on IQM"

# get SKBUILD_BUILD_DIR from the environment as the stem for the documentation
# Get the IQM QDMI build directory as the stem for the generated headers.
try:
skbuild_build_dir = os.environ["SKBUILD_BUILD_DIR"]
iqm_qdmi_build_dir = os.environ["IQM_QDMI_BUILD_DIR"]
except KeyError:
msg = "No build dir set in environment. Consider setting SKBUILD_BUILD_DIR."
msg = "No build dir set in environment. Consider setting IQM_QDMI_BUILD_DIR."
raise RuntimeError(msg) from None

breathe_projects_source = {
"QDMI on IQM": (f"../{skbuild_build_dir}/src/include/iqm_qdmi", ["constants.h", "types.h", "export.h", "device.h"])
"QDMI on IQM": (
f"../{iqm_qdmi_build_dir}/src/include/iqm_qdmi",
["constants.h", "types.h", "export.h", "device.h"],
)
}
2 changes: 1 addition & 1 deletion docs/dependencies.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ These are installed when users request the `qiskit` extra via

| Dependency | Version | License | Purpose |
| :--------- | :------ | :---------- | :----------------------------------------------------------------------- |
| [MQT Core] | ~=3.7.0 | MIT License | QDMI-aware Qiskit provider, backend, sampler, and estimator integrations |
| [MQT Core] | ~=3.8.0 | MIT License | QDMI-aware Qiskit provider, backend, sampler, and estimator integrations |
| [Qiskit] | ≥1.1 | Apache-2.0 | Quantum circuit construction, transpilation, and primitive interfaces |

## End-to-End Example Dependencies
Expand Down
11 changes: 8 additions & 3 deletions docs/qiskit.md
Original file line number Diff line number Diff line change
Expand Up @@ -44,10 +44,15 @@ result = backend.run(transpiled_qc, shots=128).result()
print(result.get_counts())
```

If no explicit arguments are provided, the wrapper resolves `IQM_BASE_URL`,
Explicit arguments to `IQMBackend(...)` take precedence over `IQM_BASE_URL`,
`IQM_TOKEN`, `IQM_TOKENS_FILE`, `IQM_QC_ID`, and `IQM_QC_ALIAS` from the
environment. Alternatively, pass any of those values directly to
`IQMBackend(...)`.
environment. For the endpoint, an explicit argument takes precedence over
`IQM_BASE_URL`, which takes precedence over the registered device default.

The wrapper registers the packaged IQM QDMI device as a fallback under the
stable ID `iqm.default` with the standard Resonance endpoint as its default. An
existing configured definition with that ID is preserved, including its
endpoint. Every backend opens a fresh device session with its own configuration.

## Sampler and Estimator Primitives

Expand Down
23 changes: 23 additions & 0 deletions docs/usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -194,6 +194,29 @@ For the REST API endpoints called during each of these steps, see
[IQM API Usage in QDMI Device Implementation](contributing.md#iqm-api-usage-in-qdmi-device-implementation)
in the Contributing guide.

## Using the Device with MQT Core

The installed CMake target publishes the stable ID `iqm.default` and the `IQM`
symbol prefix. Applications that link the MQT Core driver statically can use its
runtime-copy helper to synthesize a relocatable manifest and colocate it with
the device library beside the executable:

```cmake
find_package(mqt-core 3.8 CONFIG REQUIRED)
find_package(iqm-qdmi-device CONFIG REQUIRED)

add_executable(my-application main.cpp)
target_link_libraries(my-application PRIVATE MQT::CoreFoMaC)
mqt_copy_qdmi_runtime(my-application iqm-qdmi-device)
```

For dynamically linked consumers, automatic discovery searches beside the driver
library rather than the executable. Such consumers must place the generated
manifest in a discovered location or select a complete configuration with
`MQT_CORE_QDMI_CONFIG_FILE`. Python integrations instead register the same
stable ID directly from the packaged library path and open a fresh device
session for each backend.

## Running Jobs via Slurm

For Slurm-backed native job submission, see the
Expand Down
10 changes: 7 additions & 3 deletions noxfile.py
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,12 @@ def docs(session: nox.Session) -> None:
"run",
"--no-build-isolation-package",
"iqm-qdmi",
"--config-settings-package",
"iqm-qdmi:cmake.define.BUILD_IQM_QDMI_DOCS=ON",
"--config-settings-package",
"iqm-qdmi:build.targets=iqm-qdmi-device;iqm-qdmi-device-docs",
"--config-settings-package",
"iqm-qdmi:build-dir=build/docs",
"--group",
"docs",
"--verbose",
Expand All @@ -187,10 +193,8 @@ def docs(session: nox.Session) -> None:
f"docs/_build/{args.builder}",
*posargs,
env={
"IQM_QDMI_BUILD_DIR": "build/docs",
"UV_PROJECT_ENVIRONMENT": session.virtualenv.location,
"SKBUILD_CMAKE_DEFINE": "BUILD_IQM_QDMI_DOCS=ON",
"SKBUILD_BUILD_TARGETS": "iqm-qdmi-device;iqm-qdmi-device-docs",
"SKBUILD_BUILD_DIR": "build/docs",
},
)

Expand Down
5 changes: 2 additions & 3 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ classifiers = [

[project.optional-dependencies]
qiskit = [
"mqt-core~=3.7.0",
"mqt-core~=3.8.0",
"qiskit[qasm3-import]>=1.1",
"qiskit-algorithms>=0.4.0",
"numpy>=2.1; python_version >= '3.13'",
Expand All @@ -64,7 +64,7 @@ iqm-estimator = "iqm.qdmi.estimator:main"

[dependency-groups]
qiskit = [
"mqt-core~=3.7.0",
"mqt-core~=3.8.0",
"qiskit[qasm3-import]>=1.1",
"qiskit-algorithms>=0.4.0",
"numpy>=2.1; python_version >= '3.13'",
Expand Down Expand Up @@ -92,7 +92,6 @@ dev = [
"nox>=2026.4.10",
]


[tool.scikit-build]
minimum-version = "build-system.requires"
cmake.version = "CMakeLists.txt"
Expand Down
5 changes: 5 additions & 0 deletions python/iqm/qdmi/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,16 @@

__all__ = [
"IQM_QDMI_CMAKE_DIR",
"IQM_QDMI_DEVICE_ID",
"IQM_QDMI_INCLUDE_DIR",
"IQM_QDMI_LIBRARY_PATH",
"IQM_QDMI_PREFIX",
"__version__",
]

IQM_QDMI_DEVICE_ID = "iqm.default"
IQM_QDMI_PREFIX = "IQM"


def __dir__() -> list[str]:
return __all__
Expand Down
31 changes: 21 additions & 10 deletions python/iqm/qdmi/qiskit.py
Original file line number Diff line number Diff line change
Expand Up @@ -20,10 +20,11 @@
from __future__ import annotations

import os
from pathlib import Path
from typing import Any

try:
from mqt.core.fomac import add_dynamic_device_library
from mqt.core.fomac import DeviceDefinition, open_device, register_device_if_absent
from mqt.core.plugins.qiskit.backend import QDMIBackend
from mqt.core.plugins.qiskit.estimator import QDMIEstimator
from mqt.core.plugins.qiskit.sampler import QDMISampler
Expand All @@ -34,10 +35,12 @@
)
raise ImportError(msg) from e

from ._paths import IQM_QDMI_LIBRARY_PATH
from . import IQM_QDMI_DEVICE_ID, IQM_QDMI_LIBRARY_PATH, IQM_QDMI_PREFIX

__all__ = ["IQMBackend"]

IQM_DEFAULT_BASE_URL = "https://resonance.iqm.tech"


def __dir__() -> list[str]:
return __all__
Expand All @@ -50,8 +53,8 @@ class IQMBackend(QDMIBackend):
exposes it through MQT Core's Qiskit-compatible QDMI backend.

Args:
base_url: Base URL of the IQM service. Defaults to `IQM_BASE_URL` or
the standard Resonance endpoint.
base_url: Base URL of the IQM service. Overrides `IQM_BASE_URL` and
the registered device default when provided.
token: Authentication token. Defaults to `IQM_TOKEN`.
tokens_file: Path to an authentication file. Defaults to `IQM_TOKENS_FILE`.
qc_id: Optional IQM quantum computer identifier. Defaults to `IQM_QC_ID`.
Expand All @@ -63,20 +66,28 @@ def __init__(
*,
base_url: str | None = None,
token: str | None = None,
tokens_file: str | None = None,
tokens_file: str | os.PathLike[str] | None = None,
qc_id: str | None = None,
qc_alias: str | None = None,
) -> None:
"""Initialize the IQM Qiskit backend."""
resolved_base_url = base_url or os.getenv("IQM_BASE_URL") or "https://resonance.iqm.tech"
resolved_base_url = base_url or os.getenv("IQM_BASE_URL") or None
resolved_token = token or os.getenv("IQM_TOKEN")
resolved_tokens_file = tokens_file or os.getenv("IQM_TOKENS_FILE")
tokens_file_value = tokens_file or os.getenv("IQM_TOKENS_FILE")
resolved_tokens_file = Path(tokens_file_value) if tokens_file_value else None
resolved_qc_id = qc_id or os.getenv("IQM_QC_ID")
resolved_qc_alias = qc_alias or os.getenv("IQM_QC_ALIAS")

device = add_dynamic_device_library(
library_path=str(IQM_QDMI_LIBRARY_PATH),
prefix="IQM",
register_device_if_absent(
DeviceDefinition(
IQM_QDMI_DEVICE_ID,
IQM_QDMI_LIBRARY_PATH,
IQM_QDMI_PREFIX,
base_url=IQM_DEFAULT_BASE_URL,
)
)
device = open_device(
IQM_QDMI_DEVICE_ID,
base_url=resolved_base_url,
token=resolved_token,
auth_file=resolved_tokens_file,
Expand Down
10 changes: 10 additions & 0 deletions src/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ target_link_libraries(
PRIVATE qdmi::qdmi_project_warnings nlohmann_json::nlohmann_json
iqm::qdmi_device_internals)

# Publish the metadata consumers need to identify this separately built QDMI
# device target and synthesize a relocatable manifest.
set_target_properties(
${QDMI_TARGET_NAME} PROPERTIES QDMI_DEVICE_ID iqm.default QDMI_DEVICE_PREFIX
${QDMI_PREFIX})
set_property(
TARGET ${QDMI_TARGET_NAME}
APPEND
PROPERTY EXPORT_PROPERTIES QDMI_DEVICE_ID QDMI_DEVICE_PREFIX)

set(IQM_QDMI_CONFIG_INSTALL_DIR
"${CMAKE_INSTALL_DATADIR}/cmake/${QDMI_TARGET_NAME}"
CACHE INTERNAL "Config install directory")
Expand Down
15 changes: 14 additions & 1 deletion test/python/test_init.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,14 @@

from pathlib import Path

from iqm.qdmi import IQM_QDMI_CMAKE_DIR, IQM_QDMI_INCLUDE_DIR, IQM_QDMI_LIBRARY_PATH, __version__
from iqm.qdmi import (
IQM_QDMI_CMAKE_DIR,
IQM_QDMI_DEVICE_ID,
IQM_QDMI_INCLUDE_DIR,
IQM_QDMI_LIBRARY_PATH,
IQM_QDMI_PREFIX,
__version__,
)


def test_version_exists() -> None:
Expand All @@ -31,6 +38,12 @@ def test_version_exists() -> None:
assert len(__version__) > 0


def test_qdmi_device_metadata() -> None:
"""Test the stable device metadata used by native and Python consumers."""
assert IQM_QDMI_DEVICE_ID == "iqm.default"
assert IQM_QDMI_PREFIX == "IQM"


def test_include_dir_exists() -> None:
"""Test that IQM_QDMI_INCLUDE_DIR exists and is a directory."""
assert IQM_QDMI_INCLUDE_DIR.exists()
Expand Down
Loading
Loading