-
Notifications
You must be signed in to change notification settings - Fork 208
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
Dict-based aux_operators
#406
Dict-based aux_operators
#406
Conversation
We need to properly deprecate the old function signature of `second_q_ops` before we can fully switch to dict-based aux operators. For this, I introduce a new keyword argument `return_list` which defaults to the old way of list-based aux operators. In the following commits I will add DeprecationWarnings announcing a change of this default as well as unittests to assert the correct behavior of the dict-based aux operators.
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.
Some explanatory comments
from qiskit_nature.mappers.second_quantization import QubitMapper | ||
|
||
from qiskit_nature.operators.second_quantization import SecondQuantizedOp | ||
|
||
from .utils import ListOrDict |
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.
I'm not yet sure about the location of this utility class.
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.
Since the dictionaries need to be passed on down to min eig solvers in Terra, which also define a type, maybe the type should come from there somehow rather than having to be independently defined here too.
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.
Right now, this ListOrDict
class is simply a wrapper which simplifies the iteration over whatever original type the operator were in. I did this because otherwise a lot of code would be duplicated in the internals of the QubitConverter
.
In the end of the converter, the objects get's unwrapped anyways, before continuing with the rest of the stack.
That said, I agree that if we had a utility class such as this one on the Terra level, we would not need to do the unwrapping and could simply use that type everywhere. However, introducing such a class on the Terra level + using it as the type of how to pass operators back and forth would require exposing this utility class as part of the public API. I am not sure whether that is something we (or the Terra devs) want..
qubit_ops: ListOrDict[PauliSumOp] = ListOrDict() | ||
for name, second_q_op in iter(wrapped_second_q_ops): | ||
qubit_ops[name] = self._map(second_q_op) |
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.
FYI: this is a good example show-casing the usage of the ListOrDict
utility class. We avoid the duplication of many isinstance
checks against list
or dict
qiskit_nature/converters/second_quantization/utils/list_or_dict.py
Outdated
Show resolved
Hide resolved
qiskit_nature/properties/second_quantization/electronic/angular_momentum.py
Outdated
Show resolved
Hide resolved
@@ -44,7 +46,9 @@ def __init__(self) -> None: | |||
] | |||
] | |||
] = None | |||
self._aux_operator_eigenvalues: Optional[List[float]] = None | |||
self._aux_operator_eigenvalues: Optional[ | |||
List[ListOrDictType[Tuple[complex, complex]]] |
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.
Note the fix of the return type aligned with Qiskit/qiskit#7144
@@ -102,7 +103,7 @@ def from_legacy_driver_result(cls, result: LegacyDriverResult) -> "AngularMoment | |||
qmol.num_molecular_orbitals * 2, | |||
) | |||
|
|||
def second_q_ops(self) -> List[FermionicOp]: | |||
def second_q_ops(self, return_list: bool = True) -> ListOrDictType[FermionicOp]: |
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.
I am suggesting this once on the first occurrence in the Files
tab of this PR:
I would like to add a DeprecationWarning
in the case of return_list = True
, stating that in 3 months from now, the default will be changed to False
, immediately implying that Qiskit Nature switches to dict-based operators as the default across the entire stack.
In 3 months from now, we can then add a new DeprecationWarning
, stating that another 3 months later (i.e. 6 months from now), this keyword will be removed and, therefore, support for list-based aux operators will be dropped from the properties framework (I would argue to drop it from the entire stack but this can be discussed separately).
@woodsp-ibm @pbark I'm eager to hear your comments on this proposal 🙂
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.
Since there is a flag, that the user could switch over to dicts to immediately go the future I guess its possibly to do this in one deprecation move rather than two. Earlier in one of my comments I noted about perhaps a single global control flag - having it in one place seemed easier to change behavior without changing the overall API. Maybe that could emit a single warning at some point if its set to list behavior rather than dict. Another thought, about the 'main' operator - basically this is done by having some known name on the problem right that represents the key for the main operator - what is in index 0 currently of the list. I did not look too closely at your ListDict class but was wondering if it could be done via that instead so its more self-contained. Like having a method to add main operator and then one for getting it so you can know the key or you can do ops.main_operator for instance.
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.
I was also playing around with self-containing the name of the main operator on my ListOrDict
utility. However, this runs into integrates that class very tightly into our stack and makes it a core type rather than a simple utility for iterating our operators. I noted in a reply above, that this would work better if we were to extract such a class into Terra because otherwise we run into issues with the interface to them.
For now, I would consider keeping it as is, unless you believe that the Terra devs are open to such a change. (Note, that it would expose the ListOrDict
class as the expected type for all aux_operators
in the public API)
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.
As far as Terra is concerned there the main operator and the other operators that we wish to measure are separate - it is only in Nature where they were combined into one list/dict. I didn't look too much at the class - since as you say it needs to be unpacked/packed across the interface I guess it remains in Nature as such. The ListOrDict type however is common even if this class ends up being restricted to Nature.
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.
Yes I see. So you would keep it self-contained to Nature but would still prefer it to be more integrated with the stack?
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.
Since the intent is to eliminate List support I'm not sure an integration makes sense. The basic type, given whatever packing/upacking we do, needs to be consistent however we achieve that. The utility class I guess is a temporary artifact for Nature for the transition to dict only right - I will admit I did not look at it in detail.
qiskit_nature/problems/second_quantization/electronic/electronic_structure_problem.py
Show resolved
Hide resolved
qiskit_nature/problems/second_quantization/electronic/electronic_structure_problem.py
Outdated
Show resolved
Hide resolved
qiskit_nature/algorithms/excited_states_solvers/excited_states_eigensolver.py
Outdated
Show resolved
Hide resolved
qiskit_nature/__init__.py
Outdated
from .version import __version__ | ||
from .exceptions import QiskitNatureError, UnsupportMethodError | ||
|
||
# pylint: disable=invalid-name | ||
T = TypeVar("T") | ||
ListOrDictType = Union[List[Optional[T]], Dict[Union[int, str], T]] |
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 you want to do the dict so its different than the minimum eigensolver - i.e it defines keys as just 'str'? The reason was to ensure it can be serialized. So if you define int here there arguably be an impedance mismatch when going down the stack whereby perhaps int will not behave as expected - I'm thinking here more around the runtime and remoting these.
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.
Yeh you're right. Duplicating this is probably not a good way to go. Unfortunately, Qiskit Terra does not have one ground truth. Instead they have two definitions of ListOrDict
in qiskit.algorithms.minimum_eigen_solvers
and in qiskit.algorithms.eigen_solvers
. While they are currently identical, this is rather error prone and could probably be improved.
qiskit_nature/algorithms/excited_states_solvers/excited_states_eigensolver.py
Outdated
Show resolved
Hide resolved
from qiskit_nature.mappers.second_quantization import QubitMapper | ||
|
||
from qiskit_nature.operators.second_quantization import SecondQuantizedOp | ||
|
||
from .utils import ListOrDict |
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.
Since the dictionaries need to be passed on down to min eig solvers in Terra, which also define a type, maybe the type should come from there somehow rather than having to be independently defined here too.
qiskit_nature/converters/second_quantization/qubit_converter.py
Outdated
Show resolved
Hide resolved
qiskit_nature/problems/second_quantization/electronic/electronic_structure_problem.py
Outdated
Show resolved
Hide resolved
@@ -102,7 +103,7 @@ def from_legacy_driver_result(cls, result: LegacyDriverResult) -> "AngularMoment | |||
qmol.num_molecular_orbitals * 2, | |||
) | |||
|
|||
def second_q_ops(self) -> List[FermionicOp]: | |||
def second_q_ops(self, return_list: bool = True) -> ListOrDictType[FermionicOp]: |
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.
Since there is a flag, that the user could switch over to dicts to immediately go the future I guess its possibly to do this in one deprecation move rather than two. Earlier in one of my comments I noted about perhaps a single global control flag - having it in one place seemed easier to change behavior without changing the overall API. Maybe that could emit a single warning at some point if its set to list behavior rather than dict. Another thought, about the 'main' operator - basically this is done by having some known name on the problem right that represents the key for the main operator - what is in index 0 currently of the list. I did not look too closely at your ListDict class but was wondering if it could be done via that instead so its more self-contained. Like having a method to add main operator and then one for getting it so you can know the key or you can do ops.main_operator for instance.
This property should only ever be set during construction of a certain problem type. Removing the setter ensures this scenario.
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.
@woodsp-ibm shared some more comments with me offline:
aux_operators
are not being handled at all inQEOM
. Arguably we should at least pass them on to the GSC- name conflicts for dict-based
aux_operators
provided by the user are not being handled at all, either. As things are currently, the user-provided operators would overwrite the internally created ones. I will add a guard against such name collisions and raise a warning. For the time being that should be sufficient (obviously it being documented, too)
* [WIP] naive migration to dict-based aux_operators * [WIP] extract ListOrDict logic into class * Revert ListOrDict integration We need to properly deprecate the old function signature of `second_q_ops` before we can fully switch to dict-based aux operators. For this, I introduce a new keyword argument `return_list` which defaults to the old way of list-based aux operators. In the following commits I will add DeprecationWarnings announcing a change of this default as well as unittests to assert the correct behavior of the dict-based aux operators. * Add basic unittest for dict-based aux ops * Refactor * Extend aux_operators public extension to support dict too * Fix lint * Revert some unnecessary changes * Update docstrings * Fix spell * Remove unused import * Fix lint * Reuse ListOrDict-type alias from Terra * Remove BaseProblem.main_property_name setter This property should only ever be set during construction of a certain problem type. Removing the setter ensures this scenario. * Improve commutation debug message * Extract new aux_operators interface into global setting * Run black * Add DeprecationWarning for list-based aux_operators * Log warning instead of raising it * Raise error upon aux_operator name clash * Evaluate aux_operators at ground state during QEOM Co-authored-by: Manoel Marques <[email protected]>
Summary
This leverages Qiskit/qiskit#6870 by adding support for
dict
-basedaux_operators
.For now, we definitely need to support both ways of adding
aux_operators
but I suggest that we deprecate the list-based method. See upcoming comments below.Comments
Note, that my implementation of #26 will require this functionality.
Closes #396