محاكاة نموذج إيزنغ ثنائي الأبعاد بحقل مائل باستخدام دالة QESEM
دوال Qiskit ميزةٌ تجريبية متاحة فقط لمستخدمي خطط IBM Quantum® Premium Plan وFlex Plan وOn-Prem (عبر IBM Quantum Platform API). وهي في مرحلة إصدار معاينة وقد تخضع للتغيير.
تقدير الاستخدام: 20 دقيقة على معالج Heron r2. (ملاحظة: هذا تقدير فحسب. قد يختلف وقت التشغيل الفعلي.)
الخلفية النظرية
يوضح هذا البرنامج التعليمي كيفية استخدام QESEM، دالة Qiskit من Qedma، لمحاكاة ديناميكيات نموذج دوران كمي معياري، وهو نموذج إيزنغ ثنائي الأبعاد بحقل مائل (TFI) بزوايا غير Clifford:
حيث يدل على الجيران الأقرب في الشبكة. تُعدّ محاكاة التطور الزمني للأنظمة الكمية متعددة الجسيمات مهمةً صعبة حسابياً للحواسيب الكلاسيكية. في المقابل، صُمِّمت الحواسيب الكمية بطبيعتها لأداء هذه المهمة بكفاءة. وقد أصبح نموذج TFI على وجه الخصوص مرجعاً شا ئعاً على العتاد الكمي نظراً لسلوكه الفيزيائي الثري وسهولة تطبيقه على العتاد.
بدلاً من محاكاة الديناميكيات الزمنية المستمرة، نعتمد نموذج إيزنغ المُركَل (kicked Ising) المرتبط ارتباطاً وثيقاً بالنموذج الأصلي. يمكن التعبير عن الديناميكيات بدقة على شكل دائرة كمية دورية، إذ يتألف كل خطوة تطور من ثلاث طبقات من البوابات ثنائية الكيوبت الجزئية ، تتخللها طبقات من البوابات أحادية الكيوبت و.
سنستخدم زوايا عامة تمثل تحدياً لكل من المحاكاة الكلاسيكية وتخفيف الأخطاء. تحديداً، اخترنا و و، مما يضع النموذج بعيداً عن أي نقطة قابلة للتكامل.
سنقوم في هذا البرنامج التعليمي بما يلي:
- تقدير وقت تشغيل QPU المتوقع للتخفيف الكامل للأخطاء باستخدام ميزات تقدير الوقت التحليلية والتجريبية في QESEM.
- بناء ومحاكاة دائرة نموذج إيزنغ ثنائي الأبعاد بحقل مائل باستخدام تخطيطات كيوبت مستوحاة من العتاد وطبقات البوابات.
- تصوير اتصالية كيوبتات الجهاز والرسوم البيانية الجزئية المختارة لتجربتك.
- توضيح استخدام الانتشار العكسي للمؤثرات (OBP) لتقليل عمق الدائرة. تُتيح هذه التقنية اختصار العمليات من نهاية الدائرة على حساب قياسات مؤثرات أكثر.
- تنفيذ تخفيف الأخطاء (EM) غير المتحيز لمراقِبات متعددة في آنٍ واحد باستخدام QESEM، مع مقارنة النتائج المثالية والمشوَّشة والمُخففة.
- تحليل ورسم تأثير تخفيف الأخطاء على المغنطة عبر أعماق دوائر مختلفة.
ملاحظة: سيُعيد OBP عموماً مجموعة من المراقِبات التي قد تكون غير تبديلية. يُحسِّن QESEM تلقائياً أسس القياس عندما تحتوي المراقِبات المستهدفة على حدود غير تبديلية. ويُولِّد مجموعات أسس قياس مرشحة باستخدام عدة خوارزميات إرشادية، ويختار المجموعة التي تُقلل عدد الأسس المتمايزة. يعن ي ذلك أن QESEM يجمع المراقِبات المتوافقة في أسس مشتركة للحد من إجمالي عدد إعدادات القياس المطلوبة، مما يُحسِّن الكفاءة.
نبذة عن QESEM
QESEM برنامجٌ موثوق وعالي الدقة قائم على التوصيف، يُطبِّق تخفيفاً فعَّالاً وغير متحيز للأخطاء شبه الاحتمالية. صُمِّم لتخفيف الأخطاء في الدوائر الكمية العامة وهو مستقل عن التطبيق. وقد جرى التحقق منه عبر منصات عتاد متنوعة، بما في ذلك تجارب على نطاق المنفعة باستخدام أجهزة IBM® Eagle وHeron. تتسلسل مراحل سير عمل QESEM على النحو التالي:
- توصيف الجهاز - يرصد أمانات البوابات ويحدد الأخطاء المتماسكة، مُوفِّراً بيانات معايرة آنية. تضمن هذه المرحلة أن يستفيد التخفيف من العمليات ذات أعلى أمانة متاحة.
- النقل المدرك للضوضاء - يُولِّد تعيينات كيوبتات بديلة ومجموعات عمليات وأسس قياس ويُقيِّمها، مُختاراً المتغير الذي يُقلل وقت تشغيل QPU المقدَّر، مع إمكانية التوازي الاختياري لتسريع جمع البيانات.
- قمع الأخطاء - يُعيد تعريف البوابات الأصلية، ويُطبِّق Pauli twirling، ويُحسِّن التحكم على مستوى النبضات (على المنصات المدعومة) لتحسين الأمانة.
- توصيف الدائرة - يبني نموذج خطأ محلي مُخصَّصاً ويُلائمه مع قياسات QPU لتحديد مقدار الضوضاء المتبقية.
- تخفيف الأخطاء - يبني تحليلات شبه احتمالية متعددة الأنواع، ويأخذ عينات منها في عملية تكيفية تُقلل وقت تشغيل QPU للتخفيف والحساسية لتذبذبات العتاد، محققاً دقة عالية عند أحجام دوائر كبيرة.
لمزيد من المعلومات حول QESEM وتجربة على نطاق المنفعة لهذا النموذج على رسم بياني جزئي عالي الاتصالية من 103 كيوبت ضمن الهندسة الأصلية ثقيلة السداسية لـ ibm_marrakesh، راجع تخفيف الأخطاء الموثوق عالي الدقة للدوائر الكمية على نطاق المنفعة.

المتطلبات
ثبِّت حزم Python التالية قبل تشغيل الدفتر:
- Qiskit SDK v2.0.0 أو أحدث (
pip install qiskit) - Qiskit Runtime v0.40.0 أو أحدث (
pip install qiskit-ibm-runtime) - Qiskit Functions Catalog v0.8.0 أو أحدث (
pip install qiskit-ibm-catalog) - إضافة Operator Backpropagation لـ Qiskit v0.3.0 أو أحدث (
pip install qiskit-addon-obp) - إضافة Qiskit Utils v0.1.1 أو أحدث (
pip install qiskit-addon-utils) - محاكي Qiskit Aer v0.17.1 أو أحدث (
pip install qiskit-aer) - Matplotlib v3.10.3 أو أحدث (
pip install matplotlib)
الإعداد
أولاً، استورد المكتبات ذات الصلة:
# Added by doQumentation — installs packages not in the Binder environment
%pip install -q qiskit-addon-obp
%matplotlib inline
from typing import Sequence
import matplotlib.pyplot as plt
import numpy as np
import qiskit
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_catalog import QiskitFunctionsCatalog
from qiskit_aer import AerSimulator
from qiskit_addon_utils.slicing import combine_slices, slice_by_gate_types
from qiskit_addon_obp import backpropagate
from qiskit_addon_obp.utils.simplify import OperatorBudget
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit.visualization import (
plot_gate_map,
)
بعد ذلك، قم بالمصادقة باستخدام مفتاح API الخاص بك من لوحة تحكم IBM Quantum Platform. ثم حدد دالة Qiskit كما يلي. (لاحظ أنه من الأفضل أمنياً حفظ بيانات اعتماد حسابك في بيئتك المحلية، إن كنت على جهاز موثوق، حتى لا تضطر إلى إدخال مفتاح API في كل مرة تُصادَق فيها.)
# Paste here your instance and token strings
instance = "YOUR_INSTANCE"
token = "YOUR_TOKEN"
channel = "ibm_quantum_platform"
catalog = QiskitFunctionsCatalog(
channel=channel, token=token, instance=instance
)
qesem_function = catalog.load("qedma/qesem")
الخطوة 1: تعيين المدخلات الكلاسيكية إلى مسألة كمية
نبدأ بتعريف دالة تُنشئ دائرة Trotter:
def trotter_circuit_from_layers(
steps: int,
theta_x: float,
theta_z: float,
theta_zz: float,
layers: Sequence[Sequence[tuple[int, int]]],
init_state: str | None = None,
) -> qiskit.QuantumCircuit:
"""
Generates an ising trotter circuit
:param steps: trotter steps
:param theta_x: RX angle
:param theta_z: RZ angle
:param theta_zz: RZZ angle
:param layers: list of layers (can be list of layers in device)
:param init_state: Initial state to prepare. If None, will not prepare any state. If "+", will
add Hadamard gates to all qubits.
:return: QuantumCircuit
"""
qubits = sorted({i for layer in layers for edge in layer for i in edge})
circ = qiskit.QuantumCircuit(max(qubits) + 1)
if init_state == "+":
print("init_state = +")
for q in qubits:
circ.h(q)
for _ in range(steps):
for q in qubits:
circ.rx(theta_x, q)
circ.rz(theta_z, q)
for layer in layers:
for edge in layer:
circ.rzz(theta_zz, *edge)
circ.barrier(qubits)
return circ
بعد ذلك، نُنشئ دالة لحساب قيم التوقع المثالية باستخدام AerSimulator.
لاحظ أنه بالنسبة للدوائر الكبيرة (30 كيوبت أو أكثر) نوصي باستخدام القيم المحسوبة مسبقاً من محاكاة PEPS بالانتشار الاعتقادي (BP). يتضمن هذا الكود قيماً محسوبة مسبقاً لـ 35 كيوبت كمثال، استناداً إلى نهج BP لتطور شبكة تنسور PEPS المُقدَّم في هذه الورقة (الذي نشير إليه بـ PEPS-BP)، باستخدام حزمة Python للشبكات التنسورية quimb.
def calculate_ideal_evs(circ, obs, num_qubits, step):
# Predefined results for large circuits - calculated using bppeps for 3, 5, 7, 9 trotter steps
predefined_35 = [
0.79537,
0.78653,
0.79699,
]
if num_qubits == 35:
print(
"Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits."
)
return predefined_35[step]
else:
simulator = AerSimulator()
# Use Estimator primitive to get expectation value
estimator = Estimator(simulator)
sim_result = estimator.run([(circ, [obs])], precision=0.0001).result()
# Extracting the result
ideal_values = sim_result[0].data.evs[0]
return ideal_values
نستخدم تعيين طبقة القائمة على العتاد المأخوذة من جهاز Heron، ومنها نقطع الطبقات وفق عدد الكيوبتات التي نرغب في محاكاتها. نُعرِّف رسوماً بيانية جزئية لـ 10 و21 و28 و35 كيوبت تحافظ على بنية ثنائية الأبعاد (لا تتردد في تغييرها إلى رسمك البياني الجزئي المفضل):
LAYERS_HERON_R2 = [ # the full set of hardware layers for Heron r2
[
(2, 3),
(6, 7),
(10, 11),
(14, 15),
(20, 21),
(16, 23),
(24, 25),
(17, 27),
(28, 29),
(18, 31),
(32, 33),
(19, 35),
(36, 41),
(42, 43),
(37, 45),
(46, 47),
(38, 49),
(50, 51),
(39, 53),
(60, 61),
(56, 63),
(64, 65),
(57, 67),
(68, 69),
(58, 71),
(72, 73),
(59, 75),
(76, 81),
(82, 83),
(77, 85),
(86, 87),
(78, 89),
(90, 91),
(79, 93),
(94, 95),
(100, 101),
(96, 103),
(104, 105),
(97, 107),
(108, 109),
(98, 111),
(112, 113),
(99, 115),
(116, 121),
(122, 123),
(117, 125),
(126, 127),
(118, 129),
(130, 131),
(119, 133),
(134, 135),
(140, 141),
(136, 143),
(144, 145),
(137, 147),
(148, 149),
(138, 151),
(152, 153),
(139, 155),
],
[
(1, 2),
(3, 4),
(5, 6),
(7, 8),
(9, 10),
(11, 12),
(13, 14),
(21, 22),
(23, 24),
(25, 26),
(27, 28),
(29, 30),
(31, 32),
(33, 34),
(40, 41),
(43, 44),
(45, 46),
(47, 48),
(49, 50),
(51, 52),
(53, 54),
(55, 59),
(61, 62),
(63, 64),
(65, 66),
(67, 68),
(69, 70),
(71, 72),
(73, 74),
(80, 81),
(83, 84),
(85, 86),
(87, 88),
(89, 90),
(91, 92),
(93, 94),
(95, 99),
(101, 102),
(103, 104),
(105, 106),
(107, 108),
(109, 110),
(111, 112),
(113, 114),
(120, 121),
(123, 124),
(125, 126),
(127, 128),
(129, 130),
(131, 132),
(133, 134),
(135, 139),
(141, 142),
(143, 144),
(145, 146),
(147, 148),
(149, 150),
(151, 152),
(153, 154),
],
[
(3, 16),
(7, 17),
(11, 18),
(22, 23),
(26, 27),
(30, 31),
(34, 35),
(21, 36),
(25, 37),
(29, 38),
(33, 39),
(41, 42),
(44, 45),
(48, 49),
(52, 53),
(43, 56),
(47, 57),
(51, 58),
(62, 63),
(66, 67),
(70, 71),
(74, 75),
(61, 76),
(65, 77),
(69, 78),
(73, 79),
(81, 82),
(84, 85),
(88, 89),
(92, 93),
(83, 96),
(87, 97),
(91, 98),
(102, 103),
(106, 107),
(110, 111),
(114, 115),
(101, 116),
(105, 117),
(109, 118),
(113, 119),
(121, 122),
(124, 125),
(128, 129),
(132, 133),
(123, 136),
(127, 137),
(131, 138),
(142, 143),
(146, 147),
(150, 151),
(154, 155),
(0, 1),
(4, 5),
(8, 9),
(12, 13),
(54, 55),
(15, 19),
],
]
subgraphs = { # the subgraphs for the different qubit counts such that it's 2D
10: list(range(22, 29)) + [16, 17, 37],
21: list(range(3, 12)) + list(range(23, 32)) + [16, 17, 18],
28: list(range(3, 12))
+ list(range(23, 32))
+ list(range(45, 50))
+ [16, 17, 18, 37, 38],
35: list(range(3, 12))
+ list(range(21, 32))
+ list(range(41, 50))
+ [16, 17, 18, 36, 37, 38],
42: list(range(3, 12))
+ list(range(21, 32))
+ list(range(41, 50))
+ list(range(63, 68))
+ [16, 17, 18, 36, 37, 38, 56, 57],
}
n_qubits = 35 # 21, 28, 35, 42
layers = [
[
edge
for edge in layer
if edge[0] in subgraphs[n_qubits] and edge[1] in subgraphs[n_qubits]
]
]
for layer in LAYERS_HERON_R2
]
print(layers)
[[(6, 7), (10, 11), (16, 23), (24, 25), (17, 27), (28, 29), (18, 31), (36, 41), (42, 43), (37, 45), (46, 47), (38, 49)], [(3, 4), (5, 6), (7, 8), (9, 10), (21, 22), (23, 24), (25, 26), (27, 28), (29, 30), (43, 44), (45, 46), (47, 48)], [(3, 16), (7, 17), (11, 18), (22, 23), (26, 27), (30, 31), (21, 36), (25, 37), (29, 38), (41, 42), (44, 45), (48, 49), (4, 5), (8, 9)]]
الآن نُصوِّر تخطيط القبتات على جهاز Heron للرسم البياني الجزئي المختار:
service = QiskitRuntimeService(
channel=channel,
token=token,
instance=instance,
)
backend = service.backend("ibm_fez") # or any available device
selected_qubits = subgraphs[n_qubits]
num_qubits = backend.configuration().num_qubits
qubit_color = [
"#ff7f0e" if i in selected_qubits else "#d3d3d3"
for i in range(num_qubits)
]
plot_gate_map(
backend=backend,
figsize=(15, 10),
qubit_color=qubit_color,
)
plt.show()

لاحظ أن اتصالية تخطيط القبتات المختار ليست بالضرورة خطية، وقد تغطي مناطق واسعة من جهاز Heron تبعاً لعدد القبتات المحددة.
الآن نُولِّد دائرة Trotter ومقياس المغنطة المتوسطة للعدد المختار من القبتات والمعاملات:
# Chosen parameters:
theta_x = 0.53
theta_z = 0.1
theta_zz = 1.0
steps = 9
circ = trotter_circuit_from_layers(steps, theta_x, theta_z, theta_zz, layers)
print(
f"Circuit 2q layers: {circ.depth(filter_function=lambda instr: len(instr.qubits) == 2)}"
)
print("\nCircuit structure:")
circ.draw("mpl", scale=0.8, fold=-1, idle_wires=False)
plt.show()
observable = qiskit.quantum_info.SparsePauliOp.from_sparse_list(
[("Z", [q], 1 / n_qubits) for q in subgraphs[n_qubits]],
np.max(subgraphs[n_qubits]) + 1,
) # Average magnetization observable
print(observable)
obs_list = [observable]
Circuit 2q layers: 27
Circuit structure:

SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'ZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIII', 'IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZIIIIIIIIIIIIII'],
coeffs=[0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j,
0.02857143+0.j, 0.02857143+0.j, 0.02857143+0.j])
الخطوة 2: تحسين المسألة لتنفيذها على العتاد الكمومي
تقدير وقت QPU مع OBP وبدونه
يرغب المستخدمون عموماً في معرفة مقدار وقت QPU المطلوب لتجربتهم. غير أن هذا يُعدّ مسألةً صعبة على الحواسيب الكلاسيكية.
تقدم QESEM وضعَين لتقدير الوقت لإبلاغ المستخدمين بمدى جدوى تجاربهم:
- التقدير التحليلي للوقت - يعطي تقديراً تقريبياً جداً ولا يستلزم أي وقت QPU. يمكن استخدامه لاختبار ما إذا كانت عملية transpilation ستُقلل وقت QPU المحتمل.
- التقدير التجريبي للوقت (المُوضَّح هنا) - يعطي تقديراً جيداً نسبياً ويستخدم بضع دقائق من وقت QPU.
في كلتا الحالتين، تُخرج QESEM تقدير الوقت اللازم للوصول إلى الدقة المطلوبة لـجميع المقاييس.
run_on_real_hardware = True
precision = 0.05
if run_on_real_hardware:
backend_name = "ibm_fez"
else:
backend_name = "fake_fez"
# Start a job for empirical time estimation
estimation_job_wo_obp = qesem_function.run(
pubs=[(circ, obs_list)],
instance=instance,
backend_name=backend_name, # E.g. "ibm_brisbane"
options={
"estimate_time_only": "empirical", # "empirical" - gets actual time estimates without running full mitigation
"max_execution_time": 120, # Limits the QPU time, specified in seconds.
"default_precision": precision,
},
)
print(estimation_job_wo_obp.job_id)
print(estimation_job_wo_obp.status())
17d3828e-9fdb-482e-8e9b-392f3eefe313
DONE
# Get the result object (blocking method). Use job.status() in a loop for non-blocking.
# This takes 1-3 minutes
result = estimation_job_wo_obp.result()
print(
f"Empirical time estimation (sec): {result[0].metadata['time_estimation_sec']}"
)
Empirical time estimation (sec): 1200
الآن سنستخدم backpropagation للعوامل (OBP). (راجع دليل OBP للاطلاع على مزيد من التفاصيل حول إضافة OBP لـ Qiskit.) سننشئ دالةً تُولِّد شرائح الدائرة للـ backpropagation:
def run_backpropagation(circ_vec, observable, steps_vec, max_qwc_groups=8):
"""
Runs backpropagation for a list of circuits and observables.
Returns lists of backpropagated circuits and observables.
"""
op_budget = OperatorBudget(max_qwc_groups=max_qwc_groups)
bp_circuit_vec = []
bp_observable_vec = []
for i, circ in enumerate(circ_vec):
slices = slice_by_gate_types(circ)
bp_observable, remaining_slices, metadata = backpropagate(
observable,
slices,
operator_budget=op_budget,
)
bp_circuit = combine_slices(remaining_slices, include_barriers=True)
bp_circuit_vec.append(bp_circuit)
bp_observable_vec.append(bp_observable)
print(f"n.o. steps: {steps_vec[i]}")
print(f"Backpropagated {metadata.num_backpropagated_slices} slices.")
print(
f"New observable has {len(bp_observable.paulis)} terms, which can be combined into "
f"{len(bp_observable.group_commuting(qubit_wise=True))} groups.\n"
f"After truncation, the error in our observable is bounded by {metadata.accumulated_error(0):.3e}"
)
print("-----------------")
return bp_circuit_vec, bp_observable_vec
نستدعي الدالة:
bp_circ_vec, bp_obs_vec = run_backpropagation([circ], observable, [steps])
n.o. steps: 9
Backpropagated 11 slices.
New observable has 363 terms, which can be combined into 4 groups.
After truncation, the error in our observable is bounded by 0.000e+00
-----------------
print("The remaining circuit after backpropagation looks as follows:")
bp_circ_vec[-1].draw("mpl", scale=0.8, fold=-1, idle_wires=False)
None
The remaining circuit after backpropagation looks as follows:

يتضح لنا أن الـ backpropagation أزال طبقتين من الدائرة. والآن بعد أن حصلنا على الدائرة المخفَّضة والمقاييس الموسَّعة، لنُجرِ تقدير الوقت للدائرة الناتجة عن backpropagation:
# Start a job for empirical time estimation
estimation_job_obp = qesem_function.run(
pubs=[(bp_circ_vec[-1], [bp_obs_vec[-1]])],
instance=instance,
backend_name=backend_name,
options={
"estimate_time_only": "empirical",
"max_execution_time": 120,
"default_precision": precision,
},
)
print(estimation_job_obp.job_id)
print(estimation_job_obp.status())
8bae699d-a16b-4d39-bbd9-d123fbcce55d
DONE
result_obp = estimation_job_obp.result()
print(
f"Empirical time estimation (sec): {result_obp[0].metadata['time_estimation_sec']}"
)
Empirical time estimation (sec): 900
نلاحظ أن OBP يُقلل تكلفة الوقت اللازمة لتخفيف أخطاء الدائرة.
الخطوة 3: التنفيذ باستخدام Qiskit primitives
التشغيل على backend حقيقي
الآن نُشغِّل التجربة الكاملة على عدد من خطوات Trotter. يمكن تعديل عدد القبتات والدقة المطلوبة والحد الأقصى لوقت QPU وفقاً لموارد QPU المتاحة. لاحظ أن تقييد الحد الأقصى لوقت QPU سيؤثر على الدقة النهائية، كما ستُلاحظ في الرسم البياني الأخير أدناه.
نُحلِّل أربع دوائر بـ 5 و7 و9 خطوات Trotter عند دقة 0.05، ونقارن قيم التوقع المثالية والصاخبة ومخفَّفة الخطأ:
steps_vec = [5, 7, 9]
circ_vec = []
for steps in steps_vec:
circ = trotter_circuit_from_layers(
steps, theta_x, theta_z, theta_zz, layers
)
circ_vec.append(circ)
مرةً أخرى، نُجري OBP على كل دائرة لتقليل وقت التشغيل:
bp_circ_vec_35, bp_obs_vec_35 = run_backpropagation(
circ_vec, observable, steps_vec
)
n.o. steps: 5
Backpropagated 11 slices.
New observable has 363 terms, which can be combined into 4 groups.
After truncation, the error in our observable is bounded by 0.000e+00
-----------------
n.o. steps: 7
Backpropagated 11 slices.
New observable has 363 terms, which can be combined into 4 groups.
After truncation, the error in our observable is bounded by 0.000e+00
-----------------
n.o. steps: 9
Backpropagated 11 slices.
New observable has 363 terms, which can be combined into 4 groups.
After truncation, the error in our observable is bounded by 0.000e+00
-----------------
الآن نُشغِّل دفعةً من مهام QESEM الكاملة. نُقيِّد الحد الأقصى لوقت تشغيل QPU لكل نقطة من النقاط للتحكم بشكل أفضل في ميزانية QPU.
run_on_real_hardware = True
precision = 0.05
if run_on_real_hardware:
backend_name = "ibm_marrakesh"
else:
backend_name = "fake_fez"
# Running full jobs for:
pubs_list = [
[(bp_circ_vec_35[i], bp_obs_vec_35[i])] for i in range(len(bp_obs_vec_35))
]
# Initiating multiple jobs for different lengths
job_list = []
for pubs in pubs_list:
job_obp = qesem_function.run(
pubs=pubs,
instance=instance,
backend_name=backend_name, # E.g. "ibm_brisbane"
options={
"max_execution_time": 300, # Limits the QPU time, specified in seconds.
"default_precision": 0.05,
},
)
job_list.append(job_obp)
هنا نتحقق من حالة كل مهمة:
for job in job_list:
print(job.status())
DONE
DONE
DONE
DONE
الخطوة 4: المعالجة اللاحقة وإرجاع النتيجة بالتنسيق الكلاسيكي المطلوب
عند انتهاء تشغيل جميع المهام، يمكننا مقارنة قيم التوقع الصاخبة والمخفَّفة.
ideal_values = []
noisy_values = []
error_mitigated_values = []
error_mitigated_stds = []
for i in range(len(job_list)):
job = job_list[i]
result = job.result() # Blocking - takes 3-5 minutes
noisy_results = result[0].metadata["noisy_results"]
ideal_val = calculate_ideal_evs(circ_vec[i], observable, n_qubits, i)
print("---------------------------------")
print(f"Ideal: {ideal_val}")
print(f"Noisy: {noisy_results.evs}")
print(f"QESEM: {result[0].data.evs} \u00b1 {result[0].data.stds}")
ideal_values.append(ideal_val)
noisy_values.append(noisy_results.evs)
error_mitigated_values.append(result[0].data.evs)
error_mitigated_stds.append(result[0].data.stds)
Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.
---------------------------------
Ideal: 0.79537
Noisy: 0.7039237951821501
QESEM: 0.7828018244130982 ± 0.013257266977728376
Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.
---------------------------------
Ideal: 0.78653
Noisy: 0.6478583812958806
QESEM: 0.7875259197423828 ± 0.02703045139248604
Using precalculated ideal values for large circuits calculated with belief propagation PEPS. Currently only for 35 qubits.
---------------------------------
Ideal: 0.79699
Noisy: 0.6171787879868142
QESEM: 0.6918791909168913 ± 0.0740873782039517
أخيراً، يمكننا رسم المغنطة مقابل عدد الخطوات. يلخص هذا الرسم الفائدة من استخدام دالة QESEM الخاصة بـ Qiskit لتخفيف الخطأ غير المتحيز على الأجهزة الكمومية الصاخبة.
plt.plot(steps_vec, ideal_values, "--", label="ideal")
plt.scatter(steps_vec, noisy_values, label="noisy")
plt.errorbar(
steps_vec,
error_mitigated_values,
yerr=error_mitigated_stds,
fmt="o",
capsize=5,
label="QESEM mitigation",
)
plt.legend()
plt.xlabel("n.o. steps")
plt.ylabel("Magnetization")
Text(0, 0.5, 'Magnetization')
تمتلك الخطوة التاسعة شريط خطأ إحصائي كبير لأننا قيّدنا وقت QPU بـ 5 دقائق. إذا شغّلت هذه الخطوة لمدة 15 دقيقة (كما يُشير التقدير التجريبي للوقت)، ستحصل على شريط خطأ أصغر. ومن ثَمّ ستصبح القيمة المخفَّفة أقرب إلى القيمة المثالية.