0
$\begingroup$

I want to understand how the number of function evaluation is calculated by Qiskit when running VQE algorithms. Here is some code I used in order to test this:

from qiskit.primitives import Estimator
from qiskit.providers.aer import QasmSimulator, AerSimulator
from qiskit.algorithms.optimizers import SLSQP
from qiskit.utils import QuantumInstance
from qiskit_nature.second_q.algorithms import NumPyMinimumEigensolverFactory
from qiskit_nature.second_q.algorithms.initial_points import HFInitialPoint
from qiskit_nature.second_q.algorithms.ground_state_solvers import GroundStateEigensolver, VQEUCCFactory
from qiskit_nature.second_q.formats.molecule_info import MoleculeInfo
from qiskit_nature.second_q.mappers import QubitConverter
from qiskit_nature.second_q.drivers import PySCFDriver
from qiskit_nature.second_q.transformers import FreezeCoreTransformer
from qiskit_nature.second_q.circuit.library.ansatzes import UCC

objective_function_tolerance = 1e-6
slsqp = SLSQP(maxiter=10000, tol=objective_function_tolerance)

numpy_solver = NumPyMinimumEigensolverFactory()
quantum_instance = QuantumInstance(AerSimulator(method='statevector', device="CPU"))

molecule = MoleculeInfo(["Li", "H"], [(0.0, 0.0, 0.0), (0.0, 0.0, 1.595)])

driver = PySCFDriver.from_molecule(molecule, basis="sto3g")
electronic_structure_problem = driver.run()

transformer = FreezeCoreTransformer()
electronic_structure_problem = transformer.transform(electronic_structure_problem)

num_particles = electronic_structure_problem.num_particles
num_spatial_orbitals = electronic_structure_problem.num_spatial_orbitals
uccsd = UCC(num_spatial_orbitals, num_particles, excitations='sd')

def callback(eval_count, parameters, mean, std): print(eval_count)

vqe_factory = VQEUCCFactory(Estimator(), uccsd, slsqp, initial_point = HFInitialPoint()) 
vqe_factory.minimum_eigensolver.callback = callback
converter = QubitConverter(ParityMapper(), two_qubit_reduction=True, z2symmetry_reduction=None)
gse = GroundStateEigensolver(converter, vqe_factory)
result = gse.solve(electronic_structure_problem)

print("function evaluations: ", result.raw_result.cost_function_evals)

From inspecting the Qiskit code, it seems to me that the number of function evaluations is incremented once after each expectation value of the Hamiltonian is measured or calculated in case of running a simulation on a classical computer. This might appear quite reasonable, except that I was somehow expecting/hoping that the number of function evaluation is incremented after each Pauli string (or group of Pauli strings in case those are grouped in commuting sets) is evaluated during a VQE calculation. Could someone please confirm if my conclusion is correct or not? If possible, some pointers towards the relevant Qiskit code would help.

My Qiskit version:

enter image description here

$\endgroup$

1 Answer 1

1
$\begingroup$

For VQE, the objective function is that function which computes the expectation value (energy) of the Hamiltonian operator given the current state (a parameterized ansatz). The objective function is passed to the optimizer, which varies the parameters, in whatever way it implements, to find a minimum. How it uses the objective function is thus down to the optimizer. The number of functional evals is thus just how may times that function called by the optimizer.

Lets take SLSQP as that's the optimizer you show above. It's gradient based, so from a given point, to compute the next point it will go to, it will, by default, compute a finite diff gradient. This will call the objective function for a set of points, each a small delta away in one of the dimensions from the current point, from which it can then compute the gradient. So a bunch of evals here. This would represent one iter(ation) where maxiters, the limit, is that configured on the optimizer. So an iteration of an optimizer can result in one or more functional evals. As examples COBYLA does one eval per iter, SPSA does 2 evals per iter, SLSQP as you can see depends on the parameter size since it computes a gradient. Some optimizers like L_BFGS also let you set maxfun as as well as maxiter, due to the difference.

So what happens in a functional evaluation - that comes down to your pauli grouping or not comment. It depends how the expectation value is computed. With state_vector, as you use, it can compute the resultant statevector for the ansatz running that circuit and with the operator converted to matrix form compute the expectation having run just one circuit. Let's say we have a real device (or simulator in qasm mode behaving like a real device). Here we need to run a circuit for each pauli in the operator or each pauli group if we have done grouping. How many circuits it runs depends on how many paulis or groups we have. These will all be run under a single functional eval. The total number of circuits run is not counted just what the optimizer is does. The total can be figured though based on the operator and how the expectation value is computed with the given simulator/device.

So to sum up the above circuits run >= functional evals >= iters.

Hopefully this explanation helps you.

$\endgroup$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.