Saltar al contenido principal
Este proyecto está en fase beta. ¿Encontraste un error? Abre un issue en GitHub. ¿Tienes una pregunta o una idea? Pregunta en Discussions — ¡nos encantaría tu opinión!
¿Cómo es la calidad de la traducción en esta página?

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:

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

donde J>0J>0 describe el acoplamiento de espines vecinos más cercanos, i<ji<j, y hh 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, UU, a lo largo de 2020 pasos de Trotter. La constante de acoplamiento, JJ, se fijará en J=−π2J=-\frac{\pi}{2}, y hh se fijará en π6\frac{\pi}{6}. Las interacciones ZZZZ se implementarán usando puertas Clifford (CXCX, SdgSdg, Y\sqrt{Y}).

Implementamos la evolución temporal de Trotter como un circuito cuántico y usamos π6\frac{\pi}{6} para las rotaciones no Clifford alrededor del eje x. Cuanto más se alejen estos ángulos de los ángulos Clifford (por ejemplo, θ=nπ2,n∈Z\theta=n\frac{\pi}{2}, n \in \mathbb{Z}), 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, 1N∑i=1N⟨zi⟩\frac{1}{N} \sum_{i=1}^{N} \langle z_i \rangle, donde NN 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)

Output of the previous code cell

Simula la evolución temporal del sistema con propagación de Pauli​

Una vez que tenemos nuestro circuito, UU, y el observable, OO, podemos simular el sistema fácilmente en unos pocos pasos:

  • Separa UU en su parte Clifford, CC, y su parte no Clifford, PP, de modo que U=PCU=PC, usando evolve_through_cliffords

  • Propaga OO a través de PP, obteniendo un nuevo operador, O′O^\prime, usando pauli_prop.propagate_through_circuit

  • Evoluciona O′O^\prime a través de la parte Clifford del circuito usando el soporte de evolución Clifford integrado en Qiskit

  • Aproxima el valor esperado como ⟨0∣O′∣0⟩≈⟨0∣U†OU∣0⟩\langle0|O^\prime|0\rangle \approx \langle0|U^\dagger OU|0\rangle sumando los coeficientes de O′O^\prime asociados con términos de Pauli totalmente diagonales (términos de Pauli que contienen I o Z en todos los qubits). Recuerda que se trata de una aproximación, porque truncamos términos de O′O^\prime 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 410≈1064^{10}\approx10^6, 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')

Output of the previous code cell