Conversation
The templates constitute a large amount of redundant code that must additionally be maintained when the interface changes. At the same time, the templates are hard to test as they do not actually implement any logic. Hence, we decided to remove the templates and, instead, in a future commit, enrich the reference (`examples/`) implementations with comprehensive comments where user would need to add or change code to adapt the reference implementation to their own environment.
The `tool` was a simplistic mock-up of a compilation pass. This demonstration of how to use QDMI will be replaced by an overview of actual projects using QDMI.
From the experience using QDMI, it became clear that there will not be something like a unified FoMaC as a C++ abstraction. Instead, every compiler will implement its own C++ abstraction tailored to its use case. This renders the example implementation of a FoMaC in the QDMI repository superfluous and, hence, it is removed. It is removed togehter with all related test cases, which leads to a decreased test coverage. The test coverage will be fixed later with a proper test suite.
To find and recognize CMake variables that belong to the QDMI project more easily, they were renamed to all start with the prefix `QDMI_`. Additionally, `cmake_dependent_option` was added where meaningful.
The new interface consists of a core interface and loadable modules. The core interface is purposefully kept minimal and only contains the functionality required by any quantum device, e.g., superconducting QPU, neutral-atom QPU, a simulator, or other devices like provider or orchestration layer. Each device can provide additional functionality that is encapsulated in a module. E.g., a superconducitng QUP would implement the QPU and the superconducting module. Please note that this commit leaves the project in a broken state. Currently, it cannot be built. This will be fixed by future commits. Also, the neutral atom module is still missing and will be added later.
Update all the setup contained in `cmake/` to fit the new structure of the v2 headers. In particular, update the name-shifting and implement additional name-shifting by another prefix for lines that are marked with a certain label. Finally, update the `test_defs.cpp.in`, make it to a pure C-file. Now, it only contains one function that must be loaded via dlsym from a dynamic library.
To make the example implementations more modular, i.e., such that they can build on each other, their directory structure was entirely refactored.
The added test suite just need to be instantiated for a concrete implementation. Then this implementation can immediately be tested against the specification. While this test suite is not complete yet, it sets up the structure and can be extended to a truely complete test suite testing the compliance with QDMIv2. As part of this change, the old test suite in `test/` was removed because the interface cannot be tested without an implementation. Hence, all tests moved into the example directory where the implementations are located.
The old example QPU implementation residing in `examples/src/device` was moved to `examples/{include,src}/qpu` and was updated from scratch to support QDMIv2. This implementation is based on two auxiliary classes, namely a singleton and a logger residing in `examples/{include,src}/common/`.
In the new interface there is no distinction between a client and a device interface. Different levels of abstraction are handled by different modules a device implements. The previous driver is best matched with a provider. Hence, the dirver was substituted with an updated implementation as a provider.
An entity that could not be represented by QDMIv1 and which is a unique feature of QDMIv2, is the orchestration layer, i.e., the entry point to an entire software stack. The example implementation of such an orchestration layer is very thin and does in particular not contain any compilation capabilities a normal orchestration layer would.
Many vendors have already implemented QDMIv1 to expose there devices. QDMIv2 must ensure that these devices are still compatible. To this end, we implemented an adapter as a provider that loads QDMIv1 devices instead of QDMIv2 devices and exposes them as QDMIv2 devices. It does all the required re-wiring internally.
The `example/apps` directory now contains C-executables that serve as demonstration examples how to use QDMI. They start very simple and gradually increase in complexity finally demonstrating the full job-lifecycle in the stack-as-a-device scenario. The bonus executable lets the user select the QPU a dummy job should run on.
The new example implementations use some new conventions that required to update the clang tidy configuration.
Device implementations may now be compiled with hidden symbol visibility, which is a common best practice for C++ libraries to reduce symbol clashes and improve load times. To ensure that all necessary symbols are exported, a new header file `qdmi/export.h` is provided. This header defines a macro for marking symbols for export, and you should use this macro to annotate all public symbols in your device implementation.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Motivation
QDMI v1 was designed as a uniform interface for directly connected QPUs.
While it has proven valuable in that role, its monolithic structure makes it unsuitable for the full range of deployment scenarios encountered in modern HPC–quantum integration:
QDMI_Device_Property,QDMI_Site_Property). As the number of supported technologies grows, the interface becomes increasingly cluttered with properties irrelevant to most devices.QDMI v2 resolves these issues through a modular architecture: a thin mandatory core captures what all devices share, while optional modules capture where they differ. This enables three integration scenarios—direct QPU access, provider pooling, and federated software stacks—through one uniform interface.
Changelog
New: Modular Architecture
The flat single-file interface is replaced by a structured module system:
qdmi/core.h): Mandatory for all devices. Manages the device lifecycle (context, session), exposes device identity and version, and provides the module-loading mechanism. Accessed via a function pointer table (QDMI_Core_Interface) obtained through the single entry pointQDMI_initialize.qdmi/qpu.h, ID"qpu"): Job submission and result retrieval for QPUs; replaces the v1 client job interface. Queried viaQDMI_context_query_module_by_id(ctx, "qpu", &mod)followed byQDMI_context_get_module_interface.qdmi/provider.h, ID"provider"): New. Exposes managed devices and returns theQDMI_Core_Interface*for each, enabling recursive composition of providers and orchestration layers.qdmi/orchestration_layer.h, ID"ol"): New. Extends the provider module with job management functions andQDMI_job_set_deviceto designate the target QPU. Realizes the stack-as-a-device paradigm."sc") and neutral atom ("na") modules replace technology-specific properties in the shared device/site/operation enums. Each module owns its types (QDMI_SCQubit,QDMI_SCOperation, etc.) and function table.New: Context and Function Tables
v1 relied on
QDMI_device_initialize/QDMI_device_finalizeas global lifecycle functions and exported individual symbols for each operation. v2 introduces:QDMI_initialize(version, callback, user_data, &context, &library): Single entry point resolving all library-level state into an opaqueQDMI_Contextand aQDMI_Libraryfunction table. Theversionparameter encodes the semantic version for forward/backward compatibility checking.QDMI_Core_Interface,QDMI_QPU_Interface, etc.), not direct symbol exports. This decouples the ABI from the header layout and allows stable binary interfaces across releases.QDMI_context_finalizereplacesQDMI_device_finalize.Changed: Session Management
QDMI_context_allocate_sessionreplacesQDMI_session_alloc.QDMI_session_set_token,QDMI_session_set_authentication_file,QDMI_session_set_authentication_url,QDMI_session_set_username,QDMI_session_set_password), replacing the genericQDMI_session_set_parameterwith its untypedvoid*value and enum key.QDMI_context_query_authentication_optionsallows clients to query which credential combinations a device accepts before allocating a session.Changed: Job Interface
QDMI_job_set_parameter/QDMI_job_get_resultspattern is replaced by typed functions:QDMI_job_set_payload_string,QDMI_job_set_payload_binary,QDMI_job_set_shot_count.QDMI_Program_Format) with a queryable string ID and packed version number. String and binary payload support are independently queryable per format.QDMI_job_get_shots,QDMI_job_get_histogram,QDMI_job_get_state_vector_dense/sparse,QDMI_job_get_probabilities_dense/sparse.QDMI_Devicetype andQDMI_device_create_jobare gone; jobs are created at the session level viaQDMI_session_create_jobwithin the QPU or orchestration layer module.New: Logging
A
QDMI_Log_Callbackmechanism is introduced at context, session, and job level, replacing ad-hoc error return codes as the sole feedback channel.Upgrade Guide
Migrating a v1 device implementation to v2 requires the following steps:
Replace the entry point. Remove
QDMI_device_initialize/QDMI_device_finalizeand implementQDMI_initializereturning aQDMI_Contextand a populatedQDMI_Librarystruct. UseQDMI_context_finalizefor teardown.Implement the core interface. Populate a
QDMI_Core_Interfacestruct with function pointers for context queries (id,name,version,authentication_options,modules) and session management (allocate_session,set_token,initialize,free). Export this struct via theget_interfacefunction pointer in theQDMI_Libraryreturned byQDMI_initialize.Move device introspection to the QPU module. Remove implementations of
QDMI_device_query_device_property,QDMI_device_query_site_property, andQDMI_device_query_operation_property. Technology-specific properties (coupling maps, T1/T2 times, site coordinates) move into the"sc"or"na"module function table respectively.Rewrite the job interface. Replace
QDMI_job_set_parameter/QDMI_job_get_resultsimplementations with the typed payload setters and result getters defined inqdmi/job/functions.h. Populate aQDMI_QPU_Interfacestruct and register it as the"qpu"module.Register modules. Implement
QDMI_context_query_modulesandQDMI_context_query_module_by_idto enumerate all modules the device supports. For each module, implementQDMI_context_get_module_interfaceto return the corresponding function table pointer.Update authentication. Replace
QDMI_device_session_set_parameterlogic with typed setters. ImplementQDMI_context_query_authentication_optionsto declare supported credential combinations.Providers and orchestration layers (new implementations only). Implement the
QDMI_Provider_InterfaceorQDMI_OrchestrationLayer_Interfacestruct and register it under the"provider"or"ol"module ID respectively.Examples
The
examples/directory contains a self-contained proof-of-concept that exercises all three integration scenarios end-to-end. It is organised into four layers.Client apps (
examples/apps/)Four numbered C programs correspond directly to the interaction patterns described above:
1_bootstrappingdlopen/LoadLibrary, resolvesQDMI_initialize, obtains aQDMI_ContextandQDMI_Library, retrieves theQDMI_Core_Interface, allocates a session, sets a token, and initializes the session. This sequence is identical regardless of the device type behind the library.2_job_lifecycle1_bootstrappingagainst a QPU. Loads the"qpu"module, negotiates a program format (openqasm3.0), creates a job, attaches a Bell-state circuit as a string payload, sets the shot count, submits the job, waits for completion, and retrieves measurement results as bitstrings.3_stack-as-a-device1_bootstrappingagainst an orchestration layer. Loads the"ol"module, queries the list of managed QPUs, retrieves theQDMI_Core_Interfaceand context of a managed device, and establishes a session with it—using the exact same bootstrapping sequence as app 1. Demonstrates the stack-as-a-device paradigm.BONUS_end-to-endQDMI_job_set_device).Example device implementations (
examples/src/andexamples/include/)Three mock devices implement the v2 interface in C++ and serve as reference implementations as well as targets for the apps and tests:
qpu/core,"qpu","sc"provider/core,"provider"library_wrapperutility, and returns theirQDMI_Core_Interface*andQDMI_Contextthrough the provider module.orchestration_layer/core,"provider","ol"QDMI_job_set_device, simulating a remote software stack.Additionally,
src/v1/contains a legacy v1 device, andsrc/adapter/provides a v1→v2 adapter that wraps any v1 device behind the new v2 interface, demonstrating a backwards-compatible migration path.