ניסוי בסדר גודל utility I
Tamiya Onodera (5 ביולי 2024)
הורד את ה-pdf של ההרצאה המקורית. שים לב שחלק מקטעי הקוד עשויים להפוך למיושנים, שכן מדובר בתמונות סטטיות.
זמן QPU משוער להרצת ניסוי זה ה וא 45 שניות.
1. מבוא למאמר ה-utility
בשיעור זה, אנו מריצים מעגל בסדר גודל של utility, המופיע במה שאנו מכנים בצורה לא רשמית "מאמר ה-utility", שפורסם ב-Nature כרך 618, 15 ביוני 2023. המאמר עוסק באבולוציית הזמן של מודל איזינג דו-ממדי בשדה רוחבי. בפרט, הם בוחנים את דינמיקת הזמן של ההמילטוניאן,
שבו הוא הצימוד בין ספינים שכנים עם ו- הוא השדה הרוחבי הגלובלי. הם מדמים את דינמיקת הספין ממצב התחלתי באמצעות פירוק טרוטר מסדר ראשון של אופרטור האבולוציה בזמן,
שבו זמן האבולוציה מדוּסקרת ל- צעדי טרוטר, ו- ו- הם שערי סיבוב ו-, בהתאמה.
הם הריצו ניסויים על מעבד Eagle של IBM Quantum®, שהוא מכשיר בעל 127 Qubit עם קישוריות heavy-hex, תוך הפעלת אינטראקציות על כל ה-Qubit ואינטראקציות על כל הצלעות של מפת הצימוד. שים לב שלא ניתן להפעיל את כל אינטראקציות ה- בו-זמנית בשל "תלות בנתונים". לפיכך, הם צובעים את מפת הצימוד כדי לקבץ אותן לשכבות. אלו שבאותה שכבה מקבלות את אותו צבע, וניתן להפעילן במקביל.
בנוסף, לפשטות ניסויית, הם התמקדו במקרה .
התרומה החדשנית של המאמר היא שהם בנו מעגלים קוונטיים בסדר גודל שמעבר לסימולציית וקטור מצב, הריצו אותם על מחשבים קוונטיים רועשים, והצליחו לחלץ תוצאות אמינות. כלומר, הם הוכיחו את ה-utility של מחשבים קוונטיים רועשים. לשם כך, הם הפעילו אקסטרפולציית אפס-רעש (ZNE) עם הגברת שגיאות הסתברותית (PEA) כדי לצמצם שגיאות ממכשירים רועשים.
מאז, אנו מכנים ניסויים ומעגלים כאלה "בסדר גודל utility".
1.1 המטרה שלך
המטרה שלך בשיעור זה היא לבנות מעגל בסדר גודל utility ולהריץ אותו על מעבד Eagle. מעבר לתחום מחברת זו לחלץ תוצאות אמינות, בחלקו משום ש-PEA הוא תכונה ניסיונית של Qiskit בעת כתיבת שורות אלה, ובחלקו משום שהחלת ZNE עם PEA תיקח זמן לא מועט.
באופן קונקרטי, אתה מתבקש לבנות ולהריץ את המעגל המתאים לאיור 4b של המאמר, ולשרטט את הנקודות "הלא-מוקזזות" בעצמך. כפי שאתה רואה, מדובר במעגל של 127 Qubit 60 שכבות (20 צעדי טרוטר) עם כאובזרבבל.
נשמע מאתגר? אל דאגה. שלושת השיעורים האחרונים של קורס זה מספקים אבני דרך. להתחלה, נדגים ניסוי בסדר גודל קטן יותר — בניה והרצה על מכשיר מדומה של מעגל 27 Qubit 6 שכבות (2 צעדי טרוטר) עם כאובזרבבל.
זה הכל למבוא. בואו נצא להרפתקת utility-scale!
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-aer qiskit-ibm-runtime rustworkx
import qiskit
qiskit.__version__
'2.0.2'
#!pip install qiskit_ibm_runtime
#!pip install qiskit_aer
import matplotlib.pyplot as plt
import numpy as np
import rustworkx as rx
from qiskit import QuantumCircuit, transpile
from qiskit.circuit import Parameter
from qiskit.circuit.library import YGate
from qiskit.quantum_info import SparsePauliOp
from qiskit_ibm_runtime import (
QiskitRuntimeService,
fake_provider,
EstimatorV2 as Estimator,
)
from qiskit_aer import AerSimulator
service = QiskitRuntimeService()
2. הכנה
2.1 בניית RZZ(- / 2)
ראשית, שים לב שה-Gate RZZ בכלל דורש שני Gates מסוג .
from qiskit.circuit.library import RZZGate
θ_h = Parameter("$\\theta_h$")
qc1 = QuantumCircuit(2)
qc1.append(RZZGate(θ_h), [0, 1])
qc1.decompose(reps=1).draw("mpl")
כאמור לעיל, אנו מתמקדים ב-Gate RZZ עם זווית ספציפית, - / 2, עבור ניסוי זה. כפי שמוצג במאמר, ניתן לממש זאת עם Gate אחד בלבד.
qc2 = QuantumCircuit(2)
qc2.sdg([0, 1])
qc2.append(YGate().power(1 / 2), [1])
qc2.cx(0, 1)
qc2.append(YGate().power(1 / 2).adjoint(), [1])
qc2.draw("mpl")
אנו מגדירים Gate במונחים של Circuit זה לעיון עתידי.
rzz = qc2.to_gate(label="RZZ")
בואו נשתמש בשימוש אקראי ב-rzz החדש שהגדרנו.
qc3 = QuantumCircuit(3)
qc3.append(rzz, [0, 1])
qc3.append(rzz, [0, 2])
display(qc3.draw("mpl"))
# display(qc.decompose(reps=1).draw("mpl"))
לפני שנמשיך, בואו נאמת את השקילות הלוגית של qc1 (ה-Gate RZZ) עבור -pi/2 ו-Gate rzz או qc2 החדש שהגדרנו:
from qiskit.quantum_info import Operator
op1 = Operator(qc1.assign_parameters([-np.pi / 2]))
op2 = Operator(qc2)
op1.equiv(op2)
True
2.2 צביעת מפת הצימוד
בואו נבחן כיצד אנו צובעים את מפת הצימוד של Backend. זה נדרש כדי לקבץ אינטראקציות לשכבות.
להתחלה, בואו נמחיש את מפת הצימוד של Backend. שים לב שמפות הצימוד הן בצורת heavy-hexagonal לכל מכשירי IBM Quantum הנוכחיים.
backend = service.least_busy(operational=True, simulator=False)
backend.coupling_map.draw()

לצביעת מפת הצימוד, אנו משתמשים ב-rustworkx, שהוא חבילת Python לעבודה עם גרפים ורשתות מורכבות. היא מספקת מספר אלגוריתמי צביעה, שכולם היוריסטיים ולכן אינם מובטחים למצוא צביעה מינימלית.
עם זאת, מאחר שגרפי heavy-hex הם דו-צדדיים, אנו בוחרים ב-graph_bipartite_edge_color, שאמור למצוא צביעה מינימלית עבור גרפים אלה.
def color_coupling_map(backend):
graph = backend.coupling_map.graph
undirected_graph = graph.to_undirected(multigraph=False)
edge_color_map = rx.graph_bipartite_edge_color(undirected_graph)
if edge_color_map is None:
edge_color_map = rx.graph_greedy_edge_color(undirected_graph)
# build a map from color to a list of edges
edge_index_map = undirected_graph.edge_index_map()
color_edges_map = {color: [] for color in edge_color_map.values()}
for edge_index, color in edge_color_map.items():
color_edges_map[color].append(
(edge_index_map[edge_index][0], edge_index_map[edge_index][1])
)
return edge_color_map, color_edges_map
גרפי heavy-hexagonal אמורים להיצבע בשלושה צבעים. בואו נבדוק זאת עבור מפת הצימוד לעיל.
edge_color_map, color_edges_map = color_coupling_map(backend)
print(
f"{backend.name}, {backend.num_qubits}-qubit device, {len(color_edges_map.keys())} colors assigned."
)
ibm_strasbourg, 127-qubit device, 3 colors assigned.
אכן!
לכיף, בואו נצבע את מפת הצימוד לפי הצביעה שהתקבלה, תוך שימוש בתכונת הוויזואליזציה של rustworkx.
color_str_map = {0: "green", 1: "red", 2: "blue"}
undirected_graph = backend.coupling_map.graph.to_undirected(multigraph=False)
for i in undirected_graph.edge_indices():
undirected_graph.get_edge_data_by_index(i)["color"] = color_str_map[
edge_color_map[i]
]
rx.visualization.graphviz_draw(
undirected_graph, method="neato", edge_attr_fn=lambda edge: {"color": edge["color"]}
)

3. פתרון אבולוציית הזמן של מודל איזינג 2D באמצעות טרוטר.
בואו נגדיר שגרה לבניית Circuit של מאמר ה-utility לאבולוציית הזמן של מודל איזינג דו-ממדי. השגרה מקבלת שלושה פרמטרים: Backend, מספר שלם המציין את מספר צעדי הטרוטר, וערך בוליאני השולט בהוספת מחסומים.
def get_utility_circuit(backend, num_steps: int, barrier: bool = False):
num_qubits = backend.num_qubits
_, color_edges_map = color_coupling_map(backend)
θ_h = Parameter("$\\theta_h$")
qc = QuantumCircuit(num_qubits)
for i in range(num_steps):
qc.rx(θ_h, range(num_qubits))
for _, edge_list in color_edges_map.items():
for edge in edge_list:
qc.append(rzz, edge)
if barrier:
qc.barrier()
return qc
שים לב שכבר ביצענו ידנית את מיפוי ה-Qubit והניתוב עבור ה-Circuit שנבנה. לכן, כשנבצע Transpile של ה-Circuit מאוחר יותר, אנו לא נבקש (ו__לא כדאי__ לבקש) מה-Transpiler לבצע מיפוי ניתוב של Qubit. כפי שתראה בקרוב, נפעיל אותו עם רמת אופטימיזציה 1 ושיטת layout "trivial".
לאחר מכן, אנו מגדירים שגרה פשוטה לקבלת מידע על ה-Circuit שנבנה לבדיקה מהירה.
def get_circuit_info(qc: QuantumCircuit, reps: int = 0):
qc0 = qc.decompose(reps=reps)
return (
f"{qc0.num_qubits} qubits × {qc0.depth(lambda x: x.operation.num_qubits == 2)} layers ({qc0.depth()}-depth)"
+ ", "
+ f"""Gate breakdown: {", ".join([f"{k.upper()} {v}" for k, v in qc0.count_ops().items()])}"""
)
בואו נתרגל שגרות אלה. אמור להופיע Circuit של 27 Qubit 15 שכבות (5 צעדי טרוטר). מאחר שלמכשיר המדומה יש 28 צלעות, צריכים להיות 28*5 שערי שזירה.
backend = fake_provider.FakeTorontoV2()
num_steps = 5
qc = get_utility_circuit(backend, num_steps, True)
display(qc.draw(output="mpl", fold=-1))
print(get_circuit_info(qc, reps=0))
print(get_circuit_info(qc, reps=1))

27 qubits × 15 layers (20-depth), Gate breakdown: CIRCUIT-165 140, RX 135, BARRIER 5
27 qubits × 15 layers (60-depth), Gate breakdown: SDG 280, UNITARY 280, CX 140, R 135, BARRIER 5
4. פתרון גרסת ה-27-Qubit של הבעיה.
כעת נדגים גרסה קטנה יותר של ניסוי ה-Utility. נבנה Circuit בגודל 27 Qubit 6 שכבות (2 צעדי Trotter) עם כ-Observable, ונריץ אותו גם על AerSimulator וגם על מכשיר מדומה.
כמובן, אנחנו עוקבים אחר תהליך העבודה בן ארבעת השלבים שלנו, "Qiskit pattern", שמורכב מ-Map, Optimize, Execute ו-Post-Process. ביתר פירוט:
- ממפים קלטים קלאסיים לחישוב קוו נטי.
- מייעלים Circuit לחישוב קוונטי.
- מריצים Circuit בעזרת primitives.
- מעבדים לאחר הרצה ומחזירים תוצאות בפורמט קלאסי.
בהמשך, יש לנו את שלב ה-Map ליצירת Circuit לניסוי בקנה מידה קטן יותר. לאחר מכן יש סט אחד של Optimize ו-Execute עבור AerSimulator ועוד אחד עבור מכשיר מדומה. לבסוף, יש לנו את שלב ה-Post-Process לציור התוצאות.
4.1 שלב 1: Map
backend = fake_provider.FakeTorontoV2() # a 27 qubit fake device.
num_steps = 2
qc = get_utility_circuit(backend, num_steps)
obs = SparsePauliOp.from_sparse_list(
[("Z", [13], 1)], num_qubits=backend.num_qubits
) # Falcon
angles = [
0,
0.1,
0.2,
0.3,
0.4,
0.5,
0.6,
0.7,
0.8,
1.0,
np.pi / 2,
] # We try 11 angles for theta_h.