Lanjutkan Qiskit dalam Python dengan C
Qiskit C API boleh digunakan dalam modul sambungan Python. Anda boleh menulis bahagian kritikal prestasi sambungan Qiskit anda dalam C untuk mempercepatkannya, dan kemudian mengedarkannya dengan selamat kepada pengguna anda.
Panduan ini membawa anda melalui proses mentakrifkan modul sambungan yang lengkap, mengkonfigurasi
proses pembinaannya, dan mendedahkannya kepada pengguna Python. Pakej ini menyediakan port mudah bagi
AddSpectatorMeasures daripada tambahan Qiskit kepada C. Ini ialah laluan
tersuai sebenar dengan kes penggunaan sebenar dalam tambahan Qiskit.
Anda mungkin mendapati sumber luaran berikut berguna:
- Dokumentasi CPython tentang penulisan modul sambungan.
- Dokumentasi NumPy tentang penggunaan C API-nya.
Qiskit C API didedahkan untuk modul sambungan Python dengan cara yang sangat serupa dengan NumPy C API. Jika anda pernah mengaturcara sambungan NumPy sebelum ini, anda akan mendapati proses Qiskit ini sudah biasa.
Qiskit C API masih dalam peringkat eksperimental. Oleh itu, belum ada antara muka pengaturcaraan atau binari yang sepenuhnya stabil, dan mungkin ada perubahan yang memecahkan antara versi kecil.
Sebagai contoh, modul sambungan yang menggunakan Qiskit v2.4.0 semasa pembinaan dijamin berfungsi dengan Qiskit v2.4.1 semasa masa jalan, tetapi mungkin gagal apabila menggunakan Qiskit v2.5.0 semasa masa jalan.
Keperluanβ
Mulakan dari direktori yang bersih.
Anda mesti mempunyai rantaian alat pengkompil C standard yang tersedia untuk platform anda. Anda juga mesti mempunyai versi Python yang menyertakan pengepala C API-nya (ini adalah standard).
Anda seharusnya biasa dengan, atau bersedia untuk mencari, fungsi dan objek individu yang tersedia dalam Qiskit C API. Anda seharusnya mempunyai sedikit kebiasaan dengan pengaturcaraan C.
Cipta struktur direktoriβ
Kami akan menggunakan struktur direktori berasaskan src dan sistem pembinaan berasaskan setuptools yang mudah. Arahan
ini seharusnya mudah disesuaikan dengan mana-mana sistem pembinaan yang boleh membina
modul sambungan.
Struktur akhir akan kelihatan seperti:
extension-module
βββ pyproject.toml
βββ setup.py
βββ src
βββ spectator_measures
βββ __init__.py
βββ _coremodule.c
Ringkasnya:
pyproject.tomlmentakrifkan metadata statik standard tentang pakej Python yang kami cipta, termasuk nama, pengarang, dan kebergantungan masa pembinaan dan masa jalan.setup.pymengandungi konfigurasi dinamik minimum yang kami perlukan untuk membina modul sambungan kami.src/spectator_measures/__init__.pymentakrifkan antara muka yang berhadapan dengan pengguna dan menyediakan beberapa kod untuk berinteraksi dengan komponen ruang-Python Qiskit.src/spectator_measures/_coremodule.cmentakrifkan modul sambungan C, yang akan mengandungi semua kod kritikal prestasi pakej kami.
Kami akan memeriksa setiap fail secara terperinci, membina pakej dengan modul sambungannya.
Takrifkan metadata pakejβ
Mulakan dengan mentakrifkan fail pyproject.toml. Ini adalah standard untuk projek berasaskan
setuptools, walaupun qiskit adalah keperluan tambahan dalam tatasusunan build-system.requires,
sebagai tambahan kepada setuptools.
pyproject.toml
[build-system]
requires = [
"setuptools",
"qiskit~=2.4.0",
]
build-backend = "setuptools.build_meta"
[project]
name = "spectator_measures"
authors = [
{ name = "Qiskit Developer" },
]
version = "0.0.1"
dependencies = [
"qiskit~=2.4.0",
]
# If you intend to release your package, you should
# also set the `license` information, and so on.
[tool.setuptools]
package-dir = {"" = "src"}
Mulai Qiskit v2.4, C API belum lagi stabil di luar versi kecil (sebagai contoh, C API untuk v2.4.0 akan
serasi dengan v2.4.1 tetapi bukan v2.5.0). Pada masa depan, kami berhasrat untuk melanjutkan
kestabilan ini kepada versi utama. Buat masa ini, tetapkan versi masa jalan Qiskit dalam
project.dependencies untuk sepadan dengan versi kecil yang digunakan semasa pembinaan.
Dalam banyak projek berasaskan setuptools Python tulen, fail pyproject.toml sudah mencukupi. Walau bagaimanapun, modul kami memerlukan akses kepada fail pengepala Qiskit C API semasa
proses pembinaannya. Bermula dengan v2.4, ini disertakan dalam pengedaran Python SDK Qiskit.
Untuk mencari direktori yang mengandunginya, jalankan qiskit.capi.get_include().
Ini menghasilkan fail setup.py yang kelihatan seperti:
setup.py
import qiskit
from setuptools import setup, Extension
core_ext = Extension(
# The fully qualified module name of the extension.
name="spectator_measures._core",
# The C source files needed for the extension. The file
# name is conventionally `<mod>module.c`, where `<mod>`
# is the module name (`_core`, in this case).
sources=["src/spectator_measures/_coremodule.c"],
# Directories containing additional header files used in
# the build process.
include_dirs=[qiskit.capi.get_include()],
)
setup(ext_modules=[core_ext])
Kebanyakan maklumat pakej ditakrifkan dalam pyproject.toml, dan setuptools.setup() akan
turut membaca fail tersebut.
Lihat Panduan Pengguna setuptools untuk maklumat
lanjut tentang mengkonfigurasi projek berasaskan setuptools.
Tulis pembalut ruang-Pythonβ
Secara teknikalnya, adalah mungkin untuk mentakrifkan segalanya dalam sambungan Python daripada C. Dalam amalan, ia lebih mudah untuk berinteraksi dengan kod ruang-Python lain daripada Python itu sendiri.
Pakej ini mentakrifkan laluan Transpiler tersuai yang diterbitkan daripada kelas
qiskit.transpiler.TransformationPass ruang-Python, tetapi menggunakan fungsi daripada modul sambungan C untuk
semua logik perniagaannya. Ini kelihatan seperti:
src/spectator_measures/__init__.py
from qiskit.transpiler import TransformationPass, Target
from . import _core
__version__ = "0.0.1"
__all__ = ["AddSpectatorMeasures"]
class AddSpectatorMeasures(TransformationPass):
def __init__(
self,
target: Target,
*,
include_unmeasured: bool = False,
creg_name: str | None = None,
add_barrier: bool = True
):
super().__init__()
self.target = target
self.include_unmeasured = include_unmeasured
self.creg_name = creg_name
self.add_barrier = add_barrier
def run(self, dag):
# Delegate to our C extension module.
_core.add_spectator_measures(
dag,
self.target,
include_unmeasured=self.include_unmeasured,
creg_name=self.creg_name,
add_barrier=self.add_barrier,
)
return dag
Butiran tepat laluan ini tidak penting untuk panduan ini. Jika anda berminat, anda boleh
merujuk dokumentasi API AddSpectatorMeasures dalam
qiskit-addon-utils. Panduan ini menghasilkan port mudah bagi laluan tersebut,
tanpa sokongan untuk operasi aliran kawalan.
Tulis modul sambungan Cβ
Anda mungkin mendapati sumber berikut berguna:
Bahagian ini berkaitan dengan sambungan C sebenar. Ini adalah fail paling kompleks dalam projek, jadi kami akan membahagikannya kepada peringkat-peringkat.
Konfigurasi fail pengepalaβ
Apabila membina modul sambungan Python, anda mesti menyertakan Python.h sebelum sebarang fail lain.
Untuk menggunakan Qiskit C API dalam modul sambungan, anda mesti mentakrifkan makro
QISKIT_PYTHON_EXTENSION sebelum menyertakan qiskit.h.
Include kami kemudiannya kelihatan seperti:
src/spectator_measures/_coremodule.c
#define QISKIT_PYTHON_EXTENSION
#include <Python.h>
#include <qiskit.h>
#include <limits.h>
#include <stdbool.h>
#include <stdlib.h>
#include <string.h>
Tulis kod C API tulenβ
Seterusnya, tulis semua logik perniagaan sebagai kod Qiskit C API tulen. Kami akan mendedahkan logik ini kepada ruang Python dalam bahagian berikut.
Bahagian ini mengandungi hanya kod Qiskit C API tulen. Ia menggunakan jenis C API:
QkDag *, sepadan denganDAGCircuitruang-Python.QkTarget *, sepadan denganTargetruang-Python.QkNeighbors, jenis C API natif yang mewakili kekangan gandingan dua qubit.QkCircuitInstruction, jenis C API natif untuk menanya arahan individu.
Dua yang pertama membentuk sebahagian daripada interaksi kami dengan ruang Python, tetapi apabila bekerja dengannya, kami hanya perlu mempertimbangkan C API tulen. Tiada interaksi dengan jurubahasa Python dalam kod ini.
Perhatikan bahawa semua fungsi dan simbol yang ditakrifkan dalam bahagian ini dinyatakan dengan paut static.
Ini kerana jurubahasa Python tidak akan paut terhadap modul sambungan ini; kami akan menyediakan
jurubahasa dengan butiran fungsi yang tersedia dalam bahagian seterusnya.
Kami tidak akan memperincikan butiran algoritma kod ini; adalah instruktif untuk menggunakan laluan Transpiler yang bermakna untuk demonstrasi, tetapi pelaksanaan tepat algoritma tersebut tidak penting untuk panduan ini.
src/spectator_measures/_coremodule.c (appended)
/**
* The default name to use for `creg_name` if none is supplied.
*/
static char DEFAULT_CREG_NAME[] = "spec";
/**
* Is there a 2q link from the given qubit to any active qubit?
*/
static bool adjacent_to_active(QkNeighbors *adj, uint32_t qubit,
bool *active) {
for (uint32_t offset = adj->partition[qubit];
offset < adj->partition[qubit + 1]; offset++) {
if (active[adj->neighbors[offset]]) {
return true;
}
}
return false;
}
/**
* A transpiler pass that adds terminal measurements to all "spectator"
* qubits.
*/
static uint32_t add_spectator_measures(QkDag *dag,
const QkTarget *target,
bool include_unmeasured,
const char *creg_name,
bool add_barrier) {
uint32_t num_spectators = 0;
uint32_t num_qubits = qk_dag_num_qubits(dag);
uint32_t num_instructions = qk_dag_num_op_nodes(dag);
bool *active = calloc(num_qubits, sizeof(*active));
bool *is_additional_spectator =
calloc(num_qubits, sizeof(*is_additional_spectator));
uint32_t *spectators = malloc(num_qubits * sizeof(*spectators));
uint32_t *topological =
malloc(num_instructions * sizeof(*topological));
QkNeighbors neighbors;
QkCircuitInstruction instruction;
qk_neighbors_from_target(target, &neighbors);
qk_dag_topological_op_nodes(dag, topological);
for (uint32_t i = 0; i < num_instructions; i++) {
qk_dag_get_instruction(dag, topological[i], &instruction);
if (!strcmp(instruction.name, "barrier")) {
// Barriers don't count for the purposes of determining
// final measurements, either.
qk_circuit_instruction_clear(&instruction);
continue;
}
// If we're not adding measurements to "unmeasured" active
// qubits, then nothing counts as an additional "maybe
// spectator". If we are, then it's a maybe spectator if its
// last visited instruction was not a measure.
bool additional_spectator =
include_unmeasured && strcmp(instruction.name, "measure");
for (uint32_t *qarg = instruction.qubits;
qarg != instruction.qubits + instruction.num_qubits;
qarg++) {
active[*qarg] = true;
is_additional_spectator[*qarg] = additional_spectator;
}
qk_circuit_instruction_clear(&instruction);
}
for (uint32_t qubit = 0; qubit < num_qubits; qubit++) {
bool is_spectator =
!active[qubit] &&
adjacent_to_active(&neighbors, qubit, active);
is_spectator = is_spectator || is_additional_spectator[qubit];
if (is_spectator) {
spectators[num_spectators] = qubit;
num_spectators += 1;
}
}
if (num_spectators) {
uint32_t clbit = qk_dag_num_clbits(dag);
creg_name = creg_name ? creg_name : DEFAULT_CREG_NAME;
QkClassicalRegister *creg =
qk_classical_register_new(num_spectators, creg_name);
qk_dag_add_classical_register(dag, creg);
qk_classical_register_free(creg);
if (add_barrier) {
qk_dag_apply_barrier(dag, NULL, num_qubits, false);
}
for (uint32_t i = 0; i < num_spectators; i++) {
qk_dag_apply_measure(dag, spectators[i], clbit + i, false);
}
}
qk_neighbors_clear(&neighbors);
free(topological);
free(spectators);
free(is_additional_spectator);
free(active);
return num_spectators;
}
Tulis kod interaksi Pythonβ
Semua logik perniagaan kini ditakrifkan dalam C tulen. Seterusnya, ia perlu didedahkan dengan selamat kepada Python.
Untuk memulakan, takrifkan satu-satunya fungsi yang akan didedahkan kepada Python. Ini mesti
mengikut tandatangan yang ditakrifkan, yang sepenuhnya dalam sebutan jenis Python yang kelihatan seperti kaedah
fn(self, *args, **kwargs). Kami perlu mengembalikan PyObject *, yang merupakan bentuk generik bagi
sebarang objek Python.
Fungsi lengkap kelihatan seperti:
src/spectator_measures/_coremodule.c (appended)
static PyObject *py_add_spectator_measures(PyObject *self,
PyObject *args,
PyObject *kwargs) {
// Define space to hold the C-native handles we will parse out of the
// Python-space inputs.
QkDag *dag;
QkTarget *target;
const char *creg_name;
int include_unmeasured, add_barrier;
// This `kwlist` and `PyArg_Parse*` setup is standard Python C API
// programming for extension modules. We will examine the use of
// Qiskit C API functions within it afterwards.
static char *const kwlist[] = {
"dag", "target", "include_unmeasured",
"creg_name", "add_barrier", NULL};
if (!PyArg_ParseTupleAndKeywords(args, kwargs, "O&O&|pzp", kwlist,
qk_dag_convert_from_python, &dag,
qk_target_convert_from_python,
&target, &include_unmeasured,
&creg_name, &add_barrier)) {
// An error has occurred. The Python exception state will already
// be set, so we need to return the error indicator.
return NULL;
}
// Now we have C-native types, we can delegate to our C logic.
add_spectator_measures(dag, target, include_unmeasured, creg_name,
add_barrier);
Py_RETURN_NONE;
}
Ringkasnya, fungsi ini:
- Mengikuti tandatangan yang ditakrifkan untuk menerima argumen Python sewenang-wenangnya.
- Mentakrifkan ruang untuk menyimpan objek natif-C yang dihuraikan daripada argumen Python.
- Memanggil fungsi penghuraian untuk mengekstrak objek natif-C, dikonfigurasi dengan senarai argumen yang dijangka, argumen kata kunci, dan fungsi yang digunakan untuk menukarnya. Jika ini gagal, fungsi menyebarkan ralat.
- Mengagihkan kepada logik perniagaan natif-C bahagian sebelumnya, yang mengubah DAG di tempatnya.
- Mengembalikan objek
Noneruang-Python.
Logik paling kompleks semuanya berada dalam PyArg_ParseTupleAndKeywords. Ini didokumentasikan dengan baik dalam dokumentasi
CPython tentang menghurai argumen, yang patut anda
rujuk untuk maklumat lanjut.
Qiskit C API menyediakan beberapa fungsi dengan nama seperti qk_*_convert_from_python, yang direka
sebagai fungsi "penukar" untuk digunakan dengan fungsi PyArg_Parse*. Ini sepadan dengan
kunci O& dalam rentetan format; di sini, kami menggunakan qk_dag_convert_from_python dan
qk_target_convert_from_python. Fungsi-fungsi ini meminjam objek natif-C daripada argumen Python
yang ia diterbitkan. Ini bermakna mutasi akan disebarkan ke ruang Python, tetapi juga bahawa
anda harus berhati-hati untuk tidak melepaskan rujukan anda kepada objek Python yang menyokongnya, semasa menggunakan
hasilnya. Ini adalah standard untuk pengaturcaraan Python C API.
Seterusnya, kami mentakrifkan maklumat tentang modul ini dan fungsi yang dikandunginya, supaya kami boleh menghantar ia ke ruang Python:
src/spectator_measures/_coremodule.c (appended)
static PyMethodDef core_methods[] = {
// This entry is our function, cast to the correct type.
{"add_spectator_measures",
(PyCFunction)(void (*)(void))py_add_spectator_measures,
METH_VARARGS | METH_KEYWORDS, ""},
// A sentinel marking the end of the list.
{NULL, NULL, 0, NULL},
};
static struct PyModuleDef core_module = {
.m_base = PyModuleDef_HEAD_INIT,
.m_name = "_core",
.m_methods = core_methods,
};
Jadual kaedah dan struktur takrifan-modul ini diterangkan dengan lebih terperinci dalam dokumentasi CPython tentang permulaan modul.
Akhirnya, beritahu Python cara memulakan modul. Ini adalah satu-satunya fungsi dalam fail C
yang dieksport. Namanya mesti sepadan tepat dengan corak
PyInit_<mod>, di mana <mod> adalah nama modul (tidak layak). Dalam kes ini, nama modul yang layak
sepenuhnya ialah spectator_measures._core, dan nama yang tidak layak ialah _core, jadi fungsi kami
mesti dipanggil PyInit__core, dengan garis bawah berganda.
src/spectator_measures/_coremodule.c (appended)
PyMODINIT_FUNC PyInit__core(void) {
// This line is critical to use the Qiskit C API. Your code will
// likely be immediately terminated by the operating system if you
// forget to do this.
if (qk_import() < 0) {
return NULL;
};
// The standard Python call to initialize a module.
return PyModuleDef_Init(&core_module);
}
Simbol PyMODINIT_FUNC dan PyModuleDef_Init kedua-duanya adalah pengaturcaraan Python C API standard. Komponen
khusus Qiskit ialah qk_import(). Adalah kritikal bahawa anda memanggil fungsi ini semasa fungsi
permulaan modul anda; anda tidak akan dapat memanggil sebarang fungsi Qiskit C API
sehingga ini berjaya dilaksanakan.
Gunakan pakej daripada Pythonβ
Ini kini merupakan pakej yang lengkap, termasuk modul sambungan C. Oleh sebab hanya alatan standard digunakan, dan tiada perpustakaan sistem bukan-standard yang dipaut semasa masa pembinaan, proses pembinaan adalah mudah.
Anda boleh menggunakan mana-mana alatan pembinaan serasi PEP-517. Sebagai contoh minimum, anda boleh menjalankan arahan berikut dalam akar repositori untuk memasang pakej.
pip install .
Ini mengkompil modul sambungan C dan memasang pakej Python yang lengkap dalam persekitaran anda.
Contoh penggunaan laluan Transpiler tersuai ini ialah:
from qiskit import QuantumCircuit
from qiskit.transpiler import CouplingMap, Target
from spectator_measures import AddSpectatorMeasures
num_qubits = 10
qc = QuantumCircuit(num_qubits)
qc.x(0)
qc.x(5)
target = Target.from_configuration(
basis_gates=["x", "sx", "rz", "cx"],
num_qubits=num_qubits,
coupling_map=CouplingMap.from_line(num_qubits),
)
pass_ = AddSpectatorMeasures(target)
pass_(qc).draw()
Hasilnya ialah:
βββββ β
q_0: β€ X βββββββββββββ
βββββ β βββ
q_1: βββββββββ€Mβββββββ
β ββ₯β
q_2: ββββββββββ«βββββββ
β β
q_3: ββββββββββ«βββββββ
β β βββ
q_4: ββββββββββ«ββ€Mββββ
βββββ β β ββ₯β
q_5: β€ X ββββββ«βββ«ββββ
βββββ β β β βββ
q_6: ββββββββββ«βββ«ββ€Mβ
β β β ββ₯β
q_7: ββββββββββ«βββ«βββ«β
β β β β
q_8: ββββββββββ«βββ«βββ«β
β β β β
q_9: ββββββββββ«βββ«βββ«β
β β β β
spec: 3/ββββββββββ©βββ©βββ©β
0 1 2