Skip to content

Commit 022aff6

Browse files
mrossinekCryorismergify[bot]
authored
Various improvements to AdaptVQE (#9215)
* Various improvements to AdaptVQE * A little more detail for the reno * retrigger CI Co-authored-by: Julien Gacon <gaconju@gmail.com> Co-authored-by: mergify[bot] <37929162+mergify[bot]@users.noreply.github.com>
1 parent d96158e commit 022aff6

File tree

3 files changed

+47
-9
lines changed

3 files changed

+47
-9
lines changed

qiskit/algorithms/minimum_eigensolvers/adapt_vqe.py

+36-9
Original file line numberDiff line numberDiff line change
@@ -172,7 +172,6 @@ def _check_cyclicity(indices: list[int]) -> bool:
172172
# -> this results in any sequence of at least two numbers being detected
173173
match = cycle_regex.search(" ".join(map(str, indices)))
174174
logger.debug("Cycle detected: %s", match)
175-
logger.info("Alternating sequence found. Finishing.")
176175
# Additionally we also need to check whether the last two numbers are identical, because the
177176
# reg-ex above will only find cycles of at least two consecutive numbers.
178177
# It is sufficient to assert that the last two numbers are different due to the iterative
@@ -212,18 +211,23 @@ def compute_minimum_eigenvalue(
212211
theta: list[float] = []
213212
max_grad: tuple[float, Optional[PauliSumOp]] = (0.0, None)
214213
self._excitation_list = []
214+
history: list[float] = []
215215
iteration = 0
216216
while self.max_iterations is None or iteration < self.max_iterations:
217217
iteration += 1
218218
logger.info("--- Iteration #%s ---", str(iteration))
219219
# compute gradients
220+
logger.debug("Computing gradients")
220221
cur_grads = self._compute_gradients(theta, operator)
221222
# pick maximum gradient
222223
max_grad_index, max_grad = max(
223224
enumerate(cur_grads), key=lambda item: np.abs(item[1][0])
224225
)
225-
# store maximum gradient's index for cycle detection
226-
prev_op_indices.append(max_grad_index)
226+
logger.info(
227+
"Found maximum gradient %s at index %s",
228+
str(np.abs(max_grad[0])),
229+
str(max_grad_index),
230+
)
227231
# log gradients
228232
if np.abs(max_grad[0]) < self.threshold:
229233
if iteration == 1:
@@ -238,12 +242,18 @@ def compute_minimum_eigenvalue(
238242
)
239243
termination_criterion = TerminationCriterion.CONVERGED
240244
break
245+
# store maximum gradient's index for cycle detection
246+
prev_op_indices.append(max_grad_index)
241247
# check indices of picked gradients for cycles
242248
if self._check_cyclicity(prev_op_indices):
249+
logger.info("Alternating sequence found. Finishing.")
243250
logger.info("Final maximum gradient: %s", str(np.abs(max_grad[0])))
244251
termination_criterion = TerminationCriterion.CYCLICITY
245252
break
246253
# add new excitation to self._ansatz
254+
logger.info(
255+
"Adding new operator to the ansatz: %s", str(self._excitation_pool[max_grad_index])
256+
)
247257
self._excitation_list.append(self._excitation_pool[max_grad_index])
248258
theta.append(0.0)
249259
# run VQE on current Ansatz
@@ -252,6 +262,8 @@ def compute_minimum_eigenvalue(
252262
self.solver.initial_point = theta
253263
raw_vqe_result = self.solver.compute_minimum_eigenvalue(operator)
254264
theta = raw_vqe_result.optimal_point.tolist()
265+
history.append(raw_vqe_result.eigenvalue)
266+
logger.info("Current eigenvalue: %s", str(raw_vqe_result.eigenvalue))
255267
else:
256268
# reached maximum number of iterations
257269
termination_criterion = TerminationCriterion.MAXIMUM
@@ -263,6 +275,7 @@ def compute_minimum_eigenvalue(
263275
result.num_iterations = iteration
264276
result.final_max_gradient = max_grad[0]
265277
result.termination_criterion = termination_criterion
278+
result.eigenvalue_history = history
266279

267280
# once finished evaluate auxiliary operators if any
268281
if aux_operators is not None:
@@ -284,33 +297,47 @@ def __init__(self) -> None:
284297
self._num_iterations: int = None
285298
self._final_max_gradient: float = None
286299
self._termination_criterion: str = ""
300+
self._eigenvalue_history: list[float] = None
287301

288302
@property
289303
def num_iterations(self) -> int:
290-
"""Returns number of iterations"""
304+
"""Returns the number of iterations."""
291305
return self._num_iterations
292306

293307
@num_iterations.setter
294308
def num_iterations(self, value: int) -> None:
295-
"""Sets number of iterations"""
309+
"""Sets the number of iterations."""
296310
self._num_iterations = value
297311

298312
@property
299313
def final_max_gradient(self) -> float:
300-
"""Returns final maximum gradient"""
314+
"""Returns the final maximum gradient."""
301315
return self._final_max_gradient
302316

303317
@final_max_gradient.setter
304318
def final_max_gradient(self, value: float) -> None:
305-
"""Sets final maximum gradient"""
319+
"""Sets the final maximum gradient."""
306320
self._final_max_gradient = value
307321

308322
@property
309323
def termination_criterion(self) -> str:
310-
"""Returns termination criterion"""
324+
"""Returns the termination criterion."""
311325
return self._termination_criterion
312326

313327
@termination_criterion.setter
314328
def termination_criterion(self, value: str) -> None:
315-
"""Sets termination criterion"""
329+
"""Sets the termination criterion."""
316330
self._termination_criterion = value
331+
332+
@property
333+
def eigenvalue_history(self) -> list[float]:
334+
"""Returns the history of computed eigenvalues.
335+
336+
The history's length matches the number of iterations and includes the final computed value.
337+
"""
338+
return self._eigenvalue_history
339+
340+
@eigenvalue_history.setter
341+
def eigenvalue_history(self, eigenvalue_history: list[float]) -> None:
342+
"""Sets the history of computed eigenvalues."""
343+
self._eigenvalue_history = eigenvalue_history
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
---
2+
features:
3+
- |
4+
Adds the :attr:`.AdaptVQEResult.eigenvalue_history` attribute. This tracks the lowest
5+
achieved energy per iteration of the AdaptVQE.
6+
- |
7+
Improves the logging done during the execution of an :class:`.AdaptVQE`.

test/python/algorithms/minimum_eigensolvers/test_adapt_vqe.py

+4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@
1717

1818
from ddt import ddt, data, unpack
1919

20+
import numpy as np
21+
2022
from qiskit.algorithms.minimum_eigensolvers import VQE
2123
from qiskit.algorithms.minimum_eigensolvers.adapt_vqe import AdaptVQE, TerminationCriterion
2224
from qiskit.algorithms.optimizers import SLSQP
@@ -92,6 +94,7 @@ def test_default(self):
9294
expected_eigenvalue = -1.85727503
9395

9496
self.assertAlmostEqual(res.eigenvalue, expected_eigenvalue, places=6)
97+
np.testing.assert_allclose(res.eigenvalue_history, [expected_eigenvalue], rtol=1e-6)
9598

9699
def test_converged(self):
97100
"""Test to check termination criteria"""
@@ -171,6 +174,7 @@ def test_supports_aux_operators(self):
171174

172175
self.assertAlmostEqual(res.eigenvalue, expected_eigenvalue, places=6)
173176
self.assertAlmostEqual(res.aux_operators_evaluated[0][0], expected_eigenvalue, places=6)
177+
np.testing.assert_allclose(res.eigenvalue_history, [expected_eigenvalue], rtol=1e-6)
174178

175179

176180
if __name__ == "__main__":

0 commit comments

Comments
 (0)