Nota
Esta página foi gerada, a partir do tutorials/algorithms/02_vqe_convergence.ipynb.
Execute interativamente no IBM Quantum lab.
Monitorando a convergência do VQE¶
Algoritmos variacionais no Qiskit, como o VQE e o QAOA, fornecem a opção do usuário dar um método de callback que pode ser usado para monitorar o progresso da otimização conforme o algoritmo é executado e converge para o mínimo. O callback é invocado para cada avaliação funcional pelo otimizador e fornece o valor atual do otimizador, a contagem da avaliação, os parâmetros otimizadores atuais, etc. Observe que, dependendo do otimizador específico isto pode não ser a cada iteração (step) do otimizador, portanto, por exemplo, se o otimizador estiver chamando a função de custo para computar um gradiente de diferença finita isso será visível via callback.
Este notebook demonstra o uso do algoritmo VQE do Qiskit para desenhar gráficos do caminho de convergência para a energia do estado fundamental com um conjunto selecionado de otimizadores.
[1]:
import numpy as np
import pylab
from qiskit import BasicAer
from qiskit.aqua.operators import X, Z, I
from qiskit.aqua import QuantumInstance, aqua_globals
from qiskit.aqua.algorithms import VQE, NumPyMinimumEigensolver
from qiskit.aqua.components.initial_states import Zero
from qiskit.aqua.components.optimizers import COBYLA, L_BFGS_B, SLSQP
from qiskit.circuit.library import TwoLocal
Primeiro, criamos um operador de qubit para o VQE. Aqui, vamos usar o mesmo operador usado na introdução do algoritmo, que foi originalmente calculado pelo Qiskit Chemistry para uma molécula de H2.
[2]:
H2_op = (-1.052373245772859 * I ^ I) + \
(0.39793742484318045 * I ^ Z) + \
(-0.39793742484318045 * Z ^ I) + \
(-0.01128010425623538 * Z ^ Z) + \
(0.18093119978423156 * X ^ X)
Mostraremos o uso do callback abaixo sobre um conjunto de otimizadores para comparação. A energia mínima do Hamiltoniano do H2 pode ser encontrada bastante facilmente então podemos configurar maxiters para um valor pequeno.
[3]:
optimizers = [COBYLA(maxiter=80), L_BFGS_B(maxiter=60), SLSQP(maxiter=60)]
converge_cnts = np.empty([len(optimizers)], dtype=object)
converge_vals = np.empty([len(optimizers)], dtype=object)
for i, optimizer in enumerate(optimizers):
print('\rOptimizer: {} '.format(type(optimizer).__name__), end='')
aqua_globals.random_seed = 50
var_form = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
counts = []
values = []
def store_intermediate_result(eval_count, parameters, mean, std):
counts.append(eval_count)
values.append(mean)
vqe = VQE(H2_op, var_form, optimizer, callback=store_intermediate_result,
quantum_instance=QuantumInstance(backend=BasicAer.get_backend('statevector_simulator')))
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
converge_cnts[i] = np.asarray(counts)
converge_vals[i] = np.asarray(values)
print('\rOptimization complete ');
Optimization complete
Agora a partir dos dados de callback que armazenamos podemos traçar o valor da energia em cada chamada da função objetiva que cada otimizador faz. Um otimizador usando um método de diferenças finitas para o cálculo do gradiente tem aquele gráfico com degraus característicos onde para várias avaliações ele está calculando o valor de pontos próximos para estabelecer um gradiente (os pontos próximos tendo valores muito semelhantes cuja diferença não pode ser vista na escala do gráfico aqui).
[4]:
pylab.rcParams['figure.figsize'] = (12, 8)
for i, optimizer in enumerate(optimizers):
pylab.plot(converge_cnts[i], converge_vals[i], label=type(optimizer).__name__)
pylab.xlabel('Eval count')
pylab.ylabel('Energy')
pylab.title('Energy convergence for various optimizers')
pylab.legend(loc='upper right');

Por fim, uma vez que o problema acima ainda é facilmente tratável classicamente podemos utilizar o NumPyMinimumEigensolver para computar um valor de referência para a solução. Podemos agora representar graficamente a diferença da solução exata resultante à medida que a energia converge com o VQE em direção ao valor mínimo que deveria ser aquela solução clássica exata.
[5]:
npme = NumPyMinimumEigensolver()
result = npme.compute_minimum_eigenvalue(operator=H2_op)
ref_value = result.eigenvalue.real
print(f'Reference value: {ref_value:.5f}')
Reference value: -1.85728
[6]:
pylab.rcParams['figure.figsize'] = (12, 8)
for i, optimizer in enumerate(optimizers):
pylab.plot(converge_cnts[i], abs(ref_value - converge_vals[i]), label=type(optimizer).__name__)
pylab.xlabel('Eval count')
pylab.ylabel('Energy difference from solution reference value')
pylab.title('Energy convergence for various optimizers')
pylab.yscale('log')
pylab.legend(loc='upper right');

Usando o framework Gradient¶
O Qiskit agora tem um framework Gradient como parte das funcionalidades de Operator. Com o gradiente calculado para o otimizador vemos agora apenas os próprios passos da otimização.
[7]:
from qiskit.aqua.operators.gradients import Gradient
aqua_globals.random_seed = 50
var_form = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
optimizer = SLSQP(maxiter=60)
counts = []
values = []
def store_intermediate_result(eval_count, parameters, mean, std):
counts.append(eval_count)
values.append(mean)
vqe = VQE(H2_op, var_form, optimizer, callback=store_intermediate_result,
gradient=Gradient(grad_method='fin_diff'),
quantum_instance=QuantumInstance(backend=BasicAer.get_backend('statevector_simulator')))
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(f'Value using Gradient: {result.eigenvalue.real:.5f}')
Value using Gradient: -1.85728
[8]:
pylab.rcParams['figure.figsize'] = (12, 8)
pylab.plot(counts, values, label=type(optimizer).__name__)
pylab.xlabel('Eval count')
pylab.ylabel('Energy')
pylab.title('Energy convergence using Gradient')
pylab.legend(loc='upper right');

Monitoramento via registro (logging)¶
Grande parte do código é instrumentado com declarações Python logging. O logging é configurável para ajustar o nível de logging, etc. Uma maneira fácil de ativar o logging no módulo qiskit.aqua
é executar o código a seguir, onde aqui definimos o nível de logging como INFO
[9]:
import logging
from qiskit.aqua import set_qiskit_aqua_logging
set_qiskit_aqua_logging(logging.INFO)
e com logging no nível INFO o VQE incluirá informações sobre as avaliações, como abaixo.
020-11-04 16:55:33,450:qiskit.aqua.algorithms.minimum_eigen_solvers.vqe:INFO: Energy evaluation returned [-8.88931977] - 6801.61572 (ms), eval count: 1 2020-11-04 16:55:34,463:qiskit.aqua.algorithms.minimum_eigen_solvers.vqe:INFO: Energy evaluation returned [-8.88931977] - 1012.12025 (ms), eval count: 2 2020-11-04 16:55:35,483:qiskit.aqua.algorithms.minimum_eigen_solvers.vqe:INFO: Energy evaluation returned [-8.88931977] - 1019.99474 (ms), eval count: 3 2020-11-04 16:55:36,646:qiskit.aqua.algorithms.minimum_eigen_solvers.vqe:INFO: Energy evaluation returned [-8.88931977] - 1162.09555 (ms), eval count: 4
[10]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
Qiskit | 0.23.0 |
Terra | 0.16.0 |
Aer | 0.7.0 |
Ignis | 0.5.0 |
Aqua | 0.8.0 |
IBM Q Provider | 0.11.0 |
System information | |
Python | 3.6.1 |Continuum Analytics, Inc.| (default, May 11 2017, 13:09:58) [GCC 4.4.7 20120313 (Red Hat 4.4.7-1)] |
OS | Linux |
CPUs | 1 |
Memory (Gb) | 5.827335357666016 |
Sat Nov 07 16:21:19 2020 EST |
This code is a part of Qiskit
© Copyright IBM 2017, 2020.
This code is licensed under the Apache License, Version 2.0. You may
obtain a copy of this license in the LICENSE.txt file in the root directory
of this source tree or at http://www.apache.org/licenses/LICENSE-2.0.
Any modifications or derivative works of this code must retain this
copyright notice, and modified files need to carry a notice indicating
that they have been altered from the originals.
[ ]: