Skip to main content
IBM Quantum Platform

Cicli di ottimizzazione

In questa lezione impareremo a utilizzare un ottimizzatore per esplorare iterativamente gli stati quantistici parametrizzati del nostro ansatz:

  • Bootstrap di un ciclo di ottimizzazione
  • Comprendere i compromessi nell'utilizzo di ottimizzatori locali e globali
  • Esplorare gli altipiani sterili e come evitarli

Ad alto livello, gli ottimizzatori sono fondamentali per esplorare il nostro spazio di ricerca. L'ottimizzatore utilizza le valutazioni delle funzioni di costo per selezionare la serie successiva di parametri in un ciclo variazionale e ripete il processo fino a raggiungere uno stato stabile. In questa fase, viene restituito un insieme ottimale di valori dei parametri θ\vec\theta^*.

Un diagramma di alcuni fattori importanti nell'ottimizzazione, tra cui i plateau sterili, gli ottimizzatori con o senza gradiente e il bootstrapping.

Ottimizzatori locali e globali

Prima di esplorare ogni classe di ottimizzatori, imposteremo il nostro problema. Inizieremo con un circuito contenente otto parametri variazionali:

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()

Ottimizzatori locali

Gli ottimizzatori locali cercano un punto che minimizzi la funzione di costo a partire da uno o più punti iniziali C(θ0)C(\vec{\theta_0}) e si spostano su punti diversi in base a ciò che osservano nella regione che stanno valutando nelle iterazioni successive. Ciò implica che la convergenza di questi algoritmi sarà solitamente veloce, ma può dipendere fortemente dal punto iniziale. Gli ottimizzatori locali non sono in grado di vedere oltre la regione in cui stanno valutando e possono essere particolarmente vulnerabili ai minimi locali, segnalando la convergenza quando ne trovano uno e ignorando altri stati con valutazioni più favorevoli.

# 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

Ottimizzatori globali

Gli ottimizzatori globali cercano il punto che minimizza la funzione di costo in diverse regioni del suo dominio (cioè non locali), valutandola in modo iterativo (cioè all'iterazione ii ) su un insieme di vettori di parametri Θi:=θi,jjJopti\Theta_i := \\{ {\vec\theta_{i,j} | j \in \mathcal{J}_\text{opt}^i} \\} determinati dall'ottimizzatore. Ciò li rende meno suscettibili ai minimi locali e in qualche modo indipendenti dall'inizializzazione, ma anche significativamente più lenti a convergere verso una soluzione proposta.

Ottimizzazione del bootstrapping

Il bootstrapping, ovvero l'impostazione del valore iniziale dei parametri θ\vec\theta sulla base di un'ottimizzazione precedente, può aiutare il nostro ottimizzatore a convergere più rapidamente verso una soluzione. Si parla di punto iniziale θ0\vec\theta_0 e di ψ(θ0)=UV(θ0)ρ|\psi(\vec\theta_0)\rangle = U_V(\vec\theta_0)|\rho\rangle come stato iniziale. Questo stato iniziale differisce dal nostro stato di riferimento ρ|\rho\rangle, poiché il primo si concentra sui parametri iniziali impostati durante il nostro ciclo di ottimizzazione, mentre il secondo si concentra sull'utilizzo di soluzioni "di riferimento" note. Possono coincidere se UV(θ0)IU_V(\vec\theta_0) \equiv I (cioè l'operazione di identità).

Quando gli ottimizzatori locali convergono verso minimi locali non ottimali, possiamo provare a fare il bootstrap dell'ottimizzazione a livello globale e a perfezionare la convergenza a livello locale. Sebbene ciò richieda l'impostazione di due carichi di lavoro variazionali, consente all'ottimizzatore di trovare una soluzione più ottimale rispetto al solo ottimizzatore locale.


Ottimizzatori basati sul gradiente e senza gradiente

Basato sul gradiente

Per la nostra funzione di costo C(θ)C(\vec\theta), se abbiamo accesso al gradiente della funzione C(θ)\vec{\nabla} C(\vec\theta) a partire da un punto iniziale, il modo più semplice per minimizzare la funzione è aggiornare i parametri verso la direzione di massima discesa della funzione. Cioè, aggiorniamo i parametri come θn+1=θnηC(θ)\vec\theta_{n+1} = \vec\theta_n - \eta \vec{\nabla} C(\vec\theta), dove η\eta è il tasso di apprendimento - un piccolo Un iperparametro è un parametro che utilizziamo per controllare il nostro algoritmo. Il termine iper lo distingue dai parametri (θ) che il nostro algoritmo cerca di trovare. positivo che controlla la dimensione dell'aggiornamento. Continuiamo a farlo finché non convergiamo a un Un minimo locale è il punto più basso della funzione, per un piccolo intervallo di valori di theta. Al contrario, un minimo globale è il punto più basso, in qualsiasi punto della nostra funzione (cioè per qualsiasi valore di θ). della funzione di costo, C(θ)C({\vec\theta^*}).

Possiamo utilizzare questa funzione di costo e un ottimizzatore per calcolare i parametri ottimali

# 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

I principali svantaggi di questo tipo di ottimizzazione sono la velocità di convergenza, che può essere molto lenta, e la mancanza di garanzia di raggiungere la soluzione ottimale.

grafico di f(theta) contro theta, i punti multipli mostrano diversi stati di un algoritmo di discesa del gradiente che trova il minimo di una curva.

Senza gradiente

Gli algoritmi di ottimizzazione senza gradiente non richiedono informazioni sul gradiente e possono essere utili in situazioni in cui il calcolo del gradiente è difficile, costoso o troppo rumoroso. Inoltre, tendono a essere più robusti nel trovare gli ottimali globali, mentre i metodi basati sul gradiente tendono a convergere verso gli ottimali locali. Esploreremo alcuni casi in cui un ottimizzatore senza gradiente può aiutare a evitare i plateau sterili. Tuttavia, i metodi privi di gradiente richiedono risorse computazionali più elevate, soprattutto per problemi con spazi di ricerca altamente dimensionali.

Ecco un esempio che utilizza l'ottimizzatore COBYLA invece dell'ottimizzatore:

# 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

Altipiani aridi

In effetti, il panorama dei costi può essere piuttosto complicato, come dimostrano le colline e le valli dell'esempio seguente. Il metodo di ottimizzazione ci fa navigare nel panorama dei costi, alla ricerca del minimo, come mostrato dai punti e dalle linee nere. Si può notare che due delle tre ricerche finiscono in un minimo locale del paesaggio, piuttosto che in uno globale.

Un complicato collettore curvo con molti picchi e avvallamenti.

Indipendentemente dal tipo di metodo di ottimizzazione utilizzato, se il panorama dei costi è relativamente piatto, può essere difficile per il metodo determinare la direzione di ricerca appropriata. Questo scenario viene definito Quando i gradienti dei circuiti quantistici parametrizzati diventano esponenzialmente piccoli rispetto al numero di qubit, rendendo l'ottimizzazione difficile e potenzialmente impossibile., dove il panorama dei costi diventa progressivamente più piatto (e quindi più difficile da determinare la direzione verso il minimo). Per un'ampia gamma di circuiti quantistici parametrizzati, la probabilità che il gradiente lungo qualsiasi direzione ragionevole sia non nullo con una precisione fissa diminuisce esponenzialmente all'aumentare del numero di qubit.

Un diagramma di un altopiano geografico rispetto al pendio di una montagna, per spiegare perché una pendenza ci aiuta a trovare un minimo e un altopiano ostacola i nostri sforzi.

Anche se quest'area è ancora oggetto di ricerca attiva, abbiamo alcune raccomandazioni per migliorare le prestazioni dell'ottimizzazione:

  • Il bootstrapping può aiutare il ciclo di ottimizzazione a non rimanere bloccato in uno spazio di parametri in cui il gradiente è piccolo.
  • Sperimentazione di ansatz efficienti dal punto di vista hardware : poiché stiamo usando un sistema quantistico rumoroso come oracolo black-box, la qualità di queste valutazioni può influenzare le prestazioni dell'ottimizzatore. L'utilizzo di ansatz efficienti dal punto di vista hardware, come ad esempio EfficientSU2può evitare di produrre gradienti esponenzialmente piccoli.
  • Sperimentazione con la soppressione e la mitigazione degli errori : le primitive di IBM Quantum offrono un'interfaccia semplice per sperimentare con vari valori rispettivamente per optimization_level e resilience_setting. Ciò può ridurre l'impatto del rumore e rendere più efficiente il processo di ottimizzazione.
  • Sperimentazione di ottimizzatori senza gradiente : A differenza degli algoritmi di ottimizzazione basati sul gradiente, gli ottimizzatori come COBYLA non si basano sulle informazioni sul gradiente per ottimizzare i parametri e quindi hanno meno probabilità di essere influenzati dal plateau sterile.

In questa lezione avete imparato a definire il vostro ciclo di ottimizzazione:

  • Bootstrap di un ciclo di ottimizzazione
  • Comprendere i compromessi nell'utilizzo di ottimizzatori locali e globali
  • Esplorare gli altipiani sterili e come evitarli

Il nostro carico di lavoro variazionale di alto livello è completo:

Un circuito quantistico ora con un'unità per preparare lo stato di riferimento e una seconda unità per variare lo stato usando parametri variazionali.

Successivamente, esploreremo algoritmi variazionali specifici tenendo conto di questo quadro.

Questa pagina è stata utile?
Segnala un bug, un errore di battitura o richiedi contenuti su GitHub.