This page contains example implementations of devices and other components of the software stack that use QDMI. All examples distributed with QDMI are contained in the examples/ directory in the repository.
Implementing a Device
Below you find mock implementations of a QDMI device in C++.
- Note
- Keep in mind, that even though the interface is defined in C, the device can be implemented in C++ or any other language that supports the C ABI.
Basic String Properties
Every device has to provide a name, its version, and the implemented QDMI library version through the query interface. The corresponding properties are
All of those properties are of type char* (string). Since they are properties of the device, they are returned by the QDMI_device_session_query_device_property function. Below you find the respective implementation in C++.
int CXX_QDMI_device_session_query_device_property(
const size_t size, void *value, size_t *size_ret) {
if (session == nullptr || (value != nullptr && size == 0) ||
}
if (session->status != CXX_QDMI_DEVICE_SESSION_STATUS::INITIALIZED) {
}
prop, size, value, size_ret)
prop, size, value, size_ret)
@ QDMI_ERROR_INVALIDARGUMENT
Invalid argument.
Definition constants.h:51
@ QDMI_ERROR_BADSTATE
Resource is in the wrong state for the operation.
Definition constants.h:55
@ QDMI_DEVICE_PROPERTY_NAME
char* (string) The name of the device.
Definition constants.h:336
@ QDMI_DEVICE_PROPERTY_VERSION
char* (string) The version of the device.
Definition constants.h:338
@ QDMI_DEVICE_PROPERTY_LIBRARYVERSION
char* (string) The implemented version of QDMI.
Definition constants.h:342
@ QDMI_DEVICE_PROPERTY_CUSTOM1
This enum value is reserved for a custom property.
Definition constants.h:502
@ QDMI_DEVICE_PROPERTY_MAX
The maximum value of the enum.
Definition constants.h:495
enum QDMI_DEVICE_PROPERTY_T QDMI_Device_Property
Device property type.
Definition constants.h:516
size, value, size_ret)
}
@ QDMI_ERROR_NOTSUPPORTED
Definition constants.h:53
Both implementations use an auxiliary macro to add the string properties to the device. For an explanation of the macro, see the next section Auxiliary Macros.
Auxiliary Macros
The following macro is used to add string properties to the device. The macro is used, e.g., in the implementation of the QDMI_device_session_query_device_property function.
#define ADD_STRING_PROPERTY(prop_name, prop_value, prop, size, value, \
size_ret) \
{ \
if ((prop) == (prop_name)) { \
if ((value) != nullptr) { \
if ((size) < strlen(prop_value) + 1) { \
return QDMI_ERROR_INVALIDARGUMENT; \
} \
strncpy(static_cast<char *>(value), prop_value, size); \
static_cast<char *>(value)[size - 1] = '\0'; \
} \
if ((size_ret) != nullptr) { \
*size_ret = strlen(prop_value) + 1; \
} \
return QDMI_SUCCESS; \
} \
}
A similar macro is defined for other (fixed length) data types, for example, int, double.
#define ADD_SINGLE_VALUE_PROPERTY(prop_name, prop_type, prop_value, prop, \
size, value, size_ret) \
{ \
if ((prop) == (prop_name)) { \
if ((value) != nullptr) { \
if ((size) < sizeof(prop_type)) { \
return QDMI_ERROR_INVALIDARGUMENT; \
} \
*static_cast<prop_type *>(value) = prop_value; \
} \
if ((size_ret) != nullptr) { \
*size_ret = sizeof(prop_type); \
} \
return QDMI_SUCCESS; \
} \
}
Another macro is defined for list properties of the data types above.
#define ADD_LIST_PROPERTY(prop_name, prop_type, prop_values, prop, size, \
value, size_ret) \
{ \
if ((prop) == (prop_name)) { \
if ((value) != nullptr) { \
if ((size) < (prop_values).size() * sizeof(prop_type)) { \
return QDMI_ERROR_INVALIDARGUMENT; \
} \
memcpy(static_cast<void *>(value), \
static_cast<const void *>((prop_values).data()), \
(prop_values).size() * sizeof(prop_type)); \
} \
if ((size_ret) != nullptr) { \
*size_ret = (prop_values).size() * sizeof(prop_type); \
} \
return QDMI_SUCCESS; \
} \
}
The usage of the two latter macros is demonstrated in the following sections.
Integer or Enumeration Properties
The following two examples demonstrate how to return integer or enumeration properties of the device.
int CXX_QDMI_device_session_query_device_property(
const size_t size, void *value, size_t *size_ret) {
CXX_QDMI_get_device_status(), prop, size, value,
size_ret)
enum QDMI_DEVICE_STATUS_T QDMI_Device_Status
Device status type.
Definition constants.h:538
@ QDMI_DEVICE_PROPERTY_STATUS
QDMI_Device_Status The status of the device.
Definition constants.h:340
@ QDMI_DEVICE_PROPERTY_QUBITSNUM
size_t The number of qubits in the device.
Definition constants.h:344
size, value, size_ret)
}
List Properties
Some properties are returned as a list of various data types. The following example shows how to return the coupling map of the device as a list of QDMI_Site pairs. The pairs are flattened into a single list of QDMI_Site's.
constexpr std::array<const CXX_QDMI_Site_impl_d *, 20>
DEVICE_COUPLING_MAP = {
CXX_DEVICE_SITES[0], CXX_DEVICE_SITES[1],
CXX_DEVICE_SITES[1], CXX_DEVICE_SITES[0],
CXX_DEVICE_SITES[1], CXX_DEVICE_SITES[2],
CXX_DEVICE_SITES[2], CXX_DEVICE_SITES[1],
CXX_DEVICE_SITES[2], CXX_DEVICE_SITES[3],
CXX_DEVICE_SITES[3], CXX_DEVICE_SITES[2],
CXX_DEVICE_SITES[3], CXX_DEVICE_SITES[4],
CXX_DEVICE_SITES[4], CXX_DEVICE_SITES[3],
CXX_DEVICE_SITES[4], CXX_DEVICE_SITES[0],
CXX_DEVICE_SITES[0], CXX_DEVICE_SITES[4]};
int CXX_QDMI_device_session_query_device_property(
const size_t size, void *value, size_t *size_ret) {
prop, size, value, size_ret)
CXX_DEVICE_OPERATIONS, prop, size, value, size_ret)
DEVICE_COUPLING_MAP, prop, size, value, size_ret)
prop, size, value, size_ret)
ADD_SINGLE_VALUE_PROPERTY(
size_ret)
prop, size, value, size_ret)
value, size_ret)
0.001, prop, size, value, size_ret)
value, size_ret)
}
@ QDMI_DEVICE_PULSE_SUPPORT_LEVEL_NONE
The device does not support pulse-level control.
Definition constants.h:1390
@ QDMI_DEVICE_PROPERTY_LENGTHSCALEFACTOR
double A scale factor for all length values.
Definition constants.h:410
@ QDMI_DEVICE_PROPERTY_LENGTHUNIT
char* (string) The length unit reported by the device.
Definition constants.h:400
@ QDMI_DEVICE_PROPERTY_NEEDSCALIBRATION
size_t Whether the device needs calibration.
Definition constants.h:383
@ QDMI_DEVICE_PROPERTY_PULSESUPPORT
QDMI_Device_Pulse_Support_Level Whether the device supports pulse-level control.
Definition constants.h:391
@ QDMI_DEVICE_PROPERTY_SUPPORTEDPROGRAMFORMATS
QDMI_Program_Format* (QDMI_Program_Format list) The exact program formats supported by the device.
Definition constants.h:457
@ QDMI_DEVICE_PROPERTY_COUPLINGMAP
QDMI_Site* (QDMI_Site list) The coupling map of the device.
Definition constants.h:375
@ QDMI_DEVICE_PROPERTY_DURATIONSCALEFACTOR
double A scale factor for all duration values.
Definition constants.h:429
@ QDMI_DEVICE_PROPERTY_OPERATIONS
QDMI_Operation* (QDMI_Operation list) The operations supported by the device.
Definition constants.h:363
@ QDMI_DEVICE_PROPERTY_DURATIONUNIT
char* (string) The duration unit reported by the device.
Definition constants.h:419
@ QDMI_DEVICE_PROPERTY_SITES
QDMI_Site* (QDMI_Site list) The sites of the device.
Definition constants.h:356
enum QDMI_DEVICE_PULSE_SUPPORT_LEVEL_T QDMI_Device_Pulse_Support_Level
Pulse support level type.
Definition constants.h:1422
Program-Format Execution Features
The QDMI_device_session_query_program_features function reports atomic execution features for one exact program-format descriptor. The following example device reports two unrestricted OpenQASM features and forward branching with a maximum nesting depth of one. Its QIR Base descriptors return a successful empty list because they support no optional feature beyond the QIR Base baseline.
constexpr std::array<QDMI_Program_Feature, 3> QASM2_FEATURES{
.value = 0,
.constraint_id =
.constraint_value = 1}}};
#define QDMI_PROGRAM_FEATURE_UNCONSTRAINED(feature_id, feature_value)
Initialize an unrestricted program feature for C or C++.
Definition constants.h:1123
#define QDMI_PROGRAM_FEATURE_FORWARD_BRANCHING
Select a later program region from a value computed during execution.
Definition constants.h:1154
#define QDMI_PROGRAM_FEATURE_MEASURED_QUBIT_REUSE
Apply a quantum operation to a measured qubit in the same execution.
Definition constants.h:1136
#define QDMI_PROGRAM_CONSTRAINT_MAX_CONTROL_FLOW_NESTING_DEPTH
Limit lexical control-flow nesting depth.
Definition constants.h:1219
#define QDMI_PROGRAM_FEATURE_MID_CIRCUIT_MEASUREMENT
Measure a qubit before the end of one execution.
Definition constants.h:1131
One optional feature guarantee for an exact program format.
Definition constants.h:1110
int CXX_QDMI_device_session_query_program_features(
if (session == nullptr || format == nullptr || !Valid_format(*format)) {
}
if (session->status != CXX_QDMI_DEVICE_SESSION_STATUS::INITIALIZED) {
}
if (!Supported_format(*format)) {
}
const auto copy = [&](const auto &features) {
const size_t required = sizeof(features);
if (size_ret != nullptr) {
*size_ret = required;
}
if (value == nullptr) {
}
if (size < required) {
}
std::ranges::copy(features, value);
};
if (QDMI_program_format_equal(format, &QASM2_FORMAT) != 0) {
return copy(QASM2_FEATURES);
}
if (size_ret != nullptr) {
*size_ret = 0U;
}
}
@ QDMI_SUCCESS
The operation was successful.
Definition constants.h:44
Each QDMI_Program_Feature record carries a feature ID, a feature-specific value, and an optional typed constraint. Records for one feature and value form one conjunctive group. An empty constraint ID means unrestricted support. Use QDMI_PROGRAM_FEATURE_UNCONSTRAINED to initialize such a record. Unknown or malformed constraints make the group unusable. The returned list is complete. Returning QDMI_ERROR_NOTSUPPORTED keeps feature metadata unknown. Requirements guaranteed by a standard descriptor remain implicit.
Complex Properties
The properties that are returned by QDMI_device_session_query_operation_property may depend on the actual site. The available QDMI_Operation's and QDMI_Site's, first, need to be retrieved through QDMI_device_session_query_device_property. With the handles for a QDMI_Operation and QDMI_Site, corresponding properties can be queried. The following example demonstrates how different properties of operations, for example, varying fidelities of two-qubit gates can be returned.
struct CXX_QDMI_Pair_hash {
template <class T1, class T2>
size_t operator()(const std::pair<T1, T2> &p) const {
auto hash1 = std::hash<T1>{}(p.first);
auto hash2 = std::hash<T2>{}(p.second);
return hash1 ^ hash2;
}
};
const std::unordered_map<
const CXX_QDMI_Operation_impl_d *,
std::unordered_map<
std::pair<const CXX_QDMI_Site_impl_d *, const CXX_QDMI_Site_impl_d *>,
double, CXX_QDMI_Pair_hash>>
OPERATION_FIDELITIES = {
{CXX_DEVICE_OPERATIONS[3],
{{{CXX_DEVICE_SITES[0], CXX_DEVICE_SITES[1]}, 0.99},
{{CXX_DEVICE_SITES[1], CXX_DEVICE_SITES[0]}, 0.99},
{{CXX_DEVICE_SITES[1], CXX_DEVICE_SITES[2]}, 0.98},
{{CXX_DEVICE_SITES[2], CXX_DEVICE_SITES[1]}, 0.98},
{{CXX_DEVICE_SITES[2], CXX_DEVICE_SITES[3]}, 0.97},
{{CXX_DEVICE_SITES[3], CXX_DEVICE_SITES[2]}, 0.97},
{{CXX_DEVICE_SITES[3], CXX_DEVICE_SITES[4]}, 0.96},
{{CXX_DEVICE_SITES[4], CXX_DEVICE_SITES[3]}, 0.96},
{{CXX_DEVICE_SITES[4], CXX_DEVICE_SITES[0]}, 0.95},
{{CXX_DEVICE_SITES[0], CXX_DEVICE_SITES[4]}, 0.95}}},
};
int CXX_QDMI_device_session_query_operation_property(
CXX_QDMI_Device_Session session, CXX_QDMI_Operation operation,
const size_t num_sites, const CXX_QDMI_Site *sites, const size_t num_params,
void *value, size_t *size_ret) {
if (session == nullptr || operation == nullptr ||
(sites != nullptr && num_sites == 0) ||
(params != nullptr && num_params == 0) ||
(value != nullptr && size == 0) ||
}
OPERATION_PROPERTIES.at(operation).first.c_str(), prop,
size, value, size_ret)
size, value, size_ret)
if (operation == CXX_DEVICE_OPERATIONS[3]) {
if (sites != nullptr && num_sites != 2) {
}
prop, size, value, size_ret)
OPERATION_PROPERTIES.at(operation).second, prop,
size, value, size_ret)
DEVICE_COUPLING_MAP, prop, size, value, size_ret)
if (sites == nullptr) {
prop, size, value, size_ret)
}
const std::pair site_pair = {sites[0], sites[1]};
if (site_pair.first == site_pair.second) {
}
const auto it = OPERATION_FIDELITIES.find(operation);
if (it == OPERATION_FIDELITIES.end()) {
}
const auto fit = it->second.find(site_pair);
if (fit == it->second.end()) {
}
fit->second, prop, size, value, size_ret)
} else if (operation == CXX_DEVICE_OPERATIONS[0] ||
operation == CXX_DEVICE_OPERATIONS[1] ||
operation == CXX_DEVICE_OPERATIONS[2]) {
if ((sites != nullptr && num_sites != 1) ||
(params != nullptr && num_params != 1)) {
}
prop, size, value, size_ret)
prop, size, value, size_ret)
prop, size, value, size_ret)
prop, size, value, size_ret)
CXX_DEVICE_SITES, prop, size, value, size_ret)
}
}
@ QDMI_OPERATION_PROPERTY_MAX
The maximum value of the enum.
Definition constants.h:884
@ QDMI_OPERATION_PROPERTY_ISZONED
bool Whether the operation is a zoned (global) operation.
Definition constants.h:844
@ QDMI_OPERATION_PROPERTY_QUBITSNUM
size_t The number of qubits involved in the operation.
Definition constants.h:764
@ QDMI_OPERATION_PROPERTY_NAME
char* (string) The string identifier of the operation.
Definition constants.h:762
@ QDMI_OPERATION_PROPERTY_PARAMETERSNUM
size_t The number of floating point parameters the operation takes.
Definition constants.h:766
@ QDMI_OPERATION_PROPERTY_CUSTOM1
This enum value is reserved for a custom property.
Definition constants.h:891
@ QDMI_OPERATION_PROPERTY_DURATION
uint64_t The raw, unscaled duration of an operation.
Definition constants.h:777
@ QDMI_OPERATION_PROPERTY_FIDELITY
double The fidelity of an operation.
Definition constants.h:779
@ QDMI_OPERATION_PROPERTY_SITES
QDMI_Site* (list) The sites to which the operation is applicable.
Definition constants.h:857
enum QDMI_OPERATION_PROPERTY_T QDMI_Operation_Property
Operation property type.
Definition constants.h:905
Submitting a Job
One crucial part of QDMI is that it allows submitting a job to the device for execution. The following example provides a mock implementation of the necessary functions to submit a job. The first example shows a mock implementation of QDMI_device_session_create_device_job.
int CXX_QDMI_device_session_create_device_job(CXX_QDMI_Device_Session session,
CXX_QDMI_Device_Job *job) {
if (session == nullptr || job == nullptr) {
}
if (session->status != CXX_QDMI_DEVICE_SESSION_STATUS::INITIALIZED) {
}
*job = new CXX_QDMI_Device_Job_impl_d;
(*job)->session = session;
(*job)->id = CXX_QDMI_generate_job_id();
}
@ QDMI_JOB_STATUS_CREATED
The job was created and can be configured via QDMI_job_set_parameter.
Definition constants.h:917
The function QDMI_device_job_set_parameter allows setting different parameters for the job, for example, the number of shots (QDMI_JOB_PARAMETER_SHOTSNUM).
int CXX_QDMI_device_job_set_parameter(CXX_QDMI_Device_Job job,
const size_t size, const void *value) {
if (job == nullptr || (value != nullptr && size == 0) ||
}
}
switch (param) {
if (value != nullptr) {
}
if (!Valid_format(format)) {
}
if (!Supported_format(format)) {
}
if (QDMI_program_format_equal(&job->format, &format) == 0) {
job->programs.clear();
}
job->format = format;
}
if (value != nullptr) {
job->num_shots = *static_cast<const size_t *>(value);
}
default:
}
}
@ QDMI_DEVICE_JOB_PARAMETER_SHOTSNUM
size_t The number of shots to execute for a quantum circuit job.
Definition constants.h:198
@ QDMI_DEVICE_JOB_PARAMETER_MAX
The maximum value of the enum.
Definition constants.h:207
@ QDMI_DEVICE_JOB_PARAMETER_CUSTOM1
This enum value is reserved for a custom parameter.
Definition constants.h:214
@ QDMI_DEVICE_JOB_PARAMETER_PROGRAMFORMAT
QDMI_Program_Format The format of the program to be executed.
Definition constants.h:193
enum QDMI_DEVICE_JOB_PARAMETER_T QDMI_Device_Job_Parameter
Device job parameter type.
Definition constants.h:228
The function QDMI_device_job_set_programs submits an ordered list of programs with one exact format and one job-wide shot count. The function copies the complete list before it returns. The job has one lifecycle, and result index i corresponds to input program i. A device can execute the programs in any order. The job is done only after every program succeeds. One program failure fails the job, and cancellation applies to the complete job. QDMI does not expose partial results. Use QDMI_device_job_get_results with the input index to retrieve one program's result.
int CXX_QDMI_device_job_set_programs(CXX_QDMI_Device_Job job,
const size_t count, const size_t *sizes,
const void *const *programs) {
if (job == nullptr || format == nullptr || count == 0 ||
!Valid_format(*format)) {
}
}
if (!Supported_format(*format)) {
}
if (programs == nullptr) {
}
if (sizes == nullptr) {
}
std::vector<std::vector<char>> new_programs;
try {
new_programs.reserve(count);
for (size_t i = 0; i < count; ++i) {
if (!Valid_program(*format, sizes[i], programs[i])) {
}
const auto *bytes = static_cast<const char *>(programs[i]);
new_programs.emplace_back(bytes, bytes + sizes[i]);
}
} catch (const std::bad_alloc &) {
} catch (const std::length_error &) {
}
job->format = *format;
job->programs = std::move(new_programs);
}
@ QDMI_ERROR_OUTOFMEM
Out of memory.
Definition constants.h:46
After the job is set up, it can be submitted to the device. The following example shows a mock implementation of QDMI_device_job_submit.
int CXX_QDMI_device_job_submit(CXX_QDMI_Device_Job job) {
}
if (job->programs.empty() || !Valid_format(job->format)) {
}
size_t num_qubits = 0;
CXX_QDMI_device_session_query_device_property(
nullptr);
constexpr std::array<std::string_view, 4> shot_outputs{FLAT_SHOT_OUTPUT, "10",
"11", "00"};
job->results.clear();
job->results.resize(job->programs.size());
for (size_t program_index = 0; program_index < job->results.size();
++program_index) {
auto &result = job->results[program_index];
size_t output_index = 0;
if (job->programs.size() > 1) {
const auto &program = job->programs.at(program_index);
const auto marker =
program.at(program.size() -
program.size() > 1
? 2
: 1));
output_index = static_cast<unsigned char>(marker) % shot_outputs.size();
}
result.shots.assign(job->num_shots,
std::string{shot_outputs.at(output_index)});
result.state_vec.reserve(1U << num_qubits);
double norm = 0.0;
for (size_t i = 0; i < 1U << num_qubits; ++i) {
const auto &c = result.state_vec.emplace_back(CXX_QDMI_generate_real(),
CXX_QDMI_generate_real());
norm += std::norm(c);
}
norm = std::sqrt(norm);
for (auto &c : result.state_vec) {
c /= norm;
}
}
}
@ QDMI_JOB_STATUS_SUBMITTED
The job was submitted.
Definition constants.h:919
@ QDMI_JOB_STATUS_RUNNING
The job is running, and the result is not yet available.
Definition constants.h:923
@ QDMI_DEVICE_STATUS_BUSY
The device is busy.
Definition constants.h:522
@ QDMI_PROGRAM_ENCODING_TEXT
Text with exactly one trailing NUL and no earlier NUL.
Definition constants.h:959
For the full implementation of the example devices we refer to the respective source files in the QDMI repository, that is, cxx_device.cpp for the C++ implementation.