Skip to main content
IBM Quantum Platform

Loops de otimização

Nesta lição, aprenderemos a usar um otimizador para explorar iterativamente os estados quânticos parametrizados de nosso ansatz:

  • Inicialização de um loop de otimização
  • Entenda as compensações ao usar otimizadores locais e globais
  • Explore os platôs estéreis e como evitá-los

Em um nível elevado, os otimizadores são fundamentais para explorar nosso espaço de pesquisa. O otimizador usa avaliações da função de custo para selecionar o próximo conjunto de parâmetros em um loop variacional e repete o processo até atingir um estado estável. Nesse estágio, um conjunto ideal de valores de parâmetros θ\vec\theta^* é retornado.

Um diagrama de alguns fatores importantes na otimização, incluindo platôs estéreis, otimizadores com gradiente versus otimizadores sem gradiente e bootstrapping.

Otimizadores locais e globais

Primeiro, definiremos nosso problema antes de explorar cada classe de otimizador. Começaremos com um circuito que contém oito parâmetros de variação:

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

Otimizadores locais

Os otimizadores locais buscam um ponto que minimize a função de custo a partir de um ou mais pontos iniciais C(θ0)C(\vec{\theta_0}) e se movem para pontos diferentes com base no que observam na região que estão avaliando no momento em iterações sucessivas. Isso implica que a convergência desses algoritmos geralmente é rápida, mas pode depender muito do ponto inicial. Os otimizadores locais não conseguem ver além da região em que estão avaliando e podem ser especialmente vulneráveis a mínimos locais, informando a convergência quando encontram um e ignorando outros estados com avaliações mais favoráveis.

# 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

Otimizadores globais

Os otimizadores globais buscam o ponto que minimiza a função de custo em várias regiões de seu domínio (ou seja, não local), avaliando-a iterativamente (ou seja, na iteração ii ) em um conjunto de vetores de parâmetros Θi:=θi,jjJopti\Theta_i := \\{ {\vec\theta_{i,j} | j \in \mathcal{J}_\text{opt}^i} \\} determinado pelo otimizador. Isso os torna menos suscetíveis a mínimos locais e, de certa forma, independentes da inicialização, mas também significativamente mais lentos para convergir para uma solução proposta.

Otimização de bootstrapping

O bootstrapping, ou seja, a definição do valor inicial dos parâmetros θ\vec\theta com base em uma otimização anterior, pode ajudar nosso otimizador a convergir para uma solução mais rapidamente. Referimo-nos a isso como o ponto inicial θ0\vec\theta_0, e ψ(θ0)=UV(θ0)ρ|\psi(\vec\theta_0)\rangle = U_V(\vec\theta_0)|\rho\rangle como o estado inicial. Esse estado inicial difere do nosso estado de referência ρ|\rho\rangle, pois o primeiro se concentra nos parâmetros iniciais definidos durante nosso loop de otimização, enquanto o último se concentra no uso de soluções de "referência" conhecidas. Elas podem coincidir se UV(θ0)IU_V(\vec\theta_0) \equiv I (ou seja, a operação de identidade).

Quando os otimizadores locais convergem para mínimos locais não ideais, podemos tentar inicializar a otimização globalmente e refinar a convergência localmente. Embora isso exija a configuração de duas cargas de trabalho variacionais, permite que o otimizador encontre uma solução mais otimizada do que o otimizador local sozinho.


Otimizadores baseados em gradiente e sem gradiente

Baseado em gradiente

Para nossa função de custo C(θ)C(\vec\theta), se tivermos acesso ao gradiente da função C(θ)\vec{\nabla} C(\vec\theta) a partir de um ponto inicial, a maneira mais simples de minimizar a função é atualizar os parâmetros na direção da descida mais íngreme da função. Ou seja, atualizamos os parâmetros como θn+1=θnηC(θ)\vec\theta_{n+1} = \vec\theta_n - \eta \vec{\nabla} C(\vec\theta), em que η\eta é a taxa de aprendizado - um Um hiperparâmetro é um parâmetro que usamos para controlar nosso algoritmo. O termo hyper o distingue dos parâmetros (θ) que nosso algoritmo está tentando encontrar. pequeno e positivo que controla o tamanho da atualização. Continuamos fazendo isso até convergirmos para um Um mínimo local é o ponto mais baixo da função, para um pequeno intervalo de valores de theta. Por outro lado, um mínimo global é o ponto mais baixo, em qualquer lugar em nossa função (ou seja, para qualquer valor de θ). da função de custo, C(θ)C({\vec\theta^*}).

Podemos usar essa função de custo e um otimizador para calcular os parâmetros ideais

# 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

As principais desvantagens desse tipo de otimização são a velocidade de convergência, que pode ser muito lenta, e o fato de não haver garantia de alcançar a solução ideal.

no gráfico de f(theta) em relação a theta, vários pontos mostram diferentes estados de um algoritmo de descida de gradiente que encontra o mínimo de uma curva.

Sem gradiente

Os algoritmos de otimização sem gradiente não exigem informações sobre o gradiente e podem ser úteis em situações em que o cálculo do gradiente é difícil, caro ou muito ruidoso. Eles também tendem a ser mais robustos na busca de ótimos globais, enquanto os métodos baseados em gradiente tendem a convergir para ótimos locais. Vamos explorar alguns casos em que um otimizador sem gradiente pode ajudar a evitar platôs estéreis. No entanto, os métodos sem gradiente exigem mais recursos computacionais, especialmente para problemas com espaços de pesquisa de alta dimensão.

Aqui está um exemplo que usa o otimizador COBYLA em vez do otimizador:

# 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

Planaltos áridos

De fato, o cenário de custos pode ser bastante complicado, como mostram as colinas e os vales no exemplo abaixo. O método de otimização nos conduz pelo cenário de custos, buscando o mínimo, conforme mostrado pelos pontos e linhas pretos. Podemos ver que duas das três pesquisas terminam em um mínimo local do cenário, em vez de um mínimo global.

Um coletor curvo complicado com muitos picos e depressões.

Independentemente do tipo de método de otimização usado, se o cenário de custos for relativamente plano, pode ser um desafio para o método determinar a direção apropriada para a pesquisa. Esse cenário é conhecido como Quando os gradientes dos circuitos quânticos parametrizados se tornam exponencialmente pequenos em relação ao número de qubits, dificultando a otimização e tornando-a potencialmente impossível., em que o cenário de custos se torna progressivamente mais plano (e, portanto, mais desafiador para determinar a direção para o mínimo). Para uma ampla gama de circuitos quânticos parametrizados, a probabilidade de que o gradiente ao longo de qualquer direção razoável seja diferente de zero para alguma precisão fixa diminui exponencialmente à medida que o número de qubits aumenta.

Um diagrama de um platô geográfico comparado à inclinação de uma montanha, para explicar por que um gradiente nos ajuda a encontrar um mínimo e um platô dificulta nossos esforços.

Embora essa área ainda esteja em pesquisa ativa, temos algumas recomendações para melhorar o desempenho da otimização:

  • O bootstrapping pode ajudar o loop de otimização a evitar ficar preso em um espaço de parâmetros em que o gradiente é pequeno.
  • Experimentação com ansatz eficiente de hardware : como estamos usando um sistema quântico ruidoso como um oráculo de caixa preta, a qualidade dessas avaliações pode afetar o desempenho do otimizador. O uso de ansatz eficiente em termos de hardware, como EfficientSU2pode evitar a produção de gradientes exponencialmente pequenos.
  • Experimentação com supressão e atenuação de erros : as primitivas Qiskit Runtime fornecem uma interface simples para experimentar vários valores para optimization_level e resilience_setting, respectivamente. Isso pode reduzir o impacto do ruído e tornar o processo de otimização mais eficiente.
  • Experimentando otimizadores sem gradiente : Diferentemente dos algoritmos de otimização baseados em gradiente, otimizadores como o COBYLA não dependem de informações de gradiente para otimizar os parâmetros e, portanto, têm menos probabilidade de serem afetados pelo platô estéril.

Resumo

Com esta lição, você aprendeu a definir seu loop de otimização:

  • Inicialização de um loop de otimização
  • Entenda as compensações ao usar otimizadores locais e globais
  • Explore os platôs estéreis e como evitá-los

Nossa carga de trabalho variacional de alto nível está completa:

Um circuito quântico agora com uma unidade para preparar o estado de referência e uma segunda unidade para variar o estado usando parâmetros variacionais.

Em seguida, exploraremos algoritmos variacionais específicos com essa estrutura em mente.

Esta página foi útil?
Relate um bug, erro de digitação ou solicite conteúdo no GitHub.