Rozšíření Qiskitu v Pythonu pomocí C
Chceš-li zrychlit své Qiskit Python programy pomocí C, můžeš použít rozšíření Qiskit C pro Python. To vyžaduje další kroky navíc oproti samostatnému použití v C; více podrobností najdeš v průvodci Instalace Qiskit C API.
Qiskit C API je stále experimentální a zatím se nezavázalo k plně stabilnímu programovacímu nebo binárnímu rozhraní. Rozšiřující moduly sestavené proti Qiskitu jsou zaručeny funkční pouze s verzí Qiskitu použitou při sestavení.
Tyto instrukce byly testovány pouze na systémech podobných UNIXu. Instrukce pro Windows jsou v přípravě.
Požadavky
Začni tím, že se ujistíš, že máš nainstalované Qiskit C API. Dále nainstaluj Qiskit Python rozhraní následovně:
pip install -r requirements.txt -c constraints.txt
pip install .
Definice rozšíření v C
Existují různé možnosti, jak napsat rozšíření v C pro Python. Pro jednoduchost tento průvodce začíná přístupem, který používá vestavěný modul ctypes Pythonu. V další části sekce Ruční rozšíření C ukazuje příklad sestavení rozšíření v C pomocí C API Pythonu za účelem snížení režijní zátěže za běhu.
Jako příklad předpokládejme, že napíšeš C funkci pro sestavení observablu a chceš ji vrátit do Pythonu. Můžeš převést QkObs* na straně C na objekt SparseObservable na straně Pythonu pomocí poskytnutého konvertoru 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;
}
Následující příklad ukazuje, jak toto zkompilovat do sdílené knihovny – například qiskit_cextension.so.
Po dokončení můžeš volat program v C z Pythonu:
# 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)
Sestavení
Nejprve musíš sestavit Qiskit Python rozšíření. To zahrnuje symboly C, takže můžeš přistupovat k oběma rozhraním přes stejnou sdílenou knihovnu. To je důležité pro zajištění správného přenosu dat mezi C a Pythonem.
python setup.py build_rust --inplace --release
Sdílená knihovna se nazývá _accelerate.<platform-specific-part>. Její umístění a název zjistíš takto:
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__))")
Budeš potřebovat znát umístění Python includes (Python.h) a knihoven (libpython.<suffix>) prostředí.
Ty lze například zjistit pomocí
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")
(Pokud tato umístění a názvy již znáš, můžeš je také nastavit přímo.)
Linkování
Linkování se může lišit podle platformy a linkeru. Následující popis popisuje řešení pro linkery
podporující knihovny s libovolnými názvy pomocí přepínače -l: (například GNU linker ld).
Níže jsou instrukce pro případ, že tvůj linker vyžaduje, aby se knihovna jmenovala lib<něco> (například linker ldd běžný na MacOS).
Rozšíření můžeš sestavit zadáním plného názvu knihovny _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
Poté jednoduše zadej python main.py a spusť Python program.
Alternativou k použití přesného názvu knihovny s -l: je vytvoření symbolického odkazu na knihovnu _accelerate
s požadovaným názvem.
Pro zahrnutí sdílené knihovny _accelerate vytvoř symbolický odkaz ve formátu lib<název knihovny>.<přípona> očekávaném linkerem:
ln -s $QKLIB/$QKNAME $QKLIB/libqiskit.<suffix>
kde <suffix> je například so na Linuxu nebo dylib na MacOS. Tím umožníš použít qiskit jako název knihovny:
gcc extension.c -fpic -shared -o qiskit_cextension.so \
-I/path/to/dist/c/include -L$QKLIB -lqiskit \
-I$PYINCLUDE -L$PYLIB -l$PYNAME
Poté jednoduše zadej python main.py a spusť Python program.
Ruční rozšíření C
Místo použití ctypes je možné ručně sestavit rozšíření pro Python pomocí C API Pythonu přímo. To má potenciál být
rychlejší než použití ctypes, i když vyžaduje více úsilí při implementaci.
Následující kód je stručným příkladem, jak toho dosáhnout.
// 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); }
Pro zkompilování sdílené knihovny nalinkuj knihovny Python i Qiskit, jak je popsáno v sekci
Sestavení výše. Python skript pak nepotřebuje ctypes,
ale může přímo importovat modul cextension (ujisti se, že je na tvé Python cestě):
# 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)