title: "Démarrage rapide" description: "Guide de démarrage rapide pour le package d'extensions pauli-prop de Qiskit"
Démarrage rapide
Dans ce guide, nous utilisons le package pauli-prop pour simuler de manière classique la dynamique temporelle d’un modèle d’Ising à 10 qubits avec effet de « kick » sur une chaîne de spins de type « 1D ».
Préparez les données d'entrée pour la propagation de Pauli
L'hamiltonien considéré est le suivant :
où décrit le couplage entre les spins voisins les plus proches, , et représente le champ transversal global. Une décomposition de Trotter du premier ordre de l'opérateur évolué dans le temps sera mise en œuvre sous la forme d'un circuit quantique, , sur étapes de Trotter. La constante de couplage, , sera fixée à , et sera fixée à . Les interactions seront mises en œuvre à l'aide de portes de Clifford ( , , ).
Nous mettons en œuvre l'évolution temporelle « trotterisée » sous la forme d'un circuit quantique et utilisons l' e pour les rotations non-Clifford autour de l'axe x. Plus ces angles s'éloignent des angles de Clifford (par exemple, ), plus la simulation du système sera difficile à réaliser avec les méthodes de propagation de Pauli.
Pour le choix de la grandeur observable, nous considérons la magnétisation moyenne d'un site unique, , où est le nombre de spins.
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:
Simuler l'évolution temporelle du système à l'aide de la propagation de Pauli
Une fois que nous disposons de notre circuit ( ) et de notre observable ( ), nous pouvons facilement simuler le système en quelques étapes :
- Décomposez l' en ses parties « Clifford » ( ) et « non-Clifford » ( ) de telle sorte que en utilisant
evolve_through_cliffords - Appliquer la fonction « » à « », ce qui donne un nouvel opérateur, « », en utilisant
pauli_prop.propagate_through_circuit - Faites évoluer l' e sur la partie Clifford du circuit à l'aide du support d'évolution Clifford intégré à Qiskit
- On peut approximer la valeur attendue par en additionnant les coefficients de associés aux termes de Pauli entièrement diagonaux (termes de Pauli contenant soit
IsoitZsur tous les qubits). N'oubliez pas qu'il s'agit d'une approximation, car nous avons tronqué des termes de l' e lors de sa propagation à travers la partie non-Clifford du circuit.
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())
)À mesure que nous effectuons des calculs de plus grande envergure, les approximations de la valeur attendue gagnent en précision. Dans cet exemple, nous saturons l'espace de Pauli complet aux alentours de , ce qui se traduit par un aplatissement de la courbe entre les deux derniers points.
Bien que le graphique ci-dessous montre une convergence monotone, les simulations de propagation de Pauli ne convergent généralement pas de manière monotone. Il n'est pas rare d'observer un tracé « irrégulier » sur ce type de graphiques.
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')