דלג לתוכן הראשי
פרויקט זה בשלב בטא. מצאת באג? פתח issue ב-GitHub. יש שאלה או רעיון? שאל ב-Discussions — נשמח לשמוע ממך!
מה איכות התרגום בדף זה?

התחלה מהירה


title: התחלה מהירה description: מדריך התחלה מהירה לחבילת ה-addons של Qiskit בשם pauli-prop​

במדריך הזה נשתמש בחבילה pauli-prop כדי לסמלץ בצורה קלאסית את הדינמיקה בזמן של מודל Ising עם בעיטות על 10 qubit בשרשרת ספינים חד-ממדית.

הכנת הקלטים ל-Pauli propagation​

ההמילטוניאן שבו נתמקד הוא:

H=−J∑⟨i,j⟩ZiZj+h∑iXiH = -J\sum\limits_{\langle i,j \rangle} Z_iZ_j + h\sum\limits_iX_i

כאשר J>0J>0 מתאר את הצימוד בין ספינים שכנים, i<ji<j, ו-hh הוא השדה הרוחבי הגלובלי. פירוק Trotter מסדר ראשון של האופרטור המתפתח בזמן ימומש כמעגל קוונטי, UU, על פני 2020 צעדי Trotter. קבוע הצימוד, JJ, יקבע ל-J=−π2J=-\frac{\pi}{2}, ו-hh יקבע ל-π6\frac{\pi}{6}. אינטראקציות ה-ZZZZ ימומשו באמצעות שערי Clifford ‏(CXCX, SdgSdg, Y\sqrt{Y}).

אנחנו מממשים את ההתפתחות בזמן בשיטת Trotter כמעגל קוונטי ומשתמשים ב-π6\frac{\pi}{6} לסיבובים שאינם Clifford סביב ציר x. ככל שהזוויות האלה רחוקות יותר מזוויות Clifford (לדוגמה, θ=nπ2,n∈Z\theta=n\frac{\pi}{2}, n \in \mathbb{Z}), כך יהיה קשה יותר לסמלץ את המערכת בשיטות Pauli propagation.

כאובזרבבל אנחנו בוחרים את המגנטיזציה הממוצעת של אתר בודד, 1N∑i=1N⟨zi⟩\frac{1}{N} \sum_{i=1}^{N} \langle z_i \rangle, כאשר NN הוא מספר הספינים.

# Added by doQumentation — installs the packages this notebook needs if they are missing
import importlib.util

_needed = {"matplotlib": "matplotlib", "numpy": "numpy", "pauli_prop": "pauli-prop", "qiskit": "qiskit", "qiskit_aer": "qiskit-aer"}
_missing = [pip for module, pip in _needed.items()
if importlib.util.find_spec(module) is None]
# One at a time, so a package that fails to install does not block the others
for _pip in _missing:
%pip install -q {_pip}
if not _missing:
print("\u2713 All required packages are installed")
import numpy as np
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.transpiler import CouplingMap

num_qubits = 10
coupling_map = CouplingMap.from_line(num_qubits, bidirectional=False)

# Num Trotter steps
num_steps = 20
theta_rx = np.pi / 6

# Average single-site magnetization
observable = (
SparsePauliOp(
[
"I" * iq + "Z" + "I" * (num_qubits - iq - 1)
for iq in range(num_qubits)
]
)
/ num_qubits
)

# Create the Trotter circuit
num_qubits = 10
num_steps = 20
theta_rx = np.pi / 6
circuit = QuantumCircuit(num_qubits)
edges = CouplingMap.from_line(num_qubits, bidirectional=False).get_edges()
for _ in range(num_steps):
circuit.rx(theta_rx, [i for i in range(num_qubits)])
for edge in edges:
circuit.sdg(edge)
circuit.ry(np.pi / 2, edge[1])
circuit.cx(edge[0], edge[1])
circuit.ry(-np.pi / 2, edge[1])
circuit.draw("mpl", fold=-1)

Output of the previous code cell

סימולציה של ההתפתחות בזמן של המערכת באמצעות Pauli propagation​

ברגע שיש לנו את המעגל, UU, ואת האובזרבבל, OO, אפשר לסמלץ את המערכת בכמה צעדים פשוטים:

  • מפרידים את UU לחלק ה-Clifford שלו, CC, ולחלק שאינו Clifford, PP, כך ש-U=PCU=PC, באמצעות evolve_through_cliffords

  • מקדמים את OO דרך PP, וכך מקבלים אופרטור חדש, O′O^\prime, באמצעות pauli_prop.propagate_through_circuit

  • מפתחים את O′O^\prime דרך חלק ה-Clifford של המעגל באמצעות התמיכה בהתפתחות Clifford המובנית ב-Qiskit

  • מקרבים את ערך התצפית כ-⟨0∣O′∣0⟩≈⟨0∣U†OU∣0⟩\langle0|O^\prime|0\rangle \approx \langle0|U^\dagger OU|0\rangle על ידי סכימת המקדמים ב-O′O^\prime המשויכים לאיברי Pauli אלכסוניים לגמרי (איברי Pauli שמכילים I או Z בלבד בכל ה-qubit). חשוב לזכור שזה קירוב, כי קטענו איברים מ-O′O^\prime בזמן שקידמנו אותו דרך החלק של המעגל שאינו Clifford.

import time

from pauli_prop import evolve_through_cliffords, propagate_through_circuit

cliff, non_cliff = evolve_through_cliffords(circuit)

max_terms_list = [10**i for i in range(8)]
approx_evs = []
durations = []
for max_terms in max_terms_list:
st = time.perf_counter()
evolved_obs = propagate_through_circuit(
observable, non_cliff, max_terms=max_terms, atol=1e-12, frame="h"
)[0]
evolved_obs.paulis = evolved_obs.paulis.evolve(cliff, frame="h")
durations.append(time.perf_counter() - st)
approx_evs.append(
float(evolved_obs.coeffs[~evolved_obs.paulis.x.any(axis=1)].sum())
)

ככל שמריצים חישובים גדולים יותר, קירובי ערך התצפית נעשים מדויקים יותר. בדוגמה הזאת אנחנו ממלאים את כל מרחב Pauli סביב 410≈1064^{10}\approx10^6, וזה מתבטא בכך שהעקומה מתיישרת בין שתי הנקודות האחרונות.

התרשים שלמטה מראה התכנסות מונוטונית, אבל סימולציות Pauli propagation לא מתכנסות בדרך כלל באופן מונוטוני. זה לא נדיר לראות התנהגות "מחוספסת" בתרשימים מהסוג הזה.

import matplotlib.pyplot as plt
from qiskit_aer import AerSimulator

sim_circ = circuit.copy()
sim_circ.save_statevector()
backend = AerSimulator(method="statevector")
psi = backend.run(sim_circ).result().data()["statevector"]
exact_ev = psi.expectation_value(observable)

ax1 = plt.gca()
ax1.plot(max_terms_list, approx_evs, marker="o", label="Approximate")
ax1.axhline(exact_ev, linestyle="--", color="green", label="Exact")
ax1.set_xscale("log")
ax1.set_xlabel("# terms kept")
ax1.set_ylabel(r"$\frac{1}{N} \sum_{i=1}^{N} \langle z_i \rangle$")

ax2 = ax1.twinx()
ax2.plot(
max_terms_list, durations, marker=".", label="Runtime", color="orange"
)
ax2.set_ylabel("Runtime (s)", color="orange")
ax2.set_yscale("log")

handles1, labels1 = ax1.get_legend_handles_labels()
handles2, labels2 = ax2.get_legend_handles_labels()
ax1.legend(handles1 + handles2, labels1 + labels2, loc="lower right")

plt.title(f"Simulating {num_steps}-step 1D Ising Model")
Text(0.5, 1.0, 'Simulating 20-step 1D Ising Model')

Output of the previous code cell