Nota
Esta página fue generada a partir de docs/tutorials/sea_with_sampler.ipynb.
Algoritmo de Solucionador Propio Espectroscópico con Sampler¶
Este tutorial demuestra la capacidad de enviar circuitos flexibles a la primitiva Sampler
mediante la realización de un ejemplo simple del Algoritmo de Solucionador Propio Espectroscópico (Spectroscopic Eigensolver Algorithm, SEA) (arXiv:2202.12910). El SEA se utiliza para la simulación cuántica de modelos Hamiltonianos y funciona mediante la interacción de un qubit auxiliar “probe” (“sonda”) con un registro de simulación. La energía del qubit sonda se barre y los valores propios de la simulación Hamiltoniana se observan como picos o caídas en la respuesta, similar a la herramienta experimental de la espectroscopía. Debido a que cada punto (energía) es un circuito cuántico diferente, esta técnica es costosa con respecto a la cantidad de circuitos necesarios. El Sampler
proporciona la flexibilidad de enviar un solo circuito con los Parameter
s necesarios.
Configura tu entorno de desarrollo local¶
Este tutorial requiere una instancia del servicio Qiskit Runtime. Si aún no lo has hecho, sigue estos pasos para configurar uno.
[1]:
# load necessary Runtime libraries
from qiskit_ibm_runtime import QiskitRuntimeService, Sampler, Session
backend = "ibmq_qasm_simulator" # use the simulator
Modelo Hamiltoniano simple¶
Consideremos una matriz de Pauli X actuando sobre un qubit,
donde podemos colocar \(\mu\) más tarde, o incluso barrer sus valores también. El SEA funciona tomando el Hamiltoniano del modelo Pauli (el qubit) y construyendo un Hamiltoniano de «resonancia» más grande que incluye tanto el registro de simulación como el qubit sonda q0
a través de
donde la frecuencia angular \(\omega\) corresponde a la energía del qubit sonda, y \(c\) es el acoplamiento entre el qubit sonda y un qubit en el registro de simulación (q1
en este caso ). Las letras \(I\), \(X\), y \(Z\) corresponden a las matrices de espín de Pauli y su orden refleja en qué qubit operan (ten en cuenta que esta notación es little-endian). Estableceremos \(\hbar \equiv 1\) en el siguiente código.
Podemos construir los circuitos SEA con herramientas de Qiskit Opflow.
[2]:
from qiskit.circuit import Parameter
from qiskit.opflow import I, X, Z
mu = Parameter("$\\mu$")
ham_pauli = mu * X
[3]:
cc = Parameter("$c$")
ww = Parameter("$\\omega$")
ham_res = -(1 / 2) * ww * (I ^ Z) + cc * (X ^ X) + (ham_pauli ^ I)
El tiempo evoluciona el Hamiltoniano de resonancia.
[4]:
tt = Parameter("$t$")
U_ham = (tt * ham_res).exp_i()
Del operador de evolución temporal \(U_{\rm ham}\), usamos la expansión Suzuki-Trotter para convertir este operador en circuitos cuánticos que implementan pasos de tiempo discretos de la simulación. Cuanto más pequeños sean los pasos de tiempo (más pasos de Trotter), más preciso será el circuito cuántico, pero también más profundo, lo que podría introducir errores al ejecutar en hardware cuántico ruidoso. Luego, transpilamos los circuitos a las compuertas base del backend de IBM y medimos solo el qubit sonda q0
.
[5]:
from qiskit import transpile
from qiskit.circuit import ClassicalRegister
from qiskit.opflow import PauliTrotterEvolution, Suzuki
import numpy as np
num_trot_steps = 5
total_time = 10
cr = ClassicalRegister(1, "c")
spec_op = PauliTrotterEvolution(
trotter_mode=Suzuki(order=2, reps=num_trot_steps)
).convert(U_ham)
spec_circ = spec_op.to_circuit()
spec_circ_t = transpile(spec_circ, basis_gates=["sx", "rz", "cx"])
spec_circ_t.add_register(cr)
spec_circ_t.measure(0, cr[0])
[5]:
<qiskit.circuit.instructionset.InstructionSet at 0x1273c9a00>
[6]:
spec_circ_t.draw("mpl")
[6]:

Ahora fijemos nuestros parámetros y hagamos un barrido sobre la frecuencia con varios puntos num_pts
. Los valores propios de nuestro modelo Hamiltoniano \(H_{\rm Pauli}\) son \(\pm \mu\), por lo que debemos elegir un rango que incluya esos números.
Ten en cuenta que las claves y valores de los Parameter
s deben separarse y convertirse en una List
de List
s. Las claves nos dan los Parameter
s dentro de cada circuito. En este caso, tenemos solo un circuito, por lo que la List
de claves contiene una sola List
. Para los valores de Parameter
, hay una List
para cada valor de ww
.
[7]:
# fixed Parameters
fixed_params = {cc: 0.3, mu: 0.7, tt: total_time}
# Parameter value for single circuit
param_keys = list(spec_circ_t.parameters)
# run through all the ww values to create a List of Lists of Parameter value
num_pts = 101
wvals = np.linspace(-2, 2, num_pts)
param_vals = []
for wval in wvals:
all_params = {**fixed_params, **{ww: wval}}
param_vals.append([all_params[key] for key in param_keys])
Cuando llamamos al sampler
, especificamos una lista de circuits
que apunta a los circuitos que deseamos ejecutar y los valores de los parámetros para cada circuito.
[8]:
with Session(backend=backend):
sampler = Sampler()
job = sampler.run(
circuits=[spec_circ_t] * num_pts, parameter_values=param_vals, shots=1e5
)
result = job.result()
Construye los valores esperados de \(Z\) convirtiendo las cuasi-probabilidades en \(\langle Z \rangle\).
[9]:
Zexps = []
for dist in result.quasi_dists:
if 1 in dist:
Zexps.append(1 - 2 * dist[1])
else:
Zexps.append(1)
Como verificación rápida, calcularemos los valores esperados exactos con Qiskit Opflow.
[10]:
from qiskit.opflow import PauliExpectation, Zero
param_bind = {cc: 0.3, mu: 0.7, tt: total_time}
init_state = Zero ^ 2
obsv = I ^ Z
Zexp_exact = (U_ham @ init_state).adjoint() @ obsv @ (U_ham @ init_state)
diag_meas_op = PauliExpectation().convert(Zexp_exact)
Zexact_values = []
for w_set in wvals:
param_bind[ww] = w_set
Zexact_values.append(np.real(diag_meas_op.bind_parameters(param_bind).eval()))
Y graficar todo junto muestra que la energía en la que ocurren nuestros picos es \(\pm \mu\).
[11]:
import matplotlib.pyplot as plt
plt.style.use("dark_background")
fig, ax = plt.subplots(dpi=100)
ax.plot([-param_bind[mu], -param_bind[mu]], [0, 1], ls="--", color="purple")
ax.plot([param_bind[mu], param_bind[mu]], [0, 1], ls="--", color="purple")
ax.plot(wvals, Zexact_values, label="Exact")
ax.plot(wvals, Zexps, label=f"{backend}")
ax.set_xlabel(r"$\omega$ (arb)")
ax.set_ylabel(r"$\langle Z \rangle$ Expectation")
ax.legend()
[11]:
<matplotlib.legend.Legend at 0x12c286cd0>

[12]:
import qiskit_ibm_runtime
qiskit_ibm_runtime.version.get_version_info()
[12]:
'0.7.0'
[13]:
from qiskit.tools.jupyter import *
%qiskit_version_table
Version Information
Qiskit Software | Version |
---|---|
qiskit-terra | 0.20.0 |
qiskit-aer | 0.10.3 |
qiskit-ignis | 0.7.0 |
qiskit-ibmq-provider | 0.18.3 |
qiskit | 0.35.0 |
System information | |
Python version | 3.9.10 |
Python compiler | Clang 11.1.0 |
Python build | main, Feb 1 2022 21:27:48 |
OS | Darwin |
CPUs | 2 |
Memory (Gb) | 16.0 |
Mon Apr 11 16:17:45 2022 EDT |