Início rápido
title: Início rápido description: Um guia de início rápido para o pacote pauli-prop dos addons do Qiskit
Neste guia, usamos o pacote pauli-prop para simular classicamente a dinâmica temporal de um modelo de Ising com chutes de 10 qubits em uma cadeia de spins 1D.
Prepare as entradas para a propagação de Pauli
O Hamiltoniano considerado é:
onde descreve o acoplamento de spins vizinhos mais próximos, , e é o campo transversal global. Uma decomposição de Trotter de primeira ordem do operador evoluído no tempo será implementada como um circuito quântico, , ao longo de passos de Trotter. A constante de acoplamento, , será fixada em , e será fixado em . As interações serão implementadas usando portas Clifford (, , ).
Implementamos a evolução temporal de Trotter como um circuito quântico e usamos para as rotações não Clifford em torno do eixo x. Quanto mais distantes esses ângulos estiverem dos ângulos de Clifford (por exemplo, ), mais difícil será simular o sistema com métodos de propagação de Pauli.
Como observável, consideramos a magnetização média de um único sítio, , onde é o número de spins.
# 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)
Simule a evolução temporal do sistema com propagação de Pauli
Depois de ter nosso circuito, , e nosso observável, , podemos simular o sistema facilmente em poucos passos:
-
Separe em suas partes Clifford, , e não Clifford, , de modo que , usando
evolve_through_cliffords -
Propague através de , resultando em um novo operador, , usando
pauli_prop.propagate_through_circuit -
Evolua através da parte Clifford do circuito usando o suporte à evolução de Clifford incorporado ao Qiskit
-
Aproxime o valor esperado como somando os coeficientes em associados aos termos de Pauli totalmente diagonais (termos de Pauli que contêm apenas
IouZem todos os qubits). Lembre-se: isso é uma aproximação, pois truncamos termos de ao propagá-lo através da parte não Clifford do 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())
)
À medida que executamos cálculos maiores, as aproximações do valor esperado ficam mais precisas. Neste exemplo, saturamos todo o espaço de Pauli em torno de , o que se reflete no achatamento da curva entre os dois últimos pontos.
Embora o gráfico abaixo mostre uma convergência monótona, as simulações de propagação de Pauli em geral não convergem de forma monótona. Não é incomum ver um comportamento "irregular" nesse tipo de gráfico.
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')

