QDMI v1.1.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 two QDMI devices: One is implemented in C++ and the other one 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++ and C.

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.

  • C++
    #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; \
    } \
    }
  • C
    #define ADD_STRING_PROPERTY(prop_name, prop_value, prop, size, value, \
    size_ret) \
    { \
    if ((prop) == (prop_name)) { \
    if ((value) != NULL) { \
    if ((size) < strlen(prop_value) + 1) { \
    return QDMI_ERROR_INVALIDARGUMENT; \
    } \
    strncpy((char *)(value), prop_value, (size) - 1); \
    ((char *)(value))[(size) - 1] = '\0'; \
    } \
    if ((size_ret) != NULL) { \
    *(size_ret) = strlen(prop_value) + 1; \
    } \
    return QDMI_SUCCESS; \
    } \
    }

A similar macro is defined for other (fixed length) data types, for example, int, double.

  • C++
    #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; \
    } \
    }
  • C
    #define ADD_SINGLE_VALUE_PROPERTY(prop_name, prop_type, prop_value, prop, \
    size, value, size_ret) \
    { \
    if ((prop) == (prop_name)) { \
    if ((value) != NULL) { \
    if ((size) < sizeof(prop_type)) { \
    return QDMI_ERROR_INVALIDARGUMENT; \
    } \
    *(prop_type *)(value) = prop_value; \
    } \
    if ((size_ret) != NULL) { \
    *(size_ret) = sizeof(prop_type); \
    } \
    return QDMI_SUCCESS; \
    } \
    }

Another macro is defined for list properties of the data types above.

  • C++
    #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; \
    } \
    }
  • C
    #define ADD_LIST_PROPERTY(prop_name, prop_type, prop_values, prop_length, \
    prop, size, value, size_ret) \
    { \
    if ((prop) == (prop_name)) { \
    if ((value) != NULL) { \
    if ((size) < (prop_length) * sizeof(prop_type)) { \
    return QDMI_ERROR_INVALIDARGUMENT; \
    } \
    memcpy((void *)(value), (const void *)(prop_values), \
    (prop_length) * sizeof(prop_type)); \
    } \
    if ((size_ret) != NULL) { \
    *(size_ret) = (prop_length) * 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.

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.

  • C++
    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)
    }
    @ QDMI_DEVICE_PROPERTY_NEEDSCALIBRATION
    size_t Whether the device needs calibration.
    Definition constants.h:220
    @ QDMI_DEVICE_PROPERTY_COUPLINGMAP
    QDMI_Site* (QDMI_Site list) The coupling map of the device.
    Definition constants.h:208
    @ QDMI_DEVICE_PROPERTY_OPERATIONS
    QDMI_Operation* (QDMI_Operation list) The operations supported by the device.
    Definition constants.h:196
    @ QDMI_DEVICE_PROPERTY_SITES
    QDMI_Site* (QDMI_Site list) The sites of the device.
    Definition constants.h:189
  • C
    int C_QDMI_device_session_query_device_property(C_QDMI_Device_Session session,
    const size_t size, void *value,
    size_t *size_ret) {
    ADD_LIST_PROPERTY(QDMI_DEVICE_PROPERTY_SITES, C_QDMI_Site, C_DEVICE_SITES, 5,
    prop, size, value, size_ret)
    ADD_LIST_PROPERTY(QDMI_DEVICE_PROPERTY_OPERATIONS, C_QDMI_Operation,
    C_DEVICE_OPERATIONS, 4, prop, size, value, size_ret)
    ADD_LIST_PROPERTY(
    ((C_QDMI_Site[]){C_DEVICE_SITES[0], C_DEVICE_SITES[1], C_DEVICE_SITES[1],
    C_DEVICE_SITES[0], C_DEVICE_SITES[1], C_DEVICE_SITES[2],
    C_DEVICE_SITES[2], C_DEVICE_SITES[1], C_DEVICE_SITES[2],
    C_DEVICE_SITES[3], C_DEVICE_SITES[3], C_DEVICE_SITES[2],
    C_DEVICE_SITES[3], C_DEVICE_SITES[4], C_DEVICE_SITES[4],
    C_DEVICE_SITES[3], C_DEVICE_SITES[4], C_DEVICE_SITES[0],
    C_DEVICE_SITES[0], C_DEVICE_SITES[4]}),
    20, 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)
    }

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.

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.

  • C++
    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:374
    @ QDMI_SUCCESS
    The operation was successful.
    Definition constants.h:39
  • C
    int C_QDMI_device_session_create_device_job(C_QDMI_Device_Session session,
    C_QDMI_Device_Job *job) {
    if (session == NULL || job == NULL) {
    }
    if (session->status != INITIALIZED) {
    }
    *job = (C_QDMI_Device_Job)malloc(sizeof(C_QDMI_Device_Job_impl_t));
    (*job)->session = session;
    // set job id to random number for demonstration purposes
    (*job)->id = rand();
    (*job)->status = QDMI_JOB_STATUS_CREATED;
    (*job)->num_shots = 0;
    (*job)->results = NULL;
    (*job)->state_vec = NULL;
    (*job)->program = NULL;
    return QDMI_SUCCESS;
    }

The function QDMI_device_job_set_parameter allows setting different parameters for the job, for example, the number of shots (QDMI_JOB_PARAMETER_SHOTSNUM).

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.

  • C++
    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 and is waiting to be executed.
    Definition constants.h:376
    @ QDMI_JOB_STATUS_RUNNING
    The job is running, and the result is not yet available.
    Definition constants.h:380
    @ QDMI_JOB_STATUS_DONE
    The job is done, and the result can be retrieved.
    Definition constants.h:378
    @ QDMI_DEVICE_STATUS_BUSY
    The device is busy.
    Definition constants.h:254
  • C
    int C_QDMI_device_job_submit(C_QDMI_Device_Job job) {
    if (job == NULL || 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;
    }
    C_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;
    C_QDMI_device_session_query_device_property(
    job->session, QDMI_DEVICE_PROPERTY_QUBITSNUM, sizeof(size_t), &num_qubits,
    NULL);
    job->results_length = job->num_shots * (num_qubits + 1);
    job->results = (char *)malloc(job->results_length);
    for (size_t i = 0; i < job->num_shots; ++i) {
    // generate random bitstring
    for (size_t j = 0; j < num_qubits; ++j) {
    *(job->results + (i * (num_qubits + 1) + j)) = (rand() % 2) ? '1' : '0';
    }
    if (i < job->num_shots - 1) {
    *(job->results + ((i + 1) * (num_qubits + 1) - 1)) = ',';
    }
    }
    *(job->results + (job->results_length - 1)) = '\0';
    // Generate random complex numbers and calculate the norm
    job->state_vec_length = 2ULL << num_qubits;
    job->state_vec = (double *)malloc(job->state_vec_length * sizeof(double));
    double norm = 0.0;
    for (size_t i = 0; i < job->state_vec_length / 2; ++i) {
    const double real_part = (((double)rand() / RAND_MAX) * 2.0) - 1.0;
    const double imag_part = (((double)rand() / RAND_MAX) * 2.0) - 1.0;
    norm += real_part * real_part + imag_part * imag_part;
    job->state_vec[2UL * i] = real_part;
    job->state_vec[(2UL * i) + 1] = imag_part;
    }
    // Normalize the vector
    norm = sqrt(norm);
    for (size_t i = 0; i < job->state_vec_length; ++i) {
    // NOLINTNEXTLINE(*-core.UndefinedBinaryOperatorResult)
    job->state_vec[i] = job->state_vec[i] / norm;
    }
    return QDMI_SUCCESS;
    }

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 and device.c for the C implementation.