انتقل إلى المحتوى الرئيسي

قطع الأسلاك لتقدير قيم التوقع

تقدير الاستخدام: 22 ثانية على معالج Heron (ملاحظة: هذا تقدير فحسب. قد يختلف وقت التشغيل الفعلي لديك.)

نتائج التعلم

بعد إتمام هذا الدليل التعليمي، يجب أن يفهم المستخدمون:

  • كيفية استخدام qiskit-addon-cutting لتقسيم دائرة كبيرة إلى دوائر فرعية أصغر، مما يقلل من تأثير الضوضاء

المتطلبات الأساسية

نقترح أن يكون المستخدمون على دراية بالموضوع التالي قبل إتمام هذا الدليل التعليمي:

  • استخدام الأولية Sampler، المستخدمة في هذه العملية

الخلفية

حياكة الدوائر (Circuit-knitting) مصطلح شامل يضم أساليب متعددة لتقسيم الدائرة إلى دوائر فرعية أصغر تتضمن عددًا أقل من البوابات أو الكيوبتات. يمكن تنفيذ كل دائرة فرعية باستقلالية، وتُحصل على النتيجة النهائية عبر معالجة كلاسيكية لاحقة لمخرجات كل دائرة فرعية. يمكن الوصول إلى هذه التقنية من خلال إضافة Qiskit لقطع الدوائر؛ راجع التوثيق إلى جانب مواد تمهيدية أخرى للاطلاع على شرح مفصّل للتقنية.

يُركّز هذا الدليل التعليمي على أسلوب يُعرف بـ قطع الأسلاك، حيث تُقسَّم الدائرة على طول السلك [1], [2]. تجدر الإشارة إلى أن التقسيم سهل في الدوائر الكلاسيكية لأن المخرج عند نقطة التقسيم يمكن تحديده بصورة حتمية إما 0 أو 1. غير أن حالة الكيوبت عند نقطة القطع هي في العموم حالة مختلطة. لذلك، يجب قياس كل دائرة فرعية مرات عدة في أسس مختلفة (عادةً أساس مكتمل طوموغرافيًا كأساس باولي [3], [4])، مع إعداد الحالة المقابلة في حالتها الذاتية. يوضح الشكل أدناه (بإذن من: [7]) مثالًا على قطع الأسلاك لحالة GHZ ذات أربعة كيوبتات إلى ثلاث دوائر فرعية. هنا، MjM_j يشير إلى مجموعة من الأسس (عادةً باولي X وY وZ)، وPiP_i يشير إلى مجموعة من الحالات الذاتية (عادةً 0|0\rangle و1|1\rangle و+|+\rangle و+i|+i\rangle).

wc-1.png wc-2.png

نظرًا لأن كل دائرة فرعية تحتوي على عدد أقل من الكيوبتات والبوابات، فمن المتوقع أن تكون أقل عرضةً للضوضاء. يوضح هذا الدليل التعليمي مثالًا يمكن فيه استخدام هذه الطريقة لتخفيف الضوضاء في النظام بشكل فعال.

المتطلبات

قبل البدء في هذا الدليل التعليمي، تأكد من تثبيت ما يلي:

  • Qiskit SDK الإصدار 2.0 أو أحدث، مع دعم التصور البياني
  • Qiskit Runtime الإصدار 0.22 أو أحدث ( pip install qiskit-ibm-runtime )
  • إضافة Qiskit لقطع الدوائر الإصدار 0.10.0 أو أحدث (pip install qiskit-addon-cutting)
  • Qiskit addon utils الإصدار 0.3 أو أحدث (pip install qiskit-addon-utils)
  • Qiskit Aer (pip install qiskit-aer )

الإعداد

# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-cutting qiskit-aer qiskit-ibm-runtime
import numpy as np
import matplotlib.pyplot as plt

from qiskit.circuit import Parameter, ParameterVector, QuantumCircuit
from qiskit.quantum_info import PauliList, SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_aer import AerSimulator
from qiskit.result import sampled_expectation_value

from qiskit_addon_cutting.instructions import CutWire
from qiskit_addon_cutting import (
cut_wires,
expand_observables,
partition_problem,
generate_cutting_experiments,
reconstruct_expectation_values,
)

from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import SamplerV2, Batch

مثال على محاكي صغير النطاق

ينفّذ هذا الدليل التعليمي نمطًا من أنماط Qiskit لمحاكاة دائرة Many-Body Localization (MBL) أحادية البُعد (1D). دائرة MBL هي دائرة فعّالة على مستوى الأجهزة، وهي معلّمة بمعاملين هما θ\theta وϕ\vec{\phi}. عندما يُضبط θ\theta على 00 وتُحضَّر الحالة الابتدائية في 0|0\rangle لجميع الكيوبتات، تكون قيمة التوقع المثالية لـ Zi\langle Z_i \rangle هي +1+1 لكل موقع كيوبت ii بصرف النظر عن قيم ϕ\vec{\phi}. مزيد من التفاصيل حول هذه الدائرة متاح في هذه المقالة.

لاحظ أنه في محاكٍ خالٍ من الضوضاء، ستكون قيمة التوقع المحصّلة مع قطع الدائرة وبدونه متطابقة.

الخطوة 1: تحويل المدخلات الكلاسيكية إلى مسألة كمومية

بناء دائرة MBL أحادية البُعد

نقدّم أولًا دالةً لبناء دائرة MBL أحادية البُعد.

class MBLChainCircuit(QuantumCircuit):
def __init__(
self, num_qubits: int, depth: int, use_cut: bool = False
) -> None:
super().__init__(
num_qubits, name=f"MBLChainCircuit<{num_qubits}, {depth}>"
)
evolution = MBLChainEvolution(num_qubits, depth, use_cut)
self.compose(evolution, inplace=True)

class MBLChainEvolution(QuantumCircuit):
def __init__(self, num_qubits: int, depth: int, use_cut) -> None:
super().__init__(
num_qubits, name=f"MBLChainEvolution<{num_qubits}, {depth}>"
)

theta = Parameter("θ")
phis = ParameterVector("φ", num_qubits)

for layer in range(depth):
layer_parity = layer % 2
# print("layer parity", layer_parity)
for qubit in range(layer_parity, num_qubits - 1, 2):
# print(qubit)
self.cz(qubit, qubit + 1)
self.u(theta, 0, np.pi, qubit)
self.u(theta, 0, np.pi, qubit + 1)
if (
use_cut
and layer_parity == 0
and (
qubit == num_qubits // 2 - 1
or qubit == num_qubits // 2
)
):
self.append(CutWire(), [num_qubits // 2])
if use_cut and layer < depth - 1 and layer_parity == 1:
if qubit == num_qubits // 2:
self.append(CutWire(), [qubit])
for qubit in range(num_qubits):
self.p(phis[qubit], qubit)
num_qubits = 10
depth = 2
mbl = MBLChainCircuit(num_qubits, depth)
mbl.draw("mpl", fold=-1)

Output of the previous code cell

نحسب متوسط قيمة التوقع O=1niZiO = \frac{1}{n} \sum_i Z_i على جميع الكيوبتات عند θ=0\theta = 0. بما أن قيمة التوقع المثالية لـ Zi=1\langle Z_i \rangle = 1 \forall ii، فإن قيمة التوقع المثالية لـ OO هي أيضًا 11. تُختار المعاملات ϕ\phi عشوائيًا.

np.random.seed(42)
phis = list(np.random.rand(mbl.num_parameters - 1))
theta = [0]
params = theta + phis

يجب تعليم الدائرة عبر إدراج CutWire في المواضع المطلوبة لتقسيمها. في هذا الدليل التعليمي، نختار تقسيمًا متساويًا. صُمِّمت دائرة MBL بحيث يؤدي ضبط use_cut=True في الدالة إلى إدراج التعليم في الموضع الصحيح بعد n2\frac{n}{2} كيوبت، حيث nn هو عدد الكيوبتات في الدائرة الأصلية. كما قمنا بتعيين المعاملات المُولّدة عشوائيًا للدائرة.

mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)
mbl_cut.assign_parameters(params, inplace=True)
mbl_cut.draw("mpl", fold=-1)

Output of the previous code cell

الخطوة 2: تحسين المسألة لتنفيذها على الأجهزة الكمومية

قطع الدائرة إلى دوائر فرعية أصغر

الآن نقسّم الدائرة إلى دائرتين فرعيتين أصغر باستخدام qiskit-addon-cutting. يُضيف qiskit-addon-cutting بوابة Move افتراضية لتقسيم موقع قطع السلك عبر تعديل عدد الكيوبتات بالشكل المناسب. ننشئ الآن الدائرة بهذه البوابة الافتراضية. بما أن هناك قطعة سلك واحدة، سيزداد عدد الكيوبتات المرتبطة بها بمقدار 1.

mbl_move = cut_wires(mbl_cut)
mbl_move.draw("mpl", fold=-1)

Output of the previous code cell

بناء المؤثر المرصود وتوسيعه

المؤثر المرصود، كما عُرِّف سابقًا، هو متوسط ZZ على كل كيوبت. غير أنه بعد إدراج بوابة Move الافتراضية، يزداد العدد الفعلي للكيوبتات في الدائرة. يجب توسيع المؤثر المرصود وفقًا لذلك لمراعاة هذا التغيير في عدد الكيوبتات. لاحظ أن المؤثر المرصود يعمل دائمًا بصورة تافهة (أي II) على الكيوبت الإضافي المُضاف لبوابة Move الافتراضية.

observable = PauliList(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)]
)
observable
PauliList(['ZIIIIIIIII', 'IZIIIIIIII', 'IIZIIIIIII', 'IIIZIIIIII',
'IIIIZIIIII', 'IIIIIZIIII', 'IIIIIIZIII', 'IIIIIIIZII',
'IIIIIIIIZI', 'IIIIIIIIIZ'])
new_obs = expand_observables(observable, mbl, mbl_move)
new_obs
PauliList(['ZIIIIIIIIII', 'IZIIIIIIIII', 'IIZIIIIIIII', 'IIIZIIIIIII',
'IIIIZIIIIII', 'IIIIIIZIIII', 'IIIIIIIZIII', 'IIIIIIIIZII',
'IIIIIIIIIZI', 'IIIIIIIIIIZ'])

يمكن الآن تقسيم الدائرة عند بوابة Move، فنحصل على الدوائر الفرعية وكذلك على المؤثر المرصود الفرعي، وهو الجزء من المؤثر المرصود الأصلي المرتبط بكل دائرة فرعية.

partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables

إليك تصور للدائرتين الفرعيتين:

subcircuits[0].draw("mpl", fold=-1)

Output of the previous code cell

subcircuits[1].draw("mpl", fold=-1)

Output of the previous code cell

يستلزم توسيع المؤثر المرصود باستخدام عملية Move بنية بيانات PauliList. لإعادة بناء قيمة توقع الدائرة الأصلية، نحتاج إلى المؤثر المرصود بصيغة SparsePauliOp.

M_z = SparsePauliOp(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)],
coeffs=[1 / num_qubits] * num_qubits,
)

كما نوقش سابقًا، يجب عند كل قطع قياس الدائرة السابقة (المنبع) في أساس باولي، وتحضير الدائرة اللاحقة (المصب) في الحالة الذاتية لذلك الأساس. تُنشئ الدالة generate_cutting_experiments جميع هذه الدوائر الضرورية والمعاملات المرتبطة بكل دائرة المطلوبة لإعادة البناء. تفاصيل إضافية متاحة في هذه الورقة البحثية.

subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits,
observables=subobservables,
num_samples=np.inf,
)

التحويل البرمجي للدوائر إلى الواجهة الخلفية

في المثال الأول الذي يتضمن المحاكاة فقط، نُحوِّل الدائرة برمجيًا إلى مجموعة البوابات الأساسية للواجهة الخلفية:

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, min_num_qubits=133
)

print(backend)
<IBMBackend('ibm_fez')>

الخطوة 3: التنفيذ باستخدام أوليات Qiskit

الآن، ننفّذ كل تجربة فرعية:

pm_basis = generate_preset_pass_manager(
optimization_level=2, basis_gates=backend.configuration().basis_gates
)
basis_subexperiments = {
label: pm_basis.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}
sampler = SamplerV2(mode=AerSimulator())
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in basis_subexperiments.items()
}

الخطوة 4: المعالجة اللاحقة وإعادة النتيجة بالصيغة الكلاسيكية المطلوبة

الآن نسترد نتيجة كل تجربة فرعية مُنفَّذة ونُعيد بناء قيمة التوقع للدائرة غير المقطوعة:

# Retrieve results
results = {label: job.result() for label, job in jobs.items()}
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real
reconstructed_expval
np.float64(0.9953821063041687)
methods = [
"Uncut",
"Wire cut",
]
values = [
1,
reconstructed_expval,
] # since the ideal expectation value in noiseless simulation is +1

ax = plt.gca()
plt.bar(methods, values, color="#a56eff", width=0.4, edgecolor="#8a3ffc")
ax.set_ylabel(r"$M_Z$", fontsize=12)
Text(0, 0.5, '$M_Z$')

Output of the previous code cell

مثال على الأجهزة الفعلية واسع النطاق

نُظهر الآن قطع الأسلاك لدائرة MBL ذات 60 كيوبت. ستُنفَّذ الدائرة غير المقطوعة وكذلك الدائرة المقطوعة على أجهزة IBM Quantum®:

num_qubits = 60
depth = 2

# construct the circuit
mbl = MBLChainCircuit(num_qubits, depth)

# create parameters
phis = list(np.random.rand(mbl.num_parameters - 1))
theta = [0]
params = theta + phis

# construct the cut circuit
mbl_cut = MBLChainCircuit(num_qubits, depth, use_cut=True)
mbl_cut.assign_parameters(params, inplace=True)
mbl_move = cut_wires(mbl_cut)

# Define observable and expand to account for the wire cut
observable = PauliList(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)]
)
new_obs = expand_observables(observable, mbl, mbl_move)

# Construct a SparsePauliOp version of the observable for later use in reconstruction
M_z = SparsePauliOp(
["I" * i + "Z" + "I" * (num_qubits - i - 1) for i in range(num_qubits)],
coeffs=[1 / num_qubits] * num_qubits,
)

# Partition the circuit and get subcircuits and subobservables
partitioned_problem = partition_problem(circuit=mbl_move, observables=new_obs)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables

# Obtain subexperiments and coefficients
subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits,
observables=subobservables,
num_samples=np.inf,
)

# Transpile the subexperiments to the backend
pm = generate_preset_pass_manager(optimization_level=2, backend=backend)
isa_subexperiments = {
label: pm.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}

# Execute the subexperiments and retrieve results
with Batch(backend=backend) as batch:
sampler = SamplerV2(mode=batch)
sampler.options.environment.job_tags = ["TUT_WC"]
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in isa_subexperiments.items()
}
results = {label: job.result() for label, job in jobs.items()}

# Reconstruct the expectation value of the original observable
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
reconstructed_expval = np.dot(reconstructed_expval_terms, M_z.coeffs).real

# Compute the uncut circuit to obtain the noisy expectation value for comparison
sampler = SamplerV2(mode=backend)
sampler.options.environment.job_tags = ["TUT_WC"]

if mbl.num_clbits == 0:
mbl.measure_all()
isa_mbl = pm.run(mbl)

pub = (isa_mbl, params)
uncut_job = sampler.run([pub])

uncut_counts = uncut_job.result()[0].data.meas.get_counts()
uncut_expval = sampled_expectation_value(uncut_counts, M_z)

# visualize the results
ax = plt.gca()
methods = ["uncut", "cut"]
values = [uncut_expval, reconstructed_expval]

plt.bar(methods, values, color="#a56eff", width=0.4, edgecolor="#8a3ffc")
plt.axhline(y=1, color="k", linestyle="--")
plt.text(0.3, 0.95, "Exact result")
plt.show()

Output of the previous code cell

uncut_expval
0.9202473958333336

الخطوات التالية

توصيات

إن أثار هذا العمل اهتمامك، قد تجد المواد التالية مفيدة:

المراجع

[1] Peng, T., Harrow, A. W., Ozols, M., & Wu, X. (2020). Simulating large quantum circuits on a small quantum computer. Physical review letters, 125(15), 150504.

[2] Tang, W., Tomesh, T., Suchara, M., Larson, J., & Martonosi, M. (2021, April). Cutqc: using small quantum computers for large quantum circuit evaluations. In Proceedings of the 26th ACM International conference on architectural support for programming languages and operating systems (pp. 473-486).

[3] Perlin, M. A., Saleem, Z. H., Suchara, M., & Osborn, J. C. (2021). Quantum circuit cutting with maximum-likelihood tomography. npj Quantum Information, 7(1), 64.

[4] Majumdar, R., & Wood, C. J. (2022). Error mitigated quantum circuit cutting. arXiv preprint arXiv:2211.13431.

[5] Khare, T., Majumdar, R., Sangle, R., Ray, A., Seshadri, P. V., & Simmhan, Y. (2023). Parallelizing Quantum-Classical Workloads: Profiling the Impact of Splitting Techniques. In 2023 IEEE International Conference on Quantum Computing and Engineering (QCE) (Vol. 1, pp. 990-1000). IEEE.

[6] Bhoumik, D., Majumdar, R., Saha, A., & Sur-Kolay, S. (2023). Distributed Scheduling of Quantum Circuits with Noise and Time Optimization. arXiv preprint arXiv:2309.06005.

[7] Majumdar, R. (2024). Efficient Reduction of Resources and Noise in Discrete Quantum Computing Circuits (Doctoral dissertation, Indian Statistical Institute - Kolkata). https://www.proquest.com/openview/b481def90b1cc80e6b58a77c99e8385c/1?pq-origsite=gscholar&cbl=2026366&diss=y