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

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

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

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

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
الدوائر الديناميكية متاحة الآن على جميع الـ Backends

الإصدار الجديد من الدوائر الديناميكية أصبح متاحاً الآن لجميع المستخدمين على جميع الـ Backends. يمكنك الآن تشغيل الدوائر الديناميكية على نطاق الاستخدام الفعلي. اطّلع على الإعلان لمزيد من التفاصيل.

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

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

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

ملاحظة

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

جملة if

تُستخدم جملة if لتنفيذ عمليات بشكل شرطي بناءً على قيمة بت كلاسيكي أو سجل كلاسيكي.

في المثال التالي، نطبّق بوابة هادامار على Qubit ونقيسه. إذا كانت النتيجة 1، نطبّق بوابة X على الـ Qubit مما يعيده إلى الحالة 0. ثم نقيس الـ Qubit مرة أخرى. يجب أن تكون نتيجة القياس 0 باحتمالية 100%.

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-ibm-runtime
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister

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

circuit.h(q0)
# Use MidCircuitMeasure() if it's supported by the backend.
# circuit.append(MidCircuitMeasure(), [q0], [c0])
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)):
circuit.x(q0)
circuit.measure(q0, c0)
circuit.draw("mpl")

# example output counts: {'0': 1024}

Output of the previous code cell

يمكن إعطاء جملة with هدف إسناد يكون بحد ذاته مدير سياق يمكن تخزينه واستخدامه لاحقاً لإنشاء كتلة else، التي تُنفَّذ في كل مرة لا تُنفَّذ فيها محتويات كتلة if.

في المثال التالي، نُهيئ سجلات تحتوي على Qubitين وبتّين كلاسيكيين. نطبّق بوابة هادامار على الـ Qubit الأول ونقيسه. إذا كانت النتيجة 1، نطبّق بوابة هادامار على الـ Qubit الثاني؛ وإلا نطبّق بوابة X عليه. وأخيراً، نقيس الـ Qubit الثاني أيضاً.

qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1) = qubits
(c0, c1) = clbits

circuit.h(q0)
circuit.measure(q0, c0)
with circuit.if_test((c0, 1)) as else_:
circuit.h(q1)
with else_:
circuit.x(q1)
circuit.measure(q1, c1)

circuit.draw("mpl")

# example output counts: {'01': 260, '11': 272, '10': 492}

Output of the previous code cell

بالإضافة إلى الاشتراط على بت كلاسيكي واحد، يمكن أيضاً الاشتراط على قيمة سجل كلاسيكي مؤلف من عدة بتات.

في المثال التالي، نطبّق بوابات هادامار على Qubitين ونقيسهما. إذا كانت النتيجة 01، أي أن الـ Qubit الأول هو 1 والـ Qubit الثاني هو 0، نطبّق بوابة X على Qubit ثالث. وأخيراً، نقيس الـ Qubit الثالث. لاحظ أننا اخترنا لوضوح الكود تحديد حالة البت الكلاسيكي الثالث وهي 0 في شرط if. في رسم الدائرة، يُشار إلى الشرط بالدوائر على البتات الكلاسيكية المشروط عليها. تُشير الدائرة السوداء إلى الاشتراط على 1، بينما تُشير الدائرة البيضاء إلى الاشتراط على 0.

qubits = QuantumRegister(3)
clbits = ClassicalRegister(3)
circuit = QuantumCircuit(qubits, clbits)
(q0, q1, q2) = qubits
(c0, c1, c2) = clbits

circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.if_test((clbits, 0b001)):
circuit.x(q2)
circuit.measure(q2, c2)

circuit.draw("mpl")

# example output counts: {'101': 269, '011': 260, '000': 252, '010': 243}

Output of the previous code cell

التعبيرات الكلاسيكية

تحتوي وحدة التعبيرات الكلاسيكية في Qiskit qiskit.circuit.classical على تمثيل استكشافي للعمليات التي تُنفَّذ في وقت التشغيل على القيم الكلاسيكية أثناء تنفيذ الدائرة. بسبب قيود الأجهزة، يُدعم حالياً شرط QuantumCircuit.if_test() فقط.

يُظهر المثال التالي كيف يمكنك استخدام حساب التكافؤ لإنشاء حالة GHZ بـ n Qubit باستخدام الدوائر الديناميكية. أولاً، أنشئ n/2n/2 زوج من أزواج Bell على الـ Qubits المتجاورة. ثم ألصق هذه الأزواج ببعضها باستخدام طبقة من بوابات CNOT بين الأزواج. بعد ذلك تقيس الـ Qubit الهدف لجميع بوابات CNOT السابقة وتُعيد تعيين كل Qubit مقيس إلى الحالة 0\vert 0 \rangle. تطبّق XX على كل موقع غير مقيس حيث يكون تكافؤ جميع البتات السابقة فردياً. وأخيراً، تُطبَّق بوابات CNOT على الـ Qubits المقيسة لاستعادة التشابك الضائع بسبب القياس.

في حساب التكافؤ، يتضمن العنصر الأول للتعبير المُنشأ رفع كائن Python mr[0] إلى عقدة Value (يُستخدم lift لتحويل الكائنات التعسفية إلى تعبيرات كلاسيكية). هذا غير ضروري لـ mr[1] والسجل الكلاسيكي التالي المحتمل، إذ أنهما مدخلات لـ expr.bit_xor، ويُجرى أي رفع ضروري تلقائياً في هذه الحالات. يمكن بناء مثل هذه التعبيرات في حلقات وهياكل أخرى.

from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr

num_qubits = 8
if num_qubits % 2 or num_qubits < 4:
raise ValueError("num_qubits must be an even integer ≥ 4")
meas_qubits = list(range(2, num_qubits, 2)) # qubits to measure and reset

qr = QuantumRegister(num_qubits, "qr")
mr = ClassicalRegister(len(meas_qubits), "m")
qc = QuantumCircuit(qr, mr)

# Create local Bell pairs
qc.reset(qr)
qc.h(qr[::2])
for ctrl in range(0, num_qubits, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])

# Glue neighboring pairs
for ctrl in range(1, num_qubits - 1, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])

# Measure boundary qubits between pairs,reset to 0
for k, q in enumerate(meas_qubits):
qc.measure(qr[q], mr[k])
qc.reset(qr[q])

# Parity-conditioned X corrections
# Each non-measured qubit gets flipped iff the parity (XOR) of all
# preceding measurement bits is 1
for tgt in range(num_qubits):
if tgt in meas_qubits: # skip measured qubits
continue
# all measurement registers whose physical qubit index < tgt
left_bits = [k for k, q in enumerate(meas_qubits) if q < tgt]
if not left_bits: # skip if list empty
continue

# build XOR-parity expression
parity = expr.lift(
mr[left_bits[0]]
) # lift the first bit to Value so it will be treated like a boolean.
for k in left_bits[1:]:
parity = expr.bit_xor(
mr[k], parity
) # calculate parity with all other bits
with qc.if_test(parity): # Add X if parity is 1
qc.x(qr[tgt])

# Re-entangle measured qubits
for ctrl in range(1, num_qubits - 1, 2):
qc.cx(qr[ctrl], qr[ctrl + 1])
qc.draw(output="mpl", style="iqp", idle_wires=False, fold=-1)

Output of the previous code cell

البحث عن الـ Backends التي تدعم الدوائر الديناميكية

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

ملاحظات
  • الـ Backends المتاحة للحساب تعتمد على النسخة المحددة في بيانات الاعتماد.
  • الإصدار الجديد من الدوائر الديناميكية أصبح متاحاً الآن لجميع المستخدمين على جميع الـ Backends. اطّلع على الإعلان لمزيد من التفاصيل.
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_miami')>, <IBMBackend('ibm_marrakesh')>, <IBMBackend('ibm_torino')>, <IBMBackend('ibm_kingston')>]

قيود Qiskit Runtime

انتبه إلى القيود التالية عند تشغيل الدوائر الديناميكية في Qiskit Runtime.

  • بسبب محدودية الذاكرة الفيزيائية في الإلكترونيات الضابطة، يوجد أيضاً حد لعدد جمل if وحجم معاملاتها. هذا الحد دالة في عدد عمليات البث وعدد البتات المبثوثة في مهمة (وليس دائرة).

    عند معالجة شرط 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 وQiskit Runtime.

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

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

استخدام الدوائر الديناميكية مع Estimator

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

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

  1. جمّع حدود جميع المراقبات في قسم. يمكن القيام بذلك باستخدام PauliList API، على سبيل المثال.
    ملاحظة

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

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

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

توصيات