הזנה קדמית קלאסית ובקרת זרימה
גרסאות חבילות
הקוד בדף זה פותח תוך שימוש בדרישות הבאות. אנו ממליצים להשתמש בגרסאות אלו או בגרסאות חדשות יותר.
qiskit[all]~=2.3.0
qiskit-ibm-runtime~=0.43.1
הגרסה החדשה של מעגלים דינ מיים זמינה כעת לכל המשתמשים בכל ה-Backends. כעת תוכל להריץ מעגלים דינמיים בקנה מידה שימושי. ראה את ההכרזה לפרטים נוספים.
מעגלים דינמיים הם כלים רבי עוצמה שבהם אפשר למדוד Qubits באמצע ביצוע מעגל קוונטי, ואז לבצע פעולות לוגיקה קלאסית בתוך המעגל, בהתבסס על תוצאות אותן מדידות אמצע-מעגל. תהליך זה ידוע גם בשם הזנה קדמית קלאסית. למרות שאלו עדיין ימיה הראשונים של ההבנה כיצד לנצל בצורה הטובה ביותר את המעגלים הדינמיים, קהילת המחקר הקוונטי כבר זיהתה מספר מקרי שימוש, כגון הבאים:
- הכנת מצב קוונטי יעילה, כגון מצב GHZ, מצב W, (למידע נוסף על מצב W, ראה גם "State preparation by shallow circuits using feed forward") ומחלקה רחבה של מצבי מכפלת מטריצה
- שזירה ארוכת טווח יעילה בין Qubits על אותו שבב באמצעות מעגלים רדודים
- דגימה יעילה של מעגלים דמויי IQP
השיפורים שמביאים המעגלים הדינמיים, לעומת זאת, כרוכים בפשרות. מדידות אמצע-מעגל ופעולות קלאסיות בדרך כלל דורשות זמן ביצוע ארוך יותר מ-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}
ניתן לתת למשפט ה-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}
בנוסף לתנאי על סיבית קלאסי בודד, ניתן גם להתנות על ערכו של רגיסטר קלאסי המורכב ממספר סיביות.
בדוגמה הבאה, אנו מיישמים 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}
ביטויים קלאסיים
מודול הביטויים הקלאסיים של Qiskit qiskit.circuit.classical מכיל ייצוג חקרני של פעולות זמן ריצה על ערכים קלאסיים במהלך ביצוע מעגל. בשל מגבלות חומרה, רק תנאי QuantumCircuit.if_test() נתמכים כרגע.
הדוגמה הבאה מראה שניתן להשתמש בחישוב הזוגיות ליצירת מצב GHZ בן n-Qubit באמצעות מעגלים דינמיים. ראשית, צור זוגות Bell על Qubits סמוכים. לאחר מכן, חבר את הזוגות הללו יחד באמצעות שכבה של Gates CNOT בין הזוגות. לאחר מכן מודדים את ה-Qubit היעד של כל Gates CNOT הקודמים ומאפסים כל Qubit מדוד למצב . מיישמים על כל אתר שלא נמדד שעבורו זוגיות כל הסיביות הקודמות היא אי-זוגית. לבסוף, 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)
מציאת 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)):
...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) משמש כפורמט קלט להעברת מעגלים ל-primitives של Qiskit Runtime, רק הוראות שניתן לטעון ל-Qiskit נתמכות. פעולות קלאסיות, לדוגמה, אינן נתמכות מכיוון שלא ניתן לטעון אותן ל-Qiskit. ראה ייבוא תוכנית OpenQASM 3 ל-Qiskit למידע נוסף. -
ההוראות
for,whileו-switchאינן נתמכות.
שימוש במעגלים דינמיים עם Estimator
מכיוון ש-Estimator אינו תומך במעגלים דינמיים, תוכל להשתמש ב-Sampler ולבנות את מעגלי המדידה שלך. לחלופין, תוכל להשתמש ב-primitive של Executor, שתומך במעגלים דינמיים.
כדי לשחזר את התנהגות ה-Estimator, פעל לפי התהליך הבא:
- קבץ את האיברים של כל ה-observables לחלוקה. ניתן לעשות זאת באמצעות ה-API של
PauliList, לדוגמה.הערהתוכל להשתמש בתכונת ה-primitive
BitArrayלחישוב ערכי הציפייה של ה-observables שסופקו. - הרץ מעגל שינוי בסיס אחד לכל חלוקה (כל שינוי הבסיס שנדרש לכל חלוקה). ראה את מודול
measurement_basesשל תוסף שירות בסיסי המדידה למידע נוסף. התחל לעבוד עם השירותים. - חבר בחזרה את התוצאות לכל חלוקה.
הצעדים הבאים
- למד כיצד ליישם פירוק דינמי מדויק באמצעות stretch.
- למד על מדידות אמצע-מעגל הקצרות יותר שמצמצמות את זמן המעגל.
- השתמש ב-ויזואליזציית לוח הזמנים של מעגל לניפוי שגיאות ואופטימיזציה של המעגלים הדינמיים שלך.