דלג לתוכן הראשי

הזנה קדמית קלאסית ובקרת זרימה

גרסאות חבילות

הקוד בדף זה פותח תוך שימוש בדרישות הבאות. אנו ממליצים להשתמש בגרסאות אלו או בגרסאות חדשות יותר.

qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
מעגלים דינמיים זמינים כעת בכל ה-Backends

הגרסה החדשה של מעגלים דינמיים זמינה כעת לכל המשתמשים בכל ה-Backends. כעת תוכל להריץ מעגלים דינמיים בקנה מידה שימושי. ראה את ההכרזה לפרטים נוספים.

מעגלים דינמיים הם כלים רבי עוצמה שבהם אפשר למדוד Qubits באמצע ביצוע מעגל קוונטי, ואז לבצע פעולות לוגיקה קלאסית בתוך המעגל, בהתבסס על תוצאות אותן מדידות אמצע-מעגל. תהליך זה ידוע גם בשם הזנה קדמית קלאסית. למרות שאלו עדיין ימיה הראשונים של ההבנה כיצד לנצל בצורה הטובה ביותר את המעגלים הדינמיים, קהילת המחקר הקוונטי כבר זיהתה מספר מקרי שימוש, כגון הבאים:

השיפורים שמביאים המעגלים הדינמיים, לעומת זאת, כרוכים בפשרות. מדידות אמצע-מעגל ופעולות קלאסיות בדרך כלל דורשות זמן ביצוע ארוך יותר מ-Gates דו-Qubit, ועלייה זו בזמן עשויה לבטל את היתרונות של עומק מעגל מצומצם. לכן, קיצור משך מדידת אמצע-המעגל הוא תחום מיקוד לשיפור כאשר IBM Quantum® משחררת את הגרסה החדשה של מעגלים דינמיים.

מפרט OpenQASM 3 מגדיר מספר מבני בקרת זרימה, אך Qiskit Runtime תומך כרגע רק במשפט if המותנה. ב-Qiskit SDK, זה מתאים לשיטת if_test ב-QuantumCircuit. שיטה זו מחזירה מנהל הקשר ומשמשת בדרך כלל במשפט with. מדריך זה מתאר כיצד להשתמש במשפט מותנה זה.

הערה

דוגמאות הקוד במדריך זה משתמשות בהוראת המדידה הסטנדרטית למדידות אמצע-מעגל. עם זאת, מומלץ להשתמש בהוראת MidCircuitMeasure במקום, אם ה-Backend תומך בה. ראה את תיעוד מדידות אמצע-מעגל לפרטים.

משפט if

משפט ה-if משמש לביצוע פעולות באופן מותנה בהתבסס על ערכו של סיבית קלאסי או רגיסטר.

בדוגמה הבאה, אנו מיישמים Gate הדמארד על Qubit ומודדים אותו. אם התוצאה היא 1, אז אנו מיישמים Gate 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 אינו מבוצע.

בדוגמה הבאה, אנו מאתחלים רגיסטרים עם שני Qubits ושני סיביות קלאסיות. אנו מיישמים Gate הדמארד על ה-Qubit הראשון ומודדים אותו. אם התוצאה היא 1, אז אנו מיישמים Gate הדמארד על ה-Qubit השני; אחרת, אנו מיישמים Gate X על ה-Qubit השני. לבסוף, אנו מודדים גם את ה-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

בנוסף לתנאי על סיבית קלאסי בודד, ניתן גם להתנות על ערכו של רגיסטר קלאסי המורכב ממספר סיביות.

בדוגמה הבאה, אנו מיישמים Gates הדמארד על שני Qubits ומודדים אותם. אם התוצאה היא 01, כלומר ה-Qubit הראשון הוא 1 וה-Qubit השני הוא 0, אז אנו מיישמים Gate 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 סמוכים. לאחר מכן, חבר את הזוגות הללו יחד באמצעות שכבה של Gates CNOT בין הזוגות. לאחר מכן מודדים את ה-Qubit היעד של כל Gates CNOT הקודמים ומאפסים כל Qubit מדוד למצב 0\vert 0 \rangle. מיישמים XX על כל אתר שלא נמדד שעבורו זוגיות כל הסיביות הקודמות היא אי-זוגית. לבסוף, Gates 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) משמש כפורמט קלט להעברת מעגלים ל-primitives של Qiskit Runtime, רק הוראות שניתן לטעון ל-Qiskit נתמכות. פעולות קלאסיות, לדוגמה, אינן נתמכות מכיוון שלא ניתן לטעון אותן ל-Qiskit. ראה ייבוא תוכנית OpenQASM 3 ל-Qiskit למידע נוסף.

  • ההוראות for, while ו-switch אינן נתמכות.

שימוש במעגלים דינמיים עם Estimator

מכיוון ש-Estimator אינו תומך במעגלים דינמיים, תוכל להשתמש ב-Sampler ולבנות את מעגלי המדידה שלך. לחלופין, תוכל להשתמש ב-primitive של Executor, שתומך במעגלים דינמיים.

כדי לשחזר את התנהגות ה-Estimator, פעל לפי התהליך הבא:

  1. קבץ את האיברים של כל ה-observables לחלוקה. ניתן לעשות זאת באמצעות ה-API של PauliList, לדוגמה.
    הערה

    תוכל להשתמש בתכונת ה-primitive BitArray לחישוב ערכי הציפייה של ה-observables שסופקו.

  2. הרץ מעגל שינוי בסיס אחד לכל חלוקה (כל שינוי הבסיס שנדרש לכל חלוקה). ראה את מודול measurement_bases של תוסף שירות בסיסי המדידה למידע נוסף. התחל לעבוד עם השירותים.
  3. חבר בחזרה את התוצאות לכל חלוקה.

הצעדים הבאים

המלצות