التغذية الراجعة الكلاسيكية وتدفق التحكم
إصدارات الحزم
تم تطوير الكود في هذه الصفحة باستخدام المتطلبات التالية. ننصح باستخدام هذه الإصدارات أو ما هو أحدث منها.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
الإصدار الجديد من الدوائر الديناميكية أصبح متاحاً الآن لجميع المستخدمين على جميع الـ Backends. يمكنك الآن تشغيل الدوائر الديناميكية على نطاق الاستخدام الفعلي. اطّلع على الإعلان لمزيد من التفاصيل.
الدوائر الديناميكية أدوات قوية تتيح لك قياس الـ Qubits في منتصف تنفيذ دائرة كمومية ثم إجراء عمليات منطقية كلاسيكية داخل الدائرة بناءً على نتائج قياسات منتصف الدائرة. تُعرف هذه العملية أيضاً بـ التغذية الراجعة الكلاسيكية. وعلى الرغم من أننا في بداية فهم كيفية الاستفادة القصوى من الدوائر الديناميكية، فقد رصد مجتمع البحث الكمي عدداً من حالات الاستخدام، منها:
- التحضير الكفء لحالات الكم، كـ حالة GHZ، وحالة W، (لمزيد من المعلومات حول حالة W، راجع أيضاً "تحضير الحالة بدوائر ضحلة باستخدام التغذية الراجعة") وفئة واسعة من حالات حاصل الضرب المصفوفي
- التشابك الكفء بعيد المدى بين الـ Qubits على الشريحة ذاتها باستخدام دوائر ضحلة
- أخذ عينات كفء من دوائر شبيهة بـ IQP
غير أن هذه التحسينات التي تجلبها الدوائر الديناميكية تأتي مع مقايضات. فقياسات منتصف الدائرة والعمليات الكلاسيكية تستغرق عادةً وقتاً أطول من بوابات الـ 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}
يمكن إعطاء جملة 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}
بالإضافة إلى الاشتراط على بت كلاسيكي واحد، يمكن أيضاً الاشتراط على قيمة سجل كلاسيكي مؤلف من عدة بتات.
في المثال التالي، نطبّق بوابات هادامار على 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}
التعبيرات الكلاسيكية
تحتوي وحدة ا لتعبيرات الكلاسيكية في Qiskit qiskit.circuit.classical على تمثيل استكشافي للعمليات التي تُنفَّذ في وقت التشغيل على القيم الكلاسيكية أثناء تنفيذ الدائرة. بسبب قيود الأجهزة، يُدعم حالياً شرط QuantumCircuit.if_test() فقط.
يُظهر المثال التالي كيف يمكنك استخدام حساب التكافؤ لإنشاء حالة GHZ بـ n Qubit باستخدام الدوائر الديناميكية. أولاً، أنشئ زوج من أزواج Bell على الـ Qubits المتجاورة. ثم ألصق هذه الأزواج ببعضها باستخدام طبقة من بوابات CNOT بين الأزواج. بعد ذلك تقيس الـ Qubit الهدف لجميع بوابات CNOT السابقة وتُعيد تعيين كل Qubit مقيس إلى الحالة . تط بّق على كل موقع غير مقيس حيث يكون تكافؤ جميع البتات السابقة فردياً. وأخيراً، تُطبَّق بوابات 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)
البحث عن الـ 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بأكمله، يجب أن يكون حجم ذلكClassicalRegister32 بتاً أو أقل. أما إذا كنت تقارن بتاً واحداً فقط من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)):
...cr = ClassicalRegister(50)
qr = QuantumRegister(50)
circuit = QuantumCircuit(qr, cr)
...
circ.measure(qr, cr)
with circ.if_test((cr[5], 1)):
... -
الشروط المتداخلة غير مسموح بها. على سبيل المثال، كتلة الكود التالية لن تعمل لأنها تحتوي على
if_testداخلif_testآخر:- غير صالح
- صالح
c1 = ClassicalRegister(1, "c1")
c2 = ClassicalRegister(2, "c2")
...
with circ.if_test((c1, 1)):
with circ.if_test(c2, 1)):
...cr = ClassicalRegister(2)
...
with circuit.if_test((cr, 0b11)):
... -
استخدام
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، اتبع هذه الخطوات:
- جمّع حدود جميع المراقبات في قسم. يمكن القيام بذلك باستخدام
PauliListAPI، على سبيل المثال.ملاحظةيمكنك استخدام سمة العامل الأولي
BitArrayلحساب قيم التوقع للمراقبات المُقدَّمة. - نفّذ دائرة تغيير أساس واحدة لكل قسم (أياً كان تغيير الأساس الضروري لكل قسم). راجع الوحدة الإضافية لقياس الأسس
measurement_basesmodule لمزيد من المعلومات. ابدأ مع الأدوات المساعدة. - اجمع نتائج كل قسم مع بعضها.
الخطوات التالية
- تعلّم كيفية تطبيق فصل ديناميكي دقيق باستخدام stretch.
- تعرّف على قياسات منتصف الدائرة الأقصر التي تقلل من وقت تنفيذ الدائرة.
- استخدم تصور جدول الدائرة الزمني لتصحيح الأخطاء وتحسين دوائرك الديناميكية.