توسعة Qiskit في Python باستخدام C
لتسريع برامج Qiskit المكتوبة بـ Python باستخدام C، يمكنك الاستفادة من امتداد C الخاص بـ Qiskit لـ Python. يست لزم هذا خطوات إضافية مقارنةً بالاستخدام المستقل لـ C؛ لمزيد من التفاصيل، راجع دليل تثبيت Qiskit C API.
واجهة برمجة تطبيقات Qiskit C لا تزال تجريبية، ولم تلتزم بعد بواجهة برمجية أو ثنائية مستقرة بالكامل. وحدات الامتداد المبنية مع Qiskit مضمونة للعمل فقط مع الإصدار المستخدم في عملية البناء.
هذه التعليمات جُربت فقط على أنظمة شبيهة بـ UNIX. تعليمات Windows قيد الإعداد.
المتطلبات
ابدأ بالتأكد من تثبيت Qiskit C API. بعد ذلك، ثبّت واجهة Qiskit Python كما يلي:
pip install -r requirements.txt -c constraints.txt
pip install .
تعريف امتداد C
ثمة خيارات متعددة لكتابة امتداد C لـ Python. لتبسيط الأمور، يبدأ هذا الدليل بنهج يستخدم وحدة ctypes المدمجة في Python. في القسم التالي، يوفر قسم امتداد C اليدوي مثالاً على بناء امتداد C باستخدام C API الخاص بـ Python للحدّ من عبء وقت التشغيل.
كمثال، افترض أنك كتبت دالة C لبناء observable وتريد إرجاعها إلى Python. يمكنك تحويل QkObs* من جانب C إلى كائن SparseObservable من جانب Python، باستخدام المحوّل المقدَّم 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;
}
يوضّح ما يلي كيفية تجميع هذا في مكتبة مشتركة - مثلاً qiskit_cextension.so.
بعد الانتهاء من ذلك، يمكنك استدعاء برنامج C من 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)
البناء
أولاً، عليك بناء امتداد Qiskit Python. يتضمن هذا رموز C حتى تتمكن من الوصول إلى كلتا الواجهتين عبر ن فس المكتبة المشتركة. هذا مهم لضمان تمرير البيانات بشكل صحيح بين C وPython.
python setup.py build_rust --inplace --release
تُسمى المكتبة المشتركة _accelerate.<platform-specific-part>. ابحث عن موقعها واسمها كما يلي:
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__))")
ستحتاج إلى معرفة موقع ملفات الإدراج (Python.h) والمكتبات (libpython.<suffix>) لـ Python في البيئة.
يمكن التعرف عليها مثلاً بـ
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")
(إذا كنت تعرف هذه المواقع والأسماء مسبقاً، يمكنك تعيينها مباشرةً.)
الربط
قد يختلف الربط بين المنصات وأدوات الربط. يصف ما يلي حلاً لأدوات الربط التي تدعم المكتبات بأسماء اعتباطية، باستخدام علامة -l: (مثل أداة الربط ld من GNU).
راجع القسم أدناه إذا كانت أداة الربط لديك تتطلب أن تُسمى المكتبة lib<something> (مثل أداة الربط ldd الشائعة على MacOS).
يمكنك بناء الامتداد بتحديد الاسم الكامل لمكتبة _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
ثم، أدخل فقط python main.py لتشغيل برنامج Python.
بديل عن استخدام اسم المكتبة الدقيق مع -l: هو إنشاء رابط رمزي لمكتبة _accelerate إلى الاسم المطلوب.
لتضمين المكتبة المشتركة _accelerate، أنشئ رابطاً رمزياً لها بالصيغة التي تتوقعها أداة الربط lib<library name>.<suffix>:
ln -s $QKLIB/$QKNAME $QKLIB/libqiskit.<suffix>
حيث <suffix> هو مثلاً so على Linux أو dylib على MacOS. هذا يتيح استخدام qiskit كاسم مكتبة:
gcc extension.c -fpic -shared -o qiskit_cextension.so \
-I/path/to/dist/c/include -L$QKLIB -lqiskit \
-I$PYINCLUDE -L$PYLIB -l$PYNAME
ثم، أدخل فقط python main.py لتشغيل برنامج Python.
امتداد C اليدوي
بدلاً من استخدام ctypes، يمكن بناء امتداد لـ Python يدوياً باستخدام C API الخاص بـ Python مباشرةً. قد يكون هذا أسرع من استخدام ctypes، غير أنه يتطلب مجهوداً أكبر في التنفيذ.
الكود التالي مثال موجز على كيفية تحقيق ذلك.
// 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); }
لتجميع مكتبة مشتركة، اربط مكتبتَي Python وQiskit كما هو موضح في قسم البناء أعلاه. لا يحتاج سكريبت Python حينئذٍ إلى ctypes، بل يمكنه استيراد وحدة cextension مباشرةً (تأكد من وجودها في مسار 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)