Skip to main content
IBM Quantum Platform


title: "Schnellstart" description: "Eine Kurzanleitung zum Qiskit-Add-ons-Paket „pauli-prop“"

Schnellstart

In dieser Anleitung verwenden wir das Paket pauli-prop , um die zeitliche Dynamik eines 10-Qubit-Kicked-Ising-Modells auf einer Spin-Kette vom Typ „ 1D “ klassisch zu simulieren.


Bereiten Sie die Eingaben für die Pauli-Ausbreitung vor

Der betrachtete Hamilton-Operator lautet:

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

wobei J>0J>0 die Kopplung zwischen Spins nächster Nachbarn beschreibt, i<ji<j und hh das globale Transversalfeld ist. Eine Trotter-Zerlegung erster Ordnung des zeitabhängigen Operators wird als Quantenschaltung „ UU “ über 2020 Trotter-Schritte implementiert. Die Kopplungskonstante „ JJ “ wird auf „ J=−π2J=-\frac{\pi}{2} “ festgelegt, und „ hh “ wird auf „ π6\frac{\pi}{6} “ festgelegt. Die Wechselwirkungen „ ZZZZ “ werden mithilfe von Clifford-Gattern implementiert ( CXCX, SdgSdg, Y\sqrt{Y} ).

Wir implementieren die Trotterisierte Zeitentwicklung als Quantenschaltung und verwenden „ π6\frac{\pi}{6} “ für die nicht-Clifford-Rotationen um die x-Achse. Je weiter diese Winkel von den Clifford-Winkeln abweichen (zum Beispiel θ=nπ2,n∈Z\theta=n\frac{\pi}{2}, n \in \mathbb{Z} ), desto schwieriger wird die Simulation des Systems mit Pauli-Ausbreitungsmethoden.

Als Observable wählen wir die durchschnittliche Magnetisierung an einem einzelnen Ort, 1N∑i=1N⟨zi⟩\frac{1}{N} \sum_{i=1}^{N} \langle z_i \rangle, wobei NN die Anzahl der Spins ist.

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:

Output of the previous code cell

Simuliere die zeitliche Entwicklung des Systems mit der Pauli-Ausbreitung

Sobald wir unseren Schaltkreis UU und unsere Observable OO haben, können wir das System in wenigen Schritten ganz einfach simulieren:

  • Teile UU in seine Clifford-Komponenten CC und seine Nicht-Clifford-Komponenten PP auf, sodass U=PCU=PC gilt, und zwar unter Verwendung von evolve_through_cliffords
  • Führen Sie die Ausbreitung von „ OO “ über „ PP “ durch, was zu einem neuen Operator „ O′O^\prime “ führt, und zwar mithilfe von pauli_prop.propagate_through_circuit
  • Entwickeln Sie „ O′O^\prime “ im Clifford-Teil des Schaltkreises mithilfe der in Qiskit integrierten Unterstützung für die Clifford-Evolution weiter
  • Man approximiert den Erwartungswert als ⟨0∣O′∣0⟩≈⟨0∣U†OU∣0⟩\langle0|O^\prime|0\rangle \approx \langle0|U^\dagger OU|0\rangle, indem man die Koeffizienten in O′O^\prime summiert, die mit vollständig diagonalen Pauli-Termen verbunden sind (Pauli-Terme, die auf Z allen Qubits entweder I oder enthalten). Denken Sie daran, dass es sich hierbei um eine Näherung handelt, da wir Terme aus O′O^\prime abgeschnitten haben, als wir diese durch den Nicht-Clifford-Teil der Schaltung weitergeleitet haben.
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())
    )

Je umfangreicher die Berechnungen sind, desto genauer werden die Näherungswerte für die Erwartungswerte. In diesem Beispiel erreichen wir bei etwa 410≈1064^{10}\approx10^6 die Sättigung des gesamten Pauli-Raums, was sich darin zeigt, dass die Kurve zwischen den letzten beiden Punkten abflacht.

Während die untenstehende Grafik eine monotone Konvergenz zeigt, konvergieren Pauli-Ausbreitungssimulationen im Allgemeinen nicht monoton. Es ist nicht ungewöhnlich, dass solche Diagramme ein „unregelmäßiges“ Verhalten aufweisen.

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

Output:

Text(0.5, 1.0, 'Simulating 20-step 1D Ising Model')
Output of the previous code cell
War diese Seite hilfreich?
Melden Sie auf GitHub einen Fehler, einen Tippfehler oder fordern Sie Inhalte an.