Skip to content

✨ Add QIR Program Format Support to the QDMI DDSim Device #1695

@ystade

Description

@ystade

Note

This issue is part of unitaryHACK 2026; note that you have to be registered to complete this issue. Learn more about the PR review process here

A message from unitaryHACK about AI Slop: "While we allow LLMs as collaborative tools, fully AI-generated PRs are prohibited. Maintainers have the right to reject any contribution that appears to be automated or lacks human oversight."

Learn more about unitaryHACK rules and opportunities here and about the usage of AI, in particular, here!

Description

The MQT Core QDMI DD device (include/mqt-core/qdmi/devices/dd/Device.hpp, src/qdmi/devices/dd/Device.cpp) is a Decision-Diagram-based quantum simulator exposed via the Quantum Device Management Interface (QDMI) standard.

Currently, the device supports only OpenQASM 2 and OpenQASM 3 as input program formats (QDMI_PROGRAM_FORMAT_QASM2, QDMI_PROGRAM_FORMAT_QASM3). The QDMI standard also defines four Quantum Intermediate Representation (QIR) formats:

Format constant Description
QDMI_PROGRAM_FORMAT_QIRBASESTRING QIR Base Profile as LLVM IR text (.ll)
QDMI_PROGRAM_FORMAT_QIRBASEMODULE QIR Base Profile as LLVM bitcode binary (.bc)
QDMI_PROGRAM_FORMAT_QIRADAPTIVESTRING QIR Adaptive Profile as LLVM IR text (.ll)
QDMI_PROGRAM_FORMAT_QIRADAPTIVEMODULE QIR Adaptive Profile as LLVM bitcode binary (.bc)

All four currently return QDMI_ERROR_NOTSUPPORTED (see test/qdmi/devices/dd/job_parameters_test.cpp, ProgramFormatSupport test).

The goal of this issue is to implement support for at minimum QDMI_PROGRAM_FORMAT_QIRBASESTRING and QDMI_PROGRAM_FORMAT_QIRBASEMODULE, so that a QIR program can be submitted to and executed by the DD device just like an OpenQASM program.

MQT Core already includes a fully working QIR execution stack:

  • QIR Runtime (src/qir/runtime/, include/mqt-core/qir/runtime/) — a DD-based implementation of QIR's __quantum__qis__* and __quantum__rt__* C API.
  • QIR Runner (src/qir/runner/Runner.cpp) — a standalone CLI tool using LLVM OrcJIT to JIT-compile and execute .ll/.bc files against the runtime.

This issue is about integrating that execution stack into the QDMI device's submit() / getResults() pipeline.


Background Reading

Before diving into implementation, make sure you understand the existing code:

  1. Familiarise yourself with the QDMI DD device — go through include/mqt-core/qdmi/devices/dd/Device.hpp and src/qdmi/devices/dd/Device.cpp. Pay close attention to MQT_DDSIM_QDMI_Device_Job_impl_d::setParameter(), submit(), and getResults() — these are the three entry points you will be extending.

  2. Study the existing QIR runner — skim through src/qir/runner/Runner.cpp. Understand how it uses llvm::parseIRFile, llvm::orc::LLJIT, and llvm::sys::DynamicLibrary::AddSymbol to JIT-compile a module and resolve QIR intrinsic symbols to the runtime. Note that it reads from a file path; for the device, the program arrives as an in-memory string or byte buffer, respectively.

  3. Study the QIR Runtime — go through include/mqt-core/qir/runtime/Runtime.hpp and src/qir/runtime/Runtime.cpp. Understand the qir::Runtime singleton: how it manages qubit state (qState, dd), how measurement results are stored in rRegister, and what Runtime::reset() does. Also look at include/mqt-core/qir/runtime/QIR.h for the full list of C-level QIR intrinsics.

  4. Run the existing tests — build and run test/qdmi/devices/dd/job_parameters_test.cpp and test/qdmi/devices/dd/results_sampling_test.cpp to confirm the baseline. Try running the QIR runner binary on test/qir/BellPairStatic.ll to see the existing execution path in action. For more information on how to run the test suite, see the developer documentation. For how to build and use the QIR runner the corresponding page in the documentation.

  5. Read the QIR Base Profile spec (optional but helpful) — https://github.com/qir-alliance/qir-spec/blob/main/specification/profiles/Base_Profile.md


Tasks

  1. In-memory module parsing — The runner uses llvm::parseIRFile(path, ...) which reads from disk. For the QDMI device, the QIR program arrives as a std::string (LLVM IR text for QIRBASESTRING) or raw bytes (bitcode for QIRBASEMODULE). LLVM provides llvm::parseIR(llvm::MemoryBufferRef, ...) for parsing from a buffer — verify this works for both text and bitcode and decide how to handle the two format variants.

  2. Result collection from JIT-executed QIR — After main() returns, measurement results reside in qir::Runtime's private rRegister (std::unordered_map<Result*, ResultStruct>). There is currently no public accessor. Add a public getResults() / getMeasurementBitstring() accessor to qir::Runtime.

  3. Sampling (multiple shots) — For numShots_ > 0, the QIR program must be re-executed numShots_ times and results aggregated.

  4. CMake / LLVM dependency gating — Linking MQT::CoreQIRRuntime and LLVM OrcJIT libs into mqt-core-qdmi-ddsim-device significantly increases the build footprint. Add a CMake option (e.g. BUILD_MQT_CORE_QDMI_WITH_QIR) so that QIR format support is opt-in.

  5. Accept QIR formats in setParameter() — In src/qdmi/devices/dd/Device.cpp, extend MQT_DDSIM_QDMI_Device_Job_impl_d::setParameter() to allow QDMI_PROGRAM_FORMAT_QIRBASESTRING and QDMI_PROGRAM_FORMAT_QIRBASEMODULE (remove the QDMI_ERROR_NOTSUPPORTED guard for those two). For QIRBASEMODULE, the program parameter carries raw bytes rather than a null-terminated string — handle program_ storage accordingly (the current size - 1 substring logic assumes a C string; bitcode is binary).

  6. Implement QIR execution in submit() — In submit(), add a branch alongside the existing OpenQASM path.


Testing

  1. Update job_parameters_test.cpp — Move QDMI_PROGRAM_FORMAT_QIRBASESTRING and QDMI_PROGRAM_FORMAT_QIRBASEMODULE from the "Unsupported" list to the "Supported" list in the ProgramFormatSupport test.

  2. Add QIR sampling tests — Add new test cases in test/qdmi/devices/dd/ (mirroring results_sampling_test.cpp) that submit a simple QIR Base Profile program (e.g. a Bell pair from test/qir/BellPairStatic.ll) as QIRBASESTRING, run with N shots, and assert the resulting histogram has the correct support ("00" and "11" states only) and sums to N. Repeat for QIRBASEMODULE using a pre-compiled .bc file.

  3. Guard tests with CMake option — Ensure the new tests are only compiled and run when that option is enabled.

Metadata

Metadata

Assignees

No one assigned

    Labels

    DDAnything related to the DD packageQDMIAnything related to QDMIQIRAnything related to QIRc++Anything related to C++ codefeatureNew feature or requestminorMinor version updateunitaryHACKIssues and PRs intended for unitaryHACKusabilityAnything related to usability
    No fields configured for Feature.

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions