انتقل إلى المحتوى الرئيسي
هذا المشروع في مرحلة تجريبية. وجدت خطأ؟ افتح مشكلة على GitHub. لديك سؤال أو فكرة؟ اسأل في Discussions — نحب سماع ملاحظاتك!
كيف جودة الترجمة في هذه الصفحة؟

التغذية الأمامية الكلاسيكية وتدفق التحكم (الدوائر الديناميكية)

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

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

qiskit[all]~=2.5.2

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

تُعيد كل من هذه الميثودات مدير سياق وتُستخدم عادةً في جملة with. يشرح باقي هذا الدليل كل من هذه التراكيب وكيفية استخدامها.

تنبيه

ثمة بعض القيود على عمليات التغذية الأمامية الكلاسيكية وتدفق التحكم على الأجهزة الكمومية قد تؤثر على برنامجك. لمزيد من المعلومات، راجع تنفيذ الدوائر الديناميكية.

جملة if​

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

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

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"qiskit": "qiskit"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
from qiskit.circuit import QuantumCircuit, QuantumRegister, ClassicalRegister

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

circuit.h(q0)
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.

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

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

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

في المثال التالي، نطبّق بوابات هادامار على كيوبتين ونقيسهما. إذا كانت النتيجة 01، أي أن الكيوبت الأول هو 1 والكيوبت الثاني هو 0، نطبّق بوابة X على كيوبت ثالث. وأخيراً، نقيس الكيوبت الثالث. لاحظ أننا اخترنا لوضوح الكود تحديد حالة البت الكلاسيكي الثالث وهي 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

جملة Switch​

تُستخدم جملة switch لاختيار الإجراءات بناءً على قيمة بت كلاسيكي أو سجل كلاسيكي. وهي مشابهة لجملة if، غير أنه يمكنك تحديد حالات أكثر لمنطق التفرع. يطبّق المثال التالي بوابة هادامار على كيوبت ويقيسه. إذا كانت النتيجة 0، تُطبَّق بوابة X على الكيوبت، وإذا كانت النتيجة 1، تُطبَّق بوابة Z. يجب أن تكون نتيجة القياس 1 باحتمالية 100%.

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

circuit.h(q0)
circuit.measure(q0, c0)
with circuit.switch(c0) as case:
with case(0):
circuit.x(q0)
with case(1):
circuit.z(q0)
circuit.measure(q0, c0)

circuit.draw("mpl")

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

Output of the previous code cell

نظراً لاستخدام المثال أعلاه بتاً كلاسيكياً واحداً فحسب، لم يكن ثمة إلا حالتان محتملتان، وكان بالإمكان تحقيق النتيجة ذاتها باستخدام جملة if-else. تكون حالة switch مفيدة بشكل رئيسي عند التفرع بناءً على قيمة سجل كلاسيكي مؤلف من عدة بتات. يوضح المثال التالي كيفية إنشاء حالة افتراضية (default)، التي تُنفَّذ إذا لم تتطابق أي من الحالات السابقة. لاحظ أنه في جملة switch، لا يُنفَّذ سوى كتلة واحدة فحسب. لا يوجد تسلسل تنفيذي (fallthrough).

يطبّق المثال التالي بوابات هادامار على كيوبتين ويقيسهما. إذا كانت النتيجة 00 أو 11، تُطبَّق بوابة Z على الكيوبت الثالث. إذا كانت النتيجة 01، تُطبَّق بوابة Y. وإذا لم تتطابق أي من الحالات السابقة، تُطبَّق بوابة X. وأخيراً، يُقاس الكيوبت الثالث.

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.switch(clbits) as case:
with case(0b000, 0b011):
circuit.z(q2)
with case(0b001):
circuit.y(q2)
with case(case.DEFAULT):
circuit.x(q2)
circuit.measure(q2, c2)

circuit.draw("mpl")

# example output counts: {'101': 267, '110': 249, '011': 265, '000': 243}

Output of the previous code cell

حلقة For​

تُستخدم حلقة for للتكرار عبر تسلسل من القيم الكلاسيكية وتنفيذ بعض العمليات في كل تكرار.

يستخدم المثال التالي حلقة for لتطبيق 5 بوابات X على كيوبت ثم يقيسه. ونظراً لتنفيذ عدد فردي من بوابات X، يكون التأثير الإجمالي هو قلب الكيوبت من الحالة 0 إلى الحالة 1.

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

with circuit.for_loop(range(5)) as _:
circuit.x(q0)
circuit.measure(q0, c0)

circuit.draw("mpl")

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

Output of the previous code cell

حلقة While​

تُستخدم حلقة while لتكرار التعليمات طالما تحقق شرط معين.

يطبّق المثال التالي بوابات هادامار على كيوبتين ويقيسهما. ثم ينشئ حلقة while تكرر هذا الإجراء ما دامت نتيجة القياس 11. ونتيجةً لذلك، يجب ألا تكون نتيجة القياس النهائية 11 أبداً، مع ظهور الاحتمالات المتبقية بتكرار متقارب.

qubits = QuantumRegister(2)
clbits = ClassicalRegister(2)
circuit = QuantumCircuit(qubits, clbits)

q0, q1 = qubits
c0, c1 = clbits

circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)
with circuit.while_loop((clbits, 0b11)):
circuit.h([q0, q1])
circuit.measure(q0, c0)
circuit.measure(q1, c1)

circuit.draw("mpl")

# example output counts: {'01': 334, '10': 368, '00': 322}

Output of the previous code cell

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

تحتوي وحدة التعبيرات الكلاسيكية في Qiskit qiskit.circuit.classical على تمثيل استكشافي للعمليات التي تُنفَّذ في وقت التشغيل على القيم الكلاسيكية أثناء تنفيذ الدائرة.

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

في حساب التكافؤ، يتضمن العنصر الأول للتعبير المُنشأ رفع كائن 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

Store​

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

على سبيل المثال، من الأكثر طبيعية والأكثر كفاءة في وقت التشغيل كتابة B[0]⊕B[1]⊕B[2]…B[0] \oplus B[1] \oplus B[2] \ldots، حيث B=¬AB = \neg A، بدلاً من (¬A[0])⊕(¬A[1])⊕(¬A[2])…(\neg A[0]) \oplus (\neg A[1]) \oplus (\neg A[2]) \ldots. يحسب التعبير الأول النفي في خطوة موازية واحدة قبل سلسلة XOR، بدلاً من تقييم كل نفي بشكل تسلسلي داخل التعبير.

مثال كامل:

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

qregs = QuantumRegister(4, "q")
creg = ClassicalRegister(3, "c")
# temp is a plain ClassicalRegister used as the store target
temp = ClassicalRegister(3, "temp")
qc = QuantumCircuit(qregs, creg, temp)

qc.h([0, 1, 2])
qc.measure([0, 1, 2], creg)

# Store bit-NOT of the full 3-bit register into temp
qc.store(temp, expr.bit_not(creg))

# Compute parity of temp using bit-indexed XOR
parity = expr.bit_xor(
expr.bit_xor(expr.index(temp, 0), expr.index(temp, 1)),
expr.index(temp, 2),
)

# Flip q3 if parity of ~creg is 1
with qc.if_test(parity):
qc.x(3)

qc.measure([0, 1, 2], creg)

qc.draw("mpl")

Output of the previous code cell

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

توصيات