Instancias y extensiones
En este capítulo se presentan varios algoritmos variacionales cuánticos, entre ellos:
- Variational Quantum Eigensolver (VQE)
- Subspace Search VQE (SSVQE)
- Variational Quantum Deflation (VQD)
- Quantum Sampling Regression (QSR)
A través de estos algoritmos aprenderás varias ideas de diseño que pueden incorporarse en algoritmos variacionales personalizados, como pesos, penalizaciones, sobremuestreo y submuestreo. Te animamos a experimentar con estos conceptos y a compartir tus hallazgos con la comunidad.
El marco de patrones de Qiskit aplica a todos estos algoritmos, pero solo señalaremos los pasos explícitamente en el primer ejemplo.
Variational Quantum Eigensolver (VQE)
VQE es uno de los algoritmos variacionales cuánticos más utilizados, y establece una plantilla sobre la que se construyen otros algoritmos.
Paso 1: Mapear las entradas clásicas a un problema cuántico
Esquema teórico
El esquema de VQE es sencillo:
- Preparar los operadores de referencia
- Partimos del estado y llegamos al estado de referencia
- Aplicar la forma variacional para crear un ansatz
- Pasamos del estado a
- Arranque en si tenemos un problema similar (normalmente obtenido mediante simulación clásica o muestreo)
- Cada optimizador se inicializará de forma diferente, generando un conjunto inicial de vectores de parámetros (por ejemplo, a partir de un punto inicial ).
- Evaluar la función de costo para todos los estados preparados en un ordenador cuántico.
- Usar un optimizador clásico para seleccionar el siguiente conjunto de parámetros .
- Repetir el proceso hasta alcanzar la convergencia.
Este es un ciclo de optimización clásico sencillo en el que evaluamos la función de costo. Algunos optimizadores pueden requerir varias evaluaciones para calcular un gradiente, determinar la siguiente iteración o verificar la convergencia.
A continuación se muestra el ejemplo para el siguiente observable:
Implementación
# Added by doQumentation — required packages for this notebook
!pip install -q numpy qiskit qiskit-ibm-runtime scipy
from qiskit import QuantumCircuit
from qiskit.quantum_info import SparsePauliOp
from qiskit.circuit.library import TwoLocal
import numpy as np
theta_list = (2 * np.pi * np.random.rand(1, 8)).tolist()
observable = SparsePauliOp.from_list([("II", 2), ("XX", -2), ("YY", 3), ("ZZ", -3)])
reference_circuit = QuantumCircuit(2)
reference_circuit.x(0)
variational_form = TwoLocal(
2,
rotation_blocks=["rz", "ry"],
entanglement_blocks="cx",
entanglement="linear",
reps=1,
)
ansatz = reference_circuit.compose(variational_form)
ansatz.decompose().draw("mpl")
def cost_func_vqe(parameters, ansatz, hamiltonian, estimator):
"""Return estimate of energy from estimator
Parameters:
params (ndarray): Array of ansatz parameters
ansatz (QuantumCircuit): Parameterized ansatz circuit
hamiltonian (SparsePauliOp): Operator representation of Hamiltonian
estimator (Estimator): Estimator primitive instance
Returns:
float: Energy estimate
"""
estimator_job = estimator.run([(ansatz, hamiltonian, [parameters])])
estimator_result = estimator_job.result()[0]
cost = estimator_result.data.evs[0]
return cost
from qiskit.primitives import StatevectorEstimator
estimator = StatevectorEstimator()
Podemos usar esta función de costo para calcular los parámetros óptimos:
# SciPy minimizer routine
from scipy.optimize import minimize
x0 = np.ones(8)
result = minimize(
cost_func_vqe, x0, args=(ansatz, observable, estimator), method="COBYLA"
)
result
message: Optimization terminated successfully.
success: True
status: 1
fun: -5.999999982445723
x: [ 1.741e+00 9.606e-01 1.571e+00 2.115e-05 1.899e+00
1.243e+00 6.063e-01 6.063e-01]
nfev: 136
maxcv: 0.0
Paso 2: Optimizar el problema para la ejecución cuántica
Seleccionaremos el backend menos ocupado e importaremos los componentes necesarios de qiskit_ibm_runtime.
from qiskit_ibm_runtime import SamplerV2 as Sampler
from qiskit_ibm_runtime import EstimatorV2 as Estimator
from qiskit_ibm_runtime import Session, EstimatorOptions
from qiskit_ibm_runtime import QiskitRuntimeService
service = QiskitRuntimeService()
backend = service.least_busy(operational=True, simulator=False)
print(backend)
<IBMBackend('ibm_brisbane')>
Transpilaremos el circuito usando el gestor de pasadas predefinido con nivel de optimización 3, y aplicaremos el diseño correspondiente al observable.
from qiskit.transpiler.preset_passmanagers import generate_preset_pass_manager
pm = generate_preset_pass_manager(backend=backend, optimization_level=3)
isa_ansatz = pm.run(ansatz)
isa_observable = observable.apply_layout(layout=isa_ansatz.layout)
Paso 3: Ejecutar usando las primitivas de Qiskit Runtime
Ya estamos listos para ejecutar nuestro cálculo en hardware de IBM Quantum®. Dado que la minimización de la función de costo es muy iterativa, iniciaremos una sesión de Runtime. De este modo, solo tendremos que esperar en la cola una vez. Una vez que el trabajo comience a ejecutarse, cada iteración con actualizaciones de parámetros se ejecutará de inmediato.
x0 = np.ones(8)
estimator_options = EstimatorOptions(resilience_level=1, default_shots=10_000)
with Session(backend=backend) as session:
estimator = Estimator(mode=session, options=estimator_options)
result = minimize(
cost_func_vqe,
x0,
args=(isa_ansatz, isa_observable, estimator),
method="COBYLA",
options={"maxiter": 200, "disp": True},
)
session.close()
print(result)
Paso 4: Posprocesar y devolver el resultado en formato clásico
Podemos ver que la rutina de minimización terminó con éxito, lo que significa que se alcanzó la tolerancia predeterminada del optimizador clásico COBYLA. Si necesitamos un resultado más preciso, podemos especificar una tolerancia menor. Esto podría ser necesario, ya que el resultado se alejó varios puntos porcentuales del obtenido por el simulador anterior.
El valor de x obtenido es la mejor estimación actual de los parámetros que minimizan la función de costo. Si se itera para obtener mayor precisión, esos valores deben usarse en lugar del x0 inicial (un vector de unos).
Por último, cabe señalar que la función fue evaluada 96 veces durante el proceso de optimización. Este número puede diferir de la cantidad de pasos de optimización, ya que algunos optimizadores requieren múltiples evaluaciones de la función en un solo paso, como ocurre al estimar un gradiente.
Subspace Search VQE (SSVQE)
SSVQE es una variante de VQE que permite obtener los primeros valores propios de un observable con valores propios , donde . Sin pérdida de generalidad, suponemos que . SSVQE introduce una nueva idea al añadir pesos para priorizar la optimización del término con mayor peso.
Para implementar este algoritmo, necesitamos estados de referencia mutuamente ortogonales , lo que significa que para . Estos estados pueden construirse usando operadores de Pauli. La función de costo de este algoritmo es:
donde es un número positivo arbitrario tal que si entonces , y es la forma variacional definida por el usuario.
El algoritmo SSVQE se basa en el hecho de que los estados propios correspondientes a distintos valores propios son mutuamente ortogonales. En particular, el producto interno de y puede expresarse como:
La primera igualdad es válida porque es un operador cuántico y, por tanto, unitario. La última igualdad se cumple por la ortogonalidad de los estados de referencia . El hecho de que la ortogonalidad se conserve a través de transformaciones unitarias está profundamente relacionado con el principio de conservación de la información, tal como se expresa en la ciencia de la información cuántica. Desde esta perspectiva, las transformaciones no unitarias representan procesos en los que se pierde o se inyecta información.
Los pesos ayudan a garantizar que todos los estados sean estados propios. Si los pesos son suficientemente distintos, el término con mayor peso (es decir, ) recibirá prioridad durante la optimización sobre los demás. Como resultado, el estado resultante se convertirá en el estado propio correspondiente a . Dado que