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 é retornado.
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:
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 costfrom 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 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"
)
resultOutput:
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 ) em um conjunto de vetores de parâmetros 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 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 , e como o estado inicial. Esse estado inicial difere do nosso estado de referência , 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 (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 , se tivermos acesso ao gradiente da função 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 , em que é 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, .
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"
)
resultOutput:
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.
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"
)
resultOutput:
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.
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.
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_leveleresilience_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
COBYLAnã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:
Em seguida, exploraremos algoritmos variacionais específicos com essa estrutura em mente.