بداية سريعة
يعرض هذا الدليل مثالًا عمليًا بسيطًا لحزمة qiskit-addon-obp. نستخدم الانتشار العكسي للمؤثرات (OBP) لتقليل عمق الدائرة الكمومية عن طريق امتصاص البوابات الأخيرة في المرصود.
لا تتغير القيمة المتوقعة إذا فصلنا كتلة من البوابات الأخيرة عن الدائرة وقمنا بتحويل المرصود بها كلاسيكيًا. يطبّق OBP هذا مرارًا، فيقيّم جزءًا من الدائرة كلاسيكيًا بحيث لا تعمل على العتاد إلا دائرة أقل عمقًا. الثمن هو أن كل بوابة ممتصة قد توسّع المرصود إلى حدود باولي أكثر، لذا يجب موازنة العمق الموفَّر مع نمو المرصود.
للاطلاع على أمثلة لكيفية بناء مسارات عمل واقعية بهذه الأداة وتنفيذها على العتاد الكمومي، راجع الدروس على IBM Quantum Platform (درس OBP).
تجهيز مدخلات OBP
يأخذ OBP كمدخلات قائمة من شرائح الدائرة ومرصودًا. وهو ينشر الشرائح عكسيًا واحدة تلو الأخرى من نهاية الدائرة إلى المرصود، فيقلّل عمق الدائرة على حساب حدود باولي إضافية في المرصود. هنا ننشئ دائرة تطور زمني لنموذج هايزنبرغ من 10 كيوبتات ونقسّمها حسب نوع البوابة.
نرسم أدناه الدائرة الأصلية، ثم الدائرة نفسها بعد إعادة تجميعها مع حواجز (barriers) تحدد حدود الشرائح — كل شريحة وحدة يمكن امتصاصها في المرصود في خطوة انتشار عكسي واحدة.
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"numpy": "numpy", "qiskit": "qiskit", "qiskit_addon_obp": "qiskit-addon-obp", "qiskit_addon_utils": "qiskit-addon-utils"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
import numpy as np
from qiskit.quantum_info import SparsePauliOp
from qiskit.synthesis import LieTrotter
from qiskit.transpiler import CouplingMap
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
generate_xyz_hamiltonian,
)
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types
# Generate a circuit to reduce
coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)
reduced_coupling_map = coupling_map.reduce(
[0, 13, 1, 14, 10, 16, 5, 12, 8, 18]
)
hamiltonian = generate_xyz_hamiltonian(
reduced_coupling_map,
coupling_constants=(np.pi / 8, np.pi / 4, np.pi / 2),
ext_magnetic_field=(np.pi / 3, np.pi / 6, np.pi / 9),
)
circuit = generate_time_evolution_circuit(
hamiltonian,
time=0.2,
synthesis=LieTrotter(reps=2),
)
# Slice the circuit and define an observable
slices = slice_by_gate_types(circuit)
observable = SparsePauliOp("IIIIIIIIIZ")
print(f"Original circuit depth: {circuit.depth()}")
print(f"Number of slices: {len(slices)}")
print(f"Observable terms: {len(observable)}")
Original circuit depth: 18
Number of slices: 18
Observable terms: 1
# Recombine the slices with barriers to make the slice boundaries visible
sliced_circuit = combine_slices(slices, include_barriers=True)
print("Original circuit:")
display(circuit.draw("mpl", scale=0.6, fold=-1))
print("Sliced circuit (recombined with barriers for visualization)")
sliced_circuit.draw("mpl", scale=0.6, fold=-1)
Original circuit:

Sliced circuit (recombined with barriers for visualization)

تقليل عمق الدائرة باستخدام OBP
نستدعي backpropagate لامتصاص الشرائح في المرصود. تُرجع الدالة المرصود الموسَّع، وشرائح الدائرة التي لم يُنشر عبرها، وبيانات وصفية عن الإجراء.
إذا تُرك دون ضبط، يمكن أن ينمو المرصود نحو من حدود باولي. تحدّ operator_budget من هذا النمو — هنا نسمح بحد أقصى 8 مجموعات متبادلة التبديل على مستوى الكيوبتات، وهو ما يحدد تقريبًا عدد اللقطات (shots) المطلوبة لتقييم المرصود على QPU. يتوقف الانتشار العكسي بمجرد أن يؤدي امتصاص الشريحة التالية إلى تجاوز الميزانية، وهذا بالضبط ما يحدث أدناه: يُمتص 7 فقط من 18 شريحة قبل أن يملأ المرصود المجموعات الثماني المتبادلة التبديل ويتوقف الإجراء.
للتعمق أكثر في الدائرة، يمكن استخدام الوسيط truncation_error_budget في backpropagate لإسقاط حدود باولي ذات المعاملات الصغيرة من المرصود أثناء نموه. يخفف ذلك نمو المرصود مقابل خطأ يتناسب مع حجم حدود باولي المقتطعة. الميزانيتان متكاملتان ويمكن استخدامهما معًا: تتحكم operator_budget في حجم المرصود، بينما تتحكم truncation_error_budget في الخطأ الناتج عن إسقاط الحدود.
from qiskit_addon_obp import backpropagate
from qiskit_addon_obp.utils.simplify import OperatorBudget
max_qwc_groups = 8
bp_obs, remaining_slices, metadata = backpropagate(
observable,
slices,
operator_budget=OperatorBudget(max_qwc_groups=max_qwc_groups),
)
reduced_circuit = combine_slices(remaining_slices)
num_groups = len(bp_obs.group_commuting(qubit_wise=True))
print(
f"Backpropagated {metadata.num_backpropagated_slices} of {len(slices)} slices."
)
print(
f"Reduced circuit depth: {reduced_circuit.depth()} (was {circuit.depth()})"
)
print(f"Observable grew from {len(observable)} to {len(bp_obs)} Pauli terms.")
print(
f"Filled {num_groups} of {max_qwc_groups} commuting groups, exhausting the budget."
)
Backpropagated 7 of 18 slices.
Reduced circuit depth: 11 (was 18)
Observable grew from 1 to 18 Pauli terms.
Filled 8 of 8 commuting groups, exhausting the budget.
reduced_circuit.draw("mpl", scale=0.6, fold=-1)