הזנה קדמית קלאסית ובקרת זרימה (מעגלים דינמיים)
Package versions
הקוד בדף זה פותח תוך שימוש בדרישות הבאות. אנחנו ממליצים להשתמש בגרסאות אלו או בגרסאות חדשות יותר.
qiskit[all]~=2.4.0
מעגלים דינמיים הם כלים רבי עוצמה שבהם אפשר למדוד qubits באמצע ביצוע מעגל קוונטי, ואז לבצע פעולות לוגיקה קלאסית בתוך המעגל, בהתבסס על תוצאות אותן מדידות אמצע-מעגל. תהליך זה ידוע גם בשם הזנה קדמית קלאסית. למרות שאלו עדיין ימיה הראשונים של ההבנה כיצד לנצל בצורה הטובה ביותר את המעגלים הדינמיים, קהילת המחקר הקוונטי כבר זיהתה מספר מקרי שימוש, כגון הבאים:
- הכנת מצב קוונטי יעילה, כגון מצב GHZ, מצב W (למידע נוסף על מצב W, ראה גם "State preparation by shallow circuits using feed forward"), ומחלקה רחבה של מצבי מכפלת מטריצה
- שזירה ארוכת טווח יעילה בין qubits על אותו שבב באמצעות מעגלים רדודים
- דגימה יעילה של מעגלים דמויי IQP
Qiskit תומך בארבעה מבני זרימת בקרה להזנה קדמית קלאסית, כל אחד מיושם כמתודה על QuantumCircuit. המבנים והמתודות המקבילות להם הם:
- משפט If -
QuantumCircuit.if_test - משפט Switch -
QuantumCircuit.switch - לולאת For -
QuantumCircuit.for_loop - לולאת While -
QuantumCircuit.while_loop
כל אחת מהמתודות הללו מחזירה מנהל הקשר ומשמשת בדרך כלל במשפט with. שאר המדריך מסביר כל אחד ממבנים אלה וכיצד להשתמש בהם.
ישנן כמה מגבלות של פעולות הזנה קדמית קלאסית וזרימת בקרה על חומרה קוונטית שעשויות להשפיע על התוכנית שלך. למידע נוסף, ראה ביצוע מעגלים דינמיים.
משפט if
משפט ה-if משמש לביצוע פעולות באופן מותנה בהתבסס על ערכו של סיבית קלאסי או רגיסטר.
בדוגמה הבאה, אנחנו מיישמים Gate הדמארד על qubit ומודדים אותו. אם התוצאה היא 1, אז אנחנו מיישמים Gate X על ה-Qubit, מה שהאפקט שלו הוא היפוכו בחזרה למצב 0. לאחר מכן אנחנו מודדים את ה-Qubit שוב. תוצאת המדידה המתקבלת צריכה להיות 0 עם הסתברות של 100%.
# Added by doQumentation — required packages for this notebook
!pip install -q qiskit
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}
ניתן לתת למשפט ה-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}
משפט Switch
משפט ה-switch משמש לבחירת פעולות בהתבסס על ערכו של סיבית קלאסי או רגיסטר. הוא דומה למשפט if, אך ניתן לציין מקרים נוספים עבור לוגיקת ההסתעפות. הדוגמה הבאה מיישמת Gate הדמארד על qubit ומודדת אותו. אם התוצאה היא 0, מיישמים Gate X על ה-Qubit, ואם התוצאה היא 1, מיישמים Gate 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}
מכיוון שהדוגמה למעלה השתמשה בסיבית קלאסית בודדת, היו רק שני מקרים אפשריים, ולכן ניתן היה להשיג את אותה תוצאה באמצעות משפט if-else. משפט ה-switch שימושי בעיקר כאשר מסתעפים על ערכו של רגיסטר קלאסי המורכב ממספר סיביות. הדוגמה הבאה מראה כיצד לבנות מקרה ברירת מחדל, אשר מבוצע אם אף אחד מהמקרים הקודמים אינו תואם. שים לב שבמשפט switch, רק אחד מהבלוקים מבוצע. אין fallthrough.
הדוגמה הבאה מיישמת Gates הדמארד על שני qubits ומודדת אותם. אם התוצאה היא 00 או 11, מיישמים Gate Z על ה-Qubit השלישי. אם התוצאה היא 01, מיישמים Gate Y. אם אף אחד מהמקרים הקודמים לא תאם, מיישמים Gate X. לבסוף, מודדים את ה-Qubit השלישי.
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}
לולאת For
לולאת for משמשת לאיטרציה על רצף של ערכים קלאסיים וביצוע פעולות מסוימות בכל איטרציה.
הדוגמה הבאה משתמשת בלולאת for להחלת 5 Gates X על qubit ולאחר מכן מודדת אותו. מכיוון שהיא מבצעת מספר אי-זוגי של Gates X, האפקט הכולל הוא להפוך את ה-Qubit ממצב 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}
לולאת While
לולאת while משמשת לחזרה על הוראות כל עוד תנאי מסוים מתקיים.
הדוגמה הבאה מיישמת Gates הדמארד על שני qubits ומודדת אותם. לאחר מכן היא יוצרת לולאת 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}
ביטויים קלאסיים
מודול הביטויים הקלאסיים של Qiskit qiskit.circuit.classical מכיל ייצוג חקרני של פעולות זמן ריצה על ערכים קלאסיים במהלך ביצוע מעגל.
הדוגמה הבאה מראה שניתן להשתמש בחישוב הזוגיות ליצירת מצב 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)
Store
ניתן להשתמש בהוראת store כדי לשמור את תוצאת ביטוי קלאסי, אם ביטוי זה ישמש שוב ושוב. פעולות מקביליות אוטומטית, מה שהופך את הקוד ליעיל הרבה יותר בזמן ריצה.
לדוגמה, יותר טבעי ויעיל יותר בזמן ריצה לכתוב , כאשר , מאשר . הראשון מחשב את השלילה בשלב מקבילי יחיד לפני שרשרת ה-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")
הצעדים הבאים
- למד כיצד ליישם פירוק דינמי מדויק באמצעות stretch.
- השתמש ב-ויזואליזציית לוח הזמנים של מעגל לניפוי שגיאות ואופטימיזציה של המעגלים הדינמיים שלך.
- הרץ מעגלים דינמיים.