Development Guide¶
Ready to contribute to the project? This guide will get you started.
Initial Setup¶
Get the code
External Contribution
If you do not have write access to the iqm-finland/QDMI-on-IQM repository, fork the repository on GitHub (see https://docs.github.com/en/get-started/quickstart/fork-a-repo) and clone your fork locally.
$ git clone git@github.com:your_name_here/QDMI-on-IQM.git
Internal Contribution
If you do have write access to the iqm-finland/QDMI-on-IQM repository, clone the repository locally.
$ git clone git@github.com/iqm-finland/QDMI-on-IQM.git
Change into the project directory
$ cd QDMI-on-IQM
Create a branch for local development
$ git checkout -b name-of-your-bugfix-or-feature
Now you can make your changes locally.
Install pre-commit hooks (recommended)
The project uses pre-commit hooks for running linters and formatting tools on each commit. These checks can be run automatically on every commit via prek (recommended).
To set this up, install prek, e.g., via:
macOS and Linux:
$ curl --proto '=https' --tlsv1.2 -LsSf https://github.com/j178/prek/releases/latest/download/prek-installer.sh | sh
Windows:
$ powershell -ExecutionPolicy ByPass -c "irm https://github.com/j178/prek/releases/latest/download/prek-installer.ps1 | iex"
Using uv:
$ uv tool install prek
Then run:
$ prek install
Working on the C++ library¶
Building the project requires a C++ compiler supporting C++20 and CMake with a minimum version of 3.24.
Configure and Build¶
This project uses CMake as the main build configuration tool. Building a project using CMake is a two-stage process. First, CMake needs to be configured by calling
$ cmake -S . -B build -DCMAKE_BUILD_TYPE=Release -DBUILD_IQM_QDMI_TESTS=ON
This tells CMake to
search the current directory
.(passed via-S) for aCMakeLists.txtfile.process it into a directory
build(passed via-B).the flag
-DCMAKE_BUILD_TYPE=Releasetells CMake to configure a Release build (as opposed to, e.g., a Debug build).the flag
-DBUILD_IQM_QDMI_TESTS=ONenables building the tests.the flag
-DIQM_QDMI_COVERAGE=ONcan be added to enable code coverage support (see below).
After configuring with CMake, the project can be built by calling
$ cmake --build build --config Release
This tries to build the project in the build directory (passed via --build).
Some operating systems and development environments explicitly require a configuration to be set, which is why the --config flag is also passed to the build command.
The flag --parallel <NUMBER_OF_THREADS> may be added to trigger a parallel build.
Building the project this way generates
the main project libraries in the
build/srcdirectorysome test executables in the
build/testdirectory
C++ Testing and Code Coverage¶
We use the GoogleTest framework for testing the C++ library.
The tests are located in the test directory and are separated into two categories:
Unit Tests: These are self-contained tests that do not require any external dependencies or a live connection to an endpoint. They are located in the
test/unitdirectory.Integration Tests: These tests require a live connection to an IQM backend and are located in the
test/integrationdirectory.
A corresponding CI pipeline on GitHub also runs the tests and checks for any failures.
To run the tests, you can use ctest after building the project (as described above).
Running all tests:
$ ctest -C Release --test-dir build --output-on-failure
Running only the unit tests:
$ ctest -C Release --test-dir build/test/unit --output-on-failure
Running only the integration tests:
Before running the integration tests, make sure you have set the necessary environment variables, such as IQM_BASE_URL and RESONANCE_API_KEY.
$ ctest -C Release --test-dir build/test/integration --output-on-failure
Running only install/public API tests:
The install-test setup in CI validates only the installed public API surface.
It intentionally excludes tests that depend on private/internal symbols.
$ ctest -C Release --test-dir build -L install-public --output-on-failure
To generate a code coverage report, you can configure the project with -DIQM_QDMI_COVERAGE=ON and then run lcov in the build directory.
C++ Code Formatting and Linting¶
This project mostly follows the LLVM Coding Standard, which is a set of guidelines for writing C++ code. To ensure the quality of the code and that it conforms to these guidelines, we use
clang-tidy– a static analysis tool that checks for common mistakes in C++ code, andclang-format– a tool that automatically formats C++ code according to a given style guide.
Our pre-commit configuration also includes clang-format.
If you have installed prek, it will automatically run clang-format on your code before each commit.
The respective checks can also be run via the nox session lint.
$ uvx nox -s lint
Our CI pipeline will run clang-tidy over the changes in your pull request and report any issues it finds.
C++ Documentation¶
We expect any new additions to the code base to be documented using Doxygen comments. When touching existing code, we encourage you to add Doxygen comments to the code you touch or refactor.
For some tips on how to write good Doxygen comments, see the Doxygen Manual.
Working on the Documentation¶
The documentation source files are in the docs/ directory and are built with
Sphinx + MyST, using Doxygen XML via Breathe for the C++ API and AutoAPI for the Python package.
To build the documentation locally, run the dedicated nox session (dependencies are installed automatically by the nox session):
$ uvx nox -s docs
The generated HTML site is written to docs/_build/html/.
Working on the Python Package¶
The project also comes with a thin Python wrapper around the QDMI device implementation for easier distribution and integration.
The Python package is located in the python/ directory.
To run the Python tests, you can use the nox session tests.
$ uvx nox -s tests
Or, if you want to run only a specific version of Python:
$ uvx nox -s tests-3.14
There is an additional nox session minimums that runs the tests against the minimum supported dependency versions for a specific Python version.
$ uvx nox -s minimums-3.14
IQM API Usage in QDMI Device Implementation¶
This section describes how the unified IQM Server API, as defined in iqm_api_config.hpp and iqm_api_config.cpp, is used to provide the functionality of the QDMI device implementation in iqm_device.cpp.
API Endpoints Used¶
The QDMI device implementation uses the following endpoints from the unified IQM Server API:
Quantum Computer Management:
GET_QUANTUM_COMPUTERS: Retrieves the list of available quantum computers with their IDs and aliases.GET_STATIC_QUANTUM_ARCHITECTURE: Fetches the static quantum architecture (qubits and connectivity) for a specific quantum computer.GET_DYNAMIC_QUANTUM_ARCHITECTURE: Obtains the set of calibrated gates and their implementations for a given calibration set.GET_CALIBRATION_SET_QUALITY_METRICS: Gets calibration metrics such as qubit T1/T2 times and gate fidelities.
Job Management:
SUBMIT_CIRCUIT_JOB: Submits a quantum circuit job (QIR or IQM JSON format) for execution.GET_JOB_STATUS: Checks the status of a submitted job.GET_JOB_ARTIFACT_MEASUREMENT_COUNTS: Retrieves the measurement results as aggregated histogram counts of a completed job.GET_JOB_ARTIFACT_MEASUREMENTS: Retrieves the individual shot measurement results of a completed job.CANCEL_JOB: Cancels a running job.
Calibration Jobs (if supported):
COCOS_HEALTH: Health check endpoint to determine if calibration jobs are supported.SUBMIT_CALIBRATION_JOB: Submits a calibration job for execution.GET_CALIBRATION_JOB_STATUS: Checks the status of a calibration job.ABORT_CALIBRATION_JOB: Cancels a running calibration job.
When API Calls Happen¶
During Session Initialization (IQM_QDMI_device_session_init):
GET_QUANTUM_COMPUTERS: Fetches the list of available quantum computers.The appropriate quantum computer is selected based on ID, alias, or first available.
GET_STATIC_QUANTUM_ARCHITECTURE: Retrieves the static architecture (qubits, connectivity).GET_DYNAMIC_QUANTUM_ARCHITECTURE: Fetches calibrated gates for the default calibration set.GET_CALIBRATION_SET_QUALITY_METRICS: Retrieves quality metrics (T1, T2, fidelities) if available.COCOS_HEALTH: Checks whether calibration jobs are supported.
During Job Submission and Management:
SUBMIT_CIRCUIT_JOBorSUBMIT_CALIBRATION_JOB: Called whenIQM_QDMI_device_job_submitis invoked.GET_JOB_STATUSorGET_CALIBRATION_JOB_STATUS: Polled whenIQM_QDMI_device_job_checkorIQM_QDMI_device_job_waitis called.GET_JOB_ARTIFACT_MEASUREMENT_COUNTS: Fetched when histogram results are queried.GET_JOB_ARTIFACT_MEASUREMENTS: Fetched when individual shot results are queried.CANCEL_JOBorABORT_CALIBRATION_JOB: Called whenIQM_QDMI_device_job_cancelis invoked.
After Calibration Job Completion: When querying results of a calibration job, the implementation automatically:
Extracts the new calibration set ID from the job result.
Calls
GET_DYNAMIC_QUANTUM_ARCHITECTUREwith the new calibration set ID.Calls
GET_CALIBRATION_SET_QUALITY_METRICSto update quality metrics.
API Configuration¶
The APIConfig class in iqm_api_config.hpp provides a centralized way to build URLs for API endpoints. The base URL is set during session initialization, and endpoints are constructed using string formatting with parameters like quantum computer alias, calibration set ID, and job ID.
Error Handling¶
If an API call fails, the HTTP client returns an appropriate QDMI status code.
Error messages are logged using the logging system.
For calibration job endpoints, if the server doesn’t support them (e.g.,
COCOS_HEALTHcheck fails), thesupports_calibration_jobs_flag is set to false, and calibration job submissions will returnQDMI_ERROR_NOTSUPPORTED.
API Response Expectations¶
The implementation expects JSON responses in specific formats:
Quantum Computers List: Array of objects with
idandaliasfields.Static Architecture: Array with a single object containing
qubits(array of strings) andconnectivity(array of 2-element arrays).Dynamic Architecture: Object with
calibration_set_id(string) andgates(nested objects with gate details).Quality Metrics: Object with
observationsarray containingdut_field,value, andinvalidfields.Job Status: Object with job status, errors, and messages.
Measurement Counts Artifact: Array with a single object containing
counts(object mapping bitstrings to count integers).Measurements Artifact: Array typically containing a single object where each measurement key maps to an array of all shot results. Each shot result is a single-element array containing an integer (0 or 1). For example:
[{"meas_2_0_0": [[0], [1], [0], [1]], "meas_2_0_1": [[1], [0], [1], [0]]}]represents 4 shots measuring two qubits, where each measurement key contains all results for that qubit across all shots.
For more details, see the implementation in iqm_device.cpp and the API configuration in iqm_api_config.hpp/.cpp.