Skip to content
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

Merged
merged 35 commits into from
Dec 1, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
5763cb7
[WIP] naive migration to dict-based aux_operators
mrossinek Oct 15, 2021
ef8ca14
[WIP] extract ListOrDict logic into class
mrossinek Oct 21, 2021
51f2580
Revert ListOrDict integration
mrossinek Oct 21, 2021
f1852a1
Add basic unittest for dict-based aux ops
mrossinek Oct 21, 2021
9fa525c
Refactor
mrossinek Oct 22, 2021
e8a8f97
Extend aux_operators public extension to support dict too
mrossinek Oct 22, 2021
b520b1e
Fix lint
mrossinek Oct 22, 2021
ef06a84
Revert some unnecessary changes
mrossinek Oct 22, 2021
b9904ea
Update docstrings
mrossinek Oct 26, 2021
50fc492
Fix spell
mrossinek Oct 26, 2021
cb4ff7f
Remove unused import
mrossinek Oct 26, 2021
bffba0f
Merge branch 'main' into dict-based-aux-operators
manoelmarques Oct 29, 2021
49d116d
Merge branch 'main' into dict-based-aux-operators
mrossinek Nov 3, 2021
250f3df
Merge branch 'main' into dict-based-aux-operators
mrossinek Nov 8, 2021
6448811
Fix lint
mrossinek Nov 8, 2021
702812a
Reuse ListOrDict-type alias from Terra
mrossinek Nov 8, 2021
3a518f4
Remove BaseProblem.main_property_name setter
mrossinek Nov 8, 2021
a0cc750
Improve commutation debug message
mrossinek Nov 8, 2021
1c05bd4
Extract new aux_operators interface into global setting
mrossinek Nov 8, 2021
49870c4
Run black
mrossinek Nov 8, 2021
c45c6de
Add DeprecationWarning for list-based aux_operators
mrossinek Nov 8, 2021
c344832
Log warning instead of raising it
mrossinek Nov 8, 2021
f65105f
Merge branch 'main' into dict-based-aux-operators
mrossinek Nov 8, 2021
057c4ce
Merge branch 'main' into dict-based-aux-operators
mrossinek Nov 10, 2021
93632ef
Merge branch 'main' into dict-based-aux-operators
mrossinek Nov 11, 2021
ab3e4d6
Merge branch 'main' into dict-based-aux-operators
mrossinek Nov 22, 2021
e21bfa7
Merge main and fix conflicts
manoelmarques Nov 23, 2021
985e2be
Merge branch 'main' into dict-based-aux-operators
manoelmarques Nov 23, 2021
a5de212
Merge branch 'main' into dict-based-aux-operators
manoelmarques Nov 24, 2021
646f0ea
Merge branch 'main' into dict-based-aux-operators
mrossinek Nov 25, 2021
374e30c
Raise error upon aux_operator name clash
mrossinek Nov 25, 2021
58ab2ed
Evaluate aux_operators at ground state during QEOM
mrossinek Nov 25, 2021
1f36750
Merge branch 'main' into dict-based-aux-operators
manoelmarques Nov 25, 2021
727460f
Merge branch 'main' into dict-based-aux-operators
manoelmarques Nov 26, 2021
bd3368b
Merge branch 'main' into dict-based-aux-operators
manoelmarques Nov 29, 2021
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .pylintdict
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ arxiv
asparagine
aspartic
atol
attr
autosummary
avogadro
äquivalenzverbot
Expand Down
10 changes: 9 additions & 1 deletion qiskit_nature/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,11 +49,19 @@

"""

from .version import __version__
from qiskit.algorithms.minimum_eigen_solvers.minimum_eigen_solver import (
ListOrDict as ListOrDictType,
)

from .exceptions import QiskitNatureError, UnsupportMethodError
from .settings import settings
from .version import __version__


__all__ = [
"__version__",
"ListOrDictType",
"QiskitNatureError",
"UnsupportMethodError",
"settings",
]
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@

"""The calculation of excited states via an Eigensolver algorithm"""

from typing import List, Union, Optional
from typing import Union, Optional

from qiskit.algorithms import Eigensolver
from qiskit.opflow import PauliSumOp

from qiskit_nature import ListOrDictType, QiskitNatureError
from qiskit_nature.converters.second_quantization import QubitConverter
from qiskit_nature.converters.second_quantization.utils import ListOrDict
from qiskit_nature.operators.second_quantization import SecondQuantizedOp
from qiskit_nature.problems.second_quantization import BaseProblem
from qiskit_nature.results import EigenstateResult
Expand Down Expand Up @@ -58,7 +60,7 @@ def solver(self, solver: Union[Eigensolver, EigensolverFactory]) -> None:
def solve(
self,
problem: BaseProblem,
aux_operators: Optional[List[Union[SecondQuantizedOp, PauliSumOp]]] = None,
aux_operators: Optional[ListOrDictType[Union[SecondQuantizedOp, PauliSumOp]]] = None,
) -> EigenstateResult:
"""Compute Ground and Excited States properties.

Expand All @@ -67,8 +69,12 @@ def solve(
aux_operators: Additional auxiliary operators to evaluate.

Raises:
NotImplementedError: If an operator in ``aux_operators`` is not of type
``FermionicOperator``.
ValueError: if the grouped property object returned by the driver does not contain a
main property as requested by the problem being solved (`problem.main_property_name`)
QiskitNatureError: if the user-provided `aux_operators` contain a name which clashes
with an internally constructed auxiliary operator. Note: the names used for the
internal auxiliary operators correspond to the `Property.name` attributes which
generated the respective operators.

Returns:
An interpreted :class:`~.EigenstateResult`. For more information see also
Expand All @@ -79,19 +85,46 @@ def solve(
# by the user but also additional ones from the transformation
second_q_ops = problem.second_q_ops()

aux_second_q_ops: ListOrDictType[SecondQuantizedOp]
if isinstance(second_q_ops, list):
main_second_q_op = second_q_ops[0]
aux_second_q_ops = second_q_ops[1:]
elif isinstance(second_q_ops, dict):
name = problem.main_property_name
main_second_q_op = second_q_ops.pop(name, None)
if main_second_q_op is None:
raise ValueError(
f"The main `SecondQuantizedOp` associated with the {name} property cannot be "
"`None`."
)
aux_second_q_ops = second_q_ops

main_operator = self._qubit_converter.convert(
second_q_ops[0],
main_second_q_op,
num_particles=problem.num_particles,
sector_locator=problem.symmetry_sector_locator,
)
aux_ops = self._qubit_converter.convert_match(second_q_ops[1:])
aux_ops = self._qubit_converter.convert_match(aux_second_q_ops)

if aux_operators is not None:
for aux_op in aux_operators:
wrapped_aux_operators: ListOrDict[Union[SecondQuantizedOp, PauliSumOp]] = ListOrDict(
aux_operators
)
for name, aux_op in iter(wrapped_aux_operators):
if isinstance(aux_op, SecondQuantizedOp):
aux_ops.append(self._qubit_converter.convert_match(aux_op, True))
converted_aux_op = self._qubit_converter.convert_match(aux_op, True)
else:
aux_ops.append(aux_op)
converted_aux_op = aux_op
if isinstance(aux_ops, list):
aux_ops.append(converted_aux_op)
elif isinstance(aux_ops, dict):
if name in aux_ops.keys():
raise QiskitNatureError(
f"The key '{name}' is already taken by an internally constructed "
"auxliliary operator! Please use a different name for your custom "
"operator."
)
aux_ops[name] = converted_aux_op

if isinstance(self._solver, EigensolverFactory):
# this must be called after transformation.transform
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,11 @@
""" The excited states calculation interface """

from abc import ABC, abstractmethod
from typing import List, Optional, Union
from typing import Optional, Union

from qiskit.opflow import PauliSumOp

from qiskit_nature import ListOrDictType
from qiskit_nature.operators.second_quantization import SecondQuantizedOp
from qiskit_nature.problems.second_quantization import BaseProblem
from qiskit_nature.results import EigenstateResult
Expand All @@ -29,7 +30,7 @@ class ExcitedStatesSolver(ABC):
def solve(
self,
problem: BaseProblem,
aux_operators: Optional[List[Union[SecondQuantizedOp, PauliSumOp]]] = None,
aux_operators: Optional[ListOrDictType[Union[SecondQuantizedOp, PauliSumOp]]] = None,
) -> EigenstateResult:
r"""Compute the excited states energies of the molecule that was supplied via the driver.

Expand Down
14 changes: 10 additions & 4 deletions qiskit_nature/algorithms/excited_states_solvers/qeom.py
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@
PauliSumOp,
)

from qiskit_nature import ListOrDictType
from qiskit_nature.operators.second_quantization import SecondQuantizedOp
from qiskit_nature.problems.second_quantization import BaseProblem
from qiskit_nature.results import EigenstateResult
Expand Down Expand Up @@ -79,7 +80,7 @@ def excitations(self, excitations: Union[str, List[List[int]]]) -> None:
def solve(
self,
problem: BaseProblem,
aux_operators: Optional[List[SecondQuantizedOp]] = None,
aux_operators: Optional[ListOrDictType[SecondQuantizedOp]] = None,
) -> EigenstateResult:
"""Run the excited-states calculation.

Expand All @@ -102,13 +103,18 @@ def solve(
)

# 1. Run ground state calculation
groundstate_result = self._gsc.solve(problem)
groundstate_result = self._gsc.solve(problem, aux_operators)

# 2. Prepare the excitation operators
second_q_ops = problem.second_q_ops()
if isinstance(second_q_ops, list):
main_second_q_op = second_q_ops[0]
elif isinstance(second_q_ops, dict):
main_second_q_op = second_q_ops.pop(problem.main_property_name)

self._untapered_qubit_op_main = self._gsc.qubit_converter.convert_only(
problem.second_q_ops()[0], problem.num_particles
main_second_q_op, problem.num_particles
)

matrix_operators_dict, size = self._prepare_matrix_operators(problem)

# 3. Evaluate eom operators
Expand Down
47 changes: 41 additions & 6 deletions qiskit_nature/algorithms/ground_state_solvers/adapt_vqe.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,12 @@
from qiskit.circuit import QuantumCircuit
from qiskit.opflow import OperatorBase, PauliSumOp
from qiskit.utils.validation import validate_min
from qiskit_nature import ListOrDictType
from qiskit_nature.exceptions import QiskitNatureError
from qiskit_nature.circuit.library import UCC
from qiskit_nature.operators.second_quantization import SecondQuantizedOp
from qiskit_nature.converters.second_quantization import QubitConverter
from qiskit_nature.converters.second_quantization.utils import ListOrDict
from qiskit_nature.problems.second_quantization import BaseProblem
from qiskit_nature.results import ElectronicStructureResult

Expand Down Expand Up @@ -136,7 +138,7 @@ def _check_cyclicity(indices: List[int]) -> bool:
def solve(
self,
problem: BaseProblem,
aux_operators: Optional[List[Union[SecondQuantizedOp, PauliSumOp]]] = None,
aux_operators: Optional[ListOrDictType[Union[SecondQuantizedOp, PauliSumOp]]] = None,
) -> "AdaptVQEResult":
"""Computes the ground state.

Expand All @@ -147,6 +149,12 @@ def solve(
Raises:
QiskitNatureError: if a solver other than VQE or a ansatz other than UCCSD is provided
or if the algorithm finishes due to an unforeseen reason.
ValueError: if the grouped property object returned by the driver does not contain a
main property as requested by the problem being solved (`problem.main_property_name`)
QiskitNatureError: if the user-provided `aux_operators` contain a name which clashes
with an internally constructed auxiliary operator. Note: the names used for the
internal auxiliary operators correspond to the `Property.name` attributes which
generated the respective operators.

Returns:
An AdaptVQEResult which is an ElectronicStructureResult but also includes runtime
Expand All @@ -155,19 +163,46 @@ def solve(
"""
second_q_ops = problem.second_q_ops()

aux_second_q_ops: ListOrDictType[SecondQuantizedOp]
if isinstance(second_q_ops, list):
main_second_q_op = second_q_ops[0]
aux_second_q_ops = second_q_ops[1:]
elif isinstance(second_q_ops, dict):
name = problem.main_property_name
main_second_q_op = second_q_ops.pop(name, None)
if main_second_q_op is None:
raise ValueError(
f"The main `SecondQuantizedOp` associated with the {name} property cannot be "
"`None`."
)
aux_second_q_ops = second_q_ops

self._main_operator = self._qubit_converter.convert(
second_q_ops[0],
main_second_q_op,
num_particles=problem.num_particles,
sector_locator=problem.symmetry_sector_locator,
)
aux_ops = self._qubit_converter.convert_match(second_q_ops[1:])
aux_ops = self._qubit_converter.convert_match(aux_second_q_ops)

if aux_operators is not None:
for aux_op in aux_operators:
wrapped_aux_operators: ListOrDict[Union[SecondQuantizedOp, PauliSumOp]] = ListOrDict(
aux_operators
)
for name, aux_op in iter(wrapped_aux_operators):
if isinstance(aux_op, SecondQuantizedOp):
aux_ops.append(self._qubit_converter.convert_match(aux_op, True))
converted_aux_op = self._qubit_converter.convert_match(aux_op, True)
else:
aux_ops.append(aux_op)
converted_aux_op = aux_op
if isinstance(aux_ops, list):
aux_ops.append(converted_aux_op)
elif isinstance(aux_ops, dict):
if name in aux_ops.keys():
raise QiskitNatureError(
f"The key '{name}' is already taken by an internally constructed "
"auxliliary operator! Please use a different name for your custom "
"operator."
)
aux_ops[name] = converted_aux_op

if isinstance(self._solver, MinimumEigensolverFactory):
vqe = self._solver.get_solver(problem, self._qubit_converter)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
from qiskit.algorithms import MinimumEigensolver
from qiskit.opflow import OperatorBase, PauliSumOp, StateFn, CircuitSampler

from qiskit_nature import ListOrDictType, QiskitNatureError
from qiskit_nature.operators.second_quantization import SecondQuantizedOp
from qiskit_nature.converters.second_quantization import QubitConverter
from qiskit_nature.converters.second_quantization.utils import ListOrDict
from qiskit_nature.problems.second_quantization import BaseProblem
from qiskit_nature.results import EigenstateResult
from .ground_state_solver import GroundStateSolver
Expand Down Expand Up @@ -65,7 +67,7 @@ def returns_groundstate(self) -> bool:
def solve(
self,
problem: BaseProblem,
aux_operators: Optional[List[Union[SecondQuantizedOp, PauliSumOp]]] = None,
aux_operators: Optional[ListOrDictType[Union[SecondQuantizedOp, PauliSumOp]]] = None,
) -> EigenstateResult:
"""Compute Ground State properties.

Expand All @@ -74,8 +76,12 @@ def solve(
aux_operators: Additional auxiliary operators to evaluate.

Raises:
NotImplementedError: If an operator in ``aux_operators`` is not of type
``FermionicOperator``.
ValueError: if the grouped property object returned by the driver does not contain a
main property as requested by the problem being solved (`problem.main_property_name`)
QiskitNatureError: if the user-provided `aux_operators` contain a name which clashes
with an internally constructed auxiliary operator. Note: the names used for the
internal auxiliary operators correspond to the `Property.name` attributes which
generated the respective operators.

Returns:
An interpreted :class:`~.EigenstateResult`. For more information see also
Expand All @@ -86,19 +92,46 @@ def solve(
# user but also additional ones from the transformation
second_q_ops = problem.second_q_ops()

aux_second_q_ops: ListOrDictType[SecondQuantizedOp]
if isinstance(second_q_ops, list):
main_second_q_op = second_q_ops[0]
aux_second_q_ops = second_q_ops[1:]
elif isinstance(second_q_ops, dict):
name = problem.main_property_name
main_second_q_op = second_q_ops.pop(name, None)
if main_second_q_op is None:
raise ValueError(
f"The main `SecondQuantizedOp` associated with the {name} property cannot be "
"`None`."
)
aux_second_q_ops = second_q_ops

main_operator = self._qubit_converter.convert(
second_q_ops[0],
main_second_q_op,
num_particles=problem.num_particles,
sector_locator=problem.symmetry_sector_locator,
)
aux_ops = self._qubit_converter.convert_match(second_q_ops[1:])
aux_ops = self._qubit_converter.convert_match(aux_second_q_ops)

if aux_operators is not None:
for aux_op in aux_operators:
wrapped_aux_operators: ListOrDict[Union[SecondQuantizedOp, PauliSumOp]] = ListOrDict(
aux_operators
)
for name, aux_op in iter(wrapped_aux_operators):
if isinstance(aux_op, SecondQuantizedOp):
aux_ops.append(self._qubit_converter.convert_match(aux_op, True))
converted_aux_op = self._qubit_converter.convert_match(aux_op, True)
else:
aux_ops.append(aux_op)
converted_aux_op = aux_op
if isinstance(aux_ops, list):
aux_ops.append(converted_aux_op)
elif isinstance(aux_ops, dict):
if name in aux_ops.keys():
raise QiskitNatureError(
f"The key '{name}' is already taken by an internally constructed "
"auxliliary operator! Please use a different name for your custom "
"operator."
)
aux_ops[name] = converted_aux_op

if isinstance(self._solver, MinimumEigensolverFactory):
# this must be called after transformation.transform
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@
from qiskit.result import Result
from qiskit.opflow import OperatorBase, PauliSumOp

from qiskit_nature import ListOrDictType
from qiskit_nature.operators.second_quantization import SecondQuantizedOp
from qiskit_nature.converters.second_quantization import QubitConverter
from qiskit_nature.problems.second_quantization import BaseProblem
Expand All @@ -43,7 +44,7 @@ def __init__(self, qubit_converter: QubitConverter) -> None:
def solve(
self,
problem: BaseProblem,
aux_operators: Optional[List[Union[SecondQuantizedOp, PauliSumOp]]] = None,
aux_operators: Optional[ListOrDictType[Union[SecondQuantizedOp, PauliSumOp]]] = None,
) -> EigenstateResult:
"""Compute the ground state energy of the molecule that was supplied via the driver.

Expand Down
Loading