Kvantová simulace
Yukio Kawashima (May 30, 2024)
Stáhni PDF původní přednášky. Některé ukázky kódu mohou být zastaralé, protože se jedná o statické obrázky.
Přibližná doba QPU pro spuštění tohoto experimentu je 7 sekund.
(Tento notebook je z větší části převzat z nyní zastaralého výukového notebooku pro Qiskit Algorithms.)
1. Úvod
Trotterizace je technika evoluce v reálném čase, která spočívá v postupné aplikaci kvantového Gate nebo Gates, zvolených tak, aby aproximovaly časovou evoluci systému pro daný časový úsek. Z rovnice Schrödingera vyplývá, že časová evoluce systému, který se na začátku nachází ve stavu , má tvar:
kde je na čase nezávislý Hamiltonian řídící systém. Uvažujeme Hamiltonian, který lze zapsat jako vážený součet Pauliho členů , přičemž představuje tenzorový součin Pauliho členů působících na Qubitech. Tyto Pauliho členy spolu mohou, ale nemusí komutovat. Máme-li stav v čase , jak pomocí kvantového počítače získáme stav systému v pozdějším čase ? Exponenciálu operátoru nejlépe porozumíme prostřednictvím jeho Taylorova rozvoje:
Některé velmi jednoduché exponenciály, jako , lze na kvantových počítačích snadno implementovat pomocí kompaktní sady kvantových Gates. Většina zajímavých Hamiltonianů nebude mít jen jeden člen, ale bude jich mít mnoho. Všimni si, co se stane, když :
Když a komutují, platí dobře známý případ (platný také pro čísla a proměnné a níže):
Pokud však operátory nekomutují, nelze členy v Taylorově rozvoji přeuspořádat tak, aby se zjednodušily. Vyjádření složitých Hamiltonianů pomocí kvantových Gates je tedy výzva.
Jedním z řešení je uvažovat velmi malý čas , takže v Taylorově rozvoji dominuje člen prvního řádu. Za tohoto předpokladu:
Stav ovšem může být potřeba vyvíjet po delší dobu. Toho dosáhneme mnoha takovými malými časovými kroky. Tento postup se nazývá Trotterizace:
Zde je časový úsek (evoluční krok), který volíme. Výsledkem je Gate, která se aplikuje -krát. Menší časový krok vede k přesnější aproximaci. To však zároveň vede k hlubším Circuitům, které v praxi způsobují větší hromadění chyb (nezanedbatelný problém u kvantových zařízení blízké budoucnosti).
Dnes budeme studovat časovou evoluci Isingova modelu na lineárních mřížkách s a místy. Tyto mřížky se skládají z pole spinů , které interagují pouze se svými nejbližšími sousedy. Tyto spiny mohou mít dvě orientace: a , které odpovídají magnetizaci a .
kde popisuje interakční energii a velikost vnějšího pole (ve směru x výše, ale to upravíme). Zapišme tento výraz pomocí Pauliho matic s uvážením, že vnější pole svírá úhel s příčným směrem:
Tento Hamiltonian je užitečný tím, že nám umožňuje snadno studovat účinky vnějšího pole. V kompučtační bázi bude systém zakódován takto:
| Kvantový stav | Spinová reprezentace |
|---|---|
Začneme zkoumat časovou evoluci takového kvantového systému. Konkrétně budeme vizualizovat časovou evoluci určitých vlastností systému, jako je magnetizace.
1.1 Požadavky
Před zahájením tohoto tutoriálu se ujisti, že máš nainstalováno následující:
- Qiskit SDK v1.2 nebo novější (
pip install qiskit) - Qiskit Runtime v0.30 nebo novější (
pip install qiskit-ibm-runtime) - Numpy v1.24.1 nebo novější < 2 (
pip install numpy)
1.2 Import knihoven
Všimni si, že některé knihovny, které by mohly být užitečné (MatrixExponential, QDrift), jsou zahrnuty, i když se v tomto notebooku nepoužívají. Pokud budeš mít čas, můžeš je vyzkoušet!
# Added by doQumentation — required packages for this notebook
!pip install -q matplotlib numpy qiskit qiskit-ibm-runtime
# Check the version of Qiskit
import qiskit
qiskit.__version__
'2.0.2'
# Import the qiskit library
import numpy as np
import matplotlib.pylab as plt
import warnings
from qiskit import QuantumCircuit
from qiskit.circuit.library import PauliEvolutionGate
from qiskit.primitives import StatevectorEstimator
from qiskit.quantum_info import Statevector, SparsePauliOp
from qiskit.synthesis import (
SuzukiTrotter,
LieTrotter,
)
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
from qiskit_ibm_runtime import QiskitRuntimeService, SamplerV2
warnings.filterwarnings("ignore")
2. Mapování tvého problému
2.1 Definování transverzálního Ising Hamiltoniánu
Zde uvažujeme 1D transverzální Ising model.
Nejprve vytvoříme funkci, která přijme parametry systému , , a a vrátí náš Hamiltonian jako SparsePauliOp. SparsePauliOp je řídká reprezentace operátoru z hlediska váhovaných termů Pauli.
def get_hamiltonian(nqubits, J, h, alpha):
# List of Hamiltonian terms as 3-tuples containing
# (1) the Pauli string,
# (2) the qubit indices corresponding to the Pauli string,
# (3) the coefficient.
ZZ_tuples = [("ZZ", [i, i + 1], -J) for i in range(0, nqubits - 1)]
Z_tuples = [("Z", [i], -h * np.sin(alpha)) for i in range(0, nqubits)]
X_tuples = [("X", [i], -h * np.cos(alpha)) for i in range(0, nqubits)]
# We create the Hamiltonian as a SparsePauliOp, via the method
# `from_sparse_list`, and multiply by the interaction term.
hamiltonian = SparsePauliOp.from_sparse_list(
[*ZZ_tuples, *Z_tuples, *X_tuples], num_qubits=nqubits
)
return hamiltonian.simplify()
Definování Hamiltoniánu
Systém, který nyní uvažujeme, má velikost , , a jako příklad.
n_qubits = 6
hamiltonian = get_hamiltonian(nqubits=n_qubits, J=0.2, h=1.2, alpha=np.pi / 8.0)
hamiltonian
SparsePauliOp(['IIIIZZ', 'IIIZZI', 'IIZZII', 'IZZIII', 'ZZIIII', 'IIIIIZ', 'IIIIZI', 'IIIZII', 'IIZIII', 'IZIIII', 'ZIIIII', 'IIIIIX', 'IIIIXI', 'IIIXII', 'IIXIII', 'IXIIII', 'XIIIII'],
coeffs=[-0.2 +0.j, -0.2 +0.j, -0.2 +0.j, -0.2 +0.j,
-0.2 +0.j, -0.45922012+0.j, -0.45922012+0.j, -0.45922012+0.j,
-0.45922012+0.j, -0.45922012+0.j, -0.45922012+0.j, -1.10865544+0.j,
-1.10865544+0.j, -1.10865544+0.j, -1.10865544+0.j, -1.10865544+0.j,
-1.10865544+0.j])
2.2 Nastavení parametrů simulace časového vývoje
Zde budeme uvažovat tři různé techniky Trotterizace:
- Lie–Trotter (první řád)
- Suzuki–Trotter druhého řádu
- Suzuki–Trotter čtvrtého řádu
Poslední dvě budou použity v cvičení a v příloze.
num_timesteps = 60
evolution_time = 30.0
dt = evolution_time / num_timesteps
product_formula_lt = LieTrotter()
product_formula_st2 = SuzukiTrotter(order=2)
product_formula_st4 = SuzukiTrotter(order=4)
2.3 Příprava kvantového Circuit 1 (Počáteční stav)
Vytvoř počáteční stav. Zde začneme se spinovou konfigurací .
initial_circuit = QuantumCircuit(n_qubits)
initial_circuit.prepare_state("001100")
# Change reps and see the difference when you decompose the circuit
initial_circuit.decompose(reps=1).draw("mpl")
2.4 Příprava kvantového Circuit 2 (Jeden Circuit pro časový vývoj)
Zde konstruujeme Circuit pro jeden časový krok pomocí Lie–Trotter.
Lieův produktový vzorec (prvního řádu) je implementován ve třídě LieTrotter. Vzorec prvního řádu se skládá z aproximace uvedené v úvodu, kde je maticový exponenciál součtu aproximován součinem maticových exponenciálů:
Jak bylo zmíněno dříve, velmi hluboké Circuit vedou k hromadění chyb a způsobují problémy moderním kvantovým počítačům. Protože dvouQubitové Gate mají vyšší míru chyb než jednoQubitové Gate, veličinou zvláštního zájmu je hloubka dvouQubitového Circuit. Na čem skutečně záleží, je hloubka dvouQubitového Circuit po transpilaci (protože to je Circuit, který kvantový počítač skutečně vykonává). Ale pojďme si zvyknout počítat operace pro tento Circuit, i teď při používání simulátoru.
single_step_evolution_gates_lt = PauliEvolutionGate(
hamiltonian, dt, synthesis=product_formula_lt
)
single_step_evolution_lt = QuantumCircuit(n_qubits)
single_step_evolution_lt.append(
single_step_evolution_gates_lt, single_step_evolution_lt.qubits
)
print(
f"""
Trotter step with Lie-Trotter
-----------------------------
Depth: {single_step_evolution_lt.decompose(reps=3).depth()}
Gate count: {len(single_step_evolution_lt.decompose(reps=3))}
Nonlocal gate count: {single_step_evolution_lt.decompose(reps=3).num_nonlocal_gates()}
Gate breakdown: {", ".join([f"{k.upper()}: {v}" for k, v in single_step_evolution_lt.decompose(reps=3).count_ops().items()])}
"""
)
single_step_evolution_lt.decompose(reps=3).draw("mpl", fold=-1)
Trotter step with Lie-Trotter
-----------------------------
Depth: 17
Gate count: 27
Nonlocal gate count: 10
Gate breakdown: U3: 12, CX: 10, U1: 5
2.5 Nastavení operátorů k měření
Definujme operátor magnetizace a operátor průměrné spinové korelace .
magnetization = (
SparsePauliOp.from_sparse_list(
[("Z", [i], 1.0) for i in range(0, n_qubits)], num_qubits=n_qubits
)
/ n_qubits
)
correlation = SparsePauliOp.from_sparse_list(
[("ZZ", [i, i + 1], 1.0) for i in range(0, n_qubits - 1)], num_qubits=n_qubits
) / (n_qubits - 1)
print("magnetization : ", magnetization)
print("correlation : ", correlation)
magnetization : SparsePauliOp(['IIIIIZ', 'IIIIZI', 'IIIZII', 'IIZIII', 'IZIIII', 'ZIIIII'],
coeffs=[0.16666667+0.j, 0.16666667+0.j, 0.16666667+0.j, 0.16666667+0.j,
0.16666667+0.j, 0.16666667+0.j])
correlation : SparsePauliOp(['IIIIZZ', 'IIIZZI', 'IIZZII', 'IZZIII', 'ZZIIII'],
coeffs=[0.2+0.j, 0.2+0.j, 0.2+0.j, 0.2+0.j, 0.2+0.j])
2.6 Provedení simulace časového vývoje
Budeme sledovat energii (střední hodnotu Hamiltoniánu), magnetizaci (střední hodnotu operátoru magnetizace) a průměrnou spinovou korelaci (střední hodnotu operátoru průměrné spinové korelace). Qiskitův StatevectorEstimator (EstimatorV2) primitiv odhaduje střední hodnoty pozorovatelných veličin, .
# Initiate the circuit
evolved_state = QuantumCircuit(initial_circuit.num_qubits)
# Start from the initial spin configuration
evolved_state.append(initial_circuit, evolved_state.qubits)
# Initiate Estimator (V2)
estimator = StatevectorEstimator()
# Set number of shots
shots = 10000
# Translate the precision required from the number of shots
precision = np.sqrt(1 / shots)
energy_list = []
mag_list = []
corr_list = []
# Estimate expectation values for t=0.0
job = estimator.run(
[(evolved_state, [hamiltonian, magnetization, correlation])], precision=precision
)
# Get estimated expectation values
evs = job.result()[0].data.evs
energy_list.append(evs[0])
mag_list.append(evs[1])
corr_list.append(evs[2])
# Start time evolution
for n in range(num_timesteps):
# Expand the circuit to describe delta-t
evolved_state.append(single_step_evolution_gates_lt, evolved_state.qubits)
# Estimate expectation values at delta-t
job = estimator.run(
[(evolved_state, [hamiltonian, magnetization, correlation])],
precision=precision,
)
# Retrieve results (expectation values)
evs = job.result()[0].data.evs
energy_list.append(evs[0])
mag_list.append(evs[1])
corr_list.append(evs[2])
# Transform the list of expectation values (at each time step) to arrays
energy_array = np.array(energy_list)
mag_array = np.array(mag_list)
corr_array = np.array(corr_list)
2.7 Vykreslení časového vývoje pozorovatelných veličin
Vykreslíme střední hodnoty, které jsme naměřili, v závislosti na čase.
fig, axes = plt.subplots(3, sharex=True)
times = np.linspace(0, evolution_time, num_timesteps + 1) # includes initial state
axes[0].plot(
times,
energy_array,
label="First order",
marker="x",
c="darkmagenta",
ls="-",
lw=0.8,
)
axes[1].plot(
times, mag_array, label="First order", marker="x", c="darkmagenta", ls="-", lw=0.8
)
axes[2].plot(
times, corr_array, label="First order", marker="x", c="darkmagenta", ls="-", lw=0.8
)
axes[0].set_ylabel("Energy")
axes[1].set_ylabel("Magnetization")
axes[2].set_ylabel("Mean spin correlation")
axes[2].set_xlabel("Time")
fig.suptitle("Observable evolution")
Text(0.5, 0.98, 'Observable evolution')

3. Cvičení 1. Proveď simulaci pomocí Suzuki–Trotterovy metody druhého řádu
Teď zkusíme provést simulaci pomocí Suzuki–Trotterovy metody druhého řádu podle vzoru Lie–Trotterovy metody ukázané výše.
Suzuki–Trotterovu metodu druhého řádu lze v Qiskitu použít prostřednictvím třídy SuzukiTrotter. Při použití tohoto vzorce je dekompozice druhého řádu: