انتقل إلى المحتوى الرئيسي

نشر وتشغيل قالب دالة Qiskit لديناميكيات هاميلتونيان AQC + Trotter

نظرة عامة

هذا قالب دالة Qiskit غير مرتبط بتجربة معينة لديناميكيات هاميلتونيان. بالنظر إلى هاميلتونيان باولي 1D للجار الأقرب، وحالة أولية مُعدَّة (اختيارية)، ومجموعة من المُلاحَظات، فإنه يشغّل تطور الزمن بطريقة Trotter، وضغط الدارة بواسطة التجميع الكمي التقريبي (AQC)، والتنفيذ المُخفَّف، ثم يُعيد السلسلة الزمنية لكل مُلاحَظة. بادل بين الإعداد (PRE) والتحليل (POST) وسيقود النواة نفسها تجربة مختلفة:

PRE (إعدادك)FUNCTION (المنشورة هنا)POST (تحليلك)
إعداد حالة، كدارة أو كحالة منتج، مع نبضة محلية اختياريةتركيب Trotter → ضغط AQC → تنفيذ على statevector أو fake أو runtime، مُعيدًا O(t)\langle O \rangle(t)S(q,ω)S(q, \omega) لتشتت النيوترونات، أو المغنطة، أو النقل، أو ديناميكيات الانطفاء، وما إلى ذلك

القالب منشور في مستودع قوالب دالة Qiskit، إلى جانب قوالب التطبيقات الأخرى. ينشر هذا الدفتر القالب إلى حساب Qiskit Serverless الخاص بك. شغّله مرة واحدة، ويمكن لأي دفتر بعد ذلك استدعاء الدالة باستخدام serverless.load("aqc-dynamics-function").

للاطلاع على مثال علمي متكامل، راجع محاكاة تشتت النيوترونات باستخدام سير عمل Serverless لديناميكيات AQC + Trotter، الذي يستدعي هذه الدالة لحساب عامل البنية الديناميكية لـ KCuF3_3. يغطي هذا الدفتر النشر وعقد المُدخلات بدلًا من ذلك.

المتطلبات

قبل البدء، تأكد من توفر ما يلي في بيئة النواة الخاصة بهذا الدفتر:

  • Qiskit SDK الإصدار 2.0 أو أحدث (pip install qiskit).

  • عميل Qiskit IBM Catalog (pip install qiskit-ibm-catalog)، الذي ينشر ويشغّل أعباء العمل على Qiskit Serverless.

لا يحتاج الأمر إلى تثبيت التبعيات العلمية الخاصة بالدالة (qiskit-addon-aqc-tensor، cotengrust، qiskit-aer) محليًا.

احصل على ملفات مصدر القالب

الدالة عبارة عن حزمة Python صغيرة يشغّلها Qiskit Serverless في السحابة، لذا يجب أن يوجد مصدرها كملفات محلية تُرفع عند النشر. الحزمة منشورة في مستودع قوالب دالة Qiskit.

نزّل source_files

التنزيل عبارة عن ملف مضغوط واحد، سُمّي حسب المسار الكامل للمجلد في المستودع:

qiskit-community qiskit-function-templates main physics aqc_trotter source_files.zip

  1. فك ضغطه في المجلد الذي يحتوي هذا الدفتر.

  2. أعد تسمية المجلد المستخرج من ذلك الاسم الطويل إلى source_files.

يبدو دليل عملك بعد ذلك كالتالي:

your-working-directory/
├── function-template-aqc-trotter.ipynb <- this notebook
└── source_files/ <- the renamed folder
├── __init__.py
├── program.py
└── source/
├── __init__.py
├── _serverless.py
├── app_function.py
├── aqc.py
├── build.py
├── execute.py
└── hamiltonian.py

يجب أن يكون الاسم بالضبط source_files، لأن هذا هو working_dir الذي تُحمّله الخطوة 3.

program.py هي نقطة الدخول التي تستدعيها البوابة. كل ما تحت source/ هو التنفيذ، مقسّم حسب المرحلة: تركيب هاميلتونيان وTrotter، ضغط AQC، والتنفيذ. لا يحتاج أي منها إلى تعديل لتشغيل الأمثلة التالية. تُحمّل الخطوة 3 الدليل بأكمله، لذا كرّر تلك الخطوة كلما غيّرت ملفًا.

# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-catalog

1. المصادقة

استخدم qiskit-ibm-catalog للمصادقة إلى QiskitServerless باستخدام مفتاح API الخاص بك (الرمز المميز) وCRN (النسخة)، اللذين يمكنك العثور عليهما على لوحة تحكم IBM Quantum® Platform. باستخدام بيانات الاعتماد هذه، يمكنك إنشاء نسخة من عميل Serverless محليًا لرفع الدالة المختارة أو تشغيلها:

from qiskit_ibm_catalog import QiskitServerless
serverless = QiskitServerless(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")

يمكنك اختياريًا استخدام save_account() لحفظ بيانات الاعتماد الخاصة بك في بيئتك المحلية (راجع دليل إعداد حساب IBM Cloud® الخاص بك). لاحظ أن هذا يكتب بيانات الاعتماد الخاصة بك إلى نفس الملف الذي تستخدمه QiskitRuntimeService.save_account():

QiskitServerless.save_account(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")

إذا تم حفظ الحساب، فلا حاجة لتقديم الرمز المميز للمصادقة:

from qiskit_ibm_catalog import QiskitServerless

# Authenticate to the remote cluster
# In this case, loading a saved account
serverless = QiskitServerless()

# REPLACE WITH YOUR OWN CREDENTIALS or SAVED ACCOUNT
# serverless = QiskitServerless(channel="ibm_quantum_platform", token="MY_TOKEN", instance="MY_CRN")

2. تعريف التبعيات

الحزم التي تحتاجها الدالة بالإضافة إلى صورة Serverless الأساسية المُدارة.

ملاحظة

تثبّت البوابة فقط الأسماء الموجودة في قائمتها المسموح بها (requirements-dynamic-dependencies.txt)، والمطابقة حسب اسم الحزمة والمثبَّتة على الإصدار المسموح به باستخدام ==. يجب أن يصل أي شيء آخر بشكل غير مباشر (كتبعية لحزمة مسموح بها). صياغة [extras] مدعومة: qiskit-addon-aqc-tensor[quimb-jax] هي ما يثبّت quimb وjax. cotengrust مطلوبة لكفاءة الذاكرة أثناء محاكاة الشبكة الموترية. qiskit-aer مُدرَجة بشكل منفصل لـ Backend fake (محاكاة ضوضاء محلية).

DEPENDENCIES = [
"qiskit-addon-aqc-tensor[quimb-jax]==0.3.1",
"qiskit-aer==0.17.2",
"cotengrust==0.2.0",
]

3. تعريف الدالة ورفعها

from qiskit_ibm_catalog import QiskitFunction

fn = QiskitFunction(
title="aqc-dynamics-function",
entrypoint="program.py",
working_dir="source_files/",
dependencies=DEPENDENCIES,
)
serverless.upload(fn)
QiskitFunction(aqc-dynamics-function)

4. تحقق من تسجيلها

next(p for p in serverless.list() if p.title == "aqc-dynamics-function")
QiskitFunction(aqc-dynamics-function)

مرجع الدالة

هذه مقدمة موجزة. كل حقل موثَّق بالكامل في README قالب ديناميكيات AQC: جدول المُدخلات الكامل مع قواعد التحقق الخاصة به، وحقول المُخرَجات، وBackends التنفيذ، وأمثلة عمل إضافية. ما يلي هو النسخة المختصرة، الكافية لقراءة الأمثلة التالية.

المُدخلات

كل تشغيل هو استدعاء واحد لـ fn.run(...). المُدخلات الثلاثة الأولى فقط في الجدول مطلوبة: hamiltonian، وt_steps، وaqc_segments. كل ما يليها اختياري ويعود إلى القيمة الافتراضية الموضحة، لذا فإن الاستدعاء الأدنى هو ثلاث وسيطات وبقية الجدول هي الوظائف التي يمكنك اختيار الانضمام إليها. تُحدد num_qubits الخاصة بهاميلتونيان طول السلسلة، لذا لا يوجد مُدخل حجم منفصل.

المُدخلالقيمة الافتراضيةالوصف
hamiltonianمطلوبهاميلتونيان باولي 1D للجار الأقرب كـ SparsePauliOp. السلاسل هي عوامل باولي، لذا لا يوجد عامل ضمني بمقدار نصف.
t_stepsمطلوبإجمالي خطوات Trotter. يتطور إلى T = t_steps * dt ويُبلغ عن كل مُلاحَظة عند كل t_k = k * dt.
aqc_segmentsمطلوبخطة الضغط: قائمة من {"n_steps": k, "ansatz_steps": m}. يُضغط sum(n_steps) خطوة؛ والباقي يعمل كـ Trotter عادي.
dt0.2الزمن الفيزيائي المتقدم بخطوة Trotter واحدة.
initial_state|0...0>دارة QuantumCircuit مُعدَّة للتطور. أدمج أي نبضة محلية في هذه الدارة.
observablesZ لكل موقعأي شيء تقبله EstimatorV2 كوسيطة observables الخاصة بها. مُلاحَظة واحدة لكل عمود مُخرَج.
trotter_optionsسوزوكي من الرتبة الثانية{"method": ..., "synthesis_settings": {...}}. تمتلك الدالة reps وtime.
aqc_optionsراجع الوصفmax_bond (32cutoff (1e-8autodiff_backend ("jax"fidelity_target (Noneoptimizer_settings (L-BFGS-B، jac=True، maxiter=300).
estimator_optionsDD، twirling، TREXEstimatorV2.options، تُمرَّر كما هي. القاموس المُقدَّم يستبدل القيم الافتراضية بالكامل بدلًا من دمجها معها.
transpiler_options{"optimization_level": 3}وسيطات الكلمة المفتاحية لـ generate_preset_pass_manager. تُرفض backend وtarget، لأن مسار التنفيذ يمتلكهما.
backend"runtime""statevector" أو "fake" أو "runtime".
backend_nameالأقل انشغالًااسم Backend من IBM® لـ runtime، أو Backend وهمي مُسمّى.
batches1قسّم الدارات عبر N من مهام وقت التشغيل. الدُفعة الواحدة تُقدّم مهمة واحدة ولا تنشئ جلسة.
parallel_simFalseوزّع مسارات المحاكي المحلي عبر جميع الأنوية المتاحة باستخدام Ray. لا تأثير على runtime.
return_circuitsFalseأعد دارات AQC + Trotter المنطقية في النتيجة إلى جانب سلاسل المُلاحَظات.

Backends التنفيذ

تشترك المسارات الثلاثة في نفس الكود ونفس إعدادات التخفيف. تختلف فقط في مكان تشغيل الدارات.

backendماهيتهبيانات الاعتمادملاحظات
"statevector"StatevectorEstimator دقيقحساب Serverless فقطالمسار المرجعي الدقيق. لا وقت QPU.
"fake"محاكاة محلية ضوضائية على Backend وهمي من Qiskitحساب Serverless فقطتجربة أمينة لمسار runtime المُخفَّف. تحتاج qiskit-aer. تفترض افتراضيًا fake_sherbrooke ذي 127 Qubit.
"runtime" (افتراضي)EstimatorV2 المُخفَّفة مقابل QPU حقيقيحساب Serverless ونسخة بها وصول إلى QPUbackend_name اختياري؛ حذفه يختار الجهاز الأقل انشغالًا.

لا يزال كلا مساري المحاكي يستدعيان الدالة المنشورة، لذا يحتاجان إلى حساب Serverless محفوظ رغم أنهما لا يستخدمان وقت QPU. المثالان التاليان يشغّلان نفس عبء العمل على statevector أولًا، ثم على runtime.

المُخرَج

تُعيد job.result() قاموسًا عاديًا:

{
"times": [...], # length t_steps + 1, t_k = k * dt (t=0 is the prepared state)
"expectation_values": [[...]], # shape (n_times, n_observables)
"observable_labels": [...], # for example: ["Z_0", "ZZ_0_1"]
"metadata": {
"n", "t_steps", "dt", "tier",
"aqc_compressed_steps": 5, # total compressed steps (= sum of segment n_steps)
"aqc_segments": [ # per segment: the plan plus its own results
{"n_steps": 3, "ansatz_steps": 1, "steps": [1, 2, 3], "n_params": 133,
"fidelities": {"1": ..., "2": ..., "3": ...}},
{"n_steps": 2, "ansatz_steps": 2, "steps": [4, 5], "n_params": 245,
"fidelities": {"4": ..., "5": ...}},
],
"execution_backend",
"aqc_fidelities": {"1": ..., "2": ...}, # flat per-step fidelity, all compressed steps
"circuit_stats": { # per-step 2q depth and gate count, full Trotter vs AQC
"1": {"full_trotter": {"depth_2q": ..., "num_2q_gates": ...},
"aqc_trotter": {"depth_2q": ..., "num_2q_gates": ...}},
"2": {...},
},
"warnings": [...], # non-fatal notices; for example, a cotengrust fallback
"resource_usage": { # per stage; QPU_TIME is the charged QPU time
"RUNNING: OPTIMIZING_FOR_HARDWARE": {"CPU_TIME": ...},
"RUNNING: WAITING_FOR_QPU": {"CPU_TIME": ...},
"RUNNING: EXECUTING_QPU": {"QPU_TIME": ...},
},
},
# present only when return_circuits=True
"circuits": [QuantumCircuit, ...], # one per evolved step; circuits[i] is at times[i + 1]
}

aqc_fidelities وcircuit_stats هما الاثنان اللذان يجب قراءتهما أولًا: يخبرانك معًا ما إذا كان الضغط قد بقي أمينًا وما إذا كان قد وفّر العمق فعليًا. في runtime، يُبلغ resource_usage عن انتظار الطابور بشكل منفصل عن وقت QPU الذي تُحاسب عليه. المُدخل المرفوض يفشل بسرعة كـ ServerlessError منظّم (الرمز 4615).

مثال المحاكي

شغّل الدالة على Backend statevector الدقيق أولًا. لا يستهلك وقت QPU ويتحقق من النشر من طرف إلى طرف. النموذج هنا هو سلسلة إيزنغ ذات مجال عرضي مكونة من ثماني Qubits، وتم حذف observables بحيث تقيس الدالة ZZ الافتراضي لكل موقع.

خطة الضغط هي المُدخل الجدير بالفهم. كل جزء {"n_steps": k, "ansatz_steps": m} يضغط k خطوة Trotter متتالية إلى Ansatz مبني من هدف Trotter بـ m خطوة، وأي خطوات تتجاوز sum(n_steps) تعمل كـ Trotter عادي. الخطوات المبكرة ذات التشابك المنخفض تُضغط جيدًا إلى Ansatz ضحل بطبقة واحدة؛ بينما تحتاج الخطوات اللاحقة الأكثر تشابكًا إلى واحد أعمق.

from qiskit.quantum_info import SparsePauliOp

fn = serverless.load("aqc-dynamics-function")

n = 8
H = SparsePauliOp.from_sparse_list(
[("ZZ", [i, i + 1], 1.0) for i in range(n - 1)]
+ [("X", [i], 0.8) for i in range(n)],
num_qubits=n,
)

job = fn.run(
t_steps=8,
aqc_segments=[
{
"n_steps": 4,
"ansatz_steps": 1,
}, # early steps -> shallow 1-layer ansatz
{
"n_steps": 2,
"ansatz_steps": 2,
}, # later steps -> deeper 2-layer ansatz
],
hamiltonian=H,
aqc_options={"max_bond": 32},
backend="statevector",
)
print("job ID:", job.job_id)
job ID: ee1f3793-e995-427d-81d1-5924549beb38

تابع التشغيل واقرأ النتيجة

تُبلغ status() عن كل من دورة حياة المهمة العامة والحالة الفرعية لكل مرحلة التي تنشرها الدالة أثناء تشغيلها. تنطبق نفس المراحل على تشغيل العتاد لاحقًا في هذا الدليل:

QUEUED -> INITIALIZING -> RUNNING: OPTIMIZING_FOR_HARDWARE -> RUNNING: WAITING_FOR_QPU -> RUNNING: EXECUTING_QPU -> RUNNING: POST_PROCESSING -> DONE

قيمة status()المرحلة
RUNNING: OPTIMIZING_FOR_HARDWAREإعداد الحالة، بناء Trotter، ضغط AQC
RUNNING: WAITING_FOR_QPUفي قائمة الانتظار على QPU (Backend runtime فقط)
RUNNING: EXECUTING_QPUالدارات قيد التنفيذ (تُميّز المحاكيات المحلية هذا مباشرة)
RUNNING: POST_PROCESSINGتجميع قاموس النتيجة

الحالات النهائية هي DONE وERROR وCANCELED. تشغيل statevector هذا لا يمتلك طابور QPU، لذا يتخطى RUNNING: WAITING_FOR_QPU. استخدم job.logs() في أي وقت لرؤية سجلات كل مرحلة، بما في ذلك أمانة AQC المُحققة في كل خطوة.

print(job.status()) # re-run until this reports DONE
DONE
import numpy as np

result = job.result()
ev = np.array(result["expectation_values"])

print("observables:", result["observable_labels"])
print("shape:", ev.shape, "-> (n_times, n_observables)")
print("first row (t = 0, the prepared state):", np.round(ev[0], 4))
print("last row (t = t_steps * dt):", np.round(ev[-1], 4))
print(
"AQC fidelities:",
{k: round(v, 4) for k, v in result["metadata"]["aqc_fidelities"].items()},
)

# What the compression bought: 2-qubit depth at the final time step.
stats = result["metadata"]["circuit_stats"][
str(result["metadata"]["t_steps"])
]
print(
"2q depth at the final step:",
stats["full_trotter"]["depth_2q"],
"(full Trotter) ->",
stats["aqc_trotter"]["depth_2q"],
"(AQC + Trotter)",
)
observables: ['Z_0', 'Z_1', 'Z_2', 'Z_3', 'Z_4', 'Z_5', 'Z_6', 'Z_7']
shape: (9, 8) -> (n_times, n_observables)
first row (t = 0, the prepared state): [1. 1. 1. 1. 1. 1. 1. 1.]
last row (t = t_steps * dt): [0.1442 0.2956 0.4686 0.4877 0.4869 0.4686 0.2963 0.1441]
AQC fidelities: {'1': 1.0, '2': 1.0, '3': 1.0, '4': 1.0, '5': 1.0, '6': 0.9999}
2q depth at the final step: 210 (full Trotter) -> 79 (AQC + Trotter)

مثال على الأجهزة

استدعاء دالة باستخدام backend="runtime" يقوم بالنقل والتنفيذ على معالج IBM Quantum حقيقي، مع تخفيف الأخطاء المدمج في الدالة: فك الاقتران الديناميكي (XY4)، وتدوير البوابات (gate twirling)، وإطفاء أخطاء القراءة الملتوية (TREX). يختار backend_name الجهاز؛ وإذا حُذف، تختار الدالة الأقل انشغالاً.

لا يتغير شيء في كود العلوم. ما يختلف عن مثال المحاكي هو طول السلسلة، وعدد خطوات تروتر، وخطة الضغط، والـ Backend، وإعدادات التخفيف الصريحة المذكورة في القسم التالي.

تحديد حجم المهمة لأجهزة التحكم

estimator_options هو المدخل الجدير بالضبط عمدًا. يبني تدوير البوابات num_randomizations دارات عشوائية منفصلة لكل PUB، ويجب أن تتسع المهمة بأكملها، أي كل PUB بكل عمليات التدوير الخاصة به، في ذاكرة التعليمات لنظام التحكم الكلاسيكي في QPU. تفترض الدالة افتراضيًا 1000 عملية تدوير، لذا فإن تطورًا من 10 خطوات يرسل 11 PUB بواقع 1000 دارة لكل منها: أي ما يقارب 11,000 نسخة دارة في مهمة واحدة.

إذا تجاوزت ما يستطيع نظام التحكم استيعابه، تفشل المهمة بالخطأ error 6073. توضح صفحة Job limits الحدود وكيفية احتسابها، وأهمها 26.8 مليون تعليمة لنظام التحكم لكل كيوبت، وتُطبَّق لكل مهمة وليس لكل PUB. يضيف فك الاقتران الديناميكي بوابات تُحتسب ضمنها.

يتحكم مدخلان في الحجم:

  • يضبط estimator_options ميزانية اللقطات (shots). إجمالي اللقطات هو num_randomizations * shots_per_randomization، لذا يمكنك المفاضلة بين عدد عمليات التدوير وعدد اللقطات لكل تدوير، مع الحفاظ على الإحصائيات، وتقليص حجم البرنامج في الوقت نفسه. تستخدم الخلية التالية 100 عملية تدوير بواقع 200 لقطة لكل منها، أي 20,000 لقطة لكل مرصود (observable) وهو ما يقارب عُشر عدد نسخ الدارات التي ترسلها الإعدادات الافتراضية. راجع TwirlingOptions وEstimator options للاطلاع على المجموعة الكاملة من الحقول.

  • يقسم batches مجموعات PUB على هذا العدد من المهام المنفصلة في وقت التشغيل، وهذا هو العلاج الذي يقترحه الخطأ 6073 نفسه، ولهذا يهم التأطير على أساس كل مهمة. يؤدي ضبط batches=4 إلى إرسال حوالي ثلاث PUB لكل مهمة بدلاً من إحدى عشرة دفعة واحدة، وتخرج المهام معًا في دفعة واحدة بحيث تصطف المجموعة في الطابور مرة واحدة بدلاً من أن تصطف كل مهمة على حدة.

تذكر أن تمرير estimator_options يستبدل الإعدادات الافتراضية للدالة بالكامل بدلاً من الدمج معها، لذا تُعاد كتابة فك الاقتران الديناميكي وTREX في الخلية التالية للإبقاء عليهما مفعّلين.

from qiskit.quantum_info import SparsePauliOp

fn = serverless.load("aqc-dynamics-function")

n = 10
H = SparsePauliOp.from_sparse_list(
[("ZZ", [i, i + 1], 1.0) for i in range(n - 1)]
+ [("X", [i], 0.8) for i in range(n)],
num_qubits=n,
)

job = fn.run(
t_steps=10,
aqc_segments=[
{
"n_steps": 3,
"ansatz_steps": 1,
}, # early steps -> shallow 1-layer ansatz
{
"n_steps": 3,
"ansatz_steps": 2,
}, # later steps -> deeper 2-layer ansatz
],
hamiltonian=H,
aqc_options={"max_bond": 32},
backend="runtime",
backend_name="ibm_marrakesh",
# The function defaults to 1000 twirling randomizations, which was too large
# for this device. Total shots is num_randomizations *
# shots_per_randomization, so this is 20,000 shots per observable.
estimator_options={
"dynamical_decoupling": {"enable": True, "sequence_type": "XY4"},
"twirling": {
"enable_gates": True,
"num_randomizations": 100,
"shots_per_randomization": 200,
},
"resilience": {"measure_mitigation": True},
},
)
print("job ID (save this to reconnect later):", job.job_id)
job ID (save this to reconnect later): 7229a8bf-9f83-4785-8dd4-489844abc2d9
إعادة الاتصال بمهمة طويلة التشغيل

تشغيل الأجهزة ليس سريعًا، ومعظم الوقت كلاسيكي وليس على QPU. يعمل ضغط AQC داخل الدالة قبل أن يصل أي شيء إلى QPU، وطابور QPU يُضاف فوق ذلك. لست بحاجة إلى إبقاء هذا الدفتر أو النواة مفتوحة أثناء التشغيل.

انسخ معرّف المهمة الذي طبعته الخلية السابقة واحفظه. تتيح لك الخلايا الثلاث التالية استئناف التشغيل لاحقًا:

  1. إعادة الاتصال، مطلوبة فقط في جلسة نواة جديدة: أعد تشغيل خلية Authentication لإعادة إنشاء serverless، ثم أعد بناء مقبض job من المعرّف الذي حفظته. تخطَّ هذه الخلية إذا كنت لا تزال في الجلسة نفسها التي أرسلت منها المهمة، لأن المقبض لا يزال نشطًا.

  2. تحقق من الحالة: أعد التشغيل حتى تُبلّغ بـ DONE.

  3. استرجع النتيجة: شغّل فقط بعد أن تصبح الحالة DONE.

الصق المعرّف الذي حفظته فوق العنصر النائب في خلية إعادة الاتصال التالية.

# Reconnect to a previously submitted job by its ID. Only needed in a NEW kernel
# session; if you are still in the session where you submitted, the `job` handle
# from the preceding cell is already live, so skip this cell. Replace the ID that follows with your own.
job = serverless.get_job_by_id("<your job ID>")
# Re-run this until it reports DONE, then fetch the result in the following cell.
print(job.status())
DONE
import numpy as np

# Run this only once the preceding status cell reports DONE. result() blocks until
# the job finishes, so calling it earlier just waits.
result = job.result()
ev = np.array(result["expectation_values"])

print("backend:", result["metadata"]["execution_backend"])
print("shape:", ev.shape, "-> (n_times, n_observables)")
print("last row (t = t_steps * dt):", np.round(ev[-1], 4))
print(
"AQC fidelities:",
{k: round(v, 4) for k, v in result["metadata"]["aqc_fidelities"].items()},
)

# What the compression bought: 2-qubit depth at the final time step.
stats = result["metadata"]["circuit_stats"][
str(result["metadata"]["t_steps"])
]
print(
"2q depth at the final step:",
stats["full_trotter"]["depth_2q"],
"(full Trotter) ->",
stats["aqc_trotter"]["depth_2q"],
"(AQC + Trotter)",
)
backend: runtime
shape: (11, 10) -> (n_times, n_observables)
last row (t = t_steps * dt): [0.1504 0.1361 0.218 0.2144 0.2275 0.1783 0.1749 0.1599 0.0915 0.0922]
AQC fidelities: {'1': 1.0, '2': 1.0, '3': 1.0, '4': 1.0, '5': 0.9999, '6': 0.9999}
2q depth at the final step: 342 (full Trotter) -> 171 (AQC + Trotter)

الخطوات التالية

التوصيات