Easy Integration with the iqm-qdmi Python Package¶
To ease the distribution and integration of the IQM QDMI Device library, we have
packaged it as a Python module available on PyPI under the name iqm-qdmi. This
package provides a convenient way to discover installation paths and metadata
that are useful when integrating the IQM QDMI Device into downstream build
systems, such as CMake-based projects or Python workflows.
Install From PyPI¶
Use uv (or your Python package manager of choice) to install the package:
uv pip install iqm-qdmi
Quick Usage¶
The package itself makes the following variables available for import:
__version__: installed package version.IQM_QDMI_INCLUDE_DIR: include directory for C/C++ headers.IQM_QDMI_CMAKE_DIR: CMake package directory forfind_packageintegration.IQM_QDMI_LIBRARY_PATH: full path to the shared library.
1from iqm.qdmi import __version__, IQM_QDMI_INCLUDE_DIR, IQM_QDMI_CMAKE_DIR, IQM_QDMI_LIBRARY_PATH
2
3print(f"QDMI on IQM version: {__version__}")
4print(f"Include directory: {IQM_QDMI_INCLUDE_DIR}")
5print(f"CMake directory: {IQM_QDMI_CMAKE_DIR}")
6print(f"Library path: {IQM_QDMI_LIBRARY_PATH}")
QDMI on IQM version: 1.4.0
Include directory: /home/runner/work/QDMI-on-IQM/QDMI-on-IQM/.nox/docs/lib/python3.14/site-packages/iqm/qdmi/data/include
CMake directory: /home/runner/work/QDMI-on-IQM/QDMI-on-IQM/.nox/docs/lib/python3.14/site-packages/iqm/qdmi/data/share/cmake
Library path: /home/runner/work/QDMI-on-IQM/QDMI-on-IQM/.nox/docs/lib/python3.14/site-packages/iqm/qdmi/data/lib/libiqm-qdmi-device.so
Command Line Interface¶
The above values can also be conveniently queried from the command line via the
iqm-qdmi entry point.
1!iqm-qdmi --help
usage: iqm-qdmi [-h] [--version | --include_dir | --cmake_dir | --lib_path]
Command line interface for the QDMI on IQM library.
options:
-h, --help show this help message and exit
--version show program's version number and exit
--include_dir Print the path to the iqm-qdmi C/C++ include directory
--cmake_dir Print the path to the iqm-qdmi CMake module directory
--lib_path Print the path to the iqm-qdmi shared library
1!iqm-qdmi --version
1.4.0
1!iqm-qdmi --include_dir
/home/runner/work/QDMI-on-IQM/QDMI-on-IQM/.nox/docs/lib/python3.14/site-packages/iqm/qdmi/data/include
1!iqm-qdmi --cmake_dir
/home/runner/work/QDMI-on-IQM/QDMI-on-IQM/.nox/docs/lib/python3.14/site-packages/iqm/qdmi/data/share/cmake
1!iqm-qdmi --lib_path
/home/runner/work/QDMI-on-IQM/QDMI-on-IQM/.nox/docs/lib/python3.14/site-packages/iqm/qdmi/data/lib/libiqm-qdmi-device.so
Sampler and Estimator CLI Utilities¶
If you install the package with the qiskit extra, the following additional
command-line scripts are exposed:
iqm-sampler(see thesamplerentry point module): Samples a serialized QPY circuit on the specified backend.iqm-estimator(see theestimatorentry point module): Variational Quantum Eigensolver (VQE) parameter estimation for a serialized ansatz and observable.
iqm-sampler Usage¶
For example, to execute a QPY circuit file (bell.qpy):
iqm-sampler bell.qpy --shots 128
iqm-estimator Usage¶
To run a parameter estimation job:
iqm-estimator ansatz.qpy observable.pkl --maxiter 10
The estimator CLI and offloader.estimate use Qiskit’s default precision of
1/64, corresponding to 4,096 shots per measurement circuit. See the
primitive options.
Programmatic Offloading with the offloader Module¶
For workflows running on a Slurm login node (such as Jupyter notebooks on a
gateway service), the package exposes the offloader module.
It allows you to programmatically submit quantum workloads to the Slurm quantum
queue.
The module provides two primary functions:
sample(): Serializes the circuit to QPY and submits a Slurm job usingsrun iqm-sampler. The returned sampler result is converted to joint counts across all classical registers in Qiskit’s bit order.estimate(): Serializes the ansatz and observable, submits a Slurm job usingsrun iqm-estimator, and returns the completeVQEResultproduced by the worker.
Both worker CLIs reserve stdout for one base64-encoded pickle containing the native Qiskit result; diagnostics belong on stderr. The offloader is intended for controlled Slurm deployments with trusted workers and serialized inputs. Use compatible Python and dependency environments on the submitting and worker nodes. Result loading uses standard pickle without a restricted unpickler.
Both functions support local=True to execute in the submitting process instead
of Slurm, and simulator=True to select the simulator in either mode.
Selecting the Slurm Partition¶
Both functions submit their srun jobs to the partition gating the nodes that
expose the quantum computer as a Slurm GRES resource. Its name is a per-site
choice, resolved in this order:
The
partitionkeyword argument.The
IQM_SLURM_PARTITIONenvironment variable, which lets an administrator set the site’s name once for every user. An empty value counts as unset.quantum, the name used throughout the SPANK plugin documentation and the Administrator Guide.
counts = sample(qc, shots=512, partition="qc-nodes")
Slurm’s own SLURM_PARTITION has no effect here, because the resolved name is
always passed as an explicit --partition, which takes precedence over it.
Important
Renaming the partition is not enough on its own. The SPANK plugin only runs on
the partitions its partitions= option lists, so that list has to carry the new
name too — see Configuration. Otherwise the
plugin silently skips the job and never injects IQM_BASE_URL, IQM_QC_ID, or
IQM_QC_ALIAS.
Sizing the Slurm Allocation¶
Both functions accept a nodes keyword argument, forwarded as --nodes and
defaulting to a single node. The worker itself always runs as one task
(--ntasks=1), so nodes only sizes the allocation for a site whose partition
demands more than one node; it does not distribute or parallelize the workload.
It has no environment fallback: the node count is a per-job resource request rather than a site-wide constant.
Selecting a Quantum Computer per Job¶
Both functions accept optional qc_id and qc_alias keyword arguments to pin a
specific quantum computer for a single job, without changing the process’s
default backend configuration. When set, they are passed as --iqm-qc-id and
--iqm-qc-alias options on the srun command itself, which the QDMI-on-IQM
SPANK plugin resolves into the job’s IQM_QC_ID and
IQM_QC_ALIAS environment variables. Only used when local=False.
Requesting a Slurm License¶
Both functions accept an optional licenses keyword argument, forwarded
verbatim as a --licenses option on the srun command (Slurm’s own
name[:count][,name[:count]...] syntax). This is unrelated to QC selection: a
site administrator can configure a Slurm license per QC to cap concurrent jobs
against it – see the SPANK plugin’s
Limiting Concurrent Access with Slurm Licenses
docs – and licenses is how a caller requests it. Only used when
local=False.
counts = sample(qc, shots=512, simulator=True, qc_alias="emerald", licenses="iqm_qc_emerald:1")
Programmatic Sampling Example¶
from iqm.qdmi.offloader import sample
from qiskit import QuantumCircuit
qc = QuantumCircuit(2)
qc.h(0)
qc.cx(0, 1)
qc.measure_all()
# programmatically offload via Slurm
counts = sample(qc, shots=512, simulator=True)
print("Counts:", counts)
Querying the Device Directly¶
The Qiskit backend covers circuit execution, but a QDMI device also answers
questions about itself. MQT Core discovers the installed device manifest without
importing provider code or loading the device library. Open its stable ID
IQM_QDMI_DEVICE_ID through the MQT Core QDMI driver:
from mqt.core.qdmi.builtin_driver import open_device
from iqm.qdmi import IQM_QDMI_DEVICE_ID
device = open_device(IQM_QDMI_DEVICE_ID, token="…", custom2="emerald")
print(device.status())
print(device.supported_program_formats())
Queue Length and Queue Position¶
The device reports how busy the quantum computer is, so a client can decide whether to submit now or wait:
waiting = device.queue_length() # jobs waiting, excluding those executing
queue_length() returns None when the IQM API does not supply a trustworthy
value. A queued job reports how many jobs are ahead of it; querying it refreshes
the job’s status first:
ahead = job.queue_position # None once the job is no longer queued
Retrieving an Existing Job¶
A job outlives the session that submitted it. Given its ID, a later session can pick it up again to poll, wait, cancel, or fetch results:
job = device.retrieve_job_by_id("d3416f0a-…")
print(job.check())
A retrieved job cannot be resubmitted, and its parameters cannot be changed.
Temporary development dependencies¶
This repository currently pins QDMI #509 and MQT Core #2373 commits to exercise
native multi-program jobs and installed device discovery. Replace both pins with
suitable releases and regenerate uv.lock before publishing. Remove the
temporary LLVM/MLIR setup from Python CI and Linux wheel-test containers once
Core wheels are available for these APIs. Native-only device builds do not
require LLVM/MLIR.