קומפילציה קוונטית קירובית עבור מעגלי אבולוציית זמן
הערכת שימוש: חמ ש דקות על מעבד Eagle (הערה: זוהי הערכה בלבד. זמן הריצה שלך עשוי להשתנות.)
רקע
מדריך זה מדגים כיצד ליישם קומפילציה קוונטית קירובית באמצעות רשתות טנזור (AQC-Tensor) עם Qiskit כדי לשפר את ביצועי המעגל הקוונטי. אנו מיישמים את AQC-Tensor בהקשר של אבולוציית זמן Trotterized כדי להפחית את עומק המעגל תוך שמירה על דיוק הסימולציה, תוך ביצוע תכנית העבודה של Qiskit להכנת מצב ואופטימיזציה. תלמדו כיצד ליצור מעגל ansatz בעומק נמוך ממעגל Trotter ראשוני, לבצע אופטימיזציה שלו באמצעות רשתות טנזור ולהכין אותו לביצוע על חומרה קוונטית.
המטרה העיקרית היא לדמות אבולוציית זמן עבור Hamiltonian מודל עם עומק מעגל מופחת. זה מושג באמצעות הרחבה של Qiskit בשם AQC-Tensor, qiskit-addon-aqc-tensor, המשתמשת ברשתות טנזור, במיוחד במצבי מכפלת מטריצה (MPS), כדי לדחוס ולבצע אופטימיזציה של המעגל הראשוני. באמצעות התאמות איטרטיביות, מעגל ה-ansatz המדוחס שומר על נאמנות למעגל המקורי תוך שמירה על היתכנות עבור חומרה קוונטית של הדור הקרוב. פרטים נוספים ניתן למצוא בתיעוד המתאים עם דוגמה פשוטה להתחלה.
קומפילציה קוונטית קירובית מועילה במיוחד בסימולציות קוונטיות החורגות מזמני הקוהרנטיות של החומרה, מכיוון שהיא מאפשרת לבצע סימולציות מורכבות בצורה יעילה יותר. מדריך זה מנחה אותך דרך הגדרת תהליך העבודה של AQC-Tensor ב-Qiskit, המכסה אתחול של Hamiltonian, יצירת מעגלי Trotter וטרנספילציה של המעגל המותאם הסופי עבור התקן יעד.
דרישות
לפני שתתחיל במדריך זה, ודא שיש לך את הדברים הבאים מותקנים:
- Qiskit SDK גרסה 1.0 ואילך, עם תמיכה בויזואליזציה
- Qiskit Runtime גרסה 0.22 ואילך (
pip install qiskit-ibm-runtime) - הרחבת AQC-Tensor של Qiskit (
pip install 'qiskit-addon-aqc-tensor[aer,quimb-jax]')
הגדרה
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-addon-aqc-tensor qiskit-addon-utils qiskit-ibm-runtime quimb rustworkx scipy
import numpy as np
import quimb.tensor
import datetime
import matplotlib.pyplot as plt
from scipy.optimize import OptimizeResult, minimize
from qiskit.quantum_info import SparsePauliOp, Pauli
from qiskit.transpiler import CouplingMap
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit import QuantumCircuit
from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
)
from qiskit_addon_aqc_tensor.ansatz_generation import (
generate_ansatz_from_circuit,
)
from qiskit_addon_aqc_tensor.objective import MaximizeStateFidelity
from qiskit_addon_aqc_tensor.simulation.quimb import QuimbSimulator
from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit
from qiskit_addon_aqc_tensor.simulation import compute_overlap
from qiskit_ibm_runtime import QiskitRuntimeService
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from rustworkx.visualization import graphviz_draw
חלק I. דוגמה בקנה מידה קטן
החלק הראשון של מדריך זה משתמש בדוגמה בקנה מידה קטן עם 10 אתרים כדי להמחיש את תהליך המיפוי של בעיית סימולציה קוונטית למעגל קוונטי ניתן לביצוע. כאן נחקור את הדינמיקה של מודל XXZ בן 10 אתרים, מה שמאפשר לנו לבנות ולבצע אופטימיזציה של מעגל קוונטי נוח לניהול לפני הרחבת המערכות.
מודל XXZ נחקר באופן נרחב בפיזיקה לבחינת אינטראקציות ספין ותכונות מגנטיות. אנו מגדירים את ה-Hamiltonian כך שיהיו לו תנאי גבול פתוחים עם אינטראקציות תלויות-אתר בין אתרים שכנים לאורך השרשרת.
Hamiltonian מודל ו-observable
ה-Hamiltonian עבור מודל XXZ בן 10 האתרים שלנו מוגדר כך:
כאשר הוא מקדם אקראי התואם לקצה , ו- הוא מספר האתרים.
על ידי סימולציה של האבולוציה של מערכת זו עם עומק מעגל מופחת, נוכל לקבל תובנות לגבי השימוש ב-AQC-Tensor כדי לדחוס ולבצע אופטימיזציה של מעג לים.
הגדרת ה-Hamiltonian וה-observable
לפני שנמפה את הבעיה שלנו, עלינו להגדיר את מפת הצימוד, ה-Hamiltonian וה-observable עבור מודל XXZ בן 10 האתרים.
# L is the number of sites, also the length of the 1D spin chain
L = 10
# Generate the coupling map
edge_list = [(i - 1, i) for i in range(1, L)]
# Generate an edge-coloring so we can make hw-efficient circuits
even_edges = edge_list[::2]
odd_edges = edge_list[1::2]
coupling_map = CouplingMap(edge_list)
# Generate random coefficients for our XXZ Hamiltonian
np.random.seed(0)
Js = np.random.rand(L - 1) + 0.5 * np.ones(L - 1)
hamiltonian = SparsePauliOp(Pauli("I" * L))
for i, edge in enumerate(even_edges + odd_edges):
hamiltonian += SparsePauliOp.from_sparse_list(
[
("XX", (edge), Js[i] / 2),
("YY", (edge), Js[i] / 2),
("ZZ", (edge), Js[i]),
],
num_qubits=L,
)
# Generate a ZZ observable between the two middle qubits
observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)
print("Hamiltonian:", hamiltonian)
print("Observable:", observable)
graphviz_draw(coupling_map.graph, method="circo")
Hamiltonian: SparsePauliOp(['IIIIIIIIII', 'IIIIIIIIXX', 'IIIIIIIIYY', 'IIIIIIIIZZ', 'IIIIIIXXII', 'IIIIIIYYII', 'IIIIIIZZII', 'IIIIXXIIII', 'IIIIYYIIII', 'IIIIZZIIII', 'IIXXIIIIII', 'IIYYIIIIII', 'IIZZIIIIII', 'XXIIIIIIII', 'YYIIIIIIII', 'ZZIIIIIIII', 'IIIIIIIXXI', 'IIIIIIIYYI', 'IIIIIIIZZI', 'IIIIIXXIII', 'IIIIIYYIII', 'IIIIIZZIII', 'IIIXXIIIII', 'IIIYYIIIII', 'IIIZZIIIII', 'IXXIIIIIII', 'IYYIIIIIII', 'IZZIIIIIII'],
coeffs=[1. +0.j, 0.52440675+0.j, 0.52440675+0.j, 1.0488135 +0.j,
0.60759468+0.j, 0.60759468+0.j, 1.21518937+0.j, 0.55138169+0.j,
0.55138169+0.j, 1.10276338+0.j, 0.52244159+0.j, 0.52244159+0.j,
1.04488318+0.j, 0.4618274 +0.j, 0.4618274 +0.j, 0.9236548 +0.j,
0.57294706+0.j, 0.57294706+0.j, 1.14589411+0.j, 0.46879361+0.j,
0.46879361+0.j, 0.93758721+0.j, 0.6958865 +0.j, 0.6958865 +0.j,
1.391773 +0.j, 0.73183138+0.j, 0.73183138+0.j, 1.46366276+0.j])
Observable: SparsePauliOp(['IIIIZZIIII'],
coeffs=[1.+0.j])
עם ה-Hamiltonian מוגדר, נוכל להמשיך לבנות את המצב הראשוני.
# Generate an initial state
initial_state = QuantumCircuit(L)
for i in range(L):
if i % 2:
initial_state.x(i)
שלב 1: מיפוי קלטים קלאסיים לבעיה קוונטית
כעת לאחר שבנינו את ה-Hamiltonian, המגדיר את אינטראקציות הספין-ספין והשדות המגנטיים החיצוניים המאפיינים את המערכת, נעקוב אחר שלושה שלבים עיקריים בתהליך העבודה של AQC-Tensor:
- יצירת מעגל AQC מותאם: באמצעות Trotterization, אנו מקרבים את האבולוציה הראשונית, אשר לאחר מכן נדחסת כדי להפחית את עומק המעגל.
- יצירת מעגל אבולוציית הזמן הנותר: לכידת האבולוציה עבור הזמן הנותר מעבר למקטע הראשוני.
- שילוב המעגלים: מיזוג מעגל ה-AQC המותאם עם מעגל האבולוציה הנותר למעגל אבולוציית-זמן שלם המוכן לביצוע.
גישה זו יוצרת ansatz בעומק נמוך עבור האבולוציה היעד, התומכת בסימולציה יעילה במסגרת מגבלות החומרה הקוונטית של הדור הקרוב.
קביעת חלק אבולוציית הזמן לסימולציה קלאסית
המטרה שלנו היא לדמות את אבולוציית הזמן של ה-Hamiltonian המודל שהוגדר קודם לכן באמצעות אבולוציית Trotter. כדי להפוך תהליך זה ליעיל עבור חומרה קוונטית, אנו מפצלים את האבולוציה לשני מקטעים:
-
מקטע ראשוני: חלק ראשוני זה של האבולוציה, מ- עד , ניתן לסימולציה עם MPS וניתן "לקמפל" ביעילות באמצעות AQC-Tensor. באמצעות הרחבת AQC-Tensor של Qiskit, אנו יוצרים מעגל מדוחס עבור מקטע זה, המכונה
aqc_target_circuit. מכיוון שמקטע זה יידמה על סימולטור רשת-טנזור, אנו יכולים להרשות לעצמנו להשתמש במספר גבוה יותר של שכבות Trotter מבלי להשפיע על משאבי החומרה באופן משמעותי. אנו קובעיםaqc_target_num_trotter_steps = 32עבור מקטע זה. -
מקטע עוקב: חלק נותר זה של האבולוציה, מ- עד , יבוצע על חומרה קוונטית, המכונה
subsequent_circuit. בהתחשב במגבלות החומרה, אנו שואפים להשתמש בכמה שפחות שכבות Trotter כדי לשמור על עומק מעגל ניתן לניהול. עבור מקטע זה, אנו משתמשים ב-subsequent_num_trotter_steps = 3.
בחירת זמן הפיצול
אנו בוחרים ב- כזמן הפיצול כדי לאזן בין יכולת סימולציה קלאסית והיתכנות חומרה. בתחילת האבולוציה, ההסתבכות במודל XXZ נשארת נמוכה מספיק עבור שיטות קלאסיות כמו MPS כדי לקרב במדויק.
בעת בחירת זמן פיצול, קו מנחה טוב הוא לבחור נקודה שבה ההסתבכות עדיין ניתנת ל ניהול קלאסית אך לוכדת מספיק מהאבולוציה כדי לפשט את החלק המבוצע על החומרה. ניסוי וטעייה עשויים להיות נחוצים כדי למצוא את האיזון הטוב ביותר עבור Hamiltonians שונים.
# Generate the AQC target circuit (initial segment)
aqc_evolution_time = 0.2
aqc_target_num_trotter_steps = 32
aqc_target_circuit = initial_state.copy()
aqc_target_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)
# Generate the subsequent circuit
subsequent_num_trotter_steps = 3
subsequent_evolution_time = 0.2
subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),
time=subsequent_evolution_time,
)
subsequent_circuit.draw("mpl", fold=-1)

כדי לאפשר השוואה משמעותית, ניצור שני מעגלים נוספים:
-
מעגל השוואת AQC: מעגל זה מתפתח עד
aqc_evolution_timeאך משתמש באותו משך צעד Trotter כמו ה-subsequent_circuit. הוא משמש כהשוואה ל-aqc_target_circuit, ומציג את האבולוציה שהיינו צופים מבלי להשתמש במספר מוגבר של צעדי Trotter. נתייחס למעגל זה כ-aqc_comparison_circuit. -
מעגל ייחוס: מעגל זה משמש כקו בסיס כדי להשיג את התוצאה המדויקת. הוא מדמה את האבולוציה המלאה באמצעות רשתות טנזור כדי לחשב את התוצאה המדויקת, ומספק התייחסות להערכת היעילות של AQC-Tensor. נתייחס למעגל זה כ-
reference_circuit.
# Generate the AQC comparison circuit
aqc_comparison_num_trotter_steps = int(
subsequent_num_trotter_steps
/ subsequent_evolution_time
* aqc_evolution_time
)
print(
"Number of Trotter steps for comparison:",
aqc_comparison_num_trotter_steps,
)
aqc_comparison_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),
time=aqc_evolution_time,
)
Number of Trotter steps for comparison: 3
# Generate the reference circuit
evolution_time = 0.4
reps = 200
reference_circuit = initial_state.copy()
reference_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=reps),
time=evolution_time,
),
inplace=True,
)
יצירת ansatz ופרמטרים ראשוניים ממעגל Trotter עם פחות צעדים
כעת לאחר שבנינו את ארבעת המעגלים שלנו, נמשיך עם תהליך העבודה של AQC-Tensor. ר אשית, אנו בונים מעגל "טוב" שיש לו אותו זמן אבולוציה כמו מעגל היעד, אך עם פחות צעדי Trotter (ולכן פחות שכבות).
לאחר מכן אנו מעבירים מעגל "טוב" זה לפונקציה generate_ansatz_from_circuit של AQC-Tensor. פונקציה זו מנתחת את הקישוריות של שני-qubit של המעגל ומחזירה שני דברים:
- מעגל ansatz כללי מפרמטר עם אותה קישוריות של שני-qubit כמו מעגל הקלט.
- פרמטרים שכאשר מחוברים ל-ansatz, מניבים את מעגל הקלט (הטוב).
בקרוב ניקח את הפרמטרים הללו ונתאים אותם באופן איטרטיבי כדי להביא את מעגל ה-ansatz קרוב ככל האפשר ל-MPS היעד.
aqc_ansatz_num_trotter_steps = 1
aqc_good_circuit = initial_state.copy()
aqc_good_circuit.compose(
generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),
time=aqc_evolution_time,
),
inplace=True,
)
aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(
aqc_good_circuit
)
aqc_ansatz.draw("mpl", fold=-1)

print(f"AQC Comparison circuit: depth {aqc_comparison_circuit.depth()}")
print(f"Target circuit: depth {aqc_target_circuit.depth()}")
print(
f"Ansatz circuit: depth {aqc_ansatz.depth()}, with {len(aqc_initial_parameters)} parameters"
)
AQC Comparison circuit: depth 36
Target circuit: depth 385
Ansatz circuit: depth 7, with 156 parameters