Extinde Qiskit în Python cu C
Pentru a accelera programele tale Python Qiskit cu C, poți folosi extensia C Qiskit pentru Python. Aceasta necesită pași suplimentari față de utilizarea standalone a C; pentru mai multe detalii, consultă ghidul de instalare a API-ului C Qiskit.
API-ul C Qiskit este încă experimental și nu s-a angajat încă la o interfață de programare sau binară complet stabilă. Modulele de extensie construite împotriva Qiskit au garanția că funcționează doar cu versiunea Qiskit utilizată în build.
Aceste instrucțiuni au fost testate doar pe sisteme de tip UNIX. Instrucțiunile pentru Windows sunt în curs de elaborare.
Cerințe
Începe prin a te asigura că ai instalat API-ul C Qiskit. Apoi, instalează interfața Python Qiskit, după cum urmează:
pip install -r requirements.txt -c constraints.txt
pip install .
Definește extensia C
Există diverse opțiuni pentru a scrie o extensie C pentru Python. Pentru simplitate, acest ghid începe cu o abordare care folosește modulul integrat ctypes al Python. În secțiunea următoare, secțiunea Extensia C manuală oferă un exemplu de construire a extensiei C folosind API-ul C al Python pentru a reduce overhead-ul la execuție.
Ca exemplu, să presupunem că scrii o funcție C pentru a construi un observable și dorești să îl returnezi în Python. Poți converti un QkObs* din partea C într-un obiect Python SparseObservable, folosind convertorul furnizat
qk_obs_to_python:
// file: extension.c
#define PY_SSIZE_T_CLEAN
#include <Python.h> // include Python header for access to PyObject
#define QISKIT_C_PYTHON_INTERFACE // enable C->Python conversion functions
#include <qiskit.h>
PyObject *build_observable(void) {
QkObs *obs = qk_obs_zero(100);
// build the observable ...
PyObject *pyobj = qk_obs_to_python(obs); // convert to Qiskit's Python ``SparseObservable``
qk_obs_free(obs);
return pyobj;
}
Următorul exemplu demonstrează cum să compilezi aceasta într-o bibliotecă partajată - de exemplu, qiskit_cextension.so.
Odată ce acest lucru este realizat, poți apela programul C din Python:
# file: main.py
import qiskit
import ctypes
# Load the extension, ensuring the global interpreter lock (GIL) is acquired for function calls,
# which you need for the C->Python object conversion.
lib = ctypes.PyDLL("/path/to/qiskit_cextension.so")
lib.build_observable.argtypes = None # set argument types to the function
lib.build_observable.restype = ctypes.py_object # set return type
# now you can directly call the function
obs = lib.build_observable()
print("SparseObservable instance?", isinstance(obs, qiskit.quantum_info.SparseObservable))
print(obs)
Build
Mai întâi, trebuie să construiești extensia Python Qiskit. Aceasta include simbolurile C, astfel încât să poți accesa ambele interfețe prin aceeași bibliotecă partajată. Acest lucru este important pentru a te asigura că datele pot fi transmise corect între C și Python.
python setup.py build_rust --inplace --release
Biblioteca partajată se numește _accelerate.<platform-specific-part>. Găsește locația și numele ei după cum urmează:
QKLIB=$(python -c "import os; import qiskit; print(os.path.dirname(qiskit._accelerate.__file__))")
QKNAME=$(python -c "import os; import qiskit; print(os.path.basename(qiskit._accelerate.__file__))")
Va trebui să cunoști locația include-urilor Python ale mediului (Python.h) și a bibliotecilor (libpython.<suffix>).
Acestea pot fi identificate, de exemplu, cu
PYINCLUDE=$(python -c "import sysconfig; print(sysconfig.get_path('include'))")
PYLIB=$(python -c "import sysconfig; print(sysconfig.get_config_var('LIBDIR'))")
PYNAME=$(find $PYLIB -maxdepth 1 -name "libpython*" | grep -oE "[^/]+$" | grep -oE "python[0-9]+\.[0-9]+" || echo "python")
(Dacă știi deja aceste locații și nume, le poți seta direct.)
Link
Procesul de link-uire poate diferi în funcție de platformă și de linker. Cele ce urmează descriu o soluție pentru linker-e care suportă biblioteci cu nume arbitrare, folosind flag-ul -l: (cum ar fi linker-ul ld de la GNU).
Consultă mai jos dacă linker-ul tău necesită ca biblioteca să se numească lib<ceva> (cum ar fi linker-ul ldd
obișnuit pe MacOS).
Poți construi extensia specificând numele complet al bibliotecii _accelerate:
gcc extension.c -fpic -shared -o cextension.so \
-I/path/to/dist/c/include -L$QKLIB -l:$QKNAME \
-I$PYINCLUDE -L$PYLIB -l$PYNAME
Apoi, pur și simplu introdu python main.py pentru a rula programul Python.
O alternativă la utilizarea numelui exact al bibliotecii cu -l: este crearea unui symlink pentru biblioteca _accelerate
la numele dorit.
Pentru a include biblioteca partajată _accelerate, creează un symlink la formatul așteptat de linker, lib<nume bibliotecă>.<sufix>:
ln -s $QKLIB/$QKNAME $QKLIB/libqiskit.<suffix>
unde <suffix> este, de exemplu, so pe Linux sau dylib pe MacOS. Aceasta permite utilizarea numelui qiskit ca nume de bibliotecă:
gcc extension.c -fpic -shared -o qiskit_cextension.so \
-I/path/to/dist/c/include -L$QKLIB -lqiskit \
-I$PYINCLUDE -L$PYLIB -l$PYNAME
Apoi, pur și simplu introdu python main.py pentru a rula programul Python.
Extensia C manuală
În loc să folosești ctypes, este posibil să construiești manual o extensie pentru Python folosind API-ul C al Python direct. Aceasta are potențialul de a fi
mai rapidă decât utilizarea ctypes, deși necesită mai mult efort de implementare.
Următorul cod este un exemplu scurt despre cum să realizezi acest lucru.
// file: extension.c
#define PY_SSIZE_T_CLEAN
#include <Python.h>
#include <stdio.h>
#define QISKIT_C_PYTHON_INTERFACE
#include <qiskit.h>
QkObs *build_observable() {
// build a 100-qubit empty observable
u_int32_t num_qubits = 100;
QkObs *obs = qk_obs_zero(num_qubits);
// add the term 2 * (X0 Y1 Z2) to the observable
complex double coeff = 2; // the coefficient
QkBitTerm bit_terms[3] = {QkBitTerm_X, QkBitTerm_Y, QkBitTerm_Z}; // bit terms: X Y Z
uint32_t indices[3] = {0, 1, 2}; // indices: 0 1 2
QkObsTerm term = {coeff, 3, bit_terms, indices, num_qubits};
qk_obs_add_term(obs, &term); // append the term
return obs;
}
/// Define the Python function, which will internally build the QkObs using the
/// C function defined above, and then convert the C object to the Python equivalent:
/// a SparseObservable, handled as PyObject.
static PyObject *cextension_build_observable(PyObject *self, PyObject *args) {
// At this point, ``args`` could be parsed for arguments. See PyArg_ParseTuple for details.
QkObs *obs = build_observable(); // call the C function to build the observable
PyObject *py_obs = qk_obs_to_python(obs); // convert QkObs to the Python-equivalent
return py_obs;
}
/// Define the module methods.
static PyMethodDef CExtMethods[] = {
{"build_observable", cextension_build_observable, METH_VARARGS, "Build an observable."},
{NULL, NULL, 0, NULL}, // sentinel
};
/// Define the module, which here is called ``cextension``.
static struct PyModuleDef cextension = {
PyModuleDef_HEAD_INIT,
"cextension", // module name
NULL, // docs
-1, // keep the module state in global variables
CExtMethods,
};
PyMODINIT_FUNC PyInit_cextension(void) { return PyModule_Create(&cextension); }
Pentru a compila o bibliotecă partajată, link-uiește atât bibliotecile Python, cât și Qiskit, după cum este descris în
secțiunea Build de mai sus. Scriptul Python nu are nevoie atunci de ctypes
și poate importa direct modulul cextension (asigură-te că se află în calea Python):
# file: main.py
import qiskit
import cextension
# directly call the function
obs = cextension.build_observable()
print("SparseObservable instance?", isinstance(obs, qiskit.quantum_info.SparseObservable))
print(obs)