تحسين قيم التوقع: امتصاص الضوضاء المنتشرة (PNA)
في هذا البرنامج التعليمي، سنتعلم كيفية الاستفادة من أحدث الأدوات في نظام Qiskit البيئي لتنفيذ سير عمل قابل للتخصيص الكامل مع تخفيف الأخطاء. سنستعرض تقنية PNA ونستخدمها للتخفيف من أخطاء Gate. كما سنستخدم TREX للتخفيف من أخطاء القراءة، والانتقاء اللاحق للتخفيف من الأخطاء غير المأخوذة في الاعتبار ضمن نموذج الضوضاء المُتعلَّم.
المحتويات
- تقديم نظرة عامة موجزة عن
PNA - إنشاء دائرة Circuit كمومية تروترية وعنصر قابل للرصد. Transpile إلى Backend وتضمين قياسات الانتقاء اللاحق.
- استخدام
samplomaticلتدوير طبقات بوابات 2Q والقياسات. إيجاد الطبقات الفريدة ثنائية الكيوبت لتقليل تكلفة تعلم الضوضاء. - استخدام
NoiseLearnerV3لتعلم نموذج الخطأ المؤثر في بوابات 2Q والقياسات. - استخدام
qiskit-addon-pnaلتوليد عنصر قابل للرصد يخفف الضوضاء. - استخدام أداة
qiskit-ibm-runtime.Executorالأولية لتوليد العينات الخام من وحدة معالجة الكم (QPU) التي تعكس كل لقطة لكل تعشية عشوائية وكل قاعدة قياس. - استخدام
qiskit-addon-utilsلمعالجة البيانات لاحقاً وتحويلها إلى قيمة توقع مخففة.
ما هو امتصاص الضوضاء المنتشرة (PNA)؟
تقنية للتخفيف من أخطاء Gate عن طريق نشر العنصر القابل للرصد عبر قناة الضوضاء العكسية المؤثرة في بوابات ثنائية الكيوبت، مما ينتج عنه عنصر قابل للرصد يخفف الضوضاء.
ستتأثر بوابات 2Q في التجربة التي نريد تشغيلها بضوضاء كبيرة.
إذا تعلمنا نموذج الضوضاء، يمكننا تطبيق معكوسه وإلغاء الضوضاء.
بدلاً من تنفيذ قناة الضوضاء العكسية عن طريق أخذ عينات منها على وحدة معالجة الكم كما في PEC، يمكننا تنفيذها بشكل كلاسيكي في العنصر القابل للرصد المقيس باستخدام انتشار Pauli. ينتج عن ذلك عنصر قابل للرصد أكثر تعقيداً والذي، عند قياسه، يُحقق تأثير التخفيف من ضوضاء البوابات المُتعلَّمة.

توليد دائرة Trotter المرآتية والعنصر القابل للرصد
في هذه التجربة، سندرس ديناميكيات الزمن لنموذج Ising المركل ذي 30 موقعاً على سلسلة دوران أحادية الأبعاد. الهاملتوني المأخوذ بعين الاعتبار هو:
,
حيث يصف اقتران الدورانات الأقرب جواراً، ، ويُعيَّن الحقل المستعرض العالمي على . كلما ابتعد عن زاوية Clifford (أي )، كلما أصبح نشر مولدات مكافحة الضوضاء عبر الدائرة أكثر صعوبة.
بالنسبة لاختيار العنصر القابل للرصد، سننظر في متوسط المغنطة أحادية الموقع، ، حيث هو عدد المواقع.
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-pna qiskit-addon-utils qiskit-ibm-runtime samplomatic
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import Pauli, SparsePauliOp
num_qubits = 30
num_trotter_steps = 10
rx_angle = np.pi / 8
# Avg single-site magnetization
id_pauli = Pauli("I" * num_qubits)
observable = SparsePauliOp([id_pauli.dot(Pauli("Z"), [i]) for i in range(num_qubits)]) / num_qubits
# Implement Trotterized kicked-Ising model
circuit = QuantumCircuit(num_qubits)
for _step 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)
circuit.compose(circuit.inverse(), inplace=True)
circuit.measure_active()
circuit.draw("mpl", fold=-1)

بعد ذلك، سنختار سلسلة من الكيوبتات على ibm_kingston تُسجل معدلات خطأ منخفضة وننقل الدائرة Circuit إلى Backend.
from qiskit.transpiler import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService
backend_name = "ibm_kingston"
service = QiskitRuntimeService()
backend = service.backend(backend_name, use_fractional_gates=True)
# Use a chain of low-noise qubits
layout = [
44,
45,
46,
47,
57,
67,
68,
69,
78,
89,
88,
87,
97,
107,
106,
105,
117,
125,
126,
127,
128,
129,
118,
109,
110,
111,
98,
91,
92,
93,
]
pm = generate_preset_pass_manager(backend=backend, initial_layout=layout, optimization_level=0)
isa_circuit = pm.run(circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
isa_circuit.draw("mpl", fold=-1)
qiskit_runtime_service._discover_account:WARNING:2025-11-10 14:30:57,148: Loading account with the given token. A saved account will not be used.

تدوير طبقات بوابات ثنائية الكيوبت والقياسات وإيجاد الطبقات الفريدة
هنا نضمن أن مدير التمرير يُضيف تعليقات Twirl وInjectNoise على الصناديق، مما يتيح لنا تعلم الضوضاء التي ستؤثر في دائرتنا Circuit وربط تلك الضوضاء بطبقة الدائرة المقابلة لها.
enable_gates/enable_measure: True: احتواء جميع طبقات بوابات ثنائية الكيوبت والقياسات الطرفية داخل صناديق. ستُلحق بوابات الكيوبت الواحد بالصناديق من الجانب الأيسر.measure_annotations: allتضمين تعليقاتTwirlوChangeBasisعلى صندوق القياس.twirling_strategy: active: تدوير جميع الكيوبتات النشطة في كل صندوق يحتوي على بوابات تشابك.inject_noise_targets: gates: ينبغي إضافة تعليقاتInjectNoiseإلى جميع الصناديق المُعلَّمة بـTwirlوالتي تحتوي على بوابات تشابك.inject_noise_strategy: uniform_modification: ينبغي تحجيم جميع طبقات الضوضاء بصورة متكافئة.
from samplomatic.transpiler import generate_boxing_pass_manager
# Box up circuit with Twirl and InjectNoise annotations
pm = generate_boxing_pass_manager(
enable_gates=True,
enable_measures=True,
measure_annotations="all",
twirling_strategy="active",
inject_noise_targets="gates",
inject_noise_strategy="uniform_modification",
remove_barriers=True,
)
boxed_circuit = pm.run(isa_circuit)
draw_circ = QuantumCircuit(boxed_circuit.num_qubits)
draw_circ.append(boxed_circuit.data[0], qargs=boxed_circuit.data[0].qubits)
draw_circ.append(boxed_circuit.data[1], qargs=boxed_circuit.data[1].qubits)
draw_circ.draw("mpl", fold=-1, scale=0.3, idle_wires=False)

توليد الدائرة النموذجية و samplex، وتحديد كيفية أخذ عينات الدائرة
هنا نضيف أيضاً قياسات المراقبة والانتقاء اللاحق، وهي ضرورية لتطبيق الانتقاء اللاحق على العينات الناتجة عن Executor.
import samplomatic
from qiskit.transpiler import PassManager
from qiskit_addon_utils.noise_management.post_selection.transpiler.passes import (
AddPostSelectionMeasures,
AddSpectatorMeasures,
)
# Build template circuit and samplex for later use with the "Executor"
template_circuit, samplex = samplomatic.build(boxed_circuit)
# Add post-selection instructions to the template circuit
post_selection_pm = PassManager(
[
AddSpectatorMeasures(backend.coupling_map),
AddPostSelectionMeasures(x_pulse_type="rx"),
]
)
template_circuit = post_selection_pm.run(template_circuit)
draw_circ = template_circuit.copy_empty_like()
draw_circ.data = template_circuit.data[:324]
draw_circ.draw("mpl", fold=-1, scale=0.3, idle_wires=False)

تعلم الضوضاء
قبل تشغيل التجارب، نتعلم نموذج الضوضاء المؤثر في بوابات التشابك والقياسات في الدائرة Circuit. يعد امتلاك نموذج ضوضاء دقيق أمراً ضرورياً للتخفيف الفعال من الأخطاء. إن تعلم الضوضاء قُبيل تنفيذ التجارب مباشرةً يمنح أفضل فرصة لكي يصف نموذج الضوضاء الضوضاء الفعلية المؤثرة في البوابات أثناء التنفيذ.
قبل تعلم الضوضاء، نحتاج إلى إيجاد الطبقات ثنائية الكيوبت الفريدة في دائرتنا Circuit، حتى نتمكن من تقليل عدد اللقطات اللازمة لتعلم الضوضاء لكامل الدائرة. نستخدم find_unique_box_instructions من samplomatic للحصول على الطبقات الفريدة من الدائرة ذات الصناديق، بما فيها طبقة القياس. هذه هي الطبقات التي نمررها إلى متعلم الضوضاء.
بمجرد معرفة الطبقات، يمكننا تعلم الضوضاء. ثمة بعض المعاملات التي نأخذها بعين الاعتبار:
num_randomizations: عدد الدوائر العشوائية المستخدمة لكل تكوين دائرة تعلم.shots_per_randomization: العدد الإجمالي للقطات المستخدمة لكل دائرة تعلم عشوائية.layer_pair_depths: أعماق الدائرة Circuit (قياساً بعدد الأزواج) المستخدمة في تجارب التعلم.post_selection: سنستخدم الانتقاء اللاحق القائم على الحواف أثناء التعلم باستخدام بواباتrxلتنفيذ نبضات ما بعد القياس.
from qiskit_ibm_runtime.noise_learner_v3.noise_learner_v3 import NoiseLearnerV3
from qiskit_ibm_runtime.options import NoiseLearnerV3Options
from samplomatic.utils import find_unique_box_instructions
# Load noise learner data from a shared job
load_saved_nl_result = True
# Noise learning parameters
num_randomizations_nl = 64
shots_per_randomization_nl = 128
strategy = "edge"
enable_postsel = True
x_pulse_type = "rx"
# Find the unique instructions (layers) from boxed-up circuit
unique_2q_layers_and_meas = find_unique_box_instructions(
boxed_circuit, normalize_annotations=None, undress_boxes=True
)
noise_learner_params = {
"num_randomizations": num_randomizations_nl,
"shots_per_randomization": shots_per_randomization_nl,
"layer_pair_depths": [1, 2, 4, 8, 12, 16, 24, 32, 40, 48],
"post_selection": {
"enable": enable_postsel,
"strategy": strategy,
"x_pulse_type": x_pulse_type,
},
"experimental": {},
}
# set the options
noise_learner_options = NoiseLearnerV3Options(**noise_learner_params)
# run the noise learner job
noise_learner = NoiseLearnerV3(backend, noise_learner_options)
noise_learner_job = noise_learner.run(unique_2q_layers_and_meas)
noise_learner_result = noise_learner_job.result()
nl_metadata = noise_learner_params | {"layout": layout}
import matplotlib.pyplot as plt
hw_rates_1q = []
hw_rates_2q = []
for nlr in noise_learner_result[:2]:
plm_list = nlr.to_pauli_lindblad_map().to_sparse_list()
hw_rates_1q += [rate for (pstr, qubits, rate) in plm_list if len(pstr) == 1]
hw_rates_2q += [rate for (pstr, qubits, rate) in plm_list if len(pstr) == 2]
hw_rates_1q = sorted(hw_rates_1q)
hw_rates_2q = sorted(hw_rates_2q)
median_1q = hw_rates_1q[len(hw_rates_1q) // 2]
median_2q = hw_rates_2q[len(hw_rates_2q) // 2]
fig, ax = plt.subplots(1, 1, figsize=(14, 5))
ax.scatter(
(hw_rates_1q),
[(i) / (len(hw_rates_1q) - 1) for i in range(len(hw_rates_1q))],
color="red",
label="1q rates",
)
ax.set_xscale("log")
ax.set_ylim(0, 1.1)
ax.vlines(median_1q, 0, 1, color="red")
ax.text(median_1q * 1.1, 0.1, f"{median_1q:.2e}")
ax.scatter(
(hw_rates_2q),
[(i) / (len(hw_rates_2q) - 1) for i in range(len(hw_rates_2q))],
color="blue",
label="2q rates",
)
ax.set_xscale("log")
ax.set_ylim(0, 1.1)
ax.vlines(median_2q, 0, 1, color="blue")
ax.text(median_2q * 1.1, 0.2, f"{median_2q:.2e}")
ax.set_title("Learned noise rates")
ax.set_xlabel("Noise rate")
ax.set_yticks([])
plt.legend()
<matplotlib.legend.Legend at 0x321dd63f0>

ربط صناديق الدوائر بالضوضاء المُتعلَّمة
هنا، ننشئ تعييناً بين معرّفات مرجع InjectNoise لكل صندوق ونموذج الضوضاء المُتعلَّم (PauliLindbladMap) الذي يؤثر على البوابات ذات الكيوبتين في ذلك الصندوق.
from samplomatic.annotations import InjectNoise
from samplomatic.utils import get_annotation
# map inject noise refs to pauli lindblad maps
refs_to_noise_models = {}
for instruction, result in zip(unique_2q_layers_and_meas, noise_learner_result, strict=False):
if inject_noise_annot := get_annotation(instruction.operation, InjectNoise):
refs_to_noise_models[inject_noise_annot.ref] = result.to_pauli_lindblad_map()