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

תחילת העבודה עם קומפילציה קוונטית מקורבת באמצעות רשתות טנסור (AQC-Tensor)

גרסאות חבילות

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

qiskit[all]~=2.3.0
qiskit-aer~=0.17
qiskit-addon-utils~=0.3.0
qiskit-addon-aqc-tensor[aer,quimb-jax]~=0.2.0; sys.platform != 'darwin'
scipy~=1.16.3

מדריך זה מדגים דוגמה פשוטה לתחילת העבודה עם AQC-Tensor. בדוגמה זו, תיקח מעגל Trotter המדמה את האבולוציה של מודל Ising בשדה רוחבי ותשתמש בשיטת AQC-Tensor כדי לצמצם את עומק המעגל המתקבל. בנוסף, דוגמה זו דורשת את חבילת qiskit-addon-utils למחולל הבעיה, את qiskit-aer לסימולציית רשת הטנסור, ואת scipy לאופטימיזציה של הפרמטרים.

כדי להתחיל, נזכיר שה-Hamiltonian של מודל Ising בשדה רוחבי הוא בצורה

HIsing=i=1NJi,(i+1)ZiZi+1+hiXi\mathcal{H}_{Ising} = \sum_{i=1}^N J_{i,(i+1)}Z_iZ_{i+1} + h_i X_i

כאשר נניח תנאי גבול מחזוריים, כלומר עבור i=10i=10 מתקבל i+1=111i+1=11\rightarrow 1, ו-JJ הוא עוצמת הצימוד בין שני אתרים ו-hh היא עוצמת השדה המגנטי החיצוני.

קטע הקוד הבא ייצור את ה-Hamiltonian של שרשרת Ising בת 10 אתרים עם תנאי גבול מחזוריים.

# Added by doQumentation — required packages for this notebook
!pip install -q qiskit qiskit-addon-aqc-tensor qiskit-addon-utils qiskit-aer scipy
from qiskit.transpiler import CouplingMap
from qiskit_addon_utils.problem_generators import generate_xyz_hamiltonian
from qiskit.synthesis import SuzukiTrotter
from qiskit_addon_utils.problem_generators import (
generate_time_evolution_circuit,
)
from qiskit_addon_aqc_tensor import generate_ansatz_from_circuit
from qiskit_addon_aqc_tensor.simulation import tensornetwork_from_circuit
from qiskit_addon_aqc_tensor.simulation import compute_overlap
from qiskit_addon_aqc_tensor.objective import MaximizeStateFidelity
from qiskit_aer import AerSimulator
from scipy.optimize import OptimizeResult, minimize

# Generate some coupling map to use for this example
coupling_map = CouplingMap.from_heavy_hex(3, bidirectional=False)

# Choose a 10-qubit circle on this coupling map
reduced_coupling_map = coupling_map.reduce(
[0, 13, 1, 14, 10, 16, 4, 15, 3, 9]
)

# Get a qubit operator describing the Ising field model
hamiltonian = generate_xyz_hamiltonian(
reduced_coupling_map,
coupling_constants=(0.0, 0.0, 1.0),
ext_magnetic_field=(0.4, 0.0, 0.0),
)

פיצול אבולוציית הזמן לשני חלקים לביצוע קלאסי וקוונטי

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

  1. חלק ראשוני שניתן לסימולציה באמצעות מצבי מכפלת מטריצה (MPS). זהו החלק שיעבור "קומפילציה" באמצעות AQC-Tensor.
  2. חלק עוקב שיבוצע על חומרה קוונטית.

נבחר לתת למערכת להתפתח עד זמן tf=5t_f=5 ונשתמש ב-AQC-Tensor לדחיסת אבולוציית הזמן עד t=4t=4, ולאחר מכן לאוולציה בצעדי Trotter רגילים עד tft_f.

מכאן נייצר שני מעגלים: האחד יעבור דחיסה באמצעות AQC-Tensor והשני יבוצע על QPU. עבור המעגל הראשון, מכיוון שזה יסומלץ קלאסית באמצעות מצבי מכפלת מטריצה, נשתמש במספר רב של שכבות כדי למזער שגיאת Trotter. בינתיים, המעגל השני המדמה אבולוציית זמן מ-ti=4t_i=4 עד tf=5t_f=5 ישתמש בהרבה פחות שכבות כדי למזער עומק.

# Generate circuit to be compressed
aqc_evolution_time = 4.0
aqc_target_num_trotter_steps = 45

aqc_target_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_target_num_trotter_steps),
time=aqc_evolution_time,
)

# Generate circuit to execute on hardware
subsequent_evolution_time = 1.0
subsequent_num_trotter_steps = 5

subsequent_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=subsequent_num_trotter_steps),
time=subsequent_evolution_time,
)

לצורך השוואה, ניצור גם מעגל שלישי. מעגל שמתפתח עד t=4t=4, אך עם אותו מספר שכבות כמו המעגל השני המתפתח מ-ti=4t_i=4 עד tf=5t_f=5. זהו המעגל שהיינו מריצים אילולא השתמשנו בטכניקת AQC-Tensor. נקרא לו מעגל ההשוואה לעת עתה.

aqc_comparison_num_trotter_steps = int(
subsequent_num_trotter_steps
/ subsequent_evolution_time
* aqc_evolution_time
)
aqc_comparison_num_trotter_steps

comparison_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_comparison_num_trotter_steps),
time=aqc_evolution_time,
)

יצירת ה-ansatz ובניית סימולציית MPS

בשלב הבא נייצר את ה-ansatz שנאופטם. הוא יתפתח לאותו זמן אבולוציה כמו המעגל הראשון (מ-ti=0t_i=0 עד tf=4t_f=4), אך עם פחות צעדי Trotter.

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

aqc_ansatz_num_trotter_steps = 5

aqc_good_circuit = generate_time_evolution_circuit(
hamiltonian,
synthesis=SuzukiTrotter(reps=aqc_ansatz_num_trotter_steps),
time=aqc_evolution_time,
)

aqc_ansatz, aqc_initial_parameters = generate_ansatz_from_circuit(
aqc_good_circuit, qubits_initially_zero=True
)
aqc_ansatz.draw("mpl", fold=-1)

Output of the previous code cell

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

# Generate MPS simulator settings and obtain the MPS representation of the target state
simulator_settings = AerSimulator(
method="matrix_product_state",
matrix_product_state_max_bond_dimension=100,
)
aqc_target_mps = tensornetwork_from_circuit(
aqc_target_circuit, simulator_settings
)

# Compute the fidelity between the MPS representation of the target state and the state produced by the comparison circuit
comparison_mps = tensornetwork_from_circuit(
comparison_circuit, simulator_settings
)
comparison_fidelity = (
abs(compute_overlap(comparison_mps, aqc_target_mps)) ** 2
)
print(f"Comparison fidelity: {comparison_fidelity}")
Comparison fidelity: 0.9997111919739693

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

לבסוף, נבצע אופטימיזציה של מעגל ה-ansatz כך שיייצר את המצב המטרה בנאמנות גבוהה יותר מ-comparison_fidelity. פונקציית העלות למיזום תהיה MaximizeStateFidelity ותאופטם באמצעות האופטימייזר L-BFGS של scipy.

objective = MaximizeStateFidelity(
aqc_target_mps, aqc_ansatz, simulator_settings
)

stopping_point = 1 - comparison_fidelity

def callback(intermediate_result: OptimizeResult):
print(f"Intermediate result: Fidelity {1 - intermediate_result.fun:.8}")
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": 100},
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
Intermediate result: Fidelity 0.95084365
Intermediate result: Fidelity 0.98409893
Intermediate result: Fidelity 0.99142033
Intermediate result: Fidelity 0.99521405
Intermediate result: Fidelity 0.99566673
Intermediate result: Fidelity 0.99650054
Intermediate result: Fidelity 0.99683487
Intermediate result: Fidelity 0.99720426
Intermediate result: Fidelity 0.99761726
Intermediate result: Fidelity 0.99809073
Intermediate result: Fidelity 0.99838244
Intermediate result: Fidelity 0.99861841
Intermediate result: Fidelity 0.99874617
Intermediate result: Fidelity 0.99892696
Intermediate result: Fidelity 0.99908129
Intermediate result: Fidelity 0.99917737
Intermediate result: Fidelity 0.99925456
Intermediate result: Fidelity 0.99933134
Intermediate result: Fidelity 0.99947173
Intermediate result: Fidelity 0.99956469
Intermediate result: Fidelity 0.99964488
Intermediate result: Fidelity 0.99967419
Intermediate result: Fidelity 0.99968821
Intermediate result: Fidelity 0.9997448
Done after 24 iterations.

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

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

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

Output of the previous code cell

צעדים הבאים