Skip to content

Commit

Permalink
introduce FilterType and aux_operator_eigenvalues -> aux_operators_ev…
Browse files Browse the repository at this point in the history
…aluated
  • Loading branch information
declanmillar committed Sep 27, 2022
1 parent 18b5b63 commit 0e0ebac
Show file tree
Hide file tree
Showing 5 changed files with 100 additions and 101 deletions.
15 changes: 7 additions & 8 deletions qiskit/algorithms/minimum_eigensolvers/minimum_eigensolver.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@
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
Expand Down Expand Up @@ -74,7 +75,7 @@ class MinimumEigensolverResult(AlgorithmResult):
def __init__(self) -> None:
super().__init__()
self._eigenvalue = None
self._aux_operator_eigenvalues = None
self._aux_operators_evaluated = None

@property
def eigenvalue(self) -> complex | None:
Expand All @@ -86,15 +87,13 @@ def eigenvalue(self, value: complex) -> None:
self._eigenvalue = value

@property
def aux_operator_eigenvalues(self) -> ListOrDict[tuple[complex, tuple[complex, int]]] | None:
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_operator_eigenvalues
return self._aux_operators_evaluated

@aux_operator_eigenvalues.setter
def aux_operator_eigenvalues(
self, value: ListOrDict[tuple[complex, tuple[complex, int]]]
) -> None:
self._aux_operator_eigenvalues = value
@aux_operators_evaluated.setter
def aux_operators_evaluated(self, value: ListOrDict[tuple[complex, dict[str, Any]]]) -> None:
self._aux_operators_evaluated = value
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@

logger = logging.getLogger(__name__)

FilterType = Callable[[list | np.ndarray, float, ListOrDict[float] | None], bool]


class NumPyMinimumEigensolver(MinimumEigensolver):
"""
Expand All @@ -36,9 +38,7 @@ class NumPyMinimumEigensolver(MinimumEigensolver):

def __init__(
self,
filter_criterion: Callable[
[list | np.ndarray, float, ListOrDict[float] | None], bool
] = None,
filter_criterion: FilterType | None = None,
) -> None:
"""
Args:
Expand All @@ -54,14 +54,14 @@ def __init__(
@property
def filter_criterion(
self,
) -> Callable[[list | np.ndarray, float, ListOrDict[float] | None], bool] | None:
) -> FilterType | None:
"""Returns the criterion for filtering eigenstates/eigenvalues."""
return self._eigensolver.filter_criterion

@filter_criterion.setter
def filter_criterion(
self,
filter_criterion: Callable[[list | np.ndarray, float, ListOrDict[float] | None], bool],
filter_criterion: FilterType,
) -> None:
self._eigensolver.filter_criterion = filter_criterion

Expand All @@ -81,7 +81,7 @@ def compute_minimum_eigenvalue(
result.eigenvalue = eigensolver_result.eigenvalues[0]
result.eigenstate = eigensolver_result.eigenstates[0]
if eigensolver_result.aux_operator_eigenvalues:
result.aux_operator_eigenvalues = eigensolver_result.aux_operator_eigenvalues[0]
result.aux_operators_evaluated = eigensolver_result.aux_operator_eigenvalues[0]

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

Expand Down
18 changes: 9 additions & 9 deletions qiskit/algorithms/minimum_eigensolvers/vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -182,22 +182,22 @@ def compute_minimum_eigenvalue(
fun=evaluate_energy, x0=initial_point, jac=evaluate_gradient, bounds=bounds
)

eval_time = time() - start_time
optimizer_time = time() - start_time

logger.info(
"Optimization complete in %s seconds.\nFound optimal point %s",
eval_time,
optimizer_time,
optimizer_result.x,
)

if aux_operators is not None:
aux_values = estimate_observables(
aux_operators_evaluated = estimate_observables(
self.estimator, self.ansatz, aux_operators, optimizer_result.x
)
else:
aux_values = None
aux_operators_evaluated = None

return self._build_vqe_result(optimizer_result, aux_values, eval_time)
return self._build_vqe_result(optimizer_result, aux_operators_evaluated, optimizer_time)

@classmethod
def supports_aux_operators(cls) -> bool:
Expand Down Expand Up @@ -306,17 +306,17 @@ def _check_operator_ansatz(self, operator: BaseOperator | PauliSumOp):
def _build_vqe_result(
self,
optimizer_result: OptimizerResult,
aux_values: ListOrDict[tuple[complex, tuple[complex, int]]],
eval_time: float,
aux_operators_evaluated: ListOrDict[tuple[complex, tuple[complex, int]]],
optimizer_time: float,
) -> VQEResult:
result = VQEResult()
result.eigenvalue = optimizer_result.fun
result.cost_function_evals = optimizer_result.nfev
result.optimal_point = optimizer_result.x
result.optimal_parameters = dict(zip(self.ansatz.parameters, optimizer_result.x))
result.optimal_value = optimizer_result.fun
result.optimizer_time = eval_time
result.aux_operator_eigenvalues = aux_values
result.optimizer_time = optimizer_time
result.aux_operators_evaluated = aux_operators_evaluated
result.optimizer_result = optimizer_result
return result

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,9 @@ def test_cme(self):
operator=self.qubit_op, aux_operators=self.aux_ops_list
)
self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j)
self.assertEqual(len(result.aux_operator_eigenvalues), 2)
np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[0], [2, 0])
np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[1], [0, 0])
self.assertEqual(len(result.aux_operators_evaluated), 2)
np.testing.assert_array_almost_equal(result.aux_operators_evaluated[0], [2, 0])
np.testing.assert_array_almost_equal(result.aux_operators_evaluated[1], [0, 0])

def test_cme_reuse(self):
"""Test reuse"""
Expand All @@ -62,38 +62,38 @@ def test_cme_reuse(self):
result = algo.compute_minimum_eigenvalue(operator=self.qubit_op)
self.assertEqual(result.eigenvalue.dtype, np.float64)
self.assertAlmostEqual(result.eigenvalue, -1.85727503)
self.assertIsNone(result.aux_operator_eigenvalues)
self.assertIsNone(result.aux_operators_evaluated)

with self.subTest("Test with added aux_operators"):
result = algo.compute_minimum_eigenvalue(
operator=self.qubit_op, aux_operators=self.aux_ops_list
)
self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j)
self.assertEqual(len(result.aux_operator_eigenvalues), 2)
np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[0], [2, 0])
np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[1], [0, 0])
self.assertEqual(len(result.aux_operators_evaluated), 2)
np.testing.assert_array_almost_equal(result.aux_operators_evaluated[0], [2, 0])
np.testing.assert_array_almost_equal(result.aux_operators_evaluated[1], [0, 0])

with self.subTest("Test with aux_operators removed"):
result = algo.compute_minimum_eigenvalue(operator=self.qubit_op, aux_operators=[])
self.assertEqual(result.eigenvalue.dtype, np.float64)
self.assertAlmostEqual(result.eigenvalue, -1.85727503)
self.assertIsNone(result.aux_operator_eigenvalues)
self.assertIsNone(result.aux_operators_evaluated)

with self.subTest("Test with aux_operators set again"):
result = algo.compute_minimum_eigenvalue(
operator=self.qubit_op, aux_operators=self.aux_ops_list
)
self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j)
self.assertEqual(len(result.aux_operator_eigenvalues), 2)
np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[0], [2, 0])
np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[1], [0, 0])
self.assertEqual(len(result.aux_operators_evaluated), 2)
np.testing.assert_array_almost_equal(result.aux_operators_evaluated[0], [2, 0])
np.testing.assert_array_almost_equal(result.aux_operators_evaluated[1], [0, 0])

with self.subTest("Test after setting first aux_operators as main operator"):
result = algo.compute_minimum_eigenvalue(
operator=self.aux_ops_list[0], aux_operators=[]
)
self.assertAlmostEqual(result.eigenvalue, 2 + 0j)
self.assertIsNone(result.aux_operator_eigenvalues)
self.assertIsNone(result.aux_operators_evaluated)

def test_cme_filter(self):
"""Basic test"""
Expand All @@ -108,9 +108,9 @@ def criterion(x, v, a_v):
operator=self.qubit_op, aux_operators=self.aux_ops_list
)
self.assertAlmostEqual(result.eigenvalue, -0.22491125 + 0j)
self.assertEqual(len(result.aux_operator_eigenvalues), 2)
np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[0], [2, 0])
np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues[1], [0, 0])
self.assertEqual(len(result.aux_operators_evaluated), 2)
np.testing.assert_array_almost_equal(result.aux_operators_evaluated[0], [2, 0])
np.testing.assert_array_almost_equal(result.aux_operators_evaluated[1], [0, 0])

def test_cme_filter_empty(self):
"""Test with filter always returning False"""
Expand All @@ -126,7 +126,7 @@ def criterion(x, v, a_v):
)
self.assertEqual(result.eigenvalue, None)
self.assertEqual(result.eigenstate, None)
self.assertEqual(result.aux_operator_eigenvalues, None)
self.assertEqual(result.aux_operators_evaluated, None)

@data("X", "Y", "Z")
def test_cme_1q(self, op):
Expand All @@ -144,27 +144,27 @@ def test_cme_aux_ops_dict(self):
with self.subTest("Test with an empty dictionary."):
result = algo.compute_minimum_eigenvalue(operator=self.qubit_op, aux_operators={})
self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j)
self.assertIsNone(result.aux_operator_eigenvalues)
self.assertIsNone(result.aux_operators_evaluated)

with self.subTest("Test with two auxiliary operators."):
result = algo.compute_minimum_eigenvalue(
operator=self.qubit_op, aux_operators=self.aux_ops_dict
)
self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j)
self.assertEqual(len(result.aux_operator_eigenvalues), 2)
np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues["aux_op1"], [2, 0])
np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues["aux_op2"], [0, 0])
self.assertEqual(len(result.aux_operators_evaluated), 2)
np.testing.assert_array_almost_equal(result.aux_operators_evaluated["aux_op1"], [2, 0])
np.testing.assert_array_almost_equal(result.aux_operators_evaluated["aux_op2"], [0, 0])

with self.subTest("Test with additional zero and None operators."):
extra_ops = {"None_op": None, "zero_op": 0, **self.aux_ops_dict}
result = algo.compute_minimum_eigenvalue(
operator=self.qubit_op, aux_operators=extra_ops
)
self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j)
self.assertEqual(len(result.aux_operator_eigenvalues), 3)
np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues["aux_op1"], [2, 0])
np.testing.assert_array_almost_equal(result.aux_operator_eigenvalues["aux_op2"], [0, 0])
self.assertEqual(result.aux_operator_eigenvalues["zero_op"], (0.0, 0))
self.assertEqual(len(result.aux_operators_evaluated), 3)
np.testing.assert_array_almost_equal(result.aux_operators_evaluated["aux_op1"], [2, 0])
np.testing.assert_array_almost_equal(result.aux_operators_evaluated["aux_op2"], [0, 0])
self.assertEqual(result.aux_operators_evaluated["zero_op"], (0.0, 0))

def test_aux_operators_list(self):
"""Test list-based aux_operators."""
Expand All @@ -176,30 +176,30 @@ def test_aux_operators_list(self):
with self.subTest("Test with two auxiliary operators."):
result = algo.compute_minimum_eigenvalue(operator=self.qubit_op, aux_operators=aux_ops)
self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j)
self.assertEqual(len(result.aux_operator_eigenvalues), 2)
self.assertEqual(len(result.aux_operators_evaluated), 2)
# expectation values
self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0], 2, places=6)
self.assertAlmostEqual(result.aux_operator_eigenvalues[1][0], 0, places=6)
self.assertAlmostEqual(result.aux_operators_evaluated[0][0], 2, places=6)
self.assertAlmostEqual(result.aux_operators_evaluated[1][0], 0, places=6)
# standard deviations
self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1], 0.0)
self.assertAlmostEqual(result.aux_operator_eigenvalues[1][1], 0.0)
self.assertAlmostEqual(result.aux_operators_evaluated[0][1], 0.0)
self.assertAlmostEqual(result.aux_operators_evaluated[1][1], 0.0)

with self.subTest("Test with additional zero and None operators."):
extra_ops = [*aux_ops, None, 0]
result = algo.compute_minimum_eigenvalue(
operator=self.qubit_op, aux_operators=extra_ops
)
self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j)
self.assertEqual(len(result.aux_operator_eigenvalues), 4)
self.assertEqual(len(result.aux_operators_evaluated), 4)
# expectation values
self.assertAlmostEqual(result.aux_operator_eigenvalues[0][0], 2, places=6)
self.assertAlmostEqual(result.aux_operator_eigenvalues[1][0], 0, places=6)
self.assertIsNone(result.aux_operator_eigenvalues[2], None)
self.assertEqual(result.aux_operator_eigenvalues[3][0], 0.0)
self.assertAlmostEqual(result.aux_operators_evaluated[0][0], 2, places=6)
self.assertAlmostEqual(result.aux_operators_evaluated[1][0], 0, places=6)
self.assertIsNone(result.aux_operators_evaluated[2], None)
self.assertEqual(result.aux_operators_evaluated[3][0], 0.0)
# standard deviations
self.assertAlmostEqual(result.aux_operator_eigenvalues[0][1], 0.0)
self.assertAlmostEqual(result.aux_operator_eigenvalues[1][1], 0.0)
self.assertEqual(result.aux_operator_eigenvalues[3][1], 0.0)
self.assertAlmostEqual(result.aux_operators_evaluated[0][1], 0.0)
self.assertAlmostEqual(result.aux_operators_evaluated[1][1], 0.0)
self.assertEqual(result.aux_operators_evaluated[3][1], 0.0)

def test_aux_operators_dict(self):
"""Test dict-based aux_operators."""
Expand All @@ -211,30 +211,30 @@ def test_aux_operators_dict(self):
with self.subTest("Test with two auxiliary operators."):
result = algo.compute_minimum_eigenvalue(operator=self.qubit_op, aux_operators=aux_ops)
self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j)
self.assertEqual(len(result.aux_operator_eigenvalues), 2)
self.assertEqual(len(result.aux_operators_evaluated), 2)
# expectation values
self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op1"][0], 2, places=6)
self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op2"][0], 0, places=6)
self.assertAlmostEqual(result.aux_operators_evaluated["aux_op1"][0], 2, places=6)
self.assertAlmostEqual(result.aux_operators_evaluated["aux_op2"][0], 0, places=6)
# standard deviations
self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op1"][1], 0.0)
self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op2"][1], 0.0)
self.assertAlmostEqual(result.aux_operators_evaluated["aux_op1"][1], 0.0)
self.assertAlmostEqual(result.aux_operators_evaluated["aux_op2"][1], 0.0)

with self.subTest("Test with additional zero and None operators."):
extra_ops = {**aux_ops, "None_operator": None, "zero_operator": 0}
result = algo.compute_minimum_eigenvalue(
operator=self.qubit_op, aux_operators=extra_ops
)
self.assertAlmostEqual(result.eigenvalue, -1.85727503 + 0j)
self.assertEqual(len(result.aux_operator_eigenvalues), 3)
self.assertEqual(len(result.aux_operators_evaluated), 3)
# expectation values
self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op1"][0], 2, places=6)
self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op2"][0], 0, places=6)
self.assertEqual(result.aux_operator_eigenvalues["zero_operator"][0], 0.0)
self.assertTrue("None_operator" not in result.aux_operator_eigenvalues.keys())
self.assertAlmostEqual(result.aux_operators_evaluated["aux_op1"][0], 2, places=6)
self.assertAlmostEqual(result.aux_operators_evaluated["aux_op2"][0], 0, places=6)
self.assertEqual(result.aux_operators_evaluated["zero_operator"][0], 0.0)
self.assertTrue("None_operator" not in result.aux_operators_evaluated.keys())
# standard deviations
self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op1"][1], 0.0)
self.assertAlmostEqual(result.aux_operator_eigenvalues["aux_op2"][1], 0.0)
self.assertAlmostEqual(result.aux_operator_eigenvalues["zero_operator"][1], 0.0)
self.assertAlmostEqual(result.aux_operators_evaluated["aux_op1"][1], 0.0)
self.assertAlmostEqual(result.aux_operators_evaluated["aux_op2"][1], 0.0)
self.assertAlmostEqual(result.aux_operators_evaluated["zero_operator"][1], 0.0)


if __name__ == "__main__":
Expand Down
Loading

0 comments on commit 0e0ebac

Please sign in to comment.