שזירה ארוכת טווח עם מעגלים דינמיים
אומדן שימוש: 4 דקות על מעבד Heron r2. (הערה: זהו אומדן בלבד. זמן הריצה שלך עשוי להשתנות.)
רקע
שזירה ארוכת טווח בין qubits מרוחקים היא אתגר במכשירים עם קישוריות מוגבלת. מדריך זה מראה כיצד מעגלים דינמיים יכולים ליצור שזירה כזו על ידי מימוש שער controlled-X ארוך טווח (LRCX) באמצעות פרוטוקול מבוסס מדידה.
בעקבות הגישה של Elisa Bäumer ועמיתיה ב-1, השיטה משתמשת במדידה בתוך המעגל והזנה קדימה כדי להשיג שערים בעומק קבוע ללא קשר למרחק בין ה-qubits. היא יוצרת זוגות Bell ביניים, מודדת qubit אחד מכל זוג, ומפעילה שערים מותנים קלאסית כדי להפיץ את השזירה על פני ההתקן. זה נמנע משרשראות SWAP ארוכות, ומפחית גם את עומק המעגל וגם את החשיפה לשגיאות שערים דו-qubit.
במחברת זו, אנחנו מתאימים את הפרוטוקול לחומרה של IBM Quantum® ומרחיבים אותו להפעלת פעולות LRCX מרובות במקביל, מה שמאפשר לנו לחקור כיצד הביצועים משתנים עם מספר הפעולות המותנות המתבצעות בו-זמנית.
דרישות
לפני התחלת מדריך זה, ודא שיש לך את המותקנים הבאים:
- Qiskit SDK v2.0 או מאוחר יותר, עם תמיכה ב-visualization
- Qiskit Runtime (
pip install qiskit-ibm-runtime) v0.37 או מאוחר יותר
הגדרה
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime
from qiskit import QuantumCircuit, QuantumRegister, ClassicalRegister
from qiskit.circuit.classical import expr
from qiskit.transpiler import generate_preset_pass_manager
from qiskit.visualization import plot_circuit_layout
from qiskit_ibm_runtime import (
QiskitRuntimeService,
Batch,
SamplerV2 as Sampler,
)
import matplotlib.pyplot as plt
import numpy as np
שלב 1: מיפוי קלטים קלאסיים לבעיה קוונטית
כעת נממש שער CNOT ארוך טווח בין שני qubits מרוחקים, בעקבות בניית המעגל הדינמי המוצגת להלן (מותאם מאיור 1a בהפני ה 1). הרעיון המרכזי הוא להשתמש ב-"אפיק" של qubits עזר, מאותחלים ל-, כדי לתווך טלפורטציית שער ארוכת טווח.

כפי שמודגם באיור, התהליך פועל באופן הבא:
- הכנת שרשרת של זוגות Bell המחברת את ה-qubits של הבקרה והמטרה דרך עזרים ביניים.
- ביצוע מדידות Bell בין qubits שכנים לא-משוזרים, החלפת שזירה צעד אחר צעד עד שהבקרה והמטרה חולקות זוג Bell.
- שימוש בזוג Bell זה לטלפורטציית שער, הפיכת CNOT מקומי ל-CNOT ארוך טווח דטרמיניסטי בעומק קבוע.
גישה זו מחליפה שרשראות SWAP ארוכות בפרוטוקול בעומק קבוע, מפחיתה חשיפה לשגיאות שערים דו-qubit והופכת את הפעולה לסקלבילית עם גודל ההתקן.
בהמשך, נעבור תחילה על המימוש הדינמי של מעגל LRCX. בסוף, נספק גם מימוש מבוסס יוניטרי להשוואה, כדי להדגיש את היתרונות של מעגלים דינמיים בהקשר ז ה.
(i) אתחול מעגל
נתחיל עם בעיה קוונטית פשוטה שתשמש כבסיס להשוואה. באופן ספציפי, נאתחל מעגל עם qubit בקרה באינדקס 0 ונחיל עליו שער Hadamard. זה מייצר מצב סופרפוזיציה שכאשר הוא מלווה בפעולת controlled-X, מייצר מצב Bell בין ה-qubits של הבקרה והמטרה.
בשלב זה, אנחנו עדיין לא בונים את ה-LRCX עצמו. במקום זאת, המטרה שלנו היא להגדיר מעגל התחלתי ברור ומינימלי המדגיש את תפקיד ה-LRCX. בשלב 2, נראה כיצד ניתן למימש את ה-LRCX כאופטימיזציה באמצעות מעגלים דינמיים, ונשווה את הביצועים שלו ליוניטרי שווה ערך. חשוב לציין שפרוטוקול ה-LRCX ניתן ליישום על כל מעגל התחלתי. כאן אנחנו משתמשים בהגדרת Hadamard פשוטה זו לצורך הבהרה.
distance = 6 # The distance of the CNOT gate, with the convention that a distance of zero is a nearest-neighbor CNOT.
def initialize_circuit(distance):
assert distance >= 0
control = 0 # control qubit
n = distance # number of qubits between target and control
qr = QuantumRegister(
n + 2, name="q"
) # Circuit with n qubits between control and target
cr = ClassicalRegister(
2, name="cr"
) # Classical register for measuring control and target qubits
k = int(n / 2) # Number of Bell States to be used
allcr = [cr]
if (
distance > 1
): # This classical register will be used to store ZZ measurements. It is only used for long-range CX gates with distance > 1
c1 = ClassicalRegister(
k, name="c1"
) # Classical register needed for post processing
allcr.append(c1)
if (
distance > 0
): # This classical register will be used to store XX measurements. It is only used if distance > 0
c2 = ClassicalRegister(
n - k, name="c2"
) # Classical register needed for post processing
allcr.append(c2)
qc = QuantumCircuit(qr, *allcr, name="CNOT")
# Apply a Hadamard gate to the control qubit such that the long-range CNOT gate will prepare a Bell state (|00> + |11>)/sqrt(2)
qc.h(control)
return qc
qc = initialize_circuit(distance)
qc.draw(fold=-1, output="mpl", scale=0.5)
שלב 2: אופטימיזציה של הבעיה להפעלה על חומרה קוונטית
בשלב זה, נראה כיצד לבנות את מעגל LRCX באמצעות מעגלים דינמיים. המטרה היא לבצע אופטימיזציה של המעגל להפעלה על חומרה על ידי הפחתת העומק בהשוואה למימוש יוניטרי טהור. כדי להמחיש את היתרונות, נציג את הבנייה הדינמית של LRCX ואת היוניטרי השווה ערך שלו, ומאוחר יותר נשווה את הביצועים שלהם לאחר transpilation. חשוב לציין שבעוד שכאן אנחנו מיישמים את ה-LRCX על בעיה פשוטה מאותחלת ב-Hadamard, הפרוטוקול ניתן ליישום על כל מעגל שבו נדרש CNOT ארוך טווח.
(ii) הכנת זוגות Bell
נתחיל ביצירת שרשרת של זוגות Bell לאורך הנתיב בין ה-qubits של הבקרה והמטרה. אם המרחק הוא אי-זוגי, נחיל תחילה CNOT מהבקרה לשכן שלה, שהוא ה-CNOT שיטלפורט. עבור מרחק זוגי, CNOT זה יוחל לאחר שלב הכנת זוגות ה-Bell. שרשרת זוגות ה-Bell משזרת אז זוגות עוקבים של qubits, מקימה את המשאב הדרוש להעברת מידע הבקרה על פני ההתקן.
# Determine where to start the Bell pair chain and add an extra CNOT when n is odd
def check_even(n: int) -> int:
"""Return 1 if n is even, else 2."""
return 1 if n % 2 == 0 else 2
def prepare_bell_pairs(qc, add_barriers=True):
n = qc.num_qubits - 2 # number of qubits between target and control
k = int(n / 2)
if add_barriers:
qc.barrier()
x0 = check_even(n)
if n % 2 != 0:
qc.cx(0, 1)
# Create k Bell pairs
for i in range(k):
qc.h(x0 + 2 * i)
qc.cx(x0 + 2 * i, x0 + 2 * i + 1)
return qc
qc = prepare_bell_pairs(qc)
qc.draw(output="mpl", fold=-1, scale=0.5)
(iii) מדידת זוגות qubits שכנים בבסיס Bell
לאחר מכן, נמדוד qubits שכנים לא-משוזרים בבסיס Bell (מדידות דו-qubit של ו-). זה יוצר זוג Bell ארוך טווח בין qubit המטרה וה-qubit הסמוך לבקרה (עד לתיקוני Pauli, שיוטמעו באמצעות הזנה קדימה בשלב הבא). במקביל, נממש את המדידה המשזרת שמטלפורטת את שער CNOT לפעול על qubit המטרה המיועד.
def measure_bell_basis(qc, add_barriers=True):
n = qc.num_qubits - 2 # number of qubits between target and control
k = int(n / 2)
if n > 1:
_, c1, c2 = qc.cregs
elif n > 0:
_, c2 = qc.cregs
# Determine where to start the Bell pair chain and add an extra CNOT when n is odd
x0 = 1 if n % 2 == 0 else 2
# Entangling layer that implements the Bell measurement (and additionally adds the CNOT to be teleported, if n is even)
for i in range(k + 1):
qc.cx(x0 - 1 + 2 * i, x0 + 2 * i)
for i in range(1, k + x0):
if i == 1:
qc.h(2 * i + 1 - x0)
else:
qc.h(2 * i + 1 - x0)
if add_barriers:
qc.barrier()
# Map the ZZ measurements onto classical register c1
for i in range(k):
if i == 0:
qc.measure(2 * i + x0, c1[i])
else:
qc.measure(2 * i + x0, c1[i])
# Map the XX measurements onto classical register c2
for i in range(1, k + x0):
if i == 1:
qc.measure(2 * i + 1 - x0, c2[i - 1])
else:
qc.measure(2 * i + 1 - x0, c2[i - 1])
return qc
qc = measure_bell_basis(qc)
qc.draw(output="mpl", fold=-1, scale=0.5)
(iv) לאחר מכן, החלת תיקוני הזנה קדימה לתיקון אופרטורי תוצר לוואי של Pauli
מדידות בסיס Bell מכניסות תוצרי לוואי של Pauli שיש לתקן באמצעות התוצאות המתועדות. זה נעשה בשני שלבים. ראשית, אנחנו צריכים לחשב את הזוגיות של כל מדידות ה-, שמשמשת אז להחלת מותנית של שער על qubit המטרה. באופן דומה, הזוגיות של מדידות ה- מחושבת ומשמשת להחלת מותנית של שער על qubit הבקרה.
עם מסגרת הביטוי הקלאסי החדשה ב-Qiskit, זוגיות אלה ניתנות לחישוב ישירות בשכבת העיבוד הקלאסית של המעגל. במקום להחיל רצף של שערים מותנים בודדים עבור כל ביט מדידה, נוכל לבנות ביטוי קלאסי יחיד המייצג את ה-XOR (זוגיות) של כל תוצאות המדידה הרלוונטיות. ביטוי זה משמש אז כתנאי בבלוק if_test יחיד, מה שמאפשר להחיל את שערי התיקון בעומק קבוע. גישה זו גם מפשטת את המעגל וגם מבטיחה שתיקוני ההזנה קדימה לא מכניסים עיכוב נוסף מיותר.
def apply_ffwd_corrections(qc):
control = 0 # control qubit
target = qc.num_qubits - 1 # target qubit
n = qc.num_qubits - 2 # number of qubits between target and control
k = int(n / 2)
x0 = check_even(n)
if n > 1:
_, c1, c2 = qc.cregs
elif n > 0:
_, c2 = qc.cregs
# First, let's compute the parity of all ZZ measurements
for i in range(k):
if i == 0:
parity_ZZ = expr.lift(
c1[i]
) # Store the value of the first ZZ measurement in parity_ZZ
else:
parity_ZZ = expr.bit_xor(
c1[i], parity_ZZ
) # Successively compute the parity via XOR operations
for i in range(1, k + x0):
if i == 1:
parity_XX = expr.lift(
c2[i - 1]
) # Store the value of the first XX measurement in parity_XX
else:
parity_XX = expr.bit_xor(
c2[i - 1], parity_XX
) # Successively compute the parity via XOR operations
if n > 0:
with qc.if_test(parity_XX):
qc.z(control)
if n > 1:
with qc.if_test(parity_ZZ):
qc.x(target)
return qc
qc = apply_ffwd_corrections(qc)
qc.draw(output="mpl", fold=-1, scale=0.5)
(v) לבסוף, מדידת qubits הבקרה והמטרה
נגדיר פונקציית עזר המאפשרת מדידה של qubits הבקרה והמטרה בבסיסים , , או . לאימות מצב Bell , ערכי הציפייה של ו- שניהם צריכים להיות , מכיוון שהם מייצבים של המצב. מדידת נתמכת גם כאן ותשמש להלן בעת חישוב הנאמנות.
def measure_in_basis(qc, basis="XX", add_barrier=True):
control = 0 # control qubit
target = qc.num_qubits - 1 # target qubit
assert basis in ["XX", "YY", "ZZ"]
qc = (
qc.copy()
) # We copy the circuit because we want to measure in different bases
cr = qc.cregs[0]
if add_barrier:
qc.barrier()
if basis == "XX":
qc.h(control)
qc.h(target)
elif basis == "YY":
qc.sdg(control)
qc.sdg(target)
qc.h(control)
qc.h(target)
qc.measure(control, cr[0])
qc.measure(target, cr[1])
return qc
qc_YY = measure_in_basis(qc.copy(), basis="YY")
qc_YY.draw(
output="mpl", fold=-1, scale=0.5
) # Circuit for measuring in the YY basis