QDMI v1.2.0
Quantum Device Management Interface
Loading...
Searching...
No Matches
Examples

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(
CXX_QDMI_Device_Session session, const QDMI_Device_Property prop,
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) {
}
ADD_STRING_PROPERTY(QDMI_DEVICE_PROPERTY_NAME, "C++ Device with 5 qubits",
prop, size, value, size_ret)
ADD_STRING_PROPERTY(QDMI_DEVICE_PROPERTY_VERSION, "0.1.0", prop, size, value,
size_ret)
ADD_STRING_PROPERTY(QDMI_DEVICE_PROPERTY_LIBRARYVERSION, "1.1.0", prop, size,
@ QDMI_ERROR_INVALIDARGUMENT
Invalid argument.
Definition constants.h:46
@ QDMI_ERROR_BADSTATE
Resource is in the wrong state for the operation.
Definition constants.h:50
@ QDMI_DEVICE_PROPERTY_NAME
char* (string) The name of the device.
Definition constants.h:279
@ QDMI_DEVICE_PROPERTY_CUSTOM3
Definition constants.h:353
@ QDMI_DEVICE_PROPERTY_CUSTOM2
Definition constants.h:351
@ QDMI_DEVICE_PROPERTY_VERSION
char* (string) The version of the device.
Definition constants.h:281
@ QDMI_DEVICE_PROPERTY_LIBRARYVERSION
char* (string) The implemented version of QDMI.
Definition constants.h:285
@ QDMI_DEVICE_PROPERTY_CUSTOM1
This enum value is reserved for a custom property.
Definition constants.h:349
@ QDMI_DEVICE_PROPERTY_CUSTOM4
Definition constants.h:355
@ QDMI_DEVICE_PROPERTY_CUSTOM5
Definition constants.h:357
@ QDMI_DEVICE_PROPERTY_MAX
The maximum value of the enum.
Definition constants.h:342
enum QDMI_DEVICE_PROPERTY_T QDMI_Device_Property
Device property type.
Definition constants.h:361
value, size_ret)
}
@ QDMI_ERROR_NOTSUPPORTED
Definition constants.h:48

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(
CXX_QDMI_Device_Session session, const QDMI_Device_Property prop,
const size_t size, void *value, size_t *size_ret) {
ADD_SINGLE_VALUE_PROPERTY(QDMI_DEVICE_PROPERTY_STATUS, QDMI_Device_Status,
CXX_QDMI_get_device_status(), prop, size, value,
size_ret)
ADD_SINGLE_VALUE_PROPERTY(QDMI_DEVICE_PROPERTY_QUBITSNUM, size_t, 5, prop,
enum QDMI_DEVICE_STATUS_T QDMI_Device_Status
Device status type.
Definition constants.h:383
@ QDMI_DEVICE_PROPERTY_STATUS
QDMI_Device_Status The status of the device.
Definition constants.h:283
@ QDMI_DEVICE_PROPERTY_QUBITSNUM
size_t The number of qubits in the device.
Definition constants.h:287
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(
CXX_QDMI_Device_Session session, const QDMI_Device_Property prop,
const size_t size, void *value, size_t *size_ret) {
ADD_LIST_PROPERTY(QDMI_DEVICE_PROPERTY_SITES, CXX_QDMI_Site, CXX_DEVICE_SITES,
prop, size, value, size_ret)
ADD_LIST_PROPERTY(QDMI_DEVICE_PROPERTY_OPERATIONS, CXX_QDMI_Operation,
CXX_DEVICE_OPERATIONS, prop, size, value, size_ret)
ADD_LIST_PROPERTY(QDMI_DEVICE_PROPERTY_COUPLINGMAP, CXX_QDMI_Site,
DEVICE_COUPLING_MAP, prop, size, value, size_ret)
// The example device never requires calibration
ADD_SINGLE_VALUE_PROPERTY(QDMI_DEVICE_PROPERTY_NEEDSCALIBRATION, size_t, 0,
prop, size, value, size_ret)
ADD_SINGLE_VALUE_PROPERTY(
QDMI_DEVICE_PULSE_SUPPORT_LEVEL_NONE, prop, size, value, size_ret)
}
@ QDMI_DEVICE_PULSE_SUPPORT_LEVEL_NONE
The device does not support pulse-level control.
Definition constants.h:776
@ QDMI_DEVICE_PROPERTY_NEEDSCALIBRATION
size_t Whether the device needs calibration.
Definition constants.h:325
@ QDMI_DEVICE_PROPERTY_PULSESUPPORT
QDMI_Device_Pulse_Support_Level Whether the device supports pulse-level control.
Definition constants.h:333
@ QDMI_DEVICE_PROPERTY_COUPLINGMAP
QDMI_Site* (QDMI_Site list) The coupling map of the device.
Definition constants.h:313
@ QDMI_DEVICE_PROPERTY_OPERATIONS
QDMI_Operation* (QDMI_Operation list) The operations supported by the device.
Definition constants.h:301
@ QDMI_DEVICE_PROPERTY_SITES
QDMI_Site* (QDMI_Site list) The sites of the device.
Definition constants.h:294
enum QDMI_DEVICE_PULSE_SUPPORT_LEVEL_T QDMI_Device_Pulse_Support_Level
Pulse support level type.
Definition constants.h:808

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}}},
// No need to specify single-qubit fidelities here
};
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,
const double *params, QDMI_Operation_Property prop, const size_t size,
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) ||
}
// General properties
ADD_STRING_PROPERTY(QDMI_OPERATION_PROPERTY_NAME,
OPERATION_PROPERTIES.at(operation).first.c_str(), prop,
size, value, size_ret)
if (operation == CXX_DEVICE_OPERATIONS[3]) {
if (sites != nullptr && num_sites != 2) {
}
ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_PARAMETERSNUM, size_t, 0,
prop, size, value, size_ret)
ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_DURATION, double,
OPERATION_PROPERTIES.at(operation).second, prop,
size, value, size_ret)
if (sites == nullptr) {
ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_QUBITSNUM, size_t, 2,
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()) {
}
ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_FIDELITY, double,
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)) {
}
ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_DURATION, double, 0.01,
prop, size, value, size_ret)
ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_QUBITSNUM, size_t, 1,
prop, size, value, size_ret)
ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_PARAMETERSNUM, size_t, 1,
prop, size, value, size_ret)
ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_FIDELITY, double, 0.999,
prop, size, value, size_ret)
}
}
@ QDMI_OPERATION_PROPERTY_CUSTOM2
Definition constants.h:473
@ QDMI_OPERATION_PROPERTY_MAX
The maximum value of the enum.
Definition constants.h:464
@ QDMI_OPERATION_PROPERTY_QUBITSNUM
size_t The number of qubits involved in the operation.
Definition constants.h:449
@ QDMI_OPERATION_PROPERTY_NAME
char* (string) The string identifier of the operation.
Definition constants.h:447
@ QDMI_OPERATION_PROPERTY_PARAMETERSNUM
size_t The number of floating point parameters the operation takes.
Definition constants.h:451
@ QDMI_OPERATION_PROPERTY_CUSTOM1
This enum value is reserved for a custom property.
Definition constants.h:471
@ QDMI_OPERATION_PROPERTY_CUSTOM3
Definition constants.h:475
@ QDMI_OPERATION_PROPERTY_DURATION
double The duration of an operation in µs.
Definition constants.h:453
@ QDMI_OPERATION_PROPERTY_CUSTOM5
Definition constants.h:479
@ QDMI_OPERATION_PROPERTY_FIDELITY
double The fidelity of an operation.
Definition constants.h:455
@ QDMI_OPERATION_PROPERTY_CUSTOM4
Definition constants.h:477
enum QDMI_OPERATION_PROPERTY_T QDMI_Operation_Property
Operation property type.
Definition constants.h:483

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;
// set job id to random number for demonstration purposes
(*job)->id = CXX_QDMI_generate_job_id();
(*job)->status = QDMI_JOB_STATUS_CREATED;
return QDMI_SUCCESS;
}
@ QDMI_JOB_STATUS_CREATED
The job was created and can be configured via QDMI_job_set_parameter.
Definition constants.h:495
@ QDMI_SUCCESS
The operation was successful.
Definition constants.h:39

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) ||
}
if (job->status != QDMI_JOB_STATUS_CREATED) {
}
switch (param) {
if (value != nullptr) {
const auto format = *static_cast<const QDMI_Program_Format *>(value);
if (format >= QDMI_PROGRAM_FORMAT_MAX &&
}
if (format != QDMI_PROGRAM_FORMAT_QASM2 &&
}
job->format = format;
}
return QDMI_SUCCESS;
if (value != nullptr) {
job->program = new char[size];
memcpy(job->program, value, size);
}
return QDMI_SUCCESS;
if (value != nullptr) {
job->num_shots = *static_cast<const size_t *>(value);
}
return QDMI_SUCCESS;
default:
}
}
@ QDMI_PROGRAM_FORMAT_CALIBRATION
void* A calibration program.
Definition constants.h:633
@ QDMI_PROGRAM_FORMAT_CUSTOM3
Definition constants.h:653
@ QDMI_PROGRAM_FORMAT_QIRBASESTRING
char* (string) A text-based QIR program complying to the QIR base profile.
Definition constants.h:594
@ QDMI_PROGRAM_FORMAT_CUSTOM4
Definition constants.h:655
@ QDMI_PROGRAM_FORMAT_QIRBASEMODULE
void* A QIR binary complying to the QIR base profile.
Definition constants.h:605
@ QDMI_PROGRAM_FORMAT_CUSTOM2
Definition constants.h:651
@ QDMI_PROGRAM_FORMAT_CUSTOM5
Definition constants.h:657
@ QDMI_PROGRAM_FORMAT_CUSTOM1
This enum value is reserved for a custom program format.
Definition constants.h:649
@ QDMI_PROGRAM_FORMAT_MAX
The maximum value of the enum.
Definition constants.h:642
@ QDMI_PROGRAM_FORMAT_QASM2
char* (string) An OpenQASM 2.0 program.
Definition constants.h:542
@ QDMI_DEVICE_JOB_PARAMETER_SHOTSNUM
size_t The number of shots to execute for a quantum circuit job.
Definition constants.h:174
@ QDMI_DEVICE_JOB_PARAMETER_MAX
The maximum value of the enum.
Definition constants.h:183
@ QDMI_DEVICE_JOB_PARAMETER_CUSTOM1
This enum value is reserved for a custom parameter.
Definition constants.h:190
@ QDMI_DEVICE_JOB_PARAMETER_PROGRAMFORMAT
QDMI_Program_Format The format of the program to be executed.
Definition constants.h:159
@ QDMI_DEVICE_JOB_PARAMETER_CUSTOM4
Definition constants.h:196
@ QDMI_DEVICE_JOB_PARAMETER_CUSTOM2
Definition constants.h:192
@ QDMI_DEVICE_JOB_PARAMETER_PROGRAM
void* The program to be executed.
Definition constants.h:169
@ QDMI_DEVICE_JOB_PARAMETER_CUSTOM5
Definition constants.h:198
@ QDMI_DEVICE_JOB_PARAMETER_CUSTOM3
Definition constants.h:194
enum QDMI_PROGRAM_FORMAT_T QDMI_Program_Format
Program format type.
Definition constants.h:661
enum QDMI_DEVICE_JOB_PARAMETER_T QDMI_Device_Job_Parameter
Device job parameter type.
Definition constants.h:202

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 == nullptr || job->status != QDMI_JOB_STATUS_CREATED) {
}
// Calibration jobs complete immediately
if (job->format == QDMI_PROGRAM_FORMAT_CALIBRATION) {
job->status = QDMI_JOB_STATUS_DONE;
return QDMI_SUCCESS;
}
CXX_QDMI_set_device_status(QDMI_DEVICE_STATUS_BUSY);
// here, the actual submission of the problem to the device would happen
// ...
// set job status to running for demonstration purposes
job->status = QDMI_JOB_STATUS_RUNNING;
// generate random result data
size_t num_qubits = 0;
CXX_QDMI_device_session_query_device_property(
job->session, QDMI_DEVICE_PROPERTY_QUBITSNUM, sizeof(size_t), &num_qubits,
nullptr);
job->results.clear();
job->results.reserve(job->num_shots);
for (size_t i = 0; i < job->num_shots; ++i) {
// generate random bitstring
std::string result(num_qubits, '0');
std::generate(result.begin(), result.end(),
[&]() { return CXX_QDMI_generate_bit() ? '1' : '0'; });
job->results.emplace_back(std::move(result));
}
// Generate random complex numbers and calculate the norm
job->state_vec.clear();
job->state_vec.reserve(1U << num_qubits);
double norm = 0.0;
for (size_t i = 0; i < 1U << num_qubits; ++i) {
const auto &c = job->state_vec.emplace_back(CXX_QDMI_generate_real(),
CXX_QDMI_generate_real());
norm += std::norm(c);
}
// Normalize the vector
norm = std::sqrt(norm);
for (auto &c : job->state_vec) {
c /= norm;
}
return QDMI_SUCCESS;
}
@ QDMI_JOB_STATUS_SUBMITTED
The job was submitted.
Definition constants.h:497
@ QDMI_JOB_STATUS_RUNNING
The job is running, and the result is not yet available.
Definition constants.h:501
@ QDMI_JOB_STATUS_DONE
The job is done, and the result can be retrieved.
Definition constants.h:503
@ QDMI_DEVICE_STATUS_BUSY
The device is busy.
Definition constants.h:367

For the full implementation of the example devices we refer to the respective source files in the QDMI repository, that is, device.cpp for the C++ implementation.