Skip to content

Commit

Permalink
VQE implementation with estimator primitive (Qiskit/qiskit#8702)
Browse files Browse the repository at this point in the history
* Add minimally working VQE with estimator primitive implementation.

No gradients, no tests etc.

* Revert from dataclass results to original result classes.

* Enforce positional and keyword VQE arguments.

* Move aux op eval logic to function

* Update docstring.

* Remove max_evals_grouped. Force to set directly on optimizer.

* Remove validate min import.

* Make note that eval_observables will be used to eval aux ops.

* Add initial vqe tests.

* Have VQE inherit from VariationalAlgorithm.

* Move energy evaluation to unnested function.

* Construct h2_op using SparsePauliOp

* Add gradient with primitives support.

* Update docstrings

* update broadcast handling

* update eval_energy output for batching

* add incomplete QNSPA test

* fix batch evaluation of QNSPSA test

* remove vqe callback

* move estimator to first arg

* remove usused imports

* add minimum eigensolvers test init file

* add aux ops tests and prepare for new eval_operators

* no longer support account for Nones in aux_ops

* correct typing for MinimumEigensolver

* Compute default initial point using ansatz bounds.

* Add NumPyMinimumEigensolverResult

* Fix type hints

* Fix type hints

* Formatting

* Do not store NumPyMES result inside the algo.

* Provide default values for ansatz and estimator

* Formatting

* fix old and new batching

* Add tests for NumpyMES and import in module.

* Use lazy formatting in log messages

* Use lazy formatting in log messages

* Add back callback to VQE.

* minor renaming

* raise algorithm error if primitive jobs fail

* Add return documentation

* Improve var names and docstrings.

* Apply suggestions from code review

Co-authored-by: ElePT <57907331+ElePT@users.noreply.github.com>
Co-authored-by: dlasecki <dal@zurich.ibm.com>

* minor formatting

* minor formatting

* Ensure evaluate_energy/gradient match

Co-authored-by: Max Rossmannek <max.rossmannek@uzh.ch>

* return bounds logic; fix some docstrings and minor refactor

* Force keyword arguments in vqe.

* Use estimate_observables function

* break up numpy mes tests with subTest

* formatting

* remove redundant eval_aux_ops

* add typehints to docstring attributes

* remove usused imports

* remove default ansatz

* remove usused imports

* update typehints

* avoid changing the original ansatz

* avoid changing the original ansatz

* create separate function to build vqe result

* Correct aux operator eignvalue type hint

Co-authored-by: ElePT <57907331+ElePT@users.noreply.github.com>

* add variance to callback

* use std_dev in callback rather than variance

* formatting

* return variance and shots in callback

* return full metadata in callback

* Move validation functions to algorithms/utils

* correct the callback attribute documentation

* correct the callback attribute typehint docstring

* update VQE docstring

* release note and pending-depreciate old algs

* update vqe class docstring

* Apply suggestions from code review

Co-authored-by: ElePT <57907331+ElePT@users.noreply.github.com>

* Do not copy ansatz

* Note pending depreciation of old algs

* fix docstrings and imports

* Fix issues with building docs

* Include OptimizerResult in VQEResult

* Remove trailing whitespace

* Fix math notation in docstring

* estimate_obervables to return metadata @ElePT +VQE

* add example in release note

* Update evaluate_observables docstring

* Fix observables_evaluator tests.

Co-authored-by: ElePT <57907331+ElePT@users.noreply.github.com>

* fix trotter_qrte tests and remove depreciation

* formatting

* remove unused import

* Apply suggestions to docstring from code review

Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com>

* Update VQE docstring

* Remove printing

Co-authored-by: ElePT <57907331+ElePT@users.noreply.github.com>

* Add parameters to estimate observables

* Fix estimate obs. unit test

* Update arg description

* Update arg description

* keep equation part of sentence

* dict -> dict[str, Any]

* Update qiskit/algorithms/optimizers/spsa.py

Apply suggestion Imamichi-san

* introduce FilterType and aux_operator_eigenvalues -> aux_operators_evaluated

* Correct typehint

Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>

* update vqe docstring and use old typing for type alias

Co-authored-by: ElePT <57907331+ElePT@users.noreply.github.com>
Co-authored-by: dlasecki <dal@zurich.ibm.com>
Co-authored-by: Max Rossmannek <max.rossmannek@uzh.ch>
Co-authored-by: Steve Wood <40241007+woodsp-ibm@users.noreply.github.com>
Co-authored-by: ElePT <epenatap@gmail.com>
Co-authored-by: Ikko Hamamura <ikkoham@users.noreply.github.com>
  • Loading branch information
7 people authored Sep 27, 2022
1 parent 0ee1f8b commit fee5343
Show file tree
Hide file tree
Showing 21 changed files with 1,665 additions and 71 deletions.
16 changes: 14 additions & 2 deletions qiskit_algorithms/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -183,10 +183,12 @@
linear_solvers
Minimum Eigensolvers
--------------------
Minimum Eigen Solvers
---------------------
Algorithms that can find the minimum eigenvalue of an operator.
These algorithms are pending depreciation. One should instead make use of the
Minimum Eigensolver classes in the section below, which leverage Runtime primitives.
.. autosummary::
:toctree: ../stubs/
Expand All @@ -203,6 +205,16 @@
QAOA
VQE
Minimum Eigensolvers
--------------------
Algorithms that can find the minimum eigenvalue of an operator and leverage primitives.
.. autosummary::
:toctree: ../stubs/
minimum_eigensolvers
Optimizers
----------
Expand Down
48 changes: 48 additions & 0 deletions qiskit_algorithms/minimum_eigensolvers/__init__.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
#
# 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.

"""
============================================================================
Minimum Eigensolvers Package (:mod:`qiskit.algorithms.minimum_eigensolvers`)
============================================================================
.. currentmodule:: qiskit.algorithms.minimum_eigensolvers
Minimum Eigensolvers
====================
.. autosummary::
:toctree: ../stubs/
MinimumEigensolver
NumPyMinimumEigensolver
VQE
.. autosummary::
:toctree: ../stubs/
MinimumEigensolverResult
NumPyMinimumEigensolverResult
VQEResult
"""

from .minimum_eigensolver import MinimumEigensolver, MinimumEigensolverResult
from .numpy_minimum_eigensolver import NumPyMinimumEigensolver, NumPyMinimumEigensolverResult
from .vqe import VQE, VQEResult

__all__ = [
"MinimumEigensolver",
"MinimumEigensolverResult",
"NumPyMinimumEigensolver",
"NumPyMinimumEigensolverResult",
"VQE",
"VQEResult",
]
99 changes: 99 additions & 0 deletions qiskit_algorithms/minimum_eigensolvers/minimum_eigensolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
#
# 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.

"""The minimum eigensolver interface and result."""

from __future__ import annotations

from abc import ABC, abstractmethod
from typing import Any

from qiskit.opflow import PauliSumOp
from qiskit.quantum_info.operators.base_operator import BaseOperator

from ..algorithm_result import AlgorithmResult
from ..list_or_dict import ListOrDict


class MinimumEigensolver(ABC):
"""The minimum eigensolver interface.
Algorithms that can compute a minimum eigenvalue for an operator may implement this interface to
allow different algorithms to be used interchangeably.
"""

@abstractmethod
def compute_minimum_eigenvalue(
self,
operator: BaseOperator | PauliSumOp,
aux_operators: ListOrDict[BaseOperator | PauliSumOp] | None = None,
) -> "MinimumEigensolverResult":
"""
Computes the minimum eigenvalue. The ``operator`` and ``aux_operators`` can be supplied here
and if not ``None`` will override any already set into algorithm so it can be reused with
different operators. While an ``operator`` is required by algorithms, ``aux_operators`` are
optional.
Args:
operator: Qubit operator of the observable.
aux_operators: Optional list of auxiliary operators to be evaluated with the
parameters of the minimum eigenvalue main result and their expectation values
returned. For instance in chemistry these can be dipole operators and total particle
count operators, so we can get values for these at the ground state.
Returns:
A minimum eigensolver result.
"""
return MinimumEigensolverResult()

@classmethod
def supports_aux_operators(cls) -> bool:
"""Whether computing the expectation value of auxiliary operators is supported.
If the minimum eigensolver computes an eigenvalue of the main ``operator`` then it can
compute the expectation value of the ``aux_operators`` for that state. Otherwise they will
be ignored.
Returns:
True if aux_operator expectations can be evaluated, False otherwise
"""
return False


class MinimumEigensolverResult(AlgorithmResult):
"""Minimum eigensolver result."""

def __init__(self) -> None:
super().__init__()
self._eigenvalue = None
self._aux_operators_evaluated = None

@property
def eigenvalue(self) -> complex | None:
"""The computed minimum eigenvalue."""
return self._eigenvalue

@eigenvalue.setter
def eigenvalue(self, value: complex) -> None:
self._eigenvalue = value

@property
def aux_operators_evaluated(self) -> ListOrDict[tuple[complex, dict[str, Any]]] | None:
"""The aux operator expectation values.
These values are in fact tuples formatted as (mean, (variance, shots)).
"""
return self._aux_operators_evaluated

@aux_operators_evaluated.setter
def aux_operators_evaluated(self, value: ListOrDict[tuple[complex, dict[str, Any]]]) -> None:
self._aux_operators_evaluated = value
106 changes: 106 additions & 0 deletions qiskit_algorithms/minimum_eigensolvers/numpy_minimum_eigensolver.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
# This code is part of Qiskit.
#
# (C) Copyright IBM 2022.
#
# 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.

"""The NumPy minimum eigensolver algorithm and result."""

from __future__ import annotations

from typing import Callable, List, Union, Optional
import logging
import numpy as np

from qiskit.opflow import PauliSumOp
from qiskit.quantum_info.operators.base_operator import BaseOperator

# TODO this path will need updating when VQD is merged
from ..eigen_solvers.numpy_eigen_solver import NumPyEigensolver
from .minimum_eigensolver import MinimumEigensolver, MinimumEigensolverResult
from ..list_or_dict import ListOrDict

logger = logging.getLogger(__name__)

# future type annotations not supported in type aliases in 3.8
FilterType = Callable[[Union[List, np.ndarray], float, Optional[ListOrDict[float]]], bool]


class NumPyMinimumEigensolver(MinimumEigensolver):
"""
The NumPy minimum eigensolver algorithm.
"""

def __init__(
self,
filter_criterion: FilterType | None = None,
) -> None:
"""
Args:
filter_criterion: callable that allows to filter eigenvalues/eigenstates. The minimum
eigensolver is only searching over feasible states and returns an eigenstate that
has the smallest eigenvalue among feasible states. The callable has the signature
``filter(eigenstate, eigenvalue, aux_values)`` and must return a boolean to indicate
whether to consider this value or not. If there is no feasible element, the result
can even be empty.
"""
self._eigensolver = NumPyEigensolver(filter_criterion=filter_criterion)

@property
def filter_criterion(
self,
) -> FilterType | None:
"""Returns the criterion for filtering eigenstates/eigenvalues."""
return self._eigensolver.filter_criterion

@filter_criterion.setter
def filter_criterion(
self,
filter_criterion: FilterType,
) -> None:
self._eigensolver.filter_criterion = filter_criterion

@classmethod
def supports_aux_operators(cls) -> bool:
return NumPyEigensolver.supports_aux_operators()

def compute_minimum_eigenvalue(
self,
operator: BaseOperator | PauliSumOp,
aux_operators: ListOrDict[BaseOperator | PauliSumOp] | None = None,
) -> NumPyMinimumEigensolverResult:
super().compute_minimum_eigenvalue(operator, aux_operators)
eigensolver_result = self._eigensolver.compute_eigenvalues(operator, aux_operators)
result = NumPyMinimumEigensolverResult()
if eigensolver_result.eigenvalues is not None and len(eigensolver_result.eigenvalues) > 0:
result.eigenvalue = eigensolver_result.eigenvalues[0]
result.eigenstate = eigensolver_result.eigenstates[0]
if eigensolver_result.aux_operator_eigenvalues:
result.aux_operators_evaluated = eigensolver_result.aux_operator_eigenvalues[0]

logger.debug("NumPy minimum eigensolver result: %s", result)

return result


class NumPyMinimumEigensolverResult(MinimumEigensolverResult):
"""NumPy minimum eigensolver result."""

def __init__(self) -> None:
super().__init__()
self._eigenstate = None

@property
def eigenstate(self) -> np.ndarray | None:
"""Returns the eigenstate corresponding to the computed minimum eigenvalue."""
return self._eigenstate

@eigenstate.setter
def eigenstate(self, value: np.ndarray) -> None:
self._eigenstate = value
Loading

0 comments on commit fee5343

Please sign in to comment.