QDMI v1.0.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_query_device_property_dev 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_query_device_property_dev 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, e.g., 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 pairs of QDMI_Site's. The pairs are flattened into a single list of QDMI_Site's.

  • C++
    constexpr static std::array<const CXX_QDMI_Site_impl_d *const, 20>
    DEVICE_COUPLING_MAP = {
    device_sites.data(), &device_sites[1],
    &device_sites[1], device_sites.data(),
    &device_sites[1], &device_sites[2],
    &device_sites[2], &device_sites[1],
    &device_sites[2], &device_sites[3],
    &device_sites[3], &device_sites[2],
    &device_sites[3], &device_sites[4],
    &device_sites[4], &device_sites[3],
    &device_sites[4], device_sites.data(),
    device_sites.data(), &device_sites[4]};
    int CXX_QDMI_query_device_property_dev(const QDMI_Device_Property prop,
    const size_t size, void *value,
    size_t *size_ret) {
    ADD_LIST_PROPERTY(QDMI_DEVICE_PROPERTY_COUPLINGMAP, CXX_QDMI_Site,
    DEVICE_COUPLING_MAP, prop, size, value, size_ret)
    }
    @ QDMI_DEVICE_PROPERTY_COUPLINGMAP
    int* (int list) The coupling map of the device.
    Definition enums.h:53
  • C
    int C_QDMI_query_device_property_dev(const QDMI_Device_Property prop,
    const size_t size, void *value,
    size_t *size_ret) {
    ADD_LIST_PROPERTY(
    ((C_QDMI_Site[]){
    DEVICE_SITES[0], DEVICE_SITES[1], DEVICE_SITES[1], DEVICE_SITES[0],
    DEVICE_SITES[1], DEVICE_SITES[2], DEVICE_SITES[2], DEVICE_SITES[1],
    DEVICE_SITES[2], DEVICE_SITES[3], DEVICE_SITES[3], DEVICE_SITES[2],
    DEVICE_SITES[3], DEVICE_SITES[4], DEVICE_SITES[4], DEVICE_SITES[3],
    DEVICE_SITES[4], DEVICE_SITES[0], DEVICE_SITES[0], DEVICE_SITES[4]}),
    20, prop, size, value, size_ret)
    }

Complex Properties

The properties that are returned by QDMI_query_operation_property_dev may depend on the actual site. The available QDMI_Operation's and QDMI_Site's, first, need to be retrieved through QDMI_query_get_operations_dev and QDMI_query_get_sites_dev, respectively. Following is an example of how to implement the QDMI_query_get_sites_dev function.

  • C++
    std::array<CXX_QDMI_Site_impl_d, 7> device_sites = {
    CXX_QDMI_Site_impl_d{0}, CXX_QDMI_Site_impl_d{1}, CXX_QDMI_Site_impl_d{2},
    CXX_QDMI_Site_impl_d{3}, CXX_QDMI_Site_impl_d{4}};
    int CXX_QDMI_query_get_sites_dev(const size_t num_entries, CXX_QDMI_Site *sites,
    size_t *num_sites) {
    if ((sites != nullptr && num_entries == 0) ||
    (sites == nullptr && num_sites == nullptr)) {
    }
    if (sites != nullptr) {
    for (size_t i = 0; i < std::min(num_entries, device_sites.size()); ++i) {
    sites[i] = &device_sites[i];
    }
    }
    if (num_sites != nullptr) {
    *num_sites = device_sites.size();
    }
    return QDMI_SUCCESS;
    }
    @ QDMI_SUCCESS
    The operation was successful.
    Definition enums.h:189
  • C
    const C_QDMI_Site DEVICE_SITES[] = {
    &(C_QDMI_Site_impl_t){0}, &(C_QDMI_Site_impl_t){1},
    &(C_QDMI_Site_impl_t){2}, &(C_QDMI_Site_impl_t){3},
    &(C_QDMI_Site_impl_t){4}};
    int C_QDMI_query_get_sites_dev(const size_t num_entries, C_QDMI_Site *sites,
    size_t *num_sites) {
    if ((sites != NULL && num_entries == 0) ||
    (sites == NULL && num_sites == NULL)) {
    }
    const size_t device_sites_size =
    sizeof(DEVICE_SITES) / sizeof(DEVICE_SITES[0]);
    if (sites != NULL) {
    const size_t copy_size =
    (num_entries < device_sites_size ? num_entries : device_sites_size);
    memcpy((void *)sites, (const void *)DEVICE_SITES,
    copy_size * sizeof(C_QDMI_Site));
    }
    if (num_sites != NULL) {
    *num_sites = device_sites_size;
    }
    return QDMI_SUCCESS;
    }

With the handles for a QDMI_Operation and QDMI_Site, corresponding properties can be queried. The following example demonstrates how different properties of operations, e.g., varying fidelities of two-qubit gates can be returned.

  • C++
    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 static 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 = {
    {&device_operations[3],
    {{{device_sites.data(), &device_sites[1]}, 0.99},
    {{&device_sites[1], device_sites.data()}, 0.99},
    {{&device_sites[1], &device_sites[2]}, 0.98},
    {{&device_sites[2], &device_sites[1]}, 0.98},
    {{&device_sites[2], &device_sites[3]}, 0.97},
    {{&device_sites[3], &device_sites[2]}, 0.97},
    {{&device_sites[3], &device_sites[4]}, 0.96},
    {{&device_sites[4], &device_sites[3]}, 0.96},
    {{&device_sites[4], device_sites.data()}, 0.95},
    {{device_sites.data(), &device_sites[4]}, 0.95}}},
    // No need to specify single-qubit fidelities here
    };
    int CXX_QDMI_query_operation_property_dev(
    CXX_QDMI_Operation operation, size_t num_sites, const CXX_QDMI_Site *sites,
    QDMI_Operation_Property prop, size_t size, void *value, size_t *size_ret) {
    if (prop >= QDMI_OPERATION_PROPERTY_MAX || operation == nullptr ||
    (sites != nullptr && num_sites == 0) ||
    (value == nullptr && size_ret == nullptr)) {
    }
    // General properties
    ADD_STRING_PROPERTY(QDMI_OPERATION_PROPERTY_NAME, operation->name, prop, size,
    value, size_ret)
    if (strcmp(operation->name, "cx") == 0) {
    if (sites != nullptr && num_sites != 2) {
    }
    ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_DURATION, double,
    OPERATION_DURATIONS.at(operation), 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 (strcmp(operation->name, "rx") == 0 ||
    strcmp(operation->name, "ry") == 0 ||
    strcmp(operation->name, "rz") == 0) {
    if (sites != nullptr && num_sites != 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_FIDELITY, double, 0.999,
    prop, size, value, size_ret)
    }
    }
    enum QDMI_OPERATION_PROPERTY_T QDMI_Operation_Property
    Type of the operation properties.
    Definition types.h:45
    @ QDMI_OPERATION_PROPERTY_MAX
    The maximum value of the enum.
    Definition enums.h:151
    @ QDMI_OPERATION_PROPERTY_QUBITSNUM
    size_t The number of qubits in the operation.
    Definition enums.h:126
    @ QDMI_OPERATION_PROPERTY_NAME
    char* (string) The string identifier of the operation.
    Definition enums.h:124
    @ QDMI_OPERATION_PROPERTY_DURATION
    double The duration of an operation in µs.
    Definition enums.h:128
    @ QDMI_OPERATION_PROPERTY_FIDELITY
    double The fidelity of an operation.
    Definition enums.h:130
  • C
    int C_QDMI_query_operation_property_dev(C_QDMI_Operation operation,
    const size_t num_sites,
    const C_QDMI_Site *sites,
    const size_t size, void *value,
    size_t *size_ret) {
    if (prop >= QDMI_OPERATION_PROPERTY_MAX || operation == NULL ||
    (sites != NULL && num_sites == 0) ||
    (value == NULL && size_ret == NULL)) {
    }
    // General properties
    ADD_STRING_PROPERTY(QDMI_OPERATION_PROPERTY_NAME, operation->name, prop, size,
    value, size_ret)
    // Two-qubit gates
    if (strcmp(operation->name, "cx") == 0) {
    if (sites != NULL && num_sites != 2) {
    }
    ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_DURATION, double, 0.01,
    prop, size, value, size_ret)
    if (sites == NULL) {
    ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_QUBITSNUM, size_t, 2,
    prop, size, value, size_ret)
    }
    if (sites[0] == sites[1]) {
    }
    if ((sites[0]->id == 0 && sites[1]->id == 1) ||
    (sites[0]->id == 1 && sites[1]->id == 0)) {
    ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_FIDELITY, double, 0.99,
    prop, size, value, size_ret)
    }
    if ((sites[0]->id == 1 && sites[1]->id == 2) ||
    (sites[0]->id == 2 && sites[1]->id == 1)) {
    ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_FIDELITY, double, 0.98,
    prop, size, value, size_ret)
    }
    if ((sites[0]->id == 2 && sites[1]->id == 3) ||
    (sites[0]->id == 3 && sites[1]->id == 2)) {
    ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_FIDELITY, double, 0.97,
    prop, size, value, size_ret)
    }
    if ((sites[0]->id == 3 && sites[1]->id == 4) ||
    (sites[0]->id == 4 && sites[1]->id == 3)) {
    ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_FIDELITY, double, 0.96,
    prop, size, value, size_ret)
    }
    if ((sites[0]->id == 4 && sites[1]->id == 0) ||
    (sites[0]->id == 0 && sites[1]->id == 4)) {
    ADD_SINGLE_VALUE_PROPERTY(QDMI_OPERATION_PROPERTY_FIDELITY, double, 0.95,
    prop, size, value, size_ret)
    }
    }
    }
    // Single-qubit gates
    else if (strcmp(operation->name, "rx") == 0 ||
    strcmp(operation->name, "ry") == 0 ||
    strcmp(operation->name, "rz") == 0) {
    if (sites != NULL && num_sites != 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_FIDELITY, double, 0.999,
    prop, size, value, size_ret)
    }
    }

Submitting a Job

One crucial part of QDMI is, that it allows to submit 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_control_create_job_dev.

The function QDMI_control_set_parameter_dev allows to set different parameters for the job, e.g., the number of shots (QDMI_JOB_PARAMETER_SHOTS_NUM).

After the job is set up, it can be submitted to the device. The following example shows a mock implementation of QDMI_control_submit_job_dev.

  • C++
    int CXX_QDMI_control_submit_job_dev(CXX_QDMI_Job job) {
    if (job == nullptr || job->status != QDMI_JOB_STATUS_CREATED) {
    }
    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_query_device_property_dev(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 enums.h:165
    @ QDMI_JOB_STATUS_RUNNING
    The job is running, and the result is not yet available.
    Definition enums.h:169
    @ QDMI_DEVICE_STATUS_BUSY
    The device is busy.
    Definition enums.h:81
  • C
    int C_QDMI_control_submit_job_dev(C_QDMI_Job job) {
    if (job == NULL || job->status != QDMI_JOB_STATUS_CREATED) {
    }
    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_query_device_property_dev(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, i.e., device.cpp for the C++ implementation and device.c for the C implementation.