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

הזנה קדמית קלאסית ובקרת זרימה (מעגלים דינמיים)

Package versions

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

qiskit[all]~=2.4.0

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

Qiskit תומך בארבעה מבני זרימת בקרה להזנה קדמית קלאסית, כל אחד מיושם כמתודה על QuantumCircuit. המבנים והמתודות המקבילות להם הם:

כל אחת מהמתודות הללו מחזירה מנהל הקשר ומשמשת בדרך כלל במשפט 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}

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

משפט 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}

Output of the previous code cell

מכיוון שהדוגמה למעלה השתמשה בסיבית קלאסית בודדת, היו רק שני מקרים אפשריים, ולכן ניתן היה להשיג את אותה תוצאה באמצעות משפט 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}

Output of the previous code cell

לולאת 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}

Output of the previous code cell

לולאת 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}

Output of the previous code cell

ביטויים קלאסיים

מודול הביטויים הקלאסיים של Qiskit qiskit.circuit.classical מכיל ייצוג חקרני של פעולות זמן ריצה על ערכים קלאסיים במהלך ביצוע מעגל.

הדוגמה הבאה מראה שניתן להשתמש בחישוב הזוגיות ליצירת מצב 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

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

הצעדים הבאים

המלצות