محاكاة نموذج Ising المركل باستخدام دالة TEM
طريقة Algorithmiq للتخفيف من أخطاء الشبكة التنسورية (TEM) هي خوارزمية هجينة كمية-كلاسيكية مصممة لتنفيذ تخفيف الضوضاء بالكامل داخل مرحلة المعالجة اللاحقة الكلاسيكية. مع TEM، يمكن للمستخدم حساب قيم توقع المقاييس، مع التخفيف من أخطاء الضوضاء الحتمية التي تحدث على الأجهزة الكمية بدقة أكبر وكفاءة في التكلفة، مما يجعله خياراً جذاباً للغاية للباحثين في الكم ومحترفي الصناعة على حدٍّ سواء.
يوضح هذا البرنامج التعليمي كيف يمكن لـ TEM الحصول على نتائج ذات معنى لديناميكيات نظام كمي، والتي ستكون غير قابلة للوصول دون تخفيف الأخطاء، والتي تتطلب موارد كمية أكثر بشكل كبير إذا استُخدمت طرق تخفيف الأخطاء الأخرى مثل PEC وZNE.
تقدير الاستخدام: يستخدم هذا الدفتر ما يقارب 10 دقائق QPU على أجهزة Heron r3. يمكن أن يعتمد وقت التشغيل بشكل كبير على الجهاز المختار. يمكن العثور على تقديرات الاستخدام لكل قسم أدناه.
تشغيل تجارب فيزياء الأجسام الكثيرة مع تخفيف الأخطاء باستخدام دالة TEM
يستند هذا البرنامج التعليمي إلى المرجع التالي: L. E. Fischer et al., Nat. Phys. (2026). يناقش هذا المرجع محاكاة حقيقية على الأجهزة الكمية بما يصل إلى 91 قبتاً. في هذا البرنامج التعليمي، نعيد إنشاء محاكاة مشابهة بحجم دائرة أصغر.
يتوافق نموذج Ising المركل مع نموذج Ising المعتاد:
الذي يُطبَّق عليه ركلة عرضية:
الهدف هو محاكاة ديناميكيات حالة تحت هاميلتوني Ising المركل العرضي، الذي يمكن تطبيق تطوره الزمني بواسطة وحدة Floquet . الحالة الأولية للتطور هي الحالة التي يكون فيها القبت الأول في الحالة ، بينما تُقرن الأخرى وتُضبط في حالة Bell .
الكمية التي نريد مراقبتها هي دالة الارتباط. يناقش ورقة المرجع كيف يمكن إعادة كتابة هذه الكمية كمؤثر Pauli من نوع على القبت . بعد عدد من خطوات الزمن الفيزيائي ، نحسب قيمة مؤثر Pauli . اعتماداً على معاملات النظام، تساوي قيمة هذا المقياس قيمة يمكن حسابها بدقة، أو تُحاكى فقط من خلال طرق تقريبية. تحديداً، من أجل تساوي ، وهي القيمة التي سنستخدمها لمقارنة نتائج هذا البرنامج التعليمي. علاوةً على ذلك، عند خطوة زمنية معينة ، يكون صفراً. للتفاصيل للحصول على هذه القيم، وللمقارنة مع نتائج المحاكاة الكلاسيكية التقريبية خارج هذه المعاملات، انظر L. E. Fischer et al., Nat. Phys. (2026).
تعمل TEM أولاً بتوصيف الضوضاء لكل طبقة فريدة من بوابات ثنائية القبت في الدائرة، وكذلك توصيف خطأ القراءة. ثم يتم تنفيذ الدائرة على الجهاز الكمي. وأخيراً، يتم تنفيذ تخفيف أخطاء الشبكة التنسورية على الموارد الكلاسيكية في IBM Cloud® ويتم إرجاع القيمة المخففة. في هذا المثال، تحتوي الدائرة على طبقتين فريدتين للتوصيف.
الإعداد
كشرط أساسي، تأكد من تثبيت التبعيات الضرورية.
%pip install numpy matplotlib qiskit qiskit-ibm-catalog qiskit-ibm-runtime pylatexenc qiskit_qasm3_import
import os
from matplotlib import pyplot as plt
import numpy as np
from qiskit.quantum_info import SparsePauliOp
from qiskit.qasm3 import load
from qiskit_ibm_catalog import QiskitFunctionsCatalog
تخفيف الأخطاء مع TEM
نقدم هنا دائرة تطبق نموذج Ising المركل الموصوف أعلاه. يُعدّ الدائرة على النحو التالي. أولاً، هناك مرحلة إعداد الحالة، حيث يكون القبت الأول في الحالة ، بينما يكون الآخرون في أزواج Bell . يتبع ذلك البنية الطابوقية التي تطبق التطور الوحدوي . عدد خطوات الزمن الفيزيائية يتوافق مع طبقة دائرة. يُنزّل الكود التالي ملفَّي QASM اللازمَين لهذا البرنامج التعليمي.
# Download required QASM files
import urllib
urllib.request.urlretrieve(
"https://ibm.box.com/shared/static/swy5jtq309b0xpzluzlmsmj908yphes8.qasm",
"ki_30q.qasm",
)
urllib.request.urlretrieve(
"https://ibm.box.com/shared/static/et3gkodonw6gsp2trs43lzaozrdtiu7s.qasm",
"ki_12q.qasm",
)
يمكننا تصور نسخة صغيرة من الدائرة، مع 12 قبتاً وست خطوات زمنية:
# Parameters of the kicked Ising model
h = 0.0
num_qubits = 12
t_steps = 6
# Load the circuit for the kicked Ising model
small_circuit = load("ki_12q.qasm")
# Draw the circuit
small_circuit.draw("mpl", scale=0.25, fold=-1)

بعد ذلك، نبني المقياس، . يُنشأ كسلسلة Pauli بسيطة مع الترتيب المطابق لذلك المستخدم من قِبل Qiskit:
def xt_observable(n_qubits, t_steps):
pauli_str = "".join(["I" * t_steps, "X", "I" * (n_qubits - t_steps - 1)])
pauli_str = pauli_str[::-1] # Reverse the string to match qiskit order
return SparsePauliOp(data=pauli_str, coeffs=1.0)
في مثالنا الصغير ذو 12 قبتاً، يبدو المقياس كالتالي:
# Build the observable for the kicked Ising model
small_observable = xt_observable(n_qubits=12, t_steps=6)
print(small_observable)
SparsePauliOp(['IIIIIXIIIIII'],
coeffs=[1.+0.j])
تستخدم Qiskit Functions نمط PUBs كطريقة لجمع المدخلات. في حالتنا، لنعتبر دائرة ومقياساً واحدَين كـ PUB:
# Collect the input PUBs, in this case composed of a
# single circuit and observable
pubs = [(small_circuit, [small_observable])]
بعد ذلك، نحصل على الوصول إلى دالة TEM. أولاً نُعدّ المصادقة المطلوبة لـ IBM Cloud ونختار Backend من الأجهزة المتاحة. يمكن الحصول على الرمز المميز والـ Backends المتاحة وأسماء موارد Cloud المقابلة (CRN) عن طريق تسجيل الدخول إلى حسابك على لوحة تحكم IBM Quantum Platform.
# Set IBM Quantum credentials and backend configuration
personal_token = os.environ.get(
"QISKIT_IBM_TOKEN", "<API-KEY>"
) # Replace with your personal token or set the environment variable
channel = "ibm_quantum_platform"
crn = "your_crn" # Replace with the Cloud Resource Name (CRN)
# Select the QPU backend
backend_name = "ibm_qpu_name" # Replace with your desired backend's name
قم بتحميل دالة TEM من كتالوج Qiskit Functions:
# Load the TEM function from the Qiskit Functions Catalog
catalog = QiskitFunctionsCatalog(
channel=channel,
token=personal_token,
instance=crn,
)
tem = catalog.load("algorithmiq/tem")
يمكننا الآن تشغيل تجربة على دائرة Ising المركلة مع تخفيف الأخطاء المقدم بواسطة TEM. باستخدام الإعدادات الافتراضية، يمكن تشغيل TEM بطريقة بسيطة مع وقت تشغيل QPU متوقع حوالي 2.5 دقيقة، اعتماداً على QPU:
tem_job = tem.run(pubs=pubs, backend_name=backend_name)
بالإعدادات الافتراضية، تُشغّل دالة TEM ثلاث مهام على الحاسوب الكمي: تعلم الضوضاء، وتخفيف القراءة، وأخذ عينات الدائرة. يمكن تغيير عدد الـ shots المستخدمة في كل منها في الخيارات الممررة إلى الدالة. بشكل افتراضي، تُضبط هذه المعاملات لتحقيق دقة 0.05 في قيم التوقع المخففة. يمكنك التحقق من حالة مهمتك على لوحة تحكم IBM Quantum Platform أو باستخدام:
print(tem_job.status())
QUEUED
عندما تكون الحالة DONE، يمكننا التحقق من النتائج الخام والمخففة. tem_evs المحددة أدناه هي قيم التوقع للمقاييس المطلوبة، في هذه الحالة مقياس واحد فقط، ، و tem_std هي الانحرافات المعيارية المقابلة.
# Get the results of the TEM job
tem_results = tem_job.result()[
0
] # Get the first and only result from the job
tem_evs = tem_results.data.evs[0]
tem_std = tem_results.data.stds[0]
print(f"TEM Result: {tem_evs:.3f} ± {tem_std:.3f}")
TEM Result: 1.031 ± 0.046
يمكننا أيضاً التحقق من كمية وقت التشغيل الكمي المستخدم لكل استدعاء على IBM Quantum Platform، أو عن طريق فحص بيانات تعريف النتيجة من كود Python.
# Get the TEM job runtime
tem_runtime = tem_job.result().metadata["resource_usage"][
"RUNNING: EXECUTING_QPU"
]["QPU_TIME"]
print(f"TEM Runtime: {tem_runtime} seconds")
TEM Runtime: 155.0 seconds
تخصيص معاملات TEM والخيارات المتقدمة
توفر دالة TEM عدة خيارات متقدمة لتخصيص سير عمل تخفيف الأخطاء. تتيح لك هذه الخيارات التحكم في الدقة وعدد الـ shots واستراتيجيات تعلم الضوضاء والمعاملات الأخرى لتناسب متطلبات تجربتك والموارد الكمية المتاحة.
الخيارات المتقدمة الشائعة هي:
precision: تحديد الدقة المستهدفة لقيم التوقع المخففة.default_shots: بدلاً منprecision، يمكنك تحديد عدد الـ shots المستخدمة في مهمة القياس.tem_max_bond_dimension: الحد الأقصى لبُعد الرابط المستخدم في الشبكة التنسورية.tem_compression_cutoff: قيمة القطع المستخدمة للشبكة التنسورية.- خيارات تعلم الضوضاء: تكوين طريقة توصيف الضوضاء، مثل عدد التكرارات أو دوائر المعايرة المحددة.
private: ضمان خصوصية الدوائر ونتائج التجربة لك وتعطيل تنزيلات متعددة لنتائج المهمة.
ارجع إلى وثائق TEM أو كتالوج Qiskit Functions للحصول على قائمة كاملة بالخيارات المدعومة وأوصافها. يمكنك ضبط هذه المعاملات للتوازن بين وقت التشغيل واستخدام الموارد ودقة النتائج.
يمكنك تمرير هذه الخيارات كقاموس إلى وسيطة options عند تشغيل دالة TEM:
options = {
"default_shots": 10_000,
"tem_max_bond_dimension": 512,
"tem_compression_cutoff": 1e-16,
# This option helps optimizing the measurement
# stage since the observable is strongly biased
# toward the X operator for all the qubits.
"compute_shadows_bias_from_observable": True,
# set to True to keep experiment results private,
# recommended for confidential circuits
"private": False,
}
يمكن أيضاً تمرير خيارات مخصصة لمتعلم الضوضاء. تتبع التعريفات المستخدمة في Qiskit Runtime NoiseLearnerOptions:
nl_options = {
"num_randomizations": 32,
"max_layers_to_learn": 2,
"shots_per_randomization": 128,
"layer_pair_depths": [0, 1, 2, 4, 16, 32],
}
# add noise learning options to the overall options
options |= nl_options
أعِد تشغيل التجربة مع هذه الخيارات المخصصة المضبوطة لدائرتنا. وقت التشغيل المتوقع هو حوالي أربع دقائق QPU.
tem_job_custom = tem.run(
pubs=pubs, backend_name=backend_name, options=options
)
إذا لم تُضبط المهمة كخاصة، يمكننا استرداد النتيجة في وقت لاحق. للقيام بذلك، احفظ معرف المهمة المطبوع هنا واستخدم tem_job_custom = catalog.get_job_by_id("your-job-id").
job_id = tem_job_custom.job_id
print(f"Job ID: {job_id}")
Job ID: 1ba10094-a541-457a-9287-dbd49306d12d
results_custom = tem_job_custom.result()
tem_evs = results_custom[0].data.evs[0]
tem_std = results_custom[0].data.stds[0]
print(f"TEM Result: {tem_evs:.3f} ± {tem_std:.3f}")
TEM Result: 0.956 ± 0.018
يمكننا الآن فحص النتائج والبيانات الوصفية للحصول على رؤى حول التجربة:
metadata_custom = results_custom[0].metadata
unmitigated_evs = metadata_custom["evs_non_mitigated"][0]
unmitigated_stds = metadata_custom["stds_non_mitigated"][0]
print(f"Unmitigated Result: {unmitigated_evs:.3f} ± {unmitigated_stds:.3f}")
# Exact result for the kicked Ising model from the reference paper
exact_evs = np.cos(2 * h) ** t_steps
print("Exact Result:", exact_evs)
Unmitigated Result: 0.894 ± 0.015
Exact Result: 1.0
# Plot comparing the different expectation values
plt.bar(
["Unmitigated", "TEM"],
[unmitigated_evs, tem_evs],
yerr=[unmitigated_stds, tem_std],
color=["grey", "c"],
)
plt.hlines(y=exact_evs, xmin=-0.5, xmax=1.5, colors="r", linestyles="dashed")
plt.ylabel("Expectation Value")
plt.ylim(0, 1.1)
plt.show()
أخيراً، يمكننا التحقق من تأثير الخيارات المخصصة على وقت تشغيل QPU والكلاسيكي:
# Get the metadata of the TEM job
job_metadata = results_custom.metadata
# Get the runtime of the TEM job
qpu_runtime = job_metadata["resource_usage"]["RUNNING: EXECUTING_QPU"][
"QPU_TIME"
]
classical_runtime = (
job_metadata["resource_usage"]["RUNNING: OPTIMIZING_FOR_HARDWARE"][
"CPU_TIME"
]
+ job_metadata["resource_usage"]["RUNNING: POST_PROCESSING"]["CPU_TIME"]
)
print(f"QPU Runtime: {qpu_runtime} seconds")
print(f"Classical Runtime: {classical_runtime} seconds")
QPU Runtime: 342.0 seconds
Classical Runtime: 107.632604 seconds
توسيع نطاق TEM إلى دوائر كبيرة
يمكن من حيث المبدأ تشغيل الدوائر الكبيرة مع دالة TEM. ومع ذلك، من المهم الإدراك بمحدودية الموارد الكلاسيكية، حيث يتم تنفيذ TEM على متشغلات IBM Cloud مع أوقات تشغيل طويلة محتملة. للدوائر الكبيرة جداً، اتصل بفريق دعم TEM على qiskit_ibm@algorithmiq.fi.
هنا نشغل مثالاً مع دائرة أكبر بـ 30 قبتاً بحجم مقياس المنفعة، مع تحسين معاملات TEM للسرعة بدلاً من الدقة.
# Kicked Ising model parameters
n_qubits = 30
t_steps = 15
h = 0.0
# Load the circuit for the kicked Ising model
circuit = load("ki_30q.qasm")
# Build the observable for the kicked Ising model
observable = xt_observable(n_qubits=n_qubits, t_steps=t_steps)
# Collect the input PUBs, in this case composed of a
# single circuit and observable
pubs = [(circuit, [observable])]
لنحدد بعض الخيارات الموجهة نحو الأداء:
options = {
"num_randomizations": 32,
"max_layers_to_learn": 2,
"shots_per_randomization": 128,
"layer_pair_depths": [0, 1, 2, 4, 16, 32, 64],
"default_shots": 5_000,
"tem_max_bond_dimension": 128,
"tem_compression_cutoff": 1e-10,
"compute_shadows_bias_from_observable": True,
"private": False,
}
أخيراً، شغّل التجربة، واحصل على النتيجة، وبصّرها. سيستغرق ذلك حوالي 3.5 دقائق QPU.
tem_job_large = tem.run(pubs=pubs, backend_name=backend_name, options=options)
job_id = tem_job_large.job_id
print(f"Job ID: {job_id}")
Job ID: 9f3f190f-f4b0-4dcb-bb83-5f71f37d0d77
results_large = tem_job_large.result()
tem_evs = results_large[0].data.evs[0]
tem_std = results_large[0].data.stds[0]
print(f"TEM Result: {tem_evs:.3f} ± {tem_std:.3f}")
# Get the metadata of the TEM job
job_metadata = tem_job_large.result().metadata
# Get the runtime of the TEM job
qpu_runtime = job_metadata["resource_usage"]["RUNNING: EXECUTING_QPU"][
"QPU_TIME"
]
classical_runtime = (
job_metadata["resource_usage"]["RUNNING: OPTIMIZING_FOR_HARDWARE"][
"CPU_TIME"
]
+ job_metadata["resource_usage"]["RUNNING: POST_PROCESSING"]["CPU_TIME"]
)
print(f"QPU Runtime: {qpu_runtime} seconds")
print(f"Classical Runtime: {classical_runtime} seconds")
TEM Result: 0.794 ± 0.026
QPU Runtime: 203.0 seconds
Classical Runtime: 251.71805499999996 seconds
# Plot comparing the different expectation values
metadata_large = results_large[0].metadata
unmitigated_evs = metadata_large["evs_non_mitigated"][0]
unmitigated_stds = metadata_large["stds_non_mitigated"][0]
exact_evs = np.cos(2 * h) ** t_steps
plt.bar(
["Unmitigated", "TEM"],
[unmitigated_evs, tem_evs],
yerr=[unmitigated_stds, tem_std],
color=["grey", "c"],
)
plt.hlines(y=exact_evs, xmin=-0.5, xmax=1.5, colors="r", linestyles="dashed")
plt.ylabel("Expectation Value")
plt.ylim(0, 1.1)
plt.show()