استخدام معايير Lp مختلفة لحذف حدود باولي
ملاحظة: قبل قراءة هذا الدليل، يُفضّل أن تقرأ دليل اقتطاع حدود باولي، الذي يصف اقتطاع حدود باولي ذات الوزن المنخفض المدمج في دالة backpropagate بناءً على TruncationErrorBudget محدَّدة.
في هذا الدليل ستتعرف على الوسيط p_norm في دالة backpropagate، الذي يمكن استخدامه لتغيير معيار Lp المستخدم لتقدير الخطأ الناتج عن حدود باولي المقتطعة.
بناء دائرة مثال
يستخدم هذا الدليل نفس الدائرة المثال المستخدمة في دليل اقتطاع حدود باولي:
# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util
_needed = {"matplotlib": "matplotlib", "qiskit": "qiskit", "qiskit_addon_obp": "qiskit-addon-obp", "qiskit_addon_utils": "qiskit-addon-utils", "rustworkx": "rustworkx"}
_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 rustworkx.generators
from qiskit.synthesis import LieTrotter
from qiskit_addon_utils.problem_generators import (
PauliOrderStrategy,
generate_time_evolution_circuit,
generate_xyz_hamiltonian,
)
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types
# Generate a linear chain of 10 qubits
num_qubits = 10
linear_chain = rustworkx.generators.path_graph(num_qubits)
# Use an arbitrary XY model
hamiltonian = generate_xyz_hamiltonian(
linear_chain,
coupling_constants=(0.05, 0.02, 0.0),
ext_magnetic_field=(0.02, 0.08, 0.0),
pauli_order_strategy=PauliOrderStrategy.InteractionThenColor,
)
# Evolve for some time
circuit = generate_time_evolution_circuit(
hamiltonian, synthesis=LieTrotter(reps=3), time=2.0
)
# slice the circuit by gate type
slices = slice_by_gate_types(circuit)
# For visualization purposes only, recombine the slices with barriers between them and draw the resulting circuit
combine_slices(slices, include_barriers=True).draw("mpl", fold=50, scale=0.6)

عرّف مرصودًا للمغنطة الكلية:
from qiskit.quantum_info import SparsePauliOp
obs = SparsePauliOp.from_sparse_list(
[("Z", [i], 1.0) for i in range(num_qubits)], num_qubits=num_qubits
)
للمقارنة، احسب القيمة المتوقعة الدقيقة:
from qiskit.primitives import StatevectorEstimator
estimator = StatevectorEstimator()
job = estimator.run([(circuit, obs)])
res = job.result()
exact_exp = res[0].data.evs
print(exact_exp)
9.318197859862146
استخدام معيار L1
افتراضيًا، وكما رأيت سابقًا في دليل اقتطاع حدود باولي، تكون p_norm=1، أي أن الخطأ يُقدَّر كالتالي:
حيث هي الحالة الكمومية، و هو الفرق الحقيقي بين المرصود الدقيق والمرصود المقتطع (وهو مجهول)، و هي مجموعة حدود باولي التي اقتُطعت، و هو معامل حد باولي.
هذه المتباينة حد أعلى صارم لكنه فضفاض جدًا في معظم السيناريوهات.
يقوم هذا الدليل بالانتشار العكسي لست شرائح من الدائرة المثال، باستخدام خطأ ثابت لكل شريحة قدره 0.001. يُفهم هذا الرقم على أنه الميزانية ضمن p_norm المعطاة.
from qiskit_addon_obp.utils.truncating import setup_budget
l1_truncation_error_budget = setup_budget(max_error_per_slice=0.001, p_norm=1)
print(l1_truncation_error_budget)
TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=1)
from qiskit_addon_obp import backpropagate
max_slices = 6
l1_bp_obs, l1_remaining_slices, l1_metadata = backpropagate(
obs,
slices[-max_slices:],
truncation_error_budget=l1_truncation_error_budget,
)
l1_reduced_circuit = combine_slices(
slices[:-max_slices] + l1_remaining_slices
)
print(
f"Backpropagated {max_slices - len(l1_remaining_slices)} circuit slices."
)
print(
f"New observable contains {len(l1_bp_obs)} terms and {len(l1_bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)
Backpropagated 6 circuit slices.
New observable contains 116 terms and 10 commuting groups.
يمكننا الآن حساب القيمة المتوقعة للمرصود بعد الانتشار العكسي، وكذلك الخطأ بالنسبة للمرجع الدقيق:
estimator = StatevectorEstimator()
job = estimator.run([(l1_reduced_circuit, l1_bp_obs)])
res = job.result()
l1_exp = res[0].data.evs
l1_error = exact_exp - l1_exp
print(l1_exp, l1_error)
9.317869899338842 0.00032796052330397174
أخيرًا، يمكننا رسم الخطأ الناتج أثناء الانتشار العكسي لكل شريحة، وكذلك الخطأ المتراكم. الخطأ المتراكم هو مجموع أخطاء الشرائح. نلاحظ أن الخطأ المتراكم حد أعلى فضفاض جدًا للخطأ الفعلي.
from matplotlib import pyplot as plt
from qiskit_addon_obp.utils.visualization import (
plot_accumulated_error,
plot_slice_errors,
)
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
axes[1].plot([6], [l1_error], "x", color="red", label="actual error")
plot_slice_errors(l1_metadata, axes[0])
plot_accumulated_error(l1_metadata, axes[1])

استخدام معيار L2
يمكن القول إن معيار L2 تقريب أفضل للخطأ الناتج من معيار L1. وذلك لأننا نستطيع افتراض أن الحالة الكمومية مسحوبة من مجموعة عشوائية وفق قياس هار، وفي هذه الحالة يتبع الخطأ الناتج توزيعًا متوسطه صفري وتباينه يمكن تقييده تقريبًا بمعيار L2:
ورغم أن هذا الحد ليس صارمًا، فلن يُنتهك إلا في حالات شاذة.
نقوم مرة أخرى بالانتشار العكسي لست شرائح من الدائرة المثال، باستخدام أقصى خطأ لكل شريحة قدره 0.001 (يُفسَّر هذه المرة وفق معيار L2).
l2_truncation_error_budget = setup_budget(max_error_per_slice=0.001, p_norm=2)
print(l2_truncation_error_budget)
TruncationErrorBudget(per_slice_budget=[0.001], max_error_total=inf, p_norm=2)
max_slices = 6
l2_bp_obs, l2_remaining_slices, l2_metadata = backpropagate(
obs,
slices[-max_slices:],
truncation_error_budget=l2_truncation_error_budget,
)
l2_reduced_circuit = combine_slices(
slices[:-max_slices] + l2_remaining_slices
)
print(
f"Backpropagated {max_slices - len(l2_remaining_slices)} circuit slices."
)
print(
f"New observable contains {len(l2_bp_obs)} terms and {len(l2_bp_obs.group_commuting(qubit_wise=True))} commuting groups."
)
Backpropagated 6 circuit slices.
New observable contains 84 terms and 6 commuting groups.
مرة أخرى، نحسب القيمة المتوقعة للمرصود بعد الانتشار العكسي والخطأ بالنسبة للمرجع الدقيق:
estimator = StatevectorEstimator()
job = estimator.run([(l2_reduced_circuit, l2_bp_obs)])
res = job.result()
l2_exp = res[0].data.evs
l2_error = exact_exp - l2_exp
print(l2_exp, l2_error)
9.317829770853422 0.00036808900872387085
رسم الأخطاء الناتجة لكل شريحة والخطأ المتراكم يعطي صورة مشابهة لما سبق.
fig, axes = plt.subplots(1, 2, figsize=(14, 5))
axes[1].plot([6], [l2_error], "x", color="red", label="actual error")
plot_slice_errors(l2_metadata, axes[0])
plot_accumulated_error(l2_metadata, axes[1])

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