Nota
Esta página fue generada a partir de tutorials/algorithms/04_vqe_advanced.ipynb.
Uso avanzado de VQE¶
Existen varios parámetros para configurar y utilizar capacidades más avanzadas de VQE. Este tutorial cubrirá los parámetros como initial_point
, expectation
y gradient
.
También cubrirá el uso avanzado del simulador, tal como el uso de Aer con el método de tren de tensores, también conocido como Matrix Product State o MPS.
[1]:
from qiskit import Aer
from qiskit.opflow import X, Z, I
from qiskit.utils import QuantumInstance, algorithm_globals
from qiskit.algorithms import VQE
from qiskit.algorithms.optimizers import SLSQP
from qiskit.circuit.library import TwoLocal
Aquí usaremos el mismo operador que se uso en los otros tutoriales de algoritmos VQE.
[2]:
H2_op = (-1.052373245772859 * I ^ I) + \
(0.39793742484318045 * I ^ Z) + \
(-0.39793742484318045 * Z ^ I) + \
(-0.01128010425623538 * Z ^ Z) + \
(0.18093119978423156 * X ^ X)
Punto inicial¶
El parámetro initial_point
permite que la optimización comience en el punto dado, donde el punto es una lista de parámetros que configurarán el ansatz. Por defecto, el punto inicial es None
, lo que significa que VQE elegirá uno. En este caso, la elección es si el ansatz proporcionado tiene un punto preferido, basado en el estado inicial que se le proporcionó, entonces se elegirá este; de lo contrario, se elegirá un punto inicial aleatorio que se ajuste a cualquier límite que tenga el ansatz. Sin embargo, si se proporciona un punto inicial, tendrá prioridad y se usará; ten en cuenta que debe coincidir en longitud con el número de parámetros en el circuito ansatz.
¿Por qué usar un punto inicial? Una razón puede ser que ya tengas una idea razonable de un punto inicial para el problema, o quizá tengas información de un experimento anterior.
Para mostrar su uso, primero hay que repetir el primer ejemplo del tutorial introducción a algoritmos para obtener el punto óptimo de una solución.
[3]:
seed = 50
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('statevector_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi)
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(result)
optimizer_evals = result.optimizer_evals
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 65,
'eigenstate': array([ 9.54859564e-05+0.j, -9.93766269e-01+0.j, 1.11483597e-01+0.j,
1.76972625e-05+0.j]),
'eigenvalue': -1.8572750175780233,
'optimal_parameters': { ParameterVectorElement(θ[3]): 6.092947703759512,
ParameterVectorElement(θ[2]): 0.5470754201428533,
ParameterVectorElement(θ[1]): 4.426962028746158,
ParameterVectorElement(θ[4]): -2.5983257006415026,
ParameterVectorElement(θ[5]): 1.5683259438037993,
ParameterVectorElement(θ[6]): -4.717618238771348,
ParameterVectorElement(θ[7]): 0.3602072544539939,
ParameterVectorElement(θ[0]): 4.2965202693703635},
'optimal_point': array([ 4.29652027, 4.42696203, 0.54707542, 6.0929477 , -2.5983257 ,
1.56832594, -4.71761824, 0.36020725]),
'optimal_value': -1.8572750175780233,
'optimizer_evals': 65,
'optimizer_time': 0.08263611793518066}
Ahora podemos tomar el optimal_point
del resultado de arriba y usarlo como el initial_point
aquí.
[4]:
initial_pt = result.optimal_point
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('statevector_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(ansatz, optimizer=slsqp, initial_point=initial_pt, quantum_instance=qi)
result1 = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(result1)
optimizer_evals1 = result1.optimizer_evals
print()
print(f'optimizer_evals is {optimizer_evals1} with initial point versus {optimizer_evals} without it.')
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 9,
'eigenstate': array([ 9.54859564e-05+0.j, -9.93766269e-01+0.j, 1.11483597e-01+0.j,
1.76972625e-05+0.j]),
'eigenvalue': -1.8572750175780233,
'optimal_parameters': { ParameterVectorElement(θ[3]): 6.092947703759512,
ParameterVectorElement(θ[6]): -4.717618238771348,
ParameterVectorElement(θ[5]): 1.5683259438037993,
ParameterVectorElement(θ[1]): 4.426962028746158,
ParameterVectorElement(θ[2]): 0.5470754201428533,
ParameterVectorElement(θ[7]): 0.3602072544539939,
ParameterVectorElement(θ[4]): -2.5983257006415026,
ParameterVectorElement(θ[0]): 4.2965202693703635},
'optimal_point': array([ 4.29652027, 4.42696203, 0.54707542, 6.0929477 , -2.5983257 ,
1.56832594, -4.71761824, 0.36020725]),
'optimal_value': -1.8572750175780233,
'optimizer_evals': 9,
'optimizer_time': 0.027410030364990234}
optimizer_evals is 9 with initial point versus 65 without it.
Aquí vemos que se llegó al resultado mucho más rápido con optimizer_evals
cuando comenzó a partir de un valor aleatorio cuando no se proporcionó el punto inicial (valor predeterminado de None).
Donde esto se vuelve útil para ejemplos en los que la solución a un problema se puede usar para adivinar la solución a un problema similar muy cercano. La química es un muy buen ejemplo en el que cambiamos las distancias interatómicas de la molécula para trazar un perfil de disociación. Cuando los cambios de distancia son pequeños, esperamos que la solución aún esté cerca de la anterior. Una técnica consiste simplemente en utilizar el punto óptimo de una solución como punto de partida para el siguiente paso. Ahora son posibles técnicas más complejas que hacen alguna extrapolación para calcular una posición inicial basada en soluciones anteriores en lugar de usar directamente la solución anterior.
Valor esperado¶
La energía del operador Hamiltoniano en el que está trabajando VQE es el valor esperado cuando se evalúa con el ansatz parametrizado. Para calcular el valor esperado, VQE usa una instancia de un objeto expectation. Dicha instancia se puede suministrar a través del parámetro expectation
, o en el caso predeterminado, donde tiene un valor de None
, VQE utilizará la ExpectationFactory para crear una instancia adecuada basada en el backend proporcionado.
En la mayoría de los casos, es suficiente dejar que VQE cree una instancia adecuada. Sin embargo, el aer_simulator de Qiskit Aer admite una copia de instrucción que se puede utilizar junto con el cálculo del valor de expectación del operador. Si se usa, el resultado es ideal, es decir, como el simulador de vector de estado, y no tiene ruido por la iteración. Dado que las personas normalmente eligen aer_simulator para tener ruido de iteración (ruido de muestreo) y ser más como un resultado de dispositivo real, VQE tiene un indicador include_custom
que se transmite a ExpectationFactory. Cuando se usa el simulador qasm de Aer, y esto se establece en True
, la fábrica devolverá AerPauliExpectation
que usa la copia de la instrucción, cuando es False
, por defecto, luego se devuelve la PauliExpectation
normal.
El siguiente ejemplo muestra include_custom=True
donde el resultado coincide con el simulador de vector de estado. De hecho, puede ser mejor/más rápido hacer esto en lugar de utilizar el statevector_simulator directamente. Esto es porque, cuando el Hamiltoniano es una suma de matrices de Pauli, este debe ser convertido a forma matricial y al usar la copia de la instrucción cuando include_custom es True, esto es evitado.
[5]:
from qiskit import Aer
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('aer_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi, include_custom=True)
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
optimal_value1 = result.optimal_value
print(result)
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 65,
'eigenstate': {'01': 0.9921567416492215, '10': 0.125},
'eigenvalue': -1.8572750175807682,
'optimal_parameters': { ParameterVectorElement(θ[0]): 4.296520300933687,
ParameterVectorElement(θ[4]): -2.598325866938012,
ParameterVectorElement(θ[3]): 6.092947713510392,
ParameterVectorElement(θ[1]): 4.426962159645716,
ParameterVectorElement(θ[2]): 0.5470752986013949,
ParameterVectorElement(θ[6]): -4.717618259450455,
ParameterVectorElement(θ[5]): 1.5683258132970883,
ParameterVectorElement(θ[7]): 0.3602071559531031},
'optimal_point': array([ 4.2965203 , 4.42696216, 0.5470753 , 6.09294771, -2.59832587,
1.56832581, -4.71761826, 0.36020716]),
'optimal_value': -1.8572750175807682,
'optimizer_evals': 65,
'optimizer_time': 0.17621898651123047}
En caso de que tengas dudas, aquí está el aer_simulator nuevamente, pero include_custom se ha dejado por defecto en False. La optimización terminó abruptamente, presumiblemente debido a que el ruido de la iteración confundió al optimizador SLSQP.
[6]:
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('aer_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi)
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
optimal_value = result.optimal_value
print('The optimal value can be seen to be wrong too, i.e. '
f'{optimal_value:.3f} versus the correct {optimal_value1:.3f}.')
print()
print(result)
The optimal value can be seen to be wrong too, i.e. -1.074 versus the correct -1.857.
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 9,
'eigenstate': { '00': 0.7781187248742958,
'01': 0.4881406047441659,
'10': 0.39404750665370286,
'11': 0.03125},
'eigenvalue': -1.0741921795698932,
'optimal_parameters': { ParameterVectorElement(θ[0]): 3.611860069224077,
ParameterVectorElement(θ[7]): 0.6984088030463615,
ParameterVectorElement(θ[3]): 5.949536809130025,
ParameterVectorElement(θ[2]): 0.6019852007557844,
ParameterVectorElement(θ[6]): -5.466043598406607,
ParameterVectorElement(θ[1]): 4.19301252102391,
ParameterVectorElement(θ[4]): -3.3070470445355764,
ParameterVectorElement(θ[5]): 1.8462931831829383},
'optimal_point': array([ 3.61186007, 4.19301252, 0.6019852 , 5.94953681, -3.30704704,
1.84629318, -5.4660436 , 0.6984088 ]),
'optimal_value': -1.0741921795698932,
'optimizer_evals': 9,
'optimizer_time': 0.07421207427978516}
Cambiar el optimizador a SPSA, que está diseñado para trabajar en ambientes con ruido nos da un mejor resultado. Pero el ruido ha afectado el resultado entonces no es tan preciso.
[7]:
from qiskit.algorithms.optimizers import SPSA
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('aer_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SPSA(maxiter=100)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi)
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(result)
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 200,
'eigenstate': {'01': 0.993140536379419, '10': 0.11692679333668567},
'eigenvalue': -1.8618521132262453,
'optimal_parameters': { ParameterVectorElement(θ[4]): -2.3482177711024588,
ParameterVectorElement(θ[2]): -0.23941337071033308,
ParameterVectorElement(θ[7]): 0.22755580746001913,
ParameterVectorElement(θ[3]): 6.6402821272229335,
ParameterVectorElement(θ[6]): -4.707529314643045,
ParameterVectorElement(θ[1]): 3.325806567734803,
ParameterVectorElement(θ[5]): 3.0118347096051314,
ParameterVectorElement(θ[0]): 3.676715527111659},
'optimal_point': array([ 3.67671553, 3.32580657, -0.23941337, 6.64028213, -2.34821777,
3.01183471, -4.70752931, 0.22755581]),
'optimal_value': -1.8618521132262453,
'optimizer_evals': 200,
'optimizer_time': 1.2898402214050293}
Como se mencionó anteriormente, el objeto de valor esperado puede ser dado explícitamente (para que ExpectationFactory
y include_custom nunca sean usados/necesitados). A continuación creamos AerPauliExpectation
y lo pasamos a VQE. Podemos ver que el resultado coincide con el que obtuvimos arriba donde configuramos include_custom en True y dejamos que VQE genere su propio objeto de valor esperado.
[8]:
from qiskit.opflow import AerPauliExpectation
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('aer_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi,
expectation=AerPauliExpectation())
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(result)
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 65,
'eigenstate': {'01': 0.9921567416492215, '10': 0.125},
'eigenvalue': -1.8572750175807682,
'optimal_parameters': { ParameterVectorElement(θ[3]): 6.092947713510392,
ParameterVectorElement(θ[7]): 0.3602071559531031,
ParameterVectorElement(θ[6]): -4.717618259450455,
ParameterVectorElement(θ[4]): -2.598325866938012,
ParameterVectorElement(θ[5]): 1.5683258132970883,
ParameterVectorElement(θ[0]): 4.296520300933687,
ParameterVectorElement(θ[1]): 4.426962159645716,
ParameterVectorElement(θ[2]): 0.5470752986013949},
'optimal_point': array([ 4.2965203 , 4.42696216, 0.5470753 , 6.09294771, -2.59832587,
1.56832581, -4.71761826, 0.36020716]),
'optimal_value': -1.8572750175807682,
'optimizer_evals': 65,
'optimizer_time': 0.20196986198425293}
De forma predeterminada, el objeto PauliExpectation
, que se habría elegido cuando include_custom es False (o cuando se usa aer_simulator de Aer, o un dispositivo real), agrupa a las Paulis en conjuntos de conmutación. Esto es eficiente, ya que ejecuta menos circuitos para calcular el valor esperado. Sin embargo, si por alguna razón deseas ejecutar un circuito para cada Pauli, entonces la agrupación puede desactivarse al construir la PauliExpectation. Debes pasar explícitamente dicha instancia de expectación a VQE para que funcione de esta manera, como se muestra a continuación.
[9]:
from qiskit.opflow import PauliExpectation
algorithm_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('aer_simulator'), seed_transpiler=seed, seed_simulator=seed)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SPSA(maxiter=100)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi,
expectation=PauliExpectation(group_paulis=False))
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(result)
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 200,
'eigenstate': {'01': 0.9916644782889019, '10': 0.1288470508005519},
'eigenvalue': -1.8649830308114583,
'optimal_parameters': { ParameterVectorElement(θ[5]): 2.9887235542230144,
ParameterVectorElement(θ[0]): 3.6833958480452034,
ParameterVectorElement(θ[1]): 3.3827104248942383,
ParameterVectorElement(θ[7]): 0.18495131387681946,
ParameterVectorElement(θ[6]): -4.76651492079903,
ParameterVectorElement(θ[2]): -0.2651696479429452,
ParameterVectorElement(θ[3]): 6.699789960055848,
ParameterVectorElement(θ[4]): -2.282559635034206},
'optimal_point': array([ 3.68339585, 3.38271042, -0.26516965, 6.69978996, -2.28255964,
2.98872355, -4.76651492, 0.18495131]),
'optimal_value': -1.8649830308114583,
'optimizer_evals': 200,
'optimizer_time': 2.700853109359741}
Gradiente¶
A los optimizadores que utilizan técnicas basadas en gradientes se les puede suministrar un gradiente definido por el usuario que será utilizado en lugar del gradiente definido por defecto que normalmente es calculado mediante diferencias finitas simples. Los gradientes se pasan indirectamente al optimizador a través su parámetro gradient
.
Como el uso de un gradient
suministrado por el usuario fue explicado en el tutorial Monitoreo de la convergencia de VQE simplemente te referiré allí. Además, el tutorial Framework de Gradiente Qiskit tiene mucho más sobre los propios gradientes.
Quantum Instance y simulación avanzada¶
Si bien es posible que estés familiarizado con el paso de una QuantumInstance creada a partir de un aer_simulator_statevector
, un aer_simulator
o el backend de un dispositivo real, también es posible utilizar los modos de simulación avanzados de Aer cuando corresponda. Por ejemplo, podemos usar fácilmente el método de Aer Matrix Product State, que tiene el potencial de escalar a un mayor número de qubits.
[10]:
algorithm_globals.random_seed = seed
from qiskit.providers.aer import QasmSimulator
quantum_instance = QuantumInstance(QasmSimulator(method='matrix_product_state'), shots=1)
ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(ansatz, optimizer=slsqp, quantum_instance=qi, include_custom=True)
result = vqe.compute_minimum_eigenvalue(operator=H2_op)
print(result)
{ 'aux_operator_eigenvalues': None,
'cost_function_evals': 65,
'eigenstate': {'01': 0.9921567416492215, '10': 0.125},
'eigenvalue': -1.8572750175807682,
'optimal_parameters': { ParameterVectorElement(θ[2]): 0.5470752986013949,
ParameterVectorElement(θ[3]): 6.092947713510392,
ParameterVectorElement(θ[5]): 1.5683258132970883,
ParameterVectorElement(θ[7]): 0.3602071559531031,
ParameterVectorElement(θ[4]): -2.598325866938012,
ParameterVectorElement(θ[1]): 4.426962159645716,
ParameterVectorElement(θ[6]): -4.717618259450455,
ParameterVectorElement(θ[0]): 4.296520300933687},
'optimal_point': array([ 4.2965203 , 4.42696216, 0.5470753 , 6.09294771, -2.59832587,
1.56832581, -4.71761826, 0.36020716]),
'optimal_value': -1.8572750175807682,
'optimizer_evals': 65,
'optimizer_time': 0.2019951343536377}
[11]:
import qiskit.tools.jupyter
%qiskit_version_table
%qiskit_copyright
Version Information
Qiskit Software | Version |
---|---|
Qiskit | None |
Terra | 0.18.0.dev0+5920b66 |
Aer | 0.9.0 |
Ignis | 0.7.0.dev0+8195559 |
Aqua | None |
IBM Q Provider | None |
System information | |
Python | 3.8.8 (default, Apr 13 2021, 12:59:45) [Clang 10.0.0 ] |
OS | Darwin |
CPUs | 2 |
Memory (Gb) | 12.0 |
Thu May 27 11:03:27 2021 EDT |
This code is a part of Qiskit
© Copyright IBM 2017, 2021.
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.
[ ]: