Note

Run interactively in the IBM Quantum lab.

There exist several parameters for configuring and using more advanced VQE capabilities. This tutorial will cover the parameters such as initial_point, expectation and gradient.

It will also cover advanced simulator use such as using Aer with the Matrix Product State method.

[1]:

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
from qiskit.aqua.components.optimizers import SLSQP
from qiskit.circuit.library import TwoLocal


Here we will use the same operator as used in the other VQE algorithms tutorials.

[2]:

H2_op = (-1.052373245772859 * I ^ I) + \
(0.39793742484318045 * I ^ Z) + \
(-0.39793742484318045 * Z ^ I) + \
(-0.01128010425623538 * Z ^ Z) + \
(0.18093119978423156 * X ^ X)

/home/runner/work/qiskit/qiskit/.tox/docs/lib/python3.8/site-packages/qiskit/aqua/operators/operator_base.py:46: DeprecationWarning: The package qiskit.aqua.operators is deprecated. It was moved/refactored to qiskit.opflow (pip install qiskit-terra). For more information see <https://github.com/Qiskit/qiskit-aqua/blob/master/README.md#migration-guide>
warn_package('aqua.operators', 'qiskit.opflow', 'qiskit-terra')


## Initial point¶

The initial_point parameter allows the optimization to begin at the given point, where the point is a list of parameters that will configure the variational form. By default the initial point is None which means that VQE will choose one. The choice in in this case is if the supplied variational form has a preferred point, based on the initial state provided to it, then this will be chosen, otherwise a random initial point that fits with any bounds the variational has will be chosen. If an initial point is supplied it will take priority though and be used - note though it must match in length to the number of parameters in the variational form circuit.

Why to use a initial point? One reason would be if you have guess a reasonable starting point for the problem or perhaps know have information from a prior experiment.

To demonstrate the use let’s first simply repeat the first working example from the algorithms introduction tutorial to get a solution’s optimal point.

[3]:

from qiskit.aqua import aqua_globals
seed = 50
aqua_globals.random_seed = seed
qi = QuantumInstance(BasicAer.get_backend('statevector_simulator'), seed_transpiler=seed, seed_simulator=seed)

ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(operator=H2_op, var_form=ansatz, optimizer=slsqp, quantum_instance=qi)
result = vqe.run()

import pprint
pp = pprint.PrettyPrinter(indent=4)
pp.pprint(result)

<ipython-input-1-945da066a40a>:3: DeprecationWarning: The variable qiskit.aqua.aqua_globals is deprecated. It was moved/refactored to qiskit.utils.algorithm_globals (pip install qiskit-terra). For more information see <https://github.com/Qiskit/qiskit-aqua/blob/master/README.md#migration-guide>
aqua_globals.random_seed = seed
/home/runner/work/qiskit/qiskit/.tox/docs/lib/python3.8/site-packages/qiskit/aqua/quantum_instance.py:135: DeprecationWarning: The class qiskit.aqua.QuantumInstance is deprecated. It was moved/refactored to qiskit.utils.QuantumInstance (pip install qiskit-terra). For more information see <https://github.com/Qiskit/qiskit-aqua/blob/master/README.md#migration-guide>
warn_class('aqua.QuantumInstance',
/home/runner/work/qiskit/qiskit/.tox/docs/lib/python3.8/site-packages/qiskit/aqua/components/optimizers/optimizer.py:49: DeprecationWarning: The package qiskit.aqua.components.optimizers is deprecated. It was moved/refactored to qiskit.algorithms.optimizers (pip install qiskit-terra). For more information see <https://github.com/Qiskit/qiskit-aqua/blob/master/README.md#migration-guide>
warn_package('aqua.components.optimizers',
/home/runner/work/qiskit/qiskit/.tox/docs/lib/python3.8/site-packages/qiskit/aqua/algorithms/vq_algorithm.py:70: DeprecationWarning: The class qiskit.aqua.algorithms.VQAlgorithm is deprecated. It was moved/refactored to qiskit.algorithms.VariationalAlgorithm (pip install qiskit-terra). For more information see <https://github.com/Qiskit/qiskit-aqua/blob/master/README.md#migration-guide>
warn_class('aqua.algorithms.VQAlgorithm',

{   'cost_function_evals': 65,
'eigenstate': array([ 9.55737580e-05-2.35760296e-17j, -9.93766274e-01+1.00398655e-16j,
1.11483552e-01-2.13516261e-16j,  1.77193337e-05-9.93955634e-17j]),
'eigenvalue': (-1.8572750175550534+0j),
'optimal_parameters': {   ParameterVectorElement(θ[0]): 4.296520482689243,
ParameterVectorElement(θ[1]): 4.426962145171391,
ParameterVectorElement(θ[2]): 0.5470754478159714,
ParameterVectorElement(θ[3]): 6.092947716093812,
ParameterVectorElement(θ[4]): -2.5983257657192453,
ParameterVectorElement(θ[5]): 1.568326023314705,
ParameterVectorElement(θ[6]): -4.717618242803465,
ParameterVectorElement(θ[7]): 0.36020722103416913},
'optimal_point': array([ 4.29652048,  4.42696215,  0.54707545,  6.09294772, -2.59832577,
1.56832602, -4.71761824,  0.36020722]),
'optimal_value': -1.8572750175550534,
'optimizer_evals': 65,
'optimizer_time': 0.5491065979003906}


Now we can take the optimal_point from the above result and use it as the initial_point here.

[4]:

initial_pt = result.optimal_point

aqua_globals.random_seed = seed
qi = QuantumInstance(BasicAer.get_backend('statevector_simulator'), seed_transpiler=seed, seed_simulator=seed)

ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(operator=H2_op, var_form=ansatz, optimizer=slsqp, initial_point=initial_pt, quantum_instance=qi)
result1 = vqe.run()

pp.pprint(result1)

{   'cost_function_evals': 9,
'eigenstate': array([ 9.55737580e-05-2.35760296e-17j, -9.93766274e-01+1.00398655e-16j,
1.11483552e-01-2.13516261e-16j,  1.77193337e-05-9.93955634e-17j]),
'eigenvalue': (-1.8572750175550534+0j),
'optimal_parameters': {   ParameterVectorElement(θ[5]): 1.568326023314705,
ParameterVectorElement(θ[2]): 0.5470754478159714,
ParameterVectorElement(θ[6]): -4.717618242803465,
ParameterVectorElement(θ[1]): 4.426962145171391,
ParameterVectorElement(θ[0]): 4.296520482689243,
ParameterVectorElement(θ[4]): -2.5983257657192453,
ParameterVectorElement(θ[3]): 6.092947716093812,
ParameterVectorElement(θ[7]): 0.36020722103416913},
'optimal_point': array([ 4.29652048,  4.42696215,  0.54707545,  6.09294772, -2.59832577,
1.56832602, -4.71761824,  0.36020722]),
'optimal_value': -1.8572750175550534,
'optimizer_evals': 9,
'optimizer_time': 0.04313993453979492}


Here we see that result was arrived at much more quickly where optimizer_evals is 10 versus 72 when it started from a random value when the initial point was not supplied (default of None).

Where this becomes useful for examples where we the solution to one problem can be used to for a guess for the solution to a very close similar problem. Chemistry is very good example where we change the inter-atomic distance(s) of molecule to plot a dissociation profile. When the distance changes are small we expect the solution to still be nearby the prior one. One technique is to simply use the optimal point from one solution as the starting point for the next step. Now more complex techniques are possible that do some extrapolation to compute an initial position based on prior solution(s) rather than directly use the prior solution. The Qiskit Chemistry sampling_potential_energy_surfaces tutorial shows such bootstrapping and extrapolation.

## Expectation¶

The energy of the Hamiltonian operator that VQE is working on is the expectation value when evaluated with the parameterized variational form. To compute the expectation value VQE uses an instance of an expectation object. Such an instance may be supplied via the expectation parameter, or in the default case, where it has a value of None, VQE will use the ExpectationFactory to create itself a suitable instance based on the supplied backend.

For most cases letting VQE create a suitable instance is sufficient. However the Qiskit Aer qasm_simulator supports a snapshot instruction that can be used in conjunction with the operator expectation computation. If used then the outcome is ideal, i.e. like the statevector simulator, and has no shot noise. Since people normally choose the qasm_simulator to have shot noise (sampling noise), and be more like a real-device outcome, VQE has an include_custom flag that is passed on to the ExpectationFactory. When using Aer qasm simulator, and this is set True, the factory will return AerPauliExpectation which uses the snapshot instruction, when False, default, then the regular PauliExpectation is returned.

The following example shows include_custom=True where the outcome matches the statevector simulator. In fact it can be better/faster to do this than use the statevector_simulator directly. This is because in the latter case when the Hamiltonian is a sum of Paulis it must be converted to matrix form, and this is avoided when using the snapshot instruction done when include_custom is True.

[5]:

from qiskit import Aer

aqua_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('qasm_simulator'), seed_transpiler=seed, seed_simulator=seed)

ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(operator=H2_op, var_form=ansatz, optimizer=slsqp, quantum_instance=qi, include_custom=True)
result = vqe.run()

pp.pprint(result)

{   'cost_function_evals': 65,
'eigenstate': {'01': 1008, '10': 16},
'eigenvalue': (-1.8572750175597519+0j),
'optimal_parameters': {   ParameterVectorElement(θ[7]): 0.3602072910298268,
ParameterVectorElement(θ[6]): -4.717618128585369,
ParameterVectorElement(θ[3]): 6.092947836794945,
ParameterVectorElement(θ[4]): -2.5983258956331645,
ParameterVectorElement(θ[0]): 4.296520463599476,
ParameterVectorElement(θ[1]): 4.426962139199476,
ParameterVectorElement(θ[2]): 0.5470754235069875,
ParameterVectorElement(θ[5]): 1.568326000491598},
'optimal_point': array([ 4.29652046,  4.42696214,  0.54707542,  6.09294784, -2.5983259 ,
1.568326  , -4.71761813,  0.36020729]),
'optimal_value': -1.8572750175597519,
'optimizer_evals': 65,
'optimizer_time': 0.3264896869659424}


In case you have doubts here is the qasm_simulator again but include_custom has been left to default to False. The optimization ended abruptly, presumably due to the shot noise confusing the SLSQP optimizer. The optimal value can be seen to be wrong too, i.e. -1.098 versus the correct -1.857.

[6]:

aqua_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('qasm_simulator'), seed_transpiler=seed, seed_simulator=seed)

ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(operator=H2_op, var_form=ansatz, optimizer=slsqp, quantum_instance=qi)
result = vqe.run()

pp.pprint(result)

{   'cost_function_evals': 9,
'eigenstate': {'00': 620, '01': 244, '10': 159, '11': 1},
'eigenvalue': (-1.0987888676631705+0j),
'optimal_parameters': {   ParameterVectorElement(θ[6]): -5.466043598406607,
ParameterVectorElement(θ[7]): 0.6984088030463615,
ParameterVectorElement(θ[2]): 0.6019852007557844,
ParameterVectorElement(θ[3]): 5.949536809130025,
ParameterVectorElement(θ[5]): 1.8462931831829383,
ParameterVectorElement(θ[0]): 3.611860069224077,
ParameterVectorElement(θ[4]): -3.3070470445355764,
ParameterVectorElement(θ[1]): 4.19301252102391},
'optimal_point': array([ 3.61186007,  4.19301252,  0.6019852 ,  5.94953681, -3.30704704,
1.84629318, -5.4660436 ,  0.6984088 ]),
'optimal_value': -1.0987888676631705,
'optimizer_evals': 9,
'optimizer_time': 0.3958621025085449}


Changing the optimizer to SPSA, which is designed to work in noisy environments, gets us a better result. Though the noise has affected the outcome so it’s not as accurate.

[7]:

from qiskit.aqua.components.optimizers import SPSA

aqua_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('qasm_simulator'), seed_transpiler=seed, seed_simulator=seed)

ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SPSA(maxiter=100)
vqe = VQE(operator=H2_op, var_form=ansatz, optimizer=slsqp, quantum_instance=qi)
result = vqe.run()

pp.pprint(result)

{   'cost_function_evals': 241,
'eigenstate': {'01': 1007, '10': 17},
'eigenvalue': (-1.8623464125433034+0j),
'optimal_parameters': {   ParameterVectorElement(θ[5]): 0.026059065222907917,
ParameterVectorElement(θ[4]): -4.668505888665846,
ParameterVectorElement(θ[6]): -4.442620712159775,
ParameterVectorElement(θ[7]): -1.8648388048829725,
ParameterVectorElement(θ[2]): 2.1468600656958965,
ParameterVectorElement(θ[3]): 7.149874669811866,
ParameterVectorElement(θ[0]): 4.731406884288577,
ParameterVectorElement(θ[1]): 2.004703790440874},
'optimal_point': array([ 4.73140688,  2.00470379,  2.14686007,  7.14987467, -4.66850589,
0.02605907, -4.44262071, -1.8648388 ]),
'optimal_value': -1.8623464125433034,
'optimizer_time': 9.374714136123657}


As mentioned above, an expectation object can be explicitly given (so the internal ExpectationFactory and include_custom are never used/needed. Below we create an AerPauliExpectation and pass this to VQE. We can see the result matches that above where we set include_custom to True and let VQE create its own expectation object.

[8]:

from qiskit.aqua.operators import AerPauliExpectation

aqua_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('qasm_simulator'), seed_transpiler=seed, seed_simulator=seed)

ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SLSQP(maxiter=1000)
vqe = VQE(operator=H2_op, var_form=ansatz, optimizer=slsqp, quantum_instance=qi,
expectation=AerPauliExpectation())
result = vqe.run()

pp.pprint(result)

{   'cost_function_evals': 65,
'eigenstate': {'01': 1008, '10': 16},
'eigenvalue': (-1.8572750175597519+0j),
'optimal_parameters': {   ParameterVectorElement(θ[1]): 4.426962139199476,
ParameterVectorElement(θ[4]): -2.5983258956331645,
ParameterVectorElement(θ[6]): -4.717618128585369,
ParameterVectorElement(θ[5]): 1.568326000491598,
ParameterVectorElement(θ[2]): 0.5470754235069875,
ParameterVectorElement(θ[3]): 6.092947836794945,
ParameterVectorElement(θ[7]): 0.3602072910298268,
ParameterVectorElement(θ[0]): 4.296520463599476},
'optimal_point': array([ 4.29652046,  4.42696214,  0.54707542,  6.09294784, -2.5983259 ,
1.568326  , -4.71761813,  0.36020729]),
'optimal_value': -1.8572750175597519,
'optimizer_evals': 65,
'optimizer_time': 0.31810832023620605}


By default, the PauliExpectation object, that would have be chosen when include_custom is False (or when using BasicAer qasm_simulator, or a real device) groups Paulis into commuting sets. This is efficient as it runs less circuits to compute the expectation. However, if for some reason you wanted to run a circuit for each Pauli then then grouping can be turned off when constructing the PauliExpectation. You need to explicitly pass in such an expectation instance to VQE to have it work this way though as shown below.

[9]:

from qiskit.aqua.operators import PauliExpectation

aqua_globals.random_seed = seed
qi = QuantumInstance(Aer.get_backend('qasm_simulator'), seed_transpiler=seed, seed_simulator=seed)

ansatz = TwoLocal(rotation_blocks='ry', entanglement_blocks='cz')
slsqp = SPSA(maxiter=100)
vqe = VQE(operator=H2_op, var_form=ansatz, optimizer=slsqp, quantum_instance=qi,
expectation=PauliExpectation(group_paulis=False))
result = vqe.run()

pp.pprint(result)

{   'cost_function_evals': 241,
'eigenstate': {'01': 1007, '10': 17},
'eigenvalue': (-1.8667499370593512+0j),
'optimal_parameters': {   ParameterVectorElement(θ[6]): -4.360567678129975,
ParameterVectorElement(θ[3]): 7.3174816026118705,
ParameterVectorElement(θ[4]): -4.7337899207347105,
ParameterVectorElement(θ[2]): 2.1876557567510515,
ParameterVectorElement(θ[1]): 2.1216349904032965,
ParameterVectorElement(θ[0]): 4.718047005792739,
ParameterVectorElement(θ[5]): 0.2634941452871497,
ParameterVectorElement(θ[7]): -1.9698662442728718},
'optimal_point': array([ 4.71804701,  2.12163499,  2.18765576,  7.3174816 , -4.73378992,
0.26349415, -4.36056768, -1.96986624]),
'optimal_value': -1.8667499370593512,
'optimizer_time': 13.447062492370605}


Optimizers that use a gradient-based technique can be supplied with a user defined gradient that will be used instead of their default gradient computation which is usually done by simple finite difference. Gradients are passed indirectly via to the optimizer via its gradient parameter.

As the use of a user supplied gradient was shown in the Monitoring VQE Convergence tutorial I will simply refer you there. Also the Gradients framework tutorial has much more about the gradients themselves.

## Quantum Instance and advanced simulation¶

While you may be familiar with passing a QuantumInstancen created from a statevector_simulator a qasm_simulator or real device backend, it is possible to use the advanced simulation modes of Aer too when applicable. For instance we can easily use the Aer Matrix Product State method, that has the potential to scale to larger numbers of qubits.

[10]:

aqua_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(operator=H2_op, var_form=ansatz, optimizer=slsqp, quantum_instance=qi, include_custom=True)
result = vqe.run()

pp.pprint(result)

{   'cost_function_evals': 65,
'eigenstate': {'01': 1008, '10': 16},
'eigenvalue': (-1.8572750175597519+0j),
'optimal_parameters': {   ParameterVectorElement(θ[0]): 4.296520463599476,
ParameterVectorElement(θ[1]): 4.426962139199476,
ParameterVectorElement(θ[2]): 0.5470754235069875,
ParameterVectorElement(θ[6]): -4.717618128585369,
ParameterVectorElement(θ[7]): 0.3602072910298268,
ParameterVectorElement(θ[5]): 1.568326000491598,
ParameterVectorElement(θ[3]): 6.092947836794945,
ParameterVectorElement(θ[4]): -2.5983258956331645},
'optimal_point': array([ 4.29652046,  4.42696214,  0.54707542,  6.09294784, -2.5983259 ,
1.568326  , -4.71761813,  0.36020729]),
'optimal_value': -1.8572750175597519,
'optimizer_evals': 65,
'optimizer_time': 0.33710193634033203}

[11]:

import qiskit.tools.jupyter
%qiskit_version_table


### Version Information

Qiskit SoftwareVersion
Qiskit0.25.4
Terra0.17.2
Aer0.8.2
Ignis0.6.0
Aqua0.9.1
IBM Q Provider0.12.3
System information
Python3.8.10 (default, May 4 2021, 07:16:51) [GCC 9.3.0]
OSLinux
CPUs2
Memory (Gb)6.791343688964844
Wed May 05 19:45:27 2021 UTC

### This code is a part of Qiskit

[ ]: