إلغاء الخطأ الاحتمالي مع مخاريط ضوئية مظللة
تقدير الاستخدام: 10 دقائق على معالج Heron (ملاحظة: هذا تقدير فقط. قد يختلف وقت التشغيل الخاص بك.)
نتائج التعلم
-
ما هو إلغاء الخطأ الاحتمالي (PEC)، ولماذا تنمو تكلفة عيناته أسياً مع إجمالي الضوضاء المؤثرة على الدائرة
-
كيف تُقيّد المخاريط الضوئية المظللة (SLC) مساهمة كل حد ضوضاء في المرصودة المستهدفة، بحيث يمكنك إنفاق ميزانية التخفيف حيث يهم الأمر — وكيف، بالإضافة إلى تخفيض التكلفة الإضافية الخالي من التحيز الذي توفره بالفعل مخروط ضوئي هندسي بحت، يتيح لك التظليل مبادلة تحيز متبقٍ محدود مقابل تكلفة عينات أقل
-
كيفية تعلّم ضوضاء الطبقة باستخدام
NoiseLearnerV3وحقن مضاد الضوضاء عبرsamplomaticوأداةExecutorالأساسية -
كيفية الجمع بين PEC وPEC+SLC مع TREX والاختيار اللاحق لتقدير قيمة توقع على العتاد
المتطلبات الأساسية
-
سير عمل أنماط Qiskit
-
استخدام أداة Estimator الأساسية لحساب قيم توقع مرصودة
-
تقنيات تخفيف الأخطاء مثل تدوير باولي وTREX، المُغطاة في الجمع بين خيارات تخفيف الأخطاء مع أداة Estimator الأساسية
الخلفية
يوضح هذا البرنامج التعليمي كيفية تخفيف الأخطاء باستخدام ملحق المخروط الضوئي المظلل (SLC). هذا الملحق هو تطور لـ تقنية إلغاء الخطأ الاحتمالي (PEC)، حيث يتعلّم المستخدم ضوضاء الطبقات الفريدة في الدائرة ثم يُلغي الضوضاء بتطبيق بوابات أحادية الكيوبت وتقنيات معالجة لاحقة. مقارنةً بالطرق الأخرى، توفر PEC حدوداً أكثر متانة على تحيز النتيجة المُخففة، لكنها تميل إلى المعاناة من تكلفة أعلى من حيث وقت QPU. أثناء PEC، للتعويض عن تخفيف قيمة التوقع بسبب الضوضاء، تُعاد قياس النتيجة المتوسطة بعامل ، حيث هو معدل الضوضاء المُتعلَّم لباولي الخطأ عند الطبقة في الدائرة. تزيد إعادة القياس هذه التباين بعامل ، وبالتالي تُضاعف أيضاً عدد تنفيذات الدائرة المطلوبة على QPU بـ ، وهو ما نسميه تكلفة العينات أو التكلفة الإضافية للعينات. لأن ينمو أسياً، غالباً ما تقتصر PEC على دوائر ضحلة أو ذات كيوبتات قليلة. تعرّف على المزيد حول PEC في إلغاء الخطأ الاحتمالي بنماذج باولي-ليندبلاد المتناثرة على معالجات كمومية ضوضائية.
إذا استطعنا تحديد الأخطاء التي لا تحتاج إلى تخفيف، يمكننا تقليل تكلفة العينات هذه أسياً. خطوة أولى في هذا الاتجاه هي تنفيذ تخفيف أخطاء واعٍ محلياً، والذي يستخدم "مخروطاً ضوئياً" تقليدياً يمكن حسابه بسرعة لتقليل تكلفة PEC الإضافية عن طريق تقييد حساسية مرصودة ما للأخطاء في جميع أنحاء الدائرة، مما يوسّع جدوى PEC إلى نطاقات أكبر لبعض المسائل. لا يمكن للأخطاء خارج هذا المخروط الضوئي أن تؤثر على النتيجة المقاسة، لذا يمكن استبعادها من إجراء إلغاء الخطأ. يقلل هذا الاستبعاد من التكلفة الإضافية للعينات، بشكل كبير في بعض الحالات، دون إدخال تحيز إضافي. على وجه الخصوص، لقياس مرصودة محلية لدائرة ذات عمق ثابت، تستقر التكلفة الإضافية للعينات المطلوبة في النهاية عند زيادة عدد الكيوبتات في الدائرة (انظر الشكل 2ب في محلية وتخفيف أخطاء الدوائر الكمومية).
تذهب المخاريط الضوئية المظللة (SLC) إلى أبعد من ذلك، باستخدام محاكاة كلاسيكية لتقييد حساسية الأخطاء عبر الدائرة بشكل أكثر إحكاماً. يبادل هذا بعض وقت QPU بوقت CPU ويقلل من التكلفة الإضافية للعينات اللازمة لإعادة تسوية التحيز. بدلاً من قطع صارم، يُخصَّص لكل خطأ محتمل في الدائرة "تظليل" متدرج يُحدد حداً أعلى لحساسية قيمة توقع المرصودة لذلك الخطأ. (بالنسبة للحدود المنتشرة إلى الخلف المُقدَّمة أدناه، تعتمد هذه الحساسية أيضاً على الحالة الابتدائية للدائرة — الحالة الصفرية الكاملة في سير العمل النمطي، وفي هذا البرنامج التعليمي.) يسمح هذا التوصيف المُحسَّن بتطبيقات أكثر كفاءة واستهدافاً لـ PEC مع تباين مُخفَّض، مع منح المستخدم القدرة على ضبط التحيز بشكل قابل للتحكم في تقدير المرصودة. راجع تظليل المخروط الضوئي لتخفيف الأخطاء الكمومية المُسرَّع كلاسيكياً لمزيد من التفاصيل.
يعتمد سير عملنا لملحق SLC على مكتبة samplomatic جنباً إلى جنب مع الفئتين QuantumProgram وExecutor المُضافتين إلى Qiskit Runtime في qiskit-ibm-runtime الإصدار v0.47.0، مما يتيح للمستخدمين تحكماً أكثر معيارية في إعدادات التنفيذ لقمع وتخفيف الأخطاء مع الحفاظ على سهولة الاستخدام. اقرأ المزيد في دليل نموذج التنفيذ الموجَّه.
نظرة سريعة على سير عمل تخفيف الأخطاء بـ SLC
لنمذجة ضوضاء QPU، نستخدم نموذج ضوضاء باولي-ليندبلاد متناثر بمعدلات خطأ باولي أحادية وثنائية الكيوبت، مُولَّدة محلياً على كل كيوبت وحافة في الجهاز. بهذا الاختيار، يكون سير عمل تخفيف الأخطاء بـ SLC المُقدَّم في هذا البرنامج التعليمي كما يلي:
أ. CPU — تقييد أثر كل خطأ من أخطاء باولي أحادية وثنائية الكيوبت
-
الانتشار الأمامي (تقييد التأثير على المرصودة). انشر كل خطأ إلى نهاية الدائرة واحسب مبدّله (commutator) مع المرصودة.
-
اقطع حدود المؤثر أثناء التطور للحفاظ على قابلية الحساب للتنفيذ.
-
شدّد هذه الحدود أكثر بانتشار خلفي فضفاض للمرصودة استناداً إلى حدود السرعة الكمومية.
-
-
الانتشار الخلفي (تقييد التأثير على الحالة الابتدائية). انشر كل خطأ إلى بداية الدائرة واحسب مبدّله مع الحالة الابتدائية.
ب. QPU — تعلّم معدلات الضوضاء. استخدم NoiseLearnerV3 لتقدير معدلات نموذج ضوضاء باولي-ليندبلاد.
ج. CPU — ترتيب أولويات التخفيف
-
حدّث الحدود المدمجة بمعدلات الضوضاء المُتعلَّمة. اجمع الحدود الأمامية والخلفية المحسوبة سابقاً وحدّثها بمعدلات الضوضاء المُتعلَّمة.
-
رتّب مكونات الضوضاء لتخفيفها باستخدام الحدود المحسوبة والمعدلات المُتعلَّمة. رتّب أولوية كل خطأ ضوضاء محتمل بناءً على تأثيره المُقدَّر على التحيز والتكلفة المرتبطة بتصحيحه.
د. QPU — أدرج مضاد الضوضاء وشغّل. نفّذ الدائرة المطلوبة بمضاد ضوضاء (ضوضاء معكوسة) مُحدَّد باستخدام تعليقات Box.
هـ. CPU — قدّر المرصودة. احسب قيمة التوقع، مُطبِّقاً اختياراً لاحقاً قائماً على القياس لتقليل تأثير الضوضاء غير الماركوفية.
نظرة عامة على تعلّم الضوضاء
تعلّم الضوضاء خطوة شائعة في عدة طرق لتخفيف الأخطاء، تُنفَّذ بواسطة متعلّم الضوضاء؛ كما يظهر في برنامج تضخيم الخطأ الاحتمالي التعليمي. في NoiseLearnerV3، يمكنك تحديد طبقات الضوضاء المراد تعلّمها كائنات CircuitInstruction، بحيث يمكنك حساب حدود ضوضاء SLC المطلوبة لكل طبقة بالطريقة الموصوفة أعلاه. يوفر نموذج باولي-ليندبلاد المُتعلَّم معاملات تُستخدم في ترتيب أولويات PEC+SLC. يمكن تحديد الطريقة التي تُجمَّع بها البوابات في طبقات باستخدام دالتي الملاءمة generate_boxing_pass_manager وfind_unique_box_instructions، ثم تغذيتها إلى دالة أداة SLC المساعدة generate_noise_model_paulis، كما هو موضح في الخطوة 2 أدناه.
| Part 1 | Part 2 | Part 3 |
|---|---|---|
| طبقات بوابات ذات كيوبتين مع Pauli-twirl | كرر أزواج طبقات الهوية وتعلم الضجيج | اشتق دقة (خطأ لكل قناة ضجيج) |
![]() | ![]() |
نظرة عامة على المعالجة اللاحقة
بعد التنفيذ على العتاد الكمومي باستخدام إطار عمل samplomatic وExecutor، نحول قياسات السلاسل الثنائية إلى قيمة المُلاحَظ المطلوبة. في هذا الدرس التطبيقي، نوضح سير العمل على دارة إيزنغ معكوسة (مبنية في الخطوة 1 أدناه)، حيث تكون قيمة التوقع المثالية للمُلاحَظ المقاس تساوي 1 لأن النصف الثاني من الدارة يلغي النصف الأول. عند حساب قيمة المُلاحَظ باستخدام دالة executor_expectation_values، نطبق بعض تقنيات المعالجة اللاحقة التي تقلل من تأثير الضجيج. تشمل هذه التقنيات إزالة اللقطات المتأثرة بالضجيج غير الماركوفي، وتخفيف خطأ القراءة، ومراعاة تفاصيل تنفيذنا لـ PEC. تُناقش التفاصيل في الخطوة 4 أدناه.
المتطلبات
قبل بدء هذا الدرس التطبيقي، تأكد من تثبيت ما يلي:
-
حزمة تطوير برمجيات Qiskit v2.2 أو أحدث، مع دعم التصور
-
Qiskit Runtime v0.47 أو أحدث (
pip install qiskit-ibm-runtime) -
إضافة Qiskit للمخاريط المظللة v0.1 أو أحدث (
pip install qiskit-addon-slc) -
أدوات إضافة Qiskit v0.3 أو أحدث (
pip install qiskit-addon-utils) -
Samplomatic v0.13 أو أحدث (
pip install samplomatic)
الإعداد
أولاً، استورد الحزم والدوال اللازمة لتشغيل هذا الدفتر.
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-slc qiskit-addon-utils qiskit-ibm-runtime samplomatic
from multiprocessing import set_start_method
# Setting this value prevents itertools.starmap deadlock on UNIX systems
set_start_method("spawn")
# Needed to prevent PySCF from parallelizing internally (SLC only)
%set_env OMP_NUM_THREADS=1
env: OMP_NUM_THREADS=1
import numpy as np
from matplotlib import pyplot as plt
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import generate_preset_pass_manager, PassManager
from qiskit_ibm_runtime import (
QiskitRuntimeService,
QuantumProgram,
Executor,
NoiseLearnerV3,
)
import samplomatic
from samplomatic.utils import find_unique_box_instructions
from samplomatic.transpiler import generate_boxing_pass_manager
from qiskit_addon_utils.exp_vals.measurement_bases import (
get_measurement_bases,
)
from qiskit_addon_utils.exp_vals.expectation_values import (
executor_expectation_values,
)
from qiskit_addon_utils.noise_management import (
gamma_from_noisy_boxes,
trex_factors,
)
from qiskit_addon_utils.noise_management.post_selection import PostSelector
from qiskit_addon_utils.noise_management.post_selection.transpiler.passes import (
AddPostSelectionMeasures,
AddSpectatorMeasures,
)
from qiskit_addon_slc.bounds import (
compute_backward_bounds,
compute_forward_bounds,
compute_local_scales,
merge_bounds,
tighten_with_speed_limit,
)
from qiskit_addon_slc.utils import (
generate_noise_model_paulis,
map_modifier_ref_to_ref,
)
from qiskit_addon_slc.visualization import draw_shaded_lightcone
مثال على محاكي صغير النطاق
مثل طرق تخفيف الخطأ الأخرى القائمة على التعلم، يخفف PEC مع المخاريط الضوئية المظللة الضجيج الفيزيائي لمعالج كمومي معين، لذا فهو يعتمد على خدمات العتاد التي ليس لها نظير ذو معنى على محاكي مثالي:
-
يحدد
NoiseLearnerV3تجريبياً قناة ضجيج Pauli-Lindblad المتناثرة على كل طبقة فريدة من طبقات ذات كيوبتين. على محاكي خالٍ من الضجيج، لا يوجد ضجيج لإلغائه. -
يأخذ الأساسي
Executorعينات من الدارات الملتوية والمحقونة بمضاد الضجيج التي يولدهاsamplomaticعلى خلفية.
حساب حد المخروط الضوئي المظلل عملية كلاسيكية، لكنه ذو معنى فقط بالنسبة إلى معدلات الضجيج المتعلمة من العتاد، والتي تحدد ميزانية التخفيف والحمل الزائد للعينات. لهذه الأسباب، نتخطى مثال المحاكي صغير النطاق ونوضح سير عمل PEC+SLC الكامل مباشرة على العتاد، مع تفصيل كل خطوة من نمط Qiskit أدناه.
مثال على العتاد كبير النطاق
نُشغّل سير عمل PEC+SLC الكامل على دارة إيزنغ معكوسة بعشرين كيوبت، تُنفَّذ على عتاد IBM Quantum®، باتباع الخطوات الأربع لنمط Qiskit.
الخطوة 1: رسم خريطة المشكلة
يُعَدُّ عرضنا التوضيحي كما يلي:
-
نختار إجراء تطور زمني بموجب هاملتونيان سلسلة إيزنغ أحادية البعد. تعطي سلسلة إيزنغ أحادية البعد بنية دارة كثيفة بشكل لطيف، وهو أمر مناسب لعرض تنفيذات PEC.
-
نختار قياس المُلاحَظ ، الذي يحتوي على مكوّن ومكوّن . هذا مناسب لإبراز كيفية معالجة حدود SLC لأنواع مختلفة من الأخطاء بشكل مختلف.
-
نريد أن تكون قيمة التوقع المثالية لهذا المُلاحَظ تساوي 1 بالضبط، حتى تكون جودة التقديرات المخففة سهلة الحكم عليها. يوصلنا عكس الدارة إلى معظم الطريق: لكل بوابة في النصف الثاني من الدارة توجد بوابة معكوسة في النصف الأول، بحيث تعمل الدارة المثالية كهوية.
-
أخيراً، يتطلب الحصول على قيمة توقع مثالية تساوي 1 أن تبدأ الدارة — وبالتالي، بفضل العكس، أن تنتهي أيضاً بشكل مثالي — في حالة ذاتية بقيمة +1 للمُلاحَظ. ولأن المُلاحَظ يحتوي على مكوّن غير ، ولأن الأساسي
Executorيراعي أساس القياس المطلوب في نهاية الدارة، نضيف بوابات تحضير الأساس المقابلة في بداية دارة العكس باستخدام دالةprepare_basis. نستخدم دالةget_measurement_basesلتحديد البوابات المطلوبة ومكان وضعها، مع تتبع دقائق فهرسة الكيوبتات الناشئة عن اتفاقيات توصيفbox، كما نُناقش في قسم تحضير قياسات الأساس القانونية.
# Width of the Ising chain (a free parameter of this demonstration)
num_qubits = 20
# The observable to measure: one X component (on qubit 6) and one Z component
# (on qubit 13), as motivated in "Step 1: Map the problem" above
target_obs_sparse = [("XZ", [6, 13], 1.0)]
observable = SparsePauliOp.from_sparse_list(
target_obs_sparse, num_qubits=num_qubits
)
# Determine the measurement basis each qubit requires for this observable.
# The reverser is used during post-processing in Step 4.
bases_virt, reverser_virt = get_measurement_bases(observable)
# Free parameters of the demonstration circuit: the number of Trotter steps
# sets the circuit depth, and rx_angle is the transverse-field rotation angle
# applied in each step (pi/4 makes the dynamics non-Clifford)
num_trotter_steps = 10
rx_angle = np.pi / 4
def construct_ising_circuit(
num_qubits: int,
num_trotter_steps: int,
rx_angle: float,
) -> QuantumCircuit:
circuit = QuantumCircuit(num_qubits)
for _ in range(num_trotter_steps):
circuit.rx(rx_angle, range(num_qubits))
for first_qubit in (1, 2):
for idx in range(first_qubit, num_qubits, 2):
# equivalent to Rzz(-pi/2):
circuit.sdg([idx - 1, idx])
circuit.cz(idx - 1, idx)
return circuit
def prepare_basis(
circuit: QuantumCircuit, basis: list[int]
) -> QuantumCircuit:
# basis is a list of integer values from 0 to 3. These map to the basis measurement as:
# 0 = I; 1 = Z; 2 = X; 3 = Y
assert len(basis) == circuit.num_qubits
out_circ = circuit.copy_empty_like()
for qb, bas in enumerate(basis):
if bas == 2:
out_circ.h(qb)
elif bas == 3:
out_circ.rx(-np.pi / 2, qb)
out_circ.barrier()
out_circ.compose(circuit, inplace=True)
return out_circ
def mirror_circuit(circuit: QuantumCircuit) -> QuantumCircuit:
mirror_circ = circuit.copy_empty_like()
mirror_circ.compose(circuit.inverse(), inplace=True)
mirror_circ.barrier()
mirror_circ.compose(circuit, inplace=True)
mirror_circ.measure_active()
return mirror_circ
# Instantiate the mirrored circuit and prepend the basis-preparation gates
circuit = construct_ising_circuit(num_qubits, num_trotter_steps, rx_angle)
mirrored_circuit = mirror_circuit(circuit)
mirrored_circuit = prepare_basis(mirrored_circuit, bases_virt[0])
mirrored_circuit.draw(
"mpl", fold=-1, scale=0.3, idle_wires=False, measure_arrows=False
)

الخطوة 2: التحسين
نحسّن التفاصيل المرتبطة بالدارة المراد تشغيلها، والمُلاحَظ المراد قياسه، ومعاملات تعلم الضجيج. كنقطة انطلاق، نختار معالج Heron الأقل انشغالاً ونُنشئه مع تفعيل البوابات الكسرية. تسمح هذه البوابات الكسرية بحساسية أكبر في بعض عمليات ترشيح الاختيار اللاحق لدينا.
# Initialize IBM Quantum Compute Service using your saved credentials
service = QiskitRuntimeService()
# Select the least busy Heron device. This tutorial targets Heron processors:
# the usage estimate and the non-Markovian-noise post-selection discussed in
# Step 4 are specific to this processor family.
backend = service.least_busy(
operational=True,
simulator=False,
filters=lambda b: b.processor_type["family"] == "Heron",
)
# Re-fetch with fractional gates enabled (least_busy does not forward this)
# Fractional gates are enabled so the non-Clifford Rx rotations are supported natively.
backend = service.backend(backend.name, use_fractional_gates=True)
print(f"Selected backend: {backend.name}")
Selected backend: ibm_kingston
أولاً، سنُحوّل دارتنا إلى تعليمات ISA، كما هو مطلوب للتنفيذ على وحدات المعالجة الكمومية لدينا. يكون لاختيار الكيوبتات الفيزيائية للتشغيل عليها تأثير كبير على جودة النتائج، لأن أوقات التماسك ودقة البوابات تتفاوت عبر الجهاز وتنجرف مع الوقت. بالنسبة لتجربة دقيقة، قد يكون من المفيد اختيار سلسلة من الكيوبتات يدوياً بناءً على تجارب معايرة سريعة (انظر، على سبيل المثال، الدرس التطبيقي المعايرة في الزمن الحقيقي لاختيار الكيوبت). لأغراض هذا الدرس التطبيقي، نعتمد على مُحوّل Qiskit لاختيار تخطيط بناءً على خريطة اقتران الخلفية وأحدث بيانات معايرتها.
isa_pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = isa_pm.run(mirrored_circuit)
# The chain of physical qubits selected by the transpiler
layout = isa_circuit.layout.final_index_layout()
print(f"Selected layout: {layout}")
isa_observable = observable.apply_layout(
layout, num_qubits=isa_circuit.num_qubits
)
Selected layout: [71, 58, 51, 50, 49, 48, 47, 46, 45, 44, 43, 42, 41, 36, 21, 22, 23, 16, 3, 2]
wire_order = layout + [
q for q in range(isa_circuit.num_qubits) if q not in layout
]
isa_circuit.draw(
"mpl",
fold=-1,
scale=0.3,
idle_wires=False,
wire_order=wire_order,
measure_arrows=False,
)

وضع الدارة في صناديق
لسهولة التنفيذ، نستخدم ممر التحويل generate_boxing_pass_manager، الذي يضع تعليمات الدارة في صناديق موضّحة. تشير هذه الصناديق بوضوح إلى المكان الذي ينبغي، في حالة PEC، حقن مضاد الضجيج فيه في الدارة. للاطلاع على تفاصيل الإعدادات، راجع وثائق Samplomatic.
لاحظ أن سير عمل SLC يتطلب استخدام inject_noise_strategy="individual_modification" لاحقاً في العملية لأن هذا يسمح لنا بتحديد توصيفات InjectNoise الخاصة بتعليمات BoxOp في الدارة بشكل فريد. (لا يمكن تحديد الصناديق التي لا تحمل توصيف InjectNoise بهذه الطريقة.)
تفترض حسابات حدود SLC حاليًا أن تعليقات InjectNoise موضوعة بـ site="after" (راجع qiskit-addon-slc#33). ونظرًا لأن القيمة الافتراضية لهذا الإعداد تغيرت في الإصدارات الحديثة من samplomatic (راجع samplomatic#372)، فإننا نضبط inject_noise_site="after" بشكل صريح أدناه.
تتنقل دالة find_unique_box_instructions عبر الدارة المصندقة المقدمة وتحدد تلك التي تحتوي على طبقات فريدة ذات كيوبتين (2Q) أو قياسات، لغرض تعلم الضجيج وحقنه.
# Box circuit with Twirl and InjectNoise annotations
boxes_pm = generate_boxing_pass_manager(
twirling_strategy="active",
inject_noise_strategy="individual_modification",
inject_noise_site="after",
inject_noise_targets="gates",
measure_annotations="all",
)
boxed_circuit = boxes_pm.run(isa_circuit)
# Find the unique instructions (layers) from boxed circuit
unique_2q_instructions = find_unique_box_instructions(
boxed_circuit, normalize_annotations=None, undress_boxes=True
)
boxed_circuit.draw(
"mpl",
fold=-1,
scale=0.3,
idle_wires=False,
wire_order=wire_order,
measure_arrows=False,
)

تحضير قياسات الأسس القانونية
لاحظ أنه يجب علينا الانتباه بشكل خاص لتتبع ترتيب الكيوبتات. أدناه، نُقدّم مفهوم canonical_qubits كوسيلة لتحديث ترتيب الكيوبتات بشكل مناسب عند تقديمه إلى Executor، نتيجة لطريقة التقاط ترتيب الكيوبتات عند وضع الدارات في صناديق وإيجاد التعليمات الفريدة. راجع وثائق اتفاقية ترتيب الكيوبتات للتفاصيل.
# Determine the canonical qubits order
meas_box = boxed_circuit.data[-1]
canonical_qubits = [
idx
for idx, qubit in enumerate(boxed_circuit.qubits)
if qubit in meas_box.qubits
]
# map canonical qubit to physical (isa) qubit
c_2_p = {c: p for c, p in enumerate(canonical_qubits)}
# map physical (isa) qubit to virtual qubit (index in original circuit)
p_2_v = {p: v for v, p in enumerate(layout)}
# compute map between virtual and canonical qubit indices.
c_2_v = {c: p_2_v[p] for c, p in c_2_p.items()}
assert len(c_2_v) == num_qubits
bases_canon = [
np.array([base_i[c_2_v[c]] for c in range(num_qubits)], dtype=np.uint8)
for base_i in bases_virt
]
سير العمل لتظليل المخروط الضوئي، وتعلم الضجيج، وحقن مضاد الضجيج
في هذا الدليل التعليمي، نُشغّل حسابات حدود SLC قبل اكتمال تعلم الضوضاء، بحيث تُشغَّل الـ Circuit المراد تخفيفها في أقرب وقت ممكن من نموذج الضوضاء المُتعلَّم. من حيث المبدأ، يمكن موازاة سير العمل هذا أكثر: يمكن تشغيل مهمة تعلم الضوضاء بينما تُقدَّر حدود الضوضاء بالتوازي. بالنسبة لدارة كمومية عشوائية، يمكن أن يتوسع حساب حد الضوضاء باعتماد أُسّي ضعيف، لذا فإن موازاة حساب الحدود (على سبيل المثال، عبر العديد من أنوية المعالج) يعطي حدودًا أضيق لميزانية زمن حوسبة معينة، ويمكن أيضًا موازاة تنفيذات QPU وحسابات الحدود نفسها لتحقيق سير العمل الأكثر كفاءة.
التنبؤ بـ Pauli نموذج الضجيج المراد تعلمه
قبل إجراء أي تعلم للضجيج على العتاد، نُعدد أولاً حدود خطأ Pauli التي سيحتويها نموذج الضجيج لدينا. تذكر أننا نُمثّل ضجيج كل طبقة فريدة كنموذج Pauli-Lindblad متناثر بمعدل خطأ واحد لكل Pauli ذي وزن واحد (، ، أو ) على كل كيوبت نشط، وواحد لكل Pauli ذي وزن اثنين على كل زوج متصل من الكيوبتات النشطة. تمر دالة generate_noise_model_paulis عبر كل طبقة فريدة مصندقة من الدارة المقدمة وتُولّد بالضبط قائمة حدود Pauli هذه، مع مراعاة اتصال كيوبتات الدارة.
بعبارة أخرى، تتنبأ هذه الخطوة بـبنية نموذج الضجيج الذي سيملأه NoiseLearnerV3 لاحقاً بالمعدلات المتعلمة. معرفة هذه البنية مسبقاً هو ما يسمح لنا بحساب حد أمامي وحد خلفي لكل خطأ محتمل قبل أن يعمل تعلم الضجيج حتى.
noise_model_paulis = generate_noise_model_paulis(
unique_2q_instructions, backend.coupling_map, boxed_circuit
)
noise_model_rates = {ref: None for ref in noise_model_paulis}
أ. حساب الحدود الأمامية
تُقيّم دالة compute_forward_bounds علاقات التبديل بين البوابات في كل طبقة وحدود Pauli المولدة أعلاه من حيث كيفية تأثير أخطاء الانتشار الأمامي على المُلاحَظ المطلوب . بالنسبة للبوابات التي تتبدل مع حدود Pauli، لا يُفعل شيء. أما البوابات الكليفوردية، فتُدفع نحو بداية الدارة. وبالنسبة للبوابات غير الكليفوردية، نُقرّب تأثيرها على المُلاحَظات المستهدفة ليُعطى الأولوية لاحقاً لإلغاء الضجيج (بعد دمج جميع الحدود). يتحقق هذا الحد أولاً بتطبيق معيار L2 (أي الجذر التربيعي لمجموع مربعات معاملات حدود Pauli ذات الصلة). عندما يكون هناك عدد كبير جداً من حدود الكيوبتات المتضمنة، نعود إلى حد أوسع يستخدم متباينة المثلث.
تعيين معاملات حساب الحد
تتحكم الإعدادات التالية في حسابات الحد الكلاسيكية:
-
slc_atol: التسامح المطلق للحسابات. تُعامل حدود المؤثر ذات المعاملات دون هذا الحد كمهملة وتُحذف. -
slc_eigval_max_qubits: الحد الأقصى لعدد الكيوبتات التي يُقيَّم عندها الحد الأضيق القائم على القيم الذاتية (معيار L2). تعود المؤثرات المدعومة على كيوبتات أكثر من ذلك إلى حد متباينة المثلث الأوسع الموصوف أعلاه. -
slc_evolution_max_terms: الحد الأقصى لعدد حدود Pauli المحتفظ بها أثناء نشر المؤثرات عبر الدارة. يُبقي اقتطاع الحدود الأصغر الحساب قابلاً للتنفيذ على حساب حدود أوسع قليلاً. -
slc_num_processes: عدد عمليات المعالج المركزي التي يُوزَّع عليها حساب الحد بشكل متوازٍ. -
slc_timeout: ميزانية الوقت (بالثواني) لحساب الحد. أي طبقات لم يتم الوصول إليها ضمن هذه الميزانية تُعطى الحد التافه وهو 2.
في هذا الدليل التعليمي، يتم الوصول إلى مهلة الانتظار (timeout) بشكل متعمد: نُبقي slc_timeout صغيرة عمدًا حتى يعمل الدفتر بسرعة، ولإظهار أن الوصول إلى المهلة آمن. القيم المحسوبة هي حدود عليا في كل مرحلة، لذا فإن الوصول إلى المهلة لا يؤدي إلا إلى جعل الحدود أكثر مرونة (تافهة) للطبقات التي لم يتم الوصول إليها؛ ولا يقلل أبدًا من تأثير الخطأ. يظهر هذا في التصورات أدناه على شكل امتلاء الطبقات اليسرى بالكامل بالحد التافه. زيادة المهلة (أو عدد العمليات) يُضيّق الحدود ويقلل أكثر من العبء الحسابي للأخذ بالعينات.
slc_atol = 1e-8
slc_eigval_max_qubits = 18
slc_evolution_max_terms = 1000
slc_num_processes = 8
slc_timeout = 60
forward_bounds = compute_forward_bounds(
boxed_circuit,
noise_model_paulis,
isa_observable,
evolution_max_terms=slc_evolution_max_terms,
eigval_max_qubits=slc_eigval_max_qubits,
atol=slc_atol,
num_processes=slc_num_processes,
timeout=slc_timeout,
)
Bounds computation timed out.
تصور SLC للفحص اليدوي
يمكنك تفسير سلوك الحدود المظللة بفحص كيفية تفاعل القياسات وحدود Pauli مع الأخطاء المحلية. هذه الأنماط مميزة لمشكلة تطور هاملتونيان إيزنغ المدفوع هذه، وتظهر أيضاً في الورقة البحثية تظليل المخروط الضوئي لتخفيف الخطأ الكمومي المُسرَّع كلاسيكياً، مع عدة سمات مميزة:
-
يمكننا التمييز بوضوح بين المخروطين الناشئين عن Pauli غير الهوية في المُلاحَظ.
-
يمكننا أن نرى أن قياس X على الكيوبت 6 يتبدل مع خطأ X في الطبقة الأقصى يميناً.
-
يمكننا أن نرى أن Pauli من نوع Z على الكيوبت 13 يتبدل مع خطأ Z في الطبقة الأقصى يميناً.
-
عندما نصل إلى المهلة المحددة أعلاه، تُملأ الطبقات المتبقية إلى اليسار بالكامل بحدود تافهة قيمتها اثنان.
for p in "XYZ":
display(
draw_shaded_lightcone(
boxed_circuit,
forward_bounds,
noise_model_paulis,
pauli_filter=p,
scale=0.15,
fold=-1,
idle_wires=False,
wire_order=wire_order,
measure_arrows=False,
)
)



ب. تضييق الحدود الأمامية باستخدام حدود السرعة الكمومية
نُضيّق الحدود بعد ذلك باستخدام دالة tighten_with_speed_limit. الانتشار الهندسي للمخروط الضوئي للمُلاحَظ مُراعى بالفعل بالكامل في الحدود الأمامية المحسوبة أعلاه. ما تضيفه هذه الدالة هو ممر اتساق ذاتي يستفيد من السرعة المحدودة التي يمكن أن تنتقل بها المعلومات عبر الدارة — ينمو المخروط الضوئي بكيوبت واحد على الأكثر لكل طبقة من البوابات ذات الكيوبتين — لتضييق الحدود المحسوبة سابقاً بالنسبة لبعضها البعض. تتدفق المعلومات في كلا الاتجاهين:
-
من حدود المبدّل إلى حدود المُلاحَظ المنتشر خلفياً: إذا تبدل المُلاحَظ مع خطأ على كيوبت، فلا يمكن أن يكون له مكوّن أو كبير هناك.
-
من حدود المُلاحَظ المنتشر خلفياً إلى حدود المبدّل: إذا كان للمُلاحَظ مكوّن و صغير على كيوبت، فيجب أن يتبدل تقريباً مع هناك.
forward_bounds_tighter = tighten_with_speed_limit(
forward_bounds, boxed_circuit, noise_model_paulis, isa_observable
)
تصور SLC للفحص اليدوي
من حيث المبدأ، يوفر هذا التضييق انتقالاً أكثر سلاسة بين الحدود المحسوبة والحدود التافهة المخصصة للطبقات بعد المهلة. في هذا المثال، يكاد التأثير لا يُلاحَظ، لأن المخاريط الضوئية قد انتشرت بالفعل إلى حواف الدارة عند الوصول إلى المهلة.
for p in "XYZ":
display(
draw_shaded_lightcone(
boxed_circuit,
forward_bounds_tighter,
noise_model_paulis,
pauli_filter=p,
scale=0.15,
fold=-1,
idle_wires=False,
wire_order=wire_order,
measure_arrows=False,
)
)



ج. حساب الحدود الخلفية
يُقيّم هذا الجزء من التنبؤ بالضجيج كيف يمكن لخطأ في طبقة معينة أن يؤثر على الحالة المدخلة . تعكس دالة compute_backward_bounds أولاً الدارة وتزيل بوابات القياس، ثم تُتابع بتحليل مشابه لما جرى لحسابات الحدود الأمامية.
backward_bounds = compute_backward_bounds(
boxed_circuit,
noise_model_paulis,
evolution_max_terms=slc_evolution_max_terms,
num_processes=slc_num_processes,
timeout=slc_timeout,
)
تصور SLC للفحص اليدوي
من حساب الحدود الخلفية، يمكننا أن نرى كيف تحكم بنية الحالة الابتدائية السلوك المبكر لانتشار الخطأ:
-
يمكننا أن نرى بوضوح كيف تتبدل أخطاء Z ابتدائياً مع الحالة الابتدائية |0⟩.
-
فقط على الكيوبت 6، حيث نُهيّئ الحالة الذاتية بقيمة +1 لأساس X، يفشل خطأ Z في التبدل، بينما يتبدل خطأ X.
for p in "XYZ":
display(
draw_shaded_lightcone(
boxed_circuit,
backward_bounds,
noise_model_paulis,
pauli_filter=p,
scale=0.15,
fold=-1,
idle_wires=False,
wire_order=wire_order,
measure_arrows=False,
)
)



معاينة الحدود المدمجة دون معدلات الضجيج المتعلمة
تحدد دالة merged_bounds النقطة في الدارة التي يقلل عندها التحول من الحدود الخلفية إلى الحدود الأمامية إجمالي الانحياز المقدر على المُلاحَظ المطلوب. يُحسب هذا الانحياز كمجموع مساهمات الحد الخلفي لجميع مواقع الضجيج قبل تلك النقطة، بالإضافة إلى مساهمات الحد الأمامي لجميع مواقع الضجيج بعدها. حالياً، يتم هذا بشكل موحد لجميع الكيوبتات.
تعتمد النقطة المثلى للتحول من الحدود الخلفية إلى الأمامية على معدلات الضجيج المتعلمة. في هذه المرحلة لم نتعلم بعد أي معدلات: تم تهيئة noise_model_rates إلى None لكل طبقة، والتي تُعاملها merge_bounds كمعدلات ضجيج موحدة. لذلك فإن الحدود المدمجة المعروضة هنا ليست سوى معاينة؛ سنعيد حسابها بالمعدلات المتعلمة الفعلية في الخطوة 3.
merged_bounds = merge_bounds(
boxed_circuit,
forward_bounds_tighter,
backward_bounds,
noise_model_rates,
)
Missing noise rates. Partitioning backward/forward commutator bounds by assuming uniform error rates.
Optimal spacetime partitioning not implemented!Just partitioning list of noisy boxes.
تصور SLC للفحص اليدوي
بعد دمج الحدود الخلفية والأمامية المُضيّقة، يصبح سلوك SLCs المجمعة واضحاً:
-
تخبرنا الدالة أعلاه أن هناك تجزئة يتم اختيارها ينتقل عندها التحول من الحدود الخلفية إلى الحدود الأمامية المُضيّقة.
-
يمكننا أن نرى أدناه أن SLCs تحتوي الآن على حدود خلفية جزئية وحدود أمامية مُضيّقة جزئية.
for p in "XYZ":
display(
draw_shaded_lightcone(
boxed_circuit,
merged_bounds,
noise_model_paulis,
pauli_filter=p,
scale=0.15,
fold=-1,
idle_wires=False,
wire_order=wire_order,
measure_arrows=False,
)
)



الخطوة 3: التنفيذ
في هذا القسم نبدأ الجزء من سير العمل الذي يستخدم جهازاً كمومياً حقيقياً. لهذه الطريقة القائمة على التعلم لتخفيف الخطأ، هناك خطوتان:
-
تعلم الضجيج باستخدام
NoiseLearnerV3. -
تنفيذ دارة تخفيف الخطأ باستخدام إطار عمل
samplomaticوExecutor.
بواسطة الأخطاء المحدودة من دارتنا الكمومية، نتعلم معدلات الضجيج المرتبطة لإعطاء الأولوية لميزانية الخطأ لدينا، وتحديد الحمل الزائد للعينات، والتنفيذ على وحدة معالجة كمومية.
أ. تعلم معدلات الضجيج
يُحدد متعلم الضجيج عمليات الضجيج المؤثرة على البوابات في دارة واحدة أو أكثر من الدارات المطلوبة، استناداً إلى نموذج ضجيج Pauli-Lindblad المتناثر. تُطلق طريقة run() مهمة تعلم ضجيج للطبقات الفريدة ذات الكيوبتين المقدمة، باستخدام الخيارات المحددة في تكوين متعلم الضجيج. تتحكم هذه الخيارات في استراتيجية التواء Pauli، وعدد التوزيعات العشوائية واللقطات، وأعماق التعلم، والاختيار اللاحق.
نختار أيضاً أعماق التعلم عمداً. من النتائج العملية للتخفيف القائم على التعلم مع samplomatic أنه من المفيد جداً أن يطابق أعمق عمق تعلم عمق الدارة التي تريد تخفيفها. ولأن layer_pair_depths الخاصة بـ NLv3 تُقاس بـ أزواج الطبقات (طبقة وعكسها)، نُعيّن القيمة الأعمق إلى نصف عمق طبقة الكيوبتين للدارة.
post_selection_enabled = True
# Match the deepest noise-learning depth to the depth of the circuit being
# mitigated. NLv3 ``layer_pair_depths`` are measured in layer pairs (a layer
# plus its inverse), so the deepest value is half the circuit's two-qubit-layer
# depth. Learning to this depth markedly improves the quality of the mitigation.
#
# We measure the two-qubit-layer depth on the pre-boxed ISA circuit: after
# boxing, every two-qubit gate is hidden inside a full-width ``BoxOp``, so a
# ``num_qubits == 2`` filter on ``boxed_circuit`` matches nothing (and
# ``QuantumCircuit.depth`` does not recurse into boxes).
depth_2q = isa_circuit.depth(lambda instr: instr.operation.num_qubits == 2)
max_layer_pair_depth = depth_2q // 2 # dividing by 2 since we want pairs
# Use a fixed schedule of learning depths, but drop any that exceed the circuit's
# depth and always cap the deepest value at ``max_layer_pair_depth`` so we never
# learn deeper than the circuit being mitigated.
candidate_depths = [1, 2, 4, 8, 12, 16, 24, 32, 40, 48]
layer_pair_depths = sorted(
{d for d in candidate_depths if d < max_layer_pair_depth}
| {max_layer_pair_depth}
)
noise_learner_options = {
"num_randomizations": 64,
"shots_per_randomization": 128,
"layer_pair_depths": layer_pair_depths,
"post_selection": {
"enable": post_selection_enabled,
"strategy": "edge",
"x_pulse_type": "rx",
},
"environment": {"job_tags": ["TUT_SLC"]},
}
noise_learner = NoiseLearnerV3(backend, noise_learner_options)
noise_learner_job = noise_learner.run(unique_2q_instructions)
noise_learner_result = noise_learner_job.result()
if post_selection_enabled:
print(
"Minimum fraction of shots kept for noise learning experiments: ",
end="",
)
print(
f"{min([min(d.values()) for d in [nlr.metadata['post_selection']['fraction_kept'] for nlr in noise_learner_result[:2]]]):.2f}"
)
Minimum fraction of shots kept for noise learning experiments: 0.71
# Get a dict mapping each InjectNoise.ref to its learned PauliLindbladMap
refs_2_plm = noise_learner_result.to_dict(
unique_2q_instructions, require_refs=False
)
ب.١. تحديث الحدود المدمجة بمعدلات الضجيج المتعلمة الفعلية
الآن بعد أن تم تعلم نموذج الضجيج المحدد، يمكننا تطبيق معدلات الضجيج المتعلمة على حدود الضجيج المتنبأ بها والحصول على تحديد نهائي لأي الحدود لها التأثير الأكبر على تقليل الانحياز.
merged_bounds = merge_bounds(
boxed_circuit,
forward_bounds_tighter,
backward_bounds,
refs_2_plm,
)
Optimal spacetime partitioning not implemented!Just partitioning list of noisy boxes.
ب.٢. حساب local_scales للتنفيذ على العتاد
تنظر compute_local_scales إلى كل خطأ ضجيج محتمل في الدارة وتُقدّر مدى إمكانية تحيز ذلك الخطأ للقياس النهائي، وكذلك مدى تكلفة تصحيحه. ثم تُصنّف الأخطاء حسب مدى جدارتها بالتخفيف وتختار المجموعة الفرعية التي تقلل الانحياز قدر الإمكان، مع البقاء ضمن ميزانية تكلفة العينات المسموح بها (أو تحقيق دقة مطلوبة). النتيجة هي مجموعة من عوامل التحجيم التي تشير إلى الأخطاء التي سيتم تخفيفها بفعالية وتلك التي ستُترك دون تخفيف (local_scales)، إلى جانب الحمل الزائد الكلي المتوقع لتكلفة العينات (sampling_costs) والانحياز المتبقي (residual_bias_bound).
القدرة على التحكم في الانحياز المتبقي المطلوب هي ميزة حاسمة في تنفيذ SLC لـ PEC. في حين أن التنفيذ الأصلي كان يستهدف دائماً انحيازاً صفرياً في الحمل الزائد للعينات، يمكننا ضبط الحمل الزائد المطلوب للعينات بمقايضة مع الانحياز المتبقي المتوقع. يساعد هذا في تمكين المستخدم من البقاء ضمن ميزانية عينات ثابتة، وهو ما يمكن أن يكون مفيداً بشكل خاص عند إنشاء نموذج أولي لسير عمل في البداية.
id_map = map_modifier_ref_to_ref(boxed_circuit)
summed_rates = 0.0
for box_id, noise_id in id_map.items():
learned_plm = refs_2_plm[noise_id]
summed_rates += np.sum(learned_plm.rates)
# print(f"{box_id}:\tgamma = {np.exp(2 * summed_rates):1.6e}\tsampling cost = {np.exp(4 * summed_rates):1.6e}")
total_gamma = np.exp(2 * summed_rates)
print(
f"Full PEC gamma={total_gamma}, sampling cost (gamma^2) = {total_gamma**2}"
)
Full PEC gamma=12.750876317157042, sampling cost (gamma^2) = 162.58484685543633
biases = []
costs = []
for bias in [0.0] + np.arange(0.001, 0.102, 0.01).tolist():
_, cost_, bias_ = compute_local_scales(
boxed_circuit,
merged_bounds,
refs_2_plm,
sampling_cost_budget=np.inf,
bias_tolerance=bias,
)
biases.append(bias_)
costs.append(cost_)
المقايضة بين الحمل الزائد للعينات والانحياز المتبقي
xticks = np.arange(0, 11)
fig, ax = plt.subplots()
ax.scatter(
[0], [total_gamma**2], marker="D", c="tab:orange", label="full PEC"
)
ax.plot(
100 * np.array(biases),
np.array(costs),
"o-",
c="tab:blue",
label="PEC+SLC",
)
ax.set_yscale("log")
ax.set_xticks(xticks, [f"{x:.1f}" for x in xticks])
ax.set_xlabel("Remaining bias [%]")
ax.set_ylabel(r"Sampling overhead, $\gamma^2$")
ax.grid()
ax.legend()
fig.suptitle("PEC sampling overhead reduction due to SLC")
Text(0.5, 0.98, 'PEC sampling overhead reduction due to SLC')
chosen_bias_thres = 0.1
local_scales, sampling_cost, residual_bias_bound = compute_local_scales(
boxed_circuit,
merged_bounds,
refs_2_plm,
sampling_cost_budget=np.inf,
bias_tolerance=chosen_bias_thres,
)
print(
f"PEC+SLC sampling cost (gamma^2) = {sampling_cost} "
f"w/ remaining bias = {100 * residual_bias_bound:.1f}%"
)
PEC+SLC sampling cost (gamma^2) = 48.13646509622307 w/ remaining bias = 10.0%
ج. تنفيذ الدارة المطلوبة مع مضاد الضجيج
ج.١. تحضير دارة القالب باستخدام samplex
samplex هو مُخرج لطريقة build الخاصة بـ Samplomatic، والذي يُرمّز جميع المعلومات المطلوبة لتوليد معاملات عشوائية لـ template_circuit. تُستخدم هذه بعد ذلك لإعداد كائنات QuantumProgram، التي تُشغَّل بدورها على وحدة معالجة كمومية باستخدام الأساسي Executor. يمكن أن يحتوي كل QuantumProgram على عدة عناصر، يمكنك التفكير فيها كزوج من template وsamplex.
راجع الدرس التطبيقي مرحباً بـ samplomatic للتفاصيل.
# Build template circuit and samplex for later use with the "Executor"
template_circuit, samplex = samplomatic.build(boxed_circuit)
# Set up postselection if it's been enabled
if post_selection_enabled:
# Set up post selection PM (to add PS instructions)
post_selection_pm = PassManager(
[
AddSpectatorMeasures(backend.coupling_map),
AddPostSelectionMeasures(x_pulse_type="rx"),
]
)
final_template_circuit = post_selection_pm.run(template_circuit)
else:
final_template_circuit = template_circuit
ج.٢. إعداد QuantumProgram
num_randomizations = 4096
shots_per_randomization = 64
chunk_size = 256
# Set up QuantumProgram
program = QuantumProgram(shots=shots_per_randomization, noise_maps=refs_2_plm)
# no EM
# Collect up a dict of the other arguments that need to be bound to samplex_inputs
samplex_inputs = {
f"noise_scales.{ref}": float(0) for ref in local_scales.keys()
}
samplex_inputs |= {"basis_changes": {"basis0": bases_canon[0]}}
# Convert samplex_inputs into a dict to pass to QuantumProgram
samplex_arguments = (
samplex.inputs().bind(**samplex_inputs).make_broadcastable()
)
program.append_samplex_item(
circuit=final_template_circuit,
samplex=samplex,
samplex_arguments=samplex_arguments,
shape=(num_randomizations,),
chunk_size=chunk_size,
)
# plain PEC
# Collect a dict of the other arguments that need to be bound to samplex_inputs
samplex_inputs = {
f"noise_scales.{ref}": float(-1) for ref in local_scales.keys()
}
samplex_inputs |= {"basis_changes": {"basis0": bases_canon[0]}}
# Convert samplex_inputs into a dict to pass to QuantumProgram
samplex_arguments = (
samplex.inputs().bind(**samplex_inputs).make_broadcastable()
)
program.append_samplex_item(
circuit=final_template_circuit,
samplex=samplex,
samplex_arguments=samplex_arguments,
shape=(num_randomizations,),
chunk_size=chunk_size,
)
# PEC+SLC
# Collect a dict of the other arguments that need to be bound to samplex_inputs
samplex_inputs = {
f"noise_scales.{ref}": float(-1) for ref in local_scales.keys()
}
samplex_inputs |= {"basis_changes": {"basis0": bases_canon[0]}}
samplex_inputs |= {"local_scales": local_scales}
# Convert samplex_inputs into a dict to pass to QuantumProgram
samplex_arguments = (
samplex.inputs().bind(**samplex_inputs).make_broadcastable()
)
program.append_samplex_item(
circuit=final_template_circuit,
samplex=samplex,
samplex_arguments=samplex_arguments,
shape=(num_randomizations,),
chunk_size=chunk_size,
)
ج.٣. تنفيذ البرنامج باستخدام الأساسي Executor
executor = Executor(backend)
job_exec = executor.run(program)
results_exec = job_exec.result()
الخطوة 4: المعالجة اللاحقة
عندما نحسب قيمة التوقع النهائية المطلوبة باستخدام executor_expectation_values، نُنفذ بعض تقنيات المعالجة اللاحقة للمساعدة في ضمان الحصول على أعلى جودة ممكنة للنتائج. أولاً، نُطبّق انقراض خطأ القراءة الملتوي (TREX)، الذي يراعي أي أخطاء تحدث أثناء عملية القراءة. بعد ذلك، نُصحح الأخطاء الناتجة عن الضجيج غير الماركوفي على خلفيات Heron لدينا باستخدام طريقة اختيار لاحق. تقيس هذه الطريقة الكيوبتات النشطة والمتفرجة، ثم تُطبّق دوراناً بطيئاً على كل كيوبت، ثم تقيس مرة أخرى. في الحالات التي لا يُؤكد فيها القياسان انقلاب كيوبت كما هو متوقع، تُهمل هذه اللقطات بتطبيق mask من PostSelector. ضمن حساب القناع، يمكن تعيين استراتيجية محددة للترشيح استناداً إلى عقد أحادية الكيوبت أو حواف الجيران المتفرجين، مما يمكن أن يؤثر على كل من عدد اللقطات المُرشحة وجودة النتائج.
measurement_noise_map = noise_learner_result[2].to_pauli_lindblad_map()
trex_scale_factors = trex_factors(measurement_noise_map, reverser_virt)
post_selection_strategy = "node"
def post_process_conv(datum, steps=16, gamma=None, ps=False, trex=False):
meas = datum["meas"]
flips = datum["measurement_flips.meas"]
signs = datum.get("pauli_signs", None)
meas_basis_axis = None
avg_axis = 0
mask = None
if ps and post_selection_enabled:
# Post-select the results
post_selector = PostSelector.from_circuit(
circuit=final_template_circuit, coupling_map=backend.coupling_map
)
# Compute the ps mask for filtering results
mask = post_selector.compute_mask(
datum, strategy=post_selection_strategy
)
# Compute fraction of shots kept from post selection
total_num_shots = num_randomizations * shots_per_randomization
ps_ratio = np.sum(mask) * 100 / total_num_shots / len(bases_canon)
print(
f"With {post_selection_strategy}-based post selection ({ps_ratio:.1f}% of shots kept):"
)
results = []
for i in range(steps, num_randomizations + 1, steps):
# Compute mitigated expvals w/out post-selection
res = executor_expectation_values(
meas[:i],
reverser_virt,
meas_basis_axis,
avg_axis=avg_axis,
measurement_flips=flips[:i],
pauli_signs=signs[:i] if signs is not None else None,
postselect_mask=mask[:i] if mask is not None else None,
rescale_factors=trex_scale_factors if trex else None,
gamma_factor=gamma,
)
results.append(res[0])
return results
gamma_pec = gamma_from_noisy_boxes(refs_2_plm, id_map)
gamma_slc = gamma_from_noisy_boxes(refs_2_plm, id_map, local_scales)
steps = 16
results = {}
for label, result_idx, gamma, use_ps, use_trex in [
("PEC", 1, gamma_pec, True, True),
("PEC+SLC", 2, gamma_slc, True, True),
("Unmitigated", 0, None, False, False),
]:
res = post_process_conv(
results_exec[result_idx],
steps=steps,
gamma=gamma,
ps=use_ps,
trex=use_trex,
)
results[label] = res
With node-based post selection (24.1% of shots kept):
With node-based post selection (24.2% of shots kept):
من فحص النتائج التجريبية، يمكننا مقارنة سلوك النهج المختلفة بشكل مباشر: PEC، وPEC مدمجاً مع SLC، والأساس غير المخفف. بعض التفاصيل المحددة التي يجب إبرازها:
-
تقع النتيجة غير المخففة بعيداً خارج نطاق الانحياز 10% (بالقرب من 0.35) ولا تتأثر بعدد التوزيعات العشوائية.
-
على هذا الجهاز، يحمل PEC الكامل حملاً زائداً للعينات يبلغ . بعد تقلبات مبكرة كبيرة، يستعيد تقدير PEC العادي الإشارة ويستقر حول 1.1، متجاوزاً قليلاً القيمة الدقيقة. لاحظ أن نطاق الانحياز 10% ينطبق فقط على مُقدّر PEC+SLC: PEC العادي غير منحاز من حيث المبدأ، لذا فإن هذا الانحراف المتبقي لا يخضع لضمان انحياز بل يعكس تقلبات إحصائية وعيوباً في نموذج الضجيج المتعلم، وهو أمر معقول بحجم المشكلة هذا.
-
يقلل SLC الحمل الزائد بمقدار ~3.4 ضعف إضافي (إلى ، لحد انحياز متبقي يبلغ حوالي 10%). يتقلب PEC+SLC أيضاً بشدة عند عدد قليل من التوزيعات العشوائية، حتى أنه يصل في البداية إلى المنطقة غير الفيزيائية، لكن تقديره يتقارب بثبات نحو القيمة الدقيقة، مستقراً بالقرب من 1.0 داخل النطاق بأمان. يطابق أو يتجاوز دقة PEC العادي بحوالي ثلث تكلفة العينات، مما يوضح فائدة تظليل المخروط الضوئي.
-
تنكمش أشرطة الخطأ لكلا التقديرين المخففين مع تراكم التوزيعات العشوائية؛ عند عدد ثابت من التوزيعات العشوائية، يمتلك مُقدّر PEC+SLC تبايناً إحصائياً أصغر، بما يتسق مع قيمة الأصغر لديه.
لاحظ أن معدلات الضجيج المتعلمة (وبالتالي الأحمال الزائدة والتقديرات المخففة) تعتمد على الخلفية ومعايرتها وقت التنفيذ، لذا يمكن أن تُنتج إعادة تشغيل هذا الدفتر قيماً مختلفة كمياً.
fig, ax = plt.subplots(1, 1, figsize=(12, 6))
ax.axhline(1.0, color="black", label="Exact")
ax.fill_between(
[-50, 4100], -10, 0, color="grey", alpha=0.25, label="Unphysical"
)
ax.fill_between([-50, 4100], 1, 10, color="grey", alpha=0.25)
ax.fill_between(
[-50, 4100], 0.9, 1.1, color="red", alpha=0.25, label="10% bias"
)
for label, res in results.items():
ax.errorbar(
list(range(steps, num_randomizations + 1, steps)),
[r[0] for r in res],
yerr=[r[1] for r in res],
alpha=0.75,
marker="o",
linestyle="",
markerfacecolor="none",
label=label,
)
ax.set_ylabel(r"$\langle X_{6}Z_{13}\rangle$")
ax.set_xlabel("# randomizations")
ax.grid()
ax.legend(ncols=2)
ax.set_ylim([-0.1, 2.0])
ax.set_xlim([-50, 4100])
(-50.0, 4100.0)

الخطوات التالية
إذا وجدت هذا العمل مثيراً للاهتمام، فقد تهتم بالمواد التالية:

