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

تنفيذ الـ Circuits الديناميكية

الـ Circuits الديناميكية هي أدوات قوية تتيح لك قياس الـ qubits في منتصف تنفيذ دائرة كمية ثم تنفيذ عمليات منطقية كلاسيكية داخل الدائرة بناءً على نتائج تلك القياسات الوسطى. وتُعرف هذه العملية أيضًا بـ التغذية الراجعة الكلاسيكية. وعلى الرغم من أن مجتمع أبحاث الكم لا يزال في مراحله الأولى لفهم أفضل طريقة للاستفادة من الـ Circuits الديناميكية، فقد رصد عددًا من حالات الاستخدام، مثل:

غير أن هذه التحسينات التي تجلبها الـ Circuits الديناميكية تأتي بتبادلات. فالقياسات الوسطى والعمليات الكلاسيكية عادةً ما تستغرق وقت تنفيذ أطول من بوابات Qubitين، وقد يُلغي هذا الوقت الإضافي فوائد تقليل عمق الدائرة. لذا، يُعدّ تقليل مدة قياس منتصف الدائرة من أبرز محاور التحسين في الإصدار الجديد من الـ Circuits الديناميكية التي تُصدرها IBM Quantum®. للاطلاع على القيود الأخرى عند استخدام الـ Circuits الديناميكية، راجع جدول توافق الميزات الخاص بـ Estimator أو Sampler.

يُعرِّف مواصفات OpenQASM 3 عددًا من هياكل التحكم في التدفق، لكن IBM Quantum Compute Service يدعم حاليًا فقط تعليمة if الشرطية. في Qiskit SDK، يتوافق ذلك مع طريقة if_test في QuantumCircuit. تُعيد هذه الطريقة مدير سياق وتُستخدم عادةً في عبارة with. يصف هذا الدليل كيفية استخدام هذه التعليمة الشرطية.

ملاحظة

تستخدم أمثلة الكود في هذا الدليل تعليمة القياس القياسية للقياسات الوسطى. غير أنه يُوصى باستخدام تعليمة MidCircuitMeasure بدلًا من ذلك إذا كان Backend يدعمها. راجع قسم القياسات الوسطى للتفاصيل.

إصدارات الحزم

تم تطوير الكود في هذه الصفحة باستخدام المتطلبات التالية. نوصي باستخدام هذه الإصدارات أو أحدث.

qiskit[all]~=2.5.2
qiskit-ibm-runtime~=0.47.0

البحث عن Backends تدعم الـ Circuits الديناميكية​

للعثور على جميع الـ Backends التي يمكن لحسابك الوصول إليها وتدعم الـ Circuits الديناميكية، شغِّل كودًا مشابهًا لما يلي. يفترض هذا المثال أنك حفظت بيانات تسجيل الدخول. يمكنك أيضًا تحديد البيانات الاعتمادية صراحةً عند تهيئة حساب IBM Quantum Compute Service، مما يتيح لك عرض الـ Backends المتاحة لنسخة معينة أو نوع خطة محدد، على سبيل المثال.

ملاحظات
  • تعتمد الـ Backends المتاحة للحساب على النسخة المحددة في بيانات الاعتماد.
  • الإصدار الجديد من الـ Circuits الديناميكية متاح الآن لجميع المستخدمين على جميع الـ Backends. راجع الإعلان للمزيد من التفاصيل.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
# This cell is hidden from users. It hides all those "...instance was not set..." warnings.
import warnings

warnings.filterwarnings("ignore", message=".*Instance was not set*")
from qiskit_ibm_runtime import QiskitRuntimeService

service = QiskitRuntimeService()
dc_backends = service.backends(dynamic_circuits=True)
print(dc_backends)
[<IBMBackend('ibm_pittsburgh')>, <IBMBackend('ibm_boston')>, <IBMBackend('ibm_fez')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_kingston')>]

القياسات الوسطى​

قبل qiskit-ibm-runtime الإصدار v0.43.0، كانت measure هي تعليمة القياس الوحيدة في Qiskit. غير أن القياسات الوسطى لها متطلبات ضبط مختلفة عن القياسات الطرفية (القياسات التي تحدث في نهاية الدائرة). على سبيل المثال، تحتاج إلى مراعاة مدة التعليمة عند ضبط قياس وسطي لأن التعليمات الأطول تُنتج دوائر أكثر ضوضاء. في المقابل، لا تحتاج إلى مراعاة مدة التعليمة للقياسات الطرفية لأنه لا توجد تعليمات بعدها.

ملاحظة

تُعيَّن تعليمة MidCircuitMeasure إلى تعليمة measure_2 المُبلَّغ عنها في supported_instructions الخاص بالـ Backend. غير أن measure_2 غير مدعومة على جميع الـ Backends. استخدم service.backends(filters=lambda b: "measure_2" in b.supported_instructions) للعثور على الـ Backends التي تدعمها. قد تُضاف قياسات جديدة مستقبلًا، لكن ذلك غير مضمون.

طريقة MidCircuitMeasure​

في qiskit-ibm-runtime الإصدار v0.43.0، جرى تقديم تعليمة MidCircuitMeasure. كما يوحي الاسم، هي تعليمة قياس جديدة مُحسَّنة للقياسات الوسطى على معالجات IBM® الكمية (QPUs). وبينما يمكنك استخدام QuantumCircuit.measure لقياس وسطي، يُعدّ MidCircuitMeasure خيارًا أفضل عمومًا نظرًا لتصميمه، إذ يُضيف حملًا أقل إلى دارتك مقارنةً بـ QuantumCircuit.measure.

from qiskit import QuantumCircuit
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime.circuit import MidCircuitMeasure

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, dynamic_circuits=True
)

circ = QuantumCircuit(2, 2)
circ.x(0)
circ.append(MidCircuitMeasure(), [0], [0])
# circ.measure([0], [0])
# circ.measure_all()
print(circ.draw(cregbundle=False))
┌───┐┌────────────┐
q_0: ┤ X ├┤0 ├
└───┘│ │
q_1: ─────┤ Measure_2 ├
│ │
c_0: ═════╡0 ╞
└────────────┘
c_1: ═══════════════════
ملاحظات مهمة
  • يجب أن يكون هناك سجل كلاسيكي واحد على الأقل لاستخدام القياسات.

  • يتطلب Sampler primitive قياسات Circuit. يمكنك إضافة قياسات Circuit مع Estimator primitive، لكنها تُتجاهَل.

Store​

مع qiskit-ibm-runtime الإصدار 0.47.0 أو أحدث، يمكنك استخدام تعليمة store لحفظ نتيجة تعبير كلاسيكي إذا كان سيُستخدَم بشكل متكرر. تُوازَن العمليات تلقائيًا، مما يجعل كودك أكثر كفاءة بشكل ملحوظ في وقت التشغيل.

للمزيد من المعلومات، راجع دليل التغذية الراجعة الكلاسيكية والتحكم في التدفق.

ملاحظة

عند استخدام store لحفظ قيمة في سجل كلاسيكي على Backend حقيقي، تُحفَظ القيمة في الذاكرة أثناء التنفيذ فقط ولا تُنسَخ أو تُعاد في نتيجة المهمة.

على سبيل المثال، في الكود التالي، يحمل temp نفس قيمة creg أثناء التنفيذ، وتعمل if_test كما هو متوقع. لكن بعد انتهاء المهمة، فإن BitArray temp المُعاد في نتيجة المهمة لا يحتوي على قيمة creg. أي أن job.result()[0].data.temp تساوي 0.

creg = ClassicalRegister(3, "c")
temp = ClassicalRegister(3, "temp")
...
qc.store(temp, creg)
with circuit.if_test((temp, 0b001)):
...

مثال كامل​

يُنشئ الكود التالي Circuit ديناميكية ويشغلها على معدات IBM®.

from qiskit_ibm_runtime import SamplerV2, QiskitRuntimeService
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.transpiler import generate_preset_pass_manager

service = QiskitRuntimeService()
backend = service.least_busy(
operational=True, simulator=False, dynamic_circuits=True
)

# Create a dynamic circuit

qubits = QuantumRegister(1)
clbits = ClassicalRegister(1)
qc = QuantumCircuit(qubits, clbits)
(q0,) = qubits
(c0,) = clbits

qc.h(q0)
qc.measure(q0, c0)
with qc.if_test((c0, 1)):
qc.x(q0)
qc.measure(q0, c0)

# Convert to an ISA circuit for the given backend

pm = generate_preset_pass_manager(backend=backend, optimization_level=1)
isa_circuit = pm.run(qc)

# Generate samplers for backend targets
sampler = SamplerV2(backend)

# Submit jobs
sampler_job = sampler.run([isa_circuit])
result = sampler_job.result()

print(
f">>> {' Job ID:':<10} {sampler_job.job_id()} ({sampler_job.status()})"
)
>>> Job ID: dab7lcbvpcac73ddek2g (DONE)

قيود Quantum Compute​

انتبه للقيود التالية عند تشغيل الـ Circuits الديناميكية في Quantum Compute.

  • بسبب محدودية الذاكرة الفيزيائية في أجهزة التحكم الإلكترونية، هناك حد لعدد تعليمات if وحجم معاملاتها. يعتمد هذا الحد على عدد عمليات البث (broadcasts) وعدد البتات المُبثَّة في المهمة (وليس في الدائرة).

    عند معالجة شرط if، تحتاج بيانات القياس إلى النقل إلى منطق التحكم لإجراء التقييم. البث هو نقل بيانات كلاسيكية فريدة، والبتات المُبثَّة هي عدد البتات الكلاسيكية المُنقَلة. لنأخذ المثال التالي:

    c0 = ClassicalRegister(3)
    c1 = ClassicalRegister(5)
    ...
    with circuit.if_test((c0, 1)) ...
    with circuit.if_test((c0, 3)) ...
    with circuit.if_test((c1[2], 1)) ...

    في مثال الكود السابق، يُعدّ كائنا if_test الأوليان على c0 بثًّا واحدًا لأن محتوى c0 لم يتغير ولا يحتاج إلى إعادة البث. أما if_test على c1 فهو بث ثانٍ. يبث الأول جميع البتات الثلاث في c0 والثاني يبث بتًا واحدًا فقط، ليبلغ إجمالي البتات المُبثَّة أربع بتات.

    حاليًا، إذا بثّت 60 بتًا في كل مرة، يمكن للمهمة أن تحتوي على نحو 300 بث. أما إذا بثّت بتًا واحدًا في كل مرة، فيمكن أن تصل إلى 2400 بث.

  • يجب أن يكون المُعامل المستخدم في عبارة if_test 32 بت أو أقل. لذلك، إذا كنت تقارن ClassicalRegister كاملًا، يجب أن يكون حجم ذلك ClassicalRegister 32 بت أو أقل. أما إذا كنت تقارن بت واحد فقط من ClassicalRegister، فيمكن أن يكون ذلك ClassicalRegister من أي حجم (لأن المُعامل هو بت واحد فقط).

    على سبيل المثال، كتلة "غير صالح" لا تعمل لأن cr أكبر من 32 بتًا. غير أنه يمكنك استخدام سجل كلاسيكي أوسع من 32 بتًا إذا كنت تختبر بتًا واحدًا فقط، كما هو موضح في كتلة "صالح".

    cr = ClassicalRegister(50)
    qr = QuantumRegister(50)
    circuit = QuantumCircuit(qr, cr)
    ...
    circ.measure(qr, cr)
    with circ.if_test((cr, 15)):
    ...
  • الشروط المتداخلة غير مسموح بها. على سبيل المثال، كتلة الكود التالية لن تعمل لأنها تحتوي على if_test داخل if_test آخر:

    c1 = ClassicalRegister(1, "c1")
    c2 = ClassicalRegister(2, "c2")
    ...
    with circ.if_test((c1, 1)):
    with circ.if_test(c2, 1)):
    ...
  • استخدام reset أو القياسات داخل الشروط غير مدعوم.

  • العمليات الحسابية غير مدعومة.

  • راجع جدول ميزات OpenQASM 3 لتحديد ميزات OpenQASM 3 المدعومة في Qiskit وQuantum Compute.

  • عند استخدام OpenQASM 3 (بدلًا من QuantumCircuit) كتنسيق إدخال لتمرير الـ Circuits إلى IBM Quantum primitives، يُدعم فقط ما يمكن تحميله في Qiskit. على سبيل المثال، العمليات الكلاسيكية غير مدعومة لأنه لا يمكن تحميلها في Qiskit. راجع استيراد برنامج OpenQASM 3 إلى Qiskit للمزيد من المعلومات.

  • تعليمات for وwhile وswitch غير مدعومة.

استخدام الـ Circuits الديناميكية مع Estimator​

بما أن Estimator لا يدعم الـ Circuits الديناميكية، يمكنك استخدام Sampler وبناء دوائر قياس خاصة بك.

لمحاكاة سلوك Estimator، اتبع هذه الخطوات:

  1. قسِّم حدود جميع المراقِبات إلى أقسام. يمكن تحقيق ذلك باستخدام واجهة برمجة تطبيقات PauliList مثلًا.
    ملاحظة

    يمكنك استخدام خاصية الـ primitive BitArray لحساب قيم التوقع للمراقِبات المُقدَّمة.

  2. نفِّذ Circuit تغيير أساس واحدة لكل قسم (أيًّا كان تغيير الأساس المطلوب لكل قسم). راجع وحدة measurement_bases في أداة إضافات القياس للمزيد من المعلومات. راجع أيضًا وثائق حزمة أدوات Qiskit addon.
  3. اجمع نتائج كل قسم معًا.

القيود​

راجع أي جدول توافق الميزات لفهم القيود عند استخدام الـ Circuits الديناميكية. لاحظ أن توافق الميزات لا يعتمد على نوع الـ primitive.

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

توصيات