Munich-Quantum-Software-Stack / QDMI

Quantum Device Management Interface (QDMI)
https://munich-quantum-software-stack.github.io/QDMI/
Apache License 2.0
26 stars 0 forks source link

Setting sentinel values to gate coupling mappings #2

Closed Durganshu closed 7 months ago

Durganshu commented 7 months ago

Some quantum backends provide coupling maps for gates as well. The coupling maps provide a list of qubits that the gate applies to, where each element of the list is an n-qubit list where n is the size of the gate (e.g. 1-qubit gate, 2-qubit gate). For example, the coupling map for a CX gate on a 5-qubit backend could be: [[0, 1], [1, 0],[1, 2], [2, 1], [2, 3], [3, 2], [3, 4], [4, 3]].

struct QDMI_Gate_impl_d has a coupling_mapping member: https://github.com/Munich-Quantum-Software-Stack/QDMI/blob/ea3c65888cae9ee995a477e787333d535bbe2eb5/include/qdmi_internal.h#L130

If the backend doesn't provide a coupling_mapping for a particular gate, this must be set to NULL. If it does, this member stores the corresponding mapping as an int** (int [][]) as a typedef QDMI_Gate_property. However, this raw pointer doesn't store any information regarding the size of the coupling mapping. Also, there's no member in the QDMI_Gate_impl_d struct, which stores the gate size (1-qubit, 2-qubit, etc.). When a C++-based higher-level library like FoMac or sys-sage tries to access this information, it'll be convenient to provide it.

This PR presents one of the possible solutions: setting sentinel values at the end of the pointers pointed to by the coupling_mapping.

If the size of gate->coupling_mapping is N, memory for N+1 pointers to QDMI_Gate_property could be allocated to store an extra NULL value. Similarly, for every gate->coupling_mapping[i], where i = 0,1...N-1, i+N can store the value -1 (as qubit indices can never be negative). This will help higher-level libraries to get the count of the qubits involved and the size of the coupling mapping.

The reference solution is implemented for the IBM backend

Durganshu commented 7 months ago

If the coupling maps for the CX gate is: [[0, 1], [1, 0],[1, 2], [2, 1], [2, 3], [3, 2], [3, 4], [4, 3]]

This will be saved: [[0, 1, -1], [1, 0, -1],[1, 2, -1], [2, 1, -1], [2, 3, -1], [3, 2, -1], [3, 4, -1], [4, 3, -1], NULL]

This is one of the safest methods to ensure that sys-sage/ Fomac gets the accurate value for the size of this map and the number of qubits involved. However, this requires storing one extra value per map. So, an alternative solution can also be implemented:

typedef struct QDMI_Gate_impl_d
{
    const char *name;
    QDMI_Gate_property** coupling_mapping;
    char *unitary;
    float fidelity;
    size_t size_coupling_map; // New addition: 8 for the above example
    size_t gate_size; // New addition: refers to the gate size (1-qubit, 2-qubits, 3-qubits, etc.)
} QDMI_Gate_impl_t;
burgholzer commented 7 months ago

If the coupling maps for the CX gate is: [[0, 1], [1, 0],[1, 2], [2, 1], [2, 3], [3, 2], [3, 4], [4, 3]]

This will be saved: [[0, 1, -1], [1, 0, -1],[1, 2, -1], [2, 1, -1], [2, 3, -1], [3, 2, -1], [3, 4, -1], [4, 3, -1], NULL]

This is one of the safest methods to ensure that sys-sage/ Fomac gets the accurate value for the size of this map and the number of qubits involved. However, this requires storing one extra value per map. So, an alternative solution can also be implemented:


typedef struct QDMI_Gate_impl_d

{

    const char *name;

    QDMI_Gate_property** coupling_mapping;

    char *unitary;

    float fidelity;

    size_t size_coupling_map; // New addition: 8 for the above example

    size_t gate_size; // New addition: refers to the gate size (1-qubit, 2-qubits, 3-qubits, etc.)

} QDMI_Gate_impl_t;

Just a quick thought, but isn't the second solution here much more efficient? Instead of adding |E|+1 64 bit values, it just adds 2, unconditionally. And these two perfectly describe how to interpret the int** coupling map.

Durganshu commented 7 months ago

Hi @burgholzer

Yes, it is. But since all the QDMI backends follow the same standard, I didn't want to implement the second method without getting a confirmation from others, so that every backend will have same struct members. This will ensure consistency in what QDMI can provide. @echavarria-lrz @kayaercument @martin-knudsen What do you think about that?

burgholzer commented 7 months ago

Hi @burgholzer

Yes, it is. But since all the QDMI backends follow the same standard, I didn't want to implement the second method without getting a confirmation from others, so that every backend will have same struct members. This will ensure consistency in what QDMI can provide. @echavarria-lrz @kayaercument @martin-knudsen What do you think about that?

I see your concern. IMHO, at this moment in time, we should not be afraid to make breaking changes to the interface. Especially, if those changes allow for more efficient implementations of key characteristics and features. QDMI is in such an early stage that changes are almost inevitable.

Durganshu commented 7 months ago

Fair point. Let me modify the implementation to use the second method. Thanks!

echavarria-lrz commented 7 months ago

Hi @burgholzer

Yes, it is. But since all the QDMI backends follow the same standard, I didn't want to implement the second method without getting a confirmation from others, so that every backend will have same struct members. This will ensure consistency in what QDMI can provide. @echavarria-lrz @kayaercument @martin-knudsen What do you think about that?

That's actually how we handle qubits' coupling mappings:

typedef struct QDMI_Qubit_impl_d
{
    QDMI_qubit_index index;
    QDMI_qubit_index* coupling_mapping;
    int size_coupling_mapping;
} QDMI_Qubit_impl_t;
Durganshu commented 7 months ago

Yes, indeed. Thanks!