-
Notifications
You must be signed in to change notification settings - Fork 2.4k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Support Dict[str, OperatorBase] for aux_operators (fix #6772) #6870
Changes from all commits
ef51d8b
23d0b31
11ef00e
7eb96e1
9be9006
4e09082
ccf1dc5
6824351
23cfa63
98a127f
e5dfae3
68895c9
808a193
fed1a7d
2b3a6d2
c4b4679
32e608b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -42,7 +42,7 @@ | |
from qiskit.utils import QuantumInstance, algorithm_globals | ||
from ..optimizers import Optimizer, SLSQP | ||
from ..variational_algorithm import VariationalAlgorithm, VariationalResult | ||
from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult | ||
from .minimum_eigen_solver import MinimumEigensolver, MinimumEigensolverResult, ListOrDict | ||
from ..exceptions import AlgorithmError | ||
|
||
logger = logging.getLogger(__name__) | ||
|
@@ -411,32 +411,42 @@ def supports_aux_operators(cls) -> bool: | |
def _eval_aux_ops( | ||
self, | ||
parameters: np.ndarray, | ||
aux_operators: List[OperatorBase], | ||
aux_operators: ListOrDict[OperatorBase], | ||
expectation: ExpectationBase, | ||
threshold: float = 1e-12, | ||
) -> np.ndarray: | ||
) -> ListOrDict[complex]: | ||
# Create new CircuitSampler to avoid breaking existing one's caches. | ||
sampler = CircuitSampler(self.quantum_instance) | ||
|
||
aux_op_meas = expectation.convert(StateFn(ListOp(aux_operators), is_measurement=True)) | ||
if isinstance(aux_operators, dict): | ||
list_op = ListOp(list(aux_operators.values())) | ||
else: | ||
list_op = ListOp(aux_operators) | ||
|
||
aux_op_meas = expectation.convert(StateFn(list_op, is_measurement=True)) | ||
aux_op_expect = aux_op_meas.compose(CircuitStateFn(self.ansatz.bind_parameters(parameters))) | ||
values = np.real(sampler.convert(aux_op_expect).eval()) | ||
|
||
# Discard values below threshold | ||
aux_op_results = values * (np.abs(values) > threshold) | ||
# Deal with the aux_op behavior where there can be Nones or Zero qubit Paulis in the list | ||
_aux_op_nones = [op is None for op in aux_operators] | ||
aux_operator_eigenvalues = [ | ||
None if is_none else [result] | ||
for (is_none, result) in zip(_aux_op_nones, aux_op_results) | ||
] | ||
# As this has mixed types, since it can included None, it needs to explicitly pass object | ||
# data type to avoid numpy 1.19 warning message about implicit conversion being deprecated | ||
aux_operator_eigenvalues = np.array([aux_operator_eigenvalues], dtype=object) | ||
|
||
# Return None eigenvalues for None operators if aux_operators is a list. | ||
# None operators are already dropped in compute_minimum_eigenvalue if aux_operators is a dict. | ||
if isinstance(aux_operators, list): | ||
aux_operator_eigenvalues = [None] * len(aux_operators) | ||
key_value_iterator = enumerate(aux_op_results) | ||
else: | ||
aux_operator_eigenvalues = {} | ||
key_value_iterator = zip(aux_operators.keys(), aux_op_results) | ||
|
||
for key, value in key_value_iterator: | ||
if aux_operators[key] is not None: | ||
aux_operator_eigenvalues[key] = value | ||
|
||
return aux_operator_eigenvalues | ||
|
||
def compute_minimum_eigenvalue( | ||
self, operator: OperatorBase, aux_operators: Optional[List[Optional[OperatorBase]]] = None | ||
self, operator: OperatorBase, aux_operators: Optional[ListOrDict[OperatorBase]] = None | ||
) -> MinimumEigensolverResult: | ||
super().compute_minimum_eigenvalue(operator, aux_operators) | ||
|
||
|
@@ -454,19 +464,24 @@ def compute_minimum_eigenvalue( | |
initial_point = _validate_initial_point(self.initial_point, self.ansatz) | ||
|
||
bounds = _validate_bounds(self.ansatz) | ||
|
||
# We need to handle the array entries being Optional i.e. having value None | ||
# We need to handle the array entries being zero or Optional i.e. having value None | ||
if aux_operators: | ||
zero_op = I.tensorpower(operator.num_qubits) * 0.0 | ||
converted = [] | ||
for op in aux_operators: | ||
if op is None: | ||
converted.append(zero_op) | ||
else: | ||
converted.append(op) | ||
|
||
# For some reason Chemistry passes aux_ops with 0 qubits and paulis sometimes. | ||
aux_operators = [zero_op if op == 0 else op for op in converted] | ||
# Convert the None and zero values when aux_operators is a list. | ||
# Drop None and convert zero values when aux_operators is a dict. | ||
if isinstance(aux_operators, list): | ||
key_op_iterator = enumerate(aux_operators) | ||
converted = [zero_op] * len(aux_operators) | ||
else: | ||
key_op_iterator = aux_operators.items() | ||
converted = {} | ||
for key, op in key_op_iterator: | ||
if op is not None: | ||
converted[key] = zero_op if op == 0 else op | ||
|
||
aux_operators = converted | ||
|
||
else: | ||
aux_operators = None | ||
|
||
|
@@ -532,7 +547,7 @@ def compute_minimum_eigenvalue( | |
|
||
if aux_operators is not None: | ||
aux_values = self._eval_aux_ops(opt_result.x, aux_operators, expectation=expectation) | ||
result.aux_operator_eigenvalues = aux_values[0] | ||
result.aux_operator_eigenvalues = aux_values | ||
Comment on lines
-535
to
+550
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I am not exactly sure why this was only storing the first entry.. @woodsp-ibm do you know this? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. In looking it seems to be the weird way it was built out. Starts out with a List which it then nests, when it does an np.array as below, and then in the above it unnested it |
||
|
||
return result | ||
|
||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
--- | ||
features: | ||
- | | ||
The ``EigenSolver`` and ``MinimumEigenSolver`` interfaces now support the type | ||
``Dict[str, Optional[OperatorBase]]`` for the ``aux_operators`` parameter in the respective | ||
``compute_eigenvalues`` and ``compute_minimum_eigenvalue`` methods. | ||
In this case, the auxiliary eigenvalues are also stored in a dictionary under the same keys | ||
provided by the `aux_operators` dictionary. Keys that correspond to an operator that does not commute | ||
with the main operator are dropped. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do we need to use an
OrderedDict
to ensure that this does not break?There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Starting from 3.6 a standard Python
dict
maintains the insertion order so I thought it would not be necessary to use anOrderedDict
https://stackoverflow.com/a/39537308/13115502
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks, that's very good to know! 👍