Skip to main content
IBM Quantum Platform

Bucles de optimización

Durante esta lección, aprenderemos a utilizar un optimizador para explorar iterativamente los estados cuánticos parametrizados de nuestro ansatz:

  • Crear un bucle de optimización
  • Comprender las compensaciones al utilizar optimizadores locales y globales
  • Explore las mesetas estériles y cómo evitarlas

A alto nivel, los optimizadores son fundamentales para explorar nuestro espacio de búsqueda. El optimizador utiliza las evaluaciones de las funciones de coste para seleccionar el siguiente conjunto de parámetros en un bucle variacional, y repite el proceso hasta alcanzar un estado estable. En esta etapa, se devuelve un conjunto óptimo de valores de los parámetros θ\vec\theta^*.

Un diagrama de algunos factores importantes en la optimización, incluidas las mesetas estériles, los optimizadores con gradiente frente a los optimizadores sin gradiente y el bootstrapping.

Optimizadores locales y globales

Primero configuraremos nuestro problema antes de explorar cada clase de optimizador. Empezaremos con un circuito que contiene ocho parámetros variacionales:

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([("XX", 1), ("YY", -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")

Output:

Output of the previous code cell
def cost_func_vqe(params, 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
    """
    pub = (ansatz, hamiltonian, params)
    cost = estimator.run([pub]).result()[0].data.evs
    return cost
from qiskit.primitives import StatevectorEstimator

estimator = StatevectorEstimator()

Optimizadores locales

Los optimizadores locales buscan un punto que minimice la función de coste partiendo de un punto o puntos iniciales C(θ0)C(\vec{\theta_0}) y se desplazan a puntos diferentes en función de lo que observan en la región que están evaluando en ese momento en iteraciones sucesivas. Esto implica que la convergencia de estos algoritmos suele ser rápida, pero puede depender en gran medida del punto inicial. Los optimizadores locales no pueden ver más allá de la región en la que están evaluando y pueden ser especialmente vulnerables a los mínimos locales, informando de la convergencia cuando encuentran uno e ignorando otros estados con evaluaciones más favorables.

# SciPy minimizer routine
from scipy.optimize import minimize

x0 = np.ones(8)

result = minimize(
    cost_func_vqe, x0, args=(ansatz, observable, estimator), method="SLSQP"
)

result

Output:

 message: Optimization terminated successfully
 success: True
  status: 0
     fun: -3.9999999964520634
       x: [ 1.000e+00  1.000e+00 -1.571e+00 -4.556e-05 -1.207e+00
           -1.935e+00  4.079e-01 -4.079e-01]
     nit: 12
     jac: [ 0.000e+00  0.000e+00 -7.957e-04  2.543e-04  1.381e-03
            1.381e-03  5.430e-04  5.431e-04]
    nfev: 112
    njev: 12

Optimizadores globales

Los optimizadores globales buscan el punto que minimiza la función de coste en varias regiones de su dominio (es decir, no local), evaluándola iterativamente (es decir, en la iteración ii ) sobre un conjunto de vectores de parámetros Θi:=θi,jjJopti\Theta_i := \\{ {\vec\theta_{i,j} | j \in \mathcal{J}_\text{opt}^i} \\} determinados por el optimizador. Esto los hace menos susceptibles a los mínimos locales y algo independientes de la inicialización, pero también significativamente más lentos a la hora de converger a una solución propuesta.

Optimización del arranque

Bootstrapping, o establecer el valor inicial de los parámetros θ\vec\theta basándose en una optimización previa, puede ayudar a nuestro optimizador a converger en una solución más rápidamente. Lo denominaremos punto inicial θ0\vec\theta_0, y ψ(θ0)=UV(θ0)ρ|\psi(\vec\theta_0)\rangle = U_V(\vec\theta_0)|\rho\rangle, estado inicial. Este estado inicial difiere de nuestro estado de referencia ρ|\rho\rangle, ya que el primero se centra en los parámetros iniciales establecidos durante nuestro bucle de optimización, mientras que el segundo se centra en utilizar soluciones de "referencia" conocidas. Pueden coincidir si UV(θ0)IU_V(\vec\theta_0) \equiv I (es decir, la operación de identidad).

Cuando los optimizadores locales convergen a mínimos locales no óptimos, podemos intentar arrancar la optimización globalmente y refinar la convergencia localmente. Aunque esto requiere establecer dos cargas de trabajo variacionales, permite al optimizador encontrar una solución más óptima que el optimizador local por sí solo.


Optimizadores basados en gradientes y sin gradientes

Basado en gradientes

Para nuestra función de coste C(θ)C(\vec\theta), si tenemos acceso al gradiente de la función C(θ)\vec{\nabla} C(\vec\theta) partiendo de un punto inicial, la forma más sencilla de minimizar la función es actualizar los parámetros hacia la dirección de descenso más pronunciado de la función. Es decir, actualizamos los parámetros como θn+1=θnηC(θ)\vec\theta_{n+1} = \vec\theta_n - \eta \vec{\nabla} C(\vec\theta), donde η\eta es la tasa de aprendizaje - un pequeño y positivo Un hiperparámetro es un parámetro que utilizamos para controlar nuestro algoritmo. El término hiper lo distingue de los parámetros (θ) que nuestro algoritmo intenta encontrar. que controla el tamaño de la actualización. Continuamos haciendo esto hasta que convergemos a un Un mínimo local es el punto más bajo de la función, para un rango pequeño de valores de theta. Por el contrario, un mínimo global es el punto más bajo, en cualquier lugar de nuestra función (es decir, para cualquier valor de θ). de la función de coste, C(θ)C({\vec\theta^*}).

Podemos utilizar esta función de coste y un optimizador 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="BFGS"
)

result

Output:

  message: Optimization terminated successfully.
  success: True
   status: 0
      fun: -3.9999999999997025
        x: [ 1.000e+00  1.000e+00  1.571e+00  3.220e-07  2.009e-01
            -2.009e-01  6.342e-01 -6.342e-01]
      nit: 14
      jac: [-1.192e-07 -2.980e-08  8.345e-07  1.103e-06  5.960e-08
             0.000e+00 -5.960e-08  2.980e-08]
 hess_inv: [[ 1.000e+00  1.872e-10 ...  5.077e-05  3.847e-05]
            [ 1.872e-10  1.000e+00 ... -5.208e-05 -4.060e-05]
            ...
            [ 5.077e-05 -5.208e-05 ...  7.243e-01 -2.604e-01]
            [ 3.847e-05 -4.060e-05 ... -2.604e-01  8.179e-01]]
     nfev: 144
     njev: 16

Las principales desventajas de este tipo de optimización son la velocidad de convergencia, que puede ser muy lenta, y que no hay garantía de alcanzar la solución óptima.

gráfico de f(theta) contra theta, múltiples puntos muestran diferentes estados de un algoritmo de descenso de gradiente que encuentra el mínimo de una curva.

Sin gradiente

Los algoritmos de optimización sin gradiente no requieren información sobre el gradiente y pueden ser útiles en situaciones en las que calcular el gradiente es difícil, caro o demasiado ruidoso. También tienden a ser más robustos a la hora de encontrar óptimos globales, mientras que los métodos basados en gradientes tienden a converger a óptimos locales. Exploraremos algunos casos en los que un optimizador sin gradiente puede ayudar a evitar mesetas estériles. Sin embargo, los métodos sin gradiente requieren mayores recursos computacionales, especialmente para problemas con espacios de búsqueda de alta dimensión.

He aquí un ejemplo que utiliza el COBYLA optimizador:

# 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

Output:

 message: Optimization terminated successfully.
 success: True
  status: 1
     fun: -3.999999973369678
       x: [ 1.631e+00  1.492e+00  1.571e+00  3.142e+00  1.375e+00
           -1.767e+00  1.484e+00  1.658e+00]
    nfev: 137
   maxcv: 0.0

Mesetas áridas

De hecho, el panorama de costes puede ser bastante complicado, como muestran las colinas y valles del ejemplo siguiente. El método de optimización nos guía por el paisaje de costes, buscando el mínimo, como muestran los puntos y las líneas negras. Podemos ver que dos de las tres búsquedas acaban en un mínimo local del paisaje, en lugar de en uno global.

Un colector curvo complicado con muchos picos y valles.

Independientemente del tipo de método de optimización utilizado, si el panorama de costes es relativamente plano, puede resultar difícil para el método determinar la dirección de búsqueda adecuada. Este escenario se conoce como Cuando los gradientes de los circuitos cuánticos parametrizados se vuelven exponencialmente pequeños con respecto al número de qubits, lo que dificulta la optimización y la hace potencialmente imposible., donde el panorama de costes se vuelve progresivamente más plano (y, por tanto, más difícil de determinar la dirección hacia el mínimo). Para una amplia gama de circuitos cuánticos parametrizados, la probabilidad de que el gradiente a lo largo de cualquier dirección razonable sea distinto de cero con cierta precisión fija disminuye exponencialmente a medida que aumenta el número de qubits.

Diagrama de una meseta geográfica comparada con la pendiente de una montaña, para explicar por qué una pendiente nos ayuda a encontrar un mínimo y una meseta dificulta nuestros esfuerzos.

Aunque esta área sigue siendo objeto de investigación activa, tenemos algunas recomendaciones para mejorar el rendimiento de la optimización:

  • Bootstrapping puede ayudar al bucle de optimización a evitar quedarse atascado en un espacio de parámetros donde el gradiente es pequeño.
  • Experimentar con un ansatz eficiente desde el punto de vista del hardware : Dado que estamos utilizando un sistema cuántico ruidoso como oráculo de caja negra, la calidad de esas evaluaciones puede afectar al rendimiento del optimizador. El uso de un ansatz eficiente desde el punto de vista del hardware, como EfficientSU2puede evitar la producción de gradientes exponencialmente pequeños.
  • Experimentar con la supresión y mitigación de errores : las primitivas Qiskit Runtime proporcionan una interfaz sencilla para experimentar con varios valores para optimization_level y resilience_setting, respectivamente. Esto puede reducir el impacto del ruido y hacer más eficaz el proceso de optimización.
  • Experimentar con optimizadores sin gradiente : A diferencia de los algoritmos de optimización basados en el gradiente, los optimizadores como COBYLA no se basan en la información del gradiente para optimizar los parámetros y, por lo tanto, es menos probable que se vean afectados por la meseta estéril.

Resumen

Con esta lección, ha aprendido a definir su bucle de optimización:

  • Crear un bucle de optimización
  • Comprender las compensaciones al utilizar optimizadores locales y globales
  • Explore las mesetas estériles y cómo evitarlas

Nuestra carga de trabajo variacional de alto nivel está completa:

Un circuito cuántico ahora con un unitario para preparar el estado de referencia, y un segundo unitario para variar el estado utilizando parámetros variacionales.

A continuación, exploraremos algoritmos variacionales específicos teniendo en cuenta este marco.

¿Le ha resultado útil esta página?
Informe de un error, de una errata o solicite contenido en GitHub.