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

קומפילציה קוונטית קירובית עבור מעגלי אבולוציית זמן

הערכת שימוש: חמש דקות על מעבד 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 האתרים שלנו מוגדר כך:

H^XXZ=i=1L1Ji,(i+1)(XiX(i+1)+YiY(i+1)+2ZiZ(i+1)),\hat{\mathcal{H}}_{XXZ} = \sum_{i=1}^{L-1} J_{i,(i+1)}\left(X_i X_{(i+1)}+Y_i Y_{(i+1)}+ 2\cdot Z_i Z_{(i+1)} \right) \, ,

כאשר Ji,(i+1)J_{i,(i+1)} הוא מקדם אקראי התואם לקצה (i,i+1)(i, i+1), ו-L=10L=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])

Output of the previous code cell

עם ה-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:

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

גישה זו יוצרת ansatz בעומק נמוך עבור האבולוציה היעד, התומכת בסימולציה יעילה במסגרת מגבלות החומרה הקוונטית של הדור הקרוב.

קביעת חלק אבולוציית הזמן לסימולציה קלאסית

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

  • מקטע ראשוני: חלק ראשוני זה של האבולוציה, מ-ti=0.0t_i = 0.0 עד tf=0.2t_f = 0.2, ניתן לסימולציה עם MPS וניתן "לקמפל" ביעילות באמצעות AQC-Tensor. באמצעות הרחבת AQC-Tensor של Qiskit, אנו יוצרים מעגל מדוחס עבור מקטע זה, המכונה aqc_target_circuit. מכיוון שמקטע זה יידמה על סימולטור רשת-טנזור, אנו יכולים להרשות לעצמנו להשתמש במספר גבוה יותר של שכבות Trotter מבלי להשפיע על משאבי החומרה באופן משמעותי. אנו קובעים aqc_target_num_trotter_steps = 32 עבור מקטע זה.

  • מקטע עוקב: חלק נותר זה של האבולוציה, מ-t=0.2t = 0.2 עד t=0.4t = 0.4, יבוצע על חומרה קוונטית, המכונה subsequent_circuit. בהתחשב במגבלות החומרה, אנו שואפים להשתמש בכמה שפחות שכבות Trotter כדי לשמור על עומק מעגל ניתן לניהול. עבור מקטע זה, אנו משתמשים ב-subsequent_num_trotter_steps = 3.

בחירת זמן הפיצול

אנו בוחרים ב-t=0.2t = 0.2 כזמן הפיצול כדי לאזן בין יכולת סימולציה קלאסית והיתכנות חומרה. בתחילת האבולוציה, ההסתבכות במודל 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)

Output of the previous code cell

כדי לאפשר השוואה משמעותית, ניצור שני מעגלים נוספים:

  • מעגל השוואת 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 של המעגל ומחזירה שני דברים:

  1. מעגל ansatz כללי מפרמטר עם אותה קישוריות של שני-qubit כמו מעגל הקלט.
  2. פרמטרים שכאשר מחוברים ל-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)

Output of the previous code cell

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

בחירת הגדרות לסימולציית רשת טנזור

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

simulator_settings = QuimbSimulator(
quimb.tensor.CircuitMPS, autodiff_backend="jax"
)

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

aqc_target_mps = tensornetwork_from_circuit(
aqc_target_circuit, simulator_settings
)
print("Target MPS maximum bond dimension:", aqc_target_mps.psi.max_bond())

# Obtains the reference MPS, where we can obtain the exact expectation value by examining the `local_expectation``
reference_mps = tensornetwork_from_circuit(
reference_circuit, simulator_settings
)
reference_expval = reference_mps.local_expectation(
quimb.pauli("Z") & quimb.pauli("Z"), (L // 2 - 1, L // 2)
).real.item()
print("Reference MPS maximum bond dimension:", reference_mps.psi.max_bond())
Target MPS maximum bond dimension: 5
Reference MPS maximum bond dimension: 7

שימו לב שבבחירת מספר גדול יותר של צעדי Trotter עבור מצב היעד, הפחתנו למעשה את שגיאת ה-Trotter שלו בהשוואה למעגל הראשוני. אנו יכולים להעריך את הנאמנות (ψ1ψ22|\langle \psi_1 | \psi_2 \rangle|^2) בין המצב שהוכן על ידי המעגל הראשוני למצב היעד כדי לכמת הבדל זה.

good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)
starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2
print("Starting fidelity:", starting_fidelity)
Starting fidelity: 0.9982464959067222

אופטימיזציה של הפרמטרים של ה-ansatz באמצעות חישובי MPS

בשלב זה, אנו מבצעים אופטימיזציה של פרמטרי ה-ansatz על ידי מזעור פונקציית עלות פשוטה, MaximizeStateFidelity, באמצעות אופטימייזר L-BFGS מ-SciPy. אנו בוחרים קריטריון עצירה לנאמנות המבטיח שהיא עולה על הנאמנות של המעגל הראשוני ללא AQC-Tensor. ברגע שסף זה מושג, המעגל המדוחס יציג גם שגיאת Trotter נמוכה יותר וגם עומק מופחת בהשוואה למעגל המקורי. באמצעות זמן CPU נוסף, אופטימיזציה נוספת יכולה להמשיך להגדיל את הנאמנות.

# Setting values for the optimization
aqc_stopping_fidelity = 1
aqc_max_iterations = 500

stopping_point = 1.0 - aqc_stopping_fidelity
objective = MaximizeStateFidelity(
aqc_target_mps, aqc_ansatz, simulator_settings
)

def callback(intermediate_result: OptimizeResult):
fidelity = 1 - intermediate_result.fun
print(
f"{datetime.datetime.now()} Intermediate result: Fidelity {fidelity:.8f}"
)
if intermediate_result.fun < stopping_point:
# Good enough for now
raise StopIteration

result = minimize(
objective,
aqc_initial_parameters,
method="L-BFGS-B",
jac=True,
options={"maxiter": aqc_max_iterations},
callback=callback,
)
if (
result.status
not in (
0,
1,
99,
)
): # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration
raise RuntimeError(
f"Optimization failed: {result.message} (status={result.status})"
)

print(f"Done after {result.nit} iterations.")
aqc_final_parameters = result.x
2025-04-14 11:46:52.174235 Intermediate result: Fidelity 0.99795851
2025-04-14 11:46:52.218249 Intermediate result: Fidelity 0.99822826
2025-04-14 11:46:52.280924 Intermediate result: Fidelity 0.99829675
2025-04-14 11:46:52.356214 Intermediate result: Fidelity 0.99832474
2025-04-14 11:46:52.411609 Intermediate result: Fidelity 0.99836131
2025-04-14 11:46:52.453747 Intermediate result: Fidelity 0.99839954
2025-04-14 11:46:52.496184 Intermediate result: Fidelity 0.99846517
2025-04-14 11:46:52.542046 Intermediate result: Fidelity 0.99865029
2025-04-14 11:46:52.583679 Intermediate result: Fidelity 0.99872332
2025-04-14 11:46:52.628732 Intermediate result: Fidelity 0.99892359
2025-04-14 11:46:52.690386 Intermediate result: Fidelity 0.99900640
2025-04-14 11:46:52.759398 Intermediate result: Fidelity 0.99907169
2025-04-14 11:46:52.819496 Intermediate result: Fidelity 0.99911423
2025-04-14 11:46:52.884505 Intermediate result: Fidelity 0.99918716
2025-04-14 11:46:52.947919 Intermediate result: Fidelity 0.99921278
2025-04-14 11:46:53.012808 Intermediate result: Fidelity 0.99924853
2025-04-14 11:46:53.083626 Intermediate result: Fidelity 0.99928797
2025-04-14 11:46:53.153235 Intermediate result: Fidelity 0.99933028
2025-04-14 11:46:53.221371 Intermediate result: Fidelity 0.99935757
2025-04-14 11:46:53.286211 Intermediate result: Fidelity 0.99938140
2025-04-14 11:46:53.352391 Intermediate result: Fidelity 0.99940964
2025-04-14 11:46:53.420472 Intermediate result: Fidelity 0.99944051
2025-04-14 11:46:53.486279 Intermediate result: Fidelity 0.99946828
2025-04-14 11:46:53.552338 Intermediate result: Fidelity 0.99948723
2025-04-14 11:46:53.618688 Intermediate result: Fidelity 0.99951011
2025-04-14 11:46:53.690878 Intermediate result: Fidelity 0.99954718
2025-04-14 11:46:53.762725 Intermediate result: Fidelity 0.99956267
2025-04-14 11:46:53.829784 Intermediate result: Fidelity 0.99958949
2025-04-14 11:46:53.897477 Intermediate result: Fidelity 0.99960498
2025-04-14 11:46:53.954633 Intermediate result: Fidelity 0.99961308
2025-04-14 11:46:54.010125 Intermediate result: Fidelity 0.99962894
2025-04-14 11:46:54.064717 Intermediate result: Fidelity 0.99964121
2025-04-14 11:46:54.118892 Intermediate result: Fidelity 0.99964348
2025-04-14 11:46:54.183236 Intermediate result: Fidelity 0.99964860
2025-04-14 11:46:54.245521 Intermediate result: Fidelity 0.99965695
2025-04-14 11:46:54.305792 Intermediate result: Fidelity 0.99966398
2025-04-14 11:46:54.355819 Intermediate result: Fidelity 0.99967816
2025-04-14 11:46:54.409580 Intermediate result: Fidelity 0.99968293
2025-04-14 11:46:54.457979 Intermediate result: Fidelity 0.99968936
2025-04-14 11:46:54.505891 Intermediate result: Fidelity 0.99969223
2025-04-14 11:46:54.551084 Intermediate result: Fidelity 0.99970009
2025-04-14 11:46:54.601817 Intermediate result: Fidelity 0.99970724
2025-04-14 11:46:54.650097 Intermediate result: Fidelity 0.99970987
2025-04-14 11:46:54.714727 Intermediate result: Fidelity 0.99971237
2025-04-14 11:46:54.780052 Intermediate result: Fidelity 0.99971916
2025-04-14 11:46:54.871994 Intermediate result: Fidelity 0.99971940
2025-04-14 11:46:54.958244 Intermediate result: Fidelity 0.99972465
2025-04-14 11:46:55.011057 Intermediate result: Fidelity 0.99972763
2025-04-14 11:46:55.175339 Intermediate result: Fidelity 0.99972894
2025-04-14 11:46:56.688912 Intermediate result: Fidelity 0.99972894
Done after 50 iterations.
parameters = [float(param) for param in aqc_final_parameters]
print("Final parameters:", parameters)
Final parameters: [-7.853983035039254, 1.5707966468427772, 1.5707962768868613, -1.570798010835122, 1.570794480409574, 1.5707972214146968, -1.570796593027083, 1.5707968206822998, -1.5707959018046258, -1.5707991700969144, 1.5707965852600927, 4.712386891737442, -7.853980840717957, 1.5707967508132654, 1.5707943162503217, -1.5707955382023582, 1.5707958007156742, 1.570796096113293, -1.5707928509846847, 1.5707971042943747, -1.570797909276557, -1.5707941020637393, 1.5707980179540793, 4.712389823219363, -1.5707928752386107, 1.5707996426312891, -1.5707975640471001, -1.570794132802984, 1.5707944361599957, 4.712390747060803, 0.1048818190315936, 0.06686710468840577, -0.0668645844756557, -3.1415923537135466, 1.2374931269696063, 6.323169390432535e-07, 3.53229204771738e-08, 2.1091105688681484, 6.283186439944202, 0.12152258846156239, 0.07961752617254866, -0.07961775088604585, -1.6564278051174865e-06, 2.0771163596472384, 3.141592651630471, -6.283185775192653, 1.7691609006726954, 3.1415922910116216, 0.19837572065074083, 0.11114901449078964, -0.11115124544944892, -3.141591983034976, 0.8570788408766729, 4.201601390404146e-07, -3.141593736550978, 0.34652010942396333, 6.283186232785291, 0.13606356527241956, 0.03891676349289617, -0.03891524189533726, -1.5707965732853424, 1.5707968967088564, -0.3086133992238162, 1.5707957152428194, 1.5707968398959653, -0.32062737993080026, 0.11027416939993417, 0.0726167290795046, -0.07262020423334464, -2.3729431959735024e-06, 1.8204437429254703, 9.299060301196612e-07, -3.141592899563451, 2.103269568939461, 3.1415937539734626, 0.11536891854817125, 0.09099022308254198, -0.09098864958606581, -3.1415913307373127, 2.078429034357281, -1.509777998069368e-06, -3.1415922600663255, 1.5189162645358172, -3.1415878461323583, 0.09999070991480716, 0.04352011445148391, -0.04351849541849812, -1.570797642506462, 1.570795238023824, 0.8903442644396505, 1.5707962698006606, 1.5707946765132268, 0.9098791754570567, 0.10448284343424026, 0.07317037684936827, -0.07316718173961152, -3.141592682240966, 2.1665363080039612, -7.450882112394189e-07, -5.771181304929921e-07, 2.615334999517103, -3.1415914971653898, 0.1890887078648001, 0.13578163074571992, -0.13578078143610256, 7.156734195912883e-07, 1.7915385305413096, -5.188866034727312e-07, 1.2827742939197711e-06, 1.2348316581417487, 6.28318357406372, 0.08061187643781703, 0.03820789039271876, -0.03820731868804904, 1.5707964027727628, 1.570798734462218, 4.387336153720882, -1.570795722044763, 1.570798457375325, 4.450361734163248, 0.092360147257953, 0.06047700345049011, -0.06048592856713045, -3.141591214829027, 2.6593289993286047, -2.366937342261038e-07, 8.112162974032695e-08, 1.8907014631413432, 8.355881261853104e-07, 0.23303641819370874, 0.14331998953606456, -0.1433194488304741, -3.141591621822901, 0.7455776479558791, 3.1415914520163586, -3.1415933560496105, 0.7603938554148255, -1.6230983177616282e-06, 0.07186349688535713, 0.03197144517771341, -0.031971177878588546, -4.712389048748508, 1.5707948403165752, 1.2773619319829186, -1.5707990802172127, 1.5707957676951863, 1.289083769394045, 0.13644999397718796, 0.032761460443590046, -0.032762060585195645, -1.5707977610073176, 1.5707964181578042, -3.4826435600366983, -4.712389691708343, 1.570794277502252, 2.799088046133275]

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

aqc_final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)
aqc_final_circuit.compose(subsequent_circuit, inplace=True)
aqc_final_circuit.draw("mpl", fold=-1)

Output of the previous code cell

אנחנו גם צריכים למזג את ה-aqc_comparison_circuit שלנו עם מעגל האבולוציה הנותר. מעגל זה ישמש להשוואת הביצועים של המעגל המותאם של AQC-Tensor עם המעגל המקורי.

aqc_comparison_circuit.compose(subsequent_circuit, inplace=True)
aqc_comparison_circuit.draw("mpl", fold=-1)

Output of the previous code cell

שלב 2: אופטימיזציה של הבעיה לביצוע חומרה קוונטית

בחר את החומרה. כאן נשתמש באחד מהתקני IBM Quantum® הזמינים שיש להם לפחות 127 qubits.

service = QiskitRuntimeService()
backend = service.least_busy(min_num_qubits=127)
print(backend)

אנו מבצעים טרנספילציה של PUBs (מעגל ו-observables) כדי להתאים ל-ISA של הקצה (Instruction Set Architecture). על ידי הגדרת optimization_level=3, ה-transpiler מבצע אופטימיזציה של המעגל כדי להתאים לשרשרת חד-ממדית של qubits, ומפחית את הרעש המשפיע על נאמנות המעגל. ברגע שהמעגלים מומרים לפורמט תואם עם הקצה, אנו מיישמים טרנספורמציה מתאימה על ה-observables כדי להבטיח שהם מתיישרים עם פריסת ה-qubit המשונה.

pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = pass_manager.run(aqc_final_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
print("Observable info:", isa_observable)
print("Circuit depth:", isa_circuit.depth())
isa_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ'],
coeffs=[1.+0.j])
Circuit depth: 111

Output of the previous code cell

ביצוע טרנספילציה עבור מעגל ההשוואה.

isa_comparison_circuit = pass_manager.run(aqc_comparison_circuit)
isa_comparison_observable = observable.apply_layout(
isa_comparison_circuit.layout
)
print("Observable info:", isa_comparison_observable)
print("Circuit depth:", isa_comparison_circuit.depth())
isa_comparison_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZ'],
coeffs=[1.+0.j])
Circuit depth: 158

Output of the previous code cell

שלב 3: ביצוע באמצעות primitives של Qiskit

בשלב זה, אנו מבצעים את המעגל המטורנספל על חומרה קוונטית (או קצה מדומה). באמצעות המחלקה EstimatorV2 מ-qiskit_ibm_runtime, אנו מגדירים estimator להרצת המעגל ומדידת ה-observable שצוין. תוצאת העבודה מספקת את התוצאה הצפויה עבור ה-observable, ומעניקה לנו תובנות לגבי ביצועי המעגל על חומרת היעד.

estimator = Estimator(backend)
job = estimator.run([(isa_circuit, isa_observable)])
print("Job ID:", job.job_id())
job.result()
Job ID: czyhqdxd8drg008hx0yg
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(), dtype=float64>)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})

ביצוע עבור מעגל ההשוואה.

job_comparison = estimator.run([(isa_comparison_circuit, isa_observable)])
print("Job Comparison ID:", job.job_id())
job_comparison.result()
Job Comparison ID: czyhqdxd8drg008hx0yg
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), ensemble_standard_error=np.ndarray(<shape=(), dtype=float64>)), metadata={'shots': 4096, 'target_precision': 0.015625, 'circuit_metadata': {}, 'resilience': {}, 'num_randomizations': 32})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': False, 'enable_measure': True, 'num_randomizations': 'auto', 'shots_per_randomization': 'auto', 'interleave_randomizations': True, 'strategy': 'active-accum'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': False, 'pec_mitigation': False}, 'version': 2})

שלב 4: עיבוד לאחר ביצוע והחזרת תוצאה בפורמט קלאסי רצוי

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

# AQC results
hw_results = job.result()
hw_results_dicts = [pub_result.data.__dict__ for pub_result in hw_results]
hw_expvals = [
pub_result_data["evs"].tolist() for pub_result_data in hw_results_dicts
]
aqc_expval = hw_expvals[0]

# AQC comparison results
hw_comparison_results = job_comparison.result()
hw_comparison_results_dicts = [
pub_result.data.__dict__ for pub_result in hw_comparison_results
]
hw_comparison_expvals = [
pub_result_data["evs"].tolist()
for pub_result_data in hw_comparison_results_dicts
]
aqc_compare_expval = hw_comparison_expvals[0]

print(f"Exact: \t{reference_expval:.4f}")
print(
f"AQC: \t{aqc_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_expval):.4f}"
)
print(
f"AQC Comparison:\t{aqc_compare_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_compare_expval):.4f}"
)
Exact:         	-0.5252
AQC: -0.4903, |∆| = 0.0349
AQC Comparison: 0.5424, |∆| = 1.0676

תרשים עמודות להשוואת התוצאות של מעגלי ה-AQC, ההשוואה והמדויק.

plt.style.use("seaborn-v0_8")

labels = ["AQC Result", "AQC Comparison Result"]
values = [abs(aqc_expval), abs(aqc_compare_expval)]

plt.figure(figsize=(10, 6))
bars = plt.bar(labels, values, color=["tab:blue", "tab:purple"])
plt.axhline(
y=abs(reference_expval), color="red", linestyle="--", label="Exact Result"
)
plt.xlabel("Results")
plt.ylabel("Absolute Expected Value")
plt.title("AQC Result vs AQC Comparison Result (Absolute Values)")
plt.legend()
for bar in bars:
y_val = bar.get_height()
plt.text(
bar.get_x() + bar.get_width() / 2.0,
y_val,
round(y_val, 2),
va="bottom",
)
plt.show()

Output of the previous code cell

חלק II: הגדלת קנה המידה

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

ה-Hamiltonian עבור מודל XXZ בן 50 האתרים שלנו מוגדר כך:

H^XXZ=i=1L1Ji,(i+1)(XiX(i+1)+YiY(i+1)+2ZiZ(i+1)),\hat{\mathcal{H}}_{XXZ} = \sum_{i=1}^{L-1} J_{i,(i+1)}\left(X_i X_{(i+1)}+Y_i Y_{(i+1)}+ 2\cdot Z_i Z_{(i+1)} \right) \, ,

כאשר Ji,(i+1)J_{i,(i+1)} הוא מקדם אקראי התואם לקצה (i,i+1)(i, i+1), ו-L=50L=50 הוא מספר האתרים. הגדרת מפת הצימוד והקצוות עבור ה-Hamiltonian.

L = 50  # L = length of our 1D spin chain

# Generate the edge list for this spin-chain
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]

# Instantiate a CouplingMap object
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,
)

observable = SparsePauliOp.from_sparse_list(
[("ZZ", (L // 2 - 1, L // 2), 1.0)], num_qubits=L
)

# Generate an initial state
L = hamiltonian.num_qubits
initial_state = QuantumCircuit(L)
for i in range(L):
if i % 2:
initial_state.x(i)

שלב 1: מיפוי קלטים קלאסיים לבעיה קוונטית

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

  1. יצירת מעגל AQC מותאם: שימוש ב-Trotterization כדי לקרב את האבולוציה הראשונית, לאחר מכן דחיסת מקטע זה כדי להפחית את עומק המעגל.
  2. יצירת מעגל אבולוציית הזמן הנותר: לכידת אבולוציית הזמן הנותר מעבר למקטע הראשוני.
  3. שילוב המעגלים: מיזוג מעגל ה-AQC המותאם עם מעגל האבולוציה הנותר כדי ליצור מעגל אבולוציית-זמן שלם המוכן לביצוע. יצירת מעגל יעד ה-AQC (המקטע הראשוני).
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,
)

יצירת המעגל העוקב (המקטע הנותר).

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,
)

יצירת מעגל השוואת AQC (המקטע הראשוני, אך עם אותו מספר צעדי Trotter כמו המעגל העוקב).

# 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

יצירת מעגל הייחוס.

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_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
)
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 816 parameters

הגדרת הגדרות לסימולציית רשת טנזור ולאחר מכן בניית ייצוג מצב מכפלת מטריצה של מצב היעד לאופטימיזציה. לאחר מכן, הערכת הנאמנות בין המעגל הראשוני למצב היעד כדי לכמת את ההבדל בשגיאת Trotter.

simulator_settings = QuimbSimulator(
quimb.tensor.CircuitMPS, autodiff_backend="jax"
)

# Build the matrix-product representation of the state to be approximated by AQC
aqc_target_mps = tensornetwork_from_circuit(
aqc_target_circuit, simulator_settings
)
print("Target MPS maximum bond dimension:", aqc_target_mps.psi.max_bond())

# Obtains the reference MPS, where we can obtain the exact expectation value by examining the `local_expectation``
reference_mps = tensornetwork_from_circuit(
reference_circuit, simulator_settings
)
reference_expval = reference_mps.local_expectation(
quimb.pauli("Z") & quimb.pauli("Z"), (L // 2 - 1, L // 2)
).real.item()

# Compute the starting fidelity
good_mps = tensornetwork_from_circuit(aqc_good_circuit, simulator_settings)
starting_fidelity = abs(compute_overlap(good_mps, aqc_target_mps)) ** 2
print("Starting fidelity:", starting_fidelity)
Target MPS maximum bond dimension: 5
Starting fidelity: 0.9926466919924161

כדי לבצע אופטימיזציה של פרמטרי ה-ansatz, אנו ממזערים את פונקציית העלות MaximizeStateFidelity באמצעות אופטימייזר L-BFGS מ-SciPy, עם קריטריון עצירה שנקבע לעבור את הנאמנות של המעגל הראשוני ללא AQC-Tensor. זה מבטיח שלמעגל המדוחס יש גם שגיאת Trotter נמוכה יותר וגם עומק מופחת.

# Setting values for the optimization
aqc_stopping_fidelity = 1
aqc_max_iterations = 500

stopping_point = 1.0 - aqc_stopping_fidelity
objective = MaximizeStateFidelity(
aqc_target_mps, aqc_ansatz, simulator_settings
)

def callback(intermediate_result: OptimizeResult):
fidelity = 1 - intermediate_result.fun
print(
f"{datetime.datetime.now()} Intermediate result: Fidelity {fidelity:.8f}"
)
if intermediate_result.fun < stopping_point:
# Good enough for now
raise StopIteration

result = minimize(
objective,
aqc_initial_parameters,
method="L-BFGS-B",
jac=True,
options={"maxiter": aqc_max_iterations},
callback=callback,
)
if (
result.status
not in (
0,
1,
99,
)
): # 0 => success; 1 => max iterations reached; 99 => early termination via StopIteration
raise RuntimeError(
f"Optimization failed: {result.message} (status={result.status})"
)

print(f"Done after {result.nit} iterations.")
aqc_final_parameters = result.x
2025-04-14 11:48:28.705807 Intermediate result: Fidelity 0.99795851
2025-04-14 11:48:28.743265 Intermediate result: Fidelity 0.99822826
2025-04-14 11:48:28.776629 Intermediate result: Fidelity 0.99829675
2025-04-14 11:48:28.816153 Intermediate result: Fidelity 0.99832474
2025-04-14 11:48:28.856437 Intermediate result: Fidelity 0.99836131
2025-04-14 11:48:28.896432 Intermediate result: Fidelity 0.99839954
2025-04-14 11:48:28.936670 Intermediate result: Fidelity 0.99846517
2025-04-14 11:48:28.982069 Intermediate result: Fidelity 0.99865029
2025-04-14 11:48:29.026130 Intermediate result: Fidelity 0.99872332
2025-04-14 11:48:29.067426 Intermediate result: Fidelity 0.99892359
2025-04-14 11:48:29.110742 Intermediate result: Fidelity 0.99900640
2025-04-14 11:48:29.161362 Intermediate result: Fidelity 0.99907169
2025-04-14 11:48:29.207933 Intermediate result: Fidelity 0.99911423
2025-04-14 11:48:29.266772 Intermediate result: Fidelity 0.99918716
2025-04-14 11:48:29.331727 Intermediate result: Fidelity 0.99921278
2025-04-14 11:48:29.401694 Intermediate result: Fidelity 0.99924853
2025-04-14 11:48:29.467980 Intermediate result: Fidelity 0.99928797
2025-04-14 11:48:29.533281 Intermediate result: Fidelity 0.99933028
2025-04-14 11:48:29.600833 Intermediate result: Fidelity 0.99935757
2025-04-14 11:48:29.670816 Intermediate result: Fidelity 0.99938140
2025-04-14 11:48:29.736928 Intermediate result: Fidelity 0.99940964
2025-04-14 11:48:29.802931 Intermediate result: Fidelity 0.99944051
2025-04-14 11:48:29.869177 Intermediate result: Fidelity 0.99946828
2025-04-14 11:48:29.940156 Intermediate result: Fidelity 0.99948723
2025-04-14 11:48:30.005751 Intermediate result: Fidelity 0.99951011
2025-04-14 11:48:30.070853 Intermediate result: Fidelity 0.99954718
2025-04-14 11:48:30.139171 Intermediate result: Fidelity 0.99956267
2025-04-14 11:48:30.210506 Intermediate result: Fidelity 0.99958949
2025-04-14 11:48:30.279647 Intermediate result: Fidelity 0.99960498
2025-04-14 11:48:30.348016 Intermediate result: Fidelity 0.99961308
2025-04-14 11:48:30.414311 Intermediate result: Fidelity 0.99962894
2025-04-14 11:48:30.488910 Intermediate result: Fidelity 0.99964121
2025-04-14 11:48:30.561298 Intermediate result: Fidelity 0.99964348
2025-04-14 11:48:30.632214 Intermediate result: Fidelity 0.99964860
2025-04-14 11:48:30.705703 Intermediate result: Fidelity 0.99965695
2025-04-14 11:48:30.775679 Intermediate result: Fidelity 0.99966398
2025-04-14 11:48:30.842629 Intermediate result: Fidelity 0.99967816
2025-04-14 11:48:30.912357 Intermediate result: Fidelity 0.99968293
2025-04-14 11:48:30.979420 Intermediate result: Fidelity 0.99968936
2025-04-14 11:48:31.049196 Intermediate result: Fidelity 0.99969223
2025-04-14 11:48:31.125391 Intermediate result: Fidelity 0.99970009
2025-04-14 11:48:31.201256 Intermediate result: Fidelity 0.99970724
2025-04-14 11:48:31.272424 Intermediate result: Fidelity 0.99970987
2025-04-14 11:48:31.338907 Intermediate result: Fidelity 0.99971237
2025-04-14 11:48:31.404800 Intermediate result: Fidelity 0.99971916
2025-04-14 11:48:31.475226 Intermediate result: Fidelity 0.99971940
2025-04-14 11:48:31.547746 Intermediate result: Fidelity 0.99972465
2025-04-14 11:48:31.622827 Intermediate result: Fidelity 0.99972763
2025-04-14 11:48:31.819516 Intermediate result: Fidelity 0.99972894
2025-04-14 11:48:33.444538 Intermediate result: Fidelity 0.99972894
Done after 50 iterations.
parameters = [float(param) for param in aqc_final_parameters]

בניית המעגל הסופי לטרנספילציה על ידי הרכבת ה-ansatz המותאם עם מעגל אבולוציית הזמן הנותר.

aqc_final_circuit = aqc_ansatz.assign_parameters(aqc_final_parameters)
aqc_final_circuit.compose(subsequent_circuit, inplace=True)
aqc_comparison_circuit.compose(subsequent_circuit, inplace=True)

שלב 2: אופטימיזציה של הבעיה לביצוע חומרה קוונטית

בחירת הקצה.

service = QiskitRuntimeService()
backend = service.least_busy(min_num_qubits=127)
print(backend)

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

pass_manager = generate_preset_pass_manager(
backend=backend, optimization_level=3
)
isa_circuit = pass_manager.run(aqc_final_circuit)
isa_observable = observable.apply_layout(isa_circuit.layout)
print("Observable info:", isa_observable)
print("Circuit depth:", isa_circuit.depth())
isa_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])
Circuit depth: 122

Output of the previous code cell

isa_comparison_circuit = pass_manager.run(aqc_comparison_circuit)
isa_comparison_observable = observable.apply_layout(
isa_comparison_circuit.layout
)
print("Observable info:", isa_comparison_observable)
print("Circuit depth:", isa_comparison_circuit.depth())
isa_comparison_circuit.draw("mpl", fold=-1, idle_wires=False)
Observable info: SparsePauliOp(['IIIIIIIIIIIIIIIIIIIIIIIIIIIIIIZZIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIIII'],
coeffs=[1.+0.j])
Circuit depth: 158

Output of the previous code cell

שלב 3: ביצוע באמצעות primitives של Qiskit

בשלב זה, אנו מריצים את המעגל המטורנספל על חומרה קוונטית (או קצה מדומה) באמצעות EstimatorV2 מ-qiskit_ibm_runtime כדי למדוד את ה-observable שצוין. תוצאת העבודה תספק תובנות חשובות לגבי ביצועי המעגל על חומרת היעד.

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

לרשימה מלאה של אפשרויות ביצוע זמינות, עיין בתיעוד qiskit-ibm-runtime.

twirling_options = {
"enable_gates": True,
"enable_measure": True,
"num_randomizations": 300,
"shots_per_randomization": 100,
"strategy": "active",
}

zne_options = {
"amplifier": "gate_folding",
"noise_factors": [1, 2, 3],
"extrapolated_noise_factors": list(np.linspace(0, 3, 31)),
"extrapolator": ["exponential", "linear", "fallback"],
}

meas_learning_options = {
"num_randomizations": 512,
"shots_per_randomization": 512,
}

resilience_options = {
"measure_mitigation": True,
"zne_mitigation": True,
"zne": zne_options,
"measure_noise_learning": meas_learning_options,
}

estimator_options = {
"resilience": resilience_options,
"twirling": twirling_options,
}

estimator = Estimator(backend, options=estimator_options)
job = estimator.run([(isa_circuit, isa_observable)])
print("Job ID:", job.job_id())
job.result()
Job ID: czyjx6crxz8g008f63r0
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), evs_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), ensemble_stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), evs_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>), stds_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>)), metadata={'shots': 30000, 'target_precision': 0.005773502691896258, 'circuit_metadata': {}, 'resilience': {'zne': {'extrapolator': 'exponential'}}, 'num_randomizations': 300})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': True, 'enable_measure': True, 'num_randomizations': 300, 'shots_per_randomization': 100, 'interleave_randomizations': True, 'strategy': 'active'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': True, 'pec_mitigation': False, 'zne': {'noise_factors': [1, 2, 3], 'extrapolator': ['exponential', 'linear', 'fallback'], 'extrapolated_noise_factors': [0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5, 1.6, 1.7000000000000002, 1.8, 1.9000000000000001, 2, 2.1, 2.2, 2.3000000000000003, 2.4000000000000004, 2.5, 2.6, 2.7, 2.8000000000000003, 2.9000000000000004, 3]}}, 'version': 2})
job_comparison = estimator.run([(isa_comparison_circuit, isa_observable)])
print("Job Comparison ID:", job.job_id())
job_comparison.result()
Job Comparison ID: czyjx6crxz8g008f63r0
PrimitiveResult([PubResult(data=DataBin(evs=np.ndarray(<shape=(), dtype=float64>), stds=np.ndarray(<shape=(), dtype=float64>), evs_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), ensemble_stds_noise_factors=np.ndarray(<shape=(3,), dtype=float64>), evs_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>), stds_extrapolated=np.ndarray(<shape=(3, 31), dtype=float64>)), metadata={'shots': 30000, 'target_precision': 0.005773502691896258, 'circuit_metadata': {}, 'resilience': {'zne': {'extrapolator': 'exponential'}}, 'num_randomizations': 300})], metadata={'dynamical_decoupling': {'enable': False, 'sequence_type': 'XX', 'extra_slack_distribution': 'middle', 'scheduling_method': 'alap'}, 'twirling': {'enable_gates': True, 'enable_measure': True, 'num_randomizations': 300, 'shots_per_randomization': 100, 'interleave_randomizations': True, 'strategy': 'active'}, 'resilience': {'measure_mitigation': True, 'zne_mitigation': True, 'pec_mitigation': False, 'zne': {'noise_factors': [1, 2, 3], 'extrapolator': ['exponential', 'linear', 'fallback'], 'extrapolated_noise_factors': [0, 0.1, 0.2, 0.30000000000000004, 0.4, 0.5, 0.6000000000000001, 0.7000000000000001, 0.8, 0.9, 1, 1.1, 1.2000000000000002, 1.3, 1.4000000000000001, 1.5, 1.6, 1.7000000000000002, 1.8, 1.9000000000000001, 2, 2.1, 2.2, 2.3000000000000003, 2.4000000000000004, 2.5, 2.6, 2.7, 2.8000000000000003, 2.9000000000000004, 3]}}, 'version': 2})

שלב 4: עיבוד לאחר ביצוע והחזרת תוצאה בפורמט קלאסי רצוי

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

# AQC results
hw_results = job.result()
hw_results_dicts = [pub_result.data.__dict__ for pub_result in hw_results]
hw_expvals = [
pub_result_data["evs"].tolist() for pub_result_data in hw_results_dicts
]
aqc_expval = hw_expvals[0]

# AQC comparison results
hw_comparison_results = job_comparison.result()
hw_comparison_results_dicts = [
pub_result.data.__dict__ for pub_result in hw_comparison_results
]
hw_comparison_expvals = [
pub_result_data["evs"].tolist()
for pub_result_data in hw_comparison_results_dicts
]
aqc_compare_expval = hw_comparison_expvals[0]

print(f"Exact: \t{reference_expval:.4f}")
print(
f"AQC: \t{aqc_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_expval):.4f}"
)
print(
f"AQC Comparison:\t{aqc_compare_expval:.4f}, |∆| = {np.abs(reference_expval- aqc_compare_expval):.4f}"
)
Exact:         	-0.5888
AQC: -0.4809, |∆| = 0.1078
AQC Comparison: 1.1764, |∆| = 1.7652

תרשים התוצאות של מעגלי ה-AQC, ההשוואה והמדויק עבור מודל XXZ בן 50 האתרים.

labels = ["AQC Result", "AQC Comparison Result"]
values = [abs(aqc_expval), abs(aqc_compare_expval)]

plt.figure(figsize=(10, 6))
bars = plt.bar(labels, values, color=["tab:blue", "tab:purple"])
plt.axhline(
y=abs(reference_expval), color="red", linestyle="--", label="Exact Result"
)
plt.xlabel("Results")
plt.ylabel("Absolute Expected Value")
plt.title("AQC Result vs AQC Comparison Result (Absolute Values)")
plt.legend()
for bar in bars:
y_val = bar.get_height()
plt.text(
bar.get_x() + bar.get_width() / 2.0,
y_val,
round(y_val, 2),
va="bottom",
)

plt.show()

Output of the previous code cell

סיכום

מדריך זה הדגים כיצד להשתמש בקומפילציה קוונטית קירובית עם רשתות טנזור (AQC-Tensor) כדי לדחוס ולבצע אופטימיזציה של מעגלים לסימולציה של דינמיקה קוונטית בקנה מידה. תוך שימוש במודלי Heisenberg קטנים וגדולים כאחד, יישמנו את AQC-Tensor כדי להפחית את עומק המעגל הנדרש לאבולוציית זמן Trotterized. על ידי יצירת ansatz מפרמטר ממעגל Trotter פשוט ואופטימיזציה שלו באמצעות טכניקות מצב מכפלת מטריצה (MPS), השגנו קירוב בעומק נמוך של אבולוציית היעד שהוא גם מדויק וגם יעיל.

תהליך העבודה כאן מדגיש את היתרונות העיקריים של AQC-Tensor עבור הגדלת קנה המידה של סימולציות קוונטיות:

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

ככל שיותר התקנים קוונטיים גדולים ואלגוריתמים מתקדמים יותר מופיעים, טכניקות כמו AQC-Tensor יהפכו לחיוניות להרצת סימולציות קוונטיות מורכבות על חומרה של הדור הקרוב, מדגימות התקדמות מבטיחה בניהול עומק ונאמנות עבור יישומים קוונטיים ניתנים להרחבה.

סקר מדריך

אנא קח את הסקר הקצר הזה כדי לספק משוב על מדריך זה. התובנות שלך יעזרו לנו לשפר את הצעות התוכן ואת חוויית המשתמש שלנו.

קישור לסקר