قطع البوابات لتقليل عرض الدائرة
في هذا الدفتر، سنتناول خطوات نمط Qiskit مع استخدام قطع الدائرة لتقليل عدد الـ Qubits في الدائرة. سنقطع البوابات لنتمكن من إعادة بناء قيمة التوقع لدائرة من أربعة Qubits باستخدام تجارب ثنائية الـ Qubit فحسب.
هذه هي الخطوات التي سنتبعها:
- الخطوة 1: تعيين المسألة على دوائر كمومية ومؤثرات:
- تعيين هاميلتوني على دائرة كمومية.
- الخطوة 2: التحسين للأجهزة المستهدفة [يستخدم الإضافة الخاصة بالقطع]:
- قطع الدائرة والعنصر القابل للرصد.
- نقل التجارب الفرعية عبر Transpiler للأجهزة.
- الخطوة 3: التنفيذ على الأجهزة المستهدفة:
- تشغيل التجارب الفرعية المستخرجة من الخطوة 2 باستخدام أداة
Sampler.
- تشغيل التجارب الفرعية المستخرجة من الخطوة 2 باستخدام أداة
- الخطوة 4: معالجة النتائج [يستخدم الإضافة الخاصة بالقطع]:
- دمج نتائج الخطوة 3 لإعادة بناء قيمة التوقع للعنصر القابل للرصد المعني.
الخطوة 1: التعيين
إنشاء دائرة للقطع
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-addon-cutting qiskit-aer qiskit-ibm-runtime
from qiskit.circuit.library import efficient_su2
qc = efficient_su2(4, entanglement="linear", reps=2)
qc.assign_parameters([0.4] * len(qc.parameters), inplace=True)
qc.draw("mpl", scale=0.8)

تحديد عنصر قابل للرصد
from qiskit.quantum_info import SparsePauliOp
observable = SparsePauliOp(["ZZII", "IZZI", "-IIZZ", "XIXI", "ZIZZ", "IXIX"])
الخطوة 2: التحسين
فصل الدائرة والعنصر القابل للرصد وفق تقسيم Qubit محدد
تتوافق كل تسمية في partition_labels مع Qubit الـ circuit في الفهرس نفسه. ستُجمَّع الـ Qubits التي تشترك في تسمية قسم واحدة معًا، وستُقطع البوابات غير المحلية التي تمتد على أكثر من قسم.
ملاحظة: الوسيطة observables لـ partition_problem من النوع PauliList. تُتجاهل معاملات حدود العناصر القابلة للرصد وأطوارها خلال تحليل المسألة وتنفيذ التجارب الفرعية. ويمكن إعادة تطبيقها أثناء إعادة بناء قيمة التوقع.
from qiskit_addon_cutting import partition_problem
partitioned_problem = partition_problem(
circuit=qc, partition_labels="AABB", observables=observable.paulis
)
subcircuits = partitioned_problem.subcircuits
subobservables = partitioned_problem.subobservables
bases = partitioned_problem.bases
تصور المسألة المحللة
subobservables
{'A': PauliList(['II', 'ZI', 'ZZ', 'XI', 'ZZ', 'IX']),
'B': PauliList(['ZZ', 'IZ', 'II', 'XI', 'ZI', 'IX'])}
subcircuits["A"].draw("mpl", scale=0.8)

subcircuits["B"].draw("mpl", scale=0.8)

حساب العبء الإضافي للأخذ بالعينات عند القطوع المختارة
نقطع هنا بوابتَي CNOT، مما يُفضي إلى عبء إضافي للأخذ بالعينات مقداره .
لمزيد من التفاصيل حول العبء الإضافي الناجم عن قطع الدائرة، راجع المادة التفسيرية.
import numpy as np
print(f"Sampling overhead: {np.prod([basis.overhead for basis in bases])}")
Sampling overhead: 81.0
توليد التجارب الفرعية للتشغيل على Backend
تقبل generate_cutting_experiments الوسيطتين circuits/observables على شكل قواميس تُعيّن تسميات أقسام الـ Qubit إلى الـ subcircuit/subobservables المقابلة لها.
لمحاكاة قيمة التوقع للدائرة الكاملة، تُولَّد تجارب فرعية كثيرة من التوزيع الشبه-احتمالي المشترك للبوابات المحللة، ثم تُنفَّذ على Backend واحد أو أكثر. يتحكم num_samples في عدد العينات المأخوذة من التوزيع، ويُعطى معامل مدمج واحد لكل عينة فريدة. لمزيد من المعلومات حول طريقة حساب المعاملات، راجع المادة التفسيرية.
from qiskit_addon_cutting import generate_cutting_experiments
subexperiments, coefficients = generate_cutting_experiments(
circuits=subcircuits, observables=subobservables, num_samples=np.inf
)
اختيار Backend
نستخدم هنا Backend وهميًا، مما سيُشغّل Qiskit Runtime في الوضع المحلي (أي على محاكٍ محلي).
from qiskit_ibm_runtime.fake_provider import FakeManilaV2
backend = FakeManilaV2()
إعداد التجارب الفرعية للـ Backend
يجب نقل الدوائر عبر Transpiler مع تحديد Backend كهدف قبل إرسالها إلى Qiskit Runtime.
from qiskit.transpiler import generate_preset_pass_manager
# Transpile the subexperiments to ISA circuits
pass_manager = generate_preset_pass_manager(optimization_level=1, backend=backend)
isa_subexperiments = {
label: pass_manager.run(partition_subexpts)
for label, partition_subexpts in subexperiments.items()
}
الخطوة 3: التنفيذ
تشغيل التجارب الفرعية باستخدام أداة Sampler البدائية في Qiskit Runtime
from qiskit_ibm_runtime import SamplerV2, Batch
# Submit each partition's subexperiments to the Qiskit Runtime Sampler
# primitive, in a single batch so that the jobs will run back-to-back.
with Batch(backend=backend) as batch:
sampler = SamplerV2(mode=batch)
jobs = {
label: sampler.run(subsystem_subexpts, shots=2**12)
for label, subsystem_subexpts in isa_subexperiments.items()
}
/home/garrison/Qiskit/qiskit-ibm-runtime/qiskit_ibm_runtime/session.py:157: UserWarning: Session is not supported in local testing mode or when using a simulator.
warnings.warn(
# Retrieve results
results = {label: job.result() for label, job in jobs.items()}
الخطوة 4: معالجة النتائج
إعادة بناء قيمة التوقع
إعادة بناء قيم التوقع لكل حد من حدود العنصر القابل للرصد ودمجها لإعادة بناء قيمة التوقع للعنصر القابل للرصد الأصلي.
from qiskit_addon_cutting import reconstruct_expectation_values
# Get expectation values for each observable term
reconstructed_expval_terms = reconstruct_expectation_values(
results,
coefficients,
subobservables,
)
# Reconstruct final expectation value
reconstructed_expval = np.dot(reconstructed_expval_terms, observable.coeffs)
مقارنة قيمة التوقع المعاد بناؤها بقيمة التوقع الدقيقة من الدائرة والعنصر القابل للرصد الأصليَّين
from qiskit_aer.primitives import EstimatorV2
estimator = EstimatorV2()
exact_expval = estimator.run([(qc, observable)]).result()[0].data.evs
print(f"Reconstructed expectation value: {np.real(np.round(reconstructed_expval, 8))}")
print(f"Exact expectation value: {np.round(exact_expval, 8)}")
print(f"Error in estimation: {np.real(np.round(reconstructed_expval-exact_expval, 8))}")
print(
f"Relative error in estimation: {np.real(np.round((reconstructed_expval-exact_expval) / exact_expval, 8))}"
)
Reconstructed expectation value: 0.6991539
Exact expectation value: 0.56254612
Error in estimation: 0.13660778
Relative error in estimation: 0.24283836