Inicio rápido
title: Inicio rápido description: Una guía de inicio rápido para el paquete de Qiskit addons pauli-prop
En esta guía usamos el paquete pauli-prop para simular de forma clásica la dinámica temporal de un modelo de Ising con pulsos de 10 qubits en una cadena de espines 1D.
Prepara las entradas para la propagación de Pauli
El hamiltoniano considerado es:
donde describe el acoplamiento de espines vecinos más cercanos, , y es el campo transversal global. Se implementará una descomposición de Trotter de primer orden del operador evolucionado en el tiempo como un circuito cuántico, , a lo largo de pasos de Trotter. La constante de acoplamiento, , se fijará en , y se fijará en . Las interacciones se implementarán usando puertas Clifford (, , ).
Implementamos la evolución temporal de Trotter como un circuito cuántico y usamos para las rotaciones no Clifford alrededor del eje x. Cuanto más se alejen estos ángulos de los ángulos Clifford (por ejemplo, ), más difícil será simular el sistema con los métodos de propagación de Pauli.
Como observable, consideramos la magnetización promedio de un solo sitio, , donde es el número de espines.
# 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)
Simula la evolución temporal del sistema con propagación de Pauli
Una vez que tenemos nuestro circuito, , y el observable, , podemos simular el sistema fácilmente en unos pocos pasos:
-
Separa en su parte Clifford, , y su parte no Clifford, , de modo que , usando
evolve_through_cliffords -
Propaga a través de , obteniendo un nuevo operador, , usando
pauli_prop.propagate_through_circuit -
Evoluciona a través de la parte Clifford del circuito usando el soporte de evolución Clifford integrado en Qiskit
-
Aproxima el valor esperado como sumando los coeficientes de asociados con términos de Pauli totalmente diagonales (términos de Pauli que contienen
IoZen todos los qubits). Recuerda que se trata de una aproximación, porque truncamos términos de al propagarlo a través de la parte no Clifford del circuito.
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())
)
A medida que ejecutamos cálculos más grandes, las aproximaciones del valor esperado se vuelven más precisas. En este ejemplo saturamos todo el espacio de Pauli en torno a , lo cual se refleja en que la curva se aplana entre los dos últimos puntos.
Aunque el gráfico siguiente muestra una convergencia monótona, las simulaciones de propagación de Pauli en general no convergen de forma monótona. No es raro ver un comportamiento "irregular" en este tipo de gráficos.
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')

