Szybki start
title: Szybki start description: Przewodnik szybkiego startu dla pakietu dodatków Qiskit pauli-prop
W tym przewodniku używamy pakietu pauli-prop, aby klasycznie zasymulować dynamikę czasową 10-kubitowego modelu Isinga z kopnięciami (kicked Ising) na jednowymiarowym łańcuchu spinów.
Przygotuj dane wejściowe do propagacji Pauliego
Rozważany hamiltonian ma postać:
gdzie opisuje sprzężenie spinów najbliższych sąsiadów, , a jest globalnym polem poprzecznym. Rozkład Trottera pierwszego rzędu operatora ewolucji w czasie zostanie zaimplementowany jako obwód kwantowy, , o krokach Trottera. Stała sprzężenia, , zostanie ustalona na , a na . Oddziaływania zostaną zaimplementowane za pomocą bramek Clifforda (, , ).
Implementujemy ewolucję w czasie metodą Trottera jako obwód kwantowy i używamy dla obrotów niecliffordowskich wokół osi x. Im dalej te kąty są od kątów Clifforda (na przykład ), tym trudniej symulować układ metodami propagacji Pauliego.
Jako obserwablę wybieramy średnią magnetyzację pojedynczego węzła, , gdzie jest liczbą spinów.
# 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)
Zasymuluj ewolucję czasową układu za pomocą propagacji Pauliego
Gdy mamy już obwód, , i obserwablę, , możemy łatwo zasymulować układ w kilku krokach:
-
Rozdziel na część cliffordowską, , i niecliffordowską, , tak aby , używając
evolve_through_cliffords -
Propaguj przez , otrzymując nowy operator, , za pomocą
pauli_prop.propagate_through_circuit -
Przeprowadź ewolucję przez cliffordowską część obwodu, używając obsługi ewolucji Clifforda wbudowanej w Qiskit
-
Przybliż wartość oczekiwaną jako , sumując współczynniki w związane z w pełni diagonalnymi wyrazami Pauliego (wyrazami Pauliego zawierającymi
IlubZna wszystkich kubitach). Pamiętaj, że jest to przybliżenie, ponieważ obcinaliśmy wyrazy z podczas jego propagacji przez niecliffordowską część obwodu.
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())
)
Gdy wykonujemy większe obliczenia, przybliżenia wartości oczekiwanej stają się dokładniejsze. W tym przykładzie wysycamy całą przestrzeń Pauliego około , co widać po wypłaszczeniu się krzywej między dwoma ostatnimi punktami.
Choć poniższy wykres pokazuje monotoniczną zbieżność, symulacje propagacji Pauliego na ogół nie zbiegają monotonicznie. Nie jest niczym niezwykłym, że na takich wykresach widać „poszarpane” zachowanie.
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')

