Skip to main content
IBM Quantum Platform


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 :

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

où J>0J>0 décrit le couplage entre les spins voisins les plus proches, i<ji<j, et hh 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, UU, sur 2020 étapes de Trotter. La constante de couplage, JJ, sera fixée à J=−π2J=-\frac{\pi}{2}, et hh sera fixée à π6\frac{\pi}{6}. Les interactions ZZZZ seront mises en œuvre à l'aide de portes de Clifford ( CXCX, SdgSdg, Y\sqrt{Y} ).

Nous mettons en œuvre l'évolution temporelle « trotterisée » sous la forme d'un circuit quantique et utilisons l' π6\frac{\pi}{6} e pour les rotations non-Clifford autour de l'axe x. Plus ces angles s'éloignent des angles de Clifford (par exemple, θ=nπ2,n∈Z\theta=n\frac{\pi}{2}, n \in \mathbb{Z} ), 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, 1N∑i=1N⟨zi⟩\frac{1}{N} \sum_{i=1}^{N} \langle z_i \rangle, où NN 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:

Output of the previous code cell

Simuler l'évolution temporelle du système à l'aide de la propagation de Pauli

Une fois que nous disposons de notre circuit ( UU ) et de notre observable ( OO ), nous pouvons facilement simuler le système en quelques étapes :

  • Décomposez l' UU en ses parties « Clifford » ( CC ) et « non-Clifford » ( PP ) de telle sorte que U=PCU=PC en utilisant evolve_through_cliffords
  • Appliquer la fonction « OO » à « PP », ce qui donne un nouvel opérateur, « O′O^\prime », en utilisant pauli_prop.propagate_through_circuit
  • Faites évoluer l' O′O^\prime e sur la partie Clifford du circuit à l'aide du support d'évolution Clifford intégré à Qiskit
  • On peut approximer la valeur attendue par ⟨0∣O′∣0⟩≈⟨0∣U†OU∣0⟩\langle0|O^\prime|0\rangle \approx \langle0|U^\dagger OU|0\rangle en additionnant les coefficients de O′O^\prime associés aux termes de Pauli entièrement diagonaux (termes de Pauli contenant soit I soit Z sur tous les qubits). N'oubliez pas qu'il s'agit d'une approximation, car nous avons tronqué des termes de l' O′O^\prime 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 410≈1064^{10}\approx10^6, 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')
Output of the previous code cell
Cette page a-t-elle été utile ?
Signaler un bogue, une coquille ou proposer du contenu sur GitHub.