الترحيل من Sampler وEstimator من جانب الخادم إلى جانب العميل
يصف هذا الدليل كيفية الترحيل من تطبيقات جانب الخادم لـ IBM Quantum®
Sampler وَEstimator إلى تطبيقاتهما الجديدة من جانب العميل في
qiskit-ibm-runtime. تظل الواجهات والخيارات إلى حد كبير دون تغيير، لذا يعمل معظم الكود كما هو، لكن هناك بعض الاختلافات السلوكية التي يجب فهمها.
الخلفية
Sampler وEstimator هما واجهتان أساسيتان (primitive interfaces) معرّفتان في Qiskit. لطالما وفرت IBM Quantum
Compute Service (المعروفة سابقاً باسم Qiskit Runtime) التطبيق الفعلي
لهذه الـ primitives داخل بيئة وقت التشغيل الخاصة بها. عندما تستدعي sampler.run() أو
estimator.run()، يُرسل الطلب إلى الخدمة، وتتم جميع عمليات الحساب — بما في ذلك
تثبيط الأخطاء وتخفيفها — على جانب الخادم.
هذه التجربة القائمة على الصندوق الأسود مريحة: فلا داعي للقلق بشأن تفاصيل التطبيق. لكنها أيضاً تجعل من الصعب تصحيح أخطاء الـ primitives أو تخصيصها أو التعلم منها، لأنك لا تستطيع رؤية ما يحدث أثناء المعالجة.
يتبع نموذج التنفيذ الموجّه المُقدَّم حديثاً النهج المعاكس ويوفر تجربة الصندوق الشفاف. يتم التقاط جميع نوايا التصميم على جانب العميل، ويقوم primitive واحد من جانب الخادم وهو Executor بمعالجة هذه المدخلات تماماً كما هو موجَّه — فهو لا يتخذ أي قرارات ضمنية نيابةً عنك.
ابتداءً من qiskit-ibm-runtime الإصدار v0.50.0، تمت إعادة تطبيق Sampler وEstimator
على جانب العميل فوق Executor. فهما يوفران نفس الراحة
والتجريد كما كان سابقاً، والآن يمكنك فحص تفاصيل التطبيق عند الحاجة
إلى ذلك. ونظراً لأن الواجهات والخيارات تظل إلى حد كبير كما هي، ينبغي أن يكون الترحيل
سلساً.
ملاحظة: تدعم IBM Quantum فقط الإصدار 2 من واجهتي Sampler وEstimator (BaseSamplerV2 وَBaseEstimatorV2). لذلك، يُشار إليهما ببساطة باسم Sampler وEstimator في هذا الدليل.
تحديث عمليات الاستيراد
حالياً، يجب عليك استيراد التطبيقات الجديدة بشكل صريح من وحداتها المخصصة:
from qiskit_ibm_runtime.executor_sampler import Sampler
from qiskit_ibm_runtime.executor_estimator import Estimator
في المستقبل القريب، ستُحل عمليات الاستيراد على المستوى الأعلى إلى التطبيقات الجديدة من جانب العميل، ولن يكون هناك حاجة لأي تغيير في الكود:
# Coming soon — the following code will import the new client-side implementations.
from qiskit_ibm_runtime import Sampler, Estimator
وبالمثل، إذا كنت تنشئ كائنات خيارات مكتوبة النوع، فيجب عليك استيرادها من
qiskit_ibm_runtime.options_models بدلاً من ذلك، أو ببساطة تمرير قاموس متداخل عادي (dict):
from qiskit_ibm_runtime.options_models import SamplerOptions, EstimatorOptions
ما الذي يبقى كما هو
-
إنشاء الـ primitive باستخدام
modeوَoptions. -
توقيع
run()وتنسيق PUB. -
شجرة الخيارات (
options.twirling، وَoptions.resilience، وَoptions.default_shots، وما إلى ذلك). -
بنية بيانات النتيجة التي تُرجعها
job.result().
تغييرات غير متوافقة في Sampler الجديد
| التغيير | إجراء الترحيل |
|---|---|
أصبح الـ primitive الأساسي الآن هو Executor. ستعرض كل من واجهة مستخدم IBM Quantum Platform وَjob.primitive_id كلمة executor بدلاً من sampler. | حدّث أي كود يشير إلى job.primitive_id. |
يقوم التطبيق الجديد بتعيين مدخلات Sampler إلى مدخلات Executor، لذا تُرجع job.inputs مدخلات Executor. | حدّث أي كود يشير إلى job.inputs. راجع مدخلات المهمة. |
يحدث الآن المزيد من المعالجة المسبقة واللاحقة على جانب العميل، لذا قد تستغرق sampler.run() وَjob.result() وقتاً أطول من ذي قبل. | فعّل تسجيل INFO لمتابعة تقدم المعالجة على جانب العميل. راجع تفعيل تسجيل INFO. |
تُنسخ بيانات وصفية للدارة (circuit metadata) إلى البيانات الوصفية للنتيجة. أصبحت أنواع البيانات المسموح بها في البيانات الوصفية للنتيجة الآن مقتصرة على str، وَfloat، وَint، وَbool، وقوائم أو قواميس من هذه الأنواع. | إذا احتجت إلى أنواع بيانات أخرى، فقم بترميزها كسلسلة نصية أولاً (على سبيل المثال، باستخدام base64). |
أصبحت فئات الخيارات (options_models.SamplerOptions وما إلى ذلك) الآن نماذج Pydantic بدلاً من dataclasses، لذا لم يعد بالإمكان تحويلها إلى قواميس بايثون باستخدام asdict(). | استخدم options.model_dump() بدلاً من ذلك. |
فئات الخيارات التي كانت تحمل سابقاً اللاحقة V2 (ExecutionOptionsV2 وما إلى ذلك) لم تعد كذلك، نظراً لأن primitives الإصدار V1 لم تعد مدعومة. | أزل اللاحقة V2 من فئات الخيارات هذه: استبدل ExecutionOptionsV2 بـ ExecutionOptions، وَResilienceOptionsV2 بـ ResilienceOptions، وَSamplerExecutionOptionsV2 بـ SamplerExecutionOptions. |
إذا تم تفعيل twirling وتم تحديد shots (في PUBs أو في run())، وَshots_per_randomization، وَnum_randomizations جميعها، فإن num_randomizations * shots_per_randomization تكون لها الأولوية على shots. | احذف num_randomizations وَshots_per_randomization إذا أردت استخدام قيمة shots. |
انتقل بعض التحقق من صحة المدخلات إلى جانب الخادم، وأصبح الآن يثير RuntimeError بدلاً من IBMInputValueError. | حدّث أنواع الاستثناءات التي يلتقطها كودك. |
| لم يعد يُدعم استخدام قيم shot مختلطة في مهمة واحدة. | أرسل مهمة منفصلة لكل قيمة shot. راجع تقسيم المهام للاعتبارات ذات الصلة. |
تغييرات غير متوافقة في Estimator الجديد
| التغيير | إجراء الترحيل |
|---|---|
أصبح الـ primitive الأساسي الآن هو Executor. ستعرض كل من واجهة مستخدم IBM Quantum Platform وَjob.primitive_id كلمة executor بدلاً من estimator. | حدّث أي كود يشير إلى job.primitive_id. |
يقوم التطبيق الجديد بتعيين مدخلات Estimator إلى مدخلات Executor، لذا تُرجع job.inputs مدخلات Executor. | حدّث أي كود يشير إلى job.inputs. راجع مدخلات المهمة. |
يحدث الآن المزيد من المعالجة المسبقة واللاحقة على جانب العميل، لذا قد تستغرق estimator.run() وَjob.result() وقتاً أطول من ذي قبل. | فعّل تسجيل INFO لمتابعة تقدم المعالجة على جانب العميل. راجع تفعيل تسجيل INFO. |
تُنسخ بيانات وصفية للدارة إلى البيانات الوصفية للنتيجة. أصبحت أنواع البيانات المسموح بها في البيانات الوصفية للنتيجة الآن مقتصرة على str، وَfloat، وَint، وَbool، وقوائم أو قواميس من هذه الأنواع. | إذا احتجت إلى أنواع بيانات أخرى، فقم بترميزها كسلسلة نصية أولاً (على سبيل المثال، باستخدام base64). |
أصبحت فئات الخيارات (options_models.EstimatorOptions وما إلى ذلك) الآن نماذج Pydantic بدلاً من dataclasses، لذا لم يعد بالإمكان تحويلها إلى قواميس بايثون باستخدام asdict(). | استخدم options.model_dump() بدلاً من ذلك. |
فئات الخيارات التي كانت تحمل سابقاً اللاحقة V2 (ExecutionOptionsV2 وما إلى ذلك) لم تعد كذلك، نظراً لأن primitives الإصدار V1 لم تعد مدعومة. | أزل اللاحقة V2 من فئات الخيارات هذه: استبدل ExecutionOptionsV2 بـ ExecutionOptions وَResilienceOptionsV2 بـ ResilienceOptions. |
| يتم الآن إرجاع جميع خيارات الإدخال في البيانات الوصفية للنتيجة، بدلاً من مجموعة فرعية مختارة. | لا شيء — هذا للعلم فقط. |
انتقل بعض التحقق من صحة المدخلات إلى جانب الخادم، وأصبح الآن يثير RuntimeError بدلاً من IBMInputValueError. | حدّث أنواع الاستثناءات التي يلتقطها كودك. |
| لم يعد هناك تعلم ضمني للضجيج بالنسبة لـ PEA وَPEC. لا يزال تعلم ضجيج القياس بالنسبة لـ TREX مدعوماً. | تعلّم نماذج الضجيج بشكل منفصل ومرّرها إلى Estimator. راجع إجراء تعلم ضجيج صريح لـ PEA وPEC. |
نوع الإدخال لـ ResilienceOptions.layer_noise_model مختلف ويمكن إنشاؤه من نتائج NoiseLearnerV3. | راجع إجراء تعلم ضجيج صريح لـ PEA وPEC لمعرفة كيفية تعلم نماذج الضجيج باستخدام NoiseLearnerV3 وتمريرها إلى Estimator. |
لم يعد MeasureNoiseLearningOptions.shots_per_randomization مدعوماً. | تُستخدم قيمة shot واحدة لجميع الدارات في المهمة، بما في ذلك دارات تعلم ضجيج القياس. إذا كان يجب عليك استخدام قيمة shot مختلفة، فطبّق TREX باستخدام qiskit-mitigation خارج Estimator. |
| لم يعد دعم قيم الدقة المختلطة في مهمة واحدة متاحاً. | أرسل مهمة منفصلة لكل دقة مطلوبة. راجع تقسيم المهام للاعتبارات ذات الصلة. |
لم يعد خيار seed_estimator مدعوماً. | أزل أي تعيين لـ options.seed_estimator (تعيينه يثير ValidationError). لا يوجد مكافئ من جانب العميل لذلك، لذا لم تعد النتائج قابلة لإعادة الإنتاج من خلال هذه البذرة (seed). |
تفعيل تسجيل INFO
نظراً لأن المزيد من العمل يحدث الآن على جانب العميل، فمن المفيد رؤية تقدم تلك
المعالجة. فعّل التسجيل بمستوى INFO لمسجل qiskit_ibm_runtime:
import logging
logger = logging.getLogger("qiskit_ibm_runtime")
logger.setLevel(logging.INFO)
إجراء تعلم ضجيج صريح لـ PEA وPEC
لم يعد Estimator الجديد يُجري تعلم ضجيج ضمني عند اختيار طريقة تخفيف الأخطاء
PEA أو PEC. يجب عليك تعلم نماذج الضجيج بشكل صريح وتمريرها
إليه. استخدم NoiseLearnerV3 الجديد للتحكم في كيفية
تقسيم الدارات إلى طبقات. فهو يأخذ قائمة من تعليمات الدارة المعلّبة (boxed) (على سبيل المثال،
الطبقات الفريدة) كمدخل.
تتطلب PEA وَPEC الآن بشكل إلزامي هذا النمط الصريح. لا تتخطَ خطوة تعلم الضجيج وإلا سيفشل كودك. لا يتأثر تعلم ضجيج القياس بالنسبة لـ TREX ويستمر في العمل كما كان سابقاً.
وبالمثل، إذا كان كودك يستخدم NoiseLearner ويمرر نموذج الضجيج الناتج إلى Estimator من جانب الخادم، فأنت بحاجة إلى الترحيل إلى NoiseLearnerV3. لا تستخدم NoiseLearner القديم، فهو غير متوافق مع Estimator الجديد.
جميع خيارات تعلم الضجيج في Estimator من جانب الخادم (LayerNoiseLearningOptions) تُقابل مباشرة خيار NoiseLearnerV3 (NoiseLearnerV3Options)، باستثناء max_layers_to_learn. فعدد الطبقات المراد تعلمها يعتمد بدلاً من ذلك على عدد الطبقات الممرَّرة إلى NoiseLearnerV3.
على سبيل المثال:
Estimator من جانب الخادم (مع تفعيل PEC):
from qiskit_ibm_runtime import Estimator
pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
estimator.options.resilience.layer_noise_learning.num_randomizations = 64
job = estimator.run(pubs)
Estimator من جانب العميل (مع تفعيل PEC):
from qiskit_ibm_runtime.executor_estimator import Estimator
from qiskit_ibm_runtime import NoiseLearnerV3
pubs = [...] # Your PUBs
estimator = Estimator(mode, options)
estimator.options.resilience.pec_mitigation = True # or zne_mitigation + pea amplifier
# Identify the unique layers to learn.
layers = estimator.find_unique_layers(pubs)
# Learn the noise model for those layers (runs as a separate job).
learner = NoiseLearnerV3(mode)
learner.options.num_randomizations = 64 # Same as layer_noise_learning.num_randomizations
learner_job = learner.run(layers)
learner_result = learner_job.result()
# Convert results to Pauli-Lindblad noise maps.
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
# Assign the learned noise maps so PEA/PEC uses them.
estimator.options.resilience.layer_noise_model = zip(layers, pauli_lindblad_maps)
# Now execute the target PUBs.
job = estimator.run(pubs)
الترحيل من NoiseLearner إلى NoiseLearnerV3
يعمل NoiseLearner فقط مع التطبيق من جانب الخادم لـ Estimator. لذلك، إذا كان كودك يستخدم NoiseLearner لتعلم نموذج الضجيج وتمريره إلى Estimator، فأنت بحاجة إلى تحديث كودك لاستخدام NoiseLearnerV3.
راجع دليل الترحيل من NoiseLearner إلى NoiseLearnerV3 للحصول على التفاصيل.
تقسيم المهام
عندما يتعين عليك تقسيم مهمة واحدة إلى عدة مهام لأن قيم shot أو الدقة المختلطة في مهمة واحدة لم تعد مدعومة، ضع في اعتبارك ما يلي:
-
جمّع PUBs حسب قيمتها المستهدفة — مهمة واحدة لكل قيمة مميزة، وليس مهمة واحدة لكل PUB. التقسيم هو إعادة تجميع، لذا لا يتغير العدد الإجمالي لـ PUBs التي ترسلها. على سبيل المثال، بالنظر إلى
[A@0.01, B@0.05, C@0.01]، أرسل مهمتين:[A, C]بدقةprecision=0.01وَ[B]بدقةprecision=0.05. إرسالAوَCكمهمتين منفصلتين أقل كفاءة، لأن كل مهمة تأتي مع عبء إضافي ثابت. -
تعلّم مرة واحدة واستخدم نماذج الضجيج في جميع المهام المقسَّمة. من الأكثر كفاءة تشغيل مهمة
NoiseLearnerV3واحدة على اتحاد جميع الطبقات. تحتوي نتيجة مهمة تعلم الضجيج على قائمة من كائناتNoiseLearnerV3Result، واحد لكل تعليمة إدخال، وبنفس ترتيب قائمة الإدخال. يمكنك استخدام مُخرَجات مهمة تعلم الضجيج هذه في جميع مهام (Estimator) المقسَّمة، ويتم تجاهل نماذج الضجيج للطبقات غير الموجودة في PUBs الخاصة بمهمة مقسَّمة. -
أرسل جميع المهام المقسَّمة في
Batchأولاً، ثم اجمع نتائجها. يوفر وضع تنفيذBatchتنفيذاً متوازياً فعالاً عندما تكون هناك عدة مهام. ومع ذلك، فإنjob.result()معطِّل (blocking)، لذا فإن استدعاءه داخل حلقة الإرسال يجعل تنفيذ المهام متسلسلاً ويلغي فوائد استخدامBatch. تأكد من استخدام نمط "أرسل الكل ثم اجمع" (موضح أدناه).
في المثال التالي، تتطلب pub1 وَpub2 قيمة precision=0.5، بينما تتطلب pub3 قيمة precision=0.1:
group1_pubs = [pub1, pub2]
group2_pubs = [pub3]
with Batch(backend=backend) as batch:
estimator = Estimator(mode=batch)
estimator.options.resilience.pec_mitigation = True
# Learn once, over the union of every job's layers.
all_layers = estimator.find_unique_layers(group1_pubs + group2_pubs)
learner_job = NoiseLearnerV3(mode=batch).run(all_layers)
learner_result = learner_job.result()
pauli_lindblad_maps = learner_result.to_pauli_lindblad_maps()
# Assign the learned noise maps. Any layers not found in the input PUBs are ignored.
estimator.options.resilience.layer_noise_model = zip(all_layers, pauli_lindblad_maps)
# Submit every split job with different precision values.
jobs = []
jobs.append(estimator.run(group1_pubs, precision=0.5))
jobs.append(estimator.run(group2_pubs, precision=0.1))
# Block once, at the end — the jobs run in parallel.
results = [job.result() for job in jobs]
بنية مدخلات المهمة
يقوم التطبيق الجديد بتعيين مدخلات Sampler أو Estimator إلى مدخلات Executor، لذا تُرجع job.inputs قاموساً يحتوي على مدخلات Executor. يحتوي هذا القاموس على المفاتيح التالية:
-
options: مدخلExecutorOption. -
quantum_program: مدخلQuantumProgram -
schema_version: إصدار المخطط (schema) المستخدَم من جانب الخادم.
إذا كان كودك يستخدم job.inputs['options'] للعثور على الخيارات المحددة للمهمة، يمكنك الآن استخدام job.result().metadata['options'] بدلاً من ذلك.
الاختبار محلياً باستخدام backend وهمي
قبل الإرسال إلى الأجهزة الفعلية (hardware)، يمكنك التحقق من صحة الكود المُرحَّل مقابل backend من نوع Fake*
لاكتشاف أي أخطاء نحوية مبكراً. لاحظ التفاصيل التالية حول وضع الاختبار المحلي:
-
لا يُعيد إنتاج نتائج الأجهزة الفعلية. لا تُحاكي المحاكاة المحلية الصاخبة (noisy simulation) ضجيج الجهاز الحقيقي بشكل مثالي، وبالتالي قد تختلف المخرجات. لكن التشغيل يتحقق فعلاً من صحة مسارات الخيارات وأنواع القيم.
-
ليس لدى
NoiseLearnerV3وضع اختبار محلي: يقبلmodeالخاص به فقطBackendأوSessionأوBatchحقيقياً، لذا لا يمكنك تجربة خطوة تعلم الضجيج مقابل backend وهمي. تحقق من صحة ذلك الجزء من كودك مقابل مرجع API الخاص بـNoiseLearnerV3بدلاً من ذلك. تأكد من أن المنشئ (constructor)، وشكل مدخلrun(instructions)، وأي دالة مساعدة (مثل دالة الطبقة الفريدة المساعدة) تُستخدم كما هو موثَّق.
تحويل الدارة إلى صيغة كليفورد لمحاكاة محلية فعالة
يستخدم الـ backend الوهمي محاكياً لمتجه الحالة (statevector) (صاخباً)، تنمو تكلفته أسياً مع
عدد الكيوبتات والعمق. وبالتالي، يمكن لدارة حمل عمل واقعية أن تتعلق أو تستنفد الذاكرة. وبما أن
الاختبار المحلي يحتاج فقط إلى تجربة مسارات الخيارات (وليس إعادة إنتاج نتائج فيزيائية)،
فقلّص الدارة أولاً إلى دارة كليفورد باستخدام
ConvertISAToClifford،
التي تقرّب كل زاوية RZ/RZZ/RX إلى أقرب مضاعف لـ π/2. تُحاكى دارات كليفورد
بكفاءة (محاكاة المُثبِّت stabilizer simulation) بغض النظر عن الحجم.
from qiskit.transpiler import PassManager
from qiskit_ibm_runtime.transpiler.passes import ConvertISAToClifford
clifford = PassManager([ConvertISAToClifford()]).run(isa_circuit)
# run `clifford` (not the original) through the fake-backend primitive
تتطلب ConvertISAToClifford دارة ISA كمدخل (مخرجات
generate_preset_pass_manager(...).run(...) الموجَّهة إلى الـ backend). يجب عليك مراعاة العواقب التالية
عند إنشاء PUB المحلي:
-
يتم إسقاط السمة
.layout. تحتفظ الدارة المحوَّلة إلى كليفورد بنفس عدد الكيوبتات، لكنclifford.layoutتكونNone، لذا يفشلobservable.apply_layout(clifford.layout). قم بترتيب المرصود بدلاً من ذلك انطلاقاً من دارة ISA السابقة لكليفورد:isa_obs = observable.apply_layout(isa_circuit.layout)، ثم شغّل(clifford, isa_obs). -
يتم ربط المعاملات بعيداً. تحويل زوايا الدوران إلى القيم المقرَّبة يحوّل دارة ISA بارامترية إلى دارة كليفورد ملموسة، لذا تصبح
clifford.num_parametersتساوي0. أي PUB لا يزال يحمل مصفوفة قيم معاملات يفشل في الإجبار (coercion). في التشغيل المحلي، احذف مصفوفة المعاملات من PUB؛ بينما يحتفظ التشغيل على الجهاز الفعلي بالدارة البارامترية الأصلية وقيمها.